public AudioPlayer(bool keepStreamAlive) { #if DEBUG DebugFix.Assert(BitConverter.IsLittleEndian); #endif // DEBUG m_KeepStreamAlive = keepStreamAlive; CurrentState = State.NotReady; initPreviewTimer(); }
public static long ConvertTimeToBytes(long timeInLocalUnits, uint samplingRate, ushort frameSize #if DEBUG , bool checkRoundtrip #endif ) { // ROUND: Convert.ToInt64(value) // TRUNCATE: (long)value double timeMillisecondsDecimal = (double)timeInLocalUnits / TIME_UNIT; int decimalPlaces = TIME_UNIT == 1 ? 0 : TIME_UNIT == 1000 ? 3 : TIME_UNIT == 1000000 ? 6 : 7; DebugFix.Assert(decimalPlaces != 7); timeMillisecondsDecimal = Math.Round(timeMillisecondsDecimal, decimalPlaces, MidpointRounding.AwayFromZero); double bytesDecimal = samplingRate * frameSize * timeMillisecondsDecimal / 1000; long bytesIntegral = (long)(USE_ROUND_NOT_TRUNCATE ? Math.Round(bytesDecimal) : Math.Truncate(bytesDecimal)); // checking whether we are loosing fractions //////DebugFix.Assert(bytesDecimal == (double)bytesIntegral); long bytesIntegralAligned = bytesIntegral - (bytesIntegral % frameSize); // block align ////DebugFix.Assert(bytesIntegralAligned == bytesIntegral); #if DEBUG if (checkRoundtrip) { //long bytesForOneUnit = ConvertTimeToBytes(1, samplingRate, frameSize, false); long timeInLocalUnitsForBlockAlign = ConvertBytesToTime(frameSize, samplingRate, frameSize, false); long timeRoundTrip = ConvertBytesToTime(bytesIntegralAligned, samplingRate, frameSize, false); DebugFix.Assert(Math.Abs(timeRoundTrip - timeInLocalUnits) <= timeInLocalUnitsForBlockAlign); } #endif return(bytesIntegralAligned); }
public static long ConvertBytesToTime(long bytes, uint samplingRate, ushort frameSize #if DEBUG , bool checkRoundtrip #endif ) { // ROUND: Convert.ToInt64(value) // TRUNCATE: (long)value long bytesAligned = bytes - (bytes % frameSize); // block align DebugFix.Assert(bytesAligned == bytes); double timeMillisecondsDecimal = 1000.0 * bytesAligned / (samplingRate * frameSize); int decimalPlaces = TIME_UNIT == 1 ? 0 : TIME_UNIT == 1000 ? 3 : TIME_UNIT == 1000000 ? 6 : 7; DebugFix.Assert(decimalPlaces != 7); timeMillisecondsDecimal = Math.Round(timeMillisecondsDecimal, decimalPlaces, MidpointRounding.AwayFromZero); double timeInLocalUnitsDecimal = timeMillisecondsDecimal * TIME_UNIT; long timeInLocalUnitsIntegral = (long)(USE_ROUND_NOT_TRUNCATE ? Math.Round(timeInLocalUnitsDecimal) : Math.Truncate(timeInLocalUnitsDecimal)); // checking whether we are loosing fractions //////DebugFix.Assert(timeInLocalUnitsDecimal == (double)timeInLocalUnitsIntegral); #if DEBUG if (checkRoundtrip) { long bytesRoundTrip = ConvertTimeToBytes(timeInLocalUnitsIntegral, samplingRate, frameSize, false); DebugFix.Assert(Math.Abs(bytesRoundTrip - bytesAligned) <= frameSize); } #endif return(timeInLocalUnitsIntegral); }
private int transferBytesFromWavStreamToCircularBuffer(int circularBufferBytesAvailableForWriting) { int circularBufferLength = m_CircularBuffer. #if USE_SHARPDX Capabilities #else Caps #endif .BufferBytes ; DebugFix.Assert(circularBufferBytesAvailableForWriting <= circularBufferLength); long pcmDataRemainingPlayableFromStream = m_PlaybackEndPositionInCurrentAudioStream - m_CurrentAudioStream.Position; int bytesToTransferToCircularBuffer = Math.Min(circularBufferBytesAvailableForWriting, (int)pcmDataRemainingPlayableFromStream); //bytesToTransferToCircularBuffer = Math.Min(bytesToTransferToCircularBuffer, m_CircularBufferRefreshChunkSize); bytesToTransferToCircularBuffer -= bytesToTransferToCircularBuffer % m_CurrentAudioPCMFormat.BlockAlign; if (bytesToTransferToCircularBuffer <= 0) { Debug.Fail("bytesToTransferToCircularBuffer <= 0 !!?"); return(0); } if (m_CurrentAudioStream.Position + bytesToTransferToCircularBuffer > m_PlaybackEndPositionInCurrentAudioStream) { // safeguard bytesToTransferToCircularBuffer = (int)pcmDataRemainingPlayableFromStream; } #if USE_SOUNDTOUCH if (UseSoundTouch && NotNormalPlayFactor()) { int sampleSizePerChannel = m_CurrentAudioPCMFormat.BitDepth / 8; // == m_CurrentAudioPCMFormat.BlockAlign / m_CurrentAudioPCMFormat.NumberOfChannels; #if DEBUG int sizeOfTypeInBytes = Marshal.SizeOf(typeof(TSampleType)); DebugFix.Assert(sizeOfTypeInBytes == sampleSizePerChannel); sizeOfTypeInBytes = sizeof(TSampleType); DebugFix.Assert(sizeOfTypeInBytes == sampleSizePerChannel); #endif // DEBUG if (m_SoundTouch_ByteBuffer == null) { Console.WriteLine("ALLOCATING m_SoundTouch_ByteBuffer"); m_SoundTouch_ByteBuffer = new byte[bytesToTransferToCircularBuffer]; #if !USE_SHARPDX m_SoundTouch_ByteBuffer_Stream = new MemoryStream(m_SoundTouch_ByteBuffer); #endif } else if (m_SoundTouch_ByteBuffer.Length < bytesToTransferToCircularBuffer) { Console.WriteLine("m_SoundTouch_ByteBuffer.resize"); Array.Resize(ref m_SoundTouch_ByteBuffer, bytesToTransferToCircularBuffer); //m_SoundTouch_ByteBuffer_Stream.Capacity = nbytes; //m_SoundTouch_ByteBuffer_Stream.SetLength(nbytes); #if !USE_SHARPDX m_SoundTouch_ByteBuffer_Stream = new MemoryStream(m_SoundTouch_ByteBuffer); #endif } //int bytesToReadFromAudioStream = bytesToTransferToCircularBuffer; //DebugFix.Assert(bytesToReadFromAudioStream <= bytesToTransferToCircularBuffer); int bytesReadFromAudioStream = m_CurrentAudioStream.Read(m_SoundTouch_ByteBuffer, 0, bytesToTransferToCircularBuffer); DebugFix.Assert(bytesReadFromAudioStream == bytesToTransferToCircularBuffer); int soundTouch_SampleBufferLength = (bytesReadFromAudioStream * 8) / m_CurrentAudioPCMFormat.BitDepth; // 16 if (m_SoundTouch_SampleBuffer == null) { Console.WriteLine("ALLOCATING m_SoundTouch_SampleBuffer"); m_SoundTouch_SampleBuffer = new TSampleType[soundTouch_SampleBufferLength]; } else if (m_SoundTouch_SampleBuffer.Length < soundTouch_SampleBufferLength) { Console.WriteLine("m_SoundTouch_SampleBuffer.resize"); Array.Resize(ref m_SoundTouch_SampleBuffer, soundTouch_SampleBufferLength); } int sampleBufferIndex = 0; for (int i = 0; i < bytesReadFromAudioStream;) //i += m_CurrentAudioPCMFormat.BlockAlign) { //short sampleLeft = 0; //short sampleRight = 0; for (int channel = 0; channel < m_CurrentAudioPCMFormat.NumberOfChannels; channel++) { byte byte1 = m_SoundTouch_ByteBuffer[i++]; byte byte2 = m_SoundTouch_ByteBuffer[i++]; short sample = BitConverter.IsLittleEndian ? (short)(byte1 | (byte2 << 8)) : (short)((byte1 << 8) | byte2); #if DEBUG //// Little Indian //short s1 = (short)(byte1 | (byte2 << 8)); //short s2 = (short)(byte1 + byte2 * 256); //// Big Indian //short s3 = (short)((byte1 << 8) | byte2); //short s4 = (short)(byte1 * 256 + byte2); short checkedSample = BitConverter.ToInt16(m_SoundTouch_ByteBuffer, i - 2); DebugFix.Assert(checkedSample == sample); checkedSample = (short)(byte1 + byte2 * 256); DebugFix.Assert(checkedSample == sample); #endif //DEBUG //if (channel == 0) //{ // sampleLeft = sample; //} //else //{ // sampleRight = sample; //} m_SoundTouch_SampleBuffer[sampleBufferIndex++] = sample; } //m_SoundTouch_SampleBuffer[sampleBufferIndex - 2] = sampleRight; // sampleLeft; //m_SoundTouch_SampleBuffer[sampleBufferIndex - 1] = 0; // sampleRight; } int soundTouch_SampleBufferLength_Channels = soundTouch_SampleBufferLength / m_CurrentAudioPCMFormat.NumberOfChannels; int soundTouch_SampleBufferLength_Channels_FULL = m_SoundTouch_SampleBuffer.Length / m_CurrentAudioPCMFormat.NumberOfChannels; //m_SoundTouch.Flush(); int samplesReceived = -1; //while ( // (samplesReceived = m_SoundTouch.ReceiveSamples( // (ArrayPtr<TSampleType>)m_SoundTouch_SampleBuffer, // soundTouch_SampleBufferLength_Channels_FULL // )) > 0) //{ // // Ignore. // bool debug = true; //} m_SoundTouch.PutSamples((ArrayPtr <TSampleType>)m_SoundTouch_SampleBuffer, soundTouch_SampleBufferLength_Channels); int totalBytesReceivedFromSoundTouch = 0; //totalBytesReceivedFromSoundTouch = soundTouch_SampleBufferLength * sampleSizePerChannel; while ( (samplesReceived = m_SoundTouch.ReceiveSamples( (ArrayPtr <TSampleType>)m_SoundTouch_SampleBuffer, soundTouch_SampleBufferLength_Channels_FULL )) > 0) { samplesReceived *= m_CurrentAudioPCMFormat.NumberOfChannels; int bytesReceivedFromSoundTouch = samplesReceived * sampleSizePerChannel; int predictedTotal = totalBytesReceivedFromSoundTouch + bytesReceivedFromSoundTouch; if (//predictedTotal > bytesReadFromAudioStream || predictedTotal > m_SoundTouch_ByteBuffer.Length) { #if DEBUG // The breakpoint should never hit, // because the output audio data is smaller than the source // due to the time stretching making the playback faster. // (the ratio is about the same as m_FastPlayFactor) //DebugFix.Assert(m_FastPlayFactor < 1); if (m_FastPlayFactor >= 1) { Debugger.Break(); } #endif break; } //unsafe //{ // fixed (short* pShorts = m_SoundTouch_SampleBuffer) // Marshal.Copy((IntPtr)pShorts, 0, m_SoundTouch_ByteBuffer, actualSamples); //} if (false) { // TODO: check little / big endian Buffer.BlockCopy(m_SoundTouch_SampleBuffer, 0, m_SoundTouch_ByteBuffer, totalBytesReceivedFromSoundTouch, bytesReceivedFromSoundTouch); } else { //try //{ //} //catch (Exception ex) //{ // Debugger.Break(); //} int checkTotalBytes = 0; for (int s = 0; s < samplesReceived; s += m_CurrentAudioPCMFormat.NumberOfChannels) { if (false) { int sampleSizePerChannels = sampleSizePerChannel * m_CurrentAudioPCMFormat.NumberOfChannels; checkTotalBytes += sampleSizePerChannels; // TODO: check little / big endian Buffer.BlockCopy(m_SoundTouch_SampleBuffer, s * sampleSizePerChannel, m_SoundTouch_ByteBuffer, totalBytesReceivedFromSoundTouch + s * sampleSizePerChannel, sampleSizePerChannels); } else { for (int channel = 0; channel < m_CurrentAudioPCMFormat.NumberOfChannels; channel++) { if (true) { checkTotalBytes += sampleSizePerChannel; sampleBufferIndex = (s + channel); //TSampleType sample = m_SoundTouch_SampleBuffer[sampleBufferIndex]; int byteIndex = sampleBufferIndex * sampleSizePerChannel; // TODO: check little / big endian Buffer.BlockCopy(m_SoundTouch_SampleBuffer, byteIndex, m_SoundTouch_ByteBuffer, totalBytesReceivedFromSoundTouch + byteIndex, sampleSizePerChannel); } else { TSampleType sample = m_SoundTouch_SampleBuffer[s + channel]; byte[] sampleBytes; if (BitConverter.IsLittleEndian) { sampleBytes = BitConverter.GetBytes(sample); } else { sampleBytes = BitConverter.GetBytes( (short)((sample & 0xFF) << 8 | (sample & 0xFF00) >> 8)); } DebugFix.Assert(sampleSizePerChannel == sampleBytes.Length); checkTotalBytes += sampleSizePerChannel; Buffer.BlockCopy(sampleBytes, 0, m_SoundTouch_ByteBuffer, totalBytesReceivedFromSoundTouch + (s + channel) * sampleSizePerChannel, sampleSizePerChannel); } } } } DebugFix.Assert(checkTotalBytes == bytesReceivedFromSoundTouch); } totalBytesReceivedFromSoundTouch += bytesReceivedFromSoundTouch; if (//totalBytesReceivedFromSoundTouch >= bytesReadFromAudioStream || totalBytesReceivedFromSoundTouch >= circularBufferBytesAvailableForWriting) { break; } } #if !USE_SHARPDX m_SoundTouch_ByteBuffer_Stream.Position = 0; #endif if (totalBytesReceivedFromSoundTouch > 0 && totalBytesReceivedFromSoundTouch <= circularBufferBytesAvailableForWriting) { #if DEBUG //double ratio = bytesReadFromAudioStream / (double)totalBytesReceivedFromSoundTouch; //Console.WriteLine("FAST: " + ratio + " -- " + m_FastPlayFactor); DebugFix.Assert(totalBytesReceivedFromSoundTouch <= circularBufferLength); //DebugFix.Assert(totalBytesReceivedFromSoundTouch <= bytesReadFromAudioStream); #endif // DEBUG #if USE_SHARPDX m_CircularBuffer.Write < //TSampleType byte >( //m_SoundTouch_SampleBuffer, m_SoundTouch_ByteBuffer, 0, totalBytesReceivedFromSoundTouch, m_CircularBufferWritePosition, LockFlags.None); #else m_CircularBuffer.Write(m_CircularBufferWritePosition, m_SoundTouch_ByteBuffer_Stream, totalBytesReceivedFromSoundTouch, LockFlag.None); #endif m_CircularBufferWritePosition += totalBytesReceivedFromSoundTouch; m_CircularBufferWritePosition %= circularBufferLength; //if (m_CircularBufferWritePosition >= m_CircularBuffer.Caps.BufferBytes) //{ // m_CircularBufferWritePosition -= m_CircularBuffer.Caps.BufferBytes; //} } //#if DEBUG // if ( //#if USE_SOUNDTOUCH //!UseSoundTouch || //#endif // !NotNormalPlayFactor()) // { // DebugFix.Assert(totalBytesReceivedFromSoundTouch == bytesToTransferToCircularBuffer); // } //#endif//DEBUG return(totalBytesReceivedFromSoundTouch); } else #endif //USE_SOUNDTOUCH { #if USE_SHARPDX if (SharpDX_IntermediaryTransferBuffer == null) { Console.WriteLine("ALLOCATING SharpDX_IntermediaryTransferBuffer"); SharpDX_IntermediaryTransferBuffer = new byte[bytesToTransferToCircularBuffer]; } else if (SharpDX_IntermediaryTransferBuffer.Length < bytesToTransferToCircularBuffer) { Console.WriteLine("SharpDX_IntermediaryTransferBuffer.resize"); Array.Resize(ref SharpDX_IntermediaryTransferBuffer, bytesToTransferToCircularBuffer); } int read = m_CurrentAudioStream.Read(SharpDX_IntermediaryTransferBuffer, 0, bytesToTransferToCircularBuffer); DebugFix.Assert(bytesToTransferToCircularBuffer == read); m_CircularBuffer.Write <byte>(SharpDX_IntermediaryTransferBuffer, 0, bytesToTransferToCircularBuffer, m_CircularBufferWritePosition, LockFlags.None); #else m_CircularBuffer.Write(m_CircularBufferWritePosition, m_CurrentAudioStream, bytesToTransferToCircularBuffer, LockFlag.None); #endif m_CircularBufferWritePosition += bytesToTransferToCircularBuffer; m_CircularBufferWritePosition %= circularBufferLength; //if (m_CircularBufferWritePosition >= m_CircularBuffer.Caps.BufferBytes) //{ // m_CircularBufferWritePosition -= m_CircularBuffer.Caps.BufferBytes; //} return(bytesToTransferToCircularBuffer); } //int afterWriteCursor = m_CircularBuffer.Caps.BufferBytes - m_CircularBufferWritePosition; //if (toCopy <= afterWriteCursor) //{ // m_CircularBuffer.Write(m_CircularBufferWritePosition, m_CurrentAudioStream, toCopy, LockFlag.None); //} //else //{ // m_CircularBuffer.Write(m_CircularBufferWritePosition, m_CurrentAudioStream, afterWriteCursor, LockFlag.None); // m_CircularBuffer.Write(0, m_CurrentAudioStream, toCopy - afterWriteCursor, LockFlag.None); //} }
//private long m_CircularBufferFlushTolerance; //private int m_CircularBufferPreviousBytesAvailableForWriting; private bool circularBufferRefreshThreadMethod() { //m_CircularBufferRefreshThreadIsAlive = true; //float previousFastPlayFactor = m_FastPlayFactor; //long predictedByteIncrement = -1; //int circularBufferTotalBytesPlayed = -1; //int previousCircularBufferPlayPosition = -1; Stopwatch transferBytesStopWatch = new Stopwatch(); transferBytesStopWatch.Stop(); int totalWriteSkips = 0; uint adjustedFastPlaySampleRate = (uint)Math.Round(m_CurrentAudioPCMFormat.SampleRate * m_FastPlayFactor); long pcmDataTotalPlayableFromStream = m_PlaybackEndPositionInCurrentAudioStream - m_PlaybackStartPositionInCurrentAudioStream; long pcmDataTotalPlayableFromStream_DurationMS = ( NotNormalPlayFactor() ? AudioLibPCMFormat.ConvertBytesToTime( pcmDataTotalPlayableFromStream, adjustedFastPlaySampleRate, m_CurrentAudioPCMFormat.BlockAlign #if DEBUG , true #endif ) : m_CurrentAudioPCMFormat.ConvertBytesToTime(pcmDataTotalPlayableFromStream) ) / AudioLibPCMFormat.TIME_UNIT; int slowLoop = 0; int sleepTime = REFRESH_INTERVAL_MS; //bool endOfAudioStream = false; while (true) { #if USE_SHARPDX DebugFix.Assert( m_CircularBuffer.Status == (int)BufferStatus.BufferLost || m_CircularBuffer.Status == (int)BufferStatus.Hardware || m_CircularBuffer.Status == (int)BufferStatus.Looping || m_CircularBuffer.Status == (int)BufferStatus.None || m_CircularBuffer.Status == (int)BufferStatus.Playing || m_CircularBuffer.Status == (int)BufferStatus.Software || m_CircularBuffer.Status == (int)BufferStatus.Terminated || m_CircularBuffer.Status == 5 //?! ); if (m_CircularBuffer.Status == (int)BufferStatus.BufferLost) { m_CircularBuffer.Restore(); } #else if (m_CircularBuffer.Status.BufferLost) { m_CircularBuffer.Restore(); } #endif #if USE_SHARPDX if (m_CircularBuffer.Status == (int)BufferStatus.Terminated || (m_CircularBuffer.Status != 5 && //?! m_CircularBuffer.Status != (int)BufferStatus.Playing && m_CircularBuffer.Status != (int)BufferStatus.Looping) ) { return(false); } #else if (m_CircularBuffer.Status.Terminated || (!m_CircularBuffer.Status.Playing && !m_CircularBuffer.Status.Looping) ) { return(false); } #endif Thread.Sleep(sleepTime); sleepTime = REFRESH_INTERVAL_MS; // reset after each loop // if (predictedByteIncrement < 0 // || true //m_FastPlayFactor != previousFastPlayFactor // ) // { // //previousFastPlayFactor = m_FastPlayFactor; // int fastPlaySamplesPerSecond = (int)Math.Round(m_CurrentAudioPCMFormat.SampleRate * m_FastPlayFactor); //#if DEBUG // DebugFix.Assert(m_CurrentAudioPCMFormat.SampleRate == m_CircularBuffer.Format.SamplesPerSecond); // if ( //#if USE_SOUNDTOUCH //!UseSoundTouch && //#endif //USE_SOUNDTOUCH //#if USE_SHARPDX // m_CircularBuffer.Capabilities.ControlFrequency //#else // m_CircularBuffer.Caps.ControlFrequency //#endif //) // { // DebugFix.Assert(m_CircularBuffer.Frequency == fastPlaySamplesPerSecond); // } //#endif //DEBUG // int byteRate = fastPlaySamplesPerSecond * m_CurrentAudioPCMFormat.BlockAlign; // (m_CurrentAudioPCMFormat.BitDepth / 8) * m_CurrentAudioPCMFormat.NumberOfChannels; // predictedByteIncrement = (long)(byteRate * (REFRESH_INTERVAL_MS + 15) / 1000.0); // predictedByteIncrement -= predictedByteIncrement % m_CurrentAudioPCMFormat.BlockAlign; // } #if USE_SHARPDX int circularBufferPlayPosition; int circularBufferWritePosition; m_CircularBuffer.GetCurrentPosition(out circularBufferPlayPosition, out circularBufferWritePosition); #else int circularBufferPlayPosition = m_CircularBuffer.PlayPosition; #endif int circularBufferLength = m_CircularBuffer. #if USE_SHARPDX Capabilities #else Caps #endif .BufferBytes ; //if (circularBufferTotalBytesPlayed < 0) //{ // circularBufferTotalBytesPlayed = circularBufferPlayPosition; //} //else //{ // if (circularBufferPlayPosition >= previousCircularBufferPlayPosition) // { // circularBufferTotalBytesPlayed += circularBufferPlayPosition - previousCircularBufferPlayPosition; // } // else // { // circularBufferTotalBytesPlayed += (circularBufferLength - previousCircularBufferPlayPosition) + circularBufferPlayPosition; // } //} //previousCircularBufferPlayPosition = circularBufferPlayPosition; // int totalBytesPlayed_AdjustedPlaybackRate = circularBufferTotalBytesPlayed; //#if USE_SOUNDTOUCH // if (UseSoundTouch && NotNormalPlayFactor()) // { // //m_CurrentAudioPCMFormat // totalBytesPlayed_AdjustedPlaybackRate = (int)Math.Round(totalBytesPlayed_AdjustedPlaybackRate * m_FastPlayFactor); // totalBytesPlayed_AdjustedPlaybackRate -= totalBytesPlayed_AdjustedPlaybackRate % m_CurrentAudioPCMFormat.BlockAlign; // } //#endif //USE_SOUNDTOUCH int circularBufferBytesAvailableForWriting = (circularBufferPlayPosition == m_CircularBufferWritePosition ? 0 : (circularBufferPlayPosition < m_CircularBufferWritePosition ? circularBufferPlayPosition + (circularBufferLength - m_CircularBufferWritePosition) : circularBufferPlayPosition - m_CircularBufferWritePosition)); //int circularBufferBytesAvailableForPlaying = circularBufferLength - circularBufferBytesAvailableForWriting; //realTimePlaybackPosition -= realTimePlaybackPosition % m_CurrentAudioPCMFormat.BlockAlign; //Console.WriteLine(String.Format("bytesAvailableForWriting: [{0} / {1}]", bytesAvailableForWriting, m_CircularBuffer.Caps.BufferBytes)); //Console.WriteLine("dataAvailableFromStream: " + dataAvailableFromStream); // int circularBufferBytesAvailableForPlaying_AdjustedPlaybackRate = circularBufferBytesAvailableForPlaying; //#if USE_SOUNDTOUCH // if (UseSoundTouch && NotNormalPlayFactor()) // { // circularBufferBytesAvailableForPlaying_AdjustedPlaybackRate = (int)Math.Round(circularBufferBytesAvailableForPlaying_AdjustedPlaybackRate * m_FastPlayFactor); // circularBufferBytesAvailableForPlaying_AdjustedPlaybackRate -= circularBufferBytesAvailableForPlaying_AdjustedPlaybackRate % m_CurrentAudioPCMFormat.BlockAlign; // } //#endif //USE_SOUNDTOUCH long totalPlayedMS = m_PlaybackStopWatch.ElapsedMilliseconds; long totalPlayedBytes = ( NotNormalPlayFactor() ? AudioLibPCMFormat.ConvertTimeToBytes( totalPlayedMS * AudioLibPCMFormat.TIME_UNIT, (uint)adjustedFastPlaySampleRate, (ushort)m_CurrentAudioPCMFormat.BlockAlign #if DEBUG , true #endif ) : m_CurrentAudioPCMFormat.ConvertTimeToBytes(totalPlayedMS * AudioLibPCMFormat.TIME_UNIT) ); m_CurrentBytePosition = m_PlaybackStartPositionInCurrentAudioStream + totalPlayedBytes; //safeguard if (m_CurrentBytePosition < m_PlaybackStartPositionInCurrentAudioStream) { m_CurrentBytePosition = m_PlaybackStartPositionInCurrentAudioStream; } else if (m_CurrentBytePosition > m_PlaybackEndPositionInCurrentAudioStream) { m_CurrentBytePosition = m_PlaybackEndPositionInCurrentAudioStream; } //#if DEBUG // DebugFix.Assert(m_CurrentBytePosition >= m_PlaybackStartPositionInCurrentAudioStream); // DebugFix.Assert(m_CurrentBytePosition <= m_PlaybackEndPositionInCurrentAudioStream); //#endif // DEBUG //long remainingBytesToPlay = pcmDataTotalPlayableFromStream - totalBytesPlayed_AdjustedPlaybackRate; //long realTimePlaybackPosition = Math.Max(m_PlaybackStartPositionInCurrentAudioStream, // m_CurrentAudioStream.Position - Math.Min( // circularBufferBytesAvailableForPlaying_AdjustedPlaybackRate, remainingBytesToPlay)); //realTimePlaybackPosition = Math.Min(realTimePlaybackPosition, // m_PlaybackStartPositionInCurrentAudioStream + totalBytesPlayed_AdjustedPlaybackRate); //if (m_CurrentBytePosition == m_PlaybackStartPositionInCurrentAudioStream) //{ // //Console.WriteLine(string.Format("m_CurrentBytePosition ASSIGNED: realTimePlaybackPosition [{0}]", realTimePlaybackPosition)); // m_CurrentBytePosition += totalBytesPlayed_AdjustedPlaybackRate; //} ////else if (realTimePlaybackPosition < m_CurrentBytePosition) ////{ //// Console.WriteLine(string.Format("realTimePlaybackPosition [{0}] < m_CurrentBytePosition [{1}]", realTimePlaybackPosition, m_CurrentBytePosition)); //// m_CurrentBytePosition = Math.Min(m_PlaybackEndPositionInCurrentAudioStream, m_CurrentBytePosition + predictedByteIncrement); ////} ////else if (realTimePlaybackPosition > m_CurrentBytePosition + predictedByteIncrement) ////{ //// Console.WriteLine(string.Format("realTimePlaybackPosition [{0}] > m_CurrentBytePosition [{1}] + m_PredictedByteIncrement: [{2}] (diff: [{3}])", //// realTimePlaybackPosition, m_CurrentBytePosition, predictedByteIncrement, realTimePlaybackPosition - m_CurrentBytePosition)); //// m_CurrentBytePosition = Math.Min(m_PlaybackEndPositionInCurrentAudioStream, m_CurrentBytePosition + predictedByteIncrement); ////} //else //{ // //Console.WriteLine(string.Format("m_CurrentBytePosition OK: realTimePlaybackPosition [{0}]", realTimePlaybackPosition)); // m_CurrentBytePosition = m_PlaybackStartPositionInCurrentAudioStream + totalBytesPlayed_AdjustedPlaybackRate; //} PcmDataBufferAvailableHandler del = PcmDataBufferAvailable; if (del != null) { int min = Math.Min(m_PcmDataBufferLength, #if FETCH_PCM_FROM_CIRCULAR_BUFFER circularBufferBytesAvailableForPlaying #else (int)(m_PlaybackEndPositionInCurrentAudioStream - m_CurrentBytePosition) #endif // FETCH_PCM_FROM_CIRCULAR_BUFFER ); #if DEBUG DebugFix.Assert(min <= m_PcmDataBufferLength); DebugFix.Assert(min <= m_PcmDataBuffer.Length); #endif //DEBUG if (min >= m_CurrentAudioPCMFormat.BlockAlign) { #if FETCH_PCM_FROM_CIRCULAR_BUFFER #if USE_SHARPDX if (SharpDX_IntermediaryTransferBuffer != null) { Array.Copy(SharpDX_IntermediaryTransferBuffer, m_PcmDataBuffer, Math.Min(m_PcmDataBuffer.Length, SharpDX_IntermediaryTransferBuffer.Length)); } #else byte[] array = (byte[])m_CircularBuffer.Read( circularBufferPlayPosition, typeof(byte), LockFlag.None, min); //Array.Copy(array, m_PcmDataBuffer, min); Buffer.BlockCopy(array, 0, m_PcmDataBuffer, 0, min); #endif #else // !FETCH_PCM_FROM_CIRCULAR_BUFFER long pos = m_CurrentAudioStream.Position; m_CurrentAudioStream.Position = m_CurrentBytePosition; m_CurrentAudioStream.Read(m_PcmDataBuffer, 0, min); m_CurrentAudioStream.Position = pos; #endif m_PcmDataBufferAvailableEventArgs.PcmDataBuffer = m_PcmDataBuffer; m_PcmDataBufferAvailableEventArgs.PcmDataBufferLength = min; del(this, m_PcmDataBufferAvailableEventArgs); } } //var del_ = PcmDataBufferAvailable; //if (del_ != null //&& m_PcmDataBuffer.Length <= circularBufferBytesAvailableForPlaying) //{ //#if USE_SHARPDX //if (SharpDX_IntermediaryTransferBuffer != null) //{ //Array.Copy(SharpDX_IntermediaryTransferBuffer, m_PcmDataBuffer, Math.Min(m_PcmDataBuffer.Length, SharpDX_IntermediaryTransferBuffer.Length)); //m_PcmDataBufferAvailableEventArgs.PcmDataBuffer = m_PcmDataBuffer; //PcmDataBufferAvailable(this, m_PcmDataBufferAvailableEventArgs); //} //#else //Array array = m_CircularBuffer.Read(circularBufferPlayPosition, typeof(byte), LockFlag.None, m_PcmDataBuffer.Length); //Array.Copy(array, m_PcmDataBuffer, m_PcmDataBuffer.Length); //m_PcmDataBufferAvailableEventArgs.PcmDataBuffer = m_PcmDataBuffer; //del_(this, m_PcmDataBufferAvailableEventArgs); //#endif //} long pcmDataRemainingPlayableFromStream = m_PlaybackEndPositionInCurrentAudioStream - m_CurrentAudioStream.Position; //long pcmDataAlreadyReadFromStream = pcmDataTotalPlayableFromStream - pcmDataAvailableFromStream; if (circularBufferBytesAvailableForWriting <= 0) { if (pcmDataRemainingPlayableFromStream > 0) { //Console.WriteLine("circularBufferBytesAvailableForWriting <= 0, pcmDataAvailableFromStream > 0 ... continue..."); continue; } else { Console.WriteLine("circularBufferBytesAvailableForWriting <= 0, pcmDataAvailableFromStream <= 0 ... BREAK..."); break; } } //m_CircularBufferPreviousBytesAvailableForWriting = circularBufferBytesAvailableForWriting; // We have fed all of the available bytes from the audio stream to the circular secondary buffer. // Now we have to wait until the playback ends. if (pcmDataRemainingPlayableFromStream <= 0) { if ((m_PlaybackStopWatch.ElapsedMilliseconds + REFRESH_INTERVAL_MS) >= pcmDataTotalPlayableFromStream_DurationMS) { m_CircularBuffer.Stop(); return(true); } else { int newInterval = (int)Math.Round(REFRESH_INTERVAL_MS / 2.0); sleepTime = newInterval; continue; } //if (remainingBytesToPlay > predictedByteIncrement) //{ // Console.WriteLine(string.Format("remainingBytesToPlay [{0}] [{1}] [{2}] [{3}]", // pcmDataTotalPlayableFromStream, totalBytesPlayed_AdjustedPlaybackRate, remainingBytesToPlay, predictedByteIncrement)); // continue; //} //else //{ // m_CircularBuffer.Stop(); // //Console.WriteLine("Time to break, all bytes gone."); // //break; // return true; //} //if (m_CircularBufferFlushTolerance < 0) //{ // m_CircularBufferFlushTolerance = m_CurrentAudioPCMFormat.ConvertTimeToBytes(REFRESH_INTERVAL_MS*1.5); //} //Console.WriteLine(string.Format("pcmDataTotalPlayableFromStream [{0}]", pcmDataTotalPlayableFromStream)); //Console.WriteLine(String.Format("pcmDataAvailableFromStream <= 0 // circularBufferBytesAvailableForWriting [{0}], m_CircularBufferFlushTolerance [{1}], m_CircularBuffer.Caps.BufferBytes [{2}], m_CircularBufferPreviousBytesAvailableForWriting [{3}]", // circularBufferBytesAvailableForWriting, m_CircularBufferFlushTolerance, m_CircularBuffer.Caps.BufferBytes, m_CircularBufferPreviousBytesAvailableForWriting)); //if ((circularBufferBytesAvailableForWriting + m_CircularBufferFlushTolerance) >= m_CircularBuffer.Caps.BufferBytes // || m_CircularBufferPreviousBytesAvailableForWriting > circularBufferBytesAvailableForWriting) //{ // m_CircularBuffer.Stop(); // the earlier the better ? // Console.WriteLine("Forcing closing-up."); // circularBufferBytesAvailableForWriting = 0; // will enter the IF test below //} } else { // 2 thirds minimum of the circular buffer must be available, //otherwise skip until next sleep loop double ratio = 2 / 3.0; #if USE_SOUNDTOUCH if (UseSoundTouch && NotNormalPlayFactor()) { ratio /= m_FastPlayFactor; } #endif // USE_SOUNDTOUCH bool skip = circularBufferBytesAvailableForWriting < (int)Math.Round(circularBufferLength * ratio); if (false && skip) { totalWriteSkips++; } else { // int circularBufferBytesAvailableForWriting_AdjustedPlaybackRate = // circularBufferBytesAvailableForWriting; //#if USE_SOUNDTOUCH // if (UseSoundTouch && NotNormalPlayFactor()) // { // int bytesPerSample = (int)Math.Round(m_CurrentAudioPCMFormat.BitDepth / 8.0); // int bytesPerFrame = bytesPerSample * m_CurrentAudioPCMFormat.NumberOfChannels; // DebugFix.Assert(m_CurrentAudioPCMFormat.BlockAlign == bytesPerFrame); // circularBufferBytesAvailableForWriting_AdjustedPlaybackRate = // (int) // Math.Round(circularBufferBytesAvailableForWriting_AdjustedPlaybackRate // * m_FastPlayFactor // * m_CurrentAudioPCMFormat.NumberOfChannels); // circularBufferBytesAvailableForWriting_AdjustedPlaybackRate -= // circularBufferBytesAvailableForWriting_AdjustedPlaybackRate % // m_CurrentAudioPCMFormat.BlockAlign; // } //#endif //USE_SOUNDTOUCH //Console.WriteLine("totalWriteSkips: " + totalWriteSkips + " ms: " + totalWriteSkips*REFRESH_INTERVAL_MS); totalWriteSkips = 0; #if NET4 transferBytesStopWatch.Restart(); #else transferBytesStopWatch.Stop(); transferBytesStopWatch.Reset(); transferBytesStopWatch.Start(); #endif //NET4 int bytesWrittenToCirularBuffer = transferBytesFromWavStreamToCircularBuffer(circularBufferBytesAvailableForWriting); long timeMS = transferBytesStopWatch.ElapsedMilliseconds; transferBytesStopWatch.Stop(); //Console.WriteLine("transferBytesStopWatch: " + timeMS); sleepTime = Math.Max(10, REFRESH_INTERVAL_MS - (int)timeMS); #if USE_SOUNDTOUCH if (UseSoundTouch && NotNormalPlayFactor()) { if (timeMS >= REFRESH_INTERVAL_MS) { slowLoop++; } if (slowLoop > 2) { slowLoop = 0; Console.WriteLine("SOUNDTOUCH Enable SettingId.UseQuickseek"); m_SoundTouch.SetSetting(SettingId.UseQuickseek, 1); } } #endif //USE_SOUNDTOUCH //#if USE_SOUNDTOUCH // if (UseSoundTouch && NotNormalPlayFactor()) // { // int newInterval = (int)Math.Round(REFRESH_INTERVAL_MS / m_FastPlayFactor); // //sleepTime = newInterval; // } //#endif // USE_SOUNDTOUCH } } } // WHILE LOOP return(true); //CurrentState = State.Stopped; //AudioPlaybackFinishHandler delFinished = AudioPlaybackFinished; //if (delFinished != null && !mPreviewTimer.Enabled) // delFinished(this, new AudioPlaybackFinishEventArgs()); //if (!m_AllowBackToBackPlayback) //{ // AudioPlaybackFinishHandler delFinished = AudioPlaybackFinished; // if (delFinished != null && mEventsEnabled) // delFinished(this, new AudioPlaybackFinishEventArgs()); //} //else //{ // m_FinishedPlayingCurrentStream = true; //} }
//private int m_circularBufferCapturePosition = -1; #endif private int circularBufferTransferData( #if !FORCE_SINGLE_NOTIFICATION_EVENT int eventIndex, bool catchingUp, #endif bool lastFlush ) { #if !FORCE_SINGLE_NOTIFICATION_EVENT #if SIMULATE_BUFFER_NOTIFICATION_SKIP Console.WriteLine(eventIndex); #endif if (!catchingUp) { #if SIMULATE_BUFFER_NOTIFICATION_SKIP // NOTIFICATIONS = 16, each REFRESH_INTERVAL_MS = 75 bool beginning = eventIndex == 0 || eventIndex == 1 || eventIndex == 2; bool end = eventIndex == 13 || eventIndex == 14 || eventIndex == 15; bool middle = eventIndex == 6 || eventIndex == 7 || eventIndex == 8; bool middleTwo = eventIndex == 3 || eventIndex == 4 || eventIndex == 5 || eventIndex == 9 || eventIndex == 10 || eventIndex == 11; //if (beginning) //if (end) //if (beginning || end) //if (beginning || middle) //if (middle || end) if (beginning || middle || end) //if (middle) //if (middleTwo) { Console.WriteLine("SKIPPED."); return(0); } #endif if ((m_previousEventIndex == -1 || m_previousEventIndex >= 0) && eventIndex >= 0) { int nextEventIndex = m_previousEventIndex + 1; if (nextEventIndex >= NOTIFICATIONS) { nextEventIndex = 0; } int toCatchUp = 0; if (eventIndex == nextEventIndex) { // OK } else if (eventIndex > nextEventIndex) { // ahead, but not looped back in circular buffer toCatchUp = eventIndex - nextEventIndex; } else if (eventIndex < nextEventIndex) { // ahead, but looped back in circular buffer toCatchUp = (NOTIFICATIONS - nextEventIndex) + eventIndex; } if (toCatchUp > 0) { #if SIMULATE_BUFFER_NOTIFICATION_SKIP Console.WriteLine("TO CATCHUP: {0}", toCatchUp); #endif for (int i = 0; i < toCatchUp; i++) { #if SIMULATE_BUFFER_NOTIFICATION_SKIP Console.WriteLine("Audio recording event catch-up {0}, {1} => {2}", eventIndex, m_previousEventIndex, nextEventIndex); #endif circularBufferTransferData(nextEventIndex, true, lastFlush); nextEventIndex++; if (nextEventIndex >= NOTIFICATIONS) { nextEventIndex = 0; } } //DebugFix.Assert(m_previousEventIndex == (eventIndex-1)); } } } m_previousEventIndex = eventIndex; #endif #if USE_SHARPDX int circularBufferCapturePosition = m_CircularBuffer.CurrentCapturePosition; //int readPosition = m_CircularBuffer.CurrentRealPosition; #else int circularBufferCapturePosition; int readPosition; // UNUSED m_CircularBuffer.GetCurrentPosition(out circularBufferCapturePosition, out readPosition); #endif //#if !FORCE_SINGLE_NOTIFICATION_EVENT // if (catchingUp) // { // circularBufferCapturePosition = m_circularBufferCapturePosition; // } // else // { // m_circularBufferCapturePosition = circularBufferCapturePosition; // } //#endif int circularBufferBytes = m_CircularBuffer. #if USE_SHARPDX Capabilities #else Caps #endif .BufferBytes ; int notifyChunk = circularBufferBytes / NOTIFICATIONS; int circularBufferBytesAvailableForReading = (circularBufferCapturePosition == m_CircularBufferReadPositon ? 0 : (circularBufferCapturePosition < m_CircularBufferReadPositon ? circularBufferCapturePosition + (circularBufferBytes - m_CircularBufferReadPositon) : circularBufferCapturePosition - m_CircularBufferReadPositon)); if (lastFlush) { #if !FORCE_SINGLE_NOTIFICATION_EVENT DebugFix.Assert(eventIndex == -1); #endif //circularBufferBytesAvailableForReading -= (circularBufferBytesAvailableForReading % notifyChunk); #if DEBUG Console.WriteLine("m_CircularBufferReadPositon: " + m_CircularBufferReadPositon); Console.WriteLine("circularBufferBytesAvailableForReading; " + circularBufferBytesAvailableForReading); #endif } else { #if !FORCE_SINGLE_NOTIFICATION_EVENT DebugFix.Assert(eventIndex >= 0); circularBufferBytesAvailableForReading = notifyChunk; #endif } if (circularBufferBytesAvailableForReading <= 0) { //Console.WriteLine(string.Format("circularBufferTransferData: no more bytes to fetch {0}", circularBufferBytesAvailableForReading)); return(circularBufferBytesAvailableForReading); } DebugFix.Assert(circularBufferBytesAvailableForReading <= circularBufferBytes); if (circularBufferBytesAvailableForReading > circularBufferBytes) { circularBufferBytesAvailableForReading = circularBufferBytes; } int circularBufferBytesAvailableForCapturing = circularBufferBytes - circularBufferBytesAvailableForReading; //int toRead = readPosition - m_CircularBufferReadPositon; //if (toRead < 0) // toRead += m_CircularBuffer.Caps.BufferBytes; //toRead -= (toRead % (m_CircularBuffer.Caps.BufferBytes / NOTIFICATIONS)); //if (toRead <= 0) //{ // Console.WriteLine(string.Format("BAD toRead {0}", toRead)); // continue; //} #if !FORCE_SINGLE_NOTIFICATION_EVENT if (eventIndex >= 0) { DebugFix.Assert(circularBufferBytesAvailableForReading == notifyChunk); int pos = eventIndex * notifyChunk; //DebugFix.Assert(pos == m_CircularBufferReadPositon); if (m_CircularBufferReadPositon != pos) { #if DEBUG Console.WriteLine("READ POS ADJUST: " + m_CircularBufferReadPositon + " != " + pos + " // " + notifyChunk + " -- " + (pos - m_CircularBufferReadPositon)); #endif m_CircularBufferReadPositon = pos; } } #endif #if USE_SHARPDX if (incomingPcmData == null) { Console.WriteLine("ALLOCATING incomingPcmData"); incomingPcmData = new byte[circularBufferBytesAvailableForReading]; } else if (incomingPcmData.Length < circularBufferBytesAvailableForReading) { Console.WriteLine("incomingPcmData.resize"); Array.Resize(ref incomingPcmData, circularBufferBytesAvailableForReading); } DebugFix.Assert(circularBufferBytesAvailableForReading <= incomingPcmData.Length); m_CircularBuffer.Read(incomingPcmData, 0, circularBufferBytesAvailableForReading, m_CircularBufferReadPositon, LockFlags.None); #else // !USE_SHARPDX byte[] incomingPcmData = (byte[])m_CircularBuffer.Read(m_CircularBufferReadPositon, typeof(byte), LockFlag.None, circularBufferBytesAvailableForReading); DebugFix.Assert(circularBufferBytesAvailableForReading == incomingPcmData.Length); #endif #if FORCE_SINGLE_NOTIFICATION_EVENT if (!lastFlush && m_DO_LOG_CircularBufferNotificationTimerMessage) { String msg = "DO_LOG_CircularBufferNotificationTimerMessage:: circularBufferBytes @ m_CircularBufferReadPositon # circularBufferCapturePosition - circularBufferBytesAvailableForReading / notifyChunk $ incomingPcmData.Length: " + circularBufferBytes + " @ " + m_CircularBufferReadPositon + " # " + circularBufferCapturePosition + " - " + circularBufferBytesAvailableForReading + " / " + notifyChunk + " $ " + (incomingPcmData == null ? "NULL" : ("" + incomingPcmData.Length)); #if DEBUG Console.WriteLine(msg); #endif CircularBufferNotificationTimerMessageHandler del_ = CircularBufferNotificationTimerMessage; if (del_ != null) { del_(this, new CircularBufferNotificationTimerMessageEventArgs(msg)); } } #endif //if (m_CircularBuffer != null && m_CircularBuffer.Capturing) //{ // int capturePosition; // int readPosition; // m_CircularBuffer.GetCurrentPosition(out capturePosition, out readPosition); // return // RecordingPCMFormat.ConvertBytesToTime(m_TotalRecordedBytes + capturePosition - // m_CircularBufferReadPositon); //} int length = circularBufferBytesAvailableForReading; DebugFix.Assert(length <= incomingPcmData.Length); if (length > incomingPcmData.Length) { length = incomingPcmData.Length; } if (m_TotalRecordedBytes >= (ulong.MaxValue - (ulong)length)) { #if DEBUG Debugger.Break(); #endif //DEBUG // Oh oh! :( if (CurrentState == State.Monitoring) { m_TotalRecordedBytes = 0; #if FORCE_SINGLE_NOTIFICATION_EVENT m_PreviousTotalRecordedBytes = 0; #endif } } else { if (CurrentState == State.Recording) { if (m_RecordingFileWriter == null) { string parentDir = Path.GetDirectoryName(m_RecordedFilePath); if (!Directory.Exists(parentDir)) { Directory.CreateDirectory(parentDir); //FileDataProvider.CreateDirectory(parentDir); } //FileInfo fi = new FileInfo(m_RecordedFilePath); //fi.FullName m_RecordingFileWriter = new BinaryWriter(File.OpenWrite(m_RecordedFilePath)); } m_RecordingFileWriter.BaseStream.Position = (long)m_TotalRecordedBytes + (long)m_RecordedFileRiffHeaderSize; // m_RecordingFileWriter.BaseStream.Length; m_RecordingFileWriter.Write(incomingPcmData, 0, length); } m_TotalRecordedBytes += (ulong)length; } m_CircularBufferReadPositon += length; m_CircularBufferReadPositon %= circularBufferBytes; PcmDataBufferAvailableHandler del = PcmDataBufferAvailable; if (del != null) { //if (m_PcmDataBuffer.Length != incomingPcmData.Length) //{ // //Console.WriteLine(string.Format(">>>>> Resizing buffer: m_PcmDataBuffer = {0}, incomingPcmData = {1}",m_PcmDataBuffer.Length, incomingPcmData.Length)); // Array.Resize(ref m_PcmDataBuffer, incomingPcmData.Length); //} //Array.Copy(incomingPcmData, m_PcmDataBuffer, m_PcmDataBuffer.Length); int min = Math.Min(m_PcmDataBufferLength, length); //Array.Copy(incomingPcmData, m_PcmDataBuffer, min); Buffer.BlockCopy(incomingPcmData, 0, m_PcmDataBuffer, 0, min); m_PcmDataBufferAvailableEventArgs.PcmDataBuffer = m_PcmDataBuffer; m_PcmDataBufferAvailableEventArgs.PcmDataBufferLength = min; del(this, m_PcmDataBufferAvailableEventArgs); } return(length); }
private void startPlayback(long startPosition, long endPosition) { initializeBuffers(); m_PlaybackStartPositionInCurrentAudioStream = startPosition; m_PlaybackEndPositionInCurrentAudioStream = endPosition == 0 ? m_CurrentAudioDataLength : endPosition; m_CircularBufferWritePosition = 0; //m_CircularBufferFlushTolerance = -1; //m_PredictedByteIncrement = -1; //m_PreviousCircularBufferPlayPosition = -1; //m_CircularBufferTotalBytesPlayed = -1; m_CurrentAudioStream = m_CurrentAudioStreamProvider(); m_CurrentAudioStream.Position = m_PlaybackStartPositionInCurrentAudioStream; int circularBufferLength = m_CircularBuffer. #if USE_SHARPDX Capabilities #else Caps #endif .BufferBytes ; int bytesWrittenToCirularBuffer = transferBytesFromWavStreamToCircularBuffer(circularBufferLength); m_CurrentBytePosition = m_PlaybackStartPositionInCurrentAudioStream; CurrentState = State.Playing; //if (AllowBackToBackPlayback && m_MonitoringTimer != null) m_MonitoringTimer.Start(); try { m_CircularBuffer.Play(0, #if USE_SHARPDX PlayFlags.Looping #else BufferPlayFlags.Looping // this makes it a circular buffer (which we manage manually, by tracking playback versus writing positions) #endif ); if (m_PlaybackStopWatch == null) { m_PlaybackStopWatch = new Stopwatch(); } #if NET4 m_PlaybackStopWatch.Restart(); #else m_PlaybackStopWatch.Stop(); m_PlaybackStopWatch.Reset(); m_PlaybackStopWatch.Start(); #endif //NET4 } catch (Exception) { Debug.Fail("EmergencyStopForSoundBufferProblem !"); CurrentState = State.Stopped; StopForwardRewind(); stopPlayback(); return; } ThreadStart threadDelegate = delegate() { bool endOfAudioStream = false; try { endOfAudioStream = circularBufferRefreshThreadMethod(); } catch (ThreadAbortException ex) { // } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); } finally { if (m_PlaybackStopWatch != null) { m_PlaybackStopWatch.Stop(); } if (mPreviewTimer.Enabled) { if (endOfAudioStream || CurrentState == State.Playing) { m_ResumeStartPosition = CurrentBytePosition; CurrentState = State.Paused; // before stopPlayback(), doesn't kill the stream provider } lock (LOCK) { m_CircularBufferRefreshThread = null; } stopPlayback(); } else { if (endOfAudioStream || CurrentState == State.Playing) { CurrentState = State.Stopped; } //if (CurrentState != State.Paused) CurrentState = State.Stopped; lock (LOCK) { m_CircularBufferRefreshThread = null; } StopForwardRewind(); stopPlayback(); if (endOfAudioStream) { AudioPlaybackFinishHandler delFinished = AudioPlaybackFinished; if (delFinished != null && !mPreviewTimer.Enabled) { delFinished(this, new AudioPlaybackFinishEventArgs()); } } } } //Console.WriteLine("Player refresh thread exiting...."); //CurrentState = State.Stopped; //lock (LOCK) //{ // //m_CircularBufferRefreshThreadIsAlive = false; // m_CircularBufferRefreshThread = null; //} //Console.WriteLine("Player refresh thread exit."); }; int count = 0; while (m_CircularBufferRefreshThread != null) { Console.WriteLine(@"------------ m_CircularBufferRefreshThread NOT null!!: " + count++); Thread.Sleep(20); if (count > 10) { Console.WriteLine(@"------------ m_CircularBufferRefreshThread NOT null!! ()BREAK(): " + count++); break; } } if (m_CircularBufferRefreshThread != null) { stopPlayback(); } DebugFix.Assert(m_CircularBufferRefreshThread == null); lock (LOCK) { m_CircularBufferRefreshThread = new Thread(threadDelegate); m_CircularBufferRefreshThread.Name = "Player Refresh Thread"; m_CircularBufferRefreshThread.Priority = ThreadPriority.Normal; m_CircularBufferRefreshThread.IsBackground = true; m_CircularBufferRefreshThread.Start(); } //Console.WriteLine("Player refresh thread start."); }
public void PlayBytes(StreamProviderDelegate currentAudioStreamProvider, long dataLength, AudioLibPCMFormat pcmInfo, long bytesFrom, long bytesTo) { if (pcmInfo == null) { throw new ArgumentNullException("PCM format cannot be null !"); } if (currentAudioStreamProvider == null) { throw new ArgumentNullException("Stream cannot be null !"); } if (dataLength <= 0) { throw new ArgumentOutOfRangeException("Duration cannot be <= 0 !"); } if (CurrentState == State.NotReady) { return; } if (CurrentState != State.Stopped) { Debug.Fail("Attempting to play when not stopped ? " + CurrentState); return; } #if USE_SOUNDTOUCH if (false && pcmInfo.NumberOfChannels > 1) { m_UseSoundTouch = false; //TODO: stereo all scrambled with SoundTouch !! } else { m_UseSoundTouch = m_UseSoundTouchBackup; } #endif // USE_SOUNDTOUCH m_CurrentAudioStreamProvider = currentAudioStreamProvider; m_CurrentAudioStream = m_CurrentAudioStreamProvider(); m_CurrentAudioPCMFormat = pcmInfo; m_CurrentAudioDataLength = dataLength; long startPosition = 0; if (bytesFrom > 0) { startPosition = m_CurrentAudioPCMFormat.AdjustByteToBlockAlignFrameSize(bytesFrom); } long endPosition = 0; if (bytesTo > 0) { endPosition = m_CurrentAudioPCMFormat.AdjustByteToBlockAlignFrameSize(bytesTo); } if (m_CurrentAudioPCMFormat.BytesAreEqualWithMillisecondsTolerance(startPosition, 0)) { startPosition = 0; } if (m_CurrentAudioPCMFormat.BytesAreEqualWithMillisecondsTolerance(endPosition, dataLength)) { endPosition = dataLength; } if (m_CurrentAudioPCMFormat.BytesAreEqualWithMillisecondsTolerance(endPosition, 0)) { endPosition = 0; } if (endPosition != 0 && m_CurrentAudioPCMFormat.BytesAreEqualWithMillisecondsTolerance(endPosition, startPosition)) { return; } if (startPosition >= 0 && (endPosition == 0 || startPosition < endPosition) && endPosition <= dataLength) { if (m_FwdRwdRate == 0) { startPlayback(startPosition, endPosition); Console.WriteLine("starting playback "); } else if (m_FwdRwdRate > 0) { FastForward(startPosition); Console.WriteLine("fast forward "); } else if (m_FwdRwdRate < 0) { if (startPosition == 0) { startPosition = m_CurrentAudioStream.Length; } Rewind(startPosition); Console.WriteLine("Rewind "); } } else { //throw new Exception("Start/end positions out of bounds of audio asset."); DebugFix.Assert(false); } }
/// <summary> /// Parses a RIFF WAVE PCM header of a given input <see cref="Stream"/> /// </summary> /// <remarks> /// Upon succesful parsing the <paramref name="input"/> <see cref="Stream"/> is positioned at the beginning of the actual PCM data, /// that is at the beginning of the data field of the data sub-chunk /// </remarks> /// <param name="input">The input <see cref="Stream"/> - must be positioned at the start of the RIFF chunk</param> /// <returns>A <see cref="AudioLibPCMFormat"/> containing the parsed data</returns> /// <exception cref="ArgumentOutOfRangeException"> /// Thrown when RIFF WAVE header is invalid or is not PCM data /// </exception> public static AudioLibPCMFormat RiffHeaderParse(Stream input, out uint dataLength) { DebugFix.Assert(input.Position == 0); dataLength = 0; BinaryReader rd = new BinaryReader(input); //http://www.sonicspot.com/guide/wavefiles.html // Ensures 3x4=12 bytes available to read (RIFF Type Chunk) { long availableBytes = input.Length - input.Position; if (availableBytes < 12) { throw new ArgumentOutOfRangeException( "The RIFF chunk descriptor does not fit in the input stream"); } } //Chunk ID (4 bytes) { string chunkId = Encoding.ASCII.GetString(rd.ReadBytes(4)); if (chunkId != "RIFF") { throw new ArgumentOutOfRangeException("ChunkId is not RIFF: " + chunkId); } } //Chunk Data Size (the wavEndPos variable is used further below as the upper limit position in the stream) long wavEndPos = 0; { // 4 bytes uint chunkSize = rd.ReadUInt32(); // Ensures the given size fits within the actual stream wavEndPos = input.Position + chunkSize; DebugFix.Assert(!(wavEndPos > input.Length)); //if (wavEndPos > input.Length) //{ // throw new ArgumentOutOfRangeException(String.Format( // "The WAVE PCM chunk does not fit in the input Stream (expected chunk end position is {0:0}, Stream count is {1:0})", // wavEndPos, input.Length)); //} } //RIFF Type (4 bytes) { string format = Encoding.ASCII.GetString(rd.ReadBytes(4)); if (format != "WAVE") { throw new ArgumentOutOfRangeException(String.Format( "RIFF format {0} is not supported. The only supported RIFF format is WAVE", format)); } } // We need at least the 'data' and the 'fmt ' chunks bool foundWavDataChunk = false; bool foundWavFormatChunk = false; // We memorize the position of the actual PCM data in the stream, // so we can seek back, if needed (normally, this never happens as the 'data' chunk // is always the last one. However the WAV format does not mandate the order of chunks so...) long wavDataChunkPosition = -1; AudioLibPCMFormat pcmInfo = new AudioLibPCMFormat(); //loop when there's at least 2x4=8 bytes to read (Chunk ID & Chunk Data Size) while (input.Position + 8 <= wavEndPos) { // 4 bytes string chunkId = Encoding.ASCII.GetString(rd.ReadBytes(4)); // 4 bytes uint chunkSize = rd.ReadUInt32(); // Ensures the given size fits within the actual stream if (input.Position + chunkSize > wavEndPos) { throw new ArgumentOutOfRangeException(String.Format( "ChunkId {0} does not fit in RIFF chunk", chunkId)); } switch (chunkId) { case "fmt ": { // The default information fields fit within 16 bytes int extraFormatBytes = (int)chunkSize - 16; // Compression code (2 bytes) ushort compressionCode = rd.ReadUInt16(); // Number of channels (2 bytes) ushort numChannels = rd.ReadUInt16(); if (numChannels == 0) { throw new ArgumentOutOfRangeException("0 channels of audio is not supported"); } // Sample rate (4 bytes) uint sampleRate = rd.ReadUInt32(); // Average bytes per second, aka byte-rate (4 bytes) uint byteRate = rd.ReadUInt32(); // Block align (2 bytes) ushort blockAlign = rd.ReadUInt16(); // Significant bits per sample, aka bit-depth (2 bytes) ushort bitDepth = rd.ReadUInt16(); if (compressionCode != 0 && extraFormatBytes > 0) { // Extra format bytes (2 bytes) uint extraBytes = rd.ReadUInt16(); if (extraBytes > 0) { DebugFix.Assert(extraBytes <= (extraFormatBytes - 2)); } if (extraFormatBytes > 2) { extraBytes = (uint)(extraFormatBytes - 2); // Skip (we ignore the extra information in this chunk field) rd.ReadBytes((int)extraBytes); // check word-alignment if ((extraBytes % 2) != 0) { rd.ReadByte(); } } } if ((bitDepth % 8) != 0) { throw new ArgumentOutOfRangeException(String.Format( "Invalid number of bits per sample {0:0} - must be a mulitple of 8", bitDepth)); } if (blockAlign != (numChannels * bitDepth / 8)) { throw new ArgumentOutOfRangeException(String.Format( "Invalid block align {0:0} - expected {1:0}", blockAlign, numChannels * bitDepth / 8)); } if (byteRate != sampleRate * blockAlign) { throw new ArgumentOutOfRangeException(String.Format( "Invalid byte rate {0:0} - expected {1:0}", byteRate, sampleRate * blockAlign)); } pcmInfo.BitDepth = bitDepth; pcmInfo.NumberOfChannels = numChannels; pcmInfo.SampleRate = sampleRate; pcmInfo.IsCompressed = compressionCode != 1; foundWavFormatChunk = true; break; } case "data": { if (input.Position + chunkSize > wavEndPos) { throw new ArgumentOutOfRangeException(String.Format( "ChunkId {0} does not fit in RIFF chunk", "data")); } dataLength = chunkSize; foundWavDataChunk = true; wavDataChunkPosition = input.Position; // ensure we go past the PCM data, in case there are following chunks. // (it's an unlikely scenario, but it's allowed by the WAV spec.) input.Seek(chunkSize, SeekOrigin.Current); break; } case "fact": { if (chunkSize == 4) { // 4 bytes uint totalNumberOfSamples = rd.ReadUInt32(); // This value is unused, we are just reading it for debugging as we noticed that // the WAV files generated by the Microsoft Audio Recorder // contain the 'fact' chunk with this information. Most other recordings // only contain the 'data' and 'fmt ' chunks. } else { rd.ReadBytes((int)chunkSize); } break; } case "JUNK": case "bext": case "minf": case "regn": case "umid": case "DGDA": case "wavl": case "slnt": case "cue ": case "plst": case "list": case "labl": case "note": case "ltxt": case "smpl": case "inst": default: { // Unsupported FOURCC codes, we skip. rd.ReadBytes((int)chunkSize); break; } } } if (!foundWavDataChunk) { throw new ArgumentOutOfRangeException("WAV 'data' chunk was not found !"); } if (!foundWavFormatChunk) { throw new ArgumentOutOfRangeException("WAV 'fmt ' chunk was not found !"); } if (input.Position != wavDataChunkPosition) { input.Seek(wavDataChunkPosition, SeekOrigin.Begin); } return(pcmInfo); }