Have been searching info about it on Google, but haven't found neither the way to do this, nor why is it impossible. My aim is to make a C# app, which would record the sound of a single window, while another window can make any kind of noise - the app would save only the sound of the first.
Is there any way to record single window loopback in C#? And does Windows allow it?
I have wrotten code using NAudio that is recording all loopback sound on 1 track. Have a look.
private void btnRec_Click(object sender, RoutedEventArgs e)
{
//Setting up dialog to create WAV file
var destFileDialog = new Microsoft.Win32.SaveFileDialog();
destFileDialog.Filter = "Wave files | *.wav";
destFileDialog.ShowDialog();
destFileName = destFileDialog.FileName;
//Creating capturer and file writer to save captured info
capture = new WasapiLoopbackCapture();
var writer = new WaveFileWriter(destFileName, capture.WaveFormat);
//Setting capturer behaviour
capture.DataAvailable += async (s, a) =>
{
if (writer != null)
{
await writer.WriteAsync(a.Buffer, 0, a.BytesRecorded);
await writer.FlushAsync();
}
};
capture.RecordingStopped += (s, a) =>
{
if (writer != null)
{
writer.Dispose();
writer = null;
}
btnRec.IsEnabled = true;
capture.Dispose();
};
//Disabling button to prevent exception
btnRec.IsEnabled = false;
btnStop.IsEnabled = true;
capture.StartRecording();
}
Related
Here is my code:
var w = new WaveOut();
var wf = new Mp3FileReader(new MemoryStream(Resources.click));
w.Init(wf);
w.Play();
It will play the audio once, If I Play it again, it will not play it. If I want to make a new Mp3FileReader every time to play the audio, the memory usage of my program will grow more and more.
I just need to use one WaveOut and play the sound in multiple threads as I can. possible?
I have found the solution, we can reset the memory stream position to play the audio again without memory usage:
audio = new Mp3FileReader(new MemoryStream(Resources.click)); // audio is global
player = new WaveOut(); // player is also global
player.Init(audio);
then whenever we need to play the sound we will do this:
audio.Seek(0, SeekOrigin.Begin);
player.Play();
Try wrapping the code in a using block:
using (var stream = new MemoryStream(Resources.click))
{
var wf = new Mp3FileReader(stream);
var w = new WaveOut();
w.Init(wf);
w.Play();
//w.Dispose(); //maybe? maybe not?
}
This will make sure the the object is disposed properly
Edit:
new day new hope!
as described here:
https://csharp.hotexamples.com/de/examples/NAudio.Wave/WaveOut/Play/php-waveout-play-method-examples.html
public void PlaySound(string name, Action done = null)
{
FileStream ms = File.OpenRead(_soundLibrary[name]);
var rdr = new Mp3FileReader(ms);
WaveStream wavStream = WaveFormatConversionStream.CreatePcmStream(rdr);
var baStream = new BlockAlignReductionStream(wavStream);
var waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback());
waveOut.Init(baStream);
waveOut.Play();
var bw = new BackgroundWorker();
bw.DoWork += (s, o) =>
{
while (waveOut.PlaybackState == PlaybackState.Playing)
{
Thread.Sleep(100);
}
waveOut.Dispose();
baStream.Dispose();
wavStream.Dispose();
rdr.Dispose();
ms.Dispose();
if (done != null) done();
};
bw.RunWorkerAsync();
}
I'm trying to capture my computer's audio using NAudio.WasapiLoopbackCapture, then save it to a Wavstream then pipe that into a SpeechRecognitionEngine. I can easily save my loopback audio to a wav/mp3 file. I can easily test a simple voice recognition with SRE. Can I simple chain them together using:
using (WasapiCapture capture = new WasapiLoopbackCapture())
{
audioStream = new MemoryStream();
//create a wavewriter to write the data to
//using (var w = new WaveFileWriter("C:\\users\\matth\\documents\\dump.wav", capture.WaveFormat))
using (var w = new WaveFileWriter(audioStream, capture.WaveFormat))
using (var engine = new SpeechRecognitionEngine())
{
//setup an eventhandler to receive the recorded data
capture.DataAvailable += (s, e) =>
{
//save the recorded audio
w.Write(e.Buffer, 0, e.BytesRecorded);
};
engine.SpeechRecognized += (s, e) =>
{
Console.WriteLine(e.Result.Text);
};
engine.SpeechRecognitionRejected += (s, e) =>
{
Console.WriteLine(e.Result.Text);
};
//start recording
capture.StartRecording();
audioStream.Position = 0;
var g = new DictationGrammar();
engine.LoadGrammar(g);
engine.SetInputToWaveStream(audioStream);
engine.RecognizeAsync(RecognizeMode.Multiple);
Console.ReadKey();
//stop recording
capture.StopRecording();
}
}
The execs at my company would like a particular custom control of our ui to make a best-effort at preventing screen capture. I implemented a slick solution using SetWindowDisplayAffinity and DWMEnableComposition at the top Window level of our application to prevent screen capture of the entire app but the previously mentioned execs weren't happy with that. They want only the particular UserControl to prevent screen capture, not the entire app.
The custom control is a .NET 2.0 Windows.Forms.UserControl wrapped in a 4.5 WPF WindowsFormsHost contained by a 4.5 WPF Window control.
Before I tell the execs where to go, I want to be certain there isn't a reasonable way to implement this.
So, my question is: How do I implement screen capture prevention of a .NET 2.0 UserControl?
Thanks
i had an idea:
the user click "PrintScreen", a capture from my program is copy to ClipBoard.
So, all what i need it: to catch cliboard an check if he contains an image.
if yes: i delete the image from clipboard.
the code:
//definition a timer
public static Timer getImageTimer = new Timer();
[STAThread]
static void Main()
{
getImageTimer.Interval = 500;
getImageTimer.Tick += GetImageTimer_Tick;
getImageTimer.Start();
......
}
private static void GetImageTimer_Tick(object sender, EventArgs e)
{
if (Clipboard.ContainsImage())//if clipboard contains an image
Clipboard.Clear();//delete
}
*but it avoid any time program is running. (you need to know if your program is foreground, and then check clipboard);
You might be able to capture the key stroke.
Below is a method I used for that, but it had limited success:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if ((m_parent != null) && m_parent.ScreenCapture(ref msg)) {
return true;
} else {
return base.ProcessCmdKey(ref msg, keyData);
}
}
protected override bool ProcessKeyEventArgs(ref Message msg) {
if ((m_parent != null) && m_parent.ScreenCapture(ref msg)) {
return true;
} else {
return base.ProcessKeyEventArgs(ref msg);
}
}
Returning "True" was supposed to tell the system that the PrintScreen routine was handled, but it often still found the data on the clipboard.
What I did instead, as you can see from the call to m_parent.ScreenCapture, was to save the capture to a file, then display it in my own custom image viewer.
If it helps, here is the wrapper I created for the custom image viewer in m_parent.ScreenCapture:
public bool ScreenCapture(ref Message msg) {
var WParam = (Keys)msg.WParam;
if ((WParam == Keys.PrintScreen) || (WParam == (Keys.PrintScreen & Keys.Alt))) {
ScreenCapture(Environment.GetFolderPath(Environment.SpecialFolder.Desktop));
msg = new Message(); // erases the data
return true;
}
return false;
}
public void ScreenCapture(string initialDirectory) {
this.Refresh();
var rect = new Rectangle(Location.X, Location.Y, Size.Width, Size.Height);
var imgFile = Global.ScreenCapture(rect);
//string fullName = null;
string filename = null;
string extension = null;
if ((imgFile != null) && imgFile.Exists) {
filename = Global.GetFilenameWithoutExt(imgFile.FullName);
extension = Path.GetExtension(imgFile.FullName);
} else {
using (var worker = new BackgroundWorker()) {
worker.DoWork += delegate(object sender, DoWorkEventArgs e) {
Thread.Sleep(300);
var bmp = new Bitmap(rect.Width, rect.Height);
using (var g = Graphics.FromImage(bmp)) {
g.CopyFromScreen(Location, Point.Empty, rect.Size); // WinForm Only
}
e.Result = bmp;
};
worker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e) {
if (e.Error != null) {
var err = e.Error;
while (err.InnerException != null) {
err = err.InnerException;
}
MessageBox.Show(err.Message, "Screen Capture", MessageBoxButtons.OK, MessageBoxIcon.Stop);
} else if (e.Cancelled) {
} else if (e.Result != null) {
if (e.Result is Bitmap) {
var bitmap = (Bitmap)e.Result;
imgFile = new FileInfo(Global.GetUniqueFilenameWithPath(m_screenShotPath, "Screenshot", ".jpg"));
filename = Global.GetFilenameWithoutExt(imgFile.FullName);
extension = Path.GetExtension(imgFile.FullName);
bitmap.Save(imgFile.FullName, ImageFormat.Jpeg);
}
}
};
worker.RunWorkerAsync();
}
}
if ((imgFile != null) && imgFile.Exists && !String.IsNullOrEmpty(filename) && !String.IsNullOrEmpty(extension)) {
bool ok = false;
using (SaveFileDialog dlg = new SaveFileDialog()) {
dlg.Title = "ACP Image Capture: Image Name, File Format, and Destination";
dlg.FileName = filename;
dlg.InitialDirectory = m_screenShotPath;
dlg.DefaultExt = extension;
dlg.AddExtension = true;
dlg.Filter = "PNG Image|*.png|Jpeg Image (JPG)|*.jpg|GIF Image (GIF)|*.gif|Bitmap (BMP)|*.bmp" +
"|EWM Image|*.emf|TIFF Image|*.tif|Windows Metafile (WMF)|*.wmf|Exchangable image file|*.exif";
dlg.FilterIndex = 0;
if (dlg.ShowDialog(this) == DialogResult.OK) {
imgFile = imgFile.CopyTo(dlg.FileName, true);
m_screenShotPath = imgFile.DirectoryName;
ImageFormat fmtStyle;
switch (dlg.FilterIndex) {
case 2: fmtStyle = ImageFormat.Jpeg; break;
case 3: fmtStyle = ImageFormat.Gif; break;
case 4: fmtStyle = ImageFormat.Bmp; break;
case 5: fmtStyle = ImageFormat.Emf; break;
case 6: fmtStyle = ImageFormat.Tiff; break;
case 7: fmtStyle = ImageFormat.Wmf; break;
case 8: fmtStyle = ImageFormat.Exif; break;
default: fmtStyle = ImageFormat.Png; break;
}
ok = true;
}
}
if (ok) {
string command = string.Format(#"{0}", imgFile.FullName);
try { // try default image viewer
var psi = new ProcessStartInfo(command);
Process.Start(psi);
} catch (Exception) {
try { // try IE
ProcessStartInfo psi = new ProcessStartInfo("iexplore.exe", command);
Process.Start(psi);
} catch (Exception) { }
}
}
}
}
I wrote that years ago, and I don't work there now. The source code is still in my Cloud drive so that I can leverage skills I learned once (and forgot).
That code isn't meant to get you completely done, but just to show you a way of doing it. If you need any help with something, let me know.
Not a silver bullet, but could form part of a strategy...
If you were worried about the windows snipping tool or other known snipping tools, you could subscribe to Windows events to capture a notification of when SnippingTool.exe was launched and then hide your application until it was closed:
var applicationStartWatcher = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
applicationStartWatcher.EventArrived += (sender, args) =>
{
if (args.NewEvent.Properties["ProcessName"].Value.ToString().StartsWith("SnippingTool"))
{
Dispatcher.Invoke(() => this.Visibility = Visibility.Hidden);
}
};
applicationStartWatcher.Start();
var applicationCloseWatcher = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace"));
applicationCloseWatcher.EventArrived += (sender, args) =>
{
if (args.NewEvent.Properties["ProcessName"].Value.ToString().StartsWith("SnippingTool"))
{
Dispatcher.Invoke(() =>
{
this.Visibility = Visibility.Visible;
this.Activate();
});
}
};
applicationCloseWatcher.Start();
You will probably need to be running as an administrator for this code to work.
ManagementEventWatcher is in the System.Management assembly.
This coupled with some strategy for handling the print screen key: for example something like this may at least make it difficult to screen capture.
I'm having some issues with naudio and saving sound recordings. The code I currently have works to the point where it saves the wav file, but when I open it up, Windows Media Player returns an error: "Windows Media Player encountered a problem while playing the file"
I have two buttons, a "Record" button, which turns into the stop button after it's pressed. And I have a "Save" button which when clicked, saves the recording to sample.wav.
NAudio.Wave.WaveIn sourceStream = null;
NAudio.Wave.DirectSoundOut waveOut = null;
NAudio.Wave.WaveFileWriter waveWriter = null;
private void recordButton_Click(object sender, EventArgs e)
{
int deviceNumber = sourceList.SelectedItems[0].Index;
sourceStream = new NAudio.Wave.WaveIn();
sourceStream.DeviceNumber = deviceNumber;
sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(44100, NAudio.Wave.WaveIn.GetCapabilities(deviceNumber).Channels);
NAudio.Wave.WaveInProvider waveIn = new NAudio.Wave.WaveInProvider(sourceStream);
waveOut = new NAudio.Wave.DirectSoundOut();
waveOut.Init(waveIn);
sourceStream.StartRecording();
waveOut.Play();
recordButton.Visible = false;
stopRecord.Visible = true;
}
private void saveResponse_Click(object sender, EventArgs e)
{
int deviceNumber = sourceList.SelectedItems[0].Index;
string saveLocation = "c:\\wav\\sample.wav";
sourceStream = new NAudio.Wave.WaveIn();
sourceStream.DeviceNumber = deviceNumber;
sourceStream.WaveFormat = new NAudio.Wave.WaveFormat(44100, NAudio.Wave.WaveIn.GetCapabilities(deviceNumber).Channels);
sourceStream.DataAvailable += new EventHandler<NAudio.Wave.WaveInEventArgs>(sourceStream_DataAvailable);
waveWriter = new NAudio.Wave.WaveFileWriter(saveLocation, sourceStream.WaveFormat);
sourceStream.StartRecording();
MessageBox.Show("Recording successfully saved.");
}
private void sourceStream_DataAvailable(object sender, NAudio.Wave.WaveInEventArgs e)
{
if (waveWriter == null) return;
waveWriter.WriteData(e.Buffer, 0, e.BytesRecorded);
waveWriter.Flush();
}
private void stopRecord_Click(object sender, EventArgs e)
{
if (waveOut != null)
{
waveOut.Stop();
waveOut.Dispose();
waveOut = null;
}
if (sourceStream != null)
{
sourceStream.StopRecording();
sourceStream.Dispose();
sourceStream = null;
}
if (waveWriter != null)
{
waveWriter.Dispose();
waveWriter = null;
}
recordButton.Visible = true;
stopRecord.Visible = false;
saveResponse.Enabled = true;
}
Your recordButton_Click code isn't recording, it's piping data from a WaveIn to a WaveOut, which will play the data coming from your source (microphone) directly to the output (speakers). It doesn't retain that data for later use, it just pipes it from one to the other. If you want to subsequently save that data to disk, you need to buffer it yourself.
The saveResponse_Click on the other hand is starting the direct recording of data from the microphone to a wave file on disk. If you click your Save Response button, wait for a bit, then click your Stop button, you should get a recorded wave file.
If you want to record directly to disk, this is fine. If you want to record to memory, then optionally write to disk, then you need to save the data as it comes in. Perhaps use a memory stream to hold the data while recording, then write that to the WaveFileWriter when it comes time to save the file.
Here's the code I used for testing direct recording to a wave file on disk:
public WaveIn waveSource = null;
public WaveFileWriter waveFile = null;
private void StartBtn_Click(object sender, EventArgs e)
{
StartBtn.Enabled = false;
StopBtn.Enabled = true;
waveSource = new WaveIn();
waveSource.WaveFormat = new WaveFormat(44100, 1);
waveSource.DataAvailable += new EventHandler<WaveInEventArgs>(waveSource_DataAvailable);
waveSource.RecordingStopped += new EventHandler<StoppedEventArgs>(waveSource_RecordingStopped);
waveFile = new WaveFileWriter(#"C:\Temp\Test0001.wav", waveSource.WaveFormat);
waveSource.StartRecording();
}
private void StopBtn_Click(object sender, EventArgs e)
{
StopBtn.Enabled = false;
waveSource.StopRecording();
}
void waveSource_DataAvailable(object sender, WaveInEventArgs e)
{
if (waveFile != null)
{
waveFile.Write(e.Buffer, 0, e.BytesRecorded);
waveFile.Flush();
}
}
void waveSource_RecordingStopped(object sender, StoppedEventArgs e)
{
if (waveSource != null)
{
waveSource.Dispose();
waveSource = null;
}
if (waveFile != null)
{
waveFile.Dispose();
waveFile = null;
}
StartBtn.Enabled = true;
}
I have a simple Winforms app that allows users to select multiple videos (files) simultaneously and runs background workers threads to loop through each of the videos in the BW. Have pasted code below, I get a NullReferenceException as "Unable to create capture from ..." at this line
Capture _capture = new Capture(videoFileName)
in processVideo method.
N.B: The same code work fine if I select a single video. So some issue with the multiple instances of Capture class.
I would expect the ProcessVideo method to have new instance of Capture and open it separately. Any ideas on what I might be doing wrong?
private void openVideoToolStripMenuItem_Click(object sender, EventArgs e)
{
try
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "Video | *.AVI;*.MPEG;*.WMV;*.MP4;*.MOV;*.MPG;*.MPEG;*.MTS;*.FLV";
ofd.Multiselect = true;
if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
string[] videos = ofd.FileNames;
if (videos != null)
{
BackgroundWorker[] bw = new BackgroundWorker[videos.GetLength(0)];
int n = 0;
foreach (string video in videos)
{
bw[n] = new BackgroundWorker();
bw[n].DoWork += new DoWorkEventHandler(bw_DoWork);
bw[n++].RunWorkerAsync(video);
}
}
}
}
catch (NullReferenceException excpt)
{
MessageBox.Show(excpt.Message);
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
string filename = (string)e.Argument;
ProcessVideo(filename);
}
private void ProcessVideo(string videoFileName)
{
Capture _capture = new Capture(videoFileName);
UInt64 TOTAL_FRAMES = Convert.ToUInt64(_capture.GetCaptureProperty(Emgu.CV.CvEnum.CAP_PROP.CV_CAP_PROP_FRAME_COUNT));
for (UInt64 n = 0; n < TOTAL_FRAMES; n++)
{
using (Image<Bgr, Byte> img1 = _capture.QueryFrame())
{
//do something with the frame
}
}
}
I suggest you to update Sourcesafe service pack
it may help you
[I think you code is perfect there is
nothing wrong in it.
You got an error while creating object it clearly saw that
there may be chance that file format is not supported
or may be internal error problem.]
Let me know that after doing updation it works or not.
Regards Red