private void PullAudioFrameThread()
        {
            var avsync_type    = 0;
            var bytesPerSample = 2;
            var type           = AUDIO_FRAME_TYPE.FRAME_TYPE_PCM16;
            var channels       = CHANNEL;
            var samples        = SAMPLE_RATE / PULL_FREQ_PER_SEC * CHANNEL;
            var samplesPerSec  = SAMPLE_RATE;
            var buffer         = Marshal.AllocHGlobal(samples * bytesPerSample);
            var freq           = 1000 / PULL_FREQ_PER_SEC;

            var tic = new TimeSpan(DateTime.Now.Ticks);

            while (_pullAudioFrameThreadSignal)
            {
                var toc = new TimeSpan(DateTime.Now.Ticks);
                if (toc.Subtract(tic).Duration().Milliseconds >= freq)
                {
                    tic = new TimeSpan(DateTime.Now.Ticks);
                    _audioRawDataManager.PullAudioFrame(buffer, (int)type, samples, bytesPerSample, channels,
                                                        samplesPerSec, 0, avsync_type);

                    var byteArray = new byte[samples * bytesPerSample];
                    Marshal.Copy(buffer, byteArray, 0, samples * bytesPerSample);

                    var floatArray = ConvertByteToFloat16(byteArray);
                    lock (audioBuffer)
                    {
                        audioBuffer.Put(floatArray);
                    }

                    writeCount += floatArray.Length;
                    count      += 1;
                }

                if (count == 100)
                {
                    _startSignal = true;
                }
            }

            Marshal.FreeHGlobal(buffer);
        }
        unsafe void PullAudioFrameThread()
        {
            int sampleRate = _sampleRate;
            int channels   = _channelCount;

            var frameType      = AUDIO_FRAME_TYPE.FRAME_TYPE_PCM16;
            var bytesPerSample = 2; // PCM16

            var samplesPerChannel = (int)(sampleRate * _frameDurationMs / 1000.0f);

            var frameBufferPointer = Marshal.AllocHGlobal(samplesPerChannel * channels * bytesPerSample);
            var pcmBuffer          = new float[samplesPerChannel * channels];

            _ringBuffer = new RingBuffer <float>(samplesPerChannel * channels * 4); // 4[frames]

            var tic = new TimeSpan(DateTime.Now.Ticks);

            while (_pullAudioFrame)
            {
                var toc = new TimeSpan(DateTime.Now.Ticks);
                if (toc.Subtract(tic).Duration().Milliseconds >= _frameDurationMs)
                {
                    tic = new TimeSpan(DateTime.Now.Ticks);
                    _audioRawDataManager.PullAudioFrame(frameBufferPointer, (int)frameType, samplesPerChannel, bytesPerSample, channels, sampleRate, 0, 0);

                    var span = new ReadOnlySpan <Byte>((void *)frameBufferPointer, samplesPerChannel * channels * bytesPerSample);

                    // Convert 16bit PCM data bytes to 32bit float PCM data.
                    for (var i = 0; i < pcmBuffer.Length; i++)
                    {
                        pcmBuffer[i] = ConvertBytesToInt16(span.Slice(2 * i)) / 32768f; // The maximum absolute value of Int16 is 32768.
                    }

                    lock (_ringBuffer)
                    {
                        _ringBuffer.Enqueue(pcmBuffer);
                    }
                }
            }

            Marshal.FreeHGlobal(frameBufferPointer);
        }