/*
 * zoom2wav
 * ZOOM .aud or .zaf to WAV file converter for Zoom PS-02, PS-04, or MRS-8
 *
 * Copyright (C) 2003-2005 Dwight Engen (dengen40@yahoo.com)
 *
 * 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.
 *
 * The deltas table is from aud2wav originally
 * Copyrighted under the LGPL by Randy Gordon (randy@integrand.com) 
 */

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#define max(a,b) ((a) > (b) ? (a) : (b))

/* Both .aud and .zaf files use 16 bit samples */
#define SAMPLE_BITS 16
static int deltas[] = { 
  0,1,2,5,9,14,19,0,24,29, 
  34,40,45,51,57,62,68,75,81,87, 
  94,100,107,114,120,127,134,141,148,155, 
  163,170,177,185,192,200,208,215,223,231, 
  239,246,254,262,271,279,288,297,306,316, 
  325,336,346,357,368,380,391,404,416,429, 
  443,457,471,486,501,516,533,549,566,584, 
  602,621,640,660,681,702,724,747,770,794, 
  819,845,871,898,926,955,985,1016,1048,1081, 
  1115,1151,1188,1226,1266,1308,1351,1397,1444,1493, 
  1544,1598,1653,1712,1773,1836,1903,1972,2045,2121, 
  2204,2297,2402,2520,2654,2810,2991,3207,3468,3792, 
  4206,4732,5324,5989,6738,7580,8527,9551,-9551,-8527, 
  -7580,-6738,-5989,-5324,-4732,-4206,-3792,-3468,-3207,-2991, 
  -2810,-2654,-2520,-2402,-2297,-2204,-2121,-2045,-1972,-1903, 
  -1836,-1773,-1712,-1653,-1598,-1544,-1493,-1444,-1397,-1351, 
  -1308,-1266,-1226,-1188,-1151,-1115,-1081,-1048,-1016,-985, 
  -955,-926,-898,-871,-845,-819,-794,-770,-747,-724, 
  -702,-681,-660,-640,-621,-602,-584,-566,-549,-533, 
  -516,-501,-486,-471,-457,-443,-429,-416,-404,-391, 
  -380,-368,-357,-346,-336,-325,-316,-306,-297,-288, 
  -279,-271,-262,-254,-246,-239,-231,-223,-215,-208, 
  -200,-192,-185,-177,-170,-163,-155,-148,-141,-134, 
  -127,-120,-114,-107,-100,-94,-87,-81,-75,-68, 
  -62,-57,-51,-45,-40,-34,-29,-24,0,-19, 
  -14,-9,-5,-2,-1,0 
}; 

uint32_t sample_rate;
FILE *in[2];
FILE *out;

#define WAVE_FORMAT_PCM 1
typedef struct _DataChunk	DataChunk;
typedef struct _FmtChunk	FmtChunk;
typedef struct _WaveHeader	WaveHeader;

struct _FmtChunk
{
    char	chunk_id[4];		/* "fmt " */
    uint32_t 	chunk_size;
    uint16_t	format;
    uint16_t	channels;
    uint32_t	sample_rate;
    uint32_t	avg_bytes_per_second;
    uint16_t	block_align;
    uint16_t	bits_per_sample;    
};

struct _DataChunk
{
    char	chunk_id[4];		/* "data" */
    uint32_t	chunk_size;
};

struct _WaveHeader
{
    char	group_id[4];		/* "RIFF" */
    uint32_t	size;
    char	riff_type[4];		/* "WAVE" */
    FmtChunk	fmt;
};

static void
write_wave_header(FILE *out, size_t frames, int channels, int sample_rate,
		  int sample_bits)
{
    WaveHeader	header;
    DataChunk	data;
    uint32_t	bytes;

    bytes = frames * channels * (sample_bits / 8);
    memset(&header, 0, sizeof(header));
    memcpy(header.group_id,  "RIFF", 4);
    memcpy(header.riff_type, "WAVE", 4);
    header.size = bytes+sizeof(header);

    memcpy(header.fmt.chunk_id, "fmt ", 4);
    header.fmt.chunk_size = sizeof(FmtChunk) - 8;
    header.fmt.format = WAVE_FORMAT_PCM;
    header.fmt.channels = channels;
    header.fmt.sample_rate = sample_rate;
    header.fmt.block_align = channels * (sample_bits / 8);
    header.fmt.avg_bytes_per_second = sample_rate * header.fmt.block_align;
    header.fmt.bits_per_sample = sample_bits;

    memcpy(data.chunk_id, "data", 4);
    data.chunk_size = bytes;

    fwrite(&header, 1, sizeof(header), out);
    fwrite(&data, 1, sizeof(data), out);
}

static int
safe_fgetc(FILE *f)
{
    int rc = fgetc(f);
    if (feof(f))
    {
	printf("ERROR: Unexpected early EOF!\n");
	exit(-1);
    }
    return rc;
}

static void
audblk2samples(FILE *f, short *sample_buf)
{
    short sample = 0;
    int bufIndex = 0;
    int hibyte;
    int lobyte;

    int i;
    int deltaIndex;
    int delta;

    memset(sample_buf, 0, sizeof(short) * 509);

    /* Fill smp_buf with 509 samples from AUD data */

    /* Read the absolute sample value from the AUD */
    lobyte = fgetc(f);
    if (lobyte == EOF)
    {
	static int warn = 0;
	if (!warn)
	{
	    warn = 1;
	    printf("WARNING: Reached early EOF padding with zero (aud files not same size?)\n");
	}
	return;
    }
    hibyte = safe_fgetc(f);
    sample = (short)((hibyte<<8) | (lobyte & 0xff));
    sample_buf[bufIndex++] = sample;

    /* Calculate the next 508 samples from the deltas */
    for (i = 0; i < 508; i++)
    {
	deltaIndex = safe_fgetc(f);
	delta = deltas[deltaIndex];
	sample += delta;
	sample_buf[bufIndex++] = sample;
    }

    /* Skip the terminator bytes */
    safe_fgetc(f);
    safe_fgetc(f);
}

static void
auds2wav(int channels, uint32_t length)
{
    uint32_t blocks;
    uint32_t frames;
    int i,j,k;

    blocks  = length / 512;
    frames  = blocks * 509;

    /* Create .wav header */
    write_wave_header(out, frames, channels, sample_rate, SAMPLE_BITS);

    /* Process all 512-byte data blocks from the file until EOF found */
    for(i = 0; i < blocks; i++)
    {
	short samples[2][509];

	printf("Complete: %d%%\r", (i * 100) / blocks);
	for(j = 0; j < channels; j++)
	    audblk2samples(in[j], &samples[j][0]);

	for(j = 0; j < 509; j++)
	{
	    for(k = 0; k < channels; k++)
	    {
		fputc(samples[k][j]&0xff, out); 
		fputc((samples[k][j]>>8)&0xff, out); 
	    }
	}
    }
    printf("\n");
}

int
main(int argc, char *argv[]) 
{
    int	i;
    int input_files = 0;
    size_t header_size;
    size_t samples_size = 0;
    char magic[13];

    if (argc < 3 || argc > 4)
    {
	puts("Usage: zoom2wav <[.aud|.zaf] file> [[.aud|.zaf] file] <wav file>");
	puts("Example: zoom2wav 00-00.aud leadvocal.wav");
	puts("Example: zoom2wav 00-00.aud 00-01.aud stereo.wav");
	puts("Example: zoom2wav track0_0.zaf guitarsolo.wav");
	puts("Example: zoom2wav track0_0.zaf track0_1.zaf stereo.wav");
	return 1;
    } 


    for(i = 1; i < argc - 1; i++)
    {
	if ((in[i-1] = fopen(argv[i], "rb")) == NULL)
	{
	    printf("Unable to open input file '%s'\n", argv[i]);
	    return 1;
	}
	input_files++;
    }

    if ((out = fopen(argv[i], "wb")) == NULL)
    {
	printf("Unable to open output file '%s'\n", argv[i]);
	return 1;
    }

    /* Sample the first bit to determine if its a .zaf file */
    fread(&magic, 1, sizeof(magic), in[0]);
    if (!strncmp(magic, "ZOOM1D5ZZ3AUD", sizeof(magic)))
    {
	/* ZAF file */
	sample_rate = 44100;
	header_size = 16384;
	printf("Converting in ZAF mode\n");
    }
    else
    {
	/* AUD file */
	sample_rate = 31250;
	header_size = 512;
	printf("Converting in AUD mode\n");
    }

    for(i=0; i < input_files; i++)
    {
	struct stat sbuf;

	/* Skip the header */
	fseek(in[i], header_size, SEEK_SET);

	/* Get max length of any input file */
	fstat(fileno(in[i]), &sbuf);
	samples_size = max(samples_size, sbuf.st_size);
    }

    auds2wav(input_files, samples_size - header_size);
    return 0;
}
