i am trying to implement audio recording using NAudio to a Wav file, but the default bitrate set by the WasapiLoopbackCapture class can't be changed programmatically.
I am recording the audio output to a MemoryStream (recordedStream in snippet below). However the default bitrate set by the WasapiLoobpackCapture doesn't fit my needs.
I would like to have a bit rate of 320KBPS and i tried to convert the recorded file programmatically using the WaveFormatConversionStream class, but i couldn't make it work.
WaveFormat targetFormat = WaveFormat.CreateCustomFormat(waveIn.WaveFormat.Encoding,
waveIn.WaveFormat.SampleRate, //SampleRate
waveIn.WaveFormat.Channels, //Channels
320000, //Average Bytes per Second
waveIn.WaveFormat.BlockAlign, //Block Align
waveIn.WaveFormat.BitsPerSample); //Bits per Sample
using (WaveStream inputStream = new RawSourceWaveStream(recordedStream, waveIn.WaveFormat))
{
try
{
using (var converter = new WaveFormatConversionStream(targetFormat, inputStream))
{
// ...
}
}
catch (Exception)
{
throw;
}
recordedStream.Dispose();
}
I always get an "AcmNotPossible calling acmStreamOpen" conversion exception. As you see i am using exactly the same format as the recorded WAV file (Extension encoding, 44100 etc.), except the bitrate which is lower in the target waveformat.
What would be the correct codeto do the bitrate conversion from a Wav file contained in a MemoryStream? my goal is to get a 320KBPS file.
For a given sample rate, bit depth, and channel count, PCM audio always has the same bitrate (calculated by multiplying those three values together). If you want to reduce the bitrate, you must change one of those three (lowering the sample rate is probably the best option unless you can go from stereo to mono).
Really you should be thinking of encoding to a format like MP3, WMA or AAC, which will let you select your preferred bitrate.
Related
Before applying any sound modification (using the sample frames), I'm trying to simply read a Wav file, and write out an identical one using the contents:
using (WaveFileReader reader = new WaveFileReader(#"input.wav"))
{
using (WaveFileWriter writer = new WaveFileWriter(#"output.wav", reader.WaveFormat))
{
while (true)
{
var frame = reader.ReadNextSampleFrame();
if (frame == null)
break;
writer.WriteSample(frame[0]);
}
}
}
I'd expect this to write an identical Wav file. But it actually writes a (surprisingly larger file) with a very different sound (as if it's been supressed).
Any ideas?
It's because you are converting to floating point samples, and back to whatever the source format was, and paying no attention to the number of channels. To do what you want you should just use the Read method to read into a byte array and write the same data into the writer.
Issue
My code is based upon this sample. When I capture audio it is extremely jittery/stuttery and sounds slightly 'sped up' as the playback duration is approx a quarter of the expected duration.
Code Snippet
Within my callhandler i have the following....my 'plan' is to try and hive off the received buffer asap to avoid any potential bottleneck(s) and then process the 'raw' bytes later and convert them into a WAV file.
private async void OnAudioMediaReceived(object sender, AudioMediaReceivedEventArgs e)
{
try
{
if (e.Buffer.IsSilence) return;
var managedArray = new byte[e.Buffer.Length];
Marshal.Copy(e.Buffer.Data, managedArray, 0, (int)e.Buffer.Length);
using (var fs = new FileStream($"c:\\tmp\\myTelephoneCallAudio.raw",FileMode.Append))
{
fs.Write(managedArray, 0, managedArray.Length);
fs.Close();
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
throw;
}
finally
{
e.Buffer.Dispose();
}
}
......then once the telephone call has ended i process the .raw file created above into a .wav by basically generating a wav file header and appending the wav content from the raw file.
Expected behavior
A clear and audible recording of the call lasting the expected duration.
Graph SDK(s) in use:
Microsoft.Graph (Nuget) - v3.12.0
Microsoft.Graph.Core (Nuget) - v1.21.0
Microsoft.GraphCommunications.Calls (Nuget) - v1.2.0.1702
Microsoft.GraphCommunications.Calls.Media (Nuget) - v1.2.0.1702
Microsoft.GraphCommunications.Client (Nuget) - v1.2.0.1702
Microsoft.GraphCommunications.Common (Nuget) - v1.2.0.1702
Microsoft.GraphCommunications.Core (Nuget) - v1.2.0.1702
Other information
I've raised an issue here but experience tells me I'm not going get a response. But within that raised issue I attached is a zip with the 'mangled' wav. This wav file is only 1m 25s but the call was 6.5 minutes long.
For a time I thought of the possibility of maybe the audio 'frames' were coming to me/being processed by me slightly out of sync/order....therefore i did try storing the frames in a dictionary where the key was the timestamp of the frame/buffer.....then i could order by the timestamp before attempting the conversion to the wav file.
Also after looking around online at expected wav file sizes, i.e. 10MB/min, etc.. it appears that the amount of data I'm capturing currently is a lot lower than expected for the assumed values, i.e. mono (1 channel), PCM 16K, with 32K sample rate, etc.
final note... every time when debugging OnAudioMediaReceived(), the buffer only has 640 bytes, meaning I'm appending at a rate of 640 per time...is this the same for anyone else who may be working on this?
I use Accord.Video.FFMPEG to create a video of 200 images with the H264 codec. For some reason, the video is very poor quality. Its size is less than 1MB. When choosing VideoCodec.Raw, the quality is high, but I am not happy with the huge size.
I do something like this
using (var vFWriter = new VideoFileWriter())
{
vFWriter.Open(video_name, 1920, 1080, 24, VideoCodec.H264);
for (int i = 0; i < 200; ++i)
{
var img_name_src = ...
using (Bitmap src_jpg = new Bitmap(img_name_src))
{
vFWriter.WriteVideoFrame(src_jpg);
}
}
vFWriter.Close();
}
When I run the program, messages appear:
[swscaler # 06c36d20] deprecated pixel format used, make sure you did set range correctly
[swscaler # 06e837a0] deprecated pixel format used, make sure you did set range correctly
[avi # 06c43980] Using AVStream.codec.time_base as a timebase hint to the muxer is deprecated. Set AVStream.time_base instead.
[avi # 06c43980] Using AVStream.codec to pass codec parameters to muxers is deprecated, use AVStream.codecpar instead.
I don’t know if they affect something.
It looks like 1 frame:
This is the frame from the video:
How to fix it?
Is there any other way in C# to create a video from individual frames?
Usually, video quality is down to the bitrate which can be changed with this overload:
writer.Open(fileName, width, height, frameRate, VideoCodec, BitRate);
In the millions, the video still has artifacts on high detail frames but is mostly fine. In the billions however, artifacts disappear entirely but file size sky rockets and playback speed is affected by retrieval times from the disk.
Try experimenting with different VideoCodecs, bitrates and file types (mp4, avi, webm etc) to find a suitable balance for your project.
I have a requirement in my application where I have to read all the available track stream from mp4 file.
Mp4 file is encoded with number of tracks in AAC format. I have to decode to get all available tracks from the file. Currently I am using SharpDX and IMSourceReader (Media Foundation dlls) to read the Streams. But by default SourceReader returns only the first audio stream from the file. Is it I am doing correct ? Or I have to use any other third party libraries to achieve this ?
When configuring the reader, you can select which streams will be delivered when reading samples. Often times you do not wish to select the stream. An example would be a movie which has additional audio streams (spanish, french, or perhaps director commentary). As a result, most of the time stream selection is as simple as the following:
// error checking omitted for brevity
hr = reader->SetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, nullptr, audioMediaType);
hr = reader->SetStreamSelection((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, true);
However if you look at SetStreamSelection, the first parameter takes either the enumeration used above, or a specific stream index.
// 0–0xFFFFFFFB <-- The zero-based index of a stream.
// 0xFFFFFFFC <-- MF_SOURCE_READER_FIRST_VIDEO_STREAM
// 0xFFFFFFFD <-- MF_SOURCE_READER_FIRST_AUDIO_STREAM
// 0xFFFFFFFE <-- MF_SOURCE_READER_ALL_STREAMS
// 0xFFFFFFFE <-- MF_SOURCE_READER_ANY_STREAM
// 0xFFFFFFFF <-- MF_SOURCE_READER_INVALID_STREAM_INDEX
I have never used SharpDX, but this enumeration is documented here.
Pertaining to video, sometimes additional video streams are available (usually closed captioning).
When reading the samples, using a callback or synchronously, pay close attention to the stream index, and process the sample accordingly.
You may also find these answers valuable or interesting:
Aggregate Media Source
MP4 IMFSinkWriter
Adding Audio Sample to Video
Creating NV12 Encoded Video
IMFSinkWriter Configuration
IMFSinkWriter CPU Utilization
I hope this helps.
I want send string byte to speaker something like this:
byte[] bt = {12,32,43,74,23,53,24,54,234,253,153};// example array
var ms = new MemoryStream(bt);
var sound = new System.Media.SoundPlayer();
sound.Stream = ms;
sound.Play();
but I get this exception:
my problem pic http://8pic.ir/images/g699b52xe5ap9s8yf0pz.jpg
The first bytes of a WAV stream contain info about length, etc.
You have to send this "WAV-Header" as well in the first few bytes.
See http://de.wikipedia.org/wiki/RIFF_WAVE
As you'll see its perfectly possible to compose these few bytes in the header and send them before your raw audio data,
You can use some library for reading data from microphone or playing it to speakers.
I worked successfuly with:
NAudio - http://naudio.codeplex.com/
I would not recommend building a WAV file yourself, it may be too much effort for this.
Note that this library (and probably some others, Bass - http://www.un4seen.com is also widely used) also have built in functionality for saving and reading WAV files.
NAudio is best app to play that functionality. use sample app provided.It may help.