Posted on • Originally published atmfkl.github.io
PulseMusic - music player design with Skia and LibVLC
In February 2015,Anish Chandran, a Microsoft UX and Visual designer, posted onDribble andBehance an original music player UX concept.
In August 2018,Javier Suárez Ruiz implemented andpublished on GitHub a Xamarin.Forms (iOS/Android) UI implementation of the audio player view of the PulseMusic concept by Anish.
As mentioned in thereadme:
The main objective of the sample is to show the creation of the Player's UI (circular progress, rotating cover, etc.). This App is NOT a real player.
So... let's turn it into a real player withLibVLCSharp.
This is an extract from the existingPlayerView.xaml:
<controls:CircleProgressGrid.Row="0"VerticalOptions="FillAndExpand"HorizontalOptions="FillAndExpand"Progress="{Binding Progress}"LineBackgroundColor ="{StaticResource BlackColor}"ProgressColor="{StaticResource PlayerRedColor}"StrokeWidth="12"Margin="12"/><buttonCircle:CircleButtonCommand="{Binding PlayCommand}"FontIcon="FontAwesome"Icon="{Binding Icon}"FontSize="{StaticResource FontSize16}"TextColor="{StaticResource WhiteColor}"HeightRequest="60"WidthRequest="60"BackgroundColor="{StaticResource PlayerRedColor}"HorizontalOptions="Center"VerticalOptions="Center"/><GridGrid.Row="1"Margin="70, -24, 70, 0"><Grid.ColumnDefinitions><ColumnDefinitionWidth="Auto"/><ColumnDefinitionWidth="Auto"/><ColumnDefinitionWidth="Auto"/><ColumnDefinitionWidth="Auto"/></Grid.ColumnDefinitions><LabelGrid.Column="0"Text="{Binding StartTime, Converter={StaticResource TimeSpanToStringConverter}}"Style="{StaticResource TimeTextStyle}"/><controls:ToggleButtonGrid.Column="1"Checked="False"Animate="False"CheckedImage="playonce_on"UnCheckedImage="playonce_off"/><controls:TapImageGrid.Column="2"Source="shuffle"/><LabelGrid.Column="3"Text="{Binding RemainTime, Converter={StaticResource TimeSpanToStringConverter}}"Style="{StaticResource TimeTextStyle}"/></Grid>
TheBinding
XAML keyword indicates the value is databinded to the corresponding ViewModel. For the demo code, Javier had used an artificial countdown to make the UI seem alive (ticks for position and time). Let's hook up a real player!
Xamarin.Forms has a pub/sub concept calledMessagingCenter, we will use it to propagate LibVLCSharp's playback events in the app.
readonlyMediaPlayer_mp;conststringURL="https://archive.org/download/ImagineDragons_201410/imagine%20dragons.mp4";publicvoidInit(){// create a libvlc media_mp.Media=newMedia(_libVLC,URL,Media.FromType.FromLocation);// disable video output, we only need audio_mp.Media.AddOption(":no-video");// subscribe to libvlc playback events_mp.TimeChanged+=TimeChanged;_mp.PositionChanged+=PositionChanged;_mp.LengthChanged+=LengthChanged;_mp.EndReached+=EndReached;_mp.Playing+=Playing;_mp.Paused+=Paused;}// when the libvlc mediaplayer events fire, publish an event with the MessagingCenterprivatevoidPositionChanged(objectsender,MediaPlayerPositionChangedEventArgse)=>MessagingCenter.Send(MessengerKeys.App,MessengerKeys.Position,e.Position);privatevoidPaused(objectsender,System.EventArgse)=>MessagingCenter.Send(MessengerKeys.App,MessengerKeys.Play,false);privatevoidPlaying(objectsender,System.EventArgse)=>MessagingCenter.Send(MessengerKeys.App,MessengerKeys.Play,true);privatevoidEndReached(objectsender,System.EventArgse)=>MessagingCenter.Send(MessengerKeys.App,MessengerKeys.EndReached);privatevoidLengthChanged(objectsender,MediaPlayerLengthChangedEventArgse)=>MessagingCenter.Send(MessengerKeys.App,MessengerKeys.Length,e.Length);privatevoidTimeChanged(objectsender,MediaPlayerTimeChangedEventArgse)=>MessagingCenter.Send(MessengerKeys.App,MessengerKeys.Time,e.Time);
Wecould also use the reverse event stream to control playback (one of several ways to do so).
constlongOFFSET=5000;// subscribe to UI app events for seeking.MessagingCenter.Subscribe<string>(MessengerKeys.App,MessengerKeys.Rewind,vm=>_mp.Time-=OFFSET);MessagingCenter.Subscribe<string>(MessengerKeys.App,MessengerKeys.Forward,vm=>_mp.Time+=OFFSET);
Now that our playback service is up and running, we need to use it from the ViewModel (which is bound to the UI). This is one way of doing so:
publicoverrideTaskLoadAsync(){_playbackService.Init();MessagingCenter.Subscribe<string,float>(MessengerKeys.App,MessengerKeys.Position,(app,position)=>Progress=position);MessagingCenter.Subscribe<string,long>(MessengerKeys.App,MessengerKeys.Time,(app,time)=>{RemainTime=TimeSpan.FromMilliseconds((double)newdecimal(_length-time));StartTime=TimeSpan.FromMilliseconds((double)newdecimal(time));});MessagingCenter.Subscribe<string,long>(MessengerKeys.App,MessengerKeys.Length,(app,length)=>_length=length);MessagingCenter.Subscribe<string>(MessengerKeys.App,MessengerKeys.EndReached,app=>EndReached());LoadSong();_playbackService.Play(true);returnbase.LoadAsync();}
Updating ViewModel properties such asProgress
,RemainTime
andStartTime
will automatically refresh the UI and trigger Skia animations accordingly.
Play
andPause
, as well as seeking, has also been bound from the UI to the libvlc engine usingLibVLCSharp
.
Credits:
- Anish Chandran for the PulseMusic design concept.
- Javier Suárez Ruiz for the PulseMusic Xamarin.FormsUI implementation.
Note: I deliberately picked a Creative Commons licensed version of the song "Imagine Dragons - Radioactive" (remix). Seehttps://archive.org/details/ImagineDragons_201410 and itslicense for more information.
This sample is available on theVideoLAN GitLab.
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse