1 /*----------------------------------------------------------------------------
2 ChucK Concurrent, On-the-fly Audio Programming Language
3 Compiler and Virtual Machine
5 Copyright (c) 2004 Ge Wang and Perry R. Cook. All rights reserved.
6 http://chuck.cs.princeton.edu/
7 http://soundlab.cs.princeton.edu/
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 -----------------------------------------------------------------------------*/
25 //-----------------------------------------------------------------------------
26 // file: midiio_rtmidi.cpp
29 // author: Ge Wang (gewang@cs.princeton.edu)
30 // Perry R. Cook (prc@cs.princeton.edu)
31 // Ananya Misra (amisra@cs.princeton.edu)
33 //-----------------------------------------------------------------------------
34 #include "midiio_rtmidi.h"
35 #include "chuck_errmsg.h"
43 //-----------------------------------------------------------------------------
45 //-----------------------------------------------------------------------------
46 #define BUFFER_SIZE 8192
48 std::vector
<RtMidiIn
*> MidiInManager::the_mins
;
49 std::vector
<CBufferAdvance
*> MidiInManager::the_bufs
;
50 std::vector
<RtMidiOut
*> MidiOutManager::the_mouts
;
54 //-----------------------------------------------------------------------------
57 //-----------------------------------------------------------------------------
63 m_suppress_output
= FALSE
;
69 //-----------------------------------------------------------------------------
72 //-----------------------------------------------------------------------------
75 if( mout
) this->close();
82 //-----------------------------------------------------------------------------
84 // desc: send 1 byte midi message
85 //-----------------------------------------------------------------------------
86 t_CKUINT
MidiOut::send( t_CKBYTE status
)
88 return this->send( status
, 0, 0 );
94 //-----------------------------------------------------------------------------
96 // desc: send 2 byte midi message
97 //-----------------------------------------------------------------------------
98 t_CKUINT
MidiOut::send( t_CKBYTE status
, t_CKBYTE data1
)
100 return this->send( status
, data1
, 0 );
106 //-----------------------------------------------------------------------------
108 // desc: send 3 byte midi message
109 //-----------------------------------------------------------------------------
110 t_CKUINT
MidiOut::send( t_CKBYTE status
, t_CKBYTE data1
, t_CKBYTE data2
)
112 if( !m_valid
) return 0;
117 m_msg
.push_back( status
);
118 m_msg
.push_back( data1
);
119 m_msg
.push_back( data2
);
121 mout
->sendMessage( &m_msg
);
129 //-----------------------------------------------------------------------------
132 //-----------------------------------------------------------------------------
133 t_CKUINT
MidiOut::send( const MidiMsg
* msg
)
135 if( !m_valid
) return 0;
140 m_msg
.push_back( msg
->data
[0] );
141 m_msg
.push_back( msg
->data
[1] );
142 m_msg
.push_back( msg
->data
[2] );
144 mout
->sendMessage( &m_msg
);
152 //-----------------------------------------------------------------------------
154 // desc: open midi output
155 //-----------------------------------------------------------------------------
156 t_CKBOOL
MidiOut::open( t_CKUINT device_num
)
158 // close if already opened
162 return m_valid
= MidiOutManager::open( this, (t_CKINT
)device_num
);
168 //-----------------------------------------------------------------------------
170 // desc: close midi output
171 //-----------------------------------------------------------------------------
172 t_CKBOOL
MidiOut::close( )
178 // MidiOutManager::close( this );
188 //-----------------------------------------------------------------------------
190 // desc: note on message
191 //-----------------------------------------------------------------------------
192 t_CKUINT
MidiOut::noteon( t_CKUINT channel
, t_CKUINT note
, t_CKUINT velocity
)
193 { return this->send( (t_CKBYTE
)(MIDI_NOTEON
+ channel
), note
, velocity
); }
198 //-----------------------------------------------------------------------------
200 // desc: note off message
201 //-----------------------------------------------------------------------------
202 t_CKUINT
MidiOut::noteoff( t_CKUINT channel
, t_CKUINT note
, t_CKUINT velocity
)
203 { return this->send( (t_CKBYTE
)(MIDI_NOTEOFF
+ channel
), note
, velocity
); }
208 //-----------------------------------------------------------------------------
210 // desc: polypress message
211 //-----------------------------------------------------------------------------
212 t_CKUINT
MidiOut::polypress( t_CKUINT channel
, t_CKUINT note
, t_CKUINT pressure
)
213 { return this->send( (t_CKBYTE
)(MIDI_POLYPRESS
+ channel
), note
, pressure
); }
218 //-----------------------------------------------------------------------------
219 // name: ctrlchange()
220 // desc: ctrl change message
221 //-----------------------------------------------------------------------------
222 t_CKUINT
MidiOut::ctrlchange( t_CKUINT channel
, t_CKUINT ctrl_num
, t_CKUINT ctrl_val
)
223 { return this->send( (t_CKBYTE
)(MIDI_CTRLCHANGE
+ channel
), ctrl_num
, ctrl_val
); }
228 //-----------------------------------------------------------------------------
229 // name: progchange()
230 // desc: prog change message
231 //-----------------------------------------------------------------------------
232 t_CKUINT
MidiOut::progchange( t_CKUINT channel
, t_CKUINT patch
)
233 { return this->send( (t_CKBYTE
)(MIDI_PROGCHANGE
+ channel
), patch
, 0 ); }
238 //-----------------------------------------------------------------------------
241 //-----------------------------------------------------------------------------
242 t_CKUINT
MidiOut::chanpress( t_CKUINT channel
, t_CKUINT pressure
)
243 { return this->send( (t_CKBYTE
)(MIDI_CHANPRESS
+ channel
), pressure
, 0 ); }
248 //-----------------------------------------------------------------------------
251 //-----------------------------------------------------------------------------
252 t_CKUINT
MidiOut::pitchbend( t_CKUINT channel
, t_CKUINT bend_val
)
256 // return this->send( (t_CKBYTE)(MIDI_PITCHBEND + channel),
257 // (t_CKBYTE)(HIBYTE( bend_val << 1 )),
258 // (t_CKBYTE)(LOBYTE( bend_val & 0x7f )) );
264 //-----------------------------------------------------------------------------
265 // name: allnotesoff()
267 //-----------------------------------------------------------------------------
268 t_CKUINT
MidiOut::allnotesoff( t_CKUINT channel
)
270 return this->send( (t_CKBYTE
)(MIDI_CTRLCHANGE
+ channel
),
271 (t_CKBYTE
)(MIDI_ALLNOTESOFF
), 0 );
277 //-----------------------------------------------------------------------------
280 //-----------------------------------------------------------------------------
288 m_suppress_output
= FALSE
;
295 //-----------------------------------------------------------------------------
298 //-----------------------------------------------------------------------------
302 // SAFE_DELETE( min );
308 //-----------------------------------------------------------------------------
311 //-----------------------------------------------------------------------------
312 t_CKBOOL
MidiIn::open( t_CKUINT device_num
)
314 // close if already opened
319 return m_valid
= MidiInManager::open( this, (t_CKINT
)device_num
);
325 MidiInManager::MidiInManager()
327 the_mins
.resize( 1024 );
328 the_bufs
.resize( 1024 );
332 MidiInManager::~MidiInManager()
338 t_CKBOOL
MidiInManager::open( MidiIn
* min
, t_CKINT device_num
)
340 // see if port not already open
341 if( device_num
>= (t_CKINT
)the_mins
.capacity() || !the_mins
[device_num
] )
343 // allocate the buffer
344 CBufferAdvance
* cbuf
= new CBufferAdvance
;
345 if( !cbuf
->initialize( BUFFER_SIZE
, sizeof(MidiMsg
) ) )
347 if( !min
->m_suppress_output
)
348 EM_error2( 0, "MidiIn: couldn't allocate CBuffer for port %i...", device_num
);
354 RtMidiIn
* rtmin
= new RtMidiIn
;
356 rtmin
->openPort( device_num
);
357 rtmin
->setCallback( cb_midi_input
, cbuf
);
358 } catch( RtError
& err
) {
359 if( !min
->m_suppress_output
)
362 EM_error2( 0, "MidiIn: couldn't open MIDI port %i...", device_num
);
364 // const char * e = err.getMessage().c_str();
365 // EM_error2( 0, "...(%s)", err.getMessage().c_str() );
372 if( device_num
>= (t_CKINT
)the_mins
.capacity() )
374 t_CKINT size
= the_mins
.capacity() * 2;
375 if( device_num
>= size
) size
= device_num
+ 1;
376 the_mins
.resize( size
);
377 the_bufs
.resize( size
);
380 // put cbuf and rtmin in vector for future generations
381 the_mins
[device_num
] = rtmin
;
382 the_bufs
[device_num
] = cbuf
;
386 min
->min
= the_mins
[device_num
];
388 min
->m_buffer
= the_bufs
[device_num
];
389 // get an index into your (you are min here) own buffer,
390 // and a free ticket to your own workshop
391 min
->m_read_index
= min
->m_buffer
->join( (Chuck_Event
*)min
->SELF
);
392 min
->m_device_num
= (t_CKUINT
)device_num
;
401 //-----------------------------------------------------------------------------
404 //-----------------------------------------------------------------------------
405 t_CKBOOL
MidiIn::close()
411 // MidiInManager::close( this );
421 //-----------------------------------------------------------------------------
424 //-----------------------------------------------------------------------------
425 t_CKBOOL
MidiIn::empty()
427 if( !m_valid
) return TRUE
;
428 return m_buffer
->empty( m_read_index
);
434 //-----------------------------------------------------------------------------
437 //-----------------------------------------------------------------------------
438 t_CKUINT
MidiIn::recv( MidiMsg
* msg
)
440 if( !m_valid
) return FALSE
;
441 return m_buffer
->get( msg
, 1, m_read_index
);
447 //-----------------------------------------------------------------------------
448 // name: cb_midi_output
450 //-----------------------------------------------------------------------------
451 void MidiInManager::cb_midi_input( double deltatime
, std::vector
<unsigned char> * msg
,
454 unsigned int nBytes
= msg
->size();
455 CBufferAdvance
* cbuf
= (CBufferAdvance
*)userData
;
457 if( nBytes
>= 1 ) m
.data
[0] = msg
->at(0);
458 if( nBytes
>= 2 ) m
.data
[1] = msg
->at(1);
459 if( nBytes
>= 3 ) m
.data
[2] = msg
->at(2);
461 // put in the buffer, make sure not active sensing
462 if( m
.data
[2] != 0xfe )
471 //-----------------------------------------------------------------------------
472 // name: probeMidiIn()
474 //-----------------------------------------------------------------------------
477 RtMidiIn
* min
= NULL
;
481 } catch( RtError
& err
) {
482 EM_error2b( 0, "%s", err
.getMessageString() );
487 t_CKUINT num
= min
->getPortCount();
488 EM_error2b( 0, "------( chuck -- %i MIDI inputs )------", num
);
490 for( t_CKUINT i
= 0; i
< num
; i
++ )
492 try { s
= min
->getPortName( i
); }
493 catch( RtError
& err
)
494 { err
.printMessage(); return; }
495 EM_error2b( 0, " [%i] : \"%s\"", i
, s
.c_str() );
502 //-----------------------------------------------------------------------------
503 // name: probeMidiOut()
505 //-----------------------------------------------------------------------------
508 RtMidiOut
* mout
= NULL
;
511 mout
= new RtMidiOut
;
512 } catch( RtError
& err
) {
513 EM_error2b( 0, "%s", err
.getMessageString() );
518 t_CKUINT num
= mout
->getPortCount();
519 EM_error2b( 0, "------( chuck -- %i MIDI outputs )-----", num
);
521 for( t_CKUINT i
= 0; i
< num
; i
++ )
523 try { s
= mout
->getPortName( i
); }
524 catch( RtError
& err
)
525 { err
.printMessage(); return; }
526 EM_error2b( 0, " [%i] : \"%s\"", i
, s
.c_str() );
531 MidiOutManager::MidiOutManager()
533 the_mouts
.resize( 1024 );
537 MidiOutManager::~MidiOutManager()
543 t_CKBOOL
MidiOutManager::open( MidiOut
* mout
, t_CKINT device_num
)
545 // see if port not already open
546 if( device_num
>= (t_CKINT
)the_mouts
.capacity() || !the_mouts
[device_num
] )
549 RtMidiOut
* rtmout
= new RtMidiOut
;
551 rtmout
->openPort( device_num
);
552 } catch( RtError
& err
) {
553 if( !mout
->m_suppress_output
)
556 EM_error2( 0, "MidiOut: couldn't open MIDI port %i...", device_num
);
558 // const char * e = err.getMessage().c_str();
559 // EM_error2( 0, "...(%s)", err.getMessage().c_str() );
565 if( device_num
>= (t_CKINT
)the_mouts
.capacity() )
567 t_CKINT size
= the_mouts
.capacity() * 2;
568 if( device_num
>= size
) size
= device_num
+ 1;
569 the_mouts
.resize( size
);
572 // put rtmout in vector for future generations
573 the_mouts
[device_num
] = rtmout
;
576 // found (always) (except when it doesn't get here)
577 mout
->mout
= the_mouts
[device_num
];
578 mout
->m_device_num
= (t_CKUINT
)device_num
;
585 //-----------------------------------------------------------------------------
586 // name: class MidiRW
587 // desc: reads and writes midi messages from file
588 //-----------------------------------------------------------------------------
590 static std::map
<MidiRW
*, MidiRW
*> g_rw
;
592 t_CKBOOL
out_detach( );
593 t_CKBOOL
midirw_detach( )
595 std::map
<MidiRW
*, MidiRW
*>::iterator iter
;
596 std::vector
<MidiRW
*> list
;
599 EM_log( CK_LOG_INFO
, "detaching MIDI devices..." );
601 for( iter
= g_rw
.begin(); iter
!= g_rw
.end(); iter
++ )
602 list
.push_back( (*iter
).second
);
603 for( t_CKUINT i
= 0; i
< list
.size(); i
++ )
606 // TODO: release the MidiRW
609 return out_detach( );
612 MidiRW::MidiRW() { file
= NULL
; }
614 MidiRW::~MidiRW() { this->close(); }
616 t_CKBOOL
MidiRW::open( const char * filename
)
620 file
= fopen( filename
, "rb+" );
623 file
= fopen( filename
, "wb+" );
629 return ( file
!= NULL
);
632 t_CKBOOL
MidiRW::close()
634 if( !file
) return FALSE
;
636 t_CKBOOL value
= fclose( file
) == 0;
639 std::map
<MidiRW
*, MidiRW
*>::iterator iter
;
640 iter
= g_rw
.find( this );
648 t_CKBOOL
MidiRW::read( MidiMsg
* msg
, t_CKTIME
* time
)
653 // is it open? i don't know...
657 // wouldn't it be cool if this worked?
658 m
= fread( msg
, sizeof(MidiMsg
), 1, file
);
659 t
= fread( time
, sizeof(t_CKTIME
), 1, file
);
665 t_CKBOOL
MidiRW::write( MidiMsg
* msg
, t_CKTIME
* time
)
672 m
= fwrite( msg
, sizeof(MidiMsg
), 1, file
);
673 t
= fwrite( time
, sizeof(t_CKTIME
), 1, file
);
682 //-----------------------------------------------------------------------------
683 // name: class MidiMsgOut
684 // desc: writes midi messages from file
685 //-----------------------------------------------------------------------------
687 static std::map
<MidiMsgOut
*, MidiMsgOut
*> g_out
;
689 t_CKBOOL
out_detach( )
691 std::map
<MidiMsgOut
*, MidiMsgOut
*>::iterator iter
;
692 std::vector
<MidiMsgOut
*> list
;
694 for( iter
= g_out
.begin(); iter
!= g_out
.end(); iter
++ )
695 list
.push_back( (*iter
).second
);
696 for( t_CKUINT i
= 0; i
< list
.size(); i
++ )
702 MidiMsgOut::MidiMsgOut() { file
= NULL
; }
704 MidiMsgOut::~MidiMsgOut() { this->close(); }
706 t_CKBOOL
MidiMsgOut::open( const char * filename
)
710 file
= fopen( filename
, "wb" );
715 return ( file
!= NULL
);
718 t_CKBOOL
MidiMsgOut::close()
720 if( !file
) return FALSE
;
722 t_CKBOOL value
= fclose( file
) == 0;
725 std::map
<MidiMsgOut
*, MidiMsgOut
*>::iterator iter
;
726 iter
= g_out
.find( this );
734 t_CKBOOL
MidiMsgOut::write( MidiMsg
* msg
, t_CKTIME
* time
)
741 m
= fwrite( msg
, sizeof(MidiMsg
), 1, file
);
742 t
= fwrite( time
, sizeof(t_CKTIME
), 1, file
);
751 //-----------------------------------------------------------------------------
752 // name: class MidiMsgIn
753 // desc: reads midi messages from file
754 //-----------------------------------------------------------------------------
756 MidiMsgIn::MidiMsgIn() { file
= NULL
; }
758 MidiMsgIn::~MidiMsgIn() { this->close(); }
760 t_CKBOOL
MidiMsgIn::open( const char * filename
)
764 file
= fopen( filename
, "rb" );
766 return ( file
!= NULL
);
769 t_CKBOOL
MidiMsgIn::close()
771 if( !file
) return FALSE
;
773 t_CKBOOL value
= fclose( file
) == 0;
780 t_CKBOOL
MidiMsgIn::read( MidiMsg
* msg
, t_CKTIME
* time
)
785 // is it open? i don't know...
789 // wouldn't it be cool if this worked?
790 m
= fread( msg
, sizeof(MidiMsg
), 1, file
);
791 t
= fread( time
, sizeof(t_CKTIME
), 1, file
);