/*
Rosegarden
A sequencer and musical notation editor .
This program is Copyright 2000 - 2008
Guillaume Laurent < glaurent @ telegraph - road . org > ,
Chris Cannam < cannam @ all - day - breakfast . com > ,
Richard Bown < bownie @ bownie . com >
The moral right of the authors to claim authorship of this work
has been asserted .
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation ; either version 2 of the
License , or ( at your option ) any later version . See the file
COPYING included with this distribution for more information .
*/
# include <iostream>
# include "misc/Debug.h"
# include <cstdlib>
# include <cstdio>
# include <algorithm>
# ifdef HAVE_ALSA
// ALSA
# include <alsa/asoundlib.h>
# include <alsa/seq_event.h>
# include <alsa/version.h>
# include <alsa/seq.h>
# include "AlsaDriver.h"
# include "AlsaPort.h"
# include "ExternalTransport.h"
# include "MappedInstrument.h"
# include "Midi.h"
# include "MappedStudio.h"
# include "misc/Strings.h"
# include "MappedCommon.h"
# include "MappedEvent.h"
# include "Audit.h"
# include "AudioPlayQueue.h"
# include "ExternalTransport.h"
# include <tqregexp.h>
# include <pthread.h>
//#define DEBUG_ALSA 1
//#define DEBUG_PROCESS_MIDI_OUT 1
//#define DEBUG_PROCESS_SOFT_SYNTH_OUT 1
//#define MTC_DEBUG 1
// This driver implements MIDI in and out via the ALSA (www.alsa-project.org)
// sequencer interface.
using std : : cerr ;
using std : : endl ;
static size_t _debug_jack_frame_count = 0 ;
# define AUTO_TIMER_NAME "(auto)"
namespace Rosegarden
{
# define FAILURE_REPORT_COUNT 256
static MappedEvent : : FailureCode _failureReports [ FAILURE_REPORT_COUNT ] ;
static int _failureReportWriteIndex = 0 ;
static int _failureReportReadIndex = 0 ;
AlsaDriver : : AlsaDriver ( MappedStudio * studio ) :
SoundDriver ( studio ,
std : : string ( " [ALSA library version " ) +
std : : string ( SND_LIB_VERSION_STR ) +
std : : string ( " , module version " ) +
getAlsaModuleVersionString ( ) +
std : : string ( " , kernel version " ) +
getKernelVersionString ( ) +
" ] " ) ,
m_client ( - 1 ) ,
m_inputPort ( - 1 ) ,
m_syncOutputPort ( - 1 ) ,
m_controllerPort ( - 1 ) ,
m_queue ( - 1 ) ,
m_maxClients ( - 1 ) ,
m_maxPorts ( - 1 ) ,
m_maxQueues ( - 1 ) ,
m_midiInputPortConnected ( false ) ,
m_midiSyncAutoConnect ( false ) ,
m_alsaPlayStartTime ( 0 , 0 ) ,
m_alsaRecordStartTime ( 0 , 0 ) ,
m_loopStartTime ( 0 , 0 ) ,
m_loopEndTime ( 0 , 0 ) ,
m_eat_mtc ( 0 ) ,
m_looping ( false ) ,
m_haveShutdown ( false )
# ifdef HAVE_LIBJACK
, m_jackDriver ( 0 )
# endif
, m_queueRunning ( false )
, m_portCheckNeeded ( false ) ,
m_needJackStart ( NeedNoJackStart ) ,
m_doTimerChecks ( false ) ,
m_firstTimerCheck ( true ) ,
m_timerRatio ( 0 ) ,
m_timerRatioCalculated ( false )
{
Audit audit ;
audit < < " Rosegarden " < < VERSION < < " - AlsaDriver "
< < m_name < < std : : endl ;
}
AlsaDriver : : ~ AlsaDriver ( )
{
if ( ! m_haveShutdown ) {
std : : cerr < < " WARNING: AlsaDriver::shutdown() was not called before destructor, calling now " < < std : : endl ;
shutdown ( ) ;
}
}
int
AlsaDriver : : checkAlsaError ( int rc , const char *
# ifdef DEBUG_ALSA
message
# endif
)
{
# ifdef DEBUG_ALSA
if ( rc < 0 ) {
std : : cerr < < " AlsaDriver:: "
< < message
< < " : " < < rc
< < " ( " < < snd_strerror ( rc ) < < " ) "
< < std : : endl ;
}
# endif
return rc ;
}
void
AlsaDriver : : shutdown ( )
{
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::~AlsaDriver - shutting down " < < std : : endl ;
# endif
processNotesOff ( getAlsaTime ( ) , true , true ) ;
# ifdef HAVE_LIBJACK
delete m_jackDriver ;
m_jackDriver = 0 ;
# endif
if ( m_midiHandle ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::shutdown - closing MIDI client " < < std : : endl ;
# endif
checkAlsaError ( snd_seq_stop_queue ( m_midiHandle , m_queue , 0 ) , " shutdown(): stopping queue " ) ;
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " shutdown(): drain output " ) ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::shutdown - stopped queue " < < std : : endl ;
# endif
snd_seq_close ( m_midiHandle ) ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::shutdown - closed MIDI handle " < < std : : endl ;
# endif
m_midiHandle = 0 ;
}
DataBlockRepository : : clear ( ) ;
m_haveShutdown = true ;
}
void
AlsaDriver : : setLoop ( const RealTime & loopStart , const RealTime & loopEnd )
{
m_loopStartTime = loopStart ;
m_loopEndTime = loopEnd ;
// currently we use this simple test for looping - it might need
// to get more sophisticated in the future.
//
if ( m_loopStartTime ! = m_loopEndTime )
m_looping = true ;
else
m_looping = false ;
}
void
AlsaDriver : : getSystemInfo ( )
{
int err ;
snd_seq_system_info_t * sysinfo ;
snd_seq_system_info_alloca ( & sysinfo ) ;
if ( ( err = snd_seq_system_info ( m_midiHandle , sysinfo ) ) < 0 ) {
std : : cerr < < " System info error: " < < snd_strerror ( err )
< < std : : endl ;
reportFailure ( MappedEvent : : FailureALSACallFailed ) ;
m_maxQueues = 0 ;
m_maxClients = 0 ;
m_maxPorts = 0 ;
return ;
}
m_maxQueues = snd_seq_system_info_get_queues ( sysinfo ) ;
m_maxClients = snd_seq_system_info_get_clients ( sysinfo ) ;
m_maxPorts = snd_seq_system_info_get_ports ( sysinfo ) ;
}
void
AlsaDriver : : showQueueStatus ( int queue )
{
int err , idx , min , max ;
snd_seq_queue_status_t * status ;
snd_seq_queue_status_alloca ( & status ) ;
min = queue < 0 ? 0 : queue ;
max = queue < 0 ? m_maxQueues : queue + 1 ;
for ( idx = min ; idx < max ; + + idx ) {
if ( ( err = snd_seq_get_queue_status ( m_midiHandle , idx , status ) ) < 0 ) {
if ( err = = - ENOENT )
continue ;
std : : cerr < < " Client " < < idx < < " info error: "
< < snd_strerror ( err ) < < std : : endl ;
reportFailure ( MappedEvent : : FailureALSACallFailed ) ;
return ;
}
# ifdef DEBUG_ALSA
std : : cerr < < " Queue " < < snd_seq_queue_status_get_queue ( status )
< < std : : endl ;
std : : cerr < < " Tick = "
< < snd_seq_queue_status_get_tick_time ( status )
< < std : : endl ;
std : : cerr < < " Realtime = "
< < snd_seq_queue_status_get_real_time ( status ) - > tv_sec
< < " . "
< < snd_seq_queue_status_get_real_time ( status ) - > tv_nsec
< < std : : endl ;
std : : cerr < < " Flags = 0x "
< < snd_seq_queue_status_get_status ( status )
< < std : : endl ;
# endif
}
}
void
AlsaDriver : : generateTimerList ( )
{
// Enumerate the available timers
snd_timer_t * timerHandle ;
snd_timer_id_t * timerId ;
snd_timer_info_t * timerInfo ;
snd_timer_id_alloca ( & timerId ) ;
snd_timer_info_alloca ( & timerInfo ) ;
snd_timer_query_t * timerQuery ;
char timerName [ 64 ] ;
m_timers . clear ( ) ;
if ( snd_timer_query_open ( & timerQuery , " hw " , 0 ) > = 0 ) {
snd_timer_id_set_class ( timerId , SND_TIMER_CLASS_NONE ) ;
while ( 1 ) {
if ( snd_timer_query_next_device ( timerQuery , timerId ) < 0 )
break ;
if ( snd_timer_id_get_class ( timerId ) < 0 )
break ;
AlsaTimerInfo info = {
snd_timer_id_get_class ( timerId ) ,
snd_timer_id_get_sclass ( timerId ) ,
snd_timer_id_get_card ( timerId ) ,
snd_timer_id_get_device ( timerId ) ,
snd_timer_id_get_subdevice ( timerId ) ,
" " ,
0
} ;
if ( info . card < 0 )
info . card = 0 ;
if ( info . device < 0 )
info . device = 0 ;
if ( info . subdevice < 0 )
info . subdevice = 0 ;
// std::cerr << "got timer: class " << info.clas << std::endl;
sprintf ( timerName , " hw:CLASS=%i,SCLASS=%i,CARD=%i,DEV=%i,SUBDEV=%i " ,
info . clas , info . sclas , info . card , info . device , info . subdevice ) ;
if ( snd_timer_open ( & timerHandle , timerName , SND_TIMER_OPEN_NONBLOCK ) < 0 ) {
std : : cerr < < " Failed to open timer: " < < timerName < < std : : endl ;
continue ;
}
if ( snd_timer_info ( timerHandle , timerInfo ) < 0 )
continue ;
info . name = snd_timer_info_get_name ( timerInfo ) ;
info . resolution = snd_timer_info_get_resolution ( timerInfo ) ;
snd_timer_close ( timerHandle ) ;
// std::cerr << "adding timer: " << info.name << std::endl;
m_timers . push_back ( info ) ;
}
snd_timer_query_close ( timerQuery ) ;
}
}
std : : string
AlsaDriver : : getAutoTimer ( bool & wantTimerChecks )
{
Audit audit ;
// Look for the apparent best-choice timer.
if ( m_timers . empty ( ) )
return " " ;
// The system RTC timer ought to be good, but it doesn't look like
// a very safe choice -- we've seen some system lockups apparently
// connected with use of this timer on 2.6 kernels. So we avoid
// using that as an auto option.
// Looks like our most reliable options for timers are, in order:
//
// 1. System timer if at 1000Hz, with timer checks (i.e. automatic
// drift correction against PCM frame count). Only available
// when JACK is running.
//
// 2. PCM playback timer currently in use by JACK (no drift, but
// suffers from jitter).
//
// 3. System timer if at 1000Hz.
//
// 4. System RTC timer.
//
// 5. System timer.
// As of Linux kernel 2.6.13 (?) the default system timer
// resolution has been reduced from 1000Hz to 250Hz, giving us
// only 4ms accuracy instead of 1ms. This may be better than the
// 10ms available from the stock 2.4 kernel, but it's not enough
// for really solid MIDI timing. If JACK is running at 44.1 or
// 48KHz with a buffer size less than 256 frames, then the PCM
// timer will give us less jitter. Even at 256 frames, it may be
// preferable in practice just because it's simpler.
// However, we can't safely choose the PCM timer over the system
// timer unless the latter has really awful resolution, because we
// don't know for certain which PCM JACK is using. We guess at
// hw:0 for the moment, which gives us a stuck timer problem if
// it's actually using something else. So if the system timer
// runs at 250Hz, we really have to choose it anyway and just give
// a warning.
bool pcmTimerAccepted = false ;
wantTimerChecks = false ; // for most options
bool rtcCouldBeOK = false ;
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
wantTimerChecks = true ;
pcmTimerAccepted = true ;
}
# endif
// look for a high frequency system timer
for ( std : : vector < AlsaTimerInfo > : : iterator i = m_timers . begin ( ) ;
i ! = m_timers . end ( ) ; + + i ) {
if ( i - > sclas ! = SND_TIMER_SCLASS_NONE )
continue ;
if ( i - > clas = = SND_TIMER_CLASS_GLOBAL ) {
if ( i - > device = = SND_TIMER_GLOBAL_SYSTEM ) {
long hz = 1000000000 / i - > resolution ;
if ( hz > = 750 ) {
return i - > name ;
}
}
}
}
// Look for the system RTC timer if available. This has been
// known to hang some real-time kernels, but reports suggest that
// recent kernels are OK. Avoid if the kernel is older than
// 2.6.20 or the ALSA driver is older than 1.0.14.
if ( versionIsAtLeast ( getAlsaModuleVersionString ( ) ,
1 , 0 , 14 ) & &
versionIsAtLeast ( getKernelVersionString ( ) ,
2 , 6 , 20 ) ) {
rtcCouldBeOK = true ;
for ( std : : vector < AlsaTimerInfo > : : iterator i = m_timers . begin ( ) ;
i ! = m_timers . end ( ) ; + + i ) {
if ( i - > sclas ! = SND_TIMER_SCLASS_NONE ) continue ;
if ( i - > clas = = SND_TIMER_CLASS_GLOBAL ) {
if ( i - > device = = SND_TIMER_GLOBAL_RTC ) {
return i - > name ;
}
}
}
}
// look for the first PCM playback timer; that's all we know about
// for now (until JACK becomes able to tell us which PCM it's on)
if ( pcmTimerAccepted ) {
for ( std : : vector < AlsaTimerInfo > : : iterator i = m_timers . begin ( ) ;
i ! = m_timers . end ( ) ; + + i ) {
if ( i - > sclas ! = SND_TIMER_SCLASS_NONE )
continue ;
if ( i - > clas = = SND_TIMER_CLASS_PCM ) {
if ( i - > resolution ! = 0 ) {
long hz = 1000000000 / i - > resolution ;
if ( hz > = 750 ) {
wantTimerChecks = false ; // pointless with PCM timer
return i - > name ;
} else {
audit < < " PCM timer: inadequate resolution " < < i - > resolution < < std : : endl ;
}
}
}
}
}
// next look for slow, unpopular 100Hz (2.4) or 250Hz (2.6) system timer
for ( std : : vector < AlsaTimerInfo > : : iterator i = m_timers . begin ( ) ;
i ! = m_timers . end ( ) ; + + i ) {
if ( i - > sclas ! = SND_TIMER_SCLASS_NONE )
continue ;
if ( i - > clas = = SND_TIMER_CLASS_GLOBAL ) {
if ( i - > device = = SND_TIMER_GLOBAL_SYSTEM ) {
audit < < " Using low-resolution system timer, sending a warning " < < std : : endl ;
if ( rtcCouldBeOK ) {
reportFailure ( MappedEvent : : WarningImpreciseTimerTryRTC ) ;
} else {
reportFailure ( MappedEvent : : WarningImpreciseTimer ) ;
}
return i - > name ;
}
}
}
// falling back to something that almost certainly won't work,
// if for any reason all of the above failed
return m_timers . begin ( ) - > name ;
}
void
AlsaDriver : : generatePortList ( AlsaPortList * newPorts )
{
Audit audit ;
AlsaPortList alsaPorts ;
snd_seq_client_info_t * cinfo ;
snd_seq_port_info_t * pinfo ;
int client ;
unsigned int writeCap = SND_SEQ_PORT_CAP_SUBS_WRITE | SND_SEQ_PORT_CAP_WRITE ;
unsigned int readCap = SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_READ ;
snd_seq_client_info_alloca ( & cinfo ) ;
snd_seq_client_info_set_client ( cinfo , - 1 ) ;
audit < < std : : endl < < " ALSA Client information: "
< < std : : endl < < std : : endl ;
// Get only the client ports we're interested in and store them
// for sorting and then device creation.
//
while ( snd_seq_query_next_client ( m_midiHandle , cinfo ) > = 0 ) {
client = snd_seq_client_info_get_client ( cinfo ) ;
snd_seq_port_info_alloca ( & pinfo ) ;
snd_seq_port_info_set_client ( pinfo , client ) ;
snd_seq_port_info_set_port ( pinfo , - 1 ) ;
// Ignore ourselves and the system client
//
if ( client = = m_client | | client = = 0 )
continue ;
while ( snd_seq_query_next_port ( m_midiHandle , pinfo ) > = 0 ) {
int client = snd_seq_port_info_get_client ( pinfo ) ;
int port = snd_seq_port_info_get_port ( pinfo ) ;
unsigned int clientType = snd_seq_client_info_get_type ( cinfo ) ;
unsigned int portType = snd_seq_port_info_get_type ( pinfo ) ;
unsigned int capability = snd_seq_port_info_get_capability ( pinfo ) ;
if ( ( ( ( capability & writeCap ) = = writeCap ) | |
( ( capability & readCap ) = = readCap ) ) & &
( ( capability & SND_SEQ_PORT_CAP_NO_EXPORT ) = = 0 ) ) {
audit < < " "
< < client < < " , "
< < port < < " - ( "
< < snd_seq_client_info_get_name ( cinfo ) < < " , "
< < snd_seq_port_info_get_name ( pinfo ) < < " ) " ;
PortDirection direction ;
if ( ( capability & SND_SEQ_PORT_CAP_DUPLEX ) | |
( ( capability & SND_SEQ_PORT_CAP_WRITE ) & &
( capability & SND_SEQ_PORT_CAP_READ ) ) ) {
direction = Duplex ;
audit < < " \t \t \t (DUPLEX) " ;
} else if ( capability & SND_SEQ_PORT_CAP_WRITE ) {
direction = WriteOnly ;
audit < < " \t \t (WRITE ONLY) " ;
} else {
direction = ReadOnly ;
audit < < " \t \t (READ ONLY) " ;
}
audit < < " [ctype " < < clientType < < " , ptype " < < portType < < " , cap " < < capability < < " ] " ;
// Generate a unique name using the client id
//
char portId [ 40 ] ;
sprintf ( portId , " %d:%d " , client , port ) ;
std : : string fullClientName =
std : : string ( snd_seq_client_info_get_name ( cinfo ) ) ;
std : : string fullPortName =
std : : string ( snd_seq_port_info_get_name ( pinfo ) ) ;
std : : string name ;
// If the first part of the client name is the same as the
// start of the port name, just use the port name. otherwise
// concatenate.
//
int firstSpace = fullClientName . find ( " " ) ;
// If no space is found then we try to match the whole string
//
if ( firstSpace < 0 )
firstSpace = fullClientName . length ( ) ;
if ( firstSpace > 0 & &
int ( fullPortName . length ( ) ) > = firstSpace & &
fullPortName . substr ( 0 , firstSpace ) = =
fullClientName . substr ( 0 , firstSpace ) ) {
name = portId + fullPortName ;
} else {
name = portId + fullClientName + " : " + fullPortName ;
}
// Sanity check for length
//
if ( name . length ( ) > 35 )
name = portId + fullPortName ;
if ( direction = = WriteOnly ) {
name + = " (write) " ;
} else if ( direction = = ReadOnly ) {
name + = " (read) " ;
} else if ( direction = = Duplex ) {
name + = " (duplex) " ;
}
AlsaPortDescription * portDescription =
new AlsaPortDescription (
Instrument : : Midi ,
name ,
client ,
port ,
clientType ,
portType ,
capability ,
direction ) ;
if ( newPorts & &
( getPortName ( ClientPortPair ( client , port ) ) = = " " ) ) {
newPorts - > push_back ( portDescription ) ;
}
alsaPorts . push_back ( portDescription ) ;
audit < < std : : endl ;
}
}
}
audit < < std : : endl ;
// Ok now sort by duplexicity
//
std : : sort ( alsaPorts . begin ( ) , alsaPorts . end ( ) , AlsaPortCmp ( ) ) ;
m_alsaPorts = alsaPorts ;
}
void
AlsaDriver : : generateInstruments ( )
{
// Reset these before each Instrument hunt
//
int audioCount = 0 ;
getAudioInstrumentNumbers ( m_audioRunningId , audioCount ) ;
m_midiRunningId = MidiInstrumentBase ;
// Clear these
//
m_instruments . clear ( ) ;
m_devices . clear ( ) ;
m_devicePortMap . clear ( ) ;
m_suspendedPortMap . clear ( ) ;
AlsaPortList : : iterator it = m_alsaPorts . begin ( ) ;
for ( ; it ! = m_alsaPorts . end ( ) ; it + + ) {
if ( ( * it ) - > m_client = = m_client ) {
std : : cerr < < " (Ignoring own port " < < ( * it ) - > m_client
< < " : " < < ( * it ) - > m_port < < " ) " < < std : : endl ;
continue ;
} else if ( ( * it ) - > m_client = = 0 ) {
std : : cerr < < " (Ignoring system port " < < ( * it ) - > m_client
< < " : " < < ( * it ) - > m_port < < " ) " < < std : : endl ;
continue ;
}
if ( ( * it ) - > isWriteable ( ) ) {
MappedDevice * device = createMidiDevice ( * it , MidiDevice : : Play ) ;
if ( ! device ) {
# ifdef DEBUG_ALSA
std : : cerr < < " WARNING: Failed to create play device " < < std : : endl ;
# else
;
# endif
} else {
addInstrumentsForDevice ( device ) ;
m_devices . push_back ( device ) ;
}
}
if ( ( * it ) - > isReadable ( ) ) {
MappedDevice * device = createMidiDevice ( * it , MidiDevice : : Record ) ;
if ( ! device ) {
# ifdef DEBUG_ALSA
std : : cerr < < " WARNING: Failed to create record device " < < std : : endl ;
# else
;
# endif
} else {
m_devices . push_back ( device ) ;
}
}
}
# ifdef HAVE_DSSI
// Create a number of soft synth Instruments
//
{
MappedInstrument * instr ;
char number [ 100 ] ;
InstrumentId first ;
int count ;
getSoftSynthInstrumentNumbers ( first , count ) ;
DeviceId ssiDeviceId = getSpareDeviceId ( ) ;
if ( m_driverStatus & AUDIO_OK ) {
for ( int i = 0 ; i < count ; + + i ) {
sprintf ( number , " #%d " , i + 1 ) ;
std : : string name = " Synth plugin " + std : : string ( number ) ;
instr = new MappedInstrument ( Instrument : : SoftSynth ,
i ,
first + i ,
name ,
ssiDeviceId ) ;
m_instruments . push_back ( instr ) ;
m_studio - > createObject ( MappedObject : : AudioFader ,
first + i ) ;
}
MappedDevice * device =
new MappedDevice ( ssiDeviceId ,
Device : : SoftSynth ,
" Synth plugin " ,
" Soft synth connection " ) ;
m_devices . push_back ( device ) ;
}
}
# endif
# ifdef HAVE_LIBJACK
// Create a number of audio Instruments - these are just
// logical Instruments anyway and so we can create as
// many as we like and then use them as Tracks.
//
{
MappedInstrument * instr ;
char number [ 100 ] ;
std : : string audioName ;
DeviceId audioDeviceId = getSpareDeviceId ( ) ;
if ( m_driverStatus & AUDIO_OK )
{
for ( int channel = 0 ; channel < audioCount ; + + channel ) {
sprintf ( number , " #%d " , channel + 1 ) ;
audioName = " Audio " + std : : string ( number ) ;
instr = new MappedInstrument ( Instrument : : Audio ,
channel ,
m_audioRunningId ,
audioName ,
audioDeviceId ) ;
m_instruments . push_back ( instr ) ;
// Create a fader with a matching id - this is the starting
// point for all audio faders.
//
m_studio - > createObject ( MappedObject : : AudioFader ,
m_audioRunningId ) ;
/*
std : : cerr < < " AlsaDriver::generateInstruments - "
< < " added audio fader (id= " < < m_audioRunningId
< < " ) " < < std : : endl ;
*/
m_audioRunningId + + ;
}
// Create audio device
//
MappedDevice * device =
new MappedDevice ( audioDeviceId ,
Device : : Audio ,
" Audio " ,
" Audio connection " ) ;
m_devices . push_back ( device ) ;
}
}
# endif
}
MappedDevice *
AlsaDriver : : createMidiDevice ( AlsaPortDescription * port ,
MidiDevice : : DeviceDirection reqDirection )
{
char deviceName [ 100 ] ;
std : : string connectionName ( " " ) ;
Audit audit ;
static int unknownCounter ;
static int counters [ 3 ] [ 2 ] ; // [system/hardware/software][out/in]
const int UNKNOWN = - 1 , SYSTEM = 0 , HARDWARE = 1 , SOFTWARE = 2 ;
static const char * firstNames [ 4 ] [ 2 ] = {
{ " MIDI output system device " , " MIDI input system device "
} ,
{ " MIDI external device " , " MIDI hardware input device " } ,
{ " MIDI software device " , " MIDI software input " }
} ;
static const char * countedNames [ 4 ] [ 2 ] = {
{ " MIDI output system device %d " , " MIDI input system device %d "
} ,
{ " MIDI external device %d " , " MIDI hardware input device %d " } ,
{ " MIDI software device %d " , " MIDI software input %d " }
} ;
static int specificCounters [ 2 ] ;
static const char * specificNames [ 2 ] = {
" MIDI soundcard synth " , " MIDI soft synth " ,
} ;
static const char * specificCountedNames [ 2 ] = {
" MIDI soundcard synth %d " , " MIDI soft synth %d " ,
} ;
DeviceId deviceId = getSpareDeviceId ( ) ;
if ( port ) {
if ( reqDirection = = MidiDevice : : Record & & ! port - > isReadable ( ) )
return 0 ;
if ( reqDirection = = MidiDevice : : Play & & ! port - > isWriteable ( ) )
return 0 ;
int category = UNKNOWN ;
bool noConnect = false ;
bool isSynth = false ;
bool synthKnown = false ;
if ( port - > m_client < 16 ) {
category = SYSTEM ;
noConnect = true ;
isSynth = false ;
synthKnown = true ;
} else {
# ifdef SND_SEQ_PORT_TYPE_HARDWARE
if ( port - > m_portType & SND_SEQ_PORT_TYPE_HARDWARE ) {
category = HARDWARE ;
}
# endif
# ifdef SND_SEQ_PORT_TYPE_SOFTWARE
if ( port - > m_portType & SND_SEQ_PORT_TYPE_SOFTWARE ) {
category = SOFTWARE ;
}
# endif
# ifdef SND_SEQ_PORT_TYPE_SYNTHESIZER
if ( port - > m_portType & SND_SEQ_PORT_TYPE_SYNTHESIZER ) {
isSynth = true ;
synthKnown = true ;
}
# endif
# ifdef SND_SEQ_PORT_TYPE_APPLICATION
if ( port - > m_portType & SND_SEQ_PORT_TYPE_APPLICATION ) {
category = SOFTWARE ;
isSynth = false ;
synthKnown = true ;
}
# endif
if ( category = = UNKNOWN ) {
if ( port - > m_client < 64 ) {
if ( versionIsAtLeast ( getAlsaModuleVersionString ( ) ,
1 , 0 , 11 ) ) {
category = HARDWARE ;
} else {
category = SYSTEM ;
noConnect = true ;
}
} else if ( port - > m_client < 128 ) {
category = HARDWARE ;
} else {
category = SOFTWARE ;
}
}
}
bool haveName = false ;
if ( ! synthKnown ) {
if ( category ! = SYSTEM & & reqDirection = = MidiDevice : : Play ) {
// We assume GM/GS/XG/MT32 devices are synths.
bool isSynth = ( port - > m_portType &
( SND_SEQ_PORT_TYPE_MIDI_GM |
SND_SEQ_PORT_TYPE_MIDI_GS |
SND_SEQ_PORT_TYPE_MIDI_XG |
SND_SEQ_PORT_TYPE_MIDI_MT32 ) ) ;
if ( ! isSynth & &
( port - > m_name . find ( " ynth " ) < port - > m_name . length ( ) ) )
isSynth = true ;
if ( ! isSynth & &
( port - > m_name . find ( " nstrument " ) < port - > m_name . length ( ) ) )
isSynth = true ;
if ( ! isSynth & &
( port - > m_name . find ( " VSTi " ) < port - > m_name . length ( ) ) )
isSynth = true ;
} else {
isSynth = false ;
}
}
if ( isSynth ) {
int clientType = ( category = = SOFTWARE ) ? 1 : 0 ;
if ( specificCounters [ clientType ] = = 0 ) {
sprintf ( deviceName , specificNames [ clientType ] ) ;
+ + specificCounters [ clientType ] ;
} else {
sprintf ( deviceName ,
specificCountedNames [ clientType ] ,
+ + specificCounters [ clientType ] ) ;
}
haveName = true ;
}
if ( ! haveName ) {
if ( counters [ category ] [ reqDirection ] = = 0 ) {
sprintf ( deviceName , firstNames [ category ] [ reqDirection ] ) ;
+ + counters [ category ] [ reqDirection ] ;
} else {
sprintf ( deviceName ,
countedNames [ category ] [ reqDirection ] ,
+ + counters [ category ] [ reqDirection ] ) ;
}
}
if ( ! noConnect ) {
m_devicePortMap [ deviceId ] = ClientPortPair ( port - > m_client ,
port - > m_port ) ;
connectionName = port - > m_name ;
}
audit < < " Creating device " < < deviceId < < " in "
< < ( reqDirection = = MidiDevice : : Play ? " Play " : " Record " )
< < " mode for connection " < < port - > m_name
< < ( noConnect ? " (not connecting) " : " " )
< < " \n Default device name for this device is "
< < deviceName < < std : : endl ;
} else { // !port
sprintf ( deviceName , " Anonymous MIDI device %d " , + + unknownCounter ) ;
audit < < " Creating device " < < deviceId < < " in "
< < ( reqDirection = = MidiDevice : : Play ? " Play " : " Record " )
< < " mode -- no connection available "
< < " \n Default device name for this device is "
< < deviceName < < std : : endl ;
}
if ( reqDirection = = MidiDevice : : Play ) {
TQString portName ;
if ( TQString ( deviceName ) . startsWith ( " Anonymous MIDI device " ) ) {
portName = TQString ( " out %1 " )
. arg ( m_outputPorts . size ( ) + 1 ) ;
} else {
portName = TQString ( " out %1 - %2 " )
. arg ( m_outputPorts . size ( ) + 1 )
. arg ( deviceName ) ;
}
int outputPort = checkAlsaError ( snd_seq_create_simple_port
( m_midiHandle ,
portName . ascii ( ) ,
SND_SEQ_PORT_CAP_READ |
SND_SEQ_PORT_CAP_SUBS_READ ,
SND_SEQ_PORT_TYPE_APPLICATION ) ,
" createMidiDevice - can't create output port " ) ;
if ( outputPort > = 0 ) {
std : : cerr < < " CREATED OUTPUT PORT " < < outputPort < < " : " < < portName . ascii ( ) < < " for device " < < deviceId < < std : : endl ;
m_outputPorts [ deviceId ] = outputPort ;
if ( port ) {
if ( connectionName ! = " " ) {
std : : cerr < < " Connecting my port " < < outputPort < < " to " < < port - > m_client < < " : " < < port - > m_port < < " on initialisation " < < std : : endl ;
snd_seq_connect_to ( m_midiHandle ,
outputPort ,
port - > m_client ,
port - > m_port ) ;
if ( m_midiSyncAutoConnect ) {
snd_seq_connect_to ( m_midiHandle ,
m_syncOutputPort ,
port - > m_client ,
port - > m_port ) ;
}
}
std : : cerr < < " done " < < std : : endl ;
}
}
}
MappedDevice * device = new MappedDevice ( deviceId ,
Device : : Midi ,
deviceName ,
connectionName ) ;
device - > setDirection ( reqDirection ) ;
return device ;
}
DeviceId
AlsaDriver : : getSpareDeviceId ( )
{
std : : set
< DeviceId > ids ;
for ( unsigned int i = 0 ; i < m_devices . size ( ) ; + + i ) {
ids . insert ( m_devices [ i ] - > getId ( ) ) ;
}
DeviceId id = 0 ;
while ( ids . find ( id ) ! = ids . end ( ) )
+ + id ;
return id ;
}
void
AlsaDriver : : addInstrumentsForDevice ( MappedDevice * device )
{
std : : string channelName ;
char number [ 100 ] ;
for ( int channel = 0 ; channel < 16 ; + + channel ) {
// Create MappedInstrument for export to GUI
//
// name is just number, derive rest from device at gui
sprintf ( number , " #%d " , channel + 1 ) ;
channelName = std : : string ( number ) ;
if ( channel = = 9 )
channelName = std : : string ( " #10[D] " ) ;
MappedInstrument * instr = new MappedInstrument ( Instrument : : Midi ,
channel ,
m_midiRunningId + + ,
channelName ,
device - > getId ( ) ) ;
m_instruments . push_back ( instr ) ;
}
}
bool
AlsaDriver : : canReconnect ( Device : : DeviceType type )
{
return ( type = = Device : : Midi ) ;
}
DeviceId
AlsaDriver : : addDevice ( Device : : DeviceType type ,
MidiDevice : : DeviceDirection direction )
{
if ( type = = Device : : Midi ) {
MappedDevice * device = createMidiDevice ( 0 , direction ) ;
if ( ! device ) {
# ifdef DEBUG_ALSA
std : : cerr < < " WARNING: Device creation failed " < < std : : endl ;
# else
;
# endif
} else {
addInstrumentsForDevice ( device ) ;
m_devices . push_back ( device ) ;
MappedEvent * mE =
new MappedEvent ( 0 , MappedEvent : : SystemUpdateInstruments ,
0 , 0 ) ;
insertMappedEventForReturn ( mE ) ;
return device - > getId ( ) ;
}
}
return Device : : NO_DEVICE ;
}
void
AlsaDriver : : removeDevice ( DeviceId id )
{
DeviceIntMap : : iterator i1 = m_outputPorts . find ( id ) ;
if ( i1 = = m_outputPorts . end ( ) ) {
std : : cerr < < " WARNING: AlsaDriver::removeDevice: Cannot find device "
< < id < < " in port map " < < std : : endl ;
return ;
}
checkAlsaError ( snd_seq_delete_port ( m_midiHandle , i1 - > second ) ,
" removeDevice " ) ;
m_outputPorts . erase ( i1 ) ;
for ( MappedDeviceList : : iterator i = m_devices . end ( ) ;
i ! = m_devices . begin ( ) ; ) {
- - i ;
if ( ( * i ) - > getId ( ) = = id ) {
delete * i ;
m_devices . erase ( i ) ;
}
}
for ( MappedInstrumentList : : iterator i = m_instruments . end ( ) ;
i ! = m_instruments . begin ( ) ; ) {
- - i ;
if ( ( * i ) - > getDevice ( ) = = id ) {
delete * i ;
m_instruments . erase ( i ) ;
}
}
MappedEvent * mE =
new MappedEvent ( 0 , MappedEvent : : SystemUpdateInstruments ,
0 , 0 ) ;
insertMappedEventForReturn ( mE ) ;
}
void
AlsaDriver : : renameDevice ( DeviceId id , TQString name )
{
DeviceIntMap : : iterator i = m_outputPorts . find ( id ) ;
if ( i = = m_outputPorts . end ( ) ) {
std : : cerr < < " WARNING: AlsaDriver::renameDevice: Cannot find device "
< < id < < " in port map " < < std : : endl ;
return ;
}
snd_seq_port_info_t * pinfo ;
snd_seq_port_info_alloca ( & pinfo ) ;
snd_seq_get_port_info ( m_midiHandle , i - > second , pinfo ) ;
TQString oldName = snd_seq_port_info_get_name ( pinfo ) ;
int sep = oldName . find ( " - " ) ;
TQString newName ;
if ( name . startsWith ( " Anonymous MIDI device " ) ) {
if ( sep < 0 )
sep = 0 ;
newName = oldName . left ( sep ) ;
} else if ( sep < 0 ) {
newName = oldName + " - " + name ;
} else {
newName = oldName . left ( sep + 3 ) + name ;
}
snd_seq_port_info_set_name ( pinfo , newName . ascii ( ) ) ;
checkAlsaError ( snd_seq_set_port_info ( m_midiHandle , i - > second , pinfo ) ,
" renameDevice " ) ;
for ( unsigned int i = 0 ; i < m_devices . size ( ) ; + + i ) {
if ( m_devices [ i ] - > getId ( ) = = id ) {
m_devices [ i ] - > setName ( newName . ascii ( ) ) ;
break ;
}
}
std : : cerr < < " Renamed " < < m_client < < " : " < < i - > second < < " to " < < name . ascii ( ) < < std : : endl ;
}
ClientPortPair
AlsaDriver : : getPortByName ( std : : string name )
{
for ( unsigned int i = 0 ; i < m_alsaPorts . size ( ) ; + + i ) {
if ( m_alsaPorts [ i ] - > m_name = = name ) {
return ClientPortPair ( m_alsaPorts [ i ] - > m_client ,
m_alsaPorts [ i ] - > m_port ) ;
}
}
return ClientPortPair ( - 1 , - 1 ) ;
}
std : : string
AlsaDriver : : getPortName ( ClientPortPair port )
{
for ( unsigned int i = 0 ; i < m_alsaPorts . size ( ) ; + + i ) {
if ( m_alsaPorts [ i ] - > m_client = = port . first & &
m_alsaPorts [ i ] - > m_port = = port . second ) {
return m_alsaPorts [ i ] - > m_name ;
}
}
return " " ;
}
unsigned int
AlsaDriver : : getConnections ( Device : : DeviceType type ,
MidiDevice : : DeviceDirection direction )
{
if ( type ! = Device : : Midi )
return 0 ;
int count = 0 ;
for ( unsigned int j = 0 ; j < m_alsaPorts . size ( ) ; + + j ) {
if ( ( direction = = MidiDevice : : Play & & m_alsaPorts [ j ] - > isWriteable ( ) ) | |
( direction = = MidiDevice : : Record & & m_alsaPorts [ j ] - > isReadable ( ) ) ) {
+ + count ;
}
}
return count ;
}
TQString
AlsaDriver : : getConnection ( Device : : DeviceType type ,
MidiDevice : : DeviceDirection direction ,
unsigned int connectionNo )
{
if ( type ! = Device : : Midi )
return " " ;
AlsaPortList tempList ;
for ( unsigned int j = 0 ; j < m_alsaPorts . size ( ) ; + + j ) {
if ( ( direction = = MidiDevice : : Play & & m_alsaPorts [ j ] - > isWriteable ( ) ) | |
( direction = = MidiDevice : : Record & & m_alsaPorts [ j ] - > isReadable ( ) ) ) {
tempList . push_back ( m_alsaPorts [ j ] ) ;
}
}
if ( connectionNo < tempList . size ( ) ) {
return tempList [ connectionNo ] - > m_name . c_str ( ) ;
}
return " " ;
}
void
AlsaDriver : : setConnectionToDevice ( MappedDevice & device , TQString connection )
{
ClientPortPair pair ( - 1 , - 1 ) ;
if ( ! connection . isNull ( ) & & connection ! = " " ) {
pair = getPortByName ( connection . ascii ( ) ) ;
}
setConnectionToDevice ( device , connection , pair ) ;
}
void
AlsaDriver : : setConnectionToDevice ( MappedDevice & device , TQString connection ,
const ClientPortPair & pair )
{
TQString prevConnection = device . getConnection ( ) . c_str ( ) ;
device . setConnection ( connection . ascii ( ) ) ;
if ( device . getDirection ( ) = = MidiDevice : : Play ) {
DeviceIntMap : : iterator j = m_outputPorts . find ( device . getId ( ) ) ;
if ( j ! = m_outputPorts . end ( ) ) {
if ( prevConnection ! = " " ) {
ClientPortPair prevPair = getPortByName ( prevConnection . ascii ( ) ) ;
if ( prevPair . first > = 0 & & prevPair . second > = 0 ) {
std : : cerr < < " Disconnecting my port " < < j - > second < < " from " < < prevPair . first < < " : " < < prevPair . second < < " on reconnection " < < std : : endl ;
snd_seq_disconnect_to ( m_midiHandle ,
j - > second ,
prevPair . first ,
prevPair . second ) ;
if ( m_midiSyncAutoConnect ) {
bool foundElsewhere = false ;
for ( MappedDeviceList : : iterator k = m_devices . begin ( ) ;
k ! = m_devices . end ( ) ; + + k ) {
if ( ( * k ) - > getId ( ) ! = device . getId ( ) ) {
if ( ( * k ) - > getConnection ( ) = = prevConnection . ascii ( ) ) {
foundElsewhere = true ;
break ;
}
}
}
if ( ! foundElsewhere ) {
snd_seq_disconnect_to ( m_midiHandle ,
m_syncOutputPort ,
pair . first ,
pair . second ) ;
}
}
}
}
if ( pair . first > = 0 & & pair . second > = 0 ) {
std : : cerr < < " Connecting my port " < < j - > second < < " to " < < pair . first < < " : " < < pair . second < < " on reconnection " < < std : : endl ;
snd_seq_connect_to ( m_midiHandle ,
j - > second ,
pair . first ,
pair . second ) ;
if ( m_midiSyncAutoConnect ) {
snd_seq_connect_to ( m_midiHandle ,
m_syncOutputPort ,
pair . first ,
pair . second ) ;
}
}
}
}
}
void
AlsaDriver : : setConnection ( DeviceId id , TQString connection )
{
Audit audit ;
ClientPortPair port ( getPortByName ( connection . ascii ( ) ) ) ;
if ( port . first ! = - 1 & & port . second ! = - 1 ) {
m_devicePortMap [ id ] = port ;
for ( unsigned int i = 0 ; i < m_devices . size ( ) ; + + i ) {
if ( m_devices [ i ] - > getId ( ) = = id ) {
setConnectionToDevice ( * m_devices [ i ] , connection , port ) ;
MappedEvent * mE =
new MappedEvent ( 0 , MappedEvent : : SystemUpdateInstruments ,
0 , 0 ) ;
insertMappedEventForReturn ( mE ) ;
break ;
}
}
}
}
void
AlsaDriver : : setPlausibleConnection ( DeviceId id , TQString idealConnection )
{
Audit audit ;
ClientPortPair port ( getPortByName ( idealConnection . ascii ( ) ) ) ;
audit < < " AlsaDriver::setPlausibleConnection: connection like "
< < idealConnection . ascii ( ) < < " requested for device " < < id < < std : : endl ;
if ( port . first ! = - 1 & & port . second ! = - 1 ) {
m_devicePortMap [ id ] = port ;
for ( unsigned int i = 0 ; i < m_devices . size ( ) ; + + i ) {
if ( m_devices [ i ] - > getId ( ) = = id ) {
setConnectionToDevice ( * m_devices [ i ] , idealConnection , port ) ;
break ;
}
}
audit < < " AlsaDriver::setPlausibleConnection: exact match available "
< < std : : endl ;
return ;
}
// What we want is a connection that:
//
// * is in the right "class" (the 0-63/64-127/128+ range of client id)
// * has at least some text in common
// * is not yet in use for any device.
//
// To do this, we exploit our privileged position as part of AlsaDriver
// and use our knowledge of how connection strings are made (see
// AlsaDriver::generatePortList above) to pick out the relevant parts
// of the requested string.
int client = - 1 ;
int colon = idealConnection . find ( " : " ) ;
if ( colon > = 0 )
client = idealConnection . left ( colon ) . toInt ( ) ;
int portNo = - 1 ;
if ( client > 0 ) {
TQString remainder = idealConnection . mid ( colon + 1 ) ;
int space = remainder . find ( " " ) ;
if ( space > = 0 )
portNo = remainder . left ( space ) . toInt ( ) ;
}
int firstSpace = idealConnection . find ( " " ) ;
int endOfText = idealConnection . find ( TQRegExp ( " [^ \\ w ] " ) , firstSpace ) ;
TQString text ;
if ( endOfText < 2 ) {
text = idealConnection . mid ( firstSpace + 1 ) ;
} else {
text = idealConnection . mid ( firstSpace + 1 , endOfText - firstSpace - 2 ) ;
}
for ( int testUsed = 1 ; testUsed > = 0 ; - - testUsed ) {
for ( int testNumbers = 1 ; testNumbers > = 0 ; - - testNumbers ) {
for ( int testName = 1 ; testName > = 0 ; - - testName ) {
int fitness =
( testName < < 3 ) +
( testNumbers < < 2 ) +
( testUsed < < 1 ) + 1 ;
for ( unsigned int i = 0 ; i < m_alsaPorts . size ( ) ; + + i ) {
AlsaPortDescription * port = m_alsaPorts [ i ] ;
if ( client > 0 ) {
if ( port - > m_client / 64 ! = client / 64 )
continue ;
if ( testNumbers ) {
// We always check the client class (above).
// But we also prefer to have something in
// common with client or port number, at least
// for ports that aren't used elsewhere
// already. We don't check both because the
// chances are the entire string would already
// have matched if both figures did; instead
// we check the port if it's > 0 (handy for
// e.g. matching the MIDI synth port on a
// multi-port soundcard) and the client
// otherwise.
if ( portNo > 0 ) {
if ( port - > m_port ! = portNo )
continue ;
} else {
if ( port - > m_client ! = client )
continue ;
}
}
}
if ( testName & & text ! = " " & &
! TQString ( port - > m_name . c_str ( ) ) . contains ( text ) )
continue ;
if ( testUsed ) {
bool used = false ;
for ( DevicePortMap : : iterator dpmi = m_devicePortMap . begin ( ) ;
dpmi ! = m_devicePortMap . end ( ) ; + + dpmi ) {
if ( dpmi - > second . first = = port - > m_client & &
dpmi - > second . second = = port - > m_port ) {
used = true ;
break ;
}
}
if ( used )
continue ;
}
// OK, this one will do
audit < < " AlsaDriver::setPlausibleConnection: fuzzy match "
< < port - > m_name < < " available with fitness "
< < fitness < < std : : endl ;
m_devicePortMap [ id ] = ClientPortPair ( port - > m_client , port - > m_port ) ;
for ( unsigned int i = 0 ; i < m_devices . size ( ) ; + + i ) {
if ( m_devices [ i ] - > getId ( ) = = id ) {
setConnectionToDevice ( * m_devices [ i ] ,
port - > m_name . c_str ( ) ,
m_devicePortMap [ id ] ) ;
// in this case we don't request a device resync,
// because this is only invoked at times such as
// file load when the GUI is well aware that the
// whole situation is in upheaval anyway
return ;
}
}
}
}
}
}
audit < < " AlsaDriver::setPlausibleConnection: nothing suitable available "
< < std : : endl ;
}
void
AlsaDriver : : checkTimerSync ( size_t frames )
{
if ( ! m_doTimerChecks )
return ;
# ifdef HAVE_LIBJACK
if ( ! m_jackDriver | | ! m_queueRunning | | frames = = 0 | |
( getMTCStatus ( ) = = TRANSPORT_SLAVE ) ) {
m_firstTimerCheck = true ;
return ;
}
static RealTime startAlsaTime ;
static size_t startJackFrames = 0 ;
static size_t lastJackFrames = 0 ;
size_t nowJackFrames = m_jackDriver - > getFramesProcessed ( ) ;
RealTime nowAlsaTime = getAlsaTime ( ) ;
if ( m_firstTimerCheck | |
( nowJackFrames < = lastJackFrames ) | |
( nowAlsaTime < = startAlsaTime ) ) {
startAlsaTime = nowAlsaTime ;
startJackFrames = nowJackFrames ;
lastJackFrames = nowJackFrames ;
m_firstTimerCheck = false ;
return ;
}
RealTime jackDiff = RealTime : : frame2RealTime
( nowJackFrames - startJackFrames ,
m_jackDriver - > getSampleRate ( ) ) ;
RealTime alsaDiff = nowAlsaTime - startAlsaTime ;
if ( alsaDiff > RealTime ( 10 , 0 ) ) {
# ifdef DEBUG_ALSA
if ( ! m_playing ) {
std : : cout < < " \n ALSA: " < < startAlsaTime < < " \t -> " < < nowAlsaTime < < " \n JACK: " < < startJackFrames < < " \t \t -> " < < nowJackFrames < < std : : endl ;
std : : cout < < " ALSA diff: " < < alsaDiff < < " \n JACK diff: " < < jackDiff < < std : : endl ;
}
# endif
double ratio = ( jackDiff - alsaDiff ) / alsaDiff ;
if ( fabs ( ratio ) > 0.1 ) {
# ifdef DEBUG_ALSA
if ( ! m_playing ) {
std : : cout < < " Ignoring excessive ratio " < < ratio
< < " , hoping for a more likely result next time "
< < std : : endl ;
}
# endif
} else if ( fabs ( ratio ) > 0.000001 ) {
# ifdef DEBUG_ALSA
if ( alsaDiff > RealTime : : zeroTime & & jackDiff > RealTime : : zeroTime ) {
if ( ! m_playing ) {
if ( jackDiff < alsaDiff ) {
std : : cout < < " <<<< ALSA timer is faster by " < < 100.0 * ( ( alsaDiff - jackDiff ) / alsaDiff ) < < " % (1/ " < < int ( 1.0 / ratio ) < < " ) " < < std : : endl ;
} else {
std : : cout < < " >>>> JACK timer is faster by " < < 100.0 * ( ( jackDiff - alsaDiff ) / alsaDiff ) < < " % (1/ " < < int ( 1.0 / ratio ) < < " ) " < < std : : endl ;
}
}
}
# endif
m_timerRatio = ratio ;
m_timerRatioCalculated = true ;
}
m_firstTimerCheck = true ;
}
# endif
}
unsigned int
AlsaDriver : : getTimers ( )
{
return m_timers . size ( ) + 1 ; // one extra for auto
}
TQString
AlsaDriver : : getTimer ( unsigned int n )
{
if ( n = = 0 )
return AUTO_TIMER_NAME ;
else
return m_timers [ n - 1 ] . name . c_str ( ) ;
}
TQString
AlsaDriver : : getCurrentTimer ( )
{
return m_currentTimer . c_str ( ) ;
}
void
AlsaDriver : : setCurrentTimer ( TQString timer )
{
Audit audit ;
if ( timer = = getCurrentTimer ( ) )
return ;
std : : cerr < < " AlsaDriver::setCurrentTimer( " < < timer . ascii ( ) < < " ) " < < std : : endl ;
std : : string name ( timer . ascii ( ) ) ;
if ( name = = AUTO_TIMER_NAME ) {
name = getAutoTimer ( m_doTimerChecks ) ;
} else {
m_doTimerChecks = false ;
}
m_timerRatioCalculated = false ;
// Stop and restart the queue around the timer change. We don't
// call stopClocks/startClocks here because they do the wrong
// thing if we're currently playing and on the JACK transport.
m_queueRunning = false ;
checkAlsaError ( snd_seq_stop_queue ( m_midiHandle , m_queue , NULL ) , " setCurrentTimer(): stopping queue " ) ;
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " setCurrentTimer(): draining output to stop queue " ) ;
snd_seq_event_t event ;
snd_seq_ev_clear ( & event ) ;
snd_seq_real_time_t z = { 0 , 0 } ;
snd_seq_ev_set_queue_pos_real ( & event , m_queue , & z ) ;
snd_seq_ev_set_direct ( & event ) ;
checkAlsaError ( snd_seq_control_queue ( m_midiHandle , m_queue , SND_SEQ_EVENT_SETPOS_TIME ,
0 , & event ) , " setCurrentTimer(): control queue " ) ;
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " setCurrentTimer(): draining output to control queue " ) ;
m_alsaPlayStartTime = RealTime : : zeroTime ;
for ( unsigned int i = 0 ; i < m_timers . size ( ) ; + + i ) {
if ( m_timers [ i ] . name = = name ) {
snd_seq_queue_timer_t * timer ;
snd_timer_id_t * timerid ;
snd_seq_queue_timer_alloca ( & timer ) ;
snd_seq_get_queue_timer ( m_midiHandle , m_queue , timer ) ;
snd_timer_id_alloca ( & timerid ) ;
snd_timer_id_set_class ( timerid , m_timers [ i ] . clas ) ;
snd_timer_id_set_sclass ( timerid , m_timers [ i ] . sclas ) ;
snd_timer_id_set_card ( timerid , m_timers [ i ] . card ) ;
snd_timer_id_set_device ( timerid , m_timers [ i ] . device ) ;
snd_timer_id_set_subdevice ( timerid , m_timers [ i ] . subdevice ) ;
snd_seq_queue_timer_set_id ( timer , timerid ) ;
snd_seq_set_queue_timer ( m_midiHandle , m_queue , timer ) ;
if ( m_doTimerChecks ) {
audit < < " Current timer set to \" " < < name < < " \" with timer checks "
< < std : : endl ;
} else {
audit < < " Current timer set to \" " < < name < < " \" "
< < std : : endl ;
}
if ( m_timers [ i ] . clas = = SND_TIMER_CLASS_GLOBAL & &
m_timers [ i ] . device = = SND_TIMER_GLOBAL_SYSTEM ) {
long hz = 1000000000 / m_timers [ i ] . resolution ;
if ( hz < 900 ) {
audit < < " WARNING: using system timer with only "
< < hz < < " Hz resolution! " < < std : : endl ;
}
}
break ;
}
}
# ifdef HAVE_LIBJACK
if ( m_jackDriver )
m_jackDriver - > prebufferAudio ( ) ;
# endif
checkAlsaError ( snd_seq_continue_queue ( m_midiHandle , m_queue , NULL ) , " checkAlsaError(): continue queue " ) ;
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " setCurrentTimer(): draining output to continue queue " ) ;
m_queueRunning = true ;
m_firstTimerCheck = true ;
}
bool
AlsaDriver : : initialise ( )
{
bool result = true ;
initialiseAudio ( ) ;
result = initialiseMidi ( ) ;
return result ;
}
// Set up queue, client and port
//
bool
AlsaDriver : : initialiseMidi ( )
{
Audit audit ;
// Create a non-blocking handle.
//
if ( snd_seq_open ( & m_midiHandle ,
" default " ,
SND_SEQ_OPEN_DUPLEX ,
SND_SEQ_NONBLOCK ) < 0 ) {
audit < < " AlsaDriver::initialiseMidi - "
< < " couldn't open sequencer - " < < snd_strerror ( errno )
< < " - perhaps you need to modprobe snd-seq-midi. "
< < std : : endl ;
reportFailure ( MappedEvent : : FailureALSACallFailed ) ;
return false ;
}
snd_seq_set_client_name ( m_midiHandle , " rosegarden " ) ;
if ( ( m_client = snd_seq_client_id ( m_midiHandle ) ) < 0 ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::initialiseMidi - can't create client "
< < std : : endl ;
# endif
return false ;
}
// Create a queue
//
if ( ( m_queue = snd_seq_alloc_named_queue ( m_midiHandle ,
" Rosegarden queue " ) ) < 0 ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::initialiseMidi - can't allocate queue "
< < std : : endl ;
# endif
return false ;
}
// Create the input port
//
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_APPLICATION ) ;
snd_seq_port_info_set_midi_channels ( pinfo , 16 ) ;
/* we want to know when the events got delivered to us */
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 , m_queue ) ;
snd_seq_port_info_set_name ( pinfo , " record in " ) ;
if ( checkAlsaError ( snd_seq_create_port ( m_midiHandle , pinfo ) ,
" initialiseMidi - can't create input port " ) < 0 )
return false ;
m_inputPort = snd_seq_port_info_get_port ( pinfo ) ;
// Subscribe the input port to the ALSA Announce port
// to receive notifications when clients, ports and subscriptions change
snd_seq_connect_from ( m_midiHandle , m_inputPort ,
SND_SEQ_CLIENT_SYSTEM , SND_SEQ_PORT_SYSTEM_ANNOUNCE ) ;
m_midiInputPortConnected = true ;
// Set the input queue size
//
if ( snd_seq_set_client_pool_output ( m_midiHandle , 2000 ) < 0 | |
snd_seq_set_client_pool_input ( m_midiHandle , 2000 ) < 0 | |
snd_seq_set_client_pool_output_room ( m_midiHandle , 2000 ) < 0 ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::initialiseMidi - "
< < " can't modify pool parameters "
< < std : : endl ;
# endif
return false ;
}
// Create sync output now as well
m_syncOutputPort = checkAlsaError ( snd_seq_create_simple_port
( m_midiHandle ,
" sync out " ,
SND_SEQ_PORT_CAP_READ |
SND_SEQ_PORT_CAP_SUBS_READ ,
SND_SEQ_PORT_TYPE_APPLICATION ) ,
" initialiseMidi - can't create sync output port " ) ;
// and port for hardware controller
m_controllerPort = checkAlsaError ( snd_seq_create_simple_port
( m_midiHandle ,
" external controller " ,
SND_SEQ_PORT_CAP_READ |
SND_SEQ_PORT_CAP_WRITE |
SND_SEQ_PORT_CAP_SUBS_READ |
SND_SEQ_PORT_CAP_SUBS_WRITE ,
SND_SEQ_PORT_TYPE_APPLICATION ) ,
" initialiseMidi - can't create controller port " ) ;
getSystemInfo ( ) ;
generatePortList ( ) ;
generateInstruments ( ) ;
// Modify status with MIDI success
//
m_driverStatus | = MIDI_OK ;
generateTimerList ( ) ;
setCurrentTimer ( AUTO_TIMER_NAME ) ;
// Start the timer
if ( checkAlsaError ( snd_seq_start_queue ( m_midiHandle , m_queue , NULL ) ,
" initialiseMidi(): couldn't start queue " ) < 0 ) {
reportFailure ( MappedEvent : : FailureALSACallFailed ) ;
return false ;
}
m_queueRunning = true ;
// process anything pending
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " initialiseMidi(): couldn't drain output " ) ;
audit < < " AlsaDriver::initialiseMidi - initialised MIDI subsystem "
< < std : : endl < < std : : endl ;
return true ;
}
// We don't even attempt to use ALSA audio. We just use JACK instead.
// See comment at the top of this file and jackProcess() for further
// information on how we use this.
//
void
AlsaDriver : : initialiseAudio ( )
{
# ifdef HAVE_LIBJACK
m_jackDriver = new JackDriver ( this ) ;
if ( m_jackDriver - > isOK ( ) ) {
m_driverStatus | = AUDIO_OK ;
} else {
delete m_jackDriver ;
m_jackDriver = 0 ;
}
# endif
}
void
AlsaDriver : : initialisePlayback ( const RealTime & position )
{
# ifdef DEBUG_ALSA
std : : cerr < < " \n \n AlsaDriver - initialisePlayback " < < std : : endl ;
# endif
// now that we restart the queue at each play, the origin is always zero
m_alsaPlayStartTime = RealTime : : zeroTime ;
m_playStartPosition = position ;
m_startPlayback = true ;
m_mtcFirstTime = - 1 ;
m_mtcSigmaE = 0 ;
m_mtcSigmaC = 0 ;
if ( getMMCStatus ( ) = = TRANSPORT_MASTER ) {
sendMMC ( 127 , MIDI_MMC_PLAY , true , " " ) ;
m_eat_mtc = 0 ;
}
if ( getMTCStatus ( ) = = TRANSPORT_MASTER ) {
insertMTCFullFrame ( position ) ;
}
// If MIDI Sync is enabled then adjust for the MIDI Clock to
// synchronise the sequencer with the clock.
//
if ( getMIDISyncStatus ( ) = = TRANSPORT_MASTER ) {
// Send the Song Position Pointer for MIDI CLOCK positioning
//
// Get time from current alsa time to start of alsa timing -
// add the initial starting point and divide by the MIDI Beat
// length. The SPP is is the MIDI Beat upon which to start the song.
// Songs are always assumed to start on a MIDI Beat of 0. Each MIDI
// Beat spans 6 MIDI Clocks. In other words, each MIDI Beat is a 16th
// note (since there are 24 MIDI Clocks in a quarter note).
//
long spp =
long ( ( ( getAlsaTime ( ) - m_alsaPlayStartTime + m_playStartPosition ) /
m_midiClockInterval ) / 6.0 ) ;
// Ok now we have the new SPP - stop the transport and restart with the
// new value.
//
sendSystemDirect ( SND_SEQ_EVENT_STOP , NULL ) ;
signed int args = spp ;
sendSystemDirect ( SND_SEQ_EVENT_SONGPOS , & args ) ;
// Now send the START/CONTINUE
//
if ( m_playStartPosition = = RealTime : : zeroTime )
sendSystemQueued ( SND_SEQ_EVENT_START , " " ,
m_alsaPlayStartTime ) ;
else
sendSystemQueued ( SND_SEQ_EVENT_CONTINUE , " " ,
m_alsaPlayStartTime ) ;
}
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
m_needJackStart = NeedJackStart ;
}
# endif
}
void
AlsaDriver : : stopPlayback ( )
{
# ifdef DEBUG_ALSA
std : : cerr < < " \n \n AlsaDriver - stopPlayback " < < std : : endl ;
# endif
if ( getMIDISyncStatus ( ) = = TRANSPORT_MASTER ) {
sendSystemDirect ( SND_SEQ_EVENT_STOP , NULL ) ;
}
if ( getMMCStatus ( ) = = TRANSPORT_MASTER ) {
sendMMC ( 127 , MIDI_MMC_STOP , true , " " ) ;
//<VN> need to throw away the next MTC event
m_eat_mtc = 3 ;
}
allNotesOff ( ) ;
m_playing = false ;
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
m_jackDriver - > stopTransport ( ) ;
m_needJackStart = NeedNoJackStart ;
}
# endif
// Flush the output and input queues
//
snd_seq_remove_events_t * info ;
snd_seq_remove_events_alloca ( & info ) ;
snd_seq_remove_events_set_condition ( info , SND_SEQ_REMOVE_INPUT |
SND_SEQ_REMOVE_OUTPUT ) ;
snd_seq_remove_events ( m_midiHandle , info ) ;
// send sounds-off to all play devices
//
for ( MappedDeviceList : : iterator i = m_devices . begin ( ) ; i ! = m_devices . end ( ) ; + + i ) {
if ( ( * i ) - > getDirection ( ) = = MidiDevice : : Play ) {
sendDeviceController ( ( * i ) - > getId ( ) ,
MIDI_CONTROLLER_SUSTAIN , 0 ) ;
sendDeviceController ( ( * i ) - > getId ( ) ,
MIDI_CONTROLLER_ALL_NOTES_OFF , 0 ) ;
}
}
punchOut ( ) ;
stopClocks ( ) ; // Resets ALSA timer to zero
clearAudioQueue ( ) ;
startClocksApproved ( ) ; // restarts ALSA timer without starting JACK transport
}
void
AlsaDriver : : punchOut ( )
{
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::punchOut " < < std : : endl ;
# endif
# ifdef HAVE_LIBJACK
// Close any recording file
if ( m_recordStatus = = RECORD_ON ) {
for ( InstrumentSet : : const_iterator i = m_recordingInstruments . begin ( ) ;
i ! = m_recordingInstruments . end ( ) ; + + i ) {
InstrumentId id = * i ;
if ( id > = AudioInstrumentBase & &
id < MidiInstrumentBase ) {
AudioFileId auid = 0 ;
if ( m_jackDriver & & m_jackDriver - > closeRecordFile ( id , auid ) ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::stopPlayback: sending back to GUI for instrument " < < id < < std : : endl ;
# endif
// Create event to return to gui to say that we've
// completed an audio file and we can generate a
// preview for it now.
//
// nasty hack -- don't have right audio id here, and
// the sequencer will wipe out the instrument id and
// replace it with currently-selected one in gui --
// so use audio id slot to pass back instrument id
// and handle accordingly in gui
try {
MappedEvent * mE =
new MappedEvent ( id ,
MappedEvent : : AudioGeneratePreview ,
id % 256 ,
id / 256 ) ;
// send completion event
insertMappedEventForReturn ( mE ) ;
} catch ( . . . ) {
;
}
}
}
}
}
# endif
// Change recorded state if any set
//
if ( m_recordStatus = = RECORD_ON )
m_recordStatus = RECORD_OFF ;
m_recordingInstruments . clear ( ) ;
}
void
AlsaDriver : : resetPlayback ( const RealTime & oldPosition , const RealTime & position )
{
# ifdef DEBUG_ALSA
std : : cerr < < " \n \n AlsaDriver - resetPlayback( " < < oldPosition < < " , " < < position < < " ) " < < std : : endl ;
# endif
if ( getMMCStatus ( ) = = TRANSPORT_MASTER ) {
unsigned char t_sec = ( unsigned char ) position . sec % 60 ;
unsigned char t_min = ( unsigned char ) ( position . sec / 60 ) % 60 ;
unsigned char t_hrs = ( unsigned char ) ( position . sec / 3600 ) ;
# define STUPID_BROKEN_EQUIPMENT
# ifdef STUPID_BROKEN_EQUIPMENT
// Some recorders assume you are talking in 30fps...
unsigned char t_frm = ( unsigned char ) ( position . nsec / 33333333U ) ;
unsigned char t_sbf = ( unsigned char ) ( ( position . nsec / 333333U ) % 100U ) ;
# else
// We always send at 25fps, it's the easiest to avoid rounding problems
unsigned char t_frm = ( unsigned char ) ( position . nsec / 40000000U ) ;
unsigned char t_sbf = ( unsigned char ) ( ( position . nsec / 400000U ) % 100U ) ;
# endif
std : : cerr < < " \n Jump using MMC LOCATE to " < < position < < std : : endl ;
std : : cerr < < " \t which is " < < int ( t_hrs ) < < " : " < < int ( t_min ) < < " : " < < int ( t_sec ) < < " . " < < int ( t_frm ) < < " . " < < int ( t_sbf ) < < std : : endl ;
unsigned char locateDataArr [ 7 ] = {
0x06 ,
0x01 ,
( unsigned char ) ( 0x60 + t_hrs ) , // (30fps flag) + hh
t_min , // mm
t_sec , // ss
t_frm , // frames
t_sbf // subframes
} ;
sendMMC ( 127 , MIDI_MMC_LOCATE , true , std : : string ( ( const char * ) locateDataArr , 7 ) ) ;
}
RealTime formerStartPosition = m_playStartPosition ;
m_playStartPosition = position ;
m_alsaPlayStartTime = getAlsaTime ( ) ;
// Reset note offs to correct positions
//
RealTime jump = position - oldPosition ;
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " Currently " < < m_noteOffQueue . size ( ) < < " in note off queue " < < std : : endl ;
# endif
// modify the note offs that exist as they're relative to the
// playStartPosition terms.
//
for ( NoteOffQueue : : iterator i = m_noteOffQueue . begin ( ) ;
i ! = m_noteOffQueue . end ( ) ; + + i ) {
// if we're fast forwarding then we bring the note off closer
if ( jump > = RealTime : : zeroTime ) {
RealTime endTime = formerStartPosition + ( * i ) - > getRealTime ( ) ;
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " Forward jump of " < < jump < < " : adjusting note off from "
< < ( * i ) - > getRealTime ( ) < < " (absolute " < < endTime
< < " ) to " ;
# endif
( * i ) - > setRealTime ( endTime - position ) ;
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < ( * i ) - > getRealTime ( ) < < std : : endl ;
# endif
} else // we're rewinding - kill the note immediately
{
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " Rewind by " < < jump < < " : setting note off to zero " < < std : : endl ;
# endif
( * i ) - > setRealTime ( RealTime : : zeroTime ) ;
}
}
pushRecentNoteOffs ( ) ;
processNotesOff ( getAlsaTime ( ) , true ) ;
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " resetPlayback(): draining " ) ;
// Ensure we clear down output queue on reset - in the case of
// MIDI clock where we might have a long queue of events already
// posted.
//
snd_seq_remove_events_t * info ;
snd_seq_remove_events_alloca ( & info ) ;
snd_seq_remove_events_set_condition ( info , SND_SEQ_REMOVE_OUTPUT ) ;
snd_seq_remove_events ( m_midiHandle , info ) ;
if ( getMTCStatus ( ) = = TRANSPORT_MASTER ) {
m_mtcFirstTime = - 1 ;
m_mtcSigmaE = 0 ;
m_mtcSigmaC = 0 ;
insertMTCFullFrame ( position ) ;
}
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
m_jackDriver - > clearSynthPluginEvents ( ) ;
m_needJackStart = NeedJackReposition ;
}
# endif
}
void
AlsaDriver : : setMIDIClockInterval ( RealTime interval )
{
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::setMIDIClockInterval( " < < interval < < " ) " < < endl ;
# endif
// Reset the value
//
SoundDriver : : setMIDIClockInterval ( interval ) ;
// Return if the clock isn't enabled
//
if ( ! m_midiClockEnabled )
return ;
if ( false ) // don't remove any events quite yet
{
// Remove all queued events (although we should filter this
// down to just the clock events.
//
snd_seq_remove_events_t * info ;
snd_seq_remove_events_alloca ( & info ) ;
//if (snd_seq_type_check(SND_SEQ_EVENT_CLOCK, SND_SEQ_EVFLG_CONTROL))
//snd_seq_remove_events_set_event_type(info,
snd_seq_remove_events_set_condition ( info , SND_SEQ_REMOVE_OUTPUT ) ;
snd_seq_remove_events_set_event_type ( info , SND_SEQ_EVFLG_CONTROL ) ;
std : : cout < < " AlsaDriver::setMIDIClockInterval - "
< < " MIDI CLOCK TYPE IS CONTROL " < < std : : endl ;
snd_seq_remove_events ( m_midiHandle , info ) ;
}
}
void
AlsaDriver : : pushRecentNoteOffs ( )
{
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " AlsaDriver::pushRecentNoteOffs: have " < < m_recentNoteOffs . size ( ) < < " in queue " < < std : : endl ;
# endif
for ( NoteOffQueue : : iterator i = m_recentNoteOffs . begin ( ) ;
i ! = m_recentNoteOffs . end ( ) ; + + i ) {
( * i ) - > setRealTime ( RealTime : : zeroTime ) ;
m_noteOffQueue . insert ( * i ) ;
}
m_recentNoteOffs . clear ( ) ;
}
void
AlsaDriver : : cropRecentNoteOffs ( const RealTime & t )
{
while ( ! m_recentNoteOffs . empty ( ) ) {
NoteOffEvent * ev = * m_recentNoteOffs . begin ( ) ;
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " AlsaDriver::cropRecentNoteOffs: " < < ev - > getRealTime ( ) < < " vs " < < t < < std : : endl ;
# endif
if ( ev - > getRealTime ( ) > = t ) break ;
delete ev ;
m_recentNoteOffs . erase ( m_recentNoteOffs . begin ( ) ) ;
}
}
void
AlsaDriver : : weedRecentNoteOffs ( unsigned int pitch , MidiByte channel ,
InstrumentId instrument )
{
for ( NoteOffQueue : : iterator i = m_recentNoteOffs . begin ( ) ;
i ! = m_recentNoteOffs . end ( ) ; + + i ) {
if ( ( * i ) - > getPitch ( ) = = pitch & &
( * i ) - > getChannel ( ) = = channel & &
( * i ) - > getInstrument ( ) = = instrument ) {
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " AlsaDriver::weedRecentNoteOffs: deleting one " < < std : : endl ;
# endif
delete * i ;
m_recentNoteOffs . erase ( i ) ;
break ;
}
}
}
void
AlsaDriver : : allNotesOff ( )
{
snd_seq_event_t event ;
ClientPortPair outputDevice ;
RealTime offTime ;
// drop any pending notes
snd_seq_drop_output_buffer ( m_midiHandle ) ;
snd_seq_drop_output ( m_midiHandle ) ;
// prepare the event
snd_seq_ev_clear ( & event ) ;
offTime = getAlsaTime ( ) ;
for ( NoteOffQueue : : iterator it = m_noteOffQueue . begin ( ) ;
it ! = m_noteOffQueue . end ( ) ; + + it ) {
// Set destination according to connection for instrument
//
outputDevice = getPairForMappedInstrument ( ( * it ) - > getInstrument ( ) ) ;
if ( outputDevice . first < 0 | | outputDevice . second < 0 )
continue ;
snd_seq_ev_set_subs ( & event ) ;
// Set source according to port for device
//
int src = getOutputPortForMappedInstrument ( ( * it ) - > getInstrument ( ) ) ;
if ( src < 0 )
continue ;
snd_seq_ev_set_source ( & event , src ) ;
snd_seq_ev_set_noteoff ( & event ,
( * it ) - > getChannel ( ) ,
( * it ) - > getPitch ( ) ,
127 ) ;
//snd_seq_event_output(m_midiHandle, &event);
int error = snd_seq_event_output_direct ( m_midiHandle , & event ) ;
if ( error < 0 ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::allNotesOff - "
< < " can't send event " < < std : : endl ;
# endif
}
delete ( * it ) ;
}
m_noteOffQueue . erase ( m_noteOffQueue . begin ( ) , m_noteOffQueue . end ( ) ) ;
/*
std : : cerr < < " AlsaDriver::allNotesOff - "
< < " queue size = " < < m_noteOffQueue . size ( ) < < std : : endl ;
*/
// flush
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " allNotesOff(): draining " ) ;
}
void
AlsaDriver : : processNotesOff ( const RealTime & time , bool now , bool everything )
{
if ( m_noteOffQueue . empty ( ) ) {
return ;
}
snd_seq_event_t event ;
ClientPortPair outputDevice ;
RealTime offTime ;
// prepare the event
snd_seq_ev_clear ( & event ) ;
RealTime alsaTime = getAlsaTime ( ) ;
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " AlsaDriver::processNotesOff( " < < time < < " ): alsaTime = " < < alsaTime < < " , now = " < < now < < std : : endl ;
# endif
while ( m_noteOffQueue . begin ( ) ! = m_noteOffQueue . end ( ) ) {
NoteOffEvent * ev = * m_noteOffQueue . begin ( ) ;
if ( ev - > getRealTime ( ) > time ) {
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " Note off time " < < ev - > getRealTime ( ) < < " is beyond current time " < < time < < std : : endl ;
# endif
if ( ! everything ) break ;
}
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " AlsaDriver::processNotesOff( " < < time < < " ): found event at " < < ev - > getRealTime ( ) < < " , instr " < < ev - > getInstrument ( ) < < " , channel " < < int ( ev - > getChannel ( ) ) < < " , pitch " < < int ( ev - > getPitch ( ) ) < < std : : endl ;
# endif
bool isSoftSynth = ( ev - > getInstrument ( ) > = SoftSynthInstrumentBase ) ;
offTime = ev - > getRealTime ( ) ;
if ( offTime < RealTime : : zeroTime ) offTime = RealTime : : zeroTime ;
bool scheduled = ( offTime > alsaTime ) & & ! now ;
if ( ! scheduled ) offTime = RealTime : : zeroTime ;
snd_seq_real_time_t alsaOffTime = { ( unsigned int ) offTime . sec ,
( unsigned int ) offTime . nsec } ;
snd_seq_ev_set_noteoff ( & event ,
ev - > getChannel ( ) ,
ev - > getPitch ( ) ,
127 ) ;
if ( ! isSoftSynth ) {
snd_seq_ev_set_subs ( & event ) ;
// Set source according to instrument
//
int src = getOutputPortForMappedInstrument ( ev - > getInstrument ( ) ) ;
if ( src < 0 ) {
std : : cerr < < " note off has no output port (instr = " < < ev - > getInstrument ( ) < < " ) " < < std : : endl ;
delete ev ;
m_noteOffQueue . erase ( m_noteOffQueue . begin ( ) ) ;
continue ;
}
snd_seq_ev_set_source ( & event , src ) ;
snd_seq_ev_set_subs ( & event ) ;
snd_seq_ev_schedule_real ( & event , m_queue , 0 , & alsaOffTime ) ;
if ( scheduled ) {
snd_seq_event_output ( m_midiHandle , & event ) ;
} else {
snd_seq_event_output_direct ( m_midiHandle , & event ) ;
}
} else {
event . time . time = alsaOffTime ;
processSoftSynthEventOut ( ev - > getInstrument ( ) , & event , now ) ;
}
if ( ! now ) {
m_recentNoteOffs . insert ( ev ) ;
} else {
delete ev ;
}
m_noteOffQueue . erase ( m_noteOffQueue . begin ( ) ) ;
}
// We don't flush the queue here, as this is called nested from
// processMidiOut, which does the flushing
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " AlsaDriver::processNotesOff - "
< < " queue size now: " < < m_noteOffQueue . size ( ) < < std : : endl ;
# endif
}
// Get the queue time and convert it to RealTime for the gui
// to use.
//
RealTime
AlsaDriver : : getSequencerTime ( )
{
RealTime t ( 0 , 0 ) ;
t = getAlsaTime ( ) + m_playStartPosition - m_alsaPlayStartTime ;
// std::cerr << "AlsaDriver::getSequencerTime: alsa time is "
// << getAlsaTime() << ", start time is " << m_alsaPlayStartTime << ", play start position is " << m_playStartPosition << endl;
return t ;
}
// Gets the time of the ALSA queue
//
RealTime
AlsaDriver : : getAlsaTime ( )
{
RealTime sequencerTime ( 0 , 0 ) ;
snd_seq_queue_status_t * status ;
snd_seq_queue_status_alloca ( & status ) ;
if ( snd_seq_get_queue_status ( m_midiHandle , m_queue , status ) < 0 ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::getAlsaTime - can't get queue status "
< < std : : endl ;
# endif
return sequencerTime ;
}
sequencerTime . sec = snd_seq_queue_status_get_real_time ( status ) - > tv_sec ;
sequencerTime . nsec = snd_seq_queue_status_get_real_time ( status ) - > tv_nsec ;
// std::cerr << "AlsaDriver::getAlsaTime: alsa time is " << sequencerTime << std::endl;
return sequencerTime ;
}
// Get all pending input events and turn them into a MappedComposition.
//
//
MappedComposition *
AlsaDriver : : getMappedComposition ( )
{
m_recordComposition . clear ( ) ;
while ( _failureReportReadIndex ! = _failureReportWriteIndex ) {
MappedEvent : : FailureCode code = _failureReports [ _failureReportReadIndex ] ;
// std::cerr << "AlsaDriver::reportFailure(" << code << ")" << std::endl;
MappedEvent * mE = new MappedEvent
( 0 , MappedEvent : : SystemFailure , code , 0 ) ;
m_returnComposition . insert ( mE ) ;
_failureReportReadIndex =
( _failureReportReadIndex + 1 ) % FAILURE_REPORT_COUNT ;
}
if ( ! m_returnComposition . empty ( ) ) {
for ( MappedComposition : : iterator i = m_returnComposition . begin ( ) ;
i ! = m_returnComposition . end ( ) ; + + i ) {
m_recordComposition . insert ( new MappedEvent ( * * i ) ) ;
}
m_returnComposition . clear ( ) ;
}
// If the input port hasn't connected we shouldn't poll it
//
if ( m_midiInputPortConnected = = false ) {
return & m_recordComposition ;
}
RealTime eventTime ( 0 , 0 ) ;
snd_seq_event_t * event ;
while ( snd_seq_event_input ( m_midiHandle , & event ) > 0 ) {
unsigned int channel = ( unsigned int ) event - > data . note . channel ;
unsigned int chanNoteKey = ( channel < < 8 ) +
( unsigned int ) event - > data . note . note ;
bool fromController = false ;
if ( event - > dest . client = = m_client & &
event - > dest . port = = m_controllerPort ) {
# ifdef DEBUG_ALSA
std : : cerr < < " Received an external controller event " < < std : : endl ;
# endif
fromController = true ;
}
unsigned int deviceId = Device : : NO_DEVICE ;
if ( fromController ) {
deviceId = Device : : CONTROL_DEVICE ;
} else {
for ( MappedDeviceList : : iterator i = m_devices . begin ( ) ;
i ! = m_devices . end ( ) ; + + i ) {
ClientPortPair pair ( m_devicePortMap [ ( * i ) - > getId ( ) ] ) ;
if ( ( ( * i ) - > getDirection ( ) = = MidiDevice : : Record ) & &
( pair . first = = event - > source . client ) & &
( pair . second = = event - > source . port ) ) {
deviceId = ( * i ) - > getId ( ) ;
break ;
}
}
}
eventTime . sec = event - > time . time . tv_sec ;
eventTime . nsec = event - > time . time . tv_nsec ;
eventTime = eventTime - m_alsaRecordStartTime + m_playStartPosition ;
# ifdef DEBUG_ALSA
if ( ! fromController ) {
std : : cerr < < " Received normal event: type " < < int ( event - > type ) < < " , chan " < < channel < < " , note " < < int ( event - > data . note . note ) < < " , time " < < eventTime < < std : : endl ;
}
# endif
switch ( event - > type ) {
case SND_SEQ_EVENT_NOTE :
case SND_SEQ_EVENT_NOTEON :
if ( fromController )
continue ;
if ( event - > data . note . velocity > 0 ) {
MappedEvent * mE = new MappedEvent ( ) ;
mE - > setPitch ( event - > data . note . note ) ;
mE - > setVelocity ( event - > data . note . velocity ) ;
mE - > setEventTime ( eventTime ) ;
mE - > setRecordedChannel ( channel ) ;
mE - > setRecordedDevice ( deviceId ) ;
// Negative duration - we need to hear the NOTE ON
// so we must insert it now with a negative duration
// and pick and mix against the following NOTE OFF
// when we create the recorded segment.
//
mE - > setDuration ( RealTime ( - 1 , 0 ) ) ;
// Create a copy of this when we insert the NOTE ON -
// keeping a copy alive on the m_noteOnMap.
//
// We shake out the two NOTE Ons after we've recorded
// them.
//
m_recordComposition . insert ( new MappedEvent ( mE ) ) ;
m_noteOnMap [ deviceId ] [ chanNoteKey ] = mE ;
break ;
}
case SND_SEQ_EVENT_NOTEOFF :
if ( fromController )
continue ;
if ( m_noteOnMap [ deviceId ] [ chanNoteKey ] ! = 0 ) {
// Set duration correctly on the NOTE OFF
//
MappedEvent * mE = m_noteOnMap [ deviceId ] [ chanNoteKey ] ;
RealTime duration = eventTime - mE - > getEventTime ( ) ;
# ifdef DEBUG_ALSA
std : : cerr < < " NOTE OFF: found NOTE ON at " < < mE - > getEventTime ( ) < < std : : endl ;
# endif
if ( duration < RealTime : : zeroTime ) {
duration = RealTime : : zeroTime ;
mE - > setEventTime ( eventTime ) ;
}
// Velocity 0 - NOTE OFF. Set duration correctly
// for recovery later.
//
mE - > setVelocity ( 0 ) ;
mE - > setDuration ( duration ) ;
// force shut off of note
m_recordComposition . insert ( mE ) ;
// reset the reference
//
m_noteOnMap [ deviceId ] [ chanNoteKey ] = 0 ;
}
break ;
case SND_SEQ_EVENT_KEYPRESS : {
if ( fromController )
continue ;
// Fix for 632964 by Pedro Lopez-Cabanillas (20030523)
//
MappedEvent * mE = new MappedEvent ( ) ;
mE - > setType ( MappedEvent : : MidiKeyPressure ) ;
mE - > setEventTime ( eventTime ) ;
mE - > setData1 ( event - > data . note . note ) ;
mE - > setData2 ( event - > data . note . velocity ) ;
mE - > setRecordedChannel ( channel ) ;
mE - > setRecordedDevice ( deviceId ) ;
m_recordComposition . insert ( mE ) ;
}
break ;
case SND_SEQ_EVENT_CONTROLLER : {
MappedEvent * mE = new MappedEvent ( ) ;
mE - > setType ( MappedEvent : : MidiController ) ;
mE - > setEventTime ( eventTime ) ;
mE - > setData1 ( event - > data . control . param ) ;
mE - > setData2 ( event - > data . control . value ) ;
mE - > setRecordedChannel ( channel ) ;
mE - > setRecordedDevice ( deviceId ) ;
m_recordComposition . insert ( mE ) ;
}
break ;
case SND_SEQ_EVENT_PGMCHANGE : {
MappedEvent * mE = new MappedEvent ( ) ;
mE - > setType ( MappedEvent : : MidiProgramChange ) ;
mE - > setEventTime ( eventTime ) ;
mE - > setData1 ( event - > data . control . value ) ;
mE - > setRecordedChannel ( channel ) ;
mE - > setRecordedDevice ( deviceId ) ;
m_recordComposition . insert ( mE ) ;
}
break ;
case SND_SEQ_EVENT_PITCHBEND : {
if ( fromController )
continue ;
// Fix for 711889 by Pedro Lopez-Cabanillas (20030523)
//
int s = event - > data . control . value + 8192 ;
int d1 = ( s > > 7 ) & 0x7f ; // data1 = MSB
int d2 = s & 0x7f ; // data2 = LSB
MappedEvent * mE = new MappedEvent ( ) ;
mE - > setType ( MappedEvent : : MidiPitchBend ) ;
mE - > setEventTime ( eventTime ) ;
mE - > setData1 ( d1 ) ;
mE - > setData2 ( d2 ) ;
mE - > setRecordedChannel ( channel ) ;
mE - > setRecordedDevice ( deviceId ) ;
m_recordComposition . insert ( mE ) ;
}
break ;
case SND_SEQ_EVENT_CHANPRESS : {
if ( fromController )
continue ;
// Fixed by Pedro Lopez-Cabanillas (20030523)
//
int s = event - > data . control . value & 0x7f ;
MappedEvent * mE = new MappedEvent ( ) ;
mE - > setType ( MappedEvent : : MidiChannelPressure ) ;
mE - > setEventTime ( eventTime ) ;
mE - > setData1 ( s ) ;
mE - > setRecordedChannel ( channel ) ;
mE - > setRecordedDevice ( deviceId ) ;
m_recordComposition . insert ( mE ) ;
}
break ;
case SND_SEQ_EVENT_SYSEX :
if ( fromController )
continue ;
if ( ! testForMTCSysex ( event ) & &
! testForMMCSysex ( event ) ) {
// Bundle up the data into a block on the MappedEvent
//
std : : string data ;
char * ptr = ( char * ) ( event - > data . ext . ptr ) ;
for ( unsigned int i = 0 ; i < event - > data . ext . len ; + + i )
data + = * ( ptr + + ) ;
# ifdef DEBUG_ALSA
if ( ( MidiByte ) ( data [ 1 ] ) = = MIDI_SYSEX_RT ) {
std : : cerr < < " REALTIME SYSEX " < < endl ;
for ( unsigned int ii = 0 ; ii < event - > data . ext . len ; + + ii ) {
printf ( " B %d = %02x \n " , ii , ( ( char * ) ( event - > data . ext . ptr ) ) [ ii ] ) ;
}
} else {
std : : cerr < < " NON-REALTIME SYSEX " < < endl ;
for ( unsigned int ii = 0 ; ii < event - > data . ext . len ; + + ii ) {
printf ( " B %d = %02x \n " , ii , ( ( char * ) ( event - > data . ext . ptr ) ) [ ii ] ) ;
}
}
# endif
MappedEvent * mE = new MappedEvent ( ) ;
mE - > setType ( MappedEvent : : MidiSystemMessage ) ;
mE - > setData1 ( MIDI_SYSTEM_EXCLUSIVE ) ;
mE - > setRecordedDevice ( deviceId ) ;
// chop off SYX and EOX bytes from data block
// Fix for 674731 by Pedro Lopez-Cabanillas (20030601)
DataBlockRepository : : setDataBlockForEvent ( mE , data . substr ( 1 , data . length ( ) - 2 ) ) ;
mE - > setEventTime ( eventTime ) ;
m_recordComposition . insert ( mE ) ;
}
break ;
case SND_SEQ_EVENT_SENSING : // MIDI device is still there
break ;
case SND_SEQ_EVENT_QFRAME :
if ( fromController )
continue ;
if ( getMTCStatus ( ) = = TRANSPORT_SLAVE ) {
handleMTCTQFrame ( event - > data . control . value , eventTime ) ;
}
break ;
case SND_SEQ_EVENT_CLOCK :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::getMappedComposition - "
< < " got realtime MIDI clock " < < std : : endl ;
# endif
break ;
case SND_SEQ_EVENT_START :
if ( ( getMIDISyncStatus ( ) = = TRANSPORT_SLAVE ) & & ! isPlaying ( ) ) {
ExternalTransport * transport = getExternalTransportControl ( ) ;
if ( transport ) {
transport - > transportJump ( ExternalTransport : : TransportStopAtTime ,
RealTime : : zeroTime ) ;
transport - > transportChange ( ExternalTransport : : TransportStart ) ;
}
}
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::getMappedComposition - "
< < " START " < < std : : endl ;
# endif
break ;
case SND_SEQ_EVENT_CONTINUE :
if ( ( getMIDISyncStatus ( ) = = TRANSPORT_SLAVE ) & & ! isPlaying ( ) ) {
ExternalTransport * transport = getExternalTransportControl ( ) ;
if ( transport ) {
transport - > transportChange ( ExternalTransport : : TransportPlay ) ;
}
}
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::getMappedComposition - "
< < " CONTINUE " < < std : : endl ;
# endif
break ;
case SND_SEQ_EVENT_STOP :
if ( ( getMIDISyncStatus ( ) = = TRANSPORT_SLAVE ) & & isPlaying ( ) ) {
ExternalTransport * transport = getExternalTransportControl ( ) ;
if ( transport ) {
transport - > transportChange ( ExternalTransport : : TransportStop ) ;
}
}
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::getMappedComposition - "
< < " STOP " < < std : : endl ;
# endif
break ;
case SND_SEQ_EVENT_SONGPOS :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::getMappedComposition - "
< < " SONG POSITION " < < std : : endl ;
# endif
break ;
// these cases are handled by checkForNewClients
//
case SND_SEQ_EVENT_CLIENT_START :
case SND_SEQ_EVENT_CLIENT_EXIT :
case SND_SEQ_EVENT_CLIENT_CHANGE :
case SND_SEQ_EVENT_PORT_START :
case SND_SEQ_EVENT_PORT_EXIT :
case SND_SEQ_EVENT_PORT_CHANGE :
case SND_SEQ_EVENT_PORT_SUBSCRIBED :
case SND_SEQ_EVENT_PORT_UNSUBSCRIBED :
m_portCheckNeeded = true ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::getMappedComposition - "
< < " got announce event ( "
< < int ( event - > type ) < < " ) " < < std : : endl ;
# endif
break ;
case SND_SEQ_EVENT_TICK :
default :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::getMappedComposition - "
< < " got unhandled MIDI event type from ALSA sequencer "
< < " ( " < < int ( event - > type ) < < " ) " < < std : : endl ;
# endif
break ;
}
}
if ( getMTCStatus ( ) = = TRANSPORT_SLAVE & & isPlaying ( ) ) {
# ifdef MTC_DEBUG
std : : cerr < < " seq time is " < < getSequencerTime ( ) < < " , last MTC receive "
< < m_mtcLastReceive < < " , first time " < < m_mtcFirstTime < < std : : endl ;
# endif
if ( m_mtcFirstTime = = 0 ) { // have received _some_ MTC quarter-frame info
RealTime seqTime = getSequencerTime ( ) ;
if ( m_mtcLastReceive < seqTime & &
seqTime - m_mtcLastReceive > RealTime ( 0 , 500000000L ) ) {
ExternalTransport * transport = getExternalTransportControl ( ) ;
if ( transport ) {
transport - > transportJump ( ExternalTransport : : TransportStopAtTime ,
m_mtcLastEncoded ) ;
}
}
}
}
return & m_recordComposition ;
}
static int lock_count = 0 ;
void
AlsaDriver : : handleMTCTQFrame ( unsigned int data_byte , RealTime the_time )
{
if ( getMTCStatus ( ) ! = TRANSPORT_SLAVE )
return ;
switch ( data_byte & 0xF0 ) {
/* Frame */
case 0x00 :
/*
* Reset everything
*/
m_mtcReceiveTime = the_time ;
m_mtcFrames = data_byte & 0x0f ;
m_mtcSeconds = 0 ;
m_mtcMinutes = 0 ;
m_mtcHours = 0 ;
m_mtcSMPTEType = 0 ;
break ;
case 0x10 :
m_mtcFrames | = ( data_byte & 0x0f ) < < 4 ;
break ;
/* Seconds */
case 0x20 :
m_mtcSeconds = data_byte & 0x0f ;
break ;
case 0x30 :
m_mtcSeconds | = ( data_byte & 0x0f ) < < 4 ;
break ;
/* Minutes */
case 0x40 :
m_mtcMinutes = data_byte & 0x0f ;
break ;
case 0x50 :
m_mtcMinutes | = ( data_byte & 0x0f ) < < 4 ;
break ;
/* Hours and SMPTE type */
case 0x60 :
m_mtcHours = data_byte & 0x0f ;
break ;
case 0x70 : {
m_mtcHours | = ( data_byte & 0x01 ) < < 4 ;
m_mtcSMPTEType = ( data_byte & 0x06 ) > > 1 ;
int fps = 30 ;
if ( m_mtcSMPTEType = = 0 )
fps = 24 ;
else if ( m_mtcSMPTEType = = 1 )
fps = 25 ;
/*
* Ok , got all the bits now
* ( Assuming time is rolling forward )
*/
/* correct for 2-frame lag */
m_mtcFrames + = 2 ;
if ( m_mtcFrames > = fps ) {
m_mtcFrames - = fps ;
if ( + + m_mtcSeconds = = 60 ) {
m_mtcSeconds = 0 ;
if ( + + m_mtcMinutes = = 60 ) {
m_mtcMinutes = 0 ;
+ + m_mtcHours ;
}
}
}
# ifdef MTC_DEBUG
printf ( " RG MTC: Got a complete sequence: %02d:%02d:%02d.%02d (type %d) \n " ,
m_mtcHours ,
m_mtcMinutes ,
m_mtcSeconds ,
m_mtcFrames ,
m_mtcSMPTEType ) ;
# endif
/* compute encoded time */
m_mtcEncodedTime . sec = m_mtcSeconds +
m_mtcMinutes * 60 +
m_mtcHours * 60 * 60 ;
switch ( fps ) {
case 24 :
m_mtcEncodedTime . nsec = ( int )
( ( 125000000UL * ( unsigned ) m_mtcFrames ) / ( unsigned ) 3 ) ;
break ;
case 25 :
m_mtcEncodedTime . nsec = ( int )
( 40000000UL * ( unsigned ) m_mtcFrames ) ;
break ;
case 30 :
default :
m_mtcEncodedTime . nsec = ( int )
( ( 100000000UL * ( unsigned ) m_mtcFrames ) / ( unsigned ) 3 ) ;
break ;
}
/*
* We only mess with the clock if we are playing
*/
if ( m_playing ) {
# ifdef MTC_DEBUG
std : : cerr < < " RG MTC: Tstamp " < < m_mtcEncodedTime ;
std : : cerr < < " Received @ " < < m_mtcReceiveTime < < endl ;
# endif
calibrateMTC ( ) ;
RealTime t_diff = m_mtcEncodedTime - m_mtcReceiveTime ;
# ifdef MTC_DEBUG
std : : cerr < < " Diff: " < < t_diff < < endl ;
# endif
/* -ve diff means ALSA time ahead of MTC time */
if ( t_diff . sec > 0 ) {
tweakSkewForMTC ( 60000 ) ;
} else if ( t_diff . sec < 0 ) {
tweakSkewForMTC ( - 60000 ) ;
} else {
/* "small" diff - use adaptive technique */
tweakSkewForMTC ( t_diff . nsec / 1400 ) ;
if ( ( t_diff . nsec / 1000000 ) = = 0 ) {
if ( + + lock_count = = 3 ) {
printf ( " Got a lock @ %02d:%02d:%02d.%02d (type %d) \n " ,
m_mtcHours ,
m_mtcMinutes ,
m_mtcSeconds ,
m_mtcFrames ,
m_mtcSMPTEType ) ;
}
} else {
lock_count = 0 ;
}
}
} else if ( m_eat_mtc > 0 ) {
# ifdef MTC_DEBUG
std : : cerr < < " MTC: Received quarter frame just after issuing MMC stop - ignore it " < < std : : endl ;
# endif
- - m_eat_mtc ;
} else {
/* If we're not playing, we should be. */
# ifdef MTC_DEBUG
std : : cerr < < " MTC: Received quarter frame while not playing - starting now " < < std : : endl ;
# endif
ExternalTransport * transport = getExternalTransportControl ( ) ;
if ( transport ) {
transport - > transportJump
( ExternalTransport : : TransportStartAtTime ,
m_mtcEncodedTime ) ;
}
}
break ;
}
/* Oh dear, demented device! */
default :
break ;
}
}
void
AlsaDriver : : insertMTCFullFrame ( RealTime time )
{
snd_seq_event_t event ;
snd_seq_ev_clear ( & event ) ;
snd_seq_ev_set_source ( & event , m_syncOutputPort ) ;
snd_seq_ev_set_subs ( & event ) ;
m_mtcEncodedTime = time ;
m_mtcSeconds = m_mtcEncodedTime . sec % 60 ;
m_mtcMinutes = ( m_mtcEncodedTime . sec / 60 ) % 60 ;
m_mtcHours = ( m_mtcEncodedTime . sec / 3600 ) ;
// We always send at 25fps, it's the easiest to avoid rounding problems
m_mtcFrames = ( unsigned ) m_mtcEncodedTime . nsec / 40000000U ;
time = time + m_alsaPlayStartTime - m_playStartPosition ;
snd_seq_real_time_t atime = { ( unsigned int ) time . sec , ( unsigned int ) time . nsec } ;
unsigned char data [ 10 ] =
{ MIDI_SYSTEM_EXCLUSIVE ,
MIDI_SYSEX_RT , 127 , 1 , 1 ,
0 , 0 , 0 , 0 ,
MIDI_END_OF_EXCLUSIVE } ;
data [ 5 ] = ( ( unsigned char ) m_mtcHours & 0x1f ) + ( 1 < < 5 ) ; // 1 indicates 25fps
data [ 6 ] = ( unsigned char ) m_mtcMinutes ;
data [ 7 ] = ( unsigned char ) m_mtcSeconds ;
data [ 8 ] = ( unsigned char ) m_mtcFrames ;
snd_seq_ev_schedule_real ( & event , m_queue , 0 , & atime ) ;
snd_seq_ev_set_sysex ( & event , 10 , data ) ;
checkAlsaError ( snd_seq_event_output ( m_midiHandle , & event ) ,
" insertMTCFullFrame event send " ) ;
if ( m_queueRunning ) {
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " insertMTCFullFrame drain " ) ;
}
}
void
AlsaDriver : : insertMTCTQFrames ( RealTime sliceStart , RealTime sliceEnd )
{
if ( sliceStart = = RealTime : : zeroTime & & sliceEnd = = RealTime : : zeroTime ) {
// not a real slice
return ;
}
// We send at 25fps, it's the easiest to avoid rounding problems
RealTime twoFrames ( 0 , 80000000U ) ;
RealTime quarterFrame ( 0 , 10000000U ) ;
int fps = 25 ;
# ifdef MTC_DEBUG
std : : cout < < " AlsaDriver::insertMTCTQFrames( " < < sliceStart < < " , "
< < sliceEnd < < " ): first time " < < m_mtcFirstTime < < std : : endl ;
# endif
RealTime t ;
if ( m_mtcFirstTime ! = 0 ) { // first time through, reset location
m_mtcEncodedTime = sliceStart ;
t = sliceStart ;
m_mtcFirstTime = 0 ;
} else {
t = m_mtcEncodedTime + quarterFrame ;
}
m_mtcSeconds = m_mtcEncodedTime . sec % 60 ;
m_mtcMinutes = ( m_mtcEncodedTime . sec / 60 ) % 60 ;
m_mtcHours = ( m_mtcEncodedTime . sec / 3600 ) ;
m_mtcFrames = ( unsigned ) m_mtcEncodedTime . nsec / 40000000U ; // 25fps
std : : string bytes = " " ;
int type = 0 ;
while ( m_mtcEncodedTime < sliceEnd ) {
snd_seq_event_t event ;
snd_seq_ev_clear ( & event ) ;
snd_seq_ev_set_source ( & event , m_syncOutputPort ) ;
snd_seq_ev_set_subs ( & event ) ;
# ifdef MTC_DEBUG
std : : cout < < " Sending MTC quarter frame at " < < t < < std : : endl ;
# endif
unsigned char c = ( type < < 4 ) ;
switch ( type ) {
case 0 :
c + = ( ( unsigned char ) m_mtcFrames & 0x0f ) ;
break ;
case 1 :
c + = ( ( ( unsigned char ) m_mtcFrames & 0xf0 ) > > 4 ) ;
break ;
case 2 :
c + = ( ( unsigned char ) m_mtcSeconds & 0x0f ) ;
break ;
case 3 :
c + = ( ( ( unsigned char ) m_mtcSeconds & 0xf0 ) > > 4 ) ;
break ;
case 4 :
c + = ( ( unsigned char ) m_mtcMinutes & 0x0f ) ;
break ;
case 5 :
c + = ( ( ( unsigned char ) m_mtcMinutes & 0xf0 ) > > 4 ) ;
break ;
case 6 :
c + = ( ( unsigned char ) m_mtcHours & 0x0f ) ;
break ;
case 7 : // hours high nibble + smpte type
c + = ( m_mtcHours > > 4 ) & 0x01 ;
c + = ( 1 < < 1 ) ; // type 1 indicates 25fps
break ;
}
RealTime scheduleTime = t + m_alsaPlayStartTime - m_playStartPosition ;
snd_seq_real_time_t atime = { ( unsigned int ) scheduleTime . sec , ( unsigned int ) scheduleTime . nsec } ;
event . type = SND_SEQ_EVENT_QFRAME ;
event . data . control . value = c ;
snd_seq_ev_schedule_real ( & event , m_queue , 0 , & atime ) ;
checkAlsaError ( snd_seq_event_output ( m_midiHandle , & event ) ,
" insertMTCTQFrames sending qframe event " ) ;
if ( + + type = = 8 ) {
m_mtcFrames + = 2 ;
if ( m_mtcFrames > = fps ) {
m_mtcFrames - = fps ;
if ( + + m_mtcSeconds = = 60 ) {
m_mtcSeconds = 0 ;
if ( + + m_mtcMinutes = = 60 ) {
m_mtcMinutes = 0 ;
+ + m_mtcHours ;
}
}
}
m_mtcEncodedTime = t ;
type = 0 ;
}
t = t + quarterFrame ;
}
}
bool
AlsaDriver : : testForMTCSysex ( const snd_seq_event_t * event )
{
if ( getMTCStatus ( ) ! = TRANSPORT_SLAVE )
return false ;
// At this point, and possibly for the foreseeable future, the only
// sysex we're interested in is full-frame transport location
# ifdef MTC_DEBUG
std : : cerr < < " MTC: testing sysex of length " < < event - > data . ext . len < < " : " < < std : : endl ;
for ( int i = 0 ; i < event - > data . ext . len ; + + i ) {
std : : cerr < < ( int ) * ( ( unsigned char * ) event - > data . ext . ptr + i ) < < " " ;
}
std : : cerr < < endl ;
# endif
if ( event - > data . ext . len ! = 10 )
return false ;
unsigned char * ptr = ( unsigned char * ) ( event - > data . ext . ptr ) ;
if ( * ptr + + ! = MIDI_SYSTEM_EXCLUSIVE )
return false ;
if ( * ptr + + ! = MIDI_SYSEX_RT )
return false ;
if ( * ptr + + > 127 )
return false ;
// 01 01 for MTC full frame
if ( * ptr + + ! = 1 )
return false ;
if ( * ptr + + ! = 1 )
return false ;
int htype = * ptr + + ;
int min = * ptr + + ;
int sec = * ptr + + ;
int frame = * ptr + + ;
if ( * ptr ! = MIDI_END_OF_EXCLUSIVE )
return false ;
int hour = ( htype & 0x1f ) ;
int type = ( htype & 0xe0 ) > > 5 ;
m_mtcFrames = frame ;
m_mtcSeconds = sec ;
m_mtcMinutes = min ;
m_mtcHours = hour ;
m_mtcSMPTEType = type ;
int fps = 30 ;
if ( m_mtcSMPTEType = = 0 )
fps = 24 ;
else if ( m_mtcSMPTEType = = 1 )
fps = 25 ;
m_mtcEncodedTime . sec = sec + min * 60 + hour * 60 * 60 ;
switch ( fps ) {
case 24 :
m_mtcEncodedTime . nsec = ( int )
( ( 125000000UL * ( unsigned ) m_mtcFrames ) / ( unsigned ) 3 ) ;
break ;
case 25 :
m_mtcEncodedTime . nsec = ( int )
( 40000000UL * ( unsigned ) m_mtcFrames ) ;
break ;
case 30 :
default :
m_mtcEncodedTime . nsec = ( int )
( ( 100000000UL * ( unsigned ) m_mtcFrames ) / ( unsigned ) 3 ) ;
break ;
}
# ifdef MTC_DEBUG
std : : cerr < < " MTC: MTC sysex found (frame type " < < type
< < " ), jumping to " < < m_mtcEncodedTime < < std : : endl ;
# endif
ExternalTransport * transport = getExternalTransportControl ( ) ;
if ( transport ) {
transport - > transportJump
( ExternalTransport : : TransportJumpToTime ,
m_mtcEncodedTime ) ;
}
return true ;
}
static int last_factor = 0 ;
static int bias_factor = 0 ;
void
AlsaDriver : : calibrateMTC ( )
{
if ( m_mtcFirstTime < 0 )
return ;
else if ( m_mtcFirstTime > 0 ) {
- - m_mtcFirstTime ;
m_mtcSigmaC = 0 ;
m_mtcSigmaE = 0 ;
} else {
RealTime diff_e = m_mtcEncodedTime - m_mtcLastEncoded ;
RealTime diff_c = m_mtcReceiveTime - m_mtcLastReceive ;
# ifdef MTC_DEBUG
printf ( " RG MTC: diffs %d %d %d \n " , diff_c . nsec , diff_e . nsec , m_mtcSkew ) ;
# endif
m_mtcSigmaE + = ( ( long long int ) diff_e . nsec ) * m_mtcSkew ;
m_mtcSigmaC + = diff_c . nsec ;
int t_bias = ( m_mtcSigmaE / m_mtcSigmaC ) - 0x10000 ;
# ifdef MTC_DEBUG
printf ( " RG MTC: sigmas %lld %lld %d \n " , m_mtcSigmaE , m_mtcSigmaC , t_bias ) ;
# endif
bias_factor = t_bias ;
}
m_mtcLastReceive = m_mtcReceiveTime ;
m_mtcLastEncoded = m_mtcEncodedTime ;
}
void
AlsaDriver : : tweakSkewForMTC ( int factor )
{
if ( factor > 50000 ) {
factor = 50000 ;
} else if ( factor < - 50000 ) {
factor = - 50000 ;
} else if ( factor = = last_factor ) {
return ;
} else {
if ( m_mtcFirstTime = = - 1 )
m_mtcFirstTime = 5 ;
}
last_factor = factor ;
snd_seq_queue_tempo_t * q_ptr ;
snd_seq_queue_tempo_alloca ( & q_ptr ) ;
snd_seq_get_queue_tempo ( m_midiHandle , m_queue , q_ptr ) ;
unsigned int t_skew = snd_seq_queue_tempo_get_skew ( q_ptr ) ;
# ifdef MTC_DEBUG
std : : cerr < < " RG MTC: skew: " < < t_skew ;
# endif
t_skew = 0x10000 + factor + bias_factor ;
# ifdef MTC_DEBUG
std : : cerr < < " changed to " < < factor < < " + " < < bias_factor < < endl ;
# endif
snd_seq_queue_tempo_set_skew ( q_ptr , t_skew ) ;
snd_seq_set_queue_tempo ( m_midiHandle , m_queue , q_ptr ) ;
m_mtcSkew = t_skew ;
}
bool
AlsaDriver : : testForMMCSysex ( const snd_seq_event_t * event )
{
if ( getMMCStatus ( ) ! = TRANSPORT_SLAVE )
return false ;
if ( event - > data . ext . len ! = 6 )
return false ;
unsigned char * ptr = ( unsigned char * ) ( event - > data . ext . ptr ) ;
if ( * ptr + + ! = MIDI_SYSTEM_EXCLUSIVE )
return false ;
if ( * ptr + + ! = MIDI_SYSEX_RT )
return false ;
if ( * ptr + + > 127 )
return false ;
if ( * ptr + + ! = MIDI_SYSEX_RT_COMMAND )
return false ;
int instruction = * ptr + + ;
if ( * ptr ! = MIDI_END_OF_EXCLUSIVE )
return false ;
if ( instruction = = MIDI_MMC_PLAY | |
instruction = = MIDI_MMC_DEFERRED_PLAY ) {
ExternalTransport * transport = getExternalTransportControl ( ) ;
if ( transport ) {
transport - > transportChange ( ExternalTransport : : TransportPlay ) ;
}
} else if ( instruction = = MIDI_MMC_STOP ) {
ExternalTransport * transport = getExternalTransportControl ( ) ;
if ( transport ) {
transport - > transportChange ( ExternalTransport : : TransportStop ) ;
}
}
return true ;
}
void
AlsaDriver : : processMidiOut ( const MappedComposition & mC ,
const RealTime & sliceStart ,
const RealTime & sliceEnd )
{
RealTime outputTime ;
RealTime outputStopTime ;
MappedInstrument * instrument ;
ClientPortPair outputDevice ;
MidiByte channel ;
snd_seq_event_t event ;
// special case for unqueued events
bool now = ( sliceStart = = RealTime : : zeroTime & & sliceEnd = = RealTime : : zeroTime ) ;
if ( ! now ) {
// This 0.5 sec is arbitrary, but it must be larger than the
// sequencer's read-ahead
RealTime diff = RealTime : : fromSeconds ( 0.5 ) ;
RealTime cutoff = sliceStart - diff ;
cropRecentNoteOffs ( cutoff - m_playStartPosition + m_alsaPlayStartTime ) ;
}
// These won't change in this slice
//
snd_seq_ev_clear ( & event ) ;
if ( ( mC . begin ( ) ! = mC . end ( ) ) & & getSequencerDataBlock ( ) ) {
getSequencerDataBlock ( ) - > setVisual ( * mC . begin ( ) ) ;
}
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " AlsaDriver::processMidiOut( " < < sliceStart < < " , " < < sliceEnd
< < " ), " < < mC . size ( ) < < " events, now is " < < now < < std : : endl ;
# endif
// NB the MappedComposition is implicitly ordered by time (std::multiset)
for ( MappedComposition : : const_iterator i = mC . begin ( ) ; i ! = mC . end ( ) ; + + i ) {
if ( ( * i ) - > getType ( ) > = MappedEvent : : Audio )
continue ;
bool isControllerOut = ( ( * i ) - > getRecordedDevice ( ) = =
Device : : CONTROL_DEVICE ) ;
bool isSoftSynth = ( ! isControllerOut & &
( ( * i ) - > getInstrument ( ) > = SoftSynthInstrumentBase ) ) ;
outputTime = ( * i ) - > getEventTime ( ) - m_playStartPosition +
m_alsaPlayStartTime ;
if ( now & & ! m_playing & & m_queueRunning ) {
// stop queue to ensure exact timing and make sure the
// event gets through right now
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " processMidiOut: stopping queue for now-event " < < std : : endl ;
# endif
checkAlsaError ( snd_seq_stop_queue ( m_midiHandle , m_queue , NULL ) , " processMidiOut(): stop queue " ) ;
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " processMidiOut(): draining " ) ;
}
RealTime alsaTimeNow = getAlsaTime ( ) ;
if ( now ) {
if ( ! m_playing ) {
outputTime = alsaTimeNow ;
} else if ( outputTime < alsaTimeNow ) {
outputTime = alsaTimeNow + RealTime ( 0 , 10000000 ) ;
}
}
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " processMidiOut[ " < < now < < " ]: event is at " < < outputTime < < " ( " < < outputTime - alsaTimeNow < < " ahead of queue time), type " < < int ( ( * i ) - > getType ( ) ) < < " , duration " < < ( * i ) - > getDuration ( ) < < std : : endl ;
# endif
if ( ! m_queueRunning & & outputTime < alsaTimeNow ) {
RealTime adjust = alsaTimeNow - outputTime ;
if ( ( * i ) - > getDuration ( ) > RealTime : : zeroTime ) {
if ( ( * i ) - > getDuration ( ) < = adjust ) {
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " processMidiOut[ " < < now < < " ]: too late for this event, abandoning it " < < std : : endl ;
# endif
continue ;
} else {
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " processMidiOut[ " < < now < < " ]: pushing event forward and reducing duration by " < < adjust < < std : : endl ;
# endif
( * i ) - > setDuration ( ( * i ) - > getDuration ( ) - adjust ) ;
}
} else {
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " processMidiOut[ " < < now < < " ]: pushing zero-duration event forward by " < < adjust < < std : : endl ;
# endif
}
outputTime = alsaTimeNow ;
}
processNotesOff ( outputTime , now ) ;
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
size_t frameCount = m_jackDriver - > getFramesProcessed ( ) ;
size_t elapsed = frameCount - _debug_jack_frame_count ;
RealTime rt = RealTime : : frame2RealTime ( elapsed , m_jackDriver - > getSampleRate ( ) ) ;
rt = rt - getAlsaTime ( ) ;
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " processMidiOut[ " < < now < < " ]: JACK time is " < < rt < < " ahead of ALSA time " < < std : : endl ;
# endif
}
# endif
// Second and nanoseconds for ALSA
//
snd_seq_real_time_t time = { ( unsigned int ) outputTime . sec , ( unsigned int ) outputTime . nsec } ;
if ( ! isSoftSynth ) {
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cout < < " processMidiOut[ " < < now < < " ]: instrument " < < ( * i ) - > getInstrument ( ) < < std : : endl ;
std : : cout < < " pitch: " < < ( int ) ( * i ) - > getPitch ( ) < < " , velocity " < < ( int ) ( * i ) - > getVelocity ( ) < < " , duration " < < ( * i ) - > getDuration ( ) < < std : : endl ;
# endif
snd_seq_ev_set_subs ( & event ) ;
// Set source according to port for device
//
int src ;
if ( isControllerOut ) {
src = m_controllerPort ;
} else {
src = getOutputPortForMappedInstrument ( ( * i ) - > getInstrument ( ) ) ;
}
if ( src < 0 ) continue ;
snd_seq_ev_set_source ( & event , src ) ;
snd_seq_ev_schedule_real ( & event , m_queue , 0 , & time ) ;
} else {
event . time . time = time ;
}
instrument = getMappedInstrument ( ( * i ) - > getInstrument ( ) ) ;
// set the stop time for Note Off
//
outputStopTime = outputTime + ( * i ) - > getDuration ( )
- RealTime ( 0 , 1 ) ; // notch it back 1nsec just to ensure
// correct ordering against any other
// note-ons at the same nominal time
bool needNoteOff = false ;
if ( isControllerOut ) {
channel = ( * i ) - > getRecordedChannel ( ) ;
# ifdef DEBUG_ALSA
std : : cerr < < " processMidiOut() - Event of type " < < ( int ) ( ( * i ) - > getType ( ) ) < < " (data1 " < < ( int ) ( * i ) - > getData1 ( ) < < " , data2 " < < ( int ) ( * i ) - > getData2 ( ) < < " ) for external controller channel " < < ( int ) channel < < std : : endl ;
# endif
} else if ( instrument ! = 0 ) {
channel = instrument - > getChannel ( ) ;
} else {
# ifdef DEBUG_ALSA
std : : cerr < < " processMidiOut() - No instrument for event of type "
< < ( int ) ( * i ) - > getType ( ) < < " at " < < ( * i ) - > getEventTime ( )
< < std : : endl ;
# endif
channel = 0 ;
}
switch ( ( * i ) - > getType ( ) ) {
case MappedEvent : : MidiNoteOneShot :
{
snd_seq_ev_set_noteon ( & event ,
channel ,
( * i ) - > getPitch ( ) ,
( * i ) - > getVelocity ( ) ) ;
needNoteOff = true ;
if ( ! isSoftSynth & & getSequencerDataBlock ( ) ) {
LevelInfo info ;
info . level = ( * i ) - > getVelocity ( ) ;
info . levelRight = 0 ;
getSequencerDataBlock ( ) - > setInstrumentLevel
( ( * i ) - > getInstrument ( ) , info ) ;
}
weedRecentNoteOffs ( ( * i ) - > getPitch ( ) , channel , ( * i ) - > getInstrument ( ) ) ;
}
break ;
case MappedEvent : : MidiNote :
// We always use plain NOTE ON here, not ALSA
// time+duration notes, because we have our own NOTE
// OFF stack (which will be augmented at the bottom of
// this function) and we want to ensure it gets used
// for the purposes of e.g. soft synths
//
if ( ( * i ) - > getVelocity ( ) > 0 ) {
snd_seq_ev_set_noteon ( & event ,
channel ,
( * i ) - > getPitch ( ) ,
( * i ) - > getVelocity ( ) ) ;
if ( ! isSoftSynth & & getSequencerDataBlock ( ) ) {
LevelInfo info ;
info . level = ( * i ) - > getVelocity ( ) ;
info . levelRight = 0 ;
getSequencerDataBlock ( ) - > setInstrumentLevel
( ( * i ) - > getInstrument ( ) , info ) ;
}
weedRecentNoteOffs ( ( * i ) - > getPitch ( ) , channel , ( * i ) - > getInstrument ( ) ) ;
} else {
snd_seq_ev_set_noteoff ( & event ,
channel ,
( * i ) - > getPitch ( ) ,
( * i ) - > getVelocity ( ) ) ;
}
break ;
case MappedEvent : : MidiProgramChange :
snd_seq_ev_set_pgmchange ( & event ,
channel ,
( * i ) - > getData1 ( ) ) ;
break ;
case MappedEvent : : MidiKeyPressure :
snd_seq_ev_set_keypress ( & event ,
channel ,
( * i ) - > getData1 ( ) ,
( * i ) - > getData2 ( ) ) ;
break ;
case MappedEvent : : MidiChannelPressure :
snd_seq_ev_set_chanpress ( & event ,
channel ,
( * i ) - > getData1 ( ) ) ;
break ;
case MappedEvent : : MidiPitchBend : {
int d1 = ( int ) ( ( * i ) - > getData1 ( ) ) ;
int d2 = ( int ) ( ( * i ) - > getData2 ( ) ) ;
int value = ( ( d1 < < 7 ) | d2 ) - 8192 ;
// keep within -8192 to +8192
//
// if (value & 0x4000)
// value -= 0x8000;
snd_seq_ev_set_pitchbend ( & event ,
channel ,
value ) ;
}
break ;
case MappedEvent : : MidiSystemMessage : {
switch ( ( * i ) - > getData1 ( ) ) {
case MIDI_SYSTEM_EXCLUSIVE : {
char out [ 2 ] ;
sprintf ( out , " %c " , MIDI_SYSTEM_EXCLUSIVE ) ;
std : : string data = out ;
data + = DataBlockRepository : : getDataBlockForEvent ( ( * i ) ) ;
sprintf ( out , " %c " , MIDI_END_OF_EXCLUSIVE ) ;
data + = out ;
snd_seq_ev_set_sysex ( & event ,
data . length ( ) ,
( char * ) ( data . c_str ( ) ) ) ;
}
break ;
case MIDI_TIMING_CLOCK : {
RealTime rt =
RealTime ( time . tv_sec , time . tv_nsec ) ;
/*
std : : cerr < < " AlsaDriver::processMidiOut - "
< < " send clock @ " < < rt < < std : : endl ;
*/
sendSystemQueued ( SND_SEQ_EVENT_CLOCK , " " , rt ) ;
continue ;
}
break ;
default :
std : : cerr < < " AlsaDriver::processMidiOut - "
< < " unrecognised system message "
< < std : : endl ;
break ;
}
}
break ;
case MappedEvent : : MidiController :
snd_seq_ev_set_controller ( & event ,
channel ,
( * i ) - > getData1 ( ) ,
( * i ) - > getData2 ( ) ) ;
break ;
case MappedEvent : : Audio :
case MappedEvent : : AudioCancel :
case MappedEvent : : AudioLevel :
case MappedEvent : : AudioStopped :
case MappedEvent : : SystemUpdateInstruments :
case MappedEvent : : SystemJackTransport : //???
case MappedEvent : : SystemMMCTransport :
case MappedEvent : : SystemMIDIClock :
case MappedEvent : : SystemMIDISyncAuto :
break ;
default :
case MappedEvent : : InvalidMappedEvent :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processMidiOut - "
< < " skipping unrecognised or invalid MappedEvent type "
< < std : : endl ;
# endif
continue ;
}
if ( isSoftSynth ) {
processSoftSynthEventOut ( ( * i ) - > getInstrument ( ) , & event , now ) ;
} else {
checkAlsaError ( snd_seq_event_output ( m_midiHandle , & event ) ,
" processMidiOut(): output queued " ) ;
if ( now ) {
if ( m_queueRunning & & ! m_playing ) {
// restart queue
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " processMidiOut: restarting queue after now-event " < < std : : endl ;
# endif
checkAlsaError ( snd_seq_continue_queue ( m_midiHandle , m_queue , NULL ) , " processMidiOut(): continue queue " ) ;
}
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " processMidiOut(): draining " ) ;
}
}
// Add note to note off stack
//
if ( needNoteOff ) {
NoteOffEvent * noteOffEvent =
new NoteOffEvent ( outputStopTime , // already calculated
( * i ) - > getPitch ( ) ,
channel ,
( * i ) - > getInstrument ( ) ) ;
# ifdef DEBUG_ALSA
std : : cerr < < " Adding NOTE OFF at " < < outputStopTime
< < std : : endl ;
# endif
m_noteOffQueue . insert ( noteOffEvent ) ;
}
}
processNotesOff ( sliceEnd - m_playStartPosition + m_alsaPlayStartTime , now ) ;
if ( getMTCStatus ( ) = = TRANSPORT_MASTER ) {
insertMTCTQFrames ( sliceStart , sliceEnd ) ;
}
if ( m_queueRunning ) {
if ( now & & ! m_playing ) {
// just to be sure
# ifdef DEBUG_PROCESS_MIDI_OUT
std : : cerr < < " processMidiOut: restarting queue after all now-events " < < std : : endl ;
# endif
checkAlsaError ( snd_seq_continue_queue ( m_midiHandle , m_queue , NULL ) , " processMidiOut(): continue queue " ) ;
}
# ifdef DEBUG_PROCESS_MIDI_OUT
// std::cerr << "processMidiOut: m_queueRunning " << m_queueRunning
// << ", now " << now << std::endl;
# endif
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " processMidiOut(): draining " ) ;
}
}
void
AlsaDriver : : processSoftSynthEventOut ( InstrumentId id , const snd_seq_event_t * ev , bool now )
{
# ifdef DEBUG_PROCESS_SOFT_SYNTH_OUT
std : : cerr < < " AlsaDriver::processSoftSynthEventOut: instrument " < < id < < " , now " < < now < < std : : endl ;
# endif
# ifdef HAVE_LIBJACK
if ( ! m_jackDriver )
return ;
RunnablePluginInstance * synthPlugin = m_jackDriver - > getSynthPlugin ( id ) ;
if ( synthPlugin ) {
RealTime t ( ev - > time . time . tv_sec , ev - > time . time . tv_nsec ) ;
if ( now )
t = RealTime : : zeroTime ;
else
t = t + m_playStartPosition - m_alsaPlayStartTime ;
# ifdef DEBUG_PROCESS_SOFT_SYNTH_OUT
std : : cerr < < " AlsaDriver::processSoftSynthEventOut: event time " < < t < < std : : endl ;
# endif
synthPlugin - > sendEvent ( t , ev ) ;
if ( now ) {
# ifdef DEBUG_PROCESS_SOFT_SYNTH_OUT
std : : cerr < < " AlsaDriver::processSoftSynthEventOut: setting haveAsyncAudioEvent " < < std : : endl ;
# endif
m_jackDriver - > setHaveAsyncAudioEvent ( ) ;
}
}
# endif
}
void
AlsaDriver : : startClocks ( )
{
int result ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::startClocks " < < std : : endl ;
# endif
if ( m_needJackStart ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::startClocks: Need JACK start (m_playing = " < < m_playing < < " ) " < < std : : endl ;
# endif
}
# ifdef HAVE_LIBJACK
// New JACK transport scheme: The initialisePlayback,
// resetPlayback and stopPlayback methods set m_needJackStart, and
// then this method checks it and calls the appropriate JACK
// transport start or relocate method, which calls back on
// startClocksApproved when ready. (Previously this method always
// called the JACK transport start method, so we couldn't handle
// moving the pointer when not playing, and we had to stop the
// transport explicitly from resetPlayback when repositioning
// during playback.)
if ( m_jackDriver ) {
// Don't need any locks on this, except for those that the
// driver methods take and hold for themselves
if ( m_needJackStart ! = NeedNoJackStart ) {
if ( m_needJackStart = = NeedJackStart | |
m_playing ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::startClocks: playing, prebuffer audio " < < std : : endl ;
# endif
m_jackDriver - > prebufferAudio ( ) ;
} else {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::startClocks: prepare audio only " < < std : : endl ;
# endif
m_jackDriver - > prepareAudio ( ) ;
}
bool rv ;
if ( m_needJackStart = = NeedJackReposition ) {
rv = m_jackDriver - > relocateTransport ( ) ;
} else {
rv = m_jackDriver - > startTransport ( ) ;
if ( ! rv ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::startClocks: Waiting for startClocksApproved " < < std : : endl ;
# endif
// need to wait for transport sync
_debug_jack_frame_count = m_jackDriver - > getFramesProcessed ( ) ;
return ;
}
}
}
}
# endif
// Restart the timer
if ( ( result = snd_seq_continue_queue ( m_midiHandle , m_queue , NULL ) ) < 0 ) {
std : : cerr < < " AlsaDriver::startClocks - couldn't start queue - "
< < snd_strerror ( result )
< < std : : endl ;
reportFailure ( MappedEvent : : FailureALSACallFailed ) ;
}
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::startClocks: started clocks " < < std : : endl ;
# endif
m_queueRunning = true ;
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
_debug_jack_frame_count = m_jackDriver - > getFramesProcessed ( ) ;
}
# endif
// process pending MIDI events
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " startClocks(): draining " ) ;
}
void
AlsaDriver : : startClocksApproved ( )
{
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::startClocks: startClocksApproved " < < std : : endl ;
# endif
//!!!
m_needJackStart = NeedNoJackStart ;
startClocks ( ) ;
return ;
int result ;
// Restart the timer
if ( ( result = snd_seq_continue_queue ( m_midiHandle , m_queue , NULL ) ) < 0 ) {
std : : cerr < < " AlsaDriver::startClocks - couldn't start queue - "
< < snd_strerror ( result )
< < std : : endl ;
reportFailure ( MappedEvent : : FailureALSACallFailed ) ;
}
m_queueRunning = true ;
// process pending MIDI events
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " startClocksApproved(): draining " ) ;
}
void
AlsaDriver : : stopClocks ( )
{
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::stopClocks " < < std : : endl ;
# endif
if ( checkAlsaError ( snd_seq_stop_queue ( m_midiHandle , m_queue , NULL ) , " stopClocks(): stopping queue " ) < 0 ) {
reportFailure ( MappedEvent : : FailureALSACallFailed ) ;
}
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " stopClocks(): draining output to stop queue " ) ;
m_queueRunning = false ;
// We used to call m_jackDriver->stop() from here, but we no
// longer do -- it's now called from stopPlayback() so as to
// handle repositioning during playback (when stopClocks is
// necessary but stopPlayback and m_jackDriver->stop() are not).
snd_seq_event_t event ;
snd_seq_ev_clear ( & event ) ;
snd_seq_real_time_t z = { 0 , 0 } ;
snd_seq_ev_set_queue_pos_real ( & event , m_queue , & z ) ;
snd_seq_ev_set_direct ( & event ) ;
checkAlsaError ( snd_seq_control_queue ( m_midiHandle , m_queue , SND_SEQ_EVENT_SETPOS_TIME ,
0 , & event ) , " stopClocks(): setting zpos to queue " ) ;
// process that
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " stopClocks(): draining output to zpos queue " ) ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::stopClocks: ALSA time now is " < < getAlsaTime ( ) < < std : : endl ;
# endif
m_alsaPlayStartTime = RealTime : : zeroTime ;
}
void
AlsaDriver : : processEventsOut ( const MappedComposition & mC )
{
processEventsOut ( mC , RealTime : : zeroTime , RealTime : : zeroTime ) ;
}
void
AlsaDriver : : processEventsOut ( const MappedComposition & mC ,
const RealTime & sliceStart ,
const RealTime & sliceEnd )
{
// special case for unqueued events
bool now = ( sliceStart = = RealTime : : zeroTime & & sliceEnd = = RealTime : : zeroTime ) ;
if ( m_startPlayback ) {
m_startPlayback = false ;
// This only records whether we're playing in principle,
// not whether the clocks are actually ticking. Contrariwise,
// areClocksRunning tells us whether the clocks are ticking
// but not whether we're actually playing (the clocks go even
// when we're not). Check both if you want to know whether
// we're really rolling.
m_playing = true ;
if ( getMTCStatus ( ) = = TRANSPORT_SLAVE ) {
tweakSkewForMTC ( 0 ) ;
}
}
AudioFile * audioFile = 0 ;
bool haveNewAudio = false ;
// insert audio events if we find them
for ( MappedComposition : : const_iterator i = mC . begin ( ) ; i ! = mC . end ( ) ; + + i ) {
# ifdef HAVE_LIBJACK
// Play an audio file
//
if ( ( * i ) - > getType ( ) = = MappedEvent : : Audio ) {
if ( ! m_jackDriver )
continue ;
// This is used for handling asynchronous
// (i.e. unexpected) audio events only
if ( ( * i ) - > getEventTime ( ) > RealTime ( - 120 , 0 ) ) {
// Not an asynchronous event
continue ;
}
// Check for existence of file - if the sequencer has died
// and been restarted then we're not always loaded up with
// the audio file references we should have. In the future
// we could make this just get the gui to reload our files
// when (or before) this fails.
//
audioFile = getAudioFile ( ( * i ) - > getAudioID ( ) ) ;
if ( audioFile ) {
MappedAudioFader * fader =
dynamic_cast < MappedAudioFader * >
( getMappedStudio ( ) - > getAudioFader ( ( * i ) - > getInstrument ( ) ) ) ;
if ( ! fader ) {
std : : cerr < < " WARNING: AlsaDriver::processEventsOut: no fader for audio instrument " < < ( * i ) - > getInstrument ( ) < < std : : endl ;
continue ;
}
unsigned int channels = fader - > getPropertyList (
MappedAudioFader : : Channels ) [ 0 ] . toInt ( ) ;
RealTime bufferLength = getAudioReadBufferLength ( ) ;
int bufferFrames = RealTime : : realTime2Frame
( bufferLength , m_jackDriver - > getSampleRate ( ) ) ;
if ( bufferFrames % m_jackDriver - > getBufferSize ( ) ) {
bufferFrames / = m_jackDriver - > getBufferSize ( ) ;
bufferFrames + + ;
bufferFrames * = m_jackDriver - > getBufferSize ( ) ;
}
//#define DEBUG_PLAYING_AUDIO
# ifdef DEBUG_PLAYING_AUDIO
std : : cout < < " Creating playable audio file: id " < < audioFile - > getId ( ) < < " , event time " < < ( * i ) - > getEventTime ( ) < < " , time now " < < getAlsaTime ( ) < < " , start marker " < < ( * i ) - > getAudioStartMarker ( ) < < " , duration " < < ( * i ) - > getDuration ( ) < < " , instrument " < < ( * i ) - > getInstrument ( ) < < " channels " < < channels < < std : : endl ;
std : : cout < < " Read buffer length is " < < bufferLength < < " ( " < < bufferFrames < < " frames) " < < std : : endl ;
# endif
PlayableAudioFile * paf = 0 ;
try {
paf = new PlayableAudioFile ( ( * i ) - > getInstrument ( ) ,
audioFile ,
getSequencerTime ( ) +
( RealTime ( 1 , 0 ) / 4 ) ,
( * i ) - > getAudioStartMarker ( ) ,
( * i ) - > getDuration ( ) ,
bufferFrames ,
getSmallFileSize ( ) * 1024 ,
channels ,
m_jackDriver - > getSampleRate ( ) ) ;
} catch ( . . . ) {
continue ;
}
if ( ( * i ) - > isAutoFading ( ) ) {
paf - > setAutoFade ( true ) ;
paf - > setFadeInTime ( ( * i ) - > getFadeInTime ( ) ) ;
paf - > setFadeOutTime ( ( * i ) - > getFadeInTime ( ) ) ;
//#define DEBUG_AUTOFADING
# ifdef DEBUG_AUTOFADING
std : : cout < < " PlayableAudioFile is AUTOFADING - "
< < " in = " < < ( * i ) - > getFadeInTime ( )
< < " , out = " < < ( * i ) - > getFadeOutTime ( )
< < std : : endl ;
# endif
}
# ifdef DEBUG_AUTOFADING
else {
std : : cout < < " PlayableAudioFile has no AUTOFADE "
< < std : : endl ;
}
# endif
// segment runtime id
paf - > setRuntimeSegmentId ( ( * i ) - > getRuntimeSegmentId ( ) ) ;
m_audioQueue - > addUnscheduled ( paf ) ;
haveNewAudio = true ;
} else {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " can't find audio file reference "
< < std : : endl ;
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " try reloading the current Rosegarden file "
< < std : : endl ;
# else
;
# endif
}
}
// Cancel a playing audio file preview (this is predicated on
// runtime segment ID and optionally start time)
//
if ( ( * i ) - > getType ( ) = = MappedEvent : : AudioCancel ) {
cancelAudioFile ( * i ) ;
}
# endif // HAVE_LIBJACK
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemMIDIClock ) {
switch ( ( int ) ( * i ) - > getData1 ( ) ) {
case 0 :
m_midiClockEnabled = false ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden MIDI CLOCK, START and STOP DISABLED "
< < std : : endl ;
# endif
setMIDISyncStatus ( TRANSPORT_OFF ) ;
break ;
case 1 :
m_midiClockEnabled = true ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden send MIDI CLOCK, START and STOP ENABLED "
< < std : : endl ;
# endif
setMIDISyncStatus ( TRANSPORT_MASTER ) ;
break ;
case 2 :
m_midiClockEnabled = false ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden accept START and STOP ENABLED "
< < std : : endl ;
# endif
setMIDISyncStatus ( TRANSPORT_SLAVE ) ;
break ;
}
}
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemMIDISyncAuto ) {
if ( ( * i ) - > getData1 ( ) ) {
m_midiSyncAutoConnect = true ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden MIDI SYNC AUTO ENABLED "
< < std : : endl ;
# endif
for ( DevicePortMap : : iterator dpmi = m_devicePortMap . begin ( ) ;
dpmi ! = m_devicePortMap . end ( ) ; + + dpmi ) {
snd_seq_connect_to ( m_midiHandle ,
m_syncOutputPort ,
dpmi - > second . first ,
dpmi - > second . second ) ;
}
} else {
m_midiSyncAutoConnect = false ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden MIDI SYNC AUTO DISABLED "
< < std : : endl ;
# endif
}
}
# ifdef HAVE_LIBJACK
// Set the JACK transport
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemJackTransport ) {
bool enabled = false ;
bool master = false ;
switch ( ( int ) ( * i ) - > getData1 ( ) ) {
case 2 :
master = true ;
enabled = true ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden to follow JACK transport and request JACK timebase master role (not yet implemented) "
< < std : : endl ;
# endif
break ;
case 1 :
enabled = true ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden to follow JACK transport "
< < std : : endl ;
# endif
break ;
case 0 :
default :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden to ignore JACK transport "
< < std : : endl ;
# endif
break ;
}
if ( m_jackDriver ) {
m_jackDriver - > setTransportEnabled ( enabled ) ;
m_jackDriver - > setTransportMaster ( master ) ;
}
}
# endif // HAVE_LIBJACK
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemMMCTransport ) {
switch ( ( int ) ( * i ) - > getData1 ( ) ) {
case 1 :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden is MMC MASTER "
< < std : : endl ;
# endif
setMMCStatus ( TRANSPORT_MASTER ) ;
break ;
case 2 :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden is MMC SLAVE "
< < std : : endl ;
# endif
setMMCStatus ( TRANSPORT_SLAVE ) ;
break ;
case 0 :
default :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden MMC Transport DISABLED "
< < std : : endl ;
# endif
setMMCStatus ( TRANSPORT_OFF ) ;
break ;
}
}
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemMTCTransport ) {
switch ( ( int ) ( * i ) - > getData1 ( ) ) {
case 1 :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden is MTC MASTER "
< < std : : endl ;
# endif
setMTCStatus ( TRANSPORT_MASTER ) ;
tweakSkewForMTC ( 0 ) ;
m_mtcFirstTime = - 1 ;
break ;
case 2 :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden is MTC SLAVE "
< < std : : endl ;
# endif
setMTCStatus ( TRANSPORT_SLAVE ) ;
m_mtcFirstTime = - 1 ;
break ;
case 0 :
default :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " Rosegarden MTC Transport DISABLED "
< < std : : endl ;
# endif
setMTCStatus ( TRANSPORT_OFF ) ;
m_mtcFirstTime = - 1 ;
break ;
}
}
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemRecordDevice ) {
DeviceId recordDevice =
( DeviceId ) ( ( * i ) - > getData1 ( ) ) ;
bool conn = ( bool ) ( ( * i ) - > getData2 ( ) ) ;
// Unset connections
//
// unsetRecordDevices();
// Special case to set for all record ports
//
if ( recordDevice = = Device : : ALL_DEVICES ) {
/* set all record devices */
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " set all record devices - not implemented "
< < std : : endl ;
# endif
/*
MappedDeviceList : : iterator it = m_devices . begin ( ) ;
std : : vector < int > ports ;
std : : vector < int > : : iterator pIt ;
for ( ; it ! = m_devices . end ( ) ; + + it )
{
std : : cout < < " DEVICE = " < < ( * it ) - > getName ( ) < < " - DIR = "
< < ( * it ) - > getDirection ( ) < < endl ;
// ignore ports we can't connect to
if ( ( * it ) - > getDirection ( ) = = MidiDevice : : WriteOnly ) continue ;
std : : cout < < " PORTS = " < < ports . size ( ) < < endl ;
ports = ( * it ) - > getPorts ( ) ;
for ( pIt = ports . begin ( ) ; pIt ! = ports . end ( ) ; + + pIt )
{
setRecordDevice ( ( * it ) - > getClient ( ) , * pIt ) ;
}
}
*/
} else {
// Otherwise just for the one device and port
//
setRecordDevice ( recordDevice , conn ) ;
}
}
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemAudioPortCounts ) {
// never actually used, I think?
}
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemAudioPorts ) {
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
int data = ( * i ) - > getData1 ( ) ;
m_jackDriver - > setAudioPorts ( data & MappedEvent : : FaderOuts ,
data & MappedEvent : : SubmasterOuts ) ;
}
# else
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " MappedEvent::SystemAudioPorts - no audio subsystem "
< < std : : endl ;
# endif
# endif
}
if ( ( * i ) - > getType ( ) = = MappedEvent : : SystemAudioFileFormat ) {
# ifdef HAVE_LIBJACK
int format = ( * i ) - > getData1 ( ) ;
switch ( format ) {
case 0 :
m_audioRecFileFormat = RIFFAudioFile : : PCM ;
break ;
case 1 :
m_audioRecFileFormat = RIFFAudioFile : : FLOAT ;
break ;
default :
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " MappedEvent::SystemAudioFileFormat - unexpected format number " < < format
< < std : : endl ;
# endif
break ;
}
# else
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::processEventsOut - "
< < " MappedEvent::SystemAudioFileFormat - no audio subsystem "
< < std : : endl ;
# endif
# endif
}
if ( ( * i ) - > getType ( ) = = MappedEvent : : Panic ) {
for ( MappedDeviceList : : iterator i = m_devices . begin ( ) ;
i ! = m_devices . end ( ) ; + + i ) {
if ( ( * i ) - > getDirection ( ) = = MidiDevice : : Play ) {
sendDeviceController ( ( * i ) - > getId ( ) ,
MIDI_CONTROLLER_SUSTAIN , 0 ) ;
sendDeviceController ( ( * i ) - > getId ( ) ,
MIDI_CONTROLLER_ALL_NOTES_OFF , 0 ) ;
sendDeviceController ( ( * i ) - > getId ( ) ,
MIDI_CONTROLLER_RESET , 0 ) ;
}
}
}
}
// Process Midi and Audio
//
processMidiOut ( mC , sliceStart , sliceEnd ) ;
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
if ( haveNewAudio ) {
if ( now ) {
m_jackDriver - > prebufferAudio ( ) ;
m_jackDriver - > setHaveAsyncAudioEvent ( ) ;
}
if ( m_queueRunning ) {
m_jackDriver - > kickAudio ( ) ;
}
}
}
# endif
}
bool
AlsaDriver : : record ( RecordStatus recordStatus ,
const std : : vector < InstrumentId > * armedInstruments ,
const std : : vector < TQString > * audioFileNames )
{
m_recordingInstruments . clear ( ) ;
if ( recordStatus = = RECORD_ON ) {
// start recording
m_recordStatus = RECORD_ON ;
m_alsaRecordStartTime = RealTime : : zeroTime ;
unsigned int audioCount = 0 ;
if ( armedInstruments ) {
for ( unsigned int i = 0 ; i < armedInstruments - > size ( ) ; + + i ) {
InstrumentId id = ( * armedInstruments ) [ i ] ;
m_recordingInstruments . insert ( id ) ;
if ( ! audioFileNames | | ( audioCount > = audioFileNames - > size ( ) ) ) {
continue ;
}
TQString fileName = ( * audioFileNames ) [ audioCount ] ;
if ( id > = AudioInstrumentBase & &
id < MidiInstrumentBase ) {
bool good = false ;
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::record: Requesting new record file \" " < < fileName < < " \" for instrument " < < id < < std : : endl ;
# endif
# ifdef HAVE_LIBJACK
if ( m_jackDriver & &
m_jackDriver - > openRecordFile ( id , fileName . ascii ( ) ) ) {
good = true ;
}
# endif
if ( ! good ) {
m_recordStatus = RECORD_OFF ;
std : : cerr < < " AlsaDriver::record: No JACK driver, or JACK driver failed to prepare for recording audio " < < std : : endl ;
return false ;
}
+ + audioCount ;
}
}
}
} else
if ( recordStatus = = RECORD_OFF ) {
m_recordStatus = RECORD_OFF ;
}
# ifdef DEBUG_ALSA
else {
std : : cerr < < " AlsaDriver::record - unsupported recording mode "
< < std : : endl ;
}
# endif
return true ;
}
ClientPortPair
AlsaDriver : : getFirstDestination ( bool duplex )
{
ClientPortPair destPair ( - 1 , - 1 ) ;
AlsaPortList : : iterator it ;
for ( it = m_alsaPorts . begin ( ) ; it ! = m_alsaPorts . end ( ) ; + + it ) {
destPair . first = ( * it ) - > m_client ;
destPair . second = ( * it ) - > m_port ;
// If duplex port is required then choose first one
//
if ( duplex ) {
if ( ( * it ) - > m_direction = = Duplex )
return destPair ;
} else {
// If duplex port isn't required then choose first
// specifically non-duplex port (should be a synth)
//
if ( ( * it ) - > m_direction ! = Duplex )
return destPair ;
}
}
return destPair ;
}
// Sort through the ALSA client/port pairs for the range that
// matches the one we're querying. If none matches then send
// back -1 for each.
//
ClientPortPair
AlsaDriver : : getPairForMappedInstrument ( InstrumentId id )
{
MappedInstrument * instrument = getMappedInstrument ( id ) ;
if ( instrument ) {
DeviceId device = instrument - > getDevice ( ) ;
DevicePortMap : : iterator i = m_devicePortMap . find ( device ) ;
if ( i ! = m_devicePortMap . end ( ) ) {
return i - > second ;
}
}
# ifdef DEBUG_ALSA
/*
else
{
cerr < < " WARNING: AlsaDriver::getPairForMappedInstrument: couldn't find instrument for id " < < id < < " , falling through " < < endl ;
}
*/
# endif
return ClientPortPair ( - 1 , - 1 ) ;
}
int
AlsaDriver : : getOutputPortForMappedInstrument ( InstrumentId id )
{
MappedInstrument * instrument = getMappedInstrument ( id ) ;
if ( instrument ) {
DeviceId device = instrument - > getDevice ( ) ;
DeviceIntMap : : iterator i = m_outputPorts . find ( device ) ;
if ( i ! = m_outputPorts . end ( ) ) {
return i - > second ;
}
# ifdef DEBUG_ALSA
else {
cerr < < " WARNING: AlsaDriver::getOutputPortForMappedInstrument: couldn't find output port for device for instrument " < < id < < " , falling through " < < endl ;
}
# endif
}
return - 1 ;
}
// Send a direct controller to the specified port/client
//
void
AlsaDriver : : sendDeviceController ( DeviceId device ,
MidiByte controller ,
MidiByte value )
{
snd_seq_event_t event ;
snd_seq_ev_clear ( & event ) ;
snd_seq_ev_set_subs ( & event ) ;
DeviceIntMap : : iterator dimi = m_outputPorts . find ( device ) ;
if ( dimi = = m_outputPorts . end ( ) )
return ;
snd_seq_ev_set_source ( & event , dimi - > second ) ;
snd_seq_ev_set_direct ( & event ) ;
for ( int i = 0 ; i < 16 ; i + + ) {
snd_seq_ev_set_controller ( & event ,
i ,
controller ,
value ) ;
snd_seq_event_output_direct ( m_midiHandle , & event ) ;
}
// we probably don't need this:
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " sendDeviceController(): draining " ) ;
}
void
AlsaDriver : : processPending ( )
{
if ( ! m_playing ) {
processNotesOff ( getAlsaTime ( ) , true ) ;
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " processPending(): draining " ) ;
}
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
m_jackDriver - > updateAudioData ( ) ;
}
# endif
scavengePlugins ( ) ;
m_audioQueueScavenger . scavenge ( ) ;
}
void
AlsaDriver : : insertMappedEventForReturn ( MappedEvent * mE )
{
// Insert the event ready for return at the next opportunity.
//
m_returnComposition . insert ( mE ) ;
}
// check for recording status on any ALSA Port
//
bool
AlsaDriver : : isRecording ( AlsaPortDescription * port )
{
if ( port - > isReadable ( ) ) {
snd_seq_query_subscribe_t * qSubs ;
snd_seq_addr_t rg_addr , sender_addr ;
snd_seq_query_subscribe_alloca ( & qSubs ) ;
rg_addr . client = m_client ;
rg_addr . port = m_inputPort ;
snd_seq_query_subscribe_set_type ( qSubs , SND_SEQ_QUERY_SUBS_WRITE ) ;
snd_seq_query_subscribe_set_index ( qSubs , 0 ) ;
snd_seq_query_subscribe_set_root ( qSubs , & rg_addr ) ;
while ( snd_seq_query_port_subscribers ( m_midiHandle , qSubs ) > = 0 ) {
sender_addr = * snd_seq_query_subscribe_get_addr ( qSubs ) ;
if ( sender_addr . client = = port - > m_client & &
sender_addr . port = = port - > m_port )
return true ;
snd_seq_query_subscribe_set_index ( qSubs ,
snd_seq_query_subscribe_get_index ( qSubs ) + 1 ) ;
}
}
return false ;
}
bool
AlsaDriver : : checkForNewClients ( )
{
Audit audit ;
bool madeChange = false ;
if ( ! m_portCheckNeeded )
return false ;
AlsaPortList newPorts ;
generatePortList ( & newPorts ) ; // updates m_alsaPorts, returns new ports as well
// If any devices have connections that no longer exist,
// clear those connections and stick them in the suspended
// port map in case they come back online later.
for ( MappedDeviceList : : iterator i = m_devices . begin ( ) ;
i ! = m_devices . end ( ) ; + + i ) {
ClientPortPair pair ( m_devicePortMap [ ( * i ) - > getId ( ) ] ) ;
bool found = false ;
for ( AlsaPortList : : iterator j = m_alsaPorts . begin ( ) ;
j ! = m_alsaPorts . end ( ) ; + + j ) {
if ( ( * j ) - > m_client = = pair . first & &
( * j ) - > m_port = = pair . second ) {
if ( ( * i ) - > getDirection ( ) = = MidiDevice : : Record ) {
bool recState = isRecording ( * j ) ;
if ( recState ! = ( * i ) - > isRecording ( ) ) {
madeChange = true ;
( * i ) - > setRecording ( recState ) ;
}
} else {
( * i ) - > setRecording ( false ) ;
}
found = true ;
break ;
}
}
if ( ! found ) {
m_suspendedPortMap [ pair ] = ( * i ) - > getId ( ) ;
m_devicePortMap [ ( * i ) - > getId ( ) ] = ClientPortPair ( - 1 , - 1 ) ;
setConnectionToDevice ( * * i , " " ) ;
( * i ) - > setRecording ( false ) ;
madeChange = true ;
}
}
// If we've increased the number of connections, we need
// to assign the new connections to existing devices that
// have none, where possible, and create new devices for
// any left over.
if ( newPorts . size ( ) > 0 ) {
audit < < " New ports: " < < std : : endl ;
for ( AlsaPortList : : iterator i = newPorts . begin ( ) ;
i ! = newPorts . end ( ) ; + + i ) {
if ( ( * i ) - > m_client = = m_client ) {
audit < < " (Ignoring own port " < < ( * i ) - > m_client < < " : " < < ( * i ) - > m_port < < " ) " < < std : : endl ;
continue ;
} else if ( ( * i ) - > m_client = = 0 ) {
audit < < " (Ignoring system port " < < ( * i ) - > m_client < < " : " < < ( * i ) - > m_port < < " ) " < < std : : endl ;
continue ;
}
audit < < ( * i ) - > m_name < < std : : endl ;
TQString portName = ( * i ) - > m_name . c_str ( ) ;
ClientPortPair portPair = ClientPortPair ( ( * i ) - > m_client ,
( * i ) - > m_port ) ;
if ( m_suspendedPortMap . find ( portPair ) ! = m_suspendedPortMap . end ( ) ) {
DeviceId id = m_suspendedPortMap [ portPair ] ;
audit < < " (Reusing suspended device " < < id < < " ) " < < std : : endl ;
for ( MappedDeviceList : : iterator j = m_devices . begin ( ) ;
j ! = m_devices . end ( ) ; + + j ) {
if ( ( * j ) - > getId ( ) = = id ) {
setConnectionToDevice ( * * j , portName ) ;
}
}
m_suspendedPortMap . erase ( m_suspendedPortMap . find ( portPair ) ) ;
m_devicePortMap [ id ] = portPair ;
madeChange = true ;
continue ;
}
bool needPlayDevice = true , needRecordDevice = true ;
if ( ( * i ) - > isReadable ( ) ) {
for ( MappedDeviceList : : iterator j = m_devices . begin ( ) ;
j ! = m_devices . end ( ) ; + + j ) {
if ( ( * j ) - > getType ( ) = = Device : : Midi & &
( * j ) - > getConnection ( ) = = " " & &
( * j ) - > getDirection ( ) = = MidiDevice : : Record ) {
audit < < " (Reusing record device " < < ( * j ) - > getId ( )
< < " ) " < < std : : endl ;
m_devicePortMap [ ( * j ) - > getId ( ) ] = portPair ;
setConnectionToDevice ( * * j , portName ) ;
needRecordDevice = false ;
madeChange = true ;
break ;
}
}
} else {
needRecordDevice = false ;
}
if ( ( * i ) - > isWriteable ( ) ) {
for ( MappedDeviceList : : iterator j = m_devices . begin ( ) ;
j ! = m_devices . end ( ) ; + + j ) {
if ( ( * j ) - > getType ( ) = = Device : : Midi & &
( * j ) - > getConnection ( ) = = " " & &
( * j ) - > getDirection ( ) = = MidiDevice : : Play ) {
audit < < " (Reusing play device " < < ( * j ) - > getId ( )
< < " ) " < < std : : endl ;
m_devicePortMap [ ( * j ) - > getId ( ) ] = portPair ;
setConnectionToDevice ( * * j , portName ) ;
needPlayDevice = false ;
madeChange = true ;
break ;
}
}
} else {
needPlayDevice = false ;
}
if ( needRecordDevice ) {
MappedDevice * device = createMidiDevice ( * i , MidiDevice : : Record ) ;
if ( ! device ) {
# ifdef DEBUG_ALSA
std : : cerr < < " WARNING: Failed to create record device " < < std : : endl ;
# else
;
# endif
} else {
audit < < " (Created new record device " < < device - > getId ( ) < < " ) " < < std : : endl ;
addInstrumentsForDevice ( device ) ;
m_devices . push_back ( device ) ;
madeChange = true ;
}
}
if ( needPlayDevice ) {
MappedDevice * device = createMidiDevice ( * i , MidiDevice : : Play ) ;
if ( ! device ) {
# ifdef DEBUG_ALSA
std : : cerr < < " WARNING: Failed to create play device " < < std : : endl ;
# else
;
# endif
} else {
audit < < " (Created new play device " < < device - > getId ( ) < < " ) " < < std : : endl ;
addInstrumentsForDevice ( device ) ;
m_devices . push_back ( device ) ;
madeChange = true ;
}
}
}
}
// If one of our ports is connected to a single other port and
// it isn't the one we thought, we should update our connection
for ( MappedDeviceList : : iterator i = m_devices . begin ( ) ;
i ! = m_devices . end ( ) ; + + i ) {
DevicePortMap : : iterator j = m_devicePortMap . find ( ( * i ) - > getId ( ) ) ;
snd_seq_addr_t addr ;
addr . client = m_client ;
DeviceIntMap : : iterator ii = m_outputPorts . find ( ( * i ) - > getId ( ) ) ;
if ( ii = = m_outputPorts . end ( ) )
continue ;
addr . port = ii - > second ;
snd_seq_query_subscribe_t * subs ;
snd_seq_query_subscribe_alloca ( & subs ) ;
snd_seq_query_subscribe_set_root ( subs , & addr ) ;
snd_seq_query_subscribe_set_index ( subs , 0 ) ;
bool haveOurs = false ;
int others = 0 ;
ClientPortPair firstOther ;
while ( ! snd_seq_query_port_subscribers ( m_midiHandle , subs ) ) {
const snd_seq_addr_t * otherEnd =
snd_seq_query_subscribe_get_addr ( subs ) ;
if ( ! otherEnd )
continue ;
if ( j ! = m_devicePortMap . end ( ) & &
otherEnd - > client = = j - > second . first & &
otherEnd - > port = = j - > second . second ) {
haveOurs = true ;
} else {
+ + others ;
firstOther = ClientPortPair ( otherEnd - > client , otherEnd - > port ) ;
}
snd_seq_query_subscribe_set_index
( subs , snd_seq_query_subscribe_get_index ( subs ) + 1 ) ;
}
if ( haveOurs ) { // leave our own connection alone, and stop worrying
continue ;
} else {
if ( others = = 0 ) {
if ( j ! = m_devicePortMap . end ( ) ) {
j - > second = ClientPortPair ( - 1 , - 1 ) ;
setConnectionToDevice ( * * i , " " ) ;
madeChange = true ;
}
} else {
for ( AlsaPortList : : iterator k = m_alsaPorts . begin ( ) ;
k ! = m_alsaPorts . end ( ) ; + + k ) {
if ( ( * k ) - > m_client = = firstOther . first & &
( * k ) - > m_port = = firstOther . second ) {
m_devicePortMap [ ( * i ) - > getId ( ) ] = firstOther ;
setConnectionToDevice ( * * i , ( * k ) - > m_name . c_str ( ) ,
firstOther ) ;
madeChange = true ;
break ;
}
}
}
}
}
if ( madeChange ) {
MappedEvent * mE =
new MappedEvent ( 0 , MappedEvent : : SystemUpdateInstruments ,
0 , 0 ) ;
// send completion event
insertMappedEventForReturn ( mE ) ;
}
m_portCheckNeeded = false ;
return true ;
}
// From a DeviceId get a client/port pair for connecting as the
// MIDI record device.
//
void
AlsaDriver : : setRecordDevice ( DeviceId id , bool connectAction )
{
Audit audit ;
// Locate a suitable port
//
if ( m_devicePortMap . find ( id ) = = m_devicePortMap . end ( ) ) {
# ifdef DEBUG_ALSA
audit < < " AlsaDriver::setRecordDevice - "
< < " couldn't match device id ( " < < id < < " ) to ALSA port "
< < std : : endl ;
# endif
return ;
}
ClientPortPair pair = m_devicePortMap [ id ] ;
snd_seq_addr_t sender , dest ;
sender . client = pair . first ;
sender . port = pair . second ;
for ( MappedDeviceList : : iterator i = m_devices . begin ( ) ;
i ! = m_devices . end ( ) ; + + i ) {
if ( ( * i ) - > getId ( ) = = id ) {
if ( ( * i ) - > getDirection ( ) = = MidiDevice : : Record ) {
if ( ( * i ) - > isRecording ( ) & & connectAction ) {
# ifdef DEBUG_ALSA
audit < < " AlsaDriver::setRecordDevice - "
< < " attempting to subscribe ( " < < id
< < " ) already subscribed " < < std : : endl ;
# endif
return ;
}
if ( ! ( * i ) - > isRecording ( ) & & ! connectAction ) {
# ifdef DEBUG_ALSA
audit < < " AlsaDriver::setRecordDevice - "
< < " attempting to unsubscribe ( " < < id
< < " ) already unsubscribed " < < std : : endl ;
# endif
return ;
}
} else {
# ifdef DEBUG_ALSA
audit < < " AlsaDriver::setRecordDevice - "
< < " attempting to set play device ( " < < id
< < " ) to record device " < < std : : endl ;
# endif
return ;
}
break ;
}
}
snd_seq_port_subscribe_t * subs ;
snd_seq_port_subscribe_alloca ( & subs ) ;
dest . client = m_client ;
dest . port = m_inputPort ;
// Set destinations and senders
//
snd_seq_port_subscribe_set_sender ( subs , & sender ) ;
snd_seq_port_subscribe_set_dest ( subs , & dest ) ;
// subscribe or unsubscribe the port
//
if ( connectAction ) {
if ( checkAlsaError ( snd_seq_subscribe_port ( m_midiHandle , subs ) ,
" setRecordDevice - failed subscription of input port " ) < 0 ) {
// Not the end of the world if this fails but we
// have to flag it internally.
//
audit < < " AlsaDriver::setRecordDevice - "
< < int ( sender . client ) < < " : " < < int ( sender . port )
< < " failed to subscribe device "
< < id < < " as record port " < < std : : endl ;
} else {
m_midiInputPortConnected = true ;
audit < < " AlsaDriver::setRecordDevice - "
< < " successfully subscribed device "
< < id < < " as record port " < < std : : endl ;
}
} else {
if ( checkAlsaError ( snd_seq_unsubscribe_port ( m_midiHandle , subs ) ,
" setRecordDevice - failed to unsubscribe a device " ) = = 0 )
audit < < " AlsaDriver::setRecordDevice - "
< < " successfully unsubscribed device "
< < id < < " as record port " < < std : : endl ;
}
}
// Clear any record device connections
//
void
AlsaDriver : : unsetRecordDevices ( )
{
snd_seq_addr_t dest ;
dest . client = m_client ;
dest . port = m_inputPort ;
snd_seq_query_subscribe_t * qSubs ;
snd_seq_addr_t tmp_addr ;
snd_seq_query_subscribe_alloca ( & qSubs ) ;
tmp_addr . client = m_client ;
tmp_addr . port = m_inputPort ;
// Unsubsribe any existing connections
//
snd_seq_query_subscribe_set_type ( qSubs , SND_SEQ_QUERY_SUBS_WRITE ) ;
snd_seq_query_subscribe_set_index ( qSubs , 0 ) ;
snd_seq_query_subscribe_set_root ( qSubs , & tmp_addr ) ;
while ( snd_seq_query_port_subscribers ( m_midiHandle , qSubs ) > = 0 ) {
tmp_addr = * snd_seq_query_subscribe_get_addr ( qSubs ) ;
snd_seq_port_subscribe_t * dSubs ;
snd_seq_port_subscribe_alloca ( & dSubs ) ;
snd_seq_addr_t dSender ;
dSender . client = tmp_addr . client ;
dSender . port = tmp_addr . port ;
snd_seq_port_subscribe_set_sender ( dSubs , & dSender ) ;
snd_seq_port_subscribe_set_dest ( dSubs , & dest ) ;
int error = snd_seq_unsubscribe_port ( m_midiHandle , dSubs ) ;
if ( error < 0 ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::unsetRecordDevices - "
< < " can't unsubscribe record port " < < std : : endl ;
# endif
}
snd_seq_query_subscribe_set_index ( qSubs ,
snd_seq_query_subscribe_get_index ( qSubs ) + 1 ) ;
}
}
void
AlsaDriver : : sendMMC ( MidiByte deviceArg ,
MidiByte instruction ,
bool isCommand ,
const std : : string & data )
{
snd_seq_event_t event ;
snd_seq_ev_clear ( & event ) ;
snd_seq_ev_set_source ( & event , m_syncOutputPort ) ;
snd_seq_ev_set_subs ( & event ) ;
unsigned char dataArr [ 10 ] =
{ MIDI_SYSTEM_EXCLUSIVE ,
MIDI_SYSEX_RT , deviceArg ,
( isCommand ? MIDI_SYSEX_RT_COMMAND : MIDI_SYSEX_RT_RESPONSE ) ,
instruction } ;
std : : string dataString = std : : string ( ( const char * ) dataArr ) +
data + ( char ) MIDI_END_OF_EXCLUSIVE ;
snd_seq_ev_set_sysex ( & event , dataString . length ( ) ,
( char * ) dataString . c_str ( ) ) ;
event . queue = SND_SEQ_QUEUE_DIRECT ;
checkAlsaError ( snd_seq_event_output_direct ( m_midiHandle , & event ) ,
" sendMMC event send " ) ;
if ( m_queueRunning ) {
checkAlsaError ( snd_seq_drain_output ( m_midiHandle ) , " sendMMC drain " ) ;
}
}
// Send a system real-time message from the sync output port
//
void
AlsaDriver : : sendSystemDirect ( MidiByte command , int * args )
{
snd_seq_event_t event ;
snd_seq_ev_clear ( & event ) ;
snd_seq_ev_set_source ( & event , m_syncOutputPort ) ;
snd_seq_ev_set_subs ( & event ) ;
event . queue = SND_SEQ_QUEUE_DIRECT ;
// set the command
event . type = command ;
// set args if we have them
if ( args ) {
event . data . control . value = * args ;
}
int error = snd_seq_event_output_direct ( m_midiHandle , & event ) ;
if ( error < 0 ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::sendSystemDirect - "
< < " can't send event ( " < < int ( command ) < < " ) "
< < std : : endl ;
# endif
}
// checkAlsaError(snd_seq_drain_output(m_midiHandle),
// "sendSystemDirect(): draining");
}
void
AlsaDriver : : sendSystemQueued ( MidiByte command ,
const std : : string & args ,
const RealTime & time )
{
snd_seq_event_t event ;
snd_seq_ev_clear ( & event ) ;
snd_seq_ev_set_source ( & event , m_syncOutputPort ) ;
snd_seq_ev_set_subs ( & event ) ;
snd_seq_real_time_t sendTime = { ( unsigned int ) time . sec , ( unsigned int ) time . nsec } ;
// Schedule the command
//
event . type = command ;
snd_seq_ev_schedule_real ( & event , m_queue , 0 , & sendTime ) ;
// set args if we have them
switch ( args . length ( ) ) {
case 1 :
event . data . control . value = args [ 0 ] ;
break ;
case 2 :
event . data . control . value = int ( args [ 0 ] ) | ( int ( args [ 1 ] ) < < 7 ) ;
break ;
default : // do nothing
break ;
}
int error = snd_seq_event_output ( m_midiHandle , & event ) ;
if ( error < 0 ) {
# ifdef DEBUG_ALSA
std : : cerr < < " AlsaDriver::sendSystemQueued - "
< < " can't send event ( " < < int ( command ) < < " ) "
< < " - error = ( " < < error < < " ) "
< < std : : endl ;
# endif
}
// if (m_queueRunning) {
// checkAlsaError(snd_seq_drain_output(m_midiHandle), "sendSystemQueued(): draining");
// }
}
void
AlsaDriver : : claimUnwantedPlugin ( void * plugin )
{
m_pluginScavenger . claim ( ( RunnablePluginInstance * ) plugin ) ;
}
void
AlsaDriver : : scavengePlugins ( )
{
m_pluginScavenger . scavenge ( ) ;
}
TQString
AlsaDriver : : getStatusLog ( )
{
return TQString : : fromUtf8 ( Audit : : getAudit ( ) . c_str ( ) ) ;
}
void
AlsaDriver : : sleep ( const RealTime & rt )
{
int npfd = snd_seq_poll_descriptors_count ( m_midiHandle , POLLIN ) ;
struct pollfd * pfd = ( struct pollfd * ) alloca ( npfd * sizeof ( struct pollfd ) ) ;
snd_seq_poll_descriptors ( m_midiHandle , pfd , npfd , POLLIN ) ;
poll ( pfd , npfd , rt . sec * 1000 + rt . msec ( ) ) ;
}
void
AlsaDriver : : runTasks ( )
{
# ifdef HAVE_LIBJACK
if ( m_jackDriver ) {
if ( ! m_jackDriver - > isOK ( ) ) {
m_jackDriver - > restoreIfRestorable ( ) ;
}
}
if ( m_doTimerChecks & & m_timerRatioCalculated ) {
double ratio = m_timerRatio ;
m_timerRatioCalculated = false ;
snd_seq_queue_tempo_t * q_ptr ;
snd_seq_queue_tempo_alloca ( & q_ptr ) ;
snd_seq_get_queue_tempo ( m_midiHandle , m_queue , q_ptr ) ;
unsigned int t_skew = snd_seq_queue_tempo_get_skew ( q_ptr ) ;
# ifdef DEBUG_ALSA
unsigned int t_base = snd_seq_queue_tempo_get_skew_base ( q_ptr ) ;
if ( ! m_playing ) {
std : : cerr < < " Skew: " < < t_skew < < " / " < < t_base ;
}
# endif
unsigned int newSkew = t_skew + ( unsigned int ) ( t_skew * ratio ) ;
if ( newSkew ! = t_skew ) {
# ifdef DEBUG_ALSA
if ( ! m_playing ) {
std : : cerr < < " changed to " < < newSkew < < endl ;
}
# endif
snd_seq_queue_tempo_set_skew ( q_ptr , newSkew ) ;
snd_seq_set_queue_tempo ( m_midiHandle , m_queue , q_ptr ) ;
} else {
# ifdef DEBUG_ALSA
if ( ! m_playing ) {
std : : cerr < < endl ;
}
# endif
}
m_firstTimerCheck = true ;
}
# endif
}
void
AlsaDriver : : reportFailure ( MappedEvent : : FailureCode code )
{
//#define REPORT_XRUNS 1
# ifndef REPORT_XRUNS
if ( code = = MappedEvent : : FailureXRuns | |
code = = MappedEvent : : FailureDiscUnderrun | |
code = = MappedEvent : : FailureBussMixUnderrun | |
code = = MappedEvent : : FailureMixUnderrun ) {
return ;
}
# endif
// Ignore consecutive duplicates
if ( _failureReportWriteIndex > 0 & &
_failureReportWriteIndex ! = _failureReportReadIndex ) {
if ( code = = _failureReports [ _failureReportWriteIndex - 1 ] )
return ;
}
_failureReports [ _failureReportWriteIndex ] = code ;
_failureReportWriteIndex =
( _failureReportWriteIndex + 1 ) % FAILURE_REPORT_COUNT ;
}
std : : string
AlsaDriver : : getAlsaModuleVersionString ( )
{
FILE * v = fopen ( " /proc/asound/version " , " r " ) ;
// Examples:
// Advanced Linux Sound Architecture Driver Version 1.0.14rc3.
// Advanced Linux Sound Architecture Driver Version 1.0.14 (Thu May 31 09:03:25 2008 UTC).
if ( v ) {
char buf [ 256 ] ;
fgets ( buf , 256 , v ) ;
fclose ( v ) ;
std : : string vs ( buf ) ;
std : : string : : size_type sp = vs . find_first_of ( ' . ' ) ;
if ( sp > 0 & & sp ! = std : : string : : npos ) {
while ( sp > 0 & & isdigit ( vs [ sp - 1 ] ) ) - - sp ;
vs = vs . substr ( sp ) ;
if ( vs . length ( ) > 0 & & vs [ vs . length ( ) - 1 ] = = ' \n ' ) {
vs = vs . substr ( 0 , vs . length ( ) - 1 ) ;
}
if ( vs . length ( ) > 0 & & vs [ vs . length ( ) - 1 ] = = ' . ' ) {
vs = vs . substr ( 0 , vs . length ( ) - 1 ) ;
}
return vs ;
}
}
return " (unknown) " ;
}
std : : string
AlsaDriver : : getKernelVersionString ( )
{
FILE * v = fopen ( " /proc/version " , " r " ) ;
if ( v ) {
char buf [ 256 ] ;
fgets ( buf , 256 , v ) ;
fclose ( v ) ;
std : : string vs ( buf ) ;
std : : string key ( " version " ) ;
std : : string : : size_type sp = vs . find ( key ) ;
if ( sp ! = std : : string : : npos ) {
vs = vs . substr ( sp + key . length ( ) ) ;
sp = vs . find ( ' ' ) ;
if ( sp ! = std : : string : : npos ) {
vs = vs . substr ( 0 , sp ) ;
}
if ( vs . length ( ) > 0 & & vs [ vs . length ( ) - 1 ] = = ' \n ' ) {
vs = vs . substr ( 0 , vs . length ( ) - 1 ) ;
}
return vs ;
}
}
return " (unknown) " ;
}
void
AlsaDriver : : extractVersion ( std : : string v , int & major , int & minor , int & subminor , std : : string & suffix )
{
major = minor = subminor = 0 ;
suffix = " " ;
if ( v = = " (unknown) " ) return ;
std : : string : : size_type sp , pp ;
sp = v . find ( ' . ' ) ;
if ( sp = = std : : string : : npos ) goto done ;
major = atoi ( v . substr ( 0 , sp ) . c_str ( ) ) ;
pp = sp + 1 ;
sp = v . find ( ' . ' , pp ) ;
if ( sp = = std : : string : : npos ) goto done ;
minor = atoi ( v . substr ( pp , sp - pp ) . c_str ( ) ) ;
pp = sp + 1 ;
while ( + + sp < v . length ( ) & & ( : : isdigit ( v [ sp ] ) | | v [ sp ] = = ' - ' ) ) ;
subminor = atoi ( v . substr ( pp , sp - pp ) . c_str ( ) ) ;
if ( sp > = v . length ( ) ) goto done ;
suffix = v . substr ( sp ) ;
done :
std : : cerr < < " extractVersion: major = " < < major < < " , minor = " < < minor < < " , subminor = " < < subminor < < " , suffix = \" " < < suffix < < " \" " < < std : : endl ;
}
bool
AlsaDriver : : versionIsAtLeast ( std : : string v , int major , int minor , int subminor )
{
int actualMajor , actualMinor , actualSubminor ;
std : : string actualSuffix ;
extractVersion ( v , actualMajor , actualMinor , actualSubminor , actualSuffix ) ;
bool ok = false ;
if ( actualMajor > major ) {
ok = true ;
} else if ( actualMajor = = major ) {
if ( actualMinor > minor ) {
ok = true ;
} else if ( actualMinor = = minor ) {
if ( actualSubminor > subminor ) {
ok = true ;
} else if ( actualSubminor = = subminor ) {
if ( strncmp ( actualSuffix . c_str ( ) , " rc " , 2 ) & &
strncmp ( actualSuffix . c_str ( ) , " pre " , 3 ) ) {
ok = true ;
}
}
}
}
std : : cerr < < " AlsaDriver::versionIsAtLeast: is version " < < v < < " at least " < < major < < " . " < < minor < < " . " < < subminor < < " ? " < < ( ok ? " yes " : " no " ) < < std : : endl ;
return ok ;
}
}
# endif // HAVE_ALSA