MIDI Plugin: Add a little Secret Sauce
Signed-off-by: Christopher Snowhill <kode54@gmail.com>main
parent
bcd5ab0dd2
commit
052a77d2cc
|
@ -204,6 +204,8 @@
|
||||||
83E5E54C18087CA5001F3284 /* miniModeOffTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */; };
|
83E5E54C18087CA5001F3284 /* miniModeOffTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54A18087CA5001F3284 /* miniModeOffTemplate.pdf */; };
|
||||||
83E5E54D18087CA5001F3284 /* miniModeOnTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54B18087CA5001F3284 /* miniModeOnTemplate.pdf */; };
|
83E5E54D18087CA5001F3284 /* miniModeOnTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83E5E54B18087CA5001F3284 /* miniModeOnTemplate.pdf */; };
|
||||||
83ED3AD1279A91C000904199 /* hdcdLogoTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83ED3AC7279A91C000904199 /* hdcdLogoTemplate.pdf */; };
|
83ED3AD1279A91C000904199 /* hdcdLogoTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 83ED3AC7279A91C000904199 /* hdcdLogoTemplate.pdf */; };
|
||||||
|
83F7AADA290B682400951B61 /* scpipe in Resources */ = {isa = PBXBuildFile; fileRef = 83F7AAD8290B682400951B61 /* scpipe */; };
|
||||||
|
83F7AADE290B8DDF00951B61 /* IIAM.bin in Resources */ = {isa = PBXBuildFile; fileRef = 83F7AAD7290B682400951B61 /* IIAM.bin */; };
|
||||||
83F9D8071A884C54007ABEC2 /* SilenceDecoder.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F9D7F61A884B46007ABEC2 /* SilenceDecoder.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
83F9D8071A884C54007ABEC2 /* SilenceDecoder.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83F9D7F61A884B46007ABEC2 /* SilenceDecoder.bundle */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
|
8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
|
||||||
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
|
||||||
|
@ -1088,6 +1090,8 @@
|
||||||
83F0E8AD287CD48800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
83F0E8AD287CD48800D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
83F0E8B1287CD50700D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
83F0E8B1287CD50700D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||||
83F0E8B2287CD52500D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = pl; path = pl.lproj/Credits.html; sourceTree = "<group>"; };
|
83F0E8B2287CD52500D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.html; name = pl; path = pl.lproj/Credits.html; sourceTree = "<group>"; };
|
||||||
|
83F7AAD7290B682400951B61 /* IIAM.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; path = IIAM.bin; sourceTree = "<group>"; };
|
||||||
|
83F7AAD8290B682400951B61 /* scpipe */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = scpipe; sourceTree = "<group>"; };
|
||||||
83F9D7F11A884B44007ABEC2 /* SilenceDecoder.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SilenceDecoder.xcodeproj; path = Plugins/SilenceDecoder/SilenceDecoder.xcodeproj; sourceTree = "<group>"; };
|
83F9D7F11A884B44007ABEC2 /* SilenceDecoder.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = SilenceDecoder.xcodeproj; path = Plugins/SilenceDecoder/SilenceDecoder.xcodeproj; sourceTree = "<group>"; };
|
||||||
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||||
8E07AB760AAC930B00A4B32F /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = PreferencesController.h; path = Preferences/PreferencesController.h; sourceTree = "<group>"; };
|
8E07AB760AAC930B00A4B32F /* PreferencesController.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = PreferencesController.h; path = Preferences/PreferencesController.h; sourceTree = "<group>"; };
|
||||||
|
@ -1564,6 +1568,8 @@
|
||||||
29B97317FDCFA39411CA2CEA /* Resources */ = {
|
29B97317FDCFA39411CA2CEA /* Resources */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
83F7AAD7290B682400951B61 /* IIAM.bin */,
|
||||||
|
83F7AAD8290B682400951B61 /* scpipe */,
|
||||||
839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */,
|
839E56F12879625100DFB5F4 /* SADIE_D02-96000.mhr */,
|
||||||
837DC92F285B3F790005C58A /* DataModel.xcdatamodeld */,
|
837DC92F285B3F790005C58A /* DataModel.xcdatamodeld */,
|
||||||
8316B3922839FFD5004CC392 /* Scenes.scnassets */,
|
8316B3922839FFD5004CC392 /* Scenes.scnassets */,
|
||||||
|
@ -2543,6 +2549,8 @@
|
||||||
17D1B2800CF8B2830028F5B5 /* s3m.icns in Resources */,
|
17D1B2800CF8B2830028F5B5 /* s3m.icns in Resources */,
|
||||||
8384916718083EAB00E7332D /* shuffleAlbumTemplate.pdf in Resources */,
|
8384916718083EAB00E7332D /* shuffleAlbumTemplate.pdf in Resources */,
|
||||||
17D1B2810CF8B2830028F5B5 /* song.icns in Resources */,
|
17D1B2810CF8B2830028F5B5 /* song.icns in Resources */,
|
||||||
|
83F7AADA290B682400951B61 /* scpipe in Resources */,
|
||||||
|
83F7AADE290B8DDF00951B61 /* IIAM.bin in Resources */,
|
||||||
831B99BF27C23E88005A969B /* Cog.sdef in Resources */,
|
831B99BF27C23E88005A969B /* Cog.sdef in Resources */,
|
||||||
832923AF279FAC400048201E /* Cog.q1.json in Resources */,
|
832923AF279FAC400048201E /* Cog.q1.json in Resources */,
|
||||||
836F462A28207FA4005B9B87 /* PauseColorful.png in Resources */,
|
836F462A28207FA4005B9B87 /* PauseColorful.png in Resources */,
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
83C35702180EDB74007E9DF0 /* MIDIContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83C35700180EDB74007E9DF0 /* MIDIContainer.mm */; };
|
83C35702180EDB74007E9DF0 /* MIDIContainer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83C35700180EDB74007E9DF0 /* MIDIContainer.mm */; };
|
||||||
83C35705180EDD1C007E9DF0 /* MIDIMetadataReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */; };
|
83C35705180EDD1C007E9DF0 /* MIDIMetadataReader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83C35703180EDD1C007E9DF0 /* MIDIMetadataReader.mm */; };
|
||||||
83E973471C4378880007F413 /* AUPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83E973451C4378880007F413 /* AUPlayer.mm */; };
|
83E973471C4378880007F413 /* AUPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83E973451C4378880007F413 /* AUPlayer.mm */; };
|
||||||
|
83F7AADD290B691900951B61 /* SCPlayer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 83F7AADB290B691900951B61 /* SCPlayer.mm */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -156,6 +157,8 @@
|
||||||
83E973451C4378880007F413 /* AUPlayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AUPlayer.mm; sourceTree = "<group>"; };
|
83E973451C4378880007F413 /* AUPlayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = AUPlayer.mm; sourceTree = "<group>"; };
|
||||||
83E973461C4378880007F413 /* AUPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AUPlayer.h; sourceTree = "<group>"; };
|
83E973461C4378880007F413 /* AUPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AUPlayer.h; sourceTree = "<group>"; };
|
||||||
83F0E6C4287CAB4300D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
83F0E6C4287CAB4300D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
|
83F7AADB290B691900951B61 /* SCPlayer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SCPlayer.mm; sourceTree = "<group>"; };
|
||||||
|
83F7AADC290B691900951B61 /* SCPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SCPlayer.h; sourceTree = "<group>"; };
|
||||||
83FAF8A618ADD60100057CAF /* PlaylistController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistController.h; path = ../../../Playlist/PlaylistController.h; sourceTree = "<group>"; };
|
83FAF8A618ADD60100057CAF /* PlaylistController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PlaylistController.h; path = ../../../Playlist/PlaylistController.h; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
@ -308,6 +311,8 @@
|
||||||
83B06690180D5668008E3612 /* MIDI */ = {
|
83B06690180D5668008E3612 /* MIDI */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
83F7AADC290B691900951B61 /* SCPlayer.h */,
|
||||||
|
83F7AADB290B691900951B61 /* SCPlayer.mm */,
|
||||||
834A42BC287AFC7F00EB9D9B /* AudioChunk.h */,
|
834A42BC287AFC7F00EB9D9B /* AudioChunk.h */,
|
||||||
8307D31E28607377000FF8EB /* SandboxBroker.h */,
|
8307D31E28607377000FF8EB /* SandboxBroker.h */,
|
||||||
831E2A9127B4B2FA006F1C86 /* json */,
|
831E2A9127B4B2FA006F1C86 /* json */,
|
||||||
|
@ -466,6 +471,7 @@
|
||||||
83A09F651CFA83F2001E7D2D /* opl3class.cpp in Sources */,
|
83A09F651CFA83F2001E7D2D /* opl3class.cpp in Sources */,
|
||||||
83C35705180EDD1C007E9DF0 /* MIDIMetadataReader.mm in Sources */,
|
83C35705180EDD1C007E9DF0 /* MIDIMetadataReader.mm in Sources */,
|
||||||
834BE91B1DE407CB00A07DCD /* resampler.c in Sources */,
|
834BE91B1DE407CB00A07DCD /* resampler.c in Sources */,
|
||||||
|
83F7AADD290B691900951B61 /* SCPlayer.mm in Sources */,
|
||||||
83A09F641CFA83F2001E7D2D /* opl3.cpp in Sources */,
|
83A09F641CFA83F2001E7D2D /* opl3.cpp in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#import "AUPlayer.h"
|
#import "AUPlayer.h"
|
||||||
#import "BMPlayer.h"
|
#import "BMPlayer.h"
|
||||||
#import "MSPlayer.h"
|
#import "MSPlayer.h"
|
||||||
|
#import "SCPlayer.h"
|
||||||
|
|
||||||
#import "Logging.h"
|
#import "Logging.h"
|
||||||
|
|
||||||
|
@ -220,6 +221,11 @@ static OSType getOSType(const char *in_) {
|
||||||
bmplayer->setFileSoundFont([soundFontPath UTF8String]);
|
bmplayer->setFileSoundFont([soundFontPath UTF8String]);
|
||||||
|
|
||||||
player = bmplayer;
|
player = bmplayer;
|
||||||
|
} else if([plugin isEqualToString:@"sauce"]) {
|
||||||
|
SCPlayer *scplayer = new SCPlayer;
|
||||||
|
player = scplayer;
|
||||||
|
|
||||||
|
scplayer->setSampleRate(sampleRate);
|
||||||
} else if([[plugin substringToIndex:4] isEqualToString:@"DOOM"]) {
|
} else if([[plugin substringToIndex:4] isEqualToString:@"DOOM"]) {
|
||||||
MSPlayer *msplayer = new MSPlayer;
|
MSPlayer *msplayer = new MSPlayer;
|
||||||
player = msplayer;
|
player = msplayer;
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "SCPlayer.h"
|
|
||||||
|
|
||||||
#define BLOCK_SIZE (512)
|
|
||||||
|
|
||||||
// YAY! OS X doesn't unload dylibs on dlclose, so we cache up to two sets of instances here
|
|
||||||
|
|
||||||
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
static const unsigned int g_max_instances = 2;
|
|
||||||
static std::vector<unsigned int> g_instances_open;
|
|
||||||
static SCCore g_sampler[3 * g_max_instances];
|
|
||||||
|
|
||||||
SCPlayer::SCPlayer()
|
|
||||||
: MIDIPlayer(), initialized(false), sccore_path(0) {
|
|
||||||
pthread_mutex_lock(&g_lock);
|
|
||||||
while(g_instances_open.size() >= g_max_instances) {
|
|
||||||
pthread_mutex_unlock(&g_lock);
|
|
||||||
usleep(10000);
|
|
||||||
pthread_mutex_lock(&g_lock);
|
|
||||||
}
|
|
||||||
unsigned int i;
|
|
||||||
for(i = 0; i < g_max_instances; ++i) {
|
|
||||||
if(std::find(g_instances_open.begin(), g_instances_open.end(), i) == g_instances_open.end())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
g_instances_open.push_back(i);
|
|
||||||
instance_id = i;
|
|
||||||
sampler = &g_sampler[i * 3];
|
|
||||||
pthread_mutex_unlock(&g_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
SCPlayer::~SCPlayer() {
|
|
||||||
shutdown();
|
|
||||||
if(sccore_path)
|
|
||||||
free(sccore_path);
|
|
||||||
if(sampler) {
|
|
||||||
pthread_mutex_lock(&g_lock);
|
|
||||||
auto it = std::find(g_instances_open.begin(), g_instances_open.end(), instance_id);
|
|
||||||
it = g_instances_open.erase(it);
|
|
||||||
pthread_mutex_unlock(&g_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SCPlayer::set_sccore_path(const char *path) {
|
|
||||||
size_t len;
|
|
||||||
if(sccore_path) free(sccore_path);
|
|
||||||
len = strlen(path);
|
|
||||||
sccore_path = (char *)malloc(len + 1);
|
|
||||||
if(sccore_path)
|
|
||||||
memcpy(sccore_path, path, len + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SCPlayer::send_event(uint32_t b) {
|
|
||||||
send_event_time(b, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SCPlayer::send_sysex(const uint8_t *data, size_t size, size_t port) {
|
|
||||||
send_sysex_time(data, size, port, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SCPlayer::send_event_time(uint32_t b, unsigned int time) {
|
|
||||||
unsigned port = (b >> 24) & 0x7F;
|
|
||||||
if(port > 2) port = 0;
|
|
||||||
sampler[port].TG_ShortMidiIn(b, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SCPlayer::send_sysex_time(const uint8_t *data, size_t size, size_t port, unsigned int time) {
|
|
||||||
if(port > 2) port = 0;
|
|
||||||
sampler[port].TG_LongMidiIn(data, time);
|
|
||||||
if(port == 0) {
|
|
||||||
sampler[1].TG_LongMidiIn(data, time);
|
|
||||||
sampler[2].TG_LongMidiIn(data, time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SCPlayer::render(float *out, unsigned long count) {
|
|
||||||
memset(out, 0, count * sizeof(float) * 2);
|
|
||||||
while(count) {
|
|
||||||
float buffer[2][BLOCK_SIZE];
|
|
||||||
unsigned long todo = count > BLOCK_SIZE ? BLOCK_SIZE : count;
|
|
||||||
for(unsigned long i = 0; i < 3; ++i) {
|
|
||||||
memset(buffer[0], 0, todo * sizeof(float));
|
|
||||||
memset(buffer[1], 0, todo * sizeof(float));
|
|
||||||
|
|
||||||
sampler[i].TG_setInterruptThreadIdAtThisTime();
|
|
||||||
sampler[i].TG_Process(buffer[0], buffer[1], (unsigned int)todo);
|
|
||||||
|
|
||||||
for(unsigned long j = 0; j < todo; ++j) {
|
|
||||||
out[j * 2 + 0] += buffer[0][j];
|
|
||||||
out[j * 2 + 1] += buffer[1][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out += todo * 2;
|
|
||||||
count -= todo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SCPlayer::shutdown() {
|
|
||||||
for(int i = 0; i < 3; i++) {
|
|
||||||
if(sampler[i].TG_deactivate) {
|
|
||||||
sampler[i].TG_flushMidi();
|
|
||||||
sampler[i].TG_deactivate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SCPlayer::startup() {
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if(initialized) return true;
|
|
||||||
|
|
||||||
if(!sccore_path) return false;
|
|
||||||
|
|
||||||
for(i = 0; i < 3; i++) {
|
|
||||||
if(!sampler[i].TG_initialize) {
|
|
||||||
if(!sampler[i].Load(sccore_path, true))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(sampler[i].TG_initialize(0) < 0)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
sampler[i].TG_activate(44100.0, 1024);
|
|
||||||
sampler[i].TG_setMaxBlockSize(256);
|
|
||||||
sampler[i].TG_setSampleRate((float)uSampleRate);
|
|
||||||
sampler[i].TG_setSampleRate((float)uSampleRate);
|
|
||||||
sampler[i].TG_setMaxBlockSize(BLOCK_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
for(int i = 0; i < 3; i++) {
|
|
||||||
reset(i, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int SCPlayer::get_playing_note_count() {
|
|
||||||
unsigned int total = 0;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
if(!initialized)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
for(i = 0; i < 3; i++)
|
|
||||||
total += sampler[i].TG_XPgetCurTotalRunningVoices();
|
|
||||||
|
|
||||||
return total;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int SCPlayer::send_event_needs_time() {
|
|
||||||
return BLOCK_SIZE;
|
|
||||||
}
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "MIDIPlayer.h"
|
#include "MIDIPlayer.h"
|
||||||
|
|
||||||
#include "SCCore.h"
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
class SCPlayer : public MIDIPlayer {
|
class SCPlayer : public MIDIPlayer {
|
||||||
public:
|
public:
|
||||||
|
@ -13,28 +13,42 @@ class SCPlayer : public MIDIPlayer {
|
||||||
// close, unload
|
// close, unload
|
||||||
virtual ~SCPlayer();
|
virtual ~SCPlayer();
|
||||||
|
|
||||||
unsigned int get_playing_note_count();
|
|
||||||
|
|
||||||
void set_sccore_path(const char* path);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual unsigned int send_event_needs_time();
|
virtual unsigned int send_event_needs_time();
|
||||||
virtual void send_event(uint32_t b);
|
virtual void send_event(uint32_t b);
|
||||||
virtual void send_sysex(const uint8_t* data, size_t size, size_t port);
|
virtual void send_sysex(const uint8_t* event, size_t size, size_t port);
|
||||||
virtual void render(float* out, unsigned long count);
|
virtual void render(float* out, unsigned long count);
|
||||||
|
|
||||||
virtual void shutdown();
|
virtual void shutdown();
|
||||||
virtual bool startup();
|
virtual bool startup();
|
||||||
|
|
||||||
virtual void send_event_time(uint32_t b, unsigned int time);
|
virtual void send_event_time(uint32_t b, unsigned int time);
|
||||||
virtual void send_sysex_time(const uint8_t* data, size_t size, size_t port, unsigned int time);
|
virtual void send_sysex_time(const uint8_t* event, size_t size, size_t port, unsigned int time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int instance_id;
|
bool LoadCore();
|
||||||
bool initialized;
|
|
||||||
SCCore* sampler;
|
|
||||||
|
|
||||||
char* sccore_path;
|
void send_command(uint32_t port, uint32_t command);
|
||||||
|
|
||||||
|
void render_port(uint32_t port, float* out, uint32_t count);
|
||||||
|
|
||||||
|
void reset(uint32_t port);
|
||||||
|
|
||||||
|
void junk(uint32_t port, unsigned long count);
|
||||||
|
|
||||||
|
bool process_create(uint32_t port);
|
||||||
|
void process_terminate(uint32_t port);
|
||||||
|
bool process_running(uint32_t port);
|
||||||
|
uint32_t process_read_code(uint32_t port);
|
||||||
|
void process_read_bytes(uint32_t port, void* buffer, uint32_t size);
|
||||||
|
uint32_t process_read_bytes_pass(uint32_t port, void* buffer, uint32_t size);
|
||||||
|
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];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -0,0 +1,243 @@
|
||||||
|
#include "SCPlayer.h"
|
||||||
|
|
||||||
|
#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;
|
||||||
|
for(unsigned int i = 0; i < 3; ++i) {
|
||||||
|
bTerminating[i] = false;
|
||||||
|
hProcess[i] = nil;
|
||||||
|
hChildStd_IN[i] = nil;
|
||||||
|
hChildStd_OUT[i] = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCPlayer::~SCPlayer() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SCPlayer::shutdown() {
|
||||||
|
for(int i = 0; i < 3; i++) {
|
||||||
|
process_terminate(i);
|
||||||
|
}
|
||||||
|
initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SCPlayer::startup() {
|
||||||
|
if(initialized) return true;
|
||||||
|
|
||||||
|
if(!LoadCore())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
|
||||||
|
setFilterMode(mode, reverb_chorus_disabled);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int SCPlayer::send_event_needs_time() {
|
||||||
|
return 0; // 4096; This doesn't work for some reason
|
||||||
|
}
|
|
@ -68,6 +68,8 @@ static void enumCallback(void *context, OSType uSubType, OSType uManufacturer, c
|
||||||
|
|
||||||
[self addObject:@{@"name": @"BASSMIDI", @"preference": @"BASSMIDI"}];
|
[self addObject:@{@"name": @"BASSMIDI", @"preference": @"BASSMIDI"}];
|
||||||
|
|
||||||
|
[self addObject:@{ @"name": @"Secret Sauce", @"preference": @"sauce" }];
|
||||||
|
|
||||||
[self addObject:@{@"name": @"DMX Generic", @"preference": @"DOOM0000"}];
|
[self addObject:@{@"name": @"DMX Generic", @"preference": @"DOOM0000"}];
|
||||||
[self addObject:@{@"name": @"DMX Doom 1", @"preference": @"DOOM0001"}];
|
[self addObject:@{@"name": @"DMX Doom 1", @"preference": @"DOOM0001"}];
|
||||||
[self addObject:@{@"name": @"DMX Doom 2", @"preference": @"DOOM0002"}];
|
[self addObject:@{@"name": @"DMX Doom 2", @"preference": @"DOOM0002"}];
|
||||||
|
|
Loading…
Reference in New Issue