Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 1 | /*************************************************************************** |
| 2 | * __________ __ ___. |
| 3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| 4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| 7 | * \/ \/ \/ \/ \/ |
Nils Wallménius | 0e49605 | 2007-09-24 15:57:32 +0000 | [diff] [blame] | 8 | * $Id$ |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 9 | * |
| 10 | * Copyright (C) 2005 Stepan Moskovchenko |
| 11 | * |
Daniel Stenberg | 2acc0ac | 2008-06-28 18:10:04 +0000 | [diff] [blame^] | 12 | * This program is free software; you can redistribute it and/or |
| 13 | * modify it under the terms of the GNU General Public License |
| 14 | * as published by the Free Software Foundation; either version 2 |
| 15 | * of the License, or (at your option) any later version. |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 16 | * |
| 17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| 18 | * KIND, either express or implied. |
| 19 | * |
| 20 | ****************************************************************************/ |
Nils Wallménius | 0e49605 | 2007-09-24 15:57:32 +0000 | [diff] [blame] | 21 | #include "plugin.h" |
| 22 | #include "guspat.h" |
| 23 | #include "midiutil.h" |
| 24 | #include "synth.h" |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 25 | |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 26 | extern struct plugin_api * rb; |
| 27 | |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 28 | void readTextBlock(int file, char * buf) |
| 29 | { |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 30 | char c = 0; |
| 31 | do |
| 32 | { |
| 33 | c = readChar(file); |
| 34 | } while(c == '\n' || c == ' ' || c=='\t'); |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 35 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 36 | rb->lseek(file, -1, SEEK_CUR); |
| 37 | int cp = 0; |
| 38 | do |
| 39 | { |
| 40 | c = readChar(file); |
| 41 | buf[cp] = c; |
| 42 | cp++; |
| 43 | } while (c != '\n' && c != ' ' && c != '\t' && !eof(file)); |
| 44 | buf[cp-1]=0; |
| 45 | rb->lseek(file, -1, SEEK_CUR); |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 46 | } |
| 47 | |
Stepan Moskovchenko | cd963d8 | 2007-11-03 04:09:38 +0000 | [diff] [blame] | 48 | void resetControllers() |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 49 | { |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 50 | int a=0; |
| 51 | for(a=0; a<MAX_VOICES; a++) |
| 52 | { |
| 53 | voices[a].cp=0; |
| 54 | voices[a].vol=0; |
| 55 | voices[a].ch=0; |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 56 | voices[a].isUsed=false; |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 57 | voices[a].note=0; |
| 58 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 59 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 60 | for(a=0; a<16; a++) |
| 61 | { |
| 62 | chVol[a]=100; /* Default, not quite full blast.. */ |
Nils Wallménius | e1940b8 | 2007-10-04 19:36:42 +0000 | [diff] [blame] | 63 | chPan[a]=64; /* Center */ |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 64 | chPat[a]=0; /* Ac Gr Piano */ |
Nils Wallménius | e1940b8 | 2007-10-04 19:36:42 +0000 | [diff] [blame] | 65 | chPW[a]=256; /* .. not .. bent ? */ |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 66 | chPBDepth[a]=2; /* Default bend value is 2 */ |
Stepan Moskovchenko | d33645b | 2007-10-17 03:48:24 +0000 | [diff] [blame] | 67 | chPBNoteOffset[a]=0; /* No offset */ |
| 68 | chPBFractBend[a]=65536; /* Center.. no bend */ |
Stepan Moskovchenko | 47d8323 | 2007-10-21 19:47:33 +0000 | [diff] [blame] | 69 | chLastCtrlMSB[a]=0; /* Set to pitch bend depth */ |
| 70 | chLastCtrlLSB[a]=0; /* Set to pitch bend depth */ |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 71 | } |
Stepan Moskovchenko | cd963d8 | 2007-11-03 04:09:38 +0000 | [diff] [blame] | 72 | } |
| 73 | |
| 74 | /* Filename is the name of the config file */ |
| 75 | /* The MIDI file should have been loaded at this point */ |
| 76 | int initSynth(struct MIDIfile * mf, char * filename, char * drumConfig) |
| 77 | { |
| 78 | char patchUsed[128]; |
| 79 | char drumUsed[128]; |
| 80 | int a=0; |
| 81 | |
| 82 | resetControllers(); |
| 83 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 84 | for(a=0; a<128; a++) |
| 85 | { |
| 86 | patchSet[a]=NULL; |
| 87 | drumSet[a]=NULL; |
| 88 | patchUsed[a]=0; |
| 89 | drumUsed[a]=0; |
| 90 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 91 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 92 | /* |
| 93 | * Always load the piano. |
| 94 | * Some files will assume its loaded without specifically |
| 95 | * issuing a Patch command... then we wonder why we can't hear anything |
| 96 | */ |
| 97 | patchUsed[0]=1; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 98 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 99 | /* Scan the file to see what needs to be loaded */ |
Stepan Moskovchenko | 8b6d287 | 2007-09-27 03:59:33 +0000 | [diff] [blame] | 100 | if(mf != NULL) |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 101 | { |
Stepan Moskovchenko | 8b6d287 | 2007-09-27 03:59:33 +0000 | [diff] [blame] | 102 | for(a=0; a<mf->numTracks; a++) |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 103 | { |
Stepan Moskovchenko | 8b6d287 | 2007-09-27 03:59:33 +0000 | [diff] [blame] | 104 | unsigned int ts=0; |
| 105 | |
| 106 | if(mf->tracks[a] == NULL) |
| 107 | { |
| 108 | printf("NULL TRACK !!!"); |
| 109 | rb->splash(HZ*2, "Null Track in loader."); |
| 110 | return -1; |
| 111 | } |
| 112 | |
| 113 | for(ts=0; ts<mf->tracks[a]->numEvents; ts++) |
| 114 | { |
| 115 | |
| 116 | if((getEvent(mf->tracks[a], ts)->status) == (MIDI_NOTE_ON+9)) |
| 117 | drumUsed[getEvent(mf->tracks[a], ts)->d1]=1; |
| 118 | |
| 119 | if( (getEvent(mf->tracks[a], ts)->status & 0xF0) == MIDI_PRGM) |
| 120 | patchUsed[getEvent(mf->tracks[a], ts)->d1]=1; |
| 121 | } |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 122 | } |
Stepan Moskovchenko | 8b6d287 | 2007-09-27 03:59:33 +0000 | [diff] [blame] | 123 | } else |
| 124 | { |
| 125 | /* Initialize the whole drum set */ |
| 126 | for(a=0; a<128; a++) |
| 127 | drumUsed[a]=1; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 128 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 129 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 130 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 131 | int file = rb->open(filename, O_RDONLY); |
Stepan Moskovchenko | 68af7ba | 2006-05-07 07:12:07 +0000 | [diff] [blame] | 132 | if(file < 0) |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 133 | { |
Jens Arnold | b1f0049 | 2007-04-21 05:35:17 +0000 | [diff] [blame] | 134 | printf(""); |
| 135 | printf("No MIDI patchset found."); |
| 136 | printf("Please install the instruments."); |
| 137 | printf("See Rockbox page for more info."); |
Stepan Moskovchenko | 68af7ba | 2006-05-07 07:12:07 +0000 | [diff] [blame] | 138 | |
Jens Arnold | 4d6374c | 2007-03-16 21:56:08 +0000 | [diff] [blame] | 139 | rb->splash(HZ*2, "No Instruments"); |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 140 | return -1; |
| 141 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 142 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 143 | char name[40]; |
| 144 | char fn[40]; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 145 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 146 | /* Scan our config file and load the right patches as needed */ |
| 147 | int c = 0; |
Jens Arnold | 79c8a8c | 2007-03-17 09:02:53 +0000 | [diff] [blame] | 148 | name[0] = '\0'; |
Jens Arnold | b1f0049 | 2007-04-21 05:35:17 +0000 | [diff] [blame] | 149 | printf("Loading instruments"); |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 150 | for(a=0; a<128; a++) |
| 151 | { |
| 152 | while(readChar(file)!=' ' && !eof(file)); |
| 153 | readTextBlock(file, name); |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 154 | |
Nils Wallménius | 04b3435 | 2007-09-10 09:46:36 +0000 | [diff] [blame] | 155 | rb->snprintf(fn, 40, ROCKBOX_DIR "/patchset/%s.pat", name); |
Stepan Moskovchenko | b2f1b5d | 2006-05-01 23:22:59 +0000 | [diff] [blame] | 156 | /* printf("\nLOADING: <%s> ", fn); */ |
Stepan Moskovchenko | 5811214 | 2005-04-15 20:27:04 +0000 | [diff] [blame] | 157 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 158 | if(patchUsed[a]==1) |
| 159 | { |
| 160 | patchSet[a]=gusload(fn); |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 161 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 162 | if(patchSet[a] == NULL) /* There was an error loading it */ |
| 163 | return -1; |
| 164 | } |
Stepan Moskovchenko | 5811214 | 2005-04-15 20:27:04 +0000 | [diff] [blame] | 165 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 166 | while((c != '\n')) |
| 167 | c = readChar(file); |
| 168 | } |
| 169 | rb->close(file); |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 170 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 171 | file = rb->open(drumConfig, O_RDONLY); |
Stepan Moskovchenko | 68af7ba | 2006-05-07 07:12:07 +0000 | [diff] [blame] | 172 | if(file < 0) |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 173 | { |
Jens Arnold | 4d6374c | 2007-03-16 21:56:08 +0000 | [diff] [blame] | 174 | rb->splash(HZ*2, "Bad drum config. Did you install the patchset?"); |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 175 | return -1; |
| 176 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 177 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 178 | /* Scan our config file and load the drum data */ |
| 179 | int idx=0; |
| 180 | char number[30]; |
Jens Arnold | b1f0049 | 2007-04-21 05:35:17 +0000 | [diff] [blame] | 181 | printf("Loading drums"); |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 182 | while(!eof(file)) |
| 183 | { |
| 184 | readTextBlock(file, number); |
| 185 | readTextBlock(file, name); |
Nils Wallménius | 04b3435 | 2007-09-10 09:46:36 +0000 | [diff] [blame] | 186 | rb->snprintf(fn, 40, ROCKBOX_DIR "/patchset/%s.pat", name); |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 187 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 188 | idx = rb->atoi(number); |
| 189 | if(idx == 0) |
| 190 | break; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 191 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 192 | if(drumUsed[idx]==1) |
| 193 | { |
| 194 | drumSet[idx]=gusload(fn); |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 195 | if(drumSet[idx] == NULL) /* Error loading patch */ |
| 196 | return -1; |
| 197 | } |
Stepan Moskovchenko | 5811214 | 2005-04-15 20:27:04 +0000 | [diff] [blame] | 198 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 199 | while((c != '\n') && (c != 255) && (!eof(file))) |
| 200 | c = readChar(file); |
| 201 | } |
| 202 | rb->close(file); |
| 203 | return 0; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 204 | } |
| 205 | |
Linus Nielsen Feltzing | 7c4b786 | 2007-04-11 10:48:50 +0000 | [diff] [blame] | 206 | #define getSample(s,wf) ((short *)(wf)->data)[s] |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 207 | |
Nils Wallménius | 6386dbe | 2007-09-30 11:21:25 +0000 | [diff] [blame] | 208 | void setPoint(struct SynthObject * so, int pt) ICODE_ATTR; |
Stepan Moskovchenko | b2f1b5d | 2006-05-01 23:22:59 +0000 | [diff] [blame] | 209 | void setPoint(struct SynthObject * so, int pt) |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 210 | { |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 211 | if(so->ch==9) /* Drums, no ADSR */ |
| 212 | { |
| 213 | so->curOffset = 1<<27; |
| 214 | so->curRate = 1; |
| 215 | return; |
| 216 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 217 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 218 | if(so->wf==NULL) |
| 219 | { |
Jens Arnold | b1f0049 | 2007-04-21 05:35:17 +0000 | [diff] [blame] | 220 | printf("Crap... null waveform..."); |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 221 | exit(1); |
| 222 | } |
| 223 | if(so->wf->envRate==NULL) |
| 224 | { |
Jens Arnold | b1f0049 | 2007-04-21 05:35:17 +0000 | [diff] [blame] | 225 | printf("Waveform has no envelope set"); |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 226 | exit(1); |
| 227 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 228 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 229 | so->curPoint = pt; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 230 | |
Stepan Moskovchenko | b2f1b5d | 2006-05-01 23:22:59 +0000 | [diff] [blame] | 231 | int r; |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 232 | int rate = so->wf->envRate[pt]; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 233 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 234 | r=3-((rate>>6) & 0x3); /* Some blatant Timidity code for rate conversion... */ |
| 235 | r*=3; |
| 236 | r = (rate & 0x3f) << r; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 237 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 238 | /* |
| 239 | * Okay. This is the rate shift. Timidity defaults to 9, and sets |
| 240 | * it to 10 if you use the fast decay option. Slow decay sounds better |
| 241 | * on some files, except on some other files... you get chords that aren't |
| 242 | * done decaying yet.. and they dont harmonize with the next chord and it |
| 243 | * sounds like utter crap. Yes, even Timitidy does that. So I'm going to |
| 244 | * default this to 10, and maybe later have an option to set it to 9 |
| 245 | * for longer decays. |
| 246 | */ |
Stepan Moskovchenko | 414724e | 2007-09-30 09:01:38 +0000 | [diff] [blame] | 247 | so->curRate = r<<10; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 248 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 249 | /* |
| 250 | * Do this here because the patches assume a 44100 sampling rate |
| 251 | * We've halved our sampling rate, ergo the ADSR code will be |
| 252 | * called half the time. Ergo, double the rate to keep stuff |
| 253 | * sounding right. |
Stepan Moskovchenko | b2f1b5d | 2006-05-01 23:22:59 +0000 | [diff] [blame] | 254 | * |
| 255 | * Or just move the 1 up one line to optimize a tiny bit. |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 256 | */ |
Stepan Moskovchenko | 94d9d15 | 2006-10-03 23:27:44 +0000 | [diff] [blame] | 257 | /* so->curRate = so->curRate << 1; */ |
Stepan Moskovchenko | 1f5fb99 | 2005-04-19 15:57:07 +0000 | [diff] [blame] | 258 | |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 259 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 260 | so->targetOffset = so->wf->envOffset[pt]<<(20); |
| 261 | if(pt==0) |
| 262 | so->curOffset = 0; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 263 | } |
| 264 | |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 265 | inline void stopVoice(struct SynthObject * so) |
| 266 | { |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 267 | if(so->state == STATE_RAMPDOWN) |
| 268 | return; |
| 269 | so->state = STATE_RAMPDOWN; |
Stepan Moskovchenko | 80b4882 | 2006-10-02 04:13:33 +0000 | [diff] [blame] | 270 | so->decay = 0; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 271 | } |
| 272 | |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 273 | static inline void synthVoice(struct SynthObject * so, int32_t * out, unsigned int samples) |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 274 | { |
Stepan Moskovchenko | b2f1b5d | 2006-05-01 23:22:59 +0000 | [diff] [blame] | 275 | struct GWaveform * wf; |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 276 | register int s1; |
| 277 | register int s2; |
| 278 | |
| 279 | register unsigned int cp_temp = so->cp; |
Stepan Moskovchenko | b2f1b5d | 2006-05-01 23:22:59 +0000 | [diff] [blame] | 280 | |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 281 | wf = so->wf; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 282 | |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 283 | const unsigned int pan = chPan[so->ch]; |
| 284 | const int volscale = so->volscale; |
| 285 | |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 286 | const int mode_mask24 = wf->mode&24; |
| 287 | const int mode_mask28 = wf->mode&28; |
| 288 | const int mode_mask_looprev = wf->mode&LOOP_REVERSE; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 289 | |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 290 | const unsigned int num_samples = (wf->numSamples-1) << FRACTSIZE; |
| 291 | |
| 292 | const unsigned int end_loop = wf->endLoop << FRACTSIZE; |
| 293 | const unsigned int start_loop = wf->startLoop << FRACTSIZE; |
| 294 | const int diff_loop = end_loop-start_loop; |
| 295 | |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 296 | while(samples-- > 0) |
Stepan Moskovchenko | 80b4882 | 2006-10-02 04:13:33 +0000 | [diff] [blame] | 297 | { |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 298 | /* Is voice being ramped? */ |
| 299 | if(so->state == STATE_RAMPDOWN) |
Stepan Moskovchenko | 80b4882 | 2006-10-02 04:13:33 +0000 | [diff] [blame] | 300 | { |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 301 | if(so->decay != 0) /* Ramp has been started */ |
| 302 | { |
| 303 | so->decay = so->decay / 2; |
Stepan Moskovchenko | 80b4882 | 2006-10-02 04:13:33 +0000 | [diff] [blame] | 304 | |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 305 | if(so->decay < 10 && so->decay > -10) |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 306 | so->isUsed = false; |
Stepan Moskovchenko | 80b4882 | 2006-10-02 04:13:33 +0000 | [diff] [blame] | 307 | |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 308 | s1=so->decay; |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 309 | s2 = s1*pan; |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 310 | s1 = (s1<<7) -s2; |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 311 | *(out++)+=((s1 << 9) & 0xFFFF0000) | ((s2 >> 7) &0xFFFF); |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 312 | continue; |
| 313 | } |
| 314 | } else /* OK to advance voice */ |
| 315 | { |
| 316 | cp_temp += so->delta; |
Stepan Moskovchenko | 80b4882 | 2006-10-02 04:13:33 +0000 | [diff] [blame] | 317 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 318 | |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 319 | s2 = getSample((cp_temp >> FRACTSIZE)+1, wf); |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 320 | |
Nils Wallménius | 65331f1 | 2007-11-15 21:20:29 +0000 | [diff] [blame] | 321 | if(mode_mask28) |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 322 | { |
Nils Wallménius | 65331f1 | 2007-11-15 21:20:29 +0000 | [diff] [blame] | 323 | /* LOOP_REVERSE|LOOP_PINGPONG = 24 */ |
| 324 | if(mode_mask24 && so->loopState == STATE_LOOPING && (cp_temp < start_loop)) |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 325 | { |
Nils Wallménius | 65331f1 | 2007-11-15 21:20:29 +0000 | [diff] [blame] | 326 | if(mode_mask_looprev) |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 327 | { |
Nils Wallménius | 65331f1 | 2007-11-15 21:20:29 +0000 | [diff] [blame] | 328 | cp_temp += diff_loop; |
| 329 | s2=getSample((cp_temp >> FRACTSIZE), wf); |
| 330 | } |
| 331 | else |
| 332 | { |
| 333 | so->delta = -so->delta; /* At this point cp_temp is wrong. We need to take a step */ |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | if(cp_temp >= end_loop) |
| 338 | { |
| 339 | so->loopState = STATE_LOOPING; |
| 340 | if(!mode_mask24) |
| 341 | { |
| 342 | cp_temp -= diff_loop; |
| 343 | s2=getSample((cp_temp >> FRACTSIZE), wf); |
| 344 | } |
| 345 | else |
| 346 | { |
| 347 | so->delta = -so->delta; |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 348 | } |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 349 | } |
| 350 | } |
| 351 | |
| 352 | /* Have we overrun? */ |
| 353 | if(cp_temp >= num_samples) |
| 354 | { |
| 355 | cp_temp -= so->delta; |
| 356 | s2 = getSample((cp_temp >> FRACTSIZE)+1, wf); |
| 357 | stopVoice(so); |
| 358 | } |
| 359 | |
| 360 | /* Better, working, linear interpolation */ |
| 361 | s1=getSample((cp_temp >> FRACTSIZE), wf); |
| 362 | |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 363 | s1 +=((signed)((s2 - s1) * (cp_temp & ((1<<FRACTSIZE)-1)))>>FRACTSIZE); |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 364 | |
| 365 | if(so->curRate == 0) |
| 366 | { |
| 367 | stopVoice(so); |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 368 | // so->isUsed = false; |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 369 | |
| 370 | } |
| 371 | |
| 372 | if(so->ch != 9 && so->state != STATE_RAMPDOWN) /* Stupid ADSR code... and don't do ADSR for drums */ |
| 373 | { |
| 374 | if(so->curOffset < so->targetOffset) |
| 375 | { |
| 376 | so->curOffset += (so->curRate); |
| 377 | if(so -> curOffset > so->targetOffset && so->curPoint != 2) |
| 378 | { |
| 379 | if(so->curPoint != 5) |
| 380 | { |
| 381 | setPoint(so, so->curPoint+1); |
| 382 | } |
| 383 | else |
| 384 | { |
| 385 | stopVoice(so); |
| 386 | } |
| 387 | } |
| 388 | } else |
| 389 | { |
| 390 | so->curOffset -= (so->curRate); |
| 391 | if(so -> curOffset < so->targetOffset && so->curPoint != 2) |
| 392 | { |
| 393 | |
| 394 | if(so->curPoint != 5) |
| 395 | { |
| 396 | setPoint(so, so->curPoint+1); |
| 397 | } |
| 398 | else |
| 399 | { |
| 400 | stopVoice(so); |
| 401 | } |
| 402 | |
| 403 | } |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | if(so->curOffset < 0) |
| 408 | { |
| 409 | so->curOffset = so->targetOffset; |
| 410 | stopVoice(so); |
| 411 | } |
| 412 | |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 413 | s1 = s1 * (so->curOffset >> 22) >> 8; |
| 414 | |
| 415 | /* Scaling by channel volume and note volume is done in sequencer.c */ |
| 416 | /* That saves us some multiplication and pointer operations */ |
| 417 | s1 = s1 * volscale >> 14; |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 418 | |
| 419 | /* need to set ramp beginning */ |
| 420 | if(so->state == STATE_RAMPDOWN && so->decay == 0) |
| 421 | { |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 422 | so->decay = s1; |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 423 | if(so->decay == 0) |
| 424 | so->decay = 1; /* stupid junk.. */ |
| 425 | } |
| 426 | |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 427 | s2 = s1*pan; |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 428 | s1 = (s1<<7) - s2; |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 429 | *(out++)+=((s1 << 9) & 0xFFFF0000) | ((s2 >> 7) &0xFFFF); |
Stepan Moskovchenko | 9ec1ff8 | 2005-04-20 21:07:13 +0000 | [diff] [blame] | 430 | } |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 431 | |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 432 | so->cp=cp_temp; /* store this again */ |
| 433 | return; |
Stepan Moskovchenko | 215e492 | 2005-04-15 06:08:55 +0000 | [diff] [blame] | 434 | } |
| 435 | |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 436 | /* buffer to hold all the samples for the current tick, this is a hack |
| 437 | neccesary for coldfire targets as pcm_play_data uses the dma which cannot |
| 438 | access iram */ |
Nils Wallménius | 2d91cf3 | 2007-10-21 22:50:10 +0000 | [diff] [blame] | 439 | int32_t samp_buf[512] IBSS_ATTR; |
Stepan Moskovchenko | 1515ff8 | 2007-10-15 05:11:37 +0000 | [diff] [blame] | 440 | |
Nils Wallménius | f619f81 | 2007-10-08 19:28:41 +0000 | [diff] [blame] | 441 | /* synth num_samples samples and write them to the */ |
| 442 | /* buffer pointed to by buf_ptr */ |
| 443 | void synthSamples(int32_t *buf_ptr, unsigned int num_samples) ICODE_ATTR; |
| 444 | void synthSamples(int32_t *buf_ptr, unsigned int num_samples) |
| 445 | { |
Nils Wallménius | 2d91cf3 | 2007-10-21 22:50:10 +0000 | [diff] [blame] | 446 | if (num_samples > 512) |
| 447 | DEBUGF("num_samples is too big!\n"); |
| 448 | else |
Nils Wallménius | f619f81 | 2007-10-08 19:28:41 +0000 | [diff] [blame] | 449 | { |
Nils Wallménius | 2d91cf3 | 2007-10-21 22:50:10 +0000 | [diff] [blame] | 450 | int i; |
| 451 | struct SynthObject *voicept; |
| 452 | |
| 453 | rb->memset(samp_buf, 0, num_samples*4); |
| 454 | |
| 455 | for(i=0; i < MAX_VOICES; i++) |
Nils Wallménius | f619f81 | 2007-10-08 19:28:41 +0000 | [diff] [blame] | 456 | { |
Nils Wallménius | 2d91cf3 | 2007-10-21 22:50:10 +0000 | [diff] [blame] | 457 | voicept=&voices[i]; |
Nils Wallménius | 0fd4c2e | 2007-11-11 01:02:45 +0000 | [diff] [blame] | 458 | if(voicept->isUsed) |
Nils Wallménius | 2d91cf3 | 2007-10-21 22:50:10 +0000 | [diff] [blame] | 459 | { |
| 460 | synthVoice(voicept, samp_buf, num_samples); |
| 461 | } |
Nils Wallménius | f619f81 | 2007-10-08 19:28:41 +0000 | [diff] [blame] | 462 | } |
Nils Wallménius | 2d91cf3 | 2007-10-21 22:50:10 +0000 | [diff] [blame] | 463 | |
| 464 | rb->memcpy(buf_ptr, samp_buf, num_samples*4); |
Nils Wallménius | f619f81 | 2007-10-08 19:28:41 +0000 | [diff] [blame] | 465 | } |
| 466 | /* TODO: Automatic Gain Control, anyone? */ |
| 467 | /* Or, should this be implemented on the DSP's output volume instead? */ |
| 468 | |
| 469 | return; /* No more ghetto lowpass filter. Linear interpolation works well. */ |
| 470 | } |
| 471 | |