From 49b0ae4becaf856a2db01a1962cc0d70f82eb8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Van=20Canneyt?= Date: Thu, 13 Jan 2022 21:42:30 +0100 Subject: [PATCH] * Player demo --- demo/player/headset.jpeg | Bin 0 -> 15480 bytes demo/player/index.html | 54 +++++++++ demo/player/main.lpi | 81 ++++++++++++++ demo/player/main.pas | 229 +++++++++++++++++++++++++++++++++++++++ demo/player/style.css | 143 ++++++++++++++++++++++++ demo/player/tracks.js | 46 ++++++++ 6 files changed, 553 insertions(+) create mode 100644 demo/player/headset.jpeg create mode 100644 demo/player/index.html create mode 100644 demo/player/main.lpi create mode 100644 demo/player/main.pas create mode 100644 demo/player/style.css create mode 100644 demo/player/tracks.js diff --git a/demo/player/headset.jpeg b/demo/player/headset.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..6d6b49de795685e16c15330ff7dbd71dcaddc1b5 GIT binary patch literal 15480 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 J8{lp6{{q9I_^AK@ literal 0 HcmV?d00001 diff --git a/demo/player/index.html b/demo/player/index.html new file mode 100644 index 0000000..2994282 --- /dev/null +++ b/demo/player/index.html @@ -0,0 +1,54 @@ + + + + Media player demo + + + + + + +
+ +
+
+
PLAYING x OF y
+
+
Track Name
+
Track Artist
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
00:00
+ +
00:00
+
+ +
+ + + +
+
+
+ Program sources   Royalty-free music from Bensound + + + \ No newline at end of file diff --git a/demo/player/main.lpi b/demo/player/main.lpi new file mode 100644 index 0000000..0a628e3 --- /dev/null +++ b/demo/player/main.lpi @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <CustomData Count="2"> + <Item0 Name="PasJSPort" Value="0"/> + <Item1 Name="PasJSWebBrowserProject" Value="1"/> + </CustomData> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <Units> + <Unit> + <Filename Value="main.pas"/> + <IsPartOfProject Value="True"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <Target FileExt=".js"> + <Filename Value="main"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="js"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <AllowLabel Value="False"/> + <CPPInline Value="False"/> + <UseAnsiStrings Value="False"/> + </SyntaxOptions> + </Parsing> + <CodeGeneration> + <TargetOS Value="browser"/> + </CodeGeneration> + <Linking> + <Debugging> + <GenerateDebugInfo Value="False"/> + <UseLineInfoUnit Value="False"/> + </Debugging> + </Linking> + <Other> + <CustomOptions Value="-Jeutf-8 -Jirtl.js -Jc -Jminclude"/> + <CompilerPath Value="$(pas2js)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/demo/player/main.pas b/demo/player/main.pas new file mode 100644 index 0000000..5db24bb --- /dev/null +++ b/demo/player/main.pas @@ -0,0 +1,229 @@ +program main; + +{$mode objfpc} + +uses + browserapp, JS, Classes, SysUtils, Web; + +type + TTrack = Record + name, + artist, + image, + path, + display, + url : String; + end; + + TMyApplication = class(TBrowserApplication) + now_playing, + track_art, + track_name, + track_artist, + display_text, + playpause_btn, + next_btn, + prev_btn, + curr_time, + total_duration : TJSHTMLElement; + curr_track : TJSHTMLAudioElement; + volume_slider, + seek_slider : TJSHTMLinputElement; + isPlaying : Boolean; + track_index : Integer; + updateTimer: Integer; + Procedure BindElements; + Procedure seekTo(Event: TJSEvent); + Procedure SetVolume(Event: TJSEvent); + Procedure DoPlayPause(Event: TJSEvent); + Procedure NextTrack(Event: TJSEvent); + Procedure PrevTrack(Event: TJSEvent); + Procedure PlayTrack; + Procedure PauseTrack; + procedure LoadTrack(aIndex : Integer); + procedure random_bg_color; + procedure resetValues; + procedure seekUpdate; + procedure doRun; override; + private + end; + +var + track_list : Array of TTrack; external name 'track_list'; + + +procedure TMyApplication.seekUpdate; + +Var + seekPosition : Double; + currentMinutes, + currentSeconds, + durationMinutes, + durationSeconds : Integer; + +begin + if jsIsNan(curr_track.duration) then exit; + seekPosition:=curr_track.currentTime * (100 / curr_track.duration); + seek_slider.value :=FloatToStr(seekPosition); + currentMinutes:=Round(curr_track.currentTime / 60); + currentSeconds:=Round(curr_track.currentTime - currentMinutes * 60); + durationMinutes:=Round(curr_track.duration / 60); + durationSeconds:=Round(curr_track.duration - durationMinutes * 60); + curr_time.innerText:= Format('%.2:%d2',[currentMinutes,currentSeconds]); + total_duration.innerText:=Format('%.2:%d2',[durationMinutes,durationSeconds]); +end; + +procedure TMyApplication.BindElements; + + Function GetElement (aClass : String) : TJSHTMLELement; + + begin + + Result:=TJSHTMLELement(Document.querySelector('.'+aClass)); + Writeln('Looking for ',aClass,' : ',assigned(Result)); + end; + +begin + now_playing :=GetElement('now-playing'); + track_art :=GetElement('track-art'); + track_name :=GetElement('track-name'); + track_artist :=GetElement('track-artist'); + display_text :=GetElement('display-text'); + + playpause_btn :=GetElement('playpause-track'); + playpause_btn.AddEventListener('click',@DoPlayPause); + next_btn :=GetElement('next-track'); + next_btn.AddEventListener('click',@NextTrack); + prev_btn :=GetElement('prev-track'); + prev_btn.AddEventListener('click',@PrevTrack); + seek_slider :=TJSHTMLInputElement(GetElement('seek_slider')); + seek_slider.AddEventListener('change',@SeekTo); + volume_slider :=TJSHTMLInputElement(GetElement('volume_slider')); + volume_slider.AddEventListener('change',@SetVolume); + curr_time :=GetElement('current-time'); + total_duration :=GetElement('total-duration'); +end; + +procedure TMyApplication.seekTo(Event: TJSEvent); + +Var + dSeekTo : Double; + +begin + dSeekTo := curr_track.duration * (StrToFloatDef(seek_slider.value,50) / 100); + curr_track.currentTime := dSeekTo; +end; + +procedure TMyApplication.SetVolume(Event: TJSEvent); +begin + curr_track.volume := StrToFloatDef(volume_slider.value,50) / 100; +end; + +procedure TMyApplication.DoPlayPause(Event: TJSEvent); +begin + if isPlaying then + pauseTrack + else + playTrack; +end; + +procedure TMyApplication.NextTrack(Event: TJSEvent); +begin + // Go back to the first track if the + // current one is the last in the track list + if (track_index < (Length(track_list) - 1)) then + Inc(track_index) + else + track_index:=0; + // Load and play the new track + loadTrack(track_index); + playTrack(); +end; + +procedure TMyApplication.PrevTrack(Event: TJSEvent); +begin + if (track_index > 0) then + Dec(track_index) + else + track_index := Length(track_list)- 1; + loadTrack(track_index); + playTrack(); +end; + +procedure TMyApplication.PlayTrack; +begin + curr_track.play(); + isPlaying:=true; + playpause_btn.innerHTML := '<i class="fa fa-pause-circle fa-5x"></i>'; +end; + +procedure TMyApplication.PauseTrack; +begin + curr_track.pause(); + isPlaying:=false; + playpause_btn.innerHTML := '<i class="fa fa-play-circle fa-5x"></i>'; +end; + +procedure TMyApplication.LoadTrack(aIndex: Integer); +begin + // Clear the previous seek timer + window.clearInterval(updateTimer); + resetValues(); + + curr_track.src :=track_list[track_index].path; + curr_track.load(); + + track_art.style.SetProperty('background-image','url("' + track_list[track_index].image+ '")'); + track_art.style.SetProperty('background-image','url("' + track_list[track_index].image+ '")'); + track_artist.InnerHTML := track_list[track_index].artist; + track_name.InnerHTML := track_list[track_index].name; + display_text.InnerHTML:=String(track_list[track_index].display); + now_playing.InnerHTML := Format('PLAYING %d OF %d',[track_index + 1,Length(track_list)]); + updateTimer :=Window.setInterval(@seekUpdate, 1000); + curr_track.addEventListener('ended', @nextTrack); + random_bg_color(); +end; + +procedure TMyApplication.random_bg_color; + +Const + Lim = 256 - 64; + +Var + red,Green,blue : Integer; + col : string; +begin + red:=Round(random(Lim) + 64); + green:=Round(random(lim) + 64); + blue:=Round(random(lim)+64); + col:= Format('rgb(%d,%d,%d)',[red, green ,blue]); + TJSHTMLELement(document.body).style.SetProperty('background',col); +end; + +procedure TMyApplication.resetValues; +begin + curr_time.InnerHTML:= '00:00'; + total_duration.InnerHTML:= '00:00'; + seek_slider.value:='0'; +end; + +procedure TMyApplication.doRun; + +begin + Terminate; + BindElements; + curr_track:=TJSHTMLAudioElement(document.createElement('audio')); + curr_track.autoplay:=true; + ResetValues; + Window.SetInterval(@seekUpdate,1000); + LoadTrack(0); +end; + +var + Application : TMyApplication; + +begin + Application:=TMyApplication.Create(nil); + Application.Initialize; + Application.Run; +end. diff --git a/demo/player/style.css b/demo/player/style.css new file mode 100644 index 0000000..d0a72b0 --- /dev/null +++ b/demo/player/style.css @@ -0,0 +1,143 @@ +body { +background-color: lightgreen; + +/* Smoothly transition the background color */ +transition: background-color .5s; +} + +/* Using flex with the column direction to +align items in a vertical direction */ +.player { +height: 95vh; +display: flex; +align-items: center; +flex-direction: column; +justify-content: center; +} + +.details { +display: flex; +align-items: center; +flex-direction: column; +justify-content: center; +margin-top: 25px; +} + +.track-art { +margin: 25px; +height: 250px; +width: 250px; +background-image: URL("headset.jpeg"); +background-size: cover; +background-position: center; +border-radius: 15%; +} + +/* Changing the font sizes to suitable ones */ +.now-playing { +font-size: 1rem; +} + +.track-name { +font-size: 3rem; +} + +.display-text { +font-size: 3rem; +} + +.track-artist { +font-size: 1.5rem; +} + +/* Using flex with the row direction to +align items in a horizontal direction */ +.buttons { +display: flex; +flex-direction: row; +align-items: center; +} + +.playpause-track, +.prev-track, +.next-track { +padding: 25px; +opacity: 0.8; + +/* Smoothly transition the opacity */ +transition: opacity .2s; +} + +/* Change the opacity when mouse is hovered */ +.playpause-track:hover, +.prev-track:hover, +.next-track:hover { +opacity: 1.0; +} + +/* Define the slider width so that it scales properly */ +.slider_container { +width: 75%; +max-width: 400px; +display: flex; +justify-content: center; +align-items: center; +} + +/* Modify the appearance of the slider */ +.seek_slider, .volume_slider { +-webkit-appearance: none; +-moz-appearance: none; +appearance: none; +height: 5px; +background: black; +opacity: 0.7; +-webkit-transition: .2s; +transition: opacity .2s; +} + +/* Modify the appearance of the slider thumb */ +.seek_slider::-webkit-slider-thumb, +.volume_slider::-webkit-slider-thumb { +-webkit-appearance: none; +-moz-appearance: none; +appearance: none; +width: 15px; +height: 15px; +background: white; +cursor: pointer; +border-radius: 50%; +} + +/* Change the opacity when mouse is hovered */ +.seek_slider:hover, +.volume_slider:hover { +opacity: 1.0; +} + +.seek_slider { +width: 60%; +} + +.volume_slider { +width: 30%; +} + +.current-time, +.total-duration { +padding: 10px; +} + +i.fa-volume-down, +i.fa-volume-up { +padding: 10px; +} + +/* Change the mouse cursor to a pointer +when hovered over */ +i.fa-play-circle, +i.fa-pause-circle, +i.fa-step-forward, +i.fa-step-backward { +cursor: pointer; +} diff --git a/demo/player/tracks.js b/demo/player/tracks.js new file mode 100644 index 0000000..9f8aa1a --- /dev/null +++ b/demo/player/tracks.js @@ -0,0 +1,46 @@ + +var track_list = [ +{ + display: "here we go with our demo!", + name: "Epic music", + artist: "Bensound epic", + image: "https://www.freepascal.org/~michael/pas2js-demos/player/epic.jpg", + path: "https://www.freepascal.org/~michael/pas2js-demos/player/bensound-epic.mp3", +}, +{ + display: "Now for something less hard...", + name: "Rumble rock", + artist: "Bensound rumble", + image: "https://www.freepascal.org/~michael/pas2js-demos/player/rumble.jpg", + path: "https://www.freepascal.org/~michael/pas2js-demos/player/bensound-rumble.mp3" +}, +{ + display: "We even have dubstep", + name: "Dubstep for the fans", + artist: "Bensound dubstep", + image: "https://www.freepascal.org/~michael/pas2js-demos/player/dubstep.jpg", + path: "https://www.freepascal.org/~michael/pas2js-demos/player/bensound-dubstep.mp3" +}, +{ + display: "We also have some pop tunes", + name: "Pop tune beyond the line", + artist: "Bensound pop tune ", + image: "https://www.freepascal.org/~michael/pas2js-demos/player/beyondtheline.jpg", + path: "https://www.freepascal.org/~michael/pas2js-demos/player/bensound-beyondtheline.mp3" +}, +{ + display: "Something soundtrack-like.", + name: "Once again", + artist: "Bensound once again", + image: "https://www.freepascal.org/~michael/pas2js-demos/player/onceagain.jpg", + path: "https://www.freepascal.org/~michael/pas2js-demos/player/bensound-onceagain.mp3", +}, +{ + display: "And going home with some jazzy tune!", + name: "Jazzy lounge", + artist: "Bensound the lounge", + image: "https://www.freepascal.org/~michael/pas2js-demos/player/thelounge.jpg", + path: "https://www.freepascal.org/~michael/pas2js-demos/player/bensound-thelounge.mp3", +} +]; +