November 17, 2012, 6:53 am
Hi, I'm programming an Application which records music from Spotify in C#. There is a timer which checks every few milliseconds if the title of the Spotify window has changed. If it has changed, Spotify is playing a new track and the programm has to save
the old recording and start a new one.
This is working pretty well, but Windows Media player cannot open the saved file and the vlc player also has problems.
But every tutorial i read saved the file like I did. I'm new to C# and don't know how I can solve this.
Here is the code.
using System;using System.Text;using System.Windows.Forms;using System.Runtime.InteropServices;using System.Diagnostics;using NAudio.Wave;using System.Collections.Generic;namespace Spotify_Recorder
{publicpartialclass MainForm : Form
{privatestring currentSongTitle;privatestring oldSongTitle;bool recording;bool rippingStarted;private List<WaveInCapabilities> recSources;private WaveIn waveIn;private WaveFileWriter waveWriter;public MainForm()
{
InitializeComponent();
recording = false;
rippingStarted = false;
}
[DllImport("user32.dll", SetLastError = true)]staticextern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]internalstaticexternint GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, int nMaxCount);privatevoid MainForm_Load(object sender, EventArgs e)
{//Load Devices
recSources = new List<WaveInCapabilities>();for (int i = 0; i < WaveIn.DeviceCount; i++)
{
recSources.Add(WaveIn.GetCapabilities(i));
}if (recSources.Count == 0)
{
MessageBox.Show("Du hast keine Aufnahmeger�te angeschlossen");this.Close();
}foreach (WaveInCapabilities source in recSources)
{
cb_RecordDevice.Items.Add(source.ProductName);
}
cb_RecordDevice.SelectedIndex = 0;
}
privatevoid startRecording()
{if (!recording)
{int deviceNumber = cb_RecordDevice.SelectedIndex;
waveIn = new WaveIn();
waveIn.DeviceNumber = deviceNumber;
waveIn.WaveFormat = new WaveFormat(88200, WaveIn.GetCapabilities(deviceNumber).Channels);
waveIn.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);//string path = folderBrowserDialog.SelectedPath + "\\" + currentSongTitle + ".wav";string path = @"D:\Users\Manuel\Desktop\_rec\test.wav";
waveWriter = new WaveFileWriter(path, waveIn.WaveFormat);
waveIn.StartRecording();
}
}
privatevoid waveIn_DataAvailable(object sender, WaveInEventArgs e)
{if (waveWriter == null) return;
waveWriter.Write(e.Buffer, 0, e.BytesRecorded);
waveWriter.Flush();
}
privatevoid stopRecording()
{if (recording)
{
waveIn.StopRecording();
waveIn.Dispose();
waveIn = null;
waveWriter.Dispose();
waveWriter = null;
gb_config.Enabled = true;
timer_checkTitle.Enabled = false;
recording = false;
lb_recordedFiles.Items.Add(oldSongTitle);
}
}privatestring getCurrentSongTitle()
{
Process[] processes = Process.GetProcessesByName("spotify");if (processes.Length > 0)
{
Process p = processes[0];
IntPtr pFoundWindow = p.MainWindowHandle;
StringBuilder stringBuilder = new StringBuilder(256);
GetWindowText(pFoundWindow, stringBuilder, stringBuilder.Capacity);string name = stringBuilder.ToString();if (name.Length > 10)
{
name = name.Substring(10);return name;
}return"";
}else
{
MessageBox.Show("Spotify muss ge�ffnet sein.");this.Close();return"";
}
}privatevoid btn_changeOutput_Click(object sender, EventArgs e)
{
DialogResult rs = folderBrowserDialog.ShowDialog();if (rs == DialogResult.OK)
{
lbl_output.Text = folderBrowserDialog.SelectedPath;
btn_openOutputFolder.Enabled = true;
}
}privatevoid btn_record_Click(object sender, EventArgs e)
{if (!rippingStarted)
{if (folderBrowserDialog.SelectedPath == "")
{
MessageBox.Show("Bitte w�hle einen Ausgabeordner.");return;
}
timer_checkTitle.Enabled = true;
btn_record.Text = "Aufnahme beenden";
rippingStarted = true;
}else
{if (recording)
{
stopRecording();
}
timer_checkTitle.Enabled = false;
btn_record.Text = "Aufnahme starten";
rippingStarted = false;
}
}privatevoid timer_checkTitle_Tick(object sender, EventArgs e)
{
currentSongTitle = getCurrentSongTitle();if (currentSongTitle != oldSongTitle)
{if (recording)
{
stopRecording();
}if (currentSongTitle != "")
{
startRecording();
}
}
oldSongTitle = currentSongTitle;
}privatevoid btn_openOutputFolder_Click(object sender, EventArgs e)
{
Process.Start(folderBrowserDialog.SelectedPath);
}
}
}
↧
November 17, 2012, 6:59 am
you'll need to dispose your waveFileWriter before you can play it. That's because it needs to set some bytes in the WAV header saying how long the DATA and RIFF chunks are, and it does this when you Dispose.
Mark
↧
↧
November 17, 2012, 7:25 am
Thats the reason, thank you.
For some reason stopRecording() is never called. It won't be difficult to fix that.
↧
November 17, 2012, 8:10 am
There was an recording = true missing in startRecording(), now the files works.
But I've got a new Problem: If the file name contains a ":" I get an NotSupportedException. Many titles contain a ":". Is there a way to solve this or should I remove them from the path.
↧
November 17, 2012, 8:35 am
yes, you must remove them from the path
↧
↧
November 19, 2012, 12:18 am
Hi Mark,
I am very thankful for all code and help you provided. Since thursday I am able to play that data flawlessly. So please understand that when we talk about that issue it is not to help me, but to improve NAudio. Let's not talk about my files, sample rates
or headers. I want to inform you that I hear stumbling if I use a block align which is not a divider of 2880 (1024 for example). You may use this information or not, it is your choice.
Bye
↧
November 19, 2012, 12:34 am
Thank you for that great hint, that solved my problem! And thank you for your fast response!
↧
November 19, 2012, 2:27 am
Second constructor in CueList file is missing the initialization of the cues member.
"private List<Cue> cues = new List<Cue>();"
This produces a null reference exception when trying to use the CueWaveFileReader.
I suggest you initialize the list inline instead.
NAudio-Source\NAudio\Wave\WaveStreams\CueList.cs, line 92
from
private List<Cue> cues;
to
private List<Cue> cues = new List<Cue>();
↧
November 19, 2012, 4:55 am
When I using ResamplerDmoStream and selecting Anything except WaveOutput (e.g. WASAPI, DirectSound) I've getting below exception:
Unable to cast COM Object 'NAudio.DMO.ResamplerMediaComObject' to interface type 'Naudio.Dmo.IMediaObject' ...
↧
↧
November 19, 2012, 10:08 am
Hi people,
I'm new to NAudio, well... more especifically, to Audio programming in general.
I already did somethings with NAudio like load a .wav file, read the content of it, plot it like a waveform, etc, but now, i want to do a thing I dont know how...
I'm studying about it on Google, etc... anyway, if the question and answer was here on the Comunnity, I think it would be a good contribution to many users.
I want to interpret some patterns in Musics. For example:
I have a eletronic music (that's just a example) where I have a pattern that gives me beats like "tucs tucs tucs tucs".
How can I reach this parts on the Music and point the time it occurs?
I want to use the same thought to anothers patterns, the basic concept I think would be the same...
Anyone can give me a help around this?
Best regards, thanks.
↧
November 19, 2012, 3:32 pm
Thanks Mark.
I finally made some progress on this. I can get the right offset numbers to display in a combobox, but I don't know how to convert that to read what I want it to.
Does this look correct?
Dim mycount AsNew AsioOutFor i AsInteger = 0 To mycount.DriverOutputChannelCount - 2 Step +2
ComboBox1.Items.Add(i)
Next
↧
November 20, 2012, 3:07 am
Hi,
I have to make audio play back in FORM without using User Control, from where you are loading "outputDevicePlugins". You have passed outputDevicePlugins parameter where it calls the user control.
↧
November 20, 2012, 4:30 am
No need to copy that example, as it is intended to show all possible output modes.
Just do something like:
this.player = new WaveOut();
var reader = new AudioFileReader(filename);
player.Init(reader);
player.Play();
↧
↧
November 20, 2012, 7:03 am
It sounds like you want a beat detection algorithm. NAudio does not come with one, but there are some
other libraries that can do this. Unfortunately, you're unlikely to find something for .NET, so you'll need to port it or write an interop wrapper for it if you want to use it with NAudio
↧
November 20, 2012, 7:04 am
Well, I really put all the NAudio-related code in my original post. I admit I simply tried to follow the tutorials listed here (which point to a blog). Not sure what "callback model" I use, but, the code is:
waveInStream.DataAvailable += new EventHandler<WaveInEventArgs>(SendCaptureSamples);
So sounds like I should look into WaveInEvent then, instead of DataAvailable. I certainly can try that, do you have a pointer to either an example or any other doc I can check?
↧
November 20, 2012, 7:04 am
sure, if your combo box only needs to display a number. You'd normally use a String.Format to do something like String.Format("Channels {0}-{1}", i, i+1)
then use the selected combo box index to work out the offset you want to use when opening Asio for playback.
↧
November 20, 2012, 7:07 am
No, your original post is missing where where you create waveInStream. Somewhere in your code there should be a line that starts with waveInStream =
what is on the other side of the equals statement?
try changing it to waveInStream = new WaveInEvent();
↧
↧
November 20, 2012, 7:58 am
Ah, of course, you're right. I had that in the declarations:
WaveIn waveInStream = new WaveIn();
I can change that, is there anything else I'd need to change? Again, if this is already documented somewhere else, just point me there.
Thanks for the help!
↧
November 20, 2012, 8:01 am
No, just change that and see if it helps. Only thing to note is that now the DataAvailable event will be coming in on a background thread, so don't try to write to the GUI without doing a BeginInvoke on your form.
↧
November 20, 2012, 10:14 am
Great API! I am having 2 small problems, I get gaps when the WPF gui I created does a big visual tree rebuild on changing tabs. Once the tab is built the problem goes away. The second problem may be related, but we did a comparison of playing the same mp3
on multiple computers and the playbacks get out of phase and wander up to a second in a 20 minute mp3. This happens if you do the same test using Windows Multimedia player on multiple PCs.
What is the best strategy to avoid audio gaps and try to maintain a consistent play rate?
Here is how I'm playing the MP3, followed by how its being opened.
publicvoid Play()
{if (!CanPlay)return;if (waveOutDevice != null)
{
waveOutDevice.Play();
}
IsPlaying = true;
CanPause = true;
CanPlay = false;
CanStop = true;
}publicbool OpenFile( string path )
{
CloseFile();if (!System.IO.File.Exists(path))returnfalse;try
{
waveOutDevice = new WaveOut()
{
DesiredLatency = 100,
NumberOfBuffers = 64,
};
ActiveStream = new Mp3FileReader(path);
inputStream = new WaveChannel32(ActiveStream);
sampleAggregator = new SampleAggregator(4096);
inputStream.Sample += inputStream_Sample;
waveOutDevice.Init(inputStream);
ChannelLength = inputStream.TotalTime.TotalSeconds;
CanPlay = true;
GenerateWaveformData(path);
}
catch(Exception ex)
{
ActiveStream = null;
CanPlay = false;
log.ErrorFormat("Can not play wav file {0} - {1}", path, ex.Message);
}return CanPlay;
}
Thanks for any advice,
Neil
↧