Лучший способ воспроизводить MIDI-звуки с помощью C#
Вопрос
Я пытаюсь восстановить старое приложение метронома, которое изначально было написано с использованием MFC
на C++ для написания .NET
с использованием C#
.Одна из проблем, с которой я столкнулся, - это воспроизведение миди-файлов, которые используются для представления «щелчков» метронома.
Я нашел в Интернете несколько статей об игре MIDI
в .NET, но большинство из них, похоже, полагаются на собственные библиотеки, которые кто-то собрал и сделал доступными.Я не прочь воспользоваться такими, но лучше разберусь сам, как это делается, раз уж вроде так должен быть в основном тривиальным упражнением.
Итак, я что-то упускаю?Или просто сложно использовать MIDI внутри приложения .NET?
Решение
Я думаю, вам нужно будет выполнить p/invoke API Windows, чтобы иметь возможность воспроизводить MIDI-файлы из .net.
Эта статья о кодовом проекте хорошо объясняет, как это сделать:Статья vb.net о воспроизведении MIDI-файлов
Чтобы переписать это на C#, вам понадобится следующий оператор импорта для mciSendString:
[DllImport("winmm.dll")]
static extern Int32 mciSendString(String command, StringBuilder buffer,
Int32 bufferSize, IntPtr hwndCallback);
Надеюсь это поможет. Удачи!
Другие советы
В данный момент я работаю над MIDI-приложением на C#, и остальные правы - для этого вам нужно использовать p/invoke.Я использую свой собственный, поскольку он кажется более подходящим для приложения (мне нужен только небольшой набор MIDI-функций), но для ваших целей MIDI-инструментарий C# возможно, подойдет лучше.По крайней мере, это лучшая MIDI-библиотека .NET, которую я нашел, и я тщательно ее искал, прежде чем приступить к проекту.
миди-точка-сетка заставил меня приступить к работе за считанные минуты - легкий и подходящий по размеру для моего домашнего проекта.Он также доступен на GitHub.(Не путать с ранее упомянутым МИДИ.НЕТ, что тоже выглядит многообещающе, просто у меня до этого руки не дошли.)
Конечно НАудио (также упомянутый выше) имеет массу возможностей, но, как и в оригинальном постере, я просто хотел сделать несколько заметок и быстро прочитать и понять исходный код.
Я не могу утверждать, что много знаю об этом, но я не думаю, что все так просто – Карл Франклин из ДотНетРокс известность сыграла с этим не последнюю роль – вы видели его ДНРТВ?
Вы можете использовать медиаплеер:
using WMPLib;
//...
WindowsMediaPlayer wmp = new WindowsMediaPlayer();
wmp.URL = Path.Combine(Application.StartupPath ,"Resources/mymidi1.mid");
wmp.controls.play();
Недавнее дополнение МИДИ.НЕТ который поддерживает Midi-порты, Midi-файлы и SysEx.
Система.Медиа.Саундплеер это хороший и простой способ воспроизведения файлов WAV.Файлы WAV имеют некоторые преимущества перед MIDI, одно из которых заключается в том, что вы можете точно контролировать звучание каждого инструмента (вместо того, чтобы полагаться на встроенный синтезатор компьютера).
Извините, этот вопрос уже немного устарел, но у меня сработало следующее (несколько скопировано из Win32 — цикл MIDI с 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());
}
Приведен полный список командных строк. здесь.Самое интересное в этом то, что вы можете использовать для игры разные вещи, помимо секвенсора. разные вещи, скажем, waveaudio для воспроизведения файлов .wav.Однако я не могу понять, как заставить его воспроизводить .mp3.
Также обратите внимание, что команды остановки и закрытия должны быть отправлены в том же потоке, в котором были отправлены команды открытия и воспроизведения, иначе они не окажут никакого эффекта, и файл останется открытым.Например:
[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);
}
Появляется новый игрок:
https://github.com/atsushieno/managed-midi
https://www.nuget.org/packages/managed-midi/
Документации не так много, но одной из задач этой библиотеки является кроссплатформенная поддержка.
Я думаю, что гораздо лучше использовать какую-нибудь библиотеку с расширенными функциями для воспроизведения MIDI-данных, чем реализовывать ее самостоятельно.Например, с СухойВлажныйМИДИ для воспроизведения MIDI-файла через синтезатор по умолчанию (Синтезатор волновой таблицы Microsoft GS):
using Melanchall.DryWetMidi.Devices;
using Melanchall.DryWetMidi.Smf;
// ...
var midiFile = MidiFile.Read("Greatest song ever.mid");
using (var outputDevice = OutputDevice.GetByName("Microsoft GS Wavetable Synth"))
{
midiFile.Play(outputDevice);
}
Play
заблокирует вызывающий поток до тех пор, пока не будет воспроизведен весь файл.Чтобы управлять воспроизведением MIDI-файла, получите Playback
объект и использовать его Start
/Stop
методы (подробнее см. Воспроизведение статья из библиотеки Вики):
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();