Best way to play MIDI sounds using C# - c#

I'm trying to rebuild an old metronome application that was originally written using MFC in C++ to be written in .NET using C#. One of the issues I'm running into is playing the midi files that are used to represent the metronome "clicks".
I've found a few articles online about playing MIDI in .NET, but most of them seem to rely on custom libraries that someone has cobbled together and made available. I'm not averse to using these, but I'd rather understand for myself how this is being done, since it seems like it should be a mostly trivial exercise.
So, am I missing something? Or is it just difficult to use MIDI inside of a .NET application?

I'm working on a C# MIDI application at the moment, and the others are right - you need to use p/invoke for this. I'm rolling my own as that seemed more appropriate for the application (I only need a small subset of MIDI functionality), but for your purposes the C# MIDI Toolkit might be a better fit. It is at least the best .NET MIDI library I found, and I searched extensively before starting the project.

I think you'll need to p/invoke out to the windows api to be able to play midi files from .net.
This codeproject article does a good job on explaining how to do this:
vb.net article to play midi files
To rewrite this is c# you'd need the following import statement for mciSendString:
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer,
Int32 bufferSize, IntPtr hwndCallback);
Hope this helps - good luck!

midi-dot-net got me up and running in minutes - lightweight and right-sized for my home project. It's also available on GitHub. (Not to be confused with the previously mentioned MIDI.NET, which also looks promising, I just never got around to it.)
Of course NAudio (also mentioned above) has tons of capability, but like the original poster I just wanted to play some notes and quickly read and understand the source code.

I think it's much better to use some library that which has advanced features for MIDI data playback instead of implementing it by your own. For example, with DryWetMIDI (I'm the author of it) to play MIDI file via default synthesizer (Microsoft GS Wavetable Synth):
using Melanchall.DryWetMidi.Devices;
using Melanchall.DryWetMidi.Core;
// ...
var midiFile = MidiFile.Read("Greatest song ever.mid");
using (var outputDevice = OutputDevice.GetByName("Microsoft GS Wavetable Synth"))
{
midiFile.Play(outputDevice);
}
Play will block the calling thread until entire file played. To control playback of a MIDI file, obtain Playback object and use its Start/Stop methods (more details in the Playback article of the library docs):
var playback = midiFile.GetPlayback(outputDevice);
// You can even loop playback and speed it up
playback.Loop = true;
playback.Speed = 2.0;
playback.Start();
// ...
playback.Stop();
// ...
playback.Dispose();
outputDevice.Dispose();

I can't claim to know much about it, but I don't think it's that straightforward - Carl Franklin of DotNetRocks fame has done a fair bit with it - have you seen his DNRTV?

You can use the media player:
using WMPLib;
//...
WindowsMediaPlayer wmp = new WindowsMediaPlayer();
wmp.URL = Path.Combine(Application.StartupPath ,"Resources/mymidi1.mid");
wmp.controls.play();

For extensive MIDI and Wave manipulation in .NET, I think hands down NAudio is the solution (Also available via NuGet).

A recent addition is MIDI.NET that supports Midi Ports, Midi Files and SysEx.

Sorry this question is a little old now, but the following worked for me (somewhat copied from Win32 - Midi looping with MCISendString):
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer, Int32 bufferSize, IntPtr hwndCallback);
public static void playMidi(String fileName, String alias)
{
mciSendString("open " + fileName + " type sequencer alias " + alias, new StringBuilder(), 0, new IntPtr());
mciSendString("play " + alias, new StringBuilder(), 0, new IntPtr());
}
public static void stopMidi(String alias)
{
mciSendString("stop " + alias, null, 0, new IntPtr());
mciSendString("close " + alias, null, 0, new IntPtr());
}
A full listing of command strings is given here. The cool part about this is you can just use different things besides sequencer to play different things, say waveaudio for playing .wav files. I can't figure out how to get it to play .mp3 though.
Also, note that the stop and close command must be sent on the same thread that the open and play commands were sent on, otherwise they will have no effect and the file will remain open. For example:
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer,
Int32 bufferSize, IntPtr hwndCallback);
public static Dictionary<String, bool> playingMidi = new Dictionary<String, bool>();
public static void PlayMidi(String fileName, String alias)
{
if (playingMidi.ContainsKey(alias))
throw new Exception("Midi with alias '" + alias + "' is already playing");
playingMidi.Add(alias, false);
Thread stoppingThread = new Thread(() => { StartAndStopMidiWithDelay(fileName, alias); });
stoppingThread.Start();
}
public static void StopMidiFromOtherThread(String alias)
{
if (!playingMidi.ContainsKey(alias))
return;
playingMidi[alias] = true;
}
public static bool isPlaying(String alias)
{
return playingMidi.ContainsKey(alias);
}
private static void StartAndStopMidiWithDelay(String fileName, String alias)
{
mciSendString("open " + fileName + " type sequencer alias " + alias, null, 0, new IntPtr());
mciSendString("play " + alias, null, 0, new IntPtr());
StringBuilder result = new StringBuilder(100);
mciSendString("set " + alias + " time format milliseconds", null, 0, new IntPtr());
mciSendString("status " + alias + " length", result, 100, new IntPtr());
int midiLengthInMilliseconds;
Int32.TryParse(result.ToString(), out midiLengthInMilliseconds);
Stopwatch timer = new Stopwatch();
timer.Start();
while(timer.ElapsedMilliseconds < midiLengthInMilliseconds && !playingMidi[alias])
{
}
timer.Stop();
StopMidi(alias);
}
private static void StopMidi(String alias)
{
if (!playingMidi.ContainsKey(alias))
throw new Exception("Midi with alias '" + alias + "' is already stopped");
// Execute calls to close and stop the player, on the same thread as the play and open calls
mciSendString("stop " + alias, null, 0, new IntPtr());
mciSendString("close " + alias, null, 0, new IntPtr());
playingMidi.Remove(alias);
}

A new player emerges:
https://github.com/atsushieno/managed-midi
https://www.nuget.org/packages/managed-midi/
Not much in the way of documentation, but one focus of this library is cross platform support.

System.Media.SoundPlayer is a good, simple way of playing WAV files. WAV files have some advantages over MIDI, one of them being that you can control precisely what each instrument sounds like (rather than relying on the computer's built-in synthesizer).

Related

.net c# windows forms, play sound without overlaping [duplicate]

Is there a way to play two sounds at the same time?
I know that SoundPlayer isn't able to do this.
I can't use SoundEffect as I believe it's only part of XNA.
The two required sounds will be called at unknown and random times. The sound needs to be be controlled after it is played. i.e., the sound must be able to be stopped before it has finished playing.
Reference PresentationCore and WindowsBase and try this...
var p1 = new System.Windows.Media.MediaPlayer();
p1.Open(new System.Uri(#"C:\windows\media\tada.wav"));
p1.Play();
// this sleep is here just so you can distinguish the two sounds playing simultaneously
System.Threading.Thread.Sleep(500);
var p2 = new System.Windows.Media.MediaPlayer();
p2.Open(new System.Uri(#"C:\windows\media\tada.wav"));
p2.Play();
EDIT
I received a downvote probably because at first glance this looks like it will play the second sound after the first is finished. It doesn't, they are played by windows asynchronously. The sleep is there so if you test this code verbatim you can hear the sounds play together, it wouldn't be noticeable without the delay since they are the same sound.
This code demonstrates the two sounds playing on separate threads on top of each other, which is sort of pointless since the playback doesn't block anyway
new System.Threading.Thread(() => {
var c = new System.Windows.Media.MediaPlayer();
c.Open(new System.Uri(#"C:\windows\media\tada.wav"));
c.Play();
}).Start();
System.Threading.Thread.Sleep(500);
new System.Threading.Thread(() => {
var c = new System.Windows.Media.MediaPlayer();
c.Open(new System.Uri(#"C:\windows\media\tada.wav"));
c.Play();
}).Start();
http://msdn.microsoft.com/en-us/library/system.windows.media.mediaplayer.stop.aspx
The class also has the control you need to stop playback
The "MediaPlayer" object will not let you play two sounds at once, even if you create two instances. You will need to bring in the native windows API "mciSendString".
[DllImport("winmm.dll")]
static extern Int32 mciSendString(string command, StringBuilder buffer, int bufferSize, IntPtr hwndCallback);
public Form1()
{
InitializeComponent();
mciSendString(#"open C:\Users\Jono\Desktop\applause.wav type waveaudio alias applause", null, 0, IntPtr.Zero);
mciSendString(#"play applause", null, 0, IntPtr.Zero);
mciSendString(#"open C:\Users\Jono\Desktop\foghorn.wav type waveaudio alias foghorn", null, 0, IntPtr.Zero);
mciSendString(#"play foghorn", null, 0, IntPtr.Zero);
}
check PlaySound method here http://msdn.microsoft.com/en-us/library/aa909766.aspx, and its flag SND_ASYNC .
Solution :
Hi,
I was developing a WP8 App and i needed multiple sounds to play simultaneously, the solutions mentioned above didnt work for me, So i used the XNA framwork. here is the link
http://msdn.microsoft.com/en-us/library/ff842408.aspx
and then play ur sound files like this...
SoundEffect Sound = SoundEffect.FromStream(Application.GetResourceStream(new Uri("Assets/Sounds/wav/sound.wav", UriKind.Relative)).Stream);
Sound.Play();
For looping...
SoundEffectInstance Sound = SoundEffect.FromStream(Application.GetResourceStream(new Uri("Assets/Sounds/wav/sound.wav", UriKind.Relative)).Stream).CreateInstance();
Sound.IsLooped = true;
Sound.Play();
Note: the files must be in ".wav" (PCM, 8 or 16-bit, 8KHz to 48KHz, mono or stereo) format
From http://alvas.net/alvas.audio,samples.aspx#sample7 and http://alvas.net/alvas.audio,samples.aspx#sample6
Player pl = new Player();
byte[] arr = File.ReadAllBytes(#"in.wav");
pl.Play(arr);
Player pl2 = new Player();
pl2.FileName = "123.mp3";
pl2.Play();
or mix audio data before playing How to mix to mix two audio file..
private void Mix(string outfile, string infile1, string infile2, int shiftSec)
{
WaveReader wr1 = new WaveReader(File.OpenRead(infile1));
WaveReader wr2 = new WaveReader(File.OpenRead(infile2));
IntPtr format1 = wr1.ReadFormat();
WaveFormat wf = AudioCompressionManager.GetWaveFormat(format1);
WaveWriter ww = new WaveWriter(File.Create(outfile), AudioCompressionManager.FormatBytes(format1));
byte[] data0 = wr1.ReadData(0, shiftSec);
byte[] data1 = wr1.ReadData(shiftSec);
byte[] data2 = wr2.ReadData();
byte[] mixData = AudioCompressionManager.Mix(format1, data2, data1);
ww.WriteData(data0);
ww.WriteData(mixData);
ww.Close();
wr2.Close();
wr1.Close();
}
The accepted answer didn't work for me.
Here is what worked:
Make sure you add references to PresentationCore and WindowsBase dlls.
private void Form1_Load(object sender, EventArgs e)
{
System.Media.SoundPlayer player = new System.Media.SoundPlayer(#"C:\Users\me\source\repos\TacticalIslandSurvival\sounds\blazer rail.wav");
player.PlayLooping();
}
private void button1_MouseEnter(object sender, EventArgs e)
{
var p1 = new System.Windows.Media.MediaPlayer();
p1.Open(new System.Uri(#"C:\Users\me\source\repos\TacticalIslandSurvival\sounds\click.wav"));
p1.Play();
}

Vlc c#, Subtitle, Dokumentation Vlc

I have a question, I read all the documentation, but I do not understand, how can I use all that, I have DB and link from movie and from subtitle to DB, I can go to VLC movie, but I can not get subtitle, I've tried everything possible, if anyone has experience with documentation from VLC, help.
Vlc Documentation
I have tried:
for example:
string movie = "file:///D:/1.avi" + " --sub-file=file:///D:/1.txt"
string movie = "file:///D:/1.avi" + " :sub-file = file:///D:/1.txt"
vlcPlayer.playliste.add (movie);
vlcPlayer.playliste.play ();
everything works, play, pause, stop .... only subtitle is missing
and by
int track = vlcPlayer.subtitle.track.count;
is always -1;
int sub = vlcPlayer.video.subtitle.count;
is always 0;
but by
int sum = vlcPlayer.playlist.count;
is 2 but if i use
vlcPlayer.playlist.playitem (1);
it does not work
Which C# wrapper are you using?
Try libvlc_media_player_add_slave

BASS WASAPI BPMCounter

I want to analyse my default playback device and detect the beats. I've been using the BASS WASAPI to get the FFT data of the selected device with:
int ret = BassWasapi.BASS_WASAPI_GetData(_fft, (int)BASSData.BASS_DATA_FFT2048);
Now I was using the data to generate spectrum data and display this to the user. In addition I want to detect the Beats using the BPMCounter Class from BASS. However as far as I can tell the BPMCounter.ProcessAudio() function requires a stream (which I don't get with WASAPI) in order to work. Is there a ways I can use BPMCounter with WASAPI? Would be great if someone can point me to the right direction. Thanks
Edit:
Tried this to convert the data to a stream, but without success:
int ret = BassWasapi.BASS_WASAPI_GetData(_fft, (int)BASSData.BASS_DATA_FFT2048); //get channel fft data
var chan = Bass.BASS_StreamCreate(0, 44100, BASSFlag.BASS_DEFAULT, BASSStreamProc.STREAMPROC_PUSH);
Bass.BASS_ChannelPlay(chan, false);
Bass.BASS_StreamPutData(chan, _fft, _fft.Length);
bool beat = _count.ProcessAudio(chan, true);
Debug.Write(beat);
beat is always False, however I can see at the Spectrum that the capturing of the FFT Data is corrent.
I've just started playing with this lib a few hours ago and i am still going through the examples. So my answer maybe is not what you want. For my project i also want to transform WASAPI into a stream and use it for a displaying a spectrum. What i did was to create a StreamPush, right after BASS_WASAPI initialization.
To init your WASAPI use this call and this delegate:
private InitWasapi()
{
WASAPIPROC _process = new WASAPIPROC(Process); // Delegate
bool res = BassWasapi.BASS_WASAPI_Init(_YourDeviceNumber, 0, 0, BASSWASAPIInit.BASS_WASAPI_BUFFER, 1f, 0f, _process, IntPtr.Zero);
if (!res)
{
// Do error checking
}
// This is the part you are looking for (maybe!)
// Use these flags because Wasapi needs 32-bit sample data
var info = BassWasapi.BASS_WASAPI_GetInfo();
_stream = Bass.BASS_StreamCreatePush(info.freq, info.chans, BASSFlag.BASS_STREAM_DECODE | BASSFlag.BASS_SAMPLE_FLOAT, IntPtr.Zero);
BassWasapi.BASS_WASAPI_Start();
}
private int Process(IntPtr buffer, int length, IntPtr user)
{
Bass.BASS_StreamPutData(_stream, buffer, length);
return length;
}
Please note: This works, but i am still experimenting. For example i am not getting the same spectrum output as when i create the stream from the music file itself. There are some (small) differences. Maybe it's because i am using a custom EQ in Winamp for playing the same .mp3. So if anyone knows more on this subject, i would like also to hear it!

Xamarin Monotouch Audio Units Callbacks

I have a problem with Audio Units in MonoTouch/Xamarin.
It seems like I can't get a callback on recording, just playback.
I used this example:
https://github.com/xamarin/monotouch-samples/blob/master/AUSoundTriggeredPlayingSoundMemoryBased/ExtAudioBufferPlayer.cs
and looked for Obj C examples. The Obj C examples are pretty much the same like my code so Im a little bit confused about this thing.
The output if running my example is:
INPUT0
Which is the bus number for output.
So the expected output should be:
INPUT1
So my question is: How do I get a recording callback and a playback callback running the same time, or just how do I get a recording callback.
My Code:
void prepareAudioUnit()
{
// AudioSession
AudioSession.Initialize();
AudioSession.Category = AudioSessionCategory.PlayAndRecord;
AudioSession.PreferredHardwareIOBufferDuration = Config.packetLength;
AudioSession.PreferredHardwareSampleRate = Format.samplingRate;
//AudioSession.SetActive (false);
AudioSession.SetActive(true);
Logger.log("HWSR:" + AudioSession.CurrentHardwareSampleRate);
// Getting AudioComponent Remote output
_audioComponent = AudioComponent.FindComponent(AudioTypeOutput.VoiceProcessingIO);
// creating an audio unit instanc
_audioUnit = new AudioUnit(_audioComponent);
// turning on microphone
_audioUnit.SetEnableIO(true,
AudioUnitScopeType.Input,
1 // Remote Input
);
_audioUnit.SetEnableIO(true,
AudioUnitScopeType.Output,
0 // Remote output
);
// setting audio format
_audioUnit.SetAudioFormat(Format.AudioStreamBasicDescription,
AudioUnitScopeType.Output,
1
);
_audioUnit.SetAudioFormat(Format.AudioStreamBasicDescription,
AudioUnitScopeType.Input,
0
);
// setting callback method
_audioUnit.SetRenderCallback(_audioUnit_OutputCallback, AudioUnitScopeType.Global, 0);
_audioUnit.SetRenderCallback(_audioUnit_InputCallback, AudioUnitScopeType.Global, 1);
}
AudioUnitStatus _audioUnit_OutputCallback(AudioUnitRenderActionFlags actionFlags, AudioTimeStamp timeStamp, uint busNumber, uint numberFrames, AudioBuffers data)
{
Logger.log("OUTPUT" + busNumber);
return AudioUnitStatus.NoError;
}
AudioUnitStatus _audioUnit_InputCallback(AudioUnitRenderActionFlags actionFlags, AudioTimeStamp timeStamp, uint busNumber, uint numberFrames, AudioBuffers data)
{
Logger.log("INPUT" + busNumber);
return AudioUnitStatus.NoError;
}
This problem is a bug in Xamarin, they forgot to add a method for InputCallbacks.
I reported the bug but for the people needing the same:
http://nopaste.info/8d0aca98d9.html
Its not good, but it shows how to solve the problem to write a fix yourself till Xamarin updates this.

Reading WMV resolution in C#

I'm trying to read the image size of a WMV file in C#.
I've tried using what is described here:
How do I get the duration of a video file using C#?
but the only attribute that has a value is Duration.
Any ideas ?
Thanks.
Only way I've seen it done is by playing it and attaching to the open event:
static WindowsMediaPlayerClass player;
static void Main()
{
player = new WindowsMediaPlayerClass();
IWMPMedia mediaInfo = player.newMedia("test.wmv");
player.OpenStateChange += new _WMPOCXEvents_OpenStateChangeEventHandler(player_OpenStateChange);
player.currentMedia = mediaInfo;
//...
Console.WriteLine("Done.");
Console.ReadKey();
}
private static void player_OpenStateChange(int state)
{
if (state == (int)WMPOpenState.wmposMediaOpen)
{
Console.WriteLine( "height = " + player.currentMedia.imageSourceHeight);
Console.WriteLine( "width = " + player.currentMedia.imageSourceWidth);
}
}
You'll want to dispose of any resources before exiting.
You use the code from the linked example, but you explicitly do a function call to get the height and width.
Example:
using WMPLib; // this file is called Interop.WMPLib.dll
WindowsMediaPlayerClass wmp = new WindowsMediaPlayerClass();
IWMPMedia mediaInfo = wmp.newMedia("myfile.wmv");
long height, width;
mediaInfo.get_imageSourceHeight(height);
mediaInfo.get_imageSourceWidth(width);
I prefer to use the free NReco.VideoInfo.dll. Mainly because I hate Windows Media Player. I have found that WMP is unreliable.
Here is the download link: http://www.nrecosite.com/video_info_net.aspx It's useful for other stuff too.
var ffProbe = new NReco.VideoInfo.FFProbe();
var videoInfo = ffProbe.GetMediaInfo(pathToFile);
Int32 tmpHeight = videoInfo.Streams[0].Height;
Int32 tmpWidth = videoInfo.Streams[0].Width;

Categories

Resources