mirror of https://github.com/probonopd/MiniDexed
https://github.com/probonopd/MiniDexed/issues/11#issuecomment-1052239623 Thanks @rsta2pull/31/head
parent
6291c234ca
commit
79fe4aabbf
@ -0,0 +1,237 @@ |
||||
//
|
||||
// sysexfileloader.cpp
|
||||
//
|
||||
#include "sysexfileloader.h" |
||||
#include <stdio.h> |
||||
#include <dirent.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <assert.h> |
||||
#include <circle/logger.h> |
||||
|
||||
extern uint8_t voices_bank[1][32][156]; |
||||
|
||||
LOGMODULE ("syxfile"); |
||||
|
||||
uint8_t CSysExFileLoader::s_DefaultVoice[SizeSingleVoice] = // FM-Piano
|
||||
{ |
||||
95, 29, 20, 50, 99, 95, 00, 00, 41, 00, 19, 00, 00, 03, 00, 06, 79, 00, 01, 00, 14, // OP6 eg_rate_1-4, level_1-4, kbd_lev_scl_brk_pt, kbd_lev_scl_lft_depth, kbd_lev_scl_rht_depth, kbd_lev_scl_lft_curve, kbd_lev_scl_rht_curve, kbd_rate_scaling, amp_mod_sensitivity, key_vel_sensitivity, operator_output_level, osc_mode, osc_freq_coarse, osc_freq_fine, osc_detune
|
||||
95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 00, 99, 00, 01, 00, 00, // OP5
|
||||
95, 29, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 06, 89, 00, 01, 00, 07, // OP4
|
||||
95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 07, // OP3
|
||||
95, 50, 35, 78, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 07, 58, 00, 14, 00, 07, // OP2
|
||||
96, 25, 25, 67, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 10, // OP1
|
||||
94, 67, 95, 60, 50, 50, 50, 50, // 4 * pitch EG rates, 4 * pitch EG level
|
||||
04, 06, 00, // algorithm, feedback, osc sync
|
||||
34, 33, 00, 00, 00, 04, // lfo speed, lfo delay, lfo pitch_mod_depth, lfo_amp_mod_depth, lfo_sync, lfo_waveform
|
||||
03, 24, // pitch_mod_sensitivity, transpose
|
||||
70, 77, 45, 80, 73, 65, 78, 79, 00, 00 // 10 * char for name ("DEFAULT ")
|
||||
}; |
||||
|
||||
CSysExFileLoader::CSysExFileLoader (const char *pDirName) |
||||
: m_DirName (pDirName), |
||||
m_nBankID (0) |
||||
{ |
||||
m_DirName += "/voice"; |
||||
|
||||
for (unsigned i = 0; i <= MaxVoiceBankID; i++) |
||||
{ |
||||
m_pVoiceBank[i] = nullptr; |
||||
} |
||||
} |
||||
|
||||
CSysExFileLoader::~CSysExFileLoader (void) |
||||
{ |
||||
for (unsigned i = 0; i <= MaxVoiceBankID; i++) |
||||
{ |
||||
delete m_pVoiceBank[i]; |
||||
} |
||||
} |
||||
|
||||
void CSysExFileLoader::Load (void) |
||||
{ |
||||
DIR *pDirectory = opendir (m_DirName.c_str ()); |
||||
if (!pDirectory) |
||||
{ |
||||
LOGWARN ("Directory %s not found", m_DirName.c_str ()); |
||||
|
||||
return; |
||||
} |
||||
|
||||
dirent *pEntry; |
||||
while ((pEntry = readdir (pDirectory)) != nullptr) |
||||
{ |
||||
unsigned nBank; |
||||
size_t nLen = strlen (pEntry->d_name); |
||||
|
||||
if ( nLen < 5 // "[NNNN]N[_name].syx"
|
||||
|| strcmp (&pEntry->d_name[nLen-4], ".syx") != 0 |
||||
|| sscanf (pEntry->d_name, "%u", &nBank) != 1) |
||||
{ |
||||
LOGWARN ("%s: Invalid filename format", pEntry->d_name); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
if ( nBank < 1 |
||||
|| nBank > MaxVoiceBankID+1) |
||||
{ |
||||
LOGWARN ("Bank #%u is not supported", nBank); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
nBank--; // zero-based internally
|
||||
|
||||
if (m_pVoiceBank[nBank]) |
||||
{ |
||||
LOGWARN ("Bank #%u already loaded", nBank+1); |
||||
|
||||
continue; |
||||
} |
||||
|
||||
m_pVoiceBank[nBank] = new TVoiceBank; |
||||
assert (m_pVoiceBank[nBank]); |
||||
|
||||
std::string Filename (m_DirName); |
||||
Filename += "/"; |
||||
Filename += pEntry->d_name; |
||||
|
||||
FILE *pFile = fopen (Filename.c_str (), "rb"); |
||||
if (pFile) |
||||
{ |
||||
if ( fread (m_pVoiceBank[nBank], sizeof (TVoiceBank), 1, pFile) == 1 |
||||
&& m_pVoiceBank[nBank]->StatusStart == 0xF0 |
||||
&& m_pVoiceBank[nBank]->CompanyID == 0x43 |
||||
&& m_pVoiceBank[nBank]->Format == 0x09 |
||||
&& m_pVoiceBank[nBank]->StatusEnd == 0xF7) |
||||
{ |
||||
LOGDBG ("Bank #%u successfully loaded", nBank+1); |
||||
} |
||||
else |
||||
{ |
||||
LOGWARN ("%s: Invalid size or format", Filename.c_str ()); |
||||
|
||||
delete m_pVoiceBank[nBank]; |
||||
m_pVoiceBank[nBank] = nullptr; |
||||
} |
||||
|
||||
fclose (pFile); |
||||
} |
||||
else |
||||
{ |
||||
delete m_pVoiceBank[nBank]; |
||||
m_pVoiceBank[nBank] = nullptr; |
||||
} |
||||
} |
||||
|
||||
closedir (pDirectory); |
||||
} |
||||
|
||||
void CSysExFileLoader::SelectVoiceBank (unsigned nBankID) |
||||
{ |
||||
if (nBankID <= MaxVoiceBankID) |
||||
{ |
||||
m_nBankID = nBankID; |
||||
} |
||||
} |
||||
|
||||
void CSysExFileLoader::GetVoice (unsigned nVoiceID, uint8_t *pVoiceData) |
||||
{ |
||||
if (nVoiceID <= VoicesPerBank) |
||||
{ |
||||
assert (m_nBankID <= MaxVoiceBankID); |
||||
if (m_pVoiceBank[m_nBankID]) |
||||
{ |
||||
DecodePackedVoice (m_pVoiceBank[m_nBankID]->Voice[nVoiceID], pVoiceData); |
||||
|
||||
return; |
||||
} |
||||
else |
||||
{ |
||||
// Use default voices_bank instead of s_DefaultVoice for bank 0,
|
||||
// if the bank was not successfully loaded from disk.
|
||||
if (m_nBankID == 0) |
||||
{ |
||||
memcpy (pVoiceData, voices_bank[0][nVoiceID], SizeSingleVoice); |
||||
|
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
memcpy (pVoiceData, s_DefaultVoice, SizeSingleVoice); |
||||
} |
||||
|
||||
// See: https://github.com/bwhitman/learnfm/blob/master/dx7db.py
|
||||
void CSysExFileLoader::DecodePackedVoice (const uint8_t *pPackedData, uint8_t *pDecodedData) |
||||
{ |
||||
const uint8_t *p = pPackedData; |
||||
uint8_t *o = pDecodedData; |
||||
|
||||
memset (o, 0, 156); |
||||
|
||||
for (unsigned op = 0; op < 6; op++) |
||||
{ |
||||
memcpy(&o[op*21], &p[op*17], 11); |
||||
|
||||
uint8_t leftrightcurves = p[op*17+11]; |
||||
o[op * 21 + 11] = leftrightcurves & 3; |
||||
o[op * 21 + 12] = (leftrightcurves >> 2) & 3; |
||||
|
||||
uint8_t detune_rs = p[op * 17 + 12]; |
||||
o[op * 21 + 13] = detune_rs & 7; |
||||
o[op * 21 + 20] = detune_rs >> 3; |
||||
|
||||
uint8_t kvs_ams = p[op * 17 + 13]; |
||||
o[op * 21 + 14] = kvs_ams & 3; |
||||
o[op * 21 + 15] = kvs_ams >> 2; |
||||
o[op * 21 + 16] = p[op * 17 + 14]; |
||||
|
||||
uint8_t fcoarse_mode = p[op * 17 + 15]; |
||||
o[op * 21 + 17] = fcoarse_mode & 1; |
||||
o[op * 21 + 18] = fcoarse_mode >> 1; |
||||
o[op * 21 + 19] = p[op * 17 + 16]; |
||||
} |
||||
|
||||
memcpy (&o[126], &p[102], 9); |
||||
uint8_t oks_fb = p[111]; |
||||
o[135] = oks_fb & 7; |
||||
o[136] = oks_fb >> 3; |
||||
memcpy (&o[137], &p[112], 4); |
||||
uint8_t lpms_lfw_lks = p[116]; |
||||
o[141] = lpms_lfw_lks & 1; |
||||
o[142] = (lpms_lfw_lks >> 1) & 7; |
||||
o[143] = lpms_lfw_lks >> 4; |
||||
memcpy (&o[144], &p[117], 11); |
||||
o[155] = 0x3f; |
||||
|
||||
// Clamp the unpacked patches to a known max.
|
||||
static uint8_t maxes[] = |
||||
{ |
||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // osc6
|
||||
3, 3, 7, 3, 7, 99, 1, 31, 99, 14, |
||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // osc5
|
||||
3, 3, 7, 3, 7, 99, 1, 31, 99, 14, |
||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // osc4
|
||||
3, 3, 7, 3, 7, 99, 1, 31, 99, 14, |
||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // osc3
|
||||
3, 3, 7, 3, 7, 99, 1, 31, 99, 14, |
||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // osc2
|
||||
3, 3, 7, 3, 7, 99, 1, 31, 99, 14, |
||||
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, // osc1
|
||||
3, 3, 7, 3, 7, 99, 1, 31, 99, 14, |
||||
99, 99, 99, 99, 99, 99, 99, 99, // pitch eg rate & level
|
||||
31, 7, 1, 99, 99, 99, 99, 1, 5, 7, 48, // algorithm etc
|
||||
126, 126, 126, 126, 126, 126, 126, 126, 126, 126, // name
|
||||
127 // operator on/off
|
||||
}; |
||||
|
||||
for (unsigned i = 0; i < 156; i++) |
||||
{ |
||||
if (o[i] > maxes[i]) |
||||
{ |
||||
o[i] = maxes[i]; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
//
|
||||
// sysexfileloader.h
|
||||
//
|
||||
// See: https://github.com/asb2m10/dexed/blob/master/Documentation/sysex-format.txt
|
||||
//
|
||||
#ifndef _sysexfileloader_h |
||||
#define _sysexfileloader_h |
||||
|
||||
#include <stdint.h> |
||||
#include <string> |
||||
#include <circle/macros.h> |
||||
|
||||
class CSysExFileLoader // Loader for DX7 .syx files
|
||||
{ |
||||
public: |
||||
static const unsigned MaxVoiceBankID = 127; // TODO? 16383
|
||||
static const unsigned VoicesPerBank = 32; |
||||
static const size_t SizePackedVoice = 128; |
||||
static const size_t SizeSingleVoice = 156; |
||||
|
||||
struct TVoiceBank |
||||
{ |
||||
uint8_t StatusStart; // 0xF0
|
||||
uint8_t CompanyID; // 0x43
|
||||
uint8_t SubStatus; // 0x00
|
||||
uint8_t Format; // 0x09
|
||||
uint8_t ByteCountMS; // 0x20
|
||||
uint8_t ByteCountLS; // 0x00
|
||||
|
||||
uint8_t Voice[VoicesPerBank][SizePackedVoice]; |
||||
|
||||
uint8_t Checksum; |
||||
uint8_t StatusEnd; // 0xF7
|
||||
} |
||||
PACKED; |
||||
|
||||
public: |
||||
CSysExFileLoader (const char *pDirName = "/sysex"); |
||||
~CSysExFileLoader (void); |
||||
|
||||
void Load (void); |
||||
|
||||
void SelectVoiceBank (unsigned nBankID); // 0 .. 127
|
||||
|
||||
void GetVoice (unsigned nVoiceID, // 0 .. 31
|
||||
uint8_t *pVoiceData); // returns unpacked format (156 bytes)
|
||||
|
||||
private: |
||||
static void DecodePackedVoice (const uint8_t *pPackedData, uint8_t *pDecodedData); |
||||
|
||||
private: |
||||
std::string m_DirName; |
||||
|
||||
TVoiceBank *m_pVoiceBank[MaxVoiceBankID+1]; |
||||
|
||||
unsigned m_nBankID; |
||||
|
||||
static uint8_t s_DefaultVoice[SizeSingleVoice]; |
||||
}; |
||||
|
||||
#endif |
Loading…
Reference in new issue