Esempio n. 1
0
        public AudioPlayer(bool keepStreamAlive)
        {
#if DEBUG
            DebugFix.Assert(BitConverter.IsLittleEndian);
#endif // DEBUG

            m_KeepStreamAlive = keepStreamAlive;

            CurrentState = State.NotReady;

            initPreviewTimer();
        }
Esempio n. 2
0
        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);
        }
Esempio n. 3
0
        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);
        }
Esempio n. 4
0
        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);
            //}
        }
Esempio n. 5
0
        //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);
        }
Esempio n. 7
0
        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.");
        }
Esempio n. 8
0
        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);
            }
        }
Esempio n. 9
0
        /// <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);
        }