EmuSC experiment goes here

This is mostly working, but kind of buggy.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-03-15 00:21:43 -07:00
parent e7779278bd
commit e233a95c2a
12 changed files with 1277 additions and 205 deletions

View file

@ -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 = "<group>"; };
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 = "<group>"; };
836E66952D854D5600B62C38 /* SCPlayer.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SCPlayer.mm; sourceTree = "<group>"; };
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 = "<absolute>"; };
836E669A2D855AAF00B62C38 /* r00233567_control.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = r00233567_control.bin; sourceTree = "<group>"; };
836E669B2D855AAF00B62C38 /* r15199858_main_mcu.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = r15199858_main_mcu.bin; sourceTree = "<group>"; };
836E669C2D855AAF00B62C38 /* r15209359_pcm_1.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = r15209359_pcm_1.bin; sourceTree = "<group>"; };
836E669D2D855AAF00B62C38 /* r15279813_pcm_2.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = r15279813_pcm_2.bin; sourceTree = "<group>"; };
83747C222862DB7F0021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
838EE8CA29A8600C00CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
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 = "<group>";
};
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 = "<group>";
};
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)";

View file

@ -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;

View file

@ -5,6 +5,8 @@
#import <Cocoa/Cocoa.h>
#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

View file

@ -2,134 +2,13 @@
#include <vector>
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;
}

View file

@ -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"}];

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
// 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 <stdint.h>
#include <array>
#include <fstream>
#include <string>
#include <vector>
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<uint8_t, 128> VelocityCurve0;
std::array<uint8_t, 128> VelocityCurve1;
std::array<uint8_t, 128> VelocityCurve2;
std::array<uint8_t, 128> VelocityCurve3;
std::array<uint8_t, 128> VelocityCurve4;
std::array<uint8_t, 128> VelocityCurve5;
std::array<uint8_t, 128> VelocityCurve6;
std::array<uint8_t, 128> VelocityCurve7;
std::array<uint8_t, 128> VelocityCurve8;
std::array<uint8_t, 128> VelocityCurve9;
std::array<uint8_t, 128> mul2;
std::array<uint8_t, 128> mul2From85;
std::array<uint8_t, 128> TVABiasPoint1;
std::array<uint8_t, 128> TVAEnvTKFP1T14Index;
std::array<uint8_t, 128> TVAEnvTKFP1T5Index;
// std::array<int, 128> mul256;
// std::array<int, 128> mul256From60;
// std::array<int, 128> mul256From96;
// std::array<int, 128> mul256Upto96;
std::array<int, 128> PitchScale1;
std::array<int, 128> PitchScale2;
std::array<int, 128> PitchScale3;
// CPUROM
std::array<uint8_t, 21> TimeKeyFollowDiv;
std::array<int, 256> TimeKeyFollow;
std::array<int, 128> envelopeTime;
std::array<int, 128> LFORate;
std::array<int, 128> LFODelayTime;
std::array<int, 128> LFOTVFDepth;
std::array<int, 128> LFOTVPDepth;
std::array<uint8_t, 128> LFOSine;
std::array<int, 128> TVFEnvDepth;
std::array<int, 128> TVFCutoffFreq;
std::array<uint8_t, 256> TVFResonanceFreq;
std::array<uint8_t, 128> TVFResonance;
std::array<int, 128> PitchEnvDepth;
std::array<uint8_t, 64> TVFEnvScale;
std::array<int, 256> TVAEnvExpChange;
std::array<uint8_t, 129> TVABiasLevel;
std::array<uint8_t, 128> TVAPanpot;
std::array<uint8_t, 128> TVALevelIndex;
std::array<uint8_t, 256> 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<uint8_t> 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<uint8_t, 128>& get_drum_sets_LUT(void) { return _drumSetsLUT; }
const uint8_t max_polyphony(void);
std::vector<std::vector<std::string>> get_instruments_list(void);
std::vector<std::vector<std::string>> get_partials_list(void);
std::vector<std::vector<std::string>> get_samples_list(void);
std::vector<std::vector<std::string>> get_variations_list(void);
std::vector<std::string> 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<uint16_t, 128>& variation(int v) const { return _variations[v]; }
inline int numSampleSets(void) { return _samples.size(); }
inline int numInstruments(void) { return _instruments.size(); }
inline std::vector<DrumSet> &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<uint32_t> _banksSC55;
// Only a placeholder, SC-88 layout is currently unkown
static const std::vector<uint32_t> _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<int, 128> &lut);
int _read_lut_16bit(std::ifstream &ifs, int pos, std::array<int, 129> &lut);
int _read_lut_16bit(std::ifstream &ifs, int pos, std::array<int, 256> &lut);
int _identify_model(std::ifstream &romFile);
const std::vector<uint32_t> &_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<uint8_t, 128> _drumSetsLUT;
std::vector<Instrument> _instruments;
std::vector<Partial> _partials;
std::vector<Sample> _samples;
std::vector<DrumSet> _drumSets;
// TODO: define constants for variation table dimensions
std::array<std::array<uint16_t, 128>, 128> _variations;
ControlRom();
};
}
#endif // __CONTROL_ROM_H__

380
ThirdParty/libemusc/include/params.h vendored Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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__

72
ThirdParty/libemusc/include/pcm_rom.h vendored Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
// 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 <stdint.h>
#include <string>
#include <vector>
namespace EmuSC {
class PcmRom
{
private:
std::string _version;
std::string _date;
struct Samples {
// std::vector<int32_t> samplesI; // All samples stored in 24 bit 32kHz mono
std::vector<float> samplesF; // 32 bit float, 32kHz, mono
};
std::vector<struct Samples> _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<char> &rom,
struct ControlRom::Sample &ctrlSample,
enum ControlRom::SynthGen synthGen);
PcmRom();
public:
PcmRom(std::vector<std::string> 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__

161
ThirdParty/libemusc/include/settings.h vendored Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef __SETTINGS_H__
#define __SETTINGS_H__
#include "control_rom.h"
#include "params.h"
#include <stdint.h>
#include <array>
#include <string>
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<uint8_t, 0x0100> _systemParams; // Both SysEx and non-SysEx data
std::array<uint8_t, 0x4000> _patchParams;
std::array<uint8_t, 0x2000> _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<uint8_t, 16> _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<uint8_t, 16> _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__

185
ThirdParty/libemusc/include/synth.h vendored Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef __SYNTH_H__
#define __SYNTH_H__
#include "control_rom.h"
#include "params.h"
#include "pcm_rom.h"
#include <array>
#include <functional>
#include <mutex>
#include <string>
#include <vector>
/* 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<float, 16> 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<uint8_t> parts);
// Unute all parts
void unmute_parts(std::vector<uint8_t> 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<void(const int)> callback);
void clear_part_midi_mod_callback(void);
void set_part_envelope_callback(int partId,
std::function<void(const float, const float,
const float, const float,
const float, const float)> callback);
void clear_part_envelope_callback(int partId);
void set_part_lfo_callback(int partId,
std::function<void(const float, const float,
const float)> 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<Part> _parts;
std::vector<std::function<void(const int)>> _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<int32_t> &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__

BIN
ThirdParty/libemusc/lib/libemusc.0.dylib vendored Executable file

Binary file not shown.