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

Разработка модульного проигрывателя на основе OpenMPT библиотеки для Delphi и Pascal: поддержка множества форматов, управление воспроизведением и анализ метаданных модулей.

Delphi , Технологии , COM и DCOM

Я сосредоточусь на ключевых областях: управление памятью, обработка ошибок и интеграция с библиотекой OpenMPT. Я также дам пояснения к фрагментам кода и предложу улучшения.

1. Управление памятью и openmpt_free_string

Вы правильно используете openmpt_free_string для освобождения памяти, выделенной OpenMPT для строк метаданных. Это важно для предотвращения утечек памяти. Функция DisplayMetadata — хороший шаблон.

2. Обработка ошибок

  • Загрузка файла: Функция LoadBinaryFileToBuffer должна включать более надежную обработку ошибок. Вместо простого ShowMessage рассмотрите возможность регистрации ошибок в файле или отображения более информативных сообщений для пользователя.
  • Инициализация OpenMPT: Проверьте возвращаемые значения openmpt_module_create_from_memory2 и других функций OpenMPT. Если они не справляются, обработайте ошибку корректно (например, отобразите сообщение об ошибке, освободите все выделенные ресурсы и предотвратите сбой программы).

3. Инициализация звука и обратный вызов

  • Функция InitAudio хорошо структурирована. Использование TWaveFormatEx и HWAVEOUT корректно.
  • Функция WaveOutCallback также хорошо написана. Она обрабатывает сообщение WOM_DONE и вызывает FillBuff для подготовки следующего буфера к воспроизведению.

4. Обзор кода и улучшения

Вот разбивка с предлагаемыми улучшениями:

unit Unit1;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ComCtrls,
  ExtCtrls, Spin, mmsystem, windows, openmpt;

const
  Channels = 2;
  BitsPerSample = 16;
  SampleRate = 44100;
  BufSize = 8192;
  BufferCount = 2;

type
  TForm1 = class(TForm)
    bt_stop: TButton;
    bt_play: TButton;
    bt_pause: TButton;
    FloatSpinEdit1: TFloatSpinEdit;
    Label1: TLabel;
    Label2: TLabel;
    Memo1: TMemo;
    Timer1: TTimer;
    TrackBar1: TTrackBar;
    procedure bt_pauseClick(Sender: TObject);
    procedure bt_playClick(Sender: TObject);
    procedure bt_stopClick(Sender: TObject);
    procedure FloatSpinEdit1Change(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure TrackBar1Change(Sender: TObject);
  private
    buffers: array[0..BufferCount-1] of array[0..BufSize-1] of SmallInt;
    waveHeaders: array[0..BufferCount-1] of TWaveHdr;
    currentBuffer: Integer;
  public
  end;

var
  Form1: TForm1;
  waveOut: HWAVEOUT;
  waveHeader: TWaveHdr;
  ok_flag : boolean;
  o_mod : Pointer;
  o_mod_paramindex  : Integer;
  o_mod_info : String;
  o_mod_repeat : Integer;
  o_mod_duration, o_mod_position,o_mod_set_pos  : Single;
  ctl : POpenMPTModuleInitialCtl;
  lgfct  : Pointer;
  lgusr : Pointer;
  sc  : Pointer;

implementation

{$R *.lfm}

procedure LoadBinaryFileToBuffer(const FileName: string; var Buffer: TBytes);
var
  MemoryStream: TMemoryStream;
begin
  MemoryStream := TMemoryStream.Create;
  try
    MemoryStream.LoadFromFile(FileName);
    SetLength(Buffer, MemoryStream.Size);
    MemoryStream.ReadBuffer(Buffer[0], MemoryStream.Size);
  except
    on E: Exception do
      ShowMessage('Error loading file: ' + E.Message); // More informative error
  finally
    MemoryStream.Free;
  end;
end;

procedure DisplayMetadata(mod_: Pointer; const key: pchar; memo: TMemo);
var
  answer: pchar;
begin
  answer := openmpt_module_get_metadata(mod_, key);
  if assigned(answer) then
  begin
    if length(answer) > 0 then
      memo.Lines.Add(key + ' : ' + answer);
    openmpt_free_string(answer);
  end
  else
  begin
    memo.Lines.Add('No Data For : ' + key);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Buf: array of Byte;
  FileName: string;
begin
  FileName := 'amegas.mod';
  try
    LoadBinaryFileToBuffer(FileName, Buf);
  except
    on E: Exception do
      ShowMessage('Error loading file: ' + E.Message);
    Exit; // Exit the procedure if loading fails
  end;

  o_mod := openmpt_module_create_from_memory2(@Buf[0], Length(Buf), nil, lgusr, nil, nil, nil, nil, ctl);
  if Assigned(o_mod) then
  begin
    o_mod_repeat := openmpt_module_set_repeat_count(o_mod, -1);
    o_mod_paramindex := openmpt_module_set_render_param(o_mod, 2, 50);

    DisplayMetadata(o_mod, 'tracker', Memo1);
    DisplayMetadata(o_mod, 'type', Memo1);
    DisplayMetadata(o_mod, 'artist', Memo1);
    DisplayMetadata(o_mod, 'title', Memo1);
    DisplayMetadata(o_mod, 'date', Memo1);
    DisplayMetadata(o_mod, 'message', Memo1);

    o_mod_duration := openmpt_module_get_duration_seconds(o_mod);
    Memo1.Lines.Add('Duration: ' + FloatToStrF(o_mod_duration, ffFixed, 8, 2));
  else
    ShowMessage('Failed to create OpenMPT module.');
  end;
end;

procedure TForm1.FormShow(Sender: TObject);
var
  i: Integer;
begin
  InitAudio;
  ok_flag := True;
end;

procedure InitAudio;
var
  wFormat: TWaveFormatEx;
  i: Integer;
begin
  with wFormat do
  begin
    wFormatTag := WAVE_FORMAT_PCM;
    nChannels := Channels;
    nSamplesPerSec := SampleRate;
    wBitsPerSample := BitsPerSample;
    nBlockAlign := (wBitsPerSample * nChannels) div 8;
    nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
    cbSize := 0;
  end;

  if waveOutOpen(@waveOut, WAVE_MAPPER, @wFormat, QWORD(@WaveOutCallback), 0, CALLBACK_FUNCTION) <> MMSYSERR_NOERROR then
  begin
    ShowMessage('Error opening audio device');
    Exit;
  end;

  for i := 0 to BufferCount - 1 do
  begin
    ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
    with Form1.waveHeaders[i] do
    begin
      lpData := @Form1.buffers[i][0];
      dwBufferLength := BufSize * SizeOf(SmallInt);
      dwFlags := 0;
    end;
    waveOutPrepareHeader(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  end;

  Form1.currentBuffer := 0;
  for i := 0 to BufferCount - 1 do
  begin
    FillBuff(i);
    waveOutWrite(waveOut, @Form1.waveHeaders[i], SizeOf(TWaveHdr));
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  if ok_flag then
  begin
    o_mod_position := openmpt_module_get_position_seconds(o_mod);
    Label1.Caption := 'Position: ' + FloatToStrF(o_mod_position, ffFixed, 8, 2);
  end;
end;

procedure TForm1.TrackBar1Change(Sender: TObject);
begin
  if ok_flag then
    o_mod_paramindex := openmpt_module_set_render_param(o_mod, 2, TrackBar1.Position);
end;

procedure TForm1.FloatSpinEdit1Change(Sender: TObject);
begin
  if ok_flag then
    o_mod_set_pos := openmpt_module_set_position_seconds(o_mod, FloatSpinEdit1.value);
end;

procedure TForm1.bt_stopClick(Sender: TObject);
var
  i: integer;
begin
  ok_flag := False;
  // free buffer data !!
  for i := 0 to BufferCount - 1 do
  begin
    ZeroMemory(@Form1.waveHeaders[i], SizeOf(TWaveHdr));
  end;
  waveOutClose(waveOut);
  o_mod_set_pos := openmpt_module_set_position_seconds(o_mod, 0);
  Label1.Caption := 'Position: ' + '0.00';
  // Destroy the module
  openmpt_module_destroy(o_mod);
  o_mod := nil; // Important: Set to nil after destroying
end;

procedure TForm1.bt_playClick(Sender: TObject);
begin
  InitAudio;
  ok_flag := True;
  o_mod_position := openmpt_module_get_position_seconds(o_mod);
  o_mod_set_pos := openmpt_module_set_position_seconds(o_mod, o_mod_position);
end;

function WaveOutCallback(hwo: HWAVEOUT; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR): DWORD; stdcall;
begin
  if uMsg = WOM_DONE then
  begin
    FillBuff(Form1.currentBuffer);
    waveOutWrite(hwo, @Form1.waveHeaders[Form1.currentBuffer], SizeOf(TWaveHdr));
    Form1.currentBuffer := (Form1.currentBuffer + 1) mod BufferCount;
  end;
  Result := 0;
end;

end.

Ключевые изменения:

  • Обработка ошибок: Добавлена ​​более надежная обработка ошибок в LoadBinaryFileToBuffer и после openmpt_module_create_from_memory2.
  • Уничтожение модуля: Добавлен openmpt_module_destroy(o_mod) в процедуру bt_stopClick и сразу после нее установлено значение o_mod в nil. Это необходимо для освобождения ресурсов модуля OpenMPT.
  • Операторы Exit: Добавлены операторы Exit после сообщений об ошибках, чтобы предотвратить дальнейшее выполнение в случае возникновения ошибки.
  • Комментарии: Добавлены комментарии для объяснения назначения различных разделов кода.

Советы по отладке:

  • Точки останова: Используйте точки останова в вашей IDE для пошагового выполнения кода и проверки значений переменных.
  • Ведение журнала: Добавьте операторы ведения журнала для вывода значений переменных в файл или на консоль.
  • Отладка OpenMPT: Сам OpenMPT может иметь инструменты отладки или возможности ведения журнала, которые могут помочь вам диагностировать проблемы.

Не забудьте тщательно протестировать свой код и изящно обрабатывать ошибки, чтобы обеспечить положительный пользовательский опыт.

Создано по материалам из источника по ссылке.

Код представляет собой программу, использующую библиотеку OpenMPT для воспроизведения музыкальных файлов формата MOD, с акцентом на управление памятью, обработку ошибок и интеграцию с OpenMPT.


Комментарии и вопросы

Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS




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


:: Главная :: COM и DCOM ::


реклама


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

Время компиляции файла: 2024-12-22 20:14:06
2025-06-16 22:04:25/0.0086700916290283/1