added support for easy MIDI-port-subscription inside LMMS

git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@29 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
Tobias Doerffel
2005-12-08 10:31:29 +00:00
parent 9c899fe33f
commit 457c39ccdf
9 changed files with 472 additions and 15 deletions

View File

@@ -29,10 +29,16 @@
#ifdef QT4
#include <Qt/QtXml>
#include <QListBox>
#include <QMenu>
#include <QToolButton>
#else
#include <qdom.h>
#include <qlistbox.h>
#include <qpopupmenu.h>
#include <qtoolbutton.h>
#endif
@@ -45,7 +51,7 @@
#include "lcd_spinbox.h"
#include "tooltip.h"
#include "song_editor.h"
#include "midi_client.h"
@@ -58,7 +64,7 @@ midiTabWidget::midiTabWidget( channelTrack * _channel_track,
{
m_setupTabWidget = new tabWidget( tr( "MIDI-SETUP FOR THIS CHANNEL" ),
this );
m_setupTabWidget->setGeometry( 4, 5, 238, 130 );
m_setupTabWidget->setGeometry( 4, 5, 238, 180 );
m_inputChannelSpinBox = new lcdSpinBox( 0, MIDI_CHANNEL_COUNT, 3,
@@ -75,7 +81,7 @@ midiTabWidget::midiTabWidget( channelTrack * _channel_track,
m_outputChannelSpinBox->addTextForValue( 0, "---" );
m_outputChannelSpinBox->setValue( m_midiPort->outputChannel() + 1 );
m_outputChannelSpinBox->setLabel( tr( "CHANNEL" ) );
m_outputChannelSpinBox->move( 190, 60 );
m_outputChannelSpinBox->move( 190, 90 );
connect( m_outputChannelSpinBox, SIGNAL( valueChanged( int ) ),
this, SLOT( outputChannelChanged( int ) ) );
@@ -91,7 +97,7 @@ midiTabWidget::midiTabWidget( channelTrack * _channel_track,
m_sendCheckBox = new ledCheckBox( tr( "SEND MIDI-EVENTS" ),
m_setupTabWidget );
m_sendCheckBox->move( 10, 64 );
m_sendCheckBox->move( 10, 94 );
connect( m_sendCheckBox, SIGNAL( toggled( bool ) ),
this, SLOT( midiPortModeToggled( bool ) ) );
connect( m_sendCheckBox, SIGNAL( toggled( bool ) ),
@@ -102,7 +108,7 @@ midiTabWidget::midiTabWidget( channelTrack * _channel_track,
m_setupTabWidget );
m_routeCheckBox->setChecked(
m_channelTrack->midiEventRoutingEnabled() );
m_routeCheckBox->move( 10, 100 );
m_routeCheckBox->move( 10, 150 );
connect( m_sendCheckBox, SIGNAL( toggled( bool ) ),
m_channelTrack, SLOT( toggleMidiEventRouting( bool ) ) );
@@ -112,6 +118,41 @@ midiTabWidget::midiTabWidget( channelTrack * _channel_track,
m_sendCheckBox->setChecked( m == midiPort::OUTPUT ||
m == midiPort::DUPLEX );
midiClient * mc = mixer::inst()->getMIDIClient();
// non-raw-clients have ports we can subscribe to!
if( mc->isRaw() == FALSE )
{
m_readablePorts = new QMenu( m_setupTabWidget );
m_readablePorts->setCheckable( TRUE );
connect( m_readablePorts, SIGNAL( activated( int ) ),
this, SLOT( activatedReadablePort( int ) ) );
m_writeablePorts = new QMenu( m_setupTabWidget );
m_writeablePorts->setCheckable( TRUE );
connect( m_writeablePorts, SIGNAL( activated( int ) ),
this, SLOT( activatedWriteablePort( int ) ) );
// fill menus
readablePortsChanged();
writeablePortsChanged();
QToolButton * rp_btn = new QToolButton( m_setupTabWidget );
rp_btn->setText( tr( "RECEIVE FROM" ) );
rp_btn->setGeometry( 24, 52, 140, 24 );
rp_btn->setPopup( m_readablePorts );
rp_btn->setPopupDelay( 1 );
QToolButton * wp_btn = new QToolButton( m_setupTabWidget );
wp_btn->setText( tr( "SEND TO" ) );
wp_btn->setGeometry( 24, 112, 140, 24 );
wp_btn->setPopup( m_writeablePorts );
wp_btn->setPopupDelay( 1 );
// we want to get informed about port-changes!
mc->connectRPChanged( this, SLOT( readablePortsChanged() ) );
mc->connectWPChanged( this, SLOT( writeablePortsChanged() ) );
}
}
@@ -193,5 +234,102 @@ void midiTabWidget::midiPortModeToggled( bool )
void midiTabWidget::readablePortsChanged( void )
{
// first save all selected ports
QStringList selected_ports;
for( csize i = 0; i < m_readablePorts->count(); ++i )
{
int id = m_readablePorts->idAt( i );
if( m_readablePorts->isItemChecked( id ) )
{
selected_ports.push_back( m_readablePorts->text( id ) );
}
}
m_readablePorts->clear();
const QStringList & rp = mixer::inst()->getMIDIClient()->
readablePorts();
// now insert new ports and restore selections
for( QStringList::const_iterator it = rp.begin(); it != rp.end(); ++it )
{
int id = m_readablePorts->insertItem( *it );
if( selected_ports.find( *it ) != selected_ports.end() )
{
m_readablePorts->setItemChecked( id, TRUE );
}
}
}
void midiTabWidget::writeablePortsChanged( void )
{
// first save all selected ports
QStringList selected_ports;
for( csize i = 0; i < m_writeablePorts->count(); ++i )
{
int id = m_writeablePorts->idAt( i );
if( m_writeablePorts->isItemChecked( id ) )
{
selected_ports.push_back( m_writeablePorts->text(
id ) );
}
}
m_writeablePorts->clear();
const QStringList & wp = mixer::inst()->getMIDIClient()->
writeablePorts();
// now insert new ports and restore selections
for( QStringList::const_iterator it = wp.begin(); it != wp.end(); ++it )
{
int id = m_writeablePorts->insertItem( *it );
if( selected_ports.find( *it ) != selected_ports.end() )
{
m_writeablePorts->setItemChecked( id, TRUE );
}
}
}
void midiTabWidget::activatedReadablePort( int _id )
{
// make sure, MIDI-port is configured for input
if( m_readablePorts->isItemChecked( _id ) == FALSE &&
m_midiPort->mode() != midiPort::INPUT &&
m_midiPort->mode() != midiPort::DUPLEX )
{
m_receiveCheckBox->setChecked( TRUE );
}
m_readablePorts->setItemChecked( _id,
!m_readablePorts->isItemChecked( _id ) );
mixer::inst()->getMIDIClient()->subscribeReadablePort(
m_midiPort, m_readablePorts->text( _id ),
!m_readablePorts->isItemChecked( _id ) );
}
void midiTabWidget::activatedWriteablePort( int _id )
{
// make sure, MIDI-port is configured for output
if( m_writeablePorts->isItemChecked( _id ) == FALSE &&
m_midiPort->mode() != midiPort::OUTPUT &&
m_midiPort->mode() != midiPort::DUPLEX )
{
m_sendCheckBox->setChecked( TRUE );
}
m_writeablePorts->setItemChecked( _id,
!m_writeablePorts->isItemChecked( _id ) );
mixer::inst()->getMIDIClient()->subscribeWriteablePort(
m_midiPort, m_writeablePorts->text( _id ),
!m_writeablePorts->isItemChecked( _id ) );
}
#include "midi_tab_widget.moc"

View File

@@ -51,11 +51,17 @@
midiALSASeq::midiALSASeq( void ) :
#ifndef QT4
QObject(),
#endif
midiClient(),
QThread(),
m_seqHandle( NULL ),
m_queueID( -1 ),
m_quit( FALSE )
m_quit( FALSE ),
m_portListUpdateTimer( this ),
m_readablePorts(),
m_writeablePorts()
{
int err;
if( ( err = snd_seq_open( &m_seqHandle,
@@ -71,6 +77,7 @@ midiALSASeq::midiALSASeq( void ) :
}
snd_seq_set_client_name( m_seqHandle, "LMMS" );
m_queueID = snd_seq_alloc_queue( m_seqHandle );
snd_seq_queue_tempo_t * tempo;
snd_seq_queue_tempo_alloca( &tempo );
@@ -84,6 +91,14 @@ midiALSASeq::midiALSASeq( void ) :
connect( songEditor::inst(), SIGNAL( bpmChanged( int ) ),
this, SLOT( changeQueueTempo( int ) ) );
// initial list-update
updatePortList();
connect( &m_portListUpdateTimer, SIGNAL( timeout() ),
this, SLOT( updatePortList() ) );
// we check for port-changes every second
m_portListUpdateTimer.start( 1000 );
start(
#if QT_VERSION >= 0x030200
QThread::LowPriority
@@ -315,6 +330,87 @@ void midiALSASeq::removePort( midiPort * _port )
void midiALSASeq::subscribeReadablePort( midiPort * _port,
const QString & _dest,
bool _unsubscribe )
{
if( m_portIDs.contains( _port ) == FALSE ||
( _port->mode() != midiPort::INPUT &&
_port->mode() != midiPort::DUPLEX ) )
{
return;
}
snd_seq_addr_t sender;
if( snd_seq_parse_address( m_seqHandle, &sender,
_dest.section( ' ', 0, 0 ).ascii() ) )
{
printf( "error parsing sender-address!!\n" );
return;
}
snd_seq_port_info_t * port_info;
snd_seq_port_info_malloc( &port_info );
snd_seq_get_port_info( m_seqHandle, m_portIDs[_port][0], port_info );
const snd_seq_addr_t * dest = snd_seq_port_info_get_addr( port_info );
snd_seq_port_subscribe_t * subs;
snd_seq_port_subscribe_alloca( &subs );
snd_seq_port_subscribe_set_sender( subs, &sender );
snd_seq_port_subscribe_set_dest( subs, dest );
if( _unsubscribe )
{
snd_seq_unsubscribe_port( m_seqHandle, subs );
}
else
{
snd_seq_subscribe_port( m_seqHandle, subs );
}
snd_seq_port_info_free( port_info );
}
void midiALSASeq::subscribeWriteablePort( midiPort * _port,
const QString & _dest,
bool _unsubscribe )
{
if( m_portIDs.contains( _port ) == FALSE ||
( _port->mode() != midiPort::OUTPUT &&
_port->mode() != midiPort::DUPLEX ) )
{
return;
}
snd_seq_addr_t dest;
if( snd_seq_parse_address( m_seqHandle, &dest,
_dest.section( ' ', 0, 0 ).ascii() ) )
{
printf( "error parsing dest-address!!\n" );
return;
}
snd_seq_port_info_t * port_info;
snd_seq_port_info_malloc( &port_info );
snd_seq_get_port_info( m_seqHandle, ( m_portIDs[_port][1] == -1 ) ?
m_portIDs[_port][0] :
m_portIDs[_port][1],
port_info );
const snd_seq_addr_t * sender = snd_seq_port_info_get_addr( port_info );
snd_seq_port_subscribe_t * subs;
snd_seq_port_subscribe_alloca( &subs );
snd_seq_port_subscribe_set_sender( subs, sender );
snd_seq_port_subscribe_set_dest( subs, &dest );
if( _unsubscribe )
{
snd_seq_unsubscribe_port( m_seqHandle, subs );
}
else
{
snd_seq_subscribe_port( m_seqHandle, subs );
}
snd_seq_port_info_free( port_info );
}
void midiALSASeq::run( void )
{
while( m_quit == FALSE )
@@ -331,10 +427,12 @@ void midiALSASeq::run( void )
dest = m_portIDs.keys()[i];
}
}
if( dest == NULL )
{
continue;
}
switch( ev->type )
{
case SND_SEQ_EVENT_NOTEON:
@@ -343,7 +441,8 @@ void midiALSASeq::run( void )
ev->data.note.note -
NOTES_PER_OCTAVE,
ev->data.note.velocity
), midiTime( ev->time.tick) );
),
midiTime( ev->time.tick ) );
break;
case SND_SEQ_EVENT_NOTEOFF:
@@ -352,7 +451,8 @@ void midiALSASeq::run( void )
ev->data.note.note -
NOTES_PER_OCTAVE,
ev->data.note.velocity
), midiTime( ev->time.tick) );
),
midiTime( ev->time.tick) );
break;
case SND_SEQ_EVENT_KEYPRESS:
@@ -373,6 +473,7 @@ void midiALSASeq::run( void )
"event %d\n", ev->type );
break;
}
}
}
@@ -390,6 +491,83 @@ void midiALSASeq::changeQueueTempo( int _bpm )
void midiALSASeq::updatePortList( void )
{
QStringList readable_ports;
QStringList writeable_ports;
// get input- and output-ports
snd_seq_client_info_t * cinfo;
snd_seq_port_info_t * pinfo;
snd_seq_client_info_alloca( &cinfo );
snd_seq_port_info_alloca( &pinfo );
snd_seq_client_info_set_client( cinfo, -1 );
while( snd_seq_query_next_client( m_seqHandle, cinfo ) >= 0 )
{
int client = snd_seq_client_info_get_client( cinfo );
snd_seq_port_info_set_client( pinfo, client );
snd_seq_port_info_set_port( pinfo, -1 );
while( snd_seq_query_next_port( m_seqHandle, pinfo ) >= 0 )
{
// we need both READ and SUBS_READ
if( ( snd_seq_port_info_get_capability( pinfo )
& ( SND_SEQ_PORT_CAP_READ |
SND_SEQ_PORT_CAP_SUBS_READ ) ) ==
( SND_SEQ_PORT_CAP_READ |
SND_SEQ_PORT_CAP_SUBS_READ ) )
{
readable_ports.push_back(
QString( "%1:%2 %3:%4" ).
arg( snd_seq_port_info_get_client(
pinfo ) ).
arg( snd_seq_port_info_get_port(
pinfo ) ).
arg( snd_seq_client_info_get_name(
cinfo ) ).
arg( snd_seq_port_info_get_name(
pinfo ) ) );
}
if( ( snd_seq_port_info_get_capability( pinfo )
& ( SND_SEQ_PORT_CAP_WRITE |
SND_SEQ_PORT_CAP_SUBS_WRITE ) ) ==
( SND_SEQ_PORT_CAP_WRITE |
SND_SEQ_PORT_CAP_SUBS_WRITE ) )
{
writeable_ports.push_back(
QString( "%1:%2 %3:%4" ).
arg( snd_seq_port_info_get_client(
pinfo ) ).
arg( snd_seq_port_info_get_port(
pinfo ) ).
arg( snd_seq_client_info_get_name(
cinfo ) ).
arg( snd_seq_port_info_get_name(
pinfo ) ) );
}
}
}
/* snd_seq_client_info_free( cinfo );
snd_seq_port_info_free( pinfo );*/
if( m_readablePorts != readable_ports )
{
m_readablePorts = readable_ports;
emit( readablePortsChanged() );
}
if( m_writeablePorts != writeable_ports )
{
m_writeablePorts = writeable_ports;
emit( writeablePortsChanged() );
}
}

View File

@@ -91,6 +91,38 @@ void midiClient::removePort( midiPort * _port )
const QStringList & midiClient::readablePorts( void ) const
{
static QStringList sl;
return( sl );
}
const QStringList & midiClient::writeablePorts( void ) const
{
static QStringList sl;
return( sl );
}
void midiClient::subscribeReadablePort( midiPort *, const QString & , bool )
{
}
void midiClient::subscribeWriteablePort( midiPort * , const QString & , bool)
{
}