Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
KANSoftWare

Почему последняя нота в MIDI-потоке не воспроизводится и как это исправить в Delphi?

Delphi , Мультимедиа , Аудио

 

При работе с 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:

MidiOutOpen(@MO, MIDI_DEVICE, 0, 0, CALLBACK_NULL);
try
  // Ваш код воспроизведения
finally
  MidiOutClose(MO);
end;

3. Альтернативное решение с событиями

Для более точного контроля над временем воспроизведения можно использовать механизм событий:

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-устройство имеет ограниченную очередь команд, и при слишком быстрой отправке команд некоторые из них могут теряться.

Рекомендации

  1. Всегда используйте try-finally при работе с MIDI-устройствами
  2. Добавляйте достаточную задержку перед закрытием устройства
  3. Для сложных композиций рассмотрите использование специализированных библиотек, таких как MIDAS или BASS
  4. При работе с потоками используйте 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




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.


:: Главная :: Аудио ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-12-22 20:14:06
2025-08-06 01:38:41/0.005810022354126/0