/// <summary> /// Retrieve a tag with an arbitrary name. /// Returns an empty tag if the value isn't found or if no Xiph tags are present. /// </summary> /// <param name="TagName"> /// A <see cref="System.String"/> containing the name of the tag to find /// </param> /// <returns> /// An <see cref="OggTag"/> containing the returned tag /// </returns> public OggTag GetTag(string TagName) { if (TagName.Length <= 0) { return(OggUtilities.GetEmptyTag()); } // Save some processing time and just exit if we haven't been given a tag name // Based on tasty examples @ "Accessing Hidden Gems": http://developer.novell.com/wiki/index.php/TagLib_Sharp:_Examples TagLib.Ogg.XiphComment XC = (TagLib.Ogg.XiphComment)m_TagLibFile.GetTag(TagTypes.Xiph); if (XC != null) { string[] TagValue = XC.GetField(TagName); if (TagValue.Length == 0) { // Tag doesn't exist, return empty return(OggUtilities.GetEmptyTag()); } else { OggTag tmpTag; tmpTag.Name = TagName; tmpTag.IsArray = (TagValue.Length > 1); tmpTag.IsEmpty = false; tmpTag.Values = TagValue; tmpTag.Value = TagValue[0]; return(tmpTag); } } else { // No valid Xiph tags found return(OggUtilities.GetEmptyTag()); } }
/// <summary> /// Retrieve an array of all tag values /// Returns a zero-length array if no Xiph tags are found /// </summary> /// <returns> /// An <see cref="OggTag[]"/> containing the returned values /// </returns> public OggTag[] GetTags() { TagLib.Ogg.XiphComment XC = (TagLib.Ogg.XiphComment)m_TagLibFile.GetTag(TagTypes.Xiph); if (XC != null) { if (XC.FieldCount > 0) { OggTag[] tmpOggTag = new OggTag[XC.FieldCount]; int Index = 0; foreach (string FieldName in XC) { string[] TagValue = XC.GetField(FieldName); if (TagValue.Length == 0) { tmpOggTag[Index] = OggUtilities.GetEmptyTag(); // This should never happen, but I bet if I don't check it it will! } else { // Populate this tag tmpOggTag[Index].Name = FieldName; tmpOggTag[Index].IsArray = (TagValue.Length > 1); tmpOggTag[Index].IsEmpty = false; tmpOggTag[Index].Values = TagValue; tmpOggTag[Index].Value = TagValue[0]; } ++Index; // Increment the index so we know which OggTag we're molesting } // Done! Return the heap of tags return(tmpOggTag); } else { // Xiph tags contain no items return(new OggTag[0]); } } else { // No valid Xiph tags found return(new OggTag[0]); } }
// Player thread private void Player_Thread() { bool Running = true; bool ReachedEOF = false; bool UnderRun = false; while (Running) { // See what we're doing if (m_PlayerState == OggPlayerStatus.Playing) { // Check number of buffers int QueuedBuffers = 0; AL.GetSource(m_Source, ALGetSourcei.BuffersQueued, out QueuedBuffers); // EOF stuff if (ReachedEOF) { // We've come to the end of the file, just see if there are any buffers left in the queue if (QueuedBuffers > 0) { // We want to remove the buffers, so carry on to the usual playing section } else { lock (OALLocker) { // End of file & all buffers played, exit. Running = false; // Stop the output device if it isn't already if (AL.GetSourceState(m_Source) != ALSourceState.Stopped) { AL.SourceStop(m_Source); } m_CurrentFile.ResetFile(); // Reset file's internal pointer // De-allocate all buffers for (int i = 0; i < m_Buffers.Length; i++) { AL.DeleteBuffer(ref m_Buffers[i]); } m_Buffers = new uint[m_BufferCount]; } // Set state stuff & return StateChange(OggPlayerStatus.Stopped, OggPlayerStateChanger.EndOfFile); SendMessage(OggPlayerMessageType.PlaybackEndOfFile); return; } } // If the number of buffers is greater than 0 & the source isn't playing, poke it so it does if ((!ReachedEOF) && (QueuedBuffers > 0) && (AL.GetError() == ALError.NoError)) { if (AL.GetSourceState(m_Source) != ALSourceState.Playing) { AL.SourcePlay(m_Source); } } // Check for buffer underrun int ProcessedBuffers = 0; uint BufferRef = 0; lock (OALLocker) { AL.GetSource(m_Source, ALGetSourcei.BuffersProcessed, out ProcessedBuffers); } if (ProcessedBuffers >= m_BufferCount) { UnderRun = true; SendMessage(OggPlayerMessageType.BufferUnderrun); } else { UnderRun = false; } // Unbuffer any processed buffers while (ProcessedBuffers > 0) { OggBufferSegment obs; lock (OALLocker) { // For each buffer thats been processed, reload and queue a new one AL.SourceUnqueueBuffers(m_Source, 1, ref BufferRef); #if (DEBUG) if (AL.GetError() != ALError.NoError) { Console.WriteLine("SourceUnqueueBuffers: ALError: " + OggUtilities.GetEnumString(AL.GetError())); } #endif if (ReachedEOF) { --ProcessedBuffers; continue; } // If we're at the EOF loop to the next buffer here - we don't want to be trying to fill any more obs = m_CurrentFile.GetBufferSegment(m_BufferSize); // Get chunk of tasty buffer data with the configured segment } // Check the buffer segment for errors if (obs.ReturnValue > 0) { lock (OALLocker) { // No error, queue data AL.BufferData((int)BufferRef, m_CurrentFile.Format, obs.Buffer, obs.ReturnValue, obs.RateHz); #if (DEBUG) if (AL.GetError() != ALError.NoError) { Console.WriteLine("BufferData: ALError: " + OggUtilities.GetEnumString(AL.GetError())); } #endif AL.SourceQueueBuffers(m_Source, 1, ref BufferRef); #if (DEBUG) if (AL.GetError() != ALError.NoError) { Console.WriteLine("SourceQueueBuffers: ALError: " + OggUtilities.GetEnumString(AL.GetError())); } #endif } } else { if (obs.ReturnValue == 0) { // End of file SendMessage(OggPlayerMessageType.BufferEndOfFile); ReachedEOF = true; break; } else { // Something went wrong with the read lock (OALLocker) { m_PlayerState = OggPlayerStatus.Error; AL.SourceStop(m_Source); Running = false; } SendMessage(OggPlayerMessageType.FileReadError); break; } } // Check for errors m_LastError = AL.GetError(); if (m_LastError != ALError.NoError) { StateChange(OggPlayerStatus.Error, OggPlayerStateChanger.Error); lock (OALLocker) { AL.SourceStop(m_Source); } SendMessage(OggPlayerMessageType.OpenALError, m_LastError); Running = false; break; } --ProcessedBuffers; } // If we under-ran, restart the player if (UnderRun) { lock (OALLocker) { AL.SourcePlay(m_Source); } } // Do stuff with the time values & tick event m_PlayingOffset = m_CurrentFile.GetTime(); if (m_TickEnabled) { if (m_PlayingOffset >= m_LastTick + m_TickInterval) { m_LastTick = m_PlayingOffset; SendTick(m_PlayingOffset, m_PlayingOffset); } } } else if (m_PlayerState == OggPlayerStatus.Seeking) { // Just wait for us to finish seeking } else if (m_PlayerState == OggPlayerStatus.Paused) { // Just wait for us to un-pause } else { // Some other state, abort the playback 'cos we shouldn't // be in the Player_Thread in this case Running = false; } // Allow other shizzle to execute if (m_UpdateDelay > 0) { Thread.Sleep(m_UpdateDelay); } } }
protected void StateChange(OggPlayerStatus NewState, OggPlayerStateChanger Reason) { if (StateChanged != null) { StateChanged(this, new OggPlayerStateChangedArgs(m_PlayerState, NewState, Reason)); } #if (DEBUG) Console.WriteLine(DateTime.Now.ToLongTimeString() + "\tOggPlayer::StateChange -- From: " + OggUtilities.GetEnumString(m_PlayerState) + " -- To: " + OggUtilities.GetEnumString(NewState)); #endif m_PlayerState = NewState; }
protected void SendMessage(OggPlayerMessageType Message, object Params) { if (PlayerMessage != null) { PlayerMessage(this, new OggPlayerMessageArgs(Message, Params)); } #if (DEBUG) Console.WriteLine(DateTime.Now.ToLongTimeString() + "\tOggPlayer::SendMessage -- Message: " + OggUtilities.GetEnumString(Message)); #endif }