/* linux: gcc -lm -o render_notes_5 render_notes_5.c if you are feeling rather saucy you can optimize for your CPU ... gcc -march=athlon-xp -lm -o render_notes_5 render_notes_5.c */ /* render_note_5.c by Peter C. Capasso version 5 adds resonance to the low pass filter resonant low pass filter code is combined from two filters by two other people: 1) Tom St. Denis (to "run" the biquad coefficients) 2) Daniel Werner (daniel_jacob_werner [AT] yaho [DOT] com [DOT] au) (to calculate the biquad coeffcicients) The rest of the program (in other words, everything but the low pass filter) is written from scratch by me. Toms filter had no resonance. Daniels filter had resonace but crashed when running the biquad on the sound. The combination runs with resonance and without crashing. */ /* * Simple implementation of Biquad filters -- Tom St Denis * * Based on the work Cookbook formulae for audio EQ biquad filter coefficients --------------------------------------------------------- by Robert Bristow-Johnson, pbjrbj@viconet.com a.k.a. robert@audioheads.com * Available on the web at http://www.smartelectronix.com/musicdsp/text/filters005.txt * Enjoy. * * This work is hereby placed in the public domain for all purposes, whether * commercial, free [as in speech] or educational, etc. Use the code and * please give me credit if you wish. * * Tom St Denis -- http://tomstdenis.home.dhs.org */ /* Cool Sounding Lowpass With Decibel Measured Resonance Type : LP 2-pole resonant tweaked butterworthReferences : Posted by daniel_jacob_werner [AT] yaho [DOT] com [DOT] au Notes : This algorithm is a modified version of the tweaked butterworth lowpass filter by Patrice Tarrabia posted on musicdsp.org's archives. It calculates the coefficients for a second order IIR filter. The resonance is specified in decibels above the DC gain. It can be made suitable to use as a SoundFont 2.0 filter by scaling the output so the overall gain matches the specification (i.e. if resonance is 6dB then you should scale the output by -3dB). Note that you can replace the sqrt(2) values in the standard butterworth highpass algorithm with my "q =" line of code to get a highpass also. How it works: normally q is the constant sqrt(2), and this value controls resonance. At sqrt(2) resonance is 0dB, smaller values increase resonance. By multiplying sqrt(2) by a power ratio we can specify the resonant gain at the cutoff frequency. The resonance power ratio is calculated with a standard formula to convert between decibels and power ratios (the pow statement...). */ /* note on the above, Dan's email bounced so I'm assuming he's released his filter code as public domain as Tom St Denis did */ /* note: render_notes_5 is coded to work on an intel (little endian) byte order cpu, which is ironic because the ".sample" format is motorola byte order */ /* includes */ #include "hacks.h" #ifndef MAX_STR #define MAX_STR 256 #endif #include #ifndef PI #define PI 3.141592654 #endif #ifndef pi #define pi PI #endif #define max_in_files 12 #define i_min(x,y) (x > y) ? y : x #define i_max(x,y) (x > y) ? x : y #define kludge_env_out ((use_volume_envelope > 0) ? env_out : 1.0) #define render_one_sample (delay(sample_env_out * play_sample() + \ (kludge_env_out * hfilter(filter(osc()))))) /* data type definitions */ struct event_node { /* one line (one note) of a macro */ struct event_node *next; short event_type; /* 0 for rest, 1 for note, 2 for call */ double note_pitch; /* ignored if a rest, used for call */ double just_note_length; /* ignored if a rest */ double total_note_length; }; struct macro_node { /* macro (riff) */ struct macro_node *next; int macro_number; /* like a variable name, but easier to do */ struct event_node *events; /* note data */ }; struct line_node { /* one line (one note) of a macro */ struct line_node *next; double level1; /* level (-1.0 - 1.0) of start of line */ double level2; /* level (-1.0 - 1.0) of end of line */ double local_duration; /* duration (relative to other lines) of this line */ }; struct eg_list_node { /* for modulation list (like Electribe motion sequence) */ struct eg_list_node *next; unsigned int eg_duration; /* in samples */ double eg_end; /* -1.0 to 1.0 range */ }; struct biquad { /* for resonant lowpass filter */ double a0, a1, a2, a3, a4; double x1, x2, y1, y2; }; /* defines */ #define delay_buffer_size 11051 #define PI_2 (PI * 2) #define NO_SUCH_WAVE 0 #define SINE_WAVE 1 #define SAWTOOTH_WAVE 2 #define SQUARE_WAVE 3 #define TRIANGLE_WAVE 4 #define CIRCLE_WAVE 5 #define LINE_WAVE 6 #define MAX_MACRO_RECURSIONS 24 #define max_sample_size 88200 #define sample_buffer_size max_sample_size #define waveform_size 2048 #define unison_detune_scale 0.03 #define mod_subsample_rate 30 #define portmento_START1 0 #define portmento_START2 1 #define portmento_COUNTING 2 #define portmento_COASTING 3 #define portmento_OFF 4 /* globals */ double *wave; /* waveform buffer (1 wavelength in length) */ double *sub_wave; /* sub osc waveform buffer */ double root12; /* 12th root of 2 */ double timing_error = 0.0; double env_sustain_level = 0.5; double sub_osc_phase = 0; double osc_phase = 0; /* 0.0 to 1.0 */ double osc_pitch; /* in Hz */ double phase_distort = 0.0; /* 0.0 to 1.0 */ double sub_osc_level = 0.0; double sub_osc_level_work = 0.0; double unison_up_osc_phase = 0; double unison_upmore_osc_phase = 0; double unison_down_osc_phase = 0; double unison_downmore_osc_phase = 0; double unison_mix = 0; double unison_spread = 0; double unison_mix_main = 1; double unison_mix_up = 0; double unison_mix_upmore = 0; double unison_mix_down = 0; double unison_mix_downmore = 0; double unison_detune_upmore = 1.0; double unison_detune_up = 1.0; /* 1 / unison_detune_down */ double unison_detune_down = 1.0; /* 1 / unison_detune_up */ double unison_detune_downmore = 1.0; double pseudo_sync = 1.0; double pseudo_sync_w = 1.0; int smod_subsample_counter = 0; double env_to_phase_distort = 0.0; double env_out; /* current envelope CV */ double env_memory; double noise_level = 0.0; double noise_non_level = 1.0; double ext_in_volume = 0.0; double ext_in_non_volume = 1.0; FILE *ext_in_file = NULL; #define ENV_off 0 #define ENV_attack 1 #define ENV_punch 2 #define ENV_decay 3 #define ENV_sustain 4 #define ENV_release 5 #define env_curve_table_size 44100 int env_state = ENV_off; /* mode that env is currently in */ int env_attack_time = 15; /* in samples */ int env_punch_time = 0; /* in samples */ int env_decay_time = 4410; /* in samples */ int env_release_time = 1323; /* in samples */ int env_x = 0; /* counter used within each env stage */ int note_on_off = 0; double env_release_start_level; double env_attack_start_level; double env_memory = 0.0; /* previous env output level */ double *env_curve_table = NULL; int which_wave = SAWTOOTH_WAVE; double filter_cutoff = 0.5; double env_to_filter_cutoff = 0.5; double filter_feedback = 0.0; double filter_save_1 = 0; double filter_save_2 = 0; double filter_eg_hold = 0.0; double filter_volume = 1.0; double hp_cut, hp_cut_i; double hp_mem = 0.0; double hp_mem2 = 0.0; double lfo_phase = 0; /* 0.0 to 1.0 */ double lfo_pitch = 12.31; /* in Hz */ double lfo_to_pitch = 0.0; double lfo_to_phase_distort = 0.0; double lfo_to_pseudo_sync = 0.0; double delay_time = 0.031; /* in seconds (0.0 to 0.25) */ double delay_regen = 0.1; /* 0.0 to 1.0 (1.0 is ridiculous) */ double delay_level = 0.1; /* 0.0 to 1.0 (0.5 for 50-50 dry-wet mix) */ double *delay_buffer = NULL; int delay_index = 0; double note_total_len; /* in quarter notes */ double just_note_len; /* in quarter notes */ double q_note_len; /* in seconds */ struct macro_node *macros = (struct macro_node *) NULL; struct line_node *lines = (struct line_node *) NULL; double fine_tune; int transpose; double samples_per_sample = 1; /* current playback rate of sample */ double sample_in_pitch = 440; /* pitch of the input sample */ double sample_release_time = 0.03; /* in seconds */ double sample_volume = 0; /* 0 to 1, also controls inverse analog volume */ double sample_normal = 1; /* used to make quiet samples maximum volume */ double sample_env_out = 0; /* output of sample envelope generator */ double sample_play_location = 0; double volume = 0.9; WORD *sample_buffer = (WORD *) NULL; double harmonic[24]; int filter_subsample_counter = mod_subsample_rate; struct biquad bq; /* for resonant lowpass filter */ int use_volume_envelope; /* for mod1 modulation (motion sequence) */ double eg_var1 = 0.0; /* current "instantaneous" eg output */ struct eg_list_node *eg_var1_list = (struct eg_list_node *) NULL; struct eg_list_node *eg_var1_ptr = (struct eg_list_node *) NULL; unsigned int eg_var1_x = 0; /* how far along the current segment */ unsigned int eg_var1_duration = 0; /* length in samples of segment */ double eg_var1_start = 0.0; /* start eg level for segment */ double eg_var1_end = 0.0; /* ending eg level for segment */ double eg_var1_to_filter_cutoff = 0.0; /* updown */ int use_updown = 0; int updown_count = 5; int updown_count_length = 5; int updown_phase = 0; /* portmento */ int portmento_state = portmento_START1; int portmento_time = 0; int portmento_x = 0; double from_pitch; double too_pitch; /* forward references */ double fixed_fabs(double); void convert_in(WORD *, int); int read_line(FILE *, char *); void trim_spaces(char *); double f_max(double, double); double f_min(double, double); int read_sample(double *, FILE *); int write_sample(double, FILE *); void add_eg_var1_node(unsigned int, double); /*void envelope(int);*/ void render_env_curve(); double curve_env(int); void envelope_sample(int); double play_sample(); double play_ext_in(); void add_harmonic(int, double); /* add one harmonic to existing wave buffer,- no clip, no normalize */ void wave_normalize(); void dave_smith_alias(); void chip_mod_alias(); void render_add_24(); void render_wave(); void render_sub_wave(); double osc(); double lfo_triangle_wave(double); double lfo(); void calc_biquad(struct biquad *, double, double); double filter_sample(double, struct biquad *); double hfilter(double); int parse_double(char *, double *); int parse_int(char *, int *); int write_a_note(double, double, double, FILE *); int write_a_rest(double, FILE *); int parse_macro_note(char *, struct event_node *); int parse_macro_rest(char *, struct event_node *); int parse_macro_call(char *, struct event_node *); int parse_call(char *, FILE *); int write_macro(struct event_node *, FILE *, int); int parse_macro(FILE *, char *); int parse_sample(char *); int parse_sample_note(char *); int parse_ext_in(char *); void render_bit_wave(char *); void clear_line_nodes(); void add_line_node(double, double, double); void render_line_wave(); void update_osc_levels(); /* start of actual program */ double fixed_fabs(dd) double dd; { /* the library function fabs returns incorrect results */ if (dd < 0.0) return((0.0 - dd)); return(dd); } void convert_from(b, len) /* convert unsigned motorola to signed intel */ /* note: coded to work on an intel byte order cpu, which is ironic becuase the ".sample" foramt is motorola byte order */ WORD *b; int len; { unsigned char *p1, *p2; unsigned char hold; p1 = (unsigned char *) b; p2 = (unsigned char *) b; p2++; while (len--) { hold = *p1 ^ 0x80; /* copy high byte, flip sign bit */ *(p1++) = *p2; /* copy low byte */ *(p2++) = hold; /* finish copying high byte */ p1++; p2++; } p1 = (unsigned char *) NULL; p2 = (unsigned char *) NULL; } /* read_line will read a complete line of text, all the way to the end of line. if the line is longer than the string, the string will be truncated but the file pointer will be pointing to the first character of the next line */ int read_line(f, s) FILE *f; char *s; { int i,c; for (i = 0;; i++) { if ((c = fgetc(f)) eq EOF) break; if (c eq '\n') break; if ((i - 1) < MAX_STR) { s[i] = c; } } if (i >= MAX_STR) i = (MAX_STR - 1); s[i] = 0; if ((c eq EOF) && (i eq 0)) return(c); return(OKDOKEY); } void trim_spaces(s) char *s; { int lead_s, end_s, s_len; s_len = strlen(s); if (s_len) { /* first deal with leading space */ lead_s = 0; while (lead_s < s_len) { if (s[lead_s] > ' ') break; lead_s++; } memmove(s, &s[lead_s], 1 + (s_len - lead_s)); /* will copy null if zero len str */ /* leading space gone. now see to the trailing spaces */ end_s = strlen(s); if (end_s > 0) { while (end_s-- > 0) { if (s[end_s] > ' ') { /* if non space .... */ s[++end_s] = '\0'; /* put null on end of string */ end_s = 0; /* signal for end of loop */ } } } } /* end if s_len */ } void force_lowercase(s) char *s; { int x; char *ptr; x = strlen(s); while (x--) { *(ptr++) = tolower(*ptr); } ptr = (char *) NULL; } double f_max(f1, f2) double f1; double f2; { if (f1 > f2) return(f1); return(f2); } double f_min(f1, f2) double f1; double f2; { if (f1 < f2) return(f1); return(f2); } /* read sample from file into the double float */ int read_sample(s, f) double *s; FILE *f; { int status; UWORD temp; unsigned char *temp2; unsigned char hold; if ((status = fread((char *) &temp, 2, 1, f)) neq 1) return(EOF); temp2 = (unsigned char *) &temp; /* flip hi and low bytes (.sample is motorola byte order and we are intel byte order) */ hold = temp2[0]; temp2[0] = temp2[1]; temp2[1] = hold; if (temp eq 0) temp = 1; *s = ((double) temp - 32768.0) / 32767.0; /* -1.0 - 1.0 range */ return(OKDOKEY); } int write_sample(s, f) /* write as unsigned motorola word */ double s; FILE *f; { unsigned char *temp; unsigned char hold; long temp2; int status; temp2 = (s * volume * 32767); /* get sample value, convert to int range */ temp2 = i_max(temp2, -32767); /* clip */ temp2 = i_min(temp2, 32767); /* clip */ temp2 += 32768; /* shift it up to positive raw */ temp = (unsigned char *) &temp2; hold = temp[0]; /* get intel low byte */ temp[0] = temp[1]; temp[1] = hold; if ((status = fwrite((char *) &temp[0], 2, 1, f)) neq 1) { return(DOOPS); } return(OKDOKEY); } void add_eg_var1_node(d, e) unsigned int d; /* 1 or greater */ double e; /* end value (-1.0 to 1.0) */ { struct eg_list_node *temp; struct eg_list_node *ptr; /* allocate new node */ temp = malloc(sizeof(struct eg_list_node)); if (temp eq NULL) { fprintf(stderr, "add_var1_eg_node: memory failure\n"); exit(DOOPS); } /* fill it */ temp->next = NULL; if (d eq 0) d = 1; /* kludge */ temp->eg_duration = d; temp->eg_end = e; /* stick it on end of list */ if (eg_var1_list eq (struct eg_list_node *) NULL) { eg_var1_list = temp; /* adding to empty list - easy */ } else { /* adding to non-empty list */ ptr = eg_var1_list; while (ptr->next neq (struct eg_list_node *) NULL) { ptr = ptr->next; } ptr->next = temp; ptr = (struct eg_list_node *) NULL; } temp = (struct eg_list_node *) NULL; } void clear_eg_var1_list() { struct eg_list_node *ptr; /* note that this can be called even when the list is still being "played" without causing problems. The eg will just stay at the level it was last set to by the last iteration in the list */ while (eg_var1_list neq (struct eg_list_node *) NULL) { ptr = eg_var1_list; eg_var1_list = eg_var1_list->next; free(ptr); } } void retrigger_eg_var1() { eg_var1_ptr = eg_var1_list; /* point to beginning of list */ eg_var1_x = 0; /* this will trigger the setup stuff next time do_eg_var1() is called */ /* if the list is empty, triggering will have no effect - this is good */ } void do_eg_var1() { /* call this every sample */ double temp; /* if eg_var1_ptr is null, then we're done or not yet started (or between) eg sections. Leave eg_var1 at whatever level it is at right now and don't do anything */ if (eg_var1_ptr neq NULL) { /* if actually in a list */ if (eg_var1_x eq 0) { /* at start of a new segment - do some setup*/ eg_var1_start = eg_var1; /* start level is current/last eg value */ eg_var1_end = eg_var1_ptr->eg_end; eg_var1_duration = eg_var1_ptr->eg_duration; /* hope the duration is never zero - kludge around that in the parser area(s) */ /* eg_var1_x is already set to zero so we are done here */ } temp = (double) eg_var1_x / (double) eg_var1_duration; eg_var1 = (eg_var1_start * (1.0 - temp)) + (eg_var1_end * temp); /* simple crossfade */ /* advance x and ... did we finish this node? */ if (++eg_var1_x >= eg_var1_duration) { /* we just finished this segment/node !! */ eg_var1 = eg_var1_start = eg_var1_end; eg_var1_ptr = eg_var1_ptr->next; /* move to next node or to NULL */ eg_var1_x = 0; /* signal that we are on a new node */ } } /* end if actually in a list */ } void render_env_curve() { int x; double temp; for (x = 0; x < env_curve_table_size; x++) { temp = (double) x / (double) env_curve_table_size; temp = sqrt(temp); env_curve_table[x] = temp; } } double curve_env(note_on_off) int note_on_off; { double temp, rc; /* 1 - note on. -1 - note off. 0 - do whatever you were doing before */ if (note_on_off eq 1) { /* note on */ env_x = 0; env_state = ENV_attack; /* env part done. Now do portmento part. It is unintuitive to do that here but it's easy/best done here */ switch (portmento_state) { case portmento_OFF : /* do nothing */ break; case portmento_START1 : /* This is the first note on of the run so there is no previous pitch for the portmento to slide from, so skip portmento for this note */ portmento_state = portmento_START2; break; case portmento_COUNTING : /* portmento was still sliding up xor down from the last note, so take current slide pitch as the starting point for a new slide */ from_pitch = (from_pitch * ((double) portmento_time - (double) portmento_x)) + (osc_pitch * (double) portmento_x); from_pitch = from_pitch / (double) portmento_time; portmento_x = 0; break; default : portmento_x = 0; /* old_pitch should already be set by a bit of portmento code in the osc() subroutine */ portmento_state = portmento_COUNTING; /* start of a new slide */ } /* end switch on portmento state */ } if (note_on_off eq -1) { /* note off */ env_x = 0; env_state = ENV_release; } env_here: switch (env_state) { case ENV_attack : /* attack */ if (env_x eq 0) { /* what if we still have a "ringing" note from before? we'll want to start from env_memory rather than from zero */ env_attack_start_level = env_memory; } if (env_x < env_attack_time) { /* still in attack stage */ temp = ((double) env_x / (double) env_attack_time); temp *= (1.0 - env_attack_start_level); temp += env_attack_start_level; temp *= (double) env_curve_table_size; rc = env_memory = env_curve_table[ (int) temp ]; env_x++; } else { /* finished attack stage */ env_x = 0; env_state = ENV_punch; env_memory = 1.0; goto env_here; } break; case ENV_punch : if (env_x < env_punch_time) { /* not done with punch stage */ rc = env_memory; env_x++; } else { /* done with punch stage */ env_state = ENV_decay; env_x = 0; goto env_here; } break; case ENV_decay : if (env_x < env_decay_time) { /* still in decay stage */ temp = ((double) env_x / (double) env_decay_time) * (double) env_curve_table_size; rc = env_memory = 1.0 - ((1.0 - env_sustain_level) * env_curve_table[ (int) temp ]); env_x++; } else { /* finished decay stage */ env_x = 0; env_state = ENV_sustain; goto env_here; } break; case ENV_sustain : rc = env_memory = env_sustain_level; break; case ENV_release : if (env_x eq 0) { env_release_start_level = env_memory; } if (env_x < env_release_time) { /* still in release stage */ temp = ((double) env_x / (double) env_release_time) * (double) env_curve_table_size; rc = env_memory = env_release_start_level * (1.0 - env_curve_table[ (int) temp ]); env_x++; } else { /* finished release stage */ env_x = 0; env_state = ENV_off; rc = env_memory = 0.0; } break; default : /* off */ rc = 0.0; } /* end switch */ return(rc * (1.0 - sample_volume)); } void envelope_sample(state) int state; /* 1 - note on, -1, note off, 0 anything else */ { static int mode = 0; /* 0 - silent, 3 sustaining, 4 releasing */ static double time_since = 0; /* time since note on or note off, in secs */ static double current_level = 0; static double sink_rate = 0; /* amount to lower volume each sample */ double rc; switch (state) { case 1 : /* note on */ mode = 3; time_since = 0; rc = current_level = sample_volume; /* rewind sample */ sample_play_location = 0; break; case -1 : /* note off */ mode = 4; /* releasing */ time_since = 0; if (env_release_time < 0.00001) sink_rate = 0.5; else sink_rate = current_level / (44100 * sample_release_time); rc = current_level; current_level = f_max(0.0, (current_level - sink_rate)); break; default : /* do whatever you did last sample */ switch (mode) { case 3 : /* sustaining (after note on, before note off) */ rc = current_level; break; case 4 : /* releasing (after note off, before silence) */ rc = current_level; current_level = f_max(0.0, (current_level - sink_rate)); time_since += (1.0 / 44100); if (current_level < 0.00001) { /* done with release */ mode = 0; /* silence between notes */ } break; default : /* between notes (silence) */ rc = 0.0; } /* end switch on mode */ break; } /* end switch on state */ sample_env_out = rc; } double play_sample() { long temp_index; double rc; if (sample_play_location < max_sample_size) { temp_index = sample_play_location; rc = ((double) sample_buffer[temp_index] / 32767.0) * sample_normal; sample_play_location += samples_per_sample; return(rc); } return(0); } double play_ext_in() { double temp; int status; if (NULL eq ext_in_file) { /* no ext in */ return(0.0); } /* yes ext in */ status = read_sample(&temp, ext_in_file); if (status eq EOF) { /* end of ext_in file */ fclose(ext_in_file); ext_in_file = NULL; status = 0.0; } return(temp); } double phase_dist(phase_in) double phase_in; { /* phase in must be in 0.0 to 1.0 range */ return( pow( phase_in, 1 + 10 * f_max( 0.0, ( phase_distort + (lfo_to_phase_distort * lfo_triangle_wave(lfo_phase)) + (2 * env_to_phase_distort * (-0.5 + env_out)) ) ) ) ); } double lfo_triangle_wave(phase_in) double phase_in; { double temp; temp = fmod(phase_in + 0.75, 1.0); /* phase in is 0.0 to 1.0 */ if (temp < 0.5) { temp = (temp * 4.0) - 1.0; /* rise from -1.0 to 1.0 */ } else { /* >= 0.5 */ temp = 1.0 - (4.0 * (temp - 0.5)); /* fall from 1.0 to -1.0 */ } return(temp); } double lfo() { double rc; rc = lfo_triangle_wave(lfo_phase); lfo_phase = fmod( (lfo_phase + (lfo_pitch / 44100.0)), 1.0 ); return(rc); } void add_harmonic(h, v) /* add one harmonic to existing wave buffer - no clip, no normalize */ int h; /* integer 1-24 only */ double v; /* 0.0 to 1.0 only */ { int x; for (x = 0; x neq waveform_size; x++) { wave[x] = wave[x] + v * sin((2.0 * PI * h * (double) x) / (double) waveform_size); } } void wave_normalize() { int x; double max_vol; /* first pass, ascertain highest volume in wave buffer */ max_vol = 0.0; for (x = 0; x neq waveform_size; x++) { if (max_vol < fixed_fabs(wave[x])) { max_vol = fabs(wave[x]); } } /* second pass: if volume is greater than 1/12, change it to peak at 1.0 */ if (max_vol > (1/12)) { for (x = 0; x neq waveform_size; x++) { wave[x] = wave[x] / max_vol; } } } void dave_smith_alias() /* 128 samples, 12 bit */ { int x, accumulator, down_factor, safe, x2; double temp; down_factor = waveform_size / 128; /* first pass, 12 bit quantize, but scaled to fit -1.0 to 1.0 range */ for (x = 0; x neq waveform_size; x++) { temp = (wave[x] + 1.0) * 2048; /* 0 to 4096 range */ accumulator = temp; /* now it's quantized (integer) */ temp = (float) accumulator / 2048.0; /* 0.0 to 2.0 range again */ wave[x] = temp - 1.0; /* -1.0 to 1.0 range */ } /* second pass, repeat every 16 samples to get the 128-samples sound in a 2048 sample waveform */ safe = waveform_size / 16; safe = safe * 16; for (x = 0; x < safe; x++) { x2 = x; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; } } void chip_mod_alias() /* 64 samples, 8 bit */ { int x, accumulator, safe, x2; double temp; /* first pass, 8 bit quantize, but scaled to fit -1.0 to 1.0 range */ for (x = 0; x neq waveform_size; x++) { temp = (wave[x] + 1.0) * 128; /* 0 to 256 range */ accumulator = temp; /* now it's quantized (integer) */ temp = (float) accumulator / 128.0; /* 0.0 to 2.0 range again */ wave[x] = temp - 1.0; /* -1.0 to 1.0 range */ } /* second pass, repeat every 32 samples to get the 64-samples sound in a 2048 sample waveform */ safe = waveform_size / 32; safe = safe * 32; for (x = 0; x < safe; x++) { x2 = x; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; wave[++x] = wave[x2]; } } void render_add_24() /* global parameters */ { int x; /* clear out (zero out) waveform buffer */ for (x = 0; x neq waveform_size; x++) wave[x] = 0; /* add in the 24 harmonics */ for (x = 1; x neq 25; x++) { /* 1 to 24 inclusive */ add_harmonic(x, harmonic[x]); } /* scale/normalize to 1.0 */ wave_normalize(); } void render_wave() { int x, halfwave; double temp; halfwave = waveform_size / 2; for (x = 0; x neq waveform_size; x++) { switch (which_wave) { case SAWTOOTH_WAVE : wave[x] = (( (double) x / (double) (1 + waveform_size) ) * -2.0) + 1.0; break; case SQUARE_WAVE : if (x < halfwave) wave[x] = 1.0; else wave[x] = -1.0; break; case TRIANGLE_WAVE : temp = (double) x / (double) waveform_size; if (temp < 0.25) { temp *= 4.0; } else { if (temp < 0.5) { temp = 1 + -4.0 * (temp - 0.25); } else { if (temp < 0.75) { temp = -4.0 * (temp - 0.5); } else { temp = (temp -0.75) * 4 - 1; } } } wave[x] = temp; break; case CIRCLE_WAVE : temp = sin((2.0 * PI * (double) x) / (double) waveform_size); if (temp < 0.0) { temp = -1.0 * sqrt(-1.0 * temp); } else { temp = sqrt(temp); } wave[x] = temp; break; default : /* sine wave */ wave[x] = sin((2.0 * PI * (double) x) / (double) waveform_size); } /* end switch */ } /* end for */ } void render_sub_wave() { int x; for (x = 0; x neq waveform_size; x++) { sub_wave[x] = sin((2.0 * PI * (double) x) / (double) waveform_size); } } double osc() { double osc_p, rc, lfo_temp, n_temp; unsigned long i_temp; /* portmento part of pitch */ osc_p = osc_pitch; /* pitch, in Hz */ switch (portmento_state) { case portmento_START2 : /* first note on in tune */ from_pitch = osc_pitch; portmento_state = portmento_COASTING; break; case portmento_START1 : /* start of tune, havn't had first note-on yet */ case portmento_COASTING : /* in a note, but past the portmento part */ case portmento_OFF : /* skipping portmento entirely */ /* do nothing */ break; case portmento_COUNTING : /* in portmento, counting */ if (portmento_x eq portmento_time) { /* done - at new pitch */ portmento_state = portmento_COASTING; /* portmento done, coasting in current note */ from_pitch = osc_pitch; } else { /* not done, still sliding, counting */ osc_p = (from_pitch * ((double) portmento_time - (double) portmento_x)) + (osc_p * (double) portmento_x); osc_p = osc_p / (double) portmento_time; portmento_x++; } break; default : fprintf(stderr, "osc(): bad portmento state\n"); exit(DOOPS); } /* end switch on portmento state */ /* calculate current pitch (in Hz) */ lfo_temp = lfo(); osc_p = osc_p * pow(2, 4 * lfo_to_pitch * lfo_temp); /* pitch, in Hz */ if (use_updown) { if (updown_phase) { osc_p = osc_p * 0.5; } if (--updown_count eq 0) { if (updown_phase) { updown_phase = 0; } else { updown_phase++; } updown_count = updown_count_length; } } if (smod_subsample_counter eq mod_subsample_rate) { /* subsampling here in this program means that we don't recalculate the filter biquad every sample. We do it every 30 or so samples. We do this to speed things up a bit */ pseudo_sync_w = pseudo_sync + (lfo_to_pseudo_sync * lfo_temp); if (pseudo_sync_w < 0.5) pseudo_sync_w = 0.5; if (pseudo_sync_w > 2.0) pseudo_sync_w = 2.0; smod_subsample_counter = 0; } else { smod_subsample_counter++; } /* get current sample for main osc */ rc = unison_mix_main * wave[ (int) ( fmod((pseudo_sync_w * phase_dist(osc_phase)), 1) * (double) waveform_size ) ]; /* get current sample for up unison oscilator */ rc += unison_mix_up * wave[ (int) ( fmod((pseudo_sync_w * phase_dist(unison_up_osc_phase)), 1) * (double) waveform_size ) ]; /* get current sample for upmore unison oscilator */ rc += unison_mix_upmore * wave[ (int) ( fmod((pseudo_sync_w * phase_dist(unison_upmore_osc_phase)), 1) * (double) waveform_size ) ]; /* get current sample for down unison oscilator */ rc += unison_mix_down * wave[ (int) ( fmod((pseudo_sync_w * phase_dist(unison_down_osc_phase)), 1) * (double) waveform_size ) ]; /* get current sample for downmore unison oscilator */ rc += unison_mix_downmore * wave[ (int) ( fmod((pseudo_sync_w * phase_dist(unison_downmore_osc_phase)), 1) * (double) waveform_size ) ]; /* get current sample for sub osc (one octave down) */ rc += sub_osc_level_work * sub_wave[ (int) ( fmod(sub_osc_phase, 1) * (double) waveform_size ) ]; /* advance each oscilator to next sample (using updated current pitch) */ osc_phase = fmod( (osc_phase + (osc_p / 44100.0)), 1.0 ); unison_up_osc_phase = fmod( (unison_up_osc_phase + ((osc_p * unison_detune_up) / 44100.0)), 1.0 ); unison_upmore_osc_phase = fmod( (unison_upmore_osc_phase + ((osc_p * unison_detune_upmore) / 44100.0)), 1.0 ); unison_down_osc_phase = fmod( (unison_down_osc_phase + ((osc_p * unison_detune_down) / 44100.0)), 1.0 ); unison_downmore_osc_phase = fmod( (unison_downmore_osc_phase + ((osc_p * unison_detune_downmore) / 44100.0)), 1.0 ); sub_osc_phase = fmod( (sub_osc_phase + (osc_p / 88200.0)), /* or is it 22050? */ 1.0 ); /* generate and mix in next noise generator sample */ i_temp = rand(); /* i_temp is unsigned ... always positive */ i_temp = i_temp % 65536; n_temp = ((double) i_temp / 32768.0) - 1.0; rc = (rc * noise_non_level) + (n_temp * noise_level); /* mix that combined signal with the external in signal */ rc = (rc * ext_in_non_volume) + (play_ext_in() * ext_in_volume); /* return current sample */ return(rc); } /* calculate biquad values from specified Hz and res */ void calc_biquad(b, Hz, Q) struct biquad *b; double Hz; double Q; /* 0 to 5. more than 5 is kinda broken */ { double c, c_squared, resonance, q, a0, a1, a2, b1, b2; c = 1.0 / (tan(pi * (Hz / 88200.0))); c_squared = c * c; resonance = pow(10.0, - (Q * 0.1)); q = sqrt(2.0) * resonance; a0 = 1.0 / (1.0 + (q * c) + (c_squared)); a1 = 2.0 * a0; a2 = a0; b1 = (2.0f * a0) * (1.0 - c_squared); b2 = a0 * (1.0 - (q * c) + c_squared); /* set the coefficients */ b->a0 = a0; b->a1 = a1; b->a2 = a2; b->a3 = b1; b->a4 = b2; } /* applies biquad filter to the input sample */ double filter_sample(sample, b) double sample; struct biquad *b; { double result; /* compute result */ result = (b->a0 * sample) + (b->a1 * b->x1) + (b->a2 * b->x2) - (b->a3 * b->y1) - (b->a4 * b->y2) ; /* shift x1 to x2, sample to x1 */ b->x2 = b->x1; b->x1 = sample; /* shift y1 to y2, result to y1 */ b->y2 = b->y1; b->y1 = result; /* return filtered sample */ return(result); } double filter(sample_in) /* resonant low pass */ double sample_in; { double filter_eg, return_sample; if (filter_subsample_counter eq mod_subsample_rate) { /* subsampling here in this program means that we don't recalculate the filter biquad every sample. We do it every 30 or so samples. We do this to speed things up a bit */ filter_eg = filter_cutoff + (2 * env_to_filter_cutoff * (env_out - 0.5)) + (eg_var1_to_filter_cutoff * eg_var1) ; filter_eg = f_max(0.0, filter_eg); filter_eg = f_min(0.9999, filter_eg); if (filter_eg > 0.9999) { fprintf(stderr, "internal error: custom f_min() routine is broken\n"); exit(DOOPS); } /* next line, convert a 0.0 -- 1.0 "voltage" into a frequency in Hz. */ filter_eg = 86.0 * pow(2, (filter_eg * 8)); /* 86 Hz to ~22050 Hz */ if (filter_eg > 22015.0) { fprintf(stderr, "internal: lowpass > 22015.0\n"); exit(DOOPS); filter_eg = 22015.0; } if (filter_eg < 86.0) filter_eg = 86.0; filter_eg_hold = filter_eg; filter_subsample_counter = 0; calc_biquad(&bq, filter_eg, (filter_feedback * 4)); } else { /* filter_eg = filter_eg_hold; */ filter_subsample_counter++; } /* mow the actual filtering (2x oversample on low pass filter ONLY */ return_sample = filter_volume * filter_sample(sample_in, &bq); return_sample += filter_volume * filter_sample(sample_in, &bq); return_sample *= 0.5; return(return_sample); } double hfilter(in) /* simple & stupid high pass filter */ double in; { double temp; /* internally it is two filters in series*/ hp_mem = (hp_mem * hp_cut_i) + (in * hp_cut); temp = (in - hp_mem); hp_mem2 = (hp_mem2 * hp_cut_i) + (temp * hp_cut); return((temp - hp_mem2)); } void usage() { fprintf(stderr, "usage: render_notes_5 \ \n"); } void usage_exit() { usage(); exit(DOOPS); } double delay(sample_in) double sample_in; { int temp; temp = delay_index + (delay_time * 44100); /* convert time to samples */ delay_buffer[delay_index] = delay_regen * delay_buffer[temp % delay_buffer_size] + (1 - delay_regen) * sample_in ; if (delay_index eq 0) delay_index = delay_buffer_size; delay_index--; return( sample_in * (1.0 - delay_level) + delay_buffer[temp % delay_buffer_size] * delay_level ); } int write_a_note(pitch, sustain, release, f2) double pitch; /* Hz */ double sustain; /* seconds */ double release; /* seconds */ FILE *f2; { int status, x, nlen, nlen2; nlen = sustain * 44100.0; nlen2 = release * 44100.0; osc_pitch = fine_tune + (double) transpose; osc_pitch = pitch * pow(root12, osc_pitch); samples_per_sample = osc_pitch / sample_in_pitch; env_out = curve_env(1); /* env: note on */ envelope_sample(1); /* note on */ do_eg_var1(); status = write_sample(render_one_sample, f2); if (status neq OKDOKEY) { fprintf(stderr, "file write error 2\n"); exit(DOOPS); } /* holding note */ for (x = 1; x < nlen; x++) { env_out = curve_env(0); /* env - keep doing what you were doing */ envelope_sample(0); do_eg_var1(); status = write_sample(render_one_sample, f2); if (status neq OKDOKEY) { fprintf(stderr, "file write error 2\n"); exit(DOOPS); } } /* note off */ env_out = curve_env(-1); /* env: note off! */ envelope_sample(-1); do_eg_var1(); status = write_sample(render_one_sample, f2); if (status neq OKDOKEY) { fprintf(stderr, "file write error 3\n"); exit(DOOPS); } /* release (after note off) */ for (x = 1; x < nlen2; x++) { env_out = curve_env(0); /* env: keep doing what you were doing */ envelope_sample(0); do_eg_var1(); status = write_sample(render_one_sample, f2); if (status neq OKDOKEY) { fprintf(stderr, "file write error 4\n"); exit(DOOPS); } } /* calculate timing roundoff error, write extra sample(s) to compensate */ timing_error = timing_error + ((sustain + release) * 44100) - (nlen + nlen2) ; nlen = timing_error; /* reuse nlen for new value */ for (x = 0; x < nlen; x++) { env_out = curve_env(0); /* env: keep doing what you were doing */ envelope_sample(0); do_eg_var1(); status = write_sample(render_one_sample, f2); if (status neq OKDOKEY) { fprintf(stderr, "file write error 5\n"); exit(DOOPS); } } timing_error = fmod(timing_error, 1.0); return(OKDOKEY); } int write_a_rest(sustain, f2) double sustain; /* seconds */ FILE *f2; { int status, x, nlen; nlen = sustain * 44100.0; /* release (if a note before this rest) */ for (x = 1; x < nlen; x++) { env_out = curve_env(0); /* env: keep doing what you were doing */ envelope_sample(0); do_eg_var1(); status = write_sample(render_one_sample, f2); if (status neq OKDOKEY) { fprintf(stderr, "file write error 4\n"); exit(DOOPS); } } /* calculate timing roundoff error, write extra samples to compensate */ timing_error = timing_error + fmod( (sustain - (nlen * 44100.0)), 1.0 ) ; nlen = timing_error; /* reuse nlen for new value */ timing_error = fmod(timing_error, 1.0); for (x = 0; x < nlen; x++) { env_out = curve_env(0); /* env: keep doing what you were doing */ envelope_sample(0); do_eg_var1(); status = write_sample(render_one_sample, f2); if (status neq OKDOKEY) { fprintf(stderr, "file write error 5\n"); exit(DOOPS); } } return(OKDOKEY); } int parse_double(s, f) char *s; double *f; { char *ptr; char num_temp[256]; /* no leading spaces */ /* look for space after the number */ ptr = strchr(s, ' '); /* find space after number */ if (ptr eq NULL) { /* entire string is number */ *f = atof(s); return(DOOPS); /* should be OKDOKEY - change *later* */ } /* ptr points to first space after the number */ memmove(&num_temp[0], s, (ptr - s)); num_temp[(ptr - s)] = '\0'; *f = atof(num_temp); memmove(s, ptr, strlen(ptr) + 1); trim_spaces(s); return(OKDOKEY); } int parse_int(s, i) char *s; int *i; { char *ptr; char num_temp[256]; /* no leading spaces */ /* look for space after the number */ ptr = strchr(s, ' '); /* find space after number */ if (ptr eq NULL) { /* entire string is number */ *i = atoi(s); return(DOOPS); /* should be OKDOKEY - change *later* */ } /* ptr points to first space after the number */ memmove(&num_temp[0], s, (ptr - s)); num_temp[(ptr - s)] = '\0'; *i = atoi(num_temp); memmove(s, ptr, strlen(ptr) + 1); trim_spaces(s); return(OKDOKEY); } int parse_pitch(the_line) char *the_line; { int here; trim_spaces(the_line); if (strlen(the_line) < 2) return(DOOPS); if (the_line[1] eq '#') { /* sharp note */ here = 3; switch(the_line[0]) { case 'c' : osc_pitch = 220 * pow(root12, 4); break; case 'd' : osc_pitch = 220 * pow(root12, 6); break; case 'f' : osc_pitch = 220 * pow(root12, 9); break; case 'g' : osc_pitch = 220 * pow(root12, 11); break; case 'a' : osc_pitch = 440 * root12; break; default : fprintf(stderr, "bad sharp pitch value\n"); return(DOOPS); } /* end switch */ /* parse octave number */ switch (the_line[2]) { case '0' : osc_pitch /= 16.0; break; case '1' : osc_pitch *= 0.125; break; case '2' : osc_pitch *= 0.25; break; case '3' : osc_pitch *= 0.5; break; case '4' : break; /* octave 4, do nothing */ case '5' : osc_pitch *= 2; break; case '6' : osc_pitch *= 4; break; case '7' : osc_pitch *= 8; break; case '8' : osc_pitch *= 16; break; case '9' : osc_pitch *= 32; break; default : fprintf(stderr, "bad octave on sharp note\n"); return(DOOPS); } } else { /* not a sharp note */ here = 2; switch (the_line[0]) { case 'c' : osc_pitch = 220 * pow(root12, 3); break; case 'd' : osc_pitch = 220 * pow(root12, 5); break; case 'e' : osc_pitch = 220 * pow(root12, 7); break; case 'f' : osc_pitch = 220 * pow(root12, 8); break; case 'g' : osc_pitch = 220 * pow(root12, 10); break; case 'a' : osc_pitch = 440; break; case 'b' : osc_pitch = 440 * pow(root12, 2); break; default : fprintf( stderr, "not a sharp note: bad pitch value \"%s\"\n", the_line ); return(DOOPS); } /* end switch */ /* parse octave number */ switch (the_line[1]) { case '0' : osc_pitch /= 16.0; break; case '1' : osc_pitch *= 0.125; break; case '2' : osc_pitch *= 0.25; break; case '3' : osc_pitch *= 0.5; break; case '4' : break; /* octave 4, do nothing */ case '5' : osc_pitch *= 2; break; case '6' : osc_pitch *= 4; break; case '7' : osc_pitch *= 8; break; case '8' : osc_pitch *= 16; break; case '9' : osc_pitch *= 16; break; default : fprintf(stderr, "bad octave on note\n"); return(DOOPS); } } /* remove token from line */ the_line[0] = ' '; the_line[1] = ' '; if (here eq 3) the_line[2] = ' '; trim_spaces(the_line); return(OKDOKEY); } int parse_macro_note(work_line, e) char *work_line; struct event_node *e; { double old_osc_pitch; double note_total_len, just_note_len; int status; e->event_type = 1; /* 0 for rest, 1 for note */ old_osc_pitch = osc_pitch; /* so a ringing note won't jump a pitch */ status = parse_pitch(work_line); /* parse pitch, discard token */ e->note_pitch = osc_pitch; osc_pitch = old_osc_pitch; /* so a ringing note won't jump a pitch */ /* parse double removes the first token from the line and trims it up */ status = parse_double(work_line, ¬e_total_len); status = parse_double(work_line, &just_note_len); /* rest of line is ignored */ if (note_total_len < just_note_len) { fprintf( stderr, "parse_macro_note error: just_note_len %f is greater than \ note_total_len %f\n", just_note_len, note_total_len ); exit(DOOPS); } if (note_total_len < 0.001) { fprintf(stderr, "parse_macro_note Error: note_total len %f \ is less than 0.001\n", note_total_len); exit(DOOPS); } e->just_note_length = just_note_len; e->total_note_length = note_total_len; e->next = (struct event_node *) NULL; return(OKDOKEY); } int parse_macro_anote(work_line, e) char *work_line; struct event_node *e; { double old_osc_pitch; double note_total_len, just_note_len; int status; e->event_type = 1; /* 0 for rest, 1 for note */ old_osc_pitch = osc_pitch; /* so a ringing note won't jump a pitch */ status = parse_pitch(work_line); /* parse pitch, discard token */ e->note_pitch = osc_pitch; osc_pitch = old_osc_pitch; /* so a ringing note won't jump a pitch */ /* parse double removes the first token from the line and trims it up */ status = parse_double(work_line, ¬e_total_len); if (note_total_len < 1) just_note_len = note_total_len * 0.5; else just_note_len = note_total_len - 0.5; /* rest of line is ignored */ if (note_total_len < just_note_len) { fprintf( stderr, "parse_macro_note error: just_note_len %f is greater than \ note_total_len %f\n", just_note_len, note_total_len ); exit(DOOPS); } if (note_total_len < 0.001) { fprintf(stderr, "parse_macro_note Error: note_total len %f \ is less than 0.001\n", note_total_len); exit(DOOPS); } e->just_note_length = just_note_len; e->total_note_length = note_total_len; e->next = (struct event_node *) NULL; return(OKDOKEY); } int parse_macro_rest(work_line, e) char *work_line; struct event_node *e; { double rest_len; int status; e->event_type = 0; /* 0 for rest, 1 for note, 2 for call, 3 for tranpose */ /* parse double removes the first token from the line and trims it up */ status = parse_double(work_line, &rest_len); /* rest of line is ignored */ if (rest_len < 0.001) { fprintf(stderr, "parse_macro_rest Error: note_total len %f is less \ than 0.001\n", rest_len); exit(DOOPS); } e->total_note_length = rest_len; e->next = (struct event_node *) NULL; return(OKDOKEY); } int parse_macro_call(s, e) char *s; struct event_node *e; { int temp, status; e->next = (struct event_node *) NULL; e->event_type = 2; /* call event */ status = parse_int(s, &temp); /* macro number to call */ e->note_pitch = (double) temp; e->just_note_length = 0.0; e->total_note_length = 0.0; return(OKDOKEY); } int parse_macro_transpose(work_line, e) char *work_line; struct event_node *e; { int transpose_amount; int status; e->event_type = 3; /* 0 for rest, 1 for note, 2 for call, 3 for tranpose */ /* parse int removes the first token from the line and trims it up */ status = parse_int(work_line, &transpose_amount); /* rest of line is ignored */ e->total_note_length = transpose_amount; e->next = (struct event_node *) NULL; return(OKDOKEY); } int parse_macro(f1, m_line) FILE *f1; char *m_line; { int m, parse_found, status; struct event_node *temp, *ptr, *header; struct macro_node *m_node; char work_line[MAX_STR]; temp = (struct event_node *) NULL; ptr = (struct event_node *) NULL; header = (struct event_node *) NULL; m = atoi(m_line); if ((m < 0) || (m > 999)) { fprintf(stderr, "parse_macro: macro ID number must be in range \ 0 to 999\n"); exit(DOOPS); } while (1) { parse_found = 0; status = read_line(f1, work_line); if (status neq OKDOKEY) { fprintf(stderr, "parse_macro: error reading from file or early EOF\n"); exit(DOOPS); } trim_spaces(&work_line[0]); /* parse the "end_macro" case */ if (0 eq stricmp(work_line, "end_macro")) { /* tie off, clean up, return. we done */ m_node = malloc(sizeof(struct macro_node)); if (m_node eq NULL) { fprintf(stderr, "parse_macro: memory failure on macro node\n"); exit(DOOPS); } m_node->macro_number = m; m_node->events = header; m_node->next = macros; macros = m_node; m_node = (struct macro_node *) NULL; ptr = (struct event_node *) NULL; temp = (struct event_node *) NULL; header = (struct event_node *) NULL; return(OKDOKEY); } /* parse the "note" case */ if (0 eq strncmp("note", work_line, 4)) { temp = malloc(sizeof(struct event_node)); if (temp eq NULL) { fprintf(stderr, "parse_macro: memory failure on event node\n"); exit(DOOPS); } /* toss the "note" token and the one known space after it */ memmove( work_line, &work_line[4], (1 + strlen(&work_line[4])) ); trim_spaces(work_line); status = parse_macro_note(work_line, temp); temp->next = NULL; if (header eq NULL) { header = temp; ptr = temp; } else { ptr->next = temp; ptr = ptr->next; } temp = (struct event_node *) NULL; parse_found = 1; } /* parse the "anote" case */ if (0 eq strncmp("anote ", work_line, 6)) { temp = malloc(sizeof(struct event_node)); if (temp eq NULL) { fprintf(stderr, "parse_macro: memory failure on event node\n"); exit(DOOPS); } /* toss the "anote" token and the one known space after it */ memmove( work_line, &work_line[5], (1 + strlen(&work_line[5])) ); trim_spaces(work_line); status = parse_macro_anote(work_line, temp); temp->next = NULL; if (header eq NULL) { header = temp; ptr = temp; } else { ptr->next = temp; ptr = ptr->next; } temp = (struct event_node *) NULL; parse_found = 1; } /* parse the "rest" case */ if (parse_found) continue; if (0 eq strncmp("rest", work_line, 4)) { temp = malloc(sizeof(struct event_node)); if (temp eq NULL) { fprintf(stderr, "parse_macro: memory failure on event node\n"); exit(DOOPS); } /* toss the "rest" token and the one known space after it */ memmove( work_line, &work_line[4], (1 + strlen(&work_line[4])) ); trim_spaces(work_line); status = parse_macro_rest(work_line, temp); temp->next = NULL; if (header eq NULL) { header = temp; ptr = temp; } else { ptr->next = temp; ptr = ptr->next; } temp = (struct event_node *) NULL; parse_found = 1; } /* parse the "call" case */ if (parse_found) continue; if (0 eq strncmp("call", work_line, 4)) { temp = malloc(sizeof(struct event_node)); if (temp eq NULL) { fprintf(stderr, "parse_macro: memory failure on event node\n"); exit(DOOPS); } /* toss the "call" token and the one known space after it */ memmove( work_line, &work_line[4], (1 + strlen(&work_line[4])) ); trim_spaces(work_line); status = parse_macro_call(work_line, temp); temp->next = NULL; if (header eq NULL) { header = temp; ptr = temp; } else { ptr->next = temp; ptr = ptr->next; } temp = (struct event_node *) NULL; parse_found = 1; } /* parse the "transpose" case */ if (parse_found) continue; if (0 eq strncmp("transpose", work_line, 9)) { temp = malloc(sizeof(struct event_node)); if (temp eq NULL) { fprintf(stderr, "parse_macro: memory failure on event node\n"); exit(DOOPS); } /* toss the "tranpose" token and the one known space after it */ memmove( work_line, &work_line[9], (1 + strlen(&work_line[9])) ); trim_spaces(work_line); status = parse_macro_transpose(work_line, temp); temp->next = NULL; if (header eq NULL) { header = temp; ptr = temp; } else { ptr->next = temp; ptr = ptr->next; } temp = (struct event_node *) NULL; parse_found = 1; } } /* end while */ } /* end of subroutine */ int write_macro(e, f2, depth) /* play/write an existing macro to the output file */ struct event_node *e; FILE *f2; int depth; { int status, which_macro; struct event_node *a; struct macro_node *m; if (depth < 1) { fprintf(stderr, "write_macro: too many nested calls - exiting\n"); exit(DOOPS); } a = e; while (a neq (struct event_node *) NULL) { switch (a->event_type) { /* 0 for rest, 1 for note */ case 1 : /* note */ status = write_a_note( a->note_pitch, q_note_len * a->just_note_length, q_note_len * (a->total_note_length - a->just_note_length), f2 ); if (status neq OKDOKEY) { fprintf(stderr, "write_macro: file write error on note\n"); exit(DOOPS); } break; case 2 : /* call another macro */ which_macro = (int) a->note_pitch; m = macros; while (m neq NULL) { if (m->macro_number eq which_macro) { status = write_macro(m->events, f2, (depth - 1)); m = (struct macro_node *) NULL; /* cause an exit from loop */ } else { m = m->next; } } /* end while */ break; case 3 : /* transpose - real easy to do */ transpose = a->total_note_length; break; default: /* rest */ status = write_a_rest( q_note_len * a->total_note_length, f2 ); if (status neq OKDOKEY) { fprintf(stderr, "write_macro: file write error on note\n"); exit(DOOPS); } } /* end switch */ a = a->next; } return(OKDOKEY); } int parse_call(line, f2) char *line; FILE *f2; /* output file */ { int status, which_macro; struct macro_node *m; status = parse_int(line, &which_macro); /* rest of line is ignored */ m = macros; while (m neq NULL) { if (m->macro_number eq which_macro) { status = write_macro(m->events, f2, MAX_MACRO_RECURSIONS); m = (struct macro_node *) NULL; return(status); } m = m->next; } /* end while */ fprintf(stderr, "parse_call: macro %d not found\n", which_macro); exit(DOOPS); } int parse_sample(line) char *line; { FILE *sf; int actual_read, x; /* get unquoted file name of sample to use */ trim_spaces(line); /* check for leading quote */ if (line[0] neq '"') { fprintf(stderr, "parse sample, no leading quote in .sam file name \n"); exit(DOOPS); } /* check for trailing quote */ if (line[(strlen(line) - 1)] neq '"') { fprintf(stderr, "parse sample, no trailing quote in .sam file name\n"); exit(DOOPS); } line[(strlen(line) - 1)] = '\0'; /* delete trailing quote */ sf = fopen(&line[1], "rb"); if (sf eq NULL) { fprintf(stderr, "error opening .sam file \"%s\"\n", &line[1]); exit(DOOPS); } actual_read = fread(&sample_buffer[0], 2, sample_buffer_size, sf); fclose(sf); if (actual_read) { convert_from(sample_buffer, actual_read); } x = actual_read; while (x < sample_buffer_size) { sample_buffer[x++] = 0; } } int parse_sample_note(line) char *line; { double save_osc_pitch; int status; save_osc_pitch = osc_pitch; trim_spaces(line); status = parse_pitch(line); /* result in global "osc_pitch" */ if (status eq DOOPS) { fprintf(stderr, "parse_sample_note: bad note syntax\n"); exit(DOOPS); } sample_in_pitch = osc_pitch; osc_pitch = save_osc_pitch; } int parse_ext_in(line) char *line; { int actual_read, x; /* get unquoted file name of sample to use */ trim_spaces(line); /* fprintf(stderr, "%s\n", line); */ /* check for leading quote */ if (line[0] neq '"') { fprintf(stderr, "parse ext_in, no leading quote in .sam file name \n"); exit(DOOPS); } /* check for trailing quote */ if (line[(strlen(line) - 1)] neq '"') { fprintf(stderr, "parse ext_in, no trailing quote in .sam file name\n"); exit(DOOPS); } line[(strlen(line) - 1)] = '\0'; /* delete trailing quote */ ext_in_file = fopen(&line[1], "rb"); if (ext_in_file eq NULL) { fprintf(stderr, "error opening .sam file \"%s\"\n", &line[1]); exit(DOOPS); } } void render_bit_wave(s) /* build waveform using the bit pattern */ char *s; /* "bit pattern" string to parse */ { int num_bits, x, y, c, string_len; int bit_pattern[128]; /* we don't allow more than 128 bits/steps */ /* parse, build "real" bit string */ num_bits = 0; string_len = strlen(s); for (x = 0; x neq string_len; x++) { switch (s[x]) { case '0' : bit_pattern[num_bits++] = 0; break; case '1' : bit_pattern[num_bits++] = 1; break; /* ignores any other characters */ } /* end switch */ } /* end for */ if (num_bits < 2) { fprintf(stderr, "render_bit_waves(): need two or more bits to specify \ a waveform\n"); exit(DOOPS); } /* use bit string to render into the wave buffer. 0 is -1 and 1 is +1 */ for (x = 0; x neq waveform_size; x++) { y = x * num_bits; y = y / waveform_size; if (bit_pattern[y]) { wave[x] = 1.0; } else { wave[x] = -1.0; } } } void clear_line_nodes() /* clear, init, whatever */ { struct line_node *temp; /* if lines is already NULL, we don't have to do anything */ while (lines neq NULL) { temp = lines; lines = lines->next; free(temp); } temp = (struct line_node *) NULL; } void add_line_node(v1, v2, dur) double v1; double v2; double dur; { struct line_node *temp, *ptr; temp = malloc(sizeof(struct line_node)); if (temp eq NULL) { fprintf(stderr, "add_line_node(): memory failure\n"); exit(DOOPS); } temp->level1 = v1; temp->level2 = v2; temp->local_duration = dur; temp->next = (struct line_node *) NULL; if (lines eq (struct line_node *) NULL) { /* adding to empty list */ lines = temp; } else { /* adding at the end of a list that is not empty */ ptr = lines; while (ptr->next neq (struct line_node *) NULL) { ptr = ptr->next; } ptr->next = temp; ptr = (struct line_node *) NULL; } temp = (struct line_node *) NULL; } void render_segment(ptr, start, end) struct line_node *ptr; double start; double end; { double temp; int x, offset, y; /* convert start and end from a range of 0.0 -- 1.0 into the waveform size */ x = start * waveform_size; y = end * waveform_size; if (x < 0) x = 0; if (y > waveform_size) y = waveform_size; offset = x; x -= offset; y -= offset; while (x < y) { temp = (double) x / (double) y; /* starts at 0, ends at 1 */ /* ramp/gradient */ temp = (ptr->level2 * temp) + (ptr->level1 * (1 - temp)); wave[(offset + x)] = temp; x++; } } void render_line_wave() { double total_length, current_position; struct line_node *ptr; if (lines eq (struct line_node *) NULL) { fprintf(stderr, "warning: line_wave() attempt to render \ line wave using empty list\n"); } else { /* list is not empty; there is at least one line segment in it */ /* add up the lengths of the lines */ total_length = 0; current_position = 0; ptr = lines; while (ptr neq (struct line_node *) NULL) { total_length += ptr->local_duration; ptr = ptr->next; } /* lengths have been added up. rewind the pointer and render the wave, one line at at time */ ptr = lines; while (ptr neq (struct line_node *) NULL) { render_segment( ptr, current_position / total_length, (current_position + ptr->local_duration) / total_length ); current_position += ptr->local_duration; ptr = ptr->next; } ptr = (struct line_node *) NULL; } } void update_osc_levels() /* adjust */ { double temp; unison_mix_main = 2; sub_osc_level_work = 2.0 * sub_osc_level; unison_mix_up = unison_mix; unison_mix_down = unison_mix; unison_mix_upmore = unison_mix * unison_spread; unison_mix_downmore = unison_mix * unison_spread; temp = unison_mix_main + unison_mix_up + unison_mix_down + unison_mix_upmore + unison_mix_downmore + sub_osc_level_work; unison_mix_main /= temp; unison_mix_up /= temp; unison_mix_upmore /= temp; unison_mix_down /= temp; unison_mix_downmore /= temp; sub_osc_level_work /= temp; } void swap_osc_sub() { double *temp; temp = wave; wave = sub_wave; sub_wave = temp; temp = (double *) NULL; } main(argc, argv) int argc; char *argv[]; { FILE *f1[max_in_files]; FILE *f2; int x, status, parse_found, file_index, keep_going; int in_pipe, out_pipe; double track_bpm, d_temp; char work_line[MAX_STR]; if (argc eq 0) exit(DOOPS); if (argc neq 4) usage_exit(); sample_in_pitch = 440; env_memory = 0.0; filter_feedback = 0.0; file_index = 0; in_pipe = out_pipe = 0; srand(0xdeadbeef); /* noise will start the same each run */ if (0 eq strcmp(argv[1], "-")) { /* input from pipe */ in_pipe = 1; f1[file_index] = stdin; } else { /* input from file */ if ((f1[file_index] = fopen(argv[1], "r")) eq NULL) { fprintf(stderr, "Error opening input file \"%s\"\n", argv[1]); exit(DOOPS); } } if (0 eq strcmp(argv[2], "-")) { /* output to pipe */ out_pipe = 1; f2 = stdout; } else { /* output to pipe */ if ((f2 = fopen(argv[2], "wb")) eq NULL) { fprintf(stderr, "Error creating output file \"%s\"\n", argv[2]); exit(DOOPS); } } if (out_pipe eq 0) { if ((status = setvbuf(f2, NULL, _IOFBF, 16384)) neq 0) { fprintf(stderr, "setvbuf error on output file\n"); exit(DOOPS); } } if ((delay_buffer = calloc(delay_buffer_size, sizeof(double))) eq NULL) { fprintf(stderr, "error allocating delay buffer\n"); exit(DOOPS); } for (status = 0; status neq delay_buffer_size; status++) delay_buffer[status] = 0.0; if ((wave = calloc(waveform_size, sizeof(double))) eq NULL) { fprintf(stderr, "error allocating waveform buffer\n"); exit(DOOPS); } if ((sub_wave = calloc(waveform_size, sizeof(double))) eq NULL) { fprintf(stderr, "error allocating sub waveform buffer\n"); exit(DOOPS); } sample_buffer = malloc(sample_buffer_size * 2); if (sample_buffer eq NULL) { fprintf(stderr, "memory failure for sample input buffer\n"); exit(DOOPS); } for (status = 0; status neq sample_buffer_size; status++) sample_buffer[status] = 0; if ((env_curve_table = calloc(env_curve_table_size, sizeof(double))) eq NULL) { fprintf(stderr, "memory failure for envelope curve table\n"); exit(DOOPS); } render_env_curve(); root12 = pow(2, (1.0 / 12.0) ); /* 12th root of 2 */ fine_tune = 0.0; transpose = 0; render_wave(); /* wave buffer must have been allocated already */ render_sub_wave(); /* sub wave buffer must have been allocated already */ track_bpm = atof(argv[3]); /* BPM */ if ((track_bpm < 40.0) || (track_bpm > 240.0)) { fprintf(stderr, "Error: BPM out of range (40.0 - 240.0)\n"); exit(DOOPS); } q_note_len = (60.0 / track_bpm); /* length in seconds of a 4th note */ hp_cut = 20.0 / 44100; hp_cut_i = 1.0 - hp_cut; use_volume_envelope = 1; /* everything is all set. Let's start reading notes from the input file and rendering them to the output file - monophonic only */ /* main loop - go! */ keep_going = 1; while (keep_going) { status = read_line(f1[file_index], work_line); if (status neq OKDOKEY) { /* at this point we've hit an end of file, but are we at the end of an included file or the topmost main file? */ work_line[0] = '\0'; /* fictional empty line */ if (file_index) { /* only the end of an included file */ fclose(f1[file_index--]); } else { /* topmost main file - set up so that we get out */ keep_going = 0; /* we are entirely done - we need to leave the parsing loop */ } } trim_spaces(work_line); parse_found = 0; switch (work_line[0]) { /* catch comment lines */ case ';' : /* old .asm comment style */ case '!' : /* bb4win/blackbox comment style */ case '#' : /* shell-script comment style */ parse_found = 1; break; case '/' : if (work_line[1] eq '/') { /* povray or c++ style comment */ parse_found = 1; } break; } /* end_switch */ if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("include ", work_line, 8)) { parse_found = 1; /* toss the "include" token and the one known space after it */ memmove( work_line, &work_line[8], (1 + strlen(&work_line[8])) ); trim_spaces(&work_line[0]); /* the rest of the line is the file name. With quotes */ /* check for leading quote */ if (work_line[0] neq '"') { fprintf(stderr, "include, no leading quote in include file name \n"); exit(DOOPS); } /* check for trailing quote */ if (work_line[(strlen(work_line) - 1)] neq '"') { fprintf(stderr, "include, no trailing quote in include file name\n"); exit(DOOPS); } work_line[(strlen(work_line) - 1)] = '\0'; /* delete trailing quote */ file_index++; if (file_index eq max_in_files) { /* have we nested too deeply? */ file_index--; fprintf(stderr, "warning: include: too many nested includes\n"); } else { /* note we open work_line[1] not [0], so that we skip the leading quote */ if ((f1[file_index] = fopen(&work_line[1], "r")) eq NULL) { fprintf( stderr, "error: include file \"%s\" could not be opened\n", &work_line[1] ); exit(DOOPS); } } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("note ", work_line, 5)) { parse_found = 1; /* toss the "note" token and the one known space after it */ memmove( work_line, &work_line[4], (1 + strlen(&work_line[4])) ); status = parse_pitch(work_line); /* sets global var "osc_pitch" */ /* parse line removes the first token from the line and trims it up */ status = parse_double(work_line, ¬e_total_len); /* parse double removes the first token from the line and trims it up */ status = parse_double(work_line, &just_note_len); /* rest of line is ignored */ if (note_total_len < just_note_len) { fprintf( stderr, "error: just_note_len %f is greater than note_total_len %f\n", just_note_len, note_total_len ); exit(DOOPS); } if (note_total_len < 0.001) { fprintf(stderr, "Error: note_total len %f is less than 0.001\n", note_total_len ); exit(DOOPS); } status = write_a_note( osc_pitch, /* Hz */ q_note_len * just_note_len, /* seconds */ q_note_len * (note_total_len - just_note_len), /* seconds */ f2 /* file */ ); if (status neq OKDOKEY) { fprintf(stderr, "Error writing notes to output file\n"); exit(DOOPS); } parse_found = 1; } /* end of note handler */ if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("anote ", work_line, 6)) { parse_found = 1; /* toss the "anote" token and the one known space after it */ memmove( work_line, &work_line[5], (1 + strlen(&work_line[5])) ); status = parse_pitch(work_line); /* sets global var "osc_pitch" */ /* parse line removes the first token from the line and trims it up */ status = parse_double(work_line, ¬e_total_len); /* parse double removes the first token from the line and trims it up */ /* rest of line is ignored */ /* automatically determine note length as a fraction of the total note length */ if (note_total_len < 1) just_note_len = note_total_len * 0.5; /* proportional gap */ else just_note_len = note_total_len - 0.5; /* 8th note gap */ if (note_total_len < just_note_len) { fprintf( stderr, "error: just_note_len %f is greater than note_total_len %f\n", just_note_len, note_total_len ); exit(DOOPS); } if (note_total_len < 0.001) { fprintf(stderr, "Error: note_total len %f is less than 0.001\n", note_total_len ); exit(DOOPS); } status = write_a_note( osc_pitch, /* Hz */ q_note_len * just_note_len, /* seconds */ q_note_len * (note_total_len - just_note_len), /* seconds */ f2 /* file */ ); if (status neq OKDOKEY) { fprintf(stderr, "Error writing notes to output file\n"); exit(DOOPS); } parse_found = 1; } /* end of note handler */ if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("rest ", work_line, 5)) { parse_found = 1; /* toss the "rest" token and the one known space after it */ memmove( work_line, &work_line[4], (1 + strlen(&work_line[4])) ); trim_spaces(&work_line[0]); /* parse double removes the first token from the line and trims it up */ status = parse_double(work_line, ¬e_total_len); /* rest of line is ignored */ status = write_a_rest( q_note_len * note_total_len, /* seconds */ f2 /* file */ ); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("wave ", work_line, 5)) { parse_found = 1; /* parse_wave */ /* toss the "wave" token and the one known space after it */ memmove( work_line, &work_line[5], (1 + strlen(&work_line[5])) ); trim_spaces(work_line); /* which wave */ which_wave = NO_SUCH_WAVE; if (0 eq strcmp(work_line, "sawtooth")) { which_wave = SAWTOOTH_WAVE; } if (0 eq strcmp(work_line, "square")) { which_wave = SQUARE_WAVE; } if (0 eq strcmp(work_line, "triangle")) { which_wave = TRIANGLE_WAVE; } if (0 eq strcmp(work_line, "sine")) { which_wave = SINE_WAVE; } if (0 eq strcmp(work_line, "circle")) { which_wave = CIRCLE_WAVE; } if (0 eq strcmp(work_line, "line")) { which_wave = LINE_WAVE; } if (which_wave eq LINE_WAVE) render_line_wave(); else { if (which_wave eq NO_SUCH_WAVE) { fprintf( stderr, "warning: unknown wave \"%s\" - ignoring\n", work_line ); } else { render_wave(); } } parse_found = 1; } /* end wave handler */ if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("delay_regen ", work_line, 12)) { parse_found = 1; /* toss the "delay_regen" token and the one known space after it */ memmove( work_line, &work_line[11], (1 + strlen(&work_line[11])) ); trim_spaces(work_line); status = parse_double(work_line, &delay_regen); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("delay_level", work_line, 11)) { parse_found = 1; /* toss the "delay_level" token and the one known space after it */ memmove( work_line, &work_line[11], (1 + strlen(&work_line[11])) ); trim_spaces(work_line); status = parse_double(work_line, &delay_level); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("delay_time", work_line, 10)) { parse_found = 1; /* toss the "delay_time" token and the one known space after it */ memmove( work_line, &work_line[10], (1 + strlen(&work_line[10])) ); trim_spaces(work_line); status = parse_double(work_line, &delay_time); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("env_attack_rate ", work_line, 16)) { parse_found = 1; /* toss the "env_attack_rate" token and the one known space after it */ memmove( work_line, &work_line[16], (1 + strlen(&work_line[16])) ); trim_spaces(work_line); status = parse_double(work_line, &d_temp); if (d_temp < 0.000001) { fprintf(stderr, "env attack rate must be > 0.0\n"); exit(DOOPS); } d_temp = 1.0 / d_temp; env_attack_time = (int) d_temp; parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("env_attack_time ", work_line, 16)) { parse_found = 1; /* toss the "env_attack_time" token and the one known space after it */ memmove( work_line, &work_line[16], (1 + strlen(&work_line[16])) ); trim_spaces(work_line); status = parse_double(work_line, &d_temp); if (d_temp < 0.0) { fprintf(stderr, "env attack time must be > 0.0 seconds\n"); exit(DOOPS); } d_temp = d_temp * 44100.0; env_attack_time = (int) d_temp; parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("env_punch_time ", work_line, 15)) { parse_found = 1; /* toss the "env_punch_time" token and the one known space after it */ memmove( work_line, &work_line[15], (1 + strlen(&work_line[15])) ); trim_spaces(work_line); status = parse_double(work_line, &d_temp); d_temp *= 44100.0; env_punch_time = (int) d_temp; fprintf(stderr, "debug: env_punch_time in samples is: %d\n", env_punch_time ); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("env_decay_time", work_line, 14)) { parse_found = 1; /* toss the "env_decay_time" token and the one known space after it */ memmove( work_line, &work_line[14], (1 + strlen(&work_line[14])) ); trim_spaces(work_line); status = parse_double(work_line, &d_temp); d_temp *= 44100.0; env_decay_time = (int) d_temp; parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("env_sustain_level", work_line, 17)) { parse_found = 1; /* toss the "env_sustain_level" token and the one known space after it */ memmove( work_line, &work_line[17], (1 + strlen(&work_line[17])) ); trim_spaces(work_line); status = parse_double(work_line, &env_sustain_level); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("env_release_time ", work_line, 17)) { parse_found = 1; /* toss the "env_release_time" token and the one known space after it */ memmove( work_line, &work_line[17], (1 + strlen(&work_line[17])) ); trim_spaces(work_line); status = parse_double(work_line, &d_temp); d_temp *= 44100.0; env_release_time = (int) d_temp; parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("phase_distort ", work_line, 14)) { parse_found = 1; /* toss the "phase_distort" token and the one known space after it */ memmove( work_line, &work_line[14], (1 + strlen(&work_line[14])) ); trim_spaces(work_line); status = parse_double(work_line, &phase_distort); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("env_to_phase_distort ", work_line, 21)) { parse_found = 1; /* toss the "env_to_phase_distort" token and the one known space after it */ memmove( work_line, &work_line[21], (1 + strlen(&work_line[21])) ); trim_spaces(work_line); status = parse_double(work_line, &env_to_phase_distort); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("filter_cutoff ", work_line, 14)) { parse_found = 1; /* toss the "filter_cutoff" token and the one known space after it */ memmove( work_line, &work_line[13], (1 + strlen(&work_line[13])) ); trim_spaces(work_line); status = parse_double(work_line, &filter_cutoff); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("filter_feedback ", work_line, 16)) { parse_found = 1; /* toss the "filter_feedback" token and the one known space after it */ memmove( work_line, &work_line[15], (1 + strlen(&work_line[15])) ); trim_spaces(work_line); status = parse_double(work_line, &filter_feedback); if (filter_feedback < 0.0) filter_feedback = 0.0; if (filter_feedback > 10.0) { fprintf( stderr, "warning: a filter_feedback of %f is rather a bit much\n", filter_feedback ); } parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("env_to_filter_cutoff", work_line, 20)) { parse_found = 1; /* toss the "env_to_filter_cutoff" token and the one known space after it */ memmove( work_line, &work_line[20], (1 + strlen(&work_line[20])) ); trim_spaces(work_line); status = parse_double(work_line, &env_to_filter_cutoff); /* note: env_to_filter_cutoff can be negative (horns sounds, etc) */ if (env_to_filter_cutoff > 1.0) { fprintf(stderr, "env_to_filter_cutoff value cannot be more than 1.0\n"); exit(DOOPS); } if (env_to_filter_cutoff < -1.0) { fprintf(stderr, "env_to_filter_cutoff value cannot be less than -1.0\n"); exit(DOOPS); } parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("lfo_pitch", work_line, 9)) { parse_found = 1; /* toss the "lfo_pitch" token and the one known space after it */ memmove( work_line, &work_line[9], (1 + strlen(&work_line[9])) ); trim_spaces(work_line); status = parse_double(work_line, &lfo_pitch); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("lfo_to_pitch ", work_line, 13)) { parse_found = 1; /* toss the "lfo_to_pitch" token and the one known space after it */ memmove( work_line, &work_line[13], (1 + strlen(&work_line[13])) ); trim_spaces(work_line); status = parse_double(work_line, &lfo_to_pitch); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("lfo_to_phase_distort ", work_line, 21)) { parse_found = 1; /* toss the "lfo_to_phase_distort" token and the one known space after it */ memmove( work_line, &work_line[21], (1 + strlen(&work_line[21])) ); trim_spaces(work_line); status = parse_double(work_line, &lfo_to_phase_distort); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("fine_tune", work_line, 9)) { /* toss the "fine_tune" token and the one known space after it */ memmove( work_line, &work_line[9], (1 + strlen(&work_line[9])) ); trim_spaces(work_line); status = parse_double(work_line, &fine_tune); if ((fine_tune > 1.001) || (fine_tune < -1.001)) { fprintf(stderr, "parse error: fine_tune is outside of range -1.0 \ to 1.0\n"); exit(DOOPS); } parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("transpose", work_line, 9)) { /* toss the "transpose" token and the one known space after it */ memmove( work_line, &work_line[9], (1 + strlen(&work_line[9])) ); trim_spaces(work_line); status = parse_int(work_line, &transpose); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("macro", work_line, 5)) { /* toss the "macro" token and the one known space after it */ memmove( work_line, &work_line[5], (1 + strlen(&work_line[5])) ); trim_spaces(work_line); status = parse_macro(f1[file_index], work_line); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("call", work_line, 4)) { parse_found = 1; /* toss the "call" token and the one known space after it */ memmove( work_line, &work_line[4], (1 + strlen(&work_line[4])) ); trim_spaces(work_line); status = parse_call(work_line, f2); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("sample ", work_line, 7)) { parse_found = 1; /* toss the "sample" token and the one known space after it */ memmove( work_line, &work_line[6], (1 + strlen(&work_line[6])) ); trim_spaces(work_line); status = parse_sample(work_line); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("sample_note", work_line, 11)) { parse_found = 1; /* toss the "sample_note" token and the one known space after it */ memmove( work_line, &work_line[11], (1 + strlen(&work_line[11])) ); trim_spaces(work_line); status = parse_sample_note(work_line); parse_found = 1; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("sample_volume", work_line, 13)) { parse_found = 1; /* toss the "sample_volume" token and the one known space after it */ memmove( work_line, &work_line[13], (1 + strlen(&work_line[13])) ); trim_spaces(work_line); status = parse_double(work_line, &sample_volume); if ((sample_volume < 0.0) || (sample_volume > 1.0)) { fprintf(stderr, "sample_volume must be in inclusive range \ 0.0 to 1.0\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("volume ", work_line, 7)) { parse_found = 1; /* toss the "volume" token and the one known space after it */ memmove( work_line, &work_line[6], (1 + strlen(&work_line[6])) ); trim_spaces(work_line); status = parse_double(work_line, &volume); if ((sample_volume < 0.0) || (volume > 2.0)) { fprintf(stderr, "volume must be in inclusive range 0.0 to 2.0\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("unison_detune ", work_line, 14)) { parse_found = 1; /* toss the "unison_detune" token and the one known space after it */ memmove( work_line, &work_line[13], (1 + strlen(&work_line[13])) ); trim_spaces(work_line); status = parse_double(work_line, &unison_detune_up); if ((unison_detune_up < 0.0) || (unison_detune_up > 1.0)) { fprintf(stderr, "unison_detune must be in inclusive range 0.0 \ to 1.0\n"); exit(DOOPS); } unison_detune_down = 1.0 + (unison_detune_up * unison_detune_scale); unison_detune_downmore = 1.0 + (unison_detune_up * (2.0 * unison_detune_scale)); unison_detune_up = 1.0 / unison_detune_down; unison_detune_upmore = 1.0 / unison_detune_downmore; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("unison_mix ", work_line, 11)) { parse_found = 1; /* toss the "unison_mix" token and the one known space after it */ memmove( work_line, &work_line[10], (1 + strlen(&work_line[10])) ); trim_spaces(work_line); status = parse_double(work_line, &unison_mix); if ((unison_mix < 0.0) || (unison_mix > 1.0)) { fprintf(stderr, "unison_mix must be in inclusive range 0.0 to 1.0\n"); exit(DOOPS); } update_osc_levels(); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("unison_spread ", work_line, 14)) { parse_found = 1; /* toss the "unison_spread" token and the one known space after it */ memmove( work_line, &work_line[14], (1 + strlen(&work_line[14])) ); trim_spaces(work_line); status = parse_double(work_line, &unison_spread); if ((unison_spread < 0.0) || (unison_spread > 1.0)) { fprintf(stderr, "unison_spread must be in inclusive range 0.0 to 1.0\n"); exit(DOOPS); } update_osc_levels(); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("sub_osc_level ", work_line, 14)) { parse_found = 1; /* toss the "sub_osc_level" token and the one known space after it */ memmove( work_line, &work_line[14], (1 + strlen(&work_line[14])) ); trim_spaces(work_line); status = parse_double(work_line, &sub_osc_level); if ((unison_mix < 0.0) || (unison_mix > 1.0)) { fprintf(stderr, "sub_osc_level must be in inclusive range 0.0 to 1.0\n"); exit(DOOPS); } update_osc_levels(); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("add24 ", work_line, 6)) { int xx; parse_found = 1; /* toss the "add24" token and the one known space after it */ memmove( work_line, &work_line[6], (1 + strlen(&work_line[6])) ); /* there must be 24 doubles (the volume for each harmonic) following the "add24" token */ for (xx = 0; xx < 24; xx++) { trim_spaces(work_line); status = parse_double(work_line, &harmonic[xx]); if ((harmonic[xx] < 0.0) || (harmonic[xx] > 1.0)) { fprintf(stderr, "all harmonics must be in inclusive range 0.0 to \ 1.0\n"); exit(DOOPS); } } render_add_24(); /* build waveform using the 24 harmonics */ } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("bit_wave ", work_line, 9)) { int xx; parse_found = 1; /* toss the "bit_wave" token and the one known space after it */ memmove( work_line, &work_line[9], (1 + strlen(&work_line[9])) ); trim_spaces(&work_line[0]); render_bit_wave(&work_line[0]); /* build waveform using the bit pattern */ } if (parse_found) continue; /* skip rest of loop */ if (0 eq strcmp("alias_vx", work_line)) { dave_smith_alias(); /* 12 bit, 128 samples per wavelength */ parse_found++; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strcmp("alias_8bit", work_line)) { chip_mod_alias(); /* 8 bit, 64 samples per wavelength */ parse_found++; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("up_phase ", work_line, 9)) { int xx; parse_found = 1; /* toss the "up_phase" token and the one known space after it */ memmove( work_line, &work_line[9], (1 + strlen(&work_line[9])) ); trim_spaces(&work_line[0]); parse_double(work_line, &unison_up_osc_phase); if ((unison_up_osc_phase < 0.0) || (unison_up_osc_phase > 1.0)) { fprintf(stderr, "error: up_phase must be 0.0 - 1.0 only\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("down_phase ", work_line, 11)) { int xx; parse_found = 1; /* toss the "down_phase" token and the one known space after it */ memmove( work_line, &work_line[11], (1 + strlen(&work_line[11])) ); trim_spaces(&work_line[0]); parse_double(work_line, &unison_down_osc_phase); if ((unison_down_osc_phase < 0.0) || (unison_down_osc_phase > 1.0)) { fprintf(stderr, "error: down_phase must be 0.0 - 1.0 only\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("upmore_phase ", work_line, 13)) { int xx; parse_found = 1; /* toss the "upmore_phase" token and the one known space after it */ memmove( work_line, &work_line[13], (1 + strlen(&work_line[13])) ); trim_spaces(&work_line[0]); parse_double(work_line, &unison_upmore_osc_phase); if ((unison_upmore_osc_phase < 0.0) || (unison_upmore_osc_phase > 1.0)) { fprintf(stderr, "error: upmore_phase must be 0.0 - 1.0 only\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("downmore_phase ", work_line, 15)) { int xx; parse_found = 1; /* toss the "downmore_phase" token and the one known space after it */ memmove( work_line, &work_line[15], (1 + strlen(&work_line[15])) ); trim_spaces(&work_line[0]); parse_double(work_line, &unison_downmore_osc_phase); if ((unison_downmore_osc_phase < 0.0) || (unison_downmore_osc_phase > 1.0)) { fprintf(stderr, "error: downmore_phase must be 0.0 - 1.0 only\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("osc_phase ", work_line, 10)) { int xx; parse_found = 1; /* toss the "osc_phase" token and the one known space after it */ memmove( work_line, &work_line[10], (1 + strlen(&work_line[10])) ); trim_spaces(&work_line[0]); parse_double(work_line, &osc_phase); if ((osc_phase < 0.0) || (osc_phase > 1.0)) { fprintf(stderr, "error: osc_phase must be 0.0 - 1.0 only\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("lfo_phase ", work_line, 10)) { int xx; parse_found = 1; /* toss the "lfo_phase" token and the one known space after it */ memmove( work_line, &work_line[10], (1 + strlen(&work_line[10])) ); trim_spaces(&work_line[0]); parse_double(work_line, &lfo_phase); if ((lfo_phase < 0.0) || (lfo_phase > 1.0)) { fprintf(stderr, "error: lfo_phase must be 0.0 - 1.0 only\n"); exit(DOOPS); } lfo_phase = fmod(lfo_phase, 1.0); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("sync ", work_line, 5)) { int xx; parse_found = 1; /* toss the "sync" token and the one known space after it */ memmove( work_line, &work_line[5], (1 + strlen(&work_line[5])) ); trim_spaces(&work_line[0]); parse_double(work_line, &pseudo_sync); if ((pseudo_sync < 0.5) || (pseudo_sync > 2.0)) { fprintf(stderr, "error: sync must be 0.5 - 2.0 only\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("filter_volume ", work_line, 14)) { int xx; parse_found = 1; /* toss the "filter_volume" token and the one known space after it */ memmove( work_line, &work_line[14], (1 + strlen(&work_line[14])) ); trim_spaces(&work_line[0]); parse_double(work_line, &filter_volume); if ((filter_volume < -1.0) || (filter_volume > 1.0)) { fprintf(stderr, "error: filter_volume must be -1.0 - 1.0 only\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("lfo_to_sync ", work_line, 12)) { int xx; parse_found = 1; /* toss the "lfo_to_sync" token and the one known space after it */ memmove( work_line, &work_line[12], (1 + strlen(&work_line[12])) ); trim_spaces(&work_line[0]); parse_double(work_line, &lfo_to_pseudo_sync); if ((lfo_to_pseudo_sync < -1.0) || (lfo_to_pseudo_sync > 1.0)) { fprintf(stderr, "error: lfo_to_sync must be -1.0 - 1.0 only\n"); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq stricmp("init_line", work_line)) { parse_found = 1; clear_line_nodes(); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("add_line ", work_line, 9)) { double l1, l2, dur; parse_found = 1; /* toss the "add_line" token and the one known space after it */ memmove( work_line, &work_line[9], (1 + strlen(&work_line[9])) ); trim_spaces(&work_line[0]); parse_double(work_line, &l1); if ((l1 < -1.0) || (l1 > 1.0)) { fprintf(stderr, "error: %f must be -1.0 - 1.0 only\n", l1); exit(DOOPS); } parse_double(work_line, &l2); if ((l2 < -1.0) || (l2 > 1.0)) { fprintf(stderr, "error: %f must be -1.0 - 1.0 only\n", l2); exit(DOOPS); } parse_double(work_line, &dur); if (dur < -1.0) { fprintf(stderr, "error: line duration must not be negative\n"); exit(DOOPS); } add_line_node(l1, l2, dur); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strcmp("mod1_list_init", work_line)) { parse_found = 1; clear_eg_var1_list(); /* erase mod1 list */ } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("mod1_list_add ", work_line, 14)) { double el; double dur; parse_found = 1; /* toss the "mod1_list_add" token and the one known space after it */ memmove( work_line, &work_line[14], (1 + strlen(&work_line[14])) ); trim_spaces(&work_line[0]); parse_double(work_line, &el); /* endlevel (double float) */ if ((el < -1.0) || (el > 1.0)) { fprintf(stderr, "mod1_list_add error: %f must be -1.0 - 1.0 \ only\n", el); exit(DOOPS); } trim_spaces(&work_line[0]); parse_double(work_line, &dur); if (dur < 0.0) { fprintf(stderr, "mod1_list_add error: %f must be positive\n", dur); exit(DOOPS); } /* right now dur is the duration in qaurter notes at the current BPM. Convert that to seconds then to samples at 44100 Hz */ dur *= q_note_len; /* now dur is in seconds */ dur *= 44100; /* now dur is in samples */ add_eg_var1_node((unsigned int) dur, el); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("mod1_to_filter_cutoff ", work_line, 22)) { parse_found = 1; /* toss the "mod1_to_filter_cutoff" token and the one known space after it */ memmove( work_line, &work_line[22], (1 + strlen(&work_line[22])) ); trim_spaces(&work_line[0]); parse_double(work_line, &eg_var1_to_filter_cutoff); if ((eg_var1_to_filter_cutoff < -1.0) || (eg_var1_to_filter_cutoff > 1.0)) { fprintf( stderr, "mod1_to_filter_cutoff error: %f must be -1.0 - 1.0 only\n", eg_var1_to_filter_cutoff ); exit(DOOPS); } } if (parse_found) continue; /* skip rest of loop */ if (0 eq stricmp("mod1_trigger", work_line)) { parse_found = 1; retrigger_eg_var1(); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("noise_volume ", work_line, 13)) { parse_found = 1; /* toss the "noise_volume" token and the one known space after it */ memmove( work_line, &work_line[13], (1 + strlen(&work_line[13])) ); trim_spaces(&work_line[0]); parse_double(work_line, &noise_level); if ((noise_level < -1.0) || (noise_level > 1.0)) { fprintf( stderr, "noise_volume error: %f must be -1.0 - 1.0 only\n", noise_level ); exit(DOOPS); } noise_non_level = 1.0 - noise_level; /* saves later work */ } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("updown ", work_line, 7)) { double upd_temp_d; int upd_temp; parse_found = 1; /* toss the "updown" token and the one known space after it */ memmove( work_line, &work_line[7], (1 + strlen(&work_line[7])) ); trim_spaces(&work_line[0]); parse_double(work_line, &upd_temp_d); if ((upd_temp_d < 0.0) || (upd_temp_d > 1.0)) { fprintf(stderr, "updown number must be in 0.001 - 1.0 range\n"); exit(DOOPS); } if (upd_temp_d < 0.001) { /* turn the effect off */ use_updown = 0; } else { /* turn the effect on */ upd_temp_d = upd_temp_d * (60.0 / track_bpm); /* length in quarter notes */ upd_temp_d *= 44100.0; /* length in samples */ upd_temp_d /= 2.0; /* one half wavelength */ updown_count = updown_count_length = (int) upd_temp_d; updown_phase = 0; use_updown = 1; } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("portmento_time ", work_line, 15)) { double pm_temp_d; parse_found = 1; /* toss the "portmento_time" token and the one known space after it */ memmove( work_line, &work_line[15], (1 + strlen(&work_line[15])) ); trim_spaces(&work_line[0]); parse_double(work_line, &pm_temp_d); if ((pm_temp_d < 0.0) || (pm_temp_d > 4.0)) { fprintf(stderr, "portmento_time must be in 0.0 - 4.0 range\n"); exit(DOOPS); } /* pm_temp_d is time in quarter_notes */ pm_temp_d *= (60.0 / track_bpm); /* time in seconds */ pm_temp_d *= 44100.0; /* time in samples */ portmento_time = (int) pm_temp_d; /* integer time in samples */ if (portmento_time < 2) { portmento_state = 5; /* entirely off, no portmento at all */ } } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("high_pass_pitch ", work_line, 16)) { parse_found = 1; /* toss the "high_pass_pitch" token and the one known space after it */ memmove( work_line, &work_line[16], (1 + strlen(&work_line[16])) ); trim_spaces(&work_line[0]); parse_double(work_line, &hp_cut); if ((hp_cut < 0.0001) || (hp_cut > 22050.0)) { fprintf(stderr, "high pass pitch must be in 0.0001 - 22050.0 Hz range\n"); exit(DOOPS); } hp_cut = hp_cut / 44100.0; hp_cut_i = 1.0 - hp_cut; } if (parse_found) continue; /* skip rest of loop */ if (0 eq stricmp("high_pass_clear", &work_line[0])) { parse_found = 1; hp_mem = hp_mem2 = 0.0; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("ext_in_volume ", work_line, 14)) { parse_found = 1; /* toss the "ext_in_volume" token and the one known space after it */ memmove( work_line, &work_line[14], (1 + strlen(&work_line[14])) ); trim_spaces(&work_line[0]); parse_double(work_line, &ext_in_volume); if ((ext_in_volume < -1.0) || (ext_in_volume > 1.0)) { fprintf(stderr, "ext_in_volume must be in the -1.0 - 1.0 range\n"); exit(DOOPS); } ext_in_non_volume = 1.0 - fabs(ext_in_volume); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strncmp("ext_in ", work_line, 7)) { parse_found = 1; /* toss the "ext_in " token and the one known space after it */ memmove( work_line, &work_line[7], (1 + strlen(&work_line[7])) ); trim_spaces(&work_line[0]); parse_ext_in(work_line); } if (parse_found) continue; /* skip rest of loop */ if (0 eq strcmp("enable_volume_envelope", &work_line[0])) { use_volume_envelope = 1; parse_found++; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strcmp("disable_volume_envelope", &work_line[0])) { use_volume_envelope = 0; parse_found++; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strcmp("swap_osc_sub", &work_line[0])) { /* swap the waveforms of hte oscialtor and the sub oscilator */ swap_osc_sub(); parse_found++; } if (parse_found) continue; /* skip rest of loop */ if (0 eq strcmp("copy_osc_sub", &work_line[0])) { /* copy oscillator waveform to sub_oscillator */ memcpy(sub_wave, wave, sizeof(double) * waveform_size); parse_found++; } if (parse_found) continue; /* skip rest of loop */ /* program will ignore any lines with invalid syntax, including empty lines. as an aid to debugging, non-empty lines of invalid content will be echoed to stderr */ if (strlen(&work_line[0])) { fprintf(stderr, "%s\n", &work_line[0]); /* print erronious line */ } } /* end while */ if (out_pipe eq 0) fclose(f2); if (sample_buffer neq NULL) free(sample_buffer); if (in_pipe eq 0) fclose(f1[file_index]); if (ext_in_file neq NULL) fclose(ext_in_file); fprintf(stderr, "done\n"); exit(OKDOKEY); } /* actual end of this file */