Initial commit

main
Christopher Snowhill 2022-10-27 22:59:57 -07:00
commit bf8391aa0d
7 changed files with 494 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.DS_Store
_CodeSignature
xcuserdata
./build
*.o
scpipe

22
Info.plist Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>scpipe</string>
<key>CFBundleIdentifier</key>
<string>co.losno.scpipe</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2022 Christopher Snowhill. All rights reserved.</string>
</dict>
</plist>

24
Makefile Normal file
View File

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

115
SCCore.cpp Executable file
View File

@ -0,0 +1,115 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#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;
}

53
SCCore.h Executable file
View File

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

10
scpipe.entitlements Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.inherit</key>
<true/>
</dict>
</plist>

264
scpipe.mm Normal file
View File

@ -0,0 +1,264 @@
// scpipe.cpp : Defines the entry point for the console application.
//
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <vector>
#import <Cocoa/Cocoa.h>
#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<float> 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;
}