#include "SC_CoreAudio.h"
#include "SC_HiddenWorld.h"
#include "SC_Prototypes.h"
#include "SC_World.h"
#include "SC_WorldOptions.h"
#include "OSC_Packet.h"
#include "SC_Reply.h"
#include "SC_ReplyImpl.hpp"
#include <cstring>
void PerformOSCBundle(World* inWorld, OSC_Packet* inPacket);
int PerformOSCMessage(World* inWorld, int inSize, char* inData, ReplyAddress* inReply);
static int wasm_print_func(const char*, va_list) { return 0; }
extern "C" {
World* scsynth_wasm_new(WorldOptions* options) {
if (!options)
return nullptr;
SetPrintFunc(wasm_print_func);
options->mRealTime = false;
options->mLoadGraphDefs = false;
World* world = World_New(options);
if (!world)
return nullptr;
World_SetSampleRate(world, (double)options->mPreferredSampleRate);
World_Start(world);
return world;
}
void scsynth_wasm_perform(World* world, const char* data, int size, ReplyFunc reply_func,
void* reply_ctx) {
if (!world || !data || size <= 0)
return;
OSC_Packet packet;
memset(&packet, 0, sizeof(packet));
char* buf = (char*)malloc((size_t)size);
if (!buf)
return;
memcpy(buf, data, (size_t)size);
packet.mData = buf;
packet.mSize = size;
packet.mReplyAddr.mReplyFunc = reply_func;
packet.mReplyAddr.mReplyData = reply_ctx;
packet.mReplyAddr.mProtocol = kUDP;
packet.mReplyAddr.mPort = 0;
packet.mReplyAddr.mSocket = -1;
world->mSampleOffset = 0;
world->mSubsampleOffset = 0.f;
if (data[0] == '#') {
packet.mIsBundle = true;
PerformOSCBundle(world, &packet);
} else {
packet.mIsBundle = false;
PerformOSCMessage(world, size, buf, &packet.mReplyAddr);
world->mLocalErrorNotification = 0;
}
free(buf);
}
void scsynth_wasm_pump(World* world, const float* in, int inCh, float* out, int outCh, int nframes) {
if (!world)
return;
const int bufLength = world->mBufLength;
const int numOutputs = (int)world->mNumOutputs;
const int numInputs = (int)world->mNumInputs;
float* outputBuses = world->mAudioBus;
float* inputBuses = world->mAudioBus + numOutputs * bufLength;
int32* outputTouched = world->mAudioBusTouched;
int32* inputTouched = world->mAudioBusTouched + numOutputs;
for (int frame = 0; frame < nframes; frame += bufLength) {
const int32 bufCounter = world->mBufCounter;
const int block = (nframes - frame < bufLength) ? (nframes - frame) : bufLength;
if (in && inCh > 0) {
for (int ch = 0; ch < numInputs && ch < inCh; ++ch) {
float* dst = inputBuses + ch * bufLength;
for (int k = 0; k < block; ++k)
dst[k] = in[(frame + k) * inCh + ch];
for (int k = block; k < bufLength; ++k)
dst[k] = 0.f;
inputTouched[ch] = bufCounter;
}
}
world->mSampleOffset = 0;
world->mSubsampleOffset = 0.f;
World_Run(world);
if (out && outCh > 0) {
for (int ch = 0; ch < outCh; ++ch) {
const bool touched = ch < numOutputs && outputTouched[ch] == bufCounter;
const float* src = outputBuses + ch * bufLength;
for (int k = 0; k < block; ++k)
out[(frame + k) * outCh + ch] = touched ? src[k] : 0.f;
}
}
world->mBufCounter++;
}
}
}
void null_reply_func(struct ReplyAddress*, char*, int) {}
bool operator==(const ReplyAddress& a, const ReplyAddress& b) {
return a.mProtocol == b.mProtocol && a.mPort == b.mPort && a.mSocket == b.mSocket;
}
bool operator<(const ReplyAddress& a, const ReplyAddress& b) {
if (a.mPort != b.mPort)
return a.mPort < b.mPort;
if (a.mSocket != b.mSocket)
return a.mSocket < b.mSocket;
return a.mProtocol < b.mProtocol;
}
namespace scsynth {
void startAsioThread() {}
void stopAsioThread() {}
bool asioThreadStarted() { return true; }
}
SC_AudioDriver* SC_NewAudioDriver(World*) { return nullptr; }
void initializeScheduler() {}
extern "C" {
int64 oscTimeNow() { return 0; }
int32 server_timeseed() {
static int32 count = 0;
return count++;
}
int World_OpenUDP(World*, const char*, int) { return 1; }
int World_OpenTCP(World*, const char*, int, int, int) { return 1; }
}