Initial commit
commit
bf8391aa0d
|
@ -0,0 +1,6 @@
|
|||
.DS_Store
|
||||
_CodeSignature
|
||||
xcuserdata
|
||||
./build
|
||||
*.o
|
||||
scpipe
|
|
@ -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>
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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>
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue