libcw/cwMidi.cpp

289 lines
6.7 KiB
C++
Raw Normal View History

//| Copyright: (C) 2020-2024 Kevin Larke <contact AT larke DOT org>
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
#include "cwCommon.h"
#include "cwLog.h"
#include "cwCommonImpl.h"
#include "cwTest.h"
#include "cwMem.h"
#include "cwTime.h"
#include "cwMidi.h"
namespace cw {
namespace midi {
typedef struct statusDesc_str
{
uint8_t status;
uint8_t byteCnt;
const char* label;
} statusDesc_t;
statusDesc_t _statusDescArray[] =
{
// channel messages
{ kNoteOffMdId, 2, "nof" },
{ kNoteOnMdId, 2, "non" },
{ kPolyPresMdId,2, "ppr" },
{ kCtlMdId, 2, "ctl" },
{ kPgmMdId, 1, "pgm" },
{ kChPresMdId, 1, "cpr" },
{ kPbendMdId, 2, "pb" },
{ kSysExMdId, kInvalidMidiByte,"sex" },
// system common
{ kSysComMtcMdId, 1, "mtc" },
{ kSysComSppMdId, 2, "spp" },
{ kSysComSelMdId, 1, "sel" },
{ kSysComUndef0MdId, 0, "cu0" },
{ kSysComUndef1MdId, 0, "cu1" },
{ kSysComTuneMdId, 0, "tun" },
{ kSysComEoxMdId, 0, "eox" },
// system real-time
{ kSysRtClockMdId, 0, "clk" },
{ kSysRtUndef0MdId,0, "ud0" },
{ kSysRtStartMdId, 0, "beg" },
{ kSysRtContMdId, 0, "cnt" },
{ kSysRtStopMdId, 0, "end" },
{ kSysRtUndef1MdId,0, "ud1" },
{ kSysRtSenseMdId, 0, "sns" },
{ kSysRtResetMdId, 0, "rst" },
{ kInvalidStatusMdId, kInvalidMidiByte, "ERR" }
};
statusDesc_t _metaStatusDescArray[] =
{
{ kSeqNumbMdId, 2, "seqn" },
{ kTextMdId, 0xff, "text" },
{ kCopyMdId, 0xff, "copy" },
{ kTrkNameMdId, 0xff, "name" },
{ kInstrNameMdId, 0xff, "instr" },
{ kLyricsMdId, 0xff, "lyric" },
{ kMarkerMdId, 0xff, "mark" },
{ kCuePointMdId, 0xff, "cue" },
{ kMidiChMdId, 1, "chan" },
{ kMidiPortMdId, 1, "port" },
{ kEndOfTrkMdId, 0, "eot" },
{ kTempoMdId, 3, "tempo" },
{ kSmpteMdId, 5, "smpte" },
{ kTimeSigMdId, 4, "tsig" },
{ kKeySigMdId, 2, "ksig" },
{ kSeqSpecMdId, 0xff, "seqs" },
{ kInvalidMetaMdId, kInvalidMidiByte, "ERROR"}
};
statusDesc_t _pedalLabel[] =
{
{ kSustainCtlMdId, 0, "sustn" },
{ kPortamentoCtlMdId, 0, "porta" },
{ kSostenutoCtlMdId, 0, "sostn" },
{ kSoftPedalCtlMdId, 0, "soft" },
{ kLegatoCtlMdId, 0, "legat" },
{ kInvalidMidiByte, kInvalidMidiByte, "ERROR"}
};
}
}
//====================================================================================================
const char* cw::midi::statusToLabel( uint8_t status )
{
unsigned i;
if( !isStatus(status) )
return NULL;
// remove the channel value from ch msg status bytes
if( isChStatus(status) )
status &= 0xf0;
for(i=0; _statusDescArray[i].status != kInvalidStatusMdId; ++i)
if( _statusDescArray[i].status == status )
return _statusDescArray[i].label;
return _statusDescArray[i].label;
}
const char* cw::midi::metaStatusToLabel( uint8_t metaStatus )
{
int i;
for(i=0; _metaStatusDescArray[i].status != kInvalidMetaMdId; ++i)
if( _metaStatusDescArray[i].status == metaStatus )
break;
return _metaStatusDescArray[i].label;
}
const char* cw::midi::pedalLabel( uint8_t d0 )
{
int i;
for(i=0; _pedalLabel[i].status != kInvalidMidiByte; ++i)
if( _pedalLabel[i].status == d0 )
break;
return _pedalLabel[i].label;
}
uint8_t cw::midi::statusToByteCount( uint8_t status )
{
unsigned i;
if( !isStatus(status) )
return kInvalidMidiByte;
// remove the channel value from ch msg status bytes
if( isChStatus(status) )
status &= 0xf0;
for(i=0; _statusDescArray[i].status != kInvalidStatusMdId; ++i)
if( _statusDescArray[i].status == status )
return _statusDescArray[i].byteCnt;
assert(0);
return 0;
}
unsigned cw::midi::to14Bits( uint8_t d0, uint8_t d1 )
{
unsigned val = d0;
val <<= 7;
val += d1;
return val;
}
void cw::midi::split14Bits( unsigned v, uint8_t& d0Ref, uint8_t& d1Ref )
{
d0Ref = (v & 0x3f80) >> 7;
d1Ref = v & 0x7f;
}
int cw::midi::toPbend( uint8_t d0, uint8_t d1 )
{
int v = to14Bits(d0,d1);
return v - 8192;
}
void cw::midi::splitPbend( int v, uint8_t& d0Ref, uint8_t& d1Ref )
{
unsigned uv = v + 8192;
split14Bits(uv,d0Ref,d1Ref);
}
//====================================================================================================
const char* cw::midi::midiToSciPitch( uint8_t pitch, char* label, unsigned labelCharCnt )
{
static char buf[ kMidiSciPitchCharCnt ];
if( label == NULL || labelCharCnt == 0 )
{
label = buf;
labelCharCnt = kMidiSciPitchCharCnt;
}
assert( labelCharCnt >= kMidiSciPitchCharCnt );
if( /*pitch < 0 ||*/ pitch > 127 )
{
label[0] = 0;
return label;
}
assert( labelCharCnt >= 5 && /*pitch >= 0 &&*/ pitch <= 127 );
char noteV[] = { 'C', 'C', 'D', 'D', 'E', 'F', 'F', 'G', 'G', 'A', 'A', 'B' };
char shrpV[] = { ' ', '#', ' ', '#', ' ', ' ', '#', ' ', '#', ' ', '#', ' ' };
int octave = (pitch / 12)-1;
unsigned noteIdx = pitch % 12;
char noteCh = noteV[ noteIdx ];
char sharpCh = shrpV[ noteIdx ];
unsigned idx = 1;
label[labelCharCnt-1] = 0;
label[0] = noteCh;
if( sharpCh != ' ' )
{
label[1] = sharpCh;
idx = 2;
}
assert( -1 <= octave && octave <= 9);
snprintf(label+idx,kMidiSciPitchCharCnt-idx-1,"%i",octave);
return label;
}
uint8_t cw::midi::sciPitchToMidiPitch( char pitch, int acc, int octave )
{
int idx = -1;
switch(tolower(pitch))
{
case 'a': idx = 9; break;
case 'b': idx = 11; break;
case 'c': idx = 0; break;
case 'd': idx = 2; break;
case 'e': idx = 4; break;
case 'f': idx = 5; break;
case 'g': idx = 7; break;
default:
return kInvalidMidiPitch;
}
unsigned rv = (octave*12) + idx + acc + 12;
if( rv <= 127 )
return rv;
return kInvalidMidiPitch;
}
uint8_t cw::midi::sciPitchToMidi( const char* sciPitchStr )
{
const char* cp = sciPitchStr;
bool sharpFl = false;
bool flatFl = false;
int octave;
int acc = 0;
if( sciPitchStr==NULL || strlen(sciPitchStr) > 5 )
return kInvalidMidiPitch;
// skip over leading letter
++cp;
if( !(*cp) )
return kInvalidMidiPitch;
if((sharpFl = *cp=='#') == true )
acc = 1;
else
if((flatFl = *cp=='b') == true )
acc = -1;
if( sharpFl || flatFl )
{
++cp;
if( !(*cp) )
return kInvalidMidiPitch;
}
if( isdigit(*cp) == false && *cp!='-' )
return kInvalidMidiPitch;
octave = atoi(cp);
return sciPitchToMidiPitch( *sciPitchStr, acc, octave );
}