/*
* Midi synth routines for the Emu8k/Emu10k1
*
* Copyright (C) 1999 Steve Ratcliffe
* Copyright (c) 1999-2000 Takashi Iwai <tiwai@suse.de>
*
* Contains code based on awe_wave.c by Takashi Iwai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "emux_voice.h"
#include <sound/asoundef.h>
/*
* Prototypes
*/
/*
* Ensure a value is between two points
* macro evaluates its args more than once, so changed to upper-case.
*/
#define LIMITVALUE(x, a, b) do { if ((x) < (a)) (x) = (a); else if ((x) > (b)) (x) = (b); } while (0)
#define LIMITMAX(x, a) do {if ((x) > (a)) (x) = (a); } while (0)
static int get_zone(snd_emux_t *emu, snd_emux_port_t *port, int *notep, int vel, snd_midi_channel_t *chan, snd_sf_zone_t **table);
static int get_bank(snd_emux_port_t *port, snd_midi_channel_t *chan);
static void terminate_note1(snd_emux_t *emu, int note, snd_midi_channel_t *chan, int free);
static void exclusive_note_off(snd_emux_t *emu, snd_emux_port_t *port, int exclass);
static void terminate_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int free);
static void update_voice(snd_emux_t *emu, snd_emux_voice_t *vp, int update);
static void setup_voice(snd_emux_voice_t *vp);
static int calc_pan(snd_emux_voice_t *vp);
static int calc_volume(snd_emux_voice_t *vp);
static int calc_pitch(snd_emux_voice_t *vp);
/*
* Start a note.
*/
void
snd_emux_note_on(void *p, int note, int vel, snd_midi_channel_t *chan)
{
snd_emux_t *emu;
int i, key, nvoices;
snd_emux_voice_t *vp;
snd_sf_zone_t *table[SNDRV_EMUX_MAX_MULTI_VOICES];
unsigned long flags;
snd_emux_port_t *port;
port = p;
snd_assert(port != NULL && chan != NULL, return);
emu = port->emu;
snd_assert(emu != NULL, return);
snd_assert(emu->ops.get_voice != NULL, return);
snd_assert(emu->ops.trigger != NULL, return);
key = note; /* remember the original note */
nvoices = get_zone(emu, port, ¬e, vel, chan, table);
if (! nvoices)
return;
/* exclusive note off */
for (i = 0; i < nvoices; i++) {
snd_sf_zone_t *zp = table[i];
if (zp && zp->v.exclusiveClass)
exclusive_note_off(emu, port, zp->v.exclusiveClass);
}
#if 0 // seems not necessary
/* Turn off the same note on the same channel. */
terminate_note1(emu, key, chan, 0);
#endif
spin_lock_irqsave(&emu->voice_lock, flags);
for (i = 0; i < nvoices; i++) {
/* set up each voice parameter */
/* at this stage, we don't trigger the voice yet. */
if (table[i] == NULL)
continue;
vp = emu->ops.get_voice(emu, port);
if (vp == NULL || vp->ch < 0)
continue;
snd_assert(vp->emu != NULL && vp->hw != NULL, return);
if (STATE_IS_PLAYING(vp->state))
emu->ops.terminate(vp);
vp->time = emu->use_time++;
vp->chan = chan;
vp->port = port;
vp->key = key;
vp->note = note;
vp->velocity = vel;
vp->zone = table[i];
if (vp->zone->sample)
vp->block = vp->zone->sample->block;
else
vp->block = NULL;
setup_voice(vp);
vp->state = SNDRV_EMUX_ST_STANDBY;
if (emu->ops.prepare) {
vp->state = SNDRV_EMUX_ST_OFF;
if (emu->ops.prepare(vp) >= 0)
vp->state = SNDRV_EMUX_ST_STANDBY;
}
}
/* start envelope now */
for (i = 0; i < emu->max_voices; i++) {
vp = &emu->voices[i];
if (vp->state == SNDRV_EMUX_ST_STANDBY &&
vp->chan == chan) {
emu