828 lines
32 KiB
ObjectPascal
828 lines
32 KiB
ObjectPascal
{**************************************************************************************************}
|
||
{ }
|
||
{ 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 <controller name>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 <20> 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.
|