/*
* 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 <linux/export.h>
#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(struct snd_emux *emu, struct snd_emux_port *port,
int *notep, int vel, struct snd_midi_channel *chan,
struct snd_sf_zone **table);
static int get_bank(struct snd_emux_port *port, struct snd_midi_channel *chan);
static void terminate_note1(struct snd_emux *emu, int note,
struct snd_midi_channel *chan, int free);
static void exclusive_note_off(struct snd_emux *emu, struct snd_emux_port *port,
int exclass);
static void terminate_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int free);
static void update_voice(struct snd_emux *emu, struct snd_emux_voice *vp, int update);
static void setup_voice(struct snd_emux_voice *vp);
static int calc_pan(struct snd_emux_voice *vp);
static int calc_volume(struct snd_emux_voice *vp);
static int calc_pitch(struct snd_emux_voice *vp);
/*
* Start a note.
*/
void
snd_emux_note_on(void *p, int note, int vel, struct snd_midi_channel *chan)
{
struct snd_emux *emu;
int i, key, nvoices;
struct snd_emux_voice *vp;
struct snd_sf_zone *table[SNDRV_EMUX_MAX_MULTI_VOICES];
unsigned long flags;
struct snd_emux_port *port;
port = p;
if (snd_BUG_ON(!port || !chan))
return;
emu = port->emu;
if (snd_BUG_ON(!emu || !emu->ops.get_voice || !emu->ops.trigger))
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++) {
struct snd_sf_zone *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;
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++) {