From bf8391aa0d1e4f7b01f062117ff544b827825363 Mon Sep 17 00:00:00 2001 From: Christopher Snowhill Date: Thu, 27 Oct 2022 22:59:57 -0700 Subject: [PATCH] Initial commit --- .gitignore | 6 + Info.plist | 22 ++++ Makefile | 24 ++++ SCCore.cpp | 115 +++++++++++++++++++ SCCore.h | 53 +++++++++ scpipe.entitlements | 10 ++ scpipe.mm | 264 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 494 insertions(+) create mode 100644 .gitignore create mode 100644 Info.plist create mode 100644 Makefile create mode 100755 SCCore.cpp create mode 100755 SCCore.h create mode 100644 scpipe.entitlements create mode 100644 scpipe.mm diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9259441 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store +_CodeSignature +xcuserdata +./build +*.o +scpipe diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..afe70ac --- /dev/null +++ b/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + scpipe + CFBundleIdentifier + co.losno.scpipe + CFBundleInfoDictionaryVersion + 6.0 + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSHumanReadableCopyright + Copyright © 2022 Christopher Snowhill. All rights reserved. + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..23488c9 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +EXE = scpipe + +ARCHFLAGS = -arch x86_64 + +CFLAGS = -Os + +SCPIPE_OBJS = scpipe.o SCCore.o + +.PHONY: all clean + +all: $(EXE) + +scpipe: $(SCPIPE_OBJS) + $(CXX) $(ARCHFLAGS) $^ -o $@ -Xlinker -sectcreate -Xlinker __TEXT -Xlinker __info_plist -Xlinker Info.plist -framework Foundation + codesign -f -s "Developer ID Application: Christopher Snowhill" --entitlements scpipe.entitlements -o runtime $@ + +scpipe.o: scpipe.mm SCCore.h SCCore.cpp + $(CXX) -c $(ARCHFLAGS) $(CFLAGS) scpipe.mm -o $@ + +SCCore.o: SCCore.cpp SCCore.h + $(CXX) -c $(ARCHFLAGS) $(CFLAGS) SCCore.cpp -o $@ + +clean: + rm -rf $(SCPIPE_OBJS) $(EXE) > /dev/null diff --git a/SCCore.cpp b/SCCore.cpp new file mode 100755 index 0000000..536ac09 --- /dev/null +++ b/SCCore.cpp @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +#include "SCCore.h" + +SCCore::SCCore() +{ + path = 0; + + handle = 0; + + TG_initialize = 0; + //TG_terminate = 0; + TG_activate = 0; + // TG_deactivate = 0; + TG_setSampleRate = 0; + TG_setMaxBlockSize = 0; + TG_flushMidi = 0; + TG_setInterruptThreadIdAtThisTime = 0; + // TG_PMidiIn = 0; + TG_ShortMidiIn = 0; + TG_LongMidiIn = 0; + // TG_isFatalError = 0; + // TG_getErrorStrings = 0; + TG_XPgetCurTotalRunningVoices = 0; + // TG_XPsetSystemConfig = 0; + // TG_XPgetCurSystemConfig = 0; + TG_Process = 0; +} + +SCCore::~SCCore() +{ + Unload(); +} + +void SCCore::Unload() +{ + if (handle) + { + dlclose(handle); + handle = 0; + } + if (path) + { + free(path); + path = 0; + } +} + +bool SCCore::Load(const char * _path) +{ + path = (char *) malloc(strlen(_path) + 1); + strcpy(path, _path); + + handle = dlopen(path, RTLD_LAZY | RTLD_LOCAL); + if (handle) + { + *(void**)&TG_initialize = dlsym(handle, "TG_initialize"); + //*(void**)&TG_terminate = dlsym(handle, "TG_terminate"); + *(void**)&TG_activate = dlsym(handle, "TG_activate"); + //*(void**)&TG_deactivate = dlsym(handle, "TG_deactivate"); + *(void**)&TG_setSampleRate = dlsym(handle, "TG_setSampleRate"); + *(void**)&TG_setMaxBlockSize = dlsym(handle, "TG_setMaxBlockSize"); + *(void**)&TG_flushMidi = dlsym(handle, "TG_flushMidi"); + *(void**)&TG_setInterruptThreadIdAtThisTime = dlsym(handle, "TG_setInterruptThreadIdAtThisTime"); + //*(void**)&TG_PMidiIn = dlsym(handle, "TG_PMidiIn"); + *(void**)&TG_ShortMidiIn = dlsym(handle, "TG_ShortMidiIn"); + *(void**)&TG_LongMidiIn = dlsym(handle, "TG_LongMidiIn"); + //*(void**)&TG_isFatalError = dlsym(handle, "TG_isFatalError"); + //*(void**)&TG_getErrorStrings = dlsym(handle, "TG_getErrorStrings"); + *(void**)&TG_XPgetCurTotalRunningVoices = dlsym(handle, "TG_XPgetCurTotalRunningVoices"); + //*(void**)&TG_XPsetSystemConfig = dlsym(handle, "TG_XPsetSystemConfig"); + //*(void**)&TG_XPgetCurSystemConfig = dlsym(handle, "TG_XPgetCurSystemConfig"); + *(void**)&TG_Process = dlsym(handle, "TG_Process"); + + if (TG_initialize && /*TG_terminate &&*/ TG_activate && /*TG_deactivate &&*/ + TG_setSampleRate && TG_setMaxBlockSize && TG_flushMidi && + TG_setInterruptThreadIdAtThisTime && /*TG_PMidiIn &&*/ + TG_ShortMidiIn && TG_LongMidiIn && /*TG_isFatalError && + TG_getErrorStrings &&*/ TG_XPgetCurTotalRunningVoices && + /*TG_XPsetSystemConfig && TG_XPgetCurSystemConfig &&*/ + TG_Process) + { + return true; + } + else + { + TG_initialize = 0; + //TG_terminate = 0; + TG_activate = 0; + // TG_deactivate = 0; + TG_setSampleRate = 0; + TG_setMaxBlockSize = 0; + TG_flushMidi = 0; + TG_setInterruptThreadIdAtThisTime = 0; + // TG_PMidiIn = 0; + TG_ShortMidiIn = 0; + TG_LongMidiIn = 0; + // TG_isFatalError = 0; + // TG_getErrorStrings = 0; + TG_XPgetCurTotalRunningVoices = 0; + // TG_XPsetSystemConfig = 0; + // TG_XPgetCurSystemConfig = 0; + TG_Process = 0; + + dlclose(handle); + handle = 0; + } + } + + return false; +} diff --git a/SCCore.h b/SCCore.h new file mode 100755 index 0000000..efa58c7 --- /dev/null +++ b/SCCore.h @@ -0,0 +1,53 @@ +#ifndef _SCCore_h_ +#define _SCCore_h_ + +// Static single instance - duplicate library to temp path for unique instance + +class SCCore +{ + char * path; + void * handle; + +public: + int (* TG_initialize)(int i); // i = 0, returns negative on failure + + //void (* TG_terminate)(); // Unused - terminates process + + void (* TG_activate)(float sampleRate, int blockSize); + + //void (* TG_deactivate)(); // Unused + + void (*TG_setSampleRate)(float sampleRate); + + void (*TG_setMaxBlockSize)(unsigned int blockSize); + + void (*TG_flushMidi)(); // Called after applying presets + + void (*TG_setInterruptThreadIdAtThisTime)(); + + //void (*TG_PMidiIn)(MpPacket *, int count); // Unknown + + void (*TG_ShortMidiIn)(unsigned int eventCode, unsigned int deltaFrames); + + void (*TG_LongMidiIn)(const unsigned char * sysEx, unsigned int deltaFrames); + + //void (*TG_isFatalError)(int errCode); // Unused + + //void (*TG_getErrorStrings)(int errCode); // Unused + + unsigned int (*TG_XPgetCurTotalRunningVoices)(); // Unused + + //void (*TG_XPsetSystemConfig)(); + + //void (*TG_XPgetCurSystemConfig)(); + + void (*TG_Process)(float * left, float * right, unsigned int count); + + SCCore(); + ~SCCore(); + + bool Load(const char * path); + void Unload(); +}; + +#endif diff --git a/scpipe.entitlements b/scpipe.entitlements new file mode 100644 index 0000000..794eada --- /dev/null +++ b/scpipe.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.inherit + + + diff --git a/scpipe.mm b/scpipe.mm new file mode 100644 index 0000000..591db44 --- /dev/null +++ b/scpipe.mm @@ -0,0 +1,264 @@ +// scpipe.cpp : Defines the entry point for the console application. +// + +#include +#include +#include + +#include + +#import + +#include "SCCore.h" + +// #define LOG_EXCHANGE + +enum +{ + BUFFER_SIZE = 4096 +}; + +static NSFileHandle *pipe_in = nil; +static NSFileHandle *pipe_out = nil; + +void put_bytes( const void * out, uint32_t size ) +{ + NSError *error; + NSData *data = [NSData dataWithBytes:out length:size]; + [pipe_out writeData:data error:&error]; +} + +void put_code( uint32_t code ) +{ + put_bytes( &code, sizeof(code) ); +} + +void get_bytes( void * in, uint32_t size ) +{ + NSError *error = nil; + NSData *data = [pipe_in readDataUpToLength:size error:&error]; + if(!data || error) { + memset( in, 0, size ); + } + NSUInteger bytesDone = [data length]; + memcpy(in, [data bytes], bytesDone); + if(bytesDone < size) { + memset(((uint8_t *)in) + bytesDone, 0, size - bytesDone); + } +} + +uint32_t get_code() +{ + uint32_t code; + get_bytes( &code, sizeof(code) ); + return code; +} + +int main( int argc, const char * argv[] ) +{ + if ( argv == NULL || argc != 2 ) return 1; + + unsigned code = 0; + + SCCore * sampler = NULL; + + uint32_t sample_rate = 44100; + + std::vector sample_buffer; + unsigned int samples_buffered = 0; + + uint8_t* msgbuf = NULL; + size_t msgsize = 0; + + pipe_in = [NSFileHandle fileHandleWithStandardInput]; + pipe_out = [NSFileHandle fileHandleWithStandardOutput]; + + sampler = new SCCore; + + if ( !sampler->Load( argv[ 1 ] ) ) + { + code = 1; + goto exit; + } + + if (sampler->TG_initialize(0) < 0) + { + code = 2; + goto exit; + } + + sample_buffer.resize( BUFFER_SIZE * 4 ); + + put_code( 0 ); + + for (;;) + { + uint32_t command = get_code(); + if ( !command ) break; + + switch ( command ) + { + case 1: // Set Sample Rate + { + uint32_t size = get_code(); + if ( size != sizeof(sample_rate) ) + { + code = 10; + goto exit; + } + + sample_rate = get_code(); + + sampler->TG_activate(44100.0, 1024); + sampler->TG_setMaxBlockSize(256); + sampler->TG_setSampleRate((float)sample_rate); + sampler->TG_setSampleRate((float)sample_rate); + sampler->TG_setMaxBlockSize(BUFFER_SIZE); + + put_code( 0 ); + } + break; + + case 2: // Send MIDI Event + { + uint32_t b = get_code(); + + sampler->TG_ShortMidiIn(b, 0); + + put_code( 0 ); + } + break; + + case 3: // Send System Exclusive Event + { + uint32_t size = get_code(); + + if (size + 1 > msgsize) + { + msgsize = (size + 1024) & ~1023; + msgbuf = (uint8_t *) realloc(msgbuf, msgsize); + } + + if (!msgbuf) + { + code = 3; + goto exit; + } + + get_bytes( msgbuf, size ); + + if ( msgbuf[size-1] != 0xF7 ) + msgbuf[size] = 0xF7; + + sampler->TG_LongMidiIn( msgbuf, 0 ); + + put_code( 0 ); + } + break; + + case 4: // Render Samples + { + uint32_t count = get_code(); + + put_code( 0 ); + + while( count ) + { + unsigned count_to_do = MIN(count, BUFFER_SIZE); + + memset(&sample_buffer[0], 0, count_to_do * sizeof(float)); + memset(&sample_buffer[BUFFER_SIZE], 0, count_to_do * sizeof(float)); + + sampler->TG_setInterruptThreadIdAtThisTime(); + sampler->TG_Process(&sample_buffer[0], &sample_buffer[BUFFER_SIZE], count_to_do); + + float * out = &sample_buffer[BUFFER_SIZE * 2]; + + for ( unsigned i = 0; i < count_to_do; ++i ) + { + float sample = sample_buffer[i]; + out[ 0 ] = sample; + sample = sample_buffer[BUFFER_SIZE + i]; + out[ 1 ] = sample; + out += 2; + } + + put_bytes( &sample_buffer[BUFFER_SIZE * 2], count_to_do * sizeof(float) * 2 ); + + count -= count_to_do; + } + } + break; + + case 5: // Junk Samples + { + uint32_t count = get_code(); + + while ( count ) + { + unsigned count_to_do = MIN(count, BUFFER_SIZE); + + sampler->TG_setInterruptThreadIdAtThisTime(); + sampler->TG_Process(&sample_buffer[0], &sample_buffer[BUFFER_SIZE], count_to_do); + + count -= count_to_do; + } + + put_code( 0 ); + } + break; + + + case 6: // Send event, with timestamp + { + uint32_t b = get_code(); + uint32_t timestamp = get_code(); + + sampler->TG_ShortMidiIn(b, timestamp); + + put_code( 0 ); + } + break; + + case 7: // Send System Exclusive, with timestamp + { + uint32_t size = get_code(); + uint32_t timestamp = get_code(); + + if (size + 1 > msgsize) + { + msgsize = (size + 1024) & ~1023; + msgbuf = (uint8_t*)realloc(msgbuf, msgsize); + } + + if (!msgbuf) + { + code = 3; + goto exit; + } + + get_bytes(msgbuf, size); + + if (msgbuf[size - 1] != 0xF7) + msgbuf[size] = 0xF7; + + sampler->TG_LongMidiIn(msgbuf, timestamp); + + put_code(0); + } + break; + + default: + code = 4; + goto exit; + break; + } + } + +exit: + delete sampler; + + put_code( code ); + + return code; +}