Skip to content

Instantly share code, notes, and snippets.

@rndmcnlly
Created July 29, 2012 02:40
Show Gist options
  • Save rndmcnlly/3195822 to your computer and use it in GitHub Desktop.
Save rndmcnlly/3195822 to your computer and use it in GitHub Desktop.
renderscript audio synthesis demo
// this is a snippet from RSAudioDemoActivity#run
Process.setThreadPriority(Process.THREAD_PRIORITY_AUDIO);
short samples[] = new short[BLOCK_FRAMES];
Allocation out = Allocation.createSized(renderScript, Element.I16(renderScript), samples.length, Allocation.USAGE_SCRIPT);
synth.set_block_frames(BLOCK_FRAMES);
synth.bind_block_out(out);
synth.set_sample_rate(SAMPLE_RATE_IN_HZ);
while(!Thread.interrupted()) {
synth.set_energy(energy);
synth.invoke_render_block();
out.copyTo(samples);
audioTrack.write(samples, 0, samples.length);
}
// rename this file to "synth.rs"; it has a C extension just for the gist
#pragma version(1)
#pragma rs java_package_name(as.adamsmith.renderscriptaudiodemo)
#define SHORT_MAX 0x7FFF
#define WT_SAMPLES 1024
#define WT_INDEX_MASK 0x3FF
// parameters controlled from dalvik side
int sample_rate;
int block_frames;
short* block_out;
float energy;
static short encode_as_short(float f) { return (short)(SHORT_MAX * f); }
static float hard_clip(float x) { return clamp(x, -1.0f, +1.0f); }
static float continuous_mtof(float pitch) { return (440.0f * pow(2, (pitch-69.0f)/12.0f)); }
// PHASOR
typedef struct phasor_state {
float phase;
float phase_increment;
} phasor_t;
static void phasor_set_freq(phasor_t* phasor, float freq) {
phasor->phase_increment = freq / sample_rate;
}
static float phasor_tick(phasor_t* phasor) {
float s = phasor->phase;
phasor->phase = fmod(phasor->phase + phasor->phase_increment, 1.0f);
return s;
}
// WAVETABLE
static float wt_sample(const float wt[], float point) {
float index = point * WT_SAMPLES;
int i = (int)index;
float alpha = index - i;
return (alpha * wt[i&WT_INDEX_MASK] + (1.0f - alpha) * wt[(i+1)&WT_INDEX_MASK]);
}
// SINGLE CYCLE WAVE DATA
static float wt_noise[WT_SAMPLES];
static float wt_saw[WT_SAMPLES];
static float wt_sine[WT_SAMPLES];
static float wt_triangle[WT_SAMPLES];
static float wt_square[WT_SAMPLES];
// MIDI PITCH TABLE
static float mtof[128];
void init() {
for(int i = 0; i < WT_SAMPLES; i++) {
float t = (float)i / WT_SAMPLES;
wt_noise[i] = rsRand(-1.0f,1.0f);
}
for(int i = 0; i < WT_SAMPLES; i++) {
float t = (float)i / WT_SAMPLES;
wt_saw[i] = 1 - 2 * t;
}
for(int i = 0; i < WT_SAMPLES; i++) {
float t = (float)i / WT_SAMPLES;
wt_sine[i] = sin(2 * M_PI * t);
}
for(int i = 0; i < WT_SAMPLES; i++) {
float t = (float)i / WT_SAMPLES;
wt_triangle[i] = (t < 0.5) ? (4*t-1) : (3-4*t);
}
for(int i = 0; i < WT_SAMPLES; i++) {
wt_square[i] = (i < WT_SAMPLES / 2) ? +1 : -1;
}
for(int i = 0; i < 128; i++) {
mtof[i] = continuous_mtof(i);
}
}
static phasor_t kick_osc;
static phasor_t lead_osc1, lead_osc2;
static phasor_t bass_osc1, bass_osc2, bass_osc3;
static phasor_t ambience;
static phasor_t metro;
void render_block() {
float tempo_scale = (energy < 0.75) ? 1 : (4-4*energy);
phasor_set_freq(&metro, 0.5f*tempo_scale);
phasor_set_freq(&ambience, 0.1f*tempo_scale);
for(int index = 0; index < block_frames; index++) {
// accumulator
float r = 0.0f;
// time within phrase
float t = phasor_tick(&metro);
float amb = phasor_tick(&ambience);
// kick drum
if(energy > 0.125 && rsFrac(4*t) < 0.5) {
float kick_v = 1.0f - rsFrac(8*t);
phasor_set_freq(&kick_osc, 100.0 * kick_v * kick_v * kick_v);
r += 1.25f * kick_v * wt_sample(wt_triangle, phasor_tick(&kick_osc));
}
// closed hat
if(energy > 0.5) {
float hat_v = 1.0f - rsFrac(16*t);
r += 0.25f * rsRand(-1.0f,1.0f) * hat_v * hat_v * hat_v * hat_v;
}
// open hat
if((rsFrac(2*t) > 0.5 && rsFrac(4*t) < 0.5)) {
r += rsRand(-1.0f,1.0f) * 0.75f * (1-rsFrac(4*t)) * (1-rsFrac(8*t));
}
// bass
if(rsFrac(4*t) > 0.5) {
float bass_freq = t < 0.5 ? mtof[45] : mtof[53];
phasor_set_freq(&bass_osc1, 1.000*bass_freq/1);
phasor_set_freq(&bass_osc2, 1.005*bass_freq/2);
phasor_set_freq(&bass_osc3, 1.007*bass_freq/4);
float b = 0;
if(energy > 0.66f) {
b += 0.5f * wt_sample(wt_saw, phasor_tick(&bass_osc1)) * 0.5f;
}
b += 0.75f * wt_sample(wt_triangle, phasor_tick(&bass_osc2)) * 0.5f;
b += 0.5f * wt_sample(wt_saw, phasor_tick(&bass_osc3)) * 0.5f;
r += sin(b)*1.2f;
}
// ambient crackles
float crust = wt_sample(wt_noise,rsFrac(16*amb))-wt_sample(wt_noise,rsFrac(16*amb-0.01*(1-amb)));
r += 0.1f * pow(amb,2) * crust;
// lead
if(energy > 0.25) {
if(rsFrac(16*t) < 0.5) {
float k_lead = energy < 0.5 ? 1 : (t < 0.75 ? 2.0f : 4.0f);
float lead_freq = rsFrac(4*t) < 0.5 ? mtof[57] : mtof[64];
phasor_set_freq(&lead_osc1, k_lead*lead_freq);
phasor_set_freq(&lead_osc2, 2.004*k_lead*lead_freq);
r += rsFrac(4*t) * wt_sample(wt_saw, phasor_tick(&lead_osc1)) * 0.25f;
r += rsFrac(4*t) * wt_sample(wt_saw, phasor_tick(&lead_osc2)) * 0.25f;
}
}
// distortion
if(energy > 0.75) {
float alpha = 4*energy-3;
r = (1-alpha) * r + alpha * sin(r*alpha*10);
}
block_out[index] = encode_as_short(hard_clip( r ));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment