/*
* Copyright (c) by Uros Bizjak <uros@kss-loka.si>
*
* Midi synth routines for OPL2/OPL3/OPL4 FM
*
* 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
*
*/
#undef DEBUG_ALLOC
#undef DEBUG_MIDI
#include "opl3_voice.h"
#include <sound/asoundef.h>
extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
extern int use_internal_drums;
static void snd_opl3_note_off_unsafe(void *p, int note, int vel,
struct snd_midi_channel *chan);
/*
* The next table looks magical, but it certainly is not. Its values have
* been calculated as table[i]=8*log(i/64)/log(2) with an obvious exception
* for i=0. This log-table converts a linear volume-scaling (0..127) to a
* logarithmic scaling as present in the FM-synthesizer chips. so : Volume
* 64 = 0 db = relative volume 0 and: Volume 32 = -6 db = relative
* volume -8 it was implemented as a table because it is only 128 bytes and
* it saves a lot of log() calculations. (Rob Hooft <hooft@chem.ruu.nl>)
*/
static char opl3_volume_table[128] =
{
-63, -48, -40, -35, -32, -29, -27, -26,
-24, -23, -21, -20, -19, -18, -18, -17,
-16, -15, -15, -14, -13, -13, -12, -12,
-11, -11, -10, -10, -10, -9, -9, -8,
-8, -8, -7, -7, -7, -6, -6, -6,
-5, -5, -5, -5, -4, -4, -4, -4,
-3, -3, -3, -3, -2, -2, -2, -2,
-2, -1, -1, -1, -1, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1,
1, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 4,
4, 4, 4, 4, 4, 4, 4, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6,
6, 7, 7, 7, 7, 7, 7, 7,
7, 7, 7, 8, 8, 8, 8, 8
};
void snd_opl3_calc_volume(unsigned char *volbyte, int vel,
struct snd_midi_channel *chan)
{
int oldvol, newvol, n;
int volume;
volume = (vel * chan->gm_volume * chan->gm_expression) / (127*127);
if (volume > 127)
volume = 127;
oldvol = OPL3_TOTAL_LEVEL_MASK - (*volbyte & OPL3_TOTAL_LEVEL_MASK);
newvol = opl3_volume_table[volume] + oldvol;
if (newvol > OPL3_TOTAL_LEVEL_MASK)
newvol = OPL3_TOTAL_LEVEL_MASK;
else if (newvol < 0)
newvol = 0;
n = OPL3_TOTAL_LEVEL_MASK - (newvol & OPL3_TOTAL_LEVEL_MASK);
*volbyte = (*volbyte & OPL3_KSL_MASK) | (n & OPL3_TOTAL_LEVEL_MASK);
}
/*
* Converts the note frequency to block and fnum values for the FM chip
*/
static short opl3_note_table[16] =
{
305, 323, /* for pitch bending, -2 semitones */
343, 363, 385, 408, 432, 458, 485, 514, 544, 577, 611, 647,
686, 726 /* for pitch bending, +2 semitones */
};
static void snd_opl3_calc_pitch(unsigned char *fnum, unsigned char *blocknum,
int note, struct snd_midi_channel *chan)
{
int block = ((note / 12) & 0x07) - 1;
int idx = (note % 12) + 2;
int freq;
if (chan->midi_pitchbend) {
int pitchbend = chan->midi_pitchbend;
int segment;
if (pitchbend > 0x1FFF)
pitchbend = 0x1FFF;
segment = pitchbend / 0x1000;
freq = opl3_note_table[idx+segment];
freq += ((opl3_note_table[idx+segment+1] - freq) *
(pitchbend % 0x1000)) / 0x1000;
} else {
freq = opl3_note_table[idx];
}
*fnum = (unsigned char) freq;
*blocknum = ((freq >> 8) & OPL3_FNUM_HIGH_MASK) |
((block << 2) &