{**************************************************************************************************} { } { Project JEDI Code Library (JCL) } { } { The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); } { you may not use this file except in compliance with the License. You may obtain a copy of the } { License at http://www.mozilla.org/MPL/ } { } { Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF } { ANY KIND, either express or implied. See the License for the specific language governing rights } { and limitations under the License. } { } { The Original Code is JclMIDI.pas. } { } { The Initial Developer of the Original Code is Robert Rossmair. } { Portions created by Robert Rossmair are Copyright (C) Robert Rossmair. All rights reserved. } { } { Contributor(s): } { Robert Rossmair } { } {**************************************************************************************************} { } { Platform-independent MIDI declarations } { } { Unit owner: Robert Rossmair } { } {**************************************************************************************************} // Last modified: $Date: 2005/08/07 13:09:54 $ // For history see end of file unit JclMIDI; {$I jcl.inc} interface uses Classes, JclBase; // manifest constants for MIDI message protocol const // MIDI Status Bytes for Channel Voice Messages MIDIMsgNoteOff = $80; MIDIMsgNoteOn = $90; MIDIMsgPolyKeyPressure = $A0; MIDIMsgControlChange = $B0; MIDIMsgProgramChange = $C0; MIDIMsgChannelKeyPressure = $D0; MIDIMsgAftertouch = MIDIMsgChannelKeyPressure; // Synonym MIDIMsgPitchWheelChange = $E0; // MIDI Status Bytes for System Common Messages MIDIMsgSysEx = $F0; MIDIMsgMTCQtrFrame = $F1; // MIDI Time Code Qtr. Frame MIDIMsgSongPositionPtr = $F2; MIDIMsgSongSelect = $F3; MIDIMsgTuneRequest = $F6; MIDIMsgEOX = $F7; // marks end of system exclusive message // MIDI Status Bytes for System Real-Time Messages MIDIMsgTimingClock = $F8; MIDIMsgStartSequence = $FA; MIDIMsgContinueSequence = $FB; MIDIMsgStopSequence = $FC; MIDIMsgActiveSensing = $FE; MIDIMsgSystemReset = $FF; // MIDICC...: MIDI Control Change Messages // Continuous Controllers MSB MIDICCBankSelect = $00; MIDICCModulationWheel = $01; MIDICCBreathControl = $02; MIDICCFootController = $04; MIDICCPortamentoTime = $05; MIDICCDataEntry = $06; MIDICCChannelVolume = $07; MIDICCMainVolume = MIDICCChannelVolume; MIDICCBalance = $08; MIDICCPan = $0A; MIDICCExpression = $0B; MIDICCEffectControl = $0C; MIDICCEffectControl2 = $0D; MIDICCGeneralPurpose1 = $10; MIDICCGeneralPurpose2 = $11; MIDICCGeneralPurpose3 = $12; MIDICCGeneralPurpose4 = $13; // Continuous Controllers LSB MIDICCBankSelectLSB = $20; MIDICCModulationWheelLSB = $21; MIDICCBreathControlLSB = $22; MIDICCFootControllerLSB = $24; MIDICCPortamentoTimeLSB = $25; MIDICCDataEntryLSB = $26; MIDICCChannelVolumeLSB = $27; MIDICCMainVolumeLSB = MIDICCChannelVolumeLSB; MIDICCBalanceLSB = $28; MIDICCPanLSB = $2A; MIDICCExpressionLSB = $2B; MIDICCEffectControlLSB = $2C; MIDICCEffectControl2LSB = $2D; MIDICCGeneralPurpose1LSB = $30; MIDICCGeneralPurpose2LSB = $31; MIDICCGeneralPurpose3LSB = $32; MIDICCGeneralPurpose4LSB = $33; // Switches MIDICCSustain = $40; MIDICCPortamento = $41; MIDICCSustenuto = $42; MIDICCSoftPedal = $43; MIDICCLegato = $44; MIDICCHold2 = $45; MIDICCSound1 = $46; // (Sound Variation) MIDICCSound2 = $47; // (Timbre/Harmonic Intens.) MIDICCSound3 = $48; // (Release Time) MIDICCSound4 = $49; // (Attack Time) MIDICCSound5 = $4A; // (Brightness) MIDICCSound6 = $4B; // (Decay Time) MIDICCSound7 = $4C; // (Vibrato Rate) MIDICCSound8 = $4D; // (Vibrato Depth) MIDICCSound9 = $4E; // (Vibrato Delay) MIDICCSound10 = $4F; // MIDICCGeneralPurpose5 = $50; MIDICCGeneralPurpose6 = $51; MIDICCGeneralPurpose7 = $52; MIDICCGeneralPurpose8 = $53; MIDICCPortamentoControl = $54; MIDICCReverbSendLevel = $5B; MIDICCEffects2Depth = $5C; MIDICCTremoloDepth = MIDICCEffects2Depth; MIDICCChorusSendLevel = $5D; MIDICCEffects4Depth = $5E; MIDICCCelesteDepth = MIDICCEffects4Depth; MIDICCEffects5Depth = $5F; MIDICCPhaserDepth = MIDICCEffects5Depth; MIDICCDataEntryInc = $60; MIDICCDataEntryDec = $61; MIDICCNonRegParamNumLSB = $62; MIDICCNonRegParamNumMSB = $63; MIDICCRegParamNumLSB = $64; MIDICCRegParamNumMSB = $65; // Registered Parameter Numbers [CC# 65H,64H] // ----------------------------------------------------------- // CC#65 (MSB) | CC#64 (LSB) | Function // Hex|Dec| | Hex|Dec| | // - - - - - - | - - - - - - |- - - - - - - - - - - - - - - - // 00 = 0 | 00 = 0 | Pitch Bend Sensitivity // 00 = 0 | 01 = 1 | Channel Fine Tuning // 00 = 0 | 02 = 2 | Channel Coarse Tuning // 00 = 0 | 03 = 3 | Tuning Program Change // 00 = 0 | 04 = 4 | Tuning Bank Select // Channel Mode Messages (Control Change >= $78) MIDICCAllSoundOff = $78; MIDICCResetAllControllers = $79; MIDICCLocalControl = $7A; MIDICCAllNotesOff = $7B; MIDICCOmniModeOff = $7C; MIDICCOmniModeOn = $7D; MIDICCMonoModeOn = $7E; MIDICCPolyModeOn = $7F; type TMIDIChannel = 1..16; TMIDIDataByte = 0..$7F; // 7 bits TMIDIDataWord = 0..$3FFF; // 14 bits TMIDIStatusByte = $80..$FF; TMIDIVelocity = TMIDIDataByte; TMIDIKey = TMIDIDataByte; TMIDINote = TMIDIKey; const // Helper definitions MIDIDataMask = $7F; MIDIDataWordMask = $3FFF; MIDIChannelMsgMask = $F0; MIDIInvalidStatus = TMIDIStatusByte(0); BitsPerMIDIDataByte = 7; BitsPerMIDIDataWord = BitsPerMIDIDataByte * 2; MIDIPitchWheelCenter = 1 shl (BitsPerMIDIDataWord - 1); type TMIDINotes = set of TMIDINote; TSingleNoteTuningData = packed record case Integer of 0: (Key: TMIDINote; Frequency: array [0..2] of TMIDIDataByte); 1: (DWord: LongWord); end; EJclMIDIError = class(EJclError); // MIDI Out IJclMIDIOut = interface ['{A29C3EBD-EB70-4C72-BEC5-700AF57FD4C8}'] // property access methods function GetActiveNotes(Channel: TMIDIChannel): TMIDINotes; function GetName: string; function GetMIDIStatus: TMIDIStatusByte; function GetRunningStatusEnabled: Boolean; procedure SetRunningStatusEnabled(const Value: Boolean); // General message send method procedure SendMessage(const Data: array of Byte); // Channel Voice Messages procedure SendNoteOff(Channel: TMIDIChannel; Key: TMIDINote; Velocity: TMIDIDataByte = $40); procedure SendNoteOn(Channel: TMIDIChannel; Key: TMIDINote; Velocity: TMIDIDataByte); procedure SendPolyphonicKeyPressure(Channel: TMIDIChannel; Key: TMIDINote; Value: TMIDIDataByte); procedure SendControlChange(Channel: TMIDIChannel; ControllerNum, Value: TMIDIDataByte); // High Resolution "macro" for controller numbers <= $13, sends upper 7 bits first, // lower 7 bits per additional LSB message afterwards procedure SendControlChangeHR(Channel: TMIDIChannel; ControllerNum: TMIDIDataByte; Value: TMIDIDataWord); procedure SendSwitchChange(Channel: TMIDIChannel; ControllerNum: TMIDIDataByte; Value: Boolean); procedure SendProgramChange(Channel: TMIDIChannel; ProgramNum: TMIDIDataByte); procedure SendChannelPressure(Channel: TMIDIChannel; Value: TMIDIDataByte); procedure SendPitchWheelChange(Channel: TMIDIChannel; Value: TMIDIDataWord); procedure SendPitchWheelPos(Channel: TMIDIChannel; Value: Single); // Control Change Messages procedure SelectProgram(Channel: TMIDIChannel; BankNum: TMIDIDataWord; ProgramNum: TMIDIDataByte); procedure SendModulationWheelChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendBreathControlChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendFootControllerChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendPortamentoTimeChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendDataEntry(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendChannelVolumeChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendBalanceChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendPanChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendExpressionChange(Channel: TMIDIChannel; Value: TMidiDataByte); // "high resolution" variants procedure SendModulationWheelChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendBreathControlChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendFootControllerChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendPortamentoTimeChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendDataEntryHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendChannelVolumeChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendBalanceChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendPanChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendExpressionChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); // Control Change Messages: Switches procedure SwitchSustain(Channel: TMIDIChannel; Value: Boolean); procedure SwitchPortamento(Channel: TMIDIChannel; Value: Boolean); procedure SwitchSostenuto(Channel: TMIDIChannel; Value: Boolean); procedure SwitchSoftPedal(Channel: TMIDIChannel; Value: Boolean); procedure SwitchLegato(Channel: TMIDIChannel; Value: Boolean); procedure SwitchHold2(Channel: TMIDIChannel; Value: Boolean); // Channel Mode Messages procedure SwitchAllSoundOff(Channel: TMIDIChannel); procedure ResetAllControllers(Channel: TMIDIChannel); procedure SwitchLocalControl(Channel: TMIDIChannel; Value: Boolean); procedure SwitchAllNotesOff(Channel: TMIDIChannel); procedure SwitchOmniModeOff(Channel: TMIDIChannel); procedure SwitchOmniModeOn(Channel: TMIDIChannel); procedure SwitchMonoModeOn(Channel: TMIDIChannel; ChannelCount: Integer); procedure SwitchPolyModeOn(Channel: TMIDIChannel); // procedure SendSingleNoteTuningChange(const TargetDeviceID, TuningProgramNum: TMidiDataByte; const TuningData: array of TSingleNoteTuningData); function NoteIsOn(Channel: TMIDIChannel; Key: TMIDINote): Boolean; procedure SwitchActiveNotesOff(Channel: TMIDIChannel); overload; procedure SwitchActiveNotesOff; overload; // Properties property ActiveNotes[Channel: TMIDIChannel]: TMIDINotes read GetActiveNotes; property Name: string read GetName; property LocalControl[Channel: TMIDIChannel]: Boolean write SwitchLocalControl; property MIDIStatus: TMIDIStatusByte read GetMIDIStatus; // Tribute to some braindead devices which cannot handle running status (e.g. ESS Solo 1 Win2k driver) property RunningStatusEnabled: Boolean read GetRunningStatusEnabled write SetRunningStatusEnabled; end; // Abstract MIDI Out device class TJclMIDIOut = class(TInterfacedObject, IJclMIDIOut) private FMIDIStatus: TMIDIStatusByte; FRunningStatusEnabled: Boolean; FActiveNotes: array [TMIDIChannel] of TMIDINotes; protected function GetActiveNotes(Channel: TMIDIChannel): TMIDINotes; function GetName: string; virtual; abstract; function GetMIDIStatus: TMIDIStatusByte; function IsRunningStatus(StatusByte: TMIDIStatusByte): Boolean; function GetRunningStatusEnabled: Boolean; procedure SetRunningStatusEnabled(const Value: Boolean); procedure SendChannelMessage(Msg: TMIDIStatusByte; Channel: TMIDIChannel; Data1, Data2: TMIDIDataByte); procedure DoSendMessage(const Data: array of Byte); virtual; abstract; procedure SendMessage(const Data: array of Byte); public destructor Destroy; override; // Channel Voice Messages procedure SendNoteOff(Channel: TMIDIChannel; Key: TMIDINote; Velocity: TMIDIDataByte = $40); procedure SendNoteOn(Channel: TMIDIChannel; Key: TMIDINote; Velocity: TMIDIDataByte); procedure SendPolyphonicKeyPressure(Channel: TMIDIChannel; Key: TMIDINote; Value: TMIDIDataByte); procedure SendControlChange(Channel: TMIDIChannel; ControllerNum, Value: TMIDIDataByte); procedure SendControlChangeHR(Channel: TMIDIChannel; ControllerNum: TMIDIDataByte; Value: TMIDIDataWord); procedure SendSwitchChange(Channel: TMIDIChannel; ControllerNum: TMIDIDataByte; Value: Boolean); procedure SendProgramChange(Channel: TMIDIChannel; ProgramNum: TMIDIDataByte); procedure SendChannelPressure(Channel: TMIDIChannel; Value: TMIDIDataByte); procedure SendPitchWheelChange(Channel: TMIDIChannel; Value: TMIDIDataWord); procedure SendPitchWheelPos(Channel: TMIDIChannel; Value: Single); // Control Change Messages procedure SelectProgram(Channel: TMIDIChannel; BankNum: TMIDIDataWord; ProgramNum: TMIDIDataByte); procedure SendModulationWheelChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendBreathControlChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendFootControllerChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendPortamentoTimeChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendDataEntry(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendChannelVolumeChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendBalanceChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendPanChange(Channel: TMIDIChannel; Value: TMidiDataByte); procedure SendExpressionChange(Channel: TMIDIChannel; Value: TMidiDataByte); // ...high Resolution procedure SendModulationWheelChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendBreathControlChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendFootControllerChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendPortamentoTimeChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendDataEntryHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendChannelVolumeChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendBalanceChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendPanChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); procedure SendExpressionChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); // Control Change Messages: Switches procedure SwitchSustain(Channel: TMIDIChannel; Value: Boolean); procedure SwitchPortamento(Channel: TMIDIChannel; Value: Boolean); procedure SwitchSostenuto(Channel: TMIDIChannel; Value: Boolean); procedure SwitchSoftPedal(Channel: TMIDIChannel; Value: Boolean); procedure SwitchLegato(Channel: TMIDIChannel; Value: Boolean); procedure SwitchHold2(Channel: TMIDIChannel; Value: Boolean); // Channel Mode Messages procedure SwitchAllSoundOff(Channel: TMIDIChannel); procedure ResetAllControllers(Channel: TMIDIChannel); procedure SwitchLocalControl(Channel: TMIDIChannel; Value: Boolean); procedure SwitchAllNotesOff(Channel: TMIDIChannel); procedure SwitchOmniModeOff(Channel: TMIDIChannel); procedure SwitchOmniModeOn(Channel: TMIDIChannel); procedure SwitchMonoModeOn(Channel: TMIDIChannel; ChannelCount: Integer); procedure SwitchPolyModeOn(Channel: TMIDIChannel); // procedure SendSingleNoteTuningChange(const TargetDeviceID, TuningProgramNum: TMidiDataByte; const TuningData: array of TSingleNoteTuningData); function NoteIsOn(Channel: TMIDIChannel; Key: TMIDINote): Boolean; procedure SwitchActiveNotesOff(Channel: TMIDIChannel); overload; procedure SwitchActiveNotesOff; overload; property ActiveNotes[Channel: TMIDIChannel]: TMIDINotes read GetActiveNotes; property Name: string read GetName; property RunningStatusEnabled: Boolean read GetRunningStatusEnabled write SetRunningStatusEnabled; end; function MIDIOut(DeviceID: Cardinal = 0): IJclMIDIOut; procedure GetMidiOutputs(const List: TStrings); function MIDISingleNoteTuningData(Key: TMIDINote; Frequency: Single): TSingleNoteTuningData; function MIDINoteToStr(Note: TMIDINote): string; implementation uses SysUtils, {$IFDEF MSWINDOWS} JclWinMIDI, {$ENDIF MSWINDOWS} {$IFDEF UNIX} //JclUnixMIDI, {$ENDIF UNIX} JclResources; {$IFDEF UNIX} procedure ErrorNotImplemented; begin raise EJclInternalError.CreateRes(@RsMidiNotImplemented); end; {$ENDIF UNIX} function MIDIOut(DeviceID: Cardinal = 0): IJclMIDIOut; begin Result := nil; {$IFDEF MSWINDOWS} Result := JclWinMIDI.MIDIOut(DeviceID); {$ENDIF MSWINDOWS} {$IFDEF UNIX} { TODO -oRobert Rossmair : Unix MIDI Out } //Result := JclUnixMIDI.MidiOut(DeviceID); ErrorNotImplemented; {$ENDIF UNIX} end; procedure GetMidiOutputs(const List: TStrings); begin {$IFDEF MSWINDOWS} JclWinMIDI.GetMidiOutputs(List); {$ENDIF MSWINDOWS} {$IFDEF UNIX} { TODO -oRobert Rossmair : Unix GetMIDIOutputs } //JclUnixMIDI.GetMidiOutputs(List); ErrorNotImplemented; {$ENDIF UNIX} end; function MIDISingleNoteTuningData(Key: TMIDINote; Frequency: Single): TSingleNoteTuningData; var F: Cardinal; begin Result.Key := Key; F := Trunc(Frequency * (1 shl BitsPerMIDIDataWord)); Result.Frequency[0] := (F shr BitsPerMIDIDataWord) and MIDIDataMask; Result.Frequency[1] := (F shr BitsPerMIDIDataByte) and MIDIDataMask; Result.Frequency[2] := F and MIDIDataMask; end; procedure CheckMIDIChannelNum(Channel: TMIDIChannel); begin if (Channel < Low(TMIDIChannel)) or (Channel > High(TMIDIChannel)) then raise EJclMIDIError.CreateResFmt(@RsMidiInvalidChannelNum, [Channel]); end; function MIDINoteToStr(Note: TMIDINote): string; const HalftonesPerOctave = 12; begin case Note mod HalftonesPerOctave of 0: Result := RsOctaveC; 1: Result := RsOctaveCSharp; 2: Result := RsOctaveD; 3: Result := RsOctaveDSharp; 4: Result := RsOctaveE; 5: Result := RsOctaveF; 6: Result := RsOctaveFSharp; 7: Result := RsOctaveG; 8: Result := RsOctaveGSharp; 9: Result := RsOctaveA; 10: Result := RsOctaveASharp; 11: Result := RsOctaveB; end; Result := Format('%s%d', [Result, Note div HalftonesPerOctave - 2]); end; // TJclMIDIOut destructor TJclMIDIOut.Destroy; begin SwitchActiveNotesOff; inherited Destroy; end; function TJclMIDIOut.GetActiveNotes(Channel: TMIDIChannel): TMIDINotes; begin CheckMIDIChannelNum(Channel); Result := FActiveNotes[Channel]; end; procedure TJclMIDIOut.SendChannelMessage(Msg: TMIDIStatusByte; Channel: TMIDIChannel; Data1, Data2: TMIDIDataByte); begin SendMessage([Msg or (Channel - Low(Channel)), Data1, Data2]); end; function TJclMIDIOut.GetRunningStatusEnabled: Boolean; begin Result := FRunningStatusEnabled; end; function TJclMIDIOut.NoteIsOn(Channel: TMIDIChannel; Key: TMIDINote): Boolean; begin Result := Key in FActiveNotes[Channel]; end; procedure TJclMIDIOut.SendNoteOff(Channel: TMIDIChannel; Key: TMIDINote; Velocity: TMIDIDataByte); begin SendChannelMessage(MIDIMsgNoteOff, Channel, Key, Velocity); Exclude(FActiveNotes[Channel], Key); end; procedure TJclMIDIOut.SendNoteOn(Channel: TMIDIChannel; Key: TMIDINote; Velocity: TMIDIDataByte); begin SendChannelMessage(MIDIMsgNoteOn, Channel, Key, Velocity); Include(FActiveNotes[Channel], Key); end; procedure TJclMIDIOut.SendPolyphonicKeyPressure(Channel: TMIDIChannel; Key: TMIDINote; Value: TMIDIDataByte); begin SendChannelMessage(MIDIMsgPolyKeyPressure, Channel, Key, Value); end; procedure TJclMIDIOut.SendControlChange(Channel: TMIDIChannel; ControllerNum, Value: TMIDIDataByte); begin SendChannelMessage(MIDIMsgControlChange, Channel, ControllerNum, Value); end; procedure TJclMIDIOut.SendControlChangeHR(Channel: TMIDIChannel; ControllerNum: TMIDIDataByte; Value: TMIDIDataWord); begin SendControlChange(Channel, ControllerNum, Value shr BitsPerMIDIDataByte and MIDIDataMask); if ControllerNum <= $13 then SendControlChange(Channel, ControllerNum or $20, Value and MIDIDataMask); end; procedure TJclMIDIOut.SendSwitchChange(Channel: TMIDIChannel; ControllerNum: TMIDIDataByte; Value: Boolean); const DataByte: array [Boolean] of Byte = ($00, $7F); begin SendChannelMessage(MIDIMsgControlChange, Channel, ControllerNum, DataByte[Value]); end; procedure TJclMIDIOut.SendProgramChange(Channel: TMIDIChannel; ProgramNum: TMIDIDataByte); begin SendChannelMessage(MIDIMsgProgramChange, Channel, ProgramNum, 0); end; procedure TJclMIDIOut.SendChannelPressure(Channel: TMIDIChannel; Value: TMIDIDataByte); begin SendChannelMessage(MIDIMsgChannelKeyPressure, Channel, Value, 0); end; procedure TJclMIDIOut.SendPitchWheelChange(Channel: TMIDIChannel; Value: TMIDIDataWord); begin SendChannelMessage(MIDIMsgPitchWheelChange, Channel, Value and MidiDataMask, Value shr BitsPerMIDIDataByte); end; procedure TJclMIDIOut.SendPitchWheelPos(Channel: TMIDIChannel; Value: Single); var Temp: TMIDIDataWord; begin if Value < 0 then Temp := Round(Value * (1 shl 13)) else Temp := Round(Value * (1 shl 13 - 1)); SendPitchWheelChange(Channel, Temp); end; procedure TJclMIDIOut.SwitchAllSoundOff(Channel: TMIDIChannel); begin SendControlChange(Channel, MIDICCAllSoundOff, 0); end; procedure TJclMIDIOut.SwitchLocalControl(Channel: TMIDIChannel; Value: Boolean); begin SendSwitchChange(Channel, MIDICCLocalControl, Value); end; procedure TJclMIDIOut.ResetAllControllers(Channel: TMIDIChannel); begin SendControlChange(Channel, MIDICCResetAllControllers, 0); end; procedure TJclMIDIOut.SwitchAllNotesOff(Channel: TMIDIChannel); begin CheckMIDIChannelNum(Channel); SendControlChange(Channel, MIDICCAllNotesOff, 0); FActiveNotes[Channel] := []; end; procedure TJclMIDIOut.SetRunningStatusEnabled(const Value: Boolean); begin FMIDIStatus := MIDIInvalidStatus; FRunningStatusEnabled := Value; end; procedure TJclMIDIOut.SendSingleNoteTuningChange(const TargetDeviceID, TuningProgramNum: TMidiDataByte; const TuningData: array of TSingleNoteTuningData); var BufSize, Count: Integer; Buf: array of Byte; begin Count := High(TuningData) - Low(TuningData) + 1; BufSize := 8 + Count * SizeOf(TSingleNoteTuningData); SetLength(Buf, BufSize); Buf[0] := MIDIMsgSysEx; // Universal Real Time SysEx header, first byte Buf[1] := $7F; // second byte Buf[2] := TargetDeviceID; // ID of target device (?) Buf[3] := 8; // sub-ID#1 (MIDI Tuning) Buf[4] := 2; // sub-ID#2 (note change) Buf[5] := TuningProgramNum; // tuning program number (0 – 127) Buf[6] := Count; Move(TuningData, Buf[7], Count * SizeOf(TSingleNoteTuningData)); Buf[BufSize - 1] := MIDIMsgEOX; SendMessage(Buf); end; procedure TJclMIDIOut.SwitchActiveNotesOff(Channel: TMIDIChannel); var Note: TMIDINote; begin CheckMIDIChannelNum(Channel); if FActiveNotes[Channel] <> [] then for Note := Low(Note) to High(Note) do if Note in FActiveNotes[Channel] then SendNoteOff(Channel, Note, $7F); end; procedure TJclMIDIOut.SwitchActiveNotesOff; var Channel: TMIDIChannel; begin for Channel := Low(Channel) to High(Channel) do SwitchActiveNotesOff(Channel); end; procedure TJclMIDIOut.SelectProgram(Channel: TMIDIChannel; BankNum: TMIDIDataWord; ProgramNum: TMIDIDataByte); begin SendControlChangeHR(Channel, MIDICCBankSelect, BankNum); SendProgramChange(Channel, ProgramNum); end; procedure TJclMIDIOut.SendMessage(const Data: array of Byte); begin if IsRunningStatus(Data[0]) then {$IFDEF FPC} DoSendMessage(PJclByteArray(@Data[1])^) {$ELSE} DoSendMessage(Slice(Data, 1)) {$ENDIF FPC} else DoSendMessage(Data); end; function TJclMIDIOut.GetMIDIStatus: TMIDIStatusByte; begin Result := FMIDIStatus; end; function TJclMIDIOut.IsRunningStatus(StatusByte: TMIDIStatusByte): Boolean; begin Result := (StatusByte = FMIDIStatus) and ((StatusByte and $F0) <> $F0) and // is channel message RunningStatusEnabled; end; procedure TJclMIDIOut.SendBalanceChange(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCBalance, Value); end; procedure TJclMIDIOut.SendBalanceChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCBalance, Value); end; procedure TJclMIDIOut.SendBreathControlChange(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCBreathControl, Value); end; procedure TJclMIDIOut.SendBreathControlChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCBreathControl, Value); end; procedure TJclMIDIOut.SendDataEntry(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCDataEntry, Value); end; procedure TJclMIDIOut.SendDataEntryHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCDataEntry, Value); end; procedure TJclMIDIOut.SendExpressionChange(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCExpression, Value); end; procedure TJclMIDIOut.SendExpressionChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCExpression, Value); end; procedure TJclMIDIOut.SendFootControllerChange(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCFootController, Value); end; procedure TJclMIDIOut.SendFootControllerChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCFootController, Value); end; procedure TJclMIDIOut.SwitchHold2(Channel: TMIDIChannel; Value: Boolean); begin SendSwitchChange(Channel, MIDICCHold2, Value); end; procedure TJclMIDIOut.SwitchLegato(Channel: TMIDIChannel; Value: Boolean); begin SendSwitchChange(Channel, MIDICCLegato, Value); end; procedure TJclMIDIOut.SendChannelVolumeChange(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCChannelVolume, Value); end; procedure TJclMIDIOut.SendChannelVolumeChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCChannelVolume, Value); end; procedure TJclMIDIOut.SendModulationWheelChange(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCModulationWheel, Value); end; procedure TJclMIDIOut.SendModulationWheelChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCModulationWheel, Value); end; procedure TJclMIDIOut.SendPanChange(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCPan, Value); end; procedure TJclMIDIOut.SendPanChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCPan, Value); end; procedure TJclMIDIOut.SwitchPortamento(Channel: TMIDIChannel; Value: Boolean); begin SendSwitchChange(Channel, MIDICCPortamento, Value); end; procedure TJclMIDIOut.SendPortamentoTimeChange(Channel: TMIDIChannel; Value: TMidiDataByte); begin SendControlChange(Channel, MIDICCPortamentoTime, Value); end; procedure TJclMIDIOut.SendPortamentoTimeChangeHR(Channel: TMIDIChannel; Value: TMidiDataWord); begin SendControlChangeHR(Channel, MIDICCPortamentoTime, Value); end; procedure TJclMIDIOut.SwitchSoftPedal(Channel: TMIDIChannel; Value: Boolean); begin SendSwitchChange(Channel, MIDICCSoftPedal, Value); end; procedure TJclMIDIOut.SwitchSustain(Channel: TMIDIChannel; Value: Boolean); begin SendSwitchChange(Channel, MIDICCSustain, Value); end; procedure TJclMIDIOut.SwitchSostenuto(Channel: TMIDIChannel; Value: Boolean); begin SendSwitchChange(Channel, MIDICCSustenuto, Value); end; procedure TJclMIDIOut.SwitchOmniModeOff(Channel: TMIDIChannel); begin SendControlChange(Channel, MIDICCOmniModeOff, 0); FActiveNotes[Channel] := []; // implicit All Notes Off end; procedure TJclMIDIOut.SwitchOmniModeOn(Channel: TMIDIChannel); begin SendControlChange(Channel, MIDICCOmniModeOn, 0); FActiveNotes[Channel] := []; // implicit All Notes Off end; procedure TJclMIDIOut.SwitchMonoModeOn(Channel: TMIDIChannel; ChannelCount: Integer); begin SendControlChange(Channel, MIDICCMonoModeOn, ChannelCount); FActiveNotes[Channel] := []; // implicit All Notes Off end; procedure TJclMIDIOut.SwitchPolyModeOn(Channel: TMIDIChannel); begin SendControlChange(Channel, MIDICCPolyModeOn, 0); FActiveNotes[Channel] := []; // implicit All Notes Off end; // History: // $Log: JclMIDI.pas,v $ // Revision 1.13 2005/08/07 13:09:54 outchy // Changed PByteArray to PJclByteArray to avoid RangeCheck exceptions. // // Revision 1.12 2005/03/08 08:33:17 marquardt // overhaul of exceptions and resourcestrings, minor style cleaning // // Revision 1.11 2005/02/24 16:34:40 marquardt // remove divider lines, add section lines (unfinished) // // Revision 1.10 2004/10/12 18:29:52 rrossmair // cleanup // // Revision 1.9 2004/08/03 07:22:37 marquardt // resourcestring cleanup // // Revision 1.8 2004/07/28 18:00:50 marquardt // various style cleanings, some minor fixes // // Revision 1.7 2004/06/16 07:30:27 marquardt // added tilde to all IFNDEF ENDIFs, inherited qualified // // Revision 1.6 2004/06/07 04:27:07 rrossmair // "Not implemented" error for Unix added as placeholder. // // Revision 1.5 2004/05/05 00:09:59 mthoma // Updated headers: Added donors as contributors, adjusted the initial authors, added cvs names when they were not obvious. Changed $data to $date where necessary, // end.