diff --git a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj index 7fe282cb3..b55e538c2 100644 --- a/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj +++ b/Plugins/MIDI/MIDI.xcodeproj/project.pbxproj @@ -23,6 +23,13 @@ 8356BCD527B354050074E50C /* libbasswv.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8356BCCD27B353E30074E50C /* libbasswv.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83686AAC1C5C69D400671C7A /* AUPlayerView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83686AAB1C5C69D400671C7A /* AUPlayerView.mm */; }; 83686AB11C5C783000671C7A /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83686AB01C5C783000671C7A /* CoreAudioKit.framework */; }; + 836E66962D854D5700B62C38 /* SCPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 836E66952D854D5600B62C38 /* SCPlayer.mm */; }; + 836E66992D855A7500B62C38 /* libemusc.0.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 836E66982D855A7500B62C38 /* libemusc.0.dylib */; }; + 836E669E2D855AC600B62C38 /* r00233567_control.bin in Resources */ = {isa = PBXBuildFile; fileRef = 836E669A2D855AAF00B62C38 /* r00233567_control.bin */; }; + 836E669F2D855AC800B62C38 /* r15199858_main_mcu.bin in Resources */ = {isa = PBXBuildFile; fileRef = 836E669B2D855AAF00B62C38 /* r15199858_main_mcu.bin */; }; + 836E66A02D855ACA00B62C38 /* r15209359_pcm_1.bin in Resources */ = {isa = PBXBuildFile; fileRef = 836E669C2D855AAF00B62C38 /* r15209359_pcm_1.bin */; }; + 836E66A12D855ACC00B62C38 /* r15279813_pcm_2.bin in Resources */ = {isa = PBXBuildFile; fileRef = 836E669D2D855AAF00B62C38 /* r15279813_pcm_2.bin */; }; + 836E66A22D855B2700B62C38 /* libemusc.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 836E66982D855A7500B62C38 /* libemusc.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 8398F2E01C438C7D00EB9639 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8398F2DF1C438C7D00EB9639 /* AudioUnit.framework */; }; 839CA224180D902100553DBA /* midi_processing.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83B066E0180D56BA008E3612 /* midi_processing.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83A09F621CFA83F2001E7D2D /* i_oplmusic.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83A09F561CFA83F2001E7D2D /* i_oplmusic.cpp */; }; @@ -66,6 +73,7 @@ dstSubfolderSpec = 10; files = ( 839CA224180D902100553DBA /* midi_processing.framework in CopyFiles */, + 836E66A22D855B2700B62C38 /* libemusc.0.dylib in CopyFiles */, 83EA54232A6A6CF400CD0580 /* libbass_mpc.dylib in CopyFiles */, 8356BCD127B353F60074E50C /* libbass.dylib in CopyFiles */, 8356BCD227B353F90074E50C /* libbassmidi.dylib in CopyFiles */, @@ -115,6 +123,13 @@ 83686AAD1C5C6A2700671C7A /* AUPlayerView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AUPlayerView.h; sourceTree = ""; }; 83686AAE1C5C780500671C7A /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 83686AB01C5C783000671C7A /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; }; + 836E66942D854D5600B62C38 /* SCPlayer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SCPlayer.h; sourceTree = ""; }; + 836E66952D854D5600B62C38 /* SCPlayer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SCPlayer.mm; sourceTree = ""; }; + 836E66982D855A7500B62C38 /* libemusc.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libemusc.0.dylib; path = /Users/chris/Source/Repos/cog/ThirdParty/libemusc/lib/libemusc.0.dylib; sourceTree = ""; }; + 836E669A2D855AAF00B62C38 /* r00233567_control.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = r00233567_control.bin; sourceTree = ""; }; + 836E669B2D855AAF00B62C38 /* r15199858_main_mcu.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = r15199858_main_mcu.bin; sourceTree = ""; }; + 836E669C2D855AAF00B62C38 /* r15209359_pcm_1.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = r15209359_pcm_1.bin; sourceTree = ""; }; + 836E669D2D855AAF00B62C38 /* r15279813_pcm_2.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = r15279813_pcm_2.bin; sourceTree = ""; }; 83747C222862DB7F0021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = ""; }; 838EE8CA29A8600C00CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; 8398F2DF1C438C7D00EB9639 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; @@ -171,6 +186,7 @@ 8398F2E01C438C7D00EB9639 /* AudioUnit.framework in Frameworks */, 83686AB11C5C783000671C7A /* CoreAudioKit.framework in Frameworks */, 83B06701180D5747008E3612 /* midi_processing.framework in Frameworks */, + 836E66992D855A7500B62C38 /* libemusc.0.dylib in Frameworks */, 8356BCCA27B353CB0074E50C /* libbassmidi.dylib in Frameworks */, 8356BCC927B353CB0074E50C /* libbass.dylib in Frameworks */, 83B0668B180D5668008E3612 /* Cocoa.framework in Frameworks */, @@ -206,6 +222,17 @@ path = ../../../ThirdParty/json; sourceTree = ""; }; + 836E66972D855A2600B62C38 /* Resources */ = { + isa = PBXGroup; + children = ( + 836E669A2D855AAF00B62C38 /* r00233567_control.bin */, + 836E669B2D855AAF00B62C38 /* r15199858_main_mcu.bin */, + 836E669C2D855AAF00B62C38 /* r15209359_pcm_1.bin */, + 836E669D2D855AAF00B62C38 /* r15279813_pcm_2.bin */, + ); + path = Resources; + sourceTree = ""; + }; 83747C212862DB7F0021245F /* Xcode-config */ = { isa = PBXGroup; children = ( @@ -263,6 +290,7 @@ 83B0667E180D5668008E3612 = { isa = PBXGroup; children = ( + 836E66972D855A2600B62C38 /* Resources */, 83747C212862DB7F0021245F /* Xcode-config */, 83B06690180D5668008E3612 /* MIDI */, 83B06689180D5668008E3612 /* Frameworks */, @@ -295,6 +323,7 @@ 83B0668C180D5668008E3612 /* Other Frameworks */ = { isa = PBXGroup; children = ( + 836E66982D855A7500B62C38 /* libemusc.0.dylib */, 83EA54212A6A6CE200CD0580 /* libbass_mpc.dylib */, 8356BCC727B353CB0074E50C /* libbass.dylib */, 8356BCC827B353CB0074E50C /* libbassmidi.dylib */, @@ -311,6 +340,8 @@ 83B06690180D5668008E3612 /* MIDI */ = { isa = PBXGroup; children = ( + 836E66942D854D5600B62C38 /* SCPlayer.h */, + 836E66952D854D5600B62C38 /* SCPlayer.mm */, 834A42BC287AFC7F00EB9D9B /* AudioChunk.h */, 8307D31E28607377000FF8EB /* SandboxBroker.h */, 831E2A9127B4B2FA006F1C86 /* json */, @@ -445,6 +476,10 @@ buildActionMask = 2147483647; files = ( 83B06695180D5668008E3612 /* InfoPlist.strings in Resources */, + 836E669E2D855AC600B62C38 /* r00233567_control.bin in Resources */, + 836E669F2D855AC800B62C38 /* r15199858_main_mcu.bin in Resources */, + 836E66A02D855ACA00B62C38 /* r15209359_pcm_1.bin in Resources */, + 836E66A12D855ACC00B62C38 /* r15279813_pcm_2.bin in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -471,6 +506,7 @@ 83A09F651CFA83F2001E7D2D /* opl3class.cpp in Sources */, 83C35705180EDD1C007E9DF0 /* MIDIMetadataReader.mm in Sources */, 834BE91B1DE407CB00A07DCD /* resampler.c in Sources */, + 836E66962D854D5700B62C38 /* SCPlayer.mm in Sources */, 83A09F641CFA83F2001E7D2D /* opl3.cpp in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -623,6 +659,7 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../../ThirdParty/BASS", "$(SRCROOT)/../../ThirdParty/json", + "$(SRCROOT)/../../ThirdParty/libemusc/include", ); INFOPLIST_FILE = "MIDI/MIDI-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; @@ -630,6 +667,8 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/../../ThirdParty/BASS", + "$(PROJECT_DIR)/../../ThirdParty/libemusc/lib", + "$(PROJECT_DIR)", ); PRODUCT_BUNDLE_IDENTIFIER = net.kode54.midi; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -649,6 +688,7 @@ HEADER_SEARCH_PATHS = ( "$(SRCROOT)/../../ThirdParty/BASS", "$(SRCROOT)/../../ThirdParty/json", + "$(SRCROOT)/../../ThirdParty/libemusc/include", ); INFOPLIST_FILE = "MIDI/MIDI-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Bundles"; @@ -656,6 +696,8 @@ LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/../../ThirdParty/BASS", + "$(PROJECT_DIR)/../../ThirdParty/libemusc/lib", + "$(PROJECT_DIR)", ); PRODUCT_BUNDLE_IDENTIFIER = net.kode54.midi; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/Plugins/MIDI/MIDI/MIDIDecoder.mm b/Plugins/MIDI/MIDI/MIDIDecoder.mm index 25476fe99..8d1d8e6ff 100644 --- a/Plugins/MIDI/MIDI/MIDIDecoder.mm +++ b/Plugins/MIDI/MIDI/MIDIDecoder.mm @@ -11,6 +11,7 @@ #import "AUPlayer.h" #import "BMPlayer.h" #import "MSPlayer.h" +#import "SCPlayer.h" #import "Logging.h" @@ -244,14 +245,19 @@ static OSType getOSType(const char *in_) { } else if([[plugin substringToIndex:5] isEqualToString:@"OPL3W"]) { MSPlayer *msplayer = new MSPlayer; player = msplayer; - + msplayer->set_synth(1); - + msplayer->set_bank([[plugin substringFromIndex:5] intValue]); - + msplayer->set_extp(1); - + msplayer->setSampleRate(sampleRate); + } else if([plugin isEqualToString:@"emusc"]) { + SCPlayer *scplayer = new SCPlayer; + player = scplayer; + + scplayer->setSampleRate(sampleRate); } else { const char *cplugin = [plugin UTF8String]; OSType componentSubType; diff --git a/Plugins/MIDI/MIDI/SCPlayer.h b/Plugins/MIDI/MIDI/SCPlayer.h index e594bdd99..240ae9f7c 100644 --- a/Plugins/MIDI/MIDI/SCPlayer.h +++ b/Plugins/MIDI/MIDI/SCPlayer.h @@ -5,6 +5,8 @@ #import +#include "synth.h" + class SCPlayer : public MIDIPlayer { public: // zero variables @@ -22,9 +24,6 @@ class SCPlayer : public MIDIPlayer { virtual void shutdown(); virtual bool startup(); - virtual void send_event_time(uint32_t b, unsigned int time); - virtual void send_sysex_time(const uint8_t* event, size_t size, size_t port, unsigned int time); - private: bool LoadCore(); @@ -45,10 +44,9 @@ class SCPlayer : public MIDIPlayer { void process_write_code(uint32_t port, uint32_t code); void process_write_bytes(uint32_t port, const void* buffer, uint32_t size); - bool bTerminating[3]; - NSTask* hProcess[3]; - NSPipe* hChildStd_IN[3]; - NSPipe* hChildStd_OUT[3]; + EmuSC::ControlRom *_controlROM; + EmuSC::PcmRom *_pcmROM; + EmuSC::Synth *_synth[3]; }; #endif diff --git a/Plugins/MIDI/MIDI/SCPlayer.mm b/Plugins/MIDI/MIDI/SCPlayer.mm index 19add2b8f..bc14da4c9 100644 --- a/Plugins/MIDI/MIDI/SCPlayer.mm +++ b/Plugins/MIDI/MIDI/SCPlayer.mm @@ -2,134 +2,13 @@ #include -static uint16_t getwordle(uint8_t *pData) { - return (uint16_t)(pData[0] | (((uint16_t)pData[1]) << 8)); -} - -static uint32_t getdwordle(uint8_t *pData) { - return pData[0] | (((uint32_t)pData[1]) << 8) | (((uint32_t)pData[2]) << 16) | (((uint32_t)pData[3]) << 24); -} - -bool SCPlayer::LoadCore() { - bool rval = process_create(0); - if(rval) rval = process_create(1); - if(rval) rval = process_create(2); - - return rval; -} - -bool SCPlayer::process_create(uint32_t port) { - bTerminating[port] = false; - - hChildStd_IN[port] = [[NSPipe alloc] init]; - hChildStd_OUT[port] = [[NSPipe alloc] init]; - - NSURL *launcherUrl = [[NSBundle mainBundle] URLForResource:@"scpipe" withExtension:@""]; - NSURL *coreUrl = [[NSBundle mainBundle] URLForResource:@"IIAM" withExtension:@"bin"]; - - hProcess[port] = [[NSTask alloc] init]; - [hProcess[port] setExecutableURL:launcherUrl]; - [hProcess[port] setArguments:@[[coreUrl path]]]; - [hProcess[port] setStandardInput:hChildStd_IN[port]]; - [hProcess[port] setStandardOutput:hChildStd_OUT[port]]; - - NSError *error = nil; - if(![hProcess[port] launchAndReturnError:&error] || error != nil) { - process_terminate(port); - return false; - } - - uint32_t code = process_read_code(port); - - if(code != 0) { - process_terminate(port); - return false; - } - - return true; -} - -void SCPlayer::process_terminate(uint32_t port) { - if(bTerminating[port]) return; - - bTerminating[port] = true; - - if(hProcess[port]) { - process_write_code(port, 0); - [hProcess[port] interrupt]; - [hProcess[port] waitUntilExit]; - [hProcess[port] terminate]; - hProcess[port] = nil; - } - if(hChildStd_IN[port]) { - hChildStd_IN[port] = nil; - } - if(hChildStd_OUT[port]) { - hChildStd_OUT[port] = nil; - } - - bTerminating[port] = false; -} - -bool SCPlayer::process_running(uint32_t port) { - if(hProcess[port] && [hProcess[port] isRunning]) return true; - return false; -} - -uint32_t SCPlayer::process_read_bytes_pass(uint32_t port, void *out, uint32_t size) { - NSError *error = nil; - NSData *data = [[hChildStd_OUT[port] fileHandleForReading] readDataUpToLength:size error:&error]; - if(!data || error) return 0; - NSUInteger bytesDone = [data length]; - memcpy(out, [data bytes], bytesDone); - return bytesDone; -} - -void SCPlayer::process_read_bytes(uint32_t port, void *out, uint32_t size) { - if(process_running(port) && size) { - uint8_t *ptr = (uint8_t *)out; - uint32_t done = 0; - while(done < size) { - uint32_t delta = process_read_bytes_pass(port, ptr + done, size - done); - if(delta == 0) { - memset(out, 0xFF, size); - break; - } - done += delta; - } - } else - memset(out, 0xFF, size); -} - -uint32_t SCPlayer::process_read_code(uint32_t port) { - uint32_t code; - process_read_bytes(port, &code, sizeof(code)); - return code; -} - -void SCPlayer::process_write_bytes(uint32_t port, const void *in, uint32_t size) { - if(process_running(port)) { - if(size == 0) return; - NSError *error = nil; - NSData *data = [NSData dataWithBytes:in length:size]; - if(![[hChildStd_IN[port] fileHandleForWriting] writeData:data error:&error] || error) { - process_terminate(port); - } - } -} - -void SCPlayer::process_write_code(uint32_t port, uint32_t code) { - process_write_bytes(port, &code, sizeof(code)); -} - SCPlayer::SCPlayer() : MIDIPlayer() { initialized = false; + _controlROM = NULL; + _pcmROM = NULL; for(unsigned int i = 0; i < 3; ++i) { - bTerminating[i] = false; - hProcess[i] = nil; - hChildStd_IN[i] = nil; - hChildStd_OUT[i] = nil; + _synth[i] = NULL; } } @@ -140,95 +19,57 @@ SCPlayer::~SCPlayer() { void SCPlayer::send_event(uint32_t b) { uint32_t port = (b >> 24) & 0xFF; if(port > 2) port = 0; - process_write_code(port, 2); - process_write_code(port, b & 0xFFFFFF); - if(process_read_code(port) != 0) - process_terminate(port); + _synth[port]->midi_input(b & 0xFF, (b >> 8) & 0xFF, (b >> 16) & 0xFF); } void SCPlayer::send_sysex(const uint8_t *event, size_t size, size_t port) { - process_write_code(port, 3); - process_write_code(port, (uint32_t)size); - process_write_bytes(port, event, size); - if(process_read_code(port) != 0) - process_terminate(port); - if(port == 0) { - send_sysex(event, size, 1); - send_sysex(event, size, 2); - } -} - -void SCPlayer::send_event_time(uint32_t b, unsigned int time) { - uint32_t port = (b >> 24) & 0xFF; - if(port > 2) port = 0; - process_write_code(port, 6); - process_write_code(port, b & 0xFFFFFF); - process_write_code(port, time); - if(process_read_code(port) != 0) - process_terminate(port); -} - -void SCPlayer::send_sysex_time(const uint8_t *event, size_t size, size_t port, unsigned int time) { - process_write_code(port, 7); - process_write_code(port, size); - process_write_code(port, time); - process_write_bytes(port, event, size); - if(process_read_code(port) != 0) - process_terminate(port); - if(port == 0) { - send_sysex_time(event, size, 1, time); - send_sysex_time(event, size, 2, time); - } -} - -void SCPlayer::render_port(uint32_t port, float *out, uint32_t count) { - process_write_code(port, 4); - process_write_code(port, count); - if(process_read_code(port) != 0) { - process_terminate(port); - memset(out, 0, count * sizeof(float) * 2); - return; - } - process_read_bytes(port, out, count * sizeof(float) * 2); + _synth[0]->midi_input_sysex(event, size); + _synth[1]->midi_input_sysex(event, size); + _synth[2]->midi_input_sysex(event, size); } void SCPlayer::render(float *out, unsigned long count) { - memset(out, 0, count * sizeof(float) * 2); - while(count) { - unsigned long todo = count > 4096 ? 4096 : count; - float buffer[4096 * 2]; - for(unsigned long i = 0; i < 3; ++i) { - render_port(i, buffer, todo); - - for(unsigned long j = 0; j < todo; ++j) { - out[j * 2 + 0] += buffer[j * 2 + 0]; - out[j * 2 + 1] += buffer[j * 2 + 1]; - } - } - out += todo * 2; - count -= todo; + for(unsigned long i = 0; i < count; ++i) { + float sampleOut[6]; + _synth[0]->get_next_sample(sampleOut + 0); + _synth[1]->get_next_sample(sampleOut + 2); + _synth[2]->get_next_sample(sampleOut + 4); + out[0] = sampleOut[0] + sampleOut[2] + sampleOut[4]; + out[1] = sampleOut[1] + sampleOut[3] + sampleOut[5]; + out += 2; } } void SCPlayer::shutdown() { for(int i = 0; i < 3; i++) { - process_terminate(i); + delete _synth[i]; + _synth[i] = NULL; } + delete _pcmROM; + _pcmROM = NULL; + delete _controlROM; + _controlROM = NULL; initialized = false; } bool SCPlayer::startup() { if(initialized) return true; - if(!LoadCore()) - return false; + NSBundle *bundle = [NSBundle bundleWithIdentifier:@"net.kode54.midi"]; + NSString *controlROMPath = [bundle pathForResource:@"r00233567_control" ofType:@"bin"]; + NSString *cpuROMPath = [bundle pathForResource:@"r15199858_main_mcu" ofType:@"bin"]; + NSString *pcmROMPath0 = [bundle pathForResource:@"r15209359_pcm_1" ofType:@"bin"]; + NSString *pcmROMPath1 = [bundle pathForResource:@"r15279813_pcm_2" ofType:@"bin"]; + + _controlROM = new EmuSC::ControlRom(std::string([controlROMPath UTF8String]), std::string([cpuROMPath UTF8String])); + _pcmROM = new EmuSC::PcmRom({ std::string([pcmROMPath0 UTF8String]), std::string([pcmROMPath1 UTF8String]) }, *_controlROM); for(int i = 0; i < 3; i++) { - process_write_code(i, 1); - process_write_code(i, sizeof(uint32_t)); - process_write_code(i, uSampleRate); - if(process_read_code(i) != 0) - return false; + _synth[i] = new EmuSC::Synth(*_controlROM, *_pcmROM); + _synth[i]->set_audio_format(uSampleRate, 2); + if(i > 0) { + _synth[i]->set_param(EmuSC::SystemParam::DeviceID, (uint8_t)(17 + i)); + } } initialized = true; @@ -239,5 +80,5 @@ bool SCPlayer::startup() { } unsigned int SCPlayer::send_event_needs_time() { - return 0; // 4096; This doesn't work for some reason + return 0; } diff --git a/Plugins/MIDI/Resources/unzip-sc55mk2.zip-here.txt b/Plugins/MIDI/Resources/unzip-sc55mk2.zip-here.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Preferences/Preferences/MIDIPluginBehaviorArrayController.m b/Preferences/Preferences/MIDIPluginBehaviorArrayController.m index cfd0e9cd0..951c16f3b 100644 --- a/Preferences/Preferences/MIDIPluginBehaviorArrayController.m +++ b/Preferences/Preferences/MIDIPluginBehaviorArrayController.m @@ -68,6 +68,8 @@ static void enumCallback(void *context, OSType uSubType, OSType uManufacturer, c [self addObject:@{@"name": @"BASSMIDI", @"preference": @"BASSMIDI"}]; + [self addObject:@{@"name": @"EmuSC", @"preference": @"emusc"}]; + [self addObject:@{@"name": @"DMX Generic", @"preference": @"DOOM0000"}]; [self addObject:@{@"name": @"DMX Doom 1", @"preference": @"DOOM0001"}]; [self addObject:@{@"name": @"DMX Doom 2", @"preference": @"DOOM0002"}]; diff --git a/ThirdParty/libemusc/include/control_rom.h b/ThirdParty/libemusc/include/control_rom.h new file mode 100644 index 000000000..80b214dbe --- /dev/null +++ b/ThirdParty/libemusc/include/control_rom.h @@ -0,0 +1,385 @@ +/* + * 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__ diff --git a/ThirdParty/libemusc/include/params.h b/ThirdParty/libemusc/include/params.h new file mode 100644 index 000000000..e722f3b4a --- /dev/null +++ b/ThirdParty/libemusc/include/params.h @@ -0,0 +1,380 @@ +/* + * This file is part of libEmuSC, a Sound Canvas emulator library + * Copyright (C) 2023-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 . + */ + + +#ifndef __PARAMS_H__ +#define __PARAMS_H__ + + +namespace EmuSC { + + +// All variables for master settings as defined by the Sound Canvas lineup +enum class SystemParam : int { + + // Part 1: Settings defined by SysEx chart + // These settings can be both requested and modified by SysEx messages + Tune = 0x0000, // 4B: [0x0014 - 0x07e8 : 0x0400]-100.0-100.0 + Tune1 = 0x0001, + Tune2 = 0x0002, + Tune3 = 0x0003, + Volume = 0x0004, // [0x00 - 0x7f : 0x7f] + KeyShift = 0x0005, // [0x28 - 0x58 : 0x40] + Pan = 0x0006, // [0x01 - 0x7f : 0x40] + + ResetGSstandardMode = 0x007f, // No data stored, but triggers a reset event + + // Part 2: Settings outside SysEx chart + RxSysEx = 0x0090, // [0 - 1 : 1] + RxGMOn = 0x0091, // [0 - 1 : 1] + RxGSReset = 0x0092, // [0 - 1 : 1] + RxInstrumentChange = 0x0093, // [0 - 1 : 1] + RxFunctionControl = 0x0094, // [0 - 1 : 1] + DeviceID = 0x0095, // [1 - 32 : 17] + +}; + + +enum class PatchParam : int { + + // Part 1: Settings defined by SysEx chart + // ======================================= + // These settings can be both requested and modified by SysEx messages + + PatchName = 0x0100, + PartialReserve = 0x0110, + + ReverbMacro = 0x0130, // [0x00 - 0x07 : 0x04] + ReverbCharacter = 0x0131, // [0x00 - 0x07 : 0x04] + ReverbPreLPF = 0x0132, // [0x00 - 0x7f : 0x00] + ReverbLevel = 0x0133, // [0x00 - 0x7f : 0x40] + ReverbTime = 0x0134, // [0x00 - 0x7f : 0x40] + ReverbDelayFeedback = 0x0135, // [0x00 - 0x7f : 0x00] + ReverbPreDelayTime = 0x0137, // SC-88 + + ChorusMacro = 0x0138, // [0x00 - 0x07 : 0x02] + ChorusPreLPF = 0x0139, // [0x00 - 0x07 : 0x00] + ChorusLevel = 0x013a, // [0x00 - 0x7f : 0x40] + ChorusFeedback = 0x013b, // [0x00 - 0x7f : 0x08] + ChorusDelay = 0x013c, // [0x00 - 0x7f : 0x50] + ChorusRate = 0x013d, // [0x00 - 0x7f : 0x03] + ChorusDepth = 0x013e, // [0x00 - 0x7f : 0x13] + ChorusSendToReverb = 0x013f, // [0x00 - 0x7f : 0x00] + + // The remaining patch paramters have separate values for each part + // All addresses are either 0x1PXX or 0x2PXX, where P = part number + + ToneNumber = 0x1000, // 2B: Byte 1 = CtrlChg, Byte 2 = ProgChg + ToneNumber2 = 0x1001, // Only helper for easy access 2nd. byte + RxChannel = 0x1002, // [1 - 17] X = Part number, 17 = Off + RxPitchBend = 0x1003, // [0 - 1 : 1] 0 = Off, 1 = On + RxChPressure = 0x1004, // [0 - 1 : 1] 0 = Off, 1 = On + RxProgramChange = 0x1005, // [0 - 1 : 1] 0 = Off, 1 = On + RxControlChange = 0x1006, // [0 - 1 : 1] 0 = Off, 1 = On + RxPolyPressure = 0x1007, // [0 - 1 : 1] 0 = Off, 1 = On + RxNoteMessage = 0x1008, // [0 - 1 : 1] 0 = Off, 1 = On + RxRPN = 0x1009, // [0 - 1 : 1] 0 = Off, 1 = On + RxNRPN = 0x100a, // [0 - 1 : 1] 0 = Off, 1 = On + // Note: Set to Off if mode is set to 'GM' + RxModulation = 0x100b, // [0 - 1 : 1] 0 = Off, 1 = On + RxVolume = 0x100c, // [0 - 1 : 1] 0 = Off, 1 = On + RxPanpot = 0x100d, // [0 - 1 : 1] 0 = Off, 1 = On + RxExpression = 0x100e, // [0 - 1 : 1] 0 = Off, 1 = On + RxHold1 = 0x100f, // [0 - 1 : 1] 0 = Off, 1 = On + RxPortamento = 0x1010, // [0 - 1 : 1] 0 = Off, 1 = On + RxSostenuto = 0x1011, // [0 - 1 : 1] 0 = Off, 1 = On + RxSoft = 0x1012, // [0 - 1 : 1] 0 = Off, 1 = On + PolyMode = 0x1013, // [0 (Mono) | 1 (Poly) : 1] + AssignMode = 0x1014, // [0 - 2] 0 = Single, 1 = Limited-Multi, + // 2 = Full-Multi + UseForRhythm = 0x1015, // [0 - 3] 0 = Off, 1 = Map1, 2 = Map2 + PitchKeyShift = 0x1016, // [0x28 - 0x58 : 0x40] -24 - 24 semitone + PitchOffsetFine = 0x1017, // 2B [0x08 - 0xf8 : 0x0800] -12.0 - 12.0 Hz + PitchOffsetFine2 = 0x1018, + PartLevel = 0x1019, // [0x00 - 0x7f : 0x64] + VelocitySenseDepth = 0x101a, // [0x00 - 0x7f : 0x40] + VelocitySenseOffset = 0x101b, // [0x00 - 0x7f : 0x40] + PartPanpot = 0x101c, // [0x00 - 0x7f : 0x40] 0 = rnd, left > right + KeyRangeLow = 0x101d, // [0x00 - 0x7f : 0x00] + KeyRangeHigh = 0x101e, // [0x00 - 0x7f : 0x7f] + CC1ControllerNumber = 0x101f, // [0x00 - 0x5f : 0x40] + CC2ControllerNumber = 0x1020, // [0x00 - 0x5f : 0x40] + ChorusSendLevel = 0x1021, // [0x00 - 0x7f : 0x00] (CC# 93) + ReverbSendLevel = 0x1022, // [0x00 - 0x7f : 0x28] (CC# 91) + RxBankSelect = 0x1023, // [0 - 1 : 1] Note: Missing from SC-55 OM, + // but present on SC-55mkII and onwards + RxBankSelectLSB = 0x1024, // (SC88+) + PitchFineTune = 0x102a, // [SC88+][0x0000 - 0x7f7f : 0x400] (14 bit) + PitchFineTune2 = 0x102b, // Also RPN #1 so used for that on SC-55 + DelaySendLevel = 0x102c, // (SC88+) + + // Tone modify + // Note: SC-55 has [0x0e - 0x72 : 0x40] for all tone modify parameters + // SC-88 has [0x00 - 0x7f : 0x40] for all tone modify parameters + VibratoRate = 0x1030, // NRPN# 8 + VibratoDepth = 0x1031, // NRPN# 9 + TVFCutoffFreq = 0x1032, // NRPN# 32 + TVFResonance = 0x1033, // NRPN# 33 + TVFAEnvAttack = 0x1034, // NRPN# 99 + TVFAEnvDecay = 0x1035, // NRPN# 100 + TVFAEnvRelease = 0x1036, // NRPN# 102 + VibratoDelay = 0x1037, // NRPN# 10 + + // Scale tuning + ScaleTuningC = 0x1040, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningC_ = 0x1041, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningD = 0x1042, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningD_ = 0x1043, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningE = 0x1044, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningF = 0x1045, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningF_ = 0x1046, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningG = 0x1047, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningG_ = 0x1048, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningA = 0x1049, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningA_ = 0x104a, // [0x00 - 0x7f : 0x40], -63 - 63 cent + ScaleTuningB = 0x104b, // [0x00 - 0x7f : 0x40], -63 - 63 cent + + // Controller setttings (0x41 0x2P 0xXX) + // These settings contains the current value for each controller parameter. + + // Modulator + MOD_PitchControl = 0x2000, // [0x20 - 0x58 : 0x40], -24 - +24 semitones + MOD_TVFCutoffControl= 0x2001, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + MOD_AmplitudeControl= 0x2002, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + MOD_LFO1RateControl = 0x2003, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + MOD_LFO1PitchDepth = 0x2004, // [0x00 - 0x7f : 0x0a], 0 - 600 cent + MOD_LFO1TVFDepth = 0x2005, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + MOD_LFO1TVADepth = 0x2006, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + MOD_LFO2RateControl = 0x2007, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + MOD_LFO2PitchDepth = 0x2008, // [0x00 - 0x7f : 0x00], 0 - 600 cent + MOD_LFO2TVFDepth = 0x2009, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + MOD_LFO2TVADepth = 0x200a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // Pitch Bend + PB_PitchControl = 0x2010, // [0x40 - 0x58 : 0x40], 0 - +24 semitones + PB_TVFCutoffControl = 0x2011, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + PB_AmplitudeControl = 0x2012, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + PB_LFO1RateControl = 0x2013, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + PB_LFO1PitchDepth = 0x2014, // [0x00 - 0x7f : 0x00], 0 - 600 cent + PB_LFO1TVFDepth = 0x2015, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + PB_LFO1TVADepth = 0x2016, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + PB_LFO2RateControl = 0x2017, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + PB_LFO2PitchDepth = 0x2018, // [0x00 - 0x7f : 0x00], 0 - 600 cent + PB_LFO2TVFDepth = 0x2019, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + PB_LFO2TVADepth = 0x201a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // Channel Aftertouch + CAf_PitchControl = 0x2020, // [0x20 - 0x58 : 0x40], -24 - +24 semitones + CAf_TVFCutoffControl= 0x2021, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + CAf_AmplitudeControl= 0x2022, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + CAf_LFO1RateControl = 0x2023, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CAf_LFO1PitchDepth = 0x2024, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CAf_LFO1TVFDepth = 0x2025, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CAf_LFO1TVADepth = 0x2026, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + CAf_LFO2RateControl = 0x2027, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CAf_LFO2PitchDepth = 0x2028, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CAf_LFO2TVFDepth = 0x2029, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CAf_LFO2TVADepth = 0x202a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // Poly Aftertouch + PAf_PitchControl = 0x2030, // [0x20 - 0x58 : 0x40], -24 - +24 semitones + PAf_TVFCutoffControl= 0x2031, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + PAf_AmplitudeControl= 0x2032, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + PAf_LFO1RateControl = 0x2033, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + PAf_LFO1PitchDepth = 0x2034, // [0x00 - 0x7f : 0x00], 0 - 600 cent + PAf_LFO1TVFDepth = 0x2035, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + PAf_LFO1TVADepth = 0x2036, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + PAf_LFO2RateControl = 0x2037, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + PAf_LFO2PitchDepth = 0x2038, // [0x00 - 0x7f : 0x00], 0 - 600 cent + PAf_LFO2TVFDepth = 0x2039, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + PAf_LFO2TVADepth = 0x203a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // CC#1 + CC1_PitchControl = 0x2040, // [0x20 - 0x58 : 0x40], -24 - +24 semitones + CC1_TVFCutoffControl= 0x2041, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + CC1_AmplitudeControl= 0x2042, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + CC1_LFO1RateControl = 0x2043, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CC1_LFO1PitchDepth = 0x2044, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CC1_LFO1TVFDepth = 0x2045, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CC1_LFO1TVADepth = 0x2046, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + CC1_LFO2RateControl = 0x2047, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CC1_LFO2PitchDepth = 0x2048, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CC1_LFO2TVFDepth = 0x2049, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CC1_LFO2TVADepth = 0x204a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // CC#2 + CC2_PitchControl = 0x2050, // [0x20 - 0x58 : 0x40], -24 - 24 semitones + CC2_TVFCutoffControl= 0x2051, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + CC2_AmplitudeControl= 0x2052, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + CC2_LFO1RateControl = 0x2053, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CC2_LFO1PitchDepth = 0x2054, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CC2_LFO1TVFDepth = 0x2055, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CC2_LFO1TVADepth = 0x2056, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + CC2_LFO2RateControl = 0x2057, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CC2_LFO2PitchDepth = 0x2058, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CC2_LFO2TVFDepth = 0x2059, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CC2_LFO2TVADepth = 0x205a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // SC-88 only additions TODO: FIX +// ToneMapNumber = 0x4000, // [SC-88Pro] +// ToneMap0Number = 0x4001, // [SC-88Pro] +// EqOnOff = 0x4020, // [SC-88] +// OutputAssign = 0x4021, // [SC-88Pro] +// PartEfxAssign = 0x4022, // [SC-88Pro] + + + // Part 2: Settings outside SysEx chart + // ==================================== + // The following settings contains the latest controller inputs. These + // values are controller parameter states multiplied with controller value. + // The values of these controller inputs are updated whenever a controller + // changes value. + + // Modulator + CtM_PitchControl = 0x2060, // [0x20 - 0x58 : 0x40], -24 - +24 semitones + CtM_TVFCutoffControl= 0x2061, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + CtM_AmplitudeControl= 0x2062, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + CtM_LFO1RateControl = 0x2063, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CtM_LFO1PitchDepth = 0x2064, // [0x00 - 0x7f : 0x0a], 0 - 600 cent + CtM_LFO1TVFDepth = 0x2065, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CtM_LFO1TVADepth = 0x2066, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + CtM_LFO2RateControl = 0x2067, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CtM_LFO2PitchDepth = 0x2068, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CtM_LFO2TVFDepth = 0x2069, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CtM_LFO2TVADepth = 0x206a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // Pitch Bend + CtB_PitchControl = 0x2070, // [0x40 - 0x58 : 0x40], 0 - +24 semitones 2B + CtB_TVFCutoffControl= 0x2071, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + CtB_AmplitudeControl= 0x2072, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + CtB_LFO1RateControl = 0x2073, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CtB_LFO1PitchDepth = 0x2074, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CtB_LFO1TVFDepth = 0x2075, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CtB_LFO1TVADepth = 0x2076, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + CtB_LFO2RateControl = 0x2077, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CtB_LFO2PitchDepth = 0x2078, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CtB_LFO2TVFDepth = 0x2079, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CtB_LFO2TVADepth = 0x207a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // Channel Aftertouch + CtC_PitchControl = 0x2080, // [0x20 - 0x58 : 0x40], -24 - +24 semitones + CtC_TVFCutoffControl= 0x2081, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + CtC_AmplitudeControl= 0x2082, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + CtC_LFO1RateControl = 0x2083, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CtC_LFO1PitchDepth = 0x2084, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CtC_LFO1TVFDepth = 0x2085, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CtC_LFO1TVADepth = 0x2086, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + CtC_LFO2RateControl = 0x2087, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CtC_LFO2PitchDepth = 0x2088, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CtC_LFO2TVFDepth = 0x2089, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CtC_LFO2TVADepth = 0x208a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // Poly Aftertouch + CtP_PitchControl = 0x2090, // [0x20 - 0x58 : 0x40], -24 - +24 semitones + CtP_TVFCutoffControl= 0x2091, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + CtP_AmplitudeControl= 0x2092, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + CtP_LFO1RateControl = 0x2093, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CtP_LFO1PitchDepth = 0x2094, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CtP_LFO1TVFDepth = 0x2095, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CtP_LFO1TVADepth = 0x2096, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + CtP_LFO2RateControl = 0x2097, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + CtP_LFO2PitchDepth = 0x2098, // [0x00 - 0x7f : 0x00], 0 - 600 cent + CtP_LFO2TVFDepth = 0x2099, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + CtP_LFO2TVADepth = 0x209a, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // CC#1 + Ct1_PitchControl = 0x20a0, // [0x20 - 0x58 : 0x40], -24 - +24 semitones + Ct1_TVFCutoffControl= 0x20a1, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + Ct1_AmplitudeControl= 0x20a2, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + Ct1_LFO1RateControl = 0x20a3, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + Ct1_LFO1PitchDepth = 0x20a4, // [0x00 - 0x7f : 0x00], 0 - 600 cent + Ct1_LFO1TVFDepth = 0x20a5, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + Ct1_LFO1TVADepth = 0x20a6, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + Ct1_LFO2RateControl = 0x20a7, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + Ct1_LFO2PitchDepth = 0x20a8, // [0x00 - 0x7f : 0x00], 0 - 600 cent + Ct1_LFO2TVFDepth = 0x20a9, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + Ct1_LFO2TVADepth = 0x20aa, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // CC#2 + Ct2_PitchControl = 0x20b0, // [0x20 - 0x58 : 0x40], -24 - 24 semitones + Ct2_TVFCutoffControl= 0x20b1, // [0x00 - 0x7f : 0x40], -9600 - 9600 cent + Ct2_AmplitudeControl= 0x20b2, // [0x00 - 0x7f : 0x40], -100.0 - 100.0 % + Ct2_LFO1RateControl = 0x20b3, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + Ct2_LFO1PitchDepth = 0x20b4, // [0x00 - 0x7f : 0x00], 0 - 600 cent + Ct2_LFO1TVFDepth = 0x20b5, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + Ct2_LFO1TVADepth = 0x20b6, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + Ct2_LFO2RateControl = 0x20b7, // [0x00 - 0x7f : 0x40], -10.0 - 10.0 Hz + Ct2_LFO2PitchDepth = 0x20b8, // [0x00 - 0x7f : 0x00], 0 - 600 cent + Ct2_LFO2TVFDepth = 0x20b9, // [0x00 - 0x7f : 0x00], 0 - 2400 cent + Ct2_LFO2TVADepth = 0x20ba, // [0x00 - 0x7f : 0x00], 0 - 100.0 % + + // And finally a list of controller parameters that contains accumulated + // values from all 6 controllers. Updated at the same time as the controller + // inputs above, i.e. whenever a controller changes value. + Acc_PitchControl = 0x20f0, // [0x0000 - 0x7f7f : 0x4000], Note: 2 Bytes + Acc_TVFCutoffControl= 0x20f2, // [0x00 - 0x7f : 0x40] + Acc_AmplitudeControl= 0x20f3, // [0x00 - 0x7f : 0x40] + Acc_LFO1RateControl = 0x20f4, // [0x00 - 0x7f : 0x40] + Acc_LFO1PitchDepth = 0x20f5, // [0x00 - 0x7f : 0x00] + Acc_LFO1TVFDepth = 0x20f6, // [0x00 - 0x7f : 0x00] + Acc_LFO1TVADepth = 0x20f7, // [0x00 - 0x7f : 0x00] + Acc_LFO2RateControl = 0x20f8, // [0x00 - 0x7f : 0x40] + Acc_LFO2PitchDepth = 0x20f9, // [0x00 - 0x7f : 0x00] + Acc_LFO2TVFDepth = 0x20fa, // [0x00 - 0x7f : 0x00] + Acc_LFO2TVADepth = 0x20fb, // [0x00 - 0x7f : 0x00] + + // Controller state values + Modulation = 0x1080, // [0x00 - 0x7f] + PitchBend = 0x1081, // [0x00 - 0x4000 : 0x2000] Note: 2 Bytes! + ChannelPressure = 0x1083, // [0x00 - 0x7f : 0] + PolyKeyPressure = 0x1084, // [0x00 - 0x7f : 0] pr. key. Need an array? + CC1Controller = 0x1085, // [0x00 - 0x7f : 0] + CC2Controller = 0x1086, // [0x00 - 0x7f : 0] + Hold1 = 0x1087, // [0x00 - 0x7f : 0] 0-63: OFF, 64-127: ON + Sostenuto = 0x1088, // [0x00 - 0x7f : 0] 0-63: OFF, 64-127: ON + Soft = 0x1089, // [0x00 - 0x7f : 0] 0-63: OFF, 64-127: ON + Expression = 0x108a, // [0x00 - 0x7f : 0xff] + Portamento = 0x108b, // [0 - 1 : 0] + PortamentoTime = 0x108c, // [0x00 - 0x7f : 0] + + // Current RPN and NRPN + RPN_LSB = 0x1090, // [0x00 - 0x7f : 0] + RPN_MSB = 0x1091, // [0x00 - 0x7f : 0] + NRPN_LSB = 0x1092, // [0x00 - 0x7f : 0] + NRPN_MSB = 0x1093, // [0x00 - 0x7f : 0] + + // Other patch parameters from CC / RPN / NRPN / menu + PitchCoarseTune = 0x1094 // [0x28 - 0x58 : 0x40] -24 - 24 semit. RPN#2 +}; + +// All variables for individual parts as defined by the Sound Canvas lineup +enum class DrumParam : int { + DrumsMapName = 0x0000, // 12B ASCII + PlayKeyNumber = 0x0100, // [0x00 - 0x7f] semitones + Level = 0x0200, // [0x00 - 0x7f] + AssignGroupNumber = 0x0300, // [0x00 - 0x7f] + Panpot = 0x0400, // [0x00 - 0x7f] + ReverbDepth = 0x0500, // [0x00 - 0x7f] + ChorusDepth = 0x0600, // [0x00 - 0x7f] + RxNoteOff = 0x0700, // [0 - 1] + RxNoteOn = 0x0800 // [0 - 1] +}; + +} + +#endif // __PARAMS_H__ diff --git a/ThirdParty/libemusc/include/pcm_rom.h b/ThirdParty/libemusc/include/pcm_rom.h new file mode 100644 index 000000000..95cbc30f0 --- /dev/null +++ b/ThirdParty/libemusc/include/pcm_rom.h @@ -0,0 +1,72 @@ +/* + * 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 . + */ + +// PCM ROM decoding is based on the SC55_Soundfont generator written by +// Kitrinx and NewRisingSun [ https://github.com/Kitrinx/SC55_Soundfont ] + + +#ifndef __PCM_ROM_H__ +#define __PCM_ROM_H__ + + +#include "control_rom.h" + +#include + +#include +#include + + +namespace EmuSC { + +class PcmRom +{ +private: + std::string _version; + std::string _date; + + struct Samples { +// std::vector samplesI; // All samples stored in 24 bit 32kHz mono + std::vector samplesF; // 32 bit float, 32kHz, mono + }; + std::vector _sampleSets; + + uint32_t _unscramble_address(uint32_t address); + int8_t _unscramble_data(int8_t byte); + + uint32_t _find_samples_rom_address(uint32_t address, + enum ControlRom::SynthGen synthGen); + int _read_samples(std::vector &rom, + struct ControlRom::Sample &ctrlSample, + enum ControlRom::SynthGen synthGen); + + PcmRom(); + +public: + PcmRom(std::vector romPath, ControlRom &ctrlRom); + ~PcmRom(); + + inline struct Samples& samples(uint16_t ss) { return _sampleSets[ss]; } + + std::string version(void) { return _version; } + std::string date(void) { return _date; } +}; + +} + +#endif // __PCM_ROM_H__ diff --git a/ThirdParty/libemusc/include/settings.h b/ThirdParty/libemusc/include/settings.h new file mode 100644 index 000000000..5dc440a3d --- /dev/null +++ b/ThirdParty/libemusc/include/settings.h @@ -0,0 +1,161 @@ +/* + * This file is part of libEmuSC, a Sound Canvas emulator library + * Copyright (C) 2023-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 . + */ + + +#ifndef __SETTINGS_H__ +#define __SETTINGS_H__ + + +#include "control_rom.h" +#include "params.h" + +#include + +#include +#include + + +namespace EmuSC { + +class Settings +{ +public: + Settings(ControlRom & ctrlRom); + ~Settings(); + + // Sound Canvas modes + enum class Mode { + GS, // Standard GS mode. Default. + MT32, // MT32 mode + SC55 // SC-55 mode for SC-88 TODO + + // TODO: What about GM mode? + }; + + // Interpolation modes + enum class InterpMode { + Nearest = 0, + Linear = 1, + Cubic = 2 + }; + + // Retrieve settings from Config paramters + uint8_t get_param(enum SystemParam sp); + uint8_t* get_param_ptr(enum SystemParam sp); + uint32_t get_param_uint32(enum SystemParam sp); + uint16_t get_param_32nib(enum SystemParam sp); + uint8_t get_param(enum PatchParam pp, int8_t part = -1); + uint8_t* get_param_ptr(enum PatchParam pp, int8_t part = -1); + uint16_t get_param_uint14(enum PatchParam pp, int8_t part = -1); + uint16_t get_param_uint16(enum PatchParam pp, int8_t part = -1); + uint8_t get_param_nib16(enum PatchParam pp, int8_t part = -1); + uint8_t get_patch_param(uint16_t address, int8_t part = -1); + uint8_t get_param(enum DrumParam, uint8_t map, uint8_t key); + int8_t* get_param_ptr(enum DrumParam, uint8_t map); + + // Set settings from Config paramters + void set_param(enum SystemParam sp, uint8_t value); + void set_param(enum SystemParam sp, const uint8_t *data, uint8_t size); + void set_param_uint32(enum SystemParam sp, uint32_t value); + void set_param_32nib(enum SystemParam sp, uint16_t value); + void set_system_param(uint16_t address, const uint8_t *data, uint8_t size = 1); + + void set_param(enum PatchParam pp, uint8_t value, int8_t part = -1); + void set_param(enum PatchParam pp, const uint8_t *data, uint8_t size = 1, + int8_t part = -1); + void set_param_uint14(enum PatchParam pp, uint16_t value, int8_t part = -1); + void set_param_nib16(enum PatchParam pp, uint8_t value, int8_t part = -1); + void set_patch_param(uint16_t address, const uint8_t *data, uint8_t size = 1); + void set_patch_param(uint16_t address, uint8_t value, int8_t part = -1); + void set_param(enum DrumParam dp, uint8_t map, uint8_t key, uint8_t value); + void set_param(enum DrumParam dp, uint8_t map, const uint8_t *data, uint8_t length); + void set_drum_param(uint16_t address, const uint8_t *data, uint8_t size = 1); + + int update_drum_set(uint8_t map, uint8_t bank); + + // Store settings paramters to file (aka battery backup) + bool load(std::string filePath); + bool save(std::string filePath); + + // Reset all settings to default GS mode + void reset(); + void set_gm_mode(void); + void set_map_mt32(void); + + // Temporary solution + // Figure out the need for a common way with all controllers + void update_pitchBend_factor(int8_t part); + float get_pitchBend_factor(int8_t part) { return _PBController[part]; } + + static int8_t convert_from_roland_part_id(int8_t part); + + void set_sample_rate(int sampleRate) { _sampleRate = sampleRate; } + inline int sample_rate(void) { return _sampleRate; } + + void set_channels(int channels) { _channels = channels; } + inline int channels(void) { return _channels; } + + void set_interpolation_mode(enum InterpMode im) { _interpMode = im; } + inline enum InterpMode interpolation_mode(void) { return _interpMode; } + +private: + std::array _systemParams; // Both SysEx and non-SysEx data + std::array _patchParams; + std::array _drumParams; + + ControlRom &_ctrlRom; + + // Non-native parameters + int _sampleRate; + int _channels; + InterpMode _interpMode; + + void _initialize_system_params(enum Mode = Mode::GS); + void _initialize_patch_params(enum Mode = Mode::GS); + void _initialize_drumSet_params(); + + // BE / LE conversion + inline bool _le_native(void) { uint16_t n = 1; return (*(const uint8_t *) & n); } + uint8_t _to_native_endian_nib16(const uint8_t *ptr); + uint16_t _to_native_endian_uint14(const uint8_t *ptr); + uint16_t _to_native_endian_uint16(const uint8_t *ptr); + uint32_t _to_native_endian_uint32(const uint8_t *ptr); + + // Macros for certain settings + void _run_macro_chorus(uint8_t value); + void _run_macro_reverb(uint8_t value); + + // Update accumulated controller inputs + void _update_controller_input(enum PatchParam pp, uint8_t value, int8_t part); + void _update_controller_input_acc(enum PatchParam pp, int8_t part); + void _accumulate_controller_values(enum PatchParam ctm, enum PatchParam acc, + int8_t partm, int min, int max, bool center); + + // Temporary storage for pitchbend + float _PBController[16]; + + static constexpr std::array _convert_to_roland_part_id_LUT = + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 10, 11, 12, 13, 14, 15 }; + static constexpr std::array _convert_from_roland_part_id_LUT = + { 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15 }; + +}; + +} + +#endif // __SETTINGS_H__ diff --git a/ThirdParty/libemusc/include/synth.h b/ThirdParty/libemusc/include/synth.h new file mode 100644 index 000000000..b8c2bca3e --- /dev/null +++ b/ThirdParty/libemusc/include/synth.h @@ -0,0 +1,185 @@ +/* + * 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 . + */ + + +#ifndef __SYNTH_H__ +#define __SYNTH_H__ + + +#include "control_rom.h" +#include "params.h" +#include "pcm_rom.h" + +#include +#include +#include +#include +#include + + +/* Public API for libEmuSC + * + * This Synth class is responsible for receiving MIDI events from client, + * process the event based on information in Control and PCM ROMS, and + * finally responding to audio buffer requests. + * + * Synth class constructor depends on a valid Control Rom and PCM ROM. + * + * MIDI events is sent to the emulator via the midi_input() method using the + * three bytes from raw MIDI events. + * + * Audio samples are extracted by calling the get_next_sample() method. This + * is typically done from a callback function triggered by the OS audio + * driver when the audio buffer is running low. + * + * All settings are configured through the Settings class. + */ + + +namespace EmuSC { + +class Part; +class Settings; + +class Synth +{ +public: + + enum class SoundMap { + GS, // Default GS settings + GS_GM, // GS settings in GM mode (available on SC55mk2+) + MT32 // MT32 arrangement + }; + + Synth(ControlRom &cRom, PcmRom &pRom, SoundMap map = SoundMap::GS); + ~Synth(); + + // Add start() and stop()? Won't start if sampleRate is not set? + + void midi_input(uint8_t status, uint8_t data1, uint8_t data2); + void midi_input_sysex(const uint8_t *data, uint16_t length); + + int get_next_sample(int16_t *sample); + int get_next_sample(float *sample); + std::array get_parts_last_peak_sample(void); + + // Setting audio properties (default is 44100, 2) + void set_audio_format(uint32_t sampleRate, uint8_t channels); + void set_interpolation_mode(int mode); + + void reset(SoundMap sm, bool resetParts = false); + + void panic(void); + + // Mute all parts. Similar to push MUTE-button on real hardware + void mute(void); + + // Unmute all parts. Similar to push MUTE-button on real hardware + void unmute(void); + + // Mute 1-n parts + void mute_parts(std::vector parts); + + // Unute all parts + void unmute_parts(std::vector parts); + + // Returns libEmuSC version as a string + static std::string version(void); + + // REMOVE! + bool get_part_mute(uint8_t partId); + uint8_t get_part_instrument(uint8_t partId, uint8_t &bank); + void set_part_mute(uint8_t partId, bool mute); + void set_part_instrument(uint8_t partId, uint8_t index, uint8_t bank); + + void add_part_midi_mod_callback(std::function callback); + void clear_part_midi_mod_callback(void); + + void set_part_envelope_callback(int partId, + std::function callback); + void clear_part_envelope_callback(int partId); + void set_part_lfo_callback(int partId, + std::function callback); + void clear_part_lfo_callback(int partId); + + // EmuSC clients methods for getting synth paramters + uint8_t get_param(enum SystemParam sp); + uint8_t* get_param_ptr(enum SystemParam sp); + uint16_t get_param_32nib(enum SystemParam sp); + uint8_t get_param(enum PatchParam pp, int8_t part = -1); + uint8_t* get_param_ptr(enum PatchParam pp, int8_t part = -1); + uint16_t get_param_uint14(enum PatchParam pp, int8_t part = -1); + uint8_t get_param_nib16(enum PatchParam pp, int8_t part = -1); + uint8_t get_patch_param(uint16_t address, int8_t part = -1); + uint8_t get_param(enum DrumParam, uint8_t map, uint8_t key); + int8_t* get_param_ptr(enum DrumParam, uint8_t map); + + // EmuSC clients methods for setting synth paramters + void set_param(enum SystemParam sp, uint8_t value); + void set_param(enum SystemParam sp, uint32_t value); + void set_param(enum SystemParam sp, const uint8_t *data, uint8_t size = 1); + void set_param_32nib(enum SystemParam sp, uint16_t value); + void set_param(enum PatchParam pp, uint8_t value, int8_t part = -1); + void set_param(enum PatchParam sp, const uint8_t *data, uint8_t size = 1, + int8_t part = -1); + void set_param_uint14(enum PatchParam pp, uint16_t value, int8_t part = -1); + void set_param_nib16(enum PatchParam pp, uint8_t value, int8_t part = -1); + void set_patch_param(uint16_t address, uint8_t value, int8_t part = 1); + void set_param(enum DrumParam dp, uint8_t map, uint8_t key, uint8_t value); + void set_param(enum DrumParam dp, uint8_t map, const uint8_t *data, uint8_t length); + + /* End of public API. Below are internal data structures only */ + +private: + Settings *_settings; + + uint32_t _sampleRate; + uint8_t _channels; + + std::mutex midiMutex; + + struct std::vector _parts; + std::vector> _partMidiModCallbacks; + + ControlRom &_ctrlRom; + PcmRom &_pcmRom; + + // MIDI message types + static const uint8_t midi_NoteOff = 0x80; + static const uint8_t midi_NoteOn = 0x90; + static const uint8_t midi_PolyKeyPressure = 0xa0; + static const uint8_t midi_CtrlChange = 0xb0; + static const uint8_t midi_PrgChange = 0xc0; + static const uint8_t midi_ChPressure = 0xd0; + static const uint8_t midi_PitchBend = 0xe0; + + void _init_parts(void); +// int _export_sample_24(std::vector &sampleSet, std::string filename); + void _add_note(uint8_t midiChannel, uint8_t key, uint8_t velocity); + + void _midi_input_sysex_DT1(uint8_t model, const uint8_t *data, uint16_t length); + + Synth(); +}; + +} + +#endif // __SYNTH_H__ diff --git a/ThirdParty/libemusc/lib/libemusc.0.dylib b/ThirdParty/libemusc/lib/libemusc.0.dylib new file mode 100755 index 000000000..76a4ec442 Binary files /dev/null and b/ThirdParty/libemusc/lib/libemusc.0.dylib differ