-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
206 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
#!/usr/bin/env node | ||
|
||
// node ks2c_osx.js > test.c && clang -Os -fomit-frame-pointer -ffunction-sections -fdata-sections -g0 -flto -framework AudioToolbox test.c -o test && strip test && ./test | ||
// pnpm ks2c | ||
|
||
import fs from "node:fs/promises"; | ||
import * as core from "@kabelsalat/core/src/index.js"; | ||
import * as lib from "@kabelsalat/lib/src/index.js"; | ||
// for some reason, node will always take main and not module file... | ||
import { existsSync } from "node:fs"; | ||
import path from "node:path"; | ||
|
||
async function main() { | ||
let file = process.argv[2]; | ||
if (!file) { | ||
// file = "kabelsalat.js"; | ||
file = "./kabelsalat.js"; | ||
console.log(`no input file given -> using "${file}"`); | ||
} | ||
const fileExists = existsSync(file); | ||
|
||
if (!fileExists) { | ||
console.log(`file "${file}" not found.`); | ||
return; | ||
} | ||
|
||
const filePath = path.resolve(process.cwd(), file); | ||
const ksCode = await fs.readFile(filePath, { encoding: "utf8" }); | ||
const ugenPath = path.resolve(process.cwd(), "./clib/ugens.c"); | ||
const ugenCode = await fs.readFile(ugenPath, { encoding: "utf8" }); | ||
Object.assign(globalThis, core); | ||
Object.assign(globalThis, lib); | ||
const node = core.evaluate(ksCode); | ||
const unit = node.compile({ lang: "c" }); | ||
const cCode = ks2c(unit, ugenCode); | ||
const outFileName = file + "-osx.c"; | ||
// console.log(cCode); | ||
try { | ||
await fs.writeFile(outFileName, cCode); | ||
|
||
console.log(`written ${outFileName}`); | ||
} catch (err) { | ||
console.log(`error writing ${outFileName}: ${err.message}`); | ||
} | ||
} | ||
|
||
main(); | ||
|
||
let ks2c = ( | ||
unit, | ||
ugens | ||
) => `// this file has been compiled from kabelsalat, using ks2c-osx | ||
// this file uses AudioToolbox, which will only work on OSX. | ||
// compile and run with: | ||
// clang -Os -framework AudioToolbox -flto -o kabelsalat-osx kabelsalat.js-osx.c && ./kabelsalat-osx | ||
// this will spit out a self-contained ~34kB binary that plays the compiled kabelsalat patch | ||
#include <AudioToolbox/AudioToolbox.h> | ||
${ugens} | ||
#define SAMPLE_RATE 44100 | ||
#define CHANNELS 2 | ||
#define SAMPLE_TIME (1.0 / SAMPLE_RATE) | ||
typedef struct | ||
{ | ||
double time; | ||
float* r; | ||
float* o; | ||
int osize; | ||
float* s; | ||
void** nodes; // wtf am i doing | ||
} CallbackEnv; | ||
static OSStatus DSPCallback( | ||
void *inRefCon, | ||
AudioUnitRenderActionFlags *ioActionFlags, | ||
const AudioTimeStamp *inTimeStamp, | ||
UInt32 inBusNumber, | ||
UInt32 inNumberFrames, | ||
AudioBufferList *ioData) | ||
{ | ||
CallbackEnv *env = (CallbackEnv *)inRefCon; | ||
float *buffer = (float *)ioData->mBuffers[0].mData; | ||
void **nodes = env->nodes; | ||
float *o = env->o; | ||
float *r = env->r; | ||
float *s = env->s; | ||
for (UInt32 j = 0; j < inNumberFrames; j++) | ||
{ | ||
double time = env->time; | ||
// start of autogenerated callback | ||
memset(o, 0, env->osize); // reset outputs | ||
${unit.src} | ||
// end of autogenerated callback | ||
float left = o[0]; | ||
float right = o[1]; | ||
buffer[j*CHANNELS] = left*0.3; | ||
buffer[j*CHANNELS+1] = right*0.3; | ||
env->time += SAMPLE_TIME; | ||
} | ||
return noErr; | ||
} | ||
void SetupAudioUnit(CallbackEnv *env) | ||
{ | ||
AudioComponentDescription desc = {0}; | ||
desc.componentType = kAudioUnitType_Output; | ||
desc.componentSubType = kAudioUnitSubType_DefaultOutput; | ||
desc.componentManufacturer = kAudioUnitManufacturer_Apple; | ||
AudioComponent outputComponent = AudioComponentFindNext(NULL, &desc); | ||
AudioUnit audioUnit; | ||
AudioComponentInstanceNew(outputComponent, &audioUnit); | ||
AURenderCallbackStruct callbackStruct; | ||
callbackStruct.inputProc = DSPCallback; | ||
callbackStruct.inputProcRefCon = env; | ||
AudioUnitSetProperty(audioUnit, | ||
kAudioUnitProperty_SetRenderCallback, | ||
kAudioUnitScope_Input, | ||
0, | ||
&callbackStruct, | ||
sizeof(callbackStruct)); | ||
AudioStreamBasicDescription streamFormat; | ||
streamFormat.mSampleRate = SAMPLE_RATE; | ||
streamFormat.mFormatID = kAudioFormatLinearPCM; | ||
streamFormat.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked; | ||
streamFormat.mFramesPerPacket = 1; | ||
streamFormat.mChannelsPerFrame = CHANNELS; | ||
streamFormat.mBitsPerChannel = sizeof(float) * 8; | ||
streamFormat.mBytesPerPacket = sizeof(float) * CHANNELS; | ||
streamFormat.mBytesPerFrame = sizeof(float) * CHANNELS; | ||
AudioUnitSetProperty(audioUnit, | ||
kAudioUnitProperty_StreamFormat, | ||
kAudioUnitScope_Input, | ||
0, | ||
&streamFormat, | ||
sizeof(streamFormat)); | ||
AudioUnitInitialize(audioUnit); | ||
AudioOutputUnitStart(audioUnit); | ||
} | ||
int main() | ||
{ | ||
float o[16] = {0}; // output registry | ||
float s[16] = {0}; // source registry | ||
// start of autogenerated init | ||
float r[${unit.registers}] = {0}; // node registry | ||
void *nodes[${unit.ugens.length}]; | ||
${unit.ugens | ||
.map((ugen, i) => `nodes[${i}] = ${ugen.type}_create();`) | ||
.join("\n")} | ||
// end of autogenerated init | ||
CallbackEnv env; | ||
env.nodes = nodes; | ||
env.r = (float *)r; | ||
env.o = (float *)o; | ||
env.osize = sizeof(o); | ||
env.s = (float *)s; | ||
SetupAudioUnit(&env); | ||
while(true) {} | ||
return 0; | ||
} | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters