From 3260749d369d3466c345d40a8b2189c32c8c1b60 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Mon, 7 Nov 2011 15:26:44 +0100 Subject: removed pascal code --- src/lib/midi/MidiFile.pas | 968 ----------------------------------------- src/lib/midi/MidiScope.pas | 198 --------- src/lib/midi/Midicons.pas | 47 -- src/lib/midi/Midiin.pas | 727 ------------------------------- src/lib/midi/Midiout.pas | 619 -------------------------- src/lib/midi/demo/MidiTest.pas | 249 ----------- 6 files changed, 2808 deletions(-) delete mode 100644 src/lib/midi/MidiFile.pas delete mode 100644 src/lib/midi/MidiScope.pas delete mode 100644 src/lib/midi/Midicons.pas delete mode 100644 src/lib/midi/Midiin.pas delete mode 100644 src/lib/midi/Midiout.pas delete mode 100644 src/lib/midi/demo/MidiTest.pas (limited to 'src/lib/midi') diff --git a/src/lib/midi/MidiFile.pas b/src/lib/midi/MidiFile.pas deleted file mode 100644 index acf44c04..00000000 --- a/src/lib/midi/MidiFile.pas +++ /dev/null @@ -1,968 +0,0 @@ -{ - Load a midifile and get access to tracks and events - I did build this component to convert midifiles to wave files - or play the files on a software synthesizer which I'm currenly - building. - - version 1.0 first release - - version 1.1 - added some function - function KeyToStr(key : integer) : string; - function MyTimeToStr(val : integer) : string; - Bpm can be set to change speed - - version 1.2 - added some functions - function GetTrackLength:integer; - function Ready: boolean; - - version 1.3 - update by Chulwoong, - He knows how to use the MM timer, the timing is much better now, thank you - - for comments/bugs - F.Bouwmans - fbouwmans@spiditel.nl - - if you think this component is nice and you use it, sent me a short email. - I've seen that other of my components have been downloaded a lot, but I've - got no clue wether they are actually used. - Don't worry because you are free to use these components - - Timing has improved, however because the messages are handled by the normal - windows message loop (of the main window) it is still influenced by actions - done on the window (minimize/maximize ..). - Use of a second thread with higher priority which only handles the - timer message should increase performance. If somebody knows such a component - which is freeware please let me know. - - interface description: - - procedure ReadFile: - actually read the file which is set in Filename - - function GetTrack(index: integer) : TMidiTrack; - - property Filename - set/read filename of midifile - - property NumberOfTracks - read number of tracks in current file - - property TicksPerQuarter: integer - ticks per quarter, tells how to interpret the time value in midi events - - property FileFormat: TFileFormat - tells the format of the current midifile - - property Bpm:integer - tells Beats per minut - - property OnMidiEvent:TOnMidiEvent - called while playing for each midi event - - procedure StartPlaying; - start playing the current loaded midifile from the beginning - - procedure StopPlaying; - stop playing the current midifile - - procedure PlayToTime(time : integer); - if playing yourself then events from last time to this time are produced - - - function KeyToStr(key : integer) : string; - give note string on key value: e.g. C4 - - function MyTimeToStr(val : integer) : string; - give time string from msec time - - function GetTrackLength:integer; - gives the track lenght in msec (assuming the bpm at the start oof the file) - - function Ready: boolean; - now you can check wether the playback is finished - -} - -unit MidiFile; - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - Windows, - Messages, - Classes, - {$IFDEF FPC} - WinAllocation, - {$ENDIF} - SysUtils, - UPath; - -type - TChunkType = (illegal, header, track); - TFileFormat = (single, multi_synch, multi_asynch); - PByte = ^byte; - - TMidiEvent = record - event: byte; - data1: byte; - data2: byte; - str: string; - dticks: integer; - time: integer; - mtime: integer; - len: integer; - end; - PMidiEvent = ^TMidiEvent; - - TOnMidiEvent = procedure(event: PMidiEvent) of object; - TEvent = procedure of object; - - TMidiTrack = class(TObject) - protected - events: TList; - name: string; - instrument: string; - currentTime: integer; - currentPos: integer; - ready: boolean; - trackLenght: integer; - procedure checkReady; - public - OnMidiEvent: TOnMidiEvent; - OnTrackReady: TEvent; - constructor Create; - destructor Destroy; override; - - procedure Rewind(pos: integer); - procedure PlayUntil(pos: integer); - procedure GoUntil(pos: integer); - - procedure putEvent(event: PMidiEvent); - function getEvent(index: integer): PMidiEvent; - function getName: string; - function getInstrument: string; - function getEventCount: integer; - function getCurrentTime: integer; - function getTrackLength: integer; - function isReady:boolean; - end; - - TMidiFile = class(TComponent) - private - { Private declarations } - procedure MidiTimer(sender : TObject); - procedure WndProc(var Msg : TMessage); - protected - { Protected declarations } - midiFile: TBinaryFileStream; - chunkType: TChunkType; - chunkLength: integer; - chunkData: PByte; - chunkIndex: PByte; - chunkEnd: PByte; - FPriority: DWORD; - - // midi file attributes - FFileFormat: TFileFormat; - numberTracks: integer; - deltaTicks: integer; - FBpm: integer; - FBeatsPerMeasure: integer; - FusPerTick: double; - FFilename: IPath; - - Tracks: TList; - currentTrack: TMidiTrack; - FOnMidiEvent: TOnMidiEvent; - FOnUpdateEvent: TNotifyEvent; - - // playing attributes - playing: boolean; - PlayStartTime: integer; - currentTime: integer; // Current playtime in msec - currentPos: Double; // Current Position in ticks - - procedure OnTrackReady; - procedure SetFilename(val: IPath); - procedure ReadChunkHeader; - procedure ReadChunkContent; - procedure ReadChunk; - procedure ProcessHeaderChunk; - procedure ProcessTrackChunk; - function ReadVarLength: integer; - function ReadString(l: integer): string; - procedure SetOnMidiEvent(handler: TOnMidiEvent); - procedure SetBpm(val: integer); - public - { Public declarations } - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - - procedure ReadFile; - function GetTrack(index: integer): TMidiTrack; - - procedure StartPlaying; - procedure StopPlaying; - procedure ContinuePlaying; - - procedure PlayToTime(time: integer); - procedure GoToTime(time: integer); - function GetCurrentTime: integer; - function GetFusPerTick : Double; - function GetTrackLength:integer; - function Ready: boolean; - published - { Published declarations } - property Filename: IPath read FFilename write SetFilename; - property NumberOfTracks: integer read numberTracks; - property TicksPerQuarter: integer read deltaTicks; - property FileFormat: TFileFormat read FFileFormat; - property Bpm: integer read FBpm write SetBpm; - property OnMidiEvent: TOnMidiEvent read FOnMidiEvent write SetOnMidiEvent; - property OnUpdateEvent: TNotifyEvent read FOnUpdateEvent write FOnUpdateEvent; - end; - -function KeyToStr(key: integer): string; -function MyTimeToStr(val: integer): string; -procedure Register; - -implementation - -uses mmsystem; - -type -{$IFDEF FPC} - TTimerProc = TTIMECALLBACK; - TTimeCaps = TIMECAPS; -{$ELSE} - TTimerProc = TFNTimeCallBack; -{$ENDIF} - -const TIMER_RESOLUTION=10; -const WM_MULTIMEDIA_TIMER=WM_USER+127; - -var MIDIFileHandle : HWND; - TimerProc : TTimerProc; - MIDITimerID : Integer; - TimerPeriod : Integer; - -procedure TimerCallBackProc(uTimerID,uMsg: Cardinal; 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; - - timeBeginPeriod(TimerPeriod); - MIDITimerID:=timeSetEvent(TimerPeriod,TimerPeriod,TimerProc, - DWORD(MIDIFileHandle),TIME_PERIODIC); - if MIDITimerID=0 then - timeEndPeriod(TimerPeriod); -end; - -procedure KillMIDITimer; -begin - timeKillEvent(MIDITimerID); - timeEndPeriod(TimerPeriod); -end; - -constructor TMidiTrack.Create; -begin - inherited Create; - events := TList.Create; - currentTime := 0; - currentPos := 0; -end; - -destructor TMidiTrack.Destroy; -var - i: integer; -begin - for i := 0 to events.count - 1 do - Dispose(PMidiEvent(events.items[i])); - events.Free; - inherited Destroy; -end; - -procedure TMidiTRack.putEvent(event: PMidiEvent); -var - command: integer; - i: integer; - pevent: PMidiEvent; -begin - if (event.event = $FF) then - begin - if (event.data1 = 3) then - name := event.str; - if (event.data1 = 4) then - instrument := event.str; - end; - currentTime := currentTime + event.dticks; - event.time := currentTime; // for the moment just add dticks - event.len := 0; - events.add(TObject(event)); - command := event.event and $F0; - - if ((command = $80) // note off - or ((command = $90) and (event.data2 = 0))) //note on with speed 0 - then - begin - // this is a note off, try to find the accompanion note on - command := event.event or $90; - i := events.count - 2; - while i >= 0 do - begin - pevent := PMidiEvent(events[i]); - if (pevent.event = command) and - (pevent.data1 = event.data1) - then - begin - pevent.len := currentTIme - pevent.time; - i := 0; - event.len := -1; - end; - dec(i); - end; - end; -end; - -function TMidiTrack.getName: string; -begin - result := name; -end; - -function TMidiTrack.getInstrument: string; -begin - result := instrument; -end; - -function TMiditrack.getEventCount: integer; -begin - result := events.count; -end; - -function TMiditrack.getEvent(index: integer): PMidiEvent; -begin - if ((index < events.count) and (index >= 0)) then - result := events[index] - else - result := nil; -end; - -function TMiditrack.getCurrentTime: integer; -begin - result := currentTime; -end; - -procedure TMiditrack.Rewind(pos: integer); -begin - if currentPos = events.count then - dec(currentPos); - while ((currentPos > 0) and - (PMidiEvent(events[currentPos]).time > pos)) - do - begin - dec(currentPos); - end; - checkReady; -end; - -procedure TMiditrack.PlayUntil(pos: integer); -begin - if assigned(OnMidiEvent) then - begin - while ((currentPos < events.count) and - (PMidiEvent(events[currentPos]).time < pos)) do - begin - OnMidiEvent(PMidiEvent(events[currentPos])); - inc(currentPos); - end; - end; - checkReady; -end; - -procedure TMidiTrack.GoUntil(pos: integer); -begin - while ((currentPos < events.count) and - (PMidiEvent(events[currentPos]).time < pos)) do - begin - inc(currentPos); - end; - checkReady; -end; - -procedure TMidiTrack.checkReady; -begin - if currentPos >= events.count then - begin - ready := true; - if assigned(OnTrackReady) then - OnTrackReady; - end - else - ready := false; -end; - -function TMidiTrack.getTrackLength: integer; -begin - result := PMidiEvent(events[events.count-1]).time -end; - -function TMidiTrack.isReady: boolean; -begin - result := ready; -end; - -constructor TMidifile.Create(AOwner: TComponent); -begin - inherited Create(AOWner); - MIDIFileHandle:=AllocateHWnd(WndProc); - chunkData := nil; - chunkType := illegal; - Tracks := TList.Create; - TimerProc:=@TimerCallBackProc; - FPriority:=GetPriorityClass(MIDIFileHandle); -end; - -destructor TMidifile.Destroy; -var - i: integer; -begin - if not (chunkData = nil) then FreeMem(chunkData); - for i := 0 to Tracks.Count - 1 do - TMidiTrack(Tracks.Items[i]).Free; - Tracks.Free; - SetPriorityClass(MIDIFileHandle,FPriority); - - if MIDITimerID<>0 then KillMIDITimer; - - DeallocateHWnd(MIDIFileHandle); - - inherited Destroy; -end; - -function TMidiFile.GetTrack(index: integer): TMidiTrack; -begin - result := Tracks.Items[index]; -end; - -procedure TMidifile.SetFilename(val: IPath); -begin - FFilename := val; -// ReadFile; -end; - -procedure TMidifile.SetOnMidiEvent(handler: TOnMidiEvent); -var - i: integer; -begin -// if not (FOnMidiEvent = handler) then -// begin - FOnMidiEvent := handler; - for i := 0 to tracks.count - 1 do - TMidiTrack(tracks.items[i]).OnMidiEvent := handler; -// end; -end; - -{$WARNINGS OFF} -procedure TMidifile.MidiTimer(Sender: TObject); -begin - if playing then - begin - PlayToTime(GetTickCount - PlayStartTime); - if assigned(FOnUpdateEvent) then FOnUpdateEvent(self); - end; -end; -{$WARNINGS ON} - -procedure TMidifile.StartPlaying; -var - i: integer; -begin - for i := 0 to tracks.count - 1 do - TMidiTrack(tracks[i]).Rewind(0); - playStartTime := getTickCount; - playing := true; - - SetPriorityClass(MIDIFileHandle,REALTIME_PRIORITY_CLASS); - - SetMIDITimer; - currentPos := 0.0; - currentTime := 0; -end; - -{$WARNINGS OFF} -procedure TMidifile.ContinuePlaying; -begin - PlayStartTime := GetTickCount - currentTime; - playing := true; - - SetPriorityClass(MIDIFileHandle,REALTIME_PRIORITY_CLASS); - - SetMIDITimer; -end; -{$WARNINGS ON} - -procedure TMidifile.StopPlaying; -begin - playing := false; - KillMIDITimer; - SetPriorityClass(MIDIFileHandle,FPriority); -end; - -function TMidiFile.GetCurrentTime: integer; -begin - Result := currentTime; -end; - -procedure TMidifile.PlayToTime(time: integer); -var - i: integer; - track: TMidiTrack; - pos: integer; - deltaTime: integer; -begin - // calculate the pos in the file. - // pos is actually tick - // Current FusPerTick is uses to determine the actual pos - - deltaTime := time - currentTime; - currentPos := currentPos + (deltaTime * 1000) / FusPerTick; - pos := round(currentPos); - - for i := 0 to tracks.count - 1 do - begin - TMidiTrack(tracks.items[i]).PlayUntil(pos); - end; - currentTime := time; -end; - -procedure TMidifile.GoToTime(time: integer); -var - i: integer; - track: TMidiTrack; - pos: integer; -begin - // this function should be changed because FusPerTick might not be constant - pos := round((time * 1000) / FusPerTick); - for i := 0 to tracks.count - 1 do - begin - TMidiTrack(tracks.items[i]).Rewind(0); - TMidiTrack(tracks.items[i]).GoUntil(pos); - end; -end; - -procedure TMidifile.SetBpm(val: integer); -var - us_per_quarter: integer; -begin - if not (val = FBpm) then - begin - us_per_quarter := 60000000 div val; - - FBpm := 60000000 div us_per_quarter; - FusPerTick := us_per_quarter / deltaTicks; - end; -end; - -procedure TMidifile.ReadChunkHeader; -var - theByte: array[0..7] of byte; -begin - midiFile.Read(theByte[0], 8); - if (theByte[0] = $4D) and (theByte[1] = $54) then - begin - if (theByte[2] = $68) and (theByte[3] = $64) then - chunkType := header - else if (theByte[2] = $72) and (theByte[3] = $6B) then - chunkType := track - else - chunkType := illegal; - end - else - begin - chunkType := illegal; - end; - chunkLength := theByte[7] + theByte[6] * $100 + theByte[5] * $10000 + theByte[4] * $1000000; -end; - -procedure TMidifile.ReadChunkContent; -begin - if not (chunkData = nil) then - FreeMem(chunkData); - GetMem(chunkData, chunkLength + 10); - midiFile.Read(chunkData^, chunkLength); - chunkIndex := chunkData; - chunkEnd := PByte(integer(chunkIndex) + integer(chunkLength) - 1); -end; - -procedure TMidifile.ReadChunk; -begin - ReadChunkHeader; - ReadChunkContent; - case chunkType of - header: - ProcessHeaderChunk; - track: - ProcessTrackCHunk; - end; -end; - -procedure TMidifile.ProcessHeaderChunk; -begin - chunkIndex := chunkData; - inc(chunkIndex); - if chunkType = header then - begin - case chunkIndex^ of - 0: FfileFormat := single; - 1: FfileFormat := multi_synch; - 2: FfileFormat := multi_asynch; - end; - inc(chunkIndex); - numberTracks := chunkIndex^ * $100; - inc(chunkIndex); - numberTracks := numberTracks + chunkIndex^; - inc(chunkIndex); - deltaTicks := chunkIndex^ * $100; - inc(chunkIndex); - deltaTicks := deltaTicks + chunkIndex^; - end; -end; - -procedure TMidifile.ProcessTrackChunk; -var - dTime: integer; - event: integer; - len: integer; - str: string; - midiEvent: PMidiEvent; - i: integer; - us_per_quarter: integer; -begin - chunkIndex := chunkData; -// inc(chunkIndex); - event := 0; - if chunkType = track then - begin - currentTrack := TMidiTrack.Create; - currentTrack.OnMidiEvent := FOnMidiEvent; - Tracks.add(currentTrack); - while integer(chunkIndex) < integer(chunkEnd) do - begin - // each event starts with var length delta time - dTime := ReadVarLength; - if chunkIndex^ >= $80 then - begin - event := chunkIndex^; - inc(chunkIndex); - end; - // else it is a running status event (just the same event as before) - - if event = $FF then - begin -{ case chunkIndex^ of - $00: // sequence number, not implemented jet - begin - inc(chunkIndex); // $02 - inc(chunkIndex); - end; - $01 .. $0f: // text events FF ty len text - begin - New(midiEvent); - midiEvent.event := $FF; - midiEvent.data1 := chunkIndex^; // type is stored in data1 - midiEvent.dticks := dtime; - - inc(chunkIndex); - len := ReadVarLength; - midiEvent.str := ReadString(len); - - currentTrack.putEvent(midiEvent); - end; - $20: // Midi channel prefix FF 20 01 cc - begin - inc(chunkIndex); // $01 - inc(chunkIndex); // channel - inc(chunkIndex); - end; - $2F: // End of track FF 2F 00 - begin - inc(chunkIndex); // $00 - inc(chunkIndex); - end; - $51: // Set Tempo FF 51 03 tttttt - begin - inc(chunkIndex); // $03 - inc(chunkIndex); // tt - inc(chunkIndex); // tt - inc(chunkIndex); // tt - inc(chunkIndex); - end; - $54: // SMPTE offset FF 54 05 hr mn se fr ff - begin - inc(chunkIndex); // $05 - inc(chunkIndex); // hr - inc(chunkIndex); // mn - inc(chunkIndex); // se - inc(chunkIndex); // fr - inc(chunkIndex); // ff - inc(chunkIndex); - end; - $58: // Time signature FF 58 04 nn dd cc bb - begin - inc(chunkIndex); // $04 - inc(chunkIndex); // nn - inc(chunkIndex); // dd - inc(chunkIndex); // cc - inc(chunkIndex); // bb - inc(chunkIndex); - end; - $59: // Key signature FF 59 02 df mi - begin - inc(chunkIndex); // $02 - inc(chunkIndex); // df - inc(chunkIndex); // mi - inc(chunkIndex); - end; - $7F: // Sequence specific Meta-event - begin - inc(chunkIndex); - len := ReadVarLength; - str := ReadString(len); - end; - else // unknown meta event - } - begin - New(midiEvent); - midiEvent.event := $FF; - midiEvent.data1 := chunkIndex^; // type is stored in data1 - midiEvent.dticks := dtime; - - inc(chunkIndex); - len := ReadVarLength; - midiEvent.str := ReadString(len); - currentTrack.putEvent(midiEvent); - - case midiEvent.data1 of - $51: - begin - us_per_quarter := - (integer(byte(midiEvent.str[1])) shl 16 + - integer(byte(midiEvent.str[2])) shl 8 + - integer(byte(midiEvent.str[3]))); - FBpm := 60000000 div us_per_quarter; - FusPerTick := us_per_quarter / deltaTicks; - end; - end; - end; -// end; - end - else - begin - // these are all midi events - New(midiEvent); - midiEvent.event := event; - midiEvent.dticks := dtime; -// inc(chunkIndex); - case event of - $80..$8F, // note off - $90..$9F, // note on - $A0..$AF, // key aftertouch - $B0..$BF, // control change - $E0..$EF: // pitch wheel change - begin - midiEvent.data1 := chunkIndex^; inc(chunkIndex); - midiEvent.data2 := chunkIndex^; inc(chunkIndex); - end; - $C0..$CF, // program change - $D0..$DF: // channel aftertouch - begin - midiEvent.data1 := chunkIndex^; inc(chunkIndex); - end; - else - // error - end; - currentTrack.putEvent(midiEvent); - end; - end; - end; -end; - - -function TMidifile.ReadVarLength: integer; -var - i: integer; - b: byte; -begin - b := 128; - i := 0; - while b > 127 do - begin - i := i shl 7; - b := chunkIndex^; - i := i + b and $7F; - inc(chunkIndex); - end; - result := i; -end; - -function TMidifile.ReadString(l: integer): string; -var - s: PChar; - i: integer; -begin - GetMem(s, l + 1); ; - s[l] := chr(0); - for i := 0 to l - 1 do - begin - s[i] := Chr(chunkIndex^); - inc(chunkIndex); - end; - result := string(s); -end; - -procedure TMidifile.ReadFile; -var - i: integer; -begin - for i := 0 to Tracks.Count - 1 do - TMidiTrack(Tracks.Items[i]).Free; - Tracks.Clear; - chunkType := illegal; - - midiFile := TBinaryFileStream.Create(FFilename, fmOpenRead); - while (midiFile.Position < midiFile.Size) do - ReadChunk; - FreeAndNil(midiFile); - numberTracks := Tracks.Count; -end; - -function KeyToStr(key: integer): string; -var - n: integer; - str: string; -begin - n := key mod 12; - case n of - 0: str := 'C'; - 1: str := 'C#'; - 2: str := 'D'; - 3: str := 'D#'; - 4: str := 'E'; - 5: str := 'F'; - 6: str := 'F#'; - 7: str := 'G'; - 8: str := 'G#'; - 9: str := 'A'; - 10: str := 'A#'; - 11: str := 'B'; - end; - Result := str + IntToStr(key div 12); -end; - -function IntToLenStr(val: integer; len: integer): string; -var - str: string; -begin - str := IntToStr(val); - while Length(str) < len do - str := '0' + str; - Result := str; -end; - -function MyTimeToStr(val: integer): string; - var - hour: integer; - min: integer; - sec: integer; - msec: integer; -begin - msec := val mod 1000; - sec := val div 1000; - min := sec div 60; - sec := sec mod 60; - hour := min div 60; - min := min mod 60; - Result := IntToStr(hour) + ':' + IntToLenStr(min, 2) + ':' + IntToLenStr(sec, 2) + '.' + IntToLenStr(msec, 3); -end; - -function TMidiFIle.GetFusPerTick : Double; -begin - Result := FusPerTick; -end; - -function TMidiFIle.GetTrackLength:integer; -var i,length : integer; - time : extended; -begin - length := 0; - for i := 0 to Tracks.Count - 1 do - if TMidiTrack(Tracks.Items[i]).getTrackLength > length then - length := TMidiTrack(Tracks.Items[i]).getTrackLength; - time := length * FusPerTick; - time := time / 1000.0; - result := round(time); -end; - -function TMidiFIle.Ready: boolean; -var i : integer; -begin - result := true; - for i := 0 to Tracks.Count - 1 do - if not TMidiTrack(Tracks.Items[i]).isready then - result := false; -end; - -procedure TMidiFile.OnTrackReady; -begin - if ready then - if assigned(FOnUpdateEvent) then FOnUpdateEvent(self); -end; - -procedure TMidiFile.WndProc(var Msg : TMessage); -begin - with MSG do - begin - case Msg of - WM_MULTIMEDIA_TIMER: - begin - //try - MidiTimer(self); - //except - // Note: HandleException() is called by default if exception is not handled - // Application.HandleException(Self); - //end; - end; - else - begin - Result := DefWindowProc(MIDIFileHandle, Msg, wParam, lParam); - end; - end; - end; -end; - -procedure Register; -begin - RegisterComponents('Synth', [TMidiFile]); -end; - -end. - diff --git a/src/lib/midi/MidiScope.pas b/src/lib/midi/MidiScope.pas deleted file mode 100644 index afc20b0f..00000000 --- a/src/lib/midi/MidiScope.pas +++ /dev/null @@ -1,198 +0,0 @@ -{ - Shows a large black area where midi note/controller events are shown - just to monitor midi activity (for the MidiPlayer) - - version 1.0 first release - - for comments/bugs - F.Bouwmans - fbouwmans@spiditel.nl - - if you think this component is nice and you use it, sent me a short email. - I've seen that other of my components have been downloaded a lot, but I've - got no clue wether they are actually used. - Don't worry because you are free to use these components -} - -unit MidiScope; - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs; - -type - TMidiScope = class(TGraphicControl) - private - { Private declarations } - protected - { Protected declarations } - notes : array[0..15,0..127] of integer; - controllers : array[0..15,0..17] of integer; - aftertouch : array[0..15,0..127] of integer; - - selectedChannel : integer; - - procedure PaintSlide(ch,pos,val: integer); - - procedure NoteOn(channel, note, speed : integer); - procedure Controller(channel,number,value : integer); - procedure AfterTch(channel, note, value : integer); - - public - { Public declarations } - constructor Create(AOwner: TComponent); override; - procedure MidiEvent(event,data1,data2 : integer); - procedure Paint; override; - published - { Published declarations } - end; - - -procedure Register; - -const - BarHeight = 16; - BarHeightInc = BarHeight+2; - BarWidth = 3; - BarWidthInc = BarWidth+1; - HeightDiv = 128 div BarHeight; - -implementation - -uses Midicons; - -procedure Register; -begin - RegisterComponents('Synth', [TMidiScope]); -end; - -constructor TMidiScope.Create(AOwner: TComponent); -var - i,j : integer; -begin - inherited Create(AOwner); - Height := BarHeightinc * 16 + 4; - Width := 147*BarWidthInc + 4 + 20; // for channel number - for i := 0 to 15 do - begin - for j := 0 to 127 do - begin - notes[i,j] := 0; - aftertouch[i,j] := 0; - end; - end; - for i := 0 to 17 do - begin - for j := 0 to 15 do - controllers[i,j] := 0; - end; -end; - -procedure TMidiScope.PaintSlide(ch,pos,val: integer); -var x,y:integer; -begin - Canvas.Brush.Color := clBlack; - Canvas.Pen.color := clBlack; - x := pos * BarWidthInc + 2; - y := 2 + ch * BarHeightInc; - Canvas.Rectangle(x, y, x+BarWidthInc, y+BarHeightInc); - Canvas.Brush.Color := clGreen; - Canvas.Pen.Color := clGreen; - Canvas.Rectangle(x, y + (BarHeight - (val div HeightDiv )), x + BarWidth, y + BarHeight) -end; - -procedure TMidiScope.Paint; -var i,j : integer; -x : integer; -begin - Canvas.Brush.color := clBlack; - Canvas.Rectangle(0,0,Width,Height); - Canvas.Pen.Color := clGreen; - x := 128*BarWidthInc+2; - Canvas.MoveTo(x,0); - Canvas.LineTo(x,Height); - x := 148*BarWIdthInc+2; - canvas.Font.Color := clGreen; - for i := 0 to 15 do - Canvas.TextOut(x,((i+1)*BarHeightInc) - Canvas.font.size-3,IntToStr(i+1)); - canvas.Pen.color := clBlack; - begin - for j := 0 to 127 do - begin - PaintSlide(i,j,notes[i,j]); - end; - for j := 0 to 17 do - begin - PaintSlide(i,j+129,controllers[i,j]); - end; - end; -end; -procedure TMidiScope.NoteOn(channel, note, speed : integer); -begin - notes[channel,note] := speed; - PaintSlide(channel,note,notes[channel,note]); -end; -procedure TMidiScope.AfterTch(channel, note, value : integer); -begin - aftertouch[channel,note] := value; -end; - -procedure TMidiScope.Controller(channel,number,value : integer); -var i : integer; -begin - if number < 18 then - begin - controllers[channel,number] := value; - PaintSlide(channel,number+129,value); - end - else if number >= $7B then - begin - // all notes of for channel - for i := 0 to 127 do - begin - if notes[channel,i] > 0 then - begin - notes[channel,i] := 0; - PaintSlide(channel,i,0); - end; - end; - end; -end; - -procedure TMidiScope.MidiEvent(event,data1,data2 : integer); -begin - case (event AND $F0) of - MIDI_NOTEON : - begin - NoteOn((event AND $F),data1,data2); - end; - MIDI_NOTEOFF: - begin - NoteOn((event AND $F),data1,0); - end; - MIDI_CONTROLCHANGE : - begin - Controller((event AND $F),data1,data2); - end; - MIDI_CHANAFTERTOUCH: - begin - Controller((Event AND $F),16,Data1); - end; - MIDI_PITCHBEND: - begin - begin - Controller((Event AND $F),17,data2); - end; - end; - MIDI_KEYAFTERTOUCH: - begin - end; - end; -end; -end. diff --git a/src/lib/midi/Midicons.pas b/src/lib/midi/Midicons.pas deleted file mode 100644 index 72259beb..00000000 --- a/src/lib/midi/Midicons.pas +++ /dev/null @@ -1,47 +0,0 @@ -{ $Header: /MidiComp/MIDICONS.PAS 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - - -{ MIDI Constants } -unit Midicons; - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses Messages; - -const - MIDI_ALLNOTESOFF = $7B; - MIDI_NOTEON = $90; - MIDI_NOTEOFF = $80; - MIDI_KEYAFTERTOUCH = $a0; - MIDI_CONTROLCHANGE = $b0; - MIDI_PROGRAMCHANGE = $c0; - MIDI_CHANAFTERTOUCH = $d0; - MIDI_PITCHBEND = $e0; - MIDI_SYSTEMMESSAGE = $f0; - MIDI_BEGINSYSEX = $f0; - MIDI_MTCQUARTERFRAME = $f1; - MIDI_SONGPOSPTR = $f2; - MIDI_SONGSELECT = $f3; - MIDI_ENDSYSEX = $F7; - MIDI_TIMINGCLOCK = $F8; - MIDI_START = $FA; - MIDI_CONTINUE = $FB; - MIDI_STOP = $FC; - MIDI_ACTIVESENSING = $FE; - MIDI_SYSTEMRESET = $FF; - - MIM_OVERFLOW = WM_USER; { Input buffer overflow } - MOM_PLAYBACK_DONE = WM_USER+1; { Timed playback complete } - - -implementation - -end. diff --git a/src/lib/midi/Midiin.pas b/src/lib/midi/Midiin.pas deleted file mode 100644 index 66e4f76d..00000000 --- a/src/lib/midi/Midiin.pas +++ /dev/null @@ -1,727 +0,0 @@ -{ $Header: /MidiComp/Midiin.pas 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - -unit MidiIn; - -{ - Properties: - DeviceID: Windows numeric device ID for the MIDI input device. - Between 0 and NumDevs-1. - Read-only while device is open, exception when changed while open - - MIDIHandle: The input handle to the MIDI device. - 0 when device is not open - Read-only, runtime-only - - MessageCount: Number of input messages waiting in input buffer - - Capacity: Number of messages input buffer can hold - Defaults to 1024 - Limited to (64K/event size) - Read-only when device is open (exception when changed while open) - - SysexBufferSize: Size in bytes of each sysex buffer - Defaults to 10K - Minimum 0K (no buffers), Maximum 64K-1 - - SysexBufferCount: Number of sysex buffers - Defaults to 16 - Minimum 0 (no buffers), Maximum (avail mem/SysexBufferSize) - Check where these buffers are allocated? - - SysexOnly: True to ignore all non-sysex input events. May be changed while - device is open. Handy for patch editors where you have lots of short MIDI - events on the wire which you are always going to ignore anyway. - - DriverVersion: Version number of MIDI device driver. High-order byte is - major version, low-order byte is minor version. - - ProductName: Name of product (e.g. 'MPU 401 In') - - MID and PID: Manufacturer ID and Product ID, see - "Manufacturer and Product IDs" in MMSYSTEM.HLP for list of possible values. - - Methods: - GetMidiEvent: Read Midi event at the head of the FIFO input buffer. - Returns a TMyMidiEvent object containing MIDI message data, timestamp, - and sysex data if applicable. - This method automatically removes the event from the input buffer. - It makes a copy of the received sysex buffer and puts the buffer back - on the input device. - The TMyMidiEvent object must be freed by calling MyMidiEvent.Free. - - Open: Opens device. Note no input will appear until you call the Start - method. - - Close: Closes device. Any pending system exclusive output will be cancelled. - - Start: Starts receiving MIDI input. - - Stop: Stops receiving MIDI input. - - Events: - OnMidiInput: Called when MIDI input data arrives. Use the GetMidiEvent to - get the MIDI input data. - - OnOverflow: Called if the MIDI input buffer overflows. The caller must - clear the buffer before any more MIDI input can be received. - - Notes: - Buffering: Uses a circular buffer, separate pointers for next location - to fill and next location to empty because a MIDI input interrupt may - be adding data to the buffer while the buffer is being read. Buffer - pointers wrap around from end to start of buffer automatically. If - buffer overflows then the OnBufferOverflow event is triggered and no - further input will be received until the buffer is emptied by calls - to GetMidiEvent. - - Sysex buffers: There are (SysexBufferCount) buffers on the input device. - When sysex events arrive these buffers are removed from the input device and - added to the circular buffer by the interrupt handler in the DLL. When the sysex events - are removed from the circular buffer by the GetMidiEvent method the buffers are - put back on the input. If all the buffers are used up there will be no - more sysex input until at least one sysex event is removed from the input buffer. - In other words if you're expecting lots of sysex input you need to set the - SysexBufferCount property high enough so that you won't run out of - input buffers before you get a chance to read them with GetMidiEvent. - - If the synth sends a block of sysex that's longer than SysexBufferSize it - will be received as separate events. - TODO: Component derived from this one that handles >64K sysex blocks cleanly - and can stream them to disk. - - Midi Time Code (MTC) and Active Sensing: The DLL is currently hardcoded - to filter these short events out, so that we don't spend all our time - processing them. - TODO: implement a filter property to select the events that will be filtered - out. -} - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - Classes, - SysUtils, - Messages, - Windows, - MMSystem, - {$IFDEF FPC} - WinAllocation, - {$ENDIF} - MidiDefs, - MidiType, - MidiCons, - Circbuf, - Delphmcb; - -type - MidiInputState = (misOpen, misClosed, misCreating, misDestroying); - EMidiInputError = class(Exception); - - {-------------------------------------------------------------------} - TMidiInput = class(TComponent) - private - Handle: THandle; { Window handle used for callback notification } - FDeviceID: Word; { MIDI device ID } - FMIDIHandle: HMIDIIn; { Handle to input device } - FState: MidiInputState; { Current device state } - - FError: Word; - FSysexOnly: Boolean; - - { Stuff from MIDIINCAPS } - FDriverVersion: MMVERSION; - FProductName: string; - FMID: Word; { Manufacturer ID } - FPID: Word; { Product ID } - - { Queue } - FCapacity: Word; { Buffer capacity } - PBuffer: PCircularBuffer; { Low-level MIDI input buffer created by Open method } - FNumdevs: Word; { Number of input devices on system } - - { Events } - FOnMIDIInput: TNotifyEvent; { MIDI Input arrived } - FOnOverflow: TNotifyEvent; { Input buffer overflow } - { TODO: Some sort of error handling event for MIM_ERROR } - - { Sysex } - FSysexBufferSize: Word; - FSysexBufferCount: Word; - MidiHdrs: Tlist; - - PCtlInfo: PMidiCtlInfo; { Pointer to control info for DLL } - - protected - procedure Prepareheaders; - procedure UnprepareHeaders; - procedure AddBuffers; - procedure SetDeviceID(DeviceID: Word); - procedure SetProductName(NewProductName: string); - function GetEventCount: Word; - procedure SetSysexBufferSize(BufferSize: Word); - procedure SetSysexBufferCount(BufferCount: Word); - procedure SetSysexOnly(bSysexOnly: Boolean); - function MidiInErrorString(WError: Word): string; - - public - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - - property MIDIHandle: HMIDIIn read FMIDIHandle; - - property DriverVersion: MMVERSION read FDriverVersion; - property MID: Word read FMID; { Manufacturer ID } - property PID: Word read FPID; { Product ID } - - property Numdevs: Word read FNumdevs; - - property MessageCount: Word read GetEventCount; - { TODO: property to select which incoming messages get filtered out } - - procedure Open; - procedure Close; - procedure Start; - procedure Stop; - { Get first message in input queue } - function GetMidiEvent: TMyMidiEvent; - procedure MidiInput(var Message: TMessage); - - { Some functions to decode and classify incoming messages would be good } - - published - - { TODO: Property editor with dropdown list of product names } - property ProductName: string read FProductName write SetProductName; - - property DeviceID: Word read FDeviceID write SetDeviceID default 0; - property Capacity: Word read FCapacity write FCapacity default 1024; - property Error: Word read FError; - property SysexBufferSize: Word - read FSysexBufferSize - write SetSysexBufferSize - default 10000; - property SysexBufferCount: Word - read FSysexBufferCount - write SetSysexBufferCount - default 16; - property SysexOnly: Boolean - read FSysexOnly - write SetSysexOnly - default False; - - { Events } - property OnMidiInput: TNotifyEvent read FOnMidiInput write FOnMidiInput; - property OnOverflow: TNotifyEvent read FOnOverflow write FOnOverflow; - - end; - -procedure Register; - -{====================================================================} -implementation - -uses Controls, - Graphics; - -(* Not used in Delphi 3 -{ This is the callback procedure in the external DLL. - It's used when midiInOpen is called by the Open method. - There are special requirements and restrictions for this callback - procedure (see midiInOpen in MMSYSTEM.HLP) so it's impractical to - make it an object method } -{$IFDEF WIN32} -function midiHandler( - hMidiIn: HMidiIn; - wMsg: UINT; - dwInstance: DWORD; - dwParam1: DWORD; - dwParam2: DWORD): Boolean; stdcall; external 'DELMID32.DLL'; -{$ELSE} -procedure midiHandler( - hMidiIn: HMidiIn; - wMsg: Word; - dwInstance: DWORD; - dwParam1: DWORD; - dwParam2: DWORD); far; external 'DELPHMID'; -{$ENDIF} -*) -{-------------------------------------------------------------------} - -constructor TMidiInput.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - FState := misCreating; - - FSysexOnly := False; - FNumDevs := midiInGetNumDevs; - MidiHdrs := nil; - - { Set defaults } - if (FNumDevs > 0) then - SetDeviceID(0); - FCapacity := 1024; - FSysexBufferSize := 4096; - FSysexBufferCount := 16; - - { Create the window for callback notification } - if not (csDesigning in ComponentState) then - begin - Handle := AllocateHwnd(MidiInput); - end; - - FState := misClosed; - -end; - -{-------------------------------------------------------------------} -{ Close the device if it's open } - -destructor TMidiInput.Destroy; -begin - if (FMidiHandle <> 0) then - begin - Close; - FMidiHandle := 0; - end; - - if (PCtlInfo <> nil) then - GlobalSharedLockedFree(PCtlinfo^.hMem, PCtlInfo); - - DeallocateHwnd(Handle); - inherited Destroy; -end; - -{-------------------------------------------------------------------} -{ Convert the numeric return code from an MMSYSTEM function to a string - using midiInGetErrorText. TODO: These errors aren't very helpful - (e.g. "an invalid parameter was passed to a system function") so - sort out some proper error strings. } - -function TMidiInput.MidiInErrorString(WError: Word): string; -var - errorDesc: PChar; -begin - errorDesc := nil; - try - errorDesc := StrAlloc(MAXERRORLENGTH); - if midiInGetErrorText(WError, errorDesc, MAXERRORLENGTH) = 0 then - result := StrPas(errorDesc) - else - result := 'Specified error number is out of range'; - finally - if errorDesc <> nil then StrDispose(errorDesc); - end; -end; - -{-------------------------------------------------------------------} -{ Set the sysex buffer size, fail if device is already open } - -procedure TMidiInput.SetSysexBufferSize(BufferSize: Word); -begin - if FState = misOpen then - raise EMidiInputError.Create('Change to SysexBufferSize while device was open') - else - { TODO: Validate the sysex buffer size. Is this necessary for WIN32? } - FSysexBufferSize := BufferSize; -end; - -{-------------------------------------------------------------------} -{ Set the sysex buffer count, fail if device is already open } - -procedure TMidiInput.SetSysexBuffercount(Buffercount: Word); -begin - if FState = misOpen then - raise EMidiInputError.Create('Change to SysexBuffercount while device was open') - else - { TODO: Validate the sysex buffer count } - FSysexBuffercount := Buffercount; -end; - -{-------------------------------------------------------------------} -{ Set the Sysex Only flag to eliminate unwanted short MIDI input messages } - -procedure TMidiInput.SetSysexOnly(bSysexOnly: Boolean); -begin - FSysexOnly := bSysexOnly; - { Update the interrupt handler's copy of this property } - if PCtlInfo <> nil then - PCtlInfo^.SysexOnly := bSysexOnly; -end; - -{-------------------------------------------------------------------} -{ Set the Device ID to select a new MIDI input device - Note: If no MIDI devices are installed, throws an 'Invalid Device ID' exception } - -procedure TMidiInput.SetDeviceID(DeviceID: Word); -var - MidiInCaps: TMidiInCaps; -begin - if FState = misOpen then - raise EMidiInputError.Create('Change to DeviceID while device was open') - else - if (DeviceID >= midiInGetNumDevs) then - raise EMidiInputError.Create('Invalid device ID') - else - begin - FDeviceID := DeviceID; - - { Set the name and other MIDIINCAPS properties to match the ID } - FError := - midiInGetDevCaps(DeviceID, @MidiInCaps, sizeof(TMidiInCaps)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - - FProductName := StrPas(MidiInCaps.szPname); - FDriverVersion := MidiInCaps.vDriverVersion; - FMID := MidiInCaps.wMID; - FPID := MidiInCaps.wPID; - - end; -end; - -{-------------------------------------------------------------------} -{ Set the product name and put the matching input device number in FDeviceID. - This is handy if you want to save a configured input/output device - by device name instead of device number, because device numbers may - change if users add or remove MIDI devices. - Exception if input device with matching name not found, - or if input device is open } - -procedure TMidiInput.SetProductName(NewProductName: string); -var - MidiInCaps: TMidiInCaps; - testDeviceID: Word; - testProductName: string; -begin - if FState = misOpen then - raise EMidiInputError.Create('Change to ProductName while device was open') - else - { Don't set the name if the component is reading properties because - the saved Productname will be from the machine the application was compiled - on, which may not be the same for the corresponding DeviceID on the user's - machine. The FProductname property will still be set by SetDeviceID } - if not (csLoading in ComponentState) then - begin - begin - for testDeviceID := 0 to (midiInGetNumDevs - 1) do - begin - FError := - midiInGetDevCaps(testDeviceID, @MidiInCaps, sizeof(TMidiInCaps)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - testProductName := StrPas(MidiInCaps.szPname); - if testProductName = NewProductName then - begin - FProductName := NewProductName; - Break; - end; - end; - if FProductName <> NewProductName then - raise EMidiInputError.Create('MIDI Input Device ' + - NewProductName + ' not installed ') - else - SetDeviceID(testDeviceID); - end; - end; -end; - - -{-------------------------------------------------------------------} -{ Get the sysex buffers ready } - -procedure TMidiInput.PrepareHeaders; -var - ctr: Word; - MyMidiHdr: TMyMidiHdr; -begin - if (FSysexBufferCount > 0) and (FSysexBufferSize > 0) - and (FMidiHandle <> 0) then - begin - Midihdrs := TList.Create; - for ctr := 1 to FSysexBufferCount do - begin - { Initialize the header and allocate buffer memory } - MyMidiHdr := TMyMidiHdr.Create(FSysexBufferSize); - - { Store the address of the MyMidiHdr object in the contained MIDIHDR - structure so we can get back to the object when a pointer to the - MIDIHDR is received. - E.g. see TMidiOutput.Output method } - MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr); - - { Get MMSYSTEM's blessing for this header } - FError := midiInPrepareHeader(FMidiHandle, MyMidiHdr.hdrPointer, - sizeof(TMIDIHDR)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - - { Save it in our list } - MidiHdrs.Add(MyMidiHdr); - end; - end; - -end; - -{-------------------------------------------------------------------} -{ Clean up from PrepareHeaders } - -procedure TMidiInput.UnprepareHeaders; -var - ctr: Word; -begin - if (MidiHdrs <> nil) then { will be Nil if 0 sysex buffers } - begin - for ctr := 0 to MidiHdrs.Count - 1 do - begin - FError := midiInUnprepareHeader(FMidiHandle, - TMyMidiHdr(MidiHdrs.Items[ctr]).hdrPointer, - sizeof(TMIDIHDR)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - TMyMidiHdr(MidiHdrs.Items[ctr]).Free; - end; - MidiHdrs.Free; - MidiHdrs := nil; - end; -end; - -{-------------------------------------------------------------------} -{ Add sysex buffers, if required, to input device } - -procedure TMidiInput.AddBuffers; -var - ctr: Word; -begin - if MidiHdrs <> nil then { will be Nil if 0 sysex buffers } - begin - if MidiHdrs.Count > 0 then - begin - for ctr := 0 to MidiHdrs.Count - 1 do - begin - FError := midiInAddBuffer(FMidiHandle, - TMyMidiHdr(MidiHdrs.Items[ctr]).hdrPointer, - sizeof(TMIDIHDR)); - if FError <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - end; - end; - end; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.Open; -var - hMem: THandle; -begin - try - { Create the buffer for the MIDI input messages } - if (PBuffer = nil) then - PBuffer := CircBufAlloc(FCapacity); - - { Create the control info for the DLL } - if (PCtlInfo = nil) then - begin - PCtlInfo := GlobalSharedLockedAlloc(Sizeof(TMidiCtlInfo), hMem); - PctlInfo^.hMem := hMem; - end; - PctlInfo^.pBuffer := PBuffer; - Pctlinfo^.hWindow := Handle; { Control's window handle } - PCtlInfo^.SysexOnly := FSysexOnly; - FError := midiInOpen(@FMidiHandle, FDeviceId, - DWORD(@midiHandler), - DWORD(PCtlInfo), - CALLBACK_FUNCTION); - - if (FError <> MMSYSERR_NOERROR) then - { TODO: use CreateFmtHelp to add MIDI device name/ID to message } - raise EMidiInputError.Create(MidiInErrorString(FError)); - - { Get sysex buffers ready } - PrepareHeaders; - - { Add them to the input } - AddBuffers; - - FState := misOpen; - - except - if PBuffer <> nil then - begin - CircBufFree(PBuffer); - PBuffer := nil; - end; - - if PCtlInfo <> nil then - begin - GlobalSharedLockedFree(PCtlInfo^.hMem, PCtlInfo); - PCtlInfo := nil; - end; - - end; - -end; - -{-------------------------------------------------------------------} - -function TMidiInput.GetMidiEvent: TMyMidiEvent; -var - thisItem: TMidiBufferItem; -begin - if (FState = misOpen) and - CircBufReadEvent(PBuffer, @thisItem) then - begin - Result := TMyMidiEvent.Create; - with thisItem do - begin - Result.Time := Timestamp; - if (Sysex = nil) then - begin - { Short message } - Result.MidiMessage := LoByte(LoWord(Data)); - Result.Data1 := HiByte(LoWord(Data)); - Result.Data2 := LoByte(HiWord(Data)); - Result.Sysex := nil; - Result.SysexLength := 0; - end - else - { Long Sysex message } - begin - Result.MidiMessage := MIDI_BEGINSYSEX; - Result.Data1 := 0; - Result.Data2 := 0; - Result.SysexLength := Sysex^.dwBytesRecorded; - if Sysex^.dwBytesRecorded <> 0 then - begin - { Put a copy of the sysex buffer in the object } - GetMem(Result.Sysex, Sysex^.dwBytesRecorded); - StrMove(Result.Sysex, Sysex^.lpData, Sysex^.dwBytesRecorded); - end; - - { Put the header back on the input buffer } - FError := midiInPrepareHeader(FMidiHandle, Sysex, - sizeof(TMIDIHDR)); - if Ferror = 0 then - FError := midiInAddBuffer(FMidiHandle, - Sysex, sizeof(TMIDIHDR)); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - end; - end; - CircbufRemoveEvent(PBuffer); - end - else - { Device isn't open, return a nil event } - Result := nil; -end; - -{-------------------------------------------------------------------} - -function TMidiInput.GetEventCount: Word; -begin - if FState = misOpen then - Result := PBuffer^.EventCount - else - Result := 0; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.Close; -begin - if FState = misOpen then - begin - FState := misClosed; - - { MidiInReset cancels any pending output. - Note that midiInReset causes an MIM_LONGDATA callback for each sysex - buffer on the input, so the callback function and Midi input buffer - should still be viable at this stage. - All the resulting MIM_LONGDATA callbacks will be completed by the time - MidiInReset returns, though. } - FError := MidiInReset(FMidiHandle); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - - { Remove sysex buffers from input device and free them } - UnPrepareHeaders; - - { Close the device (finally!) } - FError := MidiInClose(FMidiHandle); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - - FMidiHandle := 0; - - if (PBuffer <> nil) then - begin - CircBufFree(PBuffer); - PBuffer := nil; - end; - end; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.Start; -begin - if FState = misOpen then - begin - FError := MidiInStart(FMidiHandle); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - end; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.Stop; -begin - if FState = misOpen then - begin - FError := MidiInStop(FMidiHandle); - if Ferror <> MMSYSERR_NOERROR then - raise EMidiInputError.Create(MidiInErrorString(FError)); - end; -end; - -{-------------------------------------------------------------------} - -procedure TMidiInput.MidiInput(var Message: TMessage); -{ Triggered by incoming message from DLL. - Note DLL has already put the message in the queue } -begin - case Message.Msg of - mim_data: - { Trigger the user's MIDI input event, if they've specified one and - we're not in the process of closing the device. The check for - GetEventCount > 0 prevents unnecessary event calls where the user has - already cleared all the events from the input buffer using a GetMidiEvent - loop in the OnMidiInput event handler } - if Assigned(FOnMIDIInput) and (FState = misOpen) - and (GetEventCount > 0) then - FOnMIDIInput(Self); - - mim_Overflow: { input circular buffer overflow } - if Assigned(FOnOverflow) and (FState = misOpen) then - FOnOverflow(Self); - end; -end; - -{-------------------------------------------------------------------} - -procedure Register; -begin - RegisterComponents('Synth', [TMIDIInput]); -end; - -end. - diff --git a/src/lib/midi/Midiout.pas b/src/lib/midi/Midiout.pas deleted file mode 100644 index 98e6e3fb..00000000 --- a/src/lib/midi/Midiout.pas +++ /dev/null @@ -1,619 +0,0 @@ -{ $Header: /MidiComp/MidiOut.pas 2 10/06/97 7:33 Davec $ } - -{ Written by David Churcher , - released to the public domain. } - -{ Thanks very much to Fred Kohler for the Technology code. } - -unit MidiOut; - -{ - MIDI Output component. - - Properties: - DeviceID: Windows numeric device ID for the MIDI output device. - Between 0 and (midioutGetNumDevs-1), or MIDI_MAPPER (-1). - Special value MIDI_MAPPER specifies output to the Windows MIDI mapper - Read-only while device is open, exception if changed while open - - MIDIHandle: The output handle to the MIDI device. - 0 when device is not open - Read-only, runtime-only - - ProductName: Name of the output device product that corresponds to the - DeviceID property (e.g. 'MPU 401 out'). - You can write to this while the device is closed to select a particular - output device by name (the DeviceID property will change to match). - Exception if this property is changed while the device is open. - - Numdevs: Number of MIDI output devices installed on the system. This - is the value returned by midiOutGetNumDevs. It's included for - completeness. - - Technology: Type of technology used by the MIDI device. You can set this - property to one of the values listed for OutportTech (below) and the component - will find an appropriate MIDI device. For example: - MidiOutput.Technology := opt_FMSynth; - will set MidiInput.DeviceID to the MIDI device ID of the FM synth, if one - is installed. If no such device is available an exception is raised, - see MidiOutput.SetTechnology. - - See the MIDIOUTCAPS entry in MMSYSTEM.HLP for descriptions of the - following properties: - DriverVersion - Voices - Notes - ChannelMask - Support - - Error: The error code for the last MMSYSTEM error. See the MMSYSERR_ - entries in MMSYSTEM.INT for possible values. - - Methods: - Open: Open MIDI device specified by DeviceID property for output - - Close: Close device - - PutMidiEvent(Event:TMyMidiEvent): Output a note or sysex message to the - device. This method takes a TMyMidiEvent object and transmits it. - Notes: - 1. If the object contains a sysex event the OnMidiOutput event will - be triggered when the sysex transmission is complete. - 2. You can queue up multiple blocks of system exclusive data for - transmission by chucking them at this method; they will be - transmitted as quickly as the device can manage. - 3. This method will not free the TMyMidiEvent object, the caller - must do that. Any sysex data in the TMyMidiEvent is copied before - transmission so you can free the TMyMidiEvent immediately after - calling PutMidiEvent, even if output has not yet finished. - - PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte): Output a short - MIDI message. Handy when you can't be bothered to build a TMyMidiEvent. - If the message you're sending doesn't use Data1 or Data2, set them to 0. - - PutLong(TheSysex: Pointer; msgLength: Word): Output sysex data. - SysexPointer: Pointer to sysex data to send - msgLength: Length of sysex data. - This is handy when you don't have a TMyMidiEvent. - - SetVolume(Left: Word, Right: Word): Set the volume of the - left and right channels on the output device (only on internal devices?). - 0xFFFF is maximum volume. If the device doesn't support separate - left/right volume control, the value of the Left parameter will be used. - Check the Support property to see whether the device supports volume - control. See also other notes on volume control under midiOutSetVolume() - in MMSYSTEM.HLP. - - Events: - OnMidiOutput: Procedure called when output of a system exclusive block - is completed. - - Notes: - I haven't implemented any methods for midiOutCachePatches and - midiOutCacheDrumpatches, mainly 'cause I don't have any way of testing - them. Does anyone really use these? -} - -interface - -{$IFDEF FPC} - {$MODE Delphi} - {$H+} // use long strings -{$ENDIF} - -uses - SysUtils, - Windows, - Messages, - Classes, - MMSystem, - {$IFDEF FPC} - WinAllocation, - {$ENDIF} - Circbuf, - MidiType, - MidiDefs, - Delphmcb; - -{$IFDEF FPC} -type TmidioutCaps = MIDIOUTCAPS; -{$ENDIF} - -type - midioutputState = (mosOpen, mosClosed); - EmidioutputError = class(Exception); - - { These are the equivalent of constants prefixed with mod_ - as defined in MMSystem. See SetTechnology } - OutPortTech = ( - opt_None, { none } - opt_MidiPort, { output port } - opt_Synth, { generic internal synth } - opt_SQSynth, { square wave internal synth } - opt_FMSynth, { FM internal synth } - opt_Mapper); { MIDI mapper } - TechNameMap = array[OutPortTech] of string[18]; - - -const - TechName: TechNameMap = ( - 'None', 'MIDI Port', 'Generic Synth', 'Square Wave Synth', - 'FM Synth', 'MIDI Mapper'); - -{-------------------------------------------------------------------} -type - TMidiOutput = class(TComponent) - protected - Handle: THandle; { Window handle used for callback notification } - FDeviceID: Cardinal; { MIDI device ID } - FMIDIHandle: Hmidiout; { Handle to output device } - FState: midioutputState; { Current device state } - PCtlInfo: PMidiCtlInfo; { Pointer to control info for DLL } - - PBuffer: PCircularBuffer; { Output queue for PutTimedEvent, set by Open } - - FError: Word; { Last MMSYSTEM error } - - { Stuff from midioutCAPS } - FDriverVersion: MMVERSION; { Driver version from midioutGetDevCaps } - FProductName: string; { product name } - FTechnology: OutPortTech; { Type of MIDI output device } - FVoices: Word; { Number of voices (internal synth) } - FNotes: Word; { Number of notes (internal synth) } - FChannelMask: Word; { Bit set for each MIDI channels that the - device responds to (internal synth) } - FSupport: DWORD; { Technology supported (volume control, - patch caching etc. } - FNumdevs: Word; { Number of MIDI output devices on system } - - - FOnMIDIOutput: TNotifyEvent; { Sysex output finished } - - procedure MidiOutput(var Message: TMessage); - procedure SetDeviceID(DeviceID: Cardinal); - procedure SetProductName(NewProductName: string); - procedure SetTechnology(NewTechnology: OutPortTech); - function midioutErrorString(WError: Word): string; - - public - { Properties } - property MIDIHandle: Hmidiout read FMIDIHandle; - property DriverVersion: MMVERSION { Driver version from midioutGetDevCaps } - read FDriverVersion; - property Technology: OutPortTech { Type of MIDI output device } - read FTechnology - write SetTechnology - default opt_Synth; - property Voices: Word { Number of voices (internal synth) } - read FVoices; - property Notes: Word { Number of notes (internal synth) } - read FNotes; - property ChannelMask: Word { Bit set for each MIDI channels that the } - read FChannelMask; { device responds to (internal synth) } - property Support: DWORD { Technology supported (volume control, } - read FSupport; { patch caching etc. } - property Error: Word read FError; - property Numdevs: Word read FNumdevs; - - { Methods } - function Open: Boolean; virtual; - function Close: Boolean; virtual; - procedure PutMidiEvent(theEvent: TMyMidiEvent); virtual; - procedure PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte); virtual; - procedure PutLong(TheSysex: Pointer; msgLength: Word); virtual; - procedure SetVolume(Left: Word; Right: Word); - constructor Create(AOwner: TComponent); override; - destructor Destroy; override; - - { Some functions to decode and classify incoming messages would be nice } - - published - { TODO: Property editor with dropdown list of product names } - property ProductName: string read FProductName write SetProductName; - - property DeviceID: Cardinal read FDeviceID write SetDeviceID default 0; - { TODO: midiOutGetVolume? Or two properties for Left and Right volume? - Is it worth it?? - midiOutMessage?? Does anyone use this? } - - { Events } - property Onmidioutput: TNotifyEvent - read FOnmidioutput - write FOnmidioutput; - end; - -procedure Register; - -{-------------------------------------------------------------------} -implementation - -(* Not used in Delphi 3 - -{ This is the callback procedure in the external DLL. - It's used when midioutOpen is called by the Open method. - There are special requirements and restrictions for this callback - procedure (see midioutOpen in MMSYSTEM.HLP) so it's impractical to - make it an object method } -{$IFDEF WIN32} -function midiHandler( - hMidiIn: HMidiIn; - wMsg: UINT; - dwInstance: DWORD; - dwParam1: DWORD; - dwParam2: DWORD): Boolean; stdcall; external 'DELMID32.DLL'; -{$ELSE} -function midiHandler( - hMidiIn: HMidiIn; - wMsg: Word; - dwInstance: DWORD; - dwParam1: DWORD; - dwParam2: DWORD): Boolean; far; external 'DELPHMID.DLL'; -{$ENDIF} -*) - -{-------------------------------------------------------------------} - -constructor Tmidioutput.Create(AOwner: TComponent); -begin - inherited Create(AOwner); - FState := mosClosed; - FNumdevs := midiOutGetNumDevs; - - { Create the window for callback notification } - if not (csDesigning in ComponentState) then - begin - Handle := AllocateHwnd(MidiOutput); - end; - -end; - -{-------------------------------------------------------------------} - -destructor Tmidioutput.Destroy; -begin - if FState = mosOpen then - Close; - if (PCtlInfo <> nil) then - GlobalSharedLockedFree(PCtlinfo^.hMem, PCtlInfo); - DeallocateHwnd(Handle); - inherited Destroy; -end; - -{-------------------------------------------------------------------} -{ Convert the numeric return code from an MMSYSTEM function to a string - using midioutGetErrorText. TODO: These errors aren't very helpful - (e.g. "an invalid parameter was passed to a system function") so - some proper error strings would be nice. } - - -function Tmidioutput.midioutErrorString(WError: Word): string; -var - errorDesc: PChar; -begin - errorDesc := nil; - try - errorDesc := StrAlloc(MAXERRORLENGTH); - if midioutGetErrorText(WError, errorDesc, MAXERRORLENGTH) = 0 then - result := StrPas(errorDesc) - else - result := 'Specified error number is out of range'; - finally - if errorDesc <> nil then StrDispose(errorDesc); - end; -end; - -{-------------------------------------------------------------------} -{ Set the output device ID and change the other properties to match } - -procedure Tmidioutput.SetDeviceID(DeviceID: Cardinal); -var - midioutCaps: TmidioutCaps; -begin - if FState = mosOpen then - raise EmidioutputError.Create('Change to DeviceID while device was open') - else - if (DeviceID >= midioutGetNumDevs) and (DeviceID <> MIDI_MAPPER) then - raise EmidioutputError.Create('Invalid device ID') - else - begin - FDeviceID := DeviceID; - - { Set the name and other midioutCAPS properties to match the ID } - FError := - midioutGetDevCaps(DeviceID, @midioutCaps, sizeof(TmidioutCaps)); - if Ferror > 0 then - raise EmidioutputError.Create(midioutErrorString(FError)); - - with midiOutCaps do - begin - FProductName := StrPas(szPname); - FDriverVersion := vDriverVersion; - FTechnology := OutPortTech(wTechnology); - FVoices := wVoices; - FNotes := wNotes; - FChannelMask := wChannelMask; - FSupport := dwSupport; - end; - - end; -end; - -{-------------------------------------------------------------------} -{ Set the product name property and put the matching output device number - in FDeviceID. - This is handy if you want to save a configured output/output device - by device name instead of device number, because device numbers may - change if users install or remove MIDI devices. - Exception if output device with matching name not found, - or if output device is open } - -procedure Tmidioutput.SetProductName(NewProductName: string); -var - midioutCaps: TmidioutCaps; - testDeviceID: Integer; - testProductName: string; -begin - if FState = mosOpen then - raise EmidioutputError.Create('Change to ProductName while device was open') - else - { Don't set the name if the component is reading properties because - the saved Productname will be from the machine the application was compiled - on, which may not be the same for the corresponding DeviceID on the user's - machine. The FProductname property will still be set by SetDeviceID } - if not (csLoading in ComponentState) then - begin - { Loop uses -1 to test for MIDI_MAPPER as well } - for testDeviceID := -1 to (midioutGetNumDevs - 1) do - begin - FError := - midioutGetDevCaps(testDeviceID, @midioutCaps, sizeof(TmidioutCaps)); - if Ferror > 0 then - raise EmidioutputError.Create(midioutErrorString(FError)); - testProductName := StrPas(midioutCaps.szPname); - if testProductName = NewProductName then - begin - FProductName := NewProductName; - Break; - end; - end; - if FProductName <> NewProductName then - raise EmidioutputError.Create('MIDI output Device ' + - NewProductName + ' not installed') - else - SetDeviceID(testDeviceID); - end; -end; - -{-------------------------------------------------------------------} -{ Set the output technology property and put the matching output device - number in FDeviceID. - This is handy, for example, if you want to be able to switch between a - sound card and a MIDI port } - -procedure TMidiOutput.SetTechnology(NewTechnology: OutPortTech); -var - midiOutCaps: TMidiOutCaps; - testDeviceID: Integer; - testTechnology: OutPortTech; -begin - if FState = mosOpen then - raise EMidiOutputError.Create( - 'Change to Product Technology while device was open') - else - begin - { Loop uses -1 to test for MIDI_MAPPER as well } - for testDeviceID := -1 to (midiOutGetNumDevs - 1) do - begin - FError := - midiOutGetDevCaps(testDeviceID, - @midiOutCaps, sizeof(TMidiOutCaps)); - if Ferror > 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); - testTechnology := OutPortTech(midiOutCaps.wTechnology); - if testTechnology = NewTechnology then - begin - FTechnology := NewTechnology; - Break; - end; - end; - if FTechnology <> NewTechnology then - raise EMidiOutputError.Create('MIDI output technology ' + - TechName[NewTechnology] + ' not installed') - else - SetDeviceID(testDeviceID); - end; -end; - -{-------------------------------------------------------------------} - -function Tmidioutput.Open: Boolean; -var - hMem: THandle; -begin - Result := False; - try - { Create the control info for the DLL } - if (PCtlInfo = nil) then - begin - PCtlInfo := GlobalSharedLockedAlloc(Sizeof(TMidiCtlInfo), hMem); - PctlInfo^.hMem := hMem; - end; - - Pctlinfo^.hWindow := Handle; { Control's window handle } - - FError := midioutOpen(@FMidiHandle, FDeviceId, - DWORD(@midiHandler), - DWORD(PCtlInfo), - CALLBACK_FUNCTION); -{ FError := midioutOpen(@FMidiHandle, FDeviceId, - Handle, - DWORD(PCtlInfo), - CALLBACK_WINDOW); } - if (FError <> 0) then - { TODO: use CreateFmtHelp to add MIDI device name/ID to message } - raise EmidioutputError.Create(midioutErrorString(FError)) - else - begin - Result := True; - FState := mosOpen; - end; - - except - if PCtlInfo <> nil then - begin - GlobalSharedLockedFree(PCtlInfo^.hMem, PCtlInfo); - PCtlInfo := nil; - end; - end; - -end; - -{-------------------------------------------------------------------} - -procedure TMidiOutput.PutShort(MidiMessage: Byte; Data1: Byte; Data2: Byte); -var - thisMsg: DWORD; -begin - thisMsg := DWORD(MidiMessage) or - (DWORD(Data1) shl 8) or - (DWORD(Data2) shl 16); - - FError := midiOutShortMsg(FMidiHandle, thisMsg); - if Ferror > 0 then - raise EmidioutputError.Create(midioutErrorString(FError)); -end; - -{-------------------------------------------------------------------} - -procedure TMidiOutput.PutLong(TheSysex: Pointer; msgLength: Word); -{ Notes: This works asynchronously; you send your sysex output by -calling this function, which returns immediately. When the MIDI device -driver has finished sending the data the MidiOutPut function in this -component is called, which will in turn call the OnMidiOutput method -if the component user has defined one. } -{ TODO: Combine common functions with PutTimedLong into subroutine } - -var - MyMidiHdr: TMyMidiHdr; -begin - { Initialize the header and allocate buffer memory } - MyMidiHdr := TMyMidiHdr.Create(msgLength); - - { Copy the data over to the MidiHdr buffer - We can't just use the caller's PChar because the buffer memory - has to be global, shareable, and locked. } - StrMove(MyMidiHdr.SysexPointer, TheSysex, msgLength); - - { Store the MyMidiHdr address in the header so we can find it again quickly - (see the MidiOutput proc) } - MyMidiHdr.hdrPointer^.dwUser := DWORD(MyMidiHdr); - - { Get MMSYSTEM's blessing for this header } - FError := midiOutPrepareHeader(FMidiHandle, MyMidiHdr.hdrPointer, - sizeof(TMIDIHDR)); - if Ferror > 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); - - { Send it } - FError := midiOutLongMsg(FMidiHandle, MyMidiHdr.hdrPointer, - sizeof(TMIDIHDR)); - if Ferror > 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); - -end; - -{-------------------------------------------------------------------} - -procedure Tmidioutput.PutMidiEvent(theEvent: TMyMidiEvent); -begin - if FState <> mosOpen then - raise EMidiOutputError.Create('MIDI Output device not open'); - - with theEvent do - begin - if Sysex = nil then - begin - PutShort(MidiMessage, Data1, Data2) - end - else - PutLong(Sysex, SysexLength); - end; -end; - -{-------------------------------------------------------------------} - -function Tmidioutput.Close: Boolean; -begin - Result := False; - if FState = mosOpen then - begin - - { Note this sends a lot of fast control change messages which some synths can't handle. - TODO: Make this optional. } -{ FError := midioutReset(FMidiHandle); - if Ferror <> 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); } - - FError := midioutClose(FMidiHandle); - if Ferror <> 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)) - else - Result := True; - end; - - FMidiHandle := 0; - FState := mosClosed; - -end; - -{-------------------------------------------------------------------} - -procedure TMidiOutput.SetVolume(Left: Word; Right: Word); -var - dwVolume: DWORD; -begin - dwVolume := (DWORD(Left) shl 16) or Right; - FError := midiOutSetVolume(DeviceID, dwVolume); - if Ferror <> 0 then - raise EMidiOutputError.Create(MidiOutErrorString(FError)); -end; - -{-------------------------------------------------------------------} - -procedure Tmidioutput.midioutput(var Message: TMessage); -{ Triggered when sysex output from PutLong is complete } -var - MyMidiHdr: TMyMidiHdr; - thisHdr: PMidiHdr; -begin - if Message.Msg = Mom_Done then - begin - { Find the MIDIHDR we used for the output. Message.lParam is its address } - thisHdr := PMidiHdr(Message.lParam); - - { Remove it from the output device } - midiOutUnprepareHeader(FMidiHandle, thisHdr, sizeof(TMIDIHDR)); - - { Get the address of the MyMidiHdr object containing this MIDIHDR structure. - We stored this address in the PutLong procedure } - MyMidiHdr := TMyMidiHdr(thisHdr^.dwUser); - - { Header and copy of sysex data no longer required since output is complete } - MyMidiHdr.Free; - - { Call the user's event handler if any } - if Assigned(FOnmidioutput) then - FOnmidioutput(Self); - end; - { TODO: Case for MOM_PLAYBACK_DONE } -end; - -{-------------------------------------------------------------------} - -procedure Register; -begin - RegisterComponents('Synth', [Tmidioutput]); -end; - -end. - diff --git a/src/lib/midi/demo/MidiTest.pas b/src/lib/midi/demo/MidiTest.pas deleted file mode 100644 index 793db730..00000000 --- a/src/lib/midi/demo/MidiTest.pas +++ /dev/null @@ -1,249 +0,0 @@ -// Test application for TMidiFile - -unit MidiTest; - -interface - -uses - Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, - StdCtrls, MidiFile, ExtCtrls, MidiOut, MidiType, MidiScope, Grids; -type - TMidiPlayer = class(TForm) - OpenDialog1: TOpenDialog; - Button1: TButton; - Button3: TButton; - Button4: TButton; - MidiOutput1: TMidiOutput; - cmbInput: TComboBox; - MidiFile1: TMidiFile; - MidiScope1: TMidiScope; - Label3: TLabel; - edtBpm: TEdit; - Memo2: TMemo; - edtTime: TEdit; - Button2: TButton; - TrackGrid: TStringGrid; - TracksGrid: TStringGrid; - edtLength: TEdit; - procedure Button1Click(Sender: TObject); - procedure MidiFile1MidiEvent(event: PMidiEvent); - procedure Button3Click(Sender: TObject); - procedure Button4Click(Sender: TObject); - procedure FormCreate(Sender: TObject); - procedure cmbInputChange(Sender: TObject); - procedure MidiFile1UpdateEvent(Sender: TObject); - procedure Button2Click(Sender: TObject); - procedure edtBpmKeyPress(Sender: TObject; var Key: Char); - procedure TracksGridSelectCell(Sender: TObject; Col, Row: Integer; - var CanSelect: Boolean); - procedure FormShow(Sender: TObject); - private - { Private declarations } - MidiOpened : boolean; - procedure SentAllNotesOff; - - procedure MidiOpen; - procedure MidiClose; - - public - { Public declarations } - end; - -var - MidiPlayer: TMidiPlayer; - -implementation - -{$R *.DFM} - -procedure TMidiPlayer.Button1Click(Sender: TObject); -var - i,j: integer; - track : TMidiTrack; - event : PMidiEvent; -begin - if opendialog1.execute then - begin - midifile1.filename := opendialog1.filename; - midifile1.readfile; -// label1.caption := IntToStr(midifile1.NumberOfTracks); - edtBpm.text := IntToStr(midifile1.Bpm); -// TracksGrid.cells.clear; - for i := 0 to midifile1.NumberOfTracks-1 do - begin - track := midifile1.getTrack(i); - TracksGrid.cells[0,i] := 'Tr: '+ track.getName + ' '+ track.getInstrument ; - end; - edtLength.Text := MyTimeToStr(MidiFile1.GetTrackLength); - end; -end; - -procedure TMidiPlayer.MidiFile1MidiEvent(event: PMidiEvent); -var mEvent : TMyMidiEvent; -begin - mEvent := TMyMidiEvent.Create; - if not (event.event = $FF) then - begin - mEvent.MidiMessage := event.event; - mEvent.data1 := event.data1; - mEvent.data2 := event.data2; - midioutput1.PutMidiEvent(mEvent); - end - else - begin - if (event.data1 >= 1) and (event.data1 < 15) then - begin - memo2.Lines.add(IntToStr(event.data1) + ' '+ event.str); - end - end; - midiScope1.MidiEvent(event.event,event.data1,event.data2); - mEvent.Destroy; -end; - -procedure TMidiPlayer.SentAllNotesOff; -var mEvent : TMyMidiEvent; -channel : integer; -begin - mEvent := TMyMidiEvent.Create; - for channel:= 0 to 15 do - begin - mEvent.MidiMessage := $B0 + channel; - mEvent.data1 := $78; - mEvent.data2 := 0; - if MidiOpened then - midioutput1.PutMidiEvent(mEvent); - midiScope1.MidiEvent(mEvent.MidiMessage,mEvent.data1,mEvent.data2); - end; - mEvent.Destroy; -end; - -procedure TMidiPlayer.Button3Click(Sender: TObject); -begin - midifile1.StartPlaying; -end; - -procedure TMidiPlayer.Button4Click(Sender: TObject); -begin - midifile1.StopPlaying; - SentAllNotesOff; -end; - -procedure TMidiPlayer.MidiOpen; -begin - if not (cmbInput.Text = '') then - begin - MidiOutput1.ProductName := cmbInput.Text; - MidiOutput1.OPEN; - MidiOpened := true; - end; -end; - -procedure TMidiPlayer.MidiClose; -begin - if MidiOpened then - begin - MidiOutput1.Close; - MidiOpened := false; - end; -end; - - -procedure TMidiPlayer.FormCreate(Sender: TObject); -var thisDevice : integer; -begin - for thisDevice := 0 to MidiOutput1.NumDevs - 1 do - begin - MidiOutput1.DeviceID := thisDevice; - cmbInput.Items.Add(MidiOutput1.ProductName); - end; - cmbInput.ItemIndex := 0; - MidiOpened := false; - MidiOpen; -end; - -procedure TMidiPlayer.cmbInputChange(Sender: TObject); -begin - MidiClose; - MidiOPen; -end; - -procedure TMidiPlayer.MidiFile1UpdateEvent(Sender: TObject); -begin - edtTime.Text := MyTimeToStr(MidiFile1.GetCurrentTime); - edtTime.update; - if MidiFile1.ready then - begin - midifile1.StopPlaying; - SentAllNotesOff; - end; -end; - -procedure TMidiPlayer.Button2Click(Sender: TObject); -begin - MidiFile1.ContinuePlaying; -end; - -procedure TMidiPlayer.edtBpmKeyPress(Sender: TObject; var Key: Char); -begin - if Key = char(13) then - begin - MidiFile1.Bpm := StrToInt(edtBpm.Text); - edtBpm.text := IntToStr(midifile1.Bpm); - abort; - end; - -end; - -procedure TMidiPlayer.TracksGridSelectCell(Sender: TObject; Col, - Row: Integer; var CanSelect: Boolean); -var - MidiTrack : TMidiTrack; - i : integer; - j : integer; - event : PMidiEvent; -begin - CanSelect := false; - if Row < MidiFile1.NumberOfTracks then - begin - CanSelect := true; - MidiTrack := MidiFile1.GetTrack(Row); - TrackGrid.RowCount := 2; - TrackGrid.RowCount := MidiTrack.getEventCount; - j := 1; - for i := 0 to MidiTrack.GetEventCount-1 do - begin - event := MidiTrack.getEvent(i); - if not (event.len = -1) then - begin // do not print when - TrackGrid.cells[0,j] := IntToStr(i); - TrackGrid.cells[1,j] := MyTimeToStr(event.time); - TrackGrid.cells[2,j] := IntToHex(event.event,2); - if not (event.event = $FF) then - begin - TrackGrid.cells[3,j] := IntToStr(event.len); - TrackGrid.cells[4,j] := KeyToStr(event.data1); - TrackGrid.cells[5,j] := IntToStr(event.data2); - end - else - begin - TrackGrid.cells[3,j] := IntToStr(event.data1); - TrackGrid.cells[4,j] := ''; - TrackGrid.cells[5,j] := event.str; - end; - inc(j); - end; - end; - TrackGrid.RowCount := j; - end; -end; - -procedure TMidiPlayer.FormShow(Sender: TObject); -begin - TrackGrid.ColWidths[0] := 30; - TrackGrid.ColWidths[2] := 30; - TrackGrid.ColWidths[3] := 30; - TrackGrid.ColWidths[4] := 30; - TrackGrid.ColWidths[5] := 100; -end; - -end. -- cgit v1.2.3