352 lines
8.2 KiB
C++
352 lines
8.2 KiB
C++
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <pthread.h>
|
|
#include <vector>
|
|
#include <unistd.h>
|
|
|
|
#include "SCPlayer.h"
|
|
|
|
// 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), mode(sc_default), 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);
|
|
}
|
|
}
|
|
|
|
static const uint8_t syx_reset_gm[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7 };
|
|
static const uint8_t syx_reset_gm2[] = { 0xF0, 0x7E, 0x7F, 0x09, 0x03, 0xF7 };
|
|
static const uint8_t syx_reset_gs[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7 };
|
|
static const uint8_t syx_reset_xg[] = { 0xF0, 0x43, 0x10, 0x4C, 0x00, 0x00, 0x7E, 0x00, 0xF7 };
|
|
|
|
static const uint8_t syx_gs_limit_bank_lsb[] = { 0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x41, 0x00, 0x03, 0x00, 0xF7 };
|
|
|
|
static bool syx_equal(const uint8_t * a, const uint8_t * b)
|
|
{
|
|
while (*a != 0xF7 && *b != 0xF7 && *a == *b)
|
|
{
|
|
a++; b++;
|
|
}
|
|
|
|
return *a == *b;
|
|
}
|
|
|
|
static bool syx_is_reset(const uint8_t * data)
|
|
{
|
|
return syx_equal(data, syx_reset_gm) || syx_equal(data, syx_reset_gm2) || syx_equal(data, syx_reset_gs) || syx_equal(data, syx_reset_xg);
|
|
}
|
|
|
|
void SCPlayer::send_sysex(uint32_t port, const uint8_t * data)
|
|
{
|
|
sampler[port].TG_LongMidiIn( data, 0 );
|
|
if (syx_is_reset(data) && mode != sc_default)
|
|
{
|
|
reset(port);
|
|
}
|
|
}
|
|
|
|
void SCPlayer::send_gs(uint32_t port, uint8_t * data)
|
|
{
|
|
unsigned long i;
|
|
unsigned char checksum = 0;
|
|
for (i = 5; data[i+1] != 0xF7; ++i)
|
|
checksum += data[i];
|
|
checksum = (128 - checksum) & 127;
|
|
data[i] = checksum;
|
|
sampler[port].TG_LongMidiIn( data, 0 );
|
|
}
|
|
|
|
void SCPlayer::reset_sc(uint32_t port)
|
|
{
|
|
unsigned int i;
|
|
uint8_t message[11];
|
|
|
|
memcpy(message, syx_gs_limit_bank_lsb, 11);
|
|
|
|
message[7] = 1;
|
|
|
|
switch (mode)
|
|
{
|
|
default: break;
|
|
|
|
case sc_sc55:
|
|
message[8] = 1;
|
|
break;
|
|
|
|
case sc_sc88:
|
|
message[8] = 2;
|
|
break;
|
|
|
|
case sc_sc88pro:
|
|
message[8] = 3;
|
|
break;
|
|
|
|
case sc_sc8850:
|
|
case sc_default:
|
|
message[8] = 4;
|
|
break;
|
|
}
|
|
|
|
for (i = 0x41; i <= 0x49; ++i)
|
|
{
|
|
message[6] = i;
|
|
send_gs(port, message);
|
|
}
|
|
message[6] = 0x40;
|
|
send_gs(port, message);
|
|
for (i = 0x4A; i <= 0x4F; ++i)
|
|
{
|
|
message[6] = i;
|
|
send_gs(port, message);
|
|
}
|
|
}
|
|
|
|
void SCPlayer::reset(uint32_t port)
|
|
{
|
|
if (initialized)
|
|
{
|
|
sampler[port].TG_LongMidiIn(syx_reset_xg, 0); junk(port, 1024);
|
|
sampler[port].TG_LongMidiIn(syx_reset_gm2, 0); junk(port, 1024);
|
|
sampler[port].TG_LongMidiIn(syx_reset_gm, 0); junk(port, 1024);
|
|
|
|
switch (mode)
|
|
{
|
|
case sc_gm:
|
|
/*sampler[port].TG_LongMidiIn( syx_reset_gm, 0 );*/
|
|
break;
|
|
|
|
case sc_gm2:
|
|
sampler[port].TG_LongMidiIn( syx_reset_gm2, 0 );
|
|
break;
|
|
|
|
case sc_sc55:
|
|
case sc_sc88:
|
|
case sc_sc88pro:
|
|
case sc_sc8850:
|
|
case sc_default:
|
|
sampler[port].TG_LongMidiIn( syx_reset_gs, 0 ); junk(port, 1024);
|
|
reset_sc(port);
|
|
break;
|
|
|
|
case sc_xg:
|
|
sampler[port].TG_LongMidiIn( syx_reset_xg, 0 );
|
|
break;
|
|
}
|
|
|
|
junk(port, 1024);
|
|
|
|
{
|
|
unsigned int i;
|
|
for (i = 0; i < 16; ++i)
|
|
{
|
|
sampler[port].TG_ShortMidiIn(0x78B0 + i, 0);
|
|
sampler[port].TG_ShortMidiIn(0x79B0 + i, 0);
|
|
if (mode != sc_xg || i != 9)
|
|
{
|
|
sampler[port].TG_ShortMidiIn(0x20B0 + i, 0);
|
|
sampler[port].TG_ShortMidiIn(0x00B0 + i, 0);
|
|
sampler[port].TG_ShortMidiIn(0xC0 + i, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mode == sc_xg)
|
|
{
|
|
sampler[port].TG_ShortMidiIn(0x20B9, 0);
|
|
sampler[port].TG_ShortMidiIn(0x7F00B9, 0);
|
|
sampler[port].TG_ShortMidiIn(0xC9, 0);
|
|
}
|
|
|
|
junk(port, uSampleRate * 2 / 3);
|
|
}
|
|
}
|
|
|
|
void SCPlayer::junk(uint32_t port, unsigned long count)
|
|
{
|
|
float temp[2][1024];
|
|
unsigned long i, j;
|
|
for (i = 0, j = count / 1024; i < j; ++i)
|
|
{
|
|
memset(temp, 0, sizeof(temp));
|
|
sampler[port].TG_setInterruptThreadIdAtThisTime();
|
|
sampler[port].TG_Process(temp[0], temp[1], 1024);
|
|
}
|
|
count %= 1024;
|
|
if (count)
|
|
{
|
|
memset(temp, 0, sizeof(temp));
|
|
sampler[port].TG_setInterruptThreadIdAtThisTime();
|
|
sampler[port].TG_Process(temp[0], temp[1], (unsigned int) count);
|
|
}
|
|
}
|
|
|
|
void SCPlayer::set_mode(sc_mode m)
|
|
{
|
|
mode = m;
|
|
reset(0);
|
|
reset(1);
|
|
reset(2);
|
|
}
|
|
|
|
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)
|
|
{
|
|
if (!(b & 0x80000000))
|
|
{
|
|
unsigned port = (b >> 24) & 0x7F;
|
|
if ( port > 2 ) port = 2;
|
|
sampler[port].TG_ShortMidiIn(b, 0);
|
|
}
|
|
else
|
|
{
|
|
uint32_t n = b & 0xffffff;
|
|
const uint8_t * data;
|
|
std::size_t size, port;
|
|
mSysexMap.get_entry( n, data, size, port );
|
|
if ( port > 2 ) port = 2;
|
|
send_sysex( (uint32_t) port, data );
|
|
if ( port == 0 )
|
|
{
|
|
send_sysex( 1, data );
|
|
send_sysex( 2, data );
|
|
}
|
|
}
|
|
}
|
|
|
|
void SCPlayer::render(float * out, unsigned long count)
|
|
{
|
|
memset(out, 0, count * sizeof(float) * 2);
|
|
while (count)
|
|
{
|
|
float buffer[2][4096];
|
|
unsigned long todo = count > 4096 ? 4096 : 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(4096);
|
|
}
|
|
|
|
initialized = true;
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
reset(i);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
bool SCPlayer::send_event_needs_time()
|
|
{
|
|
return true;
|
|
}
|