Mentezza
Mentezza

Reputation: 637

AudioPlayerAgent, timer and webservice

my application read a shoutcast.

The meta data of the music played is collected from a webservice that return me a Json (so i don't have to decode the stream). I call the webservice every 20 seconds with a timer, this works in my application, but doesn't works in the AudioPlayer.cs

    //Timer
    private DispatcherTimer timer;


    /// <remarks>
    /// AudioPlayer instances can share the same process. 
    /// Static fields can be used to share state between AudioPlayer instances
    /// or to communicate with the Audio Streaming agent.
    /// </remarks>
    public AudioPlayer()
    {
        if (!_classInitialized)
        {
            _classInitialized = true;
            // Subscribe to the managed exception handler
            Deployment.Current.Dispatcher.BeginInvoke(delegate
            {
                Application.Current.UnhandledException += AudioPlayer_UnhandledException;

            });
        }

        //I activate the timer
        timer = new DispatcherTimer
        {
            Interval = TimeSpan.FromSeconds(20) // <------- Error here UnauthorizedAccessException was unhandled. Invalid cross-thread access.
        };
        timer.Tick += timer_Tick;
        timer.Start();
    }


    private void timer_Tick(object sender, EventArgs e)
    {
        HttpWebRequest request = WebRequest.Create("http://127.0.0.81:8003/getmeta") as HttpWebRequest;
        request.BeginGetResponse(new AsyncCallback(AsyncBack), request);
    }

    private void AsyncBack(IAsyncResult ias)
    {
            HttpWebRequest req = (HttpWebRequest)ias.AsyncState;

            try
            {
                using (HttpWebResponse res = req.EndGetResponse(ias) as HttpWebResponse)
                {
                    StreamReader stream = new StreamReader(res.GetResponseStream());
                    String jsonToParse = stream.ReadToEnd();
                    JObject jObject = JObject.Parse(jsonToParse);

                    AudioTrack track = BackgroundAudioPlayer.Instance.Track;

                    track.BeginEdit();
                    track.Title = (string) jObject["title"];
                    track.Artist = (string) jObject["artist"];
                    track.Album = (string) jObject["album"];
                    track.EndEdit();


                    res.Close();
                }
            }
            catch (WebException e)
            {
                timer.Stop();
            }
    }

Thanks you for the help

Upvotes: 3

Views: 1287

Answers (1)

Shawn Kendrot
Shawn Kendrot

Reputation: 12465

The AudioPlayer class is quite unique. It only lives for a small period of time. In an application that uses the BackgroundAudioPlayer, yous implementation of the AudioPlayer class will only stay alive to accomplish the task of changing play state. So, when a user begins playing something, an instance of your AudioPlayer class will be created to accomplish the task of begin playing. Once you call NotifyComplete() within the OnUserAction or OnPlayStateChanged, the instance of your AudioPlayer goes away.

The background thread that the AudioPlayer is associated with will still be active and you can have other objects alive within that thread, but the AudioPlayer will be terminated. The default AudioPlayer that is created hints at this with the _classInitialized field. This is static because the AudioPlayer will be created many times, but we only want to subscrrbe to that event once.

I would suggest one of two things. The first is to only get the json response when you need to advance to the next song. You would not call NotifyComplete() until after the response has returned. Here is some pseudo code:

override OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
    GetJsonResponse();
}
private void GetJsonResponce()
{
    // async call to your service
    // When async completes:
    track.BeginEdit();
    track.Title = (string) jObject["title"];
    track.Artist = (string) jObject["artist"];
    track.Album = (string) jObject["album"];
    track.EndEdit();
    NotifyComplete();
}

The second would be to have a class that does this in the background. That class would have a static property to get the instance that is alive in the thread. Then your AudioPlayer would get the info it needs from that object

public class Songs
{
    static Songs _instance;
    public static Songs Instance
    { 
        get { return _instance ?? new Songs(); }
    }
    // Do you timer stuff here

    // Allow the ability to access the timer stuff also.
}
// in AudioPlayer
override OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
    Songs.Instance.GetStuff
    NotifyComplete();
}

Upvotes: 4

Related Questions