При работе с MIDI в Delphi разработчики часто сталкиваются с проблемой, когда последняя нота в последовательности либо не воспроизводится полностью, либо не звучит вообще. Эта проблема особенно заметна при использовании асинхронных задач и потоков. Давайте разберем причины этого поведения и предложим решения.
Причины проблемы
Основная причина, по которой последняя нота не воспроизводится полностью, связана с тем, что функция midiOutShortMsg не блокирует выполнение кода до окончания воспроизведения ноты. Когда вы сразу после отправки последней ноты закрываете MID-устройство с помощью midiOutClose, система не успевает воспроизвести звук.
В вашем коде:
for n in Notes do begin
MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_ON, n, 127));
Sleep(200);
MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_OFF, n, 127));
end;
MidiOutClose(MO); // Закрытие происходит сразу после последней ноты
Решение проблемы
1. Добавление задержки перед закрытием устройства
Самый простой способ решить проблему - добавить задержку перед закрытием MIDI-устройства:
procedure Music(Notes: TArray<Integer>);
const
MIDI_DEVICE = 0;
MIDI_NOTE_ON = $90;
MIDI_NOTE_OFF = $80;
var
MO: HMIDIOUT;
n: Integer;
begin
MidiOutOpen(@MO, MIDI_DEVICE, 0, 0, CALLBACK_NULL);
try
for n in Notes do begin
MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_ON, n, 127));
Sleep(200);
MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_OFF, n, 127));
end;
Sleep(200); // Добавляем задержку перед закрытием
finally
MidiOutClose(MO);
end;
end;
2. Использование Try-Finally для безопасного закрытия
Как было замечено в комментариях, важно обернуть открытие и закрытие MIDI-устройства в блок try-finally:
Для более точного контроля над временем воспроизведения можно использовать механизм событий:
procedure Music(Notes: TArray<Integer>);
const
MIDI_DEVICE = 0;
MIDI_NOTE_ON = $90;
MIDI_NOTE_OFF = $80;
var
MO: HMIDIOUT;
n: Integer;
begin
MidiOutOpen(@MO, MIDI_DEVICE, 0, 0, CALLBACK_NULL);
try
for n in Notes do begin
MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_ON, n, 127));
TThread.Sleep(200);
MidiOutShortMsg(MO, MIDIEncodeMessage(MIDI_NOTE_OFF, n, 127));
end;
// Ждем завершения всех нот
while MidiOutUnprepareHeader(MO, nil, 0) = MIDIERR_STILLPLAYING do
TThread.Sleep(10);
finally
MidiOutClose(MO);
end;
end;
Почему при уменьшении Sleep ноты перестают звучать?
Когда вы уменьшаете значение Sleep с 200 до 100 мс, система не успевает обработать команды воспроизведения, особенно в сочетании с асинхронным характером midiOutShortMsg. MIDI-устройство имеет ограниченную очередь команд, и при слишком быстрой отправке команд некоторые из них могут теряться.
Рекомендации
Всегда используйте try-finally при работе с MIDI-устройствами
Добавляйте достаточную задержку перед закрытием устройства
Для сложных композиций рассмотрите использование специализированных библиотек, таких как MIDAS или BASS
При работе с потоками используйте TThread.Sleep вместо Sleep для лучшей совместимости
Пример улучшенной версии вашего кода:
procedure TMainForm.RandomBtnClick(Sender: TObject);
begin
TTask.Run(
procedure
begin
try
Music([64, 66, 67, 69, 71]);
except
on E: Exception do
TThread.Synchronize(nil,
procedure
begin
ShowMessage('Ошибка воспроизведения: ' + E.Message);
end);
end;
end);
end;
Эти решения помогут вам избежать проблем с воспроизведением MIDI в Delphi и обеспечат стабильную работу вашего приложения.
Проблема с недостижением последней ноты в MIDI-потоке в Delphi возникает из-за преждевременного закрытия устройства без задержки, что можно исправить добавлением паузы перед `MidiOutClose` или использованием механизма событий для контроля завершения восп
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.