Ejemplo n.º 1
0
    static void _TrackFinished( TrackFinishedInfo info )
    {
        Console.WriteLine( "TrackFinished: " + info.reason.ToString() );

         Exception e = info.exception;
         if (null != e)
            Console.WriteLine( e.ToString() );
    }
Ejemplo n.º 2
0
        ///
        /// This is the entry point for the buffer processing thread.
        ///
        /// \todo This needs exception handling.
        ///
        void _Mp3ReaderThread()
        {
            Trace.WriteLine( "Hello", "MP3" );

             Thread audioThread = null;
             try
             {
            audioThread = new Thread( new ThreadStart( _AudioThread ) );
            audioThread.Priority = ThreadPriority.AboveNormal;
            audioThread.Start();

            Trace.WriteLine( "Entering main loop", "MP3" );
            _configMutex.WaitOne();
            while (true)
            {
               // Execution will stop here if the parent thread tries to
               // change the configuration in some way.
               _configMutex.ReleaseMutex();
               _configMutex.WaitOne();

               switch (_state)
               {
               case State.STOP:
                  Trace.WriteLine( "  State.STOP", "MP3" );

                  // In case we were playing or whatever, be sure files and
                  // streams are closed:
                  if (_mp3Stream != null)
                  {
                     _mp3Stream.Close();
                     _mp3Stream = null;
                  }

                  // Stop the audio writer while we delete the estream.
                  if (false == _audioThreadMutex.WaitOne( AUDIO_TIMEOUT,
                                                          false ))
                  {
                     // If this happened, we probably want to kill the
                     // audio thread and restart esd. :(

                     throw new ApplicationException(
                        "Timed out waiting for the audio mutex" );
                  }
                  try
                  {
                     _ShutDownEstream();
                  }
                  finally
                  {
                     _audioThreadMutex.ReleaseMutex();
                  }

                  // Wait until the parent wakes us up.
                  _configMutex.ReleaseMutex();
                  _fileToPlayEvent.WaitOne();
                  Trace.WriteLine( "STOP -> " + _state.ToString(), "MP3" );
                  _configMutex.WaitOne();

                  // We've got the  _configMutex, so no race condition exists.
                  // I think. Maybe.
                  _fileToPlayEvent.Reset();
                  break;

               case State.PLAY_FILE_REQUEST:
                  Trace.WriteLine( "  State.PLAY_FILE_REQUEST", "MP3" );

                  if (_bufferSizeChanged ||  null == _estream)
                  {
                     // Stop the audio writer stream to handle buffer resize.
                     if (false == _audioThreadMutex.WaitOne( AUDIO_TIMEOUT,
                                                 false ))
                     {
                        // If this happened, we probably want to kill the
                        // audio thread and restart esd. :(

                        throw new ApplicationException(
                           "Timed out waiting for the audio mutex" );
                     }

                     try
                     {
                        if (!_StartUpEstream())
                        {
                           _state = State.STOP; // stop!
                           break; // * BREAK OUT **
                        }

                        _CreateMp3Buffers();
                        _underflowEvent.Set();
                        _bufferSizeChanged = false; // no longer
                     }
                     finally
                     {
                        _audioThreadMutex.ReleaseMutex();
                     }
                  }

                  // Start playing if we can.
                  if (_InternalStartNextFile() == false)
                     _state = State.STOP; // Nothing in the queue
                  else
                     _state = State.PLAYING;

                  break;

               case State.PLAYING:
                  //  Trace.WriteLine( "  State.PLAYING", "MP3" );
                  Debug.Assert( null != _estream,
                                "not created in PLAY_FILE_REQUEST?" );

                  // Wait for a free audio buffer
                  Buffer buffer = _WaitForAndPopFreeBuffer();

                  // The Mp3Stream wrapper is still a bit flaky. Especially
                  // it seems to throw exceptions at end-of-file sometimes,
                  // probably trying to read garbage. Encase it in a
                  // try/catch block:
                  Exception playbackException = null;
                  try
                  {
                     // Fill the buffer with goodness.
                     buffer.validBytes =
                        _mp3Stream.Read( buffer.mp3Buffer,
                                         0,
                                         buffer.mp3Buffer.Length );

                     if (null != OnReadBuffer)
                        OnReadBuffer( buffer.mp3Buffer, buffer.validBytes );
                  }
                  catch (Exception e)
                  {
                     // I'm not sure, but I think we may have to destroy
                     // and recreate the Mp3Stream to get it working again
                     // here.
                     Trace.WriteLine( "Problem Reading from MP3:"
                                        + e.ToString(),
                                      "MP3" );

                     // Flag the stream as finished. Heh!
                     buffer.validBytes = 0;

                     playbackException = e; // save for reporting
                  }

                  if (buffer.validBytes <= 0)
                  {
                     _PushFreeBuffer( buffer );

                     // Done with prev file: notify any listeners. Send them
                     // the exception if something went wrong

                     TrackFinishedInfo info;
                     if (null == playbackException)
                     {
                        info = new TrackFinishedInfo(
                           _playingTrack.index,
                           TrackFinishedInfo.Reason.NORMAL );
                     }
                     else
                     {
                        info = new TrackFinishedInfo
                           ( _playingTrack.index,
                             playbackException,
                             TrackFinishedInfo.Reason.PLAY_ERROR
                             );
                     }

                     if (null != OnTrackFinished)
                        OnTrackFinished( info );

                     if (_InternalStartNextFile() == false)
                        _state = State.STOP; // end of file
                  }
                  else
                  {
                     _PushMp3Buffer( buffer ); // Loaded with sample goodness
                  }
                  break;

               case State.SHUTDOWN_REQUEST:
                  Trace.WriteLine( "  State.SHUTDOWN_REQUEST", "MP3" );
                  _configMutex.ReleaseMutex();
                  // Handle cleanup in the "finally" block
                  return;

               default:
                  break;
               }
            }
             }
             catch (Exception reasonForCrashing)
             {
            Trace.WriteLine( reasonForCrashing.ToString(), "MP3" );
             }
             finally
             {
            if (null != audioThread)
            {
               // Try to get the audio thread mutex, but eventually give up
               // so we can clean up regardless.
               if (false == _audioThreadMutex.WaitOne( AUDIO_TIMEOUT, false ))
               {
                  // Couldn't get mutex. This is bad.
                  audioThread.Abort(); // Just kill the thread.
               }
               else
               {
                  try
                  {
                     ///
                     /// \todo Shut down audioThread properly (Join it)
                     /// instead of calling Abort().
                     ///
                     audioThread.Abort();
                  }
                  finally
                  {
                     _audioThreadMutex.ReleaseMutex();
                  }
               }
            }

            if (null != _estream)
            {
               _estream.FreeWriteBuffer();
               _estream.Close();
            }

            // Don't want to exit the thread while holding this mutex,
            // do we? It's possible we aren't holding it, but  certainly
            // we don't have to worry about releasing other threads'
            // claim.
            _configMutex.ReleaseMutex();

            Trace.WriteLine( "bye", "MP3" );
             }
        }
Ejemplo n.º 3
0
        ///
        /// Called by the mp3 reader when a track is finished. 
        ///
        /// \warning Called in the context of the reader thread!
        ///
        void _TrackFinishedCallback(  TrackFinishedInfo info )
        {
            _Trace( "[_TrackFinishedCallback]" );
             ++ _changeCount;

             // Previous track could be nothing?
             if (null != info)
             {
            Trace.WriteLine(
               String.Format(
                  "Track {0} finished. status:{1}",
                  info.key,
                  info.reason ) );

            // Because of the threading, I don't know we can guarantee
            // this will never happen. Let's find out:
            Debug.Assert( info.key == currentTrack.key,
                          "finished track != playing track" );

            switch (info.reason)
            {
            case TrackFinishedInfo.Reason.NORMAL:
            case TrackFinishedInfo.Reason.USER_REQUEST:
            case TrackFinishedInfo.Reason.PLAY_ERROR: // What to do with this?
               // Log that this track played until such time as it either
               // failed or was cancelled. (Don't log on error, because it
               // didn't really play, did it?)
               _database.IncrementPlayCount( info.key );
               break;

            case TrackFinishedInfo.Reason.OPEN_ERROR: // File probably missing
               // Ouch, potential deadlock.
               lock (_serializer)
               {
                  // Update the ref to the current-playing track data
                  PlayableData currentInfo = _PlaylistGetCurrent();
                  currentInfo.status = TrackStatus.MISSING;

                  _database.SetTrackStatus
                     ( info.key,
                       StatusDatabase.TrackStatus.MISSING );
               }
               break;

            default:
               throw new ApplicationException( "unexpected case in switch" );
            }
             }

             if (_shouldBePlaying)  // don't bother if we should be stopped
             {
            // Don't acquire the _serializer lock here, because this could
            // cause a deadlock!

            // Advance the playlist (_Playlist* functions are threadsafe)
            GotoNext( false );
             }
        }