// Add more buffers to the playback queue - keep it full private void RefillPlayBuffers() { while (m_qBuffers.Count < MaxBuffers) { int cb = (int)(BufferLen * m_format.SamplesPerSec * m_format.Channels); byte[] data = new byte[cb]; for (int i = 0; i < m_streamArray.Count; i++) { if (m_streamArray[i] != null) { cb = ((Stream)m_streamArray[i]).Read(data, 0, cb); break; } } if (cb == 0) { break; } WaveHeader hdr = new WaveHeader(data); Monitor.Enter(m_HandleMap.SyncRoot); m_HandleMap.Add(hdr.Pointer.ToInt32(), hdr); Monitor.Exit(m_HandleMap.SyncRoot); // prepare the header CheckWaveError(NativeMethods.waveOutPrepareHeader(m_hWaveOut, hdr.Pointer, hdr.HeaderLength)); Monitor.Enter(m_qBuffers); m_qBuffers.Enqueue(hdr); Monitor.Exit(m_qBuffers); } }
/// <summary> /// Creates a recording buffer /// </summary> /// <param name="dwBufferSize"></param> /// <returns>new buffer as WaveHeader</returns> private WaveHeader GetNewRecordBuffer(int dwBufferSize) { WaveHeader hdr = new WaveHeader(dwBufferSize); Monitor.Enter(m_HandleMap.SyncRoot); m_HandleMap.Add(hdr.Pointer.ToInt32(), hdr); Monitor.Exit(m_HandleMap.SyncRoot); // prepare the header CheckWaveError(NativeMethods.waveInPrepareHeader(m_hWaveIn, hdr.Pointer, hdr.HeaderLength)); return(hdr); }
private void m_recmw_WaveDoneMessage(object sender, IntPtr wParam, IntPtr lParam) { Debug.WriteLine("received WaveDone"); #if !NDOC // Retrieve Waveheader object by the lpHeader pointer Monitor.Enter(m_HandleMap.SyncRoot); WaveHeader hdr = m_HandleMap[lParam.ToInt32()] as WaveHeader; m_HandleMap.Remove(hdr.Pointer.ToInt32()); Monitor.Exit(m_HandleMap.SyncRoot); // unprepare the header CheckWaveError(NativeMethods.waveInUnprepareHeader(m_hWaveIn, hdr.Pointer, hdr.HeaderLength)); // hdr.RetrieveHeader(); m_qBuffers.Enqueue(hdr); if (recordingFinished) // last chunk { DumpRecordBuffers(); CheckWaveError(NativeMethods.waveInClose(m_hWaveIn)); m_streamRecord.Flush(); m_streamRecord.Close(); mw_WaveCloseMessage(this); // clean up the messageWindow m_recmw.Dispose(); // reset the global flag recording = false; // set our event if (DoneRecording != null) { DoneRecording(); } foreach (WaveHeader whdr in m_HandleMap.Values) { whdr.Dispose(); } m_HandleMap.Clear(); } else { hdr = GetNewRecordBuffer(m_recBufferSize); CheckWaveError(NativeMethods.waveInAddBuffer(m_hWaveIn, hdr.Pointer, hdr.HeaderLength)); DumpRecordBuffers(); } if (PositionChanged != null) { PositionChanged(this, EventArgs.Empty); } #endif }
private void DumpRecordBuffers() { while (m_qBuffers.Count > 0) { Monitor.Enter(m_qBuffers); WaveHeader hdr = (WaveHeader)m_qBuffers.Dequeue(); Monitor.Exit(m_qBuffers); try { m_streamRecord.Write(hdr.BufferData, 0, hdr.BytesRecorded); // m_streamRecord.Write(hdr.GetData(), 0, hdr.BytesRecorded); } catch (Exception ex) { Debug.Write("Exception in stream.Write: " + ex.ToString()); } hdr.Dispose(); } }
private void BufferWriteThreadProc(object header) { WaveHeader hdr = header as WaveHeader; Interlocked.Increment(ref m_workers); Debug.WriteLine(string.Format("Queued Worker Thread on header {0} - Flags == {1} [{2} running]", hdr.Pointer.ToString(), hdr.Flags.ToString(), m_workers)); int ret = NativeMethods.waveOutWrite(m_hWaveOut, hdr.Pointer, hdr.HeaderLength); while ((hdr.Flags & WHDR_FLAGS.DONE) == 0) { Thread.Sleep(1); } Interlocked.Decrement(ref m_workers); Debug.WriteLine(string.Format("Worker Thread on header {0} Completed [{1} remain]", hdr.Pointer.ToString(), m_workers)); CheckWaveError(NativeMethods.waveOutUnprepareHeader(m_hWaveOut, hdr.Pointer, hdr.HeaderLength)); hdr.Dispose(); }
/// <summary> /// Record sound data for specified number of seconds using given wave format /// The stream will be a properly formatted RIFF file /// </summary> /// <param name="st">Stream into which recorded samples are written</param> /// <param name="Seconds">Seconds of data to record</param> /// <param name="format">Sound format to record in.</param> public RiffStream RecordFor(Stream st, short Seconds, WaveFormat2 format) { // only allow 1 recording session at a time if (recording) { throw new InvalidOperationException("Already recording"); } m_hWaveIn = IntPtr.Zero; // set our global flag recording = true; if (m_qBuffers == null) { m_qBuffers = new Queue <WaveHeader>(MaxBuffers); } if (m_HandleMap == null) { m_HandleMap = new System.Collections.Hashtable(MaxBuffers); } m_recformat = new WaveFormat2(); #if !NDOC // create the callback message window m_recmw = new SoundMessageWindow(); m_recmw.WaveDoneMessage += new WaveDoneHandler(m_recmw_WaveDoneMessage); m_recmw.WaveCloseMessage += new WaveCloseHandler(mw_WaveCloseMessage); m_recmw.WaveOpenMessage += new WaveOpenHandler(mw_WaveOpenMessage); #endif bool append = st.Length >= 42; if (format.FormatTag == FormatTag.PCM) { m_recformat = format; if (append) { m_streamRecord = RiffStream.Append(st); } else { m_streamRecord = RiffStream.OpenWrite(st, m_recformat); } } else { m_recformat = WaveFormat2.GetPCMWaveFormat(SoundFormats.Mono8bit11kHz); if (append) { m_streamRecord = ACMStream.Append(st, m_recformat); } else { m_streamRecord = ACMStream.OpenWrite(st, m_recformat, format); } } #if !NDOC // check for support of selected format CheckWaveError(NativeMethods.waveInOpen(out m_hWaveIn, WAVE_MAPPER, m_recformat.GetBytes(), IntPtr.Zero, 0, WAVE_FORMAT_QUERY)); // open wave device CheckWaveError(NativeMethods.waveInOpen(out m_hWaveIn, (uint)m_deviceID, m_recformat.GetBytes(), m_recmw.Hwnd, 0, CALLBACK_WINDOW)); m_recBufferSize = (int)(Math.Min((int)Seconds, BufferLen) * m_recformat.AvgBytesPerSec); for (int i = 0; i < 2; i++) { WaveHeader hdr = GetNewRecordBuffer(m_recBufferSize); // send the buffer to the device CheckWaveError(NativeMethods.waveInAddBuffer(m_hWaveIn, hdr.Pointer, hdr.HeaderLength)); } // begin recording CheckWaveError(NativeMethods.waveInStart(m_hWaveIn)); recordingFinished = false; m_recTimer = new Timer(new TimerCallback(RecTimerCallback), this, Seconds * 1000, Timeout.Infinite); #endif return(m_streamRecord); }
private void mw_WaveDoneMessage(object sender, IntPtr wParam, IntPtr lParam) { Monitor.Enter(m_syncRoot); try { if (m_hWaveOut == IntPtr.Zero) { return; } // Are there any pending buffers? if (m_qBuffers.Count == 0) { try { // No more buffers - wait for all workers to end while (m_workers > 0) { Thread.Sleep(10); } // Close the device CheckWaveError(NativeMethods.waveOutClose(m_hWaveOut)); m_playing = false; m_qBuffers = null; m_hWaveOut = IntPtr.Zero; // Notify clients if (DonePlaying != null) { // On a separate thread in case the app calls Play in the Done handler. ThreadPool.QueueUserWorkItem(RaiseDonePlaying, new Message { LParam = lParam, WParam = wParam }); } } catch (Exception e) { if (!e.Message.Equals("Sound still playing")) { throw e; } } } else { //Get next buffer (already prepared) Monitor.Enter(m_qBuffers); WaveHeader hdr = m_qBuffers.Dequeue() as WaveHeader; Monitor.Exit(m_qBuffers); // play the file ThreadPool.QueueUserWorkItem(BufferWriteThreadProc, hdr); // Add more buffers RefillPlayBuffers(); } if (PositionChanged != null) { PositionChanged(this, EventArgs.Empty); } } finally { Monitor.Exit(m_syncRoot); } }
/// <summary> /// Plays waveform contained in the given stream. Stream is exepcted to contain full riff header /// </summary> /// <param name="playStream">Stream with the waveform</param> public void Play(Stream playStream) { Monitor.Enter(m_syncRoot); try { if (m_playing) { return; } if (playStream == null) { throw new Exception("No valid WAV file has been opened"); } #if !NDOC if (m_qBuffers == null) { m_qBuffers = new Queue <WaveHeader>(MaxBuffers); } if (m_HandleMap == null) { m_HandleMap = new System.Collections.Hashtable(MaxBuffers); } // create a window to catch waveOutxxx messages SoundMessageWindow mw = new SoundMessageWindow(); // wire in events mw.WaveOpenMessage += new WaveOpenHandler(mw_WaveOpenMessage); mw.WaveCloseMessage += new WaveCloseHandler(mw_WaveCloseMessage); mw.WaveDoneMessage += new WaveDoneHandler(mw_WaveDoneMessage); // add it to the global array m_mwArray.Add(mw); int i = m_mwArray.Count - 1; if (playStream is ACMStream) { m_streamArray.Add(playStream as ACMStream); } else if (playStream is RiffStream) { m_streamArray.Add(playStream as RiffStream); } else { m_streamArray.Add(ACMStream.OpenRead(playStream)); } m_format = (m_streamArray[m_streamArray.Count - 1] as RiffStream).Format; // open the waveOut device and register the callback CheckWaveError(NativeMethods.waveOutOpen(out m_hWaveOut, m_deviceID, m_format.GetBytes(), m_mwArray[i].Hwnd, 0, CALLBACK_WINDOW)); // see if we need to adjust playback rate if ((PlaybackRate < 0.9f) || (PlaybackRate > 1.1f)) { SetPlaybackRate(PlaybackRate); } RefillPlayBuffers(); while (m_qBuffers.Count > 0) { Monitor.Enter(m_qBuffers); WaveHeader hdr = m_qBuffers.Dequeue(); Monitor.Exit(m_qBuffers); // play the file ThreadPool.QueueUserWorkItem(BufferWriteThreadProc, hdr); m_playing = true; if (m_qBuffers.Count <= 1) { RefillPlayBuffers(); } } #endif } finally { Monitor.Exit(m_syncRoot); } }