Created
July 29, 2012 02:40
-
-
Save rndmcnlly/3195822 to your computer and use it in GitHub Desktop.
renderscript audio synthesis demo
This file contains hidden or 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 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); | |
} |
This file contains hidden or 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
// 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