File:  [RetroPC.NET] / np2 / x11 / soundmng.c
Revision 1.6: download - view: text, annotated - select for diffs
Sun Jun 27 00:48:35 2004 JST (21 years, 4 months ago) by monaka
Branches: MAIN
CVS tags: HEAD
fix for amd64.

/*
 * Copyright (c) 2001-2003 NONAKA Kimihiro
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "compiler.h"

#include "soundmng.h"

BYTE
snddrv_drv2num(const char* cfgstr)
{

	if (strcasecmp(cfgstr, "NetBSD") == 0)
		return SNDDRV_NETBSD;
	else if (strcasecmp(cfgstr, "OSS") == 0)
		return SNDDRV_OSS;
	else if (strcasecmp(cfgstr, "ESD") == 0)
		return SNDDRV_ESD;
	else if (strcasecmp(cfgstr, "SDL") == 0)
		return SNDDRV_SDL;
	return SNDDRV_NODRV;
}

const char *
snddrv_num2drv(BYTE num)
{

	switch (num) {
	case SNDDRV_NETBSD:
		return "NetBSD";
	case SNDDRV_OSS:
		return "OSS";
	case SNDDRV_ESD:
		return "ESD";
	case SNDDRV_SDL:
		return "SDL";
	}
	return "nosound";
}

#if !defined(NOSOUND)

#include "np2.h"
#include "pccore.h"
#include "ini.h"
#include "dosio.h"
#include "parts.h"

#include "sound.h"
#if defined(VERMOUTH_LIB)
#include "vermouth.h"
#endif

#include "sysmng.h"

#if defined(VERMOUTH_LIB)
MIDIMOD vermouth_module = NULL;
#endif

/*
 * driver
 */
int audio_fd = -1;
snddrv_t snddrv;

static BOOL opened = FALSE;
static UINT mute = 0;
static UINT opna_frame;

static BOOL nosound_setup(void);

static void PARTSCALL (*fnmix)(SINT16* dst, const SINT32* src, UINT size);

#if defined(GCC_CPU_ARCH_IA32)
void PARTSCALL _saturation_s16(SINT16 *dst, const SINT32 *src, UINT size);
void PARTSCALL _saturation_s16x(SINT16 *dst, const SINT32 *src, UINT size);
void PARTSCALL saturation_s16mmx(SINT16 *dst, const SINT32 *src, UINT size);
#endif

/*
 * PCM
 */
static void *pcm_channel[SOUND_MAXPCM];

static void soundmng_pcminit(void);
static void soundmng_pcmdestroy(void);

/*
 * buffer
 */
int sound_nextbuf;
char *sound_event;
char *sound_buffer[NSOUNDBUFFER];

static BOOL buffer_init(void);
static void buffer_destroy(void);
static void buffer_clear(void);


UINT
calc_blocksize(UINT size)
{
	UINT s = size;

	if (size & (size - 1))
		for (s = 32; s < size; s <<= 1)
			continue;
	return s;
}

UINT
calc_fragment(UINT size)
{
	UINT f;

	for (f = 0; size > (UINT)(1 << f); f++)
		continue;
	return f;
}

void
snddrv_setup(void)
{

	if (np2oscfg.snddrv < SNDDRV_DRVMAX) {
		switch (np2oscfg.snddrv) {
#if defined(USE_NETBSDAUDIO)
		case SNDDRV_NETBSD:
			netbsdaudio_setup();
			return;
#endif
#if defined(USE_OSSAUDIO)
		case SNDDRV_OSS:
			ossaudio_setup();
			return;
#endif
#if defined(USE_ESDAUDIO)
		case SNDDRV_ESD:
			esdaudio_setup();
			return;
#endif
#if defined(USE_SDLAUDIO) || defined(USE_SDLMIXER)
		case SNDDRV_SDL:
#if defined(USE_SDLMIXER)
			sdlmixer_setup();
#else
			sdlaudio_setup();
#endif
			return;
#endif
		}
	} else {
#if defined(USE_SDLMIXER)
		if (sdlmixer_setup() == SUCCESS) {
			np2oscfg.snddrv = SNDDRV_SDL;
			sysmng_update(SYS_UPDATEOSCFG);
			return;
		} else
#endif
#if defined(USE_SDLAUDIO)
		if (sdlaudio_setup() == SUCCESS) {
			np2oscfg.snddrv = SNDDRV_SDL;
			sysmng_update(SYS_UPDATEOSCFG);
			return;
		} else
#endif
#if defined(USE_ESDAUDIO)
		if (esdaudio_setup() == SUCCESS) {
			np2oscfg.snddrv = SNDDRV_ESD;
			sysmng_update(SYS_UPDATEOSCFG);
			return;
		} else
#endif
#if defined(USE_OSSAUDIO)
		if (ossaudio_setup() == SUCCESS) {
			np2oscfg.snddrv = SNDDRV_OSS;
			sysmng_update(SYS_UPDATEOSCFG);
			return;
		} else
#endif
#if defined(USE_NETBSDAUDIO)
		if (netbsdaudio_setup() == SUCCESS) {
			np2oscfg.snddrv = SNDDRV_NETBSD;
			sysmng_update(SYS_UPDATEOSCFG);
			return;
		} else
#endif
		{
			/* Nothing to do */
			/* fall thourgh "no match" */
		}
	}

	/* no match */
	nosound_setup();
	np2oscfg.snddrv = SNDDRV_NODRV;
	sysmng_update(SYS_UPDATEOSCFG);
}

UINT
soundmng_create(UINT rate, UINT bufmsec)
{
	UINT samples;

	if (opened || ((rate != 11025) && (rate != 22050) && (rate != 44100))) {
		return 0;
	}

	if (bufmsec < 20)
		bufmsec = 20;
	else if (bufmsec > 1000)
		bufmsec = 1000;

	snddrv_setup();

	samples = (rate * bufmsec) / 1000 / 2;
	samples = calc_blocksize(samples);
	opna_frame = samples * 2 * sizeof(SINT16);

	if ((*snddrv.drvinit)(rate, samples) != SUCCESS) {
		audio_fd = -1;
		np2oscfg.snddrv = SNDDRV_NODRV;
		sysmng_update(SYS_UPDATEOSCFG);
		return 0;
	}

#if defined(VERMOUTH_LIB)
	vermouth_module = midimod_create(rate);
	midimod_loadall(vermouth_module);
#endif

	soundmng_setreverse(FALSE);
	buffer_init();
	soundmng_reset();

	opened = TRUE;

	return samples;
}

void
soundmng_reset(void)
{

	sound_nextbuf = 0;
	sound_event = NULL;
	buffer_clear();
}

void
soundmng_destroy(void)
{
	UINT i;

	if (opened) {
#if defined(VERMOUTH_LIB)
		midimod_destroy(vermouth_module);
		vermouth_module = NULL;
#endif
		for (i = 0; i < SOUND_MAXPCM; i++) {
			soundmng_pcmstop(i);
		}
		(*snddrv.sndstop)();
		(*snddrv.drvterm)();
		buffer_destroy();
		nosound_setup();
		audio_fd = -1;
		opened = FALSE;
	}
}

void
soundmng_play(void)
{

	if (!mute) {
		(*snddrv.sndplay)();
	}
}

void
soundmng_stop(void)
{

	if (!mute) {
		(*snddrv.sndstop)();
	}
}

BOOL
soundmng_initialize(void)
{

	snddrv_setup();
	soundmng_pcminit();

	return SUCCESS;
}

void
soundmng_deinitialize(void)
{

	soundmng_pcmdestroy();
	soundmng_destroy();
}

void
soundmng_sync(void)
{
	const SINT32 *pcm;
	SINT16 *q;

	if (opened) {
		if (sound_event) {
			pcm = sound_pcmlock();
			q = (SINT16 *)sound_event;
			sound_event = NULL;
			(*fnmix)(q, pcm, opna_frame);
			sound_pcmunlock(pcm);
		}
	}
}

void
soundmng_setreverse(BOOL reverse)
{

#if defined(GCC_CPU_ARCH_AMD64)
	if (!reverse) {
		if (mmxflag & (MMXFLAG_NOTSUPPORT|MMXFLAG_DISABLE)) {
			fnmix = satuation_s16;
		} else {
			fnmix = saturation_s16mmx;
		}
	} else {
		fnmix = satuation_s16x;
	}
#elif defined(GCC_CPU_ARCH_IA32)
	if (!reverse) {
		if (mmxflag & (MMXFLAG_NOTSUPPORT|MMXFLAG_DISABLE)) {
			fnmix = _saturation_s16;
		} else {
			fnmix = saturation_s16mmx;
		}
	} else {
		fnmix = _saturation_s16x;
	}
#else
	if (!reverse) {
		fnmix = satuation_s16;
	} else {
		fnmix = satuation_s16x;
	}
#endif
}

/*
 * PCM function
 */
void
soundmng_pcminit(void)
{
	int i;

	for (i = 0; i < SOUND_MAXPCM; i++) {
		pcm_channel[i] = NULL;
	}
}

void
soundmng_pcmdestroy(void)
{
	int i;

	for (i = 0; i < SOUND_MAXPCM; i++) {
		if (pcm_channel[i]) {
			(*snddrv.pcmdestroy)(pcm_channel[i], i);
			pcm_channel[i] = NULL;
		}
	}
}

BOOL
soundmng_pcmload(UINT num, const char *filename)
{

	if (num < SOUND_MAXPCM) {
		if (pcm_channel[num])
			(*snddrv.pcmdestroy)(pcm_channel[num], num);
		pcm_channel[num] = (*snddrv.pcmload)(num, filename);
		if (pcm_channel[num])
			return SUCCESS;
	}
	return FAILURE;
}

void
soundmng_pcmvolume(UINT num, int volume)
{

	if ((num < SOUND_MAXPCM) && (pcm_channel[num])) {
		(*snddrv.pcmvolume)(pcm_channel[num], num, volume);
	}
}

BOOL
soundmng_pcmplay(UINT num, BOOL loop)
{

	if ((num < SOUND_MAXPCM) && (pcm_channel[num])) {
		(*snddrv.pcmplay)(pcm_channel[num], num, loop);
		return SUCCESS;
	}
	return FAILURE;
}

void
soundmng_pcmstop(UINT num)
{

	if ((num < SOUND_MAXPCM) && (pcm_channel[num])) {
		(*snddrv.pcmstop)(pcm_channel[num], num);
	}
}

/*
 * sound buffer
 */
static BOOL
buffer_init(void)
{
	int i;

	for (i = 0; i < NSOUNDBUFFER; i++) {
		if (sound_buffer[i]) {
			_MFREE(sound_buffer[i]);
		}
		sound_buffer[i] = (char *)_MALLOC(opna_frame, "sound buffer");
		if (sound_buffer[i] == NULL) {
			fprintf(stderr, "buffer_init: can't alloc memory\n");
			return FAILURE;
		}
	}
	return SUCCESS;
}

static void
buffer_clear(void)
{
	int i;

	for (i = 0; i < NSOUNDBUFFER; i++) {
		if (sound_buffer[i]) {
			memset(sound_buffer[i], 0, opna_frame);
		}
	}
}

static void
buffer_destroy(void)
{
	int i;

	for (i = 0; i < NSOUNDBUFFER; i++) {
		if (sound_buffer[i]) {
			_MFREE(sound_buffer[i]);
			sound_buffer[i] = NULL;
		}
	}
}

/*
 * No sound support
 */
static BOOL
nosound_drvinit(UINT rate, UINT bufmsec)
{

	UNUSED(rate);
	UNUSED(bufmsec);

	return SUCCESS;
}

static BOOL
nosound_drvterm(void)
{

	return SUCCESS;
}

static void
nosound_sndplay(void)
{

	/* Nothing to do */
}

static void
nosound_sndstop(void)
{

	/* Nothing to do */
}

void *
nosound_pcmload(UINT num, const char *path)
{

	UNUSED(num);
	UNUSED(path);

	return NULL;
}

void
nosound_pcmdestroy(void *chanp, UINT num)
{

	UNUSED(chanp);
	UNUSED(num);

	/* Nothing to do */
}

void
nosound_pcmplay(void *chanp, UINT num, BOOL loop)
{

	UNUSED(chanp);
	UNUSED(num);
	UNUSED(loop);

	/* Nothing to do */
}

void
nosound_pcmstop(void *chanp, UINT num)
{

	UNUSED(chanp);
	UNUSED(num);

	/* Nothing to do */
}

void
nosound_pcmvolume(void *chanp, UINT num, int volume)
{

	UNUSED(chanp);
	UNUSED(num);
	UNUSED(volume);

	/* Nothing to do */
}

static BOOL
nosound_setup(void)
{

	snddrv.drvinit = nosound_drvinit;
	snddrv.drvterm = nosound_drvterm;
	snddrv.sndplay = nosound_sndplay;
	snddrv.sndstop = nosound_sndstop;
	snddrv.pcmload = nosound_pcmload;
	snddrv.pcmdestroy = nosound_pcmdestroy;
	snddrv.pcmplay = nosound_pcmplay;
	snddrv.pcmstop = nosound_pcmstop;
	snddrv.pcmvolume = nosound_pcmvolume;

	return SUCCESS;
}

#if defined(USE_NETBSDAUDIO) || defined(USE_OSSAUDIO) || defined(USE_ESDAUDIO)
/*
 * common driver
 */
static pthread_t ptid;
static BOOL is_proc;

static void *
buffer_play(void *arg)
{
	char *buf;
	size_t len = opna_frame;
	size_t s;
	ssize_t r;
	int nextbuf;

	UNUSED(arg);

	is_proc = TRUE;
	while (is_proc) {
		nextbuf = sound_nextbuf;
		if (sound_event)
			memset(sound_event, 0, len);
		sound_nextbuf = (sound_nextbuf + 1) % NSOUNDBUFFER;
		sound_event = sound_buffer[sound_nextbuf];

		buf = sound_buffer[nextbuf];
		s = 0;
		for (;;) {
			r = write(audio_fd, buf + s, len - s);
			if (r >= 0) {
				s += r;
				if (len <= s)
					break;
			}
		}
	}
	is_proc = FALSE;

	return NULL;
}

void
snddrv_play(void)
{

	if (pthread_create(&ptid, NULL, buffer_play, NULL) != 0) {
		fprintf(stderr, "audio_play: can't create thread.\n");
	}
}

void
snddrv_stop(void)
{

	is_proc = FALSE;
}

#endif	/* USE_NETBSDAUDIO || USE_OSSAUDIO || USE_ESDAUDIO */

#if defined(GCC_CPU_ARCH_AMD64)
void PARTSCALL
saturation_s16mmx(SINT16 *dst, const SINT32 *src, UINT size)
{

	asm volatile (
		"movq	%0, %%rcx;"
		"movq	%1, %%rdx;"
		"movl	%2, %%eax;"
		"shrl	$3, %%eax;"
		"je	.ss16m_ed;"
		"pxor	%%mm0, %%mm0;"
	".ss16m_lp:"
		"movq	(%%rdx), %%mm1;"
		"movq	8(%%rdx), %%mm2;"
		"packssdw %%mm2, %%mm1;"
		"leaq	16(%%rdx), %%rdx;"
		"movq	%%mm1, (%%rcx);"
		"leaq	8(%%rcx), %%rcx;"
		"dec	%%eax;"
		"jne	.ss16m_lp;"
		"emms;"
	".ss16m_ed:"
		: /* output */
		: "m" (dst), "m" (src), "m" (size));
}
#elif defined(GCC_CPU_ARCH_IA32)
void PARTSCALL
_saturation_s16(SINT16 *dst, const SINT32 *src, UINT size)
{
	asm volatile (
		"movl	%0, %%ecx;"
		"movl	%1, %%edx;"
		"movl	%2, %%ebx;"
		"shrl	$1, %%ebx;"
		"je	.ss16_ed;"
	".ss16_lp:"
		"movl	(%%edx), %%eax;"
		"cmpl	$0x000008000, %%eax;"
		"jl	.ss16_min;"
		"movw	$0x7fff, %%ax;"
		"jmp	.ss16_set;"
	".ss16_min:"
		"cmpl	$0x0ffff8000, %%eax;"
		"jg	.ss16_set;"
		"movw	$0x8001, %%ax;"
	".ss16_set:"
		"leal	4(%%edx), %%edx;"
		"movw	%%ax, (%%ecx);"
		"decl	%%ebx;"
		"leal	2(%%ecx), %%ecx;"
		"jne	.ss16_lp;"
	".ss16_ed:"
		: /* output */
		: "m" (dst), "m" (src), "m" (size)
		: "ebx");
}

void PARTSCALL
_saturation_s16x(SINT16 *dst, const SINT32 *src, UINT size)
{

	asm volatile (
		"movl	%0, %%ecx;"
		"movl	%1, %%edx;"
		"movl	%2, %%ebx;"
		"shrl	$2, %%ebx;"
		"je	.ss16x_ed;"
	".ss16x_lp:"
		"movl	(%%edx), %%eax;"
		"cmpl	$0x000008000, %%eax;"
		"jl	.ss16xl_min;"
		"movw	$0x7fff, %%ax;"
		"jmp	.ss16xl_set;"
	".ss16xl_min:"
		"cmpl	$0x0ffff8000, %%eax;"
		"jg	.ss16xl_set;"
		"movw	$0x8001, %%ax;"
	".ss16xl_set:"
		"movw	%%ax, 2(%%ecx);"
		"movl	4(%%edx), %%eax;"
		"cmpl	$0x000008000, %%eax;"
		"jl	.ss16xr_min;"
		"movw	$0x7fff, %%ax;"
		"jmp	.ss16xr_set;"
	".ss16xr_min:"
		"cmpl	$0x0ffff8000, %%eax;"
		"jg	.ss16xr_set;"
		"mov	$0x8001, %%ax;"
	".ss16xr_set:"
		"movw	%%ax, (%%ecx);"
		"leal	8(%%edx), %%edx;"
		"decl	%%ebx;"
		"leal	4(%%ecx), %%ecx;"
		"jne	.ss16x_lp;"
	".ss16x_ed:"
		: /* output */
		: "m" (dst), "m" (src), "m" (size)
		: "ebx");
}

void PARTSCALL
saturation_s16mmx(SINT16 *dst, const SINT32 *src, UINT size)
{

	asm volatile (
		"movl	%0, %%ecx;"
		"movl	%1, %%edx;"
		"movl	%2, %%eax;"
		"shrl	$3, %%eax;"
		"je	.ss16m_ed;"
		"pxor	%%mm0, %%mm0;"
	".ss16m_lp:"
		"movq	(%%edx), %%mm1;"
		"movq	8(%%edx), %%mm2;"
		"packssdw %%mm2, %%mm1;"
		"leal	16(%%edx), %%edx;"
		"movq	%%mm1, (%%ecx);"
		"leal	8(%%ecx), %%ecx;"
		"dec	%%eax;"
		"jne	.ss16m_lp;"
		"emms;"
	".ss16m_ed:"
		: /* output */
		: "m" (dst), "m" (src), "m" (size));
}
#endif	/* GCC_CPU_ARCH_AMD64 */

#endif	/* !NOSOUND */

RetroPC.NET-CVS <cvs@retropc.net>