/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

#include "common/debug.h"
#include "common/system.h"

#include "audio/fmopl.h"
#include "audio/mpu401.h"
#include "audio/mididrv.h"

namespace Parallaction {

const uint kNumVoices = 9;
// adlib FM voices 0-5
const uint kNumMelodic = 6;
// adlib FM voice 6 and 7-8
const uint kNumPercussion = 5;

// mask for maximum volume level
#define LEVEL_MASK 0x7f

struct OPLOperator {
	uint8 characteristic; // amplitude modulation, vibrato, envelope, keyboard scaling, modulator frequency
	uint8 levels;
	uint8 attackDecay;
	uint8 sustainRelease;
	uint8 waveform;
};

struct MelodicProgram {
	OPLOperator op[2];
	uint8 feedbackAlgo;
};

struct PercussionNote {
	OPLOperator op[2];
	uint8 feedbackAlgo;
	uint8 percussion;
	uint8 valid;
	uint16 frequency;
	uint8 octave;
};

static const MelodicProgram melodicPrograms[128] = {
	{{{   0x1, 0x51, 0xf2, 0xb2,  0x0 }, { 0x11,  0x0, 0xf2, 0xa2,  0x0 }},  0x0 },
	{{{  0xc2, 0x4b, 0xf1, 0x53,  0x0 }, { 0xd2,  0x0, 0xf2, 0x74,  0x0 }},  0x4 },
	{{{  0x81, 0x9d, 0xf2, 0x74,  0x0 }, { 0x13,  0x0, 0xf2, 0xf1,  0x0 }},  0x6 },
	{{{   0x3, 0x4f, 0xf1, 0x53,  0x0 }, { 0x17,  0x3, 0xf2, 0x74,  0x0 }},  0x6 },
	{{{  0xd1, 0x81, 0x81, 0x73,  0x2 }, { 0xd4,  0x0, 0xe1, 0x34,  0x0 }},  0x3 },
	{{{   0x1,  0x0, 0x94, 0xa6,  0x0 }, {  0x2,  0x0, 0x83, 0x26,  0x0 }},  0x1 },
	{{{  0xf3, 0x84, 0x81,  0x2,  0x1 }, { 0x55, 0x80, 0xdd,  0x3,  0x0 }},  0x4 },
	{{{   0x5, 0x8a, 0xf2, 0x26,  0x0 }, {  0x1, 0x80, 0xf3, 0x48,  0x0 }},  0x0 },
	{{{  0x32,  0x0, 0xb1, 0x14,  0x0 }, { 0x12,  0x0, 0xfd, 0x36,  0x0 }},  0x3 },
	{{{   0x1,  0x0, 0x82,  0xa,  0x2 }, {  0x2,  0x0, 0x85, 0x15,  0x0 }},  0x3 },
	{{{  0xd1,  0x1, 0x97, 0xaa,  0x0 }, {  0x4,  0xd, 0xf3, 0xa5,  0x1 }},  0x9 },
	{{{  0x17,  0x0, 0xf2, 0x62,  0x0 }, { 0x12,  0x0, 0xf2, 0x72,  0x0 }},  0x8 },
	{{{   0x6,  0x0, 0xff, 0xf4,  0x0 }, { 0xc4,  0x0, 0xf8, 0xb5,  0x0 }},  0xe },
	{{{  0xc0, 0x81, 0xf2, 0x13,  0x2 }, { 0xc0, 0xc1, 0xf3, 0x14,  0x2 }},  0xb },
	{{{  0x44, 0x53, 0xf5, 0x31,  0x0 }, { 0x60, 0x80, 0xfd, 0x22,  0x0 }},  0x6 },
	{{{  0xe0, 0x80, 0xf4, 0xf2,  0x0 }, { 0x61,  0x0, 0xf2,  0x6,  0x0 }},  0x8 },
	{{{  0xc1,  0x6, 0x83, 0x23,  0x0 }, { 0xc1,  0x4, 0xf0, 0x26,  0x0 }},  0x1 },
	{{{  0x26,  0x0, 0xf4, 0xb6,  0x0 }, { 0x21,  0x0, 0x81, 0x4b,  0x0 }},  0x1 },
	{{{  0x24, 0x80, 0xff,  0xf,  0x0 }, { 0x21, 0x80, 0xff,  0xf,  0x0 }},  0x1 },
	{{{  0x24, 0x4f, 0xf2,  0xb,  0x0 }, { 0x31,  0x0, 0x52,  0xb,  0x0 }},  0xb },
	{{{  0x31,  0x8, 0x81,  0xb,  0x0 }, { 0xa1, 0x80, 0x92, 0x3b,  0x0 }},  0x0 },
	{{{  0x70, 0xc5, 0x52, 0x11,  0x1 }, { 0x71, 0x80, 0x31, 0xfe,  0x1 }},  0x0 },
	{{{  0x51, 0x88, 0x10, 0xf0,  0x0 }, { 0x42, 0x83, 0x40, 0xfc,  0x0 }},  0x8 },
	{{{  0xf0, 0xd9, 0x81,  0x3,  0x0 }, { 0xb1, 0x80, 0xf1,  0x5,  0x0 }},  0xa },
	{{{  0x21, 0x4f, 0xf1, 0x31,  0x0 }, {  0x2, 0x80, 0xc3, 0x45,  0x0 }},  0x0 },
	{{{   0x7, 0x8f, 0x9c, 0x33,  0x1 }, {  0x1, 0x80, 0x8a, 0x13,  0x0 }},  0x0 },
	{{{  0x21, 0x40, 0xf1, 0x31,  0x0 }, {  0x6, 0x80, 0xf4, 0x44,  0x0 }},  0x0 },
	{{{  0x21, 0x40, 0xf1, 0x31,  0x3 }, { 0x81,  0x0, 0xf4, 0x44,  0x2 }},  0x2 },
	{{{  0x11, 0x8d, 0xfd, 0x11,  0x0 }, { 0x11, 0x80, 0xfd, 0x11,  0x0 }},  0x8 },
	{{{  0xf0,  0x1, 0x97, 0x17,  0x0 }, { 0x21,  0xd, 0xf1, 0x18,  0x0 }},  0x8 },
	{{{  0xf1,  0x1, 0x97, 0x17,  0x0 }, { 0x21,  0xd, 0xf1, 0x18,  0x0 }},  0x8 },
	{{{  0xcd, 0x9e, 0x55, 0xd1,  0x0 }, { 0xd1,  0x0, 0xf2, 0x71,  0x0 }},  0xe },
	{{{   0x1,  0x0, 0xf2, 0x88,  0x0 }, {  0x1,  0x0, 0xf5, 0x88,  0x0 }},  0x1 },
	{{{  0x30,  0xd, 0xf2, 0xef,  0x0 }, { 0x21,  0x0, 0xf5, 0x78,  0x0 }},  0x6 },
	{{{   0x0, 0x10, 0xf4, 0xd9,  0x0 }, {  0x0,  0x0, 0xf5, 0xd7,  0x0 }},  0x4 },
	{{{   0x1, 0x4c, 0xf2, 0x50,  0x0 }, {  0x1, 0x40, 0xd2, 0x59,  0x0 }},  0x8 },
	{{{  0x20, 0x11, 0xe2, 0x8a,  0x0 }, { 0x20,  0x0, 0xe4, 0xa8,  0x0 }},  0xa },
	{{{  0x21, 0x40, 0x7b,  0x4,  0x1 }, { 0x21,  0x0, 0x75, 0x72,  0x0 }},  0x2 },
	{{{  0x31,  0xd, 0xf2, 0xef,  0x0 }, { 0x21,  0x0, 0xf5, 0x78,  0x0 }},  0xa },
	{{{   0x1,  0xc, 0xf5, 0x2f,  0x1 }, {  0x0, 0x80, 0xf5, 0x5c,  0x0 }},  0x0 },
	{{{  0xb0, 0x1c, 0x81,  0x3,  0x2 }, { 0x20,  0x0, 0x54, 0x67,  0x2 }},  0xe },
	{{{   0x1,  0x0, 0xf1, 0x65,  0x0 }, {  0x1, 0x80, 0xa3, 0xa8,  0x2 }},  0x1 },
	{{{  0xe1, 0x4f, 0xc1, 0xd3,  0x2 }, { 0x21,  0x0, 0x32, 0x74,  0x1 }},  0x0 },
	{{{   0x2,  0x0, 0xf6, 0x16,  0x0 }, { 0x12,  0x0, 0xf2, 0xf8,  0x0 }},  0x1 },
	{{{  0xe0, 0x63, 0xf8, 0xf3,  0x0 }, { 0x70, 0x80, 0xf7, 0xf3,  0x0 }},  0x4 },
	{{{   0x1,  0x6, 0xf3, 0xff,  0x0 }, {  0x8,  0x0, 0xf7, 0xff,  0x0 }},  0x4 },
	{{{  0x21, 0x16, 0xb0, 0x81,  0x1 }, { 0x22,  0x0, 0xb3, 0x13,  0x1 }},  0xc },
	{{{   0x1, 0x4f, 0xf0, 0xff,  0x0 }, { 0x30,  0x0, 0x90,  0xf,  0x0 }},  0x6 },
	{{{   0x0, 0x10, 0xf1, 0xf2,  0x2 }, {  0x1,  0x0, 0xf1, 0xf2,  0x3 }},  0x0 },
	{{{   0x1, 0x4f, 0xf1, 0x50,  0x0 }, { 0x21, 0x80, 0xa3,  0x5,  0x3 }},  0x6 },
	{{{  0xb1,  0x3, 0x55,  0x3,  0x0 }, { 0xb1,  0x3,  0x8,  0xa,  0x0 }},  0x9 },
	{{{  0x22,  0x0, 0xa9, 0x34,  0x1 }, {  0x1,  0x0, 0xa2, 0x42,  0x2 }},  0x2 },
	{{{  0xa0, 0xdc, 0x81, 0x31,  0x3 }, { 0xb1, 0x80, 0xf1,  0x1,  0x3 }},  0x0 },
	{{{   0x1, 0x4f, 0xf1, 0x50,  0x0 }, { 0x21, 0x80, 0xa3,  0x5,  0x3 }},  0x6 },
	{{{  0xf1, 0x80, 0xa0, 0x72,  0x0 }, { 0x74,  0x0, 0x90, 0x22,  0x0 }},  0x9 },
	{{{  0xe1, 0x13, 0x71, 0xae,  0x0 }, { 0xe1,  0x0, 0xf0, 0xfc,  0x1 }},  0xa },
	{{{  0x31, 0x1c, 0x41,  0xb,  0x0 }, { 0xa1, 0x80, 0x92, 0x3b,  0x0 }},  0xe },
	{{{  0x71, 0x1c, 0x41, 0x1f,  0x0 }, { 0xa1, 0x80, 0x92, 0x3b,  0x0 }},  0xe },
	{{{  0x21, 0x1c, 0x53, 0x1d,  0x0 }, { 0xa1, 0x80, 0x52, 0x3b,  0x0 }},  0xc },
	{{{  0x21, 0x1d, 0xa4, 0xae,  0x1 }, { 0x21,  0x0, 0xb1, 0x9e,  0x0 }},  0xc },
	{{{  0xe1, 0x16, 0x71, 0xae,  0x0 }, { 0xe1,  0x0, 0x81, 0x9e,  0x0 }},  0xa },
	{{{  0xe1, 0x15, 0x71, 0xae,  0x0 }, { 0xe2,  0x0, 0x81, 0x9e,  0x0 }},  0xe },
	{{{  0x21, 0x16, 0x71, 0xae,  0x0 }, { 0x21,  0x0, 0x81, 0x9e,  0x0 }},  0xe },
	{{{  0x71, 0x1c, 0x41, 0x1f,  0x0 }, { 0xa1, 0x80, 0x92, 0x3b,  0x0 }},  0xe },
	{{{  0x21, 0x4f, 0x81, 0x53,  0x0 }, { 0x32,  0x0, 0x22, 0x2c,  0x0 }},  0xa },
	{{{  0x22, 0x4f, 0x81, 0x53,  0x0 }, { 0x32,  0x0, 0x22, 0x2c,  0x0 }},  0xa },
	{{{  0x23, 0x4f, 0x81, 0x53,  0x0 }, { 0x34,  0x0, 0x22, 0x2c,  0x0 }},  0xa },
	{{{  0xe1, 0x16, 0x71, 0xae,  0x0 }, { 0xe1,  0x0, 0x81, 0x9e,  0x0 }},  0xa },
	{{{  0x71, 0xc5, 0x6e, 0x17,  0x0 }, { 0x22,  0x5, 0x8b,  0xe,  0x0 }},  0x2 },
	{{{  0xe6, 0x27, 0x70,  0xf,  0x1 }, { 0xe3,  0x0, 0x60, 0x9f,  0x0 }},  0xa },
	{{{  0x30, 0xc8, 0xd5, 0x19,  0x0 }, { 0xb1, 0x80, 0x61, 0x1b,  0x0 }},  0xc },
	{{{  0x32, 0x9a, 0x51, 0x1b,  0x0 }, { 0xa1, 0x82, 0xa2, 0x3b,  0x0 }},  0xc },
	{{{  0xad,  0x3, 0x74, 0x29,  0x0 }, { 0xa2, 0x82, 0x73, 0x29,  0x0 }},  0x7 },
	{{{  0x21, 0x83, 0x74, 0x17,  0x0 }, { 0x62, 0x8d, 0x65, 0x17,  0x0 }},  0x7 },
	{{{  0x94,  0xb, 0x85, 0xff,  0x1 }, { 0x13,  0x0, 0x74, 0xff,  0x0 }},  0xc },
	{{{  0x74, 0x87, 0xa4,  0x2,  0x0 }, { 0xd6, 0x80, 0x45, 0x42,  0x0 }},  0x2 },
	{{{  0xb3, 0x85, 0x76, 0x21,  0x1 }, { 0x20,  0x0, 0x3d, 0xc1,  0x0 }},  0x6 },
	{{{  0x17, 0x4f, 0xf2, 0x61,  0x0 }, { 0x12,  0x8, 0xf1, 0xb4,  0x0 }},  0x8 },
	{{{  0x4f, 0x86, 0x65,  0x1,  0x0 }, { 0x1f,  0x0, 0x32, 0x74,  0x0 }},  0x4 },
	{{{  0xe1, 0x23, 0x71, 0xae,  0x0 }, { 0xe4,  0x0, 0x82, 0x9e,  0x0 }},  0xa },
	{{{  0x11, 0x86, 0xf2, 0xbd,  0x0 }, {  0x4, 0x80, 0xa0, 0x9b,  0x1 }},  0x8 },
	{{{  0x20, 0x90, 0xf5, 0x9e,  0x2 }, { 0x11,  0x0, 0xf4, 0x5b,  0x3 }},  0xc },
	{{{  0xf0, 0x80, 0x34, 0xe4,  0x0 }, { 0x7e,  0x0, 0xa2,  0x6,  0x0 }},  0x8 },
	{{{  0x90,  0xf, 0xff,  0x1,  0x3 }, {  0x0,  0x0, 0x1f,  0x1,  0x0 }},  0xe },
	{{{   0x1, 0x4f, 0xf0, 0xff,  0x0 }, { 0x33,  0x0, 0x90,  0xf,  0x0 }},  0x6 },
	{{{  0x1e,  0x0, 0x1f,  0xf,  0x0 }, { 0x10,  0x0, 0x1f, 0x7f,  0x0 }},  0x0 },
	{{{  0xbe,  0x0, 0xf1,  0x1,  0x3 }, { 0x31,  0x0, 0xf1,  0x1,  0x0 }},  0x4 },
	{{{  0xbe,  0x0, 0xf1,  0x1,  0x3 }, { 0x31,  0x0, 0xf1,  0x1,  0x0 }},  0x4 },
	{{{  0x93,  0x6, 0xc1,  0x4,  0x1 }, { 0x82,  0x0, 0x51,  0x9,  0x0 }},  0x6 },
	{{{  0xa0,  0x0, 0x96, 0x33,  0x0 }, { 0x20,  0x0, 0x55, 0x2b,  0x0 }},  0x6 },
	{{{   0x0, 0xc0, 0xff,  0x5,  0x0 }, {  0x0,  0x0, 0xff,  0x5,  0x3 }},  0x0 },
	{{{   0x4,  0x8, 0xf8,  0x7,  0x0 }, {  0x1,  0x0, 0x82, 0x74,  0x0 }},  0x8 },
	{{{   0x0,  0x0, 0x2f,  0x5,  0x0 }, { 0x20,  0x0, 0xff,  0x5,  0x3 }},  0xa },
	{{{  0x93,  0x0, 0xf7,  0x7,  0x2 }, {  0x0,  0x0, 0xf7,  0x7,  0x0 }},  0xa },
	{{{   0x0, 0x40, 0x80, 0x7a,  0x0 }, { 0xc4,  0x0, 0xc0, 0x7e,  0x0 }},  0x8 },
	{{{  0x90, 0x80, 0x55, 0xf5,  0x0 }, {  0x0,  0x0, 0x55, 0xf5,  0x0 }},  0x8 },
	{{{  0xe1, 0x80, 0x34, 0xe4,  0x0 }, { 0x69,  0x0, 0xf2,  0x6,  0x0 }},  0x8 },
	{{{   0x3,  0x2, 0xf0, 0xff,  0x3 }, { 0x11, 0x80, 0xf0, 0xff,  0x2 }},  0x2 },
	{{{  0x1e,  0x0, 0x1f,  0xf,  0x0 }, { 0x10,  0x0, 0x1f, 0x7f,  0x0 }},  0x0 },
	{{{   0x0,  0x0, 0x2f,  0x1,  0x0 }, {  0x0,  0x0, 0xff,  0x1,  0x0 }},  0x4 },
	{{{  0xbe,  0x0, 0xf1,  0x1,  0x3 }, { 0x31,  0x0, 0xf1,  0x1,  0x0 }},  0x4 },
	{{{  0x93, 0x85, 0x3f,  0x6,  0x1 }, {  0x0,  0x0, 0x5f,  0x7,  0x0 }},  0x6 },
	{{{   0x6,  0x0, 0xa0, 0xf0,  0x0 }, { 0x44,  0x0, 0xc5, 0x75,  0x0 }},  0xe },
	{{{  0x60,  0x0, 0x10, 0x81,  0x0 }, { 0x20, 0x8c, 0x12, 0x91,  0x0 }},  0xe },
	{{{   0x1, 0x40, 0xf1, 0x53,  0x0 }, {  0x8, 0x40, 0xf1, 0x53,  0x0 }},  0x0 },
	{{{  0x31,  0x0, 0x56, 0x31,  0x0 }, { 0x16,  0x0, 0x7d, 0x41,  0x0 }},  0x0 },
	{{{   0x0, 0x10, 0xf2, 0x72,  0x0 }, { 0x13,  0x0, 0xf2, 0x72,  0x0 }},  0xc },
	{{{  0x10,  0x0, 0x75, 0x93,  0x1 }, {  0x1,  0x0, 0xf5, 0x82,  0x1 }},  0x0 },
	{{{   0x0,  0x0, 0xf6, 0xff,  0x2 }, {  0x0,  0x0, 0xf6, 0xff,  0x0 }},  0x8 },
	{{{  0x30,  0x0, 0xff, 0xa0,  0x3 }, { 0x63,  0x0, 0x65,  0xb,  0x2 }},  0x0 },
	{{{  0x2a,  0x0, 0xf6, 0x87,  0x0 }, { 0x2b,  0x0, 0x76, 0x25,  0x0 }},  0x0 },
	{{{  0x85,  0x0, 0xb8, 0x84,  0x0 }, { 0x43,  0x0, 0xe5, 0x8f,  0x0 }},  0x6 },
	{{{   0x7, 0x4f, 0xf2, 0x60,  0x0 }, { 0x12,  0x0, 0xf2, 0x72,  0x0 }},  0x8 },
	{{{   0x5, 0x40, 0xb3, 0xd3,  0x0 }, { 0x86, 0x80, 0xf2, 0x24,  0x0 }},  0x2 },
	{{{  0xd0,  0x0, 0x11, 0xcf,  0x0 }, { 0xd1,  0x0, 0xf4, 0xe8,  0x3 }},  0x0 },
	{{{   0x5, 0x4e, 0xda, 0x25,  0x2 }, {  0x1,  0x0, 0xf9, 0x15,  0x0 }},  0xa },
	{{{   0x3,  0x0, 0x8f,  0x7,  0x2 }, {  0x2,  0x0, 0xff,  0x6,  0x0 }},  0x0 },
	{{{  0x13,  0x0, 0x8f,  0x7,  0x2 }, {  0x2,  0x0, 0xf9,  0x5,  0x0 }},  0x0 },
	{{{  0xf0,  0x1, 0x97, 0x17,  0x0 }, { 0x21,  0xd, 0xf1, 0x18,  0x0 }},  0x8 },
	{{{  0xf1, 0x41, 0x11, 0x11,  0x0 }, { 0xf1, 0x41, 0x11, 0x11,  0x0 }},  0x2 },
	{{{  0x13,  0x0, 0x8f,  0x7,  0x2 }, {  0x2,  0x0, 0xff,  0x6,  0x0 }},  0x0 },
	{{{   0x1,  0x0, 0x2f,  0x1,  0x0 }, {  0x1,  0x0, 0xaf,  0x1,  0x3 }},  0xf },
	{{{   0x1,  0x6, 0xf3, 0xff,  0x0 }, {  0x8,  0x0, 0xf7, 0xff,  0x0 }},  0x4 },
	{{{  0xc0, 0x4f, 0xf1,  0x3,  0x0 }, { 0xbe,  0xc, 0x10,  0x1,  0x0 }},  0x2 },
	{{{   0x0,  0x2, 0xf0, 0xff,  0x0 }, { 0x11, 0x80, 0xf0, 0xff,  0x0 }},  0x6 },
	{{{  0x81, 0x47, 0xf1, 0x83,  0x0 }, { 0xa2,  0x4, 0x91, 0x86,  0x0 }},  0x6 },
	{{{  0xf0, 0xc0, 0xff, 0xff,  0x3 }, { 0xe5,  0x0, 0xfb, 0xf0,  0x0 }},  0xe },
	{{{   0x0,  0x2, 0xf0, 0xff,  0x0 }, { 0x11, 0x80, 0xf0, 0xff,  0x0 }},  0x6 }
};

static const PercussionNote percussionNotes[47] = {
	{{{  0x0,  0xb, 0xa8, 0x38,  0x0 }, {  0x0,  0x0, 0xd6, 0x49,  0x0 }},  0x0,  0x4,  0x1,   0x97,  0x4 },
	{{{ 0xc0, 0xc0, 0xf8, 0x3f,  0x2 }, { 0xc0,  0x0, 0xf6, 0x8e,  0x0 }},  0x0,  0x4,  0x1,   0xf7,  0x4 },
	{{{ 0xc0, 0x80, 0xc9, 0xab,  0x0 }, { 0xeb, 0x40, 0xb5, 0xf6,  0x0 }},  0x1,  0x3,  0x1,   0x6a,  0x6 },
	{{{  0xc,  0x0, 0xd8, 0xa6,  0x0 }, {  0x0,  0x0, 0xd6, 0x4f,  0x0 }},  0x1,  0x3,  0x1,   0x6c,  0x5 },
	{{{  0x1,  0x0, 0xe2, 0xd2,  0x0 }, {  0x3, 0x41, 0x8f, 0x48, 0x49 }},  0xc,  0x4,  0x1,   0x2f,  0x5 },
	{{{  0x0,  0x0, 0xc8, 0x58,  0x3 }, {  0x0,  0x0, 0xf6, 0x4f,  0x0 }},  0x9,  0x3,  0x1,  0x108,  0x4 },
	{{{  0x1,  0x0, 0xff,  0x5,  0x0 }, { 0xf2, 0xff, 0xe0, 0x50, 0x52 }}, 0x5d,  0x2,  0x1,   0x9f,  0x5 },
	{{{  0xe,  0x9, 0xb9, 0x47,  0x0 }, { 0xeb, 0x40, 0xf5, 0xe6,  0x0 }},  0x0,  0x0,  0x1,   0x82,  0x6 },
	{{{  0x0,  0x0, 0xd6, 0x83,  0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a,  0x2,  0x1,   0xc7,  0x5 },
	{{{  0x1,  0x9, 0x89, 0x67,  0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a,  0x0,  0x1,   0x80,  0x6 },
	{{{  0x1,  0x0, 0xd6, 0x96,  0x0 }, { 0xd6, 0xd7, 0xe0, 0x41, 0x5e }}, 0x4a,  0x2,  0x1,   0xed,  0x5 },
	{{{  0x0,  0x9, 0xa9, 0x55,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x1,   0x82,  0x6 },
	{{{  0x2,  0x0, 0xc6, 0x96,  0x0 }, { 0xe0,  0x0, 0xe0, 0x40,  0x0 }},  0x1,  0x2,  0x1,  0x123,  0x5 },
	{{{  0x5,  0x0, 0xf6, 0x56,  0x0 }, { 0xf7, 0xff, 0xb3, 0x90, 0x4f }},  0x1,  0x2,  0x1,  0x15b,  0x5 },
	{{{  0x1,  0x0, 0xf7, 0x14,  0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7,  0x1,  0x1,  0x1ac,  0x5 },
	{{{  0x0,  0x0, 0xf6, 0x56,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x1,  0x2,  0x1,  0x18b,  0x5 },
	{{{  0x0, 0x83, 0xfb,  0x5,  0x0 }, { 0xf7, 0x41, 0x39, 0x90, 0x79 }},  0x1,  0x1,  0x1,   0xc8,  0x5 },
	{{{  0x0,  0x0, 0xff,  0x5,  0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7,  0x1,  0x1,   0xf9,  0x5 },
	{{{  0x1,  0x0, 0xa0,  0x5,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x2,  0x1,  0x27a,  0x6 },
	{{{  0x0,  0x5, 0xf3,  0x6,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x2,  0x1,  0x108,  0x7 },
	{{{  0x1,  0x0, 0xf9, 0x34,  0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7,  0x1,  0x1,  0x147,  0x4 },
	{{{  0x0,  0x0, 0xf7, 0x16,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x2,  0x1,  0x120,  0x6 },
	{{{  0x1,  0x0, 0xff,  0x5,  0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7,  0x1,  0x1,   0x42,  0x6 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x1,  0x0, 0xff,  0x5,  0x0 }, { 0xf7, 0xff, 0x36, 0x90, 0x79 }}, 0xe7,  0x1,  0x1,   0x6d,  0x5 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 },
	{{{  0x0,  0x0,  0x0,  0x0,  0x0 }, {  0x0,  0x0,  0x0,  0x0,  0x0 }},  0x0,  0x0,  0x0,  0x3fc,  0x4 }
};

const uint16 melodicFrequencies[36] = {
	 0x55,   0x5a,   0x60,   0x66,   0x6c,   0x72,   0x79,   0x80,   0x88,
	 0x90,   0x99,   0xa1,   0xab,   0xb5,   0xc0,   0xcc,   0xd8,   0xe5,
	 0xf2,  0x101,  0x110,  0x120,  0x132,  0x143,  0x156,  0x16b,  0x181,
	0x198,  0x1b0,  0x1ca,  0x1e5,  0x202,  0x220,  0x241,  0x263,  0x286
};

class AdLibDriver;

class AdLibChannel : public MidiChannel_MPU401 {
public:
	void reset();

	uint8 _program;
	uint8 _volume;
	uint8 _pedal;
};

struct MelodicVoice {
	bool _used;
	uint8 _channel;
	uint8 _program;

	uint8 _key;
	uint32 _timestamp;
	uint16 _frequency;
	int8 _octave;
};

class AdLibDriver : public MidiDriver {
public:
	AdLibDriver(Audio::Mixer *mixer) {
		for (uint i = 0; i < 16; ++i)
			_channels[i].init(this, i);

		_isOpen = false;

		_opl = NULL;
		memset(_voices, 0, sizeof(_voices));

		_lastVoice = 0;
		_percussionMask = 0;

		_adlibTimerProc = NULL;
		_adlibTimerParam = NULL;
	}

	int open();
	void close();
	void send(uint32 b);
	MidiChannel *allocateChannel();
	MidiChannel *getPercussionChannel() { return &_channels[9]; }
	bool isOpen() const { return _isOpen; }
	uint32 getBaseTempo() { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }

	virtual void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
		_adlibTimerProc = timerProc;
		_adlibTimerParam = timerParam;
	}

protected:
	OPL::OPL *_opl;
	AdLibChannel _channels[16];
	MelodicVoice _voices[kNumMelodic];
	uint8 _notesPerPercussion[kNumPercussion];

	uint _lastVoice;

	uint8 _percussionMask;

	void noteOff(uint8 channel, uint8 note);
	void noteOn(uint8 channel, uint8 note, uint8 velocity);
	void allNotesOff();
	void setModulationWheel(uint8 channel, uint8 value);
	void setFootController(uint8 channel, uint8 value);
	void setVolume(uint8 channel, uint8 value);
	void setPitchBend(uint8 channel, int16 value);

	void playNote(uint8 voice, uint8 octave, uint16 frequency);

	void programOperatorSimple(uint8 offset, const OPLOperator &op);
	void programOperator(uint8 offset, const OPLOperator &op);
	void setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume);

	void setupPercussion(const PercussionNote &note);
	void playPercussion(uint8 channel, const PercussionNote &note, uint8 velocity);

	void programMelodicVoice(uint8 voice, uint8 program);
	void playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity);
	void muteMelodicVoice(uint8 voice);

	void initVoices();

private:
	void onTimer();

	Common::TimerManager::TimerProc _adlibTimerProc;
	void *_adlibTimerParam;
	bool _isOpen;
};

MidiDriver *createAdLibDriver() {
	return new AdLibDriver(g_system->getMixer());
}

void AdLibChannel::reset() {
	_program = 0;
	_volume = 127;
	_pedal = 0;
}

/*
	bit 7 - Clear:  AM depth is 1 dB
	bit 6 - Clear:  Vibrato depth is 7 cent
	bit 5 - Set:    Rhythm enabled  (6 melodic voices)
	bit 4 - Bass drum off
	bit 3 - Snare drum off
	bit 2 - Tom tom off
	bit 1 - Cymbal off
	bit 0 - Hi Hat off
*/
const uint8 kDefaultPercussionMask = 0x20;

int AdLibDriver::open() {
	if (_isOpen)
		return MERR_ALREADY_OPEN;

	_isOpen = true;

	_opl = OPL::Config::create();
	_opl->init();
	_opl->writeReg(0x1, 0x20); // set bit 5 (enable all waveforms)

	// Reset the OPL registers.
	for (uint i = 0; i < kNumVoices; ++i) {
		_opl->writeReg(0xA0 + i, 0); // frequency
		_opl->writeReg(0xB0 + i, 0); // key on
		_opl->writeReg(0xC0 + i, 0); // feedback
	}
	_opl->writeReg(0xBD, kDefaultPercussionMask);

	initVoices();

	_opl->start(new Common::Functor0Mem<void, AdLibDriver>(this, &AdLibDriver::onTimer));
	return 0;
}

void AdLibDriver::close() {
	if (!_isOpen)
		return;

	_isOpen = false;

	delete _opl;
}

void AdLibDriver::send(uint32 b) {
	uint channel = b & 0xf;
	uint cmd = (b >> 4) & 0xf;
	uint param1 = (b >> 8) & 0xff;
	uint param2 = (b >> 16) & 0xff;

	switch (cmd) {
	case 8:
		noteOff(channel, param1);
		break;
	case 9:
		// TODO: map volume?
		noteOn(channel, param1, param2);
		break;
	case 11:
		// controller change
		switch (param1) {
		case 1:
			setModulationWheel(channel, param2);
			break;
		case 4:
			setFootController(channel, param2);
			break;
		case 7:
			setVolume(channel, param2);
			break;
		case 123:
			// all notes off
			allNotesOff();
			break;
		}
		break;
	case 12:
		// program change
		_channels[channel]._program = param1;
		break;
	case 14:
		setPitchBend(channel, (param1 | (param2 << 7)) - 0x2000);
		break;
	}
}

void AdLibDriver::noteOff(uint8 channel, uint8 note) {
	if (channel == 9) {
		if (note < 35 || note > 81)
			return;

		_percussionMask &= ~(1 << percussionNotes[note - 35].percussion);
		_opl->writeReg(0xBD, _percussionMask);
		return;
	}

	for (int i = kNumMelodic - 1; i >= 0; --i) {
		if (_voices[i]._channel != channel)
			continue;
		if (_voices[i]._key != note)
			continue;
		muteMelodicVoice(i);
		_voices[i]._used = false;
		return;
	}

	//debug(1, "failed to find voice off for channel %d, note %d", channel, note);
}

void AdLibDriver::noteOn(uint8 channel, uint8 note, uint8 velocity) {
	if (channel == 9) {
		if (note < 35 || note > 81)
			return;

		const PercussionNote &info = percussionNotes[note - 35];
		if (!info.valid)
			return;

		if (note != _notesPerPercussion[info.percussion]) {
			setupPercussion(info);
			_notesPerPercussion[info.percussion] = note;
		}

		playPercussion(channel, info, velocity);
		return;
	}

	if (velocity == 0) {
		noteOff(channel, note);
		return;
	}

	// We want to play a note on a melodic (voice) channel.

	// First, look for a voice playing the same note with the same program.
	for (uint i = 0; i < kNumMelodic; ++i) {
		if (_voices[i]._channel != channel || _voices[i]._key != note)
			continue;
		if (_voices[i]._program != _channels[channel]._program)
			continue;
		muteMelodicVoice(i);
		playMelodicNote(i, channel, note, velocity);
		return;
	}

	// The loops below try to start at _lastVoice and find a voice to use.
	// They ignore _lastVoice itself, and update _lastVoice if they succeed.

	// Then, try finding a melodic voice with the same program.
	for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
		if (_voices[i]._used)
			continue;
		if (_voices[i]._program != _channels[channel]._program)
			continue;
		playMelodicNote(i, channel, note, velocity);
		_lastVoice = i;
		return;
	}

	// Then, try finding a free melodic voice of any kind.
	for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
		if (_voices[i]._used)
			continue;
		programMelodicVoice(i, _channels[channel]._program);
		playMelodicNote(i, channel, note, velocity);
		_lastVoice = i;
		return;
	}

	// Then just try finding a melodic voice with the same program,
	// and steal it.
	for (uint i = (_lastVoice + 1) % kNumMelodic; i != _lastVoice; i = (i + 1) % kNumMelodic) {
		if (_voices[i]._program != _channels[channel]._program)
			continue;
		muteMelodicVoice(i);
		playMelodicNote(i, channel, note, velocity);
		_lastVoice = i;
		return;
	}

	// Finally, just take control of the channel used least recently.
	uint voiceId = 0;
	uint32 bestTimestamp = 0xffffffff;
	for (uint i = 0; i < kNumMelodic; ++i)
		if (bestTimestamp > _voices[i]._timestamp) {
			voiceId = i;
			bestTimestamp = _voices[i]._timestamp;
		}

	//debug(1, "ran out of voices for channel %d, note %d, program %d: reused voice %d", channel, note, _channels[channel]._program, voiceId);
	programMelodicVoice(voiceId, _channels[channel]._program);
	playMelodicNote(voiceId, channel, note, velocity);
	_lastVoice = voiceId;
}

// TODO: this doesn't match original
void AdLibDriver::allNotesOff() {
	for (uint i = 0; i < kNumMelodic; ++i) {
		muteMelodicVoice(i);
		_voices[i]._used = false;
	}

	_percussionMask = kDefaultPercussionMask;
	_opl->writeReg(0xBD, kDefaultPercussionMask);
}

void AdLibDriver::setModulationWheel(uint8 channel, uint8 value) {
	if (value >= 64)
		_percussionMask |= 0x80;
	else
		_percussionMask &= 0x7f;

	_opl->writeReg(0xBD, _percussionMask);
}

void AdLibDriver::setFootController(uint8 channel, uint8 value) {
	_channels[channel]._pedal = (value >= 64);
}

void AdLibDriver::setVolume(uint8 channel, uint8 value) {
	_channels[channel]._volume = value;
}

void AdLibDriver::setPitchBend(uint8 channel, int16 value) {
	for (uint i = 0; i < kNumMelodic; ++i) {
		if (_voices[i]._channel != channel || !_voices[i]._used)
			continue;

		// index into frequency table
		uint f = 12 + (_voices[i]._key % 12);

		int16 bendAmount = value;
		if (bendAmount > 0) {
			// bend up two semitones
			bendAmount *= (melodicFrequencies[f + 2] - melodicFrequencies[f]);
		} else {
			// bend down two semitones
			bendAmount *= (melodicFrequencies[f] - melodicFrequencies[f - 2]);
		}
		bendAmount /= 0x2000;
		bendAmount += melodicFrequencies[f]; // add the base frequency
		playNote(i, _voices[i]._octave, bendAmount);
		_voices[i]._timestamp = g_system->getMillis();
	}
}

void AdLibDriver::playNote(uint8 voice, uint8 octave, uint16 frequency) {
	/* Percussions are always fed keyOn = 0 even to set the note, as they are activated using the
	   BD register instead. I wonder if they can just be fed the same value as melodic voice and
	   be done with it. */
	uint8 keyOn = (voice < kNumMelodic) ? 0x20 : 0;

	// key on, octave, high 2 bits of frequency
	_opl->writeReg(0xB0 + voice, keyOn | ((octave & 7) << 2) | ((frequency >> 8) & 3));
	// low 8 bits of frequency
	_opl->writeReg(0xA0 + voice, frequency & 0xff);
}

void AdLibDriver::programOperatorSimple(uint8 offset, const OPLOperator &op) {
	_opl->writeReg(0x40 + offset, op.levels & LEVEL_MASK);
	_opl->writeReg(0x60 + offset, op.attackDecay);
	_opl->writeReg(0x80 + offset, op.sustainRelease);
}

void AdLibDriver::programOperator(uint8 offset, const OPLOperator &op) {
	_opl->writeReg(0x20 + offset, op.characteristic);
	_opl->writeReg(0x60 + offset, op.attackDecay);
	_opl->writeReg(0x80 + offset, op.sustainRelease);
	_opl->writeReg(0xE0 + offset, op.waveform);
	_opl->writeReg(0x40 + offset, op.levels);
}

const uint16 adlibLogVolume[] = {
	0, 37, 58, 73, 85, 95, 103, 110, 116, 121, 127, 131, 135, 139, 143, 146,
	149, 153, 155, 158, 161, 163, 165, 168, 170, 172, 174, 176, 178, 179, 181, 183,
	184, 186, 188, 189, 191, 192, 193, 195, 196, 197, 198, 200, 201, 202, 203, 204,
	205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 219,
	220, 221, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228, 229, 230, 231, 231,
	232, 233, 233, 234, 234, 235, 236, 236, 237, 237, 238, 239, 239, 240, 240, 241,
	241, 242, 242, 243, 244, 244, 245, 245, 246, 246, 247, 247, 248, 248, 248, 249,
	249, 250, 250, 251, 251, 252, 252, 253, 253, 253, 254, 254, 255, 255, 256, 256,
	256
};

void AdLibDriver::setOperatorLevel(uint8 offset, const OPLOperator &op, uint8 velocity, uint8 channel, bool forceVolume) {
	uint8 programLevel = LEVEL_MASK;
	if (!forceVolume)
		programLevel -= (op.levels & LEVEL_MASK);

	uint32 noteLevel = adlibLogVolume[velocity];
	uint32 channelLevel = adlibLogVolume[_channels[channel]._volume];
	// programLevel comes from the static data and is probably already in the correct logarithmic scale
	uint32 finalLevel = LEVEL_MASK - ((noteLevel * channelLevel * programLevel) >> 16);

	// high 2 bits are scaling level, the rest is (inversed) volume
	_opl->writeReg(0x40 + offset, (op.levels & 0xc0) | (finalLevel & 0x3f));
}

const uint8 operatorOffsetsForPercussion[] = {
	0x11, // hi-hat
	0x15, // cymbal
	0x12, // tom tom
	0x14  // snare drum
};

void AdLibDriver::setupPercussion(const PercussionNote &note) {
	if (note.percussion < 4) {
		// simple percussion (1 operator)

		// turn off relevant percussion
		_percussionMask &= ~(1 << note.percussion);
		_opl->writeReg(0xBD, _percussionMask);

		programOperatorSimple(operatorOffsetsForPercussion[note.percussion], note.op[0]);
		return;
	}

	// bass drum (2 operators)

	// turn off bass drum
	_percussionMask &= ~(0x10);
	_opl->writeReg(0xBD, _percussionMask);

	programOperator(0x10, note.op[0]);
	programOperator(0x13, note.op[1]);

	_opl->writeReg(0xC0 + 6, note.feedbackAlgo);
}

void AdLibDriver::playPercussion(uint8 channel, const PercussionNote &note, uint8 velocity) {
	if (note.percussion < 4) {
		// simple percussion (1 operator)

		// turn off relevant percussion
		_percussionMask &= ~(1 << note.percussion);
		_opl->writeReg(0xBD, _percussionMask);

		setOperatorLevel(operatorOffsetsForPercussion[note.percussion], note.op[0], velocity, channel, true);

		if (note.percussion == 2) {
			// tom tom
			playNote(8, note.octave, note.frequency);
		} else if (note.percussion == 3) {
			// snare drum
			playNote(7, note.octave, note.frequency);
		}

		// turn on relevant percussion
		_percussionMask |= (1 << note.percussion);
		_opl->writeReg(0xBD, _percussionMask);
		return;
	}

	// turn off bass drum
	_percussionMask &= ~(0x10);
	_opl->writeReg(0xBD, _percussionMask);

	if (note.feedbackAlgo & 1) {
		// operators 1 and 2 in additive synthesis
		setOperatorLevel(0x10, note.op[0], velocity, channel, true);
		setOperatorLevel(0x13, note.op[1], velocity, channel, true);
	} else {
		// operator 2 is modulating operator 1
		setOperatorLevel(0x13, note.op[1], velocity, channel, true);
	}

	playNote(6, note.octave, note.frequency);

	// turn on bass drum
	_percussionMask |= 0x10;
	_opl->writeReg(0xBD, _percussionMask);
}

const uint8 offset1ForMelodic[kNumVoices] = { 0x0,  0x1,  0x2,  0x8,  0x9,  0xa, 0x10, 0x11, 0x12 };
const uint8 offset2ForMelodic[kNumVoices] = { 0x3,  0x4,  0x5,  0xb,  0xc,  0xd, 0x13, 0x14, 0x15 };

void AdLibDriver::programMelodicVoice(uint8 voice, uint8 program) {
	assert(program < 128);
	assert(voice < kNumMelodic);

	const MelodicProgram &info = melodicPrograms[program];
	uint8 offset1 = offset1ForMelodic[voice];
	uint8 offset2 = offset2ForMelodic[voice];

	// Start at lowest volume.
	_opl->writeReg(0x40 + offset1, LEVEL_MASK);
	_opl->writeReg(0x40 + offset2, LEVEL_MASK);

	muteMelodicVoice(voice);

	programOperator(offset1, info.op[0]);
	programOperator(offset2, info.op[1]);

	_opl->writeReg(0xC0 + voice, info.feedbackAlgo);
}

void AdLibDriver::playMelodicNote(uint8 voice, uint8 channel, uint8 note, uint8 velocity) {
	assert(voice < kNumMelodic);

	uint8 octave = note / 12;
	uint8 f = 12 + (note % 12);

	if (octave > 7)
		octave = 7;

	const MelodicProgram &info = melodicPrograms[_channels[channel]._program];
	uint8 offset1 = offset1ForMelodic[voice];
	uint8 offset2 = offset2ForMelodic[voice];

	if (info.feedbackAlgo & 1) {
		setOperatorLevel(offset1, info.op[0], velocity, channel, false);
		setOperatorLevel(offset2, info.op[1], velocity, channel, false);
	} else {
		setOperatorLevel(offset2, info.op[1], velocity, channel, true);
	}

	playNote(voice, octave, melodicFrequencies[f]);

	_voices[voice]._program = _channels[channel]._program;
	_voices[voice]._key = note;
	_voices[voice]._channel = channel;
	_voices[voice]._timestamp = g_system->getMillis();
	_voices[voice]._frequency = melodicFrequencies[f];
	_voices[voice]._octave = octave;
	_voices[voice]._used = true;
}

void AdLibDriver::muteMelodicVoice(uint8 voice) {
	_opl->writeReg(0xB0 + voice, 0 | ((_voices[voice]._octave & 7) << 2) | ((_voices[voice]._frequency >> 8) & 3));
}

MidiChannel *AdLibDriver::allocateChannel() {
	for (uint i = 0; i < 16; ++i) {
		if (i == 9)
			continue;

		if (_channels[i].allocate())
			return &_channels[i];
	}

	return NULL;
}

void AdLibDriver::onTimer() {
	if (_adlibTimerProc)
		(*_adlibTimerProc)(_adlibTimerParam);
}

void AdLibDriver::initVoices() {
	_percussionMask = kDefaultPercussionMask;
	_opl->writeReg(0xBD, _percussionMask);

	for (uint i = 0; i < 16; ++i)
		_channels[i].reset();

	for (uint i = 0; i < kNumMelodic; ++i) {
		_voices[i]._key = 0xff;
		_voices[i]._program = 0xff;
		_voices[i]._channel = 0xff;
		_voices[i]._timestamp = 0;
		_voices[i]._frequency = 0;
		_voices[i]._octave = 0;
		_voices[i]._used = false;
	}

	for (uint i = 0; i < kNumPercussion; ++i)
		_notesPerPercussion[i] = 0xff;

	_lastVoice = 0;
}

} // namespace Parallaction
