Multiple audio stream in Universal App(Runtime API), XNA SoundEffect replacement - c#

As the XNA SoundEffect is no longer available in the Windows Runtime API (for developing Universal App), I need something similar to play multiple audio streams at the same time.
Requirements:
Play the same audio file multiple times, simultaneously.
Previous Silverlight implementation with SoundEffect:
// Play sound 10 times, sound can be played together.
// i.e. First sound continues playing while second sound starts playing.
for(int i=0; i++; i < 10)
{
Stream stream = TitleContainer.OpenStream("sounds/Ding.wav");
SoundEffect effect = SoundEffect.FromStream(stream);
FrameworkDispatcher.Update();
effect.Play();
// Wait a while before playing again.
Thread.Sleep(500);
}
SoundEffect supports multiple (up to 16 I think) SoundEffectInstance being played simultaneously.
The standard MediaElement API only supports 1 audio stream for Windows Phone 8.1.
I bumped into this: https://github.com/rajenki/audiohelper which uses the XAudio2 API but it doesn't seem to support simultaneous audio either.

Solved. I used SharpDX. Huge thanks to the author here: http://www.hoekstraonline.net/2013/01/13/how-to-play-a-wav-sound-file-with-directx-in-c-for-windows-8/?utm_source=rss&utm_medium=rss&utm_campaign=how-to-play-a-wav-sound-file-with-directx-in-c-for-windows-8
Here is the code to the solution:
Initialization:
xAudio = new XAudio2();
var masteringVoice = new MasteringVoice(xAudio);
var nativeFileStream = new NativeFileStream("Assets/Ding.wav", NativeFileMode.Open, NativeFileAccess.Read, NativeFileShare.Read);
stream = new SoundStream(nativeFileStream);
waveFormat = stream.Format;
buffer = new AudioBuffer
{
Stream = stream.ToDataStream(),
AudioBytes = (int)stream.Length,
Flags = BufferFlags.EndOfStream
};
Event handler:
var sourceVoice = new SourceVoice(xAudio, waveFormat, true);
sourceVoice.SubmitSourceBuffer(buffer, stream.DecodedPacketsInfo);
sourceVoice.Start();
The officially provided code by SharpDX's sample does not use NativeFileStream, it is required to make it work.

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();
}
};

C# Windows Universal App MediaElement playing AAC-LC/m4a audio data

I am using MediaCapture class to record voice in aac-lc/m4a format. I am trying to play same audio data on MediaElement. It is not playing the m4a audio. However if i record the voice in 'WAV' format, MediaElement is able to play the audio without any issues. I tried with all possible MIME Types for aac-lc/m4a audio.
Here is the player code:
var inMemoryRas = new InMemoryRandomAccessStream();
var writeStream = inMemoryRas.AsStreamForWrite();
await writeStream.WriteAsync(audioData, 0, audioData.Length);
await writeStream.FlushAsync();
inMemoryRas.Seek(0);
mMediaPlayer_.AudioCategory = AudioCategory.ForegroundOnlyMedia;
String mimeType_ = "";
mMediaPlayer_.SetSource(inMemoryRas, mimeType_);
mMediaPlayer_.AutoPlay = true;
mMediaPlayer_.Play();
I tried with following set of mime types, still no help.
audio/mpeg, audio/mp4, audio/aac, video/mp4, audio/m4a
Again if i record the audio in wav format it plays without any issues.
Here is the recorder code:
MediaEncodingProfile recordProfile = null;
recordProfile = MediaEncodingProfile.CreateM4a(Windows.Media.MediaProperties.AudioEncodingQuality.Low);
mRecordingStream_ = new InMemoryRandomAccessStream();
await m_mediaCaptureMgr.StartRecordToStreamAsync(recordProfile, mRecordingStream_);
I appreciate any help on this, Thanks in advance.

WP8/VS2013 MediaLibrary Songs collection is empty

I'm having a play with writing a mp3 player app for WP8, using MediaLibrary to handle the phone's own mp3 collection.
I want to test the result in the phone emulator on VS2013, but when I use the following code:
using (MediaLibrary library = new MediaLibrary())
{
SongCollection songs = library.Songs;
Song song = songs[0];
MediaPlayer.Play(song);
}
The song collection is empty, presumably because VS doesn't have any knowledge of a media library with songs in.
Is there any way to test this in the emulator using a fake medialibrary or for VS to use windows' media library? I just want to see (or hear) the code working before I proceed :)
I have managed to find a workaround!
If you add an mp3 file to the app's assets, the following code will add the mp3 to the media player library:
private void AddSong()
{
Uri file = new Uri("Assets/someSong.mp3", UriKind.Relative);
//copy file to isolated storage
var myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication();
var fileStream = myIsolatedStorage.CreateFile("someSong.mp3");
var resource = Application.GetResourceStream(file);
int chunkSize = 4096;
byte[] bytes = new byte[chunkSize];
int byteCount;
while ((byteCount = resource.Stream.Read(bytes, 0, chunkSize)) > 0)
{
fileStream.Write(bytes, 0, byteCount);
}
fileStream.Close();
Microsoft.Xna.Framework.Media.PhoneExtensions.SongMetadata metaData = new Microsoft.Xna.Framework.Media.PhoneExtensions.SongMetadata();
metaData.AlbumName = "Some Album name";
metaData.ArtistName = "Some Artist Name";
metaData.GenreName = "test";
metaData.Name = "someSongName";
var ml = new MediaLibrary();
Uri songUri = new Uri("someSong.mp3", UriKind.RelativeOrAbsolute);
var song = Microsoft.Xna.Framework.Media.PhoneExtensions.MediaLibraryExtensions.SaveSong(ml, songUri, metaData, Microsoft.Xna.Framework.Media.PhoneExtensions.SaveSongOperation.CopyToLibrary);
}
I also needed to add:
using System.IO.IsolatedStorage;
I would love to claim credit for this, but I found the answer here:
http://social.msdn.microsoft.com/forums/wpapps/en-US/f5fa73da-176b-4aaa-8960-8f704236bda5/medialibrary-savesong-method
By default the media library on the emulator is empty. I also do not think it is possible to automagically hook up your dev machine's music folder to the emulator to test that way. It might be possible to manually configure the emulated phone with an email account! and save music onto it that way, but even if that worked you'd have to do it each and every time you restart the emulator.
Best way to test would be t deploy to a real device.

Is there a way to Play a WAV File Synchronously in NAudio?

I might be wrong on this, but it appears that my WAV file in NAudio is playing asynchronously, and if I call it from a library, I can hear the WAV start, but it exits (and stops playing) almost immediately.
If I run it from a Form (which stays open) it is fine. But if I try to run it from code as a background thread, I only hear a small fragment in the beginning. It SEEMS like the method is exiting before the sound is finished playing.
I am using the Text To Speech Synthesizer to create the WAV on a Memory stream:
SpeechSynthesizer m_speechSynth = new SpeechSynthesizer();
m_speechSynth.Volume = 100;
m_speechSynth.Rate = 2;
MemoryStream waveStream = new MemoryStream();
m_speechSynth.SetOutputToWaveStream(waveStream);
m_speechSynth.Speak(txtToRead);
m_speechSynth.SetOutputToNull();
waveStream.Position = 0; // reset counter to start
That works fine. I then create a class that I wrapped around NAudio:
VoiceThroughNetAudio netAudio = new VoiceThroughNetAudio(waveStream, "WAV");
I'm trying to keep the code sample here easy to follow... but what this class does is simply initializes things, and gives me a higher level of control to do the main things on an NAudio player... Inside the VoiceThroughNetAudio constructor,
First I create a reader Stream from the MemStream:
readerStream = new WaveFileReader(memStream);
readerStream = WaveFormatConversionStream.CreatePcmStream(readerStream);
readerStream = new BlockAlignReductionStream(readerStream);
And attach a Volume Control (so I can amplify it):
_volumeStream = new WaveChannel32(Reader);
This all works fine when I call it from my WinForm Code:
MemoryStream waveStream = new MemoryStream();
ReadStringAmplified(readTextField.Text, waveStream); // TTS to waveStream
VoiceThroughNetAudio PlaySound = new VoiceThroughNetAudio (waveStream, "WAV");
PlaySound.Volume = 9;
PlaySound.Play();
But when I move this code into the Voice class instead, the sound STARTS to play, and then stops (almost immediately). Like it is starting an event and then exiting before the sound finishes? If so, is there a good way of making the method wait until the sound is finished? (I tried putting a while loop in and check the state, but this just ends up going into the while loop for an infinite period (commented out here...)
Here is how the code looks when I move it to the Voice Class. If I instantiate it from the form, and run it all there, and I get the truncated sound:
SpeechSynthesizer m_speechSynth = new SpeechSynthesizer();
m_speechSynth.Volume = 100;
m_speechSynth.Rate = 2;
MemoryStream waveStream = new MemoryStream();
m_speechSynth.SetOutputToWaveStream(waveStream);
m_speechSynth.Speak(txtToRead);
m_speechSynth.SetOutputToNull();
waveStream.Position = 0; // reset counter to start
VoiceThroughNetAudio netAudio = new VoiceThroughNetAudio(waveStream, "WAV");
netAudio.Volume = 9;
TimeSpan ts = netAudio.TotalDuration;
string len = ts.Milliseconds.ToString();
netAudio.Play();
//VoiceThroughNetAudio.PlayBackState chkState = netAudio.State;
//while (netAudio.State != VoiceThroughNetAudio.PlayBackState.Stopped)
//{
// chkState = netAudio.State;
//}
It seems like I am missing something simple, but I can't seem to get this to work in the Voice Class, only from the Form.
Most likely explanations for your problem are:
You are trying to play from a MemoryStream that is still being written to
Your output device has gone out of scope and been garbage collected while it is still playing
Try subscribing to the PlaybackStopped event of your IWavePlayer to see when and why it is firing (it can contain an exception if there is a problem).
Also, I very much doubt you need a WaveFormatConversionStream and a BlockAlignReductionStream, so simplify your code by removing those.

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