/* spk2wav --  Scumm V2 sound resource to wav converter.
 * Copyright (C) 2002-2003 Jochen Hoenicke, Jamieson Christian
 *
 * 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.
 *
 * $Header$
 */


#include <stdio.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include "scummsys.h"

#if !defined(__GNUC__)
	#pragma START_PACK_STRUCTS
#endif	

#define SAMPLE_RATE 22050
#define FREQ_HZ 236                /* Don't change! */
#define FRAGMENTS ((2<<16) | 7)
#define SAMPLE_LENGTH (SAMPLE_RATE / FREQ_HZ)

#define AUTHENTIC_SOUND
#define DECAY  0xf400              /* Depends on sample rate */
#define PCJR_DECAY  0xd000              /* Depends on sample rate */

FILE *sound_fd;

int pc_jr = 0;
int sample_length = 0;

typedef unsigned int uint;
typedef unsigned short uint16;
typedef short int16;
typedef unsigned char uint8;
typedef signed char int8;

struct channel_data {
	uint16 time_left;          // 00
	uint16 next_cmd;           // 02
	uint16 base_freq;          // 04
	uint16 freq_delta;         // 06
	uint16 freq;               // 08
	uint16 volume;             // 10
	uint16 volume_delta;       // 12
	uint16 tempo;              // 14
	uint16 inter_note_pause;   // 16
	uint16 transpose;          // 18
	uint16 note_length;        // 20
	uint16 hull_curve;         // 22
	uint16 hull_offset;        // 24
	uint16 hull_counter;       // 26
	uint16 freqmod_table;      // 28
	uint16 freqmod_offset;     // 30
	uint16 freqmod_incr;       // 32
	uint16 freqmod_multiplier; // 34
	uint16 freqmod_modulo;     // 36
	uint16 unknown[5];         // 38 - 46
	uint16 music_script_nr;    // 48
} GCC_PACK;

union channel_info {
	channel_data d;
	uint16 array[sizeof(channel_data)/2];
};

void debug_channel(channel_info *channel) {
    fprintf(stdout, "chn: %5d, %5d, %5d, %5d, %5d, %5d, %5d, %5d\n",
	    channel->d.time_left, channel->d.next_cmd, 
	    channel->d.base_freq, channel->d.freq_delta, 
	    channel->d.freq, channel->d.volume, 
	    channel->d.volume_delta, channel->d.tempo);
    fprintf(stdout, "     %5d, %5d, %5d, %5d, %5d, %5d, %5d, %5d\n",
	    channel->d.inter_note_pause, channel->d.transpose,
	    channel->d.note_length, channel->d.hull_curve,
	    channel->d.hull_offset, channel->d.hull_counter,
	    channel->d.freqmod_table, channel->d.freqmod_offset);
    fprintf(stdout, "     %5d, %5d, %5d, %5d, %5d, %5d, %5d, %5d\n",
	    channel->d.freqmod_incr, channel->d.freqmod_multiplier,
	    channel->d.freqmod_modulo, channel->d.unknown[0],
	    channel->d.unknown[1], channel->d.unknown[2],
	    channel->d.unknown[3], channel->d.unknown[4]);
    fprintf(stdout, "     %5d\n",
	    channel->d.music_script_nr);
}


channel_info channels[8];
uint8 * retaddr;

uint8  m_lfl[128];
uint16 m_off[128];
uint8 * music_script[128];

uint8 note_lengths[] = {
    0,  
    0,  0,  2,
    0,  3,  4,
    0,  6,  8,
    0, 12, 16,
    0, 24, 32,
    0, 48, 64
};

uint16 hull_offsets[] = {
    0, 12, 24, 36, 48, 60, 
    72, 88, 104, 120, 136, 240, 
    152, 164
};

int16 hulls[] = {
    // hull 0
    3, -1, 0, 0, 0, 0, 0, 0,
    0, -1, 0, 0,
    // hull 1 (staccato)
    3, -1, 0, 30, 0, -1, 0, 0,
    0, -1, 0, 0,
    // hull 2 (legato)
    3, -1, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0,
    // hull 3 (staccatissimo)
    3, -1, 0, 2, 0, -1, 0, 0,
    0, -1, 0, 0,
    // hull 4
    3, -1, 0, 6, 0, -1, 0, 0,
    0, -1, 0, 0,
    // hull 5
    3, -1, 0, 10, 0, -1, 0, 0,
    0, -1, 0, 0,
    // hull 6
    (int16) 60000, -1, -1000, 20, 0, 0, 0, 0,
    (int16) 40000, -1, -5000,  5, 0, -1, 0, 0,
    // hull 7
    (int16) 50000, -1, 0, 8, 30000, -1, 0, 0,
    28000, -1, -5000,  5, 0, -1, 0, 0,
    // hull 8
    (int16) 60000, -1, -2000, 16, 0, 0, 0, 0,
    28000, -1, -6000,  5, 0, -1, 0, 0,
    // hull 9
    (int16) 55000, -1,     0,  8, (int16) 35000, -1, 0, 0,
    (int16) 40000, -1, -2000, 10, 0, -1, 0, 0,
    // hull 10
    (int16) 60000, -1,     0,  4, -2000, 8, 0, 0,
    (int16) 40000, -1, -6000,  5, 0, -1, 0, 0,
    // hull 12
    0, -1,   150, 340, -150, 340, 0, -1,
    0, -1, 0, 0,
    // hull 13  == 164
    20000, -1,  4000,  7, 1000, 15, 0, 0,
    (int16) 35000, -1, -2000, 15, 0, -1, 0, 0,
    
    // hull misc = 180
    (int16) 44000, -1, -4400, 10, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 53000, -1, -5300, 10, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 63000, -1, -6300, 10, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 44000, -1, -1375, 32, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 53000, -1, -1656, 32, 0, -1, 0, 0,
    0, -1, 0, 0,

    // hull 11 == 240
    (int16) 63000, -1, -1968, 32, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 44000, -1, - 733, 60, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 53000, -1, - 883, 60, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 63000, -1, -1050, 60, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 44000, -1, - 488, 90, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 53000, -1, - 588, 90, 0, -1, 0, 0,
    0, -1, 0, 0,

    (int16) 63000, -1, - 700, 90, 0, -1, 0, 0,
    0, -1, 0, 0
};

uint16 freqmod_lengths[] = {
    0x1000, 0x1000, 0x20, 0x2000, 0x1000
};

uint16 freqmod_offsets[] = {
    0, 0x100, 0x200, 0x302, 0x202
};

int8 freqmod_table[0x502] = {
     0,   3,   6,   9,  12,  15,  18,  21,
    24,  27,  30,  33,  36,  39,  42,  45,
    48,  51,  54,  57,  59,  62,  65,  67,
    70,  73,  75,  78,  80,  82,  85,  87,
    89,  91,  94,  96,  98, 100, 102, 103,
   105, 107, 108, 110, 112, 113, 114, 116,
   117, 118, 119, 120, 121, 122, 123, 123,
   124, 125, 125, 126, 126, 126, 126, 126,
   126, 126, 126, 126, 126, 126, 125, 125,
   124, 123, 123, 122, 121, 120, 119, 118,
   117, 116, 114, 113, 112, 110, 108, 107,
   105, 103, 102, 100,  98,  96,  94,  91,
    89,  87,  85,  82,  80,  78,  75,  73,
    70,  67,  65,  62,  59,  57,  54,  51,
    48,  45,  42,  39,  36,  33,  30,  27,
    24,  21,  18,  15,  12,   9,   6,   3,
     0,  -3,  -6,  -9, -12, -15, -18, -21,
   -24, -27, -30, -33, -36, -39, -42, -45,
   -48, -51, -54, -57, -59, -62, -65, -67,
   -70, -73, -75, -78, -80, -82, -85, -87,
   -89, -91, -94, -96, -98,-100,-102,-103,
  -105,-107,-108,-110,-112,-113,-114,-116,
  -117,-118,-119,-120,-121,-122,-123,-123,
  -124,-125,-125,-126,-126,-126,-126,-126,
  -126,-126,-126,-126,-126,-126,-125,-125,
  -124,-123,-123,-122,-121,-120,-119,-118,
  -117,-116,-114,-113,-112,-110,-108,-107,
  -105,-103,-102,-100, -98, -96, -94, -91,
   -89, -87, -85, -82, -80, -78, -75, -73,
   -70, -67, -65, -62, -59, -57, -54, -51,
   -48, -45, -42, -39, -36, -33, -30, -27,
   -24, -21, -18, -15, -12,  -9,  -6,  -3,
  
     0,   1,   2,   3,   4,   5,   6,   7,
     8,   9,  10,  11,  12,  13,  14,  15,
    16,  17,  18,  19,  20,  21,  22,  23,
    24,  25,  26,  27,  28,  29,  30,  31,
    32,  33,  34,  35,  36,  37,  38,  39,
    40,  41,  42,  43,  44,  45,  46,  47,
    48,  49,  50,  51,  52,  53,  54,  55,
    56,  57,  58,  59,  60,  61,  62,  63,
    64,  65,  66,  67,  68,  69,  70,  71,
    72,  73,  74,  75,  76,  77,  78,  79,
    80,  81,  82,  83,  84,  85,  86,  87,
    88,  89,  90,  91,  92,  93,  94,  95,
    96,  97,  98,  99, 100, 101, 102, 103,
   104, 105, 106, 107, 108, 109, 110, 111,
   112, 113, 114, 115, 116, 117, 118, 119,
   120, 121, 122, 123, 124, 125, 126, 127,
  -128,-127,-126,-125,-124,-123,-122,-121,
  -120,-119,-118,-117,-116,-115,-114,-113,
  -112,-111,-110,-109,-108,-107,-106,-105,
  -104,-103,-102,-101,-100, -99, -98, -97,
   -96, -95, -94, -93, -92, -91, -90, -89,
   -88, -87, -86, -85, -84, -83, -82, -81,
   -80, -79, -78, -77, -76, -75, -74, -73,
   -72, -71, -70, -69, -68, -67, -66, -65,
   -64, -63, -62, -61, -60, -59, -58, -57,
   -56, -55, -54, -53, -52, -51, -50, -49,
   -48, -47, -46, -45, -44, -43, -42, -41,
   -40, -39, -38, -37, -36, -35, -34, -33,
   -32, -31, -30, -29, -28, -27, -26, -25,
   -24, -23, -22, -21, -20, -19, -18, -17,
   -16, -15, -14, -13, -12, -11, -10,  -9,
    -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,
  
  -120, 120,
  
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
  -120,-120,-120,-120,-120,-120,-120,-120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,
   120, 120, 120, 120, 120, 120, 120, 120,

    41,  35, -66,-124, -31, 108, -42, -82,
    82,-112,  73, -15, -15, -69, -23, -21,
   -77, -90, -37,  60,-121,  12,  62,-103,
    36,  94,  13,  28,   6, -73,  71, -34,
   -77,  18,  77, -56,  67, -69,-117, -90,
    31,   3,  90, 125,   9,  56,  37,  31,
    93, -44, -53,  -4,-106, -11,  69,  59,
    19,  13,-119,  10,  28, -37, -82,  50,
    32,-102,  80, -18,  64, 120,  54,  -3,
    18,  73,  50, -10, -98, 125,  73, -36,
   -83,  79,  20, -14,  68,  64, 102, -48,
   107, -60,  48, -73,  50,  59, -95,  34,
   -10,  34,-111, -99, -31,-117,  31, -38,
   -80, -54,-103,   2, -71, 114, -99,  73,
    44,-128, 126, -59,-103, -43, -23,-128,
   -78, -22, -55, -52,  83, -65, 103, -42,
   -65,  20, -42, 126,  45, -36,-114, 102,
  -125, -17,  87,  73,  97,  -1, 105,-113,
    97, -51, -47,  30, -99,-100,  22, 114,
   114, -26,  29, -16,-124,  79,  74, 119,
     2, -41, -24,  57,  44,  83, -53, -55,
    18,  30,  51, 116, -98,  12, -12, -43,
   -44, -97, -44, -92,  89, 126,  53, -49,
    50,  34, -12, -52, -49, -45,-112,  45,
    72, -45,-113, 117, -26, -39,  29,  42,
   -27, -64,  -9,  43, 120,-127,-121,  68,
    14,  95,  80,   0, -44,  97,-115, -66,
   123,   5,  21,   7,  59,  51,-126,  31,
    24, 112,-110, -38, 100,  84, -50, -79,
  -123,  62, 105,  21,  -8,  70, 106,   4,
  -106, 115,  14, -39,  22,  47, 103, 104,
   -44,  -9,  74,  74, -48,  87, 104, 118,
};

uint16 *freqs_table;
uint16  spk_freq_table[12] = {
    36484, 34436, 32503, 30679, 29007, 27332, 
    25798, 24350, 22983, 21693,  20476, 19326
};
uint16 pcjr_freq_table[12] = {
    65472, 61760, 58304, 55040, 52032, 49024, 
    46272, 43648, 41216, 38912, 36736, 34624
};

void execute_cmd(channel_info *channel) {
    uint16 value;
    int16 offset;
	uint8 *script_start;
    uint8 *script_ptr;
    channel_info * current_channel;
    channel_info * dest_channel;

    current_channel = channel;

    if (channel->d.next_cmd == 0)
		return;
	script_start = music_script[channel->d.music_script_nr];
    script_ptr = script_start + channel->d.next_cmd;

    for (;;) {
		uint8 opcode = *script_ptr++;
		if (opcode >= 0xf8) {
			switch (opcode) {
			case 0xf8: // set hull curve
#if 0
				printf("channel[%d]: hull curve %2d\n", 
					   channel - channels, (int) *script_ptr);
#endif
				channel->d.hull_curve = hull_offsets[(*script_ptr)/2];
				script_ptr++;
				break;

			case 0xf9: // set freqmod curve
#if 0
				printf("channel[%d]: freqmod curve %2d\n", 
					   channel - channels, (int) *script_ptr);
#endif
				channel->d.freqmod_table = freqmod_offsets[(*script_ptr)/4];
				channel->d.freqmod_modulo = freqmod_lengths[(*script_ptr)/4];
				script_ptr++;
				break;

			case 0xfd: // clear other channel
				value = READ_LE_UINT16 (script_ptr);
				printf("clear channel %d\n", (int) (value / sizeof(channel_info)));
				script_ptr += 2;
				channel = &channels[value / sizeof(channel_info)];
				// fall through

			case 0xfa: // clear current channel
				if (opcode == 0xfa)
					printf("clear channel\n");
				channel->d.next_cmd   = 0;
				channel->d.base_freq  = 0;
				channel->d.freq_delta = 0;
				channel->d.freq = 0;
				channel->d.volume = 0;
				channel->d.volume_delta = 0;
				channel->d.inter_note_pause = 0;
				channel->d.transpose = 0;
				channel->d.hull_curve = 0;
				channel->d.hull_offset = 0;
				channel->d.hull_counter = 0;
				channel->d.freqmod_table = 0;
				channel->d.freqmod_offset = 0;
				channel->d.freqmod_incr = 0;
				channel->d.freqmod_multiplier = 0;
				channel->d.freqmod_modulo = 0;
				break;

			case 0xfb: // ret from subroutine
				printf("ret from sub %p\n", script_ptr);
				script_ptr = retaddr;
				break;

			case 0xfc: // call subroutine
				offset = READ_LE_UINT16 (script_ptr);
				printf("subroutine %d\n", offset);
				script_ptr += 2;
				retaddr = script_ptr;

				// FIXME: I don't think the following original code was quite right!
//				script_ptr = (uint8 *) ((void*)(int)offset);
				script_ptr = script_start + offset;

				printf("XXX1: %p -> %04x", script_ptr, (int) offset);
				break;

			case 0xfe: // loop music
				opcode = *script_ptr++;
				offset = READ_LE_UINT16 (script_ptr);
				script_ptr += 2;
				printf("loop if %d to %d\n", opcode, offset);
				
				if (!channel->array[opcode/2] || --channel->array[opcode/2])
					script_ptr += offset;
				break;

			case 0xff: // set parameter
				opcode = *script_ptr++;
				value = READ_LE_UINT16 (script_ptr);
				channel->array[opcode/2] = value;
// #if 0
				printf("channel[%d]: set param %2d = %5d\n", 
					   channel - &channels[0], (int) opcode, (int) value);
// #endif
				if (opcode == 18)
					printf ("    Transpose = %d\n", (signed short) channel->d.transpose);

				script_ptr += 2;
				if (opcode == 0)
					goto end;
				break;
			}
		} else { // opcode < 0xf8
			for (;;) {
				int16 note, octave;
				dest_channel = &(channels[opcode >> 5]);
printf ("  channels = %p, sizeof(channels[0]) = %d, channel = %d, dest_channel = %p\n",
        channels, (int) sizeof (channels[0]), (int) (opcode >> 5), dest_channel);
				channel->d.time_left = channel->d.tempo * note_lengths[opcode & 0x1f];
#if 0
				printf("Set time_left to %d (tempo %d, note length %d)\n",
				       (int) channel->d.time_left,
					   (int) channel->d.tempo,
					   (int) note_lengths[opcode & 0x1f]);
#endif
// #if 0
				fprintf(stdout, "channel[%d]: @%04x note: %3d+%d len: %2d hull: %d mod: %d/%d/%d %s\n", 
					(int) opcode>>5, (int) (script_ptr ? script_ptr - music_script[channel->d.music_script_nr] : 0),
					(int) *script_ptr & 0x7f, (signed short) dest_channel->d.transpose, opcode & 0x1f,
					(int) dest_channel->d.hull_curve, (int) dest_channel->d.freqmod_table,
					(int) dest_channel->d.freqmod_incr, (int) dest_channel->d.freqmod_multiplier,
					*script_ptr & 0x80 ? "last":"");
// #endif
				opcode = *script_ptr++;
				note = opcode & 0x7f;
				if (note != 0x7f) {
					uint16 freq;
					dest_channel->d.time_left = channel->d.time_left;
					dest_channel->d.note_length = 
						channel->d.time_left -
						dest_channel->d.inter_note_pause;
					note += dest_channel->d.transpose;
					while (note < 0)
						note += 12;
					octave = note / 12;
					note = note % 12;
					dest_channel->d.hull_offset = 0;
					dest_channel->d.hull_counter = 1;
					if (pc_jr && dest_channel == &channels[3]) {
						dest_channel->d.hull_curve = 180 + note * 12;
						freq = 384 - 64 * octave;
					} else {
						freq = freqs_table[note] >> octave;
					}
					dest_channel->d.freq = dest_channel->d.base_freq = freq;
				}
				if ((opcode & 0x80) != 0)
					goto end;
				opcode = *script_ptr++;
			}
		}
    }
 end:
	channel = current_channel;
	if (channel->d.time_left)
		goto finish;
#if 0
	script_ptr = 0;
	if (--fc8_c)
		goto finish;

	channel->d.next_cmd = 0;
	sound_status[fc8_4]--;
	fc8_4 = 0;
	look_for_pending_sound();
#endif
	return;

 finish:
	channel->d.next_cmd = script_ptr - 
	music_script[channel->d.music_script_nr];
	return;
}



void next_freqs(channel_info *channel) {
	channel->d.volume    += channel->d.volume_delta;
	channel->d.base_freq += channel->d.freq_delta;

	channel->d.freqmod_offset += channel->d.freqmod_incr;
	if (channel->d.freqmod_offset > channel->d.freqmod_modulo)
		channel->d.freqmod_offset -= channel->d.freqmod_modulo;
	channel->d.freq = 
		(int) (freqmod_table[channel->d.freqmod_table + (channel->d.freqmod_offset >> 4)])
		* (int) channel->d.freqmod_multiplier / 256
		+ channel->d.base_freq;
#if 0
    printf("Freq: %d/%d, %d/%d/%d*%d %d\n",
	    channel->d.base_freq, (int16)channel->d.freq_delta,
	    channel->d.freqmod_table, channel->d.freqmod_offset,
	    channel->d.freqmod_incr, channel->d.freqmod_multiplier,
	    channel->d.freq);
#endif
    
	if (channel->d.note_length && !--channel->d.note_length) {
		channel->d.hull_offset += 16;
		channel->d.hull_counter = 1;
	}

	if (!--channel->d.time_left) {
		execute_cmd(channel);
	}
#if 0
    fprintf(stdout, "channel[%d]: freq %d hull %d/%d/%d\n", 
	    channel - &channels[0], channel->d.freq, channel->d.hull_curve, channel->d.hull_offset, channel->d.hull_counter);
#endif
    
	if (channel->d.hull_counter && !--channel->d.hull_counter) {
		for (;;) {
			int16 *hull_ptr = hulls + channel->d.hull_curve + channel->d.hull_offset/2;
			if (hull_ptr[1] == -1) {
				channel->d.volume = hull_ptr[0];
				if (hull_ptr[0] == 0)
					channel->d.volume_delta = 0;
				channel->d.hull_offset += 4;
			} else {
				channel->d.volume_delta = hull_ptr[0];
				channel->d.hull_counter = hull_ptr[1];
				channel->d.hull_offset += 4;
				break;
			}
		}
	}
}

#ifdef AUTHENTIC_SOUND
/**
 * This simulates the pc speaker sound, which is driven
 * by the 8253 (square wave generator) and a low-band filter.
 */
void produce_spk_sound() {
    int8 sample[SAMPLE_LENGTH];
    static int sample_offset = 0;
    static int last_freq=100;
    int winning_channel = -1;
    int i, j;
    int freq; // , volume;
    static uint level = 0;

	for (j = 0; j < SAMPLE_LENGTH; j++) {
		sample[j] = 0;
	}
	for (i = 0; i < 4; i++) {
		if (!channels[i].d.time_left)
			continue;
		next_freqs(&channels[i]);
		if (winning_channel == -1
		    && channels[i].d.volume
		    && channels[i].d.time_left)
		{
			winning_channel = i;
		}
	}
	if (winning_channel != -1) {
		freq = channels[winning_channel].d.freq;
	} else {
		freq = 0;
	}

	for (j = 0; j < SAMPLE_LENGTH; j++) {
		level = (level * DECAY) >> 16;
		if (sample_offset < freq*500)
			level += 0xffff-DECAY;
		sample[j] = (level >> 8);
	
		sample_offset += 1193000*1000 / SAMPLE_RATE;
		if (sample_offset >= last_freq*1000) {
			sample_offset -= last_freq*1000;
			last_freq = freq;
		}
	}

#if 0
    fprintf(stdout, "channel[%d]: freq %d  ; %d\n", winning_channel, freq, sample_offset);
#endif
    fflush(stdout);
    for (j = 0; j < SAMPLE_LENGTH;) {
		j += fwrite (sample + j, 1, SAMPLE_LENGTH - j, sound_fd);
		if (j < SAMPLE_LENGTH) {
			fprintf(stderr, "Incomplete write: %d < %d!\n", j, SAMPLE_LENGTH);
			exit(0);
		}
    }
    sample_length += SAMPLE_LENGTH;
}

#else
void produce_spk_sound() {
    int8 sample[SAMPLE_LENGTH];
    static int sample_offset = 0;
    int winning_channel = -1;
    int i, j;
    float freq, volume;
    for (j = 0; j < SAMPLE_LENGTH; j++) {
	sample[j] = 0;
    }
    for (i = 0; i < 4; i++) {
	if (!channels[i].d.time_left)
	    continue;
	next_freqs(&channels[i]);
	if (winning_channel == -1
	    && channels[i].volume
	    && channels[i].d.time_left) {
	    winning_channel = i;
	}
    }
    if (winning_channel != -1) {
	i = winning_channel;
	freq = channels[i].freq;
	volume = channels[i].volume;
	volume /= 3.0;
	freq = 1193000 / freq;

	for (j = 0; j < SAMPLE_LENGTH; j++) {
	    sample[j] += (int8) 
		(volume * 100
#ifdef SAEGEZAHN
		 * (sample_offset < SAMPLE_RATE/2
		    ? sample_offset - SAMPLE_RATE/4
		    : 3*SAMPLE_RATE/4 - sample_offset) /(SAMPLE_RATE/4)
#elif defined(RECHTECK)
		 * (sample_offset < SAMPLE_RATE/2 ? 1 : -1)
#else
		 * sin((double)sample_offset*2*3.145926 / SAMPLE_RATE)
#endif
		 );
		sample[j] = sample[j] + 0x80;
	    sample_offset += freq;
	    sample_offset %= SAMPLE_RATE;
	}

#if 0
	fprintf(stdout, "channel[%d]: freq %d = %0f  ; volume %f  %d\n", i, channels[i].freq, freq, volume, sample_offset);
#endif
    }
    fflush(stdout);
    for (j = 0; j < SAMPLE_LENGTH;)
	j += fwrite(sample + j, 1, SAMPLE_LENGTH - j, sound_fd);
    sample_length += SAMPLE_LENGTH;
}
#endif

#define STEP 0x10000
#define UPDATESTEP ((STEP * SAMPLE_RATE) / (111860*2))
#define MAX_OUTPUT 0x7fff

#define FB_WNOISE 0x12000       /* bit15.d(16bits) = bit0(out) ^ bit2 */
#define FB_PNOISE 0x08000       /* JH 981127 - fixes Do Run Run */
#define NG_PRESET 0x0f35

int SN76496_count[4];
int SN76496_output[4];
int SN76496_voltable[16];

void init_pcjr()
{
	int i;
	double out;
	int gain;

	// increase max output basing on gain (0.2 dB per step)
	out = MAX_OUTPUT / 3;
	gain = 10;
	while (gain-- > 0)
		out *= 1.023292992; // = (10 ^ (0.2/20))
	// build volume table (2dB per step)
	for (i = 0; i < 15; i++) {
		// limit volume to avoid clipping
		if (out > MAX_OUTPUT / 3)
			SN76496_voltable[i] = MAX_OUTPUT / 3;
		else
			SN76496_voltable[i] = (int) out;
		printf("%2d: %4x\n", i, SN76496_voltable[i]);
		out /= 1.258925412; // = 10 ^ (2/20) = 2dB
	}
	SN76496_voltable[15] = 0;
}

void produce_pcjr_sound() {
	uint16 sample[SAMPLE_LENGTH];
	int i, j;
	uint16 freq, vol;
	static int16 lastvol = 0;

	memset(sample, 0, sizeof(sample));
	for (i = 0; i < 4; i++) {
		if (!channels[i].d.time_left)
			continue;
		next_freqs(&channels[i]);

		freq = channels[i].d.freq >> 6;
		vol  = (65535 - channels[i].d.volume) >> 12;
		if (!channels[i].d.volume
		    && !channels[i].d.time_left)
		{
			if (SN76496_count[i] >= SAMPLE_LENGTH * STEP)
				SN76496_count[i] -= STEP * SAMPLE_LENGTH;
		} else if (i < 3) {
			int period = UPDATESTEP * freq;
			if (period == 0)
				period = UPDATESTEP;

			for (j = 0; j < SAMPLE_LENGTH; j++) {
				unsigned int volume = 0;

				if (SN76496_output[i])
					volume += SN76496_count[i];

				SN76496_count[i] -= STEP;
				while (SN76496_count[i] <= 0) {
					SN76496_count[i] += period;
					if (SN76496_count[i] > 0) {
						SN76496_output[i] ^= 1;
						if (SN76496_output[i])
							volume += period;
						break;
					}
					SN76496_count[i] += period;
					volume += period;
				}

				if (SN76496_output[i])
					volume -= SN76496_count[i];
				volume *= SN76496_voltable[vol];
				sample[j] += volume / STEP;
			}
		} else {
			int period, RNG, NoiseFB; // , left

			int n = (freq & 3);
			if (n == 3) {
				period = 2 * UPDATESTEP * (channels[2].d.freq>>6);
				if (!period)
					period = 2 * UPDATESTEP;
			} else
				period = UPDATESTEP << (5 + n);

			RNG = NG_PRESET;
			NoiseFB = (freq & 4) ? FB_WNOISE : FB_PNOISE;
			SN76496_output[3] = RNG & 1;

			for (j = 0; j < SAMPLE_LENGTH; j++) {
				unsigned int volume = 0;

				if (SN76496_output[3])
					volume += SN76496_count[3];
				SN76496_count[3] -= STEP;
				while (SN76496_count[3] <= 0) {
					if (RNG & 1)
						RNG ^= NoiseFB;
					RNG >>= 1;
					SN76496_output[3] = RNG & 1;
					SN76496_count[3] += period;
					if (SN76496_output[3])
						volume += period;
				}
				if (SN76496_output[3])
					volume -= SN76496_count[3];

				volume *= SN76496_voltable[vol];
				sample[j] += volume / STEP;
			}
		}
#if 0
	fprintf(stdout, "channel[%d]: freq %d %.1f ; volume %d \n", i, freq, 111860.0/freq,  vol);
#endif
    }

    for (j = 0; j < SAMPLE_LENGTH; j++) {
		lastvol = ((int)lastvol * PCJR_DECAY + (int)sample[j] * (0x10000-PCJR_DECAY)) >> 16;
		sample[j] = lastvol;
    }

    fflush(stdout);
    for (j = 0; j < 2*SAMPLE_LENGTH;)
		j += fwrite(sample + j, 1, 2*SAMPLE_LENGTH - j, sound_fd);
#if 0
    for (j = 0; j < 2*SAMPLE_LENGTH;)
		j += fwrite(sample + j, 1, 2*SAMPLE_LENGTH - j, stdout);
#endif
    sample_length += 2*SAMPLE_LENGTH;
}


void init_channel(channel_info *channel) {
    channel->d.next_cmd   = 0;
    channel->d.base_freq  = 0;
    channel->d.freq_delta = 0;
    channel->d.freq = 0;
    channel->d.volume = 0;
    channel->d.volume_delta = 0;
    channel->d.inter_note_pause = 0;
    channel->d.transpose = 0;
    channel->d.hull_curve = 0;
    channel->d.hull_offset = 0;
    channel->d.hull_counter = 0;
    channel->d.freqmod_table = 0;
    channel->d.freqmod_offset = 0;
    channel->d.freqmod_incr = 0;
    channel->d.freqmod_multiplier = 0;
    channel->d.freqmod_modulo = 0;
    channel->d.unknown[0] = 0;
    channel->d.unknown[1] = 0;
    channel->d.unknown[2] = 0;
    channel->d.unknown[3] = 0;
    channel->d.unknown[4] = 0;
    channel->d.tempo = 0;
    channel->d.note_length = 0;
}


void load_music(int nr) {
	int i;
	uint16 *music = (uint16*) music_script[nr];
	int offset = pc_jr ? 7 : 3;
	for (i = 0; i < 4; i++) {
		init_channel(&channels[i]);
		if (music [offset + i]) {
			channels[i].d.music_script_nr = nr;
			channels[i].d.next_cmd = READ_LE_UINT16 (&music[offset+i]);
			channels[i].d.time_left = 1;
		}
	}

	while (channels[0].d.time_left
	       || channels[1].d.time_left
	       || channels[2].d.time_left
	       || channels[3].d.time_left)
	{
		if (pc_jr)
			produce_pcjr_sound();
		else
			produce_spk_sound();
	}
}

uint8 readByte(FILE *file) {
    uint8 a;
    fread(&a, 1, 1, file);
    return a;
}

uint16 readUint16LE(FILE *file) {
    uint8 a = readByte(file);
    uint8 b = readByte(file);
    return a | (b << 8);
}

void WRITE_LE_UINT16(uint8 *ptr, uint16 val) {
	*ptr = (val & 0xff);
	*(ptr+1) = ((val >> 8) & 0xff);
}

void WRITE_LE_UINT32(uint8 *ptr, uint32 val) {
	WRITE_LE_UINT16(ptr, val & 0xffff);
	WRITE_LE_UINT16(ptr+2, val >> 16);
}

void read_level0() {
	FILE * file;
	uint8  b;
	uint16 w;
	int i;
	file = fopen("00.lfl", "rb");

	w = readUint16LE(file) ^ 0xffff;

	/* skip object */
	w = readUint16LE(file) ^ 0xffff;
	fseek(file, w, SEEK_CUR);

	/* skip unknowns */
	b = readByte(file) ^ 0xff;
	fseek(file, 3*b, SEEK_CUR);

	/* skip costumes */
	b = readByte(file) ^ 0xff;
	fseek(file, 3*b, SEEK_CUR);

	/* skip scripts */
	b = readByte(file) ^ 0xff;
	fseek(file, 3*b, SEEK_CUR);

	/* read music */
	b = readByte(file) ^ 0xff;
	fread(&m_lfl, 1, b, file);
	for (i = 0; i < b; i++) {
		m_lfl[i] ^= 0xff;
		m_off[i] = readUint16LE(file) ^ 0xffff;
	}
	fclose(file); 
}

void read_music(int nr) {
	char lvl[20];
	FILE * file;
	int i;
	uint16 len;
	sprintf(lvl, "%02d.lfl", m_lfl[nr]);
	printf("Opening %s\n", lvl);
	file = fopen(lvl, "rb");
	if (!file) {
		printf ("Could not open file\n");
		exit (-1);
	}
	fseek(file, m_off[nr], SEEK_CUR);
	len = readUint16LE(file) ^ 0xffff;
	music_script[nr] = (uint8 *) malloc(len);
	fseek(file, -2, SEEK_CUR);
	fread(music_script[nr], 1, len, file);
	for (i = 0; i < len; i++)
		music_script[nr][i] ^= 0xff;
	fclose(file);
}

void open_sound() {
	long size = -1;
	long length = -1;
	sound_fd = fopen("output.wav", "wb");
	uint8 outbuff[4];
	int bps = 1;
	if (pc_jr)
	  bps = 2;

	memcpy (outbuff, "RIFF", 4);
	fwrite (outbuff, 1, 4, sound_fd);
	outbuff[0] = size & 0xFF;
	outbuff[1] = (size >> 8) & 0xFF;
	outbuff[2] = (size >> 16) & 0xFF;
	outbuff[3] = (size >> 24) & 0xFF;
	fwrite (outbuff, 1, 4, sound_fd);
	memcpy (outbuff, "WAVE", 4);
	fwrite (outbuff, 1, 4, sound_fd);
	memcpy (outbuff, "fmt ", 4);
	fwrite (outbuff, 1, 4, sound_fd);
	memcpy (outbuff, "\x10\x00\x00\x00", 4);
	fwrite (outbuff, 1, 4, sound_fd);
	memcpy (outbuff, "\x01\x00\x01\x00", 4);
	fwrite (outbuff, 1, 4, sound_fd);
	WRITE_LE_UINT32(outbuff, SAMPLE_RATE);
	fwrite (outbuff, 1, 4, sound_fd);
	WRITE_LE_UINT32(outbuff, bps*SAMPLE_RATE);
	fwrite (outbuff, 1, 4, sound_fd);
	WRITE_LE_UINT16(outbuff, bps);
	WRITE_LE_UINT16(outbuff+2, bps*8);
	fwrite (outbuff, 1, 4, sound_fd);
	memcpy (outbuff, "data", 4);
	fwrite (outbuff, 1, 4, sound_fd);
	outbuff[0] = length & 0xFF;
	outbuff[1] = (length >> 8) & 0xFF;
	outbuff[2] = (length >> 16) & 0xFF;
	outbuff[3] = (length >> 24) & 0xFF;
	fwrite (outbuff, 1, 4, sound_fd);
}

void write_length() {
	char outbuff[4];
	long length = sample_length;
	long size = length + 36;
	printf ("Input file size: %d\n", length);

	fseek(sound_fd, 4, SEEK_SET);
	outbuff[0] = size & 0xFF;
	outbuff[1] = (size >> 8) & 0xFF;
	outbuff[2] = (size >> 16) & 0xFF;
	outbuff[3] = (size >> 24) & 0xFF;
	fwrite (outbuff, 1, 4, sound_fd);
	fseek(sound_fd, 32, SEEK_CUR);
	outbuff[0] = length & 0xFF;
	outbuff[1] = (length >> 8) & 0xFF;
	outbuff[2] = (length >> 16) & 0xFF;
	outbuff[3] = (length >> 24) & 0xFF;
	fwrite (outbuff, 1, 4, sound_fd);
}

int main(int argc, char** argv) {
	int nr; // , i;
	if (argc > 2)
	pc_jr = 1;
	open_sound();
	read_level0();
	if (argc > 1)
		nr = atoi(argv[1]);
	else
		nr = 95;
	srand(1234);
	/*
	for (i = 0x302; i < 0x502; i++)
	freqmod_table[i] = (rand()>> 8) & 0xff;
	*/
	read_music(nr);
	if (pc_jr)
		init_pcjr();

	freqs_table = pc_jr ? pcjr_freq_table : spk_freq_table;

	load_music(nr);
	fclose (sound_fd);
	write_length();
	return 0;
}

