Playing Audio File using NAudio 1.7+ - c#

I was having issues playing back network streams as well as audio files using AAC and MP3 using NAudio. Took a while to figure out but the below solution works.

Hope to helps anyone else that is having a hard time utilizing NAudio properly.
//Create Output Stream with Data from Audio File / Network Stream
WaveOutputStream outputStream = new MediaFoundationReader("Path to File");
//Create Volume Stream to control volume of output
//ex: volumeStream.Volume = 0.5f; Float between 0 & 1
WaveChannel32 volumeStream = new WaveChannel32(outputStream);
//Create WaveOutEvent since it works in Background and UI Threads
WaveOutEvent player = new WaveOutEvent();
//Init Player with Configured Volume Stream
player.Init(volumeStream);
player.Play();
This code can play any audio file that MediaFoundationReader supports (MP3,AAC,WAV) as well as network streams of these codecs. To reuse the above player call Dispose() on player,outputStream, and volumeStream then set each to null.

Related

How to convert audio wav file to Mp3 file Xamarin forms

In my app I am recording voice using AudioRecorder as given in the following site, Audio Recorder it is working but it produce large size WAV file.
For example : If I record audio for 1 minute it takes 4MB to 5MB. So that I want to convert the wave file into MP3 file to reduce the size of the file. Please help me to compress the wav file ,give some example. Thanks in advance.
I never tried converting files before so i looked up on
some threads that might be helpful to you.
One is converting wav to mp3 which require file conversion into a byte[]
public byte[] ConvertToMp3(Uri uri)
{
using (var client = new WebClient())
{
var file = client.DownloadData(uri);
var target = new WaveFormat(8000, 16, 1);
using (var outPutStream = new MemoryStream())
using (var waveStream = new WaveFileReader(new MemoryStream(file)))
using (var conversionStream = new WaveFormatConversionStream(target, waveStream))
using (var writer = new LameMP3FileWriter(outPutStream, conversionStream.WaveFormat, 32, null))
{
conversionStream.CopyTo(writer);
return outPutStream.ToArray();
}
}
}
however on this method he is using a third party service which downloads the
wav file and then to be called on that method but this does not guaranty if the file size will be reduced.
however i have check that you can compress wav files using a library called zlib.
just decompress it whenever u need it.
Please check the link below:
How to convert wav file to mp3 in memory?
Reducing WAV sound file size, without losing quality

Microsoft SpeechSynthesizer crackles when outputting to files and streams

I'm writing a thing that uses the SpeechSynthesizer to generate wave files on request, but I'm having problems with crackling noises. The weird thing is that output directly to the sound card is just fine.
This short powershell script demonstrates the issue, though I'm writing my program in C#.
Add-Type -AssemblyName System.Speech
$speech = New-Object System.Speech.Synthesis.SpeechSynthesizer
$speech.Speak('Guybrush Threepwood, mighty pirate!')
$speech.SetOutputToWaveFile("${PSScriptRoot}\foo.wav")
$speech.Speak('Guybrush Threepwood, mighty pirate!')
What this should do, is output to the speakers, and then save that same sound as "foo.wav" next to the script.
What it does is output to the speakers, and then save a crackling, old record player sounding version as a wave file. I've tested this on three different machines, and though they select different voices by default (all Microsoft provided default ones), they all sound like garbage falling down stairs in the wave file.
Why?
EDIT: I am testing this on Windows 10 Pro, with the latest updates that add that annoying "People" button on the taskbar.
EDIT 2: Here's a link to an example sound generated with the above script. Notice the crackling voice, that's not there when the script outputs directly to the speakers.
EDIT 3: It's even more noticeable with a female voice
EDIT 4: The same voice as above, saved to file with TextAloud 3 - no cracking, no vertical spikes.
I find it hard to believe this is a PoSH issue.
It's not PoSH doing the encoding on the serialization to disk. Its the API/Class that is being used.
'msdn.microsoft.com/en-us/library/system.speech.synthesis.speechsynthesizer(v=vs.110).aspx'
As per the MSDN, there is no option to control the encoding, bit rate, etc.
.wav has never been HQ stuff. So, I'd wonder if you take that .wav through a converter to make it an .mp3 or mp4, if that would correct your quality concerns. But that also means getting the converter on users systems.
Secondly, since Win8, the default player does not even play .wav correctly or at all. Sure, you can still set the default play of .wav to Windows Media Player or call the file via VLC, but it's still a .wav file. Yet, that also means, you having to set the Media Player assignment on every target system.
This is an issue with the SpeechSynthesizer API, which simply provides bad quality, crackling audio as seen in the samples above. The solution is to do what TextAloud does, which is to use the SpeechLib COM objects directly.
This is done by adding a COM reference to "Microsoft Speech Object Library (5.4)". Here is a snippet of the code I ended up with, which produces audio clips of the same quality as TextAloud:
public new static byte[] GetSound(Order o)
{
const SpeechVoiceSpeakFlags speechFlags = SpeechVoiceSpeakFlags.SVSFlagsAsync;
var synth = new SpVoice();
var wave = new SpMemoryStream();
var voices = synth.GetVoices();
try
{
// synth setup
synth.Volume = Math.Max(1, Math.Min(100, o.Volume ?? 100));
synth.Rate = Math.Max(-10, Math.Min(10, o.Rate ?? 0));
foreach (SpObjectToken voice in voices)
{
if (voice.GetAttribute("Name") == o.Voice.Name)
{
synth.Voice = voice;
}
}
wave.Format.Type = SpeechAudioFormatType.SAFT22kHz16BitMono;
synth.AudioOutputStream = wave;
synth.Speak(o.Text, speechFlags);
synth.WaitUntilDone(Timeout.Infinite);
var waveFormat = new WaveFormat(22050, 16, 1);
using (var ms = new MemoryStream((byte[])wave.GetData()))
using (var reader = new RawSourceWaveStream(ms, waveFormat))
using (var outStream = new MemoryStream())
using (var writer = new WaveFileWriter(outStream, waveFormat))
{
reader.CopyTo(writer);
return o.Mp3 ? ConvertToMp3(outStream) : outStream.GetBuffer();
}
}
finally
{
Marshal.ReleaseComObject(voices);
Marshal.ReleaseComObject(wave);
Marshal.ReleaseComObject(synth);
}
}
This is the code to convert a wave file to mp3. It uses NAudio.Lame from nuget.
internal static byte[] ConvertToMp3(Stream wave)
{
wave.Position = 0;
using (var mp3 = new MemoryStream())
using (var reader = new WaveFileReader(wave))
using (var writer = new LameMP3FileWriter(mp3, reader.WaveFormat, 128))
{
reader.CopyTo(writer);
mp3.Position = 0;
return mp3.ToArray();
}
}

Streaming video (C# using FFmpeg AutoGen) sends multiple data requests

I've written a video generator that rights a video in h264 format (mp4). When I stream the video from my azure service, i'm seeing the following network traffic:
The AVCodecContext layout I'm using is as follows:
AVCodec* videoCodec = ffmpeg.avcodec_find_encoder(AVCodecID.AV_CODEC_ID_H264)
AVCodecContext* videoCodecContext = ffmpeg.avcodec_alloc_context3(videoCodec);
videoCodecContext->bit_rate = 400000;
videoCodecContext->width = 1280;
videoCodecContext->height = 720;
videoCodecContext->gop_size = 12;
videoCodecContext->max_b_frames = 1;
videoCodecContext->pix_fmt = videoCodec->pix_fmts[0];
videoCodecContext->codec_id = videoCodec->id;
videoCodecContext->codec_type = videoCodec->type;
videoCodecContext->time_base = new AVRational
{
num = 1,
den = 30
};
ffmpeg.av_opt_set(videoCodecContext->priv_data, "preset", "ultrafast");
I'm also tried setting the "movflags" option for avformat_write_header() via an AVDictionary, but then av_write_trailer() returns -2, cause the file to not finish writing.
I cannot figure out how to solve this problem. Videos generating using Windows Movie Maker stream perfectly.
I know this has something to do with mdat and mov positions.
Also, this appears to only happening in Google Chrome.
OK, figured this out. I've been writing the video frames first and the audio frames afterwards. Instead, you have to write them side by side in order for faststart to actually work and allow the video to stream.
So, write a specific amount of audio and then determine if a video frame should be written by checking the timebases against the current writing indexes.
This example will show you how its done.
Also, to get the video and audio streams to have accurate PTS/DTS values, look at this question.

How do I resample an in-memory audio stream (byte[]) with NAudio?

I want to resample audio byte array from 8Khz to 48Khz. The audio stream is obtained as a byte[] from a network socket.
Reading Mark Heath's Blog about resampling using NAudio, I came across the following code
int outRate = 16000;
var inFile = #"test.mp3";
var outFile = #"test resampled WDL.wav";
using (var reader = new AudioFileReader(inFile))
{
var resampler = new WdlResamplingSampleProvider(reader, outRate);
WaveFileWriter.CreateWaveFile16(outFile, resampler);
}
But this code acts on a file stream (AudioFileReader) rather than in memory data (byte[]). How could I modify this code to up-sample my byte array?
Edit: Basically I want to up-sample the 8 KHz data obtained from a network peer to 48 KHz and play using WASAPI.
Your input to the resampler could be a BufferedWaveProvider or a RawSourceWaveStream. You can't use CreateWaveFile16 to resample in real-time though. You'd need to read only the amount of audio you expect to be available and write it to the WAV file.

Play sound on specific channel with NAudio

I'm using NAudio for sound work. And i have specific task. I have 8 channel sound card. How can I play sound in only 1, 2 or more specific channels. For example I have this code:
Device = new WaveOut();
var provider = new SineWaveProvider32();
provider.SetWaveFormat(44100, 1);
provider.Frequency = 1000f;
provider.Amplitude = 1f;
Device.DeviceNumber = number;
Device.Init(provider);
Device.Play();
This code play sound on all channels.
What i need to change in this?
You could use a MultiplexingWaveProvider and pass in a silence producing wave provider for one channel and a SineWaveProvider32 for the other.
Also note that your soundcard may not necessarily support multi-channel audio through the WaveOut API.

Categories