Using Vorbis and NAudio to play OGG files in C# - c#

I'm trying to play OGG file stream with NVorbis and NAudio, as described in the documention, I'm trying to run this code when on a Button click, but I get an exception:
System.ArgumentException: 'Could not initialize container!'
I'm targeting .Net Framework 4.5
Here is my code:
private void button1_Click(object sender, EventArgs e)
{
using (var vorbisStream = new NAudio.Vorbis.VorbisWaveReader(#"OGG file path"))
using (var waveOut = new NAudio.Wave.WaveOutEvent())
{
waveOut.Init(vorbisStream);
waveOut.Play();
// wait here until playback stops or should stop
}
}

Two issues here:
The first is that you cannot declare with using statements either the WaveOutReader or WaveOut object, since the latter is playing the sound asynchronously and it needs the WaveOutReader's Stream open
You were targeting .Net Framework 4.5, which is not suitable to handle the current version of both NAudio and NVorbis
I assume you have already successfully installed the NAudio 2.1.0 and NAudio.Vorbis 1.5.0 packages in a new .Net 6+ Project.
You just need to add references to NAudio.Vorbis and NAudio.Wave
Targeting .Net 6+, nullable enabled.
Add two Button Controls to a Form, one named btnPlayAudio and the other btnStopPlayback, initially disabled; subscribe to their Click event using the event handlers shown here.
The WaveOutReader and the WaveOut object that acts as player are declared as instance Fields, initially set to null.
When you press the Play Audio Button, the audio Stream and the Player are initialized, calling the WaveOutInit() method, which also subscribes to the WaveOut object's PlaybackStopped event.
When you call the WaveOut object's Stop() method or the OGG playback terminates, this event is raised. Here, the Stop() method is called in the btnStopPlayback click handler.
When this happens, another method, WaveOutReset(), is called; this method disposes of the WaveOut object and the Stream of the WaveOutReader. The event handler is also removed.
using NAudio.Vorbis;
using NAudio.Wave;
public partial class MainForm : Form {
private VorbisWaveReader? vorbis = null;
private WaveOut? oggPlayer = null;
private void btnPlayAudio_Click(object sender, EventArgs e)
{
btnPlayAudio.Enabled = false;
btnStopPlayback.Enabled = true;
vorbis = new VorbisWaveReader("OGG File path"));
oggPlayer = WaveOutInit(vorbis);
oggPlayer.Play();
}
private void btnStopPlayback_Click(object sender, EventArgs e)
{
oggPlayer?.Stop();
btnStopPlayback.Enabled = false;
}
private void WaveOut_PlaybackStopped(object? sender, StoppedEventArgs e)
{
WaveOutReset(oggPlayer, vorbis);
btnPlayAudio.Enabled = true;
}
private WaveOut WaveOutInit(IWaveProvider reader)
{
var waveOut = new WaveOut();
waveOut.PlaybackStopped += WaveOut_PlaybackStopped;
waveOut.Init(reader);
return waveOut;
}
private void WaveOutReset(WaveOut? player, VorbisWaveReader? reader)
{
if (player != null) {
player.PlaybackStopped -= WaveOut_PlaybackStopped;
player.Dispose();
}
reader?.Dispose();
}
}

Related

MediaPlayer is not looping/repeating the audio provided

I am using System.Windows.Media.MediaPlayer for playing .wma audio file of 5 seconds length .
I was not able to find any direct option for repeatedly playing this media file so I have implemented looping logic as follows. But it seems to be not working.
For the following code, MediaEnded event is not getting fired even after playback is ended. What am I doing wrong here?
public void PlayAudio(string audioFilePath, TimeSpan duration)
{
var thread = new Thread(() => { PlayAudioThreadProc(audioFilePath, duration); });
thread.Start();
}
private void PlayAudioThreadProc(string audioFilePath, TimeSpan duration)
{
MediaPlayer mediaPlayer = CreateMediaPlayer(audioFilePath);
mediaPlayer.Play();
_stopSignal.WaitOne(duration);
Stop(mediaPlayer);
}
private static MediaPlayer CreateMediaPlayer(string audioFilePath)
{
var mediaPlayer = new MediaPlayer();
mediaPlayer.MediaEnded += MediaPlayer_MediaEnded; //This event is not getting fired.
mediaPlayer.Open(new Uri(audioFilePath));
return mediaPlayer;
}
private static void MediaPlayer_MediaEnded(object sender, EventArgs e)
{
//This part of code is supposed to start the media again after it is ended playing.
var player = (MediaPlayer)sender;
player.Position = TimeSpan.Zero;
player.Play();
}
private static void Stop(MediaPlayer mediaPlayer)
{
mediaPlayer.Stop();
mediaPlayer.MediaEnded -= MediaPlayer_MediaEnded;
mediaPlayer.Close();
}
Looping logic in above code is not working.
If above approach is not possible, please recommend me another audio player which supports Volume adjustments and Repeat media option. (I tried System.Media.SoundPlayer but it does not support Volume adjustments and .wma files are also not supported in it.)
First, you seem to be using the MediaPlayer class incorrectly. It inherits DispatcherObject and also is not blocking, so it should really be used on the UI thread.
Now on the main subject.
I was not able to find any direct option for repeatedly playing this media file
Well, actually it supports everything you need except the total play duration time. But you are right - it's not so direct (as most of the WPF stuff) - the repeating is achieved by using MediaTimeline through RepeatBehavior property. You can find sample usage in How to: Play Media using a VideoDrawing. So the basic playing code with repeating is like this:
var timeLine = new MediaTimeline(new Uri(audioFilePath));
timeLine.RepeatBehavior = RepeatBehavior.Forever;
var mediaPlayer = new MediaPlayer();
mediaPlayer.Clock = timeLine.CreateClock();
mediaPlayer.Clock.Controller.Begin();
You create MediaTimeline object, set properties (use RepeatBehavior.Forever to get indefinite repeating, but you can also use the constructor and specify concrete count), then create MediaClock from it and assign it to the MediaPlayer.Clock property. Make sure to read the documentation, because in this mode you should not use Position property and Play, Pause and Stop methods of the MediaPlayer class, but the Clock.Controller methods.
The MediaTimeline also has a property Duration, but it allows you (along with the BeginTime property) to select a portion of the audio file to be played, hence cannot be used to set up the total play duration. So the play time duration problem should be solved in a separate way.
The easiest way I see to support what you need, along with stop function, is to use async method:
public async Task PlayAudioAsync(string audioFilePath, TimeSpan duration, CancellationToken cancellationToken)
{
var timeLine = new MediaTimeline(new Uri(audioFilePath));
timeLine.RepeatBehavior = RepeatBehavior.Forever;
var mediaPlayer = new MediaPlayer();
mediaPlayer.Clock = timeLine.CreateClock();
mediaPlayer.Clock.Controller.Begin();
try
{
await Task.Delay(duration, cancellationToken);
}
finally
{
mediaPlayer.Clock.Controller.Stop();
}
}
Task.Delay will give you the desired play duration, and CancellationToken - the stop functionality.
Jus make sure to call it from UI thread. Here is a sample usage:
XAML:
<Canvas>
<Button x:Name="playButton" Content="Play" Width="75" RenderTransformOrigin="1.2,5.24" Canvas.Left="98" Canvas.Top="135" Click="playButton_Click"/>
<Button x:Name="stopButton" Content="Stop" IsEnabled="False" Width="75" Canvas.Left="208" Canvas.Top="135" Click="stopButton_Click"/>
</Canvas>
Code behind:
private CancellationTokenSource ctsPlay;
private async void playButton_Click(object sender, RoutedEventArgs e)
{
string audioFile = ...;
TimeSpan duration = ...;
ctsPlay = new CancellationTokenSource();
playButton.IsEnabled = false;
stopButton.IsEnabled = true;
try
{
await PlayAudioAsync(audioFile, duration, ctsPlay.Token);
}
catch
{
}
ctsPlay.Dispose();
ctsPlay = null;
stopButton.IsEnabled = false;
playButton.IsEnabled = true;
}
private void stopButton_Click(object sender, RoutedEventArgs e)
{
ctsPlay.Cancel();
}
MediaPlayers "Play" is not thread locking. the thread ends execution as soon as the sound starts playing. I've setup a local class testing this out and I get the event to fire (On a background thread) like this (I've changed it to be OOP, not a statically used class, you have to call Stop from somewhere else):
public class MediaStuff
{
private bool _closing = false;
public void PlayAudio(string audioFilePath)
{
var thread = new Thread(() => { PlayAudioThreadProc(audioFilePath); });
thread.Start();
}
private void PlayAudioThreadProc(string audioFilePath)
{
MediaPlayer mediaPlayer = CreateMediaPlayer(audioFilePath);
mediaPlayer.Play();
while (!_closing)
{
System.Threading.Thread.Sleep(10);
Dispatcher.Run();
}
mediaPlayer.Stop();
mediaPlayer.MediaEnded -= MediaPlayer_MediaEnded;
mediaPlayer.Close();
}
private MediaPlayer CreateMediaPlayer(string audioFilePath)
{
var mediaPlayer = new MediaPlayer();
mediaPlayer.MediaEnded += MediaPlayer_MediaEnded; //This event is not getting fired.
mediaPlayer.Open(new Uri(Path.GetFullPath(audioFilePath)));
return mediaPlayer;
}
private void MediaPlayer_MediaEnded(object sender, EventArgs e)
{
//This part of code is supposed to start the media again after it is ended playing.
var player = (MediaPlayer)sender;
player.Position = TimeSpan.Zero;
player.Play();
}
public void Stop()
{
_closing = true;
}
}
I too had your problem when testing your code. After changing and testing my code, the event now fires, and the sound loops.
Alternatively, you can use mciSendString(). Tried to make an example and it succeed, try this;
winmm.dll import,
[DllImport("winmm.dll")]
private static extern long mciSendString( string command, string returnValue,
int returnLength, IntPtr winHandle);
Need to catch operations done by mciSendString(), so we need WndProc;
private const int MM_MCINOTIFY = 0x03b9; // notify mci completed operation
private const int MCI_NOTIFY_SUCCESS = 0x01; // mci successfully executed command
protected override void WndProc(ref Message m)
{
if (m.Msg == MM_MCINOTIFY)
{
switch (m.WParam.ToInt32())
{
case MCI_NOTIFY_SUCCESS: // if successfull
if (IsLoop) // check if we gave parameter true
PlayMediaFile(IsLoop); // so it should run forever, call again function
break;
default:
break;
}
}
base.WndProc(ref m);
}
Method that executes, media file by using mciSendString() that we imported at the beginning
private bool IsLoop = false; // need this to check inside WndProc
private void PlayMediaFile(bool RepeatForever)
{
IsLoop = RepeatForever;
mciSendString("close voice1", null, 0, this.Handle); // first close, after first run, the previous opened file should be terminated
mciSendString("stop voice1", null, 0, this.Handle); // close file
string playCommand = "open " + "yourFilePath" + " type waveaudio alias voice1"; // open command string
mciSendString(playCommand, null, 0, this.Handle); // open file
mciSendString("play voice1 notify", null, 0, this.Handle); // play file
}
Then call the method by giving parameter anywhere.
Hope helps,

Sharing kinect stream data between parallel threads

The problem I'm trying to solve is that I'm combining classes from different projects into a single project. The functions in these classes use KinectSensor variable which is a member of their class and recognize different gestures. I'm trying to run the gesture recognition functions in parallel threads but want to know how to pass kinect data to them.
Is there a method to share kinect sensor stream data (Color stream, depth stream etc.) data among threads of a single program running in parallel? Will a event triggered like "skeleton frame ready" be received by all the threads receiving the kinect stream data?
I'm using kinect 360, in c# using sdk 1.8
The KinectSensor object represents a single Kinect, and a single Kinect can only be used by a single program.
If the two thread you are talking about are part of the same program, you can "share the streams" by simply share the access to the same object.
However, the color, depth and skeleton streams are obtained by registering callbacks methods to an event. So what you can do is the following:
share the reference to the same KinectSensor object (preinitialized) to both threads;
in each thread, register a callback to the depth, color and skeleton stream, which update the content of the streams in a variable that is local to the thread (or used only by one thread):
// Reference to the single KinectSensor object
private KinectSensor kinectSensor;
// Local variables with depth, color and skeletal information
private Skeleton[] skeleton_thread1;
private Skeleton[] skeleton_thread2;
private short[] depth_thread1;
private short[] depth_thread2;
private byte[] color_thread1;
private byte[] color_thread2;
// ...
// Register callbacks (you must so this both in thread1 and thread2)
// Assume that here we are refererring to thread1
kinectSensor.ColorFrameReady += new EventHandler<ColorFrameReadyEventArgs>(kinectSensor_ColorFrameReady1);
kinectSensor.DepthFrameReady += new EventHandler<DepthFrameReadyEventArgs>(kinectSensor_DepthFrameReady1);
kinectSensor.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(kinectSensor_SkeletonFrameReady1);
// ...
private void kinectSensor_SkeletonFrameReady1(object sender, SkeletonFrameReadyEventArgs e)
{
this.skeletonFrame_thread1 =
using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())
{
if (skeletonFrame != null)
{
this.skeleton_thread1 = new Skeleton[skeletonFrame.SkeletonArrayLength];
skeletonFrame.CopySkeletonDataTo(this.skeleton_thread1);
// Do stuff
}
else
{
// Do stuff
}
}
}
private void kinectSensor_ColorFrameReady1(object sender, ColorImageFrameReadyEventArgs e)
{
using (ColorImageFrame colorImageFrame = e.OpenColorImageFrame())
{
if (colorImageFrame != null)
{
this.color_thread1 = new byte[colorImageFrame.PixelDataLength];
colorImageFrame.CopyPixelDataTo(this.color_thread1);
// Do Stuff
}
else
{
// Do stuff
}
}
}
private void kinectSensor_DepthFrameReady1(object sender, DepthImageFrameReadyEventArgs e)
{
using (DepthImageFrame depthImageFrame = e.OpenDepthImageFrame())
{
if (depthImageFrame != null)
{
this.depth_thread1 = new short[depthImageFrame.PixelDataLength];
depthImageFrame.CopyPixelDataTo(this.depth_thread1);
// Do Stuff
}
else
{
// Do stuff
}
}
}

Streaming mp3 file from external server

I am developing a Windows Phone 8 application. I need to stream a .mp3 sound file from a remote server in my application.
I have tried to use MediaElement:
private MediaElement media;
// Constructor of class
media = new MediaElement();
media.Source = new Uri(string.Format("{0}b10en_US.mp3", mp3HostName), UriKind.Absolute);
media.MediaFailed += media_MediaFailed;
media.MediaEnded += media_MediaEnded;
media.MediaOpened += media_MediaOpened;
media.Loaded += media_Loaded;
media.BufferingProgressChanged += media_BufferingProgressChanged;
// In a method I call the following
media.play();
However no sound gets played whatsoever. I added breakpoints to the mediaelement's events, but none gets fired.
I have double checked, the URI to the mp3 file is correct.
What am I doing wrong?
You can play mp3 after media opened event is raised. I think your method(in which you are calling play is called before media opened event is raised.
you can implement some hack in mediaOpened event and your method(play). like
private bool isMediaLoaded = false;
private bool isPlayCalled = false;
private void PlayMP3()
{
if(isMediaLoaded)
media.Play();
else
isPlayCalled = true;
}
void MediaElement1_MediaOpened(object sender, RoutedEventArgs e)
{
isMediaLoaded = true;
if(isPlayCalled)
MediaElement1.Play();
}
Try this :
string url = "http://traffic.libsyn.com/slashfilmcast/Davidmichod.mp3";//your url link
// Constructor
public MainPage()
{
InitializeComponent();
Microsoft.Xna.Framework.FrameworkDispatcher.Update();
Song track = Song.FromUri("Sample Song", new Uri(url));
MediaPlayer.Play(track);
}
using Microsoft.Xna.Framework.Media; //for getting MediaPlayer

Continuous record and play audio in C# visual studio

I have been trying to record and play the audio simultaneously with out using a temporary wav file. And later I would like to create a VOIP chat program.
I have used the Naudio library to capture and play audio in C# and it seems to work quite well.
below is the c # code that i have written:
using System.IO.Ports;
using NAudio.Wave;
using System.IO;
namespace VOIP
{
public partial class Form1 : Form
{
WaveIn wab = new WaveIn();
MemoryStream s;
int k;
public Form1()
{
InitializeComponent();
wab.BufferMilliseconds = 100;
wab.NumberOfBuffers=5;
wab.DataAvailable += new EventHandler<WaveInEventArgs>(wa_DataAvailable);
}
void wa_DataAvailable(object sender, WaveInEventArgs e)
{
Play(e.Buffer);
}
private void Play(byte[] p)
{
WaveOut ou = new WaveOut();
s = new MemoryStream(p);
RawSourceWaveStream r = new RawSourceWaveStream(s, wab.WaveFormat);
ou.Init(r);
ou.Play();
ou.Stop();
ou.Dispose();
s.Dispose();
r.Dispose();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text == "Stop")
{
wab.StopRecording();
button1.Text = "Record";
}
else if (button1.Text == "Record")
{
wab.StartRecording();
button1.Text = "Stop";
}
}
}
}
The Problem is at the "Play" method .Since the waveout object is created and disposed every time the data is available : i can hear some clicking sound. Is there a way to avoid this way of creating and disposing object and instead just create one object and then initialize with the new data. I also observed that the memory consumed by this program keeps increasing.
Thanks in advance.
sanatan
Use a BufferedWaveProvider, and as audio arrives, decompress it and add it to the BufferedWaveProvider. Then have a single instance of WaveOut that is playing constantly from the BufferedWaveProvider.
The source code for NAudioDemo shows how to do this in the chat example.

Playing multiple sounds using muliple buttons

How to make multiple buttons play different sounds one at a time and have a common stop button using Windows Phone XNA framework? When a sound is played, it should play looped until one hits the stop button or hits another button.
The way I used SoundEffect with CreateInstance, it looped and played fine but when a second button is clicked, the second sound starts playing along with the first. Also need help with creating the common stop button. Thanks a lot in advance.
I was trying something like below for each button.
private void button2_Click(object sender, RoutedEventArgs e)
{
var stream = TitleContainer.OpenStream("Sounds/A3.wav");
var effect = SoundEffect.FromStream(stream);
SoundEffectInstance instance = effect.CreateInstance();
instance.IsLooped = true;
instance.Play();
But since the instance created isn't on a program-wide level, I'm having trouble creating a common stop button.
I'm a beginner in programming. Thank you for your understanding.
You can add a member variable to your class and some helper methods:
public class YourClass
{
private SoundEffectInstance currentSoundEffect = null;
private void StopCurrentSoundEffect()
{
this.currentSoundEffect.Stop();
this.currentSoundEffect = null;
}
private void PlaySoundEffect(string fileName)
{
this.StopCurrentSoundEffect();
using (var stream = TitleContainer.OpenStream("Sounds/A3.wav"))
{
var soundEffect = SoundEffect.FromStream(stream);
this.currentSoundEffect = soundEffect.CreateInstance();
this.currentSoundEffect.IsLooped = true;
this.currentSoundEffect.Play();
}
}
}
Now, each of your event handlers can just call this.PlaySoundEffect with the desired file name.

Categories