I'm trying to write my own VST Host and for that i need to record and play audio from an Asio Driver (in my case for an audio interface). That's why i'm trying to use NAudio's AsioOut.
For testing purposes i'm currently just trying to record the input, copy and play it to the output.
My code looks like this:
var asioout = new AsioOut();
BufferedWaveProvider wavprov = new BufferedWaveProvider(new WaveFormat(44100, 2));
asioout.AudioAvailable += new EventHandler<AsioAudioAvailableEventArgs>(asio_DataAvailable);
asioout.InitRecordAndPlayback(wavprov, 2, 25);
asioout.Play();
...
void asio_DataAvailable(object sender, AsioAudioAvailableEventArgs e)
{
Array.Copy(e.InputBuffers, e.OutputBuffers, e.InputBuffers.Length);
e.WrittenToOutputBuffers = true;
}
This way i can't hear any output. I also tried it this way:
void asio_DataAvailable(object sender, AsioAudioAvailableEventArgs e)
{
byte[] buf = new byte[e.SamplesPerBuffer];
for (int i = 0; i < e.InputBuffers.Length; i++)
{
//Marshal.Copy(e.InputBuffers[i], e.OutputBuffers, 0, e.InputBuffers.Length);
//also tried to upper one but this way i also couldn't hear anything
Marshal.Copy(e.InputBuffers[i], buf, 0, e.SamplesPerBuffer);
Marshal.Copy(buf, 0, e.OutputBuffers[i], e.SamplesPerBuffer);
}
e.WrittenToOutputBuffers = true;
}
This way i can hear sound in the volume of my input but it's very distorded.
What am i doing wrong here?
PS: I know how to record and playback.... exists but i couldn't really get a complete answer from this thread, just the idea to try it with Marshall.Copy....
Your second attempt is more correct than the first: each input buffer must be copied separately. However the final parameter of the copy method should be the number of bytes, not the number of samples in the buffer. This will typically be 3 or 4 bytes per sample, depending on your ASIO bit depth.
Related
I am using WasapiLoopbackCapture from the NAudio C# library to capture audio being sent to my computer speakers. My goal is to get the samples and send them to an FFT function to get the spectrum data.
I'm trying to find out how exactly to parse the incoming audio data, but it seems that the answer to my question is split up and scattered around several blog posts and Stack Overflow answers, so I would like to get a definitive answer for my situation.
My waveIn.WaveFormat says I have 32 bits/sample and 2 channels. It also says that the encoding is IeeeFloat.
From what I've gathered, left and right channel samples are interleaved (left, right, left, right, etc.) and if there are 4 bytes/sample, every 4 bytes in the buffer makes up a sample (this is probably obvious to most, but I want to verify). Also, is the data little endian? and will the BitConverter.ToSingle() function take care of that?
This is my code:
static void Main(string[] args)
{
IWaveIn waveIn = new WasapiLoopbackCapture();
waveIn.DataAvailable += waveIn_DataAvailable;
waveIn.StartRecording();
}
static void waveIn_DataAvailable(object sender, WaveInEventArgs e)
{
for (int i = 0; i < e.BytesRecorded; i += 8)
{
float leftSample = BitConverter.ToSingle(e.Buffer, i);
float rightSample = BitConverter.ToSingle(e.Buffer, i + 4);
}
}
Does this recover the samples correctly?
I am trying to understand object pooling. I am able to get the script to pull one object at a time, but I need to be able to pull three or more from the list at the same time.
My object pooling script is big, so I don't really want to share the whole thing unless it is necessary.
I need to be able to change the location of the spawn of a flame, so I created a script to do that:
private void CreateWavesForFlames(GameObject flame, float xBase, float xDisplacement, float dropHeight)
{
flame.transform.position = new Vector3(xBase + xDisplacement, dropHeight, 0);
flame.SetActive(true); //this turn the pooled object on
}
So I need to spawn three flames at the same time and change their spawn locations
The wave call would look something like this:
void Wave1() {
Debug.Log("Wave1");
tempGOHolder = gm.GetLargeFire();
CreateWavesForFlames(tempGOHolder, 0, 0, 12);
CreateWavesForFlames(tempGOHolder, 10, 0, 12);
CreateWavesForFlames(tempGOHolder, 15, 0, 12);
}
What happens is only one fire flame is created and it uses the last CreatWavesForFlames. I need the three to be different.
Any suggestions on how to do this would be awesome.
I know this has been answered but I think there is a better solution. The answer above is great. Assuming you want to make your code shorter by not repeatedly calling the GetLargeFire() function, you can use the method below.
Lets say that your GetLargeFire() function in your pool script looks like this:
GameObject GetLargeFire()
{
return availableGameObject;
}
You can create an overload of the GetLargeFire() function that fills up an array that is passed to it. I wouldn't recommend returning array because that allocates memory, therefore making your pooling script useless. Filling up array that is passed in is better.
public void GetLargeFire(GameObject[] gOBJ, int amountToReturn)
{
for (int i = 0; i < gOBJ.Length; i++)
{
if (i < amountToReturn)
{
gOBJ[i] = GetLargeFire();
}
else
{
//Fill the rest with null to override what was inside of it previously
gOBJ[i] = null;
}
}
}
Now, to use it like the example you have in your question, you should declare tempGOHolder as an array and choose a number you you think is enough for it. We will use 3 for this example.
GameObject[] tempGOHolder;
void Start()
{
tempGOHolder = new GameObject[3];
}
void Wave1() {
Debug.Log("Wave1");
gm.GetLargeFire(tempGOHolder, 3);
CreateWavesForFlames(tempGOHolder[0], 0, 0, 12);
CreateWavesForFlames(tempGOHolder[1], 10, 0, 12);
CreateWavesForFlames(tempGOHolder[2], 15, 0, 12);
}
You can even use loop to create Waves with CreateWavesForFlames with less code.
Well.. that is what is expected from your code. if you want 3 different flame objects, Then you are going to have to do this(assuming "gm" is your pool manager object):
tempGOHolder = gm.GetLargeFire();
CreateWavesForFlames(tempGOHolder, 0, 0, 12);
tempGOHolder = gm.GetLargeFire();
CreateWavesForFlames(tempGOHolder, 10, 0, 12);
tempGOHolder = gm.GetLargeFire();
CreateWavesForFlames(tempGOHolder, 15, 0, 12);
I am currently creating a Winforms application for Windows 8.1, I have been able to perform an FFT on the input data from the devices microphone using ASIO Out, however to be able to use ASIO on my machine I needed to download ASIO4ALL,
This is causing a huge amount of feedback in the microphone and is resulting in very inaccurate frequency readings (to make sure it was the sound itself I wrote a copy to disc to playback),
So to get around this I have been trying to adapt my code to work with Naudio's WaveIn class, however this is returning either no data or NaN for the FFT algorithm (although I can save a recording to disk which plays back with no issues),
I've been trying to fix this for some time now and am sure it is just a silly mistake somewhere, any help would be greatly appreciated!
Below is the code for the "OnDataAvailable" event (where I'm 99% sure I am going wrong):
void OnDataAvailable(object sender, WaveInEventArgs e)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new EventHandler<WaveInEventArgs>(OnDataAvailable), sender, e);
}
else
{
byte[] buffer = e.Buffer;
int bytesRecorded = e.BytesRecorded;
int bufferIncrement = waveIn.WaveFormat.BlockAlign;
for (int index = 0; index < bytesRecorded; index += bufferIncrement)
{
float sample32 = BitConverter.ToSingle(buffer, index);
sampleAggregator.Add(sample32);
}
if (waveFile != null)
{
waveFile.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Flush();
}
}
}
If any more details and/or code is required please let me know.
waveFile: Name of the file writer
e.Buffer: The buffer containing the recorded data
e.BytesRecorded: The total number of bytes recorded
For reference below is the working code when using the ASIO class:
void asio_DataAvailable(object sender, AsioAudioAvailableEventArgs e)
{
byte[] buf = new byte[e.SamplesPerBuffer * 4];
for (int i = 0; i < e.InputBuffers.Length; i++)
{
Marshal.Copy(e.InputBuffers[i], buf, 0, e.SamplesPerBuffer * 4);
}
for (int i = 0; i < e.SamplesPerBuffer * 4; i++)
{
float sample32 = Convert.ToSingle(buf[i]);
sampleAggregator.Add(sample32);
}
}
EDIT: The samples which are being returned are now accurate after changing the convert statement to Int16 as per the advice on this page, I had some other issues in my code which prevented actual results from being returned originally.
However, the file which is being written to disk is very choppy, I'm sure this is a problem with my laptop and the number of processes which is trying to perform, could anyone please advise a way around this issue?
In the NAudio WPF demo project there is an example of calculating FFTs while playback is happening with a class called SampleAggregator, that stores up blocks of 1024 samples and then performs FFTs on them.
It looks like you are trying to do something similar to this. I suspect the problem is that you are getting 16 bit samples, not 32 bit. Try using BitConverter.ToShort on every pair of bytes.
mWaveInDevice = new WaveIn();
mWaveInDevice.WaveFormat = WaveFormat.**CreateIeeeFloatWaveFormat(44100,2)**;
Set CreateIeeeFloatWaveFormat for WaveFormat, and then you will get right values after fft.
Hi im very new to this thing so please bear with me. I am trying to convert a WAV file to a spectrogram but arent sure how to begin with. I read on something that says to read the PCM data(which i think is my WAV file) and store it in an array in the WavReader class before apply the FFT on it and converting it to GUI. Im currently using Naudio to achieve this but could not find anything that shows how to convert the WAV file to a spectrogram. Thanks
Edit :
I found out about converting PCM to FFT with Naudio and im stuck.
using (var reader = new AudioFileReader("test1.wav"))
{
// test1.wav is my file to process
// test0.wav is my temp file
IWaveProvider stream16 = new WaveFloatTo16Provider(reader);
using (WaveFileWriter converted = new WaveFileWriter("test0.wav", stream16.WaveFormat))
{
// buffer length needs to be a power of 2 for FFT to work nicely
// however, make the buffer too long and pitches aren't detected fast enough
// successful buffer sizes: 8192, 4096, 2048, 1024
// (some pitch detection algorithms need at least 2048)
byte[] buffer = new byte[8192];
int bytesRead;
do
{
bytesRead = stream16.Read(buffer, 0, buffer.Length);
converted.WriteData(buffer, 0, bytesRead);
} while (bytesRead != 0 && converted.Length < reader.Length);
}
}
Edit : I would also like to know if it is possible to compare 2 spectrograms of 2 different files programmatically.
You could also use BASS.NET library which natively provides all these features and is free.
The Visuals.CreateSpectrum3DVoicePrint Method does exactly that.
Feel free to ask for assistance if you're having a hard time using it.
EDIT : here's a quick and dirty sample
public partial class Form1 : Form
{
private int _handle;
private int _pos;
private BASSTimer _timer;
private Visuals _visuals;
public Form1()
{
InitializeComponent();
}
private void timer_Tick(object sender, EventArgs e)
{
bool spectrum3DVoicePrint = _visuals.CreateSpectrum3DVoicePrint(_handle, pictureBox1.CreateGraphics(),
pictureBox1.Bounds, Color.Cyan, Color.Green,
_pos, false, true);
_pos++;
if (_pos >= pictureBox1.Width)
{
_pos = 0;
}
}
private void Form1_Load(object sender, EventArgs e)
{
string file = "..\\..\\mysong.mp3";
if (Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, Handle))
{
_handle = Bass.BASS_StreamCreateFile(file, 0, 0, BASSFlag.BASS_DEFAULT);
if (Bass.BASS_ChannelPlay(_handle, false))
{
_visuals = new Visuals();
_timer = new BASSTimer((int) (1.0d/10*1000));
_timer.Tick += timer_Tick;
_timer.Start();
}
}
}
}
EDIT 2
You can provide a file name but you can also provide your own audio data using the other overload that accepts an IntPtr or use Bass.BASS_StreamCreatePush with Bass.BASS_StreamPutData.
Regarding comparing spectrograms you could do the following :
Resize the image to a smaller size, reduce information by dithering it to 8-bit (with a good algorithm however)
Compare the two images
However for comparing audio data I'd strongly suggest you to use fingerprints, it roughly does that but is much more robust than my suggestion.
Here's a fingerprinting library that is free to use :
http://www.codeproject.com/Articles/206507/Duplicates-detector-via-audio-fingerprinting
Not entirely sure it would work for small samples, though.
EDIT 3
I'm afraid I can't find the link where I've read that but that's what they do: reducing data and comparing images such as the example below (last image):
(note : not to compare at all with image 1, it's something else but just to show why using a lower resolution might give better yields)
(from http://blog.echonest.com/post/545323349/the-echo-nest-musical-fingerprint-enmfp)
Now a very basic explanation of the process:
Comparison source A:
Comparison source B: (I've just changed a region of A)
Comparison result:
(done with Paint.Net by adding the former images as layers and setting 2nd layer blending to difference instead of normal)
If the fingerprints were to be identical the resulting image would be completely black.
And by reducing data to a 8-bit image you are easing the comparison process but keep in mind you will need a good dithering algorithm.
This is one is quite good :
http://www.codeproject.com/Articles/66341/A-Simple-Yet-Quite-Powerful-Palette-Quantizer-in-C
Well it's not on par with Photoshop or Hypersnap's one (which IMO is exceptional) but that might be enough for the task.
And avoid at all costs Floyd–Steinberg dithering or something that does error diffusion.
Here some attempts on creating dithering algorithms : http://bisqwit.iki.fi/story/howto/dither/jy/
Take this with caution as I'm not an expert in the field but that's roughly how it's done.
Go to https://dsp.stackexchange.com/ and ask a few questions there, you might get useful hints on achieving this.
Is there a way to play two sounds at the same time?
I know that SoundPlayer isn't able to do this.
I can't use SoundEffect as I believe it's only part of XNA.
The two required sounds will be called at unknown and random times. The sound needs to be be controlled after it is played. i.e., the sound must be able to be stopped before it has finished playing.
Reference PresentationCore and WindowsBase and try this...
var p1 = new System.Windows.Media.MediaPlayer();
p1.Open(new System.Uri(#"C:\windows\media\tada.wav"));
p1.Play();
// this sleep is here just so you can distinguish the two sounds playing simultaneously
System.Threading.Thread.Sleep(500);
var p2 = new System.Windows.Media.MediaPlayer();
p2.Open(new System.Uri(#"C:\windows\media\tada.wav"));
p2.Play();
EDIT
I received a downvote probably because at first glance this looks like it will play the second sound after the first is finished. It doesn't, they are played by windows asynchronously. The sleep is there so if you test this code verbatim you can hear the sounds play together, it wouldn't be noticeable without the delay since they are the same sound.
This code demonstrates the two sounds playing on separate threads on top of each other, which is sort of pointless since the playback doesn't block anyway
new System.Threading.Thread(() => {
var c = new System.Windows.Media.MediaPlayer();
c.Open(new System.Uri(#"C:\windows\media\tada.wav"));
c.Play();
}).Start();
System.Threading.Thread.Sleep(500);
new System.Threading.Thread(() => {
var c = new System.Windows.Media.MediaPlayer();
c.Open(new System.Uri(#"C:\windows\media\tada.wav"));
c.Play();
}).Start();
http://msdn.microsoft.com/en-us/library/system.windows.media.mediaplayer.stop.aspx
The class also has the control you need to stop playback
The "MediaPlayer" object will not let you play two sounds at once, even if you create two instances. You will need to bring in the native windows API "mciSendString".
[DllImport("winmm.dll")]
static extern Int32 mciSendString(string command, StringBuilder buffer, int bufferSize, IntPtr hwndCallback);
public Form1()
{
InitializeComponent();
mciSendString(#"open C:\Users\Jono\Desktop\applause.wav type waveaudio alias applause", null, 0, IntPtr.Zero);
mciSendString(#"play applause", null, 0, IntPtr.Zero);
mciSendString(#"open C:\Users\Jono\Desktop\foghorn.wav type waveaudio alias foghorn", null, 0, IntPtr.Zero);
mciSendString(#"play foghorn", null, 0, IntPtr.Zero);
}
check PlaySound method here http://msdn.microsoft.com/en-us/library/aa909766.aspx, and its flag SND_ASYNC .
Solution :
Hi,
I was developing a WP8 App and i needed multiple sounds to play simultaneously, the solutions mentioned above didnt work for me, So i used the XNA framwork. here is the link
http://msdn.microsoft.com/en-us/library/ff842408.aspx
and then play ur sound files like this...
SoundEffect Sound = SoundEffect.FromStream(Application.GetResourceStream(new Uri("Assets/Sounds/wav/sound.wav", UriKind.Relative)).Stream);
Sound.Play();
For looping...
SoundEffectInstance Sound = SoundEffect.FromStream(Application.GetResourceStream(new Uri("Assets/Sounds/wav/sound.wav", UriKind.Relative)).Stream).CreateInstance();
Sound.IsLooped = true;
Sound.Play();
Note: the files must be in ".wav" (PCM, 8 or 16-bit, 8KHz to 48KHz, mono or stereo) format
From http://alvas.net/alvas.audio,samples.aspx#sample7 and http://alvas.net/alvas.audio,samples.aspx#sample6
Player pl = new Player();
byte[] arr = File.ReadAllBytes(#"in.wav");
pl.Play(arr);
Player pl2 = new Player();
pl2.FileName = "123.mp3";
pl2.Play();
or mix audio data before playing How to mix to mix two audio file..
private void Mix(string outfile, string infile1, string infile2, int shiftSec)
{
WaveReader wr1 = new WaveReader(File.OpenRead(infile1));
WaveReader wr2 = new WaveReader(File.OpenRead(infile2));
IntPtr format1 = wr1.ReadFormat();
WaveFormat wf = AudioCompressionManager.GetWaveFormat(format1);
WaveWriter ww = new WaveWriter(File.Create(outfile), AudioCompressionManager.FormatBytes(format1));
byte[] data0 = wr1.ReadData(0, shiftSec);
byte[] data1 = wr1.ReadData(shiftSec);
byte[] data2 = wr2.ReadData();
byte[] mixData = AudioCompressionManager.Mix(format1, data2, data1);
ww.WriteData(data0);
ww.WriteData(mixData);
ww.Close();
wr2.Close();
wr1.Close();
}
The accepted answer didn't work for me.
Here is what worked:
Make sure you add references to PresentationCore and WindowsBase dlls.
private void Form1_Load(object sender, EventArgs e)
{
System.Media.SoundPlayer player = new System.Media.SoundPlayer(#"C:\Users\me\source\repos\TacticalIslandSurvival\sounds\blazer rail.wav");
player.PlayLooping();
}
private void button1_MouseEnter(object sender, EventArgs e)
{
var p1 = new System.Windows.Media.MediaPlayer();
p1.Open(new System.Uri(#"C:\Users\me\source\repos\TacticalIslandSurvival\sounds\click.wav"));
p1.Play();
}