static enum nz_pull_rc sequencer_pull(struct nz_port * port) { struct nz_node * node = port->port_node; nz_obj_p inp_time = NZ_NODE_PULL(node, 0); nz_obj_p inp_stream = NZ_NODE_PULL(node, 1); if (inp_time == NULL || inp_stream == NULL) { return NZ_PULL_RC_NULL; } int t = (int) NZ_CAST(double, inp_time); size_t stream_idx = t % nz_vector_get_size(inp_stream); nz_obj_p* sequence = NZ_CAST(nz_obj_p*, inp_stream); nz_obj_p out = nz_obj_swap(&port->port_value, sequence[stream_idx]); return (out == NULL) ? NZ_PULL_RC_NULL : NZ_PULL_RC_OBJECT; }
int record_and_write(struct nz_node * recorder_node, const char * filename, double time_seconds) { DSL_DECLS; MAKE_LONG_CONSTANT(recorder_len, nz_frame_rate * time_seconds); CONNECT(recorder_node, 1, recorder_len, 0); // Trigger the computation struct nz_obj * sample = nz_port_pull(&recorder_node->node_outputs[0]); printf("Writing %lu frames to %s", nz_vector_get_size(sample), filename); SF_INFO fdata = { .frames = nz_vector_get_size(sample), .samplerate = nz_frame_rate, .channels = 1, .format = SF_FORMAT_WAV | SF_FORMAT_PCM_16, .sections = 1, .seekable = 0, }; SNDFILE * f = sf_open(filename, SFM_WRITE, &fdata); sf_write_double(f, NZ_CAST(double *, sample), nz_vector_get_size(sample)); sf_write_sync(f); sf_close(f); return 0; } int main(void) { DSL_DECLS; // Timebase // (frames / chunk) / (frames / second) * (beats / minute) / (seconds / minute) // = (frames * second * beats * minute) / (chunk * frames * minute * second) // = (beats / chunk) BLOCK(timebase, accumulator); MAKE_DOUBLE_CONSTANT(delta_t, nz_chunk_size / nz_frame_rate * 280 / 60.); CONNECT(_blk, 0, _cons, 0); BLOCK(time_tee, tee, 4); CONNECT(time_tee, 0, _pipe, 0); // Debug BLOCK(debug_time, debug, "time", 0); CONNECT(_blk, 0, _pipe, 0); BLOCK(time_wye, wye, 2); CONNECT(time_wye, 1, _pipe, 0); // Melody // TODO: Come up with a better way of specifying these double unison[] = {None, None, None, 65, 75, None, 72, 67, 67, 68, None, 65, 70, 72, 70, 65, 65, None, None, 65, 75, None, 72, 67, 67, 68, 65, 72, 75, None, 72, 77}; size_t unison_len = sizeof(unison) / sizeof(*unison); struct nz_obj * melody_obj = make_double_vector(unison, unison_len); BLOCK(melody, constant, melody_obj); // Sequencer BLOCK(seq, sequencer); CONNECT(_blk, 0, time_tee, 1); CONNECT(_blk, 1, melody, 0); // Debug BLOCK(debug_seq, debug, "seq", 0); CONNECT(_blk, 0, seq, 0); BLOCK(lpf, lpf); CONNECT(_blk, 0, _pipe, 0); MAKE_CONSTANT(lpf_alpha, nz_double_type, double, 2); CONNECT(_blk, 1, _cons, 0); // Debug BLOCK(debug_lpf, debug, "lpf", 0); CONNECT(_blk, 0, _pipe, 0); // Math BLOCK(n2f, math, NZ_MATH_NOTE_TO_FREQ); CONNECT(_blk, 0, _pipe, 0); // Instrument BLOCK(wave, wave); CONNECT(_blk, 0, _pipe, 0); MAKE_CONSTANT(wtype, nz_long_type, long, NZ_WAVE_SAW); CONNECT(_blk, 1, _cons, 0); // --- Percussion --- // -- Snare -- BLOCK(snare_imp, impulse); BLOCK(snare_lpf, clpf); CONNECT(_blk, 0, snare_imp, 0); MAKE_DOUBLE_CONSTANT(snare_tau, 8.5); CONNECT(_blk, 1, _cons, 0); BLOCK(snare_wav, white); BLOCK(snare_mix, cmixer, 1); CONNECT(_blk, 0, snare_wav, 0); CONNECT(_blk, 1, snare_lpf, 0); BLOCK(snare_rec, recorder); CONNECT(_blk, 0, _pipe, 0); MAKE_LONG_CONSTANT(snare_len, nz_frame_rate * 0.5); CONNECT(_blk, 1, _cons, 0); //nz_record_and_write(snare_rec, "snare.wav", 0.5); // -- Kick -- BLOCK(kick_imp, impulse); MAKE_DOUBLE_CONSTANT(kick_tau, 8.5); BLOCK(kick_lpf, clpf); CONNECT(_blk, 0, kick_imp, 0); CONNECT(_blk, 1, kick_tau, 0); BLOCK(kick_wav, wave); MAKE_DOUBLE_CONSTANT(kick_freq, 80); CONNECT(_blk, 0, _cons, 0); MAKE_LONG_CONSTANT(kick_wtype, NZ_WAVE_SINE); CONNECT(_blk, 1, _cons, 0); BLOCK(kick_mix, cmixer, 1); CONNECT(_blk, 0, kick_wav, 0); CONNECT(_blk, 1, kick_lpf, 0); BLOCK(kick_rec, recorder); CONNECT(_blk, 0, _pipe, 0); MAKE_LONG_CONSTANT(kick_len, nz_frame_rate * 0.5); CONNECT(_blk, 1, _cons, 0); // Kick & Snare sampling long snare_pat[] = {NZ_SAMPLER_COMMAND_PLAY, NZ_SAMPLER_COMMAND_PLAY, NZ_SAMPLER_COMMAND_STOP, NZ_SAMPLER_COMMAND_STOP}; long kick_pat[] = {NZ_SAMPLER_COMMAND_STOP, NZ_SAMPLER_COMMAND_STOP, NZ_SAMPLER_COMMAND_PLAY, NZ_SAMPLER_COMMAND_PLAY}; struct nz_node * snare = make_drum(snare_rec, snare_pat, 4, time_tee, 2); struct nz_node * kick = make_drum(kick_rec, kick_pat, 4, time_tee, 3); // Mixer BLOCK(mixer, mixer, 3); MAKE_DOUBLE_CONSTANT(wave_vol, 0.40); MAKE_DOUBLE_CONSTANT(snare_vol, 0.80); MAKE_DOUBLE_CONSTANT(kick_vol, 2.0); CONNECT(mixer, 0, wave, 0); CONNECT(mixer, 1, wave_vol, 0); CONNECT(mixer, 2, snare, 0); CONNECT(mixer, 3, snare_vol, 0); CONNECT(mixer, 4, kick, 0); CONNECT(mixer, 5, kick_vol, 0); CONNECT(time_wye, 0, mixer, 0); // Debug BLOCK(debug_ch, debug, "ch", 0); CONNECT(_blk, 0, _pipe, 0); // Compressor BLOCK(compressor, compressor); CONNECT(_blk, 0, _pipe, 0); // Recorder BLOCK(recorder, recorder); CONNECT(_blk, 0, _pipe, 0); nz_debug_print_graph(recorder); nz_debug_print_dot(recorder, "unison.dot"); // This triggers everything! record_and_write(recorder, "unison.wav", 10); /* // Soundcard printf("Initing soundcard\n"); struct nz_node * soundcard = soundcard_get(); node_connect(soundcard, 0, time_wye, 0); printf("soundcard inited\n"); debug_print_graph(soundcard); soundcard_run(); node_destroy(delta_t); node_destroy(melody); node_destroy(mixer); node_destroy(n2f); node_destroy(seq); node_destroy(soundcard); node_destroy(time_tee); node_destroy(time_wye); node_destroy(timebase); node_destroy(wave); node_destroy(wave_vol); node_destroy(wtype); printf("Successfully destroyed everything!\n"); */ return 0; }