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.
2020-01-27 22:53:44 +00:00
# include "cwCommon.h"
# include "cwLog.h"
# include "cwCommonImpl.h"
2024-05-29 16:36:57 +00:00
# include "cwTest.h"
2020-01-27 22:53:44 +00:00
# include "cwMem.h"
2021-01-20 18:12:35 +00:00
# include "cwObject.h"
2020-01-27 22:53:44 +00:00
# include "cwText.h"
# include "cwTextBuf.h"
2020-03-23 17:14:42 +00:00
# include "cwIo.h"
2020-01-27 22:53:44 +00:00
# include "cwMidi.h"
2024-02-14 15:56:44 +00:00
# include "cwMidiDevice.h"
2020-03-23 17:14:42 +00:00
2020-01-27 22:53:44 +00:00
# include "cwObject.h"
# include "cwThread.h"
2021-01-20 18:12:35 +00:00
# include "cwThreadMach.h"
# include "cwMutex.h"
2020-01-27 22:53:44 +00:00
# include "cwSerialPort.h"
# include "cwSerialPortSrv.h"
2020-02-13 16:30:46 +00:00
# include "cwAudioDevice.h"
# include "cwAudioBuf.h"
# include "cwAudioDeviceAlsa.h"
2023-02-15 01:55:03 +00:00
# include "cwAudioDeviceFile.h"
2020-02-13 16:30:46 +00:00
2021-01-31 16:14:22 +00:00
# include "cwSocket.h"
2021-01-20 18:12:35 +00:00
# include "cwWebSock.h"
# include "cwUi.h"
2024-02-10 16:34:06 +00:00
# include "cwVectOps.h"
2020-01-27 22:53:44 +00:00
namespace cw
{
namespace io
{
2021-01-20 18:12:35 +00:00
struct io_str ;
2021-04-10 17:37:07 +00:00
2022-06-12 20:50:01 +00:00
typedef struct thread_str
{
unsigned id ;
void * arg ;
2022-12-12 17:20:59 +00:00
bool asyncFl ;
2022-06-12 20:50:01 +00:00
struct io_str * p ;
struct thread_str * link ;
} thread_t ;
2021-04-10 17:37:07 +00:00
typedef struct timer_str
{
struct io_str * io ;
bool deletedFl ;
bool startedFl ;
char * label ;
unsigned id ;
2023-05-09 11:29:43 +00:00
unsigned index ;
2021-04-10 17:37:07 +00:00
unsigned periodMicroSec ;
2022-12-12 17:20:59 +00:00
bool asyncFl ;
2023-06-27 21:24:12 +00:00
time : : spec_t nextTime ;
2021-04-10 17:37:07 +00:00
} timer_t ;
2021-01-20 18:12:35 +00:00
2020-01-27 22:53:44 +00:00
typedef struct serialPort_str
{
2021-10-11 15:53:36 +00:00
char * label ;
unsigned userId ;
2022-12-12 17:20:59 +00:00
bool asyncFl ;
2020-01-27 22:53:44 +00:00
char * device ;
unsigned baudRate ;
unsigned flags ;
serialPortSrv : : handle_t serialH ;
} serialPort_t ;
2020-02-29 05:12:57 +00:00
2021-01-20 18:12:35 +00:00
typedef struct audioGroup_str
2020-02-29 05:12:57 +00:00
{
2022-12-12 17:20:59 +00:00
bool enableFl ;
bool asyncFl ;
2021-01-20 18:12:35 +00:00
audio_msg_t msg ;
mutex : : handle_t mutexH ;
unsigned threadTimeOutMs ;
struct io_str * p ;
} audioGroup_t ;
typedef struct audioDev_str
{
2023-02-19 19:16:37 +00:00
bool activeFl ; // True if this device was enabled by the user
2023-01-05 12:30:57 +00:00
bool meterFl ; // True if meters are enabled on this device
2021-01-22 14:49:57 +00:00
const char * label ; // User label
2021-01-31 16:14:22 +00:00
unsigned userId ; // User id
char * devName ; // System device name
2021-01-22 14:49:57 +00:00
unsigned devIdx ; // AudioDevice interface device index
audioGroup_t * iGroup ; // Audio group pointers for this device
2021-01-20 18:12:35 +00:00
audioGroup_t * oGroup ; //
2021-01-22 14:49:57 +00:00
audio_group_dev_t * iagd ; // Audio group device record assoc'd with this device
audio_group_dev_t * oagd ; //
2024-06-11 00:38:44 +00:00
unsigned cycleCnt ;
unsigned framesPerCycle ;
2023-02-15 01:55:03 +00:00
struct audioDev_str * clockInList ; // List of devices sync'd to this devices input clock
struct audioDev_str * clockOutList ; // List of devices sync'd to this devices output clock
struct audioDev_str * clockLink ; // links used by clockIn/OutList
2021-01-20 18:12:35 +00:00
} audioDev_t ;
2021-01-31 16:14:22 +00:00
typedef struct socket_str
{
bool enableFl ;
2022-12-12 17:20:59 +00:00
bool asyncFl ;
2021-01-31 16:14:22 +00:00
char * label ;
unsigned sockA_index ;
2022-06-12 20:50:01 +00:00
unsigned userId ;
2021-01-31 16:14:22 +00:00
} socket_t ;
2020-01-27 22:53:44 +00:00
typedef struct io_str
{
2021-01-20 18:12:35 +00:00
std : : atomic < bool > quitFl ;
2023-03-21 12:27:32 +00:00
std : : atomic < bool > startedFl ;
2021-01-22 14:49:57 +00:00
time : : spec_t t0 ;
2020-01-27 22:53:44 +00:00
2021-01-20 18:12:35 +00:00
cbFunc_t cbFunc ;
void * cbArg ;
2022-12-12 17:20:59 +00:00
mutex : : handle_t cbMutexH ;
unsigned cbMutexTimeOutMs ;
2020-01-27 22:53:44 +00:00
2021-01-20 18:12:35 +00:00
thread_mach : : handle_t threadMachH ;
object_t * cfg ;
2021-04-10 17:37:07 +00:00
2022-06-12 20:50:01 +00:00
thread_t * threadL ;
2021-04-10 17:37:07 +00:00
timer_t * timerA ;
unsigned timerN ;
2021-01-20 18:12:35 +00:00
serialPort_t * serialA ;
unsigned serialN ;
2021-10-11 15:53:36 +00:00
serialPortSrv : : handle_t serialPortSrvH ;
2021-01-20 18:12:35 +00:00
midi : : device : : handle_t midiH ;
2022-12-12 17:20:59 +00:00
bool midiAsyncFl ;
2021-01-31 16:14:22 +00:00
socket_t * sockA ;
unsigned sockN ;
sock : : handle_t sockH ;
unsigned sockThreadTimeOutMs ;
2020-01-27 22:53:44 +00:00
2020-02-13 16:30:46 +00:00
audio : : device : : handle_t audioH ;
audio : : device : : alsa : : handle_t alsaH ;
2023-02-15 01:55:03 +00:00
audio : : device : : file : : handle_t audioDevFileH ;
2021-01-20 18:12:35 +00:00
audio : : buf : : handle_t audioBufH ;
unsigned audioThreadTimeOutMs ;
2021-01-22 14:49:57 +00:00
unsigned audioMeterDevEnabledN ;
unsigned audioMeterCbPeriodMs ;
time : : spec_t audioMeterNextTime ;
2021-01-20 18:12:35 +00:00
audioDev_t * audioDevA ;
unsigned audioDevN ;
2020-02-13 16:30:46 +00:00
2021-01-20 18:12:35 +00:00
audioGroup_t * audioGroupA ;
unsigned audioGroupN ;
ui : : ws : : handle_t wsUiH ; // ui::ws handle (invalid if no UI was specified)
ui : : appIdMap_t * uiMapA ; // Application supplied id's for the UI resource supplied with the cfg script via 'uiCfgFn'.
unsigned uiMapN ; //
2022-12-12 17:20:59 +00:00
bool uiAsyncFl ;
2024-02-10 16:34:06 +00:00
sample_t latency_meas_thresh_db ;
sample_t latency_meas_thresh_lin ;
bool latency_meas_enable_fl ;
latency_meas_result_t latency_meas_result ;
2020-01-27 22:53:44 +00:00
} io_t ;
2022-12-12 17:20:59 +00:00
//----------------------------------------------------------------------------------------------------------
//
// io
//
2020-01-27 22:53:44 +00:00
io_t * _handleToPtr ( handle_t h )
{ return handleToPtr < handle_t , io_t > ( h ) ; }
2022-06-12 20:50:01 +00:00
2022-12-12 17:20:59 +00:00
// All callbacks to the application occur through this function
2022-12-13 18:27:32 +00:00
rc_t _ioCallback ( io_t * p , bool asyncFl , const msg_t * m , rc_t * app_rc_ref = nullptr )
2022-12-12 17:20:59 +00:00
{
rc_t rc = kOkRC ;
bool unlockFl = false ;
2022-12-13 18:27:32 +00:00
bool isSynchronousFl = ! asyncFl ;
2023-03-21 12:27:32 +00:00
bool isStartedFl = p - > startedFl . load ( ) ;
2022-12-12 17:20:59 +00:00
2023-03-21 12:27:32 +00:00
if ( isStartedFl )
2022-12-12 17:20:59 +00:00
{
2023-03-21 12:27:32 +00:00
// if this is a synchronous callback then lock the mutex
if ( isSynchronousFl )
2022-12-12 17:20:59 +00:00
{
2023-03-21 12:27:32 +00:00
switch ( rc = mutex : : lock ( p - > cbMutexH , p - > cbMutexTimeOutMs ) )
{
case kOkRC :
unlockFl = true ;
break ;
2022-12-12 17:20:59 +00:00
2023-03-21 12:27:32 +00:00
case kTimeOutRC :
rc = cwLogError ( rc , " io mutex callback mutex lock timed out. " ) ;
break ;
2022-12-12 17:20:59 +00:00
2023-03-21 12:27:32 +00:00
default :
rc = cwLogError ( rc , " io mutex callback mutex lock failed. " ) ;
}
2022-12-12 17:20:59 +00:00
2023-03-21 12:27:32 +00:00
}
2022-12-12 17:20:59 +00:00
2023-03-21 12:27:32 +00:00
// make the callback to the client
if ( rc = = kOkRC )
{
2022-12-27 23:10:26 +00:00
2023-03-21 12:27:32 +00:00
rc_t app_rc = p - > cbFunc ( p - > cbArg , m ) ;
if ( app_rc_ref ! = nullptr )
* app_rc_ref = app_rc ;
}
2022-12-12 17:20:59 +00:00
2023-03-21 12:27:32 +00:00
// if the mutex is locked
if ( unlockFl )
2022-12-12 17:20:59 +00:00
{
2023-03-21 12:27:32 +00:00
if ( ( rc = mutex : : unlock ( p - > cbMutexH ) ) ! = kOkRC )
{
// Time out is not a failure
if ( rc = = kTimeOutRC )
rc = kOkRC ;
else
rc = cwLogError ( rc , " io mutex callback mutex unlock failed. " ) ;
}
2022-12-12 17:20:59 +00:00
}
}
2022-12-13 18:27:32 +00:00
return rc ;
2022-12-12 17:20:59 +00:00
}
rc_t _ioParse ( io_t * p , const object_t * cfg )
{
2022-12-22 20:29:00 +00:00
rc_t rc = kOkRC ;
2022-12-12 17:20:59 +00:00
const object_t * ioCfg ;
if ( ( ioCfg = cfg - > find ( " io " ) ) = = nullptr )
{
cwLogError ( kInvalidArgRC , " The 'io' configuration block could not be found. " ) ;
goto errLabel ;
}
if ( ( rc = ioCfg - > getv ( " callbackMutexTimeOutMs " , p - > cbMutexTimeOutMs ) ) ! = kOkRC )
{
cwLogError ( rc , " Parsing of 'io' block configuration failed. " ) ;
goto errLabel ;
}
errLabel :
return rc ;
}
2022-06-12 20:50:01 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Thread
//
bool _threadFunc ( void * arg )
{
2022-12-13 18:27:32 +00:00
rc_t rc = kOkRC ;
2022-06-12 20:50:01 +00:00
thread_t * t = ( thread_t * ) arg ;
thread_msg_t tm = { . id = t - > id , . arg = t - > arg } ;
msg_t m ;
m . tid = kThreadTId ;
m . u . thread = & tm ;
2022-12-13 18:27:32 +00:00
if ( ( rc = _ioCallback ( t - > p , t - > asyncFl , & m ) ) ! = kOkRC )
cwLogError ( rc , " Thread app callback failed. " ) ;
2022-06-12 20:50:01 +00:00
return true ;
}
void _threadRelease ( io_t * p )
{
thread_t * t0 = p - > threadL ;
for ( ; t0 ! = nullptr ; t0 = t0 - > link )
{
thread_t * t1 = t0 - > link ;
mem : : release ( t0 ) ;
t0 = t1 ;
}
}
2023-06-27 21:24:12 +00:00
2021-04-10 17:37:07 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Timer
//
bool _timerThreadCb ( void * arg )
{
timer_t * t = ( timer_t * ) arg ;
2023-06-27 21:24:12 +00:00
time : : spec_t t0 ;
time : : get ( t0 ) ;
int usec = time : : diffMicros ( t0 , t - > nextTime ) ;
2021-04-10 17:37:07 +00:00
2023-06-27 21:24:12 +00:00
if ( usec > 0 )
sleepUs ( usec ) ;
time : : advanceMicros ( t - > nextTime , t - > periodMicroSec ) ;
2021-04-10 17:37:07 +00:00
if ( t - > startedFl & & ! t - > deletedFl )
{
2022-12-13 18:27:32 +00:00
rc_t rc = kOkRC ;
2021-04-10 17:37:07 +00:00
msg_t m ;
timer_msg_t tm ;
tm . id = t - > id ;
2023-05-09 11:29:43 +00:00
tm . index = t - > index ;
2021-04-10 17:37:07 +00:00
m . tid = kTimerTId ;
m . u . timer = & tm ;
2022-12-13 18:27:32 +00:00
if ( ( rc = _ioCallback ( t - > io , t - > asyncFl , & m ) ) ! = kOkRC )
cwLogError ( rc , " Timer app callback failed. " ) ;
2021-04-10 17:37:07 +00:00
}
return ! t - > deletedFl ;
}
2022-12-12 17:20:59 +00:00
rc_t _timerCreate ( io_t * p , const char * label , unsigned id , unsigned periodMicroSec , bool asyncFl )
2021-04-10 17:37:07 +00:00
{
rc_t rc = kOkRC ;
timer_t * t = nullptr ;
2023-05-09 11:29:43 +00:00
unsigned timer_idx = kInvalidIdx ;
2023-06-27 21:24:12 +00:00
2021-04-10 17:37:07 +00:00
// look for a deleted timer
for ( unsigned i = 0 ; i < p - > timerN ; + + i )
if ( p - > timerA [ i ] . deletedFl )
{
t = p - > timerA + i ;
2023-05-09 11:29:43 +00:00
timer_idx = i ;
2021-04-10 17:37:07 +00:00
break ;
}
// if no deleted timer was found
if ( t = = nullptr )
{
// reallocate the timer array with an additional slot
timer_t * tA = mem : : allocZ < timer_t > ( p - > timerN + 1 ) ;
for ( unsigned i = 0 ; i < p - > timerN ; + + i )
tA [ i ] = p - > timerA [ i ] ;
// keep a pointer to the empty slot
t = tA + p - > timerN ;
2023-05-09 11:29:43 +00:00
timer_idx = p - > timerN ;
2021-04-10 17:37:07 +00:00
// update the timer array
mem : : release ( p - > timerA ) ;
p - > timerA = tA ;
p - > timerN = p - > timerN + 1 ;
}
assert ( t ! = nullptr ) ;
t - > io = p ;
t - > label = mem : : duplStr ( label ) ;
t - > id = id ;
2023-05-09 11:29:43 +00:00
t - > index = timer_idx ;
2022-12-12 17:20:59 +00:00
t - > asyncFl = asyncFl ;
2021-04-10 17:37:07 +00:00
t - > periodMicroSec = periodMicroSec ;
2023-06-27 21:24:12 +00:00
time : : get ( t - > nextTime ) ;
2024-02-18 13:37:33 +00:00
if ( ( rc = thread_mach : : add ( p - > threadMachH , _timerThreadCb , t , label ) ) ! = kOkRC )
2021-04-10 17:37:07 +00:00
{
rc = cwLogError ( rc , " Timer thread assignment failed. " ) ;
}
return rc ;
}
timer_t * _timerIndexToPtr ( io_t * p , unsigned timerIdx )
{
if ( timerIdx > = p - > timerN | | p - > timerA [ timerIdx ] . deletedFl = = true )
{
cwLogError ( kInvalidIdRC , " The timer index '%i' is invalid. " , timerIdx ) ;
return nullptr ;
}
return p - > timerA + timerIdx ;
}
rc_t _timerStart ( io_t * p , unsigned timerIdx , bool startFl )
{
rc_t rc = kOkRC ;
timer_t * t = _timerIndexToPtr ( p , timerIdx ) ;
if ( t = = nullptr )
rc = kInvalidIdRC ;
else
p - > timerA [ timerIdx ] . startedFl = startFl ;
return rc ;
}
2021-01-20 18:12:35 +00:00
2021-01-22 14:49:57 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Serial
//
2021-01-20 18:12:35 +00:00
2021-10-11 15:53:36 +00:00
void _serialPortCb ( void * arg , unsigned serialCfgIdx , const void * byteA , unsigned byteN )
2020-01-27 22:53:44 +00:00
{
2022-12-12 17:20:59 +00:00
io_t * p = ( io_t * ) arg ;
2021-10-11 15:53:36 +00:00
if ( serialCfgIdx > p - > serialN )
cwLogError ( kAssertFailRC , " The serial cfg index %i is out of range %i in serial port callback. " , serialCfgIdx , p - > serialN ) ;
else
2022-12-13 18:27:32 +00:00
{
rc_t rc = kOkRC ;
2021-10-11 15:53:36 +00:00
const serialPort_t * sp = p - > serialA + serialCfgIdx ;
msg_t m ;
serial_msg_t sm ;
sm . label = sp - > label ;
sm . userId = sp - > userId ;
sm . dataA = byteA ;
sm . byteN = byteN ;
m . tid = kSerialTId ;
m . u . serial = & sm ;
2022-12-13 18:27:32 +00:00
if ( ( rc = _ioCallback ( p , sp - > asyncFl , & m ) ) ! = kOkRC )
cwLogError ( rc , " Serial port app callback failed. " ) ;
2021-10-11 15:53:36 +00:00
}
2020-01-27 22:53:44 +00:00
}
2021-10-11 15:53:36 +00:00
rc_t _serialPortParseCfg ( const object_t & e , serialPort_t * port , bool & enableFlRef )
2020-01-27 22:53:44 +00:00
{
rc_t rc = kOkRC ;
char * parityLabel = nullptr ;
unsigned bits = 8 ;
unsigned stop = 1 ;
idLabelPair_t parityA [ ] =
{
{ serialPort : : kEvenParityFl , " even " } ,
{ serialPort : : kOddParityFl , " odd " } ,
{ serialPort : : kNoParityFl , " no " } ,
{ 0 , nullptr }
} ;
if ( ( rc = e . getv (
2022-12-12 17:20:59 +00:00
" enableFl " , enableFlRef ,
" asyncFl " , port - > asyncFl ,
2021-10-11 15:53:36 +00:00
" label " , port - > label ,
" device " , port - > device ,
" baud " , port - > baudRate ,
" bits " , bits ,
" stop " , stop ,
" parity " , parityLabel ) ) ! = kOkRC )
2020-01-27 22:53:44 +00:00
{
rc = cwLogError ( kSyntaxErrorRC , " Serial configuration parse failed. " ) ;
}
switch ( bits )
{
2021-01-20 18:12:35 +00:00
case 5 : port - > flags | = serialPort : : kDataBits5Fl ; break ;
case 6 : port - > flags | = serialPort : : kDataBits6Fl ; break ;
case 7 : port - > flags | = serialPort : : kDataBits7Fl ; break ;
case 8 : port - > flags | = serialPort : : kDataBits8Fl ; break ;
2020-01-27 22:53:44 +00:00
default :
rc = cwLogError ( kSyntaxErrorRC , " Invalid serial data bits cfg:%i. " , bits ) ;
}
switch ( stop )
{
2021-01-20 18:12:35 +00:00
case 1 : port - > flags | = serialPort : : k1StopBitFl ; break ;
case 2 : port - > flags | = serialPort : : k2StopBitFl ; break ;
2020-01-27 22:53:44 +00:00
default :
rc = cwLogError ( kSyntaxErrorRC , " Invalid serial stop bits cfg:%i. " , stop ) ;
}
unsigned i ;
for ( i = 0 ; parityA [ i ] . label ! = nullptr ; + + i )
if ( textCompare ( parityLabel , parityA [ i ] . label ) = = 0 )
{
port - > flags | = parityA [ i ] . id ;
break ;
}
if ( parityA [ i ] . label = = nullptr )
rc = cwLogError ( kSyntaxErrorRC , " Invalid parity cfg:'%s'. " , cwStringNullGuard ( parityLabel ) ) ;
return rc ;
}
rc_t _serialPortCreate ( io_t * p , const object_t * c )
{
2021-10-11 15:53:36 +00:00
rc_t rc = kOkRC ;
const object_t * cfg = nullptr ;
const object_t * port_array = nullptr ;
unsigned pollPeriodMs = 50 ;
unsigned recvBufByteN = 512 ;
2024-03-07 16:44:48 +00:00
bool enableFl = false ;
2020-01-27 22:53:44 +00:00
// get the serial port list node
2021-10-11 15:53:36 +00:00
if ( ( cfg = c - > find ( " serial " ) ) = = nullptr )
2022-06-12 20:50:01 +00:00
{
cwLogWarning ( " No 'serial' configuration. " ) ;
return kOkRC ;
}
2020-01-27 22:53:44 +00:00
2021-10-11 15:53:36 +00:00
// the serial header values
2024-03-07 16:44:48 +00:00
if ( ( rc = cfg - > getv ( " enableFl " , enableFl ,
" pollPeriodMs " , pollPeriodMs ,
2021-10-11 15:53:36 +00:00
" recvBufByteN " , recvBufByteN ,
" array " , port_array ) ) ! = kOkRC )
{
rc = cwLogError ( kSyntaxErrorRC , " Serial cfg header parse failed. " ) ;
goto errLabel ;
}
p - > serialN = port_array - > child_count ( ) ;
2024-03-07 16:44:48 +00:00
if ( enableFl = = false | | p - > serialN = = 0 )
{
2024-03-22 17:01:08 +00:00
p - > serialN = 0 ;
2024-03-07 16:44:48 +00:00
cwLogInfo ( " Serial port sub-system disabled. " ) ;
goto errLabel ;
}
2020-01-27 22:53:44 +00:00
p - > serialA = mem : : allocZ < serialPort_t > ( p - > serialN ) ;
2021-10-11 15:53:36 +00:00
// create the serial server
if ( ( rc = serialPortSrv : : create ( p - > serialPortSrvH , pollPeriodMs , recvBufByteN ) ) ! = kOkRC )
{
rc = cwLogError ( kSyntaxErrorRC , " Serial port server failed. " ) ;
goto errLabel ;
}
2020-01-27 22:53:44 +00:00
// for each serial port cfg
for ( unsigned i = 0 ; i < p - > serialN ; + + i )
{
2021-10-11 15:53:36 +00:00
const object_t * e = port_array - > child_ele ( i ) ;
2020-01-27 22:53:44 +00:00
serialPort_t * r = p - > serialA + i ;
if ( e = = nullptr )
{
rc = cwLogError ( kSyntaxErrorRC , " Unable to access a 'serial' port configuration record at index:%i. " , i ) ;
break ;
}
else
{
2021-10-11 15:53:36 +00:00
bool enableFl = false ;
2020-01-27 22:53:44 +00:00
// parse the cfg record
2021-10-11 15:53:36 +00:00
if ( ( rc = _serialPortParseCfg ( * e , r , enableFl ) ) ! = kOkRC )
2020-01-27 22:53:44 +00:00
{
rc = cwLogError ( rc , " Serial configuration parse failed on record index:%i. " , i ) ;
break ;
}
2021-10-11 15:53:36 +00:00
if ( enableFl )
2020-01-27 22:53:44 +00:00
{
2021-10-11 15:53:36 +00:00
r - > userId = i ; // default the serial port userId to the index into serialA[]
serialPort : : handle_t spH = serialPortSrv : : serialHandle ( p - > serialPortSrvH ) ;
// create the serial port object
if ( ( rc = serialPort : : createPort ( spH , i , r - > device , r - > baudRate , r - > flags , _serialPortCb , p ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Serial port create failed on record index:%i. " , i ) ;
break ;
}
2020-01-27 22:53:44 +00:00
}
}
}
2021-10-11 15:53:36 +00:00
errLabel :
return rc ;
}
void _serialPortDestroy ( io_t * p )
{
serialPortSrv : : destroy ( p - > serialPortSrvH ) ;
2021-12-30 02:36:16 +00:00
mem : : release ( p - > serialA ) ;
2021-10-11 15:53:36 +00:00
p - > serialN = 0 ;
}
rc_t _serialPortStart ( io_t * p )
{
2021-12-30 02:36:16 +00:00
rc_t rc = kOkRC ;
2022-06-12 20:50:01 +00:00
if ( p - > serialPortSrvH . isValid ( ) )
// the service is only started if at least one serial port is enabled
if ( serialPort : : portCount ( serialPortSrv : : serialHandle ( p - > serialPortSrvH ) ) > 0 )
if ( ( rc = serialPortSrv : : start ( p - > serialPortSrvH ) ) ! = kOkRC )
rc = cwLogError ( rc , " The serial port server start failed. " ) ;
2021-10-11 15:53:36 +00:00
2020-01-27 22:53:44 +00:00
return rc ;
}
2021-01-22 14:49:57 +00:00
//----------------------------------------------------------------------------------------------------------
//
// MIDI
//
2024-04-06 20:06:17 +00:00
void _midiCallback ( void * cbArg , const midi : : packet_t * pktArray , unsigned pktCnt )
2020-01-27 22:53:44 +00:00
{
2021-04-10 17:37:07 +00:00
unsigned i ;
2020-01-27 22:53:44 +00:00
for ( i = 0 ; i < pktCnt ; + + i )
{
2021-04-10 17:37:07 +00:00
msg_t m ;
midi_msg_t mm ;
const midi : : packet_t * pkt = pktArray + i ;
2024-04-06 20:06:17 +00:00
io_t * p = reinterpret_cast < io_t * > ( cbArg ) ;
2022-12-13 18:27:32 +00:00
rc_t rc = kOkRC ;
2021-04-10 17:37:07 +00:00
2020-01-27 22:53:44 +00:00
2021-04-10 17:37:07 +00:00
mm . pkt = pkt ;
m . tid = kMidiTId ;
m . u . midi = & mm ;
2020-01-27 22:53:44 +00:00
2022-12-13 18:27:32 +00:00
if ( ( rc = _ioCallback ( p , p - > midiAsyncFl , & m ) ) ! = kOkRC )
2023-05-09 11:29:43 +00:00
cwLogError ( rc , " MIDI app callback failed: async fl:%i " , p - > midiAsyncFl ) ;
2021-04-10 17:37:07 +00:00
/*
for ( unsigned j = 0 ; j < pkt - > msgCnt ; + + j )
2020-01-27 22:53:44 +00:00
if ( pkt - > msgArray ! = NULL )
2021-01-20 18:12:35 +00:00
printf ( " io midi cb: %ld %ld 0x%x %i %i \n " , pkt - > msgArray [ j ] . timeStamp . tv_sec , pkt - > msgArray [ j ] . timeStamp . tv_nsec , pkt - > msgArray [ j ] . status , pkt - > msgArray [ j ] . d0 , pkt - > msgArray [ j ] . d1 ) ;
2020-01-27 22:53:44 +00:00
else
2021-01-20 18:12:35 +00:00
printf ( " io midi cb: 0x%x " , pkt - > sysExMsg [ j ] ) ;
2021-04-10 17:37:07 +00:00
*/
2020-01-27 22:53:44 +00:00
}
}
2021-01-22 14:49:57 +00:00
rc_t _midiPortCreate ( io_t * p , const object_t * c )
{
2024-03-07 16:44:48 +00:00
rc_t rc = kOkRC ;
const object_t * cfg = nullptr ;
bool enableFl = false ;
2021-11-03 15:09:07 +00:00
// get the MIDI port cfg
if ( ( cfg = c - > find ( " midi " ) ) = = nullptr )
2022-06-12 20:50:01 +00:00
{
cwLogWarning ( " No 'MIDI' configuration. " ) ;
return kOkRC ;
}
2024-03-07 16:44:48 +00:00
if ( ( rc = cfg - > getv ( " enableFl " , enableFl ,
" asyncFl " , p - > midiAsyncFl ) ) ! = kOkRC )
2021-01-22 14:49:57 +00:00
{
rc = cwLogError ( kSyntaxErrorRC , " MIDI configuration parse failed. " ) ;
}
2024-03-07 16:44:48 +00:00
if ( ! enableFl )
cwLogInfo ( " MIDI device system disabled. " ) ;
else
{
if ( ( rc = create ( p - > midiH , _midiCallback , p , cfg ) ) ! = kOkRC )
return rc ;
}
2021-01-22 14:49:57 +00:00
return rc ;
}
2021-01-31 16:14:22 +00:00
//------------------------------------------------------------------------------------------------
//
// Socket
//
socket_t * _socketIndexToRecd ( io_t * p , unsigned sockIdx )
{
if ( sockIdx > = p - > sockN )
cwLogError ( kInvalidArgRC , " Invalid socket index (%i >= %i) " , sockIdx , p - > sockN ) ;
return p - > sockA + sockIdx ;
}
bool _socketThreadFunc ( void * arg )
{
rc_t rc = kOkRC ;
io_t * p = reinterpret_cast < io_t * > ( arg ) ;
unsigned readByteN = 0 ;
if ( ( rc = receive_all ( p - > sockH , p - > sockThreadTimeOutMs , readByteN ) ) ! = kOkRC )
{
if ( rc ! = kTimeOutRC )
cwLogWarning ( " Socket receive_all() failed. " ) ;
}
return true ;
}
void _socketCallback ( void * cbArg , sock : : cbOpId_t cbId , unsigned sockArray_index , unsigned connId , const void * byteA , unsigned byteN , const struct sockaddr_in * srcAddr )
{
io_t * p = reinterpret_cast < io_t * > ( cbArg ) ;
if ( sockArray_index > = p - > sockN )
cwLogError ( kInvalidArgRC , " The socket index '%i' outside range (0-%i). " , sockArray_index , p - > sockN ) ;
else
{
socket_msg_t sm ;
sm . cbId = cbId ;
sm . sockIdx = sockArray_index ,
sm . userId = p - > sockA [ sockArray_index ] . userId ;
sm . connId = connId ;
sm . byteA = byteA ;
sm . byteN = byteN ;
sm . srcAddr = srcAddr ;
msg_t m ;
m . tid = kSockTId ;
m . u . sock = & sm ;
2022-12-13 18:27:32 +00:00
rc_t rc ;
if ( ( rc = _ioCallback ( p , p - > sockA [ sockArray_index ] . asyncFl , & m ) ) ! = kOkRC )
cwLogError ( rc , " Socket app callback failed. " ) ;
2021-01-31 16:14:22 +00:00
}
}
rc_t _socketParseAttrs ( const object_t * attrL , unsigned & flagsRef )
{
rc_t rc = kOkRC ;
const object_t * node = nullptr ;
idLabelPair_t attrA [ ] =
{
{ . id = sock : : kNonBlockingFl , . label = " non_blocking " } , // Create a non-blocking socket.
{ . id = sock : : kBlockingFl , . label = " blocking " } , // Create a blocking socket.
{ . id = sock : : kTcpFl , . label = " tcp " } , // Create a TCP socket rather than a UDP socket.
{ . id = 0 , . label = " udp " } , //
{ . id = sock : : kBroadcastFl , . label = " broadcast " } , //
{ . id = sock : : kReuseAddrFl , . label = " reuse_addr " } , //
{ . id = sock : : kReusePortFl , . label = " reuse_port " } , //
{ . id = sock : : kMultiCastTtlFl , . label = " multicast_ttl " } , //
{ . id = sock : : kMultiCastLoopFl , . label = " multicast_loop " } , //
{ . id = sock : : kListenFl , . label = " listen " } , // Use this socket to listen for incoming connections
{ . id = sock : : kStreamFl , . label = " stream " } , // Connected stream (vs. Datagram)
2023-07-04 16:02:13 +00:00
{ . id = sock : : kTcpNoDelayFl , . label = " tcp_no_delay " } , // Implements TCP_NODELAY
2021-01-31 16:14:22 +00:00
{ . id = 0 , . label = nullptr , }
} ;
flagsRef = 0 ;
for ( unsigned j = 0 ; j < attrL - > child_count ( ) ; + + j )
if ( ( node = attrL - > child_ele ( j ) ) ! = nullptr & & node - > is_type ( kStringTId ) )
{
unsigned k ;
for ( k = 0 ; attrA [ k ] . label ! = nullptr ; + + k )
if ( textCompare ( attrA [ k ] . label , node - > u . str ) = = 0 )
{
flagsRef + = attrA [ k ] . id ;
break ;
}
if ( attrA [ k ] . label = = nullptr )
{
rc = cwLogError ( kInvalidArgRC , " The attribute label '%s'. " , cwStringNullGuard ( node - > u . str ) ) ;
}
}
return rc ;
}
rc_t _socketParseConfig ( io_t * p , const object_t * cfg )
{
const object_t * node = nullptr ;
rc_t rc = kOkRC ;
unsigned maxSocketCnt = 10 ;
unsigned recvBufByteCnt = 4096 ;
const object_t * socketL = nullptr ;
2024-03-07 16:44:48 +00:00
bool enableFl = false ;
2021-01-31 16:14:22 +00:00
// get the socket configuration node
if ( ( node = cfg - > find ( " socket " ) ) = = nullptr )
{
2022-06-12 20:50:01 +00:00
cwLogWarning ( " No 'socket' configuration node. " ) ;
return kOkRC ;
2021-01-31 16:14:22 +00:00
}
// get the required socket arguments
if ( ( rc = node - > getv (
2024-03-07 16:44:48 +00:00
" enableFl " , enableFl ,
" maxSocketCnt " , maxSocketCnt ,
" recvBufByteCnt " , recvBufByteCnt ,
" threadTimeOutMs " , p - > sockThreadTimeOutMs ,
" socketL " , socketL ) ) ! = kOkRC )
2021-01-31 16:14:22 +00:00
{
rc = cwLogError ( kSyntaxErrorRC , " Unable to parse the 'socket' configuration node. " ) ;
goto errLabel ;
}
// THe max socket count must be at least as large as the number of defined sockets
maxSocketCnt = std : : max ( p - > sockN , maxSocketCnt ) ;
// create the socket control array
p - > sockN = socketL - > child_count ( ) ;
2024-03-07 16:44:48 +00:00
if ( enableFl = = false | | p - > sockN = = 0 )
{
2024-03-22 17:01:08 +00:00
p - > sockN = 0 ;
2024-03-07 16:44:48 +00:00
cwLogInfo ( " Socket system disabled. " ) ;
goto errLabel ;
}
2021-01-31 16:14:22 +00:00
p - > sockA = mem : : allocZ < socket_t > ( p - > sockN ) ;
// create the socket manager
if ( ( rc = sock : : createMgr ( p - > sockH , recvBufByteCnt , maxSocketCnt ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Socket manager creation failed. " ) ;
goto errLabel ;
}
// parse each socket configuration
for ( unsigned i = 0 ; i < p - > sockN ; + + i )
{
if ( ( node = socketL - > child_ele ( i ) ) ! = nullptr )
{
unsigned port = sock : : kInvalidPortNumber ;
unsigned timeOutMs = 50 ;
const object_t * attrL = nullptr ;
char * remoteAddr = nullptr ;
unsigned remotePort = sock : : kInvalidPortNumber ;
char * localAddr = nullptr ;
unsigned flags = 0 ;
// parse the required arguments
if ( ( rc = node - > getv (
" enableFl " , p - > sockA [ i ] . enableFl ,
2022-12-12 17:20:59 +00:00
" asyncFl " , p - > sockA [ i ] . asyncFl ,
2021-01-31 16:14:22 +00:00
" label " , p - > sockA [ i ] . label ,
" port " , port ,
" timeOutMs " , timeOutMs ,
" attrL " , attrL ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error parsing required socket cfg record at index:%i " , i ) ;
goto errLabel ;
}
// parse the optional arguments
if ( ( rc = node - > getv_opt (
" userId " , p - > sockA [ i ] . userId ,
" remoteAddr " , remoteAddr ,
" remotePort " , remotePort ,
" localAddr " , localAddr ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error parsing optional socket cfg record at index:%i " , i ) ;
goto errLabel ;
}
// parse the socket attribute list
if ( ( rc = _socketParseAttrs ( attrL , flags ) ) ! = kOkRC )
goto errLabel ;
// create the socket object
if ( ( rc = create ( p - > sockH , i , port , flags , timeOutMs , _socketCallback , p , remoteAddr , remotePort , localAddr ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Socket create failed. " ) ;
goto errLabel ;
}
}
}
2021-04-10 17:37:07 +00:00
// create the socket thread
if ( p - > sockN > 0 )
2024-02-18 13:37:33 +00:00
if ( ( rc = thread_mach : : add ( p - > threadMachH , _socketThreadFunc , p , " io_socket " ) ) ! = kOkRC )
2021-04-10 17:37:07 +00:00
{
rc = cwLogError ( rc , " Error creating socket thread. " ) ;
goto errLabel ;
}
2021-01-31 16:14:22 +00:00
errLabel :
return rc ;
}
2021-01-22 14:49:57 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Audio
//
2023-01-05 12:30:57 +00:00
2021-01-22 14:49:57 +00:00
// Start or stop all the audio devices in p->audioDevA[]
rc_t _audioDeviceStartStop ( io_t * p , bool startFl )
{
rc_t rc = kOkRC ;
2023-02-15 01:55:03 +00:00
rc_t rc1 = kOkRC ;
2021-01-22 14:49:57 +00:00
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
2023-02-19 19:16:37 +00:00
if ( p - > audioDevA [ i ] . activeFl )
2021-01-22 14:49:57 +00:00
{
rc_t rc0 = kOkRC ;
if ( startFl )
rc0 = audio : : device : : start ( p - > audioH , p - > audioDevA [ i ] . devIdx ) ;
else
rc0 = audio : : device : : stop ( p - > audioH , p - > audioDevA [ i ] . devIdx ) ;
if ( rc0 ! = kOkRC )
rc = cwLogError ( rc0 , " The audio device: %s failed to %s. " , cwStringNullGuard ( p - > audioDevA [ i ] . devName ) , startFl ? " start " : " stop " ) ;
2023-02-15 01:55:03 +00:00
}
2021-01-22 14:49:57 +00:00
2023-02-15 01:55:03 +00:00
if ( p - > audioDevFileH . isValid ( ) )
{
if ( startFl )
rc1 = audio : : device : : file : : start ( p - > audioDevFileH ) ;
else
rc1 = audio : : device : : file : : stop ( p - > audioDevFileH ) ;
if ( rc1 ! = kOkRC )
rc1 = cwLogError ( rc1 , " Audio device file sub-system %s failed. " , startFl ? " start " : " stop " ) ;
}
return rcSelect ( rc , rc1 ) ;
2021-01-22 14:49:57 +00:00
}
// Release all resource associated with a group-device record.
void _audioGroupDestroyDevs ( audio_group_dev_t * agd )
{
while ( agd ! = nullptr )
{
audio_group_dev_t * agd0 = agd - > link ;
mem : : release ( agd - > meterA ) ;
mem : : release ( agd ) ;
agd = agd0 ;
}
}
// Release all resource associated with all audio group records
rc_t _audioGroupDestroyAll ( io_t * p )
{
rc_t rc = kOkRC ;
for ( unsigned i = 0 ; i < p - > audioGroupN ; + + i )
{
audioGroup_t * ag = p - > audioGroupA + i ;
_audioGroupDestroyDevs ( ag - > msg . iDevL ) ;
_audioGroupDestroyDevs ( ag - > msg . oDevL ) ;
mem : : release ( ag - > msg . iBufArray ) ;
mem : : release ( ag - > msg . oBufArray ) ;
mutex : : unlock ( ag - > mutexH ) ; // the mutex is expected to be locked at this point
mutex : : destroy ( ag - > mutexH ) ;
}
mem : : release ( p - > audioGroupA ) ;
p - > audioGroupN = 0 ;
return rc ;
}
rc_t _audioDestroy ( io_t * p )
{
rc_t rc = kOkRC ;
// stop each device - this will stop the callbacks to _audioDeviceCallback()
if ( ( rc = _audioDeviceStartStop ( p , false ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio device stop failed. " ) ;
goto errLabel ;
}
if ( ( rc = audio : : device : : alsa : : destroy ( p - > alsaH ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " ALSA sub-system shutdown failed. " ) ;
goto errLabel ;
}
2023-02-15 01:55:03 +00:00
if ( ( rc = audio : : device : : file : : destroy ( p - > audioDevFileH ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio device file sub-system shutdown failed. " ) ;
goto errLabel ;
}
2021-01-22 14:49:57 +00:00
if ( ( rc = audio : : device : : destroy ( p - > audioH ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio device sub-system shutdown failed. " ) ;
goto errLabel ;
}
if ( ( rc = audio : : buf : : destroy ( p - > audioBufH ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio buffer release failed. " ) ;
goto errLabel ;
}
if ( ( rc = _audioGroupDestroyAll ( p ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio group release failed. " ) ;
goto errLabel ;
}
2021-01-31 16:14:22 +00:00
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
mem : : release ( p - > audioDevA [ i ] . devName ) ;
2021-01-22 14:49:57 +00:00
mem : : free ( p - > audioDevA ) ;
p - > audioDevN = 0 ;
errLabel :
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " Audio sub-system shutdown failed. " ) ;
return rc ;
}
// Are either input or output meter enabled on this audio devices.
// Set flags to kInFl or kOutput to select inut or output meters.
// Set both flags to check if either input or output meters are enabled.
bool _audioDeviceIsMeterEnabled ( audioDev_t * ad , unsigned flags )
{
return ( cwIsFlag ( flags , kInFl ) & & ad - > iagd ! = nullptr & & cwIsFlag ( ad - > iagd - > flags , kMeterFl ) )
| | ( cwIsFlag ( flags , kOutFl ) & & ad - > oagd ! = nullptr & & cwIsFlag ( ad - > oagd - > flags , kMeterFl ) ) ;
}
2022-12-12 17:20:59 +00:00
rc_t _audioGroupDeviceProcessMeter ( io_t * p , audio_group_dev_t * agd , audioGroup_t * ag , unsigned audioBufFlags )
2020-01-27 22:53:44 +00:00
{
2021-01-22 14:49:57 +00:00
rc_t rc = kOkRC ;
2022-12-12 17:20:59 +00:00
if ( agd ! = nullptr & & ag ! = nullptr & & cwIsFlag ( agd - > flags , kMeterFl ) )
2020-01-27 22:53:44 +00:00
{
2022-12-13 18:27:32 +00:00
rc_t app_rc = kOkRC ;
2021-01-31 16:14:22 +00:00
msg_t m ;
m . tid = kAudioMeterTId ;
m . u . audioGroupDev = agd ;
2021-01-22 14:49:57 +00:00
2021-01-31 16:14:22 +00:00
// get the current meter values from the audioBuf
audio : : buf : : meter ( p - > audioBufH , agd - > devIdx , audioBufFlags , agd - > meterA , agd - > chCnt ) ;
// callback the application with the current meter values
2022-12-13 18:27:32 +00:00
if ( ( rc = _ioCallback ( p , ag - > asyncFl , & m , & app_rc ) ) ! = kOkRC )
cwLogError ( rc , " Audio meter app callback failed. " ) ;
2021-01-31 16:14:22 +00:00
2022-12-13 18:27:32 +00:00
rc = app_rc ;
2020-01-27 22:53:44 +00:00
}
2021-01-31 16:14:22 +00:00
2021-01-22 14:49:57 +00:00
return rc ;
}
2021-01-31 16:14:22 +00:00
2021-01-22 14:49:57 +00:00
rc_t _audioDeviceProcessMeters ( io_t * p )
{
rc_t rc = kOkRC ;
2021-01-31 16:14:22 +00:00
// if it is time to execute the next meter update
2021-01-22 14:49:57 +00:00
if ( time : : isGTE ( p - > t0 , p - > audioMeterNextTime ) )
{
2021-01-31 16:14:22 +00:00
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
2023-02-19 19:16:37 +00:00
if ( p - > audioDevA [ i ] . activeFl )
2021-01-31 16:14:22 +00:00
{
audioDev_t * ad = p - > audioDevA + i ;
rc_t rc0 ;
// update the input meters
2022-12-12 17:20:59 +00:00
if ( ( rc0 = _audioGroupDeviceProcessMeter ( p , ad - > iagd , ad - > iGroup , audio : : buf : : kInFl ) ) ! = kOkRC )
2021-01-31 16:14:22 +00:00
rc = rc0 ;
// update the output meters
2022-12-12 17:20:59 +00:00
if ( ( rc0 = _audioGroupDeviceProcessMeter ( p , ad - > oagd , ad - > oGroup , audio : : buf : : kOutFl ) ) ! = kOkRC )
2021-01-31 16:14:22 +00:00
rc = rc0 ;
}
// schedule the next meter update
2021-01-22 14:49:57 +00:00
p - > audioMeterNextTime = p - > t0 ;
time : : advanceMs ( p - > audioMeterNextTime , p - > audioMeterCbPeriodMs ) ;
}
2021-01-31 16:14:22 +00:00
2020-01-27 22:53:44 +00:00
return rc ;
}
2021-01-22 14:49:57 +00:00
2020-02-13 16:30:46 +00:00
2021-01-20 18:12:35 +00:00
bool _audioGroupBufIsReady ( io_t * p , audioGroup_t * ag , bool inputFl )
{
audio_group_dev_t * agd = inputFl ? ag - > msg . iDevL : ag - > msg . oDevL ;
for ( ; agd ! = nullptr ; agd = agd - > link )
if ( ! audio : : buf : : isDeviceReady ( p - > audioBufH , agd - > devIdx , inputFl ? audio : : buf : : kInFl : audio : : buf : : kOutFl ) )
return false ;
return true ;
}
2024-02-10 16:34:06 +00:00
// Get sample pointers or advance the pointers on the buffer associates with each device.
2021-01-20 18:12:35 +00:00
enum { kAudioGroupGetBuf , kAudioGroupAdvBuf } ;
void _audioGroupProcSampleBufs ( io_t * p , audioGroup_t * ag , unsigned processTypeId , unsigned inputFl )
2020-02-13 16:30:46 +00:00
{
2021-01-22 14:49:57 +00:00
sample_t * * bufArray = inputFl ? ag - > msg . iBufArray : ag - > msg . oBufArray ;
unsigned bufArrayChCnt = inputFl ? ag - > msg . iBufChCnt : ag - > msg . oBufChCnt ;
audio_group_dev_t * agd = inputFl ? ag - > msg . iDevL : ag - > msg . oDevL ;
2021-01-20 18:12:35 +00:00
unsigned audioBufFlags = inputFl ? audio : : buf : : kInFl : audio : : buf : : kOutFl ;
2020-02-29 05:12:57 +00:00
2021-01-20 18:12:35 +00:00
unsigned chIdx = 0 ;
for ( ; agd ! = nullptr & & chIdx < bufArrayChCnt ; agd = agd - > link )
{
switch ( processTypeId )
{
case kAudioGroupGetBuf :
audio : : buf : : get ( p - > audioBufH , agd - > devIdx , audioBufFlags , bufArray + chIdx , agd - > chCnt ) ;
break ;
case kAudioGroupAdvBuf :
audio : : buf : : advance ( p - > audioBufH , agd - > devIdx , audioBufFlags ) ;
break ;
default :
assert ( 0 ) ;
}
chIdx + = agd - > chCnt ;
}
2020-02-13 16:30:46 +00:00
}
2024-02-10 16:34:06 +00:00
void _audio_latency_measure ( io_t * p , const audio_msg_t & msg )
{
if ( p - > latency_meas_enable_fl )
{
if ( msg . iBufChCnt & & p - > latency_meas_result . audio_in_ts . tv_nsec = = 0 )
{
sample_t rms = vop : : rms ( msg . iBufArray [ 0 ] , msg . dspFrameCnt ) ;
if ( rms > p - > latency_meas_thresh_lin )
time : : get ( p - > latency_meas_result . audio_in_ts ) ;
if ( rms > p - > latency_meas_result . audio_in_rms_max )
p - > latency_meas_result . audio_in_rms_max = rms ;
}
if ( msg . oBufChCnt & & p - > latency_meas_result . audio_out_ts . tv_nsec = = 0 )
{
sample_t rms = vop : : rms ( msg . oBufArray [ 0 ] , msg . dspFrameCnt ) ;
if ( rms > p - > latency_meas_thresh_lin )
time : : get ( p - > latency_meas_result . audio_out_ts ) ;
if ( rms > p - > latency_meas_result . audio_out_rms_max )
p - > latency_meas_result . audio_out_rms_max = rms ;
}
p - > latency_meas_enable_fl = p - > latency_meas_result . audio_in_ts . tv_nsec = = 0 | |
p - > latency_meas_result . audio_out_ts . tv_nsec = = 0 ;
}
}
2021-01-20 18:12:35 +00:00
// This is the audio processing thread function. Block on the audio group condition var
// which is triggered when all the devices in the group are ready by _audioGroupNotifyIfReady().
bool _audioGroupThreadFunc ( void * arg )
2020-02-13 16:30:46 +00:00
{
2021-01-20 18:12:35 +00:00
rc_t rc = kOkRC ;
2021-01-22 14:49:57 +00:00
2021-01-20 18:12:35 +00:00
audioGroup_t * ag = reinterpret_cast < audioGroup_t * > ( arg ) ;
2020-02-29 05:12:57 +00:00
2021-01-20 18:12:35 +00:00
// block on the cond. var
if ( ( rc = mutex : : waitOnCondVar ( ag - > mutexH , false , ag - > threadTimeOutMs ) ) ! = kOkRC )
{
if ( rc ! = kTimeOutRC )
{
cwLogError ( rc , " Audio thread Wait-on-condition-var failed. " ) ;
return false ;
}
}
// if the cond. var was signaled and ag->mutexH is locked
if ( rc = = kOkRC )
2020-02-29 05:12:57 +00:00
{
2021-01-20 18:12:35 +00:00
msg_t msg ;
msg . tid = kAudioTId ;
msg . u . audio = & ag - > msg ;
// While the all audio devices for this group are ready
while ( _audioGroupBufIsReady ( ag - > p , ag , true ) & & _audioGroupBufIsReady ( ag - > p , ag , false ) )
{
_audioGroupProcSampleBufs ( ag - > p , ag , kAudioGroupGetBuf , true ) ;
_audioGroupProcSampleBufs ( ag - > p , ag , kAudioGroupGetBuf , false ) ;
2021-01-22 14:49:57 +00:00
2022-12-13 18:27:32 +00:00
if ( ( rc = _ioCallback ( ag - > p , ag - > asyncFl , & msg ) ) ! = kOkRC )
cwLogError ( rc , " Audio app callback failed %i. " , ag - > asyncFl ) ;
2021-01-20 18:12:35 +00:00
2024-02-10 16:34:06 +00:00
_audio_latency_measure ( ag - > p , ag - > msg ) ;
2021-01-20 18:12:35 +00:00
_audioGroupProcSampleBufs ( ag - > p , ag , kAudioGroupAdvBuf , true ) ;
_audioGroupProcSampleBufs ( ag - > p , ag , kAudioGroupAdvBuf , false ) ;
}
}
2021-01-22 14:49:57 +00:00
2021-01-20 18:12:35 +00:00
return true ;
}
// Given a device index return the associated audioDev_t record.
2021-01-22 14:49:57 +00:00
audioDev_t * _audioDeviceIndexToRecd ( io_t * p , unsigned devIdx , bool reportMissingFl = true )
2021-01-20 18:12:35 +00:00
{
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
if ( p - > audioDevA [ i ] . devIdx = = devIdx )
return p - > audioDevA + i ;
2021-01-22 14:49:57 +00:00
2021-01-31 16:14:22 +00:00
if ( reportMissingFl )
cwLogError ( kInvalidArgRC , " A device with index %i could not be found. " , devIdx ) ;
return nullptr ;
}
// Given a device name return the associated audioDev_t record.
audioDev_t * _audioDeviceNameToRecd ( io_t * p , const char * devName , bool reportMissingFl = true )
{
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
if ( textCompare ( p - > audioDevA [ i ] . devName , devName ) = = 0 )
return p - > audioDevA + i ;
if ( reportMissingFl )
cwLogError ( kInvalidArgRC , " A device named '%s' was not found. " , cwStringNullGuard ( devName ) ) ;
2021-01-20 18:12:35 +00:00
return nullptr ;
}
2021-01-31 16:14:22 +00:00
// Given a user label return the associated audioDev_t record.
audioDev_t * _audioDeviceLabelToRecd ( io_t * p , const char * label , bool reportMissingFl = true )
{
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
if ( textCompare ( label , p - > audioDevA [ i ] . label ) = = 0 )
return p - > audioDevA + i ;
if ( reportMissingFl )
cwLogError ( kInvalidArgRC , " An audio device with label '%s' could not be found. " , cwStringNullGuard ( label ) ) ;
return nullptr ;
}
2023-01-05 12:30:57 +00:00
rc_t _audioDeviceParams ( io_t * p , unsigned devIdx , unsigned flags , audioDev_t * & adRef , unsigned & audioBufFlagsRef )
{
rc_t rc = kOkRC ;
if ( ( adRef = _audioDeviceIndexToRecd ( p , devIdx ) ) = = nullptr )
rc = kInvalidArgRC ;
else
{
audioBufFlagsRef = 0 ;
if ( cwIsFlag ( flags , kInFl ) )
audioBufFlagsRef + = audio : : buf : : kInFl ;
if ( cwIsFlag ( flags , kOutFl ) )
audioBufFlagsRef + = audio : : buf : : kOutFl ;
if ( cwIsFlag ( flags , kEnableFl ) )
audioBufFlagsRef + = audio : : buf : : kEnableFl ;
}
return rc ;
}
rc_t _audioDeviceEnableMeter ( io_t * p , unsigned devIdx , unsigned inOutEnaFlags )
{
rc_t rc = kOkRC ;
audioDev_t * ad = nullptr ;
unsigned audioBufFlags = 0 ;
if ( ( rc = _audioDeviceParams ( p , devIdx , inOutEnaFlags , ad , audioBufFlags ) ) ! = kOkRC )
2023-02-02 23:21:34 +00:00
rc = cwLogError ( rc , " Enable tone failed. " ) ;
2023-01-05 12:30:57 +00:00
else
{
2023-02-02 23:21:34 +00:00
bool enaFl = inOutEnaFlags & kEnableFl ;
bool enaState0Fl = _audioDeviceIsMeterEnabled ( ad , kInFl | kOutFl ) ;
2023-01-05 12:30:57 +00:00
2023-02-02 23:21:34 +00:00
audioBufFlags + = audio : : buf : : kMeterFl ;
2023-01-05 12:30:57 +00:00
2023-02-15 01:55:03 +00:00
if ( ad - > iagd ! = nullptr & & ( inOutEnaFlags & kInFl ) )
2023-02-02 23:21:34 +00:00
ad - > iagd - > flags = cwEnaFlag ( ad - > iagd - > flags , kMeterFl , enaFl ) ;
2023-01-05 12:30:57 +00:00
2023-02-15 01:55:03 +00:00
if ( ad - > oagd ! = nullptr & & ( inOutEnaFlags & kOutFl ) )
2023-02-02 23:21:34 +00:00
ad - > oagd - > flags = cwEnaFlag ( ad - > oagd - > flags , kMeterFl , enaFl ) ;
2023-01-05 12:30:57 +00:00
2023-02-02 23:21:34 +00:00
audio : : buf : : setFlag ( p - > audioBufH , devIdx , kInvalidIdx , audioBufFlags ) ;
2023-01-05 12:30:57 +00:00
2023-02-02 23:21:34 +00:00
bool enaState1Fl = _audioDeviceIsMeterEnabled ( ad , kInFl | kOutFl ) ;
2023-01-05 12:30:57 +00:00
2023-02-02 23:21:34 +00:00
if ( enaState1Fl and ! enaState0Fl )
p - > audioMeterDevEnabledN + = 1 ;
else
if ( p - > audioMeterDevEnabledN > 0 & & ! enaState1Fl & & enaState0Fl )
p - > audioMeterDevEnabledN - = 1 ;
2023-01-05 12:30:57 +00:00
}
if ( rc ! = kOkRC )
2023-02-02 23:21:34 +00:00
rc = cwLogError ( rc , " Enable meters failed. " ) ;
2023-01-05 12:30:57 +00:00
return rc ;
}
2021-01-20 18:12:35 +00:00
2021-01-22 14:49:57 +00:00
// Add an audioGroup pointer to groupA[] and return the new count of elements in the array.
2021-01-20 18:12:35 +00:00
unsigned _audioDeviceUpdateGroupArray ( audioGroup_t * * groupA , unsigned groupN , unsigned curGroupN , audioGroup_t * ag )
{
if ( ag ! = nullptr )
{
for ( unsigned i = 0 ; i < curGroupN ; + + i )
if ( groupA [ i ] = = ag )
return curGroupN ;
if ( curGroupN > = groupN )
{
cwLogError ( kAssertFailRC , " The group array was found to be too small during an audio device callback. " ) ;
goto errLabel ;
}
groupA [ curGroupN + + ] = ag ;
}
errLabel :
return curGroupN ;
}
// If audioDev (devIdx) is ready then update audio_group_dev.readyCnt and store a pointer to it's associated group in groupA[].
// Return the count of pointers stored in groupA[].
2023-02-15 01:55:03 +00:00
unsigned _audioDeviceUpdateReadiness ( io_t * p , unsigned devIdx , bool inputFl , audioGroup_t * * groupA , unsigned groupN , unsigned curGroupN , audioDev_t * & syncAdRef )
2021-01-20 18:12:35 +00:00
{
audioDev_t * ad ;
2023-02-15 01:55:03 +00:00
syncAdRef = nullptr ;
2021-01-20 18:12:35 +00:00
// get the device record assoc'ed with this device
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) = = nullptr )
{
cwLogError ( kAssertFailRC , " An unexpected audio device index was encountered in an audio device callback. " ) ;
2020-02-29 05:12:57 +00:00
goto errLabel ;
}
2021-01-20 18:12:35 +00:00
// if an input packet was received on this device
if ( inputFl )
{
2023-02-15 01:55:03 +00:00
if ( audio : : buf : : isDeviceReady ( p - > audioBufH , devIdx , audio : : buf : : kInFl ) & & ad - > iagd ! = nullptr )
2021-01-20 18:12:35 +00:00
{
// atomic incr - note that the ordering doesn't matter because the update does not control access to any other variables from another thread
2021-01-22 14:49:57 +00:00
std : : atomic_store_explicit ( & ad - > iagd - > readyCnt , ad - > iagd - > readyCnt + 1 , std : : memory_order_relaxed ) ;
2021-01-20 18:12:35 +00:00
curGroupN = _audioDeviceUpdateGroupArray ( groupA , groupN , curGroupN , ad - > iGroup ) ;
ad - > iagd - > cbCnt + = 1 ; // update the callback count for this device
2023-02-15 01:55:03 +00:00
syncAdRef = ad - > clockInList ;
2021-01-20 18:12:35 +00:00
}
}
else // if an output packet was received on this device
{
2023-02-15 01:55:03 +00:00
if ( audio : : buf : : isDeviceReady ( p - > audioBufH , devIdx , audio : : buf : : kOutFl ) & & ad - > oagd ! = nullptr )
2021-01-20 18:12:35 +00:00
{
2021-01-22 14:49:57 +00:00
std : : atomic_store_explicit ( & ad - > oagd - > readyCnt , ad - > oagd - > readyCnt + 1 , std : : memory_order_relaxed ) ; // atomic incr
2021-01-20 18:12:35 +00:00
curGroupN = _audioDeviceUpdateGroupArray ( groupA , groupN , curGroupN , ad - > oGroup ) ;
ad - > oagd - > cbCnt + = 1 ;
2023-02-15 01:55:03 +00:00
syncAdRef = ad - > clockOutList ;
2021-01-20 18:12:35 +00:00
}
}
errLabel :
// return the count of dev indexes in groupA[]
return curGroupN ;
}
// Return true if all the devices in the linked list 'agd' are ready to source/sink data.
bool _audioGroupIsReady ( audio_group_dev_t * agd )
{
// are all devices in this group ready to provide/accept new audio data
for ( ; agd ! = nullptr ; agd = agd - > link )
2021-01-22 14:49:57 +00:00
if ( std : : atomic_load_explicit ( & agd - > readyCnt , std : : memory_order_acquire ) = = 0 ) // ACQUIRE
2021-01-20 18:12:35 +00:00
return false ;
return true ;
}
// Decrement the ready count on all devices in the linked list pointed to by 'agd'.
void _audioGroupDecrReadyCount ( audio_group_dev_t * agd )
{
for ( ; agd ! = nullptr ; agd = agd - > link )
std : : atomic_store_explicit ( & agd - > readyCnt , agd - > readyCnt - - , std : : memory_order_release ) ; // REALEASE
}
// This function is called by the audio device driver callback _audioDeviceCallback().
// If all devices in any of the groups contained in groupA[] are ready to source/sink
// audio data then this function triggers the condition var on the associated
// group thread to trigger audio processing on those devices. See _audioGroupThreadFunc().
void _audioGroupNotifyIfReady ( io_t * p , audioGroup_t * * groupA , unsigned groupN )
{
// for each device whose audio buffer state changed
for ( unsigned i = 0 ; i < groupN ; + + i )
{
audioGroup_t * ag = groupA [ i ] ;
2021-01-22 14:49:57 +00:00
2021-01-20 18:12:35 +00:00
if ( _audioGroupIsReady ( ag - > msg . iDevL ) & & _audioGroupIsReady ( ag - > msg . oDevL ) )
{
// we now know the group is ready and so the ready count maybe decremented on each device
_audioGroupDecrReadyCount ( ag - > msg . iDevL ) ;
_audioGroupDecrReadyCount ( ag - > msg . oDevL ) ;
2021-01-22 14:49:57 +00:00
2021-01-20 18:12:35 +00:00
// notify the audio group thread that all devices are ready by signaling the condition var that it is blocked on
mutex : : signalCondVar ( ag - > mutexH ) ;
}
}
}
2023-02-15 01:55:03 +00:00
void _audioDevSync ( io_t * p , audioDev_t * * syncDevA , unsigned syncDevN )
{
2023-06-27 21:24:12 +00:00
for ( unsigned i = 0 ; i < syncDevN ; + + i )
2023-02-15 01:55:03 +00:00
for ( audioDev_t * ad = syncDevA [ i ] ; ad ! = nullptr ; ad = ad - > clockLink )
if ( audio : : device : : execute ( p - > audioH , ad - > devIdx ) ! = kOkRC )
cwLogWarning ( " Synced audio device '%s' execution failed. " , ad - > label ) ;
}
2021-12-11 20:14:27 +00:00
// This function is called by the audio device drivers when incoming audio arrives
2021-01-20 18:12:35 +00:00
// or when there is available space to write outgoing audio.
// If all in/out devices in a group are ready to be source/sink audio data then this function
// triggers the group thread condition var thereby causing an application callback
// to process the audio data.
void _audioDeviceCallback ( void * cbArg , audio : : device : : audioPacket_t * inPktArray , unsigned inPktCnt , audio : : device : : audioPacket_t * outPktArray , unsigned outPktCnt )
{
io_t * p = reinterpret_cast < io_t * > ( cbArg ) ;
unsigned groupN = 2 * inPktCnt + outPktCnt ;
audioGroup_t * groupA [ groupN ] ;
unsigned curGroupN = 0 ;
2023-02-15 01:55:03 +00:00
// These arrays are allocated with more space than they need - to avoid 0 length array when inPktCnt or outPktCnt is 0.
audioDev_t * iSyncDevListA [ inPktCnt + outPktCnt ] ;
audioDev_t * oSyncDevListA [ inPktCnt + outPktCnt ] ;
2021-01-20 18:12:35 +00:00
// update the audio buffer
audio : : buf : : update ( p - > audioBufH , inPktArray , inPktCnt , outPktArray , outPktCnt ) ;
// update the readiness of the input devices
for ( unsigned i = 0 ; i < inPktCnt ; + + i )
2023-02-15 01:55:03 +00:00
curGroupN = _audioDeviceUpdateReadiness ( p , inPktArray [ i ] . devIdx , true , groupA , groupN , curGroupN , iSyncDevListA [ i ] ) ;
2021-01-20 18:12:35 +00:00
// update the readiness of the output devices
for ( unsigned i = 0 ; i < outPktCnt ; + + i )
2023-02-15 01:55:03 +00:00
curGroupN = _audioDeviceUpdateReadiness ( p , outPktArray [ i ] . devIdx , false , groupA , groupN , curGroupN , oSyncDevListA [ i ] ) ;
2021-01-20 18:12:35 +00:00
// groupA[] contains the set of groups which may have been made ready during this callback
_audioGroupNotifyIfReady ( p , groupA , curGroupN ) ;
2021-01-22 14:49:57 +00:00
2023-02-15 01:55:03 +00:00
// trigger any devices synced to the calling device
_audioDevSync ( p , iSyncDevListA , inPktCnt ) ;
2023-06-27 21:24:12 +00:00
_audioDevSync ( p , oSyncDevListA , outPktCnt ) ;
2021-01-20 18:12:35 +00:00
}
// Allocate a group-device record and link it to the associated group record.
rc_t _audioGroupAddDevice ( audioGroup_t * ag , bool inputFl , audioDev_t * ad , unsigned chCnt )
{
rc_t rc = kOkRC ;
audio_group_dev_t * new_agd = mem : : allocZ < audio_group_dev_t > ( ) ;
2021-01-22 14:49:57 +00:00
new_agd - > label = ad - > label ;
2021-01-31 16:14:22 +00:00
new_agd - > userId = ad - > userId ;
2021-01-22 14:49:57 +00:00
new_agd - > devName = ad - > devName ;
2021-01-20 18:12:35 +00:00
new_agd - > devIdx = ad - > devIdx ;
2021-01-22 14:49:57 +00:00
new_agd - > flags = inputFl ? kInFl : kOutFl ;
2021-01-20 18:12:35 +00:00
new_agd - > chCnt = chCnt ;
2021-01-22 14:49:57 +00:00
new_agd - > chIdx = inputFl ? ag - > msg . iBufChCnt : ag - > msg . oBufChCnt ;
new_agd - > meterA = mem : : allocZ < sample_t > ( chCnt ) ;
2021-01-20 18:12:35 +00:00
audio_group_dev_t * & agd = inputFl ? ag - > msg . iDevL : ag - > msg . oDevL ;
if ( agd = = nullptr )
agd = new_agd ;
else
{
for ( ; agd ! = nullptr ; agd = agd - > link )
if ( agd - > link = = nullptr )
{
agd - > link = new_agd ;
break ;
}
}
// update the audio group channel count and set the pointers from
// the private audioDevice_t to the public audio_group_dev_t.
if ( inputFl )
{
ag - > msg . iBufChCnt + = chCnt ;
ad - > iagd = new_agd ;
}
else
{
ag - > msg . oBufChCnt + = chCnt ;
ad - > oagd = new_agd ;
}
return rc ;
}
// Given an audio group id return the associated audio group.
2021-01-31 16:14:22 +00:00
audioGroup_t * _audioGroupFromId ( io_t * p , unsigned userId , bool reportMissingFl = true )
2021-01-20 18:12:35 +00:00
{
for ( unsigned i = 0 ; i < p - > audioGroupN ; + + i )
2021-01-31 16:14:22 +00:00
if ( p - > audioGroupA [ i ] . msg . userId = = userId )
2021-01-20 18:12:35 +00:00
return p - > audioGroupA + i ;
2021-01-31 16:14:22 +00:00
if ( reportMissingFl )
cwLogError ( kInvalidArgRC , " An audio group with user id %i could not be found. " , userId ) ;
return nullptr ;
}
audioGroup_t * _audioGroupFromIndex ( io_t * p , unsigned groupIdx , bool reportMissingFl = true )
{
if ( groupIdx < p - > audioGroupN )
return p - > audioGroupA + groupIdx ;
if ( reportMissingFl )
cwLogError ( kInvalidArgRC , " '%i' is not a valid audio group index. " , groupIdx ) ;
2021-01-20 18:12:35 +00:00
return nullptr ;
}
2021-01-31 16:14:22 +00:00
// Given an audio group id return the associated audio group.
audioGroup_t * _audioGroupFromLabel ( io_t * p , const char * label , bool reportMissingFl = true )
{
for ( unsigned i = 0 ; i < p - > audioGroupN ; + + i )
if ( textCompare ( p - > audioGroupA [ i ] . msg . label , label ) = = 0 )
return p - > audioGroupA + i ;
if ( reportMissingFl )
cwLogError ( kInvalidArgRC , " An audio group with labe '%s' could not be found. " , label ) ;
return nullptr ;
}
2021-01-20 18:12:35 +00:00
// Create the audio group records by parsing the cfg. audio.groupL[] list.
rc_t _audioDeviceParseAudioGroupList ( io_t * p , const object_t * c )
{
rc_t rc = kOkRC ;
const object_t * groupL = nullptr ;
if ( ( groupL = c - > find ( " groupL " ) ) = = nullptr )
return cwLogError ( kSyntaxErrorRC , " Audio Group list 'groupL' not found. " ) ;
p - > audioGroupN = groupL - > child_count ( ) ;
p - > audioGroupA = mem : : allocZ < audioGroup_t > ( p - > audioGroupN ) ;
for ( unsigned i = 0 ; i < p - > audioGroupN ; + + i )
{
const object_t * node ;
if ( ( node = groupL - > child_ele ( i ) ) ! = nullptr )
{
if ( ( rc = node - > getv (
" enableFl " , p - > audioGroupA [ i ] . enableFl ,
2022-12-13 18:27:32 +00:00
" asyncFl " , p - > audioGroupA [ i ] . asyncFl ,
2021-01-22 14:49:57 +00:00
" label " , p - > audioGroupA [ i ] . msg . label ,
2021-01-31 16:14:22 +00:00
" id " , p - > audioGroupA [ i ] . msg . userId ,
2021-01-20 18:12:35 +00:00
" srate " , p - > audioGroupA [ i ] . msg . srate ,
" dspFrameCnt " , p - > audioGroupA [ i ] . msg . dspFrameCnt ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error parsing audio group cfg record at index:%i " , i ) ;
goto errLabel ;
}
// create the audio group thread mutex/cond var
if ( ( rc = mutex : : create ( p - > audioGroupA [ i ] . mutexH ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error creating audio group mutex. " ) ;
goto errLabel ;
}
2021-01-22 14:49:57 +00:00
// Lock the mutex so that it is already locked when it is used to block the audio thread
// This avoids having to use logic in the thread callback to lock it on the first entry
// while not locking it on all following entries.
2021-01-20 18:12:35 +00:00
if ( ( rc = mutex : : lock ( p - > audioGroupA [ i ] . mutexH ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error locking audio group mutex. " ) ;
goto errLabel ;
}
// create the audio group thread
2024-02-18 13:37:33 +00:00
if ( ( rc = thread_mach : : add ( p - > threadMachH , _audioGroupThreadFunc , p - > audioGroupA + i , " io_audio_group " ) ) ! = kOkRC )
2021-01-20 18:12:35 +00:00
{
rc = cwLogError ( rc , " Error creating audio group thread. " ) ;
goto errLabel ;
}
p - > audioGroupA [ i ] . p = p ;
p - > audioGroupA [ i ] . threadTimeOutMs = p - > audioThreadTimeOutMs ;
2021-01-31 16:14:22 +00:00
p - > audioGroupA [ i ] . msg . groupIndex = i ;
2021-01-20 18:12:35 +00:00
}
}
errLabel :
return rc ;
}
2023-02-15 01:55:03 +00:00
// Link dstAudioDev onto the list of devices that depend on srcAudioDev's clock.
rc_t _audioDeviceConnectToClockSource ( io_t * p , const char * srcAudioDevLabel , bool clockSrcInputFl , audioDev_t * dstAudioDev )
{
rc_t rc = kOkRC ;
audioDev_t * srcAudioDev = nullptr ;
if ( ( srcAudioDev = _audioDeviceLabelToRecd ( p , srcAudioDevLabel ) ) = = nullptr )
{
rc = cwLogError ( kInvalidArgRC , " The clock source '%s' could not be found for the audio device '%s'. " , cwStringNullGuard ( srcAudioDevLabel ) , cwStringNullGuard ( dstAudioDev - > label ) ) ;
goto errLabel ;
}
if ( clockSrcInputFl )
{
dstAudioDev - > clockLink = srcAudioDev - > clockInList ;
srcAudioDev - > clockInList = dstAudioDev ;
}
else
{
dstAudioDev - > clockLink = srcAudioDev - > clockOutList ;
srcAudioDev - > clockOutList = dstAudioDev ;
}
errLabel :
return rc ;
}
2024-06-11 00:38:44 +00:00
rc_t _audioDeviceConfigure ( io_t * p , audioDev_t * ad , audioGroup_t * iag , audioGroup_t * oag , unsigned cycleCnt , unsigned framesPerCycle )
{
rc_t rc = kOkRC ;
double israte = 0 ;
double osrate = 0 ;
double srate = 0 ;
unsigned iDspFrameCnt = 0 ;
unsigned oDspFrameCnt = 0 ;
unsigned dspFrameCnt = 0 ;
unsigned iChCnt = 0 ;
unsigned oChCnt = 0 ;
const char * inGroupLabel = iag = = nullptr | | iag - > msg . label = = nullptr ? " <no in-group> " : iag - > msg . label ;
const char * outGroupLabel = oag = = nullptr | | oag - > msg . label = = nullptr ? " <no out-group> " : oag - > msg . label ;
// get the ingroup
if ( iag ! = nullptr )
{
israte = iag - > msg . srate ;
iDspFrameCnt = iag - > msg . dspFrameCnt ;
}
// get the outgroup
if ( oag ! = nullptr )
{
osrate = oag - > msg . srate ;
oDspFrameCnt = oag - > msg . dspFrameCnt ;
}
// in-srate and out-srate must be equal or one must be 0
if ( osrate = = 0 | | israte = = 0 | | osrate = = israte )
{
// the true sample rate is the non-zero sample rate
srate = std : : max ( israte , osrate ) ;
}
else
{
rc = cwLogError ( kInvalidArgRC , " The device '%s' belongs to two groups (%s and %s) at different sample rates (%f != %f). " , cwStringNullGuard ( ad - > devName ) , cwStringNullGuard ( inGroupLabel ) , cwStringNullGuard ( outGroupLabel ) , israte , osrate ) ;
goto errLabel ;
}
// in-dspFrameCnt an out-dspFrameCnt must be equal or one must be 0
if ( oDspFrameCnt = = 0 | | iDspFrameCnt = = 0 | | oDspFrameCnt = = iDspFrameCnt )
{
// the true sample rate is the non-zero sample rate
dspFrameCnt = std : : max ( iDspFrameCnt , oDspFrameCnt ) ;
}
else
{
rc = cwLogError ( kInvalidArgRC , " The device '%s' belongs to two groups (%s and %s) width different dspFrameCnt values (%i != %i). " , cwStringNullGuard ( ad - > devName ) , cwStringNullGuard ( inGroupLabel ) , cwStringNullGuard ( outGroupLabel ) , iDspFrameCnt , oDspFrameCnt ) ;
goto errLabel ;
}
// setup the device based on the configuration
if ( ( rc = audio : : device : : setup ( p - > audioH , ad - > devIdx , srate , framesPerCycle , _audioDeviceCallback , p ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Unable to setup the audio hardware device:'%s'. " , ad - > devName ) ;
goto errLabel ;
}
// get the device channel counts
iChCnt = audio : : device : : channelCount ( p - > audioH , ad - > devIdx , true ) ;
oChCnt = audio : : device : : channelCount ( p - > audioH , ad - > devIdx , false ) ;
// initialize the audio bufer for this device
if ( ( rc = audio : : buf : : setup ( p - > audioBufH , ad - > devIdx , srate , dspFrameCnt , cycleCnt , iChCnt , framesPerCycle , oChCnt , framesPerCycle ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio device buffer channel setup failed. " ) ;
goto errLabel ;
}
errLabel :
return rc ;
}
2021-01-20 18:12:35 +00:00
// Create the audio device records by parsing the cfg audio.deviceL[] list.
rc_t _audioDeviceParseAudioDeviceList ( io_t * p , const object_t * cfg )
{
rc_t rc = kOkRC ;
const object_t * deviceL_Node ;
2020-02-29 05:12:57 +00:00
// get the audio device list
2021-01-20 18:12:35 +00:00
if ( ( deviceL_Node = cfg - > find ( " deviceL " ) ) = = nullptr )
2020-02-29 05:12:57 +00:00
{
rc = cwLogError ( kSyntaxErrorRC , " Audio 'deviceL' failed. " ) ;
goto errLabel ;
}
// create an audio device cfg list
2021-01-31 16:14:22 +00:00
p - > audioDevN = audio : : device : : count ( p - > audioH ) ;
2021-01-20 18:12:35 +00:00
p - > audioDevA = mem : : allocZ < audioDev_t > ( p - > audioDevN ) ;
2020-02-29 05:12:57 +00:00
2021-01-31 16:14:22 +00:00
// Initial audioDev record setup
2021-01-20 18:12:35 +00:00
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
2020-02-29 05:12:57 +00:00
{
2021-01-31 16:14:22 +00:00
p - > audioDevA [ i ] . devName = mem : : duplStr ( audio : : device : : label ( p - > audioH , i ) ) ;
p - > audioDevA [ i ] . devIdx = i ;
p - > audioDevA [ i ] . userId = kInvalidId ;
}
// fill in the audio device cfg list
for ( unsigned i = 0 ; i < deviceL_Node - > child_count ( ) ; + + i )
{
2024-06-11 00:38:44 +00:00
audioDev_t * ad = nullptr ;
2023-02-19 19:16:37 +00:00
bool activeFl = false ;
2023-02-02 23:21:34 +00:00
bool meterFl = false ;
2021-01-31 16:14:22 +00:00
char * userLabel = nullptr ;
unsigned userId = kInvalidId ;
char * devName = nullptr ;
2021-01-20 18:12:35 +00:00
unsigned framesPerCycle = 0 ;
2021-01-31 16:14:22 +00:00
unsigned cycleCnt = 0 ;
2021-01-20 18:12:35 +00:00
2021-01-31 16:14:22 +00:00
audioGroup_t * iag = nullptr ;
audioGroup_t * oag = nullptr ;
2024-06-11 00:38:44 +00:00
/*
2021-01-31 16:14:22 +00:00
double israte = 0 ;
double osrate = 0 ;
double srate = 0 ;
unsigned iDspFrameCnt = 0 ;
unsigned oDspFrameCnt = 0 ;
unsigned dspFrameCnt = 0 ;
2024-06-11 00:38:44 +00:00
*/
2021-01-31 16:14:22 +00:00
char * inGroupLabel = nullptr ;
char * outGroupLabel = nullptr ;
2023-02-15 01:55:03 +00:00
const char * clockSrcDev = nullptr ;
bool clockSrcInputFl = false ;
2021-01-20 18:12:35 +00:00
const object_t * node ;
if ( ( node = deviceL_Node - > child_ele ( i ) ) ! = nullptr )
2020-02-29 05:12:57 +00:00
{
if ( ( rc = node - > getv (
2023-02-19 19:16:37 +00:00
" activeFl " , activeFl ,
2021-01-31 16:14:22 +00:00
" label " , userLabel ,
" device " , devName ,
2021-01-20 18:12:35 +00:00
" framesPerCycle " , framesPerCycle ,
" cycleCnt " , cycleCnt ) ) ! = kOkRC )
2020-02-29 05:12:57 +00:00
{
2021-01-31 16:14:22 +00:00
rc = cwLogError ( rc , " Error parsing required audio cfg record at index:%i " , i ) ;
2020-02-29 05:12:57 +00:00
goto errLabel ;
2021-01-31 16:14:22 +00:00
}
2020-02-29 05:12:57 +00:00
2021-01-31 16:14:22 +00:00
if ( ( rc = node - > getv_opt (
" userId " , userId ,
2023-02-02 23:21:34 +00:00
" meterFl " , meterFl ,
2021-01-31 16:14:22 +00:00
" inGroup " , inGroupLabel ,
2023-02-15 01:55:03 +00:00
" outGroup " , outGroupLabel ,
" clockSrcDev " , clockSrcDev ,
" syncToClockSrcDevInputFl " , clockSrcInputFl ) ) ! = kOkRC )
2021-01-31 16:14:22 +00:00
{
rc = cwLogError ( rc , " Error parsing optional audio cfg record at index:%i " , i ) ;
goto errLabel ;
}
}
2020-02-29 05:12:57 +00:00
// if the configuration is enabled
2023-02-19 19:16:37 +00:00
if ( activeFl )
2020-02-29 05:12:57 +00:00
{
2021-01-31 16:14:22 +00:00
// locate the record assoc'd with devName
if ( ( ad = _audioDeviceNameToRecd ( p , devName ) ) = = nullptr )
{
rc = kInvalidArgRC ;
goto errLabel ;
}
2020-02-29 05:12:57 +00:00
// get the hardware device index
2021-01-20 18:12:35 +00:00
if ( ( ad - > devIdx = audio : : device : : labelToIndex ( p - > audioH , ad - > devName ) ) = = kInvalidIdx )
{
rc = cwLogError ( rc , " Unable to locate the audio hardware device:'%s'. " , cwStringNullGuard ( ad - > devName ) ) ;
goto errLabel ;
}
2024-06-11 00:38:44 +00:00
if ( inGroupLabel ! = nullptr )
iag = _audioGroupFromLabel ( p , inGroupLabel ) ;
// get the outgroup
if ( outGroupLabel ! = nullptr )
oag = _audioGroupFromLabel ( p , outGroupLabel ) ;
2021-01-20 18:12:35 +00:00
2024-06-11 00:38:44 +00:00
if ( ( rc = _audioDeviceConfigure ( p , ad , iag , oag , cycleCnt , framesPerCycle ) ) ! = kOkRC )
goto errLabel ;
/*
2021-01-20 18:12:35 +00:00
// get the ingroup
2021-01-31 16:14:22 +00:00
if ( inGroupLabel ! = nullptr )
if ( ( iag = _audioGroupFromLabel ( p , inGroupLabel ) ) ! = nullptr )
{
israte = iag - > msg . srate ;
iDspFrameCnt = iag - > msg . dspFrameCnt ;
}
// get the outgroup
if ( outGroupLabel ! = nullptr )
if ( ( oag = _audioGroupFromLabel ( p , outGroupLabel ) ) ! = nullptr )
{
osrate = oag - > msg . srate ;
oDspFrameCnt = oag - > msg . dspFrameCnt ;
}
2021-01-20 18:12:35 +00:00
2024-06-11 00:38:44 +00:00
// in-srate and out-srate must be equal or one must be 0
2021-01-20 18:12:35 +00:00
if ( osrate = = 0 | | israte = = 0 | | osrate = = israte )
2020-02-29 05:12:57 +00:00
{
2021-01-20 18:12:35 +00:00
// the true sample rate is the non-zero sample rate
srate = std : : max ( israte , osrate ) ;
}
else
{
2021-01-31 16:14:22 +00:00
rc = cwLogError ( kInvalidArgRC , " The device '%s' belongs to two groups (%s and %s) at different sample rates (%f != %f). " , cwStringNullGuard ( ad - > devName ) , cwStringNullGuard ( inGroupLabel ) , cwStringNullGuard ( outGroupLabel ) , israte , osrate ) ;
2020-02-29 05:12:57 +00:00
goto errLabel ;
}
2021-01-20 18:12:35 +00:00
// in-dspFrameCnt an out-dspFrameCnt must be equal or one must be 0
if ( oDspFrameCnt = = 0 | | iDspFrameCnt = = 0 | | oDspFrameCnt = = iDspFrameCnt )
{
// the true sample rate is the non-zero sample rate
dspFrameCnt = std : : max ( iDspFrameCnt , oDspFrameCnt ) ;
}
else
{
2021-01-31 16:14:22 +00:00
rc = cwLogError ( kInvalidArgRC , " The device '%s' belongs to two groups (%s and %s) width different dspFrameCnt values (%i != %i). " , cwStringNullGuard ( ad - > devName ) , cwStringNullGuard ( inGroupLabel ) , cwStringNullGuard ( outGroupLabel ) , iDspFrameCnt , oDspFrameCnt ) ;
2021-01-20 18:12:35 +00:00
goto errLabel ;
}
2020-02-29 05:12:57 +00:00
// setup the device based on the configuration
2021-01-20 18:12:35 +00:00
if ( ( rc = audio : : device : : setup ( p - > audioH , ad - > devIdx , srate , framesPerCycle , _audioDeviceCallback , p ) ) ! = kOkRC )
2020-02-29 05:12:57 +00:00
{
2021-01-20 18:12:35 +00:00
rc = cwLogError ( rc , " Unable to setup the audio hardware device:'%s'. " , ad - > devName ) ;
2020-02-29 05:12:57 +00:00
goto errLabel ;
2021-01-20 18:12:35 +00:00
}
2023-02-15 01:55:03 +00:00
// get the device channel counts
2024-06-11 00:38:44 +00:00
unsigned iChCnt = audio : : device : : channelCount ( p - > audioH , ad - > devIdx , true ) ;
unsigned oChCnt = audio : : device : : channelCount ( p - > audioH , ad - > devIdx , false ) ;
2023-02-15 01:55:03 +00:00
2021-01-20 18:12:35 +00:00
// initialize the audio bufer for this device
if ( ( rc = audio : : buf : : setup ( p - > audioBufH , ad - > devIdx , srate , dspFrameCnt , cycleCnt , iChCnt , framesPerCycle , oChCnt , framesPerCycle ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio device buffer channel setup failed. " ) ;
goto errLabel ;
}
2024-06-11 00:38:44 +00:00
*/
unsigned iChCnt = audio : : device : : channelCount ( p - > audioH , ad - > devIdx , true ) ;
unsigned oChCnt = audio : : device : : channelCount ( p - > audioH , ad - > devIdx , false ) ;
2021-01-20 18:12:35 +00:00
2024-06-11 00:38:44 +00:00
2021-01-31 16:14:22 +00:00
// if an input group was assigned to this device then create a assoc'd audio_group_dev_t
2021-01-20 18:12:35 +00:00
if ( iag ! = nullptr )
2021-01-22 14:49:57 +00:00
{
2021-01-20 18:12:35 +00:00
if ( ( rc = _audioGroupAddDevice ( iag , true , ad , iChCnt ) ) ! = kOkRC )
goto errLabel ;
2021-01-22 14:49:57 +00:00
}
2021-01-31 16:14:22 +00:00
// if an output group was assigned to this device then create a assoc'd audio_group_dev_t
2021-01-20 18:12:35 +00:00
if ( oag ! = nullptr )
2021-01-22 14:49:57 +00:00
{
2021-01-20 18:12:35 +00:00
if ( ( rc = _audioGroupAddDevice ( oag , false , ad , oChCnt ) ) ! = kOkRC )
2021-01-31 16:14:22 +00:00
goto errLabel ;
2021-01-22 14:49:57 +00:00
}
2023-01-05 12:30:57 +00:00
2023-02-15 01:55:03 +00:00
// if this device is dependent on another device as a clock
if ( clockSrcDev ! = nullptr )
{
// ... then link it to the device
if ( ( rc = _audioDeviceConnectToClockSource ( p , clockSrcDev , clockSrcInputFl , ad ) ) ! = kOkRC )
goto errLabel ;
}
2021-01-20 18:12:35 +00:00
// set the device group pointers
2023-02-19 19:16:37 +00:00
ad - > activeFl = activeFl ;
2023-02-02 23:21:34 +00:00
ad - > meterFl = meterFl ;
2021-01-31 16:14:22 +00:00
ad - > label = userLabel ;
ad - > userId = userId ;
ad - > iGroup = iag ;
ad - > oGroup = oag ;
2024-06-11 00:38:44 +00:00
ad - > cycleCnt = cycleCnt ;
ad - > framesPerCycle = framesPerCycle ;
2023-01-05 12:30:57 +00:00
2020-02-29 05:12:57 +00:00
}
}
errLabel :
2020-02-13 16:30:46 +00:00
return rc ;
}
2023-01-05 12:30:57 +00:00
rc_t _audioDeviceEnableMeters ( io_t * p )
{
rc_t rc = kOkRC ;
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
2023-02-19 19:16:37 +00:00
if ( p - > audioDevA [ i ] . activeFl & & p - > audioDevA [ i ] . meterFl )
2023-01-05 12:30:57 +00:00
if ( ( rc = _audioDeviceEnableMeter ( p , p - > audioDevA [ i ] . devIdx , kEnableFl | kInFl | kOutFl ) ) ! = kOkRC )
{
cwLogError ( rc , " Audio enable on device '%s' failed. " , p - > audioDevA [ i ] . label ) ;
goto errLabel ;
}
errLabel :
return rc ;
}
2021-01-20 18:12:35 +00:00
// Allocate the sample ptr buffers for each audio group.
rc_t _audioGroupAllocBuffer ( io_t * p )
{
rc_t rc = kOkRC ;
for ( unsigned i = 0 ; i < p - > audioGroupN ; + + i )
{
audioGroup_t * ag = p - > audioGroupA + i ;
2020-02-13 16:30:46 +00:00
2021-01-20 18:12:35 +00:00
if ( ag - > msg . iBufChCnt )
2021-01-22 14:49:57 +00:00
ag - > msg . iBufArray = mem : : allocZ < sample_t * > ( ag - > msg . iBufChCnt ) ;
2021-01-20 18:12:35 +00:00
if ( ag - > msg . oBufChCnt )
2021-01-22 14:49:57 +00:00
ag - > msg . oBufArray = mem : : allocZ < sample_t * > ( ag - > msg . oBufChCnt ) ;
2021-01-20 18:12:35 +00:00
}
return rc ;
}
2023-02-15 01:55:03 +00:00
rc_t _audioParseConfig ( io_t * p , const object_t * node )
2021-01-20 18:12:35 +00:00
{
2023-02-15 01:55:03 +00:00
rc_t rc = kOkRC ;
2022-06-12 20:50:01 +00:00
2021-01-20 18:12:35 +00:00
// get the meterMs value
2021-01-22 14:49:57 +00:00
if ( ( rc = node - > getv ( " meterMs " , p - > audioMeterCbPeriodMs , " threadTimeOutMs " , p - > audioThreadTimeOutMs ) ) ! = kOkRC )
2021-01-20 18:12:35 +00:00
{
rc = cwLogError ( kSyntaxErrorRC , " Audio 'meterMs' or 'dspFrameCnt' parse failed. " ) ;
goto errLabel ;
}
// initialize the audio buffer
2021-01-22 14:49:57 +00:00
if ( ( rc = audio : : buf : : create ( p - > audioBufH , audio : : device : : count ( p - > audioH ) , p - > audioMeterCbPeriodMs ) ) ! = kOkRC )
2021-01-20 18:12:35 +00:00
{
rc = cwLogError ( rc , " Audio device buffer failed. " ) ;
goto errLabel ;
}
// parse the audio group list
2021-11-03 15:09:07 +00:00
if ( ( rc = _audioDeviceParseAudioGroupList ( p , node ) ) ! = kOkRC )
2021-01-20 18:12:35 +00:00
{
rc = cwLogError ( rc , " Parse audio group list. " ) ;
goto errLabel ;
}
// parse the audio device list
2021-11-03 15:09:07 +00:00
if ( ( rc = _audioDeviceParseAudioDeviceList ( p , node ) ) ! = kOkRC )
2021-01-20 18:12:35 +00:00
{
rc = cwLogError ( rc , " Parse audio device list. " ) ;
goto errLabel ;
}
// create the audio buffer pointer arrays
if ( ( rc = _audioGroupAllocBuffer ( p ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio group buffer allocation failed. " ) ;
goto errLabel ;
}
2023-01-05 12:30:57 +00:00
// initialize enabled audio meters
if ( ( rc = _audioDeviceEnableMeters ( p ) ) ! = kOkRC )
{
2023-02-15 01:55:03 +00:00
rc = cwLogError ( rc , " Audio device enabled failed. " ) ;
goto errLabel ;
2023-01-05 12:30:57 +00:00
}
2021-01-20 18:12:35 +00:00
errLabel :
return rc ;
}
2023-02-15 01:55:03 +00:00
rc_t _audioCreateDeviceFiles ( io_t * p , const object_t * cfg , audio : : device : : driver_t * & audioDrvRef )
{
rc_t rc = kOkRC ;
const object_t * flist = nullptr ;
audioDrvRef = nullptr ;
// locate the audio device file cfg. node
if ( ( rc = cfg - > getv_opt ( " files " , flist ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error obtaining audio device file cfg. node. " ) ;
goto errLabel ;
}
// if no audio device file cfg. node was given - then there are no audio device files
if ( flist = = nullptr )
goto errLabel ;
// create the audio devicce file mgr
if ( ( rc = create ( p - > audioDevFileH , audioDrvRef ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " The audio device file manager create failed. " ) ;
goto errLabel ;
}
for ( unsigned i = 0 ; i < flist - > child_count ( ) ; + + i )
{
const object_t * fnode = flist - > child_ele ( i ) ;
if ( fnode = = nullptr )
{
cwLogWarning ( " The audio device file descriptor at index %i is empty. " , i ) ;
}
else
{
2023-02-26 18:43:39 +00:00
bool enableFl = false ;
const char * label = nullptr ;
const char * iFname = nullptr ;
bool iRwdOnStartFl = false ;
bool iCacheFl = true ;
bool iUseInternalClkFl = false ;
2023-02-15 01:55:03 +00:00
2023-02-26 18:43:39 +00:00
const char * oFname = nullptr ;
unsigned oChCnt = 0 ;
unsigned oCacheBlockSec = 10 ;
bool oRwdOnStartFl = false ;
bool oCacheFl = true ;
bool oUseInternalClkFl = false ;
2023-02-15 01:55:03 +00:00
if ( ( rc = fnode - > getv ( " device_label " , label ) ) ! = kOkRC | | label = = nullptr )
{
cwLogError ( rc , " The label parse failed on audio device descriptor at index %i. " , i ) ;
goto errLabel ;
}
if ( ( rc = fnode - > getv_opt ( " enableFl " , enableFl ,
" in_fname " , iFname ,
" in_rewind_on_start_fl " , iRwdOnStartFl ,
2023-02-20 20:42:11 +00:00
" in_cache_fl " , iCacheFl ,
2023-02-26 18:43:39 +00:00
" out_use_internal_clock_fl " , iUseInternalClkFl ,
2023-02-15 01:55:03 +00:00
" out_fname " , oFname ,
" out_rewind_on_start_fl " , oRwdOnStartFl ,
2023-02-26 18:43:39 +00:00
" out_cache_fl " , oCacheFl ,
" out_use_internal_clock_fl " , oUseInternalClkFl ,
" out_cache_block_sec " , oCacheBlockSec ,
2023-02-15 01:55:03 +00:00
" out_ch_count " , oChCnt ) ) ! = kOkRC )
{
cwLogError ( rc , " The optional variables parse failed on audio device descriptor at index %i label:%s. " , i , cwStringNullGuard ( label ) ) ;
goto errLabel ;
}
if ( enableFl )
{
if ( iFname ! = nullptr )
{
unsigned iFlags = iRwdOnStartFl ? audio : : device : : file : : kRewindOnStartFl : 0 ;
2023-02-20 20:42:11 +00:00
2023-02-26 18:43:39 +00:00
iFlags + = iCacheFl ? audio : : device : : file : : kCacheFl : 0 ;
iFlags + = iUseInternalClkFl ? audio : : device : : file : : kUseInternalClockFl : 0 ;
2023-02-15 01:55:03 +00:00
if ( ( rc = createInDevice ( p - > audioDevFileH , label , iFname , iFlags ) ) ! = kOkRC )
{
cwLogError ( rc , " Create failed on input audio device file '%s'. " , cwStringNullGuard ( label ) ) ;
goto errLabel ;
}
}
if ( oFname ! = nullptr )
{
unsigned oFlags = oRwdOnStartFl ? audio : : device : : file : : kRewindOnStartFl : 0 ;
2023-02-26 18:43:39 +00:00
oFlags + = oCacheFl ? audio : : device : : file : : kCacheFl : 0 ;
oFlags + = oUseInternalClkFl ? audio : : device : : file : : kUseInternalClockFl : 0 ;
// bitsPerSample==0 indicates the output file should use a floating point sample format
if ( ( rc = createOutDevice ( p - > audioDevFileH , label , oFname , oFlags , oChCnt , 0 , oCacheBlockSec ) ) ! = kOkRC )
2023-02-15 01:55:03 +00:00
{
cwLogError ( rc , " Create failed on output audio device file '%s'. " , cwStringNullGuard ( label ) ) ;
goto errLabel ;
}
}
}
}
}
errLabel :
if ( rc ! = kOkRC )
{
audioDrvRef = nullptr ;
rc = cwLogError ( rc , " Audio device file iniitaliztion failed. " ) ;
}
return rc ;
}
2021-01-20 18:12:35 +00:00
2021-01-31 16:14:22 +00:00
rc_t _audioCreate ( io_t * p , const object_t * c )
2020-02-13 16:30:46 +00:00
{
rc_t rc = kOkRC ;
audio : : device : : driver_t * audioDrv = nullptr ;
2024-03-07 16:44:48 +00:00
const object_t * cfg = nullptr ;
bool enableFl = false ;
// get the audio port node
if ( ( cfg = c - > find ( " audio " ) ) = = nullptr )
{
cwLogWarning ( " No 'audio' configuration node. " ) ;
goto errLabel ;
}
2020-02-13 16:30:46 +00:00
2024-03-07 16:44:48 +00:00
if ( ( rc = cfg - > getv ( " enableFl " , enableFl ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Error reading top level audio cfg. " ) ;
goto errLabel ;
}
if ( ! enableFl )
{
cwLogInfo ( " Audio sub-system disabled. " ) ;
goto errLabel ;
}
2023-02-15 01:55:03 +00:00
2020-02-13 16:30:46 +00:00
// initialize the audio device interface
if ( ( rc = audio : : device : : create ( p - > audioH ) ) ! = kOkRC )
{
2021-01-20 18:12:35 +00:00
rc = cwLogError ( rc , " Initialize failed. " ) ;
2020-02-13 16:30:46 +00:00
goto errLabel ;
}
// initialize the ALSA device driver interface
if ( ( rc = audio : : device : : alsa : : create ( p - > alsaH , audioDrv ) ) ! = kOkRC )
{
2021-01-20 18:12:35 +00:00
rc = cwLogError ( rc , " ALSA initialize failed. " ) ;
2020-02-13 16:30:46 +00:00
goto errLabel ;
}
// register the ALSA device driver with the audio interface
if ( ( rc = audio : : device : : registerDriver ( p - > audioH , audioDrv ) ) ! = kOkRC )
{
2021-01-20 18:12:35 +00:00
rc = cwLogError ( rc , " ALSA driver registration failed. " ) ;
2020-02-13 16:30:46 +00:00
goto errLabel ;
}
2024-03-07 16:44:48 +00:00
// create the audio device sub-system - audio device files must be created
// before they can be referenced in _audioParseConfig().
if ( ( rc = _audioCreateDeviceFiles ( p , cfg , audioDrv ) ) ! = kOkRC )
2023-02-15 01:55:03 +00:00
{
rc = cwLogError ( rc , " The audio device file creation failed. " ) ;
goto errLabel ;
}
2024-03-07 16:44:48 +00:00
// register audio device file driver
if ( audioDrv ! = nullptr )
if ( ( rc = audio : : device : : registerDriver ( p - > audioH , audioDrv ) ) ! = kOkRC )
2023-02-15 01:55:03 +00:00
{
2024-03-07 16:44:48 +00:00
rc = cwLogError ( rc , " The audio device file driver registration failed. " ) ;
2023-02-15 01:55:03 +00:00
goto errLabel ;
}
2020-02-13 16:30:46 +00:00
2024-03-07 16:44:48 +00:00
// read the configuration information and setup the audio hardware
if ( ( rc = _audioParseConfig ( p , cfg ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Audio device configuration failed. " ) ;
goto errLabel ;
2023-02-15 01:55:03 +00:00
}
2020-02-13 16:30:46 +00:00
errLabel :
2024-04-06 20:06:17 +00:00
if ( rc ! = kOkRC & & p - > audioH . isValid ( ) )
audio : : device : : report ( p - > audioH ) ;
2020-02-13 16:30:46 +00:00
return rc ;
}
2021-01-22 14:49:57 +00:00
//----------------------------------------------------------------------------------------------------------
//
// UI
//
2021-01-20 18:12:35 +00:00
2022-05-06 20:05:04 +00:00
// This function is called by the websocket with messages coming from a remote UI.
2021-11-03 15:09:07 +00:00
rc_t _uiCallback ( void * cbArg , unsigned wsSessId , ui : : opId_t opId , unsigned parentAppId , unsigned uuId , unsigned appId , unsigned chanId , const ui : : value_t * v )
2021-01-20 18:12:35 +00:00
{
io_t * p = ( io_t * ) cbArg ;
msg_t r ;
2022-12-13 18:27:32 +00:00
rc_t rc = kOkRC ;
rc_t app_rc = kOkRC ;
2021-01-20 18:12:35 +00:00
r . tid = kUiTId ;
2021-11-03 15:09:07 +00:00
r . u . ui = { . opId = opId , . wsSessId = wsSessId , . parentAppId = parentAppId , . uuId = uuId , . appId = appId , . chanId = chanId , . value = v } ;
2021-01-20 18:12:35 +00:00
2022-12-13 18:27:32 +00:00
if ( ( rc = _ioCallback ( p , p - > uiAsyncFl , & r , & app_rc ) ) ! = kOkRC )
cwLogError ( rc , " UI app callback failed. " ) ;
return app_rc ;
2021-01-20 18:12:35 +00:00
}
rc_t _uiConfig ( io_t * p , const object_t * c , const ui : : appIdMap_t * mapA , unsigned mapN )
{
2022-12-12 17:20:59 +00:00
rc_t rc = kOkRC ;
const char * uiCfgLabel = " ui " ;
ui : : ws : : args_t args = { } ;
const object_t * ui_cfg = nullptr ;
2024-03-07 16:44:48 +00:00
bool enableFl = false ;
2021-01-20 18:12:35 +00:00
// Duplicate the application id map
if ( mapN > 0 )
{
p - > uiMapA = mem : : allocZ < ui : : appIdMap_t > ( mapN ) ;
p - > uiMapN = mapN ;
for ( unsigned i = 0 ; i < mapN ; + + i )
{
p - > uiMapA [ i ] = mapA [ i ] ;
p - > uiMapA [ i ] . eleName = mem : : duplStr ( mapA [ i ] . eleName ) ;
}
}
// if a UI cfg record was given
2022-12-12 17:20:59 +00:00
if ( ( ui_cfg = c - > find ( uiCfgLabel ) ) ! = nullptr )
2021-01-20 18:12:35 +00:00
{
2024-03-07 16:44:48 +00:00
if ( ( rc = ui_cfg - > getv ( " enableFl " , enableFl ,
" asyncFl " , p - > uiAsyncFl ) ) ! = kOkRC )
2022-12-12 17:20:59 +00:00
{
rc = cwLogError ( rc , " UI configuration parse failed. " ) ;
goto errLabel ;
}
2024-03-07 16:44:48 +00:00
if ( ! enableFl )
{
cwLogInfo ( " UI sub-system disabled. " ) ;
goto errLabel ;
}
2021-01-20 18:12:35 +00:00
// parse the ui
if ( ( rc = ui : : ws : : parseArgs ( * c , args , uiCfgLabel ) ) = = kOkRC )
{
rc = ui : : ws : : create ( p - > wsUiH , args , p , _uiCallback , args . uiRsrc , p - > uiMapA , p - > uiMapN ) ;
ui : : ws : : releaseArgs ( args ) ;
2023-12-29 18:43:08 +00:00
//ui::enableCache( ui::ws::uiHandle(p->wsUiH) );
2021-01-20 18:12:35 +00:00
}
}
2022-12-12 17:20:59 +00:00
errLabel :
2021-01-20 18:12:35 +00:00
return rc ;
}
rc_t _handleToWsUiHandle ( handle_t h , ui : : ws : : handle_t & uiH_Ref )
{
rc_t rc = kOkRC ;
io_t * p = _handleToPtr ( h ) ;
if ( p ! = nullptr & & p - > wsUiH . isValid ( ) )
uiH_Ref = p - > wsUiH ;
else
rc = cwLogError ( kInvalidStateRC , " Invalid ui::ws handle in io request. " ) ;
return rc ;
}
rc_t _handleToUiHandle ( handle_t h , ui : : handle_t & uiHRef )
{
rc_t rc ;
ui : : ws : : handle_t wsUiH ;
uiHRef . clear ( ) ;
if ( ( rc = _handleToWsUiHandle ( h , wsUiH ) ) ! = kOkRC )
return rc ;
uiHRef = ui : : ws : : uiHandle ( wsUiH ) ;
return uiHRef . isValid ( ) ? rc : cwLogError ( rc , " Invalid ui handle in io request. " ) ;
}
2021-01-22 14:49:57 +00:00
//----------------------------------------------------------------------------------------------------------
//
// IO
//
rc_t _destroy ( io_t * p )
{
rc_t rc = kOkRC ;
// stop thread callbacks
if ( ( rc = thread_mach : : destroy ( p - > threadMachH ) ) ! = kOkRC )
return rc ;
2021-12-30 02:36:16 +00:00
for ( unsigned i = 0 ; i < p - > timerN ; + + i )
mem : : release ( p - > timerA [ i ] . label ) ;
mem : : release ( p - > timerA ) ;
2021-04-10 17:37:07 +00:00
p - > timerN = 0 ;
2023-05-25 20:00:11 +00:00
destroy ( p - > cbMutexH ) ;
2021-10-11 15:53:36 +00:00
_serialPortDestroy ( p ) ;
2021-01-22 14:49:57 +00:00
_audioDestroy ( p ) ;
midi : : device : : destroy ( p - > midiH ) ;
2021-12-30 02:36:16 +00:00
sock : : destroyMgr ( p - > sockH ) ;
2021-01-31 16:14:22 +00:00
mem : : release ( p - > sockA ) ;
2021-01-22 14:49:57 +00:00
for ( unsigned i = 0 ; i < p - > uiMapN ; + + i )
mem : : free ( const_cast < char * > ( p - > uiMapA [ i ] . eleName ) ) ;
mem : : release ( p - > uiMapA ) ;
p - > uiMapN = 0 ;
ui : : ws : : destroy ( p - > wsUiH ) ;
2021-12-30 02:36:16 +00:00
2021-01-22 14:49:57 +00:00
// free the cfg object
if ( p - > cfg ! = nullptr )
p - > cfg - > free ( ) ;
mem : : release ( p ) ;
return rc ;
}
2020-01-27 22:53:44 +00:00
}
}
2021-01-22 14:49:57 +00:00
//----------------------------------------------------------------------------------------------------------
//
// IO
//
2020-01-27 22:53:44 +00:00
2021-01-20 18:12:35 +00:00
cw : : rc_t cw : : io : : create (
handle_t & h ,
const object_t * o ,
cbFunc_t cbFunc ,
void * cbArg ,
const ui : : appIdMap_t * mapA ,
unsigned mapN ,
const char * cfgLabel )
2020-01-27 22:53:44 +00:00
{
rc_t rc ;
if ( ( rc = destroy ( h ) ) ! = kOkRC )
return rc ;
2021-01-22 14:49:57 +00:00
2020-01-27 22:53:44 +00:00
// create the io_t object
io_t * p = mem : : allocZ < io_t > ( ) ;
2021-01-20 18:12:35 +00:00
// duplicate the cfg object so that we can maintain pointers into its elements without
// any chance that they will be delted before the application completes
2021-10-11 15:53:36 +00:00
p - > cfg = o - > duplicate ( ) ;
2021-04-10 17:37:07 +00:00
p - > cbFunc = cbFunc ;
p - > cbArg = cbArg ;
2020-01-27 22:53:44 +00:00
2022-12-12 17:20:59 +00:00
// parse the 'io' configuration block
if ( ( rc = _ioParse ( p , o ) ) ! = kOkRC )
goto errLabel ;
// create the callback mutex
if ( ( rc = mutex : : create ( p - > cbMutexH ) ) ! = kOkRC )
goto errLabel ;
2021-01-20 18:12:35 +00:00
// create the the thread machine
if ( ( rc = thread_mach : : create ( p - > threadMachH ) ) ! = kOkRC )
2020-01-27 22:53:44 +00:00
goto errLabel ;
2021-01-20 18:12:35 +00:00
2020-01-27 22:53:44 +00:00
// create the serial port device
2021-01-20 18:12:35 +00:00
if ( ( rc = _serialPortCreate ( p , p - > cfg ) ) ! = kOkRC )
2020-01-27 22:53:44 +00:00
goto errLabel ;
// create the MIDI port device
2021-01-20 18:12:35 +00:00
if ( ( rc = _midiPortCreate ( p , p - > cfg ) ) ! = kOkRC )
2020-01-27 22:53:44 +00:00
goto errLabel ;
2020-02-13 16:30:46 +00:00
// create the Audio device interface
2021-01-31 16:14:22 +00:00
if ( ( rc = _audioCreate ( p , p - > cfg ) ) ! = kOkRC )
goto errLabel ;
// create the Socket manager
if ( ( rc = _socketParseConfig ( p , p - > cfg ) ) ! = kOkRC )
2020-02-13 16:30:46 +00:00
goto errLabel ;
2020-01-27 22:53:44 +00:00
2021-01-20 18:12:35 +00:00
// create the UI interface
if ( ( rc = _uiConfig ( p , p - > cfg , mapA , mapN ) ) ! = kOkRC )
2020-01-27 22:53:44 +00:00
goto errLabel ;
2021-01-20 18:12:35 +00:00
2020-01-27 22:53:44 +00:00
2021-01-20 18:12:35 +00:00
p - > quitFl . store ( false ) ;
2023-03-21 12:27:32 +00:00
p - > startedFl . store ( false ) ;
2021-01-22 14:49:57 +00:00
time : : get ( p - > t0 ) ;
2021-01-20 18:12:35 +00:00
h . set ( p ) ;
2020-01-27 22:53:44 +00:00
errLabel :
if ( rc ! = kOkRC )
_destroy ( p ) ;
return rc ;
}
cw : : rc_t cw : : io : : destroy ( handle_t & h )
{
rc_t rc = kOkRC ;
if ( ! h . isValid ( ) )
return rc ;
io_t * p = _handleToPtr ( h ) ;
if ( ( rc = _destroy ( p ) ) ! = kOkRC )
return rc ;
h . clear ( ) ;
return rc ;
}
cw : : rc_t cw : : io : : start ( handle_t h )
{
2023-03-21 12:27:32 +00:00
rc_t rc = kOkRC ;
2020-01-27 22:53:44 +00:00
io_t * p = _handleToPtr ( h ) ;
2021-01-20 18:12:35 +00:00
2023-03-21 12:27:32 +00:00
if ( ( rc = _audioDeviceStartStop ( p , true ) ) ! = kOkRC )
goto errLabel ;
if ( ( rc = _serialPortStart ( p ) ) ! = kOkRC )
goto errLabel ;
if ( ( rc = thread_mach : : start ( p - > threadMachH ) ) ! = kOkRC )
{
cwLogError ( rc , " Thread machine start failed. " ) ;
goto errLabel ;
}
p - > startedFl . store ( true ) ;
errLabel :
2021-10-11 15:53:36 +00:00
2023-03-21 12:27:32 +00:00
if ( rc ! = kOkRC )
stop ( h ) ;
2021-01-20 18:12:35 +00:00
2023-03-21 12:27:32 +00:00
return rc ;
2020-01-27 22:53:44 +00:00
}
cw : : rc_t cw : : io : : pause ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
2023-03-21 12:27:32 +00:00
rc_t rc ;
if ( ( rc = thread_mach : : stop ( p - > threadMachH ) ) ! = kOkRC )
{
cwLogError ( rc , " Thread machine stop failed. " ) ;
goto errLabel ;
}
p - > startedFl . store ( false ) ;
errLabel :
return rc ;
2021-01-20 18:12:35 +00:00
}
cw : : rc_t cw : : io : : stop ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
p - > quitFl . store ( true ) ;
2021-12-19 17:16:22 +00:00
2023-03-21 12:27:32 +00:00
rc_t rc0 = thread_mach : : stop ( p - > threadMachH ) ;
2022-12-27 23:10:26 +00:00
2022-12-13 18:27:32 +00:00
// stop the audio devices
2023-03-21 12:27:32 +00:00
rc_t rc1 = _audioDeviceStartStop ( p , false ) ;
2022-12-13 18:27:32 +00:00
// clear the UI
2022-12-27 23:10:26 +00:00
//if( p->wsUiH.isValid() )
// uiDestroyElement(h,ui::kRootUuId);
2021-12-19 17:16:22 +00:00
2023-03-21 12:27:32 +00:00
rc_t rc = rcSelect ( rc0 , rc1 ) ;
if ( rc = = kOkRC )
p - > startedFl . store ( false ) ;
return rc ;
2020-01-27 22:53:44 +00:00
}
2024-03-28 23:48:46 +00:00
cw : : rc_t cw : : io : : exec ( handle_t h , unsigned timeOutMs , void * execCbArg )
2021-01-20 18:12:35 +00:00
{
rc_t rc = kOkRC ;
io_t * p = _handleToPtr ( h ) ;
2021-01-22 14:49:57 +00:00
2023-05-25 20:00:11 +00:00
if ( p - > wsUiH . isValid ( ) )
{
ui : : flushCache ( ui : : ws : : uiHandle ( p - > wsUiH ) ) ;
2024-03-28 23:48:46 +00:00
2024-01-06 13:43:42 +00:00
// Note this call blocks on the websocket handle: See cwUi.h:ws:exec()
2024-03-28 23:48:46 +00:00
rc = ui : : ws : : exec ( p - > wsUiH , timeOutMs ) ;
2023-05-25 20:00:11 +00:00
}
2021-01-22 14:49:57 +00:00
time : : get ( p - > t0 ) ;
if ( p - > audioMeterDevEnabledN )
_audioDeviceProcessMeters ( p ) ;
2022-12-12 17:20:59 +00:00
msg_t m ;
m . tid = kExecTId ;
m . u . exec . execArg = execCbArg ;
_ioCallback ( p , false , & m ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
bool cw : : io : : isShuttingDown ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
2020-01-27 22:53:44 +00:00
2021-01-20 18:12:35 +00:00
return p - > quitFl . load ( ) ;
//return thread_mach::is_shutdown(p->threadMachH);
}
2021-01-31 16:14:22 +00:00
void cw : : io : : report ( handle_t h )
{
for ( unsigned i = 0 ; i < serialDeviceCount ( h ) ; + + i )
printf ( " serial: %s \n " , serialDeviceLabel ( h , i ) ) ;
for ( unsigned i = 0 ; i < midiDeviceCount ( h ) ; + + i )
for ( unsigned j = 0 ; j < 2 ; + + j )
{
bool inputFl = j = = 0 ;
unsigned m = midiDevicePortCount ( h , i , inputFl ) ;
for ( unsigned k = 0 ; k < m ; + + k )
printf ( " midi: %s: %s : %s \n " , inputFl ? " in " : " out " , midiDeviceName ( h , i ) , midiDevicePortName ( h , i , inputFl , k ) ) ;
}
for ( unsigned i = 0 ; i < audioDeviceCount ( h ) ; + + i )
2024-03-28 23:48:46 +00:00
printf ( " audio: %s \n " , cwStringNullGuard ( audioDeviceName ( h , i ) ) ) ;
2023-02-15 01:55:03 +00:00
}
2024-04-06 20:06:17 +00:00
void cw : : io : : hardwareReport ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
audio : : device : : report ( p - > audioH ) ;
midi : : device : : report ( p - > midiH ) ;
}
2023-02-15 01:55:03 +00:00
void cw : : io : : realTimeReport ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
audio : : device : : realTimeReport ( p - > audioH ) ;
2024-03-25 18:30:47 +00:00
uiRealTimeReport ( h ) ;
2021-01-31 16:14:22 +00:00
}
2021-01-20 18:12:35 +00:00
2023-02-15 01:55:03 +00:00
2022-06-12 20:50:01 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Thread
//
2024-02-18 13:37:33 +00:00
cw : : rc_t cw : : io : : threadCreate ( handle_t h , unsigned id , bool asyncFl , void * arg , const char * label )
2022-06-12 20:50:01 +00:00
{
rc_t rc = kOkRC ;
io_t * p = _handleToPtr ( h ) ;
thread_t * t = mem : : allocZ < thread_t > ( 1 ) ;
t - > id = id ;
2022-12-12 17:20:59 +00:00
t - > asyncFl = asyncFl ;
2022-06-12 20:50:01 +00:00
t - > arg = arg ;
t - > p = p ;
t - > link = p - > threadL ;
p - > threadL = t ;
2024-02-18 13:37:33 +00:00
if ( ( rc = thread_mach : : add ( p - > threadMachH , _threadFunc , t , label ) ) ! = kOkRC )
2022-06-12 20:50:01 +00:00
rc = cwLogError ( rc , " Thread create failed. " ) ;
return rc ;
}
2021-04-10 17:37:07 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Timer
//
2022-12-12 17:20:59 +00:00
cw : : rc_t cw : : io : : timerCreate ( handle_t h , const char * label , unsigned id , unsigned periodMicroSec , bool asyncFl )
2021-04-10 17:37:07 +00:00
{
io_t * p = _handleToPtr ( h ) ;
2022-12-12 17:20:59 +00:00
return _timerCreate ( p , label , id , periodMicroSec , asyncFl ) ;
2021-04-10 17:37:07 +00:00
}
cw : : rc_t cw : : io : : timerDestroy ( handle_t h , unsigned timerIdx )
{
io_t * p = _handleToPtr ( h ) ;
timer_t * t = _timerIndexToPtr ( p , timerIdx ) ;
if ( t ! = nullptr )
{
t - > startedFl = false ;
t - > deletedFl = true ;
}
return t = = nullptr ? kInvalidIdRC : kOkRC ;
}
unsigned cw : : io : : timerCount ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return p - > timerN ;
}
unsigned cw : : io : : timerLabelToIndex ( handle_t h , const char * label )
{
io_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > timerN ; + + i )
if ( ! p - > timerA [ i ] . deletedFl & & strcmp ( label , p - > timerA [ i ] . label ) = = 0 )
return i ;
return kInvalidIdx ;
}
unsigned cw : : io : : timerIdToIndex ( handle_t h , unsigned timerId )
{
io_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > timerN ; + + i )
{
timer_t * t = p - > timerA + i ;
if ( ! t - > deletedFl & & t - > id = = timerId )
return i ;
}
return kInvalidIdx ;
}
const char * cw : : io : : timerLabel ( handle_t h , unsigned timerIdx )
{
io_t * p = _handleToPtr ( h ) ;
timer_t * t = _timerIndexToPtr ( p , timerIdx ) ;
return t = = nullptr ? nullptr : t - > label ;
}
unsigned cw : : io : : timerId ( handle_t h , unsigned timerIdx )
{
io_t * p = _handleToPtr ( h ) ;
timer_t * t = _timerIndexToPtr ( p , timerIdx ) ;
return t = = nullptr ? kInvalidId : t - > id ;
}
unsigned cw : : io : : timerPeriodMicroSec ( handle_t h , unsigned timerIdx )
{
io_t * p = _handleToPtr ( h ) ;
timer_t * t = _timerIndexToPtr ( p , timerIdx ) ;
return t = = nullptr ? 0 : t - > periodMicroSec ;
}
cw : : rc_t cw : : io : : timerSetPeriodMicroSec ( handle_t h , unsigned timerIdx , unsigned periodMicroSec )
{
rc_t rc = kOkRC ;
io_t * p = _handleToPtr ( h ) ;
timer_t * t = _timerIndexToPtr ( p , timerIdx ) ;
if ( t = = nullptr )
rc = kInvalidIdRC ;
else
2023-06-27 21:24:12 +00:00
{
2021-04-10 17:37:07 +00:00
p - > timerA [ timerIdx ] . periodMicroSec = periodMicroSec ;
2023-06-27 21:24:12 +00:00
}
return rc ;
}
cw : : rc_t cw : : io : : timerSetNextTime ( handle_t h , unsigned timerIdx , const time : : spec_t & time )
{
rc_t rc = kOkRC ;
io_t * p = _handleToPtr ( h ) ;
timer_t * t = _timerIndexToPtr ( p , timerIdx ) ;
if ( t = = nullptr )
rc = kInvalidIdRC ;
else
{
p - > timerA [ timerIdx ] . nextTime = time ;
}
2021-04-10 17:37:07 +00:00
return rc ;
}
cw : : rc_t cw : : io : : timerStart ( handle_t h , unsigned timerIdx )
{
io_t * p = _handleToPtr ( h ) ;
return _timerStart ( p , timerIdx , true ) ;
}
cw : : rc_t cw : : io : : timerStop ( handle_t h , unsigned timerIdx )
{
io_t * p = _handleToPtr ( h ) ;
return _timerStart ( p , timerIdx , false ) ;
}
2021-01-20 18:12:35 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Serial
//
2024-06-10 20:34:52 +00:00
bool cw : : io : : serialIsEnabled ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return p - > serialN ! = 0 ;
}
2020-01-27 22:53:44 +00:00
unsigned cw : : io : : serialDeviceCount ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return p - > serialN ;
}
2021-10-11 15:53:36 +00:00
unsigned cw : : io : : serialDeviceIndex ( handle_t h , const char * label )
{
io_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > serialN ; + + i )
if ( textCompare ( label , p - > serialA [ i ] . label ) = = 0 )
return i ;
return kInvalidIdx ;
}
2021-01-31 16:14:22 +00:00
const char * cw : : io : : serialDeviceLabel ( handle_t h , unsigned devIdx )
2020-01-27 22:53:44 +00:00
{
io_t * p = _handleToPtr ( h ) ;
2021-01-31 16:14:22 +00:00
return p - > serialA [ devIdx ] . label ;
2020-01-27 22:53:44 +00:00
}
2021-10-11 15:53:36 +00:00
unsigned cw : : io : : serialDeviceId ( handle_t h , unsigned devIdx )
2020-01-27 22:53:44 +00:00
{
io_t * p = _handleToPtr ( h ) ;
2021-10-11 15:53:36 +00:00
return p - > serialA [ devIdx ] . userId ;
}
2020-01-27 22:53:44 +00:00
2021-10-11 15:53:36 +00:00
void cw : : io : : serialDeviceSetId ( handle_t h , unsigned devIdx , unsigned id )
{
io_t * p = _handleToPtr ( h ) ;
p - > serialA [ devIdx ] . userId = id ;
2020-01-27 22:53:44 +00:00
}
2021-10-11 15:53:36 +00:00
2020-01-27 22:53:44 +00:00
cw : : rc_t cw : : io : : serialDeviceSend ( handle_t h , unsigned devIdx , const void * byteA , unsigned byteN )
{
rc_t rc = kOkRC ;
2024-10-12 19:25:03 +00:00
io_t * p = _handleToPtr ( h ) ;
if ( devIdx > = p - > serialN )
{
rc = cwLogError ( kInvalidArgRC , " %i is an invalid serial device index. " , devIdx ) ;
goto errLabel ;
}
if ( ( rc = send ( p - > serialPortSrvH , p - > serialA [ devIdx ] . userId , byteA , byteN ) ) ! = kOkRC )
{
rc = cwLogError ( rc , " Send on serial device index %i failed. " , devIdx ) ;
goto errLabel ;
}
errLabel :
2020-01-27 22:53:44 +00:00
return rc ;
}
2021-01-20 18:12:35 +00:00
//----------------------------------------------------------------------------------------------------------
//
// MIDI
//
2020-01-27 22:53:44 +00:00
2024-06-10 20:34:52 +00:00
bool cw : : io : : midiIsEnabled ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return p - > midiH . isValid ( ) ;
}
2020-01-27 22:53:44 +00:00
unsigned cw : : io : : midiDeviceCount ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
2024-06-10 20:34:52 +00:00
if ( ! p - > midiH . isValid ( ) )
return 0 ;
2020-01-27 22:53:44 +00:00
return midi : : device : : count ( p - > midiH ) ;
}
const char * cw : : io : : midiDeviceName ( handle_t h , unsigned devIdx )
{
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : name ( p - > midiH , devIdx ) ;
}
unsigned cw : : io : : midiDeviceIndex ( handle_t h , const char * devName )
{
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : nameToIndex ( p - > midiH , devName ) ;
}
unsigned cw : : io : : midiDevicePortCount ( handle_t h , unsigned devIdx , bool inputFl )
{
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : portCount ( p - > midiH , devIdx , inputFl ? midi : : kInMpFl : midi : : kOutMpFl ) ;
}
const char * cw : : io : : midiDevicePortName ( handle_t h , unsigned devIdx , bool inputFl , unsigned portIdx )
{
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : portName ( p - > midiH , devIdx , inputFl ? midi : : kInMpFl : midi : : kOutMpFl , portIdx ) ;
}
unsigned cw : : io : : midiDevicePortIndex ( handle_t h , unsigned devIdx , bool inputFl , const char * portName )
{
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : portNameToIndex ( p - > midiH , devIdx , inputFl ? midi : : kInMpFl : midi : : kOutMpFl , portName ) ;
}
2020-03-23 17:14:42 +00:00
cw : : rc_t cw : : io : : midiDeviceSend ( handle_t h , unsigned devIdx , unsigned portIdx , uint8_t status , uint8_t d0 , uint8_t d1 )
2020-01-27 22:53:44 +00:00
{
2021-04-10 17:37:07 +00:00
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : send ( p - > midiH , devIdx , portIdx , status , d0 , d1 ) ;
2020-01-27 22:53:44 +00:00
}
2020-02-29 05:12:57 +00:00
2024-04-06 20:06:17 +00:00
unsigned cw : : io : : midiDeviceMaxBufferMsgCount ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : maxBufferMsgCount ( p - > midiH ) ;
}
const cw : : midi : : ch_msg_t * cw : : io : : midiDeviceBuffer ( handle_t h , unsigned & msgCntRef )
{
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : getBuffer ( p - > midiH , msgCntRef ) ;
}
cw : : rc_t cw : : io : : midiDeviceClearBuffer ( handle_t h , unsigned msgCnt )
{
io_t * p = _handleToPtr ( h ) ;
return midi : : device : : clearBuffer ( p - > midiH , msgCnt ) ;
}
2024-02-21 12:50:52 +00:00
cw : : rc_t cw : : io : : midiOpenMidiFile ( handle_t h , unsigned devIdx , unsigned portIdx , const char * fname )
{
return midi : : device : : openMidiFile ( _handleToPtr ( h ) - > midiH , devIdx , portIdx , fname ) ;
}
cw : : rc_t cw : : io : : midiLoadMsgPacket ( handle_t h , const midi : : packet_t & pkt )
{
return midi : : device : : loadMsgPacket ( _handleToPtr ( h ) - > midiH , pkt ) ;
}
unsigned cw : : io : : midiMsgCount ( handle_t h , unsigned devIdx , unsigned portIdx )
{
return midi : : device : : msgCount ( _handleToPtr ( h ) - > midiH , devIdx , portIdx ) ;
}
cw : : rc_t cw : : io : : midiSeekToMsg ( handle_t h , unsigned devIdx , unsigned portIdx , unsigned msgIdx )
{
return midi : : device : : seekToMsg ( _handleToPtr ( h ) - > midiH , devIdx , portIdx , msgIdx ) ;
}
cw : : rc_t cw : : io : : midiSetEndMsg ( handle_t h , unsigned devIdx , unsigned portIdx , unsigned msgIdx )
{
return midi : : device : : setEndMsg ( _handleToPtr ( h ) - > midiH , devIdx , portIdx , msgIdx ) ;
}
cw : : rc_t cw : : io : : midiFileStart ( handle_t h )
{
return midi : : device : : start ( _handleToPtr ( h ) - > midiH ) ;
}
cw : : rc_t cw : : io : : midiFileStop ( handle_t h )
{
return midi : : device : : stop ( _handleToPtr ( h ) - > midiH ) ;
}
cw : : rc_t cw : : io : : midiFilePause ( handle_t h , bool pauseFl )
{
return midi : : device : : pause ( _handleToPtr ( h ) - > midiH , pauseFl ) ;
}
2021-01-20 18:12:35 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Audio
//
2020-02-29 05:12:57 +00:00
2023-12-29 18:43:08 +00:00
bool cw : : io : : audioIsEnabled ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
for ( unsigned devIdx = 0 ; devIdx < p - > audioDevN ; + + devIdx )
{
audioDev_t * ad ;
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) ! = nullptr & & ad - > activeFl )
return true ;
}
return false ;
}
2020-02-29 05:12:57 +00:00
unsigned cw : : io : : audioDeviceCount ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
2021-01-22 14:49:57 +00:00
return p - > audioDevN ;
2020-02-29 05:12:57 +00:00
}
unsigned cw : : io : : audioDeviceLabelToIndex ( handle_t h , const char * label )
{
2021-01-31 16:14:22 +00:00
io_t * p = _handleToPtr ( h ) ;
audioDev_t * ad ;
if ( ( ad = _audioDeviceLabelToRecd ( p , label ) ) ! = nullptr )
return ad - > devIdx ;
2021-01-22 14:49:57 +00:00
return kInvalidIdx ;
}
2021-12-11 20:14:27 +00:00
const char * cw : : io : : audioDeviceLabel ( handle_t h , unsigned devIdx )
{
io_t * p = _handleToPtr ( h ) ;
audioDev_t * ad ;
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) ! = nullptr )
return ad - > label ;
return nullptr ;
}
2021-01-31 16:14:22 +00:00
cw : : rc_t cw : : io : : audioDeviceSetUserId ( handle_t h , unsigned devIdx , unsigned userId )
{
io_t * p = _handleToPtr ( h ) ;
audioDev_t * ad ;
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) ! = nullptr )
{
ad - > userId = userId ;
if ( ad - > iagd ! = nullptr )
ad - > iagd - > userId = userId ;
if ( ad - > oagd ! = nullptr )
ad - > oagd - > userId = userId ;
}
return kInvalidArgRC ;
}
2023-02-19 19:16:37 +00:00
bool cw : : io : : audioDeviceIsActive ( handle_t h , unsigned devIdx )
2021-01-31 16:14:22 +00:00
{
io_t * p = _handleToPtr ( h ) ;
audioDev_t * ad ;
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) ! = nullptr )
2023-02-19 19:16:37 +00:00
return ad - > activeFl ;
2021-01-31 16:14:22 +00:00
return false ;
}
2021-01-22 14:49:57 +00:00
const char * cw : : io : : audioDeviceName ( handle_t h , unsigned devIdx )
{
2021-01-31 16:14:22 +00:00
io_t * p = _handleToPtr ( h ) ;
2021-01-22 14:49:57 +00:00
audioDev_t * ad ;
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) = = nullptr )
return nullptr ;
return ad - > devName ;
}
2021-12-11 20:14:27 +00:00
unsigned cw : : io : : audioDeviceUserId ( handle_t h , unsigned devIdx )
{
io_t * p = _handleToPtr ( h ) ;
audioDev_t * ad ;
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) = = nullptr )
return kInvalidId ;
return ad - > userId ;
}
2023-02-19 19:16:37 +00:00
cw : : rc_t cw : : io : : audioDeviceEnable ( handle_t h , unsigned devIdx , bool inputFl , bool enableFl )
{
io_t * p = _handleToPtr ( h ) ;
return audio : : device : : enable ( p - > audioH , devIdx , inputFl , enableFl ) ;
}
cw : : rc_t cw : : io : : audioDeviceSeek ( handle_t h , unsigned devIdx , bool inputFl , unsigned frameOffset )
{
io_t * p = _handleToPtr ( h ) ;
return audio : : device : : seek ( p - > audioH , devIdx , inputFl , frameOffset ) ;
}
2021-12-11 20:14:27 +00:00
2021-01-22 14:49:57 +00:00
double cw : : io : : audioDeviceSampleRate ( handle_t h , unsigned devIdx )
{
io_t * p = _handleToPtr ( h ) ;
return audio : : device : : sampleRate ( p - > audioH , devIdx ) ;
}
unsigned cw : : io : : audioDeviceFramesPerCycle ( handle_t h , unsigned devIdx )
{
io_t * p = _handleToPtr ( h ) ;
audioDev_t * ad ;
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) = = nullptr )
return audio : : device : : framesPerCycle ( p - > audioH , devIdx , ad - > iGroup ! = nullptr ) ;
return 0 ;
}
unsigned cw : : io : : audioDeviceChannelCount ( handle_t h , unsigned devIdx , unsigned inOrOutFlag )
{
io_t * p = _handleToPtr ( h ) ;
return audio : : device : : channelCount ( p - > audioH , devIdx , inOrOutFlag & kInFl ) ;
}
cw : : rc_t cw : : io : : audioDeviceEnableMeters ( handle_t h , unsigned devIdx , unsigned inOutEnaFlags )
{
2023-01-05 12:30:57 +00:00
io_t * p = _handleToPtr ( h ) ;
return _audioDeviceEnableMeter ( p , devIdx , inOutEnaFlags ) ;
2020-02-29 05:12:57 +00:00
}
2021-01-22 14:49:57 +00:00
const cw : : io : : sample_t * cw : : io : : audioDeviceMeters ( handle_t h , unsigned devIdx , unsigned & chCntRef , unsigned inOrOutFlag )
2020-02-29 05:12:57 +00:00
{
2021-01-22 14:49:57 +00:00
rc_t rc = kOkRC ;
2020-02-29 05:12:57 +00:00
io_t * p = _handleToPtr ( h ) ;
2022-12-22 20:29:00 +00:00
sample_t * meterA = nullptr ;
2021-01-22 14:49:57 +00:00
audioDev_t * ad ;
if ( ( ad = _audioDeviceIndexToRecd ( p , devIdx ) ) = = nullptr )
rc = kInvalidArgRC ;
else
{
bool inputFl = inOrOutFlag & kInFl ;
audio_group_dev_t * agd = inputFl ? ad - > iagd : ad - > oagd ;
2023-02-15 01:55:03 +00:00
if ( agd ! = nullptr )
2021-01-22 14:49:57 +00:00
{
2023-02-15 01:55:03 +00:00
if ( ! cwIsFlag ( agd - > flags , kMeterFl ) )
rc = cwLogError ( kInvalidArgRC , " The %s meters on device %s are not enabled. " , inputFl ? " input " : " output " , cwStringNullGuard ( ad - > label ) ) ;
else
{
meterA = agd - > meterA ;
chCntRef = agd - > chCnt ;
}
2021-01-22 14:49:57 +00:00
}
}
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " Get meters failed. " ) ;
return meterA ;
}
cw : : rc_t cw : : io : : audioDeviceEnableTone ( handle_t h , unsigned devIdx , unsigned inOutEnaFlags )
{
rc_t rc = kOkRC ;
2023-01-05 12:30:57 +00:00
io_t * p = _handleToPtr ( h ) ;
2021-01-22 14:49:57 +00:00
audioDev_t * ad = nullptr ;
unsigned audioBufFlags = 0 ;
2023-01-05 12:30:57 +00:00
if ( ( rc = _audioDeviceParams ( p , devIdx , inOutEnaFlags , ad , audioBufFlags ) ) ! = kOkRC )
2021-01-22 14:49:57 +00:00
rc = cwLogError ( rc , " Enable tone failed. " ) ;
else
{
audioBufFlags + = audio : : buf : : kToneFl ;
audio : : buf : : setFlag ( p - > audioBufH , devIdx , kInvalidIdx , audioBufFlags ) ;
}
return rc ;
}
cw : : rc_t cw : : io : : audioDeviceToneFlags ( handle_t h , unsigned devIdx , unsigned inOrOutFlag , bool * toneFlA , unsigned chCnt )
{
rc_t rc = kOkRC ;
2023-01-05 12:30:57 +00:00
io_t * p = _handleToPtr ( h ) ;
2021-01-22 14:49:57 +00:00
audioDev_t * ad = nullptr ;
unsigned audioBufFlags = 0 ;
2023-01-05 12:30:57 +00:00
if ( ( rc = _audioDeviceParams ( p , devIdx , inOrOutFlag , ad , audioBufFlags ) ) ! = kOkRC )
2021-01-31 16:14:22 +00:00
rc = cwLogError ( rc , " Get tone flags failed. " ) ;
2021-01-22 14:49:57 +00:00
else
{
audioBufFlags + = audio : : buf : : kToneFl ;
audio : : buf : : toneFlags ( p - > audioBufH , devIdx , audioBufFlags , toneFlA , chCnt ) ;
}
return rc ;
}
cw : : rc_t cw : : io : : audioDeviceEnableMute ( handle_t h , unsigned devIdx , unsigned inOutEnaFlags )
{
rc_t rc = kOkRC ;
2023-01-05 12:30:57 +00:00
io_t * p = _handleToPtr ( h ) ;
2021-01-22 14:49:57 +00:00
audioDev_t * ad = nullptr ;
unsigned audioBufFlags = 0 ;
2023-01-05 12:30:57 +00:00
if ( ( rc = _audioDeviceParams ( p , devIdx , inOutEnaFlags , ad , audioBufFlags ) ) ! = kOkRC )
2021-01-22 14:49:57 +00:00
rc = cwLogError ( rc , " Enable mute failed. " ) ;
else
{
audioBufFlags + = audio : : buf : : kMuteFl ;
audio : : buf : : setFlag ( p - > audioBufH , devIdx , kInvalidIdx , audioBufFlags ) ;
}
return rc ;
}
cw : : rc_t cw : : io : : audioDeviceMuteFlags ( handle_t h , unsigned devIdx , unsigned inOrOutFlag , bool * muteFlA , unsigned chCnt )
{
rc_t rc = kOkRC ;
2023-01-05 12:30:57 +00:00
io_t * p = _handleToPtr ( h ) ;
2021-01-22 14:49:57 +00:00
audioDev_t * ad = nullptr ;
unsigned audioBufFlags = 0 ;
2023-01-05 12:30:57 +00:00
if ( ( rc = _audioDeviceParams ( p , devIdx , inOrOutFlag , ad , audioBufFlags ) ) ! = kOkRC )
2021-01-31 16:14:22 +00:00
rc = cwLogError ( rc , " Get mute flags failed. " ) ;
2021-01-22 14:49:57 +00:00
else
{
audioBufFlags + = audio : : buf : : kMuteFl ;
audio : : buf : : muteFlags ( p - > audioBufH , devIdx , audioBufFlags , muteFlA , chCnt ) ;
}
return rc ;
2020-02-29 05:12:57 +00:00
}
2021-01-20 18:12:35 +00:00
2021-01-31 16:14:22 +00:00
cw : : rc_t cw : : io : : audioDeviceSetGain ( handle_t h , unsigned devIdx , unsigned inOrOutFlag , double gain )
2021-01-22 14:49:57 +00:00
{
rc_t rc = kOkRC ;
2023-01-05 12:30:57 +00:00
io_t * p = _handleToPtr ( h ) ;
2021-01-22 14:49:57 +00:00
audioDev_t * ad = nullptr ;
unsigned audioBufFlags = 0 ;
2023-01-05 12:30:57 +00:00
if ( ( rc = _audioDeviceParams ( p , devIdx , inOrOutFlag , ad , audioBufFlags ) ) ! = kOkRC )
2021-01-22 14:49:57 +00:00
rc = cwLogError ( rc , " Set gain failed. " ) ;
else
{
audio : : buf : : setGain ( p - > audioBufH , devIdx , kInvalidIdx , audioBufFlags , gain ) ;
}
return rc ;
}
2021-01-31 16:14:22 +00:00
cw : : rc_t cw : : io : : audioDeviceGain ( handle_t h , unsigned devIdx , unsigned inOrOutFlag , double * gainA , unsigned chCnt )
{
rc_t rc = kOkRC ;
2023-01-05 12:30:57 +00:00
io_t * p = _handleToPtr ( h ) ;
2021-01-31 16:14:22 +00:00
audioDev_t * ad = nullptr ;
unsigned audioBufFlags = 0 ;
2023-01-05 12:30:57 +00:00
if ( ( rc = _audioDeviceParams ( p , devIdx , inOrOutFlag , ad , audioBufFlags ) ) ! = kOkRC )
2021-01-31 16:14:22 +00:00
rc = cwLogError ( rc , " Get gain failed. " ) ;
else
{
audioBufFlags + = audio : : buf : : kMuteFl ;
audio : : buf : : gain ( p - > audioBufH , devIdx , audioBufFlags , gainA , chCnt ) ;
}
return rc ;
}
unsigned cw : : io : : audioGroupCount ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return p - > audioGroupN ;
}
unsigned cw : : io : : audioGroupLabelToIndex ( handle_t h , const char * label )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
if ( ( ag = _audioGroupFromLabel ( p , label ) ) = = nullptr )
return kInvalidIdx ;
return ag - > msg . groupIndex ;
}
const char * cw : : io : : audioGroupLabel ( handle_t h , unsigned groupIdx )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) ! = nullptr )
return ag - > msg . label ;
return nullptr ;
}
bool cw : : io : : audioGroupIsEnabled ( handle_t h , unsigned groupIdx )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) ! = nullptr )
return ag - > enableFl ;
return false ;
}
unsigned cw : : io : : audioGroupUserId ( handle_t h , unsigned groupIdx )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) ! = nullptr )
return ag - > msg . userId ;
return kInvalidIdx ;
}
cw : : rc_t cw : : io : : audioGroupSetUserId ( handle_t h , unsigned groupIdx , unsigned userId )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) ! = nullptr )
ag - > msg . userId = userId ;
return kInvalidArgRC ;
}
double cw : : io : : audioGroupSampleRate ( handle_t h , unsigned groupIdx )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) ! = nullptr )
return ag - > msg . srate ;
return 0 ;
}
unsigned cw : : io : : audioGroupDspFrameCount ( handle_t h , unsigned groupIdx )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) ! = nullptr )
return ag - > msg . dspFrameCnt ;
return 0 ;
}
2021-01-20 18:12:35 +00:00
2024-06-11 00:38:44 +00:00
cw : : rc_t cw : : io : : audioGroupReconfigure ( handle_t h , unsigned groupIdx , double srate , unsigned dspFrameN )
{
rc_t rc = kOkRC ;
audioGroup_t * ag = nullptr ;
io_t * p = _handleToPtr ( h ) ;
// locate the group record
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) = = nullptr )
goto errLabel ;
// if the parameters are not changing then there is nothing to do
if ( ag - > msg . dspFrameCnt = = dspFrameN & & ag - > msg . srate = = srate )
goto errLabel ;
// change the parameters in the group record
ag - > msg . dspFrameCnt = dspFrameN ;
ag - > msg . srate = srate ;
// stop the audio sub-system
if ( ( rc = _audioDeviceStartStop ( p , false ) ) ! = kOkRC )
goto errLabel ;
// TODO: be sure the audio subsystem is really stopped
// for each audio device
for ( unsigned i = 0 ; i < p - > audioDevN ; + + i )
{
audioDev_t * ad = p - > audioDevA + i ;
// if this devices in-group/out-group was reconfigured
bool iGroupFl = ad - > iGroup ! = nullptr & & ad - > iGroup - > msg . groupIndex = = groupIdx ;
bool oGroupFl = ad - > oGroup ! = nullptr & & ad - > oGroup - > msg . groupIndex = = groupIdx ;
if ( iGroupFl | | oGroupFl )
{
// reconfigure the device with the updated srate and framesPerCycle
if ( ( rc = _audioDeviceConfigure ( p , ad , ad - > oGroup , ad - > oGroup , ad - > cycleCnt , ad - > framesPerCycle ) ) ! = kOkRC )
goto errLabel ;
cwLogInfo ( " The audio device: '%s' was reconfigured srate=%f dspFrameCnt:%i. " , cwStringNullGuard ( ad - > label ) , srate , dspFrameN ) ;
}
}
// restart the audio sub-system
if ( ( rc = _audioDeviceStartStop ( p , true ) ) ! = kOkRC )
goto errLabel ;
errLabel :
if ( rc ! = kOkRC )
rc = cwLogError ( rc , " Audio group reconfiguration failed. " ) ;
return rc ;
}
2021-12-11 20:14:27 +00:00
unsigned cw : : io : : audioGroupDeviceCount ( handle_t h , unsigned groupIdx , unsigned inOrOutFl )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
unsigned n = 0 ;
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) ! = nullptr )
{
audio_group_dev_t * agd = cwIsFlag ( inOrOutFl , kInFl ) ? ag - > msg . iDevL : ag - > msg . oDevL ;
for ( ; agd ! = nullptr ; agd = agd - > link )
+ + n ;
}
return n ;
}
unsigned cw : : io : : audioGroupDeviceIndex ( handle_t h , unsigned groupIdx , unsigned inOrOutFl , unsigned groupDevIdx )
{
audioGroup_t * ag ;
io_t * p = _handleToPtr ( h ) ;
unsigned n = 0 ;
if ( ( ag = _audioGroupFromIndex ( p , groupIdx ) ) ! = nullptr )
{
audio_group_dev_t * agd = cwIsFlag ( inOrOutFl , kInFl ) ? ag - > msg . iDevL : ag - > msg . oDevL ;
for ( ; agd ! = nullptr ; agd = agd - > link )
{
if ( n = = groupDevIdx )
return agd - > devIdx ;
+ + n ;
}
}
cwLogError ( kInvalidIdRC , " The audio group device index '%i' could found on group index: '%i' . " , groupDevIdx , groupIdx ) ;
return kInvalidIdx ;
}
2021-01-20 18:12:35 +00:00
//----------------------------------------------------------------------------------------------------------
//
// Socket
//
2024-06-10 20:34:52 +00:00
bool cw : : io : : socketIsEnabled ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return p - > sockN ! = 0 ;
}
2021-01-20 18:12:35 +00:00
2021-01-31 16:14:22 +00:00
unsigned cw : : io : : socketCount ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return p - > sockN ;
}
unsigned cw : : io : : socketLabelToIndex ( handle_t h , const char * label )
{
io_t * p = _handleToPtr ( h ) ;
for ( unsigned i = 0 ; i < p - > sockN ; + + i )
if ( textCompare ( p - > sockA [ i ] . label , label ) = = 0 )
return i ;
cwLogError ( kInvalidArgRC , " '%s' is not a valid socket label. " , cwStringNullGuard ( label ) ) ;
return kInvalidIdx ;
}
unsigned cw : : io : : socketUserId ( handle_t h , unsigned sockIdx )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return kInvalidId ;
return s - > userId ;
}
cw : : rc_t cw : : io : : socketSetUserId ( handle_t h , unsigned sockIdx , unsigned userId )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return kInvalidArgRC ;
s - > userId = userId ;
return kOkRC ;
}
const char * cw : : io : : socketLabel ( handle_t h , unsigned sockIdx )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return nullptr ;
return s - > label ;
}
const char * cw : : io : : socketHostName ( handle_t h , unsigned sockIdx )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return nullptr ;
return sock : : hostName ( p - > sockH , s - > userId ) ;
}
const char * cw : : io : : socketIpAddress ( handle_t h , unsigned sockIdx )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return nullptr ;
return sock : : ipAddress ( p - > sockH , s - > userId ) ;
}
unsigned cw : : io : : socketInetAddress ( handle_t h , unsigned sockIdx )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return 0 ;
return sock : : inetAddress ( p - > sockH , s - > userId ) ;
}
cw : : sock : : portNumber_t cw : : io : : socketPort ( handle_t h , unsigned sockIdx )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return sock : : kInvalidPortNumber ;
return sock : : port ( p - > sockH , s - > userId ) ;
}
cw : : rc_t cw : : io : : socketPeername ( handle_t h , unsigned sockIdx , struct sockaddr_in * addr )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return kInvalidArgRC ;
return sock : : peername ( p - > sockH , s - > userId , addr ) ;
}
2022-11-14 23:32:32 +00:00
bool cw : : io : : socketIsConnected ( handle_t h , unsigned sockIdx )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return sock : : isConnected ( p - > sockH , s - > userId ) ;
return false ;
}
2021-01-31 16:14:22 +00:00
cw : : rc_t cw : : io : : socketSend ( handle_t h , unsigned sockIdx , unsigned connId , const void * data , unsigned dataByteCnt )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return kInvalidArgRC ;
return sock : : send ( p - > sockH , s - > userId , connId , data , dataByteCnt ) ;
}
cw : : rc_t cw : : io : : socketSend ( handle_t h , unsigned sockIdx , const void * data , unsigned dataByteCnt , const struct sockaddr_in * remoteAddr )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return kInvalidArgRC ;
return sock : : send ( p - > sockH , s - > userId , data , dataByteCnt , remoteAddr ) ;
}
cw : : rc_t cw : : io : : socketSend ( handle_t h , unsigned sockIdx , const void * data , unsigned dataByteCnt , const char * remoteAddr , sock : : portNumber_t remotePort )
{
io_t * p = _handleToPtr ( h ) ;
socket_t * s ;
if ( ( s = _socketIndexToRecd ( p , sockIdx ) ) = = nullptr )
return kInvalidArgRC ;
return sock : : send ( p - > sockH , s - > userId , data , dataByteCnt , remoteAddr , remotePort ) ;
}
2021-01-20 18:12:35 +00:00
//----------------------------------------------------------------------------------------------------------
//
// WebSocket
//
//----------------------------------------------------------------------------------------------------------
//
// UI
//
2024-06-10 20:34:52 +00:00
bool cw : : io : : uiIsEnabled ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
return p - > wsUiH . isValid ( ) ;
}
2021-11-06 02:21:44 +00:00
unsigned cw : : io : : parentAndNameToAppId ( handle_t h , unsigned parentAppId , const char * eleName )
2020-02-29 05:12:57 +00:00
{
2021-01-20 18:12:35 +00:00
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-06 02:21:44 +00:00
return ui : : parentAndNameToAppId ( uiH , parentAppId , eleName ) ;
2021-01-20 18:12:35 +00:00
return kInvalidId ;
2020-02-29 05:12:57 +00:00
}
2021-11-06 02:21:44 +00:00
unsigned cw : : io : : parentAndNameToUuId ( handle_t h , unsigned parentAppId , const char * eleName )
2020-02-29 05:12:57 +00:00
{
2021-01-20 18:12:35 +00:00
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-06 02:21:44 +00:00
return ui : : parentAndNameToUuId ( uiH , parentAppId , eleName ) ;
2021-01-20 18:12:35 +00:00
return kInvalidId ;
2020-02-29 05:12:57 +00:00
}
2021-11-06 02:21:44 +00:00
unsigned cw : : io : : parentAndAppIdToUuId ( handle_t h , unsigned parentAppId , unsigned appId )
2020-02-29 05:12:57 +00:00
{
2021-01-20 18:12:35 +00:00
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-06 02:21:44 +00:00
return ui : : parentAndAppIdToUuId ( uiH , parentAppId , appId ) ;
return kInvalidId ;
2021-01-20 18:12:35 +00:00
}
2021-11-06 02:21:44 +00:00
2021-01-20 18:12:35 +00:00
unsigned cw : : io : : uiFindElementAppId ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
return ui : : findElementAppId ( uiH , uuId ) ;
return kInvalidId ;
}
2021-11-06 02:21:44 +00:00
unsigned cw : : io : : uiFindElementUuId ( handle_t h , const char * eleName , unsigned chanId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
return ui : : findElementUuId ( uiH , eleName , chanId ) ;
return kInvalidId ;
}
unsigned cw : : io : : uiFindElementUuId ( handle_t h , unsigned appId , unsigned chanId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
return ui : : findElementUuId ( uiH , kInvalidId , appId , chanId ) ;
return kInvalidId ;
}
unsigned cw : : io : : uiFindElementUuId ( handle_t h , unsigned parentUuId , const char * eleName , unsigned chanId )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-06 02:21:44 +00:00
return ui : : findElementUuId ( uiH , parentUuId , eleName , chanId ) ;
2021-01-20 18:12:35 +00:00
return kInvalidId ;
2020-02-29 05:12:57 +00:00
}
2021-01-20 18:12:35 +00:00
2021-11-06 02:21:44 +00:00
unsigned cw : : io : : uiFindElementUuId ( handle_t h , unsigned parentUuId , unsigned appId , unsigned chanId )
2021-01-22 14:49:57 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-06 02:21:44 +00:00
return ui : : findElementUuId ( uiH , parentUuId , appId , chanId ) ;
2021-01-22 14:49:57 +00:00
return kInvalidId ;
}
2021-11-06 02:21:44 +00:00
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateFromObject ( handle_t h , const object_t * o , unsigned parentUuId , unsigned chanId , const char * eleName )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createFromObject ( uiH , o , parentUuId , chanId , eleName ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateFromFile ( handle_t h , const char * fn , unsigned parentUuId , unsigned chanId )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createFromFile ( uiH , fn , parentUuId , chanId ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateFromText ( handle_t h , const char * text , unsigned parentUuId , unsigned chanId )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createFromText ( uiH , text , parentUuId , chanId ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2022-12-13 21:53:31 +00:00
cw : : rc_t cw : : io : : uiCreateFromRsrc ( handle_t h , const char * label , unsigned parentUuId , unsigned chanId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : createFromRsrc ( uiH , label , parentUuId , chanId ) ;
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateDiv ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createDiv ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateLabel ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createLabel ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateButton ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createButton ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateCheck ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createCheck ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateCheck ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , bool value )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createCheck ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , value ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateSelect ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createSelect ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateOption ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createOption ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-12-27 21:48:58 +00:00
cw : : rc_t cw : : io : : uiCreateStrDisplay ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : createStrDisplay ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
return rc ;
}
cw : : rc_t cw : : io : : uiCreateStrDisplay ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , const char * value )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : createStrDisplay ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , value ) ;
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateStr ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createStr ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateStr ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , const char * value )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createStr ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , value ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateNumbDisplay ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , unsigned decPl )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createNumbDisplay ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , decPl ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateNumbDisplay ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , unsigned decPl , double value )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createNumbDisplay ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , value ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateNumb ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , double minValue , double maxValue , double stepValue , unsigned decPl )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createNumb ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , minValue , maxValue , stepValue , decPl ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateNumb ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , double minValue , double maxValue , double stepValue , unsigned decPl , double value )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createNumb ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , minValue , maxValue , stepValue , decPl , value ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateProg ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , double minValue , double maxValue )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createProg ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , minValue , maxValue ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateProg ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title , double minValue , double maxValue , double value )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createProg ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title , minValue , maxValue , value ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiCreateLog ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
2021-01-20 18:12:35 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : createLog ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
2021-01-20 18:12:35 +00:00
return rc ;
}
2024-10-12 19:25:03 +00:00
cw : : rc_t cw : : io : : uiCreateVList ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : createVList ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
return rc ;
}
cw : : rc_t cw : : io : : uiCreateHList ( handle_t h , unsigned & uuIdRef , unsigned parentUuId , const char * eleName , unsigned appId , unsigned chanId , const char * clas , const char * title )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : createHList ( uiH , uuIdRef , parentUuId , eleName , appId , chanId , clas , title ) ;
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiSetNumbRange ( handle_t h , unsigned uuId , double minValue , double maxValue , double stepValue , unsigned decPl , double value )
2021-10-12 20:52:08 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : setNumbRange ( uiH , uuId , minValue , maxValue , stepValue , decPl , value ) ;
2021-10-12 20:52:08 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiSetProgRange ( handle_t h , unsigned uuId , double minValue , double maxValue , double value )
2021-10-12 20:52:08 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : setProgRange ( uiH , uuId , minValue , maxValue , value ) ;
2021-10-12 20:52:08 +00:00
return rc ;
}
2021-11-06 02:21:44 +00:00
cw : : rc_t cw : : io : : uiSetLogLine ( handle_t h , unsigned uuId , const char * text )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setLogLine ( uiH , uuId , text ) ;
return rc ;
}
2024-09-21 21:18:12 +00:00
cw : : rc_t cw : : io : : uiEmptyParent ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : emptyParent ( uiH , uuId ) ;
return rc ;
}
2021-11-06 02:21:44 +00:00
cw : : rc_t cw : : io : : uiSetClickable ( handle_t h , unsigned uuId , bool clickableFl )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setClickable ( uiH , uuId , clickableFl ) ;
return rc ;
}
cw : : rc_t cw : : io : : uiClearClickable ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : clearClickable ( uiH , uuId ) ;
return rc ;
}
bool cw : : io : : uiIsClickable ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
return ui : : isClickable ( uiH , uuId ) ;
return false ;
}
cw : : rc_t cw : : io : : uiSetSelect ( handle_t h , unsigned uuId , bool enableFl )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setSelect ( uiH , uuId , enableFl ) ;
return rc ;
}
cw : : rc_t cw : : io : : uiClearSelect ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : clearSelect ( uiH , uuId ) ;
return rc ;
}
bool cw : : io : : uiIsSelected ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
return ui : : isSelected ( uiH , uuId ) ;
return false ;
}
cw : : rc_t cw : : io : : uiSetVisible ( handle_t h , unsigned uuId , bool enableFl )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setVisible ( uiH , uuId , enableFl ) ;
return rc ;
}
cw : : rc_t cw : : io : : uiClearVisible ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : clearVisible ( uiH , uuId ) ;
return rc ;
}
bool cw : : io : : uiIsVisible ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
return ui : : isVisible ( uiH , uuId ) ;
return false ;
}
cw : : rc_t cw : : io : : uiSetEnable ( handle_t h , unsigned uuId , bool enableFl )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setEnable ( uiH , uuId , enableFl ) ;
return rc ;
}
cw : : rc_t cw : : io : : uiClearEnable ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : clearEnable ( uiH , uuId ) ;
return rc ;
}
bool cw : : io : : uiIsEnabled ( handle_t h , unsigned uuId )
2021-11-14 16:54:52 +00:00
{
rc_t rc ;
2021-11-06 02:21:44 +00:00
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
return ui : : isEnabled ( uiH , uuId ) ;
return false ;
2021-11-14 16:54:52 +00:00
}
2021-11-06 02:21:44 +00:00
2021-11-14 16:54:52 +00:00
cw : : rc_t cw : : io : : uiSetOrderKey ( handle_t h , unsigned uuId , int orderKey )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setOrderKey ( uiH , uuId , orderKey ) ;
return rc ;
2021-11-06 02:21:44 +00:00
}
2021-10-12 20:52:08 +00:00
2021-11-14 16:54:52 +00:00
int cw : : io : : uiGetOrderKey ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
int orderKey = 0 ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
orderKey = ui : : getOrderKey ( uiH , uuId ) ;
return orderKey ;
}
2024-05-11 16:10:21 +00:00
cw : : rc_t cw : : io : : uiSetScrollTop ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setScrollTop ( uiH , uuId ) ;
return rc ;
}
2024-10-12 19:25:03 +00:00
cw : : rc_t cw : : io : : uiSetTitle ( handle_t h , unsigned uuId , const char * title )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setTitle ( uiH , uuId , title ) ;
return rc ;
}
2024-05-11 16:10:21 +00:00
2021-11-14 16:54:52 +00:00
cw : : rc_t cw : : io : : uiSetBlob ( handle_t h , unsigned uuId , const void * blob , unsigned blobByteN )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : setBlob ( uiH , uuId , blob , blobByteN ) ;
return rc ;
}
const void * cw : : io : : uiGetBlob ( handle_t h , unsigned uuId , unsigned & blobByteN_Ref )
{
ui : : handle_t uiH ;
if ( _handleToUiHandle ( h , uiH ) = = kOkRC )
return ui : : getBlob ( uiH , uuId , blobByteN_Ref ) ;
blobByteN_Ref = 0 ;
return nullptr ;
}
2024-10-12 19:25:03 +00:00
cw : : rc_t cw : : io : : uiGetBlob ( handle_t h , unsigned uuId , void * buf , unsigned & bufByteN_Ref )
{
unsigned bN = 0 ;
const void * b = uiGetBlob ( h , uuId , bN ) ;
if ( bN > bufByteN_Ref )
{
bufByteN_Ref = 0 ;
return cwLogError ( kBufTooSmallRC , " UI blob buffer is too small. " ) ;
}
memcpy ( buf , b , bN ) ;
bufByteN_Ref = bN ;
return kOkRC ;
}
2021-11-14 16:54:52 +00:00
cw : : rc_t cw : : io : : uiClearBlob ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : clearBlob ( uiH , uuId ) ;
return rc ;
}
2021-01-20 18:12:35 +00:00
cw : : rc_t cw : : io : : uiRegisterAppIdMap ( handle_t h , const ui : : appIdMap_t * map , unsigned mapN )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : registerAppIdMap ( uiH , map , mapN ) ;
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiSendValue ( handle_t h , unsigned uuId , bool value )
2021-01-22 14:49:57 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : sendValueBool ( uiH , uuId , value ) ;
2021-01-22 14:49:57 +00:00
return rc ;
}
2021-11-14 16:54:52 +00:00
cw : : rc_t cw : : io : : uiDestroyElement ( handle_t h , unsigned uuId )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : destroyElement ( uiH , uuId ) ;
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiSendValue ( handle_t h , unsigned uuId , int value )
2021-01-22 14:49:57 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : sendValueInt ( uiH , uuId , value ) ;
2021-01-22 14:49:57 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiSendValue ( handle_t h , unsigned uuId , unsigned value )
2021-01-22 14:49:57 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : sendValueUInt ( uiH , uuId , value ) ;
2021-01-22 14:49:57 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiSendValue ( handle_t h , unsigned uuId , float value )
2021-01-22 14:49:57 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : sendValueFloat ( uiH , uuId , value ) ;
2021-01-22 14:49:57 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiSendValue ( handle_t h , unsigned uuId , double value )
2021-01-22 14:49:57 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : sendValueDouble ( uiH , uuId , value ) ;
2021-01-22 14:49:57 +00:00
return rc ;
}
2021-11-03 15:09:07 +00:00
cw : : rc_t cw : : io : : uiSendValue ( handle_t h , unsigned uuId , const char * value )
2021-01-22 14:49:57 +00:00
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
2021-11-03 15:09:07 +00:00
rc = ui : : sendValueString ( uiH , uuId , value ) ;
2021-01-22 14:49:57 +00:00
return rc ;
}
2023-12-29 18:43:08 +00:00
cw : : rc_t cw : : io : : uiSendMsg ( handle_t h , const char * msg )
{
rc_t rc ;
ui : : handle_t uiH ;
if ( ( rc = _handleToUiHandle ( h , uiH ) ) = = kOkRC )
rc = ui : : sendMsg ( uiH , msg ) ;
return rc ;
}
2024-02-10 16:34:06 +00:00
void cw : : io : : latency_measure_setup ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
p - > latency_meas_enable_fl = true ;
p - > latency_meas_thresh_db = - 50 ;
p - > latency_meas_thresh_lin = pow ( 10.0 , p - > latency_meas_thresh_db / 20.0 ) ;
2024-03-25 14:46:45 +00:00
p - > latency_meas_result . note_on_input_ts = { } ;
p - > latency_meas_result . note_on_output_ts = { } ;
p - > latency_meas_result . audio_in_ts = { } ;
p - > latency_meas_result . audio_out_ts = { } ;
2024-02-10 16:34:06 +00:00
p - > latency_meas_result . audio_in_rms_max = 0 ;
p - > latency_meas_result . audio_out_rms_max = 0 ;
if ( p - > midiH . isValid ( ) )
2024-02-14 15:56:44 +00:00
latency_measure_reset ( p - > midiH ) ;
2024-02-10 16:34:06 +00:00
}
cw : : io : : latency_meas_result_t cw : : io : : latency_measure_result ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
if ( p - > midiH . isValid ( ) )
{
2024-02-14 15:56:44 +00:00
midi : : device : : latency_meas_combined_result_t r = latency_measure_result ( p - > midiH ) ;
2024-02-10 16:34:06 +00:00
2024-02-14 15:56:44 +00:00
p - > latency_meas_result . note_on_input_ts = r . alsa_dev . note_on_input_ts ;
p - > latency_meas_result . note_on_output_ts = r . alsa_dev . note_on_output_ts ;
2024-02-10 16:34:06 +00:00
}
return p - > latency_meas_result ;
}
void cw : : io : : latency_measure_report ( handle_t h )
{
io_t * p = _handleToPtr ( h ) ;
latency_meas_result_t r = latency_measure_result ( h ) ;
unsigned t0 , t1 ;
if ( r . note_on_input_ts . tv_nsec )
{
if ( r . note_on_output_ts . tv_nsec )
{
t0 = time : : elapsedMicros ( r . note_on_input_ts , r . note_on_output_ts ) ;
printf ( " midi in - midi out: %6i \n " , t0 ) ;
}
if ( r . audio_in_ts . tv_nsec )
{
t0 = time : : elapsedMicros ( r . note_on_output_ts , r . audio_in_ts ) ;
t1 = time : : elapsedMicros ( r . note_on_input_ts , r . audio_in_ts ) ;
printf ( " midi out - audio in: %6i %6i \n " , t0 , t1 ) ;
}
if ( r . audio_out_ts . tv_nsec )
{
t0 = time : : elapsedMicros ( r . audio_in_ts , r . audio_out_ts ) ;
t1 = time : : elapsedMicros ( r . note_on_input_ts , r . audio_out_ts ) ;
printf ( " audio in - audio out: %6i %6i \n " , t0 , t1 ) ;
}
}
printf ( " Thresh: %f %f db \n " , p - > latency_meas_thresh_db , p - > latency_meas_thresh_lin ) ;
if ( r . audio_in_rms_max ! = 0 )
printf ( " audio in max:%f %f dB \n " , r . audio_in_rms_max , 20.0 * log10 ( r . audio_in_rms_max ) ) ;
if ( r . audio_out_rms_max ! = 0 )
printf ( " audio out max:%f %f dB \n " , r . audio_out_rms_max , 20.0 * log10 ( r . audio_out_rms_max ) ) ;
}
2023-12-29 18:43:08 +00:00
2022-01-22 14:43:01 +00:00
void cw : : io : : uiReport ( handle_t h )
{
ui : : handle_t uiH ;
if ( _handleToUiHandle ( h , uiH ) = = kOkRC )
ui : : report ( uiH ) ;
}
2023-12-03 16:18:02 +00:00
void cw : : io : : uiRealTimeReport ( handle_t h )
{
2024-03-25 18:30:47 +00:00
ui : : ws : : handle_t uiH ;
if ( _handleToWsUiHandle ( h , uiH ) = = kOkRC )
ui : : ws : : realTimeReport ( uiH ) ;
2023-12-03 16:18:02 +00:00
}