有关MIDI文件格式中提取对应事件的时间问题高分请教?详细的解答200分赠送!!参与的另外给分,问题解决马上结案(200分)

  • 主题发起人 主题发起人 waderlym
  • 开始时间 开始时间
W

waderlym

Unregistered / Unconfirmed
GUEST, unregistred user!
MIDI格式分3种 0,1,2 MTrk CHUNK中有相关的事件和其delta TIME ,Head chunk中有tick time .我的问题就是:如何计算MIDI中某一个事情的离开始播放的时间间隔,不要通过播放就能计算出的时间.
比如说MIDI文件中有个事件是放歌词'我',如何知道播放'我'的开始时间和播放完的间隔时间,怎么计算.详细解答MIDI格式和播放原理的给全分,参考的看情况给分
 
http://www.borg.com/~jglatt/tech/midifile/ppqn.htm
相信你自己已经了解了MIDI的大部分原理,深入的理解可能还需要很大的工夫,但是
这个网叶估计能帮助你.另外附上一段:
BPM = 60,000,000/MicroTempo
MicrosPerPPQN = MicroTempo/TimeBase
MicrosPerMIDIClock = MicroTempo/24
PPQNPerMIDIClock = TimeBase/24
MicrosPerSubFrame = 1000000 * Frames * SubFrames
SubFramesPerQuarterNote = MicroTempo/(Frames * SubFrames)
SubFramesPerPPQN = SubFramesPerQuarterNote/TimeBase
MicrosPerPPQN = SubFramesPerPPQN * Frames * SubFrames


{
TMidiPlay, play midi tracks events.
}

unit MidiPlayer;

interface

uses
Windows, Messages, SysUtils, Classes, Controls, Forms, Dialogs, MMSystem,
StdCtrls, ExtCtrls, WinProcs, MidiFile;

const
TIMER_RESOLUTION = 10;
WM_MULTIMEDIA_TIMER = WM_USER + 127;

type
TTimerProc = procedure(uTimerID, uMsg: Integer;
dwUser, dwParam1, dwParam2: DWORD);
stdcall;

var
MidiPlayerHandle: HWND;
TimerProc: TTimerProc;
MIDITimerID: Integer;
TimerPeriod: Integer;

type
TOnMidiEvent = procedure(Event: PMidiEvent) of object;
TOnReadEvent = procedure(Track: Integer) of object;
TOnSpeedChange = procedure(Value: Integer) of object;

TMidiPlayer = class(TComponent)
private
FMidiFile: TMidiFile;

FPriority: DWORD;
FPlaying: Boolean;
FStartTime: Integer;
FCurrentPos:do
uble;
FCurrentTime: Integer;
// Current Playing Time
FSpeed: Integer;
procedure PlayEvent(iTrack: Integer;
pEvent: PMidiEvent);

procedure PlayAtTime(const iTime: Integer);
procedure CalculateCurrentPos(const iTime: Integer);
procedure SetCurrentPos(const Value:do
uble);
procedure SetCurrentTime(const Value: Integer);
procedure SetSpeed(const Value: Integer);
procedure SetMidiFile(const Value: TMidiFile);
protected
FOnMidiEvent: TOnMidiEvent;
FOnSpeedChange: TOnSpeedChange;
FOnUpdateEvent: TNotifyEvent;
FOnReadyEvent: TOnReadEvent;
procedure MidiTimer(Sender: TObject);
procedure WndProc(var Msg: TMessage);
public
constructor Create(AOwner: TComponent);
override;
destructor Destroy;
override;

procedure StartPlaying;
procedure StopPlaying;
procedure ContinuePlaying;
published
property MidiFile: TMidiFile read FMidiFile write SetMidiFile;
property Speed: Integer read FSpeed write SetSpeed;
property Playing: Boolean read FPlaying;
property CurrentPos:do
uble read FCurrentPos write SetCurrentPos;
property CurrentTime: Integer read FCurrentTime write SetCurrentTime;
property OnMidiEvent: TOnMidiEvent read FOnMidiEvent write FOnMidiEvent;
property OnSpeedChange: TOnSpeedChange read FOnSpeedChange write FOnSpeedChange;
property OnUpdateEvent: TNotifyEvent read FOnUpdateEvent write FOnUpdateEvent;
property OnReadyEvent: TOnReadEvent read FOnReadyEvent write FOnReadyEvent;
end;


procedure Register;

implementation

procedure Register;
begin

RegisterComponents('Piano Suite', [TMidiPlayer]);
end;


{ TMidiTimer}

procedure TimerCallBackProc(uTimerID, uMsg: Integer;
dwUser, dwParam1, dwParam2: DWORD);
stdcall;
begin

PostMessage(HWND(dwUser), WM_MULTIMEDIA_TIMER, 0, 0);
end;


procedure SetMIDITimer;
var
TimeCaps: TTimeCaps;
begin

timeGetDevCaps(@TimeCaps, SizeOf(TimeCaps));
if TIMER_RESOLUTION < TimeCaps.wPeriodMin then

TimerPeriod := TimeCaps.wPeriodMin
else
if TIMER_RESOLUTION > TimeCaps.wPeriodMax then

TimerPeriod := TimeCaps.wPeriodMax
else

TimerPeriod := TIMER_RESOLUTION;

timebegin
Period(TimerPeriod);
MIDITimerID := timeSetEvent(TimerPeriod, TimerPeriod, @TimerProc,
DWORD(MidiPlayerHandle), TIME_PERIODIC);
if MIDITimerID = 0 then

timeEndPeriod(TimerPeriod);
end;


procedure KillMIDITimer;
begin

timeKillEvent(MIDITimerID);
timeEndPeriod(TimerPeriod);
end;


{ TMidiPlayer }

constructor TMidiPlayer.Create(AOwner: TComponent);
begin

inherited Create(AOWner);

FSpeed := 100;
FStartTime := 0;
FCurrentPos := 0.0;
FCurrentTime := 0;

MidiPlayerHandle := AllocateHWnd(WndProc);
TimerProc := TimerCallBackProc;
FPriority := GetPriorityClass(MidiPlayerHandle);
end;


destructor TMidiPlayer.Destroy;
begin

SetPriorityClass(MidiPlayerHandle, FPriority);
if MIDITimerID <> 0 then
KillMIDITimer;
DeallocateHWnd(MidiPlayerHandle);

inherited;
end;


procedure TMidiPlayer.SetMidiFile(const Value: TMidiFile);
begin

FMidiFile := Value;
end;


procedure TMidiPlayer.PlayEvent(iTrack: Integer;
pEvent: PMidiEvent);
begin

if PEvent.iEvent = $FF then

begin

if pEvent^.iData1 = $2F then
// End this track
begin

FMidiFile.GetTrack(iTrack).Ready := True;
if Assigned(FOnReadyEvent) then

FOnReadyEvent(iTrack);
end;

if PEvent^.iData1 = $51 then

begin

FSpeed := Integer(Byte(PEvent^.sLetter[1])) shl 16 +
Integer(Byte(PEvent^.sLetter[2])) shl 8 +
Integer(Byte(PEvent^.sLetter[3]));
FSpeed := 60000000 div FSpeed;
if Assigned(FOnSpeedChange) then

FOnSpeedChange(FSpeed);
end;

end;

end;


procedure TMIdiPlayer.CalculateCurrentPos(const iTime: Integer);
var
secPerPulse:do
uble;
begin

secPerPulse := 60000 / Speed / FMidiFile.GetMidiHead.PulsesPerQuarter;
FCurrentPos := FCurrentPos + (iTime - FCurrentTime) / secPerPulse;
end;


procedure TMidiPlayer.SetCurrentPos(const Value:do
uble);
var
iPositon: Integer;
secPerPulse:do
uble;
begin

if not Assigned(FMidiFile) then
Exit;

secPerPulse := 60000 / Speed / FMidiFile.GetMidiHead.PulsesPerQuarter;
iPositon := Round((Value - FCurrentPos) * secPerPulse) + FCurrentTime;
SetCurrentTime(iPositon);
end;


procedure TMidiPlayer.SetCurrentTime(const Value: Integer);
var
i: Integer;
begin

if not Assigned(FMidiFile) then
Exit;

if Value = 0 then
// Replay
begin

FCurrentTime := 0;
FCurrentPos := 0.0;
end;


CalculateCurrentPos(Value);
FStartTime := GetTickCount - Value;
for i := 0 to FMidiFile.TrackCount - 1do

begin

with FMidiFile.GetTrack(i)do

begin

Position := 0;
while (Position < EventCount) and (Round(FCurrentPos) > GetEvent(Position).iPositon)do

begin

PlayEvent(i, GetEvent(Position));
Position := Position + 1;
end;

end;

end;

FCurrentTime := Value;
end;


procedure TMidiPlayer.PlayAtTime(const iTime: Integer);
var
i: Integer;
begin

if not Assigned(FMidiFile) then
Exit;

CalculateCurrentPos(iTime);
for i := 0 to FMidiFile.TrackCount - 1do

begin

with FMidiFile.GetTrack(i)do

begin

while (Position < EventCount) and (Round(FCurrentPos) > GetEvent(Position).iPositon)do

begin

PlayEvent(i, GetEvent(Position));
if Active and Assigned(FOnMidiEvent) then

FOnMidiEvent(GetEvent(Position));
Position := Position + 1;
end;

end;

end;

FCurrentTime := iTime;
end;


procedure TMidiPlayer.StartPlaying;
begin

SetCurrentTime(0);

SetPriorityClass(MidiPlayerHandle, REALTIME_PRIORITY_CLASS);
SetMIDITimer;
FPlaying := True;
end;


procedure TMidiPlayer.ContinuePlaying;
begin

SetCurrentTime(FCurrentTime);

SetPriorityClass(MidiPlayerHandle, REALTIME_PRIORITY_CLASS);
SetMIDITimer;
FPlaying := True;
end;


procedure TMidiPlayer.StopPlaying;
begin

KillMIDITimer;
SetPriorityClass(MidiPlayerHandle, FPriority);
FPlaying := False;
end;


procedure TMidiPlayer.MidiTimer(Sender: TObject);
begin

if FPlaying then

begin

PlayAtTime(GetTickCount - FStartTime);
if Assigned(FOnUpdateEvent) then
FOnUpdateEvent(self);
end;

end;


procedure TMidiPlayer.WndProc(var Msg: TMessage);
begin

case Msg.Msg of
WM_MULTIMEDIA_TIMER:
begin

try
MidiTimer(self);
except
Application.HandleException(Self);
end;

end;

else

Msg.Result := DefWindowProc(MidiPlayerHandle, Msg.Msg, Msg.WParam, Msg.LParam);
end;

end;


procedure TMidiPlayer.SetSpeed(const Value: Integer);
begin

FSpeed := Value;
end;


end.
 
谢谢JJ,虽然还是没弄的很明白但是估计也没人再回答了,也许在这里不该问这样的问题.
 
后退
顶部