SafeDispatch/SafeMobileLIB_DLL/NSpeexVoiceClass.cs
2024-02-22 18:43:59 +02:00

220 lines
7.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using NAudio.Wave;
using NSpeex;
namespace SafeMobileLib
{
public class NSpeexVoiceClass
{
private IWaveIn waveIn;
private IWavePlayer waveOut;
private int INdeviceID, OUTdeviceID;
private System.Threading.Timer tCheck;
private bool waveOutisPlaying = false;
public NSpeexVoiceClass(Mode m, int Q, int INdeviceID, int OUTdeviceID)
{
//Console.WriteLine("Voice started with NSpeex");
this.INdeviceID = INdeviceID;
this.OUTdeviceID = OUTdeviceID;
encoder = new NSpeex.Encoder(m);
encoder.Quality = Q;
encoder.VBR = true;
preProcessor = new PreProcessor(encoder) { AGC = true, AGCIncrement = 3, Denoise = true, VAD = true };
//Console.WriteLine(encoder.FrameSize*50);
waveFormat = new NAudio.Wave.WaveFormat(encoder.FrameSize * 50, 16, 1);
waveIn = new WaveIn(WaveCallbackInfo.FunctionCallback()) { WaveFormat = waveFormat,BufferMilliseconds= 20, NumberOfBuffers = 2,DeviceNumber = INdeviceID };
waveIn.DataAvailable += waveIn_DataAvailable;
waveIn.RecordingStopped += waveIn_RecordingStopped;
//waveProvider = new BufferedWaveProvider();
waveProvider = new JitterBufferWaveProvider(m);
waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()) {DeviceNumber = OUTdeviceID };
waveProvider.VolumeUpdated += new EventHandler<VolumeUpdatedEventArgs>(waveProvider_StreamVolume);
waveOut.Init(waveProvider);
//waveOut.Init(new WaveMixerStream32());
waveOut.Volume = 1.0f;
}
void waveProvider_StreamVolume(object sender, VolumeUpdatedEventArgs e)
{
if (OnStreamVolume != null)
{
OnStreamVolume(e.Volume);
}
}
public void Start()
{
try
{
waveIn.StartRecording();
IsRecording = true;
tCheck = new System.Threading.Timer(Check, null, new TimeSpan(0, 0, 0, 0, 0), new TimeSpan(0, 0, 0, 0, 200));
waveOut.Play();
}
catch (Exception ex)
{
SM.Debug("Unable to start audio device");
Console.WriteLine(ex.ToString());
}
}
public void Stop()
{
try
{
waveIn.StopRecording();
tCheck.Dispose();
if (waveOut != null)
{
//waveOut.Stop();
//waveOut = null;
//waveOut.Dispose();
}
}
catch (Exception ex)
{
Console.WriteLine("exception in NSpeexVoiceClass ->stop");
Console.WriteLine(ex.ToString());
}
}
public delegate void newData4Send(byte[] data, int dataLen);
public event newData4Send OnNewDataRecv;
public void DataArrived(byte[] data)
{
BinaryReader reader = new BinaryReader(new MemoryStream(data));
//while (reader.PeekChar() != -1)
while(reader.BaseStream.Length-reader.BaseStream.Position>4)
{
int frameLength = reader.ReadInt32();
//Console.WriteLine("frame len" + frameLength);
byte[] frame = reader.ReadBytes(frameLength);
waveProvider.Write(frame, 0, frameLength);
}
}
private void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
int samplesPerFrame = encoder.FrameSize;
byte[] buffer = e.Buffer;
short[] frame = new short[samplesPerFrame];
byte[] encodedFrame = new byte[e.BytesRecorded];
int encodedBytesSample = 0;
byte[] upstreamEncodedSample = new byte[e.BytesRecorded];
int frameIndex = 0;
for (int index = 0; index < e.BytesRecorded; index += 2, frameIndex = ++frameIndex % samplesPerFrame)
{
frame[frameIndex] = BitConverter.ToInt16(buffer, index);
if (frameIndex != samplesPerFrame - 1) continue;
if (preProcessor.Process(frame))
{
int encodedBytes = encoder.Encode(frame, encodedFrame);
if (encodedBytes != 0)
{
Array.Copy(BitConverter.GetBytes(encodedBytes), 0, upstreamEncodedSample, encodedBytesSample,
sizeof(int));
encodedBytesSample += 4;
Array.Copy(encodedFrame, 0, upstreamEncodedSample, encodedBytesSample, encodedBytes);
encodedBytesSample += encodedBytes;
}
}
}
if (frameIndex != 0)
{
while (frameIndex < samplesPerFrame)
{
// fill the last frame with 0s
frame[frameIndex++] = 0;
}
int encodedBytes = encoder.Encode(frame, encodedFrame);
if (encodedBytes != 0)
{
Array.Copy(BitConverter.GetBytes(encodedBytes), 0, upstreamEncodedSample, encodedBytesSample,
sizeof(int));
encodedBytesSample += 4;
Array.Copy(encodedFrame, 0, upstreamEncodedSample, encodedBytesSample, encodedBytes);
encodedBytesSample += encodedBytes;
}
}
if (encodedBytesSample != 0)
{
var upstreamFrame = new byte[encodedBytesSample];
Array.Copy(upstreamEncodedSample, upstreamFrame, encodedBytesSample);
//SM.Debug("Publishing: " + encodedBytesSample + " bytes");
if (OnNewDataRecv != null)
{
OnNewDataRecv(upstreamFrame, encodedBytesSample);
}
}
else
{
//Console.WriteLine("DTX");
}
}
private bool isRecording;
public bool IsRecording
{
get { return isRecording; }
set
{
if (value != isRecording)
{
isRecording = value;
}
}
}
private void waveIn_RecordingStopped(object sender, EventArgs e)
{
IsRecording = false;
}
private void Check(Object state)
{
// This method is executed by a thread pool thread
//Console.WriteLine("len=" + waveProvider.Length);
if (waveProvider.Length == 0)
{
if (waveOutisPlaying)
{
Console.WriteLine("done playing");
waveOutisPlaying = false;
if (OnFilePlayingComplet != null)
{
OnFilePlayingComplet();
}
}
}
else
{
//Console.WriteLine("starting to play");
waveOutisPlaying = true;
}
}
public delegate void FilePlayingComplet();
public event FilePlayingComplet OnFilePlayingComplet;
public delegate void StreamVolume(int volume);
public event StreamVolume OnStreamVolume;
}
}