/*************************************************************************/
/*                                                                       */
/*                  Language Technologies Institute                      */
/*                     Carnegie Mellon University                        */
/*                        Copyright (c) 2001                             */
/*                        All Rights Reserved.                           */
/*                                                                       */
/*  Permission is hereby granted, free of charge, to use and distribute  */
/*  this software and its documentation without restriction, including   */
/*  without limitation the rights to use, copy, modify, merge, publish,  */
/*  distribute, sublicense, and/or sell copies of this work, and to      */
/*  permit persons to whom this work is furnished to do so, subject to   */
/*  the following conditions:                                            */
/*   1. The code must retain the above copyright notice, this list of    */
/*      conditions and the following disclaimer.                         */
/*   2. Any modifications must be clearly marked as such.                */
/*   3. Original authors' names are not deleted.                         */
/*   4. The authors' names are not used to endorse or promote products   */
/*      derived from this software without specific prior written        */
/*      permission.                                                      */
/*                                                                       */
/*  CARNEGIE MELLON UNIVERSITY AND THE CONTRIBUTORS TO THIS WORK         */
/*  DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING      */
/*  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT   */
/*  SHALL CARNEGIE MELLON UNIVERSITY NOR THE CONTRIBUTORS BE LIABLE      */
/*  FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES    */
/*  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN   */
/*  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,          */
/*  ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF       */
/*  THIS SOFTWARE.                                                       */
/*                                                                       */
/*************************************************************************/
/*             Author:  Alan W Black (awb@cs.cmu.edu)                    */
/*               Date:  January 2001                                     */
/*************************************************************************/
/*                                                                       */
/*  au_ahi.c: audio support for AHI (AmigaOS)                            */
/*  by Fredrik Wikstrom (fredrik@a500.org) 5.3.08                        */
/*                                                                       */
/*************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include "cst_string.h" 
#include "cst_wave.h"
#include "cst_audio.h"

#include <devices/ahi.h>
#include <proto/exec.h>

#define AHI_BUFFER_SIZE 32768
//#define dbug(x) DebugPrintF x
#define dbug(x)

typedef struct {
	void *buf, *buf2;
	LONG buf_size;
	LONG buf_end;
	LONG fmt, rate;
	struct MsgPort *mp;
	struct AHIRequest *io, *io2;
	struct AHIRequest *join;
} au_ahi_pdata;

int audio_close_ahi(cst_audiodev *ad);

cst_audiodev * audio_open_ahi(int sps, int channels, int fmt) {
	cst_audiodev *ad;
	au_ahi_pdata *pd;

	dbug(("audio_open_ahi\n"));

	ad = cst_alloc(cst_audiodev,1);
	if (!ad) return NULL;
	ad->sps = ad->real_sps = sps;
	ad->channels = ad->real_channels = channels;
	ad->fmt = ad->real_fmt = fmt;
    
	ad->platform_data = cst_alloc(au_ahi_pdata,1);
	pd = ad->platform_data;
	if (!pd) return NULL;

	switch (fmt) {
		case CST_AUDIO_LINEAR8:
			switch (channels) {
				case 1: pd->fmt = AHIST_M8S; break;
				case 2: pd->fmt = AHIST_S8S; break;
				default: return NULL;
			}
			break;
		case CST_AUDIO_LINEAR16:
			switch (channels) {
				case 1: pd->fmt = AHIST_M16S; break;
				case 2: pd->fmt = AHIST_S16S; break;
				default: return NULL;
			}
			break;
		default:
			return NULL;
	}
	pd->rate = sps;

	pd->buf_size = AHI_BUFFER_SIZE;
	pd->buf = AllocVec(pd->buf_size, MEMF_ANY);
	pd->buf2 = AllocVec(pd->buf_size, MEMF_ANY);
	if (!pd->buf || !pd->buf2) return NULL;
    
	pd->mp = CreateMsgPort();
	pd->io = CreateIORequest(pd->mp, sizeof(struct AHIRequest));
	pd->io2 = CreateIORequest(pd->mp, sizeof(struct AHIRequest));
	if (!pd->io || !pd->io2) return NULL;

	if (OpenDevice((STRPTR)"ahi.device", 0, (void *)pd->io, 0)) {
		pd->io->ahir_Std.io_Device = NULL;
		return NULL;
	}

	CopyMem(pd->io, pd->io2, sizeof(struct AHIRequest));

	return ad;
}

int _ahi_write_buffer (au_ahi_pdata *s) {
	struct AHIRequest *io = s->io;
	void *buf = s->buf;

	if (!s->buf_end) return 0;

	dbug(("_ahi_write_buffer\n"));

	io->ahir_Std.io_Message.mn_Node.ln_Pri = 0;
	io->ahir_Std.io_Command = CMD_WRITE;
	io->ahir_Std.io_Data = buf;
	io->ahir_Std.io_Length = s->buf_end;
	io->ahir_Std.io_Offset = 0;
	io->ahir_Frequency = s->rate;
	io->ahir_Type = s->fmt;
	io->ahir_Volume = 0x10000;
	io->ahir_Position = 0x8000;
	io->ahir_Link = s->join;
	SendIO((void *)io);

	if (s->join) {
		WaitIO((void *)s->join);
	}
	s->join = io;

	s->buf = s->buf2;
	s->buf2 = buf;

	s->io = s->io2;
	s->io2 = io;

	s->buf_end = 0;
	return 1;
}

int audio_write_ahi(cst_audiodev *ad, void *samples_ptr, int num_bytes) {
	au_ahi_pdata *pd = ad->platform_data;
	const char *samples = samples_ptr;
	LONG packed = 0;
	LONG copy_len;
	int ok = 1;

	dbug(("audio_write_ahi\n"));

	while (packed < num_bytes && ok) {
		/* Pack the buffer */
		if (num_bytes-packed < pd->buf_size - pd->buf_end)
			copy_len = num_bytes - packed;
		else
			copy_len = pd->buf_size - pd->buf_end;

		CopyMem(samples + packed, pd->buf + pd->buf_end,
		       copy_len);
		pd->buf_end += copy_len;

		if(pd->buf_end == pd->buf_size)
			ok = _ahi_write_buffer(pd);

		if (ok) packed += copy_len;
	}

	return packed;
}

int audio_drain_ahi(cst_audiodev *ad) {
	au_ahi_pdata *pd = ad->platform_data;

	dbug(("audio_drain_ahi\n"));
	
	if (pd->join) {
		AbortIO((void *)pd->join);		
		WaitIO((void *)pd->join);
		pd->join = NULL;
	}
	
	return 0;
}

int audio_flush_ahi(cst_audiodev *ad) {
	au_ahi_pdata *pd = ad->platform_data;

	dbug(("audio_flush_ahi\n"));
	
	_ahi_write_buffer(pd);
	if (pd->join) {
		WaitIO((void *)pd->join);
		pd->join = NULL;
	}

	return 0;
}

int audio_close_ahi(cst_audiodev *ad)
{
	au_ahi_pdata *pd;

	dbug(("audio_close_ahi\n"));

	if (ad) {
		pd = (au_ahi_pdata *)ad->platform_data;
		if (pd) {
			if (pd->io && pd->io->ahir_Std.io_Device) {
				_ahi_write_buffer(pd);

				if (pd->join) {
					WaitIO((void *)pd->join);
				}

				CloseDevice((void *)pd->io);
			}

			DeleteIORequest(pd->io);
			DeleteIORequest(pd->io2);
			DeleteMsgPort(pd->mp);
			FreeVec(pd->buf);
			FreeVec(pd->buf2);
			cst_free(pd);
		}
		cst_free(ad);
	}
	return 0;
}
