This project has moved. For the latest updates, please go here.

Looping audio to Skype using WasapiLoopbackCapture and Skype4com gives Whitenoise

Feb 9, 2015 at 4:44 PM
Edited Feb 9, 2015 at 4:45 PM
Hello! I read the article posted on Coding4Fun where someone created a Skype-voicechanger and had an idea to loop my audio through skype, since I and my friends always have to put our microphones near headphones to make them hear funny songs etc that we share.. Anyway..

I thought that the best method would be to use the WasapiLoopbackCapture and write the buffer to a networkstream (like the article sort of did), but when I'm in a call and call start recording and change the output-device to a port. All my friends can hear is really loud whitenoise which doesn't seem to have any correlation to the audio I'm hearing in my headphones (ie the noise doesn't stop when I mute my volume etc).

What is it that I'm doing wrong here? Is there some kind of conversion in bitrates I have to do? I'm really new to audio and DSP so I'm not sure what to do.

Here is the class I created. I call loopSystemAudio to start recording and initializing the network sockets.

class Interceptor
{
    #region fields
    public TCallStatus currentCallStatus = TCallStatus.clsUnknown;
    public delegate void OnStatusChanged(object sender, TCallStatus oldStatus, TCallStatus newStatus);
    public event OnStatusChanged OnCallStatusChanged;

    public NAudio.Wave.WasapiLoopbackCapture capture;
    #endregion

    private const int micInPort = 667;
    private const int micOutPort = 668;
    private const int Protocol = 8;
    private Skype skype;
    private Call currentCall;


    NAudio.CoreAudioApi.MMDevice device;

    private NetworkStream outputDeviceStream;
    TcpServer outputDeviceServer;

    #region Public Methods
    public void loopSystemAudio()
    {


        outputDeviceServer = new TcpServer(micOutPort);
        outputDeviceServer.Connect += outputDeviceServer_Connect;
        outputDeviceServer.Disconnect += outputDeviceServer_Disconnect;
        outputDeviceServer.DataReceived += outputDeviceServer_DataReceived;

        capture.StartRecording();

        currentCall.InputDevice[TCallIoDeviceType.callIoDeviceTypePort] = micOutPort.ToString();
        currentCall.set_InputDevice(TCallIoDeviceType.callIoDeviceTypePort, micOutPort.ToString());
    }

    public void loopSkypeAudio(bool includeUserMic)
    {

    }

    #endregion


    #region Constructors
    public Interceptor()
    {
        skype = new Skype();
        skype.Attach(Protocol, false);
        _ISkypeEvents_Event events = (_ISkypeEvents_Event)skype;
        events.AttachmentStatus += events_AttachmentStatus;
        skype.CallStatus += skype_CallStatus;


        device = NAudio.Wave.WasapiLoopbackCapture.GetDefaultLoopbackCaptureDevice();
        capture = new WasapiLoopbackCapture(device);
        capture.ShareMode = NAudio.CoreAudioApi.AudioClientShareMode.Shared;
        capture.DataAvailable += capture_DataAvailable;
        capture.RecordingStopped += capture_RecordingStopped;
    }
    #endregion

    #region Private Methods
    void capture_RecordingStopped(object sender, StoppedEventArgs e)
    {
        Debug.WriteLine("Recording Stopped");
    }

    void capture_DataAvailable(object sender, WaveInEventArgs e)
    {
        outputDeviceStream.Write(e.Buffer, 0, e.BytesRecorded);
    }

    void outputDeviceServer_DataReceived(object sender, DataReceivedEventArgs e)
    {
        Debug.WriteLine(e.Buffer.Length.ToString());
    }

    void outputDeviceServer_Disconnect(object sender, EventArgs e)
    {
        Debug.WriteLine("OutputDeviceServer Disconnected");
    }

    void outputDeviceServer_Connect(object sender, ConnectedEventArgs e)
    {
        Debug.WriteLine("OutputDeviceServer Connected");
        outputDeviceStream = e.Stream;


    }
    void skype_CallStatus(Call pCall, TCallStatus Status)
    {
        currentCall = pCall;
    }

    void events_AttachmentStatus(TAttachmentStatus Status)
    {
        //throw new NotImplementedException();
    }
    #endregion
}
class SkypeBufferStream : WaveStream
{
    byte[] latestInBuffer;
    WaveFormat waveFormat;

    public SkypeBufferStream(int sampleRate)
    {
        waveFormat = new WaveFormat(sampleRate, 16, 2);
    }

    public override WaveFormat WaveFormat
    {
        get { return waveFormat; }
    }

    public override long Length
    {
        get { return 0; }
    }

    public override long Position
    {
        get
        {
            return 0;
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public void SetLatestInBuffer(byte[] buffer)
    {
        latestInBuffer = buffer;
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        if (offset != 0)
            throw new ArgumentOutOfRangeException("offset");
        if (buffer != latestInBuffer)
            Array.Copy(latestInBuffer, buffer, count);
        return count;
    }
}
class TcpServer : IDisposable
{
    TcpListener listener;
    public event EventHandler<ConnectedEventArgs> Connect;
    public event EventHandler Disconnect;
    public event EventHandler<DataReceivedEventArgs> DataReceived;

    public TcpServer(int port)
    {
        listener = new TcpListener(IPAddress.Loopback, port);
        listener.Start();
        ThreadPool.QueueUserWorkItem(Listen);
    }

    private void Listen(object state)
    {
        while (true)
        {
            using (TcpClient client = listener.AcceptTcpClient())
            {
                AcceptClient(client);
            }
        }
    }

    private void AcceptClient(TcpClient client)
    {
        using (NetworkStream inStream = client.GetStream())
        {
            OnConnect(inStream);
            while (client.Connected)
            {
                int available = client.Available;
                if (available > 0)
                {
                    byte[] buffer = new byte[available];
                    int read = inStream.Read(buffer, 0, available);
                    Debug.Assert(read == available);
                    OnDataReceived(buffer);
                }
                else
                {
                    Thread.Sleep(50);
                }
            }
        }
        OnDisconnect();
    }

    private void OnConnect(NetworkStream stream)
    {
        var connect = Connect;
        if (connect != null)
        {
            connect(this, new ConnectedEventArgs() { Stream = stream });
        }
    }

    private void OnDisconnect()
    {
        var disconnect = Disconnect;
        if (disconnect != null)
        {
            disconnect(this, EventArgs.Empty);
        }
    }

    private void OnDataReceived(byte[] buffer)
    {
        var execute = DataReceived;
        if (execute != null)
        {
            execute(this, new DataReceivedEventArgs() { Buffer = buffer });
        }
    }

    #region IDisposable Members

    public void Dispose()
    {
        listener.Stop();
    }

    #endregion
}
public class DataReceivedEventArgs : EventArgs
{
    public byte[] Buffer { get; set; }
}

public class ConnectedEventArgs : EventArgs
{
    public NetworkStream Stream { get; set; }
}
If there is anything more you need to know, feel free to ask me here =)

Thank you guys, and excuse my english(!)
Coordinator
Feb 12, 2015 at 7:39 PM
hi there, I was the author of that Coding4Fun article. The first thing you need to know is that Skype deals with mono, 16bit PCM audio at 16kHz, whereas the audio you are capturing with WASAPI will likely be 44.1kHz IEEE floating point and stereo. So you are going to need to go through a multi-step conversion process to do that. Have a read of my article on converting audio between different formats to learn how to do this.

Mark