AcmNotPossible calling acmStreamOpen with WaveFormatConversionStream - c#

What I'm trying to do is convert a WaveIn microphone input to a different WaveFormat, and add that to a MixingSampleProvider.
WaveIn waveIn = new WaveIn(this.Handle);
waveIn.BufferMilliseconds = 25;
waveIn.DataAvailable += waveIn_DataAvailable;
// create wave provider
WaveProvider waveProvider = new BufferedWaveProvider(waveIn.WaveFormat);
WaveFormat commonWaveFormat = WaveFormat.CreateIeeeFloatWaveFormat(44100, 2);
MixingSampleProvider msp = new MixingSampleProvider(commonWaveFormat);
WaveFormatConversionStream wfcs = new WaveFormatConversionStream(commonWaveFormat,new WaveProviderToWaveStream(waveProvider));
msp.AddMixerInput(wfcs);
// create wave output to speakers
waveOut = new WaveOut();
waveOut.DesiredLatency = 100;
waveOut.Init(msp);
where WaveProviderToWaveStream is a class from this answer.
However, that gives me the following exception at WaveFormatConversionStream.
NAudio.MmException:AcmNotPossible calling acmStreamOpen
I tried
msp.addMixerInput(MediaFoundationResampler(waveProvider, commonWaveFormat).toSampleProvider());
which worked but produced a too large delay between talking into the microphone and hearing the output.

It will be much easier for you simply to specify the format you want to record in on the to WaveIn object itself. (Just set the WaveFormat property before starting to record. It should still be 16 bit PCM, but specify the sample rate and channel count you want)
Then you can turn your BufferedWaveProvider into an ISampleProvider by using the ToSampleProvider extension method which will let you add it to the MixingSampleProvider

Related

The end of an audio file that's looping?

I'm trying to create a simple audio Looper to record my own songs. For those of you who don't know what an audio Looper is, you first record a layer of a song(let's say a piece of piano) then you start Looping that piece to add a piece of guitar on top of that layered. Since I already know how to add both together that's not the issue. But I would like to safe my layer which I add every time on top of that layer, so I would have to check if the audio file I loop is restarting or not. To safe a 2nd file with the layer to add. Mix it together afterwards(since you can't write 2 different streams in 1 file) and loop that.
For the Looping I am using the SoudPlayer Class using System.Media.
Player = new SoundPlayer(path);
Player.PlayLooping();
int deviceNumber = sourceList.SelectedItems[0].Index;
sourceStream = new NAudio.Wave.WaveIn();
sourceStream.DeviceNumber = deviceNumber;
sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(44100, NAudio.Wave.WaveIn.GetCapabilities(deviceNumber).Channels);
sourceStream.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs>(sourceStream_DataAvailable);
waveWriter = new NAudio.Wave.WaveFileWriter(open.FileName, sourceStream.WaveFormat);
sourceStream.StartRecording();
The recording should stop everytime the audio file is at the end.
Thanks in advance!
You need take a look on the NAudio documentation.
One safety feature I often add when recording WAV is to limit the size of a WAV file. They grow quickly and can't be over 4GB in any case. Here I'll request that recording stops after 30 seconds:
waveIn.DataAvailable += (s, a) =>
{
writer.Write(a.Buffer, 0, a.BytesRecorded);
if (writer.Position > waveIn.WaveFormat.AverageBytesPerSecond * 30)
{
waveIn.StopRecording();
}
};

I am using nAudio library to record from MIC, How to increase gain of MIC?

WaveIn InputSource = new WaveIn() { DeviceNumber = deviceNumber, WaveFormat = new WaveFormat(44100, WaveIn.GetCapabilities(cmbInputDevices.SelectedIndex).Channels) };
InputSource.DataAvailable += (s,e)
{
//Lines to write into a wave file
};
InputSource.RecordingStopped += (s,e)
{
};
This is the snippet I use to record from default input device which fine, but as my mic sensitivity is not good I want to increase gain so that I could get a louder recording
Check this sample Voice recorder. It is a sample application demonstrating how to use NAudio to record audio
Voice Recorder

NAudio playing 32 bit float IEE

I have an application which is generating 32 bit floats (big endian). If I write these to a file and then open them in Audacity the file plays correctly.
I am trying to play the stream using NAudio. If I create a WaveFormat of 24k samples, 32 bit and 2 channels I do hear noise although as it's the wrong format the stream isn't rendering of course, if I create the correct format (IeeeFloatWave) then I don't hear anything at all. I know the samples are arriving correctly as I can save them to disk but I just can't play them. Anybody see what I'm doing that is wrong?
Updated and solved - little to big endian change is in the feeding routine
FloatTo16 bit provider converts format before playing
private bool _streamActive = false;
private BufferedWaveProvider bufferedWaveProvider = null;
private WaveFloatTo16Provider waveFloatTo16Provider = null;
private WaveOut waveOut = null;
private WaveFormat waveFormat=null;
// ProcessSound is fed incoming byte packets
// (28 bytes header plus 1024 bytes audio)
// by background thread
// Data is converted from little to big endian in background thread
public void ProcessSound(byte[] rxData)
{
// get data length
int datalen = rxData.Length;
// check to activate player
if (_streamActive == true)
{
// add samples to buffer ('28' allows for header information)
bufferedWaveProvider.AddSamples(rxData, 28, datalen - 28);
return;
}
// start it going
waveFormat = WaveFormat.CreateIeeeFloatWaveFormat(24000, 2);
// create buffer to allow samples to be added
bufferedWaveProvider = new BufferedWaveProvider(waveFormat);
// convert from 32 bit float to 16 bit PCM
waveFloatTo16Provider = new WaveFloatTo16Provider(bufferedWaveProvider);
// add samples to buffer
bufferedWaveProvider.AddSamples(rxData, 28, datalen - 28);
// create waveOut player
waveOut = new WaveOut();
waveOut.Init(waveFloatTo16Provider);
waveOut.Volume = 0.25f;
waveOut.Play();
// mark stream is active
_streamActive = true;
}

Handle setting up WaveFormat for mp3 memory stream in NAudio

I'm trying to set up the WaveStream so that it uses the same format as the mp3 data passed in. I get the format by reading a frame, but when I try to actually create the new conversion stream using the new format I get an "AcmNotPossible calling AcmStreamOpen" exception.
Here's where I'm trying to set the new format:
Mp3Frame f = Mp3Frame.LoadFromStream(ms);
WaveFormat targetFormat = new Mp3WaveFormat(f.SampleRate, f.ChannelMode == ChannelMode.Mono ? 1 : 2, f.FrameLength, f.BitRate);
WaveFormatConversionStream conversionStream;
try
{
using (WaveStream blockAlignedStream =
new BlockAlignReductionStream(conversionStream = new WaveFormatConversionStream(targetFormat,
new Mp3FileReader(ms))))
{
using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
{
waveOut.Init(blockAlignedStream);
waveOut.Play();
I'm not sure if I even need to convert anything if I set up the wave stream to match the format of the mp3 data.
NOTE: I tried using WaveFormatStream.CreatePcmStream but I was getting 'static/white noise' for some mp3's. They appeared to be 16bit 44,100 stereo, but were being labeled as Version 1, Layer 1, as opposed to version 1, layer 3 which plays back correctly.
This code sample seems to have come from a long time back. You don;t need the BlockAlignReductionStream or the WaveFormatConversionStream, and you should stay away from function callbacks in WaveOut. This should be sufficient to play from a memory stream:
var reader = new Mp3FileReader(ms)
var waveOut = new WaveOutEvent();
waveOut.Init(reader);
waveOut.Play();

Playing a sound from a generated buffer in a Windows 8 app

I'm porting some C# Windows Phone 7 apps over to Windows 8.
The phone apps used an XNA SoundEffect to play arbitrary sounds from a buffer. In the simplest cases I'd just create a sine wave of the required duration and frequency. Both the duration and frequency can vary greatly, so I'd prefer not to rely on MediaElements (unless there is someway to shift the frequency of a base file, but that will only help me with the single frequency generation).
What is the equivalent of an XNA SoundEffectInstance in WinRT?
I assume I'll need to use DirectX for this, but I'm not sure how to go about this from an otherwise C#/XAML app. I've had a look at SharpDX, but it didn't seem to have the DirectSound, SecondaryBuffer, SecondaryBuffer classes that I assume I'd need to use.
I've made a number of assumptions above. It may be I'm looking for the wrong classes or there is an entirely separate way to generate arbitrary sound from a Windows 8 app.
I found an example using XAudio2 from SharpDX to play a wav file via an AudioBuffer. This seems promising, I'd just need to substitute my generated audio buffer for the native file stream.
PM> Install-Package SharpDX
PM> Install-Package SharpDX.XAudio2
public void PlaySound()
{
XAudio2 xaudio;
MasteringVoice masteringVoice;
xaudio = new XAudio2();
masteringVoice = new MasteringVoice(xaudio);
var nativefilestream = new NativeFileStream(
#"Assets\SpeechOn.wav",
NativeFileMode.Open,
NativeFileAccess.Read,
NativeFileShare.Read);
var soundstream = new SoundStream(nativefilestream);
var waveFormat = soundstream.Format;
var buffer = new AudioBuffer
{
Stream = soundstream.ToDataStream(),
AudioBytes = (int)soundstream.Length,
Flags = BufferFlags.EndOfStream
};
var sourceVoice = new SourceVoice(xaudio, waveFormat, true);
// There is also support for shifting the frequency.
sourceVoice.SetFrequencyRatio(0.5f);
sourceVoice.SubmitSourceBuffer(buffer, soundstream.DecodedPacketsInfo);
sourceVoice.Start();
}
The only way to generate dynamic sound in Win8RT is to use XAudio2, so you should be able to do this with SharpDX.XAudio2.
Instead of using NativeFileStream, just instantiate a DataStream directly giving your managed buffer (or you can use an unmanaged buffer or let DataStream instantiate one for you). The code would be like this:
// Initialization phase, keep this buffer during the life of your application
// Allocate 10s at 44.1Khz of stereo 16bit signals
var myBufferOfSamples = new short[44100 * 10 * 2];
// Create a DataStream with pinned managed buffer
var dataStream = DataStream.Create(myBufferOfSamples, true, true);
var buffer = new AudioBuffer
{
Stream = dataStream,
AudioBytes = (int)dataStream.Length,
Flags = BufferFlags.EndOfStream
};
//...
// Fill myBufferOfSamples
//...
// PCM 44.1Khz stereo 16 bit format
var waveFormat = new WaveFormat();
XAudio2 xaudio = new XAudio2();
MasteringVoice masteringVoice = new MasteringVoice(xaudio);
var sourceVoice = new SourceVoice(xaudio, waveFormat, true);
// Submit the buffer
sourceVoice.SubmitSourceBuffer(buffer, null);
// Start playing
sourceVoice.Start();
Sample method to fill the buffer with a Sine wave:
private void FillBuffer(short[] buffer, int sampleRate, double frequency)
{
double totalTime = 0;
for (int i = 0; i < buffer.Length - 1; i += 2)
{
double time = (double)totalTime / (double)sampleRate;
short currentSample = (short)(Math.Sin(2 * Math.PI * frequency * time) * (double)short.MaxValue);
buffer[i] = currentSample; //(short)(currentSample & 0xFF);
buffer[i + 1] = currentSample; //(short)(currentSample >> 8);
totalTime += 2;
}
}
You can also use WASAPI to play dynamically-generated sound buffers in WinRT. (xaudio2 isn't the only solution).
I wrote sample code for it in VB here (the C# will be essentially the same):
http://www.codeproject.com/Articles/460145/Recording-and-playing-PCM-audio-on-Windows-8-VB
I believe that the NAudio guy is planning to translate+incorporate my sample code into NAudio, for a Win8-supported version, so that'll be easier to use.

Categories