hydrogen 1.2.3
CoreMidiDriver.cpp
Go to the documentation of this file.
1/*
2 * Hydrogen
3 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
4 * Copyright(c) 2008-2024 The hydrogen development team [hydrogen-devel@lists.sourceforge.net]
5 *
6 * http://www.hydrogen-music.org
7 *
8 * CoreMidi driver for Hydrogen
9 * Copyright(c) 2005-2006 by Jonathan Dempsey
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY, without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see https://www.gnu.org/licenses
23 *
24 * Code cleanup (20060222 Jonathan Dempsey)
25 * More cleaning (20060511 Jonathan Dempsey)
26 * ... and a little bit more cleaning . . . (20060512 Jonathan Dempsey)
27 * Added CFRelease code (20060514 Jonathan Dempsey)
28 */
29
30#include <core/Hydrogen.h>
31#include <core/Basics/Note.h>
36
37#if defined(H2CORE_HAVE_COREMIDI) || _DOXYGEN_
38
39namespace H2Core
40{
41
42
43static void midiProc ( const MIDIPacketList * pktlist,
44 void * readProcRefCon,
45 void * srcConnRefCon )
46{
47 UNUSED( srcConnRefCon );
48
49 MIDIPacket* packet = ( MIDIPacket * )pktlist->packet;
50
51 CoreMidiDriver *instance = ( CoreMidiDriver * )readProcRefCon;
52 for ( uint i = 0; i < pktlist->numPackets; i++ ) {
53 MidiMessage msg;
54 int nEventType = packet->data[0];
55 msg.setType( nEventType );
56
57 if ( nEventType == 240 ) {
58 // SysEx messages also contain arbitrary data which has to
59 // be copied manually.
60 for ( int i = 0; i < packet->length; i++ ) {
61 msg.m_sysexData.push_back( packet->data[ i ] );
62 }
63 }
64 else {
65 msg.m_nData1 = packet->data[1];
66 msg.m_nData2 = packet->data[2];
67 }
68
69 instance->handleMidiMessage( msg );
70 packet = MIDIPacketNext( packet );
71 }
72}
73
74
77 , m_bRunning( false )
78{
79
80 OSStatus err = noErr;
81 err = MIDIClientCreate ( CFSTR( "h2MIDIClient" ), NULL, NULL, &h2MIDIClient );
82 if ( err != noErr ) {
83 ERRORLOG( QString( "Cannot create CoreMIDI client: %1" ).arg( err ));
84 }
85
86 err = MIDIInputPortCreate ( h2MIDIClient, CFSTR( "h2InputPort" ), midiProc, this, &h2InputRef );
87 if ( err != noErr ) {
88 ERRORLOG( QString( "Cannot create CoreMIDI input port: %1" ).arg( err ));
89 }
90
91 err = MIDIOutputPortCreate ( h2MIDIClient, CFSTR( "h2OutputPort" ), &h2OutputRef );
92 if ( err != noErr ) {
93 ERRORLOG( QString( "Cannot create CoreMIDI output port: %1" ).arg( err ));
94 }
95
96 err = MIDISourceCreate ( h2MIDIClient, CFSTR( "Hydrogen" ), &h2VirtualOut );
97 if ( err != noErr ) {
98 ERRORLOG( QString( "Cannot create CoreMIDI virtual output: %1" ).arg( err ));
99 }
100}
101
102
103
105{
106 /*if ( isMidiDriverRunning ) {
107 close();
108 } */
109 close();
110 INFOLOG( "DESTROY" );
111}
112
113
114
116{
117 INFOLOG( "open" );
118
119 OSStatus err = noErr;
120
121 QString sMidiPortName = Preferences::get_instance()->m_sMidiPortName;
122
123 cmSources = MIDIGetNumberOfSources();
124 unsigned i;
125 for ( i = 0; i < cmSources; i++ ) {
126 CFStringRef H2MidiNames;
127 cmH2Src = MIDIGetSource( i );
128
129 if ( cmH2Src ) {
130 err = MIDIObjectGetStringProperty( cmH2Src, kMIDIPropertyName, &H2MidiNames );
131 char cmName[64];
132 err = CFStringGetCString( H2MidiNames, cmName, 64, kCFStringEncodingASCII );
133 QString h2MidiPortName = cmName;
134 if ( h2MidiPortName == sMidiPortName &&
135 sMidiPortName != Preferences::getNullMidiPort() ) {
136 MIDIPortConnectSource ( h2InputRef, cmH2Src, NULL );
137 m_bRunning = true;
138 }
139 }
140 CFRelease ( H2MidiNames );
141 }
142
143 int n = MIDIGetNumberOfDestinations();
144 if (n > 0) {
145 cmH2Dst = MIDIGetDestination(0);
146 }
147
148 if (cmH2Dst != 0) {
149 CFStringRef H2MidiNames;
150
151 MIDIObjectGetStringProperty(cmH2Dst, kMIDIPropertyName, &H2MidiNames);
152 //CFStringGetCString(pname, name, sizeof(name), 0);
153 //MIDIPortConnectSource ( h2OutputRef, cmH2Dst, NULL );
154 MIDIPortConnectSource ( h2OutputRef, cmH2Dst, NULL );
155 if( H2MidiNames != NULL){
156 CFRelease( H2MidiNames );
157 }
158 }
159}
160
161
162
164{
165 OSStatus err = noErr;
166 err = MIDIPortDisconnectSource( h2InputRef, cmH2Src );
167 err = MIDIPortDispose( h2InputRef );
168 err = MIDIEndpointDispose( h2VirtualOut );
169 //err = MIDIPortDisconnectSource( h2OutputRef, cmH2Dst );
170 //err = MIDIPortDispose( h2OutputRef );
171 err = MIDIClientDispose( h2MIDIClient );
172}
173
175{
176 INFOLOG( "retrieving output list" );
177 OSStatus err = noErr;
178
179 std::vector<QString> cmPortList;
180
181 cmSources = MIDIGetNumberOfDestinations();
182
183 INFOLOG ( "Getting number of MIDI sources . . .\n" );
184
185 unsigned i;
186 for ( i = 0; i < cmSources; i++ ) {
187 CFStringRef H2MidiNames;
188 cmH2Src = MIDIGetDestination( i );
189 if ( cmH2Src == 0 ) {
190 ERRORLOG( "Could not open output device" );
191 }
192 if ( cmH2Src ) {
193 err = MIDIObjectGetStringProperty( cmH2Src, kMIDIPropertyName, &H2MidiNames );
194 INFOLOG ( "Getting MIDI object string property . . .\n" );
195 char cmName[ 64 ];
196 CFStringGetCString( H2MidiNames, cmName, 64, kCFStringEncodingASCII );
197 INFOLOG ( "Getting MIDI object name . . .\n" );
198 QString h2MidiPortName = cmName;
199 cmPortList.push_back( h2MidiPortName );
200 }
201 CFRelease( H2MidiNames );
202 }
203
204 return cmPortList;
205}
206
208{
209 INFOLOG( "retrieving output list" );
210 OSStatus err = noErr;
211
212 std::vector<QString> cmPortList;
213 cmSources = MIDIGetNumberOfSources();
214
215 INFOLOG ( "Getting number of MIDI sources . . .\n" );
216
217 unsigned i;
218 for ( i = 0; i < cmSources; i++ ) {
219 CFStringRef H2MidiNames;
220 cmH2Src = MIDIGetSource( i );
221 if ( cmH2Src == 0 ) {
222 ERRORLOG( "Could not open input device" );
223 }
224 if ( cmH2Src ) {
225 err = MIDIObjectGetStringProperty( cmH2Src, kMIDIPropertyName, &H2MidiNames );
226 INFOLOG ( "Getting MIDI object string property . . .\n" );
227 char cmName[ 64 ];
228 CFStringGetCString( H2MidiNames, cmName, 64, kCFStringEncodingASCII );
229 INFOLOG ( "Getting MIDI object name . . .\n" );
230 QString h2MidiPortName = cmName;
231 cmPortList.push_back( h2MidiPortName );
232 }
233 CFRelease( H2MidiNames );
234 }
235
236 return cmPortList;
237}
238
240{
241 if (cmH2Dst == 0 ) {
242 ERRORLOG( "cmH2Dst = 0 " );
243 return;
244 }
245
246 int channel = pNote->get_instrument()->get_midi_out_channel();
247 if (channel < 0) {
248 return;
249 }
250
251 int key = pNote->get_midi_key();
252 int velocity = pNote->get_midi_velocity();
253
254 MIDIPacketList packetList;
255 packetList.numPackets = 1;
256
257 packetList.packet->timeStamp = 0;
258 packetList.packet->length = 3;
259 packetList.packet->data[0] = 0x80 | channel;
260 packetList.packet->data[1] = key;
261 packetList.packet->data[2] = velocity;
262
263 sendMidiPacket ( &packetList );
264
265 packetList.packet->data[0] = 0x90 | channel;
266 packetList.packet->data[1] = key;
267 packetList.packet->data[2] = velocity;
268
269 sendMidiPacket ( &packetList );
270}
271
272void CoreMidiDriver::handleQueueNoteOff( int channel, int key, int velocity )
273{
274 if (cmH2Dst == 0 ) {
275 ERRORLOG( "cmH2Dst = 0 " );
276 return;
277 }
278
279// int channel = pNote->get_instrument()->get_midi_out_channel();
280 if (channel < 0) {
281 return;
282 }
283
284// int key = pNote->get_instrument()->get_midi_out_note();
285// int velocity = pNote->get_velocity() * 127;
286
287 MIDIPacketList packetList;
288 packetList.numPackets = 1;
289
290 packetList.packet->timeStamp = 0;
291 packetList.packet->length = 3;
292 packetList.packet->data[0] = 0x80 | channel;
293 packetList.packet->data[1] = key;
294 packetList.packet->data[2] = velocity;
295
296 sendMidiPacket ( &packetList );
297}
298
300{
301 if (cmH2Dst == 0 ) {
302 ERRORLOG( "cmH2Dst = 0 " );
303 return;
304 }
305
306 auto instList = Hydrogen::get_instance()->getSong()->getInstrumentList();
307
308 unsigned int numInstruments = instList->size();
309 for (int index = 0; index < numInstruments; ++index) {
310 auto curInst = instList->get(index);
311
312 int channel = curInst->get_midi_out_channel();
313 if (channel < 0) {
314 continue;
315 }
316 int key = curInst->get_midi_out_note();
317
318 MIDIPacketList packetList;
319 packetList.numPackets = 1;
320
321 packetList.packet->timeStamp = 0;
322 packetList.packet->length = 3;
323 packetList.packet->data[0] = 0x80 | channel;
324 packetList.packet->data[1] = key;
325 packetList.packet->data[2] = 0;
326
327 sendMidiPacket ( &packetList );
328 }
329}
330
331void CoreMidiDriver::handleOutgoingControlChange( int param, int value, int channel )
332{
333 if (cmH2Dst == 0 ) {
334 ERRORLOG( "cmH2Dst = 0 " );
335 return;
336 }
337
338 if (channel < 0) {
339 return;
340 }
341
342 MIDIPacketList packetList;
343 packetList.numPackets = 1;
344
345 packetList.packet->timeStamp = 0;
346 packetList.packet->length = 3;
347 packetList.packet->data[0] = 0xB0 | channel;
348 packetList.packet->data[1] = param;
349 packetList.packet->data[2] = value;
350
351 sendMidiPacket ( &packetList );
352}
353
354
355void CoreMidiDriver::sendMidiPacket (MIDIPacketList *packetList)
356{
357 OSStatus err = noErr;
358
359 err = MIDISend(h2OutputRef, cmH2Dst, packetList);
360 if ( err != noErr ) {
361 ERRORLOG( QString( "Cannot send MIDI packet to output port: %1" ).arg( err ));
362 }
363
364 err = MIDIReceived(h2VirtualOut, packetList);
365 if ( err != noErr ) {
366 ERRORLOG( QString( "Cannot send MIDI packet to virtual output: %1" ).arg( err ));
367 }
368}
369
370} // namespace H2CORE
371#endif
372
#define INFOLOG(x)
Definition Object.h:237
#define ERRORLOG(x)
Definition Object.h:239
MIDIClientRef h2MIDIClient
virtual void open() override
MIDIEndpointRef h2VirtualOut
MIDIEndpointRef cmH2Dst
virtual std::vector< QString > getInputPortList() override
virtual void close() override
virtual void handleQueueNoteOff(int channel, int key, int velocity) override
virtual std::vector< QString > getOutputPortList() override
virtual void handleQueueAllNoteOff() override
virtual void handleOutgoingControlChange(int param, int value, int channel) override
void sendMidiPacket(MIDIPacketList *packetList)
MIDIEndpointRef cmH2Src
virtual void handleQueueNote(Note *pNote) override
std::shared_ptr< Song > getSong() const
Get the current song.
Definition Hydrogen.h:122
static Hydrogen * get_instance()
Returns the current Hydrogen instance __instance.
Definition Hydrogen.h:83
MIDI input base class.
Definition MidiInput.h:39
void handleMidiMessage(const MidiMessage &msg)
Definition MidiInput.cpp:52
void setType(int nStatusByte)
Derives and set m_type (and if applicable m_nChannel) using the statusByte of an incoming MIDI messag...
std::vector< unsigned char > m_sysexData
Definition MidiCommon.h:91
MIDI input base class.
Definition MidiOutput.h:41
A note plays an associated instrument with a velocity left and right pan.
Definition Note.h:102
std::shared_ptr< Instrument > get_instrument()
__instrument accessor
Definition Note.h:500
int get_midi_key() const
return scaled key for midi output, !!! DO NOT CHECK IF INSTRUMENT IS SET !!!
Definition Note.h:682
int get_midi_velocity() const
midi velocity accessor
Definition Note.h:690
static Preferences * get_instance()
Returns a pointer to the current Preferences singleton stored in __instance.
static QString getNullMidiPort()
Choice of m_sMidiPortName and m_sMidiOutputPortName in case no port/device was selected.
#define UNUSED(v)
Definition Globals.h:42
static void midiProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon)