2024-12-01 19:35:24 +00:00
//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
2019-12-24 15:05:24 +00:00
# include "cwCommon.h"
# include "cwLog.h"
# include "cwCommonImpl.h"
2024-05-29 16:36:57 +00:00
# include "cwTest.h"
2019-12-24 15:05:24 +00:00
# include "cwMem.h"
# include "cwTime.h"
2024-02-14 16:20:40 +00:00
# include "cwObject.h"
# include "cwText.h"
2019-12-24 15:05:24 +00:00
# include "cwTextBuf.h"
2024-02-14 16:20:40 +00:00
# include "cwThread.h"
# include "cwMidi.h"
# include "cwMidiDecls.h"
# include "cwMidiFile.h"
# include "cwMidiDevice.h"
# include <poll.h>
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
# include "cwMidiAlsa.h"
# include "cwMidiFileDev.h"
2019-12-24 15:05:24 +00:00
namespace cw
{
namespace midi
{
2024-02-14 16:20:40 +00:00
namespace device
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
typedef enum {
kStoppedStateId ,
kPausedStateId ,
kPlayingStateId
} transportStateId_t ;
2024-04-06 19:44:43 +00:00
2024-02-14 16:20:40 +00:00
typedef struct device_str
{
cbFunc_t cbFunc ;
void * cbArg ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
alsa : : handle_t alsaDevH ;
unsigned alsaPollfdN ;
struct pollfd * alsaPollfdA ;
unsigned alsa_dev_cnt ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
file_dev : : handle_t fileDevH ;
unsigned file_dev_cnt ;
unsigned total_dev_cnt ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
unsigned thread_timeout_microsecs ;
thread : : handle_t threadH ;
transportStateId_t fileDevStateId ;
unsigned long long offset_micros ;
unsigned long long last_posn_micros ;
time : : spec_t start_time ;
2024-04-06 19:44:43 +00:00
ch_msg_t * buf ;
unsigned bufN ;
std : : atomic < unsigned > buf_ii ;
std : : atomic < unsigned > buf_oi ;
2024-07-08 20:56:33 +00:00
bool filterRtSenseFl ;
2024-02-14 16:20:40 +00:00
} device_t ;
device_t * _handleToPtr ( handle_t h )
{ return handleToPtr < handle_t , device_t > ( h ) ; }
rc_t _validate_dev_index ( device_t * p , unsigned devIdx )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
rc_t rc = kOkRC ;
if ( devIdx > = p - > total_dev_cnt )
rc = cwLogError ( kInvalidArgRC , " Invalid MIDI device index (%i >= %i). " , devIdx , p - > total_dev_cnt ) ;
return rc ;
}
unsigned _devIdxToAlsaDevIdx ( device_t * p , unsigned devIdx )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
return devIdx > = p - > alsa_dev_cnt ? kInvalidIdx : devIdx ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
unsigned _devIdxToFileDevIdx ( device_t * p , unsigned devIdx )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
return devIdx = = kInvalidIdx | | devIdx < p - > alsa_dev_cnt ? kInvalidIdx : devIdx - p - > alsa_dev_cnt ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
unsigned _alsaDevIdxToDevIdx ( device_t * p , unsigned alsaDevIdx )
{ return alsaDevIdx ; }
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
unsigned _fileDevIdxToDevIdx ( device_t * p , unsigned fileDevIdx )
{ return fileDevIdx = = kInvalidIdx ? kInvalidIdx : p - > alsa_dev_cnt + fileDevIdx ; }
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
bool _isAlsaDevIdx ( device_t * p , unsigned devIdx )
{ return devIdx = = kInvalidIdx ? false : devIdx < p - > alsa_dev_cnt ; }
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
bool _isFileDevIdx ( device_t * p , unsigned devIdx )
{ return devIdx = = kInvalidIdx ? false : ( p - > alsa_dev_cnt < = devIdx & & devIdx < p - > total_dev_cnt ) ; }
rc_t _destroy ( device_t * p )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
rc_t rc = kOkRC ;
if ( ( rc = destroy ( p - > threadH ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " MIDI port thread destroy failed. " ) ;
goto errLabel ;
}
2024-04-06 19:44:43 +00:00
2024-02-14 16:20:40 +00:00
destroy ( p - > alsaDevH ) ;
destroy ( p - > fileDevH ) ;
2024-04-06 19:44:43 +00:00
mem : : release ( p - > buf ) ;
2024-02-14 16:20:40 +00:00
mem : : release ( p ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
errLabel :
return rc ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
bool _thread_func ( void * arg )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
device_t * p = ( device_t * ) arg ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
unsigned max_sleep_micros = p - > thread_timeout_microsecs / 2 ;
unsigned sleep_millis = max_sleep_micros / 1000 ;
if ( p - > fileDevStateId = = kPlayingStateId )
{
time : : spec_t cur_time = time : : current_time ( ) ;
unsigned elapsed_micros = time : : elapsedMicros ( p - > start_time , cur_time ) ;
unsigned long long file_posn_micros = p - > offset_micros + elapsed_micros ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
// Send any messages whose time has expired and get the
// wait time for the next message.
file_dev : : exec_result_t r = exec ( p - > fileDevH , file_posn_micros ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
// If the file dev has no more messages to play then sleep for the maximum time.
unsigned file_dev_sleep_micros = r . eof_fl ? max_sleep_micros : r . next_msg_wait_micros ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
// Prevent the wait time from being longer than the thread state change timeout.
unsigned sleep_micros = std : : min ( max_sleep_micros , file_dev_sleep_micros ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
p - > last_posn_micros = file_posn_micros + sleep_micros ;
// If the wait time is less than one millisecond then make it one millisecond.
// (remember that we allowed the file device to go 3 milliseconds ahead and
// and so it is safe, and better for preventing many very short timeout's,
// to wait at least 1 millisecond)
sleep_millis = std : : max ( 1U , sleep_micros / 1000 ) ;
}
2024-02-21 12:47:09 +00:00
// TODO: Consider replacing the poll() with epoll_wait2() is apparently
// optimized for more accurate time outs.
2024-02-14 16:20:40 +00:00
// Block here waiting for ALSA events or timeout when the next file msg should be sent
int sysRC = poll ( p - > alsaPollfdA , p - > alsaPollfdN , sleep_millis ) ;
if ( sysRC = = 0 )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
// time-out
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
else
{
if ( sysRC > 0 )
{
rc_t rc ;
if ( ( rc = handleInputMsg ( p - > alsaDevH ) ) ! = kOkRC )
{
cwLogError ( rc , " ALSA MIDI dev. input failed " ) ;
}
}
else
{
cwLogSysError ( kOpFailRC , sysRC , " MIDI device poll failed. " ) ;
}
}
return true ;
}
2019-12-24 15:05:24 +00:00
2024-04-06 19:44:43 +00:00
void _callback ( void * cbArg , const packet_t * pktArray , unsigned pktCnt )
{
device_t * p = ( device_t * ) cbArg ;
for ( unsigned i = 0 ; i < pktCnt ; + + i )
{
const packet_t * pkt = pktArray + i ;
if ( pkt - > msgArray ! = nullptr )
{
unsigned ii = p - > buf_ii . load ( ) ;
unsigned oi = p - > buf_oi . load ( ) ;
for ( unsigned j = 0 ; j < pkt - > msgCnt ; + + j )
{
ch_msg_t * m = p - > buf + ii ;
m - > devIdx = pkt - > devIdx ;
m - > portIdx = pkt - > portIdx ;
m - > timeStamp = pkt - > msgArray [ j ] . timeStamp ;
m - > uid = pkt - > msgArray [ j ] . uid ;
m - > ch = pkt - > msgArray [ j ] . ch ;
m - > status = pkt - > msgArray [ j ] . status ;
m - > d0 = pkt - > msgArray [ j ] . d0 ;
m - > d1 = pkt - > msgArray [ j ] . d1 ;
ii = ( ii + 1 = = p - > bufN ? 0 : ii + 1 ) ;
if ( ii = = oi )
{
cwLogError ( kBufTooSmallRC , " The MIDI device buffer is full %i. " , p - > bufN ) ;
}
}
p - > buf_ii . store ( ii ) ;
}
}
if ( p - > cbFunc ! = nullptr )
p - > cbFunc ( p - > cbArg , pktArray , pktCnt ) ;
}
2024-02-14 16:20:40 +00:00
} // device
} // midi
} // cw
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : create ( handle_t & hRef ,
cbFunc_t cbFunc ,
void * cbArg ,
const char * filePortLabelA [ ] ,
unsigned max_file_cnt ,
const char * appNameStr ,
const char * fileDevName ,
unsigned fileDevReadAheadMicros ,
2024-04-06 19:44:43 +00:00
unsigned parserBufByteCnt ,
bool enableBufFl ,
2024-07-08 20:56:33 +00:00
unsigned bufferMsgCnt ,
bool filterRtSenseFl )
2024-02-14 16:20:40 +00:00
{
rc_t rc = kOkRC ;
rc_t rc1 = kOkRC ;
if ( ( rc = destroy ( hRef ) ) ! = kOkRC )
return rc ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
device_t * p = mem : : allocZ < device_t > ( ) ;
2024-04-06 19:44:43 +00:00
if ( ( rc = create ( p - > alsaDevH ,
enableBufFl ? _callback : cbFunc ,
enableBufFl ? p : cbArg ,
parserBufByteCnt ,
2024-07-08 20:56:33 +00:00
appNameStr ,
filterRtSenseFl ) ) ! = kOkRC )
2024-02-14 16:20:40 +00:00
{
rc = cwLogError ( rc , " ALSA MIDI device create failed. " ) ;
goto errLabel ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
p - > alsa_dev_cnt = count ( p - > alsaDevH ) ;
2019-12-24 15:05:24 +00:00
2024-04-06 19:44:43 +00:00
if ( ( rc = create ( p - > fileDevH ,
enableBufFl ? _callback : cbFunc ,
enableBufFl ? p : cbArg ,
p - > alsa_dev_cnt ,
filePortLabelA ,
max_file_cnt ,
fileDevName ,
fileDevReadAheadMicros ) ) ! = kOkRC )
2024-02-14 16:20:40 +00:00
{
rc = cwLogError ( rc , " MIDI file device create failed. " ) ;
goto errLabel ;
}
2019-12-24 15:05:24 +00:00
2024-04-06 19:44:43 +00:00
p - > cbFunc = cbFunc ;
p - > cbArg = cbArg ;
2024-02-14 16:20:40 +00:00
p - > file_dev_cnt = count ( p - > fileDevH ) ;
p - > total_dev_cnt = p - > alsa_dev_cnt + p - > file_dev_cnt ;
p - > alsaPollfdA = pollFdArray ( p - > alsaDevH , p - > alsaPollfdN ) ;
p - > fileDevStateId = kStoppedStateId ;
2024-04-06 19:44:43 +00:00
p - > buf = mem : : allocZ < ch_msg_t > ( bufferMsgCnt ) ;
p - > bufN = bufferMsgCnt ;
p - > buf_ii . store ( 0 ) ;
p - > buf_oi . store ( 0 ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( rc = thread : : create ( p - > threadH ,
_thread_func ,
2024-02-18 13:37:33 +00:00
p ,
" midi_dev " ) ) ! = kOkRC )
2024-02-14 16:20:40 +00:00
{
rc = cwLogError ( rc , " The MIDI file device thread create failed. " ) ;
goto errLabel ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
p - > thread_timeout_microsecs = stateTimeOutMicros ( p - > threadH ) ;
2019-12-24 15:05:24 +00:00
hRef . set ( p ) ;
2024-02-14 16:20:40 +00:00
if ( ( rc = unpause ( p - > threadH ) ) ! = kOkRC )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
rc = cwLogError ( rc , " Initial thread un-pause failed. " ) ;
goto errLabel ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
errLabel :
if ( rc ! = kOkRC )
rc1 = _destroy ( p ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( rc = rcSelect ( rc , rc1 ) ) ! = kOkRC )
rc = cwLogError ( rc , " MIDI device mgr. create failed. " ) ;
2019-12-24 15:05:24 +00:00
return rc ;
2024-02-14 16:20:40 +00:00
}
cw : : rc_t cw : : midi : : device : : create ( handle_t & h ,
cbFunc_t cbFunc ,
void * cbArg ,
const object_t * args )
{
rc_t rc = kOkRC ;
const char * appNameStr = nullptr ;
const char * fileDevName = " file_dev " ;
unsigned fileDevReadAheadMicros = 3000 ;
unsigned parseBufByteCnt = 1024 ;
2024-04-06 19:44:43 +00:00
bool enableBufFl = false ;
unsigned bufMsgCnt = 0 ;
2024-02-14 16:20:40 +00:00
const object_t * file_ports = nullptr ;
const object_t * port = nullptr ;
2024-07-08 20:56:33 +00:00
bool filterRtSenseFl = true ; ;
2024-02-14 16:20:40 +00:00
if ( ( rc = args - > getv ( " appNameStr " , appNameStr ,
" fileDevName " , fileDevName ,
" fileDevReadAheadMicros " , fileDevReadAheadMicros ,
" parseBufByteCnt " , parseBufByteCnt ,
2024-04-06 19:44:43 +00:00
" enableBufFl " , enableBufFl ,
" bufferMsgCnt " , bufMsgCnt ,
2024-07-08 20:56:33 +00:00
" file_ports " , file_ports ,
" filterRtSenseFl " , filterRtSenseFl ) ) ! = kOkRC )
2024-02-14 16:20:40 +00:00
{
rc = cwLogError ( rc , " MIDI port parse args. failed. " ) ;
}
else
{
unsigned fpi = 0 ;
unsigned filePortArgCnt = file_ports - > child_count ( ) ;
const char * labelArray [ filePortArgCnt ] ;
memset ( labelArray , 0 , sizeof ( labelArray ) ) ;
for ( unsigned i = 0 ; i < filePortArgCnt ; + + i )
{
if ( ( port = file_ports - > child_ele ( i ) ) ! = nullptr )
{
if ( ( rc = port - > getv ( " label " , labelArray [ fpi ] ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " MIDI file dev. port arg parse failed. " ) ;
goto errLabel ;
}
fpi + = 1 ;
}
}
2024-07-08 20:56:33 +00:00
rc = create ( h ,
cbFunc ,
cbArg ,
labelArray ,
fpi ,
appNameStr ,
fileDevName ,
fileDevReadAheadMicros ,
parseBufByteCnt ,
enableBufFl ,
bufMsgCnt ,
filterRtSenseFl ) ;
2024-02-14 16:20:40 +00:00
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
errLabel :
return rc ;
}
cw : : rc_t cw : : midi : : device : : destroy ( handle_t & hRef )
2019-12-24 15:05:24 +00:00
{
rc_t rc = kOkRC ;
if ( ! hRef . isValid ( ) )
return rc ;
2024-02-14 16:20:40 +00:00
device_t * p = _handleToPtr ( hRef ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( rc = _destroy ( p ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " MIDI device mgr. destroy failed. " ) ;
goto errLabel ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
hRef . clear ( ) ;
errLabel :
2019-12-24 15:05:24 +00:00
return rc ;
}
2024-02-14 16:20:40 +00:00
bool cw : : midi : : device : : isInitialized ( handle_t h )
{ return h . isValid ( ) ; }
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
unsigned cw : : midi : : device : : count ( handle_t h )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
device_t * p = _handleToPtr ( h ) ;
return p - > total_dev_cnt ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
const char * cw : : midi : : device : : name ( handle_t h , unsigned devIdx )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
device_t * p = _handleToPtr ( h ) ;
const char * ret_name = nullptr ;
unsigned alsaDevIdx = kInvalidIdx ;
unsigned fileDevIdx = kInvalidIdx ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( alsaDevIdx = _devIdxToAlsaDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
ret_name = name ( p - > alsaDevH , alsaDevIdx ) ;
else
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
if ( ( fileDevIdx = _devIdxToFileDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
ret_name = name ( p - > fileDevH , fileDevIdx ) ;
else
cwLogError ( kInvalidArgRC , " %i is an invalid device index. " , devIdx ) ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ret_name = = nullptr )
cwLogError ( kOpFailRC , " The name of device index %i could not be found. " , devIdx ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
return ret_name ;
}
unsigned cw : : midi : : device : : nameToIndex ( handle_t h , const char * deviceName )
{
device_t * p = _handleToPtr ( h ) ;
unsigned devIdx = kInvalidIdx ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( devIdx = nameToIndex ( p - > alsaDevH , deviceName ) ) ! = kInvalidIdx )
devIdx = _alsaDevIdxToDevIdx ( p , devIdx ) ;
else
{
if ( ( devIdx = nameToIndex ( p - > fileDevH , deviceName ) ) ! = kInvalidIdx )
devIdx = _fileDevIdxToDevIdx ( p , devIdx ) ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( devIdx = = kInvalidIdx )
cwLogError ( kOpFailRC , " MIDI device name to index failed on '%s'. " , cwStringNullGuard ( deviceName ) ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
return devIdx ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
unsigned cw : : midi : : device : : portNameToIndex ( handle_t h , unsigned devIdx , unsigned flags , const char * portNameStr )
{
device_t * p = _handleToPtr ( h ) ;
unsigned alsaDevIdx = kInvalidIdx ;
unsigned fileDevIdx = kInvalidIdx ;
unsigned portIdx = kInvalidIdx ;
if ( ( alsaDevIdx = _devIdxToAlsaDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
portIdx = portNameToIndex ( p - > alsaDevH , alsaDevIdx , flags , portNameStr ) ;
else
if ( ( fileDevIdx = _devIdxToFileDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
portIdx = portNameToIndex ( p - > fileDevH , fileDevIdx , flags , portNameStr ) ;
if ( portIdx = = kInvalidIdx )
cwLogError ( kInvalidArgRC , " The MIDI port name '%s' could not be found. " , cwStringNullGuard ( portNameStr ) ) ;
return portIdx ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : portEnable ( handle_t h , unsigned devIdx , unsigned flags , unsigned portIdx , bool enableFl )
{
rc_t rc = kOkRC ;
device_t * p = _handleToPtr ( h ) ;
unsigned alsaDevIdx = kInvalidIdx ;
unsigned fileDevIdx = kInvalidIdx ;
if ( ( alsaDevIdx = _devIdxToAlsaDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
rc = portEnable ( p - > alsaDevH , alsaDevIdx , flags , portIdx , enableFl ) ;
else
if ( ( fileDevIdx = _devIdxToFileDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
rc = portEnable ( p - > fileDevH , fileDevIdx , flags , portIdx , enableFl ) ;
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " The MIDI port %s failed on dev '%s' port '%s'. " , enableFl ? " enable " : " disable " , cwStringNullGuard ( name ( h , devIdx ) ) , cwStringNullGuard ( portName ( h , devIdx , flags , portIdx ) ) ) ;
return rc ;
}
unsigned cw : : midi : : device : : portCount ( handle_t h , unsigned devIdx , unsigned flags )
{
device_t * p = _handleToPtr ( h ) ;
unsigned alsaDevIdx = kInvalidIdx ;
unsigned fileDevIdx = kInvalidIdx ;
unsigned portCnt = 0 ;
if ( ( alsaDevIdx = _devIdxToAlsaDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
portCnt = portCount ( p - > alsaDevH , alsaDevIdx , flags ) ;
else
{
if ( ( fileDevIdx = _devIdxToFileDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
portCnt = portCount ( p - > fileDevH , fileDevIdx , flags ) ;
else
cwLogError ( kInvalidArgRC , " The device index %i is not valid. Port count access failed. " , devIdx ) ;
}
return portCnt ;
}
const char * cw : : midi : : device : : portName ( handle_t h , unsigned devIdx , unsigned flags , unsigned portIdx )
{
device_t * p = _handleToPtr ( h ) ;
unsigned alsaDevIdx = kInvalidIdx ;
unsigned fileDevIdx = kInvalidIdx ;
const char * name = nullptr ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( alsaDevIdx = _devIdxToAlsaDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
2024-04-06 19:44:43 +00:00
name = portName ( p - > alsaDevH , alsaDevIdx , flags , portIdx ) ;
2024-02-14 16:20:40 +00:00
else
if ( ( fileDevIdx = _devIdxToFileDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
2024-04-06 19:44:43 +00:00
name = portName ( p - > fileDevH , fileDevIdx , flags , portIdx ) ;
2024-02-14 16:20:40 +00:00
else
cwLogError ( kInvalidArgRC , " The device index %i is not valid. " ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( name = = nullptr )
2024-04-06 19:44:43 +00:00
cwLogError ( kOpFailRC , " The access to %s port name on device index %i port index %i failed. " , flags & kInMpFl ? " input " : " output " , devIdx , portIdx ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
return name ;
}
cw : : rc_t cw : : midi : : device : : send ( handle_t h , unsigned devIdx , unsigned portIdx , uint8_t st , uint8_t d0 , uint8_t d1 )
{
rc_t rc = kOkRC ;
device_t * p = _handleToPtr ( h ) ;
unsigned alsaDevIdx = kInvalidIdx ;
unsigned fileDevIdx = kInvalidIdx ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( alsaDevIdx = _devIdxToAlsaDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
rc = send ( p - > alsaDevH , alsaDevIdx , portIdx , st , d0 , d1 ) ;
else
{
if ( ( fileDevIdx = _devIdxToFileDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
rc = send ( p - > fileDevH , fileDevIdx , portIdx , st , d0 , d1 ) ;
else
rc = cwLogError ( kInvalidArgRC , " The device %i is not valid. " , devIdx ) ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " The MIDI msg (0x%x %i %i) transmit failed. " , st , d0 , d1 ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
return rc ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : sendData ( handle_t h , unsigned devIdx , unsigned portIdx , const uint8_t * dataPtr , unsigned byteCnt )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
rc_t rc = kOkRC ;
device_t * p = _handleToPtr ( h ) ;
unsigned alsaDevIdx = kInvalidIdx ;
unsigned fileDevIdx = kInvalidIdx ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( alsaDevIdx = _devIdxToAlsaDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
rc = sendData ( p - > alsaDevH , alsaDevIdx , portIdx , dataPtr , byteCnt ) ;
2019-12-24 15:05:24 +00:00
else
2024-02-14 16:20:40 +00:00
{
if ( ( fileDevIdx = _devIdxToFileDevIdx ( p , devIdx ) ) ! = kInvalidIdx )
rc = sendData ( p - > fileDevH , fileDevIdx , portIdx , dataPtr , byteCnt ) ;
2019-12-24 15:05:24 +00:00
else
2024-02-14 16:20:40 +00:00
rc = cwLogError ( kInvalidArgRC , " The device %i is not valid. " , devIdx ) ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " The MIDI msg transmit data failed. " ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
return rc ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : openMidiFile ( handle_t h , unsigned devIdx , unsigned portIdx , const char * fname )
{
rc_t rc = kOkRC ;
device_t * p = _handleToPtr ( h ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( _devIdxToFileDevIdx ( p , devIdx ) = = kInvalidIdx )
{
cwLogError ( kInvalidArgRC , " The device index %i does not identify a valid file device. " , devIdx ) ;
goto errLabel ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
if ( ( rc = open_midi_file ( p - > fileDevH , portIdx , fname ) ) ! = kOkRC )
goto errLabel ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
errLabel :
2019-12-24 15:05:24 +00:00
return rc ;
2024-02-21 12:49:28 +00:00
}
cw : : rc_t cw : : midi : : device : : loadMsgPacket ( handle_t h , const packet_t & pkt )
{
rc_t rc = kOkRC ;
device_t * p = _handleToPtr ( h ) ;
2019-12-24 15:05:24 +00:00
2024-02-21 12:49:28 +00:00
if ( _devIdxToFileDevIdx ( p , pkt . devIdx ) = = kInvalidIdx )
{
cwLogError ( kInvalidArgRC , " The device index %i does not identify a valid file device. " , pkt . devIdx ) ;
goto errLabel ;
}
if ( ( rc = load_messages ( p - > fileDevH , pkt . portIdx , pkt . msgArray , pkt . msgCnt ) ) ! = kOkRC )
goto errLabel ;
errLabel :
return rc ;
}
unsigned cw : : midi : : device : : msgCount ( handle_t h , unsigned devIdx , unsigned portIdx )
{
device_t * p = _handleToPtr ( h ) ;
if ( _devIdxToFileDevIdx ( p , devIdx ) = = kInvalidIdx )
{
cwLogError ( kInvalidArgRC , " The device index %i does not identify a valid file device. " , devIdx ) ;
goto errLabel ;
}
return msg_count ( p - > fileDevH , portIdx ) ;
errLabel :
return 0 ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : seekToMsg ( handle_t h , unsigned devIdx , unsigned portIdx , unsigned msgIdx )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
rc_t rc = kOkRC ;
2024-02-21 12:49:28 +00:00
device_t * p = _handleToPtr ( h ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( _devIdxToFileDevIdx ( p , devIdx ) = = kInvalidIdx )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
cwLogError ( kInvalidArgRC , " The device index %i does not identify a valid file device. " , devIdx ) ;
goto errLabel ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
if ( ( rc = seek_to_msg_index ( p - > fileDevH , portIdx , msgIdx ) ) ! = kOkRC )
goto errLabel ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
errLabel :
return rc ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : setEndMsg ( handle_t h , unsigned devIdx , unsigned portIdx , unsigned msgIdx )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
rc_t rc = kOkRC ;
2024-04-06 19:44:43 +00:00
device_t * p = _handleToPtr ( h ) ;
2024-02-14 16:20:40 +00:00
if ( _devIdxToFileDevIdx ( p , devIdx ) = = kInvalidIdx )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
cwLogError ( kInvalidArgRC , " The device index %i does not identify a valid file device. " , devIdx ) ;
goto errLabel ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
if ( ( rc = set_end_msg_index ( p - > fileDevH , portIdx , msgIdx ) ) ! = kOkRC )
goto errLabel ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
errLabel :
return rc ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
2024-04-06 19:44:43 +00:00
unsigned cw : : midi : : device : : maxBufferMsgCount ( handle_t h )
{
device_t * p = _handleToPtr ( h ) ;
return p - > bufN ;
}
const cw : : midi : : ch_msg_t * cw : : midi : : device : : getBuffer ( handle_t h , unsigned & msgCntRef )
{
device_t * p = _handleToPtr ( h ) ;
unsigned ii = p - > buf_ii . load ( ) ;
unsigned oi = p - > buf_oi . load ( ) ;
ch_msg_t * m = nullptr ;
msgCntRef = ii > = oi ? ii - oi : p - > bufN - oi ;
if ( msgCntRef > 0 )
m = p - > buf + oi ;
return m ;
}
cw : : rc_t cw : : midi : : device : : clearBuffer ( handle_t h , unsigned msgCnt )
{
if ( msgCnt > 0 )
{
device_t * p = _handleToPtr ( h ) ;
unsigned oi = p - > buf_oi . load ( ) ;
oi = ( oi + msgCnt ) % p - > bufN ;
p - > buf_oi . store ( oi ) ;
}
return kOkRC ;
}
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : start ( handle_t h )
2019-12-24 15:05:24 +00:00
{
2024-02-18 13:37:33 +00:00
rc_t rc = kOkRC ;
2024-02-14 16:20:40 +00:00
device_t * p = _handleToPtr ( h ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( p - > fileDevStateId ! = kPlayingStateId )
{
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( ( rc = rewind ( p - > fileDevH ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Rewind failed on MIDI file device. " ) ;
goto errLabel ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
p - > start_time = time : : current_time ( ) ;
p - > offset_micros = 0 ;
p - > last_posn_micros = 0 ;
p - > fileDevStateId = kPlayingStateId ;
}
errLabel :
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " MIDI port start failed. " ) ;
return rc ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : stop ( handle_t h )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
device_t * p = _handleToPtr ( h ) ;
p - > fileDevStateId = kStoppedStateId ;
return kOkRC ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : pause ( handle_t h , bool pause_fl )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
rc_t rc = kOkRC ;
device_t * p = _handleToPtr ( h ) ;
switch ( p - > fileDevStateId )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
case kStoppedStateId :
// unpausing does nothing from a 'stopped' state
break ;
case kPausedStateId :
if ( ! pause_fl )
2019-12-24 15:05:24 +00:00
{
2024-02-14 16:20:40 +00:00
p - > start_time = time : : current_time ( ) ;
p - > fileDevStateId = kPlayingStateId ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
break ;
case kPlayingStateId :
if ( pause_fl )
{
p - > offset_micros = p - > last_posn_micros ;
p - > fileDevStateId = kPausedStateId ;
}
break ;
}
return rc ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
cw : : rc_t cw : : midi : : device : : report ( handle_t h )
{
rc_t rc = kOkRC ;
textBuf : : handle_t tbH ;
2019-12-24 15:05:24 +00:00
if ( ( rc = textBuf : : create ( tbH ) ) ! = kOkRC )
goto errLabel ;
2020-01-27 22:47:14 +00:00
report ( h , tbH ) ;
2024-04-06 19:44:43 +00:00
printf ( " %s \n " , text ( tbH ) ) ;
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
errLabel :
destroy ( tbH ) ;
return rc ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
void cw : : midi : : device : : report ( handle_t h , textBuf : : handle_t tbH )
{
device_t * p = _handleToPtr ( h ) ;
report ( p - > alsaDevH , tbH ) ;
report ( p - > fileDevH , tbH ) ;
}
2019-12-24 15:05:24 +00:00
2024-02-14 16:20:40 +00:00
void cw : : midi : : device : : latency_measure_reset ( handle_t h )
{
device_t * p = _handleToPtr ( h ) ;
latency_measure_reset ( p - > alsaDevH ) ;
latency_measure_reset ( p - > fileDevH ) ;
2019-12-24 15:05:24 +00:00
}
2024-02-14 16:20:40 +00:00
cw : : midi : : device : : latency_meas_combined_result_t cw : : midi : : device : : latency_measure_result ( handle_t h )
{
device_t * p = _handleToPtr ( h ) ;
latency_meas_combined_result_t r ;
r . alsa_dev = latency_measure_result ( p - > alsaDevH ) ;
r . file_dev = latency_measure_result ( p - > fileDevH ) ;
return r ;
}