/* * This file is part of libEmuSC, a Sound Canvas emulator library * Copyright (C) 2022-2024 HÃ¥kon Skjelten * * libEmuSC is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * libEmuSC is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with libEmuSC. If not, see . */ // Control ROM decoding is based on the SC55_Soundfont generator written by // Kitrinx and NewRisingSun [ https://github.com/Kitrinx/SC55_Soundfont ] #ifndef __CONTROL_ROM_H__ #define __CONTROL_ROM_H__ #include #include #include #include #include namespace EmuSC { class ControlRom { public: ControlRom(std::string romPath, std::string cpuRomPath); ~ControlRom(); // Internal data structures extracted from the control ROM file struct Sample { // 16 bytes uint8_t volume; // Volume attenuation (0x7f - 0) uint32_t address; // Offset on vsc, bank + scrambled address on SC55. // Bits above 20 are wave bank. uint16_t attackEnd; // boundry between attack and decay? Unconfirmed. uint16_t sampleLen; // Sample Size uint16_t loopLen; // Loop point, used as sample_len - loop_len - 1 uint8_t loopMode; // 2 if not a looping sound, 1 forward then back, // 0 forward only. uint8_t rootKey; // Base pitch of the sample uint16_t pitch; // Fine pitch adjustment, 2048 to 0. Pos. incr. pitch. uint16_t fineVolume; // Always 0x400 on VSC, appears to be 1000ths of a // decibel. Positive is higher volume. }; struct Partial { // 48 bytes in total std::string name; uint8_t breaks[16]; // Note breakpoints corresponding to sample addresses uint16_t samples[16]; // Set of addresses to the sample table. 0 is default }; // and above corresponds to breakpoints struct InstPartial { // 92 bytes in total uint8_t LFO2Waveform; uint8_t LFO2Rate; // LFO frequency uint8_t LFO2Delay; // LFO delay before LFO Fade starts uint8_t LFO2Fade; // LFO fade in, linear increase uint16_t partialIndex; // Partial table index, 0xFFFF for unused int8_t panpot; // [-64, 64]. Default 0x40 (0-127) int8_t coarsePitch; // Shifts pitch in semitones. Default 0x40 int8_t finePitch; // Shifts pitch in cents. Default 0x40 int8_t randPitch; int8_t pitchKeyFlw; uint8_t TVPLFO1Depth; uint8_t TVPLFO2Depth; uint8_t pitchEnvDepth; uint8_t pitchEnvL0; // Pitch Envelope L0 uint8_t pitchEnvL1; // Pitch Envelope L1 uint8_t pitchEnvL2; // Pitch Envelope L2 uint8_t pitchEnvL3; // Pitch Envelope L3 (L4 = 0) uint8_t pitchEnvL5; // Pitch Envelope L5 uint8_t pitchEnvT1; // Pitch Envelope T1 (Attack1) uint8_t pitchEnvT2; // Pitch Envelope T2 (Attack2) uint8_t pitchEnvT3; // Pitch Envelope T3 (Decay1) uint8_t pitchEnvT4; // Pitch Envelope T4 (Decay2) uint8_t pitchEnvT5; // Pitch Envelope T5 (Release) uint8_t pitchETKeyF14; // Pitch Envelope Time Key Follow (T1 - T4) uint8_t pitchETKeyF5; // Pitch Envelope Time Key Follow (T5) uint8_t TVFCOFVelCur; // TVF Cutoff Velocity Curve int8_t TVFBaseFlt; int8_t TVFResonance; int8_t TVFType; // TVF Type [ low pass | high pass | disabled ] uint8_t TVFCFKeyFlw; // TVF Cutoff Frequency Key Follow uint8_t TVFCFKeyFlwC; // TVF Cutoff Frequency Key Follow Curves uint8_t TVFLFO1Depth; uint8_t TVFLFO2Depth; uint8_t TVFEnvDepth; uint8_t TVFEnvL1; // TVF Envelope L1 (L0 = 0) uint8_t TVFEnvL2; // TVF Envelope L2 uint8_t TVFEnvL3; // TVF Envelope L3 uint8_t TVFEnvL4; // TVF Envelope L4 uint8_t TVFEnvL5; // TVF Envelope L5 uint8_t TVFEnvT1; // TVF Envelope T1 uint8_t TVFEnvT2; // TVF Envelope T2 uint8_t TVFEnvT3; // TVF Envelope T3 uint8_t TVFEnvT4; // TVF Envelope T4 uint8_t TVFEnvT5; // TVF Envelope T5 uint8_t TVFETKeyF14; // TVF Envelope Time Key Follow (T1 - T4) uint8_t TVFETKeyF5; // TVF Envelope Time Key Follow (T5) uint8_t TVALvlVelCur; int8_t volume; // Volume attenuation (0x7f - 0) uint8_t TVABiasPoint; // TVA Bias Point, 0=V shape, 1=key>85, 2=flat curve uint8_t TVABiasLevel; uint8_t TVALFO1Depth; uint8_t TVALFO2Depth; uint8_t TVAEnvL1; // TVA Envelope L1 (L0 = 0) uint8_t TVAEnvL2; // TVA Envelope L2 uint8_t TVAEnvL3; // TVA Envelope L3 uint8_t TVAEnvL4; // TVA Envelope L4 (L5 = 0) uint8_t TVAEnvT1; // TVA Envelope T1 (Attack1) uint8_t TVAEnvT2; // TVA Envelope T2 (Attack2) uint8_t TVAEnvT3; // TVA Envelope T3 (Decay1) uint8_t TVAEnvT4; // TVA Envelope T4 (Decay2) uint8_t TVAEnvT5; // TVA Envelope T5 (Release) uint8_t TVAETKeyP14; // TVA Envelope Time Key Presets (T1 - T4) uint8_t TVAETKeyP5; // TVA Envelope Time Key Presets (T5) uint8_t TVAETKeyF14; // TVA Envelope Time Key Follow (T1 - T4) uint8_t TVAETKeyF5; // TVA Envelope Time Key Follow (T5) uint8_t TVAETVSens14; // TVA Envelope Time Velocity Sensitivity (T1 - T4) uint8_t TVAETVSens5; // TVA Envelope Time Velocity Sensitivity (T5) }; struct Instrument { // 204 bytes in total std::string name; uint8_t volume; // Volume attenuation (0x7f - 0) uint8_t LFO1Waveform; uint8_t LFO1Rate; // LFO frequency uint8_t LFO1Delay; uint8_t LFO1Fade; uint8_t partialsUsed; // Bit 0 & 1 => which of the two partials are in use uint8_t pitchCurve; struct InstPartial partials[2]; }; struct DrumSet { // 1164 bytes uint16_t preset[128]; uint8_t volume[128]; uint8_t key[128]; uint8_t assignGroup[128];// AKA exclusive class uint8_t panpot[128]; uint8_t reverb[128]; uint8_t chorus[128]; uint8_t flags[128]; // 0x10 -> accept note on, 0x01 -> accept note off std::string name; // 12 chars }; struct LookupTables { // PROGROM std::array VelocityCurve0; std::array VelocityCurve1; std::array VelocityCurve2; std::array VelocityCurve3; std::array VelocityCurve4; std::array VelocityCurve5; std::array VelocityCurve6; std::array VelocityCurve7; std::array VelocityCurve8; std::array VelocityCurve9; std::array mul2; std::array mul2From85; std::array TVABiasPoint1; std::array TVAEnvTKFP1T14Index; std::array TVAEnvTKFP1T5Index; // std::array mul256; // std::array mul256From60; // std::array mul256From96; // std::array mul256Upto96; std::array PitchScale1; std::array PitchScale2; std::array PitchScale3; // CPUROM std::array TimeKeyFollowDiv; std::array TimeKeyFollow; std::array envelopeTime; std::array LFORate; std::array LFODelayTime; std::array LFOTVFDepth; std::array LFOTVPDepth; std::array LFOSine; std::array TVFEnvDepth; std::array TVFCutoffFreq; std::array TVFResonanceFreq; std::array TVFResonance; std::array PitchEnvDepth; std::array TVFEnvScale; std::array TVAEnvExpChange; std::array TVABiasLevel; std::array TVAPanpot; std::array TVALevelIndex; std::array TVALevel; }; struct LookupTables lookupTables; enum class SynthGen { SC55 = 0, SC55mk2 = 1, SC88 = 2, SC88Pro = 3 }; int dump_demo_songs(std::string path); bool intro_anim_available(void); std::vector get_intro_anim(int animIndex = 0); std::string model(void) { return _model; } std::string version(void) { return _version; } std::string date(void) { return _date; } enum SynthGen generation(void) { return _synthGeneration; } const std::array& get_drum_sets_LUT(void) { return _drumSetsLUT; } const uint8_t max_polyphony(void); std::vector> get_instruments_list(void); std::vector> get_partials_list(void); std::vector> get_samples_list(void); std::vector> get_variations_list(void); std::vector get_drum_sets_list(void); inline struct Instrument& instrument(int i) { return _instruments[i]; } inline struct Partial& partial(int p) { return _partials[p]; } inline struct Sample& sample(int s) { return _samples[s]; } inline struct DrumSet& drumSet(int ds) { return _drumSets[ds]; } inline const std::array& variation(int v) const { return _variations[v]; } inline int numSampleSets(void) { return _samples.size(); } inline int numInstruments(void) { return _instruments.size(); } inline std::vector &get_drumsets_ref(void) { return _drumSets; } private: std::string _romPath; std::string _model; std::string _version; std::string _date; enum SynthModel { sm_SC55, // Original Sound Canvas sm_SC55mkII, // Upgraded model sm_SCC1, // ISA card version sm_SC88, sm_SC88Pro, }; enum SynthModel _synthModel; enum SynthGen _synthGeneration; static constexpr uint8_t _maxPolyphonySC55 = 24; static constexpr uint8_t _maxPolyphonySC55mkII = 28; static constexpr uint8_t _maxPolyphonySC88 = 64; static const std::vector _banksSC55; // Only a placeholder, SC-88 layout is currently unkown static const std::vector _banksSC88; struct _ProgMemoryMapLUT { int VelocityCurve0; int VelocityCurve1; int VelocityCurve2; int VelocityCurve3; int VelocityCurve4; int VelocityCurve5; int VelocityCurve6; int VelocityCurve7; int VelocityCurve8; int VelocityCurve9; int mul2; int mul2From85; int TVABiasPoint1; int TVAEnvTKFP1T14Index; int TVAEnvTKFP1T5Index; // int mul256; // int mul256From60; // int mul256From96; // int mul256Upto96; int PitchScale1; int PitchScale2; int PitchScale3; }; const _ProgMemoryMapLUT SC55_1_21_Prog_LUT { 0x3d1e8, 0x3d268, 0x3d2e8, 0x3d368, 0x3d3e8, 0x3d468, 0x3d4e8, 0x3d568, 0x3d5e8, 0x3d668, 0x3dd82, 0x3de02, 0x3de02, 0x3df82, 0x3e102, 0x3e982, 0x3ea82, 0x3eb82 }; const _ProgMemoryMapLUT SC55mkII_1_01_Prog_LUT { 0x3d1e8, 0x3d268, 0x3d2e8, 0x3d368, 0x3d3e8, 0x3d468, 0x3d4e8, 0x3d568, 0x3d5e8, 0x3d668, 0x3de8c, 0x3df0c, 0x3df0c, 0x3e10c, 0x3e30c, 0x3ee0c, 0x3ef0c, 0x3f00c }; struct _CPUMemoryMapLUT { int TimeKeyFollowDiv; int TimeKeyFollow; int EnvelopeTime; int LFORate; int LFODelayTime; int LFOTVFDepth; int LFOTVPDepth; int LFOSine; int TVFEnvDepth; int TVFCutoffFreq; int TVFResonanceFreq; int TVFResonance; int PitchEnvDepth; int TVFEnvScale; int TVAEnvExpChange; int TVABiasLevel; int TVAPanpot; int TVALevelIndex; int TVALevel; }; const _CPUMemoryMapLUT SC55_1_21_CPU_LUT { 0x679a, 0x67c6, 0x6f12, 0x7012, 0x7112, 0x7212, 0x7312, 0x7412, 0x7512, 0x7612, 0x7715, 0x7816, 0x78f2, 0x79f2, 0x6d10, 0x69c6, 0x6c8f, 0x6b0f, 0x6b8f }; const _CPUMemoryMapLUT SC55mkII_1_01_CPU_LUT { 0x650e, 0x653a, 0x6c86, 0x6486, 0x6e86, 0x6f86, 0x7086, 0x7186, 0x7286, 0x7386, 0x7489, 0x758a, 0x7765, 0x7766, 0x6a84, 0x673a, 0x6a03, 0x6883, 0x6903 }; int _read_lookup_tables_progrom(std::ifstream &romFile); int _read_lookup_tables_cpurom(std::ifstream &romFile); int _read_lut_16bit(std::ifstream &ifs, int pos, std::array &lut); int _read_lut_16bit(std::ifstream &ifs, int pos, std::array &lut); int _read_lut_16bit(std::ifstream &ifs, int pos, std::array &lut); int _identify_model(std::ifstream &romFile); const std::vector &_banks(void); // To be replaced with std::endian::native from C++20 inline bool _le_native(void) { uint16_t n = 1; return (*(uint8_t *) & n); } uint16_t _native_endian_uint16(uint8_t *ptr); uint32_t _native_endian_3bytes_uint32(uint8_t *ptr); uint32_t _native_endian_4bytes_uint32(uint8_t *ptr); int _read_instruments(std::ifstream &romFile); int _read_partials(std::ifstream &romFile); int _read_variations(std::ifstream &romFile); int _read_samples(std::ifstream &romFile); int _read_drum_sets(std::ifstream &romFile); std::array _drumSetsLUT; std::vector _instruments; std::vector _partials; std::vector _samples; std::vector _drumSets; // TODO: define constants for variation table dimensions std::array, 128> _variations; ControlRom(); }; } #endif // __CONTROL_ROM_H__