#include "instrument.h" #include #include /* A hard limit */ #define REVERB_MAX 0 /* This is not exposed to other files, as this should be controlled by individual instruments. */ typedef struct { NUMT freq; NUMT amp; unsigned char pred; /* if this is 0, then no frequency modulation is present. */ } FM; /* By similar reasoning, ADSR is not exposed either. */ typedef struct { NUMT attack_p; NUMT decay_p; NUMT sustain_p; NUMT release_p; NUMT sustain_level; unsigned char pred; /* If this is 0 then the instrument does not adapt the ADSR model. */ } ADSR; /* NOTE: The default method for an instrument to produce sounds is in accordance with its FM, ADSR, and Fourier coefficients. But individual instruments can override that default method by providing their own functions. This way we would have more flexibility. */ struct Instrument_s { NUMT *sin_coefs; /* coefficients of sin in the Fourier expansion. */ NUMT *cos_coefs; /* coefficients of cos in the Fourier expansion. */ sound_t sound; /* the function to produce sounds. */ merger_t merger; /* the function to merge waves. */ NUMT reverb; /* The percentage of reverberation. */ FM fm; ADSR adsr; }; WaveFrag make_sound(Instrument * in, Volume v, Hertz h, Seconds s) { return in->sound(in, v, h, s); } U_ATTR WaveFrag merge_waves(Instrument *in, WaveFrag *frags, LENT len, int step) { return in->merger(in, frags, len, step); } /* Now comes various instruments */ /* The first is piano */ UH_ATTR static NUMT smooth_step(NUMT t) { if (t <= 0.0) { return 0.0; } else if (t >= 1.0) { return 1.0; } else { return 6*pow(t, 5)-15*pow(t, 4)+10*pow(t, 3); } } UH_ATTR static NUMT old_piano_sound_internal(Instrument *piano, NUMT theta) { return sin(theta)+1.869*sin(2.0*theta)+0.042*sin(3.0*theta) +0.022*sin(4.0*theta)+cos(theta); } UH_ATTR static NUMT piano_sound_internal(Instrument *piano, NUMT theta) { return (*(piano->sin_coefs) * sin(theta)+ *(piano->sin_coefs+1) * sin(2.0*theta)+ *(piano->sin_coefs+2) * sin(3.0*theta))* exp(-0.004*theta); } UH_ATTR WaveFrag piano_sound(void * in, Volume v, Hertz h, Seconds s) { Instrument *piano = (Instrument *)in; NUMT step = (NUMT) (h * 2 * M_PI) / SAMPLE_RATE; LENT sample_num = (LENT) floor(SAMPLE_RATE * s); Wave w = MYALLOC(Pulse, sizeof(*w) * sample_num); NUMT theta = 0.0, temp = 0.0; for (LENT i = 0; i < sample_num; i++, theta += step) { temp = piano_sound_internal(piano, theta); temp += temp*temp*temp; *(w+i) = (NUMT) v * piano_sound_internal(piano, temp); } return (WaveFrag) { w, sample_num }; } /* Just concatenates waves together. */ UH_ATTR WaveFrag simple_merger(void *in, WaveFrag *frags, LENT len, int step) { WaveFrag wf; LENT total_len = 0; for (LENT i = 0; i < len; i += step) total_len += (frags+i)->n; wf.n = total_len; wf.w = MYALLOC(Pulse, total_len); for (LENT i = 0, counter = 0, local = 0; i < len && counter < total_len; i += step) { local = (frags+i)->n; for (LENT j = 0; j < local;) *(wf.w+counter++) = *((frags+i)->w+j++); free((frags+i)->w); } free(frags); return wf; } Instrument *make_piano() { Instrument *ins = malloc(sizeof *ins * 1); /* The piano's sound is simulated directly by some coefficients. */ ins->fm.pred = 0; ins->adsr.pred = 0; ins->sin_coefs = malloc(sizeof(*(ins->sin_coefs)) * 3); ins->cos_coefs = malloc(sizeof(*(ins->cos_coefs)) * 1); ins->reverb = 0.0; *(ins->sin_coefs) = 0.65; *(ins->sin_coefs+1) = 0.45; *(ins->sin_coefs+2) = 0.1; *(ins->cos_coefs) = 1; ins->sound = piano_sound; ins->merger = simple_merger; return ins; } void destroy_piano(Instrument *in) { free(in->sin_coefs); free(in->cos_coefs); free(in); } /* Then comes violin */ UH_ATTR Pulse violin_sound_internal(Instrument *in, NUMT theta) { return -1.0*fmod(theta, 2.0*M_PI)/M_PI + 1.0; /* return (*(in->sin_coefs)*sin(theta)+ * *(in->sin_coefs+1)*sin(2.0*theta)+ * *(in->sin_coefs+2)*sin(3.0*theta)+ * *(in->sin_coefs+3)*sin(4.0*theta)+ * *(in->sin_coefs+4)*sin(5.0*theta)); */ /* Pulse result = 0.0; * for (int i = 0; i < 4; i++) * result += *(in->sin_coefs+i) * sin((NUMT) (i+1)*theta); */ /* result = sin(theta); */ /* return result; */ } UH_ATTR NUMT violin_adsr(Instrument *violin, NUMT t) { if (t <= 0.0 || t > 1.0) return 0.0; if (t <= violin->adsr.attack_p) return log(1.0 + 2.7*(t / violin->adsr.attack_p)); else if (t <= violin->adsr.decay_p+violin->adsr.attack_p) return (t - violin->adsr.attack_p) / violin->adsr.decay_p; else if (t <= 1.0 - violin->adsr.release_p) return violin->adsr.sustain_level; else return 1.0 - (1.0 - t) / violin->adsr.release_p; } UH_ATTR WaveFrag violin_sound(void * in, Volume v, Hertz h, Seconds s) { Instrument *violin = (Instrument *)in; /* We produce a little more samples so that we can create the effect of reverberation later on. */ s *= (1.0+violin->reverb); NUMT step = (NUMT) (h * 2 * M_PI) / SAMPLE_RATE; LENT sample_num = (LENT) floor(SAMPLE_RATE * s); /* NUMT attack_step = 1.0 / (NUMT) (sample_num * violin->adsr.attack_p); * NUMT decay_step = 1.0 / (NUMT) (sample_num * violin->adsr.decay_p); * NUMT sustain_step = 1.0 / (NUMT) (sample_num * violin->adsr.sustain_p); * NUMT release_step = 1.0 / (NUMT) (sample_num * violin->adsr.release_p); */ Wave w = MYALLOC(Pulse, sizeof(*w) * sample_num); NUMT theta = 0.0, temp = 0.0, adsrcounter = 0.0; for (LENT i = 0; i < sample_num; i++, theta += step) { temp = violin_sound_internal(violin, theta); adsrcounter = violin_adsr(violin, (NUMT) i / sample_num); /* temp += temp*temp*temp; */ /* *(w+i) = (NUMT) v * temp * ((sustain_flag) ? SUSTAIN_LEVEL : adsrcounter); */ *(w+i) = (NUMT) v * temp * adsrcounter; /* switch (phase) { * case 0: /\* attack phase *\/ * adsrcounter += attack_step; * if (adsrcounter >= 1.0) { * adsrcounter = 1.0; * phase++; * } * break; * case 1: /\* decay phase *\/ * adsrcounter -= decay_step; * if (adsrcounter <= SUSTAIN_LEVEL) { * adsrcounter = 0.0; * sustain_flag = 1; * phase++; * } * break; * case 2: /\* sustain phase *\/ * adsrcounter += sustain_step; * if (adsrcounter >= 1.0) { * adsrcounter = SUSTAIN_LEVEL; * sustain_flag = 0; * phase++; * } * break; * default: /\* release phase *\/ * adsrcounter -= release_step; * if (adsrcounter <= 0.0) adsrcounter = 0.0; * break; * } */ } return (WaveFrag) { w, sample_num }; } UH_ATTR WaveFrag violin_merger(void *in, WaveFrag *frags, LENT len, int step) { Instrument *violin = (Instrument*) in; WaveFrag wf, local_last_wave = (WaveFrag) { NULL, 0 }; WaveFrag local_current_wave = (WaveFrag) { NULL, 0 }; WaveFrag local_mix_wave, local_mix_input[2]; LENT total_len = 0; NUMT reverb_ratio = 1.0 / (1.0 + violin->reverb); for (LENT i = 0; i < len; i += step) total_len += (LENT) ((frags+i)->n * reverb_ratio); wf.n = total_len; wf.w = MYALLOC(Pulse, total_len); for (LENT i = 0, counter = 0, local = 0, local_last = 0; i < len && counter < total_len; i += step) { /* The last fragment that we want to mix. */ local_last = (!i) ? 0 : (frags+i-1)->n - local; local = (LENT) ((frags+i)->n * reverb_ratio); /* Make sure everything stays within bounds. */ local_last = min(local_last, local); local_last = min(local_last, REVERB_MAX); if (local_last && i) { /* If we reach here then i > 0. */ local_last_wave.n = local_last; local_last_wave.w = realloc(local_last_wave.w, sizeof(Pulse) * local_last); local_current_wave.n = local_last; local_current_wave.w = realloc(local_current_wave.w, sizeof(Pulse) * local_last); for (LENT j = 0; j < local_last; j++) { *(local_last_wave.w+j) = *((frags+i-1)->w+(frags+i-1)->n-local_last+j); *(local_current_wave.w+j) = *((frags+i)->w+j); } local_mix_input[0] = local_last_wave; local_mix_input[1] = local_current_wave; local_mix_wave = mix_waves(local_mix_input, 2); for (LENT j = 0; j < local_last;) *(wf.w+counter++) = *(local_mix_wave.w+j++); free(local_mix_wave.w); for (LENT j = local_last; j < local;) *(wf.w+counter++) = *((frags+i)->w+j++); } else { for (LENT j = 0; j < local;) *(wf.w+counter++) = *((frags+i)->w+j++); } free((frags+i)->w); } free(local_last_wave.w); free(local_current_wave.w); free(frags); return wf; } UH_ATTR Instrument * make_violin() { Instrument * violin= malloc(sizeof *violin); violin->fm.pred = 0; violin->adsr.pred = 1; violin->reverb = 0.01; violin->adsr.attack_p = 0.09; violin->adsr.release_p = 0.09; violin->adsr.decay_p = 0.05; violin->adsr.sustain_p = max(1.0 - (violin->adsr.attack_p+ violin->adsr.decay_p+ violin->adsr.release_p), 0.0); violin->adsr.sustain_level = 0.9; double temp [] = { 1.0, 0.399064778, 0.229404484, 0.151836061, 0.196754229, 0.093742264, 0.060871957, 0.138605419, 0.010535002, 0.071021868, 0.029954614, 0.051299684, 0.055948288, 0.066208224, 0.010067391, 0.00753679, 0.008196947, 0.012955577, 0.007316738, 0.006216476, 0.005116215, 0.006243983, 0.002860679, 0.002558108, 0.0, 0.001650392 }; violin->sin_coefs = malloc(sizeof(NUMT)*26); for (int i = 0; i < 26; i++) *(violin->sin_coefs+i) = *(temp+i); /* *(violin->sin_coefs) = 1.0; * *(violin->sin_coefs+1) = 0.41; * *(violin->sin_coefs+2) = 0.35; * *(violin->sin_coefs+3) = 0.39; * *(violin->sin_coefs+4) = 0.45; * *(violin->sin_coefs+5) = 0.35; * *(violin->sin_coefs+6) = 0.5; * *(violin->sin_coefs+7) = 0.4; * *(violin->sin_coefs+8) = 0.35; * *(violin->sin_coefs+9) = 0.1; */ violin->sound = violin_sound; violin->merger = simple_merger; return violin; } UH_ATTR void destroy_violin(Instrument *in) { free(in->sin_coefs); free(in); }