I am using Naudios Demo app to demostrait a litle bit. I found out that when i play some mp3 file, in the end om the sound/mp3 i get a
error nullreferenceexception was unhandled
nullreferenceexception object reference not set to an instance of an
object
in the file Mp3FrameDecompressor.cs
using System;
using System.Collections.Generic;
using System.Text;
using NAudio.Wave;
using NAudio.Wave.Compression;
using System.Diagnostics;
namespace NAudio.Wave
{
/// <summary>
/// MP3 Frame Decompressor using ACM
/// </summary>
public class AcmMp3FrameDecompressor : IDisposable, IMp3FrameDecompressor
{
private AcmStream conversionStream;
private WaveFormat pcmFormat;
/// <summary>
/// Creates a new ACM frame decompressor
/// </summary>
/// <param name="sourceFormat">The MP3 source format</param>
public AcmMp3FrameDecompressor(WaveFormat sourceFormat)
{
this.pcmFormat = AcmStream.SuggestPcmFormat(sourceFormat);
conversionStream = new AcmStream(sourceFormat, pcmFormat);
}
/// <summary>
/// Output format (PCM)
/// </summary>
public WaveFormat OutputFormat { get { return pcmFormat; } }
/// <summary>
/// Decompresses a frame
/// </summary>
/// <param name="frame">The MP3 frame</param>
/// <param name="dest">destination buffer</param>
/// <param name="destOffset">Offset within destination buffer</param>
/// <returns>Bytes written into destination buffer</returns>
public int DecompressFrame(Mp3Frame frame, byte[] dest, int destOffset)
{
Array.Copy(frame.RawData, conversionStream.SourceBuffer, frame.FrameLength);
int sourceBytesConverted = 0;
int converted = conversionStream.Convert(frame.FrameLength, out sourceBytesConverted);
if (sourceBytesConverted != frame.FrameLength)
{
throw new InvalidOperationException(String.Format("Couldn't convert the whole MP3 frame (converted {0}/{1})",
sourceBytesConverted, frame.FrameLength));
}
Array.Copy(conversionStream.DestBuffer, 0, dest, destOffset, converted);
return converted;
}
/// <summary>
/// Disposes of this MP3 frame decompressor
/// </summary>
public void Dispose()
{
if (this.conversionStream != null)
{
this.conversionStream.Dispose();
this.conversionStream = null;
}
}
}
}
This is the Form i use
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using NAudio.Wave;
using System.Net;
using System.Threading;
using System.Net.Sockets;
using System.IO;
using System.Diagnostics;
using System.ComponentModel.Composition;
namespace NAudioDemo
{
public partial class MP3StreamingPanel : Form
{
public MP3StreamingPanel()
{
InitializeComponent();
this.volumeSlider1.VolumeChanged += new EventHandler(volumeSlider1_VolumeChanged);
this.Disposed += this.MP3StreamingPanel_Disposing;
}
enum StreamingPlaybackState
{
Stopped,
Playing,
Buffering,
Paused
}
void volumeSlider1_VolumeChanged(object sender, EventArgs e)
{
if (this.volumeProvider != null)
{
this.volumeProvider.Volume = this.volumeSlider1.Volume;
}
}
private BufferedWaveProvider bufferedWaveProvider;
private IWavePlayer waveOut;
private volatile StreamingPlaybackState playbackState;
private volatile bool fullyDownloaded;
private HttpWebRequest webRequest;
private VolumeWaveProvider16 volumeProvider;
delegate void ShowErrorDelegate(string message);
private void ShowError(string message)
{
if (this.InvokeRequired)
{
this.BeginInvoke(new ShowErrorDelegate(ShowError), message);
}
else
{
MessageBox.Show(message);
}
}
private void StreamMP3(object state)
{
this.fullyDownloaded = false;
string url = (string)state;
webRequest = (HttpWebRequest)WebRequest.Create(url);
HttpWebResponse resp = null;
try
{
resp = (HttpWebResponse)webRequest.GetResponse();
}
catch(WebException e)
{
if (e.Status != WebExceptionStatus.RequestCanceled)
{
ShowError(e.Message);
}
return;
}
byte[] buffer = new byte[16384 * 4]; // needs to be big enough to hold a decompressed frame
IMp3FrameDecompressor decompressor = null;
try
{
using (var responseStream = resp.GetResponseStream())
{
var readFullyStream = new ReadFullyStream(responseStream);
do
{
if (bufferedWaveProvider != null && bufferedWaveProvider.BufferLength - bufferedWaveProvider.BufferedBytes < bufferedWaveProvider.WaveFormat.AverageBytesPerSecond / 4)
{
Debug.WriteLine("Buffer getting full, taking a break");
Thread.Sleep(500);
}
else
{
Mp3Frame frame = null;
try
{
frame = Mp3Frame.LoadFromStream(readFullyStream);
}
catch (EndOfStreamException)
{
this.fullyDownloaded = true;
// reached the end of the MP3 file / stream
break;
}
catch (WebException)
{
// probably we have aborted download from the GUI thread
break;
}
if (decompressor == null)
{
// don't think these details matter too much - just help ACM select the right codec
// however, the buffered provider doesn't know what sample rate it is working at
// until we have a frame
WaveFormat waveFormat = new Mp3WaveFormat(frame.SampleRate, frame.ChannelMode == ChannelMode.Mono ? 1 : 2, frame.FrameLength, frame.BitRate);
decompressor = new AcmMp3FrameDecompressor(waveFormat);
this.bufferedWaveProvider = new BufferedWaveProvider(decompressor.OutputFormat);
this.bufferedWaveProvider.BufferDuration = TimeSpan.FromSeconds(20); // allow us to get well ahead of ourselves
//this.bufferedWaveProvider.BufferedDuration = 250;
}
int decompressed = decompressor.DecompressFrame(frame, buffer, 0);
//Debug.WriteLine(String.Format("Decompressed a frame {0}", decompressed));
bufferedWaveProvider.AddSamples(buffer, 0, decompressed);
}
} while (playbackState != StreamingPlaybackState.Stopped);
Debug.WriteLine("Exiting");
// was doing this in a finally block, but for some reason
// we are hanging on response stream .Dispose so never get there
decompressor.Dispose();
}
}
finally
{
if (decompressor != null)
{
decompressor.Dispose();
}
}
}
private void buttonPlay_Click(object sender, EventArgs e)
{
if (playbackState == StreamingPlaybackState.Stopped)
{
playbackState = StreamingPlaybackState.Buffering;
this.bufferedWaveProvider = null;
ThreadPool.QueueUserWorkItem(new WaitCallback(StreamMP3), textBoxStreamingUrl.Text);
timer1.Enabled = true;
}
else if (playbackState == StreamingPlaybackState.Paused)
{
playbackState = StreamingPlaybackState.Buffering;
}
}
private void StopPlayback()
{
if (playbackState != StreamingPlaybackState.Stopped)
{
if (!fullyDownloaded)
{
webRequest.Abort();
}
this.playbackState = StreamingPlaybackState.Stopped;
if (waveOut != null)
{
waveOut.Stop();
waveOut.Dispose();
waveOut = null;
}
timer1.Enabled = false;
// n.b. streaming thread may not yet have exited
Thread.Sleep(500);
ShowBufferState(0);
}
}
private void ShowBufferState(double totalSeconds)
{
}
private void timer1_Tick(object sender, EventArgs e)
{
if (playbackState != StreamingPlaybackState.Stopped)
{
if (this.waveOut == null && this.bufferedWaveProvider != null)
{
Debug.WriteLine("Creating WaveOut Device");
this.waveOut = CreateWaveOut();
waveOut.PlaybackStopped += waveOut_PlaybackStopped;
this.volumeProvider = new VolumeWaveProvider16(bufferedWaveProvider);
this.volumeProvider.Volume = this.volumeSlider1.Volume;
waveOut.Init(volumeProvider);
progressBarBuffer.Maximum = (int)bufferedWaveProvider.BufferDuration.TotalMilliseconds;
}
else if (bufferedWaveProvider != null)
{
var bufferedSeconds = bufferedWaveProvider.BufferedDuration.TotalSeconds;
ShowBufferState(bufferedSeconds);
// make it stutter less if we buffer up a decent amount before playing
if (bufferedSeconds < 0.5 && this.playbackState == StreamingPlaybackState.Playing && !this.fullyDownloaded)
{
this.playbackState = StreamingPlaybackState.Buffering;
waveOut.Pause();
Debug.WriteLine(String.Format("Paused to buffer, waveOut.PlaybackState={0}", waveOut.PlaybackState));
}
else if (bufferedSeconds > 1 && this.playbackState == StreamingPlaybackState.Buffering)
{
waveOut.Play();
Debug.WriteLine(String.Format("Started playing, waveOut.PlaybackState={0}", waveOut.PlaybackState));
this.playbackState = StreamingPlaybackState.Playing;
}
else if (this.fullyDownloaded && bufferedSeconds == 0)
{
Debug.WriteLine("Reached end of stream");
StopPlayback();
}
}
}
}
private IWavePlayer CreateWaveOut()
{
return new WaveOut();
//return new DirectSoundOut();
}
private void MP3StreamingPanel_Disposing(object sender, EventArgs e)
{
StopPlayback();
}
private void buttonPause_Click(object sender, EventArgs e)
{
if (playbackState == StreamingPlaybackState.Playing || playbackState == StreamingPlaybackState.Buffering)
{
waveOut.Pause();
Debug.WriteLine(String.Format("User requested Pause, waveOut.PlaybackState={0}", waveOut.PlaybackState));
playbackState = StreamingPlaybackState.Paused;
}
}
private void buttonStop_Click(object sender, EventArgs e)
{
StopPlayback();
}
private void waveOut_PlaybackStopped(object sender, StoppedEventArgs e)
{
Debug.WriteLine("Playback Stopped");
if (e.Exception != null)
{
MessageBox.Show(String.Format("Playback Error {0}", e.Exception.Message));
}
}
}
[Export(typeof(INAudioDemoPlugin))]
public class MP3StreamingPanelPlugin : INAudioDemoPlugin
{
public string Name
{
get { return "MP3 Streaming"; }
}
public Control CreatePanel()
{
return new MP3StreamingPanel();
}
}
}
i get the error on this line
Array.Copy(frame.RawData, conversionStream.SourceBuffer, frame.FrameLength);
Related
I am trying to use SetInputToAudioStream method for speech engine.
But it does not work.
I've also searched few articles and tried every possible way. but it still does not work.
https://stackoverflow.com/a/6203533/1336662
https://stackoverflow.com/a/6203533/1336662
I had to use the SpeechStreamer class described in Sean's response in order for the SpeechRecognitionEngine to work.
Here is my code, please if anyone can help me, that will be great.
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Speech.Recognition;
using System.Speech.AudioFormat;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Threading;
namespace WpfAppNAudio
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
console.AppendText("Click to start recording");
}
public WaveIn waveSource = null;
public WaveFileWriter waveFile = null;
private SpeechRecognitionEngine _recognizer = null;
//Stream a = null;
SpeechStreamer stream = null;
private bool _recognizing;
void RecognizeSpeechAndWriteToConsole()
{
_recognizer = new SpeechRecognitionEngine();
try
{
//_recognizer.
// Create and load a grammar.
Grammar dictation = new DictationGrammar();
dictation.Name = "Dictation Grammar";
_recognizer.LoadGrammar(dictation);
_recognizer.SpeechRecognized += _recognizer_SpeechRecognized; // if speech is recognized, call the specified method
_recognizer.SpeechRecognitionRejected += _recognizer_SpeechRecognitionRejected; // if recognized speech is rejected, call the specified method
_recognizer.SpeechDetected += _recognizer_SpeechDetected;
_recognizer.RecognizeCompleted += _recognizer_RecognizeCompleted;
}
catch (Exception)
{
}
}
private void _recognizer_RecognizeCompleted(object sender, RecognizeCompletedEventArgs e)
{
}
private void _recognizer_SpeechDetected(object sender, SpeechDetectedEventArgs e)
{
}
private void _recognizer_SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
{
console.AppendText("speech rejected");
}
private void _recognizer_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
console.AppendText("speech recognized" + e.Result.Text);
}
private void StartBtn_Click()
{
waveSource = new WaveIn();
waveSource.WaveFormat = new WaveFormat(22050, 8, 1);
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
waveFile = new WaveFileWriter(#"C:\Temp\Test0001.wav", waveSource.WaveFormat);
console.AppendText("Starting recording");
RecognizeSpeechAndWriteToConsole();
waveSource.StartRecording();
}
void StopBtn_Click(object sender, EventArgs e)
{
waveSource.StopRecording();
}
void waveSource_DataAvailable(object sender, WaveInEventArgs e)
{
if (waveFile != null)
{
stream = new SpeechStreamer(e.Buffer.Length);
stream.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Flush();
if (!_recognizing)
{
_recognizing = true;
_recognizer.SetInputToAudioStream(stream, new System.Speech.AudioFormat.SpeechAudioFormatInfo(22050, System.Speech.AudioFormat.AudioBitsPerSample.Eight, System.Speech.AudioFormat.AudioChannel.Mono));
var s = _recognizer.RecognizerInfo.SupportedAudioFormats;
_recognizer.RecognizeAsync(RecognizeMode.Multiple);
}
}
}
void waveSource_RecordingStopped(object sender, StoppedEventArgs e)
{
if (waveSource != null)
{
waveSource.Dispose();
waveSource = null;
}
if (waveFile != null)
{
waveFile.Dispose();
waveFile = null;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
StartBtn_Click();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
waveSource.StopRecording();
}
}
class SpeechStreamer : Stream
{
private AutoResetEvent _writeEvent;
private List<byte> _buffer;
private int _buffersize;
private int _readposition;
private int _writeposition;
private bool _reset;
public SpeechStreamer(int bufferSize)
{
_writeEvent = new AutoResetEvent(false);
_buffersize = bufferSize;
_buffer = new List<byte>(_buffersize);
for (int i = 0; i < _buffersize; i++)
_buffer.Add(new byte());
_readposition = 0;
_writeposition = 0;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return true; }
}
public override long Length
{
get { return -1L; }
}
public override long Position
{
get { return 0L; }
set { }
}
public override long Seek(long offset, SeekOrigin origin)
{
return 0L;
}
public override void SetLength(long value)
{
}
public override int Read(byte[] buffer, int offset, int count)
{
int i = 0;
while (i < count && _writeEvent != null)
{
if (!_reset && _readposition >= _writeposition)
{
_writeEvent.WaitOne(100, true);
continue;
}
buffer[i] = _buffer[_readposition + offset];
_readposition++;
if (_readposition == _buffersize)
{
_readposition = 0;
_reset = false;
}
i++;
}
return count;
}
public override void Write(byte[] buffer, int offset, int count)
{
for (int i = offset; i < offset + count; i++)
{
_buffer[_writeposition] = buffer[i];
_writeposition++;
if (_writeposition == _buffersize)
{
_writeposition = 0;
_reset = true;
}
}
_writeEvent.Set();
}
public override void Close()
{
_writeEvent.Close();
_writeEvent = null;
base.Close();
}
public override void Flush()
{
}
}
}
stream = new SpeechStreamer(e.Buffer.Length);
stream.Write(e.Buffer, 0, e.BytesRecorded);
Is this really the only place that is writing to the stream, no where else in the application? If it only writes some bytes when it initializes it will not continue to get audio coming in to the recognizer.
So I am designing an application for windows laptops to connect to a custom designed pressure sensor. The application pairs to the device and then receives notifications from the device every 10 ms. Then for some reason the communication stops. I know it is a problem with my application and not with the device, because when I connect to my phone, I do not have this problem.
Here is the main page where I create the devicewatcher and discover the device:
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.Devices.Bluetooth;
using Windows.Devices.Enumeration;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace BLEInterfaceTest
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
private DeviceWatcher deviceWatcher;
private ObservableCollection<DeviceInformation> deviceList = new ObservableCollection<DeviceInformation>();
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.DataContext = deviceList;
deviceListView.ItemsSource = deviceList;
deviceWatcher = DeviceInformation.CreateWatcher(
"System.ItemNameDisplay:~~\"Button\"",
new string[] {
"System.Devices.Aep.DeviceAddress",
"System.Devices.Aep.IsConnected" },
DeviceInformationKind.AssociationEndpoint);
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Removed += DeviceWatcher_Removed;
deviceWatcher.Start();
base.OnNavigatedTo(e);
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
AppViewBackButtonVisibility.Collapsed;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
deviceWatcher.Stop();
base.OnNavigatedFrom(e);
}
private async void DeviceWatcher_Removed(DeviceWatcher sender, DeviceInformationUpdate args)
{
var toRemove = (from a in deviceList where a.Id == args.Id select a).FirstOrDefault();
if (toRemove != null)
{
await this.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { deviceList.Remove(toRemove); });
}
}
private async void DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation args)
{
await this.Dispatcher.RunAsync(
Windows.UI.Core.CoreDispatcherPriority.Normal,
() => { deviceList.Add(args); });
}
private void deviceListView_ItemClick(object sender, ItemClickEventArgs e)
{
this.Frame.Navigate(typeof(DevicePage), e.ClickedItem);
}
}
}'
This next code is the page where the pressure sensor is connected to and where data is read from the device.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Popups;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Devices.Bluetooth;
using Windows.Devices.Enumeration;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.Storage.Streams;
using System.Threading.Tasks;
using Windows.ApplicationModel.Background;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238
namespace BLEInterfaceTest
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class DevicePage : Page
{
private DeviceInformation device { get; set; }
private PressureSensor pSensor { get; set; }
public static DateTime startTime { get; set; }
public ObservableCollection<DataPoint> PressureData = new ObservableCollection<DataPoint>();
public static ObservableCollection<DataPoint> inbetween;
private static TextBox txtP;
private BluetoothLEDevice leDevice;
private DispatcherTimer timer = new DispatcherTimer();
private int packetNum = 0;
public DevicePage()
{
this.InitializeComponent();
SystemNavigationManager.GetForCurrentView().BackRequested += DevicePage_BackRequested;
txtP = txtValue1;
inbetween = PressureData;
}
public static void ChangeText(string text)
{
txtP.Text = text;
}
private async void InitializePressureSensor(GattDeviceService service)
{
pSensor = new PressureSensor(service, SensorUUIDs.PressureSensorUuid);
await pSensor.EnableNotifications();
btnStart.IsEnabled = true;
}
private async void StartRecievingData()
{
try
{
leDevice = await BluetoothLEDevice.FromIdAsync(device.Id);
string selector = "(System.DeviceInterface.Bluetooth.DeviceAddress:=\"" +
leDevice.BluetoothAddress.ToString("X") + "\")";
var services = await leDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);
foreach (var service in services.Services)
{
if (service.Uuid.ToString() == SensorUUIDs.ButtonSensorServiceUuid)
{
InitializePressureSensor(service);
}
}
timer.Interval = new TimeSpan(0, 0, 0, 0, 1);
timer.Tick += Timer_Tick1;
startTime = DateTime.Now;
timer.Start();
}
catch (Exception ex)
{
var messageDialog = new MessageDialog("An error has occured Please try again. \n" + ex.Message, "Error!");
}
}
public async void UpdateAllData()
{
while (pSensor != null && pSensor.MorePacketsAvailable)
{
int[] values = await pSensor.GetPressure();
int packetNumber = values[0];
if (packetNumber > packetNum)
{
packetNum = packetNumber;
txtValue1.Text = Convert.ToString(values[1]);
txtValue2.Text = Convert.ToString(values[5]);
for (int i = 1; i < 5; i++)
{
PressureData.Add(new DataPoint(DateTime.Now - startTime, packetNumber, ((i-1)*2.5 + 10*packetNumber), values[i], values[i + 4]));
}
}
}
}
private void Timer_Tick1(object sender, object e)
{
UpdateAllData();
}
private async void PairToDevice()
{
if (device.Pairing.CanPair)
{
var customPairing = device.Pairing.Custom;
customPairing.PairingRequested += CustomPairing_PairingRequested;
var result = await customPairing.PairAsync(DevicePairingKinds.ConfirmOnly);
customPairing.PairingRequested -= CustomPairing_PairingRequested;
if ((result.Status == DevicePairingResultStatus.Paired) || (result.Status == DevicePairingResultStatus.AlreadyPaired))
{
/*while (device.Pairing.IsPaired == false)
{
device = await DeviceInformation.CreateFromIdAsync(device.Id);
}*/
StartRecievingData();
}
}
else if (device.Pairing.IsPaired)
{
StartRecievingData();
}
}
private void CustomPairing_PairingRequested(DeviceInformationCustomPairing sender, DevicePairingRequestedEventArgs args)
{
args.Accept();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
btnSave.Content = "Save";
btnStop.IsEnabled = false;
btnStart.IsEnabled = false;
this.DataContext = PressureData;
device = (DeviceInformation)e.Parameter;
PairToDevice();
//StartRecievingData();
base.OnNavigatedTo(e);
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
AppViewBackButtonVisibility.Visible;
}
}
private void DevicePage_BackRequested(object sender, BackRequestedEventArgs eventArgs)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
return;
}
// Navigate back if possible, and if the event has already been handled
if (rootFrame.CanGoBack && eventArgs.Handled ==false)
{
eventArgs.Handled = true;
rootFrame.GoBack();
}
}
private async void btnSave_Click(object sender, RoutedEventArgs e)
{
timer.Stop();
var picker = new FileSavePicker();
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.FileTypeChoices.Add("CSV", new List<string>() { ".csv" });
StorageFile file = await picker.PickSaveFileAsync();
if (file != null)
{
var stream = await file.OpenAsync(FileAccessMode.ReadWrite);
using (IOutputStream outputStream = stream.GetOutputStreamAt(0))
{
using (var writer = new DataWriter(outputStream))
{
foreach (DataPoint p in PressureData)
{
string text = p.TimeStamp.ToString() + "," + p.PacketNumber.ToString() + "," + p.InternalTimestamp.ToString() + "," + p.PressureValue1.ToString() + "," + p.PressureValue2.ToString() + "\n";
writer.WriteString(text);
}
await writer.StoreAsync();
await writer.FlushAsync();
}
}
stream.Dispose();
}
}
private async void btnStart_Click(object sender, RoutedEventArgs e)
{
if (pSensor != null)
{
btnStop.IsEnabled = true;
btnStart.IsEnabled = false;
startTime = DateTime.Now;
if (pSensor != null)
{
await pSensor.BeginCollecting();
}
}
}
private async void btnStop_Click(object sender, RoutedEventArgs e)
{
btnStart.IsEnabled = true;
btnStop.IsEnabled = false;
if (pSensor != null)
{
await pSensor.StopCollecting();
}
}
}
}
Here is where I define my SensorBase and PressureSensor class that handles the device connection:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth;
using Windows.Devices.Bluetooth.GenericAttributeProfile;
using Windows.Storage.Streams;
using Windows.Devices.Enumeration;
namespace BLEInterfaceTest
{
public static class SensorUUIDs
{
private static readonly string _packetUuid = "0000a043-0000-1000-8000-00805f9b34fb";
private static readonly string _buttonSensorServiceUuid = "0000a042-0000-1000-8000-00805f9b34fb";
private static readonly string _sensorStateUuid = "0000a044-0000-1000-8000-00805f9b34fb";
public static string PressureSensorUuid
{
get { return _packetUuid; }
}
public static string ButtonSensorServiceUuid
{
get { return _buttonSensorServiceUuid; }
}
public static string SensorStateUuid
{
get { return _sensorStateUuid; }
}
}
public class SensorBase : IDisposable
{
protected GattDeviceService deviceService;
protected string sensorDataUuid;
protected Queue<byte[]> fifoBuffer;
protected bool isNotificationSupported = false;
public bool newData = false;
private GattCharacteristic dataCharacteristic;
public SensorBase(GattDeviceService dataService, string sensorDataUuid)
{
this.deviceService = dataService;
this.sensorDataUuid = sensorDataUuid;
fifoBuffer = new Queue<byte[]>(20);
}
public bool MorePacketsAvailable
{
get
{
if (fifoBuffer.Count > 0)
{
return true;
}
else
{
return false;
}
}
}
public virtual async Task EnableNotifications()
{
GattCharacteristicsResult result = await deviceService.GetCharacteristicsAsync();
foreach (var test in result.Characteristics)
{
string t = test.Uuid.ToString();
}
isNotificationSupported = true;
dataCharacteristic = (await deviceService.GetCharacteristicsForUuidAsync(
new Guid(sensorDataUuid))).Characteristics[0];
dataCharacteristic.ValueChanged += dataCharacteristic_ValueChanged;
GattCommunicationStatus status = await dataCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
GattClientCharacteristicConfigurationDescriptorValue.Notify);
var currentDescriptorValue = await dataCharacteristic.ReadClientCharacteristicConfigurationDescriptorAsync();
if (currentDescriptorValue.Status != GattCommunicationStatus.Success
|| currentDescriptorValue.ClientCharacteristicConfigurationDescriptor != GattClientCharacteristicConfigurationDescriptorValue.Notify)
{
GattCommunicationStatus status2 = await dataCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
GattClientCharacteristicConfigurationDescriptorValue.Notify);
}
}
public virtual async Task DisableNotifications()
{
newData = false;
isNotificationSupported = false;
dataCharacteristic = (await deviceService.GetCharacteristicsForUuidAsync(
new Guid(sensorDataUuid))).Characteristics[0];
dataCharacteristic.ValueChanged -= dataCharacteristic_ValueChanged;
GattCommunicationStatus status = await dataCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(GattClientCharacteristicConfigurationDescriptorValue.None);
}
protected async Task<byte[]> ReadValue()
{
if (!isNotificationSupported)
{
if (dataCharacteristic == null)
{
dataCharacteristic = (await deviceService.GetCharacteristicsForUuidAsync(
new Guid(sensorDataUuid))).Characteristics[0];
}
GattReadResult readResult = await dataCharacteristic.ReadValueAsync();
byte[] data = new byte[readResult.Value.Length];
DataReader.FromBuffer(readResult.Value).ReadBytes(data);
fifoBuffer.Enqueue(data);
}
return fifoBuffer.Dequeue();
}
protected async Task WriteByteArray(string characteristicUuid, byte[] value)
{
GattCharacteristic writeCharacteristic = (await deviceService.GetCharacteristicsForUuidAsync(
new Guid(characteristicUuid))).Characteristics[0];
var writer = new DataWriter();
writer.WriteBytes(value);
var res = await writeCharacteristic.WriteValueAsync(writer.DetachBuffer(), GattWriteOption.WriteWithoutResponse);
}
private void dataCharacteristic_ValueChanged(GattCharacteristic sender, GattValueChangedEventArgs args)
{
byte[] data = new byte[args.CharacteristicValue.Length];
DataReader.FromBuffer(args.CharacteristicValue).ReadBytes(data);
fifoBuffer.Enqueue(data);
newData = true;
}
public async void Dispose()
{
await DisableNotifications();
}
}
public class PressureSensor: SensorBase
{
public PressureSensor(GattDeviceService dataService, string sensorDataUuid)
: base(dataService, sensorDataUuid)
{
}
public async Task BeginCollecting()
{
await WriteByteArray(SensorUUIDs.SensorStateUuid, new byte[] { 0x01 });
}
public async Task<int[]> GetPressure()
{
byte[] data = await ReadValue();
if (data != null)
{
int[] values = new int[9];
values[0] = (int)BitConverter.ToInt32(data, 0);
for (int i = 1; i < values.Length; i++)
{
values[i] = (int)BitConverter.ToInt16(data, 2 * i + 2);
}
return values;
}
else
{
return new int[] { 0 };
}
}
public async Task StopCollecting()
{
await WriteByteArray(SensorUUIDs.SensorStateUuid, new byte[] { 0x00 });
}
}
}
Here is the DataPoint Class that I use to organize the data received from the pressure sensor:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace BLEInterfaceTest
{
public class DataPoint : INotifyPropertyChanged
{
private TimeSpan _timestamp;
private int _packetNumber;
private double _internalTimestamp;
private int _pressure1;
private int _pressure2;
public event PropertyChangedEventHandler PropertyChanged;
public TimeSpan TimeStamp
{
get { return _timestamp; }
set
{
_timestamp = value;
this.NotifyPropertyChanged();
}
}
public int PacketNumber
{
get { return _packetNumber; }
set
{
_packetNumber = value;
this.NotifyPropertyChanged();
}
}
public double InternalTimestamp
{
get { return _internalTimestamp; }
set
{
_internalTimestamp = value;
this.NotifyPropertyChanged();
}
}
public int PressureValue1
{
get { return _pressure1; }
set
{
_pressure1 = value;
this.NotifyPropertyChanged();
}
}
public int PressureValue2
{
get { return _pressure2; }
set
{
_pressure2 = value;
this.NotifyPropertyChanged();
}
}
public DataPoint(TimeSpan time,int packetNumber, double internalTimestamp, int pressure1, int pressure2)
{
_timestamp = time;
_packetNumber = packetNumber;
_internalTimestamp = internalTimestamp;
_pressure1 = pressure1;
_pressure2 = pressure2;
}
private void NotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
if (!string.IsNullOrEmpty(propertyName))
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I have researched this extensively, and all I could find was help on how to initiate a disconnection. I have the opposite problem. One page I found stated that the problem might be caused by the device not properly storing the bonding state, but I have checked that and I did initialize the device to save the bonding state.
Interestingly if I do not pair the device to the computer before trying to read information from it then I do not have the problem. The connection never randomly stops. But when I do this, the computer does not receive every packet of data sent from the sensor device. It will receive one or two packets and then skip five or six packets. If I pair the device then I will receive every packet but the connection will randomly cut off.
So my question is two fold, I guess. How do I stop the connection from cutting off when the device is paired? Or alternatively, is there a way to allow the application to receive every packet of data when it is not paired?
UPDATE
I realized I should include more information on my sensor peripheral in case the error is in that code. I am currently designing a rapid prototyping of this sensor before I move on to designing the embedded version. To do this, I am using the BLE Nano 1 from RedBearLabs as a user friendly prototype. I am programing this device with the online MBED compiler. I have included the nRF51822 and BLE_API libraries to handle the bluetooth low energy communication.
UPDATE 2
So after more research into what is causing the problem, I have found that the disconnection occurs when a connection interval and a generation 2 garbage collection occur at the same time. In UWP the garbage collector can pause the UI Thread for generation 2 collections. (see here)
My thought is that if the thread is paused at the beginning of a connection interval, then the central is not able to initiate the connection with the peripheral and the peripheral therefore thinks the client is no longer listening (see more about how BLE connections work).
I discovered this by finding out exactly what is necessary to get the connection back once it has randomly stopped. I started with the entire connection process and reduced it down to this:
public async Task ReconnectDevice()
{
GattCommunicationStatus status = await dataCharacteristic.WriteClientCharacteristicConfigurationDescriptorAsync(
GattClientCharacteristicConfigurationDescriptorValue.Notify);
await WriteByteArray(SensorUUIDs.SensorStateUuid, new byte[] { 0x01 });
}
Because my BluetoothLEDevice, GattService, and GattCharacteristic objects are not disposed, all I need to do is resubscribe to notifications and write a 1 to the device so that it begins collecting data again.
I have reduced my memory allocations in my application significantly since discovering this, and the time for a gen2 collection has decreased to an average of 5 ms. Also, the amount of time before the connection disconnects has increased to around 4-5 sec.
UWP has a GattCharacteristicNotificationTrigger for receiving notifications in a BackgroundTask, but I have never had much success at incorporating background tasks in UWP.
I think I will try next to incorporate the windows.devices into a WPF application where I think I will have a better chance at getting it working.
So, after a while of trying different ideas I have finally stumbled across a solution to my problem. I had to make 2 changes:
Used the unpaired connection instead of the paired connection. This solved the problem of the connection dropping suddenly.
Increased the connection interval to 40 ms. For some reason when I did this, I received all of the data and no longer had any problems. Anything below 40 ms causes information to be lost when communicating to a Windows device (I had to make this change on the C code running on my sensors.)
I have used the devices for about 2 months now after making this change and have had no problems at all.
I seems to me that these problems are related to the BluetoothCacheMode Enum.
This indicates whether certain Bluetooth API methods should operate on values cached in the system or
retrieve those values from the Bluetooth device.
Using BluetoothCacheMode.Uncached attribute allows the service to update the attributes when needed.
If the device is paired then the BluetoothCacheMode is not needed(I think BluetoothCacheMode.Cached is default).
In your code the line:
var services = await leDevice.GetGattServicesAsync(BluetoothCacheMode.Uncached);
Can be the cause of the connection lost if paired.
GetGattServicesAsync(), GetCharacteristicsAsync() and ReadValueAsync()
must have the attribute BluetoothCacheMode.Uncached when not paired, when paired default or BluetoothCacheMode.Cached.
See https://msdn.microsoft.com/en-us/library/windows/apps/dn263758.aspx.
I'm currently working on SFML.Net to expand with mp3 support. Therefore I wrote a Stream class which uses NLayer MpegFile to decode the mp3.
public class Mp3StreamSFML : SoundStream
{
private MpegFile mp3file;
private int currentBufferSize;
private short[] currentBuffer;
public Mp3StreamSFML(String _filename)
{
mp3file = new MpegFile(_filename);
Initialize((uint)mp3file.Channels, (uint)mp3file.SampleRate);
currentBufferSize = 0;
currentBuffer = new short[currentBufferSize];
}
#region implemented abstract members of SoundStream
protected override bool OnGetData(out short[] samples)
{
if (currentBufferSize <= mp3file.Position)
{
byte[] buffer = new byte[512];
if (mp3file.ReadSamples(buffer, 0, buffer.Length) > 0)
{
Array.Resize(ref currentBuffer, currentBuffer.Length + (buffer.Length / 2));
Buffer.BlockCopy(buffer, 0, currentBuffer, currentBufferSize, buffer.Length);
currentBufferSize = currentBuffer.Length;
}
samples = currentBuffer;
return true;
}
else
{
samples = currentBuffer;
return false;
}
}
protected override void OnSeek(TimeSpan timeOffset)
{
mp3file.Position = (long)timeOffset.TotalSeconds;
}
#endregion
}
I use it this way:
try
{
stream = new Mp3StreamSFML(this.objProgram.getObjCuesheet().getAudiofilePath(true));
stream.Play();
log.debug("samplerate = " + stream.SampleRate);
}
catch(Exception ex)
{
log.fatal(ex.ToString());
}
Unfortunately, there is not the correct sound played, its just "juttering" and sound really weird. What I'm doing wrong? Seems like a problem between the 2 Frameworks.
Thanks for your help.
Sven
Solved the problem this way:
using System;
using SFML.Audio;
using NLayer;
using System.Threading;
namespace AudioCuesheetEditor.AudioBackend.SFML
{
/// <summary>
/// Class for mp3 decoded audio files to use in SFML as Soundstream, since SFML doesn't support mp3 decoding (for legal reasons).
/// </summary>
public class Mp3StreamSFML : SoundStream
{
private static readonly Logfile log = Logfile.getLogfile(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private MpegFile mp3file;
private Mutex mutex;
/// <summary>
/// Initializes a new instance of the <see cref="AudioCuesheetEditor.AudioBackend.SFML.Mp3StreamSFML"/> class.
/// </summary>
/// <param name="_filename">Full path to the file</param>
public Mp3StreamSFML(String _filename)
{
log.debug("Constructor called with " + _filename);
this.mp3file = new MpegFile(_filename);
this.Initialize((uint)this.mp3file.Channels, (uint)this.mp3file.SampleRate);
this.mutex = new Mutex();
}
public TimeSpan Duration
{
get
{
log.debug("Duration = " + this.mp3file.Duration);
return this.mp3file.Duration;
}
}
#region implemented abstract members of SoundStream
protected override bool OnGetData(out short[] samples)
{
log.debug("OnGetData called");
this.mutex.WaitOne();
//Buffer data for about 1 second
float[] normalizedaudiodata = new float[48000];
int readSamples = this.mp3file.ReadSamples(normalizedaudiodata, 0, normalizedaudiodata.Length);
short[] pcmaudiodata;
if (readSamples > 0)
{
pcmaudiodata = new short[readSamples]; // converted data
for (int i = 0; i < readSamples; i++)
{
// clip the data
if (normalizedaudiodata[i] > 1.0f)
{
normalizedaudiodata[i] = 1.0f;
}
else
{
if (normalizedaudiodata[i] < -1.0f)
{
normalizedaudiodata[i] = -1.0f;
}
}
// convert to pcm data
pcmaudiodata[i] = (short)(normalizedaudiodata[i] * short.MaxValue);
}
samples = pcmaudiodata;
this.mutex.ReleaseMutex();
return true;
}
else
{
samples = null;
this.mutex.ReleaseMutex();
return false;
}
}
protected override void OnSeek(TimeSpan timeOffset)
{
log.debug("OnSeek called with " + timeOffset);
this.mutex.WaitOne();
if ((timeOffset <= this.mp3file.Duration) && (timeOffset >= TimeSpan.Zero))
{
this.mp3file.Time = timeOffset;
}
this.mutex.ReleaseMutex();
}
#endregion
}
}
I am creating an application for WP7.1 with Phonegap in which I have to download a video and save it in Isolated Storage.
Now while reading that video, for the first time I can read it correctly, but after that I am not able to read the stream. This exception occurs every I try to read that video after I have read it once : Operation not permitted on IsolatedStorageFileStream.
Taken the code from : How to play embedded video in WP7 - Phonegap?
and added Pause and Stop functionality.
using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Phone.Controls;
using WP7CordovaClassLib.Cordova.JSON;
namespace WP7CordovaClassLib.Cordova.Commands
{
public class Video : BaseCommand
{
/// <summary>
/// Video player object
/// </summary>
private MediaElement _player;
Grid grid;
[DataContract]
public class VideoOptions
{
/// <summary>
/// Path to video file
/// </summary>
[DataMember(Name = "src")]
public string Src { get; set; }
}
public void Play(string args)
{
VideoOptions options = JsonHelper.Deserialize<VideoOptions>(args);
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
try
{
_Play(options.Src);
DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
}
catch (Exception e)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
GoBack();
}
});
}
private void _Play(string filePath)
{
if (_player != null)
{
if (_player.CurrentState == System.Windows.Media.MediaElementState.Paused)
{
_player.Play();
}
}
else
{
// this.player is a MediaElement, it must be added to the visual tree in order to play
PhoneApplicationFrame frame = Application.Current.RootVisual as PhoneApplicationFrame;
if (frame != null)
{
PhoneApplicationPage page = frame.Content as PhoneApplicationPage;
if (page != null)
{
grid = page.FindName("VideoPanel") as Grid;
if (grid != null && _player == null)
{
_player = new MediaElement();
grid.Children.Add(this._player);
grid.Visibility = Visibility.Visible;
_player.Visibility = Visibility.Visible;
_player.MediaEnded += new RoutedEventHandler(_player_MediaEnded);
}
}
}
Uri uri = new Uri(filePath, UriKind.RelativeOrAbsolute);
if (uri.IsAbsoluteUri)
{
_player.Source = uri;
}
else
{
using (IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isoFile.FileExists(filePath))
{
**using (IsolatedStorageFileStream stream =
new IsolatedStorageFileStream(filePath, FileMode.Open, isoFile))
{
_player.SetSource(stream);
stream.Close();
}
}
else
{
throw new ArgumentException("Source doesn't exist");
}
}
}
_player.Play();
}
}
void _player_MediaEnded(object sender, RoutedEventArgs e)
{
GoBack();
}
public void Pause(string args)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
try
{
_Pause(args);
DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
}
catch (Exception e)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
}
});
}
private void _Pause(string filePath)
{
if (_player != null)
{
if (_player.CurrentState == System.Windows.Media.MediaElementState.Playing)
{
_player.Pause();
}
}
}
public void Stop(string args)
{
Deployment.Current.Dispatcher.BeginInvoke(() =>
{
try
{
_Stop(args);
DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
}
catch (Exception e)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, e.Message));
}
});
}
private void _Stop(string filePath)
{
GoBack();
}
private void GoBack()
{
if (_player != null)
{
if (_player.CurrentState == System.Windows.Media.MediaElementState.Playing
|| _player.CurrentState == System.Windows.Media.MediaElementState.Paused)
{
_player.Stop();
}
_player.Visibility = Visibility.Collapsed;
_player = null;
}
if (grid != null)
{
grid.Visibility = Visibility.Collapsed;
}
}
}
}
** The exception (Operation not permitted on IsolatedStorageFileStream.) occurs at _Play function while reading the file (Please see ** in code above). First time it runs perfectly and when I come to read the file second time it gives Exception.
What might be the problem?
Is I am doing something wrong?
It sounds like the file is still open from the previous read. If this is the case, you need to specify the fileAccess and fileShare to allow it to be opened by another thread:
using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, isoFile)
I solved this problem by just setting the source property of the MediaElement to null before navigating back. So, when i come back to play the same video, the MediaElement source is free for it.
Edited the GoBack Function to :
private void GoBack()
{
// see whole code from original question.................
_player.Visibility = Visibility.Collapsed;
_player.Source = null; // added this line
_player = null;
//..................
}
Thanks All.
I want to playback a sound file in two or three external sound cards at the same time and I think that using threads is the solution but I really didn't know how to use it in the playback code.
This is the event makes on button play:
public partial class PlaybackForm : Form
{
IWavePlayer waveOut;
string fileName = null;
WaveStream mainOutputStream;
WaveChannel32 volumeStream;
int _deviceNum;
int _deviceNum1;
Thread t1;
Thread t2;
public PlaybackForm(int deviceNum,int deviceNum1)
{
InitializeComponent();
_deviceNum = deviceNum;
_deviceNum1 = deviceNum1;
}
private void buttonPlay_Click(object sender, EventArgs e)
{
if (waveOut != null)
{
if (waveOut.PlaybackState == PlaybackState.Playing)
{
return;
}
else if (waveOut.PlaybackState == PlaybackState.Paused)
{
waveOut.Play();
return;
}
}
// we are in a stopped state
// TODO: only re-initialise if necessary
if (String.IsNullOrEmpty(fileName))
{
toolStripButtonOpenFile_Click(sender, e);
}
if (String.IsNullOrEmpty(fileName))
{
return;
}
try
{
CreateWaveOut();
}
catch (Exception driverCreateException)
{
MessageBox.Show(String.Format("{0}", driverCreateException.Message));
return;
}
mainOutputStream = CreateInputStream(fileName);
trackBarPosition.Maximum = (int)mainOutputStream.TotalTime.TotalSeconds;
labelTotalTime.Text = String.Format("{0:00}:{1:00}", (int)mainOutputStream.TotalTime.TotalMinutes,
mainOutputStream.TotalTime.Seconds);
trackBarPosition.TickFrequency = trackBarPosition.Maximum / 30;
try
{
waveOut.Init(mainOutputStream);
}
catch (Exception initException)
{
MessageBox.Show(String.Format("{0}", initException.Message), "Error Initializing Output");
return;
}
// not doing Volume on IWavePlayer any more
volumeStream.Volume = volumeSlider1.Volume;
waveOut.Play();
}
And this is how to create the waveout:
private void CreateWaveOut()
{
CloseWaveOut();
int latency = (int)comboBoxLatency.SelectedItem;
//if (radioButtonWaveOut.Checked)
{
//WaveCallbackInfo callbackInfo = checkBoxWaveOutWindow.Checked ?
WaveCallbackInfo callbackInfo = WaveCallbackInfo.FunctionCallback();
// WaveCallbackInfo callbackInfo = WaveCallbackInfo.FunctionCallback();
// WaveCallbackInfo.NewWindow(): WaveCallbackInfo.FunctionCallback();
WaveOut outputDevice = new WaveOut(callbackInfo);
outputDevice.DesiredLatency = latency;
outputDevice.DeviceNumber = _deviceNum;
waveOut = outputDevice;
}
}
I declared two deviceNum but until now I can playsound only in one device,that's why I want to use thread.
Can you help me please
Thank you in advance
Do something like that:
using System.Threading;
...
private void buttonPlay_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.PlaySound), 1);
ThreadPool.QueueUserWorkItem(new WaitCallback(this.PlaySound), 2);
}
private void PlaySound(object obj)
{
int deviceNumber = (int)obj;
// Do the stuff you used to do in buttonPlay_Click
WaveOut myWaveOut = CreateWaveOut(deviceNumber);
...
}
private WaveOut CreateWaveOut(int deviceNumber)
{
...
WaveOut outputDevice = new WaveOut(callbackInfo);
outputDevice.DesiredLatency = latency;
outputDevice.DeviceNumber = _deviceNum;
return outputDevice;
}