| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
|  | 2 | *  Copyright (c) by Uros Bizjak <uros@kss-loka.si> | 
|  | 3 | * | 
|  | 4 | *  Midi synth routines for OPL2/OPL3/OPL4 FM | 
|  | 5 | * | 
|  | 6 | *   This program is free software; you can redistribute it and/or modify | 
|  | 7 | *   it under the terms of the GNU General Public License as published by | 
|  | 8 | *   the Free Software Foundation; either version 2 of the License, or | 
|  | 9 | *   (at your option) any later version. | 
|  | 10 | * | 
|  | 11 | *   This program is distributed in the hope that it will be useful, | 
|  | 12 | *   but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 13 | *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 14 | *   GNU General Public License for more details. | 
|  | 15 | * | 
|  | 16 | *   You should have received a copy of the GNU General Public License | 
|  | 17 | *   along with this program; if not, write to the Free Software | 
|  | 18 | *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA | 
|  | 19 | * | 
|  | 20 | */ | 
|  | 21 |  | 
|  | 22 | #undef DEBUG_ALLOC | 
|  | 23 | #undef DEBUG_MIDI | 
|  | 24 |  | 
|  | 25 | #include "opl3_voice.h" | 
|  | 26 | #include <sound/asoundef.h> | 
|  | 27 |  | 
|  | 28 | extern char snd_opl3_regmap[MAX_OPL2_VOICES][4]; | 
|  | 29 |  | 
| Rusty Russell | a67ff6a | 2011-12-15 13:49:36 +1030 | [diff] [blame] | 30 | extern bool use_internal_drums; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 31 |  | 
| Krzysztof Helt | 8dce39b | 2009-10-07 22:51:34 +0200 | [diff] [blame] | 32 | static void snd_opl3_note_off_unsafe(void *p, int note, int vel, | 
|  | 33 | struct snd_midi_channel *chan); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 34 | /* | 
|  | 35 | * The next table looks magical, but it certainly is not. Its values have | 
|  | 36 | * been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception | 
|  | 37 | * for i=0. This log-table converts a linear volume-scaling (0..127) to a | 
|  | 38 | * logarithmic scaling as present in the FM-synthesizer chips. so :    Volume | 
|  | 39 | * 64 =  0 db = relative volume  0 and:    Volume 32 = -6 db = relative | 
|  | 40 | * volume -8 it was implemented as a table because it is only 128 bytes and | 
|  | 41 | * it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>) | 
|  | 42 | */ | 
|  | 43 |  | 
|  | 44 | static char opl3_volume_table[128] = | 
|  | 45 | { | 
|  | 46 | -63, -48, -40, -35, -32, -29, -27, -26, | 
|  | 47 | -24, -23, -21, -20, -19, -18, -18, -17, | 
|  | 48 | -16, -15, -15, -14, -13, -13, -12, -12, | 
|  | 49 | -11, -11, -10, -10, -10, -9, -9, -8, | 
|  | 50 | -8, -8, -7, -7, -7, -6, -6, -6, | 
|  | 51 | -5, -5, -5, -5, -4, -4, -4, -4, | 
|  | 52 | -3, -3, -3, -3, -2, -2, -2, -2, | 
|  | 53 | -2, -1, -1, -1, -1, 0, 0, 0, | 
|  | 54 | 0, 0, 0, 1, 1, 1, 1, 1, | 
|  | 55 | 1, 2, 2, 2, 2, 2, 2, 2, | 
|  | 56 | 3, 3, 3, 3, 3, 3, 3, 4, | 
|  | 57 | 4, 4, 4, 4, 4, 4, 4, 5, | 
|  | 58 | 5, 5, 5, 5, 5, 5, 5, 5, | 
|  | 59 | 6, 6, 6, 6, 6, 6, 6, 6, | 
|  | 60 | 6, 7, 7, 7, 7, 7, 7, 7, | 
|  | 61 | 7, 7, 7, 8, 8, 8, 8, 8 | 
|  | 62 | }; | 
|  | 63 |  | 
|  | 64 | void snd_opl3_calc_volume(unsigned char *volbyte, int vel, | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 65 | struct snd_midi_channel *chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 66 | { | 
|  | 67 | int oldvol, newvol, n; | 
|  | 68 | int volume; | 
|  | 69 |  | 
|  | 70 | volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127); | 
|  | 71 | if (volume > 127) | 
|  | 72 | volume = 127; | 
|  | 73 |  | 
|  | 74 | oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK); | 
|  | 75 |  | 
|  | 76 | newvol = opl3_volume_table[volume] + oldvol; | 
|  | 77 | if (newvol > OPL3_TOTAL_LEVEL_MASK) | 
|  | 78 | newvol = OPL3_TOTAL_LEVEL_MASK; | 
|  | 79 | else if (newvol < 0) | 
|  | 80 | newvol = 0; | 
|  | 81 |  | 
|  | 82 | n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK); | 
|  | 83 |  | 
|  | 84 | *volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK); | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | /* | 
|  | 88 | * Converts the note frequency to block and fnum values for the FM chip | 
|  | 89 | */ | 
|  | 90 | static short opl3_note_table[16] = | 
|  | 91 | { | 
|  | 92 | 305, 323,	/* for pitch bending, -2 semitones */ | 
|  | 93 | 343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647, | 
|  | 94 | 686, 726	/* for pitch bending, +2 semitones */ | 
|  | 95 | }; | 
|  | 96 |  | 
|  | 97 | static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum, | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 98 | int note, struct snd_midi_channel *chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 99 | { | 
|  | 100 | int block = ((note / 12) & 0x07) - 1; | 
|  | 101 | int idx = (note % 12) + 2; | 
|  | 102 | int freq; | 
|  | 103 |  | 
|  | 104 | if (chan->midi_pitchbend) { | 
|  | 105 | int pitchbend = chan->midi_pitchbend; | 
|  | 106 | int segment; | 
|  | 107 |  | 
|  | 108 | if (pitchbend > 0x1FFF) | 
|  | 109 | pitchbend = 0x1FFF; | 
|  | 110 |  | 
|  | 111 | segment = pitchbend / 0x1000; | 
|  | 112 | freq = opl3_note_table[idx+segment]; | 
|  | 113 | freq += ((opl3_note_table[idx+segment+1] - freq) * | 
|  | 114 | (pitchbend % 0x1000)) / 0x1000; | 
|  | 115 | } else { | 
|  | 116 | freq = opl3_note_table[idx]; | 
|  | 117 | } | 
|  | 118 |  | 
|  | 119 | *fnum = (unsigned char) freq; | 
|  | 120 | *blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) | | 
|  | 121 | ((block << 2) & OPL3_BLOCKNUM_MASK); | 
|  | 122 | } | 
|  | 123 |  | 
|  | 124 |  | 
|  | 125 | #ifdef DEBUG_ALLOC | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 126 | static void debug_alloc(struct snd_opl3 *opl3, char *s, int voice) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 127 | int i; | 
|  | 128 | char *str = "x.24"; | 
|  | 129 |  | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 130 | printk(KERN_DEBUG "time %.5i: %s [%.2i]: ", opl3->use_time, s, voice); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 131 | for (i = 0; i < opl3->max_voices; i++) | 
|  | 132 | printk("%c", *(str + opl3->voices[i].state + 1)); | 
|  | 133 | printk("\n"); | 
|  | 134 | } | 
|  | 135 | #endif | 
|  | 136 |  | 
|  | 137 | /* | 
|  | 138 | * Get a FM voice (channel) to play a note on. | 
|  | 139 | */ | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 140 | static int opl3_get_voice(struct snd_opl3 *opl3, int instr_4op, | 
|  | 141 | struct snd_midi_channel *chan) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 142 | int chan_4op_1;		/* first voice for 4op instrument */ | 
|  | 143 | int chan_4op_2;		/* second voice for 4op instrument */ | 
|  | 144 |  | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 145 | struct snd_opl3_voice *vp, *vp2; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 146 | unsigned int voice_time; | 
|  | 147 | int i; | 
|  | 148 |  | 
|  | 149 | #ifdef DEBUG_ALLOC | 
|  | 150 | char *alloc_type[3] = { "FREE     ", "CHEAP    ", "EXPENSIVE" }; | 
|  | 151 | #endif | 
|  | 152 |  | 
|  | 153 | /* This is our "allocation cost" table */ | 
|  | 154 | enum { | 
|  | 155 | FREE = 0, CHEAP, EXPENSIVE, END | 
|  | 156 | }; | 
|  | 157 |  | 
|  | 158 | /* Keeps track of what we are finding */ | 
|  | 159 | struct best { | 
|  | 160 | unsigned int time; | 
|  | 161 | int voice; | 
|  | 162 | } best[END]; | 
|  | 163 | struct best *bp; | 
|  | 164 |  | 
|  | 165 | for (i = 0; i < END; i++) { | 
|  | 166 | best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */; | 
|  | 167 | best[i].voice = -1; | 
|  | 168 | } | 
|  | 169 |  | 
|  | 170 | /* Look through all the channels for the most suitable. */ | 
|  | 171 | for (i = 0; i < opl3->max_voices; i++) { | 
|  | 172 | vp = &opl3->voices[i]; | 
|  | 173 |  | 
|  | 174 | if (vp->state == SNDRV_OPL3_ST_NOT_AVAIL) | 
|  | 175 | /* skip unavailable channels, allocated by | 
|  | 176 | drum voices or by bounded 4op voices) */ | 
|  | 177 | continue; | 
|  | 178 |  | 
|  | 179 | voice_time = vp->time; | 
|  | 180 | bp = best; | 
|  | 181 |  | 
|  | 182 | chan_4op_1 = ((i < 3) || (i > 8 && i < 12)); | 
|  | 183 | chan_4op_2 = ((i > 2 && i < 6) || (i > 11 && i < 15)); | 
|  | 184 | if (instr_4op) { | 
|  | 185 | /* allocate 4op voice */ | 
|  | 186 | /* skip channels unavailable to 4op instrument */ | 
|  | 187 | if (!chan_4op_1) | 
|  | 188 | continue; | 
|  | 189 |  | 
|  | 190 | if (vp->state) | 
|  | 191 | /* kill one voice, CHEAP */ | 
|  | 192 | bp++; | 
|  | 193 | /* get state of bounded 2op channel | 
|  | 194 | to be allocated for 4op instrument */ | 
|  | 195 | vp2 = &opl3->voices[i + 3]; | 
|  | 196 | if (vp2->state == SNDRV_OPL3_ST_ON_2OP) { | 
|  | 197 | /* kill two voices, EXPENSIVE */ | 
|  | 198 | bp++; | 
|  | 199 | voice_time = (voice_time > vp->time) ? | 
|  | 200 | voice_time : vp->time; | 
|  | 201 | } | 
|  | 202 | } else { | 
|  | 203 | /* allocate 2op voice */ | 
|  | 204 | if ((chan_4op_1) || (chan_4op_2)) | 
|  | 205 | /* use bounded channels for 2op, CHEAP */ | 
|  | 206 | bp++; | 
|  | 207 | else if (vp->state) | 
|  | 208 | /* kill one voice on 2op channel, CHEAP */ | 
|  | 209 | bp++; | 
|  | 210 | /* raise kill cost to EXPENSIVE for all channels */ | 
|  | 211 | if (vp->state) | 
|  | 212 | bp++; | 
|  | 213 | } | 
|  | 214 | if (voice_time < bp->time) { | 
|  | 215 | bp->time = voice_time; | 
|  | 216 | bp->voice = i; | 
|  | 217 | } | 
|  | 218 | } | 
|  | 219 |  | 
|  | 220 | for (i = 0; i < END; i++) { | 
|  | 221 | if (best[i].voice >= 0) { | 
|  | 222 | #ifdef DEBUG_ALLOC | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 223 | printk(KERN_DEBUG "%s %iop allocation on voice %i\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 224 | alloc_type[i], instr_4op ? 4 : 2, | 
|  | 225 | best[i].voice); | 
|  | 226 | #endif | 
|  | 227 | return best[i].voice; | 
|  | 228 | } | 
|  | 229 | } | 
|  | 230 | /* not found */ | 
|  | 231 | return -1; | 
|  | 232 | } | 
|  | 233 |  | 
|  | 234 | /* ------------------------------ */ | 
|  | 235 |  | 
|  | 236 | /* | 
|  | 237 | * System timer interrupt function | 
|  | 238 | */ | 
|  | 239 | void snd_opl3_timer_func(unsigned long data) | 
|  | 240 | { | 
|  | 241 |  | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 242 | struct snd_opl3 *opl3 = (struct snd_opl3 *)data; | 
| Takashi Iwai | b32425a | 2005-11-18 18:52:14 +0100 | [diff] [blame] | 243 | unsigned long flags; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 244 | int again = 0; | 
|  | 245 | int i; | 
|  | 246 |  | 
| Krzysztof Helt | 8dce39b | 2009-10-07 22:51:34 +0200 | [diff] [blame] | 247 | spin_lock_irqsave(&opl3->voice_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 248 | for (i = 0; i < opl3->max_voices; i++) { | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 249 | struct snd_opl3_voice *vp = &opl3->voices[i]; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 250 | if (vp->state > 0 && vp->note_off_check) { | 
|  | 251 | if (vp->note_off == jiffies) | 
| Krzysztof Helt | 8dce39b | 2009-10-07 22:51:34 +0200 | [diff] [blame] | 252 | snd_opl3_note_off_unsafe(opl3, vp->note, 0, | 
|  | 253 | vp->chan); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 254 | else | 
|  | 255 | again++; | 
|  | 256 | } | 
|  | 257 | } | 
| Krzysztof Helt | 8dce39b | 2009-10-07 22:51:34 +0200 | [diff] [blame] | 258 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 
|  | 259 |  | 
|  | 260 | spin_lock_irqsave(&opl3->sys_timer_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 261 | if (again) { | 
|  | 262 | opl3->tlist.expires = jiffies + 1;	/* invoke again */ | 
|  | 263 | add_timer(&opl3->tlist); | 
|  | 264 | } else { | 
|  | 265 | opl3->sys_timer_status = 0; | 
|  | 266 | } | 
| Takashi Iwai | b32425a | 2005-11-18 18:52:14 +0100 | [diff] [blame] | 267 | spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 268 | } | 
|  | 269 |  | 
|  | 270 | /* | 
|  | 271 | * Start system timer | 
|  | 272 | */ | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 273 | static void snd_opl3_start_timer(struct snd_opl3 *opl3) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 274 | { | 
|  | 275 | unsigned long flags; | 
|  | 276 | spin_lock_irqsave(&opl3->sys_timer_lock, flags); | 
|  | 277 | if (! opl3->sys_timer_status) { | 
|  | 278 | opl3->tlist.expires = jiffies + 1; | 
|  | 279 | add_timer(&opl3->tlist); | 
|  | 280 | opl3->sys_timer_status = 1; | 
|  | 281 | } | 
|  | 282 | spin_unlock_irqrestore(&opl3->sys_timer_lock, flags); | 
|  | 283 | } | 
|  | 284 |  | 
|  | 285 | /* ------------------------------ */ | 
|  | 286 |  | 
|  | 287 |  | 
|  | 288 | static int snd_opl3_oss_map[MAX_OPL3_VOICES] = { | 
|  | 289 | 0, 1, 2, 9, 10, 11, 6, 7, 8, 15, 16, 17, 3, 4 ,5, 12, 13, 14 | 
|  | 290 | }; | 
|  | 291 |  | 
|  | 292 | /* | 
|  | 293 | * Start a note. | 
|  | 294 | */ | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 295 | void snd_opl3_note_on(void *p, int note, int vel, struct snd_midi_channel *chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 296 | { | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 297 | struct snd_opl3 *opl3; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 298 | int instr_4op; | 
|  | 299 |  | 
|  | 300 | int voice; | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 301 | struct snd_opl3_voice *vp, *vp2; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 302 | unsigned short connect_mask; | 
|  | 303 | unsigned char connection; | 
|  | 304 | unsigned char vol_op[4]; | 
|  | 305 |  | 
|  | 306 | int extra_prg = 0; | 
|  | 307 |  | 
|  | 308 | unsigned short reg_side; | 
|  | 309 | unsigned char op_offset; | 
|  | 310 | unsigned char voice_offset; | 
|  | 311 | unsigned short opl3_reg; | 
|  | 312 | unsigned char reg_val; | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 313 | unsigned char prg, bank; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 314 |  | 
|  | 315 | int key = note; | 
|  | 316 | unsigned char fnum, blocknum; | 
|  | 317 | int i; | 
|  | 318 |  | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 319 | struct fm_patch *patch; | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 320 | struct fm_instrument *fm; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 321 | unsigned long flags; | 
|  | 322 |  | 
|  | 323 | opl3 = p; | 
|  | 324 |  | 
|  | 325 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 326 | snd_printk(KERN_DEBUG "Note on, ch %i, inst %i, note %i, vel %i\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 327 | chan->number, chan->midi_program, note, vel); | 
|  | 328 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 329 |  | 
|  | 330 | /* in SYNTH mode, application takes care of voices */ | 
|  | 331 | /* in SEQ mode, drum voice numbers are notes on drum channel */ | 
|  | 332 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | 
|  | 333 | if (chan->drum_channel) { | 
|  | 334 | /* percussion instruments are located in bank 128 */ | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 335 | bank = 128; | 
|  | 336 | prg = note; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 337 | } else { | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 338 | bank = chan->gm_bank_select; | 
|  | 339 | prg = chan->midi_program; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 340 | } | 
|  | 341 | } else { | 
|  | 342 | /* Prepare for OSS mode */ | 
|  | 343 | if (chan->number >= MAX_OPL3_VOICES) | 
|  | 344 | return; | 
|  | 345 |  | 
|  | 346 | /* OSS instruments are located in bank 127 */ | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 347 | bank = 127; | 
|  | 348 | prg = chan->midi_program; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 349 | } | 
|  | 350 |  | 
|  | 351 | spin_lock_irqsave(&opl3->voice_lock, flags); | 
|  | 352 |  | 
|  | 353 | if (use_internal_drums) { | 
|  | 354 | snd_opl3_drum_switch(opl3, note, vel, 1, chan); | 
|  | 355 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 
|  | 356 | return; | 
|  | 357 | } | 
|  | 358 |  | 
|  | 359 | __extra_prg: | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 360 | patch = snd_opl3_find_patch(opl3, prg, bank, 0); | 
|  | 361 | if (!patch) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 362 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 
|  | 363 | return; | 
|  | 364 | } | 
|  | 365 |  | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 366 | fm = &patch->inst; | 
|  | 367 | switch (patch->type) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 368 | case FM_PATCH_OPL2: | 
|  | 369 | instr_4op = 0; | 
|  | 370 | break; | 
|  | 371 | case FM_PATCH_OPL3: | 
|  | 372 | if (opl3->hardware >= OPL3_HW_OPL3) { | 
|  | 373 | instr_4op = 1; | 
|  | 374 | break; | 
|  | 375 | } | 
|  | 376 | default: | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 377 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 
|  | 378 | return; | 
|  | 379 | } | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 380 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 381 | snd_printk(KERN_DEBUG "  --> OPL%i instrument: %s\n", | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 382 | instr_4op ? 3 : 2, patch->name); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 383 | #endif | 
|  | 384 | /* in SYNTH mode, application takes care of voices */ | 
|  | 385 | /* in SEQ mode, allocate voice on free OPL3 channel */ | 
|  | 386 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | 
|  | 387 | voice = opl3_get_voice(opl3, instr_4op, chan); | 
|  | 388 | } else { | 
|  | 389 | /* remap OSS voice */ | 
|  | 390 | voice = snd_opl3_oss_map[chan->number]; | 
|  | 391 | } | 
|  | 392 |  | 
|  | 393 | if (voice < MAX_OPL2_VOICES) { | 
|  | 394 | /* Left register block for voices 0 .. 8 */ | 
|  | 395 | reg_side = OPL3_LEFT; | 
|  | 396 | voice_offset = voice; | 
|  | 397 | connect_mask = (OPL3_LEFT_4OP_0 << voice_offset) & 0x07; | 
|  | 398 | } else { | 
|  | 399 | /* Right register block for voices 9 .. 17 */ | 
|  | 400 | reg_side = OPL3_RIGHT; | 
|  | 401 | voice_offset = voice - MAX_OPL2_VOICES; | 
|  | 402 | connect_mask = (OPL3_RIGHT_4OP_0 << voice_offset) & 0x38; | 
|  | 403 | } | 
|  | 404 |  | 
|  | 405 | /* kill voice on channel */ | 
|  | 406 | vp = &opl3->voices[voice]; | 
|  | 407 | if (vp->state > 0) { | 
|  | 408 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | 
|  | 409 | reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; | 
|  | 410 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 411 | } | 
|  | 412 | if (instr_4op) { | 
|  | 413 | vp2 = &opl3->voices[voice + 3]; | 
|  | 414 | if (vp->state > 0) { | 
|  | 415 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + | 
|  | 416 | voice_offset + 3); | 
|  | 417 | reg_val = vp->keyon_reg & ~OPL3_KEYON_BIT; | 
|  | 418 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 419 | } | 
|  | 420 | } | 
|  | 421 |  | 
|  | 422 | /* set connection register */ | 
|  | 423 | if (instr_4op) { | 
|  | 424 | if ((opl3->connection_reg ^ connect_mask) & connect_mask) { | 
|  | 425 | opl3->connection_reg |= connect_mask; | 
|  | 426 | /* set connection bit */ | 
|  | 427 | opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; | 
|  | 428 | opl3->command(opl3, opl3_reg, opl3->connection_reg); | 
|  | 429 | } | 
|  | 430 | } else { | 
|  | 431 | if ((opl3->connection_reg ^ ~connect_mask) & connect_mask) { | 
|  | 432 | opl3->connection_reg &= ~connect_mask; | 
|  | 433 | /* clear connection bit */ | 
|  | 434 | opl3_reg = OPL3_RIGHT | OPL3_REG_CONNECTION_SELECT; | 
|  | 435 | opl3->command(opl3, opl3_reg, opl3->connection_reg); | 
|  | 436 | } | 
|  | 437 | } | 
|  | 438 |  | 
|  | 439 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 440 | snd_printk(KERN_DEBUG "  --> setting OPL3 connection: 0x%x\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 441 | opl3->connection_reg); | 
|  | 442 | #endif | 
|  | 443 | /* | 
|  | 444 | * calculate volume depending on connection | 
|  | 445 | * between FM operators (see include/opl3.h) | 
|  | 446 | */ | 
|  | 447 | for (i = 0; i < (instr_4op ? 4 : 2); i++) | 
|  | 448 | vol_op[i] = fm->op[i].ksl_level; | 
|  | 449 |  | 
|  | 450 | connection = fm->feedback_connection[0] & 0x01; | 
|  | 451 | if (instr_4op) { | 
|  | 452 | connection <<= 1; | 
|  | 453 | connection |= fm->feedback_connection[1] & 0x01; | 
|  | 454 |  | 
|  | 455 | snd_opl3_calc_volume(&vol_op[3], vel, chan); | 
|  | 456 | switch (connection) { | 
|  | 457 | case 0x03: | 
|  | 458 | snd_opl3_calc_volume(&vol_op[2], vel, chan); | 
|  | 459 | /* fallthru */ | 
|  | 460 | case 0x02: | 
|  | 461 | snd_opl3_calc_volume(&vol_op[0], vel, chan); | 
|  | 462 | break; | 
|  | 463 | case 0x01: | 
|  | 464 | snd_opl3_calc_volume(&vol_op[1], vel, chan); | 
|  | 465 | } | 
|  | 466 | } else { | 
|  | 467 | snd_opl3_calc_volume(&vol_op[1], vel, chan); | 
|  | 468 | if (connection) | 
|  | 469 | snd_opl3_calc_volume(&vol_op[0], vel, chan); | 
|  | 470 | } | 
|  | 471 |  | 
|  | 472 | /* Program the FM voice characteristics */ | 
|  | 473 | for (i = 0; i < (instr_4op ? 4 : 2); i++) { | 
|  | 474 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 475 | snd_printk(KERN_DEBUG "  --> programming operator %i\n", i); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 476 | #endif | 
|  | 477 | op_offset = snd_opl3_regmap[voice_offset][i]; | 
|  | 478 |  | 
|  | 479 | /* Set OPL3 AM_VIB register of requested voice/operator */ | 
|  | 480 | reg_val = fm->op[i].am_vib; | 
|  | 481 | opl3_reg = reg_side | (OPL3_REG_AM_VIB + op_offset); | 
|  | 482 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 483 |  | 
|  | 484 | /* Set OPL3 KSL_LEVEL register of requested voice/operator */ | 
|  | 485 | reg_val = vol_op[i]; | 
|  | 486 | opl3_reg = reg_side | (OPL3_REG_KSL_LEVEL + op_offset); | 
|  | 487 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 488 |  | 
|  | 489 | /* Set OPL3 ATTACK_DECAY register of requested voice/operator */ | 
|  | 490 | reg_val = fm->op[i].attack_decay; | 
|  | 491 | opl3_reg = reg_side | (OPL3_REG_ATTACK_DECAY + op_offset); | 
|  | 492 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 493 |  | 
|  | 494 | /* Set OPL3 SUSTAIN_RELEASE register of requested voice/operator */ | 
|  | 495 | reg_val = fm->op[i].sustain_release; | 
|  | 496 | opl3_reg = reg_side | (OPL3_REG_SUSTAIN_RELEASE + op_offset); | 
|  | 497 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 498 |  | 
|  | 499 | /* Select waveform */ | 
|  | 500 | reg_val = fm->op[i].wave_select; | 
|  | 501 | opl3_reg = reg_side | (OPL3_REG_WAVE_SELECT + op_offset); | 
|  | 502 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 503 | } | 
|  | 504 |  | 
|  | 505 | /* Set operator feedback and 2op inter-operator connection */ | 
|  | 506 | reg_val = fm->feedback_connection[0]; | 
|  | 507 | /* Set output voice connection */ | 
|  | 508 | reg_val |= OPL3_STEREO_BITS; | 
|  | 509 | if (chan->gm_pan < 43) | 
|  | 510 | reg_val &= ~OPL3_VOICE_TO_RIGHT; | 
|  | 511 | if (chan->gm_pan > 85) | 
|  | 512 | reg_val &= ~OPL3_VOICE_TO_LEFT; | 
|  | 513 | opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + voice_offset); | 
|  | 514 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 515 |  | 
|  | 516 | if (instr_4op) { | 
|  | 517 | /* Set 4op inter-operator connection */ | 
|  | 518 | reg_val = fm->feedback_connection[1] & OPL3_CONNECTION_BIT; | 
|  | 519 | /* Set output voice connection */ | 
|  | 520 | reg_val |= OPL3_STEREO_BITS; | 
|  | 521 | if (chan->gm_pan < 43) | 
|  | 522 | reg_val &= ~OPL3_VOICE_TO_RIGHT; | 
|  | 523 | if (chan->gm_pan > 85) | 
|  | 524 | reg_val &= ~OPL3_VOICE_TO_LEFT; | 
|  | 525 | opl3_reg = reg_side | (OPL3_REG_FEEDBACK_CONNECTION + | 
|  | 526 | voice_offset + 3); | 
|  | 527 | opl3->command(opl3, opl3_reg, reg_val); | 
|  | 528 | } | 
|  | 529 |  | 
|  | 530 | /* | 
|  | 531 | * Special treatment of percussion notes for fm: | 
|  | 532 | * Requested pitch is really program, and pitch for | 
|  | 533 | * device is whatever was specified in the patch library. | 
|  | 534 | */ | 
|  | 535 | if (fm->fix_key) | 
|  | 536 | note = fm->fix_key; | 
|  | 537 | /* | 
|  | 538 | * use transpose if defined in patch library | 
|  | 539 | */ | 
|  | 540 | if (fm->trnsps) | 
|  | 541 | note += (fm->trnsps - 64); | 
|  | 542 |  | 
|  | 543 | snd_opl3_calc_pitch(&fnum, &blocknum, note, chan); | 
|  | 544 |  | 
|  | 545 | /* Set OPL3 FNUM_LOW register of requested voice */ | 
|  | 546 | opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); | 
|  | 547 | opl3->command(opl3, opl3_reg, fnum); | 
|  | 548 |  | 
|  | 549 | opl3->voices[voice].keyon_reg = blocknum; | 
|  | 550 |  | 
|  | 551 | /* Set output sound flag */ | 
|  | 552 | blocknum |= OPL3_KEYON_BIT; | 
|  | 553 |  | 
|  | 554 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 555 | snd_printk(KERN_DEBUG "  --> trigger voice %i\n", voice); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 556 | #endif | 
|  | 557 | /* Set OPL3 KEYON_BLOCK register of requested voice */ | 
|  | 558 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | 
|  | 559 | opl3->command(opl3, opl3_reg, blocknum); | 
|  | 560 |  | 
|  | 561 | /* kill note after fixed duration (in centiseconds) */ | 
|  | 562 | if (fm->fix_dur) { | 
|  | 563 | opl3->voices[voice].note_off = jiffies + | 
|  | 564 | (fm->fix_dur * HZ) / 100; | 
|  | 565 | snd_opl3_start_timer(opl3); | 
|  | 566 | opl3->voices[voice].note_off_check = 1; | 
|  | 567 | } else | 
|  | 568 | opl3->voices[voice].note_off_check = 0; | 
|  | 569 |  | 
|  | 570 | /* get extra pgm, but avoid possible loops */ | 
|  | 571 | extra_prg = (extra_prg) ? 0 : fm->modes; | 
|  | 572 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 573 | /* do the bookkeeping */ | 
|  | 574 | vp->time = opl3->use_time++; | 
|  | 575 | vp->note = key; | 
|  | 576 | vp->chan = chan; | 
|  | 577 |  | 
|  | 578 | if (instr_4op) { | 
|  | 579 | vp->state = SNDRV_OPL3_ST_ON_4OP; | 
|  | 580 |  | 
|  | 581 | vp2 = &opl3->voices[voice + 3]; | 
|  | 582 | vp2->time = opl3->use_time++; | 
|  | 583 | vp2->note = key; | 
|  | 584 | vp2->chan = chan; | 
|  | 585 | vp2->state = SNDRV_OPL3_ST_NOT_AVAIL; | 
|  | 586 | } else { | 
|  | 587 | if (vp->state == SNDRV_OPL3_ST_ON_4OP) { | 
|  | 588 | /* 4op killed by 2op, release bounded voice */ | 
|  | 589 | vp2 = &opl3->voices[voice + 3]; | 
|  | 590 | vp2->time = opl3->use_time++; | 
|  | 591 | vp2->state = SNDRV_OPL3_ST_OFF; | 
|  | 592 | } | 
|  | 593 | vp->state = SNDRV_OPL3_ST_ON_2OP; | 
|  | 594 | } | 
|  | 595 |  | 
|  | 596 | #ifdef DEBUG_ALLOC | 
|  | 597 | debug_alloc(opl3, "note on ", voice); | 
|  | 598 | #endif | 
|  | 599 |  | 
|  | 600 | /* allocate extra program if specified in patch library */ | 
|  | 601 | if (extra_prg) { | 
|  | 602 | if (extra_prg > 128) { | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 603 | bank = 128; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 604 | /* percussions start at 35 */ | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 605 | prg = extra_prg - 128 + 35 - 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 606 | } else { | 
| Takashi Iwai | 224a033 | 2007-10-30 11:49:22 +0100 | [diff] [blame] | 607 | bank = 0; | 
|  | 608 | prg = extra_prg - 1; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 609 | } | 
|  | 610 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 611 | snd_printk(KERN_DEBUG " *** allocating extra program\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 612 | #endif | 
|  | 613 | goto __extra_prg; | 
|  | 614 | } | 
|  | 615 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 
|  | 616 | } | 
|  | 617 |  | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 618 | static void snd_opl3_kill_voice(struct snd_opl3 *opl3, int voice) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 619 | { | 
|  | 620 | unsigned short reg_side; | 
|  | 621 | unsigned char voice_offset; | 
|  | 622 | unsigned short opl3_reg; | 
|  | 623 |  | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 624 | struct snd_opl3_voice *vp, *vp2; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 625 |  | 
| Takashi Iwai | 5e246b8 | 2008-08-08 17:12:47 +0200 | [diff] [blame] | 626 | if (snd_BUG_ON(voice >= MAX_OPL3_VOICES)) | 
|  | 627 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 628 |  | 
|  | 629 | vp = &opl3->voices[voice]; | 
|  | 630 | if (voice < MAX_OPL2_VOICES) { | 
|  | 631 | /* Left register block for voices 0 .. 8 */ | 
|  | 632 | reg_side = OPL3_LEFT; | 
|  | 633 | voice_offset = voice; | 
|  | 634 | } else { | 
|  | 635 | /* Right register block for voices 9 .. 17 */ | 
|  | 636 | reg_side = OPL3_RIGHT; | 
|  | 637 | voice_offset = voice - MAX_OPL2_VOICES; | 
|  | 638 | } | 
|  | 639 |  | 
|  | 640 | /* kill voice */ | 
|  | 641 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 642 | snd_printk(KERN_DEBUG "  --> kill voice %i\n", voice); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 643 | #endif | 
|  | 644 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | 
|  | 645 | /* clear Key ON bit */ | 
|  | 646 | opl3->command(opl3, opl3_reg, vp->keyon_reg); | 
|  | 647 |  | 
|  | 648 | /* do the bookkeeping */ | 
|  | 649 | vp->time = opl3->use_time++; | 
|  | 650 |  | 
|  | 651 | if (vp->state == SNDRV_OPL3_ST_ON_4OP) { | 
|  | 652 | vp2 = &opl3->voices[voice + 3]; | 
|  | 653 |  | 
|  | 654 | vp2->time = opl3->use_time++; | 
|  | 655 | vp2->state = SNDRV_OPL3_ST_OFF; | 
|  | 656 | } | 
|  | 657 | vp->state = SNDRV_OPL3_ST_OFF; | 
|  | 658 | #ifdef DEBUG_ALLOC | 
|  | 659 | debug_alloc(opl3, "note off", voice); | 
|  | 660 | #endif | 
|  | 661 |  | 
|  | 662 | } | 
|  | 663 |  | 
|  | 664 | /* | 
|  | 665 | * Release a note in response to a midi note off. | 
|  | 666 | */ | 
| Krzysztof Helt | 8dce39b | 2009-10-07 22:51:34 +0200 | [diff] [blame] | 667 | static void snd_opl3_note_off_unsafe(void *p, int note, int vel, | 
|  | 668 | struct snd_midi_channel *chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 669 | { | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 670 | struct snd_opl3 *opl3; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 671 |  | 
|  | 672 | int voice; | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 673 | struct snd_opl3_voice *vp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 674 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 675 | opl3 = p; | 
|  | 676 |  | 
|  | 677 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 678 | snd_printk(KERN_DEBUG "Note off, ch %i, inst %i, note %i\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 679 | chan->number, chan->midi_program, note); | 
|  | 680 | #endif | 
|  | 681 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 682 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | 
|  | 683 | if (chan->drum_channel && use_internal_drums) { | 
|  | 684 | snd_opl3_drum_switch(opl3, note, vel, 0, chan); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 685 | return; | 
|  | 686 | } | 
|  | 687 | /* this loop will hopefully kill all extra voices, because | 
|  | 688 | they are grouped by the same channel and note values */ | 
|  | 689 | for (voice = 0; voice < opl3->max_voices; voice++) { | 
|  | 690 | vp = &opl3->voices[voice]; | 
|  | 691 | if (vp->state > 0 && vp->chan == chan && vp->note == note) { | 
|  | 692 | snd_opl3_kill_voice(opl3, voice); | 
|  | 693 | } | 
|  | 694 | } | 
|  | 695 | } else { | 
|  | 696 | /* remap OSS voices */ | 
|  | 697 | if (chan->number < MAX_OPL3_VOICES) { | 
|  | 698 | voice = snd_opl3_oss_map[chan->number]; | 
|  | 699 | snd_opl3_kill_voice(opl3, voice); | 
|  | 700 | } | 
|  | 701 | } | 
| Krzysztof Helt | 8dce39b | 2009-10-07 22:51:34 +0200 | [diff] [blame] | 702 | } | 
|  | 703 |  | 
|  | 704 | void snd_opl3_note_off(void *p, int note, int vel, | 
|  | 705 | struct snd_midi_channel *chan) | 
|  | 706 | { | 
|  | 707 | struct snd_opl3 *opl3 = p; | 
|  | 708 | unsigned long flags; | 
|  | 709 |  | 
|  | 710 | spin_lock_irqsave(&opl3->voice_lock, flags); | 
|  | 711 | snd_opl3_note_off_unsafe(p, note, vel, chan); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 712 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 
|  | 713 | } | 
|  | 714 |  | 
|  | 715 | /* | 
|  | 716 | * key pressure change | 
|  | 717 | */ | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 718 | void snd_opl3_key_press(void *p, int note, int vel, struct snd_midi_channel *chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 719 | { | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 720 | struct snd_opl3 *opl3; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 721 |  | 
|  | 722 | opl3 = p; | 
|  | 723 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 724 | snd_printk(KERN_DEBUG "Key pressure, ch#: %i, inst#: %i\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 725 | chan->number, chan->midi_program); | 
|  | 726 | #endif | 
|  | 727 | } | 
|  | 728 |  | 
|  | 729 | /* | 
|  | 730 | * terminate note | 
|  | 731 | */ | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 732 | void snd_opl3_terminate_note(void *p, int note, struct snd_midi_channel *chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 733 | { | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 734 | struct snd_opl3 *opl3; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 735 |  | 
|  | 736 | opl3 = p; | 
|  | 737 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 738 | snd_printk(KERN_DEBUG "Terminate note, ch#: %i, inst#: %i\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 739 | chan->number, chan->midi_program); | 
|  | 740 | #endif | 
|  | 741 | } | 
|  | 742 |  | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 743 | static void snd_opl3_update_pitch(struct snd_opl3 *opl3, int voice) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 744 | { | 
|  | 745 | unsigned short reg_side; | 
|  | 746 | unsigned char voice_offset; | 
|  | 747 | unsigned short opl3_reg; | 
|  | 748 |  | 
|  | 749 | unsigned char fnum, blocknum; | 
|  | 750 |  | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 751 | struct snd_opl3_voice *vp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 752 |  | 
| Takashi Iwai | 5e246b8 | 2008-08-08 17:12:47 +0200 | [diff] [blame] | 753 | if (snd_BUG_ON(voice >= MAX_OPL3_VOICES)) | 
|  | 754 | return; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 755 |  | 
|  | 756 | vp = &opl3->voices[voice]; | 
|  | 757 | if (vp->chan == NULL) | 
|  | 758 | return; /* not allocated? */ | 
|  | 759 |  | 
|  | 760 | if (voice < MAX_OPL2_VOICES) { | 
|  | 761 | /* Left register block for voices 0 .. 8 */ | 
|  | 762 | reg_side = OPL3_LEFT; | 
|  | 763 | voice_offset = voice; | 
|  | 764 | } else { | 
|  | 765 | /* Right register block for voices 9 .. 17 */ | 
|  | 766 | reg_side = OPL3_RIGHT; | 
|  | 767 | voice_offset = voice - MAX_OPL2_VOICES; | 
|  | 768 | } | 
|  | 769 |  | 
|  | 770 | snd_opl3_calc_pitch(&fnum, &blocknum, vp->note, vp->chan); | 
|  | 771 |  | 
|  | 772 | /* Set OPL3 FNUM_LOW register of requested voice */ | 
|  | 773 | opl3_reg = reg_side | (OPL3_REG_FNUM_LOW + voice_offset); | 
|  | 774 | opl3->command(opl3, opl3_reg, fnum); | 
|  | 775 |  | 
|  | 776 | vp->keyon_reg = blocknum; | 
|  | 777 |  | 
|  | 778 | /* Set output sound flag */ | 
|  | 779 | blocknum |= OPL3_KEYON_BIT; | 
|  | 780 |  | 
|  | 781 | /* Set OPL3 KEYON_BLOCK register of requested voice */ | 
|  | 782 | opl3_reg = reg_side | (OPL3_REG_KEYON_BLOCK + voice_offset); | 
|  | 783 | opl3->command(opl3, opl3_reg, blocknum); | 
|  | 784 |  | 
|  | 785 | vp->time = opl3->use_time++; | 
|  | 786 | } | 
|  | 787 |  | 
|  | 788 | /* | 
|  | 789 | * Update voice pitch controller | 
|  | 790 | */ | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 791 | static void snd_opl3_pitch_ctrl(struct snd_opl3 *opl3, struct snd_midi_channel *chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 792 | { | 
|  | 793 | int voice; | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 794 | struct snd_opl3_voice *vp; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 795 |  | 
|  | 796 | unsigned long flags; | 
|  | 797 |  | 
|  | 798 | spin_lock_irqsave(&opl3->voice_lock, flags); | 
|  | 799 |  | 
|  | 800 | if (opl3->synth_mode == SNDRV_OPL3_MODE_SEQ) { | 
|  | 801 | for (voice = 0; voice < opl3->max_voices; voice++) { | 
|  | 802 | vp = &opl3->voices[voice]; | 
|  | 803 | if (vp->state > 0 && vp->chan == chan) { | 
|  | 804 | snd_opl3_update_pitch(opl3, voice); | 
|  | 805 | } | 
|  | 806 | } | 
|  | 807 | } else { | 
|  | 808 | /* remap OSS voices */ | 
|  | 809 | if (chan->number < MAX_OPL3_VOICES) { | 
|  | 810 | voice = snd_opl3_oss_map[chan->number]; | 
|  | 811 | snd_opl3_update_pitch(opl3, voice); | 
|  | 812 | } | 
|  | 813 | } | 
|  | 814 | spin_unlock_irqrestore(&opl3->voice_lock, flags); | 
|  | 815 | } | 
|  | 816 |  | 
|  | 817 | /* | 
| Robert P. J. Day | 3a4fa0a | 2007-10-19 23:10:43 +0200 | [diff] [blame] | 818 | * Deal with a controller type event.  This includes all types of | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 819 | * control events, not just the midi controllers | 
|  | 820 | */ | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 821 | void snd_opl3_control(void *p, int type, struct snd_midi_channel *chan) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 822 | { | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 823 | struct snd_opl3 *opl3; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 824 |  | 
|  | 825 | opl3 = p; | 
|  | 826 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 827 | snd_printk(KERN_DEBUG "Controller, TYPE = %i, ch#: %i, inst#: %i\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 828 | type, chan->number, chan->midi_program); | 
|  | 829 | #endif | 
|  | 830 |  | 
|  | 831 | switch (type) { | 
|  | 832 | case MIDI_CTL_MSB_MODWHEEL: | 
|  | 833 | if (chan->control[MIDI_CTL_MSB_MODWHEEL] > 63) | 
|  | 834 | opl3->drum_reg |= OPL3_VIBRATO_DEPTH; | 
|  | 835 | else | 
|  | 836 | opl3->drum_reg &= ~OPL3_VIBRATO_DEPTH; | 
|  | 837 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, | 
|  | 838 | opl3->drum_reg); | 
|  | 839 | break; | 
|  | 840 | case MIDI_CTL_E2_TREMOLO_DEPTH: | 
|  | 841 | if (chan->control[MIDI_CTL_E2_TREMOLO_DEPTH] > 63) | 
|  | 842 | opl3->drum_reg |= OPL3_TREMOLO_DEPTH; | 
|  | 843 | else | 
|  | 844 | opl3->drum_reg &= ~OPL3_TREMOLO_DEPTH; | 
|  | 845 | opl3->command(opl3, OPL3_LEFT | OPL3_REG_PERCUSSION, | 
|  | 846 | opl3->drum_reg); | 
|  | 847 | break; | 
|  | 848 | case MIDI_CTL_PITCHBEND: | 
|  | 849 | snd_opl3_pitch_ctrl(opl3, chan); | 
|  | 850 | break; | 
|  | 851 | } | 
|  | 852 | } | 
|  | 853 |  | 
|  | 854 | /* | 
|  | 855 | * NRPN events | 
|  | 856 | */ | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 857 | void snd_opl3_nrpn(void *p, struct snd_midi_channel *chan, | 
|  | 858 | struct snd_midi_channel_set *chset) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 859 | { | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 860 | struct snd_opl3 *opl3; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 861 |  | 
|  | 862 | opl3 = p; | 
|  | 863 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 864 | snd_printk(KERN_DEBUG "NRPN, ch#: %i, inst#: %i\n", | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 865 | chan->number, chan->midi_program); | 
|  | 866 | #endif | 
|  | 867 | } | 
|  | 868 |  | 
|  | 869 | /* | 
|  | 870 | * receive sysex | 
|  | 871 | */ | 
|  | 872 | void snd_opl3_sysex(void *p, unsigned char *buf, int len, | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 873 | int parsed, struct snd_midi_channel_set *chset) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 874 | { | 
| Takashi Iwai | 5b1646a | 2005-11-17 14:13:14 +0100 | [diff] [blame] | 875 | struct snd_opl3 *opl3; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 876 |  | 
|  | 877 | opl3 = p; | 
|  | 878 | #ifdef DEBUG_MIDI | 
| Takashi Iwai | 4520383 | 2009-02-05 15:51:50 +0100 | [diff] [blame] | 879 | snd_printk(KERN_DEBUG "SYSEX\n"); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 880 | #endif | 
|  | 881 | } |