Forgot to add new files.
[cantaveria.git] / synth.c
blob50c82c9a92c9450ef30f75154ca84eac5ef0c334
1 /*
2 Cantaveria - action adventure platform game
3 Copyright (C) 2009 2010 Evan Rinehart
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to
18 The Free Software Foundation, Inc.
19 51 Franklin Street, Fifth Floor
20 Boston, MA 02110-1301, USA
22 evanrinehart@gmail.com
26 the synth module exports a mixer callback for the audio output.
27 it uses support from orc (instruments) and seq (sequencer) to
28 generate that output.
29 * generate N samples of stereo output
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <math.h>
38 #include <util.h>
39 #include <list.h>
40 #include <midi.h>
41 #include <seq.h>
42 #include <synth.h>
43 #include <orc.h>
46 timing stuff
48 ticks - 384 per beat (tpb)
49 beats - 120 per minute (bpm)
50 samples - 22050 per second (srate)
52 control happens on tick boundaries
53 control must take effect on nearest sample boundary
55 samples per tick = (srate*60) / (bpm*tpb)
56 which in the above example = 28 and 32760/46080
60 typedef struct {
61 float L, R, V;
62 mix_callback mix;
63 control_callback control;
64 cleanup_callback cleanup;
65 void* data;
66 } channel;
68 int srate;
70 int serr = 0; // 1/1000 of a sample
72 int tick;
73 int terr = 0; // 1/(bpm*tpb) of a sample
74 int terrd = 46080; //bpm * tpb
77 channel channels[16];
88 void dummy_mix(void* v, float f[], int i){}
89 void dummy_control(void* v, int a, int b, int c, int d){}
90 void dummy_cleanup(void* v){}
92 channel make_dummy_channel(){
93 channel ch;
94 ch.L = 1;
95 ch.R = 1;
96 ch.V = 1;
97 ch.mix = dummy_mix;
98 ch.cleanup = dummy_cleanup;
99 ch.control = dummy_control;
100 ch.data = NULL;
101 return ch;
104 channel make_channel_from_instrument(enum instrument_name name){
105 channel ch = make_dummy_channel();
106 instrument ins = orc_load(name);
107 ch.mix = ins.mix;
108 ch.control = ins.control;
109 ch.cleanup = ins.cleanup;
110 ch.data = ins.data;
111 return ch;
116 void set_music_volume(int percent){
120 void cut_music(){
124 void fade_clear(){
128 void fadeout(int seconds){
134 void mix(channel* ch, float in[], float left[], float right[], int count){
135 int i;
136 for(i=0; i<count; i++){
137 left[i] += in[i] * ch->L * ch->V;
138 right[i] += in[i] * ch->R * ch->V;
142 void zero(float buf[], int count){
143 int i;
144 for(i=0; i<count; i++){
145 buf[i] = 0;
149 void reduce(float buf[], int count, float factor){
150 int i;
151 for(i=0; i<count; i++){
152 buf[i] /= factor;
156 void clip(float buf[], int count){
157 int i;
158 int clipped = 0;
159 float avg = 0;
160 for(i=0; i<count; i++){
161 avg += buf[i]*buf[i];
162 if(buf[i] > 1.0){
163 clipped = 1;
164 buf[i] = 1.0;
166 else if(buf[i] < -1.0) buf[i] = -1.0;
169 if(clipped){
170 error_msg("synth: clipping distortion due to output overload\n");
173 avg /= count?count:1;
174 avg = sqrt(avg);
177 void generate(float left[], float right[], int count){
178 float buf[4096];
179 int i;
180 float V = 1.0f;
182 zero(left, count);
183 zero(right, count);
184 for(i=0; i<16; i++){
185 channel* ch = &(channels[i]);
186 zero(buf, count);
187 ch->mix(ch->data, buf, count);
188 mix(ch, buf, left, right, count);
190 reduce(left, count, 16.0f/V);
191 reduce(right, count, 16.0f/V);
192 clip(left, count);
193 clip(right, count);
196 void control(event* e){
197 if(e == NULL) return;
198 int chan = e->chan;
199 int type = e->type;
200 int val1 = e->val1;
201 int val2 = e->val2;
202 int val = (e->val2 << 7) | e->val1;
203 channel* ch = &(channels[chan]);
205 switch(type){
206 case EVX_MUSICVOLUME: set_music_volume(val1); break;
207 case EVX_MUSICCUT: cut_music(); break;
208 case EVX_FADECLEAR: fade_clear(); break;
209 case EVX_FADEOUT: fadeout(val1); break;
210 default: ch->control(ch->data, type, val1, val2, val); break;
214 void immediate_control(){
215 event* e = seq_get_immediate();
216 while(e){
217 control(e);
218 e = seq_get_immediate();
222 void synth_generate(float left[], float right[], int samples){
223 int i=0;
224 int remaining = samples;
225 int used = 0;
226 event* e = NULL;
228 immediate_control();
230 while(remaining > 0){
231 e = seq_advance(remaining, &used);
232 generate(left+i, right+i, used);
233 control(e);
234 i += used;
235 remaining -= used;
236 if(e == NULL && used == 0){
237 error_msg("synth: sequencer failed to advance or provide a control event.");
238 break;
244 void synth_init(){
245 int i;
247 orc_init(SAMPLE_RATE);
249 for(i=0; i<16; i++){
250 channels[i] = make_dummy_channel();
253 //channels[0] = make_channel_from_instrument(ORC_KARPLUS);
254 //channels[1] = make_channel_from_instrument(ORC_KARPLUS);
255 //channels[2] = make_channel_from_instrument(ORC_KARPLUS);
256 //channels[3] = make_channel_from_instrument(ORC_KARPLUS);
257 channels[0] = make_channel_from_instrument(ORC_DEFAULT);
258 channels[1] = make_channel_from_instrument(ORC_DEFAULT);
259 channels[2] = make_channel_from_instrument(ORC_DEFAULT);
260 channels[3] = make_channel_from_instrument(ORC_DEFAULT);