📄 rtmidi.cpp
字号:
// Set up the ALSA sequencer client. snd_seq_t *seq; int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK); if ( result < 0 ) { errorString_ = "RtMidiIn::initialize: error creating ALSA sequencer input client object."; error( RtError::DRIVER_ERROR ); } // Set client name. snd_seq_set_client_name(seq, "RtMidi Input Client"); // Save our api-specific connection information. AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; data->seq = seq; data->vport = -1; apiData_ = (void *) data; inputData_.apiData = (void *) data; // Create the input queue data->queue_id = snd_seq_alloc_named_queue(seq, "RtMidi Queue"); // Set arbitrary tempo (mm=100) and resolution (240) snd_seq_queue_tempo_t *qtempo; snd_seq_queue_tempo_alloca(&qtempo); snd_seq_queue_tempo_set_tempo(qtempo, 600000); snd_seq_queue_tempo_set_ppq(qtempo, 240); snd_seq_set_queue_tempo(data->seq, data->queue_id, qtempo); snd_seq_drain_output(data->seq);}// This function is used to count or get the pinfo structure for a given port number.unsigned int portInfo( snd_seq_t *seq, snd_seq_port_info_t *pinfo, unsigned int type, int portNumber ){ snd_seq_client_info_t *cinfo; int client; int count = 0; snd_seq_client_info_alloca( &cinfo ); snd_seq_client_info_set_client( cinfo, -1 ); while ( snd_seq_query_next_client( seq, cinfo ) >= 0 ) { client = snd_seq_client_info_get_client( cinfo ); if ( client == 0 ) continue; // Reset query info snd_seq_port_info_set_client( pinfo, client ); snd_seq_port_info_set_port( pinfo, -1 ); while ( snd_seq_query_next_port( seq, pinfo ) >= 0 ) { if ( !PORT_TYPE( pinfo, type ) ) continue; if ( count == portNumber ) return 1; count++; } } // If a negative portNumber was used, return the port count. if ( portNumber < 0 ) return count; return 0;}void RtMidiIn :: openPort( unsigned int portNumber ){ if ( connected_ ) { errorString_ = "RtMidiIn::openPort: a valid connection already exists!"; error( RtError::WARNING ); return; } unsigned int nSrc = this->getPortCount(); if (nSrc < 1) { errorString_ = "RtMidiIn::openPort: no MIDI input sources found!"; error( RtError::NO_DEVICES_FOUND ); } snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); std::ostringstream ost; AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) == 0 ) { ost << "RtMidiIn::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } snd_seq_addr_t sender, receiver; sender.client = snd_seq_port_info_get_client( pinfo ); sender.port = snd_seq_port_info_get_port( pinfo ); receiver.client = snd_seq_client_id( data->seq ); if ( data->vport < 0 ) { snd_seq_port_info_set_client( pinfo, 0 ); snd_seq_port_info_set_port( pinfo, 0 ); snd_seq_port_info_set_capability( pinfo, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE ); snd_seq_port_info_set_type( pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); snd_seq_port_info_set_name(pinfo, "RtMidi Input"); data->vport = snd_seq_create_port(data->seq, pinfo); if ( data->vport < 0 ) { errorString_ = "RtMidiIn::openPort: ALSA error creating input port."; error( RtError::DRIVER_ERROR ); } } receiver.port = data->vport; // Make subscription snd_seq_port_subscribe_malloc( &data->subscription ); snd_seq_port_subscribe_set_sender(data->subscription, &sender); snd_seq_port_subscribe_set_dest(data->subscription, &receiver); if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { errorString_ = "RtMidiIn::openPort: ALSA error making port connection."; error( RtError::DRIVER_ERROR ); } if ( inputData_.doInput == false ) { // Start the input queue snd_seq_start_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); // Start our MIDI input thread. pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setschedpolicy(&attr, SCHED_OTHER); inputData_.doInput = true; int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); pthread_attr_destroy(&attr); if (err) { snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); inputData_.doInput = false; errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; error( RtError::THREAD_ERROR ); } } connected_ = true;}void RtMidiIn :: openVirtualPort(){ AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( data->vport < 0 ) { snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); snd_seq_port_info_set_capability( pinfo, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE ); snd_seq_port_info_set_type( pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION ); snd_seq_port_info_set_midi_channels(pinfo, 16); snd_seq_port_info_set_timestamping(pinfo, 1); snd_seq_port_info_set_timestamp_real(pinfo, 1); snd_seq_port_info_set_timestamp_queue(pinfo, data->queue_id); snd_seq_port_info_set_name(pinfo, "RtMidi Input"); data->vport = snd_seq_create_port(data->seq, pinfo); if ( data->vport < 0 ) { errorString_ = "RtMidiIn::openVirtualPort: ALSA error creating virtual port."; error( RtError::DRIVER_ERROR ); } } if ( inputData_.doInput == false ) { // Start the input queue snd_seq_start_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); // Start our MIDI input thread. pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_attr_setschedpolicy(&attr, SCHED_OTHER); inputData_.doInput = true; int err = pthread_create(&data->thread, &attr, alsaMidiHandler, &inputData_); pthread_attr_destroy(&attr); if (err) { snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); inputData_.doInput = false; errorString_ = "RtMidiIn::openPort: error starting MIDI input thread!"; error( RtError::THREAD_ERROR ); } }}void RtMidiIn :: closePort( void ){ if ( connected_ ) { AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); // Stop the input queue snd_seq_stop_queue( data->seq, data->queue_id, NULL ); snd_seq_drain_output( data->seq ); connected_ = false; }}RtMidiIn :: ~RtMidiIn(){ // Close a connection if it exists. closePort(); // Shutdown the input thread. AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( inputData_.doInput ) { inputData_.doInput = false; pthread_join( data->thread, NULL ); } // Cleanup. if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); snd_seq_free_queue( data->seq, data->queue_id ); snd_seq_close( data->seq ); delete data;}unsigned int RtMidiIn :: getPortCount(){ snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, -1 );}std::string RtMidiIn :: getPortName( unsigned int portNumber ){ snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, (int) portNumber ) ) { std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); return stringName; } // If we get here, we didn't find a match. errorString_ = "RtMidiIn::getPortName: error looking for port name!"; error( RtError::INVALID_PARAMETER ); return 0;}//*********************************************************************//// API: LINUX ALSA// Class Definitions: RtMidiOut//*********************************************************************//unsigned int RtMidiOut :: getPortCount(){ snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); return portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, -1 );}std::string RtMidiOut :: getPortName( unsigned int portNumber ){ snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) ) { std::string stringName = std::string( snd_seq_port_info_get_name( pinfo ) ); return stringName; } // If we get here, we didn't find a match. errorString_ = "RtMidiOut::getPortName: error looking for port name!"; error( RtError::INVALID_PARAMETER ); return 0;}void RtMidiOut :: initialize( void ){ // Set up the ALSA sequencer client. snd_seq_t *seq; int result = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0); if ( result < 0 ) { errorString_ = "RtMidiOut::initialize: error creating ALSA sequencer client object."; error( RtError::DRIVER_ERROR ); } // Set client name. snd_seq_set_client_name(seq, "RtMidi Output Client"); // Save our api-specific connection information. AlsaMidiData *data = (AlsaMidiData *) new AlsaMidiData; data->seq = seq; data->vport = -1; data->bufferSize = 32; data->coder = 0; data->buffer = 0; result = snd_midi_event_new( data->bufferSize, &data->coder ); if ( result < 0 ) { delete data; errorString_ = "RtMidiOut::initialize: error initializing MIDI event parser!\n\n"; error( RtError::DRIVER_ERROR ); } data->buffer = (unsigned char *) malloc( data->bufferSize ); if ( data->buffer == NULL ) { delete data; errorString_ = "RtMidiOut::initialize: error allocating buffer memory!\n\n"; error( RtError::MEMORY_ERROR ); } snd_midi_event_init( data->coder ); apiData_ = (void *) data;}void RtMidiOut :: openPort( unsigned int portNumber ){ if ( connected_ ) { errorString_ = "RtMidiOut::openPort: a valid connection already exists!"; error( RtError::WARNING ); return; } unsigned int nSrc = this->getPortCount(); if (nSrc < 1) { errorString_ = "RtMidiOut::openPort: no MIDI output sources found!"; error( RtError::NO_DEVICES_FOUND ); } snd_seq_port_info_t *pinfo; snd_seq_port_info_alloca( &pinfo ); std::ostringstream ost; AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( portInfo( data->seq, pinfo, SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, (int) portNumber ) == 0 ) { ost << "RtMidiOut::openPort: the 'portNumber' argument (" << portNumber << ") is invalid."; errorString_ = ost.str(); error( RtError::INVALID_PARAMETER ); } snd_seq_addr_t sender, receiver; receiver.client = snd_seq_port_info_get_client( pinfo ); receiver.port = snd_seq_port_info_get_port( pinfo ); sender.client = snd_seq_client_id( data->seq ); if ( data->vport < 0 ) { data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC ); if ( data->vport < 0 ) { errorString_ = "RtMidiOut::openPort: ALSA error creating output port."; error( RtError::DRIVER_ERROR ); } } sender.port = data->vport; // Make subscription snd_seq_port_subscribe_malloc( &data->subscription ); snd_seq_port_subscribe_set_sender(data->subscription, &sender); snd_seq_port_subscribe_set_dest(data->subscription, &receiver); snd_seq_port_subscribe_set_time_update(data->subscription, 1); snd_seq_port_subscribe_set_time_real(data->subscription, 1); if ( snd_seq_subscribe_port(data->seq, data->subscription) ) { errorString_ = "RtMidiOut::openPort: ALSA error making port connection."; error( RtError::DRIVER_ERROR ); } connected_ = true;}void RtMidiOut :: closePort( void ){ if ( connected_ ) { AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); snd_seq_unsubscribe_port( data->seq, data->subscription ); snd_seq_port_subscribe_free( data->subscription ); connected_ = false; }}void RtMidiOut :: openVirtualPort(){ AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( data->vport < 0 ) { data->vport = snd_seq_create_simple_port( data->seq, "RtMidi Output", SND_SEQ_PORT_CAP_READ|SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC ); if ( data->vport < 0 ) { errorString_ = "RtMidiOut::openVirtualPort: ALSA error creating virtual port."; error( RtError::DRIVER_ERROR ); } }}RtMidiOut :: ~RtMidiOut(){ // Close a connection if it exists. closePort(); // Cleanup. AlsaMidiData *data = static_cast<AlsaMidiData *> (apiData_); if ( data->vport >= 0 ) snd_seq_delete_port( data->seq, data->vport ); if ( data->coder ) snd_midi_event_free( data->coder ); if ( data->buffer ) free( data->buffer ); snd_seq_close( data->seq ); delete data;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -