I'm trying to use NAudio to record some sound in C# using WasapiLoopbackCapture and WaveFileWriter.
The problem is that after the recording is finished, the "size" field in the WAV/RIFF header is set to 0, rendering the file unplayable.
I'm using the following code:
WasapiLoopbackCapture CaptureInstance = null;
WaveFileWriter RecordedAudioWriter = null;
void StartSoundRecord()
{
string outputFilePath = #"C:\RecordedSound.wav";
// Redefine the capturer instance with a new instance of the LoopbackCapture class
CaptureInstance = new WasapiLoopbackCapture();
// Redefine the audio writer instance with the given configuration
RecordedAudioWriter = new WaveFileWriter(outputFilePath, CaptureInstance.WaveFormat);
// When the capturer receives audio, start writing the buffer into the mentioned file
CaptureInstance.DataAvailable += (s, a) =>
{
// Write buffer into the file of the writer instance
RecordedAudioWriter.Write(a.Buffer, 0, a.BytesRecorded);
};
// When the Capturer Stops, dispose instances of the capturer and writer
CaptureInstance.RecordingStopped += (s, a) =>
{
RecordedAudioWriter.Dispose();
RecordedAudioWriter = null;
CaptureInstance.Dispose();
};
// Start audio recording !
CaptureInstance.StartRecording();
}
void StopSoundRecord()
{
if(CaptureInstance != null)
{
CaptureInstance.StopRecording();
}
}
(Borrowed from: https://ourcodeworld.com/articles/read/702/how-to-record-the-audio-from-the-sound-card-system-audio-with-c-using-naudio-in-winforms )
Which I'm testing with simply:
StartSoundRecord();
Thread.Sleep(10000);
StopSoundRecord();
What am I missing, why isn't WaveFileWriter writing the size field? I have tried also calling the Flush() and Close() methods before disposing. But it makes no difference.
Sure, I could write a method to find out the size of the file and manually writing it to the final file, but that seems unnecessary.
Found the solution.
Calling RecordedAudioWriter.Flush() after every Write made it work just fine.
Don't know if doing so might be inefficient (as I assume flush is blocking until data is written to disk), but for my application that is not an issue.
Related
I'm using NAudio to capture 15 seconds of audio. Like this:
MemoryStream bufferedVoice = new MemoryStream();
voiceCapturer = new WasapiLoopbackCapture(OutputDevice);
voiceCapturer.DataAvailable += onVoiceOutputReceived;
voiceCapturer.StartRecording();
private void onVoiceOutputReceived(object sender, WaveInEventArgs e)
{
bufferedVoice.Write(e.Buffer, 0, e.BytesRecorded);
}
And after 15 seconds I want to save it to a file, and exit. I tried it like this but it didn't work:
var ResourceWaveStream = new RawSourceWaveStream(bufferedVoice, voiceCapturer.WaveFormat);
var SampleProvider = new WaveToSampleProvider(ResourceWaveStream).ToWaveProvider16();
var fileWriter = new WaveFileWriter("output.mp3", SampleProvider.WaveFormat);
byte[] buf = new byte[8192];
while(SampleProvider.Read(buf, 0, buf.Length) > 0)
{
fileWriter.Write(buf, 0, buf.Length);
}
fileWriter.Dispose();
How can I save the memorystream into a file?
Clarification: I only want to store x seconds of audio in memory. So when the max size is reached, some of the oldest part is removed. Then if I press a button, I want to save the 15 seconds of audio into a file.
Now, my question is how should I store the audio in memory, and then write it to a file?
Try this:
using(var fileWriter = new WaveFileWriter("yourOutputFile", SampleProvider.WaveFormat)
{
ResourceWaveStream.CopyTo(fileWriter);
}
Btw, the "using" block is good for you here because it will automatically dispose the writer, allowing the WaveFileWriter to write headers to the file.
Okay, I finally have the solution. First, I copy the sound data directly to the memory stream. And then when I need it, I read the whole memorystream into a RawSourceWaveStream and then I pass that to the final WaveFileWriter. If anybody is interested in how I did it exactly, then message me.
Thanks.
My code creates a power point presentation and three audio files.
I want to know the length of those audio files after the presentation is created so I use:
double duration = 0;
WindowsMediaPlayer wmp = new WindowsMediaPlayer();
IWMPMedia mediainfo = wmp.newMedia(file);
duration = mediainfo.duration;
wmp.close();
return duration;
To create the audio files I use
public void CreateAudio(string text)
{
y++;
synth = new SpeechSynthesizer();
AudioStream = new FileStream(folder + #"\audio\a" + #y.ToString() + #".wav", FileMode.OpenOrCreate);
//synth.SpeakCompleted += new EventHandler<SpeakCompletedEventArgs>(synth)
synth.SetOutputToWaveStream(AudioStream);
synth.SpeakAsync(text);
}
private void synth_SpeakCompleted(object sender, SpeakCompletedEventArgs e)
{
synth.Dispose();
}
The problem is that after the presentation is created, only the shortest audio file returns a length, the rest return 0.
If I check manually, I see 3 audio files with a valid length property, but the program doesn't read that for some reason.
My attempt at disposing the synth didn't change a thing, nor did using syth.speak so I must have done something terribly wrong at using and managing my objects and memory but I don't know what or where.
If I use the code to check the length of the audio files created in a different code for example, it works perfectly, just when I create them and want to check their length something goes wrong.
Some help from experts. I'm trying to use the function below to print strings to a file. When I use Console.Write() or Console.WriteLine() the output file grows up 3MB or 4MB per seconds, but when I try to use StreamWriter or File.AppendAllText the output in the way shown below, the file grows up only in 20KB or 30KB per second.
Why the print speed decreases too much when I use StreamWriter instead of Console.WriteLine()?
What method should I use to write to a file maintaining the same speed of Console.WriteLine()?
public static void PrintFunction()
{
//using (StreamWriter writer = File.AppendText(#"C:\OuputFile.txt"))
using (StreamWriter writer = new StreamWriter(#"C:\OuputFile.txt", true))
{
//Console.Write("This is "); // Print speed is about 3MB-4MB per second
writer.Write("This is "); //Print decreases to 20KB-30KB per second
//File.AppendAllText(#"C:\OuputFile.txt", "This is "); Print decreases to 20KB-30KB per second
// SOME CODE
// SOME CODE
//Console.WriteLine("the first line"); // Print speed is about 3MB-4MB per second
writer.WriteLine("the first line"); // Print decreases to 20KB-30KB per second
//File.AppendAllText(#"C:\OuputFile.txt", "the first line"); // Print decreases to 20KB-30KB per second
}
}
Update:
When I say I'm using Console.WriteLine() I mean, I'm using Console.WriteLine() inside the code but to save those prints in a file, I'm redirecting the output like this:
MyProgram.exe inputfile > outputfile.txt
I know the difference of memory and hard disk, but why when I use Console.WriteLine() redirecting the output as mentioned above (is printing to hard disk), the printing is more than 1000 times faster than using StreamWriter?
I've tried increasing the buffer size like below, but the speed of printing doesn't growp up.
using (StreamWriter writer = new StreamWriter(#"C:\OuputFile.txt", true, Encoding.UTF8, 65536))
Update 2:
Hello to all, Thanks for all the help, you were rigth!!!. Following all your suggestions and examples I defined StreamWriter outside
the PrintFunction and this time the writer process is called only once and the output file remains open till the end and in this way
the printing process speed is the same as Console.WrileLine().
I've passed the writer as argument of the function like below and it works. I've tested with a buffer size of 4KB, 64KB and with
default values like shown below and the faster result was when I set explicitely used buffer of 4096 bytes. The function was called
a little bit more than 10 million times and output file was 670 MB.
*StreamWriter(#"C:\OuputFile.txt", true, Encoding.UTF8, 4096) --> 660845.1181 ms --> 11.0140853 min
StreamWriter(#"C:\OuputFile.txt", true, Encoding.UTF8, 65536) --> 675755.0119 ms --> 11.2625835 min
StreamWriter(#"C:\OuputFile.txt") --> 712830.3706 ms --> 11.8805061 min*
Thanks again for the help.
Regards
The code looks like this:
public static void ProcessFunction()
{
StreamWriter writer = new StreamWriter(#"C:\OuputFile.txt", true, Encoding.UTF8, 4096);
while ( condition)
{
PrintFunction(writer);
}
if( writer != null )
{
writer.Dispose();
writer.Close();
}
}
public static void PrintFunction(StreamWriter writer)
{
//SOME CODE
writer.Write("Some string...");
//SOME CODE
}
I profiled this and it looks like it is completely the opposite. I was able to get about .25GB/s written to a standard 10K rpm drive (no SSD). It looks like you're calling this function a lot and writing to the file by connecting to it new each time. Try something like this (I snipped this together quickly from a piece of old console logging code, so it might be a bit buggy, and error handling is certainly not complete):
public static class LogWriter
{
// we keep a static reference to the StreamWriter so the stream stays open
// this could be closed when not needed, but each open() takes resources
private static StreamWriter writer = null;
private static string LogFilePath = null;
public static void Init(string FilePath)
{
LogFilePath = FilePath;
}
public static void WriteLine(string LogText)
{
// create a writer if one does not exist
if(writer==null)
{
writer = new StreamWriter(File.Open(LogFilePath,FileMode.OpenOrCreate,FileAccess.Write,FileShare.ReadWrite));
}
try
{
// do the actual work
writer.WriteLine(LogText);
}
catch (Exception ex)
{
// very simplified exception logic... Might want to expand this
if(writer!=null)
{
writer.Dispose();
}
}
}
// Make sure you call this before you end
public static void Close()
{
if(writer!=null)
{
writer.Dispose();
writer = null;
}
}
}
Why the print speed decreases too much when I use StreamWriter instead of Console.WriteLine()?
When you redirect the command output to a file, the write-only access of the output file was acquired by cmd.exe at once whatever how many times you call PrintFunction() with console.Write()
if you use stream writer in the PrintFunction(), the writer was initialized each time, try to access the file, write a line then release the file handle. The overhead kills the performance.
What method should I use to write to a file maintaining the same speed of Console.WriteLine()?
You can try one of the following
Buffer all your output in memory (e.g. using StringBuilder), then write to the file at once
Passing the StreamWriter object to the PrintFunction() to avoid the overhead. Proper handle the StreamWriter.Close() at the end
I am using MemoryMappedFile for communication between 2 programs. Program "A" creates the mmf and reads it's contents on a timer. Program "B" writes xml data to the mmf on a timer. I have the memory map working but I run into an issue where the previous iteration of the XML data is longer than the current and old data gets carried over to the next round.
so for simplicity lets say program B writes
aaaa
Program A will read correctly,
Then the next write from program B is:
b
Program A reads
baaa
It seems like there should be some simple way to flush the contents of the memory mapped file but I can't seem to figure it out. It's very possible that I'm totally wrong in the way I'm going about this.
Here's what I'm currently doing.
Program A:
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap",MemoryMappedFileRights.ReadWrite))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
string outputtext;
using (MemoryMappedViewStream stream = mmf.CreateViewStream(0,0))
{
XmlSerializer deserializer = new XmlSerializer(typeof(MyObject));
TextReader textReader = new StreamReader(stream);
outputtext = textReader.ReadToEnd();
textReader.Close();
}
mutex.ReleaseMutex();
return outputtext; //ends up in a textbox for debugging
}
Program B
using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting("testmap", MemoryMappedFileRights.ReadWrite))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream(0, 0))
{
XmlSerializer serializer = new XmlSerializer(typeof(MyObject));
TextWriter textWriter = new StreamWriter(stream);
serializer.Serialize(textWriter, myObjectToExport);
textWriter.Flush();
}
mutex.ReleaseMutex();
}
Assuming length is reasonably small, you could really clear it out
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
textWriter.BaseStream.Write(new byte[length], 0, length);
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
EDIT: I think I misunderstood the OP's question. The problem he was having was not with clearing the contents of the MMF, but with stream manipulation. This should fix the problem:
textWriter.BaseStream.Seek(0, System.IO.SeekOrigin.Begin);
textWriter.Write("");
textWriter.Flush();
That being said, you might want to do both.
I haven't really worked with MemoryMappedStreams much but this question seemed interesting so I took a crack at it. I wrote a really basic windows example with two buttons (read/write) and a single text box. I didn't pass in "0, 0" to the CreateViewStream calls and I created the file with a fixed length using a call to "CreateOrOpen" and everything worked well! The following are the key pieces of code that I wrote:
WRITE The File
// create the file if it doesn't exist
if (sharedFile == null) sharedFile = MemoryMappedFile.CreateOrOpen("testmap", 1000, MemoryMappedFileAccess.ReadWrite);
// process safe handling
Mutex mutex = new Mutex(false, "testmapmutex");
if (mutex.WaitOne()) {
try {
using (MemoryMappedViewStream stream = sharedFile.CreateViewStream()) {
var writer = new StreamWriter(stream);
writer.WriteLine(txtResult.Text);
writer.Flush();
}
}
finally { mutex.ReleaseMutex(); }
}
READ The File
// create the file if it doesn't exist
if (sharedFile == null) sharedFile = MemoryMappedFile.CreateOrOpen("testmap", 1000, MemoryMappedFileAccess.ReadWrite);
// process safe handling
Mutex mutex = new Mutex(false, "testmapmutex");
if (mutex.WaitOne()) {
try {
using (MemoryMappedViewStream stream = sharedFile.CreateViewStream()) {
var textReader = new StreamReader(stream);
txtResult.Text = textReader.ReadToEnd();
textReader.Close();
}
}
finally { mutex.ReleaseMutex(); }
}
Dispose the file (after finished)
if (sharedFile != null) sharedFile.Dispose();
For the full example, see here: https://github.com/goopyjava/memory-map-test. Hope that helps!
EDIT/NOTE - If you look at the example provided you can write to the file as many times as you want and any time you read you will read exactly/only what was written last. I believe this was the original goal of the question.
I want to load an SWF object from a Memory Stream or a byte array instead of a file on disk.
AxShockwaveFlash class provides methods and properties to load an SWF providing its path to disk as a string but I haven't seen another way of doing it. There is an InlineData property but generally the class is undocumented and I don't know what this property does. Can it be done at all?
Thanks
F
I assume what you are wanting to do is initialize this in C# rather than in Flash itself. It can be done but there are limitations to doing it (for example you may get weird security issues). Another caveat is this has only been tested on VS 2010/Flash 10 but it should work in any version in theory.
Okay, let us assume you have used the standard mechanism to put your flash control on the form. Also add the flash file you want to the resources (or an inline byte array, up to you).
Then use the following code to load the flash file.
private void InitFlashMovie(AxShockwaveFlash flashObj, byte[] swfFile)
{
using (MemoryStream stm = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(stm))
{
/* Write length of stream for AxHost.State */
writer.Write(8 + swfFile.Length);
/* Write Flash magic 'fUfU' */
writer.Write(0x55665566);
/* Length of swf file */
writer.Write(swfFile.Length);
writer.Write(swfFile);
stm.Seek(0, SeekOrigin.Begin);
/* 1 == IPeristStreamInit */
flashObj.OcxState = new AxHost.State(stm, 1, false, null);
}
}
}
Pass the form's flash object and the byte array containing the flash file to load and it should work.
you are the manthank you very much i was searching for it but didn't find except for native c++, i tried it and it worked well
private:void initflash(AxShockwaveFlashObjects::AxShockwaveFlash^ax,array<System::Byte>^data)
{
MemoryStream^ ms=gcnew MemoryStream();
BinaryWriter^ bwr=gcnew BinaryWriter(ms);
bwr->Write(8+data->Length);
bwr->Write(0x55665566);
bwr->Write(data->Length);
bwr->Write(data);
ms->Seek(0,SeekOrigin::Begin);
ax->OcxState=gcnew AxHost::State(ms,1,false,nullptr);
bwr->Close();
delete bwr;
ms->Close();
delete ms;
}
private: System::Void Form1_Load(System::Object^ sender, System::EventArgs^ e) {
axShockwaveFlash1->FlashVars="indextext=courses/en0600000000/launch.text.xml&cid=0600000000&l1=en&l2=none";
array<System::Byte>^data= File::ReadAllBytes("F:\\Learning\\unformated\\New Folder (3)\\CCNA\\theme\\index.swf");
initflash(axShockwaveFlash1,data);
SubclassHWND^ s=gcnew SubclassHWND();
s->AssignHandle(axShockwaveFlash1->Handle);
}
the last two lines i'm using the following class to prevent right click menu
ref class SubclassHWND :public NativeWindow
{
public:
SubclassHWND(){}
protected:
virtual void WndProc(Message %m) override
{
if(m.Msg==0x204)
{
return;
}
NativeWindow::WndProc(m);
}
};
VB.net Version:
first add reference to AxShockwaveFlash
Then add this code.
Dim swfFile As Byte() = IO.File.ReadAllBytes("C:\Users\root\source\file.swf")
Using stm As MemoryStream = New MemoryStream()
Using writer As BinaryWriter = New BinaryWriter(stm)
writer.Write(8 + swfFile.Length)
writer.Write(&H55665566)
writer.Write(swfFile.Length)
writer.Write(swfFile)
stm.Seek(0, SeekOrigin.Begin)
AxShockwaveFlash1.OcxState = New AxHost.State(stm, 1, False, Nothing)
End Using
End Using