Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/sound/isa/sb/emu8000_callback.c
10817 views
1
/*
2
* synth callback routines for the emu8000 (AWE32/64)
3
*
4
* Copyright (C) 1999 Steve Ratcliffe
5
* Copyright (C) 1999-2000 Takashi Iwai <[email protected]>
6
*
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
11
*
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
*
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
*/
21
22
#include "emu8000_local.h"
23
#include <sound/asoundef.h>
24
25
/*
26
* prototypes
27
*/
28
static struct snd_emux_voice *get_voice(struct snd_emux *emu,
29
struct snd_emux_port *port);
30
static int start_voice(struct snd_emux_voice *vp);
31
static void trigger_voice(struct snd_emux_voice *vp);
32
static void release_voice(struct snd_emux_voice *vp);
33
static void update_voice(struct snd_emux_voice *vp, int update);
34
static void reset_voice(struct snd_emux *emu, int ch);
35
static void terminate_voice(struct snd_emux_voice *vp);
36
static void sysex(struct snd_emux *emu, char *buf, int len, int parsed,
37
struct snd_midi_channel_set *chset);
38
#ifdef CONFIG_SND_SEQUENCER_OSS
39
static int oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2);
40
#endif
41
static int load_fx(struct snd_emux *emu, int type, int mode,
42
const void __user *buf, long len);
43
44
static void set_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
45
static void set_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
46
static void set_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
47
static void set_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
48
static void set_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
49
static void set_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
50
static void set_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp);
51
static void snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int ch);
52
53
/*
54
* Ensure a value is between two points
55
* macro evaluates its args more than once, so changed to upper-case.
56
*/
57
#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
58
#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
59
60
61
/*
62
* set up operators
63
*/
64
static struct snd_emux_operators emu8000_ops = {
65
.owner = THIS_MODULE,
66
.get_voice = get_voice,
67
.prepare = start_voice,
68
.trigger = trigger_voice,
69
.release = release_voice,
70
.update = update_voice,
71
.terminate = terminate_voice,
72
.reset = reset_voice,
73
.sample_new = snd_emu8000_sample_new,
74
.sample_free = snd_emu8000_sample_free,
75
.sample_reset = snd_emu8000_sample_reset,
76
.load_fx = load_fx,
77
.sysex = sysex,
78
#ifdef CONFIG_SND_SEQUENCER_OSS
79
.oss_ioctl = oss_ioctl,
80
#endif
81
};
82
83
void
84
snd_emu8000_ops_setup(struct snd_emu8000 *hw)
85
{
86
hw->emu->ops = emu8000_ops;
87
}
88
89
90
91
/*
92
* Terminate a voice
93
*/
94
static void
95
release_voice(struct snd_emux_voice *vp)
96
{
97
int dcysusv;
98
struct snd_emu8000 *hw;
99
100
hw = vp->hw;
101
dcysusv = 0x8000 | (unsigned char)vp->reg.parm.modrelease;
102
EMU8000_DCYSUS_WRITE(hw, vp->ch, dcysusv);
103
dcysusv = 0x8000 | (unsigned char)vp->reg.parm.volrelease;
104
EMU8000_DCYSUSV_WRITE(hw, vp->ch, dcysusv);
105
}
106
107
108
/*
109
*/
110
static void
111
terminate_voice(struct snd_emux_voice *vp)
112
{
113
struct snd_emu8000 *hw;
114
115
hw = vp->hw;
116
EMU8000_DCYSUSV_WRITE(hw, vp->ch, 0x807F);
117
}
118
119
120
/*
121
*/
122
static void
123
update_voice(struct snd_emux_voice *vp, int update)
124
{
125
struct snd_emu8000 *hw;
126
127
hw = vp->hw;
128
if (update & SNDRV_EMUX_UPDATE_VOLUME)
129
set_volume(hw, vp);
130
if (update & SNDRV_EMUX_UPDATE_PITCH)
131
set_pitch(hw, vp);
132
if ((update & SNDRV_EMUX_UPDATE_PAN) &&
133
vp->port->ctrls[EMUX_MD_REALTIME_PAN])
134
set_pan(hw, vp);
135
if (update & SNDRV_EMUX_UPDATE_FMMOD)
136
set_fmmod(hw, vp);
137
if (update & SNDRV_EMUX_UPDATE_TREMFREQ)
138
set_tremfreq(hw, vp);
139
if (update & SNDRV_EMUX_UPDATE_FM2FRQ2)
140
set_fm2frq2(hw, vp);
141
if (update & SNDRV_EMUX_UPDATE_Q)
142
set_filterQ(hw, vp);
143
}
144
145
146
/*
147
* Find a channel (voice) within the EMU that is not in use or at least
148
* less in use than other channels. Always returns a valid pointer
149
* no matter what. If there is a real shortage of voices then one
150
* will be cut. Such is life.
151
*
152
* The channel index (vp->ch) must be initialized in this routine.
153
* In Emu8k, it is identical with the array index.
154
*/
155
static struct snd_emux_voice *
156
get_voice(struct snd_emux *emu, struct snd_emux_port *port)
157
{
158
int i;
159
struct snd_emux_voice *vp;
160
struct snd_emu8000 *hw;
161
162
/* what we are looking for, in order of preference */
163
enum {
164
OFF=0, RELEASED, PLAYING, END
165
};
166
167
/* Keeps track of what we are finding */
168
struct best {
169
unsigned int time;
170
int voice;
171
} best[END];
172
struct best *bp;
173
174
hw = emu->hw;
175
176
for (i = 0; i < END; i++) {
177
best[i].time = (unsigned int)(-1); /* XXX MAX_?INT really */;
178
best[i].voice = -1;
179
}
180
181
/*
182
* Go through them all and get a best one to use.
183
*/
184
for (i = 0; i < emu->max_voices; i++) {
185
int state, val;
186
187
vp = &emu->voices[i];
188
state = vp->state;
189
190
if (state == SNDRV_EMUX_ST_OFF)
191
bp = best + OFF;
192
else if (state == SNDRV_EMUX_ST_RELEASED ||
193
state == SNDRV_EMUX_ST_PENDING) {
194
bp = best + RELEASED;
195
val = (EMU8000_CVCF_READ(hw, vp->ch) >> 16) & 0xffff;
196
if (! val)
197
bp = best + OFF;
198
}
199
else if (state & SNDRV_EMUX_ST_ON)
200
bp = best + PLAYING;
201
else
202
continue;
203
204
/* check if sample is finished playing (non-looping only) */
205
if (state != SNDRV_EMUX_ST_OFF &&
206
(vp->reg.sample_mode & SNDRV_SFNT_SAMPLE_SINGLESHOT)) {
207
val = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
208
if (val >= vp->reg.loopstart)
209
bp = best + OFF;
210
}
211
212
if (vp->time < bp->time) {
213
bp->time = vp->time;
214
bp->voice = i;
215
}
216
}
217
218
for (i = 0; i < END; i++) {
219
if (best[i].voice >= 0) {
220
vp = &emu->voices[best[i].voice];
221
vp->ch = best[i].voice;
222
return vp;
223
}
224
}
225
226
/* not found */
227
return NULL;
228
}
229
230
/*
231
*/
232
static int
233
start_voice(struct snd_emux_voice *vp)
234
{
235
unsigned int temp;
236
int ch;
237
int addr;
238
struct snd_midi_channel *chan;
239
struct snd_emu8000 *hw;
240
241
hw = vp->hw;
242
ch = vp->ch;
243
chan = vp->chan;
244
245
/* channel to be silent and idle */
246
EMU8000_DCYSUSV_WRITE(hw, ch, 0x0080);
247
EMU8000_VTFT_WRITE(hw, ch, 0x0000FFFF);
248
EMU8000_CVCF_WRITE(hw, ch, 0x0000FFFF);
249
EMU8000_PTRX_WRITE(hw, ch, 0);
250
EMU8000_CPF_WRITE(hw, ch, 0);
251
252
/* set pitch offset */
253
set_pitch(hw, vp);
254
255
/* set envelope parameters */
256
EMU8000_ENVVAL_WRITE(hw, ch, vp->reg.parm.moddelay);
257
EMU8000_ATKHLD_WRITE(hw, ch, vp->reg.parm.modatkhld);
258
EMU8000_DCYSUS_WRITE(hw, ch, vp->reg.parm.moddcysus);
259
EMU8000_ENVVOL_WRITE(hw, ch, vp->reg.parm.voldelay);
260
EMU8000_ATKHLDV_WRITE(hw, ch, vp->reg.parm.volatkhld);
261
/* decay/sustain parameter for volume envelope is used
262
for triggerg the voice */
263
264
/* cutoff and volume */
265
set_volume(hw, vp);
266
267
/* modulation envelope heights */
268
EMU8000_PEFE_WRITE(hw, ch, vp->reg.parm.pefe);
269
270
/* lfo1/2 delay */
271
EMU8000_LFO1VAL_WRITE(hw, ch, vp->reg.parm.lfo1delay);
272
EMU8000_LFO2VAL_WRITE(hw, ch, vp->reg.parm.lfo2delay);
273
274
/* lfo1 pitch & cutoff shift */
275
set_fmmod(hw, vp);
276
/* lfo1 volume & freq */
277
set_tremfreq(hw, vp);
278
/* lfo2 pitch & freq */
279
set_fm2frq2(hw, vp);
280
/* pan & loop start */
281
set_pan(hw, vp);
282
283
/* chorus & loop end (chorus 8bit, MSB) */
284
addr = vp->reg.loopend - 1;
285
temp = vp->reg.parm.chorus;
286
temp += (int)chan->control[MIDI_CTL_E3_CHORUS_DEPTH] * 9 / 10;
287
LIMITMAX(temp, 255);
288
temp = (temp <<24) | (unsigned int)addr;
289
EMU8000_CSL_WRITE(hw, ch, temp);
290
291
/* Q & current address (Q 4bit value, MSB) */
292
addr = vp->reg.start - 1;
293
temp = vp->reg.parm.filterQ;
294
temp = (temp<<28) | (unsigned int)addr;
295
EMU8000_CCCA_WRITE(hw, ch, temp);
296
297
/* clear unknown registers */
298
EMU8000_00A0_WRITE(hw, ch, 0);
299
EMU8000_0080_WRITE(hw, ch, 0);
300
301
/* reset volume */
302
temp = vp->vtarget << 16;
303
EMU8000_VTFT_WRITE(hw, ch, temp | vp->ftarget);
304
EMU8000_CVCF_WRITE(hw, ch, temp | 0xff00);
305
306
return 0;
307
}
308
309
/*
310
* Start envelope
311
*/
312
static void
313
trigger_voice(struct snd_emux_voice *vp)
314
{
315
int ch = vp->ch;
316
unsigned int temp;
317
struct snd_emu8000 *hw;
318
319
hw = vp->hw;
320
321
/* set reverb and pitch target */
322
temp = vp->reg.parm.reverb;
323
temp += (int)vp->chan->control[MIDI_CTL_E1_REVERB_DEPTH] * 9 / 10;
324
LIMITMAX(temp, 255);
325
temp = (temp << 8) | (vp->ptarget << 16) | vp->aaux;
326
EMU8000_PTRX_WRITE(hw, ch, temp);
327
EMU8000_CPF_WRITE(hw, ch, vp->ptarget << 16);
328
EMU8000_DCYSUSV_WRITE(hw, ch, vp->reg.parm.voldcysus);
329
}
330
331
/*
332
* reset voice parameters
333
*/
334
static void
335
reset_voice(struct snd_emux *emu, int ch)
336
{
337
struct snd_emu8000 *hw;
338
339
hw = emu->hw;
340
EMU8000_DCYSUSV_WRITE(hw, ch, 0x807F);
341
snd_emu8000_tweak_voice(hw, ch);
342
}
343
344
/*
345
* Set the pitch of a possibly playing note.
346
*/
347
static void
348
set_pitch(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
349
{
350
EMU8000_IP_WRITE(hw, vp->ch, vp->apitch);
351
}
352
353
/*
354
* Set the volume of a possibly already playing note
355
*/
356
static void
357
set_volume(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
358
{
359
int ifatn;
360
361
ifatn = (unsigned char)vp->acutoff;
362
ifatn = (ifatn << 8);
363
ifatn |= (unsigned char)vp->avol;
364
EMU8000_IFATN_WRITE(hw, vp->ch, ifatn);
365
}
366
367
/*
368
* Set pan and loop start address.
369
*/
370
static void
371
set_pan(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
372
{
373
unsigned int temp;
374
375
temp = ((unsigned int)vp->apan<<24) | ((unsigned int)vp->reg.loopstart - 1);
376
EMU8000_PSST_WRITE(hw, vp->ch, temp);
377
}
378
379
#define MOD_SENSE 18
380
381
static void
382
set_fmmod(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
383
{
384
unsigned short fmmod;
385
short pitch;
386
unsigned char cutoff;
387
int modulation;
388
389
pitch = (char)(vp->reg.parm.fmmod>>8);
390
cutoff = (vp->reg.parm.fmmod & 0xff);
391
modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
392
pitch += (MOD_SENSE * modulation) / 1200;
393
LIMITVALUE(pitch, -128, 127);
394
fmmod = ((unsigned char)pitch<<8) | cutoff;
395
EMU8000_FMMOD_WRITE(hw, vp->ch, fmmod);
396
}
397
398
/* set tremolo (lfo1) volume & frequency */
399
static void
400
set_tremfreq(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
401
{
402
EMU8000_TREMFRQ_WRITE(hw, vp->ch, vp->reg.parm.tremfrq);
403
}
404
405
/* set lfo2 pitch & frequency */
406
static void
407
set_fm2frq2(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
408
{
409
unsigned short fm2frq2;
410
short pitch;
411
unsigned char freq;
412
int modulation;
413
414
pitch = (char)(vp->reg.parm.fm2frq2>>8);
415
freq = vp->reg.parm.fm2frq2 & 0xff;
416
modulation = vp->chan->gm_modulation + vp->chan->midi_pressure;
417
pitch += (MOD_SENSE * modulation) / 1200;
418
LIMITVALUE(pitch, -128, 127);
419
fm2frq2 = ((unsigned char)pitch<<8) | freq;
420
EMU8000_FM2FRQ2_WRITE(hw, vp->ch, fm2frq2);
421
}
422
423
/* set filterQ */
424
static void
425
set_filterQ(struct snd_emu8000 *hw, struct snd_emux_voice *vp)
426
{
427
unsigned int addr;
428
addr = EMU8000_CCCA_READ(hw, vp->ch) & 0xffffff;
429
addr |= (vp->reg.parm.filterQ << 28);
430
EMU8000_CCCA_WRITE(hw, vp->ch, addr);
431
}
432
433
/*
434
* set the envelope & LFO parameters to the default values
435
*/
436
static void
437
snd_emu8000_tweak_voice(struct snd_emu8000 *emu, int i)
438
{
439
/* set all mod/vol envelope shape to minimum */
440
EMU8000_ENVVOL_WRITE(emu, i, 0x8000);
441
EMU8000_ENVVAL_WRITE(emu, i, 0x8000);
442
EMU8000_DCYSUS_WRITE(emu, i, 0x7F7F);
443
EMU8000_ATKHLDV_WRITE(emu, i, 0x7F7F);
444
EMU8000_ATKHLD_WRITE(emu, i, 0x7F7F);
445
EMU8000_PEFE_WRITE(emu, i, 0); /* mod envelope height to zero */
446
EMU8000_LFO1VAL_WRITE(emu, i, 0x8000); /* no delay for LFO1 */
447
EMU8000_LFO2VAL_WRITE(emu, i, 0x8000);
448
EMU8000_IP_WRITE(emu, i, 0xE000); /* no pitch shift */
449
EMU8000_IFATN_WRITE(emu, i, 0xFF00); /* volume to minimum */
450
EMU8000_FMMOD_WRITE(emu, i, 0);
451
EMU8000_TREMFRQ_WRITE(emu, i, 0);
452
EMU8000_FM2FRQ2_WRITE(emu, i, 0);
453
}
454
455
/*
456
* sysex callback
457
*/
458
static void
459
sysex(struct snd_emux *emu, char *buf, int len, int parsed, struct snd_midi_channel_set *chset)
460
{
461
struct snd_emu8000 *hw;
462
463
hw = emu->hw;
464
465
switch (parsed) {
466
case SNDRV_MIDI_SYSEX_GS_CHORUS_MODE:
467
hw->chorus_mode = chset->gs_chorus_mode;
468
snd_emu8000_update_chorus_mode(hw);
469
break;
470
471
case SNDRV_MIDI_SYSEX_GS_REVERB_MODE:
472
hw->reverb_mode = chset->gs_reverb_mode;
473
snd_emu8000_update_reverb_mode(hw);
474
break;
475
}
476
}
477
478
479
#ifdef CONFIG_SND_SEQUENCER_OSS
480
/*
481
* OSS ioctl callback
482
*/
483
static int
484
oss_ioctl(struct snd_emux *emu, int cmd, int p1, int p2)
485
{
486
struct snd_emu8000 *hw;
487
488
hw = emu->hw;
489
490
switch (cmd) {
491
case _EMUX_OSS_REVERB_MODE:
492
hw->reverb_mode = p1;
493
snd_emu8000_update_reverb_mode(hw);
494
break;
495
496
case _EMUX_OSS_CHORUS_MODE:
497
hw->chorus_mode = p1;
498
snd_emu8000_update_chorus_mode(hw);
499
break;
500
501
case _EMUX_OSS_INITIALIZE_CHIP:
502
/* snd_emu8000_init(hw); */ /*ignored*/
503
break;
504
505
case _EMUX_OSS_EQUALIZER:
506
hw->bass_level = p1;
507
hw->treble_level = p2;
508
snd_emu8000_update_equalizer(hw);
509
break;
510
}
511
return 0;
512
}
513
#endif
514
515
516
/*
517
* additional patch keys
518
*/
519
520
#define SNDRV_EMU8000_LOAD_CHORUS_FX 0x10 /* optarg=mode */
521
#define SNDRV_EMU8000_LOAD_REVERB_FX 0x11 /* optarg=mode */
522
523
524
/*
525
* callback routine
526
*/
527
528
static int
529
load_fx(struct snd_emux *emu, int type, int mode, const void __user *buf, long len)
530
{
531
struct snd_emu8000 *hw;
532
hw = emu->hw;
533
534
/* skip header */
535
buf += 16;
536
len -= 16;
537
538
switch (type) {
539
case SNDRV_EMU8000_LOAD_CHORUS_FX:
540
return snd_emu8000_load_chorus_fx(hw, mode, buf, len);
541
case SNDRV_EMU8000_LOAD_REVERB_FX:
542
return snd_emu8000_load_reverb_fx(hw, mode, buf, len);
543
}
544
return -EINVAL;
545
}
546
547
548