I have an Internet radio app that uses BackgroundAudioPlayer.
I need a timer in the Audio Playback Agent that will update the track title of the currently playing track of the BAP that is pulled from the Internet radio station's API.
Adding a DispatcherTimer into the Audio Playback Agent gives me a cross-thread exception, and using:
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
// Code
});
Didn't work.
I need the code in here because if I put the update code in the app itself, when the user navigates away from the app the updates stop (much unlike Windows 8's behavior).
I can't use Scheduled Agents since they only run once every 30 minutes (IIRC).
Is this possible or can this not be done on Windows Phone?
Below is an excerpt from the MSDN documentation for Background Audio Player:
Sending messages between tasks:
There are times when you will want to communicate between the two processes of a background audio app. For example, you might want the background task to notify the foreground task when a new track starts playing, and then send the new song title to the foreground task to display on the screen. A simple communication mechanism raises events in both the foreground and background processes. The SendMessageToForeground and SendMessageToBackground methods each invoke events in the corresponding task. Data can be passed as an argument to the event handler in the receiving task. Pass data using a new class called ValueSet. This class is a dictionary that contains a string as a key and other value types as values. You can pass simple value types such as int, string, bool, and so on.
https://msdn.microsoft.com/en-US/library/windows/apps/xaml/dn642090
Hope this helps!
I found a question which could help you : How to run a timer on background in windows phone 8?
when you set a timer which is checking every x seconds if the "title" differs from last known title then you could send this info back to it.
This could be the Code for the Timer:
Declare these:
string _newValue = string.Empty;
string _currentValue = string.Empty;
AudioTrack _tempTrack = null;
and set this as Tick for the Timer
if (this.BackgroundAudioPlayer != null)
{
if (this.BackgroundAudioPlayer.Instance != null)
{
if (this.BackgroundAudioPlayer.Instance.Track != null)
{
this._newValue= yourAPI.GetTitleOfTrack();
try
{
/* First try to get the current Track as own Var */
this._tempTrack = this.BackgroundAudioPlayer.Instance.Track;
if (this._tempTrack != null)
{
/* Then Read the .Tag Value from it, save to _currentValue */
if (this._tempTrack.Tag != null)
{ this._currentValue = this._tempTrack.Tag.ToString(); }
else
{ this._currentValue = string.Empty; }
/* Compare */
if (this._currentValue != this._newValue)
{
/* Edit the Track Tag from your original BAP */
this.BackgroundAudioPlayer.Instance.Track.Tag = this._newValue;
}
}
}
catch(Exception ex)
{
/* if something Crashes you can save the exception error for protocol */
}
}
}
}
Remember: Change "yourAPI.GetTitleOfTrack()"-Function from this with real Function Call of your API.
Have you considered updating the information in the background audio player agent as below in the track tag.
string newTag = "whatever you need to show";
AudioTrack track = BackgroundAudioPlayer.Instance.Track;
track.BeginEdit();
track.Tag = newTag;
track.EndEdit();
and then reading that tag in the front end by your application when needed?
Related
PROBLEM
I am using a secondary view to run my media files, but When I close my secondary view with close button on it ( while media is still playing ) the secondary view/window closes but the media somehow keeps playing because I can hear the sound and source of sound seems to be the primary view ( main app window ). how can I completely terminate the secondary window when I close it?
TRIED
I followed windows samples multiple views and was able to complete all steps, I copied the ViewLifetimeControl.cs file from the sample and used it in my project. the code runs fine until it reaches Windows.Current.Close() in released event of the secondary view.
Then it gives an exception when it tries "Window.Current.Close()" with in the released event. according to documentation exception occurs due to any on going changes ( which might be because of media file playing ), but I need to force close the window even when media file is playing how can I do that? btw here is the exception :
Message = "COM object that has been separated from its underlying RCW cannot be used."
Code to Create and Show secondary view
internal static async Task CompactOpen(string Title, string caption)
{
ViewLifetimeControl viewControl = null;
await CoreApplication.CreateNewView().Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
viewControl = ViewLifetimeControl.CreateForCurrentView();
viewControl.Title = Title;
viewControl.StartViewInUse();
var frame = new Frame();
frame.MinHeight = 200;
frame.MinWidth = 200;
frame.Navigate(typeof(CompactNowPlayingPage), new object[] { viewControl,caption});
Window.Current.Content = frame;
Window.Current.Activate();
ApplicationView.GetForCurrentView().Title = viewControl.Title;
});
((App)App.Current).SecondaryViews.Add(viewControl);
var selectedView = viewControl;
var sizePreference = new SizePreferenceString() { Title = "SizePreference", Preference = ViewSizePreference.Default };
var anchorSizePreference = new SizePreferenceString() { Title = "AnchorSizePreference", Preference = ViewSizePreference.Default };
if (selectedView != null && sizePreference != null && anchorSizePreference != null)
{
try
{
selectedView.StartViewInUse();
var viewShown = await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
selectedView.Id,
sizePreference.Preference,
ApplicationView.GetForCurrentView().Id,
anchorSizePreference.Preference);
if (!viewShown)
{
// The window wasn't actually shown, so release the reference to it
// This may trigger the window to be destroyed
}
// Signal that switching has completed and let the view close
selectedView.StopViewInUse();
}
catch (InvalidOperationException)
{
// The view could be in the process of closing, and
// this thread just hasn't updated. As part of being closed,
// this thread will be informed to clean up its list of
// views (see SecondaryViewPage.xaml.cs)
}
}
}
Released Event
private async void ViewLifetimeControl_Released(Object sender, EventArgs e)
{
((ViewLifetimeControl)sender).Released -= ViewLifetimeControl_Released;
// The ViewLifetimeControl object is bound to UI elements on the main thread
// So, the object must be removed from that thread
await mainDispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
((App)App.Current).SecondaryViews.Remove(thisViewControl);
});
// The released event is fired on the thread of the window
// it pertains to.
//
// It's important to make sure no work is scheduled on this thread
// after it starts to close (no data binding changes, no changes to
// XAML, creating new objects in destructors, etc.) since
// that will throw exceptions
Window.Current.Close(); //this is where that exception occurs
}
Note : both of above methods and even all the related variables, all of them I have followed the guidelines within the uwp sample for multiple views.
Thanks in advance, any help would be really appreciated, I only want to force close the secondary view ( If that's possible )
Is this in the editor or the app? If it's in your debug or build of the app, the secondary view is most likely still open but hidden. You may be using a custom close button which doesn't perform its job well enough. Instead of putting down SecondaryViews.Remove you should do what you had originally written and try StopViewInUse. It may not work, I'm not used to this kind of thing.
I am making a UWP App where I run Background Audio in the MainPage on a Button Click event. When I move to another page, there's also a different Media to play in Background Audio Task there.
How can I stop the currently playing Task to run the other? Should I define something globally? Any help regarding this issue?
Edit
I am using this sample: https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/BackgroundAudio While the backgroundAudio of the first Page is running, I go to the second page and on a click event I set a new List with the following code:
// First update the persisted start track
ApplicationSettingsHelper.SaveSettingsValue(ApplicationSettingsConstants.TrackId, RadioFacade.mySongs[0].MediaUri.ToString()); //here
ApplicationSettingsHelper.SaveSettingsValue(ApplicationSettingsConstants.Position, new TimeSpan().ToString());
// Start task
StartBackgroundAudioTask();
But the new song takes more than the estimated time to run and enter the else of this method:
private void StartBackgroundAudioTask()
{
AddMediaPlayerEventHandlers();
var startResult = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
bool result = backgroundAudioTaskStarted.WaitOne(10000);
//Send message to initiate playback
if (result == true)
{
MessageService.SendMessageToBackground(new UpdatePlaylistMessage(RadioFacade.mySongs));
MessageService.SendMessageToBackground(new StartPlaybackMessage());
}
else
{
throw new Exception("Background Audio Task didn't start in expected time");
}
});
startResult.Completed = new AsyncActionCompletedHandler(BackgroundTaskInitializationCompleted);
}
and the old (first playing) song keeps playing.
I tried to Stop the current BackgroundMediaPlayer using BackgroundMediaPLayer.Shutdown() but it didn't work.
Any idea how to let the old song stop and the current song play?
You can control the background media player by sending messages to it from the foreground. For example,
From the foreground app:
BackgroundMediaPlayer.SendMessageToBackground(new ValueSet
{
{"playnew", "some value"}
});
In your background task:
public sealed class AudioPlayer : IBackgroundTask
{
public void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundMediaPlayer.MessageReceivedFromForeground += BackgroundMediaPlayer_MessageReceivedFromForeground;
...
...
}
private async void BackgroundMediaPlayer_MessageReceivedFromForeground(object sender, MediaPlayerDataReceivedEventArgs e)
{
object value;
if (e.Data.TryGetValue("playnew", out value) && value != null)
{
// value will be whatever you pass from the foreground.
BackgroundMediaPlayer.Current.Pause();
BackgroundMediaPlayer.Current.Source = stream source;
BackgroundMediaPlayer.Current.Play();
}
}
...
}
"playnew" can be a global constant in your application. You can use the ValueSet to pass the live stream url to the background task as the value.
I am trying to change the AudioTrack title every 3 seconds, but It seems I cannot change it or it could not be possible using below:
void timer_Tick(object sender, EventArgs e)
{
BackgroundAudioPlayer player = BackgroundAudioPlayer.Instance;
switch (player.PlayerState)
{
case PlayState.Playing:
if (recitation != null && recitation.Count > 0)
{
if (++ayaRecitationCounter == recitation.Count)
{
ayaRecitationCounter = 0;
recitationTimer.Stop();
}
else
{
player.Track.Title = ayaRecitationCounter.ToString(); //Exception occurs here recitationTimer.Interval=TimeSpan.FromSeconds(recitation[ayaRecitationCounter].AyaDuration);
lsbReadingChapter.SelectedIndex = ayaRecitationCounter;
lsbReadingChapter.ScrollIntoView(lsbReadingChapter.SelectedIndex);
}
}
break;
}
}
The application is crushing and providing me below message:
Operation is not valid due to the current state of the object.
at Microsoft.Phone.BackgroundAudio.AudioTrack.set_Title(String value)
How can I change the title?
Thanks!
If you want to Edit AudioTrack once it's created you will have to use BeginEdit as MSDN says:
Once the track has been created using one of the AudioTrack constructors, then the BeginEdit() and EndEdit() methods must be used to update the object.
As I've checked - I was able to change Title (for example) while playing by this code:
AudioTrack track = BackgroundAudioPlayer.Instance.Track;
track.BeginEdit();
track.Title = "New Title";
track.EndEdit();
(accessing by a reference is needed here)
Of course it will be suitable to put some try-catch blocks in your code.
Pease also be aware that your Timer will work only when your App is in Foreground.
I currently have an application I'm writing in c# (using .NET) that requires me to start a timer as soon as a user sees an image on screen up until they respond with a key press.
Now I realise that practically this is very difficult given the monitor input lag and response time, the time the keyboard takes to physically send the message, the OS to process it, etc.
But I'm trying my best to reduce it down to mostly a constant error (the response time results will be used to compare one user to the next so a constant error isn't really an issue). However annoying hurdle is the variable caused by the monitor refresh rate, as I gather when my onPaint message is called and done with, it doesn't mean the image has actually been processed and sent from the graphics buffer?
Unfortunately time restrictions and other commitments would realistically restrict me to continuing this task in c# for windows.
So what I was wondering was if either handling all the drawing in OpenGL or DirectX or better still for me if it is possible to just using either OpenGL or DirectX to create an event when the screen is updated?
Another suggestion given to me previously was regarding V-Sync, if I switch this off is the image sent as soon as it is drawn? as opposed to sending images at a set rate synchronised to the monitor refresh rate?
You must render your graphic in a separate thread in order to:
Use vertical synchronisation to have a precise timing of the effective display of your image.
Get the precise timing of your user input (since user interface is not on the same thread than the render loop.
Initialise Direct3D to enable the VSync during render :
// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;
device = new Device(...
Perform the render in a separate thread:
Thread renderThread = new Thread(RenderLoop);
renderThread.Start();
shouldDisplayImageEvent = new AutoResetEvent();
Then use the following render loop:
void RenderLoop()
{
while(applicationActive)
{
device.BeginScene();
// Other rendering task
if (shouldDisplayImageEvent.WaitOne(0))
{
// Render image
// ...
userResponseStopwatch = new Stopwatch();
userResponseStopwatch.Start();
}
device.EndScene();
device.Present();
}
}
Then handle the user input :
void OnUserInput(object sender, EventArgs e)
{
if (userResponseStopwatch != null)
{
userResponseStopwatch.Stop();
float userResponseDuration = userResponseStopwatch.ElapsedMillisecond - 1000 / device.DisplayMode.RefreshRate - displayDeviceDelayConstant;
userResponseStopwatch = null;
}
}
You now use the shouldDisplayImageEvent.Set() event trigger to display the image as needed and start the stop watch.
First enable the VSync on your application idle loop :
// DirectX example
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.BackBufferCount = 1;
presentParams.PresentationInterval = PresentInterval.One;
device = new Device(...
Application.Idle += new EventHandler(OnApplicationIdle);
// More on this here : http://blogs.msdn.com/tmiller/archive/2005/05/05/415008.aspx
internal void OnApplicationIdle(object sender, EventArgs e)
{
Msg msg = new Msg();
while (true)
{
if (PeekMessage(out msg, IntPtr.Zero, 0, 0, 0))
break;
}
// Clearing render
// ...
if (displayImage)
{
// Render image
// ...
renderTime = DateTime.now();
}
device.Present();
}
With the vsync enabled, the device.Present function block until the next frame synchronisation, so if you compute the time between renderTime and the user input time and remove the display device delay + 16.67ms you should get your user response delay.
I'm Doing a project on FileTransfer in which i have a listview , i will get events from one of my class file for updating the percentage of the file sent so far,after receiving it i will place the percentage in my listview ,while doing that the listview got
a flickering effect how to avoid it.i used application.doevents() but it doesnt works. i have seen in torrents while updating the percent the list doesnt get flickered
how to achieve this .
void Sender_Progress(int CurrentValue, string Ip) // here im receiving Events
{
try
{
//if (CurrentValue == 1)
// UpdateTimer.Enabled = true;
//list_send.Items[CurrentValue].SubItems[4].Text = Ip.ToString();
//Application.DoEvents();
obj = new object[] {CurrentValue, Ip };
list_send.Invoke(new UpdateList(UpList), obj);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
}
public void UpList(int Val, string ind) // here im updating the listview
{
Application.DoEvents();
int index = 0;
index = Convert.ToInt32(ind);
index = index - 1;
list_send.Items[index].SubItems[4].Text = Val.ToString();
if (Val == 100)
{
list_send.Items[index].SubItems[2].Text = "Completed.";
//UpdateTimer.Enabled = false;
}
//Application.DoEvents();
}
Firstly, you don't need the DoEvents, since you are already correctly working on two threads. Remove that. After that, I expect the problem is simply doing too much too quickly. Is it possible to batch updates, and only send an update, say, every 20? 50? times? It isn't clear what the control is, but many have multiple-update modes; for example with ListView:
theList.BeginUpdate();
try {
// make multiple updates here...
} finally {
theList.EndUpdate();
}
I would then see about passing over a list of updates, say, every 20 times (unless each takes a considerable time) [note it must be a different list per Invoke, and you need to remember to send any remaining items at the end, too].
Use worker thread - it's available from the toolbox and has two events that are invoked in the main (UI) thread.
The Progress event can be used to signal the listbox that it need to refresh or that the task was completed.
i overcome the flickering effect succesfully,im getting events frequently ,i will get an integer everytime, i will store it in a variable and compare it with next variable received by the event if it matches i wont invoke the listview,otherwise i will invoke it.now the flickering goes away. thanks all.