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:
parent
742fc05e81
commit
8fe6f3627c
948
cwMidiAlsa.cpp
948
cwMidiAlsa.cpp
File diff suppressed because it is too large
Load Diff
44
cwMidiAlsa.h
Normal file
44
cwMidiAlsa.h
Normal 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1169
cwMidiDevice.cpp
1169
cwMidiDevice.cpp
File diff suppressed because it is too large
Load Diff
102
cwMidiDevice.h
102
cwMidiDevice.h
@ -1,15 +1,12 @@
|
|||||||
#ifndef cwMidiPort_H
|
#ifndef cwMidiPort_H
|
||||||
#define cwMidiPort_H
|
#define cwMidiPort_H
|
||||||
|
|
||||||
#include "cwMidiDecls.h"
|
|
||||||
|
|
||||||
namespace cw
|
namespace cw
|
||||||
{
|
{
|
||||||
namespace midi
|
namespace midi
|
||||||
{
|
{
|
||||||
|
|
||||||
//( { file_desc:"Device independent MIDI port related code." kw:[midi]}
|
|
||||||
|
|
||||||
// Flags used to identify input and output ports on MIDI devices
|
// Flags used to identify input and output ports on MIDI devices
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
@ -17,60 +14,29 @@ namespace cw
|
|||||||
kOutMpFl = 0x02
|
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
|
namespace device
|
||||||
{
|
{
|
||||||
typedef handle< struct device_str> handle_t;
|
typedef handle< struct device_str> handle_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 );
|
||||||
|
|
||||||
// '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 destroy( handle_t& h);
|
rc_t destroy( handle_t& h);
|
||||||
bool isInitialized( handle_t h );
|
bool isInitialized( handle_t h );
|
||||||
|
|
||||||
@ -80,15 +46,19 @@ namespace cw
|
|||||||
unsigned portCount( handle_t h, unsigned devIdx, unsigned flags );
|
unsigned portCount( handle_t h, unsigned devIdx, unsigned flags );
|
||||||
const char* portName( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx );
|
const char* portName( handle_t h, unsigned devIdx, unsigned flags, unsigned portIdx );
|
||||||
unsigned portNameToIndex( handle_t h, unsigned devIdx, unsigned flags, const char* portName );
|
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 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 );
|
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.
|
rc_t openMidiFile( handle_t h, unsigned devIdx, unsigned portIdx, const char* fname );
|
||||||
// Set portIdx to -1 to assign the callback to all ports on the specified devices.
|
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 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 );
|
rc_t start( handle_t h );
|
||||||
bool usesCallback( handle_t h, unsigned devIdx, unsigned portIdx, cbFunc_t cbFunc, void* cbDataPtr );
|
rc_t stop( handle_t h );
|
||||||
|
rc_t pause( handle_t h, bool pause_fl );
|
||||||
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
@ -96,13 +66,21 @@ namespace cw
|
|||||||
time::spec_t note_on_output_ts;
|
time::spec_t note_on_output_ts;
|
||||||
} latency_meas_result_t;
|
} latency_meas_result_t;
|
||||||
|
|
||||||
// Reset the latency measurement process.
|
typedef struct
|
||||||
void latency_measure_setup(handle_t h);
|
{
|
||||||
latency_meas_result_t latency_measure_result(handle_t h);
|
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);
|
void report( handle_t h, textBuf::handle_t tbH);
|
||||||
|
|
||||||
rc_t test();
|
rc_t testReport();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
1163
cwMidiFileDev.cpp
1163
cwMidiFileDev.cpp
File diff suppressed because it is too large
Load Diff
118
cwMidiFileDev.h
118
cwMidiFileDev.h
@ -2,37 +2,99 @@ namespace cw
|
|||||||
{
|
{
|
||||||
namespace midi
|
namespace midi
|
||||||
{
|
{
|
||||||
namespace file_dev
|
namespace device
|
||||||
{
|
{
|
||||||
typedef handle<struct file_dev_str> handle_t;
|
namespace file_dev
|
||||||
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
const file::trackMsg_t* msg;
|
|
||||||
unsigned file_idx;
|
typedef handle<struct file_dev_str> handle_t;
|
||||||
} msg_t;
|
|
||||||
|
|
||||||
rc_t read( handle_t h, msg_t* buf, unsigned buf_msg_cnt, unsigned& actual_msg_cnt_ref );
|
|
||||||
|
|
||||||
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 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user