*** empty log message ***
[chuck-blob.git] / v2 / midiio_rtmidi.cpp
blobb540936202adf55589e89e85ee5b5389dbfedd8d
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
22 U.S.A.
23 -----------------------------------------------------------------------------*/
25 //-----------------------------------------------------------------------------
26 // file: midiio_rtmidi.cpp
27 // desc: midi io
29 // author: Ge Wang (gewang@cs.princeton.edu)
30 // Perry R. Cook (prc@cs.princeton.edu)
31 // Ananya Misra (amisra@cs.princeton.edu)
32 // date: summer 2005
33 //-----------------------------------------------------------------------------
34 #include "midiio_rtmidi.h"
35 #include "chuck_errmsg.h"
36 #include <vector>
37 #include <map>
38 #include <fstream>
43 //-----------------------------------------------------------------------------
44 // global variables
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 //-----------------------------------------------------------------------------
55 // name: MidiOut()
56 // desc: constructor
57 //-----------------------------------------------------------------------------
58 MidiOut::MidiOut()
60 mout = new RtMidiOut;
61 m_device_num = 0;
62 m_valid = FALSE;
63 m_suppress_output = FALSE;
69 //-----------------------------------------------------------------------------
70 // name: ~MidiOut()
71 // desc: destructor
72 //-----------------------------------------------------------------------------
73 MidiOut::~MidiOut()
75 if( mout ) this->close();
76 SAFE_DELETE( mout );
82 //-----------------------------------------------------------------------------
83 // name: send()
84 // desc: send 1 byte midi message
85 //-----------------------------------------------------------------------------
86 t_CKUINT MidiOut::send( t_CKBYTE status )
88 return this->send( status, 0, 0 );
94 //-----------------------------------------------------------------------------
95 // name: send()
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 //-----------------------------------------------------------------------------
107 // name: send()
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;
114 // clear
115 m_msg.clear();
116 // add
117 m_msg.push_back( status );
118 m_msg.push_back( data1 );
119 m_msg.push_back( data2 );
121 mout->sendMessage( &m_msg );
123 return 3;
129 //-----------------------------------------------------------------------------
130 // name: send()
131 // desc: send
132 //-----------------------------------------------------------------------------
133 t_CKUINT MidiOut::send( const MidiMsg * msg )
135 if( !m_valid ) return 0;
137 // clear
138 m_msg.clear();
139 // add
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 );
146 return 3;
152 //-----------------------------------------------------------------------------
153 // name: open
154 // desc: open midi output
155 //-----------------------------------------------------------------------------
156 t_CKBOOL MidiOut::open( t_CKUINT device_num )
158 // close if already opened
159 if( m_valid )
160 this->close();
162 return m_valid = MidiOutManager::open( this, (t_CKINT)device_num );
168 //-----------------------------------------------------------------------------
169 // name: close( )
170 // desc: close midi output
171 //-----------------------------------------------------------------------------
172 t_CKBOOL MidiOut::close( )
174 if( !m_valid )
175 return FALSE;
177 // close
178 // MidiOutManager::close( this );
180 m_valid = FALSE;
182 return TRUE;
188 //-----------------------------------------------------------------------------
189 // name: noteon()
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 //-----------------------------------------------------------------------------
199 // name: noteoff()
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 //-----------------------------------------------------------------------------
209 // name: polypress()
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 //-----------------------------------------------------------------------------
239 // name: chanpress()
240 // desc: chan press
241 //-----------------------------------------------------------------------------
242 t_CKUINT MidiOut::chanpress( t_CKUINT channel, t_CKUINT pressure )
243 { return this->send( (t_CKBYTE)(MIDI_CHANPRESS + channel), pressure, 0 ); }
248 //-----------------------------------------------------------------------------
249 // name: pitchbend()
250 // desc: pitch bend
251 //-----------------------------------------------------------------------------
252 t_CKUINT MidiOut::pitchbend( t_CKUINT channel, t_CKUINT bend_val )
254 assert( FALSE );
255 return 0;
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()
266 // desc: 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 //-----------------------------------------------------------------------------
278 // name: MidiIn()
279 // desc: constructor
280 //-----------------------------------------------------------------------------
281 MidiIn::MidiIn()
283 min = NULL;
284 m_device_num = 0;
285 m_valid = FALSE;
286 m_read_index = 0;
287 m_buffer = NULL;
288 m_suppress_output = FALSE;
289 SELF = NULL;
295 //-----------------------------------------------------------------------------
296 // name: ~MidiIn()
297 // desc: destructor
298 //-----------------------------------------------------------------------------
299 MidiIn::~MidiIn( )
301 this->close();
302 // SAFE_DELETE( min );
308 //-----------------------------------------------------------------------------
309 // name: open()
310 // desc: open
311 //-----------------------------------------------------------------------------
312 t_CKBOOL MidiIn::open( t_CKUINT device_num )
314 // close if already opened
315 if( m_valid )
316 this->close();
318 // open
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()
334 // yeah right
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 );
349 delete cbuf;
350 return FALSE;
353 // allocate
354 RtMidiIn * rtmin = new RtMidiIn;
355 try {
356 rtmin->openPort( device_num );
357 rtmin->setCallback( cb_midi_input, cbuf );
358 } catch( RtError & err ) {
359 if( !min->m_suppress_output )
361 // print it
362 EM_error2( 0, "MidiIn: couldn't open MIDI port %i...", device_num );
363 err.getMessage();
364 // const char * e = err.getMessage().c_str();
365 // EM_error2( 0, "...(%s)", err.getMessage().c_str() );
367 delete cbuf;
368 return FALSE;
371 // resize?
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;
385 // set min
386 min->min = the_mins[device_num];
387 // found
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;
394 // done
395 return TRUE;
401 //-----------------------------------------------------------------------------
402 // name: close()
403 // desc: close
404 //-----------------------------------------------------------------------------
405 t_CKBOOL MidiIn::close()
407 if( !m_valid )
408 return FALSE;
410 // close
411 // MidiInManager::close( this );
413 m_valid = FALSE;
415 return TRUE;
421 //-----------------------------------------------------------------------------
422 // name: empty()
423 // desc: is empty?
424 //-----------------------------------------------------------------------------
425 t_CKBOOL MidiIn::empty()
427 if( !m_valid ) return TRUE;
428 return m_buffer->empty( m_read_index );
434 //-----------------------------------------------------------------------------
435 // name: get()
436 // desc: get message
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
449 // desc: call back
450 //-----------------------------------------------------------------------------
451 void MidiInManager::cb_midi_input( double deltatime, std::vector<unsigned char> * msg,
452 void * userData )
454 unsigned int nBytes = msg->size();
455 CBufferAdvance * cbuf = (CBufferAdvance *)userData;
456 MidiMsg m;
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 )
464 cbuf->put( &m, 1 );
471 //-----------------------------------------------------------------------------
472 // name: probeMidiIn()
473 // desc: ...
474 //-----------------------------------------------------------------------------
475 void probeMidiIn()
477 RtMidiIn * min = NULL;
479 try {
480 min = new RtMidiIn;;
481 } catch( RtError & err ) {
482 EM_error2b( 0, "%s", err.getMessageString() );
483 return;
486 // get num
487 t_CKUINT num = min->getPortCount();
488 EM_error2b( 0, "------( chuck -- %i MIDI inputs )------", num );
489 std::string s;
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()
504 // desc: ...
505 //-----------------------------------------------------------------------------
506 void probeMidiOut()
508 RtMidiOut * mout = NULL;
510 try {
511 mout = new RtMidiOut;
512 } catch( RtError & err ) {
513 EM_error2b( 0, "%s", err.getMessageString() );
514 return;
517 // get num
518 t_CKUINT num = mout->getPortCount();
519 EM_error2b( 0, "------( chuck -- %i MIDI outputs )-----", num );
520 std::string s;
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()
539 // yeah right
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] )
548 // allocate
549 RtMidiOut * rtmout = new RtMidiOut;
550 try {
551 rtmout->openPort( device_num );
552 } catch( RtError & err ) {
553 if( !mout->m_suppress_output )
555 // print it
556 EM_error2( 0, "MidiOut: couldn't open MIDI port %i...", device_num );
557 err.getMessage();
558 // const char * e = err.getMessage().c_str();
559 // EM_error2( 0, "...(%s)", err.getMessage().c_str() );
561 return FALSE;
564 // resize?
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;
580 // done
581 return TRUE;
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;
598 // log
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++ )
604 list[i]->close();
606 // TODO: release the MidiRW
607 g_rw.clear();
609 return out_detach( );
612 MidiRW::MidiRW() { file = NULL; }
614 MidiRW::~MidiRW() { this->close(); }
616 t_CKBOOL MidiRW::open( const char * filename )
618 this->close();
620 file = fopen( filename, "rb+" );
621 if( file == NULL )
623 file = fopen( filename, "wb+" );
626 // add to hash
627 g_rw[this] = this;
629 return ( file != NULL );
632 t_CKBOOL MidiRW::close()
634 if( !file ) return FALSE;
636 t_CKBOOL value = fclose( file ) == 0;
638 // remove from hash
639 std::map<MidiRW *, MidiRW *>::iterator iter;
640 iter = g_rw.find( this );
641 g_rw.erase( iter );
643 file = NULL;
645 return value;
648 t_CKBOOL MidiRW::read( MidiMsg * msg, t_CKTIME * time )
650 if( !file )
651 return FALSE;
653 // is it open? i don't know...
655 t_CKBOOL m, t;
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 );
661 return m && t;
665 t_CKBOOL MidiRW::write( MidiMsg * msg, t_CKTIME * time )
667 if( !file )
668 return FALSE;
670 t_CKBOOL m, t;
672 m = fwrite( msg, sizeof(MidiMsg), 1, file );
673 t = fwrite( time, sizeof(t_CKTIME), 1, file );
674 fflush( file );
676 return m && t;
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++ )
697 list[i]->close();
699 return TRUE;
702 MidiMsgOut::MidiMsgOut() { file = NULL; }
704 MidiMsgOut::~MidiMsgOut() { this->close(); }
706 t_CKBOOL MidiMsgOut::open( const char * filename )
708 this->close();
710 file = fopen( filename, "wb" );
712 // add to hash
713 g_out[this] = this;
715 return ( file != NULL );
718 t_CKBOOL MidiMsgOut::close()
720 if( !file ) return FALSE;
722 t_CKBOOL value = fclose( file ) == 0;
724 // remove from hash
725 std::map<MidiMsgOut *, MidiMsgOut *>::iterator iter;
726 iter = g_out.find( this );
727 g_out.erase( iter );
729 file = NULL;
731 return value;
734 t_CKBOOL MidiMsgOut::write( MidiMsg * msg, t_CKTIME * time )
736 if( !file )
737 return FALSE;
739 t_CKBOOL m, t;
741 m = fwrite( msg, sizeof(MidiMsg), 1, file );
742 t = fwrite( time, sizeof(t_CKTIME), 1, file );
743 fflush( file );
745 return m && t;
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 )
762 this->close();
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;
775 file = NULL;
777 return value;
780 t_CKBOOL MidiMsgIn::read( MidiMsg * msg, t_CKTIME * time )
782 if( !file )
783 return FALSE;
785 // is it open? i don't know...
787 t_CKBOOL m, t;
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 );
793 return m && t;