midi::device now allows a MIDI file to act as a virtual MIDI input device.

Thread polling moved out of midi::alsa and into midi::device.
Added latency measure feature to midi::device.
Improved midi::alsa device lookup time with alsa_device_t.clientIdToDevIdxMap[].
Added cwMidiAlsa.h to provide an interface for midi::alsa independent of midi::device.
This commit is contained in:
kevin 2024-02-14 11:20:40 -05:00
parent 742fc05e81
commit 8fe6f3627c
6 changed files with 2053 additions and 1491 deletions

File diff suppressed because it is too large Load Diff

44
cwMidiAlsa.h Normal file
View File

@ -0,0 +1,44 @@
namespace cw
{
namespace midi
{
namespace device
{
namespace alsa
{
typedef handle< struct alsa_device_str> handle_t;
rc_t create( handle_t& h, cbFunc_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, const char* appNameStr );
rc_t destroy( handle_t& h);
bool isInitialized( handle_t h );
struct pollfd* pollFdArray( handle_t h, unsigned& arrayCntRef );
rc_t handleInputMsg( handle_t h );
unsigned count( handle_t h );
const char* name( handle_t h, unsigned devIdx );
unsigned nameToIndex(handle_t h, const char* deviceName);
unsigned portCount( handle_t h, unsigned devIdx, unsigned flags );
const char* portName( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx );
unsigned portNameToIndex( handle_t h, unsigned devIdx, unsigned flags, const char* portName );
rc_t portEnable( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx, bool enableFl );
rc_t send( handle_t h, unsigned devIdx, unsigned portIdx, uint8_t st, uint8_t d0, uint8_t d1 );
rc_t sendData( handle_t h, unsigned devIdx, unsigned portIdx, const uint8_t* dataPtr, unsigned byteCnt );
// Latency measurment:
// Record the time of the next incoming note-on msg
// and the time of the next outgoing note-on msg
// Reset the latency measurement process.
void latency_measure_reset(handle_t h);
latency_meas_result_t latency_measure_result(handle_t h);
void report( handle_t h, textBuf::handle_t tbH);
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,12 @@
#ifndef cwMidiPort_H
#define cwMidiPort_H
#include "cwMidiDecls.h"
namespace cw
{
namespace midi
{
//( { file_desc:"Device independent MIDI port related code." kw:[midi]}
// Flags used to identify input and output ports on MIDI devices
enum
{
@ -17,60 +14,29 @@ namespace cw
kOutMpFl = 0x02
};
typedef void (*cbFunc_t)( const packet_t* pktArray, unsigned pktCnt );
//)
//( { label:parser file_desc:"MIDI event parser converts raw MIDI events into packet_t messages." kw:[midi]}
//===============================================================================================
// MIDI Parser
//
namespace parser
{
typedef handle<struct parser_str> handle_t;
// 'cbFunc' and 'cbDataPtr' are optional. If 'cbFunc' is not supplied in the call to
// create() it may be supplied later by installCallback().
// 'bufByteCnt' defines is the largest complete system-exclusive message the parser will
// by able to transmit. System-exclusive messages larger than this will be broken into
// multiple sequential callbacks.
rc_t create( handle_t& hRef, unsigned devIdx, unsigned portIdx, cbFunc_t cbFunc, void* cbArg, unsigned bufByteCnt );
rc_t destroy( handle_t& hRef );
unsigned errorCount( handle_t h );
void parseMidiData( handle_t h, const time::spec_t* timestamp, const uint8_t* buf, unsigned bufByteCnt );
// The following two functions are intended to be used togetther.
// Use midiTriple() to insert pre-parsed msg's to the output buffer,
// and then use transmit() to send the buffer via the parsers callback function.
// Set the data bytes to 0xff if they are not used by the message.
rc_t midiTriple( handle_t h, const time::spec_t* timestamp, uint8_t status, uint8_t d0, uint8_t d1 );
rc_t transmit( handle_t h );
// Install/Remove additional callbacks.
rc_t installCallback( handle_t h, cbFunc_t cbFunc, void* cbDataPtr );
rc_t removeCallback( handle_t h, cbFunc_t cbFunc, void* cbDataPtr );
// Returns true if the parser uses the given callback.
bool hasCallback( handle_t h, cbFunc_t cbFunc, void* cbDataPtr );
}
//)
//( { label:cmMidiPort file_desc:"Device independent MIDI port." kw:[midi]}
//===============================================================================================
// MIDI Device Interface
//
namespace device
{
typedef handle< struct device_str> handle_t;
// 'cbFunc' and 'cbDataPtr' are optional (they may be set to NULL). In this case
// 'cbFunc' and 'cbDataPtr' may be set in a later call to cmMpInstallCallback().
rc_t create( handle_t& h, cbFunc_t cbFunc, void* cbDataPtr, unsigned parserBufByteCnt, const char* appNameStr );
rc_t create( handle_t& h,
cbFunc_t cbFunc,
void* cbArg,
const char* filePortLabelA[], // filePortLabelA[ maxFileCnt ]
unsigned maxFileCnt, // count of file dev ports
const char* appNameStr,
const char* fileDevName = "file_dev",
unsigned fileDevReadAheadMicros = 3000,
unsigned parserBufByteCnt = 1024 );
rc_t create( handle_t& h,
cbFunc_t cbFunc,
void* cbArg,
const object_t* args );
rc_t destroy( handle_t& h);
bool isInitialized( handle_t h );
@ -80,15 +46,19 @@ namespace cw
unsigned portCount( handle_t h, unsigned devIdx, unsigned flags );
const char* portName( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx );
unsigned portNameToIndex( handle_t h, unsigned devIdx, unsigned flags, const char* portName );
rc_t portEnable( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx, bool enableFl );
rc_t send( handle_t h, unsigned devIdx, unsigned portIdx, uint8_t st, uint8_t d0, uint8_t d1 );
rc_t sendData( handle_t h, unsigned devIdx, unsigned portIdx, const uint8_t* dataPtr, unsigned byteCnt );
// Set devIdx to -1 to assign the callback to all devices.
// Set portIdx to -1 to assign the callback to all ports on the specified devices.
//
rc_t installCallback( handle_t h, unsigned devIdx, unsigned portIdx, cbFunc_t cbFunc, void* cbDataPtr );
rc_t removeCallback( handle_t h, unsigned devIdx, unsigned portIdx, cbFunc_t cbFunc, void* cbDataPtr );
bool usesCallback( handle_t h, unsigned devIdx, unsigned portIdx, cbFunc_t cbFunc, void* cbDataPtr );
rc_t openMidiFile( handle_t h, unsigned devIdx, unsigned portIdx, const char* fname );
rc_t seekToMsg( handle_t h, unsigned devIdx, unsigned portIdx, unsigned msgIdx );
rc_t setEndMsg( handle_t h, unsigned devIdx, unsigned portidx, unsigned msgIdx );
rc_t start( handle_t h );
rc_t stop( handle_t h );
rc_t pause( handle_t h, bool pause_fl );
typedef struct
{
@ -96,13 +66,21 @@ namespace cw
time::spec_t note_on_output_ts;
} latency_meas_result_t;
// Reset the latency measurement process.
void latency_measure_setup(handle_t h);
latency_meas_result_t latency_measure_result(handle_t h);
typedef struct
{
latency_meas_result_t alsa_dev;
latency_meas_result_t file_dev;
} latency_meas_combined_result_t;
// Reset the latency measurement process. Record the time of the first
// incoming note-on msg and the first outgoing note-on msg.
void latency_measure_reset(handle_t h);
latency_meas_combined_result_t latency_measure_result(handle_t h);
rc_t report( handle_t h );
void report( handle_t h, textBuf::handle_t tbH);
rc_t test();
rc_t testReport();
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -2,37 +2,99 @@ namespace cw
{
namespace midi
{
namespace file_dev
namespace device
{
typedef handle<struct file_dev_str> handle_t;
rc_t create( handle_t& hRef, const char* labelA[], unsigned max_file_cnt );
rc_t destroy( handle_t& hRef );
unsigned file_count( handle_t h );
rc_t open_midi_file( handle_t h, unsigned file_idx, const char* fname );
rc_t seek_to_event( handle_t h, unsigned file_idx, unsigned msg_idx );
rc_t start( handle_t h );
rc_t stop( handle_t h );
rc_t enable_file( handle_t h, unsigned file_idx );
rc_t disable_file( handle_t h,unsigned file_idx );
int file_descriptor( handle_t h );
typedef struct msg_str
namespace file_dev
{
const file::trackMsg_t* msg;
unsigned file_idx;
} msg_t;
rc_t read( handle_t h, msg_t* buf, unsigned buf_msg_cnt, unsigned& actual_msg_cnt_ref );
typedef handle<struct file_dev_str> handle_t;
rc_t test( const object_t* cfg );
//
// Count of labels determines the count of ports.
// Note that port indexes and file indexes are synonomous.
rc_t create( handle_t& hRef,
cbFunc_t cbFunc,
void* cbArg,
unsigned baseDevIdx, // device index assigned to packet_t.devIndex in callbacks
const char* labelA[], // labelA[ max_file_cnt ] assigns a textual label to each port.
unsigned max_file_cnt,
const char* dev_name = "file_dev", // name of the file device
unsigned read_ahead_micros = 3000); // exec() will xmit events up to 'read_ahead_micros' after the current time
rc_t destroy( handle_t& hRef );
// Return true if at least one enabled file exists, otherwise return false.
bool is_active( handle_t h );
// Returns create(...,max_file_cnt...)
unsigned file_count( handle_t h );
// Assign a MIDI file to an input port.
rc_t open_midi_file( handle_t h, unsigned file_idx, const char* fname );
rc_t load_messages( handle_t h, unsigned file_idx, const msg_t* msgA, unsigned msgN );
// Enable and disble the output of the specified file/port.
rc_t enable_file( handle_t h, unsigned file_idx, bool enableFl );
rc_t enable_file( handle_t h, unsigned file_idx );
rc_t disable_file( handle_t h,unsigned file_idx );
// Device count: Always 1.
unsigned count( handle_t h );
// Device name as set in create()
const char* name( handle_t h, unsigned devIdx );
// Returns 0 if deviceName == name() else kInvalidIdx
unsigned nameToIndex(handle_t h, const char* deviceName);
// The count of ports is determined by count of labels in create(...,labelA[],...).
unsigned portCount( handle_t h, unsigned devIdx, unsigned flags );
// Port name are provided by create(...,labelA[],...)
// Port indexes and file indexes are the same.
const char* portName( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx );
unsigned portNameToIndex( handle_t h, unsigned devIdx, unsigned flags, const char* portName );
rc_t portEnable( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx, bool enableFl );
typedef struct exec_result_str
{
unsigned next_msg_wait_micros; // microseconds before the next file msg must be transmitted
unsigned xmit_cnt; // count of msg's sent to callback during this exec
bool eof_fl; // true if there are no more file msg's to transmit
} exec_result_t;
// Set the next msg to be returned.
rc_t seek_to_msg_index( handle_t h, unsigned file_idx, unsigned msg_idx );
rc_t set_end_msg_index( handle_t h, unsigned file_idx, unsigned msg_idx );
// Seek to the start of the file or to the last msg_idx set by seek_to_event().
rc_t rewind( handle_t h );
// Delay the first MIDI msg by 'start_delay_micros'.
rc_t set_start_delay( handle_t h, unsigned start_delay_micros );
// Callback create(...,cbFunc,...) with msg's whose time has expired and return the
// time delay prior to the next message.
exec_result_t exec( handle_t h, unsigned long long elapsed_micros );
rc_t send( handle_t h, unsigned devIdx, unsigned portIdx, uint8_t st, uint8_t d0, uint8_t d1 );
rc_t sendData( handle_t h, unsigned devIdx, unsigned portIdx, const uint8_t* dataPtr, unsigned byteCnt );
// Reset the latency measurement process.
void latency_measure_reset(handle_t h);
latency_meas_result_t latency_measure_result(handle_t h);
void report( handle_t h, textBuf::handle_t tbH );
rc_t test( const object_t* cfg );
}
}
}
}