public void Dispose() { try { WinMM.ErrorCheck(WinMM.waveInReset(WaveHandle)); // free buffers foreach (RecordBuffer buffer in Buffers) { buffer.Dispose(); } Buffers = null; // free speex Speex.speex_bits_destroy(ref EncodeBits); Speex.speex_encoder_destroy(SpeexEncoder); Speex.speex_preprocess_state_destroy(PreProcessor); //Speex.speex_echo_state_destroy(EchoState); WinMM.ErrorCheck(WinMM.waveInClose(WaveHandle)); } catch (Exception ex) { Voices.Core.Network.UpdateLog("Voice", "Error Disposing: " + ex.Message); } Voices.Recorder = null; }
public PlayAudio(VoiceService voices, int frameSize, RemoteVoice user) { Voices = voices; User = user; CallbackHandler = new WinMM.WaveDelegate(WaveCallback); FrameSize = frameSize; int sampleRate = 0; // if 20ms, at high quality (16khz) is 320 samples at 2 bytes each if (FrameSize == 320) { sampleRate = 16000; BufferSize = 320 * 2 * 2; // 2 bytes each frame, 2 channels SpeexMode = Speex.SPEEX_MODEID_WB; } else if (FrameSize == 160) { sampleRate = 8000; BufferSize = 160 * 2 * 2; SpeexMode = Speex.SPEEX_MODEID_NB; } else { Dispose(); return; } try { // init speex Speex.speex_bits_init(ref DecodeBits); IntPtr modePtr = Speex.speex_lib_get_mode(SpeexMode); SpeexDecoder = Speex.speex_decoder_init(modePtr); int tmp = 1; Speex.speex_decoder_ctl(SpeexDecoder, Speex.SPEEX_SET_ENH, ref tmp); // init wave Format = new WinMM.WaveFormat(sampleRate, 16, 2); WinMM.ErrorCheck(WinMM.waveOutOpen(out WaveHandle, Voices.PlaybackDevice, Format, CallbackHandler, 0, WinMM.CALLBACK_FUNCTION)); for (int i = 0; i < BufferCount; i++) { Buffers[i] = new PlayBuffer(i, WaveHandle, BufferSize); } } catch (Exception ex) { Dispose(); Voices.Core.Network.UpdateLog("Voice", "Error starting playing: " + ex.Message); } }
private void InitSpeexEncoder() { // init Speex.speex_bits_init(ref EncodeBits); // get narrow band mode IntPtr modePtr = Speex.speex_lib_get_mode(SpeexMode); SpeexEncoder = Speex.speex_encoder_init(modePtr); //int zeroNoError = 0; int tmp = 0; // no variable bit rate Speex.speex_encoder_ctl(SpeexEncoder, Speex.SPEEX_SET_VBR, ref tmp); tmp = 4; // ok quality Speex.speex_encoder_ctl(SpeexEncoder, Speex.SPEEX_SET_QUALITY, ref tmp); tmp = 1; // uses a little more cpu for better processing Speex.speex_encoder_ctl(SpeexEncoder, Speex.SPEEX_SET_COMPLEXITY, ref tmp); tmp = 1; // voice activated, deadspace is not encoded Speex.speex_encoder_ctl(SpeexEncoder, Speex.SPEEX_SET_VAD, ref tmp); tmp = 1; // dead space no transmission Speex.speex_encoder_ctl(SpeexEncoder, Speex.SPEEX_SET_DTX, ref tmp); Speex.speex_encoder_ctl(SpeexEncoder, Speex.SPEEX_GET_FRAME_SIZE, ref FrameSize); Debug.Assert(FrameSize == BufferSize / 2); if (FrameSize != BufferSize / 2) { throw new Exception("Frame size " + FrameSize + " did agree with buffer " + BufferSize); } /* Turn this off if you want to measure SNR (on by default) */ tmp = 1; Speex.speex_encoder_ctl(SpeexEncoder, Speex.SPEEX_SET_HIGHPASS, ref tmp); // pre-processor PreProcessor = Speex.speex_preprocess_state_init(FrameSize, SampleRate); tmp = 1; Speex.speex_preprocess_ctl(PreProcessor, Speex.SPEEX_PREPROCESS_SET_DENOISE, ref tmp); // echo cancelation //EchoState = Speex.speex_echo_state_init(FrameSize, FrameSize * 3); // 100ms tail length suggested //Speex.speex_preprocess_ctl(PreProcessor, Speex.SPEEX_PREPROCESS_SET_ECHO_STATE, EchoState); EncodedBytes = new byte[BufferSize]; }
//byte[] LastBuffer; //bool LastBufferSet; //byte[] EchoBuff; private bool EncodeAudio(RecordBuffer buffer) { // done in a seperate function to avoid buffer from being re-assigned while delegate is being processed short maxVolume = 0; for (int i = 0; i < BufferSize / 2; i++) { short val = BitConverter.ToInt16(buffer.Data, i * 2); if (val > maxVolume) { maxVolume = val; } } // pre-process Speex.speex_preprocess_run(PreProcessor, buffer.Data); // echo cancel //if (EchoBuff == null) EchoBuff = new byte[BufferSize]; //Speex.speex_echo_capture(EchoState, buffer.Data, EchoBuff); //EchoBuff.CopyTo(buffer.Data, 0); // encode Speex.speex_bits_reset(ref EncodeBits); int success = Speex.speex_encode_int(SpeexEncoder, buffer.DataPtr, ref EncodeBits); if (success == 0) // dtx returns 0 if no data { return(false); } int written = Speex.speex_bits_write(ref EncodeBits, EncodedBytes, EncodedBytes.Length); // filler is 10b high quality, 6b low quality, dont write filler only good audio if (written > 10) { //buffer.Data.CopyTo(LastBuffer, 0); //LastBufferSet = true; byte[] safeBuffer = Utilities.ExtractBytes(EncodedBytes, 0, written); // pass frame size because recorder could be null by the time event gets there Voices.Core.RunInCoreAsync(() => Voices.Recorder_AudioData(safeBuffer, maxVolume, FrameSize)); return(true); } return(false); }
public void Dispose() { try { WinMM.ErrorCheck(WinMM.waveOutReset(WaveHandle)); // free buffers foreach (PlayBuffer buffer in Buffers) { buffer.Dispose(); } Buffers = null; // free speex Speex.speex_bits_destroy(ref DecodeBits); Speex.speex_decoder_destroy(SpeexDecoder); WinMM.ErrorCheck(WinMM.waveOutClose(WaveHandle)); } catch (Exception ex) { Voices.Core.Network.UpdateLog("Voice", "Error Disposing Player: " + ex.Message); } // remove wave out stream from user structure foreach (ulong routing in User.Streams.Keys) { if (User.Streams[routing] == this) { User.Streams.Remove(routing); break; } } Voices.Players.SafeRemove(this); }
public void ProcessBuffers() { try { while (FilledBuffers < BufferCount) { byte[] data = null; lock (AudioQueue) // either gets called from core, or system thread { if (AudioQueue.Count == 0) { return; } data = AudioQueue.Dequeue(); } // keep a log of received audio that user can back track through lock (History) { History.Enqueue(data); while (History.Count > HistoryLength) { History.Dequeue(); } } // decode Speex.speex_bits_reset(ref DecodeBits); Speex.speex_bits_read_from(ref DecodeBits, data, data.Length); byte[] mono = new byte[FrameSize * 2]; int success = Speex.speex_decode_int(SpeexDecoder, ref DecodeBits, mono); if (success != 0) { continue; } // cancel echo //if (Voices.Recorder != null && FrameSize == Voices.Recorder.FrameSize) // Speex.speex_echo_playback(Voices.Recorder.EchoState, mono); // get volume short maxVolume = 0; for (int i = 0; i < mono.Length / 2; i++) { short val = BitConverter.ToInt16(mono, i * 2); if (val > maxVolume) { maxVolume = val; } } if (maxVolume > User.VolumeIn) { User.VolumeIn = maxVolume; } // find out where audio should come out from, if at all // return down here so that even if user not listening, window shows volume bar AudioDirection direction = User.GetDirection(); if (direction == AudioDirection.None) { continue; } // shifting to one side PlayBuffer buffer = Buffers[NextBuffer]; for (int i = 0; i < mono.Length / 2; i++) { switch (direction) { case AudioDirection.Both: Buffer.BlockCopy(mono, i * 2, buffer.Data, i * 4, 2); // left Buffer.BlockCopy(mono, i * 2, buffer.Data, i * 4 + 2, 2); // right break; case AudioDirection.Left: Buffer.BlockCopy(mono, i * 2, buffer.Data, i * 4, 2); // left break; case AudioDirection.Right: Buffer.BlockCopy(mono, i * 2, buffer.Data, i * 4 + 2, 2); // right break; } } WinMM.ErrorCheck(WinMM.waveOutWrite(WaveHandle, ref buffer.Header, Marshal.SizeOf(buffer.Header))); FilledBuffers++; NextBuffer++; if (NextBuffer >= BufferCount) { NextBuffer = 0; } } } catch (Exception ex) { Voices.Core.RunInCoreAsync(() => { Dispose(); Voices.Core.Network.UpdateLog("Voice", "Error filling buffers: " + ex.Message); }); } }