/* Copyright (C) 2014 DaikonForge */ namespace DaikonForge.VoIP { using System.Collections.Generic; public class SpeexCodec : IAudioCodec { public class FrequencyProvider : IFrequencyProvider { public int GetFrequency( FrequencyMode mode ) { switch( mode ) { case FrequencyMode.Narrow: return 8000; case FrequencyMode.Wide: return 16000; case FrequencyMode.UltraWide: return 32000; default: return 16000; } } } private class codecWrapper { public NSpeex.SpeexEncoder encoder; public NSpeex.SpeexDecoder decoder; public codecWrapper( NSpeex.BandMode mode, bool vbr,int _quality ) { encoder = new NSpeex.SpeexEncoder( mode ); decoder = new NSpeex.SpeexDecoder( mode, false ); encoder.VBR = vbr; encoder.Quality = _quality; } } private Dictionary encoders; private Dictionary frameSizes = new Dictionary() { { 8000, 160 }, { 16000, 320 }, { 32000, 640 }, }; private ChunkBuffer chunkBuffer; private BigArray tempOutputArray; private VoicePacketWrapper tempPacketWrapper; public SpeexCodec(bool VBR, int _quality) { encoders = new Dictionary() { { 8000, new codecWrapper( NSpeex.BandMode.Narrow, VBR,_quality ) }, { 16000, new codecWrapper( NSpeex.BandMode.Wide, VBR ,_quality) }, { 32000, new codecWrapper( NSpeex.BandMode.UltraWide, VBR,_quality ) }, }; chunkBuffer = new ChunkBuffer(); tempOutputArray = new BigArray( 1024, 0 ); tempPacketWrapper = new VoicePacketWrapper( 0, 16, new byte[ 0 ] ); } public void OnAudioAvailable( BigArray rawPCM ) { chunkBuffer.AddSamples( rawPCM ); } public VoicePacketWrapper? GetNextEncodedFrame( int frequency ) { int frameSize = frameSizes[ frequency ]; codecWrapper codec = encoders[ frequency ]; float[] chunk = TempArray.Obtain( frameSize ); bool chunkAvailable = chunkBuffer.RetrieveChunk( chunk ); if( !chunkAvailable ) { TempArray.Release( chunk ); return null; } tempPacketWrapper = new VoicePacketWrapper(); tempPacketWrapper.Frequency = (byte)( frequency / 1000 ); short[] audio16bit = TempArray.Obtain( frameSize ); for( int i = 0; i < frameSize; i++ ) { float val = chunk[ i ] * short.MaxValue; audio16bit[ i ] = (short)val; } TempArray.Release( chunk ); byte[] buffer = TempArray.Obtain( audio16bit.Length * 2 ); int encoded = codec.encoder.Encode( audio16bit, 0, frameSize, buffer, 0, buffer.Length ); TempArray.Release( audio16bit ); tempPacketWrapper.RawData = new byte[ encoded ]; System.Buffer.BlockCopy( buffer, 0, tempPacketWrapper.RawData, 0, encoded ); TempArray.Release( buffer ); return tempPacketWrapper; } public BigArray DecodeFrame( VoicePacketWrapper data ) { int frameSize = frameSizes[ data.Frequency * 1000 ]; codecWrapper codec = encoders[ data.Frequency * 1000 ]; short[] decodedFrame16Bit = TempArray.Obtain( frameSize * 4 ); int decoded = codec.decoder.Decode( data.RawData, 0, data.RawData.Length, decodedFrame16Bit, 0, false ); if( tempOutputArray.Length != decoded ) tempOutputArray.Resize( decoded ); for( int i = 0; i < decoded; i++ ) { float val = (float)decodedFrame16Bit[ i ]; val /= short.MaxValue; tempOutputArray[ i ] = val; } TempArray.Release( decodedFrame16Bit ); return tempOutputArray; } public BigArray GenerateMissingFrame( int frequency ) { int frameSize = frameSizes[ frequency * 1000 ]; codecWrapper codec = encoders[ frequency * 1000 ]; short[] decodedFrame16Bit = TempArray.Obtain( frameSize * 4 ); int decoded = codec.decoder.Decode( null, 0, 0, decodedFrame16Bit, 0, true ); if( tempOutputArray.Length != decoded ) tempOutputArray.Resize( decoded ); for( int i = 0; i < decoded; i++ ) { float val = (float)decodedFrame16Bit[ i ]; val /= short.MaxValue; tempOutputArray[ i ] = val; } TempArray.Release( decodedFrame16Bit ); return tempOutputArray; } } }