Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WaveFormat cannot be set for WASAPI Loopback Capture #174

Closed
tejendra2012 opened this issue Mar 17, 2017 · 20 comments
Closed

WaveFormat cannot be set for WASAPI Loopback Capture #174

tejendra2012 opened this issue Mar 17, 2017 · 20 comments

Comments

@tejendra2012
Copy link

tejendra2012 commented Mar 17, 2017

I am trying to record from speaker (loopback recording) in sample rate-8000 and channel-1,
problem is code is recording in sample rate-44100 and channel-2, format..

Stream stream = new System.IO.MemoryStream();
waveIn = new WasapiLoopbackCapture();
waveIn.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs>(sourceStream_DataRecord);
waveWriter = new NAudio.Wave.WaveFileWriter(stream, waveIn.WaveFormat);
waveIn.StartRecording();

when i add

waveIn.WaveFormat = new NAudio.Wave.WaveFormat(16000, 1);

it thrown exception

WaveFormat cannot be set for WASAPI Loopback Capture

there is any way to record it in 16000,1 format..
actually i dont want it change after complete recording.. i want it at start time..
please help me on this

@markheath
Copy link
Contributor

Unfortunately, WasapiCapture only supports capturing at the sample rate, channel count and bit depth the device is using. This is a limitation of WASAPI itself. So you must resample manually after capturing

@tejendra2012
Copy link
Author

Actually i am new on this... is there any algorithm for that?.. i didn't find useful link on web for resample .. please help

@markheath
Copy link
Contributor

take a look at my article on input driven resampling, for one way to do this.

@tejendra2012
Copy link
Author

waveIn = new WasapiLoopbackCapture();
waveIn.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs(sourceStream_DataRecord);
WaveFormat outf = new NAudio.Wave.WaveFormat(Properties.Settings.Default.recorderSampleRate, 2);
resampleStream = new NAudio.Wave.Compression.AcmStream(waveIn.WaveFormat, outf);

code through exception

AcmNotPossible calling acmStreamOpen

but when i write code like this.. it works ok

waveIn = new WasapiLoopbackCapture();
WaveFormat outf = new NAudio.Wave.WaveFormat(Properties.Settings.Default.recorderSampleRate, 1);
WaveFormat wf = new WaveFormat(waveIn.WaveFormat.SampleRate, waveIn.WaveFormat.Channels);
resampleStream = new NAudio.Wave.Compression.AcmStream(wf, outf);

but output audio is very noisy..
actually in loopback recording my system is recording in (44100,32,2).. i want to convert into (8000,16,1) or (16000,16,1)format

@tejendra2012
Copy link
Author

voice is in slow motion(sound like aaauuooo.. ) with noise

@markheath
Copy link
Contributor

then you have the wrong waveformat. Unfortunately, the ACM resampler doesn't like working with IEEE float, so you'd manually convert to 16 bit mono before resampling to 8kHz

@tejendra2012
Copy link
Author

tejendra2012 commented Mar 22, 2017

i used soxi cmd without convert to another.. output is

formatChannels       : 2
Sample Rate    : 44100
Precision      : 25-bit
Sample Encoding: 32-bit Floating Point PCM

and My code is

NAudio.Wave.IWaveIn waveIn;
NAudio.Wave.Compression.AcmStream resampleStream;
Stream streaming;
WaveFormat inFormat;
WaveFormat outFormat;
int countBytes=0;
Byte[] AudioBuffer = new Byte[20000000]; 
public void startRecording(int deviceNumber, bool isUpload)
{	
   waveIn = new WasapiLoopbackCapture();
   waveIn.DataAvailable += sourceStream_DataRecord;
   waveWriter = new NAudio.Wave.WaveFileWriter(streaming, waveIn.WaveFormat);
   outFormat = new NAudio.Wave.WaveFormat(8000, 1);
   inFormat = new WaveFormat(waveIn.WaveFormat.SampleRate, waveIn.WaveFormat.Channels);
   resampleStream = new NAudio.Wave.Compression.AcmStream(inFormat, outFormat);
}

private void sourceStream_DataRecord(object sender, NAudio.Wave.WaveInEventArgs e)
{
    Buffer.BlockCopy(e.buffer, 0, resampleStream.SourceBuffer, 0, e.BytesRecorded);
    int sourceBytesConverted = 0;
    var convertedBytes = resampleStream.Convert(e.BytesRecorded, out sourceBytesConverted);
    if (sourceBytesConverted != e.BytesRecorded)
    {
        Console.WriteLine("We didn't convert everything {0} bytes in, {1} bytes converted");
    }
    Buffer.BlockCopy(resampleStream.DestBuffer, 0, AudioBuffer, countBytes, convertedBytes);
    countBytes += convertedBytes;
}

public void SaveAudio(string path, int deviceNumber)
{
    WaveFormat wf = new WaveFormat(8000, 1);
    NAudio.Wave.WaveFileWriter waveWriter = new NAudio.Wave.WaveFileWriter(path, wf);
    waveWriter.Write(AudioBuffer, 0, countBytes);
}

whats the wrong with code
i also https://www.codeproject.com/Articles/501521/How-to-convert-between-most-audio-formats-in-NET
try this link to convert audio stereo to mono.. but no success
please help me on this

@tejendra2012
Copy link
Author

tejendra2012 commented Mar 23, 2017

i follow these step... audio is in correct format..

sampleStream=WaveToSampleProvider(inputStream);
resamplingProvider= WdlResamplingSampleProvider(sampleStream,8000);
ieeeToPCM = new SampleToWaveProvider16(resamplingProvider);
var sampleStreams = new NAudio.Wave.StereoToMonoProvider16(ieeeToPCM);
sampleStreams.LeftVolume=0.5f;
 sampleStreams.RightVolume = 0.5f;

2% noise when listen audio .. audio is almost correct
did i miss something??

@markheath
Copy link
Contributor

looks about right. Noise floor will be a bit higher with 16 bit 8kHz. One way to troubleshoot is create a WAV file at each intermediate stage (e.g. one from sampleStream, then one from resamplingProvider etc). That way you can find out where signal quality is degraded

@tejendra2012
Copy link
Author

tejendra2012 commented Mar 24, 2017

resamplingProvider= WdlResamplingSampleProvider(sampleStream,8000);

above method disturb the samples.. i record the silence from youtube https://www.youtube.com/watch?v=R7D1f6U6TpU..

capture
capture1

i run the app without resample (44100 to 8000), i just used SampleToWaveProvider16 and StereoToMonoProvider16 ... and output audio is perfect...

@markheath
Copy link
Contributor

well, you do get some loss of quality in downsampling. If there is a clicking in the audio that might be a problem of loss of resampler state or something. You could try using one of the other resamplers in NAudio like the WdlResampler

@tejendra2012
Copy link
Author

tejendra2012 commented Mar 29, 2017

No , There is no clicking in audio.. Here is my changed code...

 public byte[] Convert16(byte[] input, int length, WaveFormat format)
        {
            if (length == 0)
                return new byte[0];
            using (var memStream = new MemoryStream(input, 0, length))
            {  
               using (var inputStream = new RawSourceWaveStream(memStream, format))
               {    
                   var sampleStream = new NAudio.Wave.SampleProviders.WaveToSampleProvider(inputStream);
                   var resamplingProvider = new NAudio.Wave.SampleProviders.WdlResamplingSampleProvider(sampleStream, 8000);
                   var ieeeToPCM = new NAudio.Wave.SampleProviders.SampleToWaveProvider16(resamplingProvider);
                   var sampleStreams = new NAudio.Wave.StereoToMonoProvider16(ieeeToPCM);
                   sampleStreams.RightVolume = 0.5f;
                   sampleStreams.LeftVolume = 0.5f;
                   return readStream(sampleStreams, length);
               }
            }
        }
        
        private byte[] readStream(IWaveProvider waveStream, int length)
        {
            byte[] buffer = new byte[length];
            using (var stream = new MemoryStream())
            {
                int read;
                while ((read = waveStream.Read(buffer, 0, length)) > 0)
                {
                    stream.Write(buffer, 0, read);
                }
                return stream.ToArray();
            }
        }
        private void sourceStream_DataRecord(object sender, NAudio.Wave.WaveInEventArgs e)
      {
                byte[] output = Convert16(e.Buffer, e.BytesRecorded, waveIn.WaveFormat);
                int tmp = countBytes + output.Length;
                if (tmp > AudioBuffer.Length)
                {
                    Array.Resize(ref AudioBuffer, AudioBuffer.Length * 2);
                }
                Buffer.BlockCopy(output, 0, AudioBuffer, countBytes, output.Length);
                countBytes += output.Length;
      }

ok i will try with WdlResampler..
thanks

@markheath
Copy link
Contributor

you must have one resampler that you keep open for the whole time and use that to resample all blocks. Or you'll get discontinuities

@tejendra2012
Copy link
Author

i am confused.
If i opened resampler once, then how will i pass next sample stream to WdlResamplingSampleProvider
and i did not find WdlResampler().. naudio.Dsp has only 6 classes (BiQuadFilter, Complex, EnvelopeGenerator, FastFourierTransform, ImpulseResponseConvolution, SmbPitchShifter ). i am using visual studio 2012 and naudio version 1.8.0

@markheath
Copy link
Contributor

WdlResampler is not public yet. I've written an article about input driven resampling which will hopefully point you in the right direction: http://markheath.net/post/fully-managed-input-driven-resampling-wdl

@tejendra2012
Copy link
Author

Thanks, I will try this.

@tejendra2012
Copy link
Author

tejendra2012 commented Apr 5, 2017

I am just doing resample (44100 to 8000 and skiping stereo to mono). and audio is completely noise.. sound coming from left earphone and then half second silence, and then sound from right earphone(again same pattern)

did i miss something??

NAudio.Wave.IWaveIn waveIn;
int countBytes=0;
Byte[] AudioBuffer = new Byte[20000000]; 
var resampler = new WdlResampler();
resampler.SetMode(true, 2, false);
 resampler.SetFilterParms();
 resampler.SetFeedMode(true); // input driven
resampler.SetRates(waveIn.WaveFormat.SampleRate, 8000);

private void sourceStream_DataRecord(object sender, NAudio.Wave.WaveInEventArgs e) 
        {
                float[] buffer = new float[e.Buffer.Length];
                Buffer.BlockCopy(e.Buffer, 0, buffer, 0, e.Buffer.Length);

                int framesAvailable = e.Buffer.Length / waveIn.WaveFormat.Channels;
                float[] inBuffer;
                int inBufferOffset;
                int inNeeded = resampler.ResamplePrepare(framesAvailable, waveIn.WaveFormat.Channels, out inBuffer, out inBufferOffset);

                Array.Copy(buffer, 0, inBuffer, inBufferOffset, inNeeded * waveIn.WaveFormat.Channels);

                int inAvailable = inNeeded;
                float[] outBuffer = new float[buffer.Length*2]; // plenty big enough
                int framesRequested = outBuffer.Length / waveIn.WaveFormat.Channels;
                int outAvailable = resampler.ResampleOut(outBuffer, 0, inAvailable, framesRequested, waveIn.WaveFormat.Channels);

                Buffer.BlockCopy(outBuffer, 0, AudioBuffer, countBytes, outBuffer.Length);
                countBytes += outBuffer.Length;
}

i more thing soxi cmd show less informtion when i saved audio
Input File : 'new.wav'
Channels : 2
Sample Rate : 8000
Precision : 16-bit
Sample Encoding: 16-bit Signed Integer PCM

and downloaded file from internet show more field
Input File : 'audio.wav'
Channels : 1
Sample Rate : 8000
Precision : 16-bit
Duration : 00:00:06.61 = 52872 samples ~ 495.675 CDDA sectors
File Size : 106k
Bit Rate : 128k
Sample Encoding: 16-bit Signed Integer PCM

code that i used to save

WaveFormat outFormat = new WaveFormat(8000, 2);
 NAudio.Wave.WaveFileWriter waveWriter = new NAudio.Wave.WaveFileWriter(path, outFormat);
waveWriter.Write(AudioBuffer, 0, countBytes);

@markheath
Copy link
Contributor

you are confusing the length of buffers in bytes with the number of samples. In WaveInEventArgs you should use bytes recorded. Divide it by 4 to get number of samples, and then by 2 (assuming stereo) to get number of sample frames.

@tejendra2012
Copy link
Author

tejendra2012 commented May 10, 2017

I solved my problem my copied WdlResamplingSampleProvider class in my project and change constructor of class

 public WdlResamplingSampleProvider(ISampleProvider source,WdlResampler resampler, int newSampleRate)
        {
            channels = source.WaveFormat.Channels;
            outFormat = WaveFormat.CreateIeeeFloatWaveFormat(newSampleRate, channels);
            this.source = source;
            this.resampler = resampler;
        }

It will be good if u make two constructor in WdlResamplingSampleProvider
Thanks

@allowwind
Copy link

我只是在进行重采样(44100至8000,并跳过立体声至单声道)。声音完全是噪音。.声音从左耳机传来,然后静音半秒钟,然后从右耳机传来声音(同样的模式)

我错过了什么??

NAudio.Wave.IWaveIn waveIn;
int countBytes=0;
Byte[] AudioBuffer = new Byte[20000000]; 
var resampler = new WdlResampler();
resampler.SetMode(true, 2, false);
 resampler.SetFilterParms();
 resampler.SetFeedMode(true); // input driven
resampler.SetRates(waveIn.WaveFormat.SampleRate, 8000);

private void sourceStream_DataRecord(object sender, NAudio.Wave.WaveInEventArgs e) 
        {
                float[] buffer = new float[e.Buffer.Length];
                Buffer.BlockCopy(e.Buffer, 0, buffer, 0, e.Buffer.Length);

                int framesAvailable = e.Buffer.Length / waveIn.WaveFormat.Channels;
                float[] inBuffer;
                int inBufferOffset;
                int inNeeded = resampler.ResamplePrepare(framesAvailable, waveIn.WaveFormat.Channels, out inBuffer, out inBufferOffset);

                Array.Copy(buffer, 0, inBuffer, inBufferOffset, inNeeded * waveIn.WaveFormat.Channels);

                int inAvailable = inNeeded;
                float[] outBuffer = new float[buffer.Length*2]; // plenty big enough
                int framesRequested = outBuffer.Length / waveIn.WaveFormat.Channels;
                int outAvailable = resampler.ResampleOut(outBuffer, 0, inAvailable, framesRequested, waveIn.WaveFormat.Channels);

                Buffer.BlockCopy(outBuffer, 0, AudioBuffer, countBytes, outBuffer.Length);
                countBytes += outBuffer.Length;
}

当我保存音频时,更多的东西soxi cmd显示更少的信息
输入文件:'new.wav'
通道:2
采样率:8000
精度:16位
采样编码:16位带符号整数PCM

和从互联网上下载的文件显示更多字段
输入文件:'audio.wav'
通道:1
采样率:8000
精度:16位
持续时间:00:00:06.61 = 52872个样本〜495.675 CDDA扇区
文件大小:106k
比特率:128k
样本编码:16位带符号整数PCM

我用来保存的代码

WaveFormat outFormat = new WaveFormat(8000, 2);
 NAudio.Wave.WaveFileWriter waveWriter = new NAudio.Wave.WaveFileWriter(path, outFormat);
waveWriter.Write(AudioBuffer, 0, countBytes);

我解决了我在项目中复制的WdlResamplingSampleProvider类的问题,并更改了该类的构造函数

 public WdlResamplingSampleProvider(ISampleProvider source,WdlResampler resampler, int newSampleRate)
        {
            channels = source.WaveFormat.Channels;
            outFormat = WaveFormat.CreateIeeeFloatWaveFormat(newSampleRate, channels);
            this.source = source;
            this.resampler = resampler;
        }

如果
您在WdlResamplingSampleProvider中创建两个构造函数,那将是很好的

我解决了我在项目中复制的WdlResamplingSampleProvider类的问题,并更改了该类的构造函数

 public WdlResamplingSampleProvider(ISampleProvider source,WdlResampler resampler, int newSampleRate)
        {
            channels = source.WaveFormat.Channels;
            outFormat = WaveFormat.CreateIeeeFloatWaveFormat(newSampleRate, channels);
            this.source = source;
            this.resampler = resampler;
        }

如果
您在WdlResamplingSampleProvider中创建两个构造函数,那将是很好的

Did you solve that problem?
can i have you all code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants