mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-12-10 21:55:10 -05:00
sdl: remove non-rockbox drivers
We never use any of these other drivers, so having them around just takes up space. Change-Id: Iced812162df1fef3fd55522b7e700acb6c3bcd41
This commit is contained in:
parent
ef373c03b9
commit
6039eb05ba
530 changed files with 0 additions and 138647 deletions
|
|
@ -1,619 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <signal.h> /* For kill() */
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "SDL_alsa_audio.h"
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
|
||||||
#include "SDL_name.h"
|
|
||||||
#include "SDL_loadso.h"
|
|
||||||
#else
|
|
||||||
#define SDL_NAME(X) X
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* The tag name used by ALSA audio */
|
|
||||||
#define DRIVER_NAME "alsa"
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void ALSA_WaitAudio(_THIS);
|
|
||||||
static void ALSA_PlayAudio(_THIS);
|
|
||||||
static Uint8 *ALSA_GetAudioBuf(_THIS);
|
|
||||||
static void ALSA_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
|
|
||||||
|
|
||||||
static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
|
|
||||||
static void *alsa_handle = NULL;
|
|
||||||
static int alsa_loaded = 0;
|
|
||||||
|
|
||||||
static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
|
|
||||||
static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);
|
|
||||||
static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
|
|
||||||
static int (*SDL_NAME(snd_pcm_recover))(snd_pcm_t *pcm, int err, int silent);
|
|
||||||
static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
|
|
||||||
static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
|
|
||||||
static const char *(*SDL_NAME(snd_strerror))(int errnum);
|
|
||||||
static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
|
|
||||||
static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);
|
|
||||||
static void (*SDL_NAME(snd_pcm_hw_params_copy))(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_set_access))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_set_format))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params, unsigned int *val);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_set_rate_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_set_period_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_get_period_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_set_periods_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_get_periods))(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_set_buffer_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params_get_buffer_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
|
|
||||||
static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
|
|
||||||
/*
|
|
||||||
*/
|
|
||||||
static int (*SDL_NAME(snd_pcm_sw_params_set_avail_min))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams, snd_pcm_uframes_t val);
|
|
||||||
static int (*SDL_NAME(snd_pcm_sw_params_current))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams);
|
|
||||||
static int (*SDL_NAME(snd_pcm_sw_params_set_start_threshold))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);
|
|
||||||
static int (*SDL_NAME(snd_pcm_sw_params))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
|
|
||||||
static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock);
|
|
||||||
static int (*SDL_NAME(snd_pcm_wait))(snd_pcm_t *pcm, int timeout);
|
|
||||||
#define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)
|
|
||||||
#define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof)
|
|
||||||
|
|
||||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
|
||||||
static struct {
|
|
||||||
const char *name;
|
|
||||||
void **func;
|
|
||||||
} alsa_functions[] = {
|
|
||||||
{ "snd_pcm_open", (void**)(char*)&SDL_NAME(snd_pcm_open) },
|
|
||||||
{ "snd_pcm_close", (void**)(char*)&SDL_NAME(snd_pcm_close) },
|
|
||||||
{ "snd_pcm_writei", (void**)(char*)&SDL_NAME(snd_pcm_writei) },
|
|
||||||
{ "snd_pcm_recover", (void**)(char*)&SDL_NAME(snd_pcm_recover) },
|
|
||||||
{ "snd_pcm_prepare", (void**)(char*)&SDL_NAME(snd_pcm_prepare) },
|
|
||||||
{ "snd_pcm_drain", (void**)(char*)&SDL_NAME(snd_pcm_drain) },
|
|
||||||
{ "snd_strerror", (void**)(char*)&SDL_NAME(snd_strerror) },
|
|
||||||
{ "snd_pcm_hw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof) },
|
|
||||||
{ "snd_pcm_sw_params_sizeof", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof) },
|
|
||||||
{ "snd_pcm_hw_params_copy", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_copy) },
|
|
||||||
{ "snd_pcm_hw_params_any", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_any) },
|
|
||||||
{ "snd_pcm_hw_params_set_access", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access) },
|
|
||||||
{ "snd_pcm_hw_params_set_format", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format) },
|
|
||||||
{ "snd_pcm_hw_params_set_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_channels) },
|
|
||||||
{ "snd_pcm_hw_params_get_channels", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_channels) },
|
|
||||||
{ "snd_pcm_hw_params_set_rate_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_rate_near) },
|
|
||||||
{ "snd_pcm_hw_params_set_period_size_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_period_size_near) },
|
|
||||||
{ "snd_pcm_hw_params_get_period_size", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_period_size) },
|
|
||||||
{ "snd_pcm_hw_params_set_periods_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_periods_near) },
|
|
||||||
{ "snd_pcm_hw_params_get_periods", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_periods) },
|
|
||||||
{ "snd_pcm_hw_params_set_buffer_size_near", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_buffer_size_near) },
|
|
||||||
{ "snd_pcm_hw_params_get_buffer_size", (void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_buffer_size) },
|
|
||||||
{ "snd_pcm_hw_params", (void**)(char*)&SDL_NAME(snd_pcm_hw_params) },
|
|
||||||
{ "snd_pcm_sw_params_set_avail_min", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_avail_min) },
|
|
||||||
{ "snd_pcm_sw_params_current", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_current) },
|
|
||||||
{ "snd_pcm_sw_params_set_start_threshold", (void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_start_threshold) },
|
|
||||||
{ "snd_pcm_sw_params", (void**)(char*)&SDL_NAME(snd_pcm_sw_params) },
|
|
||||||
{ "snd_pcm_nonblock", (void**)(char*)&SDL_NAME(snd_pcm_nonblock) },
|
|
||||||
{ "snd_pcm_wait", (void**)(char*)&SDL_NAME(snd_pcm_wait) },
|
|
||||||
};
|
|
||||||
|
|
||||||
static void UnloadALSALibrary(void) {
|
|
||||||
if (alsa_loaded) {
|
|
||||||
SDL_UnloadObject(alsa_handle);
|
|
||||||
alsa_handle = NULL;
|
|
||||||
alsa_loaded = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int LoadALSALibrary(void) {
|
|
||||||
int i, retval = -1;
|
|
||||||
|
|
||||||
alsa_handle = SDL_LoadObject(alsa_library);
|
|
||||||
if (alsa_handle) {
|
|
||||||
alsa_loaded = 1;
|
|
||||||
retval = 0;
|
|
||||||
for (i = 0; i < SDL_arraysize(alsa_functions); i++) {
|
|
||||||
*alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);
|
|
||||||
if (!*alsa_functions[i].func) {
|
|
||||||
retval = -1;
|
|
||||||
UnloadALSALibrary();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void UnloadALSALibrary(void) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int LoadALSALibrary(void) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
|
|
||||||
|
|
||||||
static const char *get_audio_device(int channels)
|
|
||||||
{
|
|
||||||
const char *device;
|
|
||||||
|
|
||||||
device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */
|
|
||||||
if ( device == NULL ) {
|
|
||||||
switch (channels) {
|
|
||||||
case 6:
|
|
||||||
device = "plug:surround51";
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
device = "plug:surround40";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
device = "default";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
int available;
|
|
||||||
int status;
|
|
||||||
snd_pcm_t *handle;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
if (LoadALSALibrary() < 0) {
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(2), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
|
||||||
if ( status >= 0 ) {
|
|
||||||
available = 1;
|
|
||||||
SDL_NAME(snd_pcm_close)(handle);
|
|
||||||
}
|
|
||||||
UnloadALSALibrary();
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
UnloadALSALibrary();
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
LoadALSALibrary();
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = ALSA_OpenAudio;
|
|
||||||
this->WaitAudio = ALSA_WaitAudio;
|
|
||||||
this->PlayAudio = ALSA_PlayAudio;
|
|
||||||
this->GetAudioBuf = ALSA_GetAudioBuf;
|
|
||||||
this->CloseAudio = ALSA_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap ALSA_bootstrap = {
|
|
||||||
DRIVER_NAME, "ALSA PCM audio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void ALSA_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* We're in blocking mode, so there's nothing to do here */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* http://bugzilla.libsdl.org/show_bug.cgi?id=110
|
|
||||||
* "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
|
|
||||||
* and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
|
|
||||||
*/
|
|
||||||
#define SWIZ6(T) \
|
|
||||||
T *ptr = (T *) mixbuf; \
|
|
||||||
Uint32 i; \
|
|
||||||
for (i = 0; i < this->spec.samples; i++, ptr += 6) { \
|
|
||||||
T tmp; \
|
|
||||||
tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
|
|
||||||
tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
|
|
||||||
}
|
|
||||||
|
|
||||||
static __inline__ void swizzle_alsa_channels_6_64bit(_THIS) { SWIZ6(Uint64); }
|
|
||||||
static __inline__ void swizzle_alsa_channels_6_32bit(_THIS) { SWIZ6(Uint32); }
|
|
||||||
static __inline__ void swizzle_alsa_channels_6_16bit(_THIS) { SWIZ6(Uint16); }
|
|
||||||
static __inline__ void swizzle_alsa_channels_6_8bit(_THIS) { SWIZ6(Uint8); }
|
|
||||||
|
|
||||||
#undef SWIZ6
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Called right before feeding this->mixbuf to the hardware. Swizzle channels
|
|
||||||
* from Windows/Mac order to the format alsalib will want.
|
|
||||||
*/
|
|
||||||
static __inline__ void swizzle_alsa_channels(_THIS)
|
|
||||||
{
|
|
||||||
if (this->spec.channels == 6) {
|
|
||||||
const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
|
|
||||||
if (fmtsize == 16)
|
|
||||||
swizzle_alsa_channels_6_16bit(this);
|
|
||||||
else if (fmtsize == 8)
|
|
||||||
swizzle_alsa_channels_6_8bit(this);
|
|
||||||
else if (fmtsize == 32)
|
|
||||||
swizzle_alsa_channels_6_32bit(this);
|
|
||||||
else if (fmtsize == 64)
|
|
||||||
swizzle_alsa_channels_6_64bit(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* !!! FIXME: update this for 7.1 if needed, later. */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void ALSA_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
snd_pcm_uframes_t frames_left;
|
|
||||||
const Uint8 *sample_buf = (const Uint8 *) mixbuf;
|
|
||||||
const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) * this->spec.channels;
|
|
||||||
|
|
||||||
swizzle_alsa_channels(this);
|
|
||||||
|
|
||||||
frames_left = ((snd_pcm_uframes_t) this->spec.samples);
|
|
||||||
|
|
||||||
while ( frames_left > 0 && this->enabled ) {
|
|
||||||
/* This works, but needs more testing before going live */
|
|
||||||
/*SDL_NAME(snd_pcm_wait)(pcm_handle, -1);*/
|
|
||||||
|
|
||||||
status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, frames_left);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
if ( status == -EAGAIN ) {
|
|
||||||
/* Apparently snd_pcm_recover() doesn't handle this case - does it assume snd_pcm_wait() above? */
|
|
||||||
SDL_Delay(1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
status = SDL_NAME(snd_pcm_recover)(pcm_handle, status, 0);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
/* Hmm, not much we can do - abort */
|
|
||||||
fprintf(stderr, "ALSA write failed (unrecoverable): %s\n", SDL_NAME(snd_strerror)(status));
|
|
||||||
this->enabled = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
sample_buf += status * frame_size;
|
|
||||||
frames_left -= status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *ALSA_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ALSA_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( pcm_handle ) {
|
|
||||||
SDL_NAME(snd_pcm_drain)(pcm_handle);
|
|
||||||
SDL_NAME(snd_pcm_close)(pcm_handle);
|
|
||||||
pcm_handle = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ALSA_finalize_hardware(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *hwparams, int override)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
snd_pcm_uframes_t bufsize;
|
|
||||||
|
|
||||||
/* "set" the hardware with the desired parameters */
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get samples for the actual buffer size */
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
if ( !override && bufsize != spec->samples * 2 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIXME: Is this safe to do? */
|
|
||||||
spec->samples = bufsize / 2;
|
|
||||||
|
|
||||||
/* This is useful for debugging */
|
|
||||||
if ( getenv("SDL_AUDIO_ALSA_DEBUG") ) {
|
|
||||||
snd_pcm_uframes_t persize = 0;
|
|
||||||
unsigned int periods = 0;
|
|
||||||
|
|
||||||
SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, NULL);
|
|
||||||
SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, NULL);
|
|
||||||
|
|
||||||
fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ALSA_set_period_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
|
|
||||||
{
|
|
||||||
const char *env;
|
|
||||||
int status;
|
|
||||||
snd_pcm_hw_params_t *hwparams;
|
|
||||||
snd_pcm_uframes_t frames;
|
|
||||||
unsigned int periods;
|
|
||||||
|
|
||||||
/* Copy the hardware parameters for this setup */
|
|
||||||
snd_pcm_hw_params_alloca(&hwparams);
|
|
||||||
SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
|
|
||||||
|
|
||||||
if ( !override ) {
|
|
||||||
env = getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
|
|
||||||
if ( env ) {
|
|
||||||
override = SDL_atoi(env);
|
|
||||||
if ( override == 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frames = spec->samples;
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
periods = 2;
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ALSA_finalize_hardware(this, spec, hwparams, override);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ALSA_set_buffer_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
|
|
||||||
{
|
|
||||||
const char *env;
|
|
||||||
int status;
|
|
||||||
snd_pcm_hw_params_t *hwparams;
|
|
||||||
snd_pcm_uframes_t frames;
|
|
||||||
|
|
||||||
/* Copy the hardware parameters for this setup */
|
|
||||||
snd_pcm_hw_params_alloca(&hwparams);
|
|
||||||
SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
|
|
||||||
|
|
||||||
if ( !override ) {
|
|
||||||
env = getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
|
|
||||||
if ( env ) {
|
|
||||||
override = SDL_atoi(env);
|
|
||||||
if ( override == 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
frames = spec->samples * 2;
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ALSA_finalize_hardware(this, spec, hwparams, override);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
snd_pcm_hw_params_t *hwparams;
|
|
||||||
snd_pcm_sw_params_t *swparams;
|
|
||||||
snd_pcm_format_t format;
|
|
||||||
unsigned int rate;
|
|
||||||
unsigned int channels;
|
|
||||||
Uint16 test_format;
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
/* Name of device should depend on # channels in spec */
|
|
||||||
status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
|
|
||||||
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Figure out what the hardware is capable of */
|
|
||||||
snd_pcm_hw_params_alloca(&hwparams);
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* SDL only uses interleaved sample output */
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try for a closest match on audio format */
|
|
||||||
status = -1;
|
|
||||||
for ( test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
test_format && (status < 0); ) {
|
|
||||||
switch ( test_format ) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
format = SND_PCM_FORMAT_U8;
|
|
||||||
break;
|
|
||||||
case AUDIO_S8:
|
|
||||||
format = SND_PCM_FORMAT_S8;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
format = SND_PCM_FORMAT_S16_LE;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
format = SND_PCM_FORMAT_S16_BE;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
format = SND_PCM_FORMAT_U16_LE;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
format = SND_PCM_FORMAT_U16_BE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
format = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( format != 0 ) {
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format);
|
|
||||||
}
|
|
||||||
if ( status < 0 ) {
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't find any hardware audio formats");
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
/* Set the number of channels */
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels);
|
|
||||||
channels = spec->channels;
|
|
||||||
if ( status < 0 ) {
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams, &channels);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set audio channels");
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->channels = channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the audio rate */
|
|
||||||
rate = spec->freq;
|
|
||||||
|
|
||||||
status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, &rate, NULL);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->freq = rate;
|
|
||||||
|
|
||||||
/* Set the buffer size, in samples */
|
|
||||||
if ( ALSA_set_period_size(this, spec, hwparams, 0) < 0 &&
|
|
||||||
ALSA_set_buffer_size(this, spec, hwparams, 0) < 0 ) {
|
|
||||||
/* Failed to set desired buffer size, do the best you can... */
|
|
||||||
if ( ALSA_set_period_size(this, spec, hwparams, 1) < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the software parameters */
|
|
||||||
snd_pcm_sw_params_alloca(&swparams);
|
|
||||||
status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
status = SDL_NAME(snd_pcm_sw_params_set_avail_min)(pcm_handle, swparams, spec->samples);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set minimum available samples: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
|
|
||||||
if ( status < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixlen = spec->size;
|
|
||||||
mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
ALSA_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* Switch to blocking mode for playback */
|
|
||||||
SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _ALSA_PCM_audio_h
|
|
||||||
#define _ALSA_PCM_audio_h
|
|
||||||
|
|
||||||
#include <alsa/asoundlib.h>
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The audio device handle */
|
|
||||||
snd_pcm_t *pcm_handle;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
int mixlen;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define pcm_handle (this->hidden->pcm_handle)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define mixlen (this->hidden->mixlen)
|
|
||||||
|
|
||||||
#endif /* _ALSA_PCM_audio_h */
|
|
||||||
|
|
@ -1,362 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#ifdef HAVE_SIGNAL_H
|
|
||||||
#include <signal.h>
|
|
||||||
#endif
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_artsaudio.h"
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
|
|
||||||
#include "SDL_name.h"
|
|
||||||
#include "SDL_loadso.h"
|
|
||||||
#else
|
|
||||||
#define SDL_NAME(X) X
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The tag name used by artsc audio */
|
|
||||||
#define ARTS_DRIVER_NAME "arts"
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void ARTS_WaitAudio(_THIS);
|
|
||||||
static void ARTS_PlayAudio(_THIS);
|
|
||||||
static Uint8 *ARTS_GetAudioBuf(_THIS);
|
|
||||||
static void ARTS_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_ARTS_DYNAMIC
|
|
||||||
|
|
||||||
static const char *arts_library = SDL_AUDIO_DRIVER_ARTS_DYNAMIC;
|
|
||||||
static void *arts_handle = NULL;
|
|
||||||
static int arts_loaded = 0;
|
|
||||||
|
|
||||||
static int (*SDL_NAME(arts_init))(void);
|
|
||||||
static void (*SDL_NAME(arts_free))(void);
|
|
||||||
static arts_stream_t (*SDL_NAME(arts_play_stream))(int rate, int bits, int channels, const char *name);
|
|
||||||
static int (*SDL_NAME(arts_stream_set))(arts_stream_t s, arts_parameter_t param, int value);
|
|
||||||
static int (*SDL_NAME(arts_stream_get))(arts_stream_t s, arts_parameter_t param);
|
|
||||||
static int (*SDL_NAME(arts_write))(arts_stream_t s, const void *buffer, int count);
|
|
||||||
static void (*SDL_NAME(arts_close_stream))(arts_stream_t s);
|
|
||||||
static int (*SDL_NAME(arts_suspend))(void);
|
|
||||||
static int (*SDL_NAME(arts_suspended))(void);
|
|
||||||
static const char *(*SDL_NAME(arts_error_text))(int errorcode);
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
const char *name;
|
|
||||||
void **func;
|
|
||||||
} arts_functions[] = {
|
|
||||||
{ "arts_init", (void **)&SDL_NAME(arts_init) },
|
|
||||||
{ "arts_free", (void **)&SDL_NAME(arts_free) },
|
|
||||||
{ "arts_play_stream", (void **)&SDL_NAME(arts_play_stream) },
|
|
||||||
{ "arts_stream_set", (void **)&SDL_NAME(arts_stream_set) },
|
|
||||||
{ "arts_stream_get", (void **)&SDL_NAME(arts_stream_get) },
|
|
||||||
{ "arts_write", (void **)&SDL_NAME(arts_write) },
|
|
||||||
{ "arts_close_stream", (void **)&SDL_NAME(arts_close_stream) },
|
|
||||||
{ "arts_suspend", (void **)&SDL_NAME(arts_suspend) },
|
|
||||||
{ "arts_suspended", (void **)&SDL_NAME(arts_suspended) },
|
|
||||||
{ "arts_error_text", (void **)&SDL_NAME(arts_error_text) },
|
|
||||||
};
|
|
||||||
|
|
||||||
static void UnloadARTSLibrary()
|
|
||||||
{
|
|
||||||
if ( arts_loaded ) {
|
|
||||||
SDL_UnloadObject(arts_handle);
|
|
||||||
arts_handle = NULL;
|
|
||||||
arts_loaded = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int LoadARTSLibrary(void)
|
|
||||||
{
|
|
||||||
int i, retval = -1;
|
|
||||||
|
|
||||||
arts_handle = SDL_LoadObject(arts_library);
|
|
||||||
if ( arts_handle ) {
|
|
||||||
arts_loaded = 1;
|
|
||||||
retval = 0;
|
|
||||||
for ( i=0; i<SDL_arraysize(arts_functions); ++i ) {
|
|
||||||
*arts_functions[i].func = SDL_LoadFunction(arts_handle, arts_functions[i].name);
|
|
||||||
if ( !*arts_functions[i].func ) {
|
|
||||||
retval = -1;
|
|
||||||
UnloadARTSLibrary();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void UnloadARTSLibrary()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int LoadARTSLibrary(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_AUDIO_DRIVER_ARTS_DYNAMIC */
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int ARTS_Suspend(void)
|
|
||||||
{
|
|
||||||
const Uint32 abortms = SDL_GetTicks() + 3000; /* give up after 3 secs */
|
|
||||||
while ( (!SDL_NAME(arts_suspended)()) && (SDL_GetTicks() < abortms) ) {
|
|
||||||
if ( SDL_NAME(arts_suspend)() ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SDL_NAME(arts_suspended)();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
int available = 0;
|
|
||||||
|
|
||||||
if ( LoadARTSLibrary() < 0 ) {
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
if ( SDL_NAME(arts_init)() == 0 ) {
|
|
||||||
if ( ARTS_Suspend() ) {
|
|
||||||
/* Play a stream so aRts doesn't crash */
|
|
||||||
arts_stream_t stream2;
|
|
||||||
stream2=SDL_NAME(arts_play_stream)(44100, 16, 2, "SDL");
|
|
||||||
SDL_NAME(arts_write)(stream2, "", 0);
|
|
||||||
SDL_NAME(arts_close_stream)(stream2);
|
|
||||||
available = 1;
|
|
||||||
}
|
|
||||||
SDL_NAME(arts_free)();
|
|
||||||
}
|
|
||||||
UnloadARTSLibrary();
|
|
||||||
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
UnloadARTSLibrary();
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
LoadARTSLibrary();
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
stream = 0;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = ARTS_OpenAudio;
|
|
||||||
this->WaitAudio = ARTS_WaitAudio;
|
|
||||||
this->PlayAudio = ARTS_PlayAudio;
|
|
||||||
this->GetAudioBuf = ARTS_GetAudioBuf;
|
|
||||||
this->CloseAudio = ARTS_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap ARTS_bootstrap = {
|
|
||||||
ARTS_DRIVER_NAME, "Analog Realtime Synthesizer",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void ARTS_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
Sint32 ticks;
|
|
||||||
|
|
||||||
/* Check to see if the thread-parent process is still alive */
|
|
||||||
{ static int cnt = 0;
|
|
||||||
/* Note that this only works with thread implementations
|
|
||||||
that use a different process id for each thread.
|
|
||||||
*/
|
|
||||||
if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
|
|
||||||
if ( kill(parent, 0) < 0 ) {
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use timer for general audio synchronization */
|
|
||||||
ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
|
|
||||||
if ( ticks > 0 ) {
|
|
||||||
SDL_Delay(ticks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ARTS_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
int written;
|
|
||||||
|
|
||||||
/* Write the audio data */
|
|
||||||
written = SDL_NAME(arts_write)(stream, mixbuf, mixlen);
|
|
||||||
|
|
||||||
/* If timer synchronization is enabled, set the next write frame */
|
|
||||||
if ( frame_ticks ) {
|
|
||||||
next_frame += frame_ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
|
||||||
if ( written < 0 ) {
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *ARTS_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ARTS_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( stream ) {
|
|
||||||
SDL_NAME(arts_close_stream)(stream);
|
|
||||||
stream = 0;
|
|
||||||
}
|
|
||||||
SDL_NAME(arts_free)();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ARTS_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int bits, frag_spec;
|
|
||||||
Uint16 test_format, format;
|
|
||||||
int error_code;
|
|
||||||
|
|
||||||
/* Reset the timer synchronization flag */
|
|
||||||
frame_ticks = 0.0;
|
|
||||||
|
|
||||||
mixbuf = NULL;
|
|
||||||
|
|
||||||
/* Try for a closest match on audio format */
|
|
||||||
format = 0;
|
|
||||||
bits = 0;
|
|
||||||
for ( test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
! format && test_format; ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
|
||||||
#endif
|
|
||||||
switch ( test_format ) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
bits = 8;
|
|
||||||
format = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
bits = 16;
|
|
||||||
format = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
format = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( ! format ) {
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( format == 0 ) {
|
|
||||||
SDL_SetError("Couldn't find any hardware audio formats");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
error_code = SDL_NAME(arts_init)();
|
|
||||||
if ( error_code != 0 ) {
|
|
||||||
SDL_SetError("Unable to initialize ARTS: %s", SDL_NAME(arts_error_text)(error_code));
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
if ( ! ARTS_Suspend() ) {
|
|
||||||
SDL_SetError("ARTS can not open audio device");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
stream = SDL_NAME(arts_play_stream)(spec->freq, bits, spec->channels, "SDL");
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Determine the power of two of the fragment size */
|
|
||||||
for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
|
|
||||||
if ( (0x01<<frag_spec) != spec->size ) {
|
|
||||||
SDL_SetError("Fragment size must be a power of two");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
frag_spec |= 0x00020000; /* two fragments, for low latency */
|
|
||||||
|
|
||||||
#ifdef ARTS_P_PACKET_SETTINGS
|
|
||||||
SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SETTINGS, frag_spec);
|
|
||||||
#else
|
|
||||||
SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_SIZE, frag_spec&0xffff);
|
|
||||||
SDL_NAME(arts_stream_set)(stream, ARTS_P_PACKET_COUNT, frag_spec>>16);
|
|
||||||
#endif
|
|
||||||
spec->size = SDL_NAME(arts_stream_get)(stream, ARTS_P_PACKET_SIZE);
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixlen = spec->size;
|
|
||||||
mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* Get the parent process id (we're the parent of the audio thread) */
|
|
||||||
parent = getpid();
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,60 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_artscaudio_h
|
|
||||||
#define _SDL_artscaudio_h
|
|
||||||
|
|
||||||
#include <artsc.h>
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The stream descriptor for the audio device */
|
|
||||||
arts_stream_t stream;
|
|
||||||
|
|
||||||
/* The parent process id, to detect when application quits */
|
|
||||||
pid_t parent;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
int mixlen;
|
|
||||||
|
|
||||||
/* Support for audio timing using a timer, in addition to select() */
|
|
||||||
float frame_ticks;
|
|
||||||
float next_frame;
|
|
||||||
};
|
|
||||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define stream (this->hidden->stream)
|
|
||||||
#define parent (this->hidden->parent)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define mixlen (this->hidden->mixlen)
|
|
||||||
#define frame_ticks (this->hidden->frame_ticks)
|
|
||||||
#define next_frame (this->hidden->next_frame)
|
|
||||||
|
|
||||||
#endif /* _SDL_artscaudio_h */
|
|
||||||
|
|
||||||
|
|
@ -1,225 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to the audio stream on BeOS */
|
|
||||||
|
|
||||||
#include <SoundPlayer.h>
|
|
||||||
|
|
||||||
#include "../../main/beos/SDL_BeApp.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
#include "../../thread/beos/SDL_systhread_c.h"
|
|
||||||
#include "SDL_beaudio.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int BE_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void BE_WaitAudio(_THIS);
|
|
||||||
static void BE_PlayAudio(_THIS);
|
|
||||||
static Uint8 *BE_GetAudioBuf(_THIS);
|
|
||||||
static void BE_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *device;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
device = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( device ) {
|
|
||||||
SDL_memset(device, 0, (sizeof *device));
|
|
||||||
device->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *device->hidden));
|
|
||||||
}
|
|
||||||
if ( (device == NULL) || (device->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( device ) {
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(device->hidden, 0, (sizeof *device->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
device->OpenAudio = BE_OpenAudio;
|
|
||||||
device->WaitAudio = BE_WaitAudio;
|
|
||||||
device->PlayAudio = BE_PlayAudio;
|
|
||||||
device->GetAudioBuf = BE_GetAudioBuf;
|
|
||||||
device->CloseAudio = BE_CloseAudio;
|
|
||||||
|
|
||||||
device->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return device;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap BAUDIO_bootstrap = {
|
|
||||||
"baudio", "BeOS BSoundPlayer",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The BeOS callback for handling the audio buffer */
|
|
||||||
static void FillSound(void *device, void *stream, size_t len,
|
|
||||||
const media_raw_audio_format &format)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *audio = (SDL_AudioDevice *)device;
|
|
||||||
|
|
||||||
/* Silence the buffer, since it's ours */
|
|
||||||
SDL_memset(stream, audio->spec.silence, len);
|
|
||||||
|
|
||||||
/* Only do soemthing if audio is enabled */
|
|
||||||
if ( ! audio->enabled )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( ! audio->paused ) {
|
|
||||||
if ( audio->convert.needed ) {
|
|
||||||
SDL_mutexP(audio->mixer_lock);
|
|
||||||
(*audio->spec.callback)(audio->spec.userdata,
|
|
||||||
(Uint8 *)audio->convert.buf,audio->convert.len);
|
|
||||||
SDL_mutexV(audio->mixer_lock);
|
|
||||||
SDL_ConvertAudio(&audio->convert);
|
|
||||||
SDL_memcpy(stream,audio->convert.buf,audio->convert.len_cvt);
|
|
||||||
} else {
|
|
||||||
SDL_mutexP(audio->mixer_lock);
|
|
||||||
(*audio->spec.callback)(audio->spec.userdata,
|
|
||||||
(Uint8 *)stream, len);
|
|
||||||
SDL_mutexV(audio->mixer_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dummy functions -- we don't use thread-based audio */
|
|
||||||
void BE_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void BE_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Uint8 *BE_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BE_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( audio_obj ) {
|
|
||||||
audio_obj->Stop();
|
|
||||||
delete audio_obj;
|
|
||||||
audio_obj = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Quit the Be Application, if there's nothing left to do */
|
|
||||||
SDL_QuitBeApp();
|
|
||||||
}
|
|
||||||
|
|
||||||
int BE_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int valid_datatype = 0;
|
|
||||||
media_raw_audio_format format;
|
|
||||||
Uint16 test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
|
|
||||||
/* Parse the audio format and fill the Be raw audio format */
|
|
||||||
memset(&format, '\0', sizeof (media_raw_audio_format));
|
|
||||||
format.byte_order = B_MEDIA_LITTLE_ENDIAN;
|
|
||||||
format.frame_rate = (float) spec->freq;
|
|
||||||
format.channel_count = spec->channels; /* !!! FIXME: support > 2? */
|
|
||||||
while ((!valid_datatype) && (test_format)) {
|
|
||||||
valid_datatype = 1;
|
|
||||||
spec->format = test_format;
|
|
||||||
switch (test_format) {
|
|
||||||
case AUDIO_S8:
|
|
||||||
format.format = media_raw_audio_format::B_AUDIO_CHAR;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDIO_U8:
|
|
||||||
format.format = media_raw_audio_format::B_AUDIO_UCHAR;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
format.format = media_raw_audio_format::B_AUDIO_SHORT;
|
|
||||||
format.byte_order = B_MEDIA_BIG_ENDIAN;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
valid_datatype = 0;
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid_datatype) { /* shouldn't happen, but just in case... */
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the Be Application, if it's not already started */
|
|
||||||
if (SDL_InitBeApp() < 0) {
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
format.buffer_size = spec->samples;
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Subscribe to the audio stream (creates a new thread) */
|
|
||||||
{ sigset_t omask;
|
|
||||||
SDL_MaskSignals(&omask);
|
|
||||||
audio_obj = new BSoundPlayer(&format, "SDL Audio", FillSound,
|
|
||||||
NULL, _this);
|
|
||||||
SDL_UnmaskSignals(&omask);
|
|
||||||
}
|
|
||||||
if ( audio_obj->Start() == B_NO_ERROR ) {
|
|
||||||
audio_obj->SetHasData(true);
|
|
||||||
} else {
|
|
||||||
SDL_SetError("Unable to start Be audio");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We're running! */
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
}; /* Extern C */
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_lowaudio_h
|
|
||||||
#define _SDL_lowaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *_this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
BSoundPlayer *audio_obj;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define audio_obj (_this->hidden->audio_obj)
|
|
||||||
|
|
||||||
#endif /* _SDL_lowaudio_h */
|
|
||||||
|
|
@ -1,404 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Driver for native OpenBSD/NetBSD audio(4).
|
|
||||||
* vedge@vedge.com.ar.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/audioio.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_bsdaudio.h"
|
|
||||||
|
|
||||||
/* The tag name used by NetBSD/OpenBSD audio */
|
|
||||||
#ifdef __NetBSD__
|
|
||||||
#define BSD_AUDIO_DRIVER_NAME "netbsd"
|
|
||||||
#define BSD_AUDIO_DRIVER_DESC "Native NetBSD audio"
|
|
||||||
#else
|
|
||||||
#define BSD_AUDIO_DRIVER_NAME "openbsd"
|
|
||||||
#define BSD_AUDIO_DRIVER_DESC "Native OpenBSD audio"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Open the audio device for playback, and don't block if busy */
|
|
||||||
/* #define USE_BLOCKING_WRITES */
|
|
||||||
|
|
||||||
/* Use timer for synchronization */
|
|
||||||
/* #define USE_TIMER_SYNC */
|
|
||||||
|
|
||||||
/* #define DEBUG_AUDIO */
|
|
||||||
/* #define DEBUG_AUDIO_STREAM */
|
|
||||||
|
|
||||||
#ifdef USE_BLOCKING_WRITES
|
|
||||||
#define OPEN_FLAGS O_WRONLY
|
|
||||||
#else
|
|
||||||
#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static void OBSD_WaitAudio(_THIS);
|
|
||||||
static int OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void OBSD_PlayAudio(_THIS);
|
|
||||||
static Uint8 *OBSD_GetAudioBuf(_THIS);
|
|
||||||
static void OBSD_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
static void OBSD_Status(_THIS);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int
|
|
||||||
Audio_Available(void)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
int available;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
|
|
||||||
if(fd >= 0) {
|
|
||||||
available = 1;
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice
|
|
||||||
*Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice*)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if(this) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden =
|
|
||||||
(struct SDL_PrivateAudioData*)SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if((this == NULL) || (this->hidden == NULL)) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if(this) SDL_free(this);
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
audio_fd = -1;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = OBSD_OpenAudio;
|
|
||||||
this->WaitAudio = OBSD_WaitAudio;
|
|
||||||
this->PlayAudio = OBSD_PlayAudio;
|
|
||||||
this->GetAudioBuf = OBSD_GetAudioBuf;
|
|
||||||
this->CloseAudio = OBSD_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap BSD_AUDIO_bootstrap = {
|
|
||||||
BSD_AUDIO_DRIVER_NAME, BSD_AUDIO_DRIVER_DESC,
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void
|
|
||||||
OBSD_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
|
|
||||||
/* See if we need to use timed audio synchronization */
|
|
||||||
if ( frame_ticks ) {
|
|
||||||
/* Use timer for general audio synchronization */
|
|
||||||
Sint32 ticks;
|
|
||||||
|
|
||||||
ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
|
|
||||||
if ( ticks > 0 ) {
|
|
||||||
SDL_Delay(ticks);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Use select() for audio synchronization */
|
|
||||||
fd_set fdset;
|
|
||||||
struct timeval timeout;
|
|
||||||
|
|
||||||
FD_ZERO(&fdset);
|
|
||||||
FD_SET(audio_fd, &fdset);
|
|
||||||
timeout.tv_sec = 10;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Waiting for audio to get ready\n");
|
|
||||||
#endif
|
|
||||||
if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
|
|
||||||
const char *message =
|
|
||||||
"Audio timeout - buggy audio driver? (disabled)";
|
|
||||||
/* In general we should never print to the screen,
|
|
||||||
but in this case we have no other way of letting
|
|
||||||
the user know what happened.
|
|
||||||
*/
|
|
||||||
fprintf(stderr, "SDL: %s\n", message);
|
|
||||||
this->enabled = 0;
|
|
||||||
/* Don't try to close - may hang */
|
|
||||||
audio_fd = -1;
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Done disabling audio\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Ready!\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif /* !USE_BLOCKING_WRITES */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
OBSD_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
int written, p=0;
|
|
||||||
|
|
||||||
/* Write the audio data, checking for EAGAIN on broken audio drivers */
|
|
||||||
do {
|
|
||||||
written = write(audio_fd, &mixbuf[p], mixlen-p);
|
|
||||||
if (written>0)
|
|
||||||
p += written;
|
|
||||||
if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR)
|
|
||||||
{
|
|
||||||
/* Non recoverable error has occurred. It should be reported!!! */
|
|
||||||
perror("audio");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( p < written || ((written < 0) && ((errno == 0) || (errno == EAGAIN))) ) {
|
|
||||||
SDL_Delay(1); /* Let a little CPU time go by */
|
|
||||||
}
|
|
||||||
} while ( p < written );
|
|
||||||
|
|
||||||
/* If timer synchronization is enabled, set the next write frame */
|
|
||||||
if ( frame_ticks ) {
|
|
||||||
next_frame += frame_ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
|
||||||
if ( written < 0 ) {
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8
|
|
||||||
*OBSD_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
OBSD_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if(mixbuf != NULL) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if(audio_fd >= 0) {
|
|
||||||
close(audio_fd);
|
|
||||||
audio_fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
void
|
|
||||||
OBSD_Status(_THIS)
|
|
||||||
{
|
|
||||||
audio_info_t info;
|
|
||||||
|
|
||||||
if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) {
|
|
||||||
fprintf(stderr,"AUDIO_GETINFO failed.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr,"\n"
|
|
||||||
"[play/record info]\n"
|
|
||||||
"buffer size : %d bytes\n"
|
|
||||||
"sample rate : %i Hz\n"
|
|
||||||
"channels : %i\n"
|
|
||||||
"precision : %i-bit\n"
|
|
||||||
"encoding : 0x%x\n"
|
|
||||||
"seek : %i\n"
|
|
||||||
"sample count : %i\n"
|
|
||||||
"EOF count : %i\n"
|
|
||||||
"paused : %s\n"
|
|
||||||
"error occured : %s\n"
|
|
||||||
"waiting : %s\n"
|
|
||||||
"active : %s\n"
|
|
||||||
"",
|
|
||||||
info.play.buffer_size,
|
|
||||||
info.play.sample_rate,
|
|
||||||
info.play.channels,
|
|
||||||
info.play.precision,
|
|
||||||
info.play.encoding,
|
|
||||||
info.play.seek,
|
|
||||||
info.play.samples,
|
|
||||||
info.play.eof,
|
|
||||||
info.play.pause ? "yes" : "no",
|
|
||||||
info.play.error ? "yes" : "no",
|
|
||||||
info.play.waiting ? "yes" : "no",
|
|
||||||
info.play.active ? "yes": "no");
|
|
||||||
|
|
||||||
fprintf(stderr,"\n"
|
|
||||||
"[audio info]\n"
|
|
||||||
"monitor_gain : %i\n"
|
|
||||||
"hw block size : %d bytes\n"
|
|
||||||
"hi watermark : %i\n"
|
|
||||||
"lo watermark : %i\n"
|
|
||||||
"audio mode : %s\n"
|
|
||||||
"",
|
|
||||||
info.monitor_gain,
|
|
||||||
info.blocksize,
|
|
||||||
info.hiwat, info.lowat,
|
|
||||||
(info.mode == AUMODE_PLAY) ? "PLAY"
|
|
||||||
: (info.mode = AUMODE_RECORD) ? "RECORD"
|
|
||||||
: (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL"
|
|
||||||
: "?"));
|
|
||||||
}
|
|
||||||
#endif /* DEBUG_AUDIO */
|
|
||||||
|
|
||||||
static int
|
|
||||||
OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
char audiodev[64];
|
|
||||||
Uint16 format;
|
|
||||||
audio_info_t info;
|
|
||||||
|
|
||||||
AUDIO_INITINFO(&info);
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
#ifdef USE_TIMER_SYNC
|
|
||||||
frame_ticks = 0.0;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
|
|
||||||
if(audio_fd < 0) {
|
|
||||||
SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set to play mode */
|
|
||||||
info.mode = AUMODE_PLAY;
|
|
||||||
if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) {
|
|
||||||
SDL_SetError("Couldn't put device into play mode");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
mixbuf = NULL;
|
|
||||||
AUDIO_INITINFO(&info);
|
|
||||||
for (format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
format; format = SDL_NextAudioFormat())
|
|
||||||
{
|
|
||||||
switch(format) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
info.play.encoding = AUDIO_ENCODING_ULINEAR;
|
|
||||||
info.play.precision = 8;
|
|
||||||
break;
|
|
||||||
case AUDIO_S8:
|
|
||||||
info.play.encoding = AUDIO_ENCODING_SLINEAR;
|
|
||||||
info.play.precision = 8;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
|
|
||||||
info.play.precision = 16;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
|
|
||||||
info.play.precision = 16;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
info.play.encoding = AUDIO_ENCODING_ULINEAR_LE;
|
|
||||||
info.play.precision = 16;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
info.play.encoding = AUDIO_ENCODING_ULINEAR_BE;
|
|
||||||
info.play.precision = 16;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (ioctl(audio_fd, AUDIO_SETINFO, &info) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!format) {
|
|
||||||
SDL_SetError("No supported encoding for 0x%x", spec->format);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
spec->format = format;
|
|
||||||
|
|
||||||
AUDIO_INITINFO(&info);
|
|
||||||
info.play.channels = spec->channels;
|
|
||||||
if (ioctl(audio_fd, AUDIO_SETINFO, &info) == -1)
|
|
||||||
spec->channels = 1;
|
|
||||||
AUDIO_INITINFO(&info);
|
|
||||||
info.play.sample_rate = spec->freq;
|
|
||||||
info.blocksize = spec->size;
|
|
||||||
info.hiwat = 5;
|
|
||||||
info.lowat = 3;
|
|
||||||
(void)ioctl(audio_fd, AUDIO_SETINFO, &info);
|
|
||||||
(void)ioctl(audio_fd, AUDIO_GETINFO, &info);
|
|
||||||
spec->freq = info.play.sample_rate;
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixlen = spec->size;
|
|
||||||
mixbuf = (Uint8*)SDL_AllocAudioMem(mixlen);
|
|
||||||
if(mixbuf == NULL) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* Get the parent process id (we're the parent of the audio thread) */
|
|
||||||
parent = getpid();
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
OBSD_Status(this);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_openbsdaudio_h
|
|
||||||
#define _SDL_openbsdaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData
|
|
||||||
{
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
int audio_fd;
|
|
||||||
|
|
||||||
/* The parent process id, to detect when application quits */
|
|
||||||
pid_t parent;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
int mixlen;
|
|
||||||
|
|
||||||
/* Support for audio timing using a timer, in addition to select() */
|
|
||||||
float frame_ticks;
|
|
||||||
float next_frame;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define audio_fd (this->hidden->audio_fd)
|
|
||||||
#define parent (this->hidden->parent)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define mixlen (this->hidden->mixlen)
|
|
||||||
#define frame_ticks (this->hidden->frame_ticks)
|
|
||||||
#define next_frame (this->hidden->next_frame)
|
|
||||||
|
|
||||||
#endif /* _SDL_openbsdaudio_h */
|
|
||||||
|
|
@ -1,441 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "SDL_dart.h"
|
|
||||||
|
|
||||||
// Buffer states:
|
|
||||||
#define BUFFER_EMPTY 0
|
|
||||||
#define BUFFER_USED 1
|
|
||||||
|
|
||||||
typedef struct _tMixBufferDesc {
|
|
||||||
int iBufferUsage; // BUFFER_EMPTY or BUFFER_USED
|
|
||||||
SDL_AudioDevice *pSDLAudioDevice;
|
|
||||||
} tMixBufferDesc, *pMixBufferDesc;
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------
|
|
||||||
// DARTEventFunc
|
|
||||||
//
|
|
||||||
// This function is called by DART, when an event occures, like end of
|
|
||||||
// playback of a buffer, etc...
|
|
||||||
//---------------------------------------------------------------------
|
|
||||||
LONG APIENTRY DARTEventFunc(ULONG ulStatus,
|
|
||||||
PMCI_MIX_BUFFER pBuffer,
|
|
||||||
ULONG ulFlags)
|
|
||||||
{
|
|
||||||
if (ulFlags && MIX_WRITE_COMPLETE)
|
|
||||||
{ // Playback of buffer completed!
|
|
||||||
|
|
||||||
// Get pointer to buffer description
|
|
||||||
pMixBufferDesc pBufDesc;
|
|
||||||
|
|
||||||
if (pBuffer)
|
|
||||||
{
|
|
||||||
pBufDesc = (pMixBufferDesc) (*pBuffer).ulUserParm;
|
|
||||||
|
|
||||||
if (pBufDesc)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *pSDLAudioDevice = pBufDesc->pSDLAudioDevice;
|
|
||||||
// Set the buffer to be empty
|
|
||||||
pBufDesc->iBufferUsage = BUFFER_EMPTY;
|
|
||||||
// And notify DART feeder thread that it will have to work a bit.
|
|
||||||
if (pSDLAudioDevice)
|
|
||||||
DosPostEventSem(pSDLAudioDevice->hidden->hevAudioBufferPlayed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int DART_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
Uint16 test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
int valid_datatype = 0;
|
|
||||||
MCI_AMP_OPEN_PARMS AmpOpenParms;
|
|
||||||
MCI_GENERIC_PARMS GenericParms;
|
|
||||||
int iDeviceOrd = 0; // Default device to be used
|
|
||||||
int bOpenShared = 1; // Try opening it shared
|
|
||||||
int iBits = 16; // Default is 16 bits signed
|
|
||||||
int iFreq = 44100; // Default is 44KHz
|
|
||||||
int iChannels = 2; // Default is 2 channels (Stereo)
|
|
||||||
int iNumBufs = 2; // Number of audio buffers: 2
|
|
||||||
int iBufSize;
|
|
||||||
int iOpenMode;
|
|
||||||
int iSilence;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
// First thing is to try to open a given DART device!
|
|
||||||
SDL_memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS));
|
|
||||||
// pszDeviceType should contain the device type in low word, and device ordinal in high word!
|
|
||||||
AmpOpenParms.pszDeviceType = (PSZ) (MCI_DEVTYPE_AUDIO_AMPMIX | (iDeviceOrd << 16));
|
|
||||||
|
|
||||||
iOpenMode = MCI_WAIT | MCI_OPEN_TYPE_ID;
|
|
||||||
if (bOpenShared) iOpenMode |= MCI_OPEN_SHAREABLE;
|
|
||||||
|
|
||||||
rc = mciSendCommand( 0, MCI_OPEN,
|
|
||||||
iOpenMode,
|
|
||||||
(PVOID) &AmpOpenParms, 0);
|
|
||||||
if (rc!=MCIERR_SUCCESS) // No audio available??
|
|
||||||
return (-1);
|
|
||||||
// Save the device ID we got from DART!
|
|
||||||
// We will use this in the next calls!
|
|
||||||
iDeviceOrd = AmpOpenParms.usDeviceID;
|
|
||||||
|
|
||||||
// Determine the audio parameters from the AudioSpec
|
|
||||||
if (spec->channels > 2)
|
|
||||||
spec->channels = 2; // !!! FIXME: more than stereo support in OS/2?
|
|
||||||
|
|
||||||
while ((!valid_datatype) && (test_format)) {
|
|
||||||
spec->format = test_format;
|
|
||||||
valid_datatype = 1;
|
|
||||||
switch (test_format) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
// Unsigned 8 bit audio data
|
|
||||||
iSilence = 0x80;
|
|
||||||
iBits = 8;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
// Signed 16 bit audio data
|
|
||||||
iSilence = 0x00;
|
|
||||||
iBits = 16;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
valid_datatype = 0;
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid_datatype) { // shouldn't happen, but just in case...
|
|
||||||
// Close DART, and exit with error code!
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0);
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
iFreq = spec->freq;
|
|
||||||
iChannels = spec->channels;
|
|
||||||
/* Update the fragment size as size in bytes */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
iBufSize = spec->size;
|
|
||||||
|
|
||||||
// Now query this device if it supports the given freq/bits/channels!
|
|
||||||
SDL_memset(&(_this->hidden->MixSetupParms), 0, sizeof(MCI_MIXSETUP_PARMS));
|
|
||||||
_this->hidden->MixSetupParms.ulBitsPerSample = iBits;
|
|
||||||
_this->hidden->MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM;
|
|
||||||
_this->hidden->MixSetupParms.ulSamplesPerSec = iFreq;
|
|
||||||
_this->hidden->MixSetupParms.ulChannels = iChannels;
|
|
||||||
_this->hidden->MixSetupParms.ulFormatMode = MCI_PLAY;
|
|
||||||
_this->hidden->MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO;
|
|
||||||
_this->hidden->MixSetupParms.pmixEvent = DARTEventFunc;
|
|
||||||
rc = mciSendCommand (iDeviceOrd, MCI_MIXSETUP,
|
|
||||||
MCI_WAIT | MCI_MIXSETUP_QUERYMODE,
|
|
||||||
&(_this->hidden->MixSetupParms), 0);
|
|
||||||
if (rc!=MCIERR_SUCCESS)
|
|
||||||
{ // The device cannot handle this format!
|
|
||||||
// Close DART, and exit with error code!
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0);
|
|
||||||
SDL_SetError("Audio device doesn't support requested audio format");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
// The device can handle this format, so initialize!
|
|
||||||
rc = mciSendCommand(iDeviceOrd, MCI_MIXSETUP,
|
|
||||||
MCI_WAIT | MCI_MIXSETUP_INIT,
|
|
||||||
&(_this->hidden->MixSetupParms), 0);
|
|
||||||
if (rc!=MCIERR_SUCCESS)
|
|
||||||
{ // The device could not be opened!
|
|
||||||
// Close DART, and exit with error code!
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0);
|
|
||||||
SDL_SetError("Audio device could not be set up");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
// Ok, the device is initialized.
|
|
||||||
// Now we should allocate buffers. For this, we need a place where
|
|
||||||
// the buffer descriptors will be:
|
|
||||||
_this->hidden->pMixBuffers = (MCI_MIX_BUFFER *) SDL_malloc(sizeof(MCI_MIX_BUFFER)*iNumBufs);
|
|
||||||
if (!(_this->hidden->pMixBuffers))
|
|
||||||
{ // Not enough memory!
|
|
||||||
// Close DART, and exit with error code!
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0);
|
|
||||||
SDL_SetError("Not enough memory for audio buffer descriptors");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
// Now that we have the place for buffer list, we can ask DART for the
|
|
||||||
// buffers!
|
|
||||||
_this->hidden->BufferParms.ulNumBuffers = iNumBufs; // Number of buffers
|
|
||||||
_this->hidden->BufferParms.ulBufferSize = iBufSize; // each with this size
|
|
||||||
_this->hidden->BufferParms.pBufList = _this->hidden->pMixBuffers; // getting descriptorts into this list
|
|
||||||
// Allocate buffers!
|
|
||||||
rc = mciSendCommand(iDeviceOrd, MCI_BUFFER,
|
|
||||||
MCI_WAIT | MCI_ALLOCATE_MEMORY,
|
|
||||||
&(_this->hidden->BufferParms), 0);
|
|
||||||
if ((rc!=MCIERR_SUCCESS) || (iNumBufs != _this->hidden->BufferParms.ulNumBuffers) || (_this->hidden->BufferParms.ulBufferSize==0))
|
|
||||||
{ // Could not allocate memory!
|
|
||||||
// Close DART, and exit with error code!
|
|
||||||
SDL_free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL;
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0);
|
|
||||||
SDL_SetError("DART could not allocate buffers");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
// Ok, we have all the buffers allocated, let's mark them!
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i=0; i<iNumBufs; i++)
|
|
||||||
{
|
|
||||||
pMixBufferDesc pBufferDesc = (pMixBufferDesc) SDL_malloc(sizeof(tMixBufferDesc));;
|
|
||||||
// Check if this buffer was really allocated by DART
|
|
||||||
if ((!(_this->hidden->pMixBuffers[i].pBuffer)) || (!pBufferDesc))
|
|
||||||
{ // Wrong buffer!
|
|
||||||
// Close DART, and exit with error code!
|
|
||||||
// Free buffer descriptions
|
|
||||||
{ int j;
|
|
||||||
for (j=0; j<i; j++) SDL_free((void *)(_this->hidden->pMixBuffers[j].ulUserParm));
|
|
||||||
}
|
|
||||||
// and cleanup
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0);
|
|
||||||
SDL_free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL;
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0);
|
|
||||||
SDL_SetError("Error at internal buffer check");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
pBufferDesc->iBufferUsage = BUFFER_EMPTY;
|
|
||||||
pBufferDesc->pSDLAudioDevice = _this;
|
|
||||||
|
|
||||||
_this->hidden->pMixBuffers[i].ulBufferLength = _this->hidden->BufferParms.ulBufferSize;
|
|
||||||
_this->hidden->pMixBuffers[i].ulUserParm = (ULONG) pBufferDesc; // User parameter: Description of buffer
|
|
||||||
_this->hidden->pMixBuffers[i].ulFlags = 0; // Some stuff should be flagged here for DART, like end of
|
|
||||||
// audio data, but as we will continously send
|
|
||||||
// audio data, there will be no end.:)
|
|
||||||
SDL_memset(_this->hidden->pMixBuffers[i].pBuffer, iSilence, iBufSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_this->hidden->iNextFreeBuffer = 0;
|
|
||||||
_this->hidden->iLastPlayedBuf = -1;
|
|
||||||
// Create event semaphore
|
|
||||||
if (DosCreateEventSem(NULL, &(_this->hidden->hevAudioBufferPlayed), 0, FALSE)!=NO_ERROR)
|
|
||||||
{
|
|
||||||
// Could not create event semaphore!
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i=0; i<iNumBufs; i++) SDL_free((void *)(_this->hidden->pMixBuffers[i].ulUserParm));
|
|
||||||
}
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0);
|
|
||||||
SDL_free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL;
|
|
||||||
mciSendCommand(iDeviceOrd, MCI_CLOSE, MCI_WAIT, &GenericParms, 0);
|
|
||||||
SDL_SetError("Could not create event semaphore");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the new settings in global variables
|
|
||||||
_this->hidden->iCurrDeviceOrd = iDeviceOrd;
|
|
||||||
_this->hidden->iCurrFreq = iFreq;
|
|
||||||
_this->hidden->iCurrBits = iBits;
|
|
||||||
_this->hidden->iCurrChannels = iChannels;
|
|
||||||
_this->hidden->iCurrNumBufs = iNumBufs;
|
|
||||||
_this->hidden->iCurrBufSize = iBufSize;
|
|
||||||
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void DART_ThreadInit(_THIS)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
void DART_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
pMixBufferDesc pBufDesc;
|
|
||||||
ULONG ulPostCount;
|
|
||||||
|
|
||||||
DosResetEventSem(_this->hidden->hevAudioBufferPlayed, &ulPostCount);
|
|
||||||
// If there is already an empty buffer, then return now!
|
|
||||||
for (i=0; i<_this->hidden->iCurrNumBufs; i++)
|
|
||||||
{
|
|
||||||
pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[i].ulUserParm;
|
|
||||||
if (pBufDesc->iBufferUsage == BUFFER_EMPTY)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If there is no empty buffer, wait for one to be empty!
|
|
||||||
DosWaitEventSem(_this->hidden->hevAudioBufferPlayed, 1000); // Wait max 1 sec!!! Important!
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DART_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
int iFreeBuf = _this->hidden->iNextFreeBuffer;
|
|
||||||
pMixBufferDesc pBufDesc;
|
|
||||||
|
|
||||||
pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[iFreeBuf].ulUserParm;
|
|
||||||
pBufDesc->iBufferUsage = BUFFER_USED;
|
|
||||||
// Send it to DART to be queued
|
|
||||||
_this->hidden->MixSetupParms.pmixWrite(_this->hidden->MixSetupParms.ulMixHandle,
|
|
||||||
&(_this->hidden->pMixBuffers[iFreeBuf]), 1);
|
|
||||||
|
|
||||||
_this->hidden->iLastPlayedBuf = iFreeBuf;
|
|
||||||
iFreeBuf = (iFreeBuf+1) % _this->hidden->iCurrNumBufs;
|
|
||||||
_this->hidden->iNextFreeBuffer = iFreeBuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8 *DART_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
int iFreeBuf;
|
|
||||||
Uint8 *pResult;
|
|
||||||
pMixBufferDesc pBufDesc;
|
|
||||||
|
|
||||||
if (_this)
|
|
||||||
{
|
|
||||||
if (_this->hidden)
|
|
||||||
{
|
|
||||||
iFreeBuf = _this->hidden->iNextFreeBuffer;
|
|
||||||
pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[iFreeBuf].ulUserParm;
|
|
||||||
|
|
||||||
if (pBufDesc)
|
|
||||||
{
|
|
||||||
if (pBufDesc->iBufferUsage == BUFFER_EMPTY)
|
|
||||||
{
|
|
||||||
pResult = _this->hidden->pMixBuffers[iFreeBuf].pBuffer;
|
|
||||||
return pResult;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
printf("[DART_GetAudioBuf] : ERROR! pBufDesc = %p\n", pBufDesc);
|
|
||||||
} else
|
|
||||||
printf("[DART_GetAudioBuf] : ERROR! _this->hidden = %p\n", _this->hidden);
|
|
||||||
} else
|
|
||||||
printf("[DART_GetAudioBuf] : ERROR! _this = %p\n", _this);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DART_WaitDone(_THIS)
|
|
||||||
{
|
|
||||||
pMixBufferDesc pBufDesc;
|
|
||||||
ULONG ulPostCount;
|
|
||||||
APIRET rc;
|
|
||||||
|
|
||||||
pBufDesc = (pMixBufferDesc) _this->hidden->pMixBuffers[_this->hidden->iLastPlayedBuf].ulUserParm;
|
|
||||||
rc = NO_ERROR;
|
|
||||||
while ((pBufDesc->iBufferUsage != BUFFER_EMPTY) && (rc==NO_ERROR))
|
|
||||||
{
|
|
||||||
DosResetEventSem(_this->hidden->hevAudioBufferPlayed, &ulPostCount);
|
|
||||||
rc = DosWaitEventSem(_this->hidden->hevAudioBufferPlayed, 1000); // 1 sec timeout! Important!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DART_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
MCI_GENERIC_PARMS GenericParms;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
// Stop DART playback
|
|
||||||
rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_STOP, MCI_WAIT, &GenericParms, 0);
|
|
||||||
if (rc!=MCIERR_SUCCESS)
|
|
||||||
{
|
|
||||||
#ifdef SFX_DEBUG_BUILD
|
|
||||||
printf("Could not stop DART playback!\n");
|
|
||||||
fflush(stdout);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close event semaphore
|
|
||||||
DosCloseEventSem(_this->hidden->hevAudioBufferPlayed);
|
|
||||||
|
|
||||||
// Free memory of buffer descriptions
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i=0; i<_this->hidden->iCurrNumBufs; i++) SDL_free((void *)(_this->hidden->pMixBuffers[i].ulUserParm));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Deallocate buffers
|
|
||||||
rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_BUFFER, MCI_WAIT | MCI_DEALLOCATE_MEMORY, &(_this->hidden->BufferParms), 0);
|
|
||||||
|
|
||||||
// Free bufferlist
|
|
||||||
SDL_free(_this->hidden->pMixBuffers); _this->hidden->pMixBuffers = NULL;
|
|
||||||
|
|
||||||
// Close dart
|
|
||||||
rc = mciSendCommand(_this->hidden->iCurrDeviceOrd, MCI_CLOSE, MCI_WAIT, &(GenericParms), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this )
|
|
||||||
{
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) )
|
|
||||||
{
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this )
|
|
||||||
SDL_free(this);
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = DART_OpenAudio;
|
|
||||||
this->ThreadInit = DART_ThreadInit;
|
|
||||||
this->WaitAudio = DART_WaitAudio;
|
|
||||||
this->PlayAudio = DART_PlayAudio;
|
|
||||||
this->GetAudioBuf = DART_GetAudioBuf;
|
|
||||||
this->WaitDone = DART_WaitDone;
|
|
||||||
this->CloseAudio = DART_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap DART_bootstrap = {
|
|
||||||
"dart", "OS/2 Direct Audio RouTines (DART)",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_lowaudio_h
|
|
||||||
#define _SDL_lowaudio_h
|
|
||||||
|
|
||||||
#define INCL_TYPES
|
|
||||||
#define INCL_DOSSEMAPHORES
|
|
||||||
#define INCL_DOSRESOURCES
|
|
||||||
#define INCL_DOSMISC
|
|
||||||
#define INCL_DOSERRORS
|
|
||||||
|
|
||||||
#define INCL_OS2MM
|
|
||||||
#define INCL_MMIOOS2
|
|
||||||
#define INCL_MCIOS2
|
|
||||||
#include <os2.h>
|
|
||||||
#include <os2me.h> // DART stuff and MMIO stuff
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the audio functions */
|
|
||||||
#define _THIS SDL_AudioDevice *_this
|
|
||||||
|
|
||||||
/* The DirectSound objects */
|
|
||||||
struct SDL_PrivateAudioData
|
|
||||||
{
|
|
||||||
int iCurrDeviceOrd;
|
|
||||||
int iCurrFreq;
|
|
||||||
int iCurrBits;
|
|
||||||
int iCurrChannels;
|
|
||||||
int iCurrNumBufs;
|
|
||||||
int iCurrBufSize;
|
|
||||||
|
|
||||||
int iLastPlayedBuf;
|
|
||||||
int iNextFreeBuffer;
|
|
||||||
|
|
||||||
MCI_BUFFER_PARMS BufferParms; // Sound buffer parameters
|
|
||||||
MCI_MIX_BUFFER *pMixBuffers; // Sound buffers
|
|
||||||
MCI_MIXSETUP_PARMS MixSetupParms; // Mixer setup parameters
|
|
||||||
HEV hevAudioBufferPlayed; // Event semaphore to indicate that an audio buffer has been played by DART
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _SDL_lowaudio_h */
|
|
||||||
|
|
@ -1,246 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Output dreamcast aica */
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_dcaudio.h"
|
|
||||||
|
|
||||||
#include "aica.h"
|
|
||||||
#include <dc/spu.h>
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int DCAUD_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void DCAUD_WaitAudio(_THIS);
|
|
||||||
static void DCAUD_PlayAudio(_THIS);
|
|
||||||
static Uint8 *DCAUD_GetAudioBuf(_THIS);
|
|
||||||
static void DCAUD_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
static int DCAUD_Available(void)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DCAUD_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *DCAUD_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = DCAUD_OpenAudio;
|
|
||||||
this->WaitAudio = DCAUD_WaitAudio;
|
|
||||||
this->PlayAudio = DCAUD_PlayAudio;
|
|
||||||
this->GetAudioBuf = DCAUD_GetAudioBuf;
|
|
||||||
this->CloseAudio = DCAUD_CloseAudio;
|
|
||||||
|
|
||||||
this->free = DCAUD_DeleteDevice;
|
|
||||||
|
|
||||||
spu_init();
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap DCAUD_bootstrap = {
|
|
||||||
"dcaudio", "Dreamcast AICA audio",
|
|
||||||
DCAUD_Available, DCAUD_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void DCAUD_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
if (this->hidden->playing) {
|
|
||||||
/* wait */
|
|
||||||
while(aica_get_pos(0)/this->spec.samples == this->hidden->nextbuf) {
|
|
||||||
thd_pass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SPU_RAM_BASE 0xa0800000
|
|
||||||
|
|
||||||
static void spu_memload_stereo8(int leftpos,int rightpos,void *src0,size_t size)
|
|
||||||
{
|
|
||||||
uint8 *src = src0;
|
|
||||||
uint32 *left = (uint32*)(leftpos +SPU_RAM_BASE);
|
|
||||||
uint32 *right = (uint32*)(rightpos+SPU_RAM_BASE);
|
|
||||||
size = (size+7)/8;
|
|
||||||
while(size--) {
|
|
||||||
unsigned lval,rval;
|
|
||||||
lval = *src++;
|
|
||||||
rval = *src++;
|
|
||||||
lval|= (*src++)<<8;
|
|
||||||
rval|= (*src++)<<8;
|
|
||||||
lval|= (*src++)<<16;
|
|
||||||
rval|= (*src++)<<16;
|
|
||||||
lval|= (*src++)<<24;
|
|
||||||
rval|= (*src++)<<24;
|
|
||||||
g2_write_32(left++,lval);
|
|
||||||
g2_write_32(right++,rval);
|
|
||||||
g2_fifo_wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spu_memload_stereo16(int leftpos,int rightpos,void *src0,size_t size)
|
|
||||||
{
|
|
||||||
uint16 *src = src0;
|
|
||||||
uint32 *left = (uint32*)(leftpos +SPU_RAM_BASE);
|
|
||||||
uint32 *right = (uint32*)(rightpos+SPU_RAM_BASE);
|
|
||||||
size = (size+7)/8;
|
|
||||||
while(size--) {
|
|
||||||
unsigned lval,rval;
|
|
||||||
lval = *src++;
|
|
||||||
rval = *src++;
|
|
||||||
lval|= (*src++)<<16;
|
|
||||||
rval|= (*src++)<<16;
|
|
||||||
g2_write_32(left++,lval);
|
|
||||||
g2_write_32(right++,rval);
|
|
||||||
g2_fifo_wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DCAUD_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
SDL_AudioSpec *spec = &this->spec;
|
|
||||||
unsigned int offset;
|
|
||||||
|
|
||||||
if (this->hidden->playing) {
|
|
||||||
/* wait */
|
|
||||||
while(aica_get_pos(0)/spec->samples == this->hidden->nextbuf) {
|
|
||||||
thd_pass();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset = this->hidden->nextbuf*spec->size;
|
|
||||||
this->hidden->nextbuf^=1;
|
|
||||||
/* Write the audio data, checking for EAGAIN on broken audio drivers */
|
|
||||||
if (spec->channels==1) {
|
|
||||||
spu_memload(this->hidden->leftpos+offset,this->hidden->mixbuf,this->hidden->mixlen);
|
|
||||||
} else {
|
|
||||||
offset/=2;
|
|
||||||
if ((this->spec.format&255)==8) {
|
|
||||||
spu_memload_stereo8(this->hidden->leftpos+offset,this->hidden->rightpos+offset,this->hidden->mixbuf,this->hidden->mixlen);
|
|
||||||
} else {
|
|
||||||
spu_memload_stereo16(this->hidden->leftpos+offset,this->hidden->rightpos+offset,this->hidden->mixbuf,this->hidden->mixlen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this->hidden->playing) {
|
|
||||||
int mode;
|
|
||||||
this->hidden->playing = 1;
|
|
||||||
mode = (spec->format==AUDIO_S8)?SM_8BIT:SM_16BIT;
|
|
||||||
if (spec->channels==1) {
|
|
||||||
aica_play(0,mode,this->hidden->leftpos,0,spec->samples*2,spec->freq,255,128,1);
|
|
||||||
} else {
|
|
||||||
aica_play(0,mode,this->hidden->leftpos ,0,spec->samples*2,spec->freq,255,0,1);
|
|
||||||
aica_play(1,mode,this->hidden->rightpos,0,spec->samples*2,spec->freq,255,255,1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *DCAUD_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(this->hidden->mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DCAUD_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
aica_stop(0);
|
|
||||||
if (this->spec.channels==2) aica_stop(1);
|
|
||||||
if ( this->hidden->mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(this->hidden->mixbuf);
|
|
||||||
this->hidden->mixbuf = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int DCAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
Uint16 test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
int valid_datatype = 0;
|
|
||||||
while ((!valid_datatype) && (test_format)) {
|
|
||||||
spec->format = test_format;
|
|
||||||
switch (test_format) {
|
|
||||||
/* only formats Dreamcast accepts... */
|
|
||||||
case AUDIO_S8:
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
valid_datatype = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid_datatype) { /* shouldn't happen, but just in case... */
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spec->channels > 2)
|
|
||||||
spec->channels = 2; /* no more than stereo on the Dreamcast. */
|
|
||||||
|
|
||||||
/* Update the fragment size as size in bytes */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
this->hidden->mixlen = spec->size;
|
|
||||||
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
|
|
||||||
if ( this->hidden->mixbuf == NULL ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
|
|
||||||
this->hidden->leftpos = 0x11000;
|
|
||||||
this->hidden->rightpos = 0x11000+spec->size;
|
|
||||||
this->hidden->playing = 0;
|
|
||||||
this->hidden->nextbuf = 0;
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_dcaudio_h
|
|
||||||
#define _SDL_dcaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
Uint32 mixlen;
|
|
||||||
int playing;
|
|
||||||
int leftpos,rightpos;
|
|
||||||
int nextbuf;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _SDL_dcaudio_h */
|
|
||||||
|
|
@ -1,271 +0,0 @@
|
||||||
/* This file is part of the Dreamcast function library.
|
|
||||||
* Please see libdream.c for further details.
|
|
||||||
*
|
|
||||||
* (c)2000 Dan Potter
|
|
||||||
* modify BERO
|
|
||||||
*/
|
|
||||||
#include "aica.h"
|
|
||||||
|
|
||||||
#include <arch/irq.h>
|
|
||||||
#include <dc/spu.h>
|
|
||||||
|
|
||||||
/* #define dc_snd_base ((volatile unsigned char *)0x00800000) */ /* arm side */
|
|
||||||
#define dc_snd_base ((volatile unsigned char *)0xa0700000) /* dc side */
|
|
||||||
|
|
||||||
/* Some convienence macros */
|
|
||||||
#define SNDREGADDR(x) (0xa0700000 + (x))
|
|
||||||
#define CHNREGADDR(ch,x) SNDREGADDR(0x80*(ch)+(x))
|
|
||||||
|
|
||||||
|
|
||||||
#define SNDREG32(x) (*(volatile unsigned long *)SNDREGADDR(x))
|
|
||||||
#define SNDREG8(x) (*(volatile unsigned char *)SNDREGADDR(x))
|
|
||||||
#define CHNREG32(ch, x) (*(volatile unsigned long *)CHNREGADDR(ch,x))
|
|
||||||
#define CHNREG8(ch, x) (*(volatile unsigned long *)CHNREGADDR(ch,x))
|
|
||||||
|
|
||||||
#define G2_LOCK(OLD) \
|
|
||||||
do { \
|
|
||||||
if (!irq_inside_int()) \
|
|
||||||
OLD = irq_disable(); \
|
|
||||||
/* suspend any G2 DMA here... */ \
|
|
||||||
while((*(volatile unsigned int *)0xa05f688c) & 0x20) \
|
|
||||||
; \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#define G2_UNLOCK(OLD) \
|
|
||||||
do { \
|
|
||||||
/* resume any G2 DMA here... */ \
|
|
||||||
if (!irq_inside_int()) \
|
|
||||||
irq_restore(OLD); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
|
|
||||||
void aica_init() {
|
|
||||||
int i, j, old = 0;
|
|
||||||
|
|
||||||
/* Initialize AICA channels */
|
|
||||||
G2_LOCK(old);
|
|
||||||
SNDREG32(0x2800) = 0x0000;
|
|
||||||
|
|
||||||
for (i=0; i<64; i++) {
|
|
||||||
for (j=0; j<0x80; j+=4) {
|
|
||||||
if ((j&31)==0) g2_fifo_wait();
|
|
||||||
CHNREG32(i, j) = 0;
|
|
||||||
}
|
|
||||||
g2_fifo_wait();
|
|
||||||
CHNREG32(i,0) = 0x8000;
|
|
||||||
CHNREG32(i,20) = 0x1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
SNDREG32(0x2800) = 0x000f;
|
|
||||||
g2_fifo_wait();
|
|
||||||
G2_UNLOCK(old);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Translates a volume from linear form to logarithmic form (required by
|
|
||||||
the AICA chip */
|
|
||||||
/* int logs[] = {
|
|
||||||
|
|
||||||
0, 40, 50, 58, 63, 68, 73, 77, 80, 83, 86, 89, 92, 94, 97, 99, 101, 103,
|
|
||||||
105, 107, 109, 111, 112, 114, 116, 117, 119, 120, 122, 123, 125, 126, 127,
|
|
||||||
129, 130, 131, 133, 134, 135, 136, 137, 139, 140, 141, 142, 143, 144, 145,
|
|
||||||
146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 156, 157, 158, 159,
|
|
||||||
160, 161, 162, 162, 163, 164, 165, 166, 166, 167, 168, 169, 170, 170, 171,
|
|
||||||
172, 172, 173, 174, 175, 175, 176, 177, 177, 178, 179, 180, 180, 181, 182,
|
|
||||||
182, 183, 183, 184, 185, 185, 186, 187, 187, 188, 188, 189, 190, 190, 191,
|
|
||||||
191, 192, 193, 193, 194, 194, 195, 196, 196, 197, 197, 198, 198, 199, 199,
|
|
||||||
200, 201, 201, 202, 202, 203, 203, 204, 204, 205, 205, 206, 206, 207, 207,
|
|
||||||
208, 208, 209, 209, 210, 210, 211, 211, 212, 212, 213, 213, 214, 214, 215,
|
|
||||||
215, 216, 216, 217, 217, 217, 218, 218, 219, 219, 220, 220, 221, 221, 222,
|
|
||||||
222, 222, 223, 223, 224, 224, 225, 225, 225, 226, 226, 227, 227, 228, 228,
|
|
||||||
228, 229, 229, 230, 230, 230, 231, 231, 232, 232, 232, 233, 233, 234, 234,
|
|
||||||
234, 235, 235, 236, 236, 236, 237, 237, 238, 238, 238, 239, 239, 240, 240,
|
|
||||||
240, 241, 241, 241, 242, 242, 243, 243, 243, 244, 244, 244, 245, 245, 245,
|
|
||||||
246, 246, 247, 247, 247, 248, 248, 248, 249, 249, 249, 250, 250, 250, 251,
|
|
||||||
251, 251, 252, 252, 252, 253, 253, 253, 254, 254, 254, 255
|
|
||||||
|
|
||||||
}; */
|
|
||||||
|
|
||||||
const static unsigned char logs[] = {
|
|
||||||
0, 15, 22, 27, 31, 35, 39, 42, 45, 47, 50, 52, 55, 57, 59, 61,
|
|
||||||
63, 65, 67, 69, 71, 73, 74, 76, 78, 79, 81, 82, 84, 85, 87, 88,
|
|
||||||
90, 91, 92, 94, 95, 96, 98, 99, 100, 102, 103, 104, 105, 106,
|
|
||||||
108, 109, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, 121,
|
|
||||||
122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
|
|
||||||
135, 136, 137, 138, 138, 139, 140, 141, 142, 143, 144, 145, 146,
|
|
||||||
146, 147, 148, 149, 150, 151, 152, 152, 153, 154, 155, 156, 156,
|
|
||||||
157, 158, 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, 167,
|
|
||||||
167, 168, 169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176,
|
|
||||||
177, 178, 178, 179, 180, 181, 181, 182, 183, 183, 184, 185, 185,
|
|
||||||
186, 187, 187, 188, 189, 189, 190, 191, 191, 192, 193, 193, 194,
|
|
||||||
195, 195, 196, 197, 197, 198, 199, 199, 200, 200, 201, 202, 202,
|
|
||||||
203, 204, 204, 205, 205, 206, 207, 207, 208, 209, 209, 210, 210,
|
|
||||||
211, 212, 212, 213, 213, 214, 215, 215, 216, 216, 217, 217, 218,
|
|
||||||
219, 219, 220, 220, 221, 221, 222, 223, 223, 224, 224, 225, 225,
|
|
||||||
226, 227, 227, 228, 228, 229, 229, 230, 230, 231, 232, 232, 233,
|
|
||||||
233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 239, 239, 240,
|
|
||||||
240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246, 246,
|
|
||||||
247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253, 254, 255
|
|
||||||
};
|
|
||||||
|
|
||||||
/* For the moment this is going to have to suffice, until we really
|
|
||||||
figure out what these mean. */
|
|
||||||
#define AICA_PAN(x) ((x)==0x80?(0):((x)<0x80?(0x1f):(0x0f)))
|
|
||||||
#define AICA_VOL(x) (0xff - logs[128 + (((x) & 0xff) / 2)])
|
|
||||||
//#define AICA_VOL(x) (0xff - logs[x&255])
|
|
||||||
|
|
||||||
static inline unsigned AICA_FREQ(unsigned freq) {
|
|
||||||
unsigned long freq_lo, freq_base = 5644800;
|
|
||||||
int freq_hi = 7;
|
|
||||||
|
|
||||||
/* Need to convert frequency to floating point format
|
|
||||||
(freq_hi is exponent, freq_lo is mantissa)
|
|
||||||
Formula is ferq = 44100*2^freq_hi*(1+freq_lo/1024) */
|
|
||||||
while (freq < freq_base && freq_hi > -8) {
|
|
||||||
freq_base >>= 1;
|
|
||||||
--freq_hi;
|
|
||||||
}
|
|
||||||
while (freq < freq_base && freq_hi > -8) {
|
|
||||||
freq_base >>= 1;
|
|
||||||
freq_hi--;
|
|
||||||
}
|
|
||||||
freq_lo = (freq<<10) / freq_base;
|
|
||||||
return (freq_hi << 11) | (freq_lo & 1023);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Sets up a sound channel completely. This is generally good if you want
|
|
||||||
a quick and dirty way to play notes. If you want a more comprehensive
|
|
||||||
set of routines (more like PC wavetable cards) see below.
|
|
||||||
|
|
||||||
ch is the channel to play on (0 - 63)
|
|
||||||
smpptr is the pointer to the sound data; if you're running off the
|
|
||||||
SH4, then this ought to be (ptr - 0xa0800000); otherwise it's just
|
|
||||||
ptr. Basically, it's an offset into sound ram.
|
|
||||||
mode is one of the mode constants (16 bit, 8 bit, ADPCM)
|
|
||||||
nsamp is the number of samples to play (not number of bytes!)
|
|
||||||
freq is the sampling rate of the sound
|
|
||||||
vol is the volume, 0 to 0xff (0xff is louder)
|
|
||||||
pan is a panning constant -- 0 is left, 128 is center, 255 is right.
|
|
||||||
|
|
||||||
This routine (and the similar ones) owe a lot to Marcus' sound example --
|
|
||||||
I hadn't gotten quite this far into dissecting the individual regs yet. */
|
|
||||||
void aica_play(int ch,int mode,unsigned long smpptr,int loopst,int loopend,int freq,int vol,int pan,int loopflag) {
|
|
||||||
/* int i;
|
|
||||||
*/
|
|
||||||
int val;
|
|
||||||
int old = 0;
|
|
||||||
|
|
||||||
/* Stop the channel (if it's already playing) */
|
|
||||||
aica_stop(ch);
|
|
||||||
/* doesn't seem to be needed, but it's here just in case */
|
|
||||||
/*
|
|
||||||
for (i=0; i<256; i++) {
|
|
||||||
asm("nop");
|
|
||||||
asm("nop");
|
|
||||||
asm("nop");
|
|
||||||
asm("nop");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
G2_LOCK(old);
|
|
||||||
/* Envelope setup. The first of these is the loop point,
|
|
||||||
e.g., where the sample starts over when it loops. The second
|
|
||||||
is the loop end. This is the full length of the sample when
|
|
||||||
you are not looping, or the loop end point when you are (though
|
|
||||||
storing more than that is a waste of memory if you're not doing
|
|
||||||
volume enveloping). */
|
|
||||||
CHNREG32(ch, 8) = loopst & 0xffff;
|
|
||||||
CHNREG32(ch, 12) = loopend & 0xffff;
|
|
||||||
|
|
||||||
/* Write resulting values */
|
|
||||||
CHNREG32(ch, 24) = AICA_FREQ(freq);
|
|
||||||
|
|
||||||
/* Set volume, pan, and some other things that we don't know what
|
|
||||||
they do =) */
|
|
||||||
CHNREG32(ch, 36) = AICA_PAN(pan) | (0xf<<8);
|
|
||||||
/* Convert the incoming volume and pan into hardware values */
|
|
||||||
/* Vol starts at zero so we can ramp */
|
|
||||||
vol = AICA_VOL(vol);
|
|
||||||
CHNREG32(ch, 40) = 0x24 | (vol<<8);
|
|
||||||
/* Convert the incoming volume and pan into hardware values */
|
|
||||||
/* Vol starts at zero so we can ramp */
|
|
||||||
|
|
||||||
/* If we supported volume envelopes (which we don't yet) then
|
|
||||||
this value would set that up. The top 4 bits determine the
|
|
||||||
envelope speed. f is the fastest, 1 is the slowest, and 0
|
|
||||||
seems to be an invalid value and does weird things). The
|
|
||||||
default (below) sets it into normal mode (play and terminate/loop).
|
|
||||||
CHNREG32(ch, 16) = 0xf010;
|
|
||||||
*/
|
|
||||||
CHNREG32(ch, 16) = 0x1f; /* No volume envelope */
|
|
||||||
|
|
||||||
|
|
||||||
/* Set sample format, buffer address, and looping control. If
|
|
||||||
0x0200 mask is set on reg 0, the sample loops infinitely. If
|
|
||||||
it's not set, the sample plays once and terminates. We'll
|
|
||||||
also set the bits to start playback here. */
|
|
||||||
CHNREG32(ch, 4) = smpptr & 0xffff;
|
|
||||||
val = 0xc000 | 0x0000 | (mode<<7) | (smpptr >> 16);
|
|
||||||
if (loopflag) val|=0x200;
|
|
||||||
|
|
||||||
CHNREG32(ch, 0) = val;
|
|
||||||
|
|
||||||
G2_UNLOCK(old);
|
|
||||||
|
|
||||||
/* Enable playback */
|
|
||||||
/* CHNREG32(ch, 0) |= 0xc000; */
|
|
||||||
g2_fifo_wait();
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
for (i=0xff; i>=vol; i--) {
|
|
||||||
if ((i&7)==0) g2_fifo_wait();
|
|
||||||
CHNREG32(ch, 40) = 0x24 | (i<<8);;
|
|
||||||
}
|
|
||||||
|
|
||||||
g2_fifo_wait();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop the sound on a given channel */
|
|
||||||
void aica_stop(int ch) {
|
|
||||||
g2_write_32(CHNREGADDR(ch, 0),(g2_read_32(CHNREGADDR(ch, 0)) & ~0x4000) | 0x8000);
|
|
||||||
g2_fifo_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* The rest of these routines can change the channel in mid-stride so you
|
|
||||||
can do things like vibrato and panning effects. */
|
|
||||||
|
|
||||||
/* Set channel volume */
|
|
||||||
void aica_vol(int ch,int vol) {
|
|
||||||
// g2_write_8(CHNREGADDR(ch, 41),AICA_VOL(vol));
|
|
||||||
g2_write_32(CHNREGADDR(ch, 40),(g2_read_32(CHNREGADDR(ch, 40))&0xffff00ff)|(AICA_VOL(vol)<<8) );
|
|
||||||
g2_fifo_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set channel pan */
|
|
||||||
void aica_pan(int ch,int pan) {
|
|
||||||
// g2_write_8(CHNREGADDR(ch, 36),AICA_PAN(pan));
|
|
||||||
g2_write_32(CHNREGADDR(ch, 36),(g2_read_32(CHNREGADDR(ch, 36))&0xffffff00)|(AICA_PAN(pan)) );
|
|
||||||
g2_fifo_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set channel frequency */
|
|
||||||
void aica_freq(int ch,int freq) {
|
|
||||||
g2_write_32(CHNREGADDR(ch, 24),AICA_FREQ(freq));
|
|
||||||
g2_fifo_wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get channel position */
|
|
||||||
int aica_get_pos(int ch) {
|
|
||||||
#if 1
|
|
||||||
/* Observe channel ch */
|
|
||||||
g2_write_32(SNDREGADDR(0x280c),(g2_read_32(SNDREGADDR(0x280c))&0xffff00ff) | (ch<<8));
|
|
||||||
g2_fifo_wait();
|
|
||||||
/* Update position counters */
|
|
||||||
return g2_read_32(SNDREGADDR(0x2814)) & 0xffff;
|
|
||||||
#else
|
|
||||||
/* Observe channel ch */
|
|
||||||
g2_write_8(SNDREGADDR(0x280d),ch);
|
|
||||||
/* Update position counters */
|
|
||||||
return g2_read_32(SNDREGADDR(0x2814)) & 0xffff;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _AICA_H_
|
|
||||||
#define _AICA_H_
|
|
||||||
|
|
||||||
#define AICA_MEM 0xa0800000
|
|
||||||
|
|
||||||
#define SM_8BIT 1
|
|
||||||
#define SM_16BIT 0
|
|
||||||
#define SM_ADPCM 2
|
|
||||||
|
|
||||||
void aica_play(int ch,int mode,unsigned long smpptr,int looptst,int loopend,int freq,int vol,int pan,int loopflag);
|
|
||||||
void aica_stop(int ch);
|
|
||||||
void aica_vol(int ch,int vol);
|
|
||||||
void aica_pan(int ch,int pan);
|
|
||||||
void aica_freq(int ch,int freq);
|
|
||||||
int aica_get_pos(int ch);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
|
|
||||||
This file written by Ryan C. Gordon (icculus@icculus.org)
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Output raw audio data to a file. */
|
|
||||||
|
|
||||||
#if HAVE_STDIO_H
|
|
||||||
#include <stdio.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "SDL_rwops.h"
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_diskaudio.h"
|
|
||||||
|
|
||||||
/* The tag name used by DISK audio */
|
|
||||||
#define DISKAUD_DRIVER_NAME "disk"
|
|
||||||
|
|
||||||
/* environment variables and defaults. */
|
|
||||||
#define DISKENVR_OUTFILE "SDL_DISKAUDIOFILE"
|
|
||||||
#define DISKDEFAULT_OUTFILE "/sdlaudio.raw"
|
|
||||||
#define DISKENVR_WRITEDELAY "SDL_DISKAUDIODELAY"
|
|
||||||
#define DISKDEFAULT_WRITEDELAY 150
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int DISKAUD_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void DISKAUD_WaitAudio(_THIS);
|
|
||||||
static void DISKAUD_PlayAudio(_THIS);
|
|
||||||
static Uint8 *DISKAUD_GetAudioBuf(_THIS);
|
|
||||||
static void DISKAUD_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
static const char *DISKAUD_GetOutputFilename(void)
|
|
||||||
{
|
|
||||||
const char *envr = SDL_getenv(DISKENVR_OUTFILE);
|
|
||||||
return((envr != NULL) ? envr : DISKDEFAULT_OUTFILE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
static int DISKAUD_Available(void)
|
|
||||||
{
|
|
||||||
//const char *envr = SDL_getenv("SDL_AUDIODRIVER");
|
|
||||||
// if (envr && (SDL_strcmp(envr, DISKAUD_DRIVER_NAME) == 0)) {
|
|
||||||
return(1);
|
|
||||||
// }
|
|
||||||
// return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DISKAUD_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *DISKAUD_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
const char *envr;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
envr = SDL_getenv(DISKENVR_WRITEDELAY);
|
|
||||||
this->hidden->write_delay = (envr) ? SDL_atoi(envr) : DISKDEFAULT_WRITEDELAY;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = DISKAUD_OpenAudio;
|
|
||||||
this->WaitAudio = DISKAUD_WaitAudio;
|
|
||||||
this->PlayAudio = DISKAUD_PlayAudio;
|
|
||||||
this->GetAudioBuf = DISKAUD_GetAudioBuf;
|
|
||||||
this->CloseAudio = DISKAUD_CloseAudio;
|
|
||||||
|
|
||||||
this->free = DISKAUD_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap DISKAUD_bootstrap = {
|
|
||||||
DISKAUD_DRIVER_NAME, "direct-to-disk audio",
|
|
||||||
DISKAUD_Available, DISKAUD_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void DISKAUD_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
SDL_Delay(this->hidden->write_delay);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DISKAUD_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
int written;
|
|
||||||
|
|
||||||
/* Write the audio data */
|
|
||||||
written = SDL_RWwrite(this->hidden->output,
|
|
||||||
this->hidden->mixbuf, 1,
|
|
||||||
this->hidden->mixlen);
|
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
|
||||||
if ( (Uint32)written != this->hidden->mixlen ) {
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *DISKAUD_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(this->hidden->mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DISKAUD_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( this->hidden->mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(this->hidden->mixbuf);
|
|
||||||
this->hidden->mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( this->hidden->output != NULL ) {
|
|
||||||
SDL_RWclose(this->hidden->output);
|
|
||||||
this->hidden->output = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int DISKAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
const char *fname = DISKAUD_GetOutputFilename();
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
this->hidden->output = SDL_RWFromFile(fname, "wb");
|
|
||||||
if ( this->hidden->output == NULL ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if HAVE_STDIO_H
|
|
||||||
fprintf(stderr, "WARNING: You are using the SDL disk writer"
|
|
||||||
" audio driver!\n Writing to file [%s].\n", fname);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
this->hidden->mixlen = spec->size;
|
|
||||||
this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
|
|
||||||
if ( this->hidden->mixbuf == NULL ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_diskaudio_h
|
|
||||||
#define _SDL_diskaudio_h
|
|
||||||
|
|
||||||
#include "SDL_rwops.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
SDL_RWops *output;
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
Uint32 mixlen;
|
|
||||||
Uint32 write_delay;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _SDL_diskaudio_h */
|
|
||||||
|
|
@ -1,455 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h> /* For strerror() */
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
|
|
||||||
/* This is installed on some systems */
|
|
||||||
#include <soundcard.h>
|
|
||||||
#else
|
|
||||||
/* This is recommended by OSS */
|
|
||||||
#include <sys/soundcard.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef MAP_FAILED
|
|
||||||
#define MAP_FAILED ((Uint8 *)-1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_dmaaudio.h"
|
|
||||||
|
|
||||||
/* The tag name used by DMA audio */
|
|
||||||
#define DMA_DRIVER_NAME "dma"
|
|
||||||
|
|
||||||
/* Open the audio device for playback, and don't block if busy */
|
|
||||||
#define OPEN_FLAGS (O_RDWR|O_NONBLOCK)
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void DMA_WaitAudio(_THIS);
|
|
||||||
static void DMA_PlayAudio(_THIS);
|
|
||||||
static Uint8 *DMA_GetAudioBuf(_THIS);
|
|
||||||
static void DMA_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
int available;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
|
|
||||||
fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
|
|
||||||
if ( fd >= 0 ) {
|
|
||||||
int caps;
|
|
||||||
struct audio_buf_info info;
|
|
||||||
|
|
||||||
if ( (ioctl(fd, SNDCTL_DSP_GETCAPS, &caps) == 0) &&
|
|
||||||
(caps & DSP_CAP_TRIGGER) && (caps & DSP_CAP_MMAP) &&
|
|
||||||
(ioctl(fd, SNDCTL_DSP_GETOSPACE, &info) == 0) ) {
|
|
||||||
available = 1;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
audio_fd = -1;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = DMA_OpenAudio;
|
|
||||||
this->WaitAudio = DMA_WaitAudio;
|
|
||||||
this->PlayAudio = DMA_PlayAudio;
|
|
||||||
this->GetAudioBuf = DMA_GetAudioBuf;
|
|
||||||
this->CloseAudio = DMA_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap DMA_bootstrap = {
|
|
||||||
DMA_DRIVER_NAME, "OSS /dev/dsp DMA audio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void DMA_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
fd_set fdset;
|
|
||||||
|
|
||||||
/* Check to see if the thread-parent process is still alive */
|
|
||||||
{ static int cnt = 0;
|
|
||||||
/* Note that this only works with thread implementations
|
|
||||||
that use a different process id for each thread.
|
|
||||||
*/
|
|
||||||
if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
|
|
||||||
if ( kill(parent, 0) < 0 ) {
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* See if we need to use timed audio synchronization */
|
|
||||||
if ( frame_ticks ) {
|
|
||||||
/* Use timer for general audio synchronization */
|
|
||||||
Sint32 ticks;
|
|
||||||
|
|
||||||
ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
|
|
||||||
if ( ticks > 0 ) {
|
|
||||||
SDL_Delay(ticks);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Use select() for audio synchronization */
|
|
||||||
struct timeval timeout;
|
|
||||||
FD_ZERO(&fdset);
|
|
||||||
FD_SET(audio_fd, &fdset);
|
|
||||||
timeout.tv_sec = 10;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Waiting for audio to get ready\n");
|
|
||||||
#endif
|
|
||||||
if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
|
|
||||||
const char *message =
|
|
||||||
#ifdef AUDIO_OSPACE_HACK
|
|
||||||
"Audio timeout - buggy audio driver? (trying ospace)";
|
|
||||||
#else
|
|
||||||
"Audio timeout - buggy audio driver? (disabled)";
|
|
||||||
#endif
|
|
||||||
/* In general we should never print to the screen,
|
|
||||||
but in this case we have no other way of letting
|
|
||||||
the user know what happened.
|
|
||||||
*/
|
|
||||||
fprintf(stderr, "SDL: %s\n", message);
|
|
||||||
#ifdef AUDIO_OSPACE_HACK
|
|
||||||
/* We may be able to use GET_OSPACE trick */
|
|
||||||
frame_ticks = (float)(this->spec->samples*1000) /
|
|
||||||
this->spec->freq;
|
|
||||||
next_frame = SDL_GetTicks()+frame_ticks;
|
|
||||||
#else
|
|
||||||
this->enabled = 0;
|
|
||||||
/* Don't try to close - may hang */
|
|
||||||
audio_fd = -1;
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Done disabling audio\n");
|
|
||||||
#endif
|
|
||||||
#endif /* AUDIO_OSPACE_HACK */
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Ready!\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DMA_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* If timer synchronization is enabled, set the next write frame */
|
|
||||||
if ( frame_ticks ) {
|
|
||||||
next_frame += frame_ticks;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *DMA_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
count_info info;
|
|
||||||
int playing;
|
|
||||||
int filling;
|
|
||||||
|
|
||||||
/* Get number of blocks, looping if we're not using select() */
|
|
||||||
do {
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_GETOPTR, &info) < 0 ) {
|
|
||||||
/* Uh oh... */
|
|
||||||
this->enabled = 0;
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
} while ( frame_ticks && (info.blocks < 1) );
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
if ( info.blocks > 1 ) {
|
|
||||||
printf("Warning: audio underflow (%d frags)\n", info.blocks-1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
playing = info.ptr / this->spec.size;
|
|
||||||
filling = (playing + 1)%num_buffers;
|
|
||||||
return (dma_buf + (filling * this->spec.size));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DMA_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( dma_buf != NULL ) {
|
|
||||||
munmap(dma_buf, dma_len);
|
|
||||||
dma_buf = NULL;
|
|
||||||
}
|
|
||||||
if ( audio_fd >= 0 ) {
|
|
||||||
close(audio_fd);
|
|
||||||
audio_fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int DMA_ReopenAudio(_THIS, const char *audiodev, int format, int stereo,
|
|
||||||
SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int frag_spec;
|
|
||||||
int value;
|
|
||||||
|
|
||||||
/* Close and then reopen the audio device */
|
|
||||||
close(audio_fd);
|
|
||||||
audio_fd = open(audiodev, O_RDWR, 0);
|
|
||||||
if ( audio_fd < 0 ) {
|
|
||||||
SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Determine the power of two of the fragment size */
|
|
||||||
for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
|
|
||||||
if ( (0x01<<frag_spec) != spec->size ) {
|
|
||||||
SDL_SetError("Fragment size must be a power of two");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the audio buffering parameters */
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set audio fragment spec");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the audio format */
|
|
||||||
value = format;
|
|
||||||
if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
|
|
||||||
(value != format) ) {
|
|
||||||
SDL_SetError("Couldn't set audio format");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set mono or stereo audio */
|
|
||||||
value = (spec->channels > 1);
|
|
||||||
if ( (ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo) < 0) ||
|
|
||||||
(value != stereo) ) {
|
|
||||||
SDL_SetError("Couldn't set audio channels");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the DSP frequency */
|
|
||||||
value = spec->freq;
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set audio frequency");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->freq = value;
|
|
||||||
|
|
||||||
/* We successfully re-opened the audio */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int DMA_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
char audiodev[1024];
|
|
||||||
int format;
|
|
||||||
int stereo;
|
|
||||||
int value;
|
|
||||||
Uint16 test_format;
|
|
||||||
struct audio_buf_info info;
|
|
||||||
|
|
||||||
/* Reset the timer synchronization flag */
|
|
||||||
frame_ticks = 0.0;
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
|
|
||||||
if ( audio_fd < 0 ) {
|
|
||||||
SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
dma_buf = NULL;
|
|
||||||
ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
|
|
||||||
|
|
||||||
/* Get a list of supported hardware formats */
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
|
|
||||||
SDL_SetError("Couldn't get audio format list");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try for a closest match on audio format */
|
|
||||||
format = 0;
|
|
||||||
for ( test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
! format && test_format; ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
|
||||||
#endif
|
|
||||||
switch ( test_format ) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
if ( value & AFMT_U8 ) {
|
|
||||||
format = AFMT_U8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_S8:
|
|
||||||
if ( value & AFMT_S8 ) {
|
|
||||||
format = AFMT_S8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
if ( value & AFMT_S16_LE ) {
|
|
||||||
format = AFMT_S16_LE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
if ( value & AFMT_S16_BE ) {
|
|
||||||
format = AFMT_S16_BE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
if ( value & AFMT_U16_LE ) {
|
|
||||||
format = AFMT_U16_LE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
if ( value & AFMT_U16_BE ) {
|
|
||||||
format = AFMT_U16_BE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
format = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( ! format ) {
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( format == 0 ) {
|
|
||||||
SDL_SetError("Couldn't find any hardware audio formats");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
/* Set the audio format */
|
|
||||||
value = format;
|
|
||||||
if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
|
|
||||||
(value != format) ) {
|
|
||||||
SDL_SetError("Couldn't set audio format");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set mono or stereo audio (currently only two channels supported) */
|
|
||||||
stereo = (spec->channels > 1);
|
|
||||||
ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
|
|
||||||
if ( stereo ) {
|
|
||||||
spec->channels = 2;
|
|
||||||
} else {
|
|
||||||
spec->channels = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Because some drivers don't allow setting the buffer size
|
|
||||||
after setting the format, we must re-open the audio device
|
|
||||||
once we know what format and channels are supported
|
|
||||||
*/
|
|
||||||
if ( DMA_ReopenAudio(this, audiodev, format, stereo, spec) < 0 ) {
|
|
||||||
/* Error is set by DMA_ReopenAudio() */
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Memory map the audio buffer */
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) < 0 ) {
|
|
||||||
SDL_SetError("Couldn't get OSPACE parameters");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->size = info.fragsize;
|
|
||||||
spec->samples = spec->size / ((spec->format & 0xFF) / 8);
|
|
||||||
spec->samples /= spec->channels;
|
|
||||||
num_buffers = info.fragstotal;
|
|
||||||
dma_len = num_buffers*spec->size;
|
|
||||||
dma_buf = (Uint8 *)mmap(NULL, dma_len, PROT_WRITE, MAP_SHARED,
|
|
||||||
audio_fd, 0);
|
|
||||||
if ( dma_buf == MAP_FAILED ) {
|
|
||||||
SDL_SetError("DMA memory map failed");
|
|
||||||
dma_buf = NULL;
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(dma_buf, spec->silence, dma_len);
|
|
||||||
|
|
||||||
/* Check to see if we need to use select() workaround */
|
|
||||||
{ char *workaround;
|
|
||||||
workaround = SDL_getenv("SDL_DSP_NOSELECT");
|
|
||||||
if ( workaround ) {
|
|
||||||
frame_ticks = (float)(spec->samples*1000)/spec->freq;
|
|
||||||
next_frame = SDL_GetTicks()+frame_ticks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Trigger audio playback */
|
|
||||||
value = 0;
|
|
||||||
ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value);
|
|
||||||
value = PCM_ENABLE_OUTPUT;
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_SETTRIGGER, &value) < 0 ) {
|
|
||||||
SDL_SetError("Couldn't trigger audio output");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the parent process id (we're the parent of the audio thread) */
|
|
||||||
parent = getpid();
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_dspaudio_h
|
|
||||||
#define _SDL_dspaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
int audio_fd;
|
|
||||||
|
|
||||||
/* The parent process id, to detect when application quits */
|
|
||||||
pid_t parent;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *dma_buf;
|
|
||||||
int dma_len;
|
|
||||||
int num_buffers;
|
|
||||||
|
|
||||||
/* Support for audio timing using a timer, in addition to select() */
|
|
||||||
float frame_ticks;
|
|
||||||
float next_frame;
|
|
||||||
};
|
|
||||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define audio_fd (this->hidden->audio_fd)
|
|
||||||
#define parent (this->hidden->parent)
|
|
||||||
#define dma_buf (this->hidden->dma_buf)
|
|
||||||
#define dma_len (this->hidden->dma_len)
|
|
||||||
#define num_buffers (this->hidden->num_buffers)
|
|
||||||
#define frame_ticks (this->hidden->frame_ticks)
|
|
||||||
#define next_frame (this->hidden->next_frame)
|
|
||||||
|
|
||||||
#endif /* _SDL_dspaudio_h */
|
|
||||||
|
|
@ -1,242 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer (For IRIX 6.5 and higher) */
|
|
||||||
/* patch for IRIX 5 by Georg Schwarz 18/07/2004 */
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "SDL_irixaudio.h"
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef AL_RESOURCE /* as a test whether we use the old IRIX audio libraries */
|
|
||||||
#define OLD_IRIX_AUDIO
|
|
||||||
#define alClosePort(x) ALcloseport(x)
|
|
||||||
#define alFreeConfig(x) ALfreeconfig(x)
|
|
||||||
#define alGetFillable(x) ALgetfillable(x)
|
|
||||||
#define alNewConfig() ALnewconfig()
|
|
||||||
#define alOpenPort(x,y,z) ALopenport(x,y,z)
|
|
||||||
#define alSetChannels(x,y) ALsetchannels(x,y)
|
|
||||||
#define alSetQueueSize(x,y) ALsetqueuesize(x,y)
|
|
||||||
#define alSetSampFmt(x,y) ALsetsampfmt(x,y)
|
|
||||||
#define alSetWidth(x,y) ALsetwidth(x,y)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int AL_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void AL_WaitAudio(_THIS);
|
|
||||||
static void AL_PlayAudio(_THIS);
|
|
||||||
static Uint8 *AL_GetAudioBuf(_THIS);
|
|
||||||
static void AL_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = AL_OpenAudio;
|
|
||||||
this->WaitAudio = AL_WaitAudio;
|
|
||||||
this->PlayAudio = AL_PlayAudio;
|
|
||||||
this->GetAudioBuf = AL_GetAudioBuf;
|
|
||||||
this->CloseAudio = AL_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap DMEDIA_bootstrap = {
|
|
||||||
"AL", "IRIX DMedia audio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void static AL_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
Sint32 timeleft;
|
|
||||||
|
|
||||||
timeleft = this->spec.samples - alGetFillable(audio_port);
|
|
||||||
if ( timeleft > 0 ) {
|
|
||||||
timeleft /= (this->spec.freq/1000);
|
|
||||||
SDL_Delay((Uint32)timeleft);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AL_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Write the audio data out */
|
|
||||||
if ( alWriteFrames(audio_port, mixbuf, this->spec.samples) < 0 ) {
|
|
||||||
/* Assume fatal error, for now */
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *AL_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AL_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( audio_port != NULL ) {
|
|
||||||
alClosePort(audio_port);
|
|
||||||
audio_port = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int AL_OpenAudio(_THIS, SDL_AudioSpec * spec)
|
|
||||||
{
|
|
||||||
Uint16 test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
long width = 0;
|
|
||||||
long fmt = 0;
|
|
||||||
int valid = 0;
|
|
||||||
|
|
||||||
#ifdef OLD_IRIX_AUDIO
|
|
||||||
{
|
|
||||||
long audio_param[2];
|
|
||||||
audio_param[0] = AL_OUTPUT_RATE;
|
|
||||||
audio_param[1] = spec->freq;
|
|
||||||
valid = (ALsetparams(AL_DEFAULT_DEVICE, audio_param, 2) < 0);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
{
|
|
||||||
ALpv audio_param;
|
|
||||||
audio_param.param = AL_RATE;
|
|
||||||
audio_param.value.i = spec->freq;
|
|
||||||
valid = (alSetParams(AL_DEFAULT_OUTPUT, &audio_param, 1) < 0);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while ((!valid) && (test_format)) {
|
|
||||||
valid = 1;
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
switch (test_format) {
|
|
||||||
case AUDIO_S8:
|
|
||||||
width = AL_SAMPLE_8;
|
|
||||||
fmt = AL_SAMPFMT_TWOSCOMP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDIO_S16SYS:
|
|
||||||
width = AL_SAMPLE_16;
|
|
||||||
fmt = AL_SAMPFMT_TWOSCOMP;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
valid = 0;
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (valid) {
|
|
||||||
ALconfig audio_config = alNewConfig();
|
|
||||||
valid = 0;
|
|
||||||
if (audio_config) {
|
|
||||||
if (alSetChannels(audio_config, spec->channels) < 0) {
|
|
||||||
if (spec->channels > 2) { /* can't handle > stereo? */
|
|
||||||
spec->channels = 2; /* try again below. */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((alSetSampFmt(audio_config, fmt) >= 0) &&
|
|
||||||
((!width) || (alSetWidth(audio_config, width) >= 0)) &&
|
|
||||||
(alSetQueueSize(audio_config, spec->samples * 2) >= 0) &&
|
|
||||||
(alSetChannels(audio_config, spec->channels) >= 0)) {
|
|
||||||
|
|
||||||
audio_port = alOpenPort("SDL audio", "w", audio_config);
|
|
||||||
if (audio_port == NULL) {
|
|
||||||
/* docs say AL_BAD_CHANNELS happens here, too. */
|
|
||||||
int err = oserror();
|
|
||||||
if (err == AL_BAD_CHANNELS) {
|
|
||||||
spec->channels = 2;
|
|
||||||
alSetChannels(audio_config, spec->channels);
|
|
||||||
audio_port = alOpenPort("SDL audio", "w",
|
|
||||||
audio_config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (audio_port != NULL) {
|
|
||||||
valid = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alFreeConfig(audio_config);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid) {
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the fragment size as size in bytes */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixbuf = (Uint8 *) SDL_AllocAudioMem(spec->size);
|
|
||||||
if (mixbuf == NULL) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_lowaudio_h
|
|
||||||
#define _SDL_lowaudio_h
|
|
||||||
|
|
||||||
#include <dmedia/audio.h>
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the audio functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The handle for the audio device */
|
|
||||||
ALport audio_port;
|
|
||||||
|
|
||||||
Uint8 *mixbuf; /* The app mixing buffer */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define audio_port (this->hidden->audio_port)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
|
|
||||||
#endif /* _SDL_lowaudio_h */
|
|
||||||
|
|
@ -1,340 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
|
|
||||||
Modified in Oct 2004 by Hannu Savolainen
|
|
||||||
hannu@opensound.com
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include <stdio.h> /* For perror() */
|
|
||||||
#include <string.h> /* For strerror() */
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
|
|
||||||
/* This is installed on some systems */
|
|
||||||
#include <soundcard.h>
|
|
||||||
#else
|
|
||||||
/* This is recommended by OSS */
|
|
||||||
#include <sys/soundcard.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_dspaudio.h"
|
|
||||||
|
|
||||||
/* The tag name used by DSP audio */
|
|
||||||
#define DSP_DRIVER_NAME "dsp"
|
|
||||||
|
|
||||||
/* Open the audio device for playback, and don't block if busy */
|
|
||||||
#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK)
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void DSP_WaitAudio(_THIS);
|
|
||||||
static void DSP_PlayAudio(_THIS);
|
|
||||||
static Uint8 *DSP_GetAudioBuf(_THIS);
|
|
||||||
static void DSP_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
int available;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
|
|
||||||
if ( fd >= 0 ) {
|
|
||||||
available = 1;
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
audio_fd = -1;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = DSP_OpenAudio;
|
|
||||||
this->WaitAudio = DSP_WaitAudio;
|
|
||||||
this->PlayAudio = DSP_PlayAudio;
|
|
||||||
this->GetAudioBuf = DSP_GetAudioBuf;
|
|
||||||
this->CloseAudio = DSP_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap DSP_bootstrap = {
|
|
||||||
DSP_DRIVER_NAME, "OSS /dev/dsp standard audio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void DSP_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Not needed at all since OSS handles waiting automagically */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DSP_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
if (write(audio_fd, mixbuf, mixlen)==-1)
|
|
||||||
{
|
|
||||||
perror("Audio write");
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *DSP_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DSP_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( audio_fd >= 0 ) {
|
|
||||||
close(audio_fd);
|
|
||||||
audio_fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
char audiodev[1024];
|
|
||||||
int format;
|
|
||||||
int value;
|
|
||||||
int frag_spec;
|
|
||||||
Uint16 test_format;
|
|
||||||
|
|
||||||
/* Make sure fragment size stays a power of 2, or OSS fails. */
|
|
||||||
/* I don't know which of these are actually legal values, though... */
|
|
||||||
if (spec->channels > 8)
|
|
||||||
spec->channels = 8;
|
|
||||||
else if (spec->channels > 4)
|
|
||||||
spec->channels = 4;
|
|
||||||
else if (spec->channels > 2)
|
|
||||||
spec->channels = 2;
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
|
|
||||||
if ( audio_fd < 0 ) {
|
|
||||||
SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
mixbuf = NULL;
|
|
||||||
|
|
||||||
/* Make the file descriptor use blocking writes with fcntl() */
|
|
||||||
{ long flags;
|
|
||||||
flags = fcntl(audio_fd, F_GETFL);
|
|
||||||
flags &= ~O_NONBLOCK;
|
|
||||||
if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) {
|
|
||||||
SDL_SetError("Couldn't set audio blocking mode");
|
|
||||||
DSP_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get a list of supported hardware formats */
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
|
|
||||||
perror("SNDCTL_DSP_GETFMTS");
|
|
||||||
SDL_SetError("Couldn't get audio format list");
|
|
||||||
DSP_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try for a closest match on audio format */
|
|
||||||
format = 0;
|
|
||||||
for ( test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
! format && test_format; ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
|
||||||
#endif
|
|
||||||
switch ( test_format ) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
if ( value & AFMT_U8 ) {
|
|
||||||
format = AFMT_U8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
if ( value & AFMT_S16_LE ) {
|
|
||||||
format = AFMT_S16_LE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
if ( value & AFMT_S16_BE ) {
|
|
||||||
format = AFMT_S16_BE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#if 0
|
|
||||||
/*
|
|
||||||
* These formats are not used by any real life systems so they are not
|
|
||||||
* needed here.
|
|
||||||
*/
|
|
||||||
case AUDIO_S8:
|
|
||||||
if ( value & AFMT_S8 ) {
|
|
||||||
format = AFMT_S8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
if ( value & AFMT_U16_LE ) {
|
|
||||||
format = AFMT_U16_LE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
if ( value & AFMT_U16_BE ) {
|
|
||||||
format = AFMT_U16_BE;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
default:
|
|
||||||
format = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( ! format ) {
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( format == 0 ) {
|
|
||||||
SDL_SetError("Couldn't find any hardware audio formats");
|
|
||||||
DSP_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
/* Set the audio format */
|
|
||||||
value = format;
|
|
||||||
if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
|
|
||||||
(value != format) ) {
|
|
||||||
perror("SNDCTL_DSP_SETFMT");
|
|
||||||
SDL_SetError("Couldn't set audio format");
|
|
||||||
DSP_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the number of channels of output */
|
|
||||||
value = spec->channels;
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
|
|
||||||
perror("SNDCTL_DSP_CHANNELS");
|
|
||||||
SDL_SetError("Cannot set the number of channels");
|
|
||||||
DSP_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->channels = value;
|
|
||||||
|
|
||||||
/* Set the DSP frequency */
|
|
||||||
value = spec->freq;
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
|
|
||||||
perror("SNDCTL_DSP_SPEED");
|
|
||||||
SDL_SetError("Couldn't set audio frequency");
|
|
||||||
DSP_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->freq = value;
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Determine the power of two of the fragment size */
|
|
||||||
for ( frag_spec = 0; (0x01U<<frag_spec) < spec->size; ++frag_spec );
|
|
||||||
if ( (0x01U<<frag_spec) != spec->size ) {
|
|
||||||
SDL_SetError("Fragment size must be a power of two");
|
|
||||||
DSP_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
frag_spec |= 0x00020000; /* two fragments, for low latency */
|
|
||||||
|
|
||||||
/* Set the audio buffering parameters */
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Requesting %d fragments of size %d\n",
|
|
||||||
(frag_spec >> 16), 1<<(frag_spec&0xFFFF));
|
|
||||||
#endif
|
|
||||||
if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
|
|
||||||
perror("SNDCTL_DSP_SETFRAGMENT");
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
{ audio_buf_info info;
|
|
||||||
ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
|
|
||||||
fprintf(stderr, "fragments = %d\n", info.fragments);
|
|
||||||
fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
|
|
||||||
fprintf(stderr, "fragsize = %d\n", info.fragsize);
|
|
||||||
fprintf(stderr, "bytes = %d\n", info.bytes);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixlen = spec->size;
|
|
||||||
mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
DSP_CloseAudio(this);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* Get the parent process id (we're the parent of the audio thread) */
|
|
||||||
parent = getpid();
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,53 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_dspaudio_h
|
|
||||||
#define _SDL_dspaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
int audio_fd;
|
|
||||||
|
|
||||||
/* The parent process id, to detect when application quits */
|
|
||||||
pid_t parent;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
int mixlen;
|
|
||||||
};
|
|
||||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define audio_fd (this->hidden->audio_fd)
|
|
||||||
#define parent (this->hidden->parent)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define mixlen (this->hidden->mixlen)
|
|
||||||
#define frame_ticks (this->hidden->frame_ticks)
|
|
||||||
#define next_frame (this->hidden->next_frame)
|
|
||||||
|
|
||||||
#endif /* _SDL_dspaudio_h */
|
|
||||||
|
|
@ -1,323 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to an ESD network stream mixing buffer */
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <esd.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_esdaudio.h"
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
|
|
||||||
#include "SDL_name.h"
|
|
||||||
#include "SDL_loadso.h"
|
|
||||||
#else
|
|
||||||
#define SDL_NAME(X) X
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The tag name used by ESD audio */
|
|
||||||
#define ESD_DRIVER_NAME "esd"
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int ESD_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void ESD_WaitAudio(_THIS);
|
|
||||||
static void ESD_PlayAudio(_THIS);
|
|
||||||
static Uint8 *ESD_GetAudioBuf(_THIS);
|
|
||||||
static void ESD_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_ESD_DYNAMIC
|
|
||||||
|
|
||||||
static const char *esd_library = SDL_AUDIO_DRIVER_ESD_DYNAMIC;
|
|
||||||
static void *esd_handle = NULL;
|
|
||||||
static int esd_loaded = 0;
|
|
||||||
|
|
||||||
static int (*SDL_NAME(esd_open_sound))( const char *host );
|
|
||||||
static int (*SDL_NAME(esd_close))( int esd );
|
|
||||||
static int (*SDL_NAME(esd_play_stream))( esd_format_t format, int rate,
|
|
||||||
const char *host, const char *name );
|
|
||||||
static struct {
|
|
||||||
const char *name;
|
|
||||||
void **func;
|
|
||||||
} esd_functions[] = {
|
|
||||||
{ "esd_open_sound", (void **)&SDL_NAME(esd_open_sound) },
|
|
||||||
{ "esd_close", (void **)&SDL_NAME(esd_close) },
|
|
||||||
{ "esd_play_stream", (void **)&SDL_NAME(esd_play_stream) },
|
|
||||||
};
|
|
||||||
|
|
||||||
static void UnloadESDLibrary()
|
|
||||||
{
|
|
||||||
if ( esd_loaded ) {
|
|
||||||
SDL_UnloadObject(esd_handle);
|
|
||||||
esd_handle = NULL;
|
|
||||||
esd_loaded = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int LoadESDLibrary(void)
|
|
||||||
{
|
|
||||||
int i, retval = -1;
|
|
||||||
|
|
||||||
esd_handle = SDL_LoadObject(esd_library);
|
|
||||||
if ( esd_handle ) {
|
|
||||||
esd_loaded = 1;
|
|
||||||
retval = 0;
|
|
||||||
for ( i=0; i<SDL_arraysize(esd_functions); ++i ) {
|
|
||||||
*esd_functions[i].func = SDL_LoadFunction(esd_handle, esd_functions[i].name);
|
|
||||||
if ( !*esd_functions[i].func ) {
|
|
||||||
retval = -1;
|
|
||||||
UnloadESDLibrary();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void UnloadESDLibrary()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int LoadESDLibrary(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_AUDIO_DRIVER_ESD_DYNAMIC */
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
int connection;
|
|
||||||
int available;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
if ( LoadESDLibrary() < 0 ) {
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
connection = SDL_NAME(esd_open_sound)(NULL);
|
|
||||||
if ( connection >= 0 ) {
|
|
||||||
available = 1;
|
|
||||||
SDL_NAME(esd_close)(connection);
|
|
||||||
}
|
|
||||||
UnloadESDLibrary();
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
UnloadESDLibrary();
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
LoadESDLibrary();
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
audio_fd = -1;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = ESD_OpenAudio;
|
|
||||||
this->WaitAudio = ESD_WaitAudio;
|
|
||||||
this->PlayAudio = ESD_PlayAudio;
|
|
||||||
this->GetAudioBuf = ESD_GetAudioBuf;
|
|
||||||
this->CloseAudio = ESD_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap ESD_bootstrap = {
|
|
||||||
ESD_DRIVER_NAME, "Enlightened Sound Daemon",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void ESD_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
Sint32 ticks;
|
|
||||||
|
|
||||||
/* Check to see if the thread-parent process is still alive */
|
|
||||||
{ static int cnt = 0;
|
|
||||||
/* Note that this only works with thread implementations
|
|
||||||
that use a different process id for each thread.
|
|
||||||
*/
|
|
||||||
if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
|
|
||||||
if ( kill(parent, 0) < 0 ) {
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Use timer for general audio synchronization */
|
|
||||||
ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
|
|
||||||
if ( ticks > 0 ) {
|
|
||||||
SDL_Delay(ticks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ESD_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
int written;
|
|
||||||
|
|
||||||
/* Write the audio data, checking for EAGAIN on broken audio drivers */
|
|
||||||
do {
|
|
||||||
written = write(audio_fd, mixbuf, mixlen);
|
|
||||||
if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) {
|
|
||||||
SDL_Delay(1); /* Let a little CPU time go by */
|
|
||||||
}
|
|
||||||
} while ( (written < 0) &&
|
|
||||||
((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) );
|
|
||||||
|
|
||||||
/* Set the next write frame */
|
|
||||||
next_frame += frame_ticks;
|
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
|
||||||
if ( written < 0 ) {
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *ESD_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ESD_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( audio_fd >= 0 ) {
|
|
||||||
SDL_NAME(esd_close)(audio_fd);
|
|
||||||
audio_fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to get the name of the program */
|
|
||||||
static char *get_progname(void)
|
|
||||||
{
|
|
||||||
char *progname = NULL;
|
|
||||||
#ifdef __LINUX__
|
|
||||||
FILE *fp;
|
|
||||||
static char temp[BUFSIZ];
|
|
||||||
|
|
||||||
SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
|
|
||||||
fp = fopen(temp, "r");
|
|
||||||
if ( fp != NULL ) {
|
|
||||||
if ( fgets(temp, sizeof(temp)-1, fp) ) {
|
|
||||||
progname = SDL_strrchr(temp, '/');
|
|
||||||
if ( progname == NULL ) {
|
|
||||||
progname = temp;
|
|
||||||
} else {
|
|
||||||
progname = progname+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return(progname);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int ESD_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
esd_format_t format;
|
|
||||||
|
|
||||||
/* Convert audio spec to the ESD audio format */
|
|
||||||
format = (ESD_STREAM | ESD_PLAY);
|
|
||||||
switch ( spec->format & 0xFF ) {
|
|
||||||
case 8:
|
|
||||||
format |= ESD_BITS8;
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
format |= ESD_BITS16;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SDL_SetError("Unsupported ESD audio format");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
if ( spec->channels == 1 ) {
|
|
||||||
format |= ESD_MONO;
|
|
||||||
} else {
|
|
||||||
format |= ESD_STEREO;
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
spec->samples = ESD_BUF_SIZE; /* Darn, no way to change this yet */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Open a connection to the ESD audio server */
|
|
||||||
audio_fd = SDL_NAME(esd_play_stream)(format, spec->freq, NULL, get_progname());
|
|
||||||
if ( audio_fd < 0 ) {
|
|
||||||
SDL_SetError("Couldn't open ESD connection");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
frame_ticks = (float)(spec->samples*1000)/spec->freq;
|
|
||||||
next_frame = SDL_GetTicks()+frame_ticks;
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixlen = spec->size;
|
|
||||||
mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* Get the parent process id (we're the parent of the audio thread) */
|
|
||||||
parent = getpid();
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_esdaudio_h
|
|
||||||
#define _SDL_esdaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
int audio_fd;
|
|
||||||
|
|
||||||
/* The parent process id, to detect when application quits */
|
|
||||||
pid_t parent;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
int mixlen;
|
|
||||||
|
|
||||||
/* Support for audio timing using a timer */
|
|
||||||
float frame_ticks;
|
|
||||||
float next_frame;
|
|
||||||
};
|
|
||||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define audio_fd (this->hidden->audio_fd)
|
|
||||||
#define parent (this->hidden->parent)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define mixlen (this->hidden->mixlen)
|
|
||||||
#define frame_ticks (this->hidden->frame_ticks)
|
|
||||||
#define next_frame (this->hidden->next_frame)
|
|
||||||
|
|
||||||
#endif /* _SDL_esdaudio_h */
|
|
||||||
|
|
@ -1,291 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#include <CoreAudio/CoreAudio.h>
|
|
||||||
#include <CoreServices/CoreServices.h>
|
|
||||||
#include <AudioUnit/AudioUnit.h>
|
|
||||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050
|
|
||||||
#include <AudioUnit/AUNTComponent.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
#include "SDL_coreaudio.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
|
|
||||||
static int Core_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Core_WaitAudio(_THIS);
|
|
||||||
static void Core_PlayAudio(_THIS);
|
|
||||||
static Uint8 *Core_GetAudioBuf(_THIS);
|
|
||||||
static void Core_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = Core_OpenAudio;
|
|
||||||
this->WaitAudio = Core_WaitAudio;
|
|
||||||
this->PlayAudio = Core_PlayAudio;
|
|
||||||
this->GetAudioBuf = Core_GetAudioBuf;
|
|
||||||
this->CloseAudio = Core_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap COREAUDIO_bootstrap = {
|
|
||||||
"coreaudio", "Mac OS X CoreAudio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* The CoreAudio callback */
|
|
||||||
static OSStatus audioCallback (void *inRefCon,
|
|
||||||
AudioUnitRenderActionFlags *ioActionFlags,
|
|
||||||
const AudioTimeStamp *inTimeStamp,
|
|
||||||
UInt32 inBusNumber,
|
|
||||||
UInt32 inNumberFrames,
|
|
||||||
AudioBufferList *ioData)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this = (SDL_AudioDevice *)inRefCon;
|
|
||||||
UInt32 remaining, len;
|
|
||||||
AudioBuffer *abuf;
|
|
||||||
void *ptr;
|
|
||||||
UInt32 i;
|
|
||||||
|
|
||||||
/* Only do anything if audio is enabled and not paused */
|
|
||||||
if ( ! this->enabled || this->paused ) {
|
|
||||||
for (i = 0; i < ioData->mNumberBuffers; i++) {
|
|
||||||
abuf = &ioData->mBuffers[i];
|
|
||||||
SDL_memset(abuf->mData, this->spec.silence, abuf->mDataByteSize);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No SDL conversion should be needed here, ever, since we accept
|
|
||||||
any input format in OpenAudio, and leave the conversion to CoreAudio.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
assert(!this->convert.needed);
|
|
||||||
assert(this->spec.channels == ioData->mNumberChannels);
|
|
||||||
*/
|
|
||||||
|
|
||||||
for (i = 0; i < ioData->mNumberBuffers; i++) {
|
|
||||||
abuf = &ioData->mBuffers[i];
|
|
||||||
remaining = abuf->mDataByteSize;
|
|
||||||
ptr = abuf->mData;
|
|
||||||
while (remaining > 0) {
|
|
||||||
if (bufferOffset >= bufferSize) {
|
|
||||||
/* Generate the data */
|
|
||||||
SDL_memset(buffer, this->spec.silence, bufferSize);
|
|
||||||
SDL_mutexP(this->mixer_lock);
|
|
||||||
(*this->spec.callback)(this->spec.userdata,
|
|
||||||
buffer, bufferSize);
|
|
||||||
SDL_mutexV(this->mixer_lock);
|
|
||||||
bufferOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
len = bufferSize - bufferOffset;
|
|
||||||
if (len > remaining)
|
|
||||||
len = remaining;
|
|
||||||
SDL_memcpy(ptr, (char *)buffer + bufferOffset, len);
|
|
||||||
ptr = (char *)ptr + len;
|
|
||||||
remaining -= len;
|
|
||||||
bufferOffset += len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dummy functions -- we don't use thread-based audio */
|
|
||||||
void Core_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8 *Core_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
OSStatus result;
|
|
||||||
struct AURenderCallbackStruct callback;
|
|
||||||
|
|
||||||
/* stop processing the audio unit */
|
|
||||||
result = AudioOutputUnitStop (outputAudioUnit);
|
|
||||||
if (result != noErr) {
|
|
||||||
SDL_SetError("Core_CloseAudio: AudioOutputUnitStop");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Remove the input callback */
|
|
||||||
callback.inputProc = 0;
|
|
||||||
callback.inputProcRefCon = 0;
|
|
||||||
result = AudioUnitSetProperty (outputAudioUnit,
|
|
||||||
kAudioUnitProperty_SetRenderCallback,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
0,
|
|
||||||
&callback,
|
|
||||||
sizeof(callback));
|
|
||||||
if (result != noErr) {
|
|
||||||
SDL_SetError("Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = CloseComponent(outputAudioUnit);
|
|
||||||
if (result != noErr) {
|
|
||||||
SDL_SetError("Core_CloseAudio: CloseComponent");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_free(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CHECK_RESULT(msg) \
|
|
||||||
if (result != noErr) { \
|
|
||||||
SDL_SetError("Failed to start CoreAudio: " msg); \
|
|
||||||
return -1; \
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int Core_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
OSStatus result = noErr;
|
|
||||||
Component comp;
|
|
||||||
ComponentDescription desc;
|
|
||||||
struct AURenderCallbackStruct callback;
|
|
||||||
AudioStreamBasicDescription requestedDesc;
|
|
||||||
|
|
||||||
/* Setup a AudioStreamBasicDescription with the requested format */
|
|
||||||
requestedDesc.mFormatID = kAudioFormatLinearPCM;
|
|
||||||
requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
|
|
||||||
requestedDesc.mChannelsPerFrame = spec->channels;
|
|
||||||
requestedDesc.mSampleRate = spec->freq;
|
|
||||||
|
|
||||||
requestedDesc.mBitsPerChannel = spec->format & 0xFF;
|
|
||||||
if (spec->format & 0x8000)
|
|
||||||
requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
|
|
||||||
if (spec->format & 0x1000)
|
|
||||||
requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
|
||||||
|
|
||||||
requestedDesc.mFramesPerPacket = 1;
|
|
||||||
requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
|
|
||||||
requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
|
|
||||||
|
|
||||||
|
|
||||||
/* Locate the default output audio unit */
|
|
||||||
desc.componentType = kAudioUnitType_Output;
|
|
||||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
|
||||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
||||||
desc.componentFlags = 0;
|
|
||||||
desc.componentFlagsMask = 0;
|
|
||||||
|
|
||||||
comp = FindNextComponent (NULL, &desc);
|
|
||||||
if (comp == NULL) {
|
|
||||||
SDL_SetError ("Failed to start CoreAudio: FindNextComponent returned NULL");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open & initialize the default output audio unit */
|
|
||||||
result = OpenAComponent (comp, &outputAudioUnit);
|
|
||||||
CHECK_RESULT("OpenAComponent")
|
|
||||||
|
|
||||||
result = AudioUnitInitialize (outputAudioUnit);
|
|
||||||
CHECK_RESULT("AudioUnitInitialize")
|
|
||||||
|
|
||||||
/* Set the input format of the audio unit. */
|
|
||||||
result = AudioUnitSetProperty (outputAudioUnit,
|
|
||||||
kAudioUnitProperty_StreamFormat,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
0,
|
|
||||||
&requestedDesc,
|
|
||||||
sizeof (requestedDesc));
|
|
||||||
CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)")
|
|
||||||
|
|
||||||
/* Set the audio callback */
|
|
||||||
callback.inputProc = audioCallback;
|
|
||||||
callback.inputProcRefCon = this;
|
|
||||||
result = AudioUnitSetProperty (outputAudioUnit,
|
|
||||||
kAudioUnitProperty_SetRenderCallback,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
0,
|
|
||||||
&callback,
|
|
||||||
sizeof(callback));
|
|
||||||
CHECK_RESULT("AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)")
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate a sample buffer */
|
|
||||||
bufferOffset = bufferSize = this->spec.size;
|
|
||||||
buffer = SDL_malloc(bufferSize);
|
|
||||||
|
|
||||||
/* Finally, start processing of the audio unit */
|
|
||||||
result = AudioOutputUnitStart (outputAudioUnit);
|
|
||||||
CHECK_RESULT("AudioOutputUnitStart")
|
|
||||||
|
|
||||||
|
|
||||||
/* We're running! */
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_coreaudio_h
|
|
||||||
#define _SDL_coreaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
AudioUnit outputAudioUnit;
|
|
||||||
void *buffer;
|
|
||||||
UInt32 bufferOffset;
|
|
||||||
UInt32 bufferSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define outputAudioUnit (this->hidden->outputAudioUnit)
|
|
||||||
#define buffer (this->hidden->buffer)
|
|
||||||
#define bufferOffset (this->hidden->bufferOffset)
|
|
||||||
#define bufferSize (this->hidden->bufferSize)
|
|
||||||
|
|
||||||
#endif /* _SDL_coreaudio_h */
|
|
||||||
|
|
@ -1,496 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#if defined(__APPLE__) && defined(__MACH__)
|
|
||||||
# include <Carbon/Carbon.h>
|
|
||||||
#elif TARGET_API_MAC_CARBON && (UNIVERSAL_INTERFACES_VERSION > 0x0335)
|
|
||||||
# include <Carbon.h>
|
|
||||||
#else
|
|
||||||
# include <Sound.h> /* SoundManager interface */
|
|
||||||
# include <Gestalt.h>
|
|
||||||
# include <DriverServices.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if !defined(NewSndCallBackUPP) && (UNIVERSAL_INTERFACES_VERSION < 0x0335)
|
|
||||||
#if !defined(NewSndCallBackProc) /* avoid circular redefinition... */
|
|
||||||
#define NewSndCallBackUPP NewSndCallBackProc
|
|
||||||
#endif
|
|
||||||
#if !defined(NewSndCallBackUPP)
|
|
||||||
#define NewSndCallBackUPP NewSndCallBackProc
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
#include "SDL_romaudio.h"
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
|
|
||||||
static void Mac_CloseAudio(_THIS);
|
|
||||||
static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mac_LockAudio(_THIS);
|
|
||||||
static void Mac_UnlockAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = Mac_OpenAudio;
|
|
||||||
this->CloseAudio = Mac_CloseAudio;
|
|
||||||
this->LockAudio = Mac_LockAudio;
|
|
||||||
this->UnlockAudio = Mac_UnlockAudio;
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
#ifdef __MACOSX__ /* Mac OS X uses threaded audio, so normal thread code is okay */
|
|
||||||
this->LockAudio = NULL;
|
|
||||||
this->UnlockAudio = NULL;
|
|
||||||
#endif
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap SNDMGR_bootstrap = {
|
|
||||||
"sndmgr", "MacOS SoundManager 3.0",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
#if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE)
|
|
||||||
/* This works correctly on Mac OS X */
|
|
||||||
|
|
||||||
#pragma options align=power
|
|
||||||
|
|
||||||
static volatile SInt32 audio_is_locked = 0;
|
|
||||||
static volatile SInt32 need_to_mix = 0;
|
|
||||||
|
|
||||||
static UInt8 *buffer[2];
|
|
||||||
static volatile UInt32 running = 0;
|
|
||||||
static CmpSoundHeader header;
|
|
||||||
static volatile Uint32 fill_me = 0;
|
|
||||||
|
|
||||||
static void mix_buffer(SDL_AudioDevice *audio, UInt8 *buffer)
|
|
||||||
{
|
|
||||||
if ( ! audio->paused ) {
|
|
||||||
#ifdef __MACOSX__
|
|
||||||
SDL_mutexP(audio->mixer_lock);
|
|
||||||
#endif
|
|
||||||
if ( audio->convert.needed ) {
|
|
||||||
audio->spec.callback(audio->spec.userdata,
|
|
||||||
(Uint8 *)audio->convert.buf,audio->convert.len);
|
|
||||||
SDL_ConvertAudio(&audio->convert);
|
|
||||||
if ( audio->convert.len_cvt != audio->spec.size ) {
|
|
||||||
/* Uh oh... probably crashes here */;
|
|
||||||
}
|
|
||||||
SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
|
|
||||||
} else {
|
|
||||||
audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size);
|
|
||||||
}
|
|
||||||
#ifdef __MACOSX__
|
|
||||||
SDL_mutexV(audio->mixer_lock);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
DecrementAtomic((SInt32 *) &need_to_mix);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mac_LockAudio(_THIS)
|
|
||||||
{
|
|
||||||
IncrementAtomic((SInt32 *) &audio_is_locked);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mac_UnlockAudio(_THIS)
|
|
||||||
{
|
|
||||||
SInt32 oldval;
|
|
||||||
|
|
||||||
oldval = DecrementAtomic((SInt32 *) &audio_is_locked);
|
|
||||||
if ( oldval != 1 ) /* != 1 means audio is still locked. */
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Did we miss the chance to mix in an interrupt? Do it now. */
|
|
||||||
if ( BitAndAtomic (0xFFFFFFFF, (UInt32 *) &need_to_mix) ) {
|
|
||||||
/*
|
|
||||||
* Note that this could be a problem if you missed an interrupt
|
|
||||||
* while the audio was locked, and get preempted by a second
|
|
||||||
* interrupt here, but that means you locked for way too long anyhow.
|
|
||||||
*/
|
|
||||||
mix_buffer (this, buffer[fill_me]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void callBackProc (SndChannel *chan, SndCommand *cmd_passed ) {
|
|
||||||
UInt32 play_me;
|
|
||||||
SndCommand cmd;
|
|
||||||
SDL_AudioDevice *audio = (SDL_AudioDevice *)chan->userInfo;
|
|
||||||
|
|
||||||
IncrementAtomic((SInt32 *) &need_to_mix);
|
|
||||||
|
|
||||||
fill_me = cmd_passed->param2; /* buffer that has just finished playing, so fill it */
|
|
||||||
play_me = ! fill_me; /* filled buffer to play _now_ */
|
|
||||||
|
|
||||||
if ( ! audio->enabled ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* queue previously mixed buffer for playback. */
|
|
||||||
header.samplePtr = (Ptr)buffer[play_me];
|
|
||||||
cmd.cmd = bufferCmd;
|
|
||||||
cmd.param1 = 0;
|
|
||||||
cmd.param2 = (long)&header;
|
|
||||||
SndDoCommand (chan, &cmd, 0);
|
|
||||||
|
|
||||||
memset (buffer[fill_me], 0, audio->spec.size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if audio device isn't locked, mix the next buffer to be queued in
|
|
||||||
* the memory block that just finished playing.
|
|
||||||
*/
|
|
||||||
if ( ! BitAndAtomic(0xFFFFFFFF, (UInt32 *) &audio_is_locked) ) {
|
|
||||||
mix_buffer (audio, buffer[fill_me]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set this callback to run again when current buffer drains. */
|
|
||||||
if ( running ) {
|
|
||||||
cmd.cmd = callBackCmd;
|
|
||||||
cmd.param1 = 0;
|
|
||||||
cmd.param2 = play_me;
|
|
||||||
|
|
||||||
SndDoCommand (chan, &cmd, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec) {
|
|
||||||
|
|
||||||
SndCallBackUPP callback;
|
|
||||||
int sample_bits;
|
|
||||||
int i;
|
|
||||||
long initOptions;
|
|
||||||
|
|
||||||
/* Very few conversions are required, but... */
|
|
||||||
switch (spec->format) {
|
|
||||||
case AUDIO_S8:
|
|
||||||
spec->format = AUDIO_U8;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
spec->format = AUDIO_S16LSB;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
spec->format = AUDIO_S16MSB;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* initialize bufferCmd header */
|
|
||||||
memset (&header, 0, sizeof(header));
|
|
||||||
callback = (SndCallBackUPP) NewSndCallBackUPP (callBackProc);
|
|
||||||
sample_bits = spec->size / spec->samples / spec->channels * 8;
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr,
|
|
||||||
"Audio format 0x%x, channels = %d, sample_bits = %d, frequency = %d\n",
|
|
||||||
spec->format, spec->channels, sample_bits, spec->freq);
|
|
||||||
#endif /* DEBUG_AUDIO */
|
|
||||||
|
|
||||||
header.numChannels = spec->channels;
|
|
||||||
header.sampleSize = sample_bits;
|
|
||||||
header.sampleRate = spec->freq << 16;
|
|
||||||
header.numFrames = spec->samples;
|
|
||||||
header.encode = cmpSH;
|
|
||||||
|
|
||||||
/* Note that we install the 16bitLittleEndian Converter if needed. */
|
|
||||||
if ( spec->format == 0x8010 ) {
|
|
||||||
header.compressionID = fixedCompression;
|
|
||||||
header.format = k16BitLittleEndianFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate 2 buffers */
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
buffer[i] = (UInt8*)malloc (sizeof(UInt8) * spec->size);
|
|
||||||
if (buffer[i] == NULL) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
memset (buffer[i], 0, spec->size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the sound manager channel */
|
|
||||||
channel = (SndChannelPtr)SDL_malloc(sizeof(*channel));
|
|
||||||
if ( channel == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
if ( spec->channels >= 2 ) {
|
|
||||||
initOptions = initStereo;
|
|
||||||
} else {
|
|
||||||
initOptions = initMono;
|
|
||||||
}
|
|
||||||
channel->userInfo = (long)this;
|
|
||||||
channel->qLength = 128;
|
|
||||||
if ( SndNewChannel(&channel, sampledSynth, initOptions, callback) != noErr ) {
|
|
||||||
SDL_SetError("Unable to create audio channel");
|
|
||||||
SDL_free(channel);
|
|
||||||
channel = NULL;
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* start playback */
|
|
||||||
{
|
|
||||||
SndCommand cmd;
|
|
||||||
cmd.cmd = callBackCmd;
|
|
||||||
cmd.param2 = 0;
|
|
||||||
running = 1;
|
|
||||||
SndDoCommand (channel, &cmd, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mac_CloseAudio(_THIS) {
|
|
||||||
|
|
||||||
int i;
|
|
||||||
|
|
||||||
running = 0;
|
|
||||||
|
|
||||||
if (channel) {
|
|
||||||
SndDisposeChannel (channel, true);
|
|
||||||
channel = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( i=0; i<2; ++i ) {
|
|
||||||
if ( buffer[i] ) {
|
|
||||||
SDL_free(buffer[i]);
|
|
||||||
buffer[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#else /* !TARGET_API_MAC_CARBON && !USE_RYANS_SOUNDCODE */
|
|
||||||
|
|
||||||
static void Mac_LockAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* no-op. */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mac_UnlockAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* no-op. */
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* This function is called by Sound Manager when it has exhausted one of
|
|
||||||
the buffers, so we'll zero it to silence and fill it with audio if
|
|
||||||
we're not paused.
|
|
||||||
*/
|
|
||||||
static pascal
|
|
||||||
void sndDoubleBackProc (SndChannelPtr chan, SndDoubleBufferPtr newbuf)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *audio = (SDL_AudioDevice *)newbuf->dbUserInfo[0];
|
|
||||||
|
|
||||||
/* If audio is quitting, don't do anything */
|
|
||||||
if ( ! audio->enabled ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memset (newbuf->dbSoundData, 0, audio->spec.size);
|
|
||||||
newbuf->dbNumFrames = audio->spec.samples;
|
|
||||||
if ( ! audio->paused ) {
|
|
||||||
if ( audio->convert.needed ) {
|
|
||||||
audio->spec.callback(audio->spec.userdata,
|
|
||||||
(Uint8 *)audio->convert.buf,audio->convert.len);
|
|
||||||
SDL_ConvertAudio(&audio->convert);
|
|
||||||
#if 0
|
|
||||||
if ( audio->convert.len_cvt != audio->spec.size ) {
|
|
||||||
/* Uh oh... probably crashes here */;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
SDL_memcpy(newbuf->dbSoundData, audio->convert.buf,
|
|
||||||
audio->convert.len_cvt);
|
|
||||||
} else {
|
|
||||||
audio->spec.callback(audio->spec.userdata,
|
|
||||||
(Uint8 *)newbuf->dbSoundData, audio->spec.size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newbuf->dbFlags |= dbBufferReady;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int DoubleBufferAudio_Available(void)
|
|
||||||
{
|
|
||||||
int available;
|
|
||||||
NumVersion sndversion;
|
|
||||||
long response;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
sndversion = SndSoundManagerVersion();
|
|
||||||
if ( sndversion.majorRev >= 3 ) {
|
|
||||||
if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
|
|
||||||
if ( (response & (1 << gestaltSndPlayDoubleBuffer)) ) {
|
|
||||||
available = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ( Gestalt(gestaltSoundAttr, &response) == noErr ) {
|
|
||||||
if ( (response & (1 << gestaltHasASC)) ) {
|
|
||||||
available = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mac_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( channel != NULL ) {
|
|
||||||
/* Clean up the audio channel */
|
|
||||||
SndDisposeChannel(channel, true);
|
|
||||||
channel = NULL;
|
|
||||||
}
|
|
||||||
for ( i=0; i<2; ++i ) {
|
|
||||||
if ( audio_buf[i] ) {
|
|
||||||
SDL_free(audio_buf[i]);
|
|
||||||
audio_buf[i] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mac_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
SndDoubleBufferHeader2 audio_dbh;
|
|
||||||
int i;
|
|
||||||
long initOptions;
|
|
||||||
int sample_bits;
|
|
||||||
SndDoubleBackUPP doubleBackProc;
|
|
||||||
|
|
||||||
/* Check to make sure double-buffered audio is available */
|
|
||||||
if ( ! DoubleBufferAudio_Available() ) {
|
|
||||||
SDL_SetError("Sound manager doesn't support double-buffering");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Very few conversions are required, but... */
|
|
||||||
switch (spec->format) {
|
|
||||||
case AUDIO_S8:
|
|
||||||
spec->format = AUDIO_U8;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
spec->format = AUDIO_S16LSB;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
spec->format = AUDIO_S16MSB;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* initialize the double-back header */
|
|
||||||
SDL_memset(&audio_dbh, 0, sizeof(audio_dbh));
|
|
||||||
doubleBackProc = NewSndDoubleBackProc (sndDoubleBackProc);
|
|
||||||
sample_bits = spec->size / spec->samples / spec->channels * 8;
|
|
||||||
|
|
||||||
audio_dbh.dbhNumChannels = spec->channels;
|
|
||||||
audio_dbh.dbhSampleSize = sample_bits;
|
|
||||||
audio_dbh.dbhCompressionID = 0;
|
|
||||||
audio_dbh.dbhPacketSize = 0;
|
|
||||||
audio_dbh.dbhSampleRate = spec->freq << 16;
|
|
||||||
audio_dbh.dbhDoubleBack = doubleBackProc;
|
|
||||||
audio_dbh.dbhFormat = 0;
|
|
||||||
|
|
||||||
/* Note that we install the 16bitLittleEndian Converter if needed. */
|
|
||||||
if ( spec->format == 0x8010 ) {
|
|
||||||
audio_dbh.dbhCompressionID = fixedCompression;
|
|
||||||
audio_dbh.dbhFormat = k16BitLittleEndianFormat;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* allocate the 2 double-back buffers */
|
|
||||||
for ( i=0; i<2; ++i ) {
|
|
||||||
audio_buf[i] = SDL_calloc(1, sizeof(SndDoubleBuffer)+spec->size);
|
|
||||||
if ( audio_buf[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
audio_buf[i]->dbNumFrames = spec->samples;
|
|
||||||
audio_buf[i]->dbFlags = dbBufferReady;
|
|
||||||
audio_buf[i]->dbUserInfo[0] = (long)this;
|
|
||||||
audio_dbh.dbhBufferPtr[i] = audio_buf[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the sound manager channel */
|
|
||||||
channel = (SndChannelPtr)SDL_malloc(sizeof(*channel));
|
|
||||||
if ( channel == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
if ( spec->channels >= 2 ) {
|
|
||||||
initOptions = initStereo;
|
|
||||||
} else {
|
|
||||||
initOptions = initMono;
|
|
||||||
}
|
|
||||||
channel->userInfo = 0;
|
|
||||||
channel->qLength = 128;
|
|
||||||
if ( SndNewChannel(&channel, sampledSynth, initOptions, 0L) != noErr ) {
|
|
||||||
SDL_SetError("Unable to create audio channel");
|
|
||||||
SDL_free(channel);
|
|
||||||
channel = NULL;
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start playback */
|
|
||||||
if ( SndPlayDoubleBuffer(channel, (SndDoubleBufferHeaderPtr)&audio_dbh)
|
|
||||||
!= noErr ) {
|
|
||||||
SDL_SetError("Unable to play double buffered audio");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* TARGET_API_MAC_CARBON || USE_RYANS_SOUNDCODE */
|
|
||||||
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_romaudio_h
|
|
||||||
#define _SDL_romaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* This is Ryan's improved MacOS sound code, with locking support */
|
|
||||||
#define USE_RYANS_SOUNDCODE
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* Sound manager audio channel */
|
|
||||||
SndChannelPtr channel;
|
|
||||||
#if defined(TARGET_API_MAC_CARBON) || defined(USE_RYANS_SOUNDCODE)
|
|
||||||
/* FIXME: Add Ryan's static data here */
|
|
||||||
#else
|
|
||||||
/* Double buffering variables */
|
|
||||||
SndDoubleBufferPtr audio_buf[2];
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define channel (this->hidden->channel)
|
|
||||||
#define audio_buf (this->hidden->audio_buf)
|
|
||||||
|
|
||||||
#endif /* _SDL_romaudio_h */
|
|
||||||
|
|
@ -1,215 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
Audio interrupt variables and callback function
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <mint/osbind.h>
|
|
||||||
#include <mint/falcon.h>
|
|
||||||
#include <mint/mintbind.h>
|
|
||||||
#include <mint/cookie.h>
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "SDL_mintaudio.h"
|
|
||||||
#include "SDL_mintaudio_stfa.h"
|
|
||||||
|
|
||||||
/* The audio device */
|
|
||||||
|
|
||||||
SDL_AudioDevice *SDL_MintAudio_device;
|
|
||||||
Uint8 *SDL_MintAudio_audiobuf[2]; /* Pointers to buffers */
|
|
||||||
unsigned long SDL_MintAudio_audiosize; /* Length of audio buffer=spec->size */
|
|
||||||
volatile unsigned short SDL_MintAudio_numbuf; /* Buffer to play */
|
|
||||||
volatile unsigned short SDL_MintAudio_mutex;
|
|
||||||
volatile unsigned long SDL_MintAudio_clocktics;
|
|
||||||
cookie_stfa_t *SDL_MintAudio_stfa;
|
|
||||||
unsigned short SDL_MintAudio_hasfpu;
|
|
||||||
|
|
||||||
/* MiNT thread variables */
|
|
||||||
SDL_bool SDL_MintAudio_mint_present;
|
|
||||||
SDL_bool SDL_MintAudio_quit_thread;
|
|
||||||
SDL_bool SDL_MintAudio_thread_finished;
|
|
||||||
long SDL_MintAudio_thread_pid;
|
|
||||||
|
|
||||||
/* The callback function, called by each driver whenever needed */
|
|
||||||
|
|
||||||
void SDL_MintAudio_Callback(void)
|
|
||||||
{
|
|
||||||
Uint8 *buffer;
|
|
||||||
SDL_AudioDevice *audio = SDL_MintAudio_device;
|
|
||||||
|
|
||||||
buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
|
|
||||||
SDL_memset(buffer, audio->spec.silence, audio->spec.size);
|
|
||||||
|
|
||||||
if (audio->paused)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (audio->convert.needed) {
|
|
||||||
int silence;
|
|
||||||
|
|
||||||
if ( audio->convert.src_format == AUDIO_U8 ) {
|
|
||||||
silence = 0x80;
|
|
||||||
} else {
|
|
||||||
silence = 0;
|
|
||||||
}
|
|
||||||
SDL_memset(audio->convert.buf, silence, audio->convert.len);
|
|
||||||
audio->spec.callback(audio->spec.userdata,
|
|
||||||
(Uint8 *)audio->convert.buf,audio->convert.len);
|
|
||||||
SDL_ConvertAudio(&audio->convert);
|
|
||||||
SDL_memcpy(buffer, audio->convert.buf, audio->convert.len_cvt);
|
|
||||||
} else {
|
|
||||||
audio->spec.callback(audio->spec.userdata, buffer, audio->spec.size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a new frequency/clock/predivisor to the current list */
|
|
||||||
void SDL_MintAudio_AddFrequency(_THIS, Uint32 frequency, Uint32 clock,
|
|
||||||
Uint32 prediv, int gpio_bits)
|
|
||||||
{
|
|
||||||
int i, p;
|
|
||||||
|
|
||||||
if (MINTAUDIO_freqcount==MINTAUDIO_maxfreqs) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search where to insert the frequency (highest first) */
|
|
||||||
for (p=0; p<MINTAUDIO_freqcount; p++) {
|
|
||||||
if (frequency > MINTAUDIO_frequencies[p].frequency) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Put all following ones farer */
|
|
||||||
if (MINTAUDIO_freqcount>0) {
|
|
||||||
for (i=MINTAUDIO_freqcount; i>p; i--) {
|
|
||||||
SDL_memcpy(&MINTAUDIO_frequencies[i], &MINTAUDIO_frequencies[i-1], sizeof(mint_frequency_t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* And insert new one */
|
|
||||||
MINTAUDIO_frequencies[p].frequency = frequency;
|
|
||||||
MINTAUDIO_frequencies[p].masterclock = clock;
|
|
||||||
MINTAUDIO_frequencies[p].predivisor = prediv;
|
|
||||||
MINTAUDIO_frequencies[p].gpio_bits = gpio_bits;
|
|
||||||
|
|
||||||
MINTAUDIO_freqcount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search for the nearest frequency */
|
|
||||||
int SDL_MintAudio_SearchFrequency(_THIS, int desired_freq)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Only 1 freq ? */
|
|
||||||
if (MINTAUDIO_freqcount==1) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check the array */
|
|
||||||
for (i=0; i<MINTAUDIO_freqcount; i++) {
|
|
||||||
if (desired_freq >= ((MINTAUDIO_frequencies[i].frequency+
|
|
||||||
MINTAUDIO_frequencies[i+1].frequency)>>1)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Not in the array, give the latest */
|
|
||||||
return MINTAUDIO_freqcount-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if FPU is present */
|
|
||||||
void SDL_MintAudio_CheckFpu(void)
|
|
||||||
{
|
|
||||||
long cookie_fpu;
|
|
||||||
|
|
||||||
SDL_MintAudio_hasfpu = 0;
|
|
||||||
if (Getcookie(C__FPU, &cookie_fpu) != C_FOUND) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch ((cookie_fpu>>16)&0xfffe) {
|
|
||||||
case 2:
|
|
||||||
case 4:
|
|
||||||
case 6:
|
|
||||||
case 8:
|
|
||||||
case 16:
|
|
||||||
SDL_MintAudio_hasfpu = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The thread function, used under MiNT with xbios */
|
|
||||||
int SDL_MintAudio_Thread(long param)
|
|
||||||
{
|
|
||||||
SndBufPtr pointers;
|
|
||||||
SDL_bool buffers_filled[2] = {SDL_FALSE, SDL_FALSE};
|
|
||||||
|
|
||||||
SDL_MintAudio_thread_finished = SDL_FALSE;
|
|
||||||
while (!SDL_MintAudio_quit_thread) {
|
|
||||||
if (Buffptr(&pointers)!=0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (( (unsigned long)pointers.play>=(unsigned long)SDL_MintAudio_audiobuf[0])
|
|
||||||
&& ( (unsigned long)pointers.play<=(unsigned long)SDL_MintAudio_audiobuf[1]))
|
|
||||||
{
|
|
||||||
/* DMA is reading buffer #0, setup buffer #1 if not already done */
|
|
||||||
if (!buffers_filled[1]) {
|
|
||||||
SDL_MintAudio_numbuf = 1;
|
|
||||||
SDL_MintAudio_Callback();
|
|
||||||
Setbuffer(0, SDL_MintAudio_audiobuf[1], SDL_MintAudio_audiobuf[1] + SDL_MintAudio_audiosize);
|
|
||||||
buffers_filled[1]=SDL_TRUE;
|
|
||||||
buffers_filled[0]=SDL_FALSE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* DMA is reading buffer #1, setup buffer #0 if not already done */
|
|
||||||
if (!buffers_filled[0]) {
|
|
||||||
SDL_MintAudio_numbuf = 0;
|
|
||||||
SDL_MintAudio_Callback();
|
|
||||||
Setbuffer(0, SDL_MintAudio_audiobuf[0], SDL_MintAudio_audiobuf[0] + SDL_MintAudio_audiosize);
|
|
||||||
buffers_filled[0]=SDL_TRUE;
|
|
||||||
buffers_filled[1]=SDL_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
usleep(100);
|
|
||||||
}
|
|
||||||
SDL_MintAudio_thread_finished = SDL_TRUE;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_MintAudio_WaitThread(void)
|
|
||||||
{
|
|
||||||
if (!SDL_MintAudio_mint_present)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (SDL_MintAudio_thread_finished)
|
|
||||||
return;
|
|
||||||
|
|
||||||
SDL_MintAudio_quit_thread = SDL_TRUE;
|
|
||||||
while (!SDL_MintAudio_thread_finished) {
|
|
||||||
Syield();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
MiNT audio driver
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SDL_mintaudio_h
|
|
||||||
#define _SDL_mintaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
#include "SDL_mintaudio_stfa.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the audio functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
/* 16 predivisors with 3 clocks max. */
|
|
||||||
#define MINTAUDIO_maxfreqs (16*3)
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
Uint32 frequency;
|
|
||||||
Uint32 masterclock;
|
|
||||||
Uint32 predivisor;
|
|
||||||
int gpio_bits; /* in case of external clock */
|
|
||||||
} mint_frequency_t;
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
mint_frequency_t frequencies[MINTAUDIO_maxfreqs];
|
|
||||||
int freq_count; /* Number of frequencies in the array */
|
|
||||||
int numfreq; /* Number of selected frequency */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
|
|
||||||
#define MINTAUDIO_frequencies (this->hidden->frequencies)
|
|
||||||
#define MINTAUDIO_freqcount (this->hidden->freq_count)
|
|
||||||
#define MINTAUDIO_numfreq (this->hidden->numfreq)
|
|
||||||
|
|
||||||
/* _MCH cookie (values>>16) */
|
|
||||||
enum {
|
|
||||||
MCH_ST=0,
|
|
||||||
MCH_STE,
|
|
||||||
MCH_TT,
|
|
||||||
MCH_F30,
|
|
||||||
MCH_CLONE,
|
|
||||||
MCH_ARANYM
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Master clocks for replay frequencies */
|
|
||||||
#define MASTERCLOCK_STE 8010666 /* Not sure of this one */
|
|
||||||
#define MASTERCLOCK_TT 16107953 /* Not sure of this one */
|
|
||||||
#define MASTERCLOCK_FALCON1 25175000
|
|
||||||
#define MASTERCLOCK_FALCON2 32000000 /* Only usable for DSP56K */
|
|
||||||
#define MASTERCLOCK_FALCONEXT -1 /* Clock on DSP56K port, unknown */
|
|
||||||
#define MASTERCLOCK_44K 22579200 /* Standard clock for 44.1 Khz */
|
|
||||||
#define MASTERCLOCK_48K 24576000 /* Standard clock for 48 Khz */
|
|
||||||
|
|
||||||
/* Master clock predivisors */
|
|
||||||
#define MASTERPREDIV_STE 160
|
|
||||||
#define MASTERPREDIV_TT 320
|
|
||||||
#define MASTERPREDIV_FALCON 256
|
|
||||||
#define MASTERPREDIV_MILAN 256
|
|
||||||
|
|
||||||
/* Variables */
|
|
||||||
extern SDL_AudioDevice *SDL_MintAudio_device;
|
|
||||||
extern Uint8 *SDL_MintAudio_audiobuf[2]; /* Pointers to buffers */
|
|
||||||
extern unsigned long SDL_MintAudio_audiosize; /* Length of audio buffer=spec->size */
|
|
||||||
extern volatile unsigned short SDL_MintAudio_numbuf; /* Buffer to play */
|
|
||||||
extern volatile unsigned short SDL_MintAudio_mutex;
|
|
||||||
extern cookie_stfa_t *SDL_MintAudio_stfa;
|
|
||||||
extern volatile unsigned long SDL_MintAudio_clocktics;
|
|
||||||
extern unsigned short SDL_MintAudio_hasfpu; /* To preserve fpu registers if needed */
|
|
||||||
|
|
||||||
/* MiNT thread variables */
|
|
||||||
extern SDL_bool SDL_MintAudio_mint_present;
|
|
||||||
extern SDL_bool SDL_MintAudio_quit_thread;
|
|
||||||
extern SDL_bool SDL_MintAudio_thread_finished;
|
|
||||||
extern long SDL_MintAudio_thread_pid;
|
|
||||||
|
|
||||||
/* Functions */
|
|
||||||
void SDL_MintAudio_Callback(void);
|
|
||||||
void SDL_MintAudio_AddFrequency(_THIS, Uint32 frequency, Uint32 clock,
|
|
||||||
Uint32 prediv, int gpio_bits);
|
|
||||||
int SDL_MintAudio_SearchFrequency(_THIS, int desired_freq);
|
|
||||||
void SDL_MintAudio_CheckFpu(void);
|
|
||||||
|
|
||||||
/* MiNT thread functions */
|
|
||||||
int SDL_MintAudio_Thread(long param);
|
|
||||||
void SDL_MintAudio_WaitThread(void);
|
|
||||||
|
|
||||||
/* ASM interrupt functions */
|
|
||||||
void SDL_MintAudio_GsxbInterrupt(void);
|
|
||||||
void SDL_MintAudio_EmptyGsxbInterrupt(void);
|
|
||||||
void SDL_MintAudio_XbiosInterruptMeasureClock(void);
|
|
||||||
void SDL_MintAudio_XbiosInterrupt(void);
|
|
||||||
void SDL_MintAudio_Dma8Interrupt(void);
|
|
||||||
void SDL_MintAudio_StfaInterrupt(void);
|
|
||||||
|
|
||||||
#endif /* _SDL_mintaudio_h */
|
|
||||||
|
|
@ -1,357 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
MiNT audio driver
|
|
||||||
using DMA 8bits (hardware access)
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Mint includes */
|
|
||||||
#include <mint/osbind.h>
|
|
||||||
#include <mint/falcon.h>
|
|
||||||
#include <mint/cookie.h>
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
#include "../../video/ataricommon/SDL_atarimxalloc_c.h"
|
|
||||||
|
|
||||||
#include "SDL_mintaudio.h"
|
|
||||||
#include "SDL_mintaudio_dma8.h"
|
|
||||||
|
|
||||||
/*--- Defines ---*/
|
|
||||||
|
|
||||||
#define MINT_AUDIO_DRIVER_NAME "mint_dma8"
|
|
||||||
|
|
||||||
/* Debug print info */
|
|
||||||
#define DEBUG_NAME "audio:dma8: "
|
|
||||||
#if 0
|
|
||||||
#define DEBUG_PRINT(what) \
|
|
||||||
{ \
|
|
||||||
printf what; \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define DEBUG_PRINT(what)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*--- Static variables ---*/
|
|
||||||
|
|
||||||
static long cookie_snd, cookie_mch;
|
|
||||||
|
|
||||||
/*--- Audio driver functions ---*/
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS);
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_LockAudio(_THIS);
|
|
||||||
static void Mint_UnlockAudio(_THIS);
|
|
||||||
|
|
||||||
/* To check/init hardware audio */
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
|
|
||||||
/* Functions called in supervisor mode */
|
|
||||||
static void Mint_InitDma(void);
|
|
||||||
static void Mint_StopReplay(void);
|
|
||||||
static void Mint_StartReplay(void);
|
|
||||||
|
|
||||||
/*--- Audio driver bootstrap functions ---*/
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
const char *envr = SDL_getenv("SDL_AUDIODRIVER");
|
|
||||||
|
|
||||||
/* Check if user asked a different audio driver */
|
|
||||||
if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie _MCH present ? if not, assume ST machine */
|
|
||||||
if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) {
|
|
||||||
cookie_mch = MCH_ST;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie _SND present ? if not, assume ST machine */
|
|
||||||
if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
|
|
||||||
cookie_snd = SND_PSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we have 8 bits audio */
|
|
||||||
if ((cookie_snd & SND_8BIT)==0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "no 8 bits sound\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if audio is lockable */
|
|
||||||
if (cookie_snd & SND_16BIT) {
|
|
||||||
if (Locksnd()!=1) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unlocksnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "8 bits audio available!\n"));
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = Mint_OpenAudio;
|
|
||||||
this->CloseAudio = Mint_CloseAudio;
|
|
||||||
this->LockAudio = Mint_LockAudio;
|
|
||||||
this->UnlockAudio = Mint_UnlockAudio;
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap MINTAUDIO_DMA8_bootstrap = {
|
|
||||||
MINT_AUDIO_DRIVER_NAME, "MiNT DMA 8 bits audio driver",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
static void Mint_LockAudio(_THIS)
|
|
||||||
{
|
|
||||||
Supexec(Mint_StopReplay);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_UnlockAudio(_THIS)
|
|
||||||
{
|
|
||||||
Supexec(Mint_StartReplay);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
Supexec(Mint_StopReplay);
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "closeaudio: replay stopped\n"));
|
|
||||||
|
|
||||||
/* Disable interrupt */
|
|
||||||
Jdisint(MFP_DMASOUND);
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "closeaudio: interrupt disabled\n"));
|
|
||||||
|
|
||||||
/* Wait if currently playing sound */
|
|
||||||
while (SDL_MintAudio_mutex != 0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "closeaudio: no more interrupt running\n"));
|
|
||||||
|
|
||||||
/* Clear buffers */
|
|
||||||
if (SDL_MintAudio_audiobuf[0]) {
|
|
||||||
Mfree(SDL_MintAudio_audiobuf[0]);
|
|
||||||
SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "closeaudio: buffers freed\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int i, masterprediv, sfreq;
|
|
||||||
unsigned long masterclock;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
if (spec->channels > 2)
|
|
||||||
spec->channels = 2;
|
|
||||||
|
|
||||||
/* Check formats available */
|
|
||||||
spec->format = AUDIO_S8;
|
|
||||||
|
|
||||||
/* Calculate and select the closest frequency */
|
|
||||||
sfreq=0;
|
|
||||||
masterclock=MASTERCLOCK_STE;
|
|
||||||
masterprediv=MASTERPREDIV_STE;
|
|
||||||
switch(cookie_mch>>16) {
|
|
||||||
/*
|
|
||||||
case MCH_STE:
|
|
||||||
masterclock=MASTERCLOCK_STE;
|
|
||||||
masterprediv=MASTERPREDIV_STE;
|
|
||||||
break;
|
|
||||||
*/
|
|
||||||
case MCH_TT:
|
|
||||||
masterclock=MASTERCLOCK_TT;
|
|
||||||
masterprediv=MASTERPREDIV_TT;
|
|
||||||
break;
|
|
||||||
case MCH_F30:
|
|
||||||
case MCH_ARANYM:
|
|
||||||
masterclock=MASTERCLOCK_FALCON1;
|
|
||||||
masterprediv=MASTERPREDIV_FALCON;
|
|
||||||
sfreq=1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
MINTAUDIO_freqcount=0;
|
|
||||||
for (i=sfreq;i<4;i++) {
|
|
||||||
SDL_MintAudio_AddFrequency(this, masterclock/(masterprediv*(1<<i)),
|
|
||||||
masterclock, i-sfreq, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
for (i=0; i<MINTAUDIO_freqcount; i++) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
|
|
||||||
i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock,
|
|
||||||
MINTAUDIO_frequencies[i].predivisor
|
|
||||||
));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq);
|
|
||||||
spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
SDL_MintAudio_device = this;
|
|
||||||
|
|
||||||
/* Check audio capabilities */
|
|
||||||
if (Mint_CheckAudio(this, spec)==-1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate memory for audio buffers in DMA-able RAM */
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size));
|
|
||||||
|
|
||||||
SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM);
|
|
||||||
if (SDL_MintAudio_audiobuf[0]==NULL) {
|
|
||||||
SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ;
|
|
||||||
SDL_MintAudio_numbuf=0;
|
|
||||||
SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2);
|
|
||||||
SDL_MintAudio_audiosize = spec->size;
|
|
||||||
SDL_MintAudio_mutex = 0;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0]));
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1]));
|
|
||||||
|
|
||||||
SDL_MintAudio_CheckFpu();
|
|
||||||
|
|
||||||
/* Set replay tracks */
|
|
||||||
if (cookie_snd & SND_16BIT) {
|
|
||||||
Settracks(0,0);
|
|
||||||
Setmontracks(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Supexec(Mint_InitDma);
|
|
||||||
|
|
||||||
/* Set interrupt */
|
|
||||||
Jdisint(MFP_DMASOUND);
|
|
||||||
Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_Dma8Interrupt);
|
|
||||||
Jenabint(MFP_DMASOUND);
|
|
||||||
|
|
||||||
if (cookie_snd & SND_16BIT) {
|
|
||||||
if (Setinterrupt(SI_TIMERA, SI_PLAY)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setinterrupt() failed\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Supexec(Mint_StartReplay);
|
|
||||||
|
|
||||||
return(1); /* We don't use threaded audio */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Functions called in supervisor mode */
|
|
||||||
|
|
||||||
static void Mint_InitDma(void)
|
|
||||||
{
|
|
||||||
unsigned long buffer;
|
|
||||||
unsigned char mode;
|
|
||||||
SDL_AudioDevice *this = SDL_MintAudio_device;
|
|
||||||
|
|
||||||
Mint_StopReplay();
|
|
||||||
|
|
||||||
/* Set buffer */
|
|
||||||
buffer = (unsigned long) SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
|
|
||||||
DMAAUDIO_IO.start_high = (buffer>>16) & 255;
|
|
||||||
DMAAUDIO_IO.start_mid = (buffer>>8) & 255;
|
|
||||||
DMAAUDIO_IO.start_low = buffer & 255;
|
|
||||||
|
|
||||||
buffer += SDL_MintAudio_audiosize;
|
|
||||||
DMAAUDIO_IO.end_high = (buffer>>16) & 255;
|
|
||||||
DMAAUDIO_IO.end_mid = (buffer>>8) & 255;
|
|
||||||
DMAAUDIO_IO.end_low = buffer & 255;
|
|
||||||
|
|
||||||
mode = 3-MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
|
|
||||||
if (this->spec.channels==1) {
|
|
||||||
mode |= 1<<7;
|
|
||||||
}
|
|
||||||
DMAAUDIO_IO.sound_ctrl = mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_StopReplay(void)
|
|
||||||
{
|
|
||||||
/* Stop replay */
|
|
||||||
DMAAUDIO_IO.control=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_StartReplay(void)
|
|
||||||
{
|
|
||||||
/* Start replay */
|
|
||||||
DMAAUDIO_IO.control=3;
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
DMA 8bits and Falcon Codec audio definitions
|
|
||||||
|
|
||||||
Patrice Mandin, Didier Méquignon
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SDL_mintaudio_dma8_h
|
|
||||||
#define _SDL_mintaudio_dma8_h
|
|
||||||
|
|
||||||
#define DMAAUDIO_IO_BASE (0xffff8900)
|
|
||||||
struct DMAAUDIO_IO_S {
|
|
||||||
unsigned char int_ctrl;
|
|
||||||
unsigned char control;
|
|
||||||
|
|
||||||
unsigned char dummy1;
|
|
||||||
unsigned char start_high;
|
|
||||||
unsigned char dummy2;
|
|
||||||
unsigned char start_mid;
|
|
||||||
unsigned char dummy3;
|
|
||||||
unsigned char start_low;
|
|
||||||
|
|
||||||
unsigned char dummy4;
|
|
||||||
unsigned char cur_high;
|
|
||||||
unsigned char dummy5;
|
|
||||||
unsigned char cur_mid;
|
|
||||||
unsigned char dummy6;
|
|
||||||
unsigned char cur_low;
|
|
||||||
|
|
||||||
unsigned char dummy7;
|
|
||||||
unsigned char end_high;
|
|
||||||
unsigned char dummy8;
|
|
||||||
unsigned char end_mid;
|
|
||||||
unsigned char dummy9;
|
|
||||||
unsigned char end_low;
|
|
||||||
|
|
||||||
unsigned char dummy10[12];
|
|
||||||
|
|
||||||
unsigned char track_ctrl; /* CODEC only */
|
|
||||||
unsigned char sound_ctrl;
|
|
||||||
unsigned short sound_data;
|
|
||||||
unsigned short sound_mask;
|
|
||||||
|
|
||||||
unsigned char dummy11[10];
|
|
||||||
|
|
||||||
unsigned short dev_ctrl;
|
|
||||||
unsigned short dest_ctrl;
|
|
||||||
unsigned short sync_div;
|
|
||||||
unsigned char track_rec;
|
|
||||||
unsigned char adderin_input;
|
|
||||||
unsigned char channel_input;
|
|
||||||
unsigned char channel_amplification;
|
|
||||||
unsigned char channel_reduction;
|
|
||||||
|
|
||||||
unsigned char dummy12[6];
|
|
||||||
|
|
||||||
unsigned char data_direction;
|
|
||||||
unsigned char dummy13;
|
|
||||||
unsigned char dev_data;
|
|
||||||
};
|
|
||||||
#define DMAAUDIO_IO ((*(volatile struct DMAAUDIO_IO_S *)DMAAUDIO_IO_BASE))
|
|
||||||
|
|
||||||
#endif /* _SDL_mintaudio_dma8_h */
|
|
||||||
|
|
@ -1,436 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
MiNT audio driver
|
|
||||||
using XBIOS functions (GSXB compatible driver)
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Mint includes */
|
|
||||||
#include <mint/osbind.h>
|
|
||||||
#include <mint/falcon.h>
|
|
||||||
#include <mint/cookie.h>
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
#include "../../video/ataricommon/SDL_atarimxalloc_c.h"
|
|
||||||
|
|
||||||
#include "SDL_mintaudio.h"
|
|
||||||
#include "SDL_mintaudio_gsxb.h"
|
|
||||||
|
|
||||||
/*--- Defines ---*/
|
|
||||||
|
|
||||||
#define MINT_AUDIO_DRIVER_NAME "mint_gsxb"
|
|
||||||
|
|
||||||
/* Debug print info */
|
|
||||||
#define DEBUG_NAME "audio:gsxb: "
|
|
||||||
#if 0
|
|
||||||
#define DEBUG_PRINT(what) \
|
|
||||||
{ \
|
|
||||||
printf what; \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define DEBUG_PRINT(what)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*--- Static variables ---*/
|
|
||||||
|
|
||||||
static long cookie_snd, cookie_gsxb;
|
|
||||||
|
|
||||||
/*--- Audio driver functions ---*/
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS);
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_LockAudio(_THIS);
|
|
||||||
static void Mint_UnlockAudio(_THIS);
|
|
||||||
|
|
||||||
/* To check/init hardware audio */
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
|
|
||||||
/* GSXB callbacks */
|
|
||||||
static void Mint_GsxbInterrupt(void);
|
|
||||||
static void Mint_GsxbNullInterrupt(void);
|
|
||||||
|
|
||||||
/*--- Audio driver bootstrap functions ---*/
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
const char *envr = SDL_getenv("SDL_AUDIODRIVER");
|
|
||||||
|
|
||||||
/* Check if user asked a different audio driver */
|
|
||||||
if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie _SND present ? if not, assume ST machine */
|
|
||||||
if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
|
|
||||||
cookie_snd = SND_PSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we have 16 bits audio */
|
|
||||||
if ((cookie_snd & SND_16BIT)==0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "no 16 bits sound\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie GSXB present ? */
|
|
||||||
cookie_gsxb = (Getcookie(C_GSXB, &cookie_gsxb) == C_FOUND);
|
|
||||||
|
|
||||||
/* Is it GSXB ? */
|
|
||||||
if (((cookie_snd & SND_GSXB)==0) || (cookie_gsxb==0)) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "no GSXB audio\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if audio is lockable */
|
|
||||||
if (Locksnd()!=1) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unlocksnd();
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "GSXB audio available!\n"));
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = Mint_OpenAudio;
|
|
||||||
this->CloseAudio = Mint_CloseAudio;
|
|
||||||
this->LockAudio = Mint_LockAudio;
|
|
||||||
this->UnlockAudio = Mint_UnlockAudio;
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap MINTAUDIO_GSXB_bootstrap = {
|
|
||||||
MINT_AUDIO_DRIVER_NAME, "MiNT GSXB audio driver",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
static void Mint_LockAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Stop replay */
|
|
||||||
Buffoper(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_UnlockAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Restart replay */
|
|
||||||
Buffoper(SB_PLA_ENA|SB_PLA_RPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Stop replay */
|
|
||||||
Buffoper(0);
|
|
||||||
|
|
||||||
/* Uninstall interrupt */
|
|
||||||
if (NSetinterrupt(2, SI_NONE, Mint_GsxbNullInterrupt)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "NSetinterrupt() failed in close\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait if currently playing sound */
|
|
||||||
while (SDL_MintAudio_mutex != 0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear buffers */
|
|
||||||
if (SDL_MintAudio_audiobuf[0]) {
|
|
||||||
Mfree(SDL_MintAudio_audiobuf[0]);
|
|
||||||
SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unlock sound system */
|
|
||||||
Unlocksnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
long snd_format = 0;
|
|
||||||
int i, resolution, format_signed, format_bigendian;
|
|
||||||
Uint16 test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
int valid_datatype = 0;
|
|
||||||
|
|
||||||
resolution = spec->format & 0x00ff;
|
|
||||||
format_signed = ((spec->format & 0x8000)!=0);
|
|
||||||
format_bigendian = ((spec->format & 0x1000)!=0);
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
if (spec->channels > 2) {
|
|
||||||
spec->channels = 2; /* no more than stereo! */
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((!valid_datatype) && (test_format)) {
|
|
||||||
/* Check formats available */
|
|
||||||
snd_format = Sndstatus(SND_QUERYFORMATS);
|
|
||||||
spec->format = test_format;
|
|
||||||
resolution = spec->format & 0xff;
|
|
||||||
format_signed = (spec->format & (1<<15));
|
|
||||||
format_bigendian = (spec->format & (1<<12));
|
|
||||||
switch (test_format) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
case AUDIO_S8:
|
|
||||||
if (snd_format & SND_FORMAT8) {
|
|
||||||
valid_datatype = 1;
|
|
||||||
snd_format = Sndstatus(SND_QUERY8BIT);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
if (snd_format & SND_FORMAT16) {
|
|
||||||
valid_datatype = 1;
|
|
||||||
snd_format = Sndstatus(SND_QUERY16BIT);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid_datatype) {
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check signed/unsigned format */
|
|
||||||
if (format_signed) {
|
|
||||||
if (snd_format & SND_FORMATSIGNED) {
|
|
||||||
/* Ok */
|
|
||||||
} else if (snd_format & SND_FORMATUNSIGNED) {
|
|
||||||
/* Give unsigned format */
|
|
||||||
spec->format = spec->format & (~0x8000);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (snd_format & SND_FORMATUNSIGNED) {
|
|
||||||
/* Ok */
|
|
||||||
} else if (snd_format & SND_FORMATSIGNED) {
|
|
||||||
/* Give signed format */
|
|
||||||
spec->format |= 0x8000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (format_bigendian) {
|
|
||||||
if (snd_format & SND_FORMATBIGENDIAN) {
|
|
||||||
/* Ok */
|
|
||||||
} else if (snd_format & SND_FORMATLITTLEENDIAN) {
|
|
||||||
/* Give little endian format */
|
|
||||||
spec->format = spec->format & (~0x1000);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (snd_format & SND_FORMATLITTLEENDIAN) {
|
|
||||||
/* Ok */
|
|
||||||
} else if (snd_format & SND_FORMATBIGENDIAN) {
|
|
||||||
/* Give big endian format */
|
|
||||||
spec->format |= 0x1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Calculate and select the closest frequency */
|
|
||||||
MINTAUDIO_freqcount=0;
|
|
||||||
for (i=1;i<4;i++) {
|
|
||||||
SDL_MintAudio_AddFrequency(this,
|
|
||||||
MASTERCLOCK_44K/(MASTERPREDIV_MILAN*(1<<i)), MASTERCLOCK_44K,
|
|
||||||
(1<<i)-1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
for (i=0; i<MINTAUDIO_freqcount; i++) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
|
|
||||||
i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock,
|
|
||||||
MINTAUDIO_frequencies[i].predivisor
|
|
||||||
));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq);
|
|
||||||
spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int channels_mode, prediv;
|
|
||||||
void *buffer;
|
|
||||||
|
|
||||||
/* Stop currently playing sound */
|
|
||||||
Buffoper(0);
|
|
||||||
|
|
||||||
/* Set replay tracks */
|
|
||||||
Settracks(0,0);
|
|
||||||
Setmontracks(0);
|
|
||||||
|
|
||||||
/* Select replay format */
|
|
||||||
switch (spec->format & 0xff) {
|
|
||||||
case 8:
|
|
||||||
if (spec->channels==2) {
|
|
||||||
channels_mode=STEREO8;
|
|
||||||
} else {
|
|
||||||
channels_mode=MONO8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
if (spec->channels==2) {
|
|
||||||
channels_mode=STEREO16;
|
|
||||||
} else {
|
|
||||||
channels_mode=MONO16;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
channels_mode=STEREO16;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (Setmode(channels_mode)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
|
|
||||||
Devconnect(DMAPLAY, DAC, CLKEXT, prediv, 1);
|
|
||||||
|
|
||||||
/* Set buffer */
|
|
||||||
buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
|
|
||||||
if (Setbuffer(0, buffer, buffer + spec->size)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Install interrupt */
|
|
||||||
if (NSetinterrupt(2, SI_PLAY, Mint_GsxbInterrupt)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "NSetinterrupt() failed\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Go */
|
|
||||||
Buffoper(SB_PLA_ENA|SB_PLA_RPT);
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "hardware initialized\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
/* Lock sound system */
|
|
||||||
if (Locksnd()!=1) {
|
|
||||||
SDL_SetError("Mint_OpenAudio: Audio system already in use");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_MintAudio_device = this;
|
|
||||||
|
|
||||||
/* Check audio capabilities */
|
|
||||||
if (Mint_CheckAudio(this, spec)==-1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate memory for audio buffers in DMA-able RAM */
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size));
|
|
||||||
|
|
||||||
SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM);
|
|
||||||
if (SDL_MintAudio_audiobuf[0]==NULL) {
|
|
||||||
SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ;
|
|
||||||
SDL_MintAudio_numbuf=0;
|
|
||||||
SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2);
|
|
||||||
SDL_MintAudio_audiosize = spec->size;
|
|
||||||
SDL_MintAudio_mutex = 0;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0]));
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1]));
|
|
||||||
|
|
||||||
SDL_MintAudio_CheckFpu();
|
|
||||||
|
|
||||||
/* Setup audio hardware */
|
|
||||||
Mint_InitAudio(this, spec);
|
|
||||||
|
|
||||||
return(1); /* We don't use threaded audio */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_GsxbInterrupt(void)
|
|
||||||
{
|
|
||||||
Uint8 *newbuf;
|
|
||||||
|
|
||||||
if (SDL_MintAudio_mutex)
|
|
||||||
return;
|
|
||||||
|
|
||||||
SDL_MintAudio_mutex=1;
|
|
||||||
|
|
||||||
SDL_MintAudio_numbuf ^= 1;
|
|
||||||
SDL_MintAudio_Callback();
|
|
||||||
newbuf = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
|
|
||||||
Setbuffer(0, newbuf, newbuf + SDL_MintAudio_audiosize);
|
|
||||||
|
|
||||||
SDL_MintAudio_mutex=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_GsxbNullInterrupt(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GSXB audio definitions
|
|
||||||
*
|
|
||||||
* Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SDL_mintaudio_gsxb_h
|
|
||||||
#define _SDL_mintaudio_gsxb_h
|
|
||||||
|
|
||||||
#include <mint/falcon.h> /* for trap_14_xxx macros */
|
|
||||||
|
|
||||||
/* Bit 5 in cookie _SND */
|
|
||||||
|
|
||||||
#define SND_GSXB (1<<5)
|
|
||||||
|
|
||||||
/* NSoundcmd modes */
|
|
||||||
|
|
||||||
#define SETRATE 7 /* Set sample rate */
|
|
||||||
#define SET8BITFORMAT 8 /* 8 bits format */
|
|
||||||
#define SET16BITFORMAT 9 /* 16 bits format */
|
|
||||||
#define SET24BITFORMAT 10 /* 24 bits format */
|
|
||||||
#define SET32BITFORMAT 11 /* 32 bits format */
|
|
||||||
#define LTATTEN_MASTER 12 /* Attenuation */
|
|
||||||
#define RTATTEN_MASTER 13
|
|
||||||
#define LTATTEN_MICIN 14
|
|
||||||
#define RTATTEN_MICIN 15
|
|
||||||
#define LTATTEN_FMGEN 16
|
|
||||||
#define RTATTEN_FMGEN 17
|
|
||||||
#define LTATTEN_LINEIN 18
|
|
||||||
#define RTATTEN_LINEIN 19
|
|
||||||
#define LTATTEN_CDIN 20
|
|
||||||
#define RTATTEN_CDIN 21
|
|
||||||
#define LTATTEN_VIDIN 22
|
|
||||||
#define RTATTEN_VIDIN 23
|
|
||||||
#define LTATTEN_AUXIN 24
|
|
||||||
#define RTATTEN_AUXIN 25
|
|
||||||
|
|
||||||
/* Setmode modes */
|
|
||||||
|
|
||||||
#define MONO16 3
|
|
||||||
#define STEREO24 4
|
|
||||||
#define STEREO32 5
|
|
||||||
#define MONO24 6
|
|
||||||
#define MONO32 7
|
|
||||||
|
|
||||||
/* Sndstatus modes */
|
|
||||||
|
|
||||||
#define SND_QUERYFORMATS 2
|
|
||||||
#define SND_QUERYMIXERS 3
|
|
||||||
#define SND_QUERYSOURCES 4
|
|
||||||
#define SND_QUERYDUPLEX 5
|
|
||||||
#define SND_QUERY8BIT 8
|
|
||||||
#define SND_QUERY16BIT 9
|
|
||||||
#define SND_QUERY24BIT 10
|
|
||||||
#define SND_QUERY32BIT 11
|
|
||||||
|
|
||||||
#define SND_FORMAT8 (1<<0)
|
|
||||||
#define SND_FORMAT16 (1<<1)
|
|
||||||
#define SND_FORMAT24 (1<<2)
|
|
||||||
#define SND_FORMAT32 (1<<3)
|
|
||||||
|
|
||||||
#define SND_FORMATSIGNED (1<<0)
|
|
||||||
#define SND_FORMATUNSIGNED (1<<1)
|
|
||||||
#define SND_FORMATBIGENDIAN (1<<2)
|
|
||||||
#define SND_FORMATLITTLEENDIAN (1<<3)
|
|
||||||
|
|
||||||
/* Devconnect prescalers */
|
|
||||||
|
|
||||||
#define CLK_44K 1
|
|
||||||
#define CLK_22K 3
|
|
||||||
#define CLK_11K 7
|
|
||||||
|
|
||||||
/* Extra xbios functions */
|
|
||||||
|
|
||||||
#define NSoundcmd(mode,data,data2) \
|
|
||||||
(long)trap_14_wwl((short)130,(short)(mode),(short)(data),(long)(data2))
|
|
||||||
#define NSetinterrupt(src_inter,cause,inth_addr) \
|
|
||||||
(long)trap_14_wwwl((short)135,(short)(src_inter),(short)(cause), \
|
|
||||||
(long)(inth_addr))
|
|
||||||
|
|
||||||
#endif /* _SDL_mintaudio_gsxb_h */
|
|
||||||
|
|
@ -1,386 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Audio interrupts
|
|
||||||
|
|
||||||
Patrice Mandin, Didier Méquignon
|
|
||||||
*/
|
|
||||||
|
|
||||||
.text
|
|
||||||
|
|
||||||
.globl _SDL_MintAudio_Callback
|
|
||||||
|
|
||||||
.globl _SDL_MintAudio_XbiosInterrupt
|
|
||||||
.globl _SDL_MintAudio_XbiosInterruptMeasureClock
|
|
||||||
.globl _SDL_MintAudio_Dma8Interrupt
|
|
||||||
.globl _SDL_MintAudio_StfaInterrupt
|
|
||||||
|
|
||||||
.globl _SDL_MintAudio_mutex
|
|
||||||
.globl _SDL_MintAudio_audiobuf
|
|
||||||
.globl _SDL_MintAudio_numbuf
|
|
||||||
.globl _SDL_MintAudio_audiosize
|
|
||||||
.globl _SDL_MintAudio_clocktics
|
|
||||||
.globl _SDL_MintAudio_hasfpu
|
|
||||||
|
|
||||||
.globl _SDL_MintAudio_stfa
|
|
||||||
|
|
||||||
/*
|
|
||||||
How it works:
|
|
||||||
- Audio is playing buffer #0 (resp. #1)
|
|
||||||
- We must calculate a sample in buffer #1 (resp. #0)
|
|
||||||
so we first call the callback to do it
|
|
||||||
- Then we swap the buffers
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define savptr 0x4a2
|
|
||||||
#define savamt 0x46
|
|
||||||
|
|
||||||
/*--- Save/restore FPU context ---*/
|
|
||||||
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
|
|
||||||
#define SAVE_FPU_CONTEXT \
|
|
||||||
lea sp@(-216),sp; \
|
|
||||||
fsave sp@; \
|
|
||||||
fmovel fpiar,sp@-; \
|
|
||||||
lea sp@(-64),sp; \
|
|
||||||
fmovemd fp0-fp7,sp@
|
|
||||||
|
|
||||||
#define RESTORE_FPU_CONTEXT \
|
|
||||||
fmovemd sp@,fp0-fp7; \
|
|
||||||
lea sp@(64),sp; \
|
|
||||||
fmovel sp@+,fpiar; \
|
|
||||||
frestore sp@; \
|
|
||||||
lea sp@(216),sp
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#define SAVE_FPU_CONTEXT \
|
|
||||||
.chip 68k/68881; \
|
|
||||||
fsave sp@-; \
|
|
||||||
fmoveml fpcr/fpsr/fpiar,sp@-; \
|
|
||||||
fmovemx fp0-fp7,sp@-; \
|
|
||||||
.chip 68k
|
|
||||||
|
|
||||||
#define RESTORE_FPU_CONTEXT \
|
|
||||||
.chip 68k/68881; \
|
|
||||||
fmovemx sp@+,fp0-fp7; \
|
|
||||||
fmoveml sp@+,fpcr/fpsr/fpiar; \
|
|
||||||
frestore sp@+; \
|
|
||||||
.chip 68k
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*--- Xbios interrupt vector to measure Falcon external clock ---*/
|
|
||||||
|
|
||||||
_SDL_MintAudio_XbiosInterruptMeasureClock: /* 1 mS */
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
movel d0,sp@-
|
|
||||||
|
|
||||||
moveql #0,d0
|
|
||||||
btst d0,0xFFFF8901:w /* state DMA sound */
|
|
||||||
#else
|
|
||||||
btst #0,0xFFFF8901:w /* state DMA sound */
|
|
||||||
#endif
|
|
||||||
beqs SDL_MintAudio_EndIntMeasure
|
|
||||||
addql #1,_SDL_MintAudio_clocktics
|
|
||||||
SDL_MintAudio_EndIntMeasure:
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
moveql #5,d0
|
|
||||||
bclr d0,0xFFFFFA0F:w /* Clear service bit */
|
|
||||||
|
|
||||||
movel sp@+,d0
|
|
||||||
#else
|
|
||||||
bclr #5,0xFFFFFA0F:w /* Clear service bit */
|
|
||||||
#endif
|
|
||||||
rte
|
|
||||||
|
|
||||||
/*--- Xbios interrupt vector ---*/
|
|
||||||
|
|
||||||
_SDL_MintAudio_XbiosInterrupt:
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
lea sp@(-60),sp
|
|
||||||
moveml d0-d7/a0-a6,sp@
|
|
||||||
#else
|
|
||||||
moveml d0-d7/a0-a6,sp@-
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Reenable interrupts, so other interrupts can work */
|
|
||||||
movew #0x2300,sr
|
|
||||||
|
|
||||||
/* Clear service bit, so other MFP interrupts can work */
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
moveql #5,d0
|
|
||||||
bclr d0,0xfffffa0f:w
|
|
||||||
#else
|
|
||||||
bclr #5,0xfffffa0f:w
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Check if we are not already running */
|
|
||||||
tstw _SDL_MintAudio_mutex
|
|
||||||
bne SDL_MintAudio_XbiosEnd
|
|
||||||
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
movew _SDL_MintAudio_mutex,d0
|
|
||||||
notl d0
|
|
||||||
movew d0,_SDL_MintAudio_mutex
|
|
||||||
|
|
||||||
movew _SDL_MintAudio_numbuf,d1
|
|
||||||
eorl #1,d1
|
|
||||||
movew d1,_SDL_MintAudio_numbuf
|
|
||||||
#else
|
|
||||||
notw _SDL_MintAudio_mutex
|
|
||||||
|
|
||||||
/* Swap buffers */
|
|
||||||
eorw #1,_SDL_MintAudio_numbuf
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Save FPU if needed */
|
|
||||||
tstw _SDL_MintAudio_hasfpu
|
|
||||||
beqs SDL_MintAudio_Xbios_nofpu1
|
|
||||||
SAVE_FPU_CONTEXT
|
|
||||||
SDL_MintAudio_Xbios_nofpu1:
|
|
||||||
|
|
||||||
/* Callback */
|
|
||||||
jsr _SDL_MintAudio_Callback
|
|
||||||
|
|
||||||
/* Restore FPU if needed */
|
|
||||||
tstw _SDL_MintAudio_hasfpu
|
|
||||||
beqs SDL_MintAudio_Xbios_nofpu2
|
|
||||||
RESTORE_FPU_CONTEXT
|
|
||||||
SDL_MintAudio_Xbios_nofpu2:
|
|
||||||
|
|
||||||
/* Reserve space for registers */
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
movel #savamt,d0
|
|
||||||
subl d0,savptr
|
|
||||||
#else
|
|
||||||
subl #savamt,savptr
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Set new buffer */
|
|
||||||
|
|
||||||
moveq #0,d0
|
|
||||||
movel _SDL_MintAudio_audiosize,d1
|
|
||||||
|
|
||||||
movew _SDL_MintAudio_numbuf,d0
|
|
||||||
lsll #2,d0
|
|
||||||
lea _SDL_MintAudio_audiobuf,a0
|
|
||||||
movel a0@(d0:l),a1
|
|
||||||
|
|
||||||
lea a1@(d1:l),a2
|
|
||||||
|
|
||||||
movel a2,sp@-
|
|
||||||
movel a1,sp@-
|
|
||||||
clrw sp@-
|
|
||||||
movew #131,sp@-
|
|
||||||
trap #14
|
|
||||||
lea sp@(12),sp
|
|
||||||
|
|
||||||
/* Restore registers space */
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
movel #savamt,d0
|
|
||||||
addl d0,savptr
|
|
||||||
#else
|
|
||||||
addl #savamt,savptr
|
|
||||||
#endif
|
|
||||||
|
|
||||||
clrw _SDL_MintAudio_mutex
|
|
||||||
SDL_MintAudio_XbiosEnd:
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
moveml sp@,d0-d7/a0-a6
|
|
||||||
lea sp@(60),sp
|
|
||||||
#else
|
|
||||||
moveml sp@+,d0-d7/a0-a6
|
|
||||||
#endif
|
|
||||||
rte
|
|
||||||
|
|
||||||
/*--- DMA 8 bits interrupt vector ---*/
|
|
||||||
|
|
||||||
_SDL_MintAudio_Dma8Interrupt:
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
lea sp@(-16),sp
|
|
||||||
moveml d0-d1/a0-a1,sp@
|
|
||||||
#else
|
|
||||||
moveml d0-d1/a0-a1,sp@-
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Reenable interrupts, so other interrupts can work */
|
|
||||||
movew #0x2300,sr
|
|
||||||
|
|
||||||
/* Clear service bit, so other MFP interrupts can work */
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
moveql #5,d0
|
|
||||||
bclr d0,0xfffffa0f:w
|
|
||||||
#else
|
|
||||||
bclr #5,0xfffffa0f:w
|
|
||||||
#endif
|
|
||||||
/* Check if we are not already running */
|
|
||||||
tstw _SDL_MintAudio_mutex
|
|
||||||
bne SDL_MintAudio_Dma8End
|
|
||||||
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
movew _SDL_MintAudio_mutex,d0
|
|
||||||
notl d0
|
|
||||||
movew d0,_SDL_MintAudio_mutex
|
|
||||||
|
|
||||||
movew _SDL_MintAudio_numbuf,d1
|
|
||||||
eorl #1,d1
|
|
||||||
movew d1,_SDL_MintAudio_numbuf
|
|
||||||
#else
|
|
||||||
notw _SDL_MintAudio_mutex
|
|
||||||
|
|
||||||
/* Swap buffers */
|
|
||||||
eorw #1,_SDL_MintAudio_numbuf
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Save FPU if needed */
|
|
||||||
tstw _SDL_MintAudio_hasfpu
|
|
||||||
beqs SDL_MintAudio_Dma8_nofpu1
|
|
||||||
SAVE_FPU_CONTEXT
|
|
||||||
SDL_MintAudio_Dma8_nofpu1:
|
|
||||||
|
|
||||||
/* Callback */
|
|
||||||
jsr _SDL_MintAudio_Callback
|
|
||||||
|
|
||||||
/* Restore FPU if needed */
|
|
||||||
tstw _SDL_MintAudio_hasfpu
|
|
||||||
beqs SDL_MintAudio_Dma8_nofpu2
|
|
||||||
RESTORE_FPU_CONTEXT
|
|
||||||
SDL_MintAudio_Dma8_nofpu2:
|
|
||||||
|
|
||||||
/* Set new buffer */
|
|
||||||
|
|
||||||
moveq #0,d0
|
|
||||||
|
|
||||||
movew _SDL_MintAudio_numbuf,d0
|
|
||||||
lsll #2,d0
|
|
||||||
lea _SDL_MintAudio_audiobuf,a0
|
|
||||||
movel a0@(d0:l),d1
|
|
||||||
|
|
||||||
/* Modify DMA addresses */
|
|
||||||
lea 0xffff8900:w,a0
|
|
||||||
|
|
||||||
movel d1,d0
|
|
||||||
|
|
||||||
moveb d0,a0@(0x07) /* Start address */
|
|
||||||
lsrl #8,d0
|
|
||||||
moveb d0,a0@(0x05)
|
|
||||||
lsrl #8,d0
|
|
||||||
moveb d0,a0@(0x03)
|
|
||||||
|
|
||||||
addl _SDL_MintAudio_audiosize,d1
|
|
||||||
|
|
||||||
movel d1,d0
|
|
||||||
|
|
||||||
moveb d0,a0@(0x13) /* End address */
|
|
||||||
lsrl #8,d0
|
|
||||||
moveb d0,a0@(0x11)
|
|
||||||
lsrl #8,d0
|
|
||||||
moveb d0,a0@(0x0f)
|
|
||||||
|
|
||||||
clrw _SDL_MintAudio_mutex
|
|
||||||
SDL_MintAudio_Dma8End:
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
moveml sp@,d0-d1/a0-a1
|
|
||||||
lea sp@(16),sp
|
|
||||||
#else
|
|
||||||
moveml sp@+,d0-d1/a0-a1
|
|
||||||
#endif
|
|
||||||
rte
|
|
||||||
|
|
||||||
/*--- STFA interrupt vector ---*/
|
|
||||||
|
|
||||||
STFA_SOUND_START = 6
|
|
||||||
STFA_SOUND_END = STFA_SOUND_START+8
|
|
||||||
|
|
||||||
_SDL_MintAudio_StfaInterrupt:
|
|
||||||
/* Reenable interrupts, so other interrupts can work */
|
|
||||||
movew #0x2300,sr
|
|
||||||
|
|
||||||
/* Check if we are not already running */
|
|
||||||
tstw _SDL_MintAudio_mutex
|
|
||||||
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
bne SDL_MintAudio_StfaEnd
|
|
||||||
|
|
||||||
lea sp@(-60),sp
|
|
||||||
moveml d0-d7/a0-a6,sp@
|
|
||||||
|
|
||||||
movew _SDL_MintAudio_mutex,d0
|
|
||||||
notl d0
|
|
||||||
movew d0,_SDL_MintAudio_mutex
|
|
||||||
|
|
||||||
movew _SDL_MintAudio_numbuf,d1
|
|
||||||
eorl #1,d1
|
|
||||||
movew d1,_SDL_MintAudio_numbuf
|
|
||||||
#else
|
|
||||||
bnes SDL_MintAudio_StfaEnd
|
|
||||||
|
|
||||||
moveml d0-d7/a0-a6,sp@-
|
|
||||||
|
|
||||||
notw _SDL_MintAudio_mutex
|
|
||||||
|
|
||||||
/* Swap buffers */
|
|
||||||
eorw #1,_SDL_MintAudio_numbuf
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Save FPU if needed */
|
|
||||||
tstw _SDL_MintAudio_hasfpu
|
|
||||||
beqs SDL_MintAudio_Stfa_nofpu1
|
|
||||||
SAVE_FPU_CONTEXT
|
|
||||||
SDL_MintAudio_Stfa_nofpu1:
|
|
||||||
|
|
||||||
/* Callback */
|
|
||||||
jsr _SDL_MintAudio_Callback
|
|
||||||
|
|
||||||
/* Restore FPU if needed */
|
|
||||||
tstw _SDL_MintAudio_hasfpu
|
|
||||||
beqs SDL_MintAudio_Stfa_nofpu2
|
|
||||||
RESTORE_FPU_CONTEXT
|
|
||||||
SDL_MintAudio_Stfa_nofpu2:
|
|
||||||
|
|
||||||
/* Set new buffer */
|
|
||||||
|
|
||||||
moveq #0,d0
|
|
||||||
movel _SDL_MintAudio_stfa,a1
|
|
||||||
|
|
||||||
movew _SDL_MintAudio_numbuf,d0
|
|
||||||
lsll #2,d0
|
|
||||||
lea _SDL_MintAudio_audiobuf,a0
|
|
||||||
movel a0@(d0:l),d1
|
|
||||||
|
|
||||||
/* Modify STFA replay buffers */
|
|
||||||
movel d1,a1@(STFA_SOUND_START)
|
|
||||||
addl _SDL_MintAudio_audiosize,d1
|
|
||||||
movel d1,a1@(STFA_SOUND_END)
|
|
||||||
|
|
||||||
#if defined(__mcoldfire__)
|
|
||||||
moveml sp@,d0-d7/a0-a6
|
|
||||||
lea sp@(60),sp
|
|
||||||
#else
|
|
||||||
moveml sp@+,d0-d7/a0-a6
|
|
||||||
#endif
|
|
||||||
clrw _SDL_MintAudio_mutex
|
|
||||||
SDL_MintAudio_StfaEnd:
|
|
||||||
rte
|
|
||||||
|
|
@ -1,405 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
MiNT audio driver
|
|
||||||
using XBIOS functions (MacSound compatible driver)
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <support.h>
|
|
||||||
|
|
||||||
/* Mint includes */
|
|
||||||
#include <mint/osbind.h>
|
|
||||||
#include <mint/falcon.h>
|
|
||||||
#include <mint/cookie.h>
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
#include "../../video/ataricommon/SDL_atarimxalloc_c.h"
|
|
||||||
|
|
||||||
#include "SDL_mintaudio.h"
|
|
||||||
#include "SDL_mintaudio_mcsn.h"
|
|
||||||
|
|
||||||
/*--- Defines ---*/
|
|
||||||
|
|
||||||
#define MINT_AUDIO_DRIVER_NAME "mint_mcsn"
|
|
||||||
|
|
||||||
/* Debug print info */
|
|
||||||
#define DEBUG_NAME "audio:mcsn: "
|
|
||||||
#if 0
|
|
||||||
#define DEBUG_PRINT(what) \
|
|
||||||
{ \
|
|
||||||
printf what; \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define DEBUG_PRINT(what)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*--- Static variables ---*/
|
|
||||||
|
|
||||||
static long cookie_snd, cookie_mch;
|
|
||||||
static cookie_mcsn_t *cookie_mcsn;
|
|
||||||
|
|
||||||
/*--- Audio driver functions ---*/
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS);
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_LockAudio(_THIS);
|
|
||||||
static void Mint_UnlockAudio(_THIS);
|
|
||||||
|
|
||||||
/* To check/init hardware audio */
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
|
|
||||||
/*--- Audio driver bootstrap functions ---*/
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
long dummy;
|
|
||||||
const char *envr = SDL_getenv("SDL_AUDIODRIVER");
|
|
||||||
|
|
||||||
SDL_MintAudio_mint_present = (Getcookie(C_MiNT, &dummy) == C_FOUND);
|
|
||||||
|
|
||||||
/* We can't use XBIOS in interrupt with Magic, don't know about thread */
|
|
||||||
if (Getcookie(C_MagX, &dummy) == C_FOUND) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if user asked a different audio driver */
|
|
||||||
if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie _MCH present ? if not, assume ST machine */
|
|
||||||
if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) {
|
|
||||||
cookie_mch = MCH_ST;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie _SND present ? if not, assume ST machine */
|
|
||||||
if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
|
|
||||||
cookie_snd = SND_PSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we have 16 bits audio */
|
|
||||||
if ((cookie_snd & SND_16BIT)==0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "no 16 bits sound\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie MCSN present ? */
|
|
||||||
if (Getcookie(C_McSn, &dummy) != C_FOUND) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "no MCSN audio\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
cookie_mcsn = (cookie_mcsn_t *) dummy;
|
|
||||||
|
|
||||||
/* Check if interrupt at end of replay */
|
|
||||||
if (cookie_mcsn->pint == 0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "no interrupt at end of replay\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if audio is lockable */
|
|
||||||
if (Locksnd()!=1) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unlocksnd();
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "MCSN audio available!\n"));
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = Mint_OpenAudio;
|
|
||||||
this->CloseAudio = Mint_CloseAudio;
|
|
||||||
this->LockAudio = Mint_LockAudio;
|
|
||||||
this->UnlockAudio = Mint_UnlockAudio;
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap MINTAUDIO_MCSN_bootstrap = {
|
|
||||||
MINT_AUDIO_DRIVER_NAME, "MiNT MCSN audio driver",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
static void Mint_LockAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Stop replay */
|
|
||||||
Buffoper(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_UnlockAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Restart replay */
|
|
||||||
Buffoper(SB_PLA_ENA|SB_PLA_RPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Stop replay */
|
|
||||||
SDL_MintAudio_WaitThread();
|
|
||||||
Buffoper(0);
|
|
||||||
|
|
||||||
if (!SDL_MintAudio_mint_present) {
|
|
||||||
/* Uninstall interrupt */
|
|
||||||
Jdisint(MFP_DMASOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait if currently playing sound */
|
|
||||||
while (SDL_MintAudio_mutex != 0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear buffers */
|
|
||||||
if (SDL_MintAudio_audiobuf[0]) {
|
|
||||||
Mfree(SDL_MintAudio_audiobuf[0]);
|
|
||||||
SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unlock sound system */
|
|
||||||
Unlocksnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
unsigned long masterclock, masterprediv;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
if (spec->channels > 2) {
|
|
||||||
spec->channels = 2; /* no more than stereo! */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check formats available */
|
|
||||||
MINTAUDIO_freqcount=0;
|
|
||||||
switch(cookie_mcsn->play) {
|
|
||||||
case MCSN_ST:
|
|
||||||
spec->channels=1;
|
|
||||||
spec->format=8; /* FIXME: is it signed or unsigned ? */
|
|
||||||
SDL_MintAudio_AddFrequency(this, 12500, 0, 0, -1);
|
|
||||||
break;
|
|
||||||
case MCSN_TT: /* Also STE, Mega STE */
|
|
||||||
spec->format=AUDIO_S8;
|
|
||||||
masterclock=MASTERCLOCK_STE;
|
|
||||||
masterprediv=MASTERPREDIV_STE;
|
|
||||||
if ((cookie_mch>>16)==MCH_TT) {
|
|
||||||
masterclock=MASTERCLOCK_TT;
|
|
||||||
masterprediv=MASTERPREDIV_TT;
|
|
||||||
}
|
|
||||||
for (i=0; i<4; i++) {
|
|
||||||
SDL_MintAudio_AddFrequency(this, masterclock/(masterprediv*(1<<i)),
|
|
||||||
masterclock, 3-i, -1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MCSN_FALCON: /* Also Mac */
|
|
||||||
for (i=1; i<12; i++) {
|
|
||||||
/* Remove unusable Falcon codec predivisors */
|
|
||||||
if ((i==6) || (i==8) || (i==10)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
SDL_MintAudio_AddFrequency(this, MASTERCLOCK_FALCON1/(MASTERPREDIV_FALCON*(i+1)),
|
|
||||||
CLK25M, i+1, -1);
|
|
||||||
}
|
|
||||||
if (cookie_mcsn->res1 != 0) {
|
|
||||||
for (i=1; i<4; i++) {
|
|
||||||
SDL_MintAudio_AddFrequency(this, (cookie_mcsn->res1)/(MASTERPREDIV_FALCON*(1<<i)),
|
|
||||||
CLKEXT, (1<<i)-1, -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spec->format |= 0x8000; /* Audio is always signed */
|
|
||||||
if ((spec->format & 0x00ff)==16) {
|
|
||||||
spec->format |= 0x1000; /* Audio is always big endian */
|
|
||||||
spec->channels=2; /* 16 bits always stereo */
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
for (i=0; i<MINTAUDIO_freqcount; i++) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
|
|
||||||
i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock,
|
|
||||||
MINTAUDIO_frequencies[i].predivisor
|
|
||||||
));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq);
|
|
||||||
spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int channels_mode, prediv, dmaclock;
|
|
||||||
void *buffer;
|
|
||||||
|
|
||||||
/* Stop currently playing sound */
|
|
||||||
SDL_MintAudio_quit_thread = SDL_FALSE;
|
|
||||||
SDL_MintAudio_thread_finished = SDL_TRUE;
|
|
||||||
SDL_MintAudio_WaitThread();
|
|
||||||
Buffoper(0);
|
|
||||||
|
|
||||||
/* Set replay tracks */
|
|
||||||
Settracks(0,0);
|
|
||||||
Setmontracks(0);
|
|
||||||
|
|
||||||
/* Select replay format */
|
|
||||||
channels_mode=STEREO16;
|
|
||||||
switch (spec->format & 0xff) {
|
|
||||||
case 8:
|
|
||||||
if (spec->channels==2) {
|
|
||||||
channels_mode=STEREO8;
|
|
||||||
} else {
|
|
||||||
channels_mode=MONO8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (Setmode(channels_mode)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
dmaclock = MINTAUDIO_frequencies[MINTAUDIO_numfreq].masterclock;
|
|
||||||
prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
|
|
||||||
switch(cookie_mcsn->play) {
|
|
||||||
case MCSN_TT:
|
|
||||||
Devconnect(DMAPLAY, DAC, CLK25M, CLKOLD, 1);
|
|
||||||
Soundcmd(SETPRESCALE, prediv);
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "STE/TT prescaler selected\n"));
|
|
||||||
break;
|
|
||||||
case MCSN_FALCON:
|
|
||||||
Devconnect(DMAPLAY, DAC, dmaclock, prediv, 1);
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Falcon prescaler selected\n"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set buffer */
|
|
||||||
buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
|
|
||||||
if (Setbuffer(0, buffer, buffer + spec->size)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SDL_MintAudio_mint_present) {
|
|
||||||
SDL_MintAudio_thread_pid = tfork(SDL_MintAudio_Thread, 0);
|
|
||||||
} else {
|
|
||||||
/* Install interrupt */
|
|
||||||
Jdisint(MFP_DMASOUND);
|
|
||||||
Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_XbiosInterrupt);
|
|
||||||
Jenabint(MFP_DMASOUND);
|
|
||||||
|
|
||||||
if (Setinterrupt(SI_TIMERA, SI_PLAY)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setinterrupt() failed\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Go */
|
|
||||||
Buffoper(SB_PLA_ENA|SB_PLA_RPT);
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "hardware initialized\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
/* Lock sound system */
|
|
||||||
if (Locksnd()!=1) {
|
|
||||||
SDL_SetError("Mint_OpenAudio: Audio system already in use");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_MintAudio_device = this;
|
|
||||||
|
|
||||||
/* Check audio capabilities */
|
|
||||||
if (Mint_CheckAudio(this, spec)==-1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate memory for audio buffers in DMA-able RAM */
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size));
|
|
||||||
|
|
||||||
SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM);
|
|
||||||
if (SDL_MintAudio_audiobuf[0]==NULL) {
|
|
||||||
SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ;
|
|
||||||
SDL_MintAudio_numbuf=0;
|
|
||||||
SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2);
|
|
||||||
SDL_MintAudio_audiosize = spec->size;
|
|
||||||
SDL_MintAudio_mutex = 0;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0]));
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1]));
|
|
||||||
|
|
||||||
SDL_MintAudio_CheckFpu();
|
|
||||||
|
|
||||||
/* Setup audio hardware */
|
|
||||||
Mint_InitAudio(this, spec);
|
|
||||||
|
|
||||||
return(1); /* We don't use SDL threaded audio */
|
|
||||||
}
|
|
||||||
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
MCSN control structure
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SDL_mintaudio_mcsh_h
|
|
||||||
#define _SDL_mintaudio_mcsh_h
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned short version; /* Version */
|
|
||||||
unsigned short size; /* Size of structure */
|
|
||||||
|
|
||||||
unsigned short play; /* Replay capability */
|
|
||||||
unsigned short record; /* Record capability */
|
|
||||||
unsigned short dsp; /* DSP56K present */
|
|
||||||
unsigned short pint; /* Interrupt at end of replay */
|
|
||||||
unsigned short rint; /* Interrupt at end of record */
|
|
||||||
|
|
||||||
unsigned long res1; /* Frequency of external clock */
|
|
||||||
unsigned long res2;
|
|
||||||
unsigned long res3;
|
|
||||||
unsigned long res4;
|
|
||||||
} cookie_mcsn_t;
|
|
||||||
|
|
||||||
enum {
|
|
||||||
MCSN_ST=0,
|
|
||||||
MCSN_TT,
|
|
||||||
MCSN_STE=MCSN_TT,
|
|
||||||
MCSN_FALCON,
|
|
||||||
MCSN_MAC=MCSN_FALCON
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SETSMPFREQ 7 /* Set sample frequency */
|
|
||||||
|
|
||||||
#endif /* _SDL_mintaudio_mcsh_h */
|
|
||||||
|
|
@ -1,326 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
MiNT audio driver
|
|
||||||
using XBIOS functions (STFA driver)
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Mint includes */
|
|
||||||
#include <mint/osbind.h>
|
|
||||||
#include <mint/falcon.h>
|
|
||||||
#include <mint/cookie.h>
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
#include "../../video/ataricommon/SDL_atarimxalloc_c.h"
|
|
||||||
#include "../../video/ataricommon/SDL_atarisuper.h"
|
|
||||||
|
|
||||||
#include "SDL_mintaudio.h"
|
|
||||||
#include "SDL_mintaudio_stfa.h"
|
|
||||||
|
|
||||||
/*--- Defines ---*/
|
|
||||||
|
|
||||||
#define MINT_AUDIO_DRIVER_NAME "mint_stfa"
|
|
||||||
|
|
||||||
/* Debug print info */
|
|
||||||
#define DEBUG_NAME "audio:stfa: "
|
|
||||||
#if 0
|
|
||||||
#define DEBUG_PRINT(what) \
|
|
||||||
{ \
|
|
||||||
printf what; \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define DEBUG_PRINT(what)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*--- Static variables ---*/
|
|
||||||
|
|
||||||
static long cookie_snd, cookie_mch;
|
|
||||||
static cookie_stfa_t *cookie_stfa;
|
|
||||||
|
|
||||||
static const int freqs[16]={
|
|
||||||
4995, 6269, 7493, 8192,
|
|
||||||
9830, 10971, 12538, 14985,
|
|
||||||
16384, 19819, 21943, 24576,
|
|
||||||
30720, 32336, 43885, 49152
|
|
||||||
};
|
|
||||||
|
|
||||||
/*--- Audio driver functions ---*/
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS);
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_LockAudio(_THIS);
|
|
||||||
static void Mint_UnlockAudio(_THIS);
|
|
||||||
|
|
||||||
/* To check/init hardware audio */
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
|
|
||||||
/*--- Audio driver bootstrap functions ---*/
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
long dummy;
|
|
||||||
const char *envr = SDL_getenv("SDL_AUDIODRIVER");
|
|
||||||
|
|
||||||
/* Check if user asked a different audio driver */
|
|
||||||
if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie _MCH present ? if not, assume ST machine */
|
|
||||||
if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) {
|
|
||||||
cookie_mch = MCH_ST;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie _SND present ? if not, assume ST machine */
|
|
||||||
if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
|
|
||||||
cookie_snd = SND_PSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie STFA present ? */
|
|
||||||
if (Getcookie(C_STFA, &dummy) != C_FOUND) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "no STFA audio\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
cookie_stfa = (cookie_stfa_t *) dummy;
|
|
||||||
|
|
||||||
SDL_MintAudio_stfa = cookie_stfa;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "STFA audio available!\n"));
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = Mint_OpenAudio;
|
|
||||||
this->CloseAudio = Mint_CloseAudio;
|
|
||||||
this->LockAudio = Mint_LockAudio;
|
|
||||||
this->UnlockAudio = Mint_UnlockAudio;
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap MINTAUDIO_STFA_bootstrap = {
|
|
||||||
MINT_AUDIO_DRIVER_NAME, "MiNT STFA audio driver",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
static void Mint_LockAudio(_THIS)
|
|
||||||
{
|
|
||||||
void *oldpile;
|
|
||||||
|
|
||||||
/* Stop replay */
|
|
||||||
oldpile=(void *)Super(0);
|
|
||||||
cookie_stfa->sound_enable=STFA_PLAY_DISABLE;
|
|
||||||
SuperToUser(oldpile);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_UnlockAudio(_THIS)
|
|
||||||
{
|
|
||||||
void *oldpile;
|
|
||||||
|
|
||||||
/* Restart replay */
|
|
||||||
oldpile=(void *)Super(0);
|
|
||||||
cookie_stfa->sound_enable=STFA_PLAY_ENABLE|STFA_PLAY_REPEAT;
|
|
||||||
SuperToUser(oldpile);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
void *oldpile;
|
|
||||||
|
|
||||||
/* Stop replay */
|
|
||||||
oldpile=(void *)Super(0);
|
|
||||||
cookie_stfa->sound_enable=STFA_PLAY_DISABLE;
|
|
||||||
SuperToUser(oldpile);
|
|
||||||
|
|
||||||
/* Wait if currently playing sound */
|
|
||||||
while (SDL_MintAudio_mutex != 0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear buffers */
|
|
||||||
if (SDL_MintAudio_audiobuf[0]) {
|
|
||||||
Mfree(SDL_MintAudio_audiobuf[0]);
|
|
||||||
SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
if (spec->channels > 2) {
|
|
||||||
spec->channels = 2; /* no more than stereo! */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check formats available */
|
|
||||||
MINTAUDIO_freqcount=0;
|
|
||||||
for (i=0;i<16;i++) {
|
|
||||||
SDL_MintAudio_AddFrequency(this, freqs[i], 0, i, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
for (i=0; i<MINTAUDIO_freqcount; i++) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
|
|
||||||
i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock,
|
|
||||||
MINTAUDIO_frequencies[i].predivisor
|
|
||||||
));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq);
|
|
||||||
spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
void *buffer;
|
|
||||||
void *oldpile;
|
|
||||||
|
|
||||||
buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
|
|
||||||
|
|
||||||
oldpile=(void *)Super(0);
|
|
||||||
|
|
||||||
/* Stop replay */
|
|
||||||
cookie_stfa->sound_enable=STFA_PLAY_DISABLE;
|
|
||||||
|
|
||||||
/* Select replay format */
|
|
||||||
cookie_stfa->sound_control = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
|
|
||||||
if ((spec->format & 0xff)==8) {
|
|
||||||
cookie_stfa->sound_control |= STFA_FORMAT_8BIT;
|
|
||||||
} else {
|
|
||||||
cookie_stfa->sound_control |= STFA_FORMAT_16BIT;
|
|
||||||
}
|
|
||||||
if (spec->channels==2) {
|
|
||||||
cookie_stfa->sound_control |= STFA_FORMAT_STEREO;
|
|
||||||
} else {
|
|
||||||
cookie_stfa->sound_control |= STFA_FORMAT_MONO;
|
|
||||||
}
|
|
||||||
if ((spec->format & 0x8000)!=0) {
|
|
||||||
cookie_stfa->sound_control |= STFA_FORMAT_SIGNED;
|
|
||||||
} else {
|
|
||||||
cookie_stfa->sound_control |= STFA_FORMAT_UNSIGNED;
|
|
||||||
}
|
|
||||||
if ((spec->format & 0x1000)!=0) {
|
|
||||||
cookie_stfa->sound_control |= STFA_FORMAT_BIGENDIAN;
|
|
||||||
} else {
|
|
||||||
cookie_stfa->sound_control |= STFA_FORMAT_LITENDIAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set buffer */
|
|
||||||
cookie_stfa->sound_start = (unsigned long) buffer;
|
|
||||||
cookie_stfa->sound_end = (unsigned long) (buffer + spec->size);
|
|
||||||
|
|
||||||
/* Set interrupt */
|
|
||||||
cookie_stfa->stfa_it = SDL_MintAudio_StfaInterrupt;
|
|
||||||
|
|
||||||
/* Restart replay */
|
|
||||||
cookie_stfa->sound_enable=STFA_PLAY_ENABLE|STFA_PLAY_REPEAT;
|
|
||||||
|
|
||||||
SuperToUser(oldpile);
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "hardware initialized\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
SDL_MintAudio_device = this;
|
|
||||||
|
|
||||||
/* Check audio capabilities */
|
|
||||||
if (Mint_CheckAudio(this, spec)==-1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate memory for audio buffers in DMA-able RAM */
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size));
|
|
||||||
|
|
||||||
SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM);
|
|
||||||
if (SDL_MintAudio_audiobuf[0]==NULL) {
|
|
||||||
SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ;
|
|
||||||
SDL_MintAudio_numbuf=0;
|
|
||||||
SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2);
|
|
||||||
SDL_MintAudio_audiosize = spec->size;
|
|
||||||
SDL_MintAudio_mutex = 0;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0]));
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1]));
|
|
||||||
|
|
||||||
SDL_MintAudio_CheckFpu();
|
|
||||||
|
|
||||||
/* Setup audio hardware */
|
|
||||||
Mint_InitAudio(this, spec);
|
|
||||||
|
|
||||||
return(1); /* We don't use threaded audio */
|
|
||||||
}
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
STFA control structure
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _SDL_mintaudio_stfa_h
|
|
||||||
#define _SDL_mintaudio_stfa_h
|
|
||||||
|
|
||||||
/*--- Defines ---*/
|
|
||||||
|
|
||||||
#define STFA_PLAY_ENABLE (1<<0)
|
|
||||||
#define STFA_PLAY_DISABLE (0<<0)
|
|
||||||
#define STFA_PLAY_REPEAT (1<<1)
|
|
||||||
#define STFA_PLAY_SINGLE (0<<1)
|
|
||||||
|
|
||||||
#define STFA_FORMAT_SIGNED (1<<15)
|
|
||||||
#define STFA_FORMAT_UNSIGNED (0<<15)
|
|
||||||
#define STFA_FORMAT_STEREO (1<<14)
|
|
||||||
#define STFA_FORMAT_MONO (0<<14)
|
|
||||||
#define STFA_FORMAT_16BIT (1<<13)
|
|
||||||
#define STFA_FORMAT_8BIT (0<<13)
|
|
||||||
#define STFA_FORMAT_LITENDIAN (1<<9)
|
|
||||||
#define STFA_FORMAT_BIGENDIAN (0<<9)
|
|
||||||
#define STFA_FORMAT_FREQ_MASK 0x0f
|
|
||||||
enum {
|
|
||||||
STFA_FORMAT_F4995=0,
|
|
||||||
STFA_FORMAT_F6269,
|
|
||||||
STFA_FORMAT_F7493,
|
|
||||||
STFA_FORMAT_F8192,
|
|
||||||
|
|
||||||
STFA_FORMAT_F9830,
|
|
||||||
STFA_FORMAT_F10971,
|
|
||||||
STFA_FORMAT_F12538,
|
|
||||||
STFA_FORMAT_F14985,
|
|
||||||
|
|
||||||
STFA_FORMAT_F16384,
|
|
||||||
STFA_FORMAT_F19819,
|
|
||||||
STFA_FORMAT_F21943,
|
|
||||||
STFA_FORMAT_F24576,
|
|
||||||
|
|
||||||
STFA_FORMAT_F30720,
|
|
||||||
STFA_FORMAT_F32336,
|
|
||||||
STFA_FORMAT_F43885,
|
|
||||||
STFA_FORMAT_F49152
|
|
||||||
};
|
|
||||||
|
|
||||||
/*--- Types ---*/
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
unsigned short sound_enable;
|
|
||||||
unsigned short sound_control;
|
|
||||||
unsigned short sound_output;
|
|
||||||
unsigned long sound_start;
|
|
||||||
unsigned long sound_current;
|
|
||||||
unsigned long sound_end;
|
|
||||||
unsigned short version;
|
|
||||||
void *old_vbl;
|
|
||||||
void *old_timera;
|
|
||||||
unsigned long old_mfp_status;
|
|
||||||
void *new_vbl;
|
|
||||||
void *drivers_list;
|
|
||||||
void *play_stop;
|
|
||||||
unsigned short frequency;
|
|
||||||
void *set_frequency;
|
|
||||||
unsigned short frequency_threshold;
|
|
||||||
unsigned short *custom_freq_table;
|
|
||||||
unsigned short stfa_on_off;
|
|
||||||
void *new_drivers_list;
|
|
||||||
unsigned long old_bit_2_of_cookie_snd;
|
|
||||||
void (*stfa_it)(void);
|
|
||||||
} cookie_stfa_t;
|
|
||||||
|
|
||||||
#endif /* _SDL_mintaudio_stfa_h */
|
|
||||||
|
|
@ -1,490 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
MiNT audio driver
|
|
||||||
using XBIOS functions (Falcon)
|
|
||||||
|
|
||||||
Patrice Mandin, Didier Méquignon
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <support.h>
|
|
||||||
|
|
||||||
/* Mint includes */
|
|
||||||
#include <mint/osbind.h>
|
|
||||||
#include <mint/falcon.h>
|
|
||||||
#include <mint/cookie.h>
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
#include "../../video/ataricommon/SDL_atarimxalloc_c.h"
|
|
||||||
#include "../../video/ataricommon/SDL_atarisuper.h"
|
|
||||||
|
|
||||||
#include "SDL_mintaudio.h"
|
|
||||||
#include "SDL_mintaudio_dma8.h"
|
|
||||||
|
|
||||||
/*--- Defines ---*/
|
|
||||||
|
|
||||||
#define MINT_AUDIO_DRIVER_NAME "mint_xbios"
|
|
||||||
|
|
||||||
/* Debug print info */
|
|
||||||
#define DEBUG_NAME "audio:xbios: "
|
|
||||||
#if 0
|
|
||||||
#define DEBUG_PRINT(what) \
|
|
||||||
{ \
|
|
||||||
printf what; \
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define DEBUG_PRINT(what)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*--- Static variables ---*/
|
|
||||||
|
|
||||||
static long cookie_snd;
|
|
||||||
|
|
||||||
/*--- Audio driver functions ---*/
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS);
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_LockAudio(_THIS);
|
|
||||||
static void Mint_UnlockAudio(_THIS);
|
|
||||||
|
|
||||||
/* To check/init hardware audio */
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
|
|
||||||
/*--- Audio driver bootstrap functions ---*/
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
/* unsigned long dummy;*/
|
|
||||||
const char *envr = SDL_getenv("SDL_AUDIODRIVER");
|
|
||||||
|
|
||||||
/*SDL_MintAudio_mint_present = (Getcookie(C_MiNT, &dummy) == C_FOUND);*/
|
|
||||||
SDL_MintAudio_mint_present = SDL_FALSE;
|
|
||||||
|
|
||||||
/* We can't use XBIOS in interrupt with Magic, don't know about thread */
|
|
||||||
/*if (Getcookie(C_MagX, &dummy) == C_FOUND) {
|
|
||||||
return(0);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
/* Check if user asked a different audio driver */
|
|
||||||
if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cookie _SND present ? if not, assume ST machine */
|
|
||||||
if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
|
|
||||||
cookie_snd = SND_PSG;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we have 16 bits audio */
|
|
||||||
if ((cookie_snd & SND_16BIT)==0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "no 16 bits sound\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if audio is lockable */
|
|
||||||
if (Locksnd()!=1) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "audio locked by other application\n"));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
Unlocksnd();
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "XBIOS audio available!\n"));
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = Mint_OpenAudio;
|
|
||||||
this->CloseAudio = Mint_CloseAudio;
|
|
||||||
this->LockAudio = Mint_LockAudio;
|
|
||||||
this->UnlockAudio = Mint_UnlockAudio;
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap MINTAUDIO_XBIOS_bootstrap = {
|
|
||||||
MINT_AUDIO_DRIVER_NAME, "MiNT XBIOS audio driver",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
static void Mint_LockAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Stop replay */
|
|
||||||
Buffoper(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_UnlockAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Restart replay */
|
|
||||||
Buffoper(SB_PLA_ENA|SB_PLA_RPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Stop replay */
|
|
||||||
SDL_MintAudio_WaitThread();
|
|
||||||
Buffoper(0);
|
|
||||||
|
|
||||||
if (!SDL_MintAudio_mint_present) {
|
|
||||||
/* Uninstall interrupt */
|
|
||||||
Jdisint(MFP_DMASOUND);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Wait if currently playing sound */
|
|
||||||
while (SDL_MintAudio_mutex != 0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear buffers */
|
|
||||||
if (SDL_MintAudio_audiobuf[0]) {
|
|
||||||
Mfree(SDL_MintAudio_audiobuf[0]);
|
|
||||||
SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Unlock sound system */
|
|
||||||
Unlocksnd();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Falcon XBIOS implementation of Devconnect() is buggy with external clock */
|
|
||||||
static void Devconnect2(int src, int dst, int sclk, int pre)
|
|
||||||
{
|
|
||||||
static const unsigned short MASK1[3] = { 0, 0x6000, 0 };
|
|
||||||
static const unsigned short MASK2[4] = { 0xFFF0, 0xFF8F, 0xF0FF, 0x0FFF };
|
|
||||||
static const unsigned short INDEX1[4] = { 1, 3, 5, 7 };
|
|
||||||
static const unsigned short INDEX2[4] = { 0, 2, 4, 6 };
|
|
||||||
unsigned short sync_div,dev_ctrl,dest_ctrl;
|
|
||||||
void *oldstack;
|
|
||||||
|
|
||||||
if (dst==0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
oldstack=(void *)Super(0);
|
|
||||||
|
|
||||||
dev_ctrl = DMAAUDIO_IO.dev_ctrl;
|
|
||||||
dest_ctrl = DMAAUDIO_IO.dest_ctrl;
|
|
||||||
dev_ctrl &= MASK2[src];
|
|
||||||
|
|
||||||
if (src==ADC) {
|
|
||||||
dev_ctrl |= MASK1[sclk];
|
|
||||||
} else {
|
|
||||||
dev_ctrl |= (INDEX1[sclk] << (src<<4));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dst & DMAREC) {
|
|
||||||
dest_ctrl &= 0xFFF0;
|
|
||||||
dest_ctrl |= INDEX1[src];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dst & DSPRECV) {
|
|
||||||
dest_ctrl &= 0xFF8F;
|
|
||||||
dest_ctrl |= (INDEX1[src]<<4);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dst & EXTOUT) {
|
|
||||||
dest_ctrl &= 0xF0FF;
|
|
||||||
dest_ctrl |= (INDEX1[src]<<8);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dst & DAC) {
|
|
||||||
dev_ctrl &= 0x0FFF;
|
|
||||||
dev_ctrl |= MASK1[sclk];
|
|
||||||
dest_ctrl &= 0x0FFF;
|
|
||||||
dest_ctrl |= (INDEX2[src]<<12);
|
|
||||||
}
|
|
||||||
|
|
||||||
sync_div = DMAAUDIO_IO.sync_div;
|
|
||||||
if (sclk==CLKEXT) {
|
|
||||||
pre<<=8;
|
|
||||||
sync_div &= 0xF0FF;
|
|
||||||
} else {
|
|
||||||
sync_div &= 0xFFF0;
|
|
||||||
}
|
|
||||||
sync_div |= pre;
|
|
||||||
|
|
||||||
DMAAUDIO_IO.dev_ctrl = dev_ctrl;
|
|
||||||
DMAAUDIO_IO.dest_ctrl = dest_ctrl;
|
|
||||||
DMAAUDIO_IO.sync_div = sync_div;
|
|
||||||
|
|
||||||
SuperToUser(oldstack);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_CheckExternalClock(_THIS)
|
|
||||||
{
|
|
||||||
#define SIZE_BUF_CLOCK_MEASURE (44100/10)
|
|
||||||
|
|
||||||
char *buffer;
|
|
||||||
int i, j;
|
|
||||||
|
|
||||||
/* DSP present with its GPIO port ? */
|
|
||||||
if ((cookie_snd & SND_DSP)==0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = Atari_SysMalloc(SIZE_BUF_CLOCK_MEASURE, MX_STRAM);
|
|
||||||
if (buffer==NULL) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Not enough memory for the measure\n"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_memset(buffer, 0, SIZE_BUF_CLOCK_MEASURE);
|
|
||||||
|
|
||||||
Buffoper(0);
|
|
||||||
Settracks(0,0);
|
|
||||||
Setmontracks(0);
|
|
||||||
Setmode(MONO8);
|
|
||||||
Jdisint(MFP_TIMERA);
|
|
||||||
|
|
||||||
for (i=0; i<2; i++) {
|
|
||||||
Gpio(GPIO_SET,7); /* DSP port gpio outputs */
|
|
||||||
Gpio(GPIO_WRITE,2+i); /* 22.5792/24.576 MHz for 44.1/48KHz */
|
|
||||||
Devconnect2(DMAPLAY, DAC, CLKEXT, CLK50K); /* Matrix and clock source */
|
|
||||||
Setbuffer(0, buffer, buffer + SIZE_BUF_CLOCK_MEASURE); /* Set buffer */
|
|
||||||
Xbtimer(XB_TIMERA, 5, 38, SDL_MintAudio_XbiosInterruptMeasureClock); /* delay mode timer A, prediv /64, 1KHz */
|
|
||||||
Jenabint(MFP_TIMERA);
|
|
||||||
SDL_MintAudio_clocktics = 0;
|
|
||||||
Buffoper(SB_PLA_ENA);
|
|
||||||
usleep(110000);
|
|
||||||
|
|
||||||
if((Buffoper(-1) & 1)==0) {
|
|
||||||
if (SDL_MintAudio_clocktics) {
|
|
||||||
unsigned long khz;
|
|
||||||
|
|
||||||
khz = ((SIZE_BUF_CLOCK_MEASURE/SDL_MintAudio_clocktics) +1) & 0xFFFFFFFE;
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "measure %d: freq=%lu KHz\n", i+1, khz));
|
|
||||||
|
|
||||||
if(khz==44) {
|
|
||||||
for (j=1; j<4; j++) {
|
|
||||||
SDL_MintAudio_AddFrequency(this, MASTERCLOCK_44K/(MASTERPREDIV_FALCON*(1<<j)), MASTERCLOCK_44K, (1<<j)-1, 2+i);
|
|
||||||
}
|
|
||||||
} else if (khz==48) {
|
|
||||||
for (j=1; j<4; j++) {
|
|
||||||
SDL_MintAudio_AddFrequency(this, MASTERCLOCK_48K/(MASTERPREDIV_FALCON*(1<<j)), MASTERCLOCK_48K, (1<<j)-1, 2+i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "No measure\n"));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "No SDMA clock\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
Buffoper(0); /* stop */
|
|
||||||
Jdisint(MFP_TIMERA); /* Uninstall interrupt */
|
|
||||||
}
|
|
||||||
|
|
||||||
Mfree(buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
if (spec->channels > 2) {
|
|
||||||
spec->channels = 2; /* no more than stereo! */
|
|
||||||
}
|
|
||||||
|
|
||||||
spec->format |= 0x8000; /* Audio is always signed */
|
|
||||||
if ((spec->format & 0x00ff)==16) {
|
|
||||||
spec->format |= 0x1000; /* Audio is always big endian */
|
|
||||||
spec->channels=2; /* 16 bits always stereo */
|
|
||||||
}
|
|
||||||
|
|
||||||
MINTAUDIO_freqcount=0;
|
|
||||||
|
|
||||||
/* Add external clocks if present */
|
|
||||||
Mint_CheckExternalClock(this);
|
|
||||||
|
|
||||||
/* Standard clocks */
|
|
||||||
for (i=1;i<12;i++) {
|
|
||||||
/* Remove unusable Falcon codec predivisors */
|
|
||||||
if ((i==6) || (i==8) || (i==10)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
SDL_MintAudio_AddFrequency(this, MASTERCLOCK_FALCON1/(MASTERPREDIV_FALCON*(i+1)), MASTERCLOCK_FALCON1, i, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
for (i=0; i<MINTAUDIO_freqcount; i++) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
|
|
||||||
i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock,
|
|
||||||
MINTAUDIO_frequencies[i].predivisor
|
|
||||||
));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq);
|
|
||||||
spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff));
|
|
||||||
DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
|
|
||||||
DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
|
|
||||||
DEBUG_PRINT(("channels=%d, ", spec->channels));
|
|
||||||
DEBUG_PRINT(("freq=%d\n", spec->freq));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int channels_mode, prediv;
|
|
||||||
void *buffer;
|
|
||||||
|
|
||||||
/* Stop currently playing sound */
|
|
||||||
SDL_MintAudio_quit_thread = SDL_FALSE;
|
|
||||||
SDL_MintAudio_thread_finished = SDL_TRUE;
|
|
||||||
SDL_MintAudio_WaitThread();
|
|
||||||
Buffoper(0);
|
|
||||||
|
|
||||||
/* Set replay tracks */
|
|
||||||
Settracks(0,0);
|
|
||||||
Setmontracks(0);
|
|
||||||
|
|
||||||
/* Select replay format */
|
|
||||||
channels_mode=STEREO16;
|
|
||||||
switch (spec->format & 0xff) {
|
|
||||||
case 8:
|
|
||||||
if (spec->channels==2) {
|
|
||||||
channels_mode=STEREO8;
|
|
||||||
} else {
|
|
||||||
channels_mode=MONO8;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (Setmode(channels_mode)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setmode() failed\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
prediv = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
|
|
||||||
if (MINTAUDIO_frequencies[MINTAUDIO_numfreq].gpio_bits != -1) {
|
|
||||||
Gpio(GPIO_SET,7); /* DSP port gpio outputs */
|
|
||||||
Gpio(GPIO_WRITE, MINTAUDIO_frequencies[MINTAUDIO_numfreq].gpio_bits);
|
|
||||||
Devconnect2(DMAPLAY, DAC|EXTOUT, CLKEXT, prediv);
|
|
||||||
} else {
|
|
||||||
Devconnect2(DMAPLAY, DAC, CLK25M, prediv);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set buffer */
|
|
||||||
buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
|
|
||||||
if (Setbuffer(0, buffer, buffer + spec->size)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setbuffer() failed\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SDL_MintAudio_mint_present) {
|
|
||||||
SDL_MintAudio_thread_pid = tfork(SDL_MintAudio_Thread, 0);
|
|
||||||
} else {
|
|
||||||
/* Install interrupt */
|
|
||||||
Jdisint(MFP_DMASOUND);
|
|
||||||
/*Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_XbiosInterrupt);*/
|
|
||||||
Xbtimer(XB_TIMERA, 8, 1, SDL_MintAudio_Dma8Interrupt);
|
|
||||||
Jenabint(MFP_DMASOUND);
|
|
||||||
|
|
||||||
if (Setinterrupt(SI_TIMERA, SI_PLAY)<0) {
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "Setinterrupt() failed\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Go */
|
|
||||||
Buffoper(SB_PLA_ENA|SB_PLA_RPT);
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "hardware initialized\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
/* Lock sound system */
|
|
||||||
if (Locksnd()!=1) {
|
|
||||||
SDL_SetError("Mint_OpenAudio: Audio system already in use");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_MintAudio_device = this;
|
|
||||||
|
|
||||||
/* Check audio capabilities */
|
|
||||||
if (Mint_CheckAudio(this, spec)==-1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate memory for audio buffers in DMA-able RAM */
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size));
|
|
||||||
|
|
||||||
SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM);
|
|
||||||
if (SDL_MintAudio_audiobuf[0]==NULL) {
|
|
||||||
SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ;
|
|
||||||
SDL_MintAudio_numbuf=0;
|
|
||||||
SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2);
|
|
||||||
SDL_MintAudio_audiosize = spec->size;
|
|
||||||
SDL_MintAudio_mutex = 0;
|
|
||||||
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0]));
|
|
||||||
DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1]));
|
|
||||||
|
|
||||||
SDL_MintAudio_CheckFpu();
|
|
||||||
|
|
||||||
/* Setup audio hardware */
|
|
||||||
Mint_InitAudio(this, spec);
|
|
||||||
|
|
||||||
return(1); /* We don't use SDL threaded audio */
|
|
||||||
}
|
|
||||||
|
|
@ -1,264 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Tru64 UNIX MME support */
|
|
||||||
#include <mme_api.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "SDL_mmeaudio.h"
|
|
||||||
|
|
||||||
static BOOL inUse[NUM_BUFFERS];
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int MME_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void MME_WaitAudio(_THIS);
|
|
||||||
static Uint8 *MME_GetAudioBuf(_THIS);
|
|
||||||
static void MME_PlayAudio(_THIS);
|
|
||||||
static void MME_WaitDone(_THIS);
|
|
||||||
static void MME_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
if ( device ) {
|
|
||||||
if ( device->hidden ) {
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
device->hidden = NULL;
|
|
||||||
}
|
|
||||||
SDL_free(device);
|
|
||||||
device = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = MME_OpenAudio;
|
|
||||||
this->WaitAudio = MME_WaitAudio;
|
|
||||||
this->PlayAudio = MME_PlayAudio;
|
|
||||||
this->GetAudioBuf = MME_GetAudioBuf;
|
|
||||||
this->WaitDone = MME_WaitDone;
|
|
||||||
this->CloseAudio = MME_CloseAudio;
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap MMEAUDIO_bootstrap = {
|
|
||||||
"waveout", "Tru64 MME WaveOut",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
static void SetMMerror(char *function, MMRESULT code)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
char errbuf[MAXERRORLENGTH];
|
|
||||||
|
|
||||||
SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
|
|
||||||
len = SDL_strlen(errbuf);
|
|
||||||
waveOutGetErrorText(code, errbuf+len, MAXERRORLENGTH-len);
|
|
||||||
SDL_SetError("%s",errbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CALLBACK MME_CALLBACK(HWAVEOUT hwo,
|
|
||||||
UINT uMsg,
|
|
||||||
DWORD dwInstance,
|
|
||||||
LPARAM dwParam1,
|
|
||||||
LPARAM dwParam2)
|
|
||||||
{
|
|
||||||
WAVEHDR *wp = (WAVEHDR *) dwParam1;
|
|
||||||
|
|
||||||
if ( uMsg == WOM_DONE )
|
|
||||||
inUse[wp->dwUser] = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int MME_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
MMRESULT result;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
mixbuf = NULL;
|
|
||||||
|
|
||||||
/* Set basic WAVE format parameters */
|
|
||||||
shm = mmeAllocMem(sizeof(*shm));
|
|
||||||
if ( shm == NULL ) {
|
|
||||||
SDL_SetError("Out of memory: shm");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
shm->sound = 0;
|
|
||||||
shm->wFmt.wf.wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
|
|
||||||
/* Determine the audio parameters from the AudioSpec */
|
|
||||||
switch ( spec->format & 0xFF ) {
|
|
||||||
case 8:
|
|
||||||
/* Unsigned 8 bit audio data */
|
|
||||||
spec->format = AUDIO_U8;
|
|
||||||
shm->wFmt.wBitsPerSample = 8;
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
/* Signed 16 bit audio data */
|
|
||||||
spec->format = AUDIO_S16;
|
|
||||||
shm->wFmt.wBitsPerSample = 16;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
shm->wFmt.wf.nChannels = spec->channels;
|
|
||||||
shm->wFmt.wf.nSamplesPerSec = spec->freq;
|
|
||||||
shm->wFmt.wf.nBlockAlign =
|
|
||||||
shm->wFmt.wf.nChannels * shm->wFmt.wBitsPerSample / 8;
|
|
||||||
shm->wFmt.wf.nAvgBytesPerSec =
|
|
||||||
shm->wFmt.wf.nSamplesPerSec * shm->wFmt.wf.nBlockAlign;
|
|
||||||
|
|
||||||
/* Check the buffer size -- minimum of 1/4 second (word aligned) */
|
|
||||||
if ( spec->samples < (spec->freq/4) )
|
|
||||||
spec->samples = ((spec->freq/4)+3)&~3;
|
|
||||||
|
|
||||||
/* Update the fragment size as size in bytes */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
result = waveOutOpen(&(shm->sound),
|
|
||||||
WAVE_MAPPER,
|
|
||||||
&(shm->wFmt.wf),
|
|
||||||
MME_CALLBACK,
|
|
||||||
NULL,
|
|
||||||
(CALLBACK_FUNCTION|WAVE_OPEN_SHAREABLE));
|
|
||||||
if ( result != MMSYSERR_NOERROR ) {
|
|
||||||
SetMMerror("waveOutOpen()", result);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the sound buffers */
|
|
||||||
mixbuf = (Uint8 *)mmeAllocBuffer(NUM_BUFFERS * (spec->size));
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
SDL_SetError("Out of memory: mixbuf");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < NUM_BUFFERS; i++) {
|
|
||||||
shm->wHdr[i].lpData = &mixbuf[i * (spec->size)];
|
|
||||||
shm->wHdr[i].dwBufferLength = spec->size;
|
|
||||||
shm->wHdr[i].dwFlags = 0;
|
|
||||||
shm->wHdr[i].dwUser = i;
|
|
||||||
shm->wHdr[i].dwLoops = 0; /* loop control counter */
|
|
||||||
shm->wHdr[i].lpNext = NULL; /* reserved for driver */
|
|
||||||
shm->wHdr[i].reserved = 0;
|
|
||||||
inUse[i] = FALSE;
|
|
||||||
}
|
|
||||||
next_buffer = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MME_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
while ( inUse[next_buffer] ) {
|
|
||||||
mmeWaitForCallbacks();
|
|
||||||
mmeProcessCallbacks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *MME_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
Uint8 *retval;
|
|
||||||
|
|
||||||
inUse[next_buffer] = TRUE;
|
|
||||||
retval = (Uint8 *)(shm->wHdr[next_buffer].lpData);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MME_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Queue it up */
|
|
||||||
waveOutWrite(shm->sound, &(shm->wHdr[next_buffer]), sizeof(WAVEHDR));
|
|
||||||
next_buffer = (next_buffer+1)%NUM_BUFFERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MME_WaitDone(_THIS)
|
|
||||||
{
|
|
||||||
MMRESULT result;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( shm->sound ) {
|
|
||||||
for (i = 0; i < NUM_BUFFERS; i++)
|
|
||||||
while ( inUse[i] ) {
|
|
||||||
mmeWaitForCallbacks();
|
|
||||||
mmeProcessCallbacks();
|
|
||||||
}
|
|
||||||
result = waveOutReset(shm->sound);
|
|
||||||
if ( result != MMSYSERR_NOERROR )
|
|
||||||
SetMMerror("waveOutReset()", result);
|
|
||||||
mmeProcessCallbacks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void MME_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
MMRESULT result;
|
|
||||||
|
|
||||||
if ( mixbuf ) {
|
|
||||||
result = mmeFreeBuffer(mixbuf);
|
|
||||||
if (result != MMSYSERR_NOERROR )
|
|
||||||
SetMMerror("mmeFreeBuffer", result);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( shm ) {
|
|
||||||
if ( shm->sound ) {
|
|
||||||
result = waveOutClose(shm->sound);
|
|
||||||
if (result != MMSYSERR_NOERROR )
|
|
||||||
SetMMerror("waveOutClose()", result);
|
|
||||||
mmeProcessCallbacks();
|
|
||||||
}
|
|
||||||
result = mmeFreeMem(shm);
|
|
||||||
if (result != MMSYSERR_NOERROR )
|
|
||||||
SetMMerror("mmeFreeMem()", result);
|
|
||||||
shm = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#ifndef _SDL_lowaudio_h
|
|
||||||
#define _SDL_lowaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
#define NUM_BUFFERS 2
|
|
||||||
|
|
||||||
struct SharedMem {
|
|
||||||
HWAVEOUT sound;
|
|
||||||
WAVEHDR wHdr[NUM_BUFFERS];
|
|
||||||
PCMWAVEFORMAT wFmt;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
Uint8 *mixbuf; /* The raw allocated mixing buffer */
|
|
||||||
struct SharedMem *shm;
|
|
||||||
int next_buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define shm (this->hidden->shm)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define next_buffer (this->hidden->next_buffer)
|
|
||||||
/* Old variable names */
|
|
||||||
#endif /* _SDL_lowaudio_h */
|
|
||||||
|
|
@ -1,423 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
|
|
||||||
This driver was written by:
|
|
||||||
Erik Inge Bolsø
|
|
||||||
knan@mo.himolde.no
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include <signal.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_nasaudio.h"
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
|
|
||||||
#include "SDL_loadso.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The tag name used by artsc audio */
|
|
||||||
#define NAS_DRIVER_NAME "nas"
|
|
||||||
|
|
||||||
static struct SDL_PrivateAudioData *this2 = NULL;
|
|
||||||
|
|
||||||
static void (*NAS_AuCloseServer) (AuServer *);
|
|
||||||
static void (*NAS_AuNextEvent) (AuServer *, AuBool, AuEvent *);
|
|
||||||
static AuBool(*NAS_AuDispatchEvent) (AuServer *, AuEvent *);
|
|
||||||
static AuFlowID(*NAS_AuCreateFlow) (AuServer *, AuStatus *);
|
|
||||||
static void (*NAS_AuStartFlow) (AuServer *, AuFlowID, AuStatus *);
|
|
||||||
static void (*NAS_AuSetElements)
|
|
||||||
(AuServer *, AuFlowID, AuBool, int, AuElement *, AuStatus *);
|
|
||||||
static void (*NAS_AuWriteElement)
|
|
||||||
(AuServer *, AuFlowID, int, AuUint32, AuPointer, AuBool, AuStatus *);
|
|
||||||
static AuServer *(*NAS_AuOpenServer)
|
|
||||||
(_AuConst char *, int, _AuConst char *, int, _AuConst char *, char **);
|
|
||||||
static AuEventHandlerRec *(*NAS_AuRegisterEventHandler)
|
|
||||||
(AuServer *, AuMask, int, AuID, AuEventHandlerCallback, AuPointer);
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
|
|
||||||
|
|
||||||
static const char *nas_library = SDL_AUDIO_DRIVER_NAS_DYNAMIC;
|
|
||||||
static void *nas_handle = NULL;
|
|
||||||
|
|
||||||
static int
|
|
||||||
load_nas_sym(const char *fn, void **addr)
|
|
||||||
{
|
|
||||||
*addr = SDL_LoadFunction(nas_handle, fn);
|
|
||||||
if (*addr == NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cast funcs to char* first, to please GCC's strict aliasing rules. */
|
|
||||||
#define SDL_NAS_SYM(x) \
|
|
||||||
if (!load_nas_sym(#x, (void **) (char *) &NAS_##x)) return -1
|
|
||||||
#else
|
|
||||||
#define SDL_NAS_SYM(x) NAS_##x = x
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int
|
|
||||||
load_nas_syms(void)
|
|
||||||
{
|
|
||||||
SDL_NAS_SYM(AuCloseServer);
|
|
||||||
SDL_NAS_SYM(AuNextEvent);
|
|
||||||
SDL_NAS_SYM(AuDispatchEvent);
|
|
||||||
SDL_NAS_SYM(AuCreateFlow);
|
|
||||||
SDL_NAS_SYM(AuStartFlow);
|
|
||||||
SDL_NAS_SYM(AuSetElements);
|
|
||||||
SDL_NAS_SYM(AuWriteElement);
|
|
||||||
SDL_NAS_SYM(AuOpenServer);
|
|
||||||
SDL_NAS_SYM(AuRegisterEventHandler);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef SDL_NAS_SYM
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_NAS_DYNAMIC
|
|
||||||
|
|
||||||
static void
|
|
||||||
UnloadNASLibrary(void)
|
|
||||||
{
|
|
||||||
if (nas_handle != NULL) {
|
|
||||||
SDL_UnloadObject(nas_handle);
|
|
||||||
nas_handle = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
LoadNASLibrary(void)
|
|
||||||
{
|
|
||||||
int retval = 0;
|
|
||||||
if (nas_handle == NULL) {
|
|
||||||
nas_handle = SDL_LoadObject(nas_library);
|
|
||||||
if (nas_handle == NULL) {
|
|
||||||
/* Copy error string so we can use it in a new SDL_SetError(). */
|
|
||||||
char *origerr = SDL_GetError();
|
|
||||||
size_t len = SDL_strlen(origerr) + 1;
|
|
||||||
char *err = (char *) alloca(len);
|
|
||||||
SDL_strlcpy(err, origerr, len);
|
|
||||||
retval = -1;
|
|
||||||
SDL_SetError("NAS: SDL_LoadObject('%s') failed: %s\n",
|
|
||||||
nas_library, err);
|
|
||||||
} else {
|
|
||||||
retval = load_nas_syms();
|
|
||||||
if (retval < 0) {
|
|
||||||
UnloadNASLibrary();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void
|
|
||||||
UnloadNASLibrary(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
LoadNASLibrary(void)
|
|
||||||
{
|
|
||||||
load_nas_syms();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_AUDIO_DRIVER_NAS_DYNAMIC */
|
|
||||||
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void NAS_WaitAudio(_THIS);
|
|
||||||
static void NAS_PlayAudio(_THIS);
|
|
||||||
static Uint8 *NAS_GetAudioBuf(_THIS);
|
|
||||||
static void NAS_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
if (LoadNASLibrary() == 0) {
|
|
||||||
AuServer *aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
|
|
||||||
if (!aud) {
|
|
||||||
UnloadNASLibrary();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
NAS_AuCloseServer(aud);
|
|
||||||
UnloadNASLibrary();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
UnloadNASLibrary();
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
if (LoadNASLibrary() < 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = NAS_OpenAudio;
|
|
||||||
this->WaitAudio = NAS_WaitAudio;
|
|
||||||
this->PlayAudio = NAS_PlayAudio;
|
|
||||||
this->GetAudioBuf = NAS_GetAudioBuf;
|
|
||||||
this->CloseAudio = NAS_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap NAS_bootstrap = {
|
|
||||||
NAS_DRIVER_NAME, "Network Audio System",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void NAS_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
while ( this->hidden->buf_free < this->hidden->mixlen ) {
|
|
||||||
AuEvent ev;
|
|
||||||
NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
|
|
||||||
NAS_AuDispatchEvent(this->hidden->aud, &ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NAS_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
while (this->hidden->mixlen > this->hidden->buf_free) { /* We think the buffer is full? Yikes! Ask the server for events,
|
|
||||||
in the hope that some of them is LowWater events telling us more
|
|
||||||
of the buffer is free now than what we think. */
|
|
||||||
AuEvent ev;
|
|
||||||
NAS_AuNextEvent(this->hidden->aud, AuTrue, &ev);
|
|
||||||
NAS_AuDispatchEvent(this->hidden->aud, &ev);
|
|
||||||
}
|
|
||||||
this->hidden->buf_free -= this->hidden->mixlen;
|
|
||||||
|
|
||||||
/* Write the audio data */
|
|
||||||
NAS_AuWriteElement(this->hidden->aud, this->hidden->flow, 0, this->hidden->mixlen, this->hidden->mixbuf, AuFalse, NULL);
|
|
||||||
|
|
||||||
this->hidden->written += this->hidden->mixlen;
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", this->hidden->mixlen);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *NAS_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(this->hidden->mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NAS_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( this->hidden->mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(this->hidden->mixbuf);
|
|
||||||
this->hidden->mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( this->hidden->aud ) {
|
|
||||||
NAS_AuCloseServer(this->hidden->aud);
|
|
||||||
this->hidden->aud = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned char sdlformat_to_auformat(unsigned int fmt)
|
|
||||||
{
|
|
||||||
switch (fmt)
|
|
||||||
{
|
|
||||||
case AUDIO_U8:
|
|
||||||
return AuFormatLinearUnsigned8;
|
|
||||||
case AUDIO_S8:
|
|
||||||
return AuFormatLinearSigned8;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
return AuFormatLinearUnsigned16LSB;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
return AuFormatLinearUnsigned16MSB;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
return AuFormatLinearSigned16LSB;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
return AuFormatLinearSigned16MSB;
|
|
||||||
}
|
|
||||||
return AuNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AuBool
|
|
||||||
event_handler(AuServer* aud, AuEvent* ev, AuEventHandlerRec* hnd)
|
|
||||||
{
|
|
||||||
switch (ev->type) {
|
|
||||||
case AuEventTypeElementNotify: {
|
|
||||||
AuElementNotifyEvent* event = (AuElementNotifyEvent *)ev;
|
|
||||||
|
|
||||||
switch (event->kind) {
|
|
||||||
case AuElementNotifyKindLowWater:
|
|
||||||
if (this2->buf_free >= 0) {
|
|
||||||
this2->really += event->num_bytes;
|
|
||||||
gettimeofday(&this2->last_tv, 0);
|
|
||||||
this2->buf_free += event->num_bytes;
|
|
||||||
} else {
|
|
||||||
this2->buf_free = event->num_bytes;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AuElementNotifyKindState:
|
|
||||||
switch (event->cur_state) {
|
|
||||||
case AuStatePause:
|
|
||||||
if (event->reason != AuReasonUser) {
|
|
||||||
if (this2->buf_free >= 0) {
|
|
||||||
this2->really += event->num_bytes;
|
|
||||||
gettimeofday(&this2->last_tv, 0);
|
|
||||||
this2->buf_free += event->num_bytes;
|
|
||||||
} else {
|
|
||||||
this2->buf_free = event->num_bytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return AuTrue;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AuDeviceID
|
|
||||||
find_device(_THIS, int nch)
|
|
||||||
{
|
|
||||||
/* These "Au" things are all macros, not functions... */
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < AuServerNumDevices(this->hidden->aud); i++) {
|
|
||||||
if ((AuDeviceKind(AuServerDevice(this->hidden->aud, i)) ==
|
|
||||||
AuComponentKindPhysicalOutput) &&
|
|
||||||
AuDeviceNumTracks(AuServerDevice(this->hidden->aud, i)) == nch) {
|
|
||||||
return AuDeviceIdentifier(AuServerDevice(this->hidden->aud, i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return AuNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int NAS_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
AuElement elms[3];
|
|
||||||
int buffer_size;
|
|
||||||
Uint16 test_format, format;
|
|
||||||
|
|
||||||
this->hidden->mixbuf = NULL;
|
|
||||||
|
|
||||||
/* Try for a closest match on audio format */
|
|
||||||
format = 0;
|
|
||||||
for ( test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
! format && test_format; ) {
|
|
||||||
format = sdlformat_to_auformat(test_format);
|
|
||||||
|
|
||||||
if (format == AuNone) {
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( format == 0 ) {
|
|
||||||
SDL_SetError("Couldn't find any hardware audio formats");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
this->hidden->aud = NAS_AuOpenServer("", 0, NULL, 0, NULL, NULL);
|
|
||||||
if (this->hidden->aud == 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("Couldn't open connection to NAS server");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->hidden->dev = find_device(this, spec->channels);
|
|
||||||
if ((this->hidden->dev == AuNone) || (!(this->hidden->flow = NAS_AuCreateFlow(this->hidden->aud, NULL)))) {
|
|
||||||
NAS_AuCloseServer(this->hidden->aud);
|
|
||||||
this->hidden->aud = 0;
|
|
||||||
SDL_SetError("Couldn't find a fitting playback device on NAS server");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer_size = spec->freq;
|
|
||||||
if (buffer_size < 4096)
|
|
||||||
buffer_size = 4096;
|
|
||||||
|
|
||||||
if (buffer_size > 32768)
|
|
||||||
buffer_size = 32768; /* So that the buffer won't get unmanageably big. */
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
this2 = this->hidden;
|
|
||||||
|
|
||||||
/* These "Au" things without a NAS_ prefix are macros, not functions... */
|
|
||||||
AuMakeElementImportClient(elms, spec->freq, format, spec->channels, AuTrue,
|
|
||||||
buffer_size, buffer_size / 4, 0, NULL);
|
|
||||||
AuMakeElementExportDevice(elms+1, 0, this->hidden->dev, spec->freq,
|
|
||||||
AuUnlimitedSamples, 0, NULL);
|
|
||||||
NAS_AuSetElements(this->hidden->aud, this->hidden->flow, AuTrue, 2, elms, NULL);
|
|
||||||
NAS_AuRegisterEventHandler(this->hidden->aud, AuEventHandlerIDMask, 0, this->hidden->flow,
|
|
||||||
event_handler, (AuPointer) NULL);
|
|
||||||
|
|
||||||
NAS_AuStartFlow(this->hidden->aud, this->hidden->flow, NULL);
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
this->hidden->mixlen = spec->size;
|
|
||||||
this->hidden->mixbuf = (Uint8 *)SDL_AllocAudioMem(this->hidden->mixlen);
|
|
||||||
if ( this->hidden->mixbuf == NULL ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* Get the parent process id (we're the parent of the audio thread) */
|
|
||||||
this->hidden->parent = getpid();
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
|
|
||||||
This driver was written by:
|
|
||||||
Erik Inge Bolsø
|
|
||||||
knan@mo.himolde.no
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_nasaudio_h
|
|
||||||
#define _SDL_nasaudio_h
|
|
||||||
|
|
||||||
#ifdef __sgi
|
|
||||||
#include <nas/audiolib.h>
|
|
||||||
#else
|
|
||||||
#include <audio/audiolib.h>
|
|
||||||
#endif
|
|
||||||
#include <sys/time.h>
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
AuServer* aud;
|
|
||||||
AuFlowID flow;
|
|
||||||
AuDeviceID dev;
|
|
||||||
|
|
||||||
/* The parent process id, to detect when application quits */
|
|
||||||
pid_t parent;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
int mixlen;
|
|
||||||
|
|
||||||
int written;
|
|
||||||
int really;
|
|
||||||
int bps;
|
|
||||||
struct timeval last_tv;
|
|
||||||
int buf_free;
|
|
||||||
};
|
|
||||||
#endif /* _SDL_nasaudio_h */
|
|
||||||
|
|
||||||
|
|
@ -1,335 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
#include <nds.h>
|
|
||||||
#include "SDL.h"
|
|
||||||
#include "SDL_endian.h"
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "SDL_ndsaudio.h"
|
|
||||||
#include "soundcommon.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int NDS_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void NDS_WaitAudio(_THIS);
|
|
||||||
static void NDS_PlayAudio(_THIS);
|
|
||||||
static Uint8 *NDS_GetAudioBuf(_THIS);
|
|
||||||
static void NDS_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
u32 framecounter = 0,soundoffset = 0;
|
|
||||||
static SDL_AudioDevice *sdl_nds_audiodevice;
|
|
||||||
|
|
||||||
//void SoundMixCallback(void *stream,u32 size)
|
|
||||||
//{
|
|
||||||
// //printf("SoundMixCallback\n");
|
|
||||||
//
|
|
||||||
// Uint8 *buffer;
|
|
||||||
//
|
|
||||||
// buffer = sdl_nds_audiodevice->hidden->mixbuf;
|
|
||||||
// memset(buffer, sdl_nds_audiodevice->spec.silence, size);
|
|
||||||
//
|
|
||||||
// if (!sdl_nds_audiodevice->paused){
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// //if (sdl_nds_audiodevice->convert.needed) {
|
|
||||||
// // int silence;
|
|
||||||
//
|
|
||||||
// // if (sdl_nds_audiodevice->convert.src_format == AUDIO_U8 ) {
|
|
||||||
// // silence = 0x80;
|
|
||||||
// // } else {
|
|
||||||
// // silence = 0;
|
|
||||||
// // }
|
|
||||||
// // memset(sdl_nds_audiodevice->convert.buf, silence, sdl_nds_audiodevice->convert.len);
|
|
||||||
// // sdl_nds_audiodevice->spec.callback(sdl_nds_audiodevice->spec.userdata,
|
|
||||||
// // (Uint8 *)sdl_nds_audiodevice->convert.buf,sdl_nds_audiodevice->convert.len);
|
|
||||||
// // SDL_ConvertAudio(&sdl_nds_audiodevice->convert);
|
|
||||||
// // memcpy(buffer, sdl_nds_audiodevice->convert.buf, sdl_nds_audiodevice->convert.len_cvt);
|
|
||||||
// //} else
|
|
||||||
// {
|
|
||||||
// sdl_nds_audiodevice->spec.callback(sdl_nds_audiodevice->spec.userdata, buffer, size);
|
|
||||||
// //memcpy((Sint16 *)stream,buffer, size);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// if(soundsystem->format == 8)
|
|
||||||
// {
|
|
||||||
// int i;
|
|
||||||
// s32 *buffer32 = (s32 *)buffer;
|
|
||||||
// s32 *stream32 = (s32 *)stream;
|
|
||||||
// for(i=0;i<size/4;i++){ *stream32++ = buffer32[i] ^ 0x80808080;}
|
|
||||||
// //for(i = 0; i < size; i++)
|
|
||||||
// // ((s8*)stream)[i]=(buffer[i]^0x80);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// int i;
|
|
||||||
// for(i = 0; i < size; i++){
|
|
||||||
// //((short*)stream)[i] =(short)buffer[i] << 8; // sound 8bit ---> buffer 16bit
|
|
||||||
// //if (buffer[i] &0x80)
|
|
||||||
// //((Sint16*)stream)[i] = 0xff00 | buffer[i];
|
|
||||||
// ((Sint16*)stream)[i] = (buffer[i] - 128) << 8;
|
|
||||||
//
|
|
||||||
// //else
|
|
||||||
// // ((Sint16*)stream)[i] = buffer[i];
|
|
||||||
// }
|
|
||||||
// //register signed char *pSrc =buffer;
|
|
||||||
// //register short *pDest =stream;
|
|
||||||
// //int x;
|
|
||||||
// // for (x=size; x>0; x--)
|
|
||||||
// // {
|
|
||||||
// // register short temp = (((short)*pSrc)-128)<<8;
|
|
||||||
// // pSrc++;
|
|
||||||
// // *pDest++ = temp;
|
|
||||||
// // }
|
|
||||||
//
|
|
||||||
// //memcpy((Sint16 *)stream,buffer, size);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
void SoundMixCallback(void *stream,u32 len)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *audio = (SDL_AudioDevice *)sdl_nds_audiodevice;
|
|
||||||
|
|
||||||
/* Silence the buffer, since it's ours */
|
|
||||||
SDL_memset(stream, audio->spec.silence, len);
|
|
||||||
|
|
||||||
/* Only do soemthing if audio is enabled */
|
|
||||||
if ( ! audio->enabled )
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ( ! audio->paused ) {
|
|
||||||
if ( audio->convert.needed ) {
|
|
||||||
//fprintf(stderr,"converting audio\n");
|
|
||||||
SDL_mutexP(audio->mixer_lock);
|
|
||||||
(*audio->spec.callback)(audio->spec.userdata,
|
|
||||||
(Uint8 *)audio->convert.buf,audio->convert.len);
|
|
||||||
SDL_mutexV(audio->mixer_lock);
|
|
||||||
SDL_ConvertAudio(&audio->convert);
|
|
||||||
SDL_memcpy(stream,audio->convert.buf,audio->convert.len_cvt);
|
|
||||||
} else {
|
|
||||||
SDL_mutexP(audio->mixer_lock);
|
|
||||||
(*audio->spec.callback)(audio->spec.userdata,
|
|
||||||
(Uint8 *)stream, len);
|
|
||||||
SDL_mutexV(audio->mixer_lock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
void MixSound(void)
|
|
||||||
{
|
|
||||||
int remain;
|
|
||||||
|
|
||||||
if(soundsystem->format == 8)
|
|
||||||
{
|
|
||||||
if((soundsystem->soundcursor + soundsystem->numsamples) > soundsystem->buffersize)
|
|
||||||
{
|
|
||||||
SoundMixCallback(&soundsystem->mixbuffer[soundsystem->soundcursor],soundsystem->buffersize - soundsystem->soundcursor);
|
|
||||||
remain = soundsystem->numsamples - (soundsystem->buffersize - soundsystem->soundcursor);
|
|
||||||
SoundMixCallback(soundsystem->mixbuffer,remain);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SoundMixCallback(&soundsystem->mixbuffer[soundsystem->soundcursor],soundsystem->numsamples);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if((soundsystem->soundcursor + soundsystem->numsamples) > (soundsystem->buffersize >> 1))
|
|
||||||
{
|
|
||||||
SoundMixCallback(&soundsystem->mixbuffer[soundsystem->soundcursor << 1],(soundsystem->buffersize >> 1) - soundsystem->soundcursor);
|
|
||||||
remain = soundsystem->numsamples - ((soundsystem->buffersize >> 1) - soundsystem->soundcursor);
|
|
||||||
SoundMixCallback(soundsystem->mixbuffer,remain);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SoundMixCallback(&soundsystem->mixbuffer[soundsystem->soundcursor << 1],soundsystem->numsamples);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void InterruptHandler(void)
|
|
||||||
{
|
|
||||||
framecounter++;
|
|
||||||
}
|
|
||||||
void FiFoHandler(void)
|
|
||||||
{
|
|
||||||
u32 command;
|
|
||||||
while ( !(REG_IPC_FIFO_CR & (IPC_FIFO_RECV_EMPTY)) )
|
|
||||||
{
|
|
||||||
command = REG_IPC_FIFO_RX;
|
|
||||||
|
|
||||||
switch(command)
|
|
||||||
{
|
|
||||||
case FIFO_NONE:
|
|
||||||
break;
|
|
||||||
case UPDATEON_ARM9:
|
|
||||||
REG_IME = 0;
|
|
||||||
MixSound();
|
|
||||||
REG_IME = 1;
|
|
||||||
SendCommandToArm7(MIXCOMPLETE_ONARM9);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = NDS_OpenAudio;
|
|
||||||
this->WaitAudio = NDS_WaitAudio;
|
|
||||||
this->PlayAudio = NDS_PlayAudio;
|
|
||||||
this->GetAudioBuf = NDS_GetAudioBuf;
|
|
||||||
this->CloseAudio = NDS_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
//fprintf(stderr,"Audio_CreateDevice\n");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap NDSAUD_bootstrap = {
|
|
||||||
"nds", "NDS audio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
void static NDS_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
//printf("NDS_WaitAudio\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NDS_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
//printf("playing audio\n");
|
|
||||||
if (this->paused)
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *NDS_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return NULL;//(this->hidden->mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NDS_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* if ( this->hidden->mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(this->hidden->mixbuf);
|
|
||||||
this->hidden->mixbuf = NULL;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static int NDS_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
//printf("NDS_OpenAudio\n");
|
|
||||||
int format = 0;
|
|
||||||
//switch(spec->format&0xff) {
|
|
||||||
//case 8: spec->format = AUDIO_S8;format=8; break;
|
|
||||||
//case 16: spec->format = AUDIO_S16LSB;format=16; break;
|
|
||||||
//default:
|
|
||||||
// SDL_SetError("Unsupported audio format");
|
|
||||||
// return(-1);
|
|
||||||
//}
|
|
||||||
switch (spec->format&~0x1000) {
|
|
||||||
case AUDIO_S8:
|
|
||||||
/* Signed 8-bit audio supported */
|
|
||||||
format=8;
|
|
||||||
break;
|
|
||||||
case AUDIO_U8:
|
|
||||||
spec->format ^= 0x80;format=8;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16:
|
|
||||||
/* Unsigned 16-bit audio unsupported, convert to S16 */
|
|
||||||
spec->format ^=0x8000;format=16;
|
|
||||||
case AUDIO_S16:
|
|
||||||
/* Signed 16-bit audio supported */
|
|
||||||
format=16;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* Update the fragment size as size in bytes */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
//this->hidden->mixlen = spec->size;
|
|
||||||
//this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
|
|
||||||
//if ( this->hidden->mixbuf == NULL ) {
|
|
||||||
// SDL_SetError("Out of Memory");
|
|
||||||
// return(-1);
|
|
||||||
//}
|
|
||||||
|
|
||||||
SDL_NDSAudio_mutex = 0;
|
|
||||||
sdl_nds_audiodevice=this;
|
|
||||||
|
|
||||||
irqInit();
|
|
||||||
irqSet(IRQ_VBLANK,&InterruptHandler);
|
|
||||||
irqSet(IRQ_FIFO_NOT_EMPTY,&FiFoHandler);
|
|
||||||
irqEnable(IRQ_FIFO_NOT_EMPTY);
|
|
||||||
|
|
||||||
REG_IPC_FIFO_CR = IPC_FIFO_ENABLE | IPC_FIFO_SEND_CLEAR | IPC_FIFO_RECV_IRQ;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SoundSystemInit(spec->freq,spec->size,0,format);
|
|
||||||
SoundStartMixer();
|
|
||||||
|
|
||||||
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_lowaudio_h
|
|
||||||
#define _SDL_lowaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the audio functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
//Uint8 *mixbuf;
|
|
||||||
//Uint32 mixlen;
|
|
||||||
};
|
|
||||||
unsigned short SDL_NDSAudio_mutex=0;
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _SDL_lowaudio_h */
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
#include "SDL_stdinc.h"
|
|
||||||
|
|
||||||
#include "soundcommon.h"
|
|
||||||
|
|
||||||
void SoundSystemInit(u32 rate,u32 buffersize,u8 channel,u8 format)
|
|
||||||
{
|
|
||||||
soundsystem->rate = rate;
|
|
||||||
|
|
||||||
if(format == 8)
|
|
||||||
soundsystem->buffersize = buffersize;
|
|
||||||
else if(format == 16)
|
|
||||||
soundsystem->buffersize = buffersize * sizeof(short);
|
|
||||||
|
|
||||||
soundsystem->mixbuffer = (s8*)SDL_malloc(soundsystem->buffersize);
|
|
||||||
//soundsystem->soundbuffer = soundsystem->mixbuffer;
|
|
||||||
soundsystem->format = format;
|
|
||||||
soundsystem->channel = channel;
|
|
||||||
soundsystem->prevtimer = 0;
|
|
||||||
soundsystem->soundcursor = 0;
|
|
||||||
soundsystem->numsamples = 0;
|
|
||||||
soundsystem->period = 0x1000000 / rate;
|
|
||||||
soundsystem->cmd = INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoundStartMixer(void)
|
|
||||||
{
|
|
||||||
soundsystem->cmd |= MIX;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SendCommandToArm7(u32 command)
|
|
||||||
{
|
|
||||||
while (REG_IPC_FIFO_CR & IPC_FIFO_SEND_FULL);
|
|
||||||
if (REG_IPC_FIFO_CR & IPC_FIFO_ERROR)
|
|
||||||
{
|
|
||||||
REG_IPC_FIFO_CR |= IPC_FIFO_SEND_CLEAR;
|
|
||||||
}
|
|
||||||
|
|
||||||
REG_IPC_FIFO_TX = command;
|
|
||||||
}
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef __SOUNDCOMMON_H
|
|
||||||
#define __SOUNDCOMMON_H
|
|
||||||
|
|
||||||
#include <nds.h>
|
|
||||||
|
|
||||||
#define CLOCK (1 << 25)
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
NONE = 0,
|
|
||||||
INIT = 1,
|
|
||||||
MIX = 2,
|
|
||||||
MIXING = 4,
|
|
||||||
STOP = 8
|
|
||||||
}CommandType;
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
FIFO_NONE = 0,
|
|
||||||
UPDATEON_ARM9 = 1,
|
|
||||||
MIXCOMPLETE_ONARM9 = 2,
|
|
||||||
}FifoType;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
s8 *mixbuffer;//,*soundbuffer;
|
|
||||||
u32 rate;
|
|
||||||
u32 buffersize;
|
|
||||||
u32 cmd;
|
|
||||||
u8 channel,format;
|
|
||||||
u32 soundcursor,numsamples;
|
|
||||||
s32 prevtimer;
|
|
||||||
s16 period;
|
|
||||||
}S_SoundSystem;
|
|
||||||
|
|
||||||
#define soundsystem ((S_SoundSystem*)((u32)(IPC)+sizeof(TransferRegion)))
|
|
||||||
|
|
||||||
#ifdef ARM9
|
|
||||||
extern void SoundSystemInit(u32 rate,u32 buffersize,u8 channel,u8 format);
|
|
||||||
extern void SoundStartMixer(void);
|
|
||||||
extern void SendCommandToArm7(u32 command);
|
|
||||||
#else
|
|
||||||
extern void SoundVBlankIrq(void);
|
|
||||||
extern void SoundSwapAndMix(void);
|
|
||||||
extern void SoundSetTimer(int period);
|
|
||||||
extern void SoundFifoHandler(void);
|
|
||||||
extern void SendCommandToArm9(u32 command);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,507 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sched.h>
|
|
||||||
#include <sys/select.h>
|
|
||||||
#include <sys/neutrino.h>
|
|
||||||
#include <sys/asoundlib.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "SDL_nto_audio.h"
|
|
||||||
|
|
||||||
/* The tag name used by NTO audio */
|
|
||||||
#define DRIVER_NAME "qsa-nto"
|
|
||||||
|
|
||||||
/* default channel communication parameters */
|
|
||||||
#define DEFAULT_CPARAMS_RATE 22050
|
|
||||||
#define DEFAULT_CPARAMS_VOICES 1
|
|
||||||
/* FIXME: need to add in the near future flexible logic with frag_size and frags count */
|
|
||||||
#define DEFAULT_CPARAMS_FRAG_SIZE 4096
|
|
||||||
#define DEFAULT_CPARAMS_FRAGS_MIN 1
|
|
||||||
#define DEFAULT_CPARAMS_FRAGS_MAX 1
|
|
||||||
|
|
||||||
/* Open the audio device for playback, and don't block if busy */
|
|
||||||
#define OPEN_FLAGS SND_PCM_OPEN_PLAYBACK
|
|
||||||
|
|
||||||
#define QSA_NO_WORKAROUNDS 0x00000000
|
|
||||||
#define QSA_MMAP_WORKAROUND 0x00000001
|
|
||||||
|
|
||||||
struct BuggyCards
|
|
||||||
{
|
|
||||||
char* cardname;
|
|
||||||
unsigned long bugtype;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define QSA_WA_CARDS 3
|
|
||||||
|
|
||||||
struct BuggyCards buggycards[QSA_WA_CARDS]=
|
|
||||||
{
|
|
||||||
{"Sound Blaster Live!", QSA_MMAP_WORKAROUND},
|
|
||||||
{"Vortex 8820", QSA_MMAP_WORKAROUND},
|
|
||||||
{"Vortex 8830", QSA_MMAP_WORKAROUND},
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static void NTO_ThreadInit(_THIS);
|
|
||||||
static int NTO_OpenAudio(_THIS, SDL_AudioSpec* spec);
|
|
||||||
static void NTO_WaitAudio(_THIS);
|
|
||||||
static void NTO_PlayAudio(_THIS);
|
|
||||||
static Uint8* NTO_GetAudioBuf(_THIS);
|
|
||||||
static void NTO_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* card names check to apply the workarounds */
|
|
||||||
static int NTO_CheckBuggyCards(_THIS, unsigned long checkfor)
|
|
||||||
{
|
|
||||||
char scardname[33];
|
|
||||||
int it;
|
|
||||||
|
|
||||||
if (snd_card_get_name(cardno, scardname, 32)<0)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (it=0; it<QSA_WA_CARDS; it++)
|
|
||||||
{
|
|
||||||
if (SDL_strcmp(buggycards[it].cardname, scardname)==0)
|
|
||||||
{
|
|
||||||
if (buggycards[it].bugtype==checkfor)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NTO_ThreadInit(_THIS)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
struct sched_param param;
|
|
||||||
|
|
||||||
/* increasing default 10 priority to 25 to avoid jerky sound */
|
|
||||||
status=SchedGet(0, 0, ¶m);
|
|
||||||
param.sched_priority=param.sched_curpriority+15;
|
|
||||||
status=SchedSet(0, 0, SCHED_NOCHANGE, ¶m);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PCM transfer channel parameters initialize function */
|
|
||||||
static void NTO_InitAudioParams(snd_pcm_channel_params_t* cpars)
|
|
||||||
{
|
|
||||||
SDL_memset(cpars, 0, sizeof(snd_pcm_channel_params_t));
|
|
||||||
|
|
||||||
cpars->channel = SND_PCM_CHANNEL_PLAYBACK;
|
|
||||||
cpars->mode = SND_PCM_MODE_BLOCK;
|
|
||||||
cpars->start_mode = SND_PCM_START_DATA;
|
|
||||||
cpars->stop_mode = SND_PCM_STOP_STOP;
|
|
||||||
cpars->format.format = SND_PCM_SFMT_S16_LE;
|
|
||||||
cpars->format.interleave = 1;
|
|
||||||
cpars->format.rate = DEFAULT_CPARAMS_RATE;
|
|
||||||
cpars->format.voices = DEFAULT_CPARAMS_VOICES;
|
|
||||||
cpars->buf.block.frag_size = DEFAULT_CPARAMS_FRAG_SIZE;
|
|
||||||
cpars->buf.block.frags_min = DEFAULT_CPARAMS_FRAGS_MIN;
|
|
||||||
cpars->buf.block.frags_max = DEFAULT_CPARAMS_FRAGS_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int NTO_AudioAvailable(void)
|
|
||||||
{
|
|
||||||
/* See if we can open a nonblocking channel.
|
|
||||||
Return value '1' means we can.
|
|
||||||
Return value '0' means we cannot. */
|
|
||||||
|
|
||||||
int available;
|
|
||||||
int rval;
|
|
||||||
snd_pcm_t* handle;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
handle = NULL;
|
|
||||||
|
|
||||||
rval = snd_pcm_open_preferred(&handle, NULL, NULL, OPEN_FLAGS);
|
|
||||||
|
|
||||||
if (rval >= 0)
|
|
||||||
{
|
|
||||||
available = 1;
|
|
||||||
|
|
||||||
if ((rval = snd_pcm_close(handle)) < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_AudioAvailable(): snd_pcm_close failed: %s\n", snd_strerror(rval));
|
|
||||||
available = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_AudioAvailable(): there are no available audio devices.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return (available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NTO_DeleteAudioDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
if ((device)&&(device->hidden))
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
}
|
|
||||||
if (device)
|
|
||||||
{
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice* NTO_CreateAudioDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if (this)
|
|
||||||
{
|
|
||||||
SDL_memset(this, 0, sizeof(SDL_AudioDevice));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc(sizeof(struct SDL_PrivateAudioData));
|
|
||||||
}
|
|
||||||
if ((this == NULL) || (this->hidden == NULL))
|
|
||||||
{
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if (this)
|
|
||||||
{
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, sizeof(struct SDL_PrivateAudioData));
|
|
||||||
audio_handle = NULL;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->ThreadInit = NTO_ThreadInit;
|
|
||||||
this->OpenAudio = NTO_OpenAudio;
|
|
||||||
this->WaitAudio = NTO_WaitAudio;
|
|
||||||
this->PlayAudio = NTO_PlayAudio;
|
|
||||||
this->GetAudioBuf = NTO_GetAudioBuf;
|
|
||||||
this->CloseAudio = NTO_CloseAudio;
|
|
||||||
|
|
||||||
this->free = NTO_DeleteAudioDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap QNXNTOAUDIO_bootstrap =
|
|
||||||
{
|
|
||||||
DRIVER_NAME, "QNX6 QSA-NTO Audio",
|
|
||||||
NTO_AudioAvailable,
|
|
||||||
NTO_CreateAudioDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void NTO_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
fd_set wfds;
|
|
||||||
int selectret;
|
|
||||||
|
|
||||||
FD_ZERO(&wfds);
|
|
||||||
FD_SET(audio_fd, &wfds);
|
|
||||||
|
|
||||||
do {
|
|
||||||
selectret=select(audio_fd + 1, NULL, &wfds, NULL, NULL);
|
|
||||||
switch (selectret)
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
case 0: SDL_SetError("NTO_WaitAudio(): select() failed: %s\n", strerror(errno));
|
|
||||||
return;
|
|
||||||
default: if (FD_ISSET(audio_fd, &wfds))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NTO_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
int written, rval;
|
|
||||||
int towrite;
|
|
||||||
void* pcmbuffer;
|
|
||||||
|
|
||||||
if (!this->enabled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
towrite = this->spec.size;
|
|
||||||
pcmbuffer = pcm_buf;
|
|
||||||
|
|
||||||
/* Write the audio data, checking for EAGAIN (buffer full) and underrun */
|
|
||||||
do {
|
|
||||||
written = snd_pcm_plugin_write(audio_handle, pcm_buf, towrite);
|
|
||||||
if (written != towrite)
|
|
||||||
{
|
|
||||||
if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
|
|
||||||
{
|
|
||||||
/* Let a little CPU time go by and try to write again */
|
|
||||||
SDL_Delay(1);
|
|
||||||
/* if we wrote some data */
|
|
||||||
towrite -= written;
|
|
||||||
pcmbuffer += written * this->spec.channels;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ((errno == EINVAL) || (errno == EIO))
|
|
||||||
{
|
|
||||||
SDL_memset(&cstatus, 0, sizeof(cstatus));
|
|
||||||
cstatus.channel = SND_PCM_CHANNEL_PLAYBACK;
|
|
||||||
if ((rval = snd_pcm_plugin_status(audio_handle, &cstatus)) < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_status failed: %s\n", snd_strerror(rval));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((cstatus.status == SND_PCM_STATUS_UNDERRUN) || (cstatus.status == SND_PCM_STATUS_READY))
|
|
||||||
{
|
|
||||||
if ((rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_PlayAudio(): snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* we wrote all remaining data */
|
|
||||||
towrite -= written;
|
|
||||||
pcmbuffer += written * this->spec.channels;
|
|
||||||
}
|
|
||||||
} while ((towrite > 0) && (this->enabled));
|
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
|
||||||
if (towrite != 0)
|
|
||||||
{
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8* NTO_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return pcm_buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void NTO_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
int rval;
|
|
||||||
|
|
||||||
this->enabled = 0;
|
|
||||||
|
|
||||||
if (audio_handle != NULL)
|
|
||||||
{
|
|
||||||
if ((rval = snd_pcm_plugin_flush(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_CloseAudio(): snd_pcm_plugin_flush failed: %s\n", snd_strerror(rval));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((rval = snd_pcm_close(audio_handle)) < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_CloseAudio(): snd_pcm_close failed: %s\n",snd_strerror(rval));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
audio_handle = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int NTO_OpenAudio(_THIS, SDL_AudioSpec* spec)
|
|
||||||
{
|
|
||||||
int rval;
|
|
||||||
int format;
|
|
||||||
Uint16 test_format;
|
|
||||||
int found;
|
|
||||||
|
|
||||||
audio_handle = NULL;
|
|
||||||
this->enabled = 0;
|
|
||||||
|
|
||||||
if (pcm_buf != NULL)
|
|
||||||
{
|
|
||||||
SDL_FreeAudioMem(pcm_buf);
|
|
||||||
pcm_buf = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* initialize channel transfer parameters to default */
|
|
||||||
NTO_InitAudioParams(&cparams);
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
rval = snd_pcm_open_preferred(&audio_handle, &cardno, &deviceno, OPEN_FLAGS);
|
|
||||||
if (rval < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_OpenAudio(): snd_pcm_open failed: %s\n", snd_strerror(rval));
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!NTO_CheckBuggyCards(this, QSA_MMAP_WORKAROUND))
|
|
||||||
{
|
|
||||||
/* enable count status parameter */
|
|
||||||
if ((rval = snd_pcm_plugin_set_disable(audio_handle, PLUGIN_DISABLE_MMAP)) < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("snd_pcm_plugin_set_disable failed: %s\n", snd_strerror(rval));
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try for a closest match on audio format */
|
|
||||||
format = 0;
|
|
||||||
/* can't use format as SND_PCM_SFMT_U8 = 0 in nto */
|
|
||||||
found = 0;
|
|
||||||
|
|
||||||
for (test_format=SDL_FirstAudioFormat(spec->format); !found ;)
|
|
||||||
{
|
|
||||||
/* if match found set format to equivalent ALSA format */
|
|
||||||
switch (test_format)
|
|
||||||
{
|
|
||||||
case AUDIO_U8:
|
|
||||||
format = SND_PCM_SFMT_U8;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_S8:
|
|
||||||
format = SND_PCM_SFMT_S8;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
format = SND_PCM_SFMT_S16_LE;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
format = SND_PCM_SFMT_S16_BE;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
format = SND_PCM_SFMT_U16_LE;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
format = SND_PCM_SFMT_U16_BE;
|
|
||||||
found = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found)
|
|
||||||
{
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* assumes test_format not 0 on success */
|
|
||||||
if (test_format == 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_OpenAudio(): Couldn't find any hardware audio formats");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
/* Set the audio format */
|
|
||||||
cparams.format.format = format;
|
|
||||||
|
|
||||||
/* Set mono or stereo audio (currently only two channels supported) */
|
|
||||||
cparams.format.voices = spec->channels;
|
|
||||||
|
|
||||||
/* Set rate */
|
|
||||||
cparams.format.rate = spec->freq;
|
|
||||||
|
|
||||||
/* Setup the transfer parameters according to cparams */
|
|
||||||
rval = snd_pcm_plugin_params(audio_handle, &cparams);
|
|
||||||
if (rval < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_OpenAudio(): snd_pcm_channel_params failed: %s\n", snd_strerror(rval));
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure channel is setup right one last time */
|
|
||||||
SDL_memset(&csetup, 0x00, sizeof(csetup));
|
|
||||||
csetup.channel = SND_PCM_CHANNEL_PLAYBACK;
|
|
||||||
if (snd_pcm_plugin_setup(audio_handle, &csetup) < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_OpenAudio(): Unable to setup playback channel\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
pcm_len = spec->size;
|
|
||||||
|
|
||||||
if (pcm_len==0)
|
|
||||||
{
|
|
||||||
pcm_len = csetup.buf.block.frag_size * spec->channels * (snd_pcm_format_width(format)/8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate memory to the audio buffer and initialize with silence (Note that
|
|
||||||
buffer size must be a multiple of fragment size, so find closest multiple)
|
|
||||||
*/
|
|
||||||
pcm_buf = (Uint8*)SDL_AllocAudioMem(pcm_len);
|
|
||||||
if (pcm_buf == NULL)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_OpenAudio(): pcm buffer allocation failed\n");
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
SDL_memset(pcm_buf, spec->silence, pcm_len);
|
|
||||||
|
|
||||||
/* get the file descriptor */
|
|
||||||
if ((audio_fd = snd_pcm_file_descriptor(audio_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("NTO_OpenAudio(): snd_pcm_file_descriptor failed with error code: %s\n", snd_strerror(rval));
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Trigger audio playback */
|
|
||||||
rval = snd_pcm_plugin_prepare(audio_handle, SND_PCM_CHANNEL_PLAYBACK);
|
|
||||||
if (rval < 0)
|
|
||||||
{
|
|
||||||
SDL_SetError("snd_pcm_plugin_prepare failed: %s\n", snd_strerror(rval));
|
|
||||||
return (-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
this->enabled = 1;
|
|
||||||
|
|
||||||
/* Get the parent process id (we're the parent of the audio thread) */
|
|
||||||
parent = getpid();
|
|
||||||
|
|
||||||
/* We're really ready to rock and roll. :-) */
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef __SDL_NTO_AUDIO_H__
|
|
||||||
#define __SDL_NTO_AUDIO_H__
|
|
||||||
|
|
||||||
#include <sys/asoundlib.h>
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the audio functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData
|
|
||||||
{
|
|
||||||
/* The audio device handle */
|
|
||||||
int cardno;
|
|
||||||
int deviceno;
|
|
||||||
snd_pcm_t* audio_handle;
|
|
||||||
|
|
||||||
/* The audio file descriptor */
|
|
||||||
int audio_fd;
|
|
||||||
|
|
||||||
/* The parent process id, to detect when application quits */
|
|
||||||
pid_t parent;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8* pcm_buf;
|
|
||||||
Uint32 pcm_len;
|
|
||||||
|
|
||||||
/* QSA parameters */
|
|
||||||
snd_pcm_channel_status_t cstatus;
|
|
||||||
snd_pcm_channel_params_t cparams;
|
|
||||||
snd_pcm_channel_setup_t csetup;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define cardno (this->hidden->cardno)
|
|
||||||
#define deviceno (this->hidden->deviceno)
|
|
||||||
#define audio_handle (this->hidden->audio_handle)
|
|
||||||
#define audio_fd (this->hidden->audio_fd)
|
|
||||||
#define parent (this->hidden->parent)
|
|
||||||
#define pcm_buf (this->hidden->pcm_buf)
|
|
||||||
#define pcm_len (this->hidden->pcm_len)
|
|
||||||
#define cstatus (this->hidden->cstatus)
|
|
||||||
#define cparams (this->hidden->cparams)
|
|
||||||
#define csetup (this->hidden->csetup)
|
|
||||||
|
|
||||||
#endif /* __SDL_NTO_AUDIO_H__ */
|
|
||||||
|
|
@ -1,511 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Carsten Griwodz
|
|
||||||
griff@kom.tu-darmstadt.de
|
|
||||||
|
|
||||||
based on linux/SDL_dspaudio.c by Sam Lantinga
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_paudio.h"
|
|
||||||
|
|
||||||
#define DEBUG_AUDIO 1
|
|
||||||
|
|
||||||
/* A conflict within AIX 4.3.3 <sys/> headers and probably others as well.
|
|
||||||
* I guess nobody ever uses audio... Shame over AIX header files. */
|
|
||||||
#include <sys/machine.h>
|
|
||||||
#undef BIG_ENDIAN
|
|
||||||
#include <sys/audio.h>
|
|
||||||
|
|
||||||
/* The tag name used by paud audio */
|
|
||||||
#define Paud_DRIVER_NAME "paud"
|
|
||||||
|
|
||||||
/* Open the audio device for playback, and don't block if busy */
|
|
||||||
/* #define OPEN_FLAGS (O_WRONLY|O_NONBLOCK) */
|
|
||||||
#define OPEN_FLAGS O_WRONLY
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int Paud_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void Paud_WaitAudio(_THIS);
|
|
||||||
static void Paud_PlayAudio(_THIS);
|
|
||||||
static Uint8 *Paud_GetAudioBuf(_THIS);
|
|
||||||
static void Paud_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
int available;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
|
|
||||||
if ( fd >= 0 ) {
|
|
||||||
available = 1;
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
audio_fd = -1;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = Paud_OpenAudio;
|
|
||||||
this->WaitAudio = Paud_WaitAudio;
|
|
||||||
this->PlayAudio = Paud_PlayAudio;
|
|
||||||
this->GetAudioBuf = Paud_GetAudioBuf;
|
|
||||||
this->CloseAudio = Paud_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap Paud_bootstrap = {
|
|
||||||
Paud_DRIVER_NAME, "AIX Paudio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void Paud_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
fd_set fdset;
|
|
||||||
|
|
||||||
/* See if we need to use timed audio synchronization */
|
|
||||||
if ( frame_ticks ) {
|
|
||||||
/* Use timer for general audio synchronization */
|
|
||||||
Sint32 ticks;
|
|
||||||
|
|
||||||
ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
|
|
||||||
if ( ticks > 0 ) {
|
|
||||||
SDL_Delay(ticks);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
audio_buffer paud_bufinfo;
|
|
||||||
|
|
||||||
/* Use select() for audio synchronization */
|
|
||||||
struct timeval timeout;
|
|
||||||
FD_ZERO(&fdset);
|
|
||||||
FD_SET(audio_fd, &fdset);
|
|
||||||
|
|
||||||
if ( ioctl(audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0 ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Couldn't get audio buffer information\n");
|
|
||||||
#endif
|
|
||||||
timeout.tv_sec = 10;
|
|
||||||
timeout.tv_usec = 0;
|
|
||||||
} else {
|
|
||||||
long ms_in_buf = paud_bufinfo.write_buf_time;
|
|
||||||
timeout.tv_sec = ms_in_buf/1000;
|
|
||||||
ms_in_buf = ms_in_buf - timeout.tv_sec*1000;
|
|
||||||
timeout.tv_usec = ms_in_buf*1000;
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf( stderr,
|
|
||||||
"Waiting for write_buf_time=%ld,%ld\n",
|
|
||||||
timeout.tv_sec,
|
|
||||||
timeout.tv_usec );
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Waiting for audio to get ready\n");
|
|
||||||
#endif
|
|
||||||
if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
|
|
||||||
const char *message = "Audio timeout - buggy audio driver? (disabled)";
|
|
||||||
/*
|
|
||||||
* In general we should never print to the screen,
|
|
||||||
* but in this case we have no other way of letting
|
|
||||||
* the user know what happened.
|
|
||||||
*/
|
|
||||||
fprintf(stderr, "SDL: %s - %s\n", strerror(errno), message);
|
|
||||||
this->enabled = 0;
|
|
||||||
/* Don't try to close - may hang */
|
|
||||||
audio_fd = -1;
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Done disabling audio\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Ready!\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Paud_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
int written;
|
|
||||||
|
|
||||||
/* Write the audio data, checking for EAGAIN on broken audio drivers */
|
|
||||||
do {
|
|
||||||
written = write(audio_fd, mixbuf, mixlen);
|
|
||||||
if ( (written < 0) && ((errno == 0) || (errno == EAGAIN)) ) {
|
|
||||||
SDL_Delay(1); /* Let a little CPU time go by */
|
|
||||||
}
|
|
||||||
} while ( (written < 0) &&
|
|
||||||
((errno == 0) || (errno == EAGAIN) || (errno == EINTR)) );
|
|
||||||
|
|
||||||
/* If timer synchronization is enabled, set the next write frame */
|
|
||||||
if ( frame_ticks ) {
|
|
||||||
next_frame += frame_ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we couldn't write, assume fatal error for now */
|
|
||||||
if ( written < 0 ) {
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Wrote %d bytes of audio data\n", written);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *Paud_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return mixbuf;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Paud_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( audio_fd >= 0 ) {
|
|
||||||
close(audio_fd);
|
|
||||||
audio_fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Paud_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
char audiodev[1024];
|
|
||||||
int format;
|
|
||||||
int bytes_per_sample;
|
|
||||||
Uint16 test_format;
|
|
||||||
audio_init paud_init;
|
|
||||||
audio_buffer paud_bufinfo;
|
|
||||||
audio_status paud_status;
|
|
||||||
audio_control paud_control;
|
|
||||||
audio_change paud_change;
|
|
||||||
|
|
||||||
/* Reset the timer synchronization flag */
|
|
||||||
frame_ticks = 0.0;
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
|
|
||||||
if ( audio_fd < 0 ) {
|
|
||||||
SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We can't set the buffer size - just ask the device for the maximum
|
|
||||||
* that we can have.
|
|
||||||
*/
|
|
||||||
if ( ioctl(audio_fd, AUDIO_BUFFER, &paud_bufinfo) < 0 ) {
|
|
||||||
SDL_SetError("Couldn't get audio buffer information");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
mixbuf = NULL;
|
|
||||||
|
|
||||||
if ( spec->channels > 1 )
|
|
||||||
spec->channels = 2;
|
|
||||||
else
|
|
||||||
spec->channels = 1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fields in the audio_init structure:
|
|
||||||
*
|
|
||||||
* Ignored by us:
|
|
||||||
*
|
|
||||||
* paud.loadpath[LOAD_PATH]; * DSP code to load, MWave chip only?
|
|
||||||
* paud.slot_number; * slot number of the adapter
|
|
||||||
* paud.device_id; * adapter identification number
|
|
||||||
*
|
|
||||||
* Input:
|
|
||||||
*
|
|
||||||
* paud.srate; * the sampling rate in Hz
|
|
||||||
* paud.bits_per_sample; * 8, 16, 32, ...
|
|
||||||
* paud.bsize; * block size for this rate
|
|
||||||
* paud.mode; * ADPCM, PCM, MU_LAW, A_LAW, SOURCE_MIX
|
|
||||||
* paud.channels; * 1=mono, 2=stereo
|
|
||||||
* paud.flags; * FIXED - fixed length data
|
|
||||||
* * LEFT_ALIGNED, RIGHT_ALIGNED (var len only)
|
|
||||||
* * TWOS_COMPLEMENT - 2's complement data
|
|
||||||
* * SIGNED - signed? comment seems wrong in sys/audio.h
|
|
||||||
* * BIG_ENDIAN
|
|
||||||
* paud.operation; * PLAY, RECORD
|
|
||||||
*
|
|
||||||
* Output:
|
|
||||||
*
|
|
||||||
* paud.flags; * PITCH - pitch is supported
|
|
||||||
* * INPUT - input is supported
|
|
||||||
* * OUTPUT - output is supported
|
|
||||||
* * MONITOR - monitor is supported
|
|
||||||
* * VOLUME - volume is supported
|
|
||||||
* * VOLUME_DELAY - volume delay is supported
|
|
||||||
* * BALANCE - balance is supported
|
|
||||||
* * BALANCE_DELAY - balance delay is supported
|
|
||||||
* * TREBLE - treble control is supported
|
|
||||||
* * BASS - bass control is supported
|
|
||||||
* * BESTFIT_PROVIDED - best fit returned
|
|
||||||
* * LOAD_CODE - DSP load needed
|
|
||||||
* paud.rc; * NO_PLAY - DSP code can't do play requests
|
|
||||||
* * NO_RECORD - DSP code can't do record requests
|
|
||||||
* * INVALID_REQUEST - request was invalid
|
|
||||||
* * CONFLICT - conflict with open's flags
|
|
||||||
* * OVERLOADED - out of DSP MIPS or memory
|
|
||||||
* paud.position_resolution; * smallest increment for position
|
|
||||||
*/
|
|
||||||
|
|
||||||
paud_init.srate = spec->freq;
|
|
||||||
paud_init.mode = PCM;
|
|
||||||
paud_init.operation = PLAY;
|
|
||||||
paud_init.channels = spec->channels;
|
|
||||||
|
|
||||||
/* Try for a closest match on audio format */
|
|
||||||
format = 0;
|
|
||||||
for ( test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
! format && test_format; ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
|
||||||
#endif
|
|
||||||
switch ( test_format ) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
bytes_per_sample = 1;
|
|
||||||
paud_init.bits_per_sample = 8;
|
|
||||||
paud_init.flags = TWOS_COMPLEMENT | FIXED;
|
|
||||||
format = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_S8:
|
|
||||||
bytes_per_sample = 1;
|
|
||||||
paud_init.bits_per_sample = 8;
|
|
||||||
paud_init.flags = SIGNED |
|
|
||||||
TWOS_COMPLEMENT | FIXED;
|
|
||||||
format = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
bytes_per_sample = 2;
|
|
||||||
paud_init.bits_per_sample = 16;
|
|
||||||
paud_init.flags = SIGNED |
|
|
||||||
TWOS_COMPLEMENT | FIXED;
|
|
||||||
format = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
bytes_per_sample = 2;
|
|
||||||
paud_init.bits_per_sample = 16;
|
|
||||||
paud_init.flags = BIG_ENDIAN |
|
|
||||||
SIGNED |
|
|
||||||
TWOS_COMPLEMENT | FIXED;
|
|
||||||
format = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
bytes_per_sample = 2;
|
|
||||||
paud_init.bits_per_sample = 16;
|
|
||||||
paud_init.flags = TWOS_COMPLEMENT | FIXED;
|
|
||||||
format = 1;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
bytes_per_sample = 2;
|
|
||||||
paud_init.bits_per_sample = 16;
|
|
||||||
paud_init.flags = BIG_ENDIAN |
|
|
||||||
TWOS_COMPLEMENT | FIXED;
|
|
||||||
format = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( ! format ) {
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( format == 0 ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Couldn't find any hardware audio formats\n");
|
|
||||||
#endif
|
|
||||||
SDL_SetError("Couldn't find any hardware audio formats");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We know the buffer size and the max number of subsequent writes
|
|
||||||
* that can be pending. If more than one can pend, allow the application
|
|
||||||
* to do something like double buffering between our write buffer and
|
|
||||||
* the device's own buffer that we are filling with write() anyway.
|
|
||||||
*
|
|
||||||
* We calculate spec->samples like this because SDL_CalculateAudioSpec()
|
|
||||||
* will give put paud_bufinfo.write_buf_cap (or paud_bufinfo.write_buf_cap/2)
|
|
||||||
* into spec->size in return.
|
|
||||||
*/
|
|
||||||
if ( paud_bufinfo.request_buf_cap == 1 )
|
|
||||||
{
|
|
||||||
spec->samples = paud_bufinfo.write_buf_cap
|
|
||||||
/ bytes_per_sample
|
|
||||||
/ spec->channels;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
spec->samples = paud_bufinfo.write_buf_cap
|
|
||||||
/ bytes_per_sample
|
|
||||||
/ spec->channels
|
|
||||||
/ 2;
|
|
||||||
}
|
|
||||||
paud_init.bsize = bytes_per_sample * spec->channels;
|
|
||||||
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The AIX paud device init can't modify the values of the audio_init
|
|
||||||
* structure that we pass to it. So we don't need any recalculation
|
|
||||||
* of this stuff and no reinit call as in linux dsp and dma code.
|
|
||||||
*
|
|
||||||
* /dev/paud supports all of the encoding formats, so we don't need
|
|
||||||
* to do anything like reopening the device, either.
|
|
||||||
*/
|
|
||||||
if ( ioctl(audio_fd, AUDIO_INIT, &paud_init) < 0 ) {
|
|
||||||
switch ( paud_init.rc )
|
|
||||||
{
|
|
||||||
case 1 :
|
|
||||||
SDL_SetError("Couldn't set audio format: DSP can't do play requests");
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
case 2 :
|
|
||||||
SDL_SetError("Couldn't set audio format: DSP can't do record requests");
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
case 4 :
|
|
||||||
SDL_SetError("Couldn't set audio format: request was invalid");
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
case 5 :
|
|
||||||
SDL_SetError("Couldn't set audio format: conflict with open's flags");
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
case 6 :
|
|
||||||
SDL_SetError("Couldn't set audio format: out of DSP MIPS or memory");
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
SDL_SetError("Couldn't set audio format: not documented in sys/audio.h");
|
|
||||||
return -1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixlen = spec->size;
|
|
||||||
mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set some paramters: full volume, first speaker that we can find.
|
|
||||||
* Ignore the other settings for now.
|
|
||||||
*/
|
|
||||||
paud_change.input = AUDIO_IGNORE; /* the new input source */
|
|
||||||
paud_change.output = OUTPUT_1; /* EXTERNAL_SPEAKER,INTERNAL_SPEAKER,OUTPUT_1 */
|
|
||||||
paud_change.monitor = AUDIO_IGNORE; /* the new monitor state */
|
|
||||||
paud_change.volume = 0x7fffffff; /* volume level [0-0x7fffffff] */
|
|
||||||
paud_change.volume_delay = AUDIO_IGNORE; /* the new volume delay */
|
|
||||||
paud_change.balance = 0x3fffffff; /* the new balance */
|
|
||||||
paud_change.balance_delay = AUDIO_IGNORE; /* the new balance delay */
|
|
||||||
paud_change.treble = AUDIO_IGNORE; /* the new treble state */
|
|
||||||
paud_change.bass = AUDIO_IGNORE; /* the new bass state */
|
|
||||||
paud_change.pitch = AUDIO_IGNORE; /* the new pitch state */
|
|
||||||
|
|
||||||
paud_control.ioctl_request = AUDIO_CHANGE;
|
|
||||||
paud_control.request_info = (char*)&paud_change;
|
|
||||||
if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Can't change audio display settings\n" );
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Tell the device to expect data. Actual start will wait for
|
|
||||||
* the first write() call.
|
|
||||||
*/
|
|
||||||
paud_control.ioctl_request = AUDIO_START;
|
|
||||||
paud_control.position = 0;
|
|
||||||
if ( ioctl(audio_fd, AUDIO_CONTROL, &paud_control) < 0 ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Can't start audio play\n" );
|
|
||||||
#endif
|
|
||||||
SDL_SetError("Can't start audio play");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check to see if we need to use select() workaround */
|
|
||||||
{ char *workaround;
|
|
||||||
workaround = SDL_getenv("SDL_DSP_NOSELECT");
|
|
||||||
if ( workaround ) {
|
|
||||||
frame_ticks = (float)(spec->samples*1000)/spec->freq;
|
|
||||||
next_frame = SDL_GetTicks()+frame_ticks;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the parent process id (we're the parent of the audio thread) */
|
|
||||||
parent = getpid();
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_paudaudio_h
|
|
||||||
#define _SDL_paudaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
int audio_fd;
|
|
||||||
|
|
||||||
/* The parent process id, to detect when application quits */
|
|
||||||
pid_t parent;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
int mixlen;
|
|
||||||
|
|
||||||
/* Support for audio timing using a timer, in addition to select() */
|
|
||||||
float frame_ticks;
|
|
||||||
float next_frame;
|
|
||||||
};
|
|
||||||
#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define audio_fd (this->hidden->audio_fd)
|
|
||||||
#define parent (this->hidden->parent)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define mixlen (this->hidden->mixlen)
|
|
||||||
#define frame_ticks (this->hidden->frame_ticks)
|
|
||||||
#define next_frame (this->hidden->next_frame)
|
|
||||||
|
|
||||||
#endif /* _SDL_paudaudio_h */
|
|
||||||
|
|
@ -1,570 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Stéphan Kochen
|
|
||||||
stephan@kochen.nl
|
|
||||||
|
|
||||||
Based on parts of the ALSA and ESounD output drivers.
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to an PulseAudio network stream mixing buffer */
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <pulse/pulseaudio.h>
|
|
||||||
#include <pulse/simple.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "../../../include/SDL_video.h" /* for SDL_WM_GetCaption(). */
|
|
||||||
#include "SDL_pulseaudio.h"
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC
|
|
||||||
#include "SDL_name.h"
|
|
||||||
#include "SDL_loadso.h"
|
|
||||||
#else
|
|
||||||
#define SDL_NAME(X) X
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The tag name used by the driver */
|
|
||||||
#define PULSE_DRIVER_NAME "pulse"
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void PULSE_WaitAudio(_THIS);
|
|
||||||
static void PULSE_PlayAudio(_THIS);
|
|
||||||
static Uint8 *PULSE_GetAudioBuf(_THIS);
|
|
||||||
static void PULSE_CloseAudio(_THIS);
|
|
||||||
static void PULSE_WaitDone(_THIS);
|
|
||||||
static void PULSE_SetCaption(_THIS, const char *str);
|
|
||||||
|
|
||||||
#ifdef SDL_AUDIO_DRIVER_PULSE_DYNAMIC
|
|
||||||
|
|
||||||
static const char *pulse_library = SDL_AUDIO_DRIVER_PULSE_DYNAMIC;
|
|
||||||
static void *pulse_handle = NULL;
|
|
||||||
static int pulse_loaded = 0;
|
|
||||||
|
|
||||||
static pa_simple* (*SDL_NAME(pa_simple_new))(
|
|
||||||
const char *server,
|
|
||||||
const char *name,
|
|
||||||
pa_stream_direction_t dir,
|
|
||||||
const char *dev,
|
|
||||||
const char *stream_name,
|
|
||||||
const pa_sample_spec *ss,
|
|
||||||
const pa_channel_map *map,
|
|
||||||
const pa_buffer_attr *attr,
|
|
||||||
int *error
|
|
||||||
);
|
|
||||||
static void (*SDL_NAME(pa_simple_free))(pa_simple *s);
|
|
||||||
|
|
||||||
static pa_channel_map* (*SDL_NAME(pa_channel_map_init_auto))(
|
|
||||||
pa_channel_map *m,
|
|
||||||
unsigned channels,
|
|
||||||
pa_channel_map_def_t def
|
|
||||||
);
|
|
||||||
|
|
||||||
static pa_mainloop * (*SDL_NAME(pa_mainloop_new))(void);
|
|
||||||
static pa_mainloop_api * (*SDL_NAME(pa_mainloop_get_api))(pa_mainloop *m);
|
|
||||||
static int (*SDL_NAME(pa_mainloop_iterate))(pa_mainloop *m, int block, int *retval);
|
|
||||||
static void (*SDL_NAME(pa_mainloop_free))(pa_mainloop *m);
|
|
||||||
|
|
||||||
static pa_operation_state_t (*SDL_NAME(pa_operation_get_state))(pa_operation *o);
|
|
||||||
static void (*SDL_NAME(pa_operation_cancel))(pa_operation *o);
|
|
||||||
static void (*SDL_NAME(pa_operation_unref))(pa_operation *o);
|
|
||||||
|
|
||||||
static pa_context * (*SDL_NAME(pa_context_new))(
|
|
||||||
pa_mainloop_api *m, const char *name);
|
|
||||||
static int (*SDL_NAME(pa_context_connect))(
|
|
||||||
pa_context *c, const char *server,
|
|
||||||
pa_context_flags_t flags, const pa_spawn_api *api);
|
|
||||||
static pa_context_state_t (*SDL_NAME(pa_context_get_state))(pa_context *c);
|
|
||||||
static void (*SDL_NAME(pa_context_disconnect))(pa_context *c);
|
|
||||||
static void (*SDL_NAME(pa_context_unref))(pa_context *c);
|
|
||||||
|
|
||||||
static pa_stream * (*SDL_NAME(pa_stream_new))(pa_context *c,
|
|
||||||
const char *name, const pa_sample_spec *ss, const pa_channel_map *map);
|
|
||||||
static int (*SDL_NAME(pa_stream_connect_playback))(pa_stream *s, const char *dev,
|
|
||||||
const pa_buffer_attr *attr, pa_stream_flags_t flags,
|
|
||||||
pa_cvolume *volume, pa_stream *sync_stream);
|
|
||||||
static pa_stream_state_t (*SDL_NAME(pa_stream_get_state))(pa_stream *s);
|
|
||||||
static size_t (*SDL_NAME(pa_stream_writable_size))(pa_stream *s);
|
|
||||||
static int (*SDL_NAME(pa_stream_write))(pa_stream *s, const void *data, size_t nbytes,
|
|
||||||
pa_free_cb_t free_cb, int64_t offset, pa_seek_mode_t seek);
|
|
||||||
static pa_operation * (*SDL_NAME(pa_stream_drain))(pa_stream *s,
|
|
||||||
pa_stream_success_cb_t cb, void *userdata);
|
|
||||||
static int (*SDL_NAME(pa_stream_disconnect))(pa_stream *s);
|
|
||||||
static void (*SDL_NAME(pa_stream_unref))(pa_stream *s);
|
|
||||||
static pa_operation* (*SDL_NAME(pa_context_set_name))(pa_context *c,
|
|
||||||
const char *name, pa_context_success_cb_t cb, void *userdata);
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
const char *name;
|
|
||||||
void **func;
|
|
||||||
} pulse_functions[] = {
|
|
||||||
{ "pa_simple_new",
|
|
||||||
(void **)&SDL_NAME(pa_simple_new) },
|
|
||||||
{ "pa_simple_free",
|
|
||||||
(void **)&SDL_NAME(pa_simple_free) },
|
|
||||||
{ "pa_channel_map_init_auto",
|
|
||||||
(void **)&SDL_NAME(pa_channel_map_init_auto) },
|
|
||||||
{ "pa_mainloop_new",
|
|
||||||
(void **)&SDL_NAME(pa_mainloop_new) },
|
|
||||||
{ "pa_mainloop_get_api",
|
|
||||||
(void **)&SDL_NAME(pa_mainloop_get_api) },
|
|
||||||
{ "pa_mainloop_iterate",
|
|
||||||
(void **)&SDL_NAME(pa_mainloop_iterate) },
|
|
||||||
{ "pa_mainloop_free",
|
|
||||||
(void **)&SDL_NAME(pa_mainloop_free) },
|
|
||||||
{ "pa_operation_get_state",
|
|
||||||
(void **)&SDL_NAME(pa_operation_get_state) },
|
|
||||||
{ "pa_operation_cancel",
|
|
||||||
(void **)&SDL_NAME(pa_operation_cancel) },
|
|
||||||
{ "pa_operation_unref",
|
|
||||||
(void **)&SDL_NAME(pa_operation_unref) },
|
|
||||||
{ "pa_context_new",
|
|
||||||
(void **)&SDL_NAME(pa_context_new) },
|
|
||||||
{ "pa_context_connect",
|
|
||||||
(void **)&SDL_NAME(pa_context_connect) },
|
|
||||||
{ "pa_context_get_state",
|
|
||||||
(void **)&SDL_NAME(pa_context_get_state) },
|
|
||||||
{ "pa_context_disconnect",
|
|
||||||
(void **)&SDL_NAME(pa_context_disconnect) },
|
|
||||||
{ "pa_context_unref",
|
|
||||||
(void **)&SDL_NAME(pa_context_unref) },
|
|
||||||
{ "pa_stream_new",
|
|
||||||
(void **)&SDL_NAME(pa_stream_new) },
|
|
||||||
{ "pa_stream_connect_playback",
|
|
||||||
(void **)&SDL_NAME(pa_stream_connect_playback) },
|
|
||||||
{ "pa_stream_get_state",
|
|
||||||
(void **)&SDL_NAME(pa_stream_get_state) },
|
|
||||||
{ "pa_stream_writable_size",
|
|
||||||
(void **)&SDL_NAME(pa_stream_writable_size) },
|
|
||||||
{ "pa_stream_write",
|
|
||||||
(void **)&SDL_NAME(pa_stream_write) },
|
|
||||||
{ "pa_stream_drain",
|
|
||||||
(void **)&SDL_NAME(pa_stream_drain) },
|
|
||||||
{ "pa_stream_disconnect",
|
|
||||||
(void **)&SDL_NAME(pa_stream_disconnect) },
|
|
||||||
{ "pa_stream_unref",
|
|
||||||
(void **)&SDL_NAME(pa_stream_unref) },
|
|
||||||
{ "pa_context_set_name",
|
|
||||||
(void **)&SDL_NAME(pa_context_set_name) },
|
|
||||||
};
|
|
||||||
|
|
||||||
static void UnloadPulseLibrary()
|
|
||||||
{
|
|
||||||
if ( pulse_loaded ) {
|
|
||||||
SDL_UnloadObject(pulse_handle);
|
|
||||||
pulse_handle = NULL;
|
|
||||||
pulse_loaded = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int LoadPulseLibrary(void)
|
|
||||||
{
|
|
||||||
int i, retval = -1;
|
|
||||||
|
|
||||||
pulse_handle = SDL_LoadObject(pulse_library);
|
|
||||||
if ( pulse_handle ) {
|
|
||||||
pulse_loaded = 1;
|
|
||||||
retval = 0;
|
|
||||||
for ( i=0; i<SDL_arraysize(pulse_functions); ++i ) {
|
|
||||||
*pulse_functions[i].func = SDL_LoadFunction(pulse_handle, pulse_functions[i].name);
|
|
||||||
if ( !*pulse_functions[i].func ) {
|
|
||||||
retval = -1;
|
|
||||||
UnloadPulseLibrary();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static void UnloadPulseLibrary()
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int LoadPulseLibrary(void)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_AUDIO_DRIVER_PULSE_DYNAMIC */
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
pa_sample_spec paspec;
|
|
||||||
pa_simple *connection;
|
|
||||||
int available;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
if ( LoadPulseLibrary() < 0 ) {
|
|
||||||
return available;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connect with a dummy format. */
|
|
||||||
paspec.format = PA_SAMPLE_U8;
|
|
||||||
paspec.rate = 11025;
|
|
||||||
paspec.channels = 1;
|
|
||||||
connection = SDL_NAME(pa_simple_new)(
|
|
||||||
NULL, /* server */
|
|
||||||
"Test stream", /* application name */
|
|
||||||
PA_STREAM_PLAYBACK, /* playback mode */
|
|
||||||
NULL, /* device on the server */
|
|
||||||
"Simple DirectMedia Layer", /* stream description */
|
|
||||||
&paspec, /* sample format spec */
|
|
||||||
NULL, /* channel map */
|
|
||||||
NULL, /* buffering attributes */
|
|
||||||
NULL /* error code */
|
|
||||||
);
|
|
||||||
if ( connection != NULL ) {
|
|
||||||
available = 1;
|
|
||||||
SDL_NAME(pa_simple_free)(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
UnloadPulseLibrary();
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden->caption);
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
UnloadPulseLibrary();
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
LoadPulseLibrary();
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = PULSE_OpenAudio;
|
|
||||||
this->WaitAudio = PULSE_WaitAudio;
|
|
||||||
this->PlayAudio = PULSE_PlayAudio;
|
|
||||||
this->GetAudioBuf = PULSE_GetAudioBuf;
|
|
||||||
this->CloseAudio = PULSE_CloseAudio;
|
|
||||||
this->WaitDone = PULSE_WaitDone;
|
|
||||||
this->SetCaption = PULSE_SetCaption;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap PULSE_bootstrap = {
|
|
||||||
PULSE_DRIVER_NAME, "PulseAudio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void PULSE_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
int size;
|
|
||||||
while(1) {
|
|
||||||
if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY ||
|
|
||||||
SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY ||
|
|
||||||
SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
|
|
||||||
this->enabled = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
size = SDL_NAME(pa_stream_writable_size)(stream);
|
|
||||||
if (size >= mixlen)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PULSE_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Write the audio data */
|
|
||||||
if (SDL_NAME(pa_stream_write)(stream, mixbuf, mixlen, NULL, 0LL, PA_SEEK_RELATIVE) < 0)
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *PULSE_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PULSE_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( stream != NULL ) {
|
|
||||||
SDL_NAME(pa_stream_disconnect)(stream);
|
|
||||||
SDL_NAME(pa_stream_unref)(stream);
|
|
||||||
stream = NULL;
|
|
||||||
}
|
|
||||||
if (context != NULL) {
|
|
||||||
SDL_NAME(pa_context_disconnect)(context);
|
|
||||||
SDL_NAME(pa_context_unref)(context);
|
|
||||||
context = NULL;
|
|
||||||
}
|
|
||||||
if (mainloop != NULL) {
|
|
||||||
SDL_NAME(pa_mainloop_free)(mainloop);
|
|
||||||
mainloop = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to get the name of the program */
|
|
||||||
static char *get_progname(void)
|
|
||||||
{
|
|
||||||
#ifdef __LINUX__
|
|
||||||
char *progname = NULL;
|
|
||||||
FILE *fp;
|
|
||||||
static char temp[BUFSIZ];
|
|
||||||
|
|
||||||
SDL_snprintf(temp, SDL_arraysize(temp), "/proc/%d/cmdline", getpid());
|
|
||||||
fp = fopen(temp, "r");
|
|
||||||
if ( fp != NULL ) {
|
|
||||||
if ( fgets(temp, sizeof(temp)-1, fp) ) {
|
|
||||||
progname = SDL_strrchr(temp, '/');
|
|
||||||
if ( progname == NULL ) {
|
|
||||||
progname = temp;
|
|
||||||
} else {
|
|
||||||
progname = progname+1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fclose(fp);
|
|
||||||
}
|
|
||||||
return(progname);
|
|
||||||
#elif defined(__NetBSD__)
|
|
||||||
return getprogname();
|
|
||||||
#else
|
|
||||||
return("unknown");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void caption_set_complete(pa_context *c, int success, void *userdata)
|
|
||||||
{
|
|
||||||
/* no-op. */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PULSE_SetCaption(_THIS, const char *str)
|
|
||||||
{
|
|
||||||
SDL_free(this->hidden->caption);
|
|
||||||
if ((str == NULL) || (*str == '\0')) {
|
|
||||||
str = get_progname(); /* set a default so SOMETHING shows up. */
|
|
||||||
}
|
|
||||||
this->hidden->caption = SDL_strdup(str);
|
|
||||||
if (context != NULL) {
|
|
||||||
SDL_NAME(pa_context_set_name)(context, this->hidden->caption,
|
|
||||||
caption_set_complete, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void stream_drain_complete(pa_stream *s, int success, void *userdata)
|
|
||||||
{
|
|
||||||
/* no-op. */
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PULSE_WaitDone(_THIS)
|
|
||||||
{
|
|
||||||
pa_operation *o;
|
|
||||||
|
|
||||||
o = SDL_NAME(pa_stream_drain)(stream, stream_drain_complete, NULL);
|
|
||||||
if (!o)
|
|
||||||
return;
|
|
||||||
|
|
||||||
while (SDL_NAME(pa_operation_get_state)(o) != PA_OPERATION_DONE) {
|
|
||||||
if (SDL_NAME(pa_context_get_state)(context) != PA_CONTEXT_READY ||
|
|
||||||
SDL_NAME(pa_stream_get_state)(stream) != PA_STREAM_READY ||
|
|
||||||
SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
|
|
||||||
SDL_NAME(pa_operation_cancel)(o);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_NAME(pa_operation_unref)(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int PULSE_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
int state;
|
|
||||||
Uint16 test_format;
|
|
||||||
pa_sample_spec paspec;
|
|
||||||
pa_buffer_attr paattr;
|
|
||||||
pa_channel_map pacmap;
|
|
||||||
pa_stream_flags_t flags = 0;
|
|
||||||
|
|
||||||
paspec.format = PA_SAMPLE_INVALID;
|
|
||||||
for ( test_format = SDL_FirstAudioFormat(spec->format); test_format; ) {
|
|
||||||
switch ( test_format ) {
|
|
||||||
case AUDIO_U8:
|
|
||||||
paspec.format = PA_SAMPLE_U8;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
paspec.format = PA_SAMPLE_S16LE;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
paspec.format = PA_SAMPLE_S16BE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( paspec.format != PA_SAMPLE_INVALID )
|
|
||||||
break;
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
if (paspec.format == PA_SAMPLE_INVALID ) {
|
|
||||||
SDL_SetError("Couldn't find any suitable audio formats");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
paspec.channels = spec->channels;
|
|
||||||
paspec.rate = spec->freq;
|
|
||||||
|
|
||||||
/* Calculate the final parameters for this audio specification */
|
|
||||||
#ifdef PA_STREAM_ADJUST_LATENCY
|
|
||||||
spec->samples /= 2; /* Mix in smaller chunck to avoid underruns */
|
|
||||||
#endif
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixlen = spec->size;
|
|
||||||
mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* Reduced prebuffering compared to the defaults. */
|
|
||||||
#ifdef PA_STREAM_ADJUST_LATENCY
|
|
||||||
paattr.tlength = mixlen * 4; /* 2x original requested bufsize */
|
|
||||||
paattr.prebuf = -1;
|
|
||||||
paattr.maxlength = -1;
|
|
||||||
paattr.minreq = mixlen; /* -1 can lead to pa_stream_writable_size()
|
|
||||||
>= mixlen never becoming true */
|
|
||||||
flags = PA_STREAM_ADJUST_LATENCY;
|
|
||||||
#else
|
|
||||||
paattr.tlength = mixlen*2;
|
|
||||||
paattr.prebuf = mixlen*2;
|
|
||||||
paattr.maxlength = mixlen*2;
|
|
||||||
paattr.minreq = mixlen;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The SDL ALSA output hints us that we use Windows' channel mapping */
|
|
||||||
/* http://bugzilla.libsdl.org/show_bug.cgi?id=110 */
|
|
||||||
SDL_NAME(pa_channel_map_init_auto)(
|
|
||||||
&pacmap, spec->channels, PA_CHANNEL_MAP_WAVEEX);
|
|
||||||
|
|
||||||
/* Set up a new main loop */
|
|
||||||
if (!(mainloop = SDL_NAME(pa_mainloop_new)())) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("pa_mainloop_new() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this->hidden->caption == NULL) {
|
|
||||||
char *title = NULL;
|
|
||||||
SDL_WM_GetCaption(&title, NULL);
|
|
||||||
PULSE_SetCaption(this, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
mainloop_api = SDL_NAME(pa_mainloop_get_api)(mainloop);
|
|
||||||
if (!(context = SDL_NAME(pa_context_new)(mainloop_api,
|
|
||||||
this->hidden->caption))) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("pa_context_new() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Connect to the PulseAudio server */
|
|
||||||
if (SDL_NAME(pa_context_connect)(context, NULL, 0, NULL) < 0) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("Could not setup connection to PulseAudio");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("pa_mainloop_iterate() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
state = SDL_NAME(pa_context_get_state)(context);
|
|
||||||
if (!PA_CONTEXT_IS_GOOD(state)) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("Could not connect to PulseAudio");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
} while (state != PA_CONTEXT_READY);
|
|
||||||
|
|
||||||
stream = SDL_NAME(pa_stream_new)(
|
|
||||||
context,
|
|
||||||
"Simple DirectMedia Layer", /* stream description */
|
|
||||||
&paspec, /* sample format spec */
|
|
||||||
&pacmap /* channel map */
|
|
||||||
);
|
|
||||||
if ( stream == NULL ) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("Could not setup PulseAudio stream");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SDL_NAME(pa_stream_connect_playback)(stream, NULL, &paattr, flags,
|
|
||||||
NULL, NULL) < 0) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("Could not connect PulseAudio stream");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (SDL_NAME(pa_mainloop_iterate)(mainloop, 1, NULL) < 0) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("pa_mainloop_iterate() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
state = SDL_NAME(pa_stream_get_state)(stream);
|
|
||||||
if (!PA_STREAM_IS_GOOD(state)) {
|
|
||||||
PULSE_CloseAudio(this);
|
|
||||||
SDL_SetError("Could not create to PulseAudio stream");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
} while (state != PA_STREAM_READY);
|
|
||||||
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Stéphan Kochen
|
|
||||||
stephan@kochen.nl
|
|
||||||
|
|
||||||
Based on parts of the ALSA and ESounD output drivers.
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_pulseaudio_h
|
|
||||||
#define _SDL_pulseaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
pa_mainloop *mainloop;
|
|
||||||
pa_mainloop_api *mainloop_api;
|
|
||||||
pa_context *context;
|
|
||||||
pa_stream *stream;
|
|
||||||
|
|
||||||
char *caption;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
Uint8 *mixbuf;
|
|
||||||
int mixlen;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if (PA_API_VERSION < 12)
|
|
||||||
/** Return non-zero if the passed state is one of the connected states */
|
|
||||||
static inline int PA_CONTEXT_IS_GOOD(pa_context_state_t x) {
|
|
||||||
return
|
|
||||||
x == PA_CONTEXT_CONNECTING ||
|
|
||||||
x == PA_CONTEXT_AUTHORIZING ||
|
|
||||||
x == PA_CONTEXT_SETTING_NAME ||
|
|
||||||
x == PA_CONTEXT_READY;
|
|
||||||
}
|
|
||||||
/** Return non-zero if the passed state is one of the connected states */
|
|
||||||
static inline int PA_STREAM_IS_GOOD(pa_stream_state_t x) {
|
|
||||||
return
|
|
||||||
x == PA_STREAM_CREATING ||
|
|
||||||
x == PA_STREAM_READY;
|
|
||||||
}
|
|
||||||
#endif /* pulseaudio <= 0.9.10 */
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define mainloop (this->hidden->mainloop)
|
|
||||||
#define mainloop_api (this->hidden->mainloop_api)
|
|
||||||
#define context (this->hidden->context)
|
|
||||||
#define stream (this->hidden->stream)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define mixlen (this->hidden->mixlen)
|
|
||||||
|
|
||||||
#endif /* _SDL_pulseaudio_h */
|
|
||||||
|
|
||||||
|
|
@ -1,432 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#ifdef __NETBSD__
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/audioio.h>
|
|
||||||
#endif
|
|
||||||
#ifdef __SVR4
|
|
||||||
#include <sys/audioio.h>
|
|
||||||
#else
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#endif
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audiomem.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_sunaudio.h"
|
|
||||||
|
|
||||||
/* Open the audio device for playback, and don't block if busy */
|
|
||||||
#define OPEN_FLAGS (O_WRONLY|O_NONBLOCK)
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void DSP_WaitAudio(_THIS);
|
|
||||||
static void DSP_PlayAudio(_THIS);
|
|
||||||
static Uint8 *DSP_GetAudioBuf(_THIS);
|
|
||||||
static void DSP_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
static Uint8 snd2au(int sample);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
int available;
|
|
||||||
|
|
||||||
available = 0;
|
|
||||||
fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 1);
|
|
||||||
if ( fd >= 0 ) {
|
|
||||||
available = 1;
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return(available);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
audio_fd = -1;
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = DSP_OpenAudio;
|
|
||||||
this->WaitAudio = DSP_WaitAudio;
|
|
||||||
this->PlayAudio = DSP_PlayAudio;
|
|
||||||
this->GetAudioBuf = DSP_GetAudioBuf;
|
|
||||||
this->CloseAudio = DSP_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap SUNAUDIO_bootstrap = {
|
|
||||||
"audio", "UNIX /dev/audio interface",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
void CheckUnderflow(_THIS)
|
|
||||||
{
|
|
||||||
#ifdef AUDIO_GETINFO
|
|
||||||
audio_info_t info;
|
|
||||||
int left;
|
|
||||||
|
|
||||||
ioctl(audio_fd, AUDIO_GETINFO, &info);
|
|
||||||
left = (written - info.play.samples);
|
|
||||||
if ( written && (left == 0) ) {
|
|
||||||
fprintf(stderr, "audio underflow!\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void DSP_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
#ifdef AUDIO_GETINFO
|
|
||||||
#define SLEEP_FUDGE 10 /* 10 ms scheduling fudge factor */
|
|
||||||
audio_info_t info;
|
|
||||||
Sint32 left;
|
|
||||||
|
|
||||||
ioctl(audio_fd, AUDIO_GETINFO, &info);
|
|
||||||
left = (written - info.play.samples);
|
|
||||||
if ( left > fragsize ) {
|
|
||||||
Sint32 sleepy;
|
|
||||||
|
|
||||||
sleepy = ((left - fragsize)/frequency);
|
|
||||||
sleepy -= SLEEP_FUDGE;
|
|
||||||
if ( sleepy > 0 ) {
|
|
||||||
SDL_Delay(sleepy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
fd_set fdset;
|
|
||||||
|
|
||||||
FD_ZERO(&fdset);
|
|
||||||
FD_SET(audio_fd, &fdset);
|
|
||||||
select(audio_fd+1, NULL, &fdset, NULL, NULL);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSP_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Write the audio data */
|
|
||||||
if ( ulaw_only ) {
|
|
||||||
/* Assuming that this->spec.freq >= 8000 Hz */
|
|
||||||
int accum, incr, pos;
|
|
||||||
Uint8 *aubuf;
|
|
||||||
|
|
||||||
accum = 0;
|
|
||||||
incr = this->spec.freq/8;
|
|
||||||
aubuf = ulaw_buf;
|
|
||||||
switch (audio_fmt & 0xFF) {
|
|
||||||
case 8: {
|
|
||||||
Uint8 *sndbuf;
|
|
||||||
|
|
||||||
sndbuf = mixbuf;
|
|
||||||
for ( pos=0; pos < fragsize; ++pos ) {
|
|
||||||
*aubuf = snd2au((0x80-*sndbuf)*64);
|
|
||||||
accum += incr;
|
|
||||||
while ( accum > 0 ) {
|
|
||||||
accum -= 1000;
|
|
||||||
sndbuf += 1;
|
|
||||||
}
|
|
||||||
aubuf += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 16: {
|
|
||||||
Sint16 *sndbuf;
|
|
||||||
|
|
||||||
sndbuf = (Sint16 *)mixbuf;
|
|
||||||
for ( pos=0; pos < fragsize; ++pos ) {
|
|
||||||
*aubuf = snd2au(*sndbuf/4);
|
|
||||||
accum += incr;
|
|
||||||
while ( accum > 0 ) {
|
|
||||||
accum -= 1000;
|
|
||||||
sndbuf += 1;
|
|
||||||
}
|
|
||||||
aubuf += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
CheckUnderflow(this);
|
|
||||||
#endif
|
|
||||||
if ( write(audio_fd, ulaw_buf, fragsize) < 0 ) {
|
|
||||||
/* Assume fatal error, for now */
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
written += fragsize;
|
|
||||||
} else {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
CheckUnderflow(this);
|
|
||||||
#endif
|
|
||||||
if ( write(audio_fd, mixbuf, this->spec.size) < 0 ) {
|
|
||||||
/* Assume fatal error, for now */
|
|
||||||
this->enabled = 0;
|
|
||||||
}
|
|
||||||
written += fragsize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8 *DSP_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
return(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DSP_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_FreeAudioMem(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( ulaw_buf != NULL ) {
|
|
||||||
SDL_free(ulaw_buf);
|
|
||||||
ulaw_buf = NULL;
|
|
||||||
}
|
|
||||||
close(audio_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
char audiodev[1024];
|
|
||||||
#ifdef AUDIO_SETINFO
|
|
||||||
int enc;
|
|
||||||
#endif
|
|
||||||
int desired_freq = spec->freq;
|
|
||||||
|
|
||||||
/* Initialize our freeable variables, in case we fail*/
|
|
||||||
audio_fd = -1;
|
|
||||||
mixbuf = NULL;
|
|
||||||
ulaw_buf = NULL;
|
|
||||||
|
|
||||||
/* Determine the audio parameters from the AudioSpec */
|
|
||||||
switch ( spec->format & 0xFF ) {
|
|
||||||
|
|
||||||
case 8: { /* Unsigned 8 bit audio data */
|
|
||||||
spec->format = AUDIO_U8;
|
|
||||||
#ifdef AUDIO_SETINFO
|
|
||||||
enc = AUDIO_ENCODING_LINEAR8;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 16: { /* Signed 16 bit audio data */
|
|
||||||
spec->format = AUDIO_S16SYS;
|
|
||||||
#ifdef AUDIO_SETINFO
|
|
||||||
enc = AUDIO_ENCODING_LINEAR;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: {
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
audio_fmt = spec->format;
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 1);
|
|
||||||
if ( audio_fd < 0 ) {
|
|
||||||
SDL_SetError("Couldn't open %s: %s", audiodev,
|
|
||||||
strerror(errno));
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
ulaw_only = 0; /* modern Suns do support linear audio */
|
|
||||||
#ifdef AUDIO_SETINFO
|
|
||||||
for(;;) {
|
|
||||||
audio_info_t info;
|
|
||||||
AUDIO_INITINFO(&info); /* init all fields to "no change" */
|
|
||||||
|
|
||||||
/* Try to set the requested settings */
|
|
||||||
info.play.sample_rate = spec->freq;
|
|
||||||
info.play.channels = spec->channels;
|
|
||||||
info.play.precision = (enc == AUDIO_ENCODING_ULAW)
|
|
||||||
? 8 : spec->format & 0xff;
|
|
||||||
info.play.encoding = enc;
|
|
||||||
if( ioctl(audio_fd, AUDIO_SETINFO, &info) == 0 ) {
|
|
||||||
|
|
||||||
/* Check to be sure we got what we wanted */
|
|
||||||
if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) {
|
|
||||||
SDL_SetError("Error getting audio parameters: %s",
|
|
||||||
strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if(info.play.encoding == enc
|
|
||||||
&& info.play.precision == (spec->format & 0xff)
|
|
||||||
&& info.play.channels == spec->channels) {
|
|
||||||
/* Yow! All seems to be well! */
|
|
||||||
spec->freq = info.play.sample_rate;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(enc) {
|
|
||||||
case AUDIO_ENCODING_LINEAR8:
|
|
||||||
/* unsigned 8bit apparently not supported here */
|
|
||||||
enc = AUDIO_ENCODING_LINEAR;
|
|
||||||
spec->format = AUDIO_S16SYS;
|
|
||||||
break; /* try again */
|
|
||||||
|
|
||||||
case AUDIO_ENCODING_LINEAR:
|
|
||||||
/* linear 16bit didn't work either, resort to µ-law */
|
|
||||||
enc = AUDIO_ENCODING_ULAW;
|
|
||||||
spec->channels = 1;
|
|
||||||
spec->freq = 8000;
|
|
||||||
spec->format = AUDIO_U8;
|
|
||||||
ulaw_only = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
/* oh well... */
|
|
||||||
SDL_SetError("Error setting audio parameters: %s",
|
|
||||||
strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* AUDIO_SETINFO */
|
|
||||||
written = 0;
|
|
||||||
|
|
||||||
/* We can actually convert on-the-fly to U-Law */
|
|
||||||
if ( ulaw_only ) {
|
|
||||||
spec->freq = desired_freq;
|
|
||||||
fragsize = (spec->samples*1000)/(spec->freq/8);
|
|
||||||
frequency = 8;
|
|
||||||
ulaw_buf = (Uint8 *)SDL_malloc(fragsize);
|
|
||||||
if ( ulaw_buf == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
spec->channels = 1;
|
|
||||||
} else {
|
|
||||||
fragsize = spec->samples;
|
|
||||||
frequency = spec->freq/1000;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Audio device %s U-Law only\n",
|
|
||||||
ulaw_only ? "is" : "is not");
|
|
||||||
fprintf(stderr, "format=0x%x chan=%d freq=%d\n",
|
|
||||||
spec->format, spec->channels, spec->freq);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Update the fragment size as size in bytes */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
mixbuf = (Uint8 *)SDL_AllocAudioMem(spec->size);
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
SDL_memset(mixbuf, spec->silence, spec->size);
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/************************************************************************/
|
|
||||||
/* This function (snd2au()) copyrighted: */
|
|
||||||
/************************************************************************/
|
|
||||||
/* Copyright 1989 by Rich Gopstein and Harris Corporation */
|
|
||||||
/* */
|
|
||||||
/* Permission to use, copy, modify, and distribute this software */
|
|
||||||
/* and its documentation for any purpose and without fee is */
|
|
||||||
/* hereby granted, provided that the above copyright notice */
|
|
||||||
/* appears in all copies and that both that copyright notice and */
|
|
||||||
/* this permission notice appear in supporting documentation, and */
|
|
||||||
/* that the name of Rich Gopstein and Harris Corporation not be */
|
|
||||||
/* used in advertising or publicity pertaining to distribution */
|
|
||||||
/* of the software without specific, written prior permission. */
|
|
||||||
/* Rich Gopstein and Harris Corporation make no representations */
|
|
||||||
/* about the suitability of this software for any purpose. It */
|
|
||||||
/* provided "as is" without express or implied warranty. */
|
|
||||||
/************************************************************************/
|
|
||||||
|
|
||||||
static Uint8 snd2au(int sample)
|
|
||||||
{
|
|
||||||
|
|
||||||
int mask;
|
|
||||||
|
|
||||||
if (sample < 0) {
|
|
||||||
sample = -sample;
|
|
||||||
mask = 0x7f;
|
|
||||||
} else {
|
|
||||||
mask = 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sample < 32) {
|
|
||||||
sample = 0xF0 | (15 - sample / 2);
|
|
||||||
} else if (sample < 96) {
|
|
||||||
sample = 0xE0 | (15 - (sample - 32) / 4);
|
|
||||||
} else if (sample < 224) {
|
|
||||||
sample = 0xD0 | (15 - (sample - 96) / 8);
|
|
||||||
} else if (sample < 480) {
|
|
||||||
sample = 0xC0 | (15 - (sample - 224) / 16);
|
|
||||||
} else if (sample < 992) {
|
|
||||||
sample = 0xB0 | (15 - (sample - 480) / 32);
|
|
||||||
} else if (sample < 2016) {
|
|
||||||
sample = 0xA0 | (15 - (sample - 992) / 64);
|
|
||||||
} else if (sample < 4064) {
|
|
||||||
sample = 0x90 | (15 - (sample - 2016) / 128);
|
|
||||||
} else if (sample < 8160) {
|
|
||||||
sample = 0x80 | (15 - (sample - 4064) / 256);
|
|
||||||
} else {
|
|
||||||
sample = 0x80;
|
|
||||||
}
|
|
||||||
return (mask & sample);
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_lowaudio_h
|
|
||||||
#define _SDL_lowaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
/* The file descriptor for the audio device */
|
|
||||||
int audio_fd;
|
|
||||||
|
|
||||||
Uint16 audio_fmt; /* The app audio format */
|
|
||||||
Uint8 *mixbuf; /* The app mixing buffer */
|
|
||||||
int ulaw_only; /* Flag -- does hardware only output U-law? */
|
|
||||||
Uint8 *ulaw_buf; /* The U-law mixing buffer */
|
|
||||||
Sint32 written; /* The number of samples written */
|
|
||||||
int fragsize; /* The audio fragment size in samples */
|
|
||||||
int frequency; /* The audio frequency in KHz */
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define audio_fd (this->hidden->audio_fd)
|
|
||||||
#define audio_fmt (this->hidden->audio_fmt)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define ulaw_only (this->hidden->ulaw_only)
|
|
||||||
#define ulaw_buf (this->hidden->ulaw_buf)
|
|
||||||
#define written (this->hidden->written)
|
|
||||||
#define fragsize (this->hidden->fragsize)
|
|
||||||
#define frequency (this->hidden->frequency)
|
|
||||||
|
|
||||||
#endif /* _SDL_lowaudio_h */
|
|
||||||
|
|
@ -1,614 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@devolution.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
SDL_epocaudio.cpp
|
|
||||||
Epoc based SDL audio driver implementation
|
|
||||||
|
|
||||||
Markus Mertama
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef SAVE_RCSID
|
|
||||||
static char rcsid =
|
|
||||||
"@(#) $Id: SDL_epocaudio.c,v 0.0.0.0 2001/06/19 17:19:56 hercules Exp $";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
|
|
||||||
#include "epoc_sdl.h"
|
|
||||||
|
|
||||||
#include <e32hal.h>
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "SDL_error.h"
|
|
||||||
#include "SDL_audiomem.h"
|
|
||||||
#include "SDL_audio_c.h"
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audiodev_c.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "SDL_epocaudio.h"
|
|
||||||
|
|
||||||
#include "streamplayer.h"
|
|
||||||
|
|
||||||
|
|
||||||
//#define DEBUG_AUDIO
|
|
||||||
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
|
|
||||||
static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec);
|
|
||||||
static void EPOC_WaitAudio(SDL_AudioDevice *thisdevice);
|
|
||||||
static void EPOC_PlayAudio(SDL_AudioDevice *thisdevice);
|
|
||||||
static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice *thisdevice);
|
|
||||||
static void EPOC_CloseAudio(SDL_AudioDevice *thisdevice);
|
|
||||||
static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice);
|
|
||||||
|
|
||||||
static int Audio_Available(void);
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex);
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device);
|
|
||||||
|
|
||||||
|
|
||||||
//void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len);
|
|
||||||
|
|
||||||
#ifdef __WINS__
|
|
||||||
#define DODUMP
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DODUMP
|
|
||||||
NONSHARABLE_CLASS(TDump)
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TInt Open();
|
|
||||||
void Close();
|
|
||||||
void Dump(const TDesC8& aDes);
|
|
||||||
private:
|
|
||||||
RFile iFile;
|
|
||||||
RFs iFs;
|
|
||||||
};
|
|
||||||
|
|
||||||
TInt TDump::Open()
|
|
||||||
{
|
|
||||||
TInt err = iFs.Connect();
|
|
||||||
if(err == KErrNone)
|
|
||||||
{
|
|
||||||
#ifdef __WINS__
|
|
||||||
_LIT(target, "C:\\sdlau.raw");
|
|
||||||
#else
|
|
||||||
_LIT(target, "E:\\sdlau.raw");
|
|
||||||
#endif
|
|
||||||
err = iFile.Replace(iFs, target, EFileWrite);
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
void TDump::Close()
|
|
||||||
{
|
|
||||||
iFile.Close();
|
|
||||||
iFs.Close();
|
|
||||||
}
|
|
||||||
void TDump::Dump(const TDesC8& aDes)
|
|
||||||
{
|
|
||||||
iFile.Write(aDes);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
NONSHARABLE_CLASS(CSimpleWait) : public CTimer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void Wait(TTimeIntervalMicroSeconds32 aWait);
|
|
||||||
static CSimpleWait* NewL();
|
|
||||||
private:
|
|
||||||
CSimpleWait();
|
|
||||||
void RunL();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
CSimpleWait* CSimpleWait::NewL()
|
|
||||||
{
|
|
||||||
CSimpleWait* wait = new (ELeave) CSimpleWait();
|
|
||||||
CleanupStack::PushL(wait);
|
|
||||||
wait->ConstructL();
|
|
||||||
CleanupStack::Pop();
|
|
||||||
return wait;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSimpleWait::Wait(TTimeIntervalMicroSeconds32 aWait)
|
|
||||||
{
|
|
||||||
After(aWait);
|
|
||||||
CActiveScheduler::Start();
|
|
||||||
}
|
|
||||||
|
|
||||||
CSimpleWait::CSimpleWait() : CTimer(CActive::EPriorityStandard)
|
|
||||||
{
|
|
||||||
CActiveScheduler::Add(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CSimpleWait::RunL()
|
|
||||||
{
|
|
||||||
CActiveScheduler::Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
const TInt KAudioBuffers(2);
|
|
||||||
|
|
||||||
|
|
||||||
NONSHARABLE_CLASS(CEpocAudio) : public CBase, public MStreamObs, public MStreamProvider
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static void* NewL(TInt BufferSize, TInt aFill);
|
|
||||||
inline static CEpocAudio& Current(SDL_AudioDevice* thisdevice);
|
|
||||||
|
|
||||||
static void Free(SDL_AudioDevice* thisdevice);
|
|
||||||
|
|
||||||
void Wait();
|
|
||||||
void Play();
|
|
||||||
// void SetBuffer(const TDesC8& aBuffer);
|
|
||||||
void ThreadInitL(TAny* aDevice);
|
|
||||||
void Open(TInt iRate, TInt iChannels, TUint32 aType, TInt aBytes);
|
|
||||||
~CEpocAudio();
|
|
||||||
TUint8* Buffer();
|
|
||||||
TBool SetPause(TBool aPause);
|
|
||||||
#ifdef DODUMP
|
|
||||||
void Dump(const TDesC8& aBuf) {iDump.Dump(aBuf);}
|
|
||||||
#endif
|
|
||||||
private:
|
|
||||||
CEpocAudio(TInt aBufferSize);
|
|
||||||
void Complete(TInt aState, TInt aError);
|
|
||||||
TPtrC8 Data();
|
|
||||||
void ConstructL(TInt aFill);
|
|
||||||
private:
|
|
||||||
TInt iBufferSize;
|
|
||||||
CStreamPlayer* iPlayer;
|
|
||||||
TInt iBufferRate;
|
|
||||||
TInt iRate;
|
|
||||||
TInt iChannels;
|
|
||||||
TUint32 iType;
|
|
||||||
TInt iPosition;
|
|
||||||
TThreadId iTid;
|
|
||||||
TUint8* iAudioPtr;
|
|
||||||
TUint8* iBuffer;
|
|
||||||
// TTimeIntervalMicroSeconds iStart;
|
|
||||||
TTime iStart;
|
|
||||||
TInt iTune;
|
|
||||||
CSimpleWait* iWait;
|
|
||||||
#ifdef DODUMP
|
|
||||||
TDump iDump;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
inline CEpocAudio& CEpocAudio::Current(SDL_AudioDevice* thisdevice)
|
|
||||||
{
|
|
||||||
return *static_cast<CEpocAudio*>((void*)thisdevice->hidden);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
TBool EndSc(TAny*)
|
|
||||||
{
|
|
||||||
CActiveScheduler::Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL_C void CleanScL()
|
|
||||||
{
|
|
||||||
CIdle* d = CIdle::NewLC(CActive:::EPriorityIdle);
|
|
||||||
d->Start(TCallBack(EndSc));
|
|
||||||
CActiveScheduler::Start();
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
void CEpocAudio::Free(SDL_AudioDevice* thisdevice)
|
|
||||||
{
|
|
||||||
CEpocAudio* ea = static_cast<CEpocAudio*>((void*)thisdevice->hidden);
|
|
||||||
if(ea)
|
|
||||||
{
|
|
||||||
ASSERT(ea->iTid == RThread().Id());
|
|
||||||
delete ea;
|
|
||||||
thisdevice->hidden = NULL;
|
|
||||||
|
|
||||||
CActiveScheduler* as = CActiveScheduler::Current();
|
|
||||||
ASSERT(as->StackDepth() == 0);
|
|
||||||
delete as;
|
|
||||||
CActiveScheduler::Install(NULL);
|
|
||||||
}
|
|
||||||
ASSERT(thisdevice->hidden == NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
CEpocAudio::CEpocAudio(TInt aBufferSize) : iBufferSize(aBufferSize), iPosition(-1)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void* CEpocAudio::NewL(TInt aBufferSize, TInt aFill)
|
|
||||||
{
|
|
||||||
CEpocAudio* eAudioLib = new (ELeave) CEpocAudio(aBufferSize);
|
|
||||||
CleanupStack::PushL(eAudioLib);
|
|
||||||
eAudioLib->ConstructL(aFill);
|
|
||||||
CleanupStack::Pop();
|
|
||||||
return eAudioLib;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CEpocAudio::ConstructL(TInt aFill)
|
|
||||||
{
|
|
||||||
iBuffer = (TUint8*) User::AllocL(KAudioBuffers * iBufferSize);
|
|
||||||
memset(iBuffer, aFill, KAudioBuffers * iBufferSize);
|
|
||||||
iAudioPtr = iBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TBool CEpocAudio::SetPause(TBool aPause)
|
|
||||||
{
|
|
||||||
if(aPause && iPosition >= 0)
|
|
||||||
{
|
|
||||||
iPosition = -1;
|
|
||||||
if(iPlayer != NULL)
|
|
||||||
iPlayer->Stop();
|
|
||||||
}
|
|
||||||
if(!aPause && iPosition < 0)
|
|
||||||
{
|
|
||||||
iPosition = 0;
|
|
||||||
if(iPlayer != NULL)
|
|
||||||
iPlayer->Start();
|
|
||||||
}
|
|
||||||
return iPosition < 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CEpocAudio::ThreadInitL(TAny* aDevice)
|
|
||||||
{
|
|
||||||
iTid = RThread().Id();
|
|
||||||
CActiveScheduler* as = new (ELeave) CActiveScheduler();
|
|
||||||
CActiveScheduler::Install(as);
|
|
||||||
|
|
||||||
EpocSdlEnv::AppendCleanupItem(TSdlCleanupItem((TSdlCleanupOperation)EPOC_CloseAudio, aDevice));
|
|
||||||
|
|
||||||
iWait = CSimpleWait::NewL();
|
|
||||||
|
|
||||||
iPlayer = new (ELeave) CStreamPlayer(*this, *this);
|
|
||||||
iPlayer->ConstructL();
|
|
||||||
iPlayer->OpenStream(iRate, iChannels, iType);
|
|
||||||
|
|
||||||
#ifdef DODUMP
|
|
||||||
User::LeaveIfError(iDump.Open());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
TUint8* CEpocAudio::Buffer()
|
|
||||||
{
|
|
||||||
iStart.UniversalTime();
|
|
||||||
// iStart = iPlayer->Position();
|
|
||||||
return iAudioPtr;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
CEpocAudio::~CEpocAudio()
|
|
||||||
{
|
|
||||||
if(iWait != NULL)
|
|
||||||
iWait->Cancel();
|
|
||||||
delete iWait;
|
|
||||||
if(iPlayer != NULL)
|
|
||||||
iPlayer->Close();
|
|
||||||
delete iPlayer;
|
|
||||||
delete iBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CEpocAudio::Complete(TInt aState, TInt aError)
|
|
||||||
{
|
|
||||||
if(aState == MStreamObs::EClose)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
if(iPlayer->Closed())
|
|
||||||
return;
|
|
||||||
switch(aError)
|
|
||||||
{
|
|
||||||
case KErrUnderflow:
|
|
||||||
case KErrInUse:
|
|
||||||
iPlayer->Start();
|
|
||||||
break;
|
|
||||||
case KErrAbort:
|
|
||||||
iPlayer->Open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void sos_adump(SDL_AudioDevice* thisdevice, void* data, int len)
|
|
||||||
{
|
|
||||||
#ifdef DODUMP
|
|
||||||
const TPtrC8 buf((TUint8*)data, len);
|
|
||||||
CEpocAudio::Current(thisdevice).Dump(buf);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const TInt KClip(256);
|
|
||||||
|
|
||||||
TPtrC8 CEpocAudio::Data()
|
|
||||||
{
|
|
||||||
if(iPosition < 0)
|
|
||||||
return KNullDesC8();
|
|
||||||
|
|
||||||
TPtrC8 data(iAudioPtr + iPosition, KClip);
|
|
||||||
|
|
||||||
#ifdef DODUMP
|
|
||||||
iDump.Dump(data);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
iPosition += KClip;
|
|
||||||
if(iPosition >= iBufferSize)
|
|
||||||
{
|
|
||||||
|
|
||||||
/* if(iAudioPtr == iBuffer)
|
|
||||||
iAudioPtr = iBuffer + iBufferSize;
|
|
||||||
else
|
|
||||||
iAudioPtr = iBuffer;
|
|
||||||
*/
|
|
||||||
iAudioPtr += iBufferSize;
|
|
||||||
|
|
||||||
if((iAudioPtr - iBuffer) >= KAudioBuffers * iBufferSize)
|
|
||||||
iAudioPtr = iBuffer;
|
|
||||||
|
|
||||||
iPosition = -1;
|
|
||||||
if(iWait->IsActive())
|
|
||||||
{
|
|
||||||
iWait->Cancel();
|
|
||||||
CActiveScheduler::Stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void CEpocAudio::Play()
|
|
||||||
{
|
|
||||||
iPosition = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CEpocAudio::Wait()
|
|
||||||
{
|
|
||||||
if(iPosition >= 0 /*&& iPlayer->Playing()*/)
|
|
||||||
{
|
|
||||||
const TInt64 bufMs = TInt64(iBufferSize - KClip) * TInt64(1000000);
|
|
||||||
const TInt64 specTime = bufMs / TInt64(iRate * iChannels * 2);
|
|
||||||
iWait->After(specTime);
|
|
||||||
|
|
||||||
CActiveScheduler::Start();
|
|
||||||
TTime end;
|
|
||||||
end.UniversalTime();
|
|
||||||
const TTimeIntervalMicroSeconds delta = end.MicroSecondsFrom(iStart);
|
|
||||||
|
|
||||||
|
|
||||||
// const TTimeIntervalMicroSeconds end = iPlayer->Position();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const TInt diff = specTime - delta.Int64();
|
|
||||||
|
|
||||||
if(diff > 0 && diff < 200000)
|
|
||||||
{
|
|
||||||
User::After(diff);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
User::After(10000);
|
|
||||||
// iWait->Wait(10000); //just give some time...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CEpocAudio::Open(TInt aRate, TInt aChannels, TUint32 aType, TInt aBytes)
|
|
||||||
{
|
|
||||||
iRate = aRate;
|
|
||||||
iChannels = aChannels;
|
|
||||||
iType = aType;
|
|
||||||
iBufferRate = iRate * iChannels * aBytes; //1/x
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
AudioBootStrap EPOCAudio_bootstrap = {
|
|
||||||
"epoc\0\0\0",
|
|
||||||
"EPOC streaming audio\0\0\0",
|
|
||||||
Audio_Available,
|
|
||||||
Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int /*devindex*/)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *thisdevice;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
thisdevice = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( thisdevice ) {
|
|
||||||
memset(thisdevice, 0, (sizeof *thisdevice));
|
|
||||||
thisdevice->hidden = NULL; /*(struct SDL_PrivateAudioData *)
|
|
||||||
malloc((sizeof thisdevice->hidden)); */
|
|
||||||
}
|
|
||||||
if ( (thisdevice == NULL) /*|| (thisdevice->hidden == NULL) */) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( thisdevice ) {
|
|
||||||
free(thisdevice);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
// memset(thisdevice->hidden, 0, (sizeof *thisdevice->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
thisdevice->OpenAudio = EPOC_OpenAudio;
|
|
||||||
thisdevice->WaitAudio = EPOC_WaitAudio;
|
|
||||||
thisdevice->PlayAudio = EPOC_PlayAudio;
|
|
||||||
thisdevice->GetAudioBuf = EPOC_GetAudioBuf;
|
|
||||||
thisdevice->CloseAudio = EPOC_CloseAudio;
|
|
||||||
thisdevice->ThreadInit = EPOC_ThreadInit;
|
|
||||||
thisdevice->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return thisdevice;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
//free(device->hidden);
|
|
||||||
free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return(1); // Audio stream modules should be always there!
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int EPOC_OpenAudio(SDL_AudioDevice *thisdevice, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
SDL_TRACE("SDL:EPOC_OpenAudio");
|
|
||||||
|
|
||||||
|
|
||||||
TUint32 type = KMMFFourCCCodePCM16;
|
|
||||||
TInt bytes = 2;
|
|
||||||
|
|
||||||
switch(spec->format)
|
|
||||||
{
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
type = KMMFFourCCCodePCMU16;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
type = KMMFFourCCCodePCM16;
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
type = KMMFFourCCCodePCMU16B;
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
type = KMMFFourCCCodePCM16B;
|
|
||||||
break;
|
|
||||||
//8 bit not supported!
|
|
||||||
case AUDIO_U8:
|
|
||||||
case AUDIO_S8:
|
|
||||||
default:
|
|
||||||
spec->format = AUDIO_S16LSB;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(spec->channels > 2)
|
|
||||||
spec->channels = 2;
|
|
||||||
|
|
||||||
spec->freq = CStreamPlayer::ClosestSupportedRate(spec->freq);
|
|
||||||
|
|
||||||
|
|
||||||
/* Allocate mixing buffer */
|
|
||||||
const TInt buflen = spec->size;// * bytes * spec->channels;
|
|
||||||
// audiobuf = NULL;
|
|
||||||
|
|
||||||
TRAPD(err, thisdevice->hidden = static_cast<SDL_PrivateAudioData*>(CEpocAudio::NewL(buflen, spec->silence)));
|
|
||||||
if(err != KErrNone)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
CEpocAudio::Current(thisdevice).Open(spec->freq, spec->channels, type, bytes);
|
|
||||||
|
|
||||||
CEpocAudio::Current(thisdevice).SetPause(ETrue);
|
|
||||||
|
|
||||||
// isSDLAudioPaused = 1;
|
|
||||||
|
|
||||||
thisdevice->enabled = 0; /* enable only after audio engine has been initialized!*/
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void EPOC_CloseAudio(SDL_AudioDevice* thisdevice)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
SDL_TRACE("Close audio\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CEpocAudio::Free(thisdevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void EPOC_ThreadInit(SDL_AudioDevice *thisdevice)
|
|
||||||
{
|
|
||||||
SDL_TRACE("SDL:EPOC_ThreadInit");
|
|
||||||
CEpocAudio::Current(thisdevice).ThreadInitL(thisdevice);
|
|
||||||
RThread().SetPriority(EPriorityMore);
|
|
||||||
thisdevice->enabled = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function waits until it is possible to write a full sound buffer */
|
|
||||||
static void EPOC_WaitAudio(SDL_AudioDevice* thisdevice)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
SDL_TRACE1("wait %d audio\n", CEpocAudio::AudioLib().StreamPlayer(KSfxChannel).SyncTime());
|
|
||||||
TInt tics = User::TickCount();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CEpocAudio::Current(thisdevice).Wait();
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
TInt ntics = User::TickCount() - tics;
|
|
||||||
SDL_TRACE1("audio waited %d\n", ntics);
|
|
||||||
SDL_TRACE1("audio at %d\n", tics);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static void EPOC_PlayAudio(SDL_AudioDevice* thisdevice)
|
|
||||||
{
|
|
||||||
if(CEpocAudio::Current(thisdevice).SetPause(SDL_GetAudioStatus() == SDL_AUDIO_PAUSED))
|
|
||||||
SDL_Delay(500); //hold on the busy loop
|
|
||||||
else
|
|
||||||
CEpocAudio::Current(thisdevice).Play();
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
SDL_TRACE("buffer has audio data\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
SDL_TRACE1("Wrote %d bytes of audio data\n", buflen);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *EPOC_GetAudioBuf(SDL_AudioDevice* thisdevice)
|
|
||||||
{
|
|
||||||
return CEpocAudio::Current(thisdevice).Buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@devolution.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef SAVE_RCSID
|
|
||||||
static char rcsid =
|
|
||||||
"@(#) $Id: SDL_epocaudio.h,v 1.1.2.2 2001/02/10 07:20:03 hercules Exp $";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _SDL_EPOCAUDIO_H
|
|
||||||
#define _SDL_EPOCAUDIO_H
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include "SDL_sysaudio.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* _SDL_EPOCAUDIO_H */
|
|
||||||
|
|
@ -1,279 +0,0 @@
|
||||||
#include "streamplayer.h"
|
|
||||||
#include<mda/common/audio.h>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const TInt KMaxVolume(256);
|
|
||||||
|
|
||||||
LOCAL_C TInt GetSampleRate(TInt aRate)
|
|
||||||
{
|
|
||||||
switch(aRate)
|
|
||||||
{
|
|
||||||
case 8000: return TMdaAudioDataSettings::ESampleRate8000Hz;
|
|
||||||
case 11025: return TMdaAudioDataSettings::ESampleRate11025Hz;
|
|
||||||
case 12000: return TMdaAudioDataSettings::ESampleRate12000Hz;
|
|
||||||
case 16000: return TMdaAudioDataSettings::ESampleRate16000Hz;
|
|
||||||
case 22050: return TMdaAudioDataSettings::ESampleRate22050Hz;
|
|
||||||
case 24000: return TMdaAudioDataSettings::ESampleRate24000Hz;
|
|
||||||
case 32000: return TMdaAudioDataSettings::ESampleRate32000Hz;
|
|
||||||
case 44100: return TMdaAudioDataSettings::ESampleRate44100Hz;
|
|
||||||
case 48000: return TMdaAudioDataSettings::ESampleRate48000Hz;
|
|
||||||
case 96000: return TMdaAudioDataSettings::ESampleRate96000Hz;
|
|
||||||
case 64000: return TMdaAudioDataSettings::ESampleRate64000Hz;
|
|
||||||
}
|
|
||||||
return KErrNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOCAL_C TInt GetChannels(TInt aChannels)
|
|
||||||
{
|
|
||||||
switch(aChannels)
|
|
||||||
{
|
|
||||||
case 1: return TMdaAudioDataSettings::EChannelsMono;
|
|
||||||
case 2: return TMdaAudioDataSettings::EChannelsStereo;
|
|
||||||
}
|
|
||||||
return KErrNotFound;
|
|
||||||
}
|
|
||||||
|
|
||||||
TInt CStreamPlayer::ClosestSupportedRate(TInt aRate)
|
|
||||||
{
|
|
||||||
if(aRate > 96000)
|
|
||||||
return 96000;
|
|
||||||
TInt rate = aRate;
|
|
||||||
while(GetSampleRate(rate) == KErrNotFound)
|
|
||||||
{
|
|
||||||
++rate;
|
|
||||||
}
|
|
||||||
return rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
CStreamPlayer::CStreamPlayer(MStreamProvider& aProvider, MStreamObs& aObs) :
|
|
||||||
iProvider(aProvider), iObs(aObs), iVolume(KMaxVolume)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CStreamPlayer::~CStreamPlayer()
|
|
||||||
{
|
|
||||||
iState |= EDied;
|
|
||||||
if(iState & EInited)
|
|
||||||
Close();
|
|
||||||
User::After(100000); //wait buffer to be flushed
|
|
||||||
ASSERT(iPtr.Length() == 0);
|
|
||||||
delete iStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CStreamPlayer::ConstructL()
|
|
||||||
{
|
|
||||||
iStream = CMdaAudioOutputStream::NewL(*this, EMdaPriorityMax);
|
|
||||||
iSilence.SetMax();
|
|
||||||
iSilence.FillZ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TInt CStreamPlayer::OpenStream(TInt aRate, TInt aChannels, TUint32 aType)
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
|
|
||||||
iType = aType;
|
|
||||||
|
|
||||||
iRate = GetSampleRate(aRate);
|
|
||||||
if(iRate == KErrNotFound)
|
|
||||||
return KErrNotSupported;
|
|
||||||
|
|
||||||
iChannels = GetChannels(aChannels);
|
|
||||||
if(iChannels == KErrNotFound)
|
|
||||||
return KErrNotSupported;
|
|
||||||
|
|
||||||
Open();
|
|
||||||
|
|
||||||
return KErrNone;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TInt CStreamPlayer::MaxVolume() const
|
|
||||||
{
|
|
||||||
return KMaxVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::SetVolume(TInt aNew)
|
|
||||||
{
|
|
||||||
|
|
||||||
const TInt maxi = MaxVolume();
|
|
||||||
if(aNew > maxi)
|
|
||||||
return;
|
|
||||||
if(aNew < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
iVolume = aNew;
|
|
||||||
|
|
||||||
iState |= EVolumeChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
TInt CStreamPlayer::Volume() const
|
|
||||||
{
|
|
||||||
return iVolume;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::Open()
|
|
||||||
{
|
|
||||||
TMdaAudioDataSettings audioSettings;
|
|
||||||
audioSettings.Query();
|
|
||||||
audioSettings.iCaps = TMdaAudioDataSettings::ERealTime |
|
|
||||||
TMdaAudioDataSettings::ESampleRateFixed;
|
|
||||||
audioSettings.iSampleRate = iRate;
|
|
||||||
audioSettings.iChannels = iChannels;
|
|
||||||
audioSettings.iFlags = TMdaAudioDataSettings::ENoNetworkRouting;
|
|
||||||
audioSettings.iVolume = 0;
|
|
||||||
|
|
||||||
iState &= ~EStopped;
|
|
||||||
iStream->Open(&audioSettings);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::Stop()
|
|
||||||
{
|
|
||||||
if(iState & (EStarted | EInited))
|
|
||||||
{
|
|
||||||
Close();
|
|
||||||
iState |= EStopped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::Start()
|
|
||||||
{
|
|
||||||
if(iPtr.Length() == 0)
|
|
||||||
{
|
|
||||||
iState |= EStarted;
|
|
||||||
if(iState & EInited)
|
|
||||||
{
|
|
||||||
Request();
|
|
||||||
}
|
|
||||||
else if(iState & EStopped)
|
|
||||||
{
|
|
||||||
Open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::Close()
|
|
||||||
{
|
|
||||||
iState &= ~EInited;
|
|
||||||
iStream->Stop();
|
|
||||||
iState &= ~EStarted;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::Request()
|
|
||||||
{
|
|
||||||
if(iState & EInited)
|
|
||||||
{
|
|
||||||
iPtr.Set(KNullDesC8);
|
|
||||||
|
|
||||||
if(iState & EVolumeChange)
|
|
||||||
{
|
|
||||||
const TReal newVol = iVolume;
|
|
||||||
const TReal newMax = MaxVolume();
|
|
||||||
const TInt maxVol = iStream->MaxVolume();
|
|
||||||
const TReal max = static_cast<TReal>(maxVol);
|
|
||||||
const TReal newvolume = (newVol * max) / newMax;
|
|
||||||
const TInt vol = static_cast<TReal>(newvolume);
|
|
||||||
iStream->SetVolume(vol);
|
|
||||||
iState &= ~EVolumeChange;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(iState & EStarted)
|
|
||||||
{
|
|
||||||
iPtr.Set(iProvider.Data());
|
|
||||||
}
|
|
||||||
if(iPtr.Length() == 0)
|
|
||||||
{
|
|
||||||
iPtr.Set(iSilence);
|
|
||||||
}
|
|
||||||
TRAPD(err, iStream->WriteL(iPtr));
|
|
||||||
if(err != KErrNone)
|
|
||||||
{
|
|
||||||
iObs.Complete(MStreamObs::EWrite, err);
|
|
||||||
}
|
|
||||||
/* else
|
|
||||||
{
|
|
||||||
iProvider.Written(iPtr.Length());
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void CStreamPlayer::SetCapsL()
|
|
||||||
{
|
|
||||||
iStream->SetDataTypeL(iType);
|
|
||||||
iStream->SetAudioPropertiesL(iRate, iChannels);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::MaoscOpenComplete(TInt aError)
|
|
||||||
{
|
|
||||||
if(aError == KErrNone)
|
|
||||||
{
|
|
||||||
TRAPD(err, SetCapsL());
|
|
||||||
if(err == KErrNone)
|
|
||||||
{
|
|
||||||
iStream->SetPriority(EPriorityNormal, EMdaPriorityPreferenceTime);
|
|
||||||
iState |= EInited;
|
|
||||||
|
|
||||||
|
|
||||||
SetVolume(Volume());
|
|
||||||
|
|
||||||
if(iState & EStarted)
|
|
||||||
{
|
|
||||||
Request();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
aError = err;
|
|
||||||
}
|
|
||||||
if(!(iState & EDied))
|
|
||||||
iObs.Complete(MStreamObs::EInit, aError);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::MaoscBufferCopied(TInt aError, const TDesC8& /*aBuffer*/)
|
|
||||||
{
|
|
||||||
iPtr.Set(KNullDesC8);
|
|
||||||
if(aError == KErrNone)
|
|
||||||
{
|
|
||||||
if(iState & EInited)
|
|
||||||
Request();
|
|
||||||
else
|
|
||||||
iStream->Stop();
|
|
||||||
}
|
|
||||||
else if(!(iState & EDied))
|
|
||||||
iObs.Complete(MStreamObs::EPlay, aError);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStreamPlayer::MaoscPlayComplete(TInt aError)
|
|
||||||
{
|
|
||||||
iPtr.Set(KNullDesC8);
|
|
||||||
iState &= ~EStarted;
|
|
||||||
if(!(iState & EDied))
|
|
||||||
iObs.Complete(MStreamObs::EClose, aError);
|
|
||||||
}
|
|
||||||
|
|
||||||
TBool CStreamPlayer::Playing() const
|
|
||||||
{
|
|
||||||
return (iState & EInited) && (iState & EStarted);
|
|
||||||
}
|
|
||||||
|
|
||||||
TBool CStreamPlayer::Closed() const
|
|
||||||
{
|
|
||||||
return !(iState & EInited) && !(iState & EDied);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
void CStreamPlayer::Request()
|
|
||||||
{
|
|
||||||
SetActive();
|
|
||||||
TRequestStatus* s = &iStatus;
|
|
||||||
User::RequestComplete(s, KErrNone);
|
|
||||||
}
|
|
||||||
// iTimer.After(0);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
#ifndef STREAMPLAYER_H
|
|
||||||
#define STREAMPLAYER_H
|
|
||||||
|
|
||||||
#include<MdaAudioOutputStream.h>
|
|
||||||
|
|
||||||
const TInt KSilenceBuffer = 256;
|
|
||||||
|
|
||||||
class MStreamObs
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
EInit,
|
|
||||||
EPlay,
|
|
||||||
EWrite,
|
|
||||||
EClose,
|
|
||||||
};
|
|
||||||
virtual void Complete(TInt aState, TInt aError) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class MStreamProvider
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual TPtrC8 Data() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
NONSHARABLE_CLASS(CStreamPlayer) : public CBase, public MMdaAudioOutputStreamCallback
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CStreamPlayer(MStreamProvider& aProvider, MStreamObs& aObs);
|
|
||||||
~CStreamPlayer();
|
|
||||||
void ConstructL();
|
|
||||||
|
|
||||||
static TInt ClosestSupportedRate(TInt aRate);
|
|
||||||
|
|
||||||
TInt OpenStream(TInt aRate, TInt aChannels, TUint32 aType = KMMFFourCCCodePCM16);
|
|
||||||
|
|
||||||
void SetVolume(TInt aNew);
|
|
||||||
TInt Volume() const;
|
|
||||||
TInt MaxVolume() const;
|
|
||||||
|
|
||||||
void Stop();
|
|
||||||
void Start();
|
|
||||||
void Open();
|
|
||||||
void Close();
|
|
||||||
|
|
||||||
TBool Playing() const;
|
|
||||||
TBool Closed() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
void MaoscOpenComplete(TInt aError) ;
|
|
||||||
void MaoscBufferCopied(TInt aError, const TDesC8& aBuffer);
|
|
||||||
void MaoscPlayComplete(TInt aError);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void Request();
|
|
||||||
void SetCapsL();
|
|
||||||
|
|
||||||
private:
|
|
||||||
MStreamProvider& iProvider;
|
|
||||||
MStreamObs& iObs;
|
|
||||||
TInt iVolume;
|
|
||||||
|
|
||||||
CMdaAudioOutputStream* iStream;
|
|
||||||
|
|
||||||
TInt iRate;
|
|
||||||
TInt iChannels;
|
|
||||||
TUint32 iType;
|
|
||||||
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
ENone = 0,
|
|
||||||
EInited = 0x1,
|
|
||||||
EStarted = 0x2,
|
|
||||||
EStopped = 0x4,
|
|
||||||
EVolumeChange = 0x8,
|
|
||||||
EDied = 0x10
|
|
||||||
};
|
|
||||||
|
|
||||||
TInt iState;
|
|
||||||
TBuf8<KSilenceBuffer> iSilence;
|
|
||||||
TPtrC8 iPtr;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
@ -1,547 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Carsten Griwodz
|
|
||||||
griff@kom.tu-darmstadt.de
|
|
||||||
|
|
||||||
based on linux/SDL_dspaudio.c by Sam Lantinga
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/time.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/mman.h>
|
|
||||||
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "../SDL_audiodev_c.h"
|
|
||||||
#include "SDL_umsaudio.h"
|
|
||||||
|
|
||||||
/* The tag name used by UMS audio */
|
|
||||||
#define UMS_DRIVER_NAME "ums"
|
|
||||||
|
|
||||||
#define DEBUG_AUDIO 1
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int UMS_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void UMS_PlayAudio(_THIS);
|
|
||||||
static Uint8 *UMS_GetAudioBuf(_THIS);
|
|
||||||
static void UMS_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADOpen(_THIS, string device, string mode, long flags);
|
|
||||||
static UMSAudioDevice_ReturnCode UADClose(_THIS);
|
|
||||||
static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long* bits);
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits);
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate, long* set_rate);
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order);
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt);
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt);
|
|
||||||
static UMSAudioDevice_ReturnCode UADInitialize(_THIS);
|
|
||||||
static UMSAudioDevice_ReturnCode UADStart(_THIS);
|
|
||||||
static UMSAudioDevice_ReturnCode UADStop(_THIS);
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt );
|
|
||||||
static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long* buff_size );
|
|
||||||
static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long* buff_size );
|
|
||||||
static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long* buff_size );
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes, long* bytes_ret );
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume );
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance );
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels );
|
|
||||||
static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block );
|
|
||||||
static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output, long* left_gain, long* right_gain);
|
|
||||||
static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer* buff, long samples, long* samples_written);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(_THIS)
|
|
||||||
{
|
|
||||||
if(this->hidden->playbuf._buffer) SDL_free(this->hidden->playbuf._buffer);
|
|
||||||
if(this->hidden->fillbuf._buffer) SDL_free(this->hidden->fillbuf._buffer);
|
|
||||||
_somFree( this->hidden->umsdev );
|
|
||||||
SDL_free(this->hidden);
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Allocate and initialize management storage and private management
|
|
||||||
* storage for this SDL-using library.
|
|
||||||
*/
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Creating UMS Audio device\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calls for UMS env initialization and audio object construction.
|
|
||||||
*/
|
|
||||||
this->hidden->ev = somGetGlobalEnvironment();
|
|
||||||
this->hidden->umsdev = UMSAudioDeviceNew();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Set the function pointers.
|
|
||||||
*/
|
|
||||||
this->OpenAudio = UMS_OpenAudio;
|
|
||||||
this->WaitAudio = NULL; /* we do blocking output */
|
|
||||||
this->PlayAudio = UMS_PlayAudio;
|
|
||||||
this->GetAudioBuf = UMS_GetAudioBuf;
|
|
||||||
this->CloseAudio = UMS_CloseAudio;
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "done\n");
|
|
||||||
#endif
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap UMS_bootstrap = {
|
|
||||||
UMS_DRIVER_NAME, "AIX UMS audio",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
static Uint8 *UMS_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "enter UMS_GetAudioBuf\n");
|
|
||||||
#endif
|
|
||||||
return this->hidden->fillbuf._buffer;
|
|
||||||
/*
|
|
||||||
long bufSize;
|
|
||||||
UMSAudioDevice_ReturnCode rc;
|
|
||||||
|
|
||||||
rc = UADSetTimeFormat(this, UMSAudioTypes_Bytes );
|
|
||||||
rc = UADWriteBuffSize(this, bufSize );
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UMS_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
UMSAudioDevice_ReturnCode rc;
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "enter UMS_CloseAudio\n");
|
|
||||||
#endif
|
|
||||||
rc = UADPlayRemainingData(this, TRUE);
|
|
||||||
rc = UADStop(this);
|
|
||||||
rc = UADClose(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void UMS_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
UMSAudioDevice_ReturnCode rc;
|
|
||||||
long samplesToWrite;
|
|
||||||
long samplesWritten;
|
|
||||||
UMSAudioTypes_Buffer swpbuf;
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "enter UMS_PlayAudio\n");
|
|
||||||
#endif
|
|
||||||
samplesToWrite = this->hidden->playbuf._length/this->hidden->bytesPerSample;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
rc = UADWrite(this, &this->hidden->playbuf,
|
|
||||||
samplesToWrite,
|
|
||||||
&samplesWritten );
|
|
||||||
samplesToWrite -= samplesWritten;
|
|
||||||
|
|
||||||
/* rc values: UMSAudioDevice_Success
|
|
||||||
* UMSAudioDevice_Failure
|
|
||||||
* UMSAudioDevice_Preempted
|
|
||||||
* UMSAudioDevice_Interrupted
|
|
||||||
* UMSAudioDevice_DeviceError
|
|
||||||
*/
|
|
||||||
if ( rc == UMSAudioDevice_DeviceError ) {
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Returning from PlayAudio with devices error\n");
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while(samplesToWrite>0);
|
|
||||||
|
|
||||||
SDL_LockAudio();
|
|
||||||
SDL_memcpy( &swpbuf, &this->hidden->playbuf, sizeof(UMSAudioTypes_Buffer) );
|
|
||||||
SDL_memcpy( &this->hidden->playbuf, &this->hidden->fillbuf, sizeof(UMSAudioTypes_Buffer) );
|
|
||||||
SDL_memcpy( &this->hidden->fillbuf, &swpbuf, sizeof(UMSAudioTypes_Buffer) );
|
|
||||||
SDL_UnlockAudio();
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Wrote audio data and swapped buffer\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
// /* Set the DSP frequency */
|
|
||||||
// value = spec->freq;
|
|
||||||
// if ( ioctl(this->hidden->audio_fd, SOUND_PCM_WRITE_RATE, &value) < 0 ) {
|
|
||||||
// SDL_SetError("Couldn't set audio frequency");
|
|
||||||
// return(-1);
|
|
||||||
// }
|
|
||||||
// spec->freq = value;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int UMS_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
char* audiodev = "/dev/paud0";
|
|
||||||
long lgain;
|
|
||||||
long rgain;
|
|
||||||
long outRate;
|
|
||||||
long outBufSize;
|
|
||||||
long bitsPerSample;
|
|
||||||
long samplesPerSec;
|
|
||||||
long success;
|
|
||||||
Uint16 test_format;
|
|
||||||
int frag_spec;
|
|
||||||
UMSAudioDevice_ReturnCode rc;
|
|
||||||
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "enter UMS_OpenAudio\n");
|
|
||||||
#endif
|
|
||||||
rc = UADOpen(this, audiodev,"PLAY", UMSAudioDevice_BlockingIO);
|
|
||||||
if ( rc != UMSAudioDevice_Success ) {
|
|
||||||
SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = UADSetAudioFormatType(this, "PCM");
|
|
||||||
|
|
||||||
success = 0;
|
|
||||||
test_format = SDL_FirstAudioFormat(spec->format);
|
|
||||||
do
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_AUDIO
|
|
||||||
fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
|
|
||||||
#endif
|
|
||||||
switch ( test_format )
|
|
||||||
{
|
|
||||||
case AUDIO_U8:
|
|
||||||
/* from the mac code: better ? */
|
|
||||||
/* sample_bits = spec->size / spec->samples / spec->channels * 8; */
|
|
||||||
success = 1;
|
|
||||||
bitsPerSample = 8;
|
|
||||||
rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
|
|
||||||
rc = UADSetByteOrder(this, "MSB"); /* irrelevant */
|
|
||||||
rc = UADSetNumberFormat(this, "UNSIGNED");
|
|
||||||
break;
|
|
||||||
case AUDIO_S8:
|
|
||||||
success = 1;
|
|
||||||
bitsPerSample = 8;
|
|
||||||
rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
|
|
||||||
rc = UADSetByteOrder(this, "MSB"); /* irrelevant */
|
|
||||||
rc = UADSetNumberFormat(this, "SIGNED");
|
|
||||||
break;
|
|
||||||
case AUDIO_S16LSB:
|
|
||||||
success = 1;
|
|
||||||
bitsPerSample = 16;
|
|
||||||
rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
|
|
||||||
rc = UADSetByteOrder(this, "LSB");
|
|
||||||
rc = UADSetNumberFormat(this, "SIGNED");
|
|
||||||
break;
|
|
||||||
case AUDIO_S16MSB:
|
|
||||||
success = 1;
|
|
||||||
bitsPerSample = 16;
|
|
||||||
rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
|
|
||||||
rc = UADSetByteOrder(this, "MSB");
|
|
||||||
rc = UADSetNumberFormat(this, "SIGNED");
|
|
||||||
break;
|
|
||||||
case AUDIO_U16LSB:
|
|
||||||
success = 1;
|
|
||||||
bitsPerSample = 16;
|
|
||||||
rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
|
|
||||||
rc = UADSetByteOrder(this, "LSB");
|
|
||||||
rc = UADSetNumberFormat(this, "UNSIGNED");
|
|
||||||
break;
|
|
||||||
case AUDIO_U16MSB:
|
|
||||||
success = 1;
|
|
||||||
bitsPerSample = 16;
|
|
||||||
rc = UADSetSampleRate(this, spec->freq << 16, &outRate );
|
|
||||||
rc = UADSetByteOrder(this, "MSB");
|
|
||||||
rc = UADSetNumberFormat(this, "UNSIGNED");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( ! success ) {
|
|
||||||
test_format = SDL_NextAudioFormat();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while ( ! success && test_format );
|
|
||||||
|
|
||||||
if ( success == 0 ) {
|
|
||||||
SDL_SetError("Couldn't find any hardware audio formats");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
spec->format = test_format;
|
|
||||||
|
|
||||||
for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
|
|
||||||
if ( (0x01<<frag_spec) != spec->size ) {
|
|
||||||
SDL_SetError("Fragment size must be a power of two");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if ( frag_spec > 2048 ) frag_spec = 2048;
|
|
||||||
|
|
||||||
this->hidden->bytesPerSample = (bitsPerSample / 8) * spec->channels;
|
|
||||||
samplesPerSec = this->hidden->bytesPerSample * outRate;
|
|
||||||
|
|
||||||
this->hidden->playbuf._length = 0;
|
|
||||||
this->hidden->playbuf._maximum = spec->size;
|
|
||||||
this->hidden->playbuf._buffer = (unsigned char*)SDL_malloc(spec->size);
|
|
||||||
this->hidden->fillbuf._length = 0;
|
|
||||||
this->hidden->fillbuf._maximum = spec->size;
|
|
||||||
this->hidden->fillbuf._buffer = (unsigned char*)SDL_malloc(spec->size);
|
|
||||||
|
|
||||||
rc = UADSetBitsPerSample(this, bitsPerSample );
|
|
||||||
rc = UADSetDMABufferSize(this, frag_spec, &outBufSize );
|
|
||||||
rc = UADSetChannels(this, spec->channels); /* functions reduces to mono or stereo */
|
|
||||||
|
|
||||||
lgain = 100; /*maximum left input gain*/
|
|
||||||
rgain = 100; /*maimum right input gain*/
|
|
||||||
rc = UADEnableOutput(this, "LINE_OUT",&lgain,&rgain);
|
|
||||||
rc = UADInitialize(this);
|
|
||||||
rc = UADStart(this);
|
|
||||||
rc = UADSetVolume(this, 100);
|
|
||||||
rc = UADSetBalance(this, 0);
|
|
||||||
|
|
||||||
/* We're ready to rock and roll. :-) */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADGetBitsPerSample(_THIS, long* bits)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_get_bits_per_sample( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
bits );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetBitsPerSample(_THIS, long bits)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_set_bits_per_sample( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
bits );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetSampleRate(_THIS, long rate, long* set_rate)
|
|
||||||
{
|
|
||||||
/* from the mac code: sample rate = spec->freq << 16; */
|
|
||||||
return UMSAudioDevice_set_sample_rate( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
rate,
|
|
||||||
set_rate );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetByteOrder(_THIS, string byte_order)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_set_byte_order( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
byte_order );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetAudioFormatType(_THIS, string fmt)
|
|
||||||
{
|
|
||||||
/* possible PCM, A_LAW or MU_LAW */
|
|
||||||
return UMSAudioDevice_set_audio_format_type( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
fmt );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetNumberFormat(_THIS, string fmt)
|
|
||||||
{
|
|
||||||
/* possible SIGNED, UNSIGNED, or TWOS_COMPLEMENT */
|
|
||||||
return UMSAudioDevice_set_number_format( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
fmt );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADInitialize(_THIS)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_initialize( this->hidden->umsdev,
|
|
||||||
this->hidden->ev );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADStart(_THIS)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_start( this->hidden->umsdev,
|
|
||||||
this->hidden->ev );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetTimeFormat(_THIS, UMSAudioTypes_TimeFormat fmt )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Switches the time format to the new format, immediately.
|
|
||||||
* possible UMSAudioTypes_Msecs, UMSAudioTypes_Bytes or UMSAudioTypes_Samples
|
|
||||||
*/
|
|
||||||
return UMSAudioDevice_set_time_format( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
fmt );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADWriteBuffSize(_THIS, long* buff_size )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* returns write buffer size in the current time format
|
|
||||||
*/
|
|
||||||
return UMSAudioDevice_write_buff_size( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
buff_size );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADWriteBuffRemain(_THIS, long* buff_size )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* returns amount of available space in the write buffer
|
|
||||||
* in the current time format
|
|
||||||
*/
|
|
||||||
return UMSAudioDevice_write_buff_remain( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
buff_size );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADWriteBuffUsed(_THIS, long* buff_size )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* returns amount of filled space in the write buffer
|
|
||||||
* in the current time format
|
|
||||||
*/
|
|
||||||
return UMSAudioDevice_write_buff_used( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
buff_size );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetDMABufferSize(_THIS, long bytes, long* bytes_ret )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Request a new DMA buffer size, maximum requested size 2048.
|
|
||||||
* Takes effect with next initialize() call.
|
|
||||||
* Devices may or may not support DMA.
|
|
||||||
*/
|
|
||||||
return UMSAudioDevice_set_DMA_buffer_size( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
bytes,
|
|
||||||
bytes_ret );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetVolume(_THIS, long volume )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Set the volume.
|
|
||||||
* Takes effect immediately.
|
|
||||||
*/
|
|
||||||
return UMSAudioDevice_set_volume( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
volume );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetBalance(_THIS, long balance )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Set the balance.
|
|
||||||
* Takes effect immediately.
|
|
||||||
*/
|
|
||||||
return UMSAudioDevice_set_balance( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
balance );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADSetChannels(_THIS, long channels )
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Set mono or stereo.
|
|
||||||
* Takes effect with next initialize() call.
|
|
||||||
*/
|
|
||||||
if ( channels != 1 ) channels = 2;
|
|
||||||
return UMSAudioDevice_set_number_of_channels( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
channels );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADOpen(_THIS, string device, string mode, long flags)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_open( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
device,
|
|
||||||
mode,
|
|
||||||
flags );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADWrite(_THIS, UMSAudioTypes_Buffer* buff,
|
|
||||||
long samples,
|
|
||||||
long* samples_written)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_write( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
buff,
|
|
||||||
samples,
|
|
||||||
samples_written );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADPlayRemainingData(_THIS, boolean block )
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_play_remaining_data( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
block);
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADStop(_THIS)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_stop( this->hidden->umsdev,
|
|
||||||
this->hidden->ev );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADClose(_THIS)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_close( this->hidden->umsdev,
|
|
||||||
this->hidden->ev );
|
|
||||||
}
|
|
||||||
|
|
||||||
static UMSAudioDevice_ReturnCode UADEnableOutput(_THIS, string output, long* left_gain, long* right_gain)
|
|
||||||
{
|
|
||||||
return UMSAudioDevice_enable_output( this->hidden->umsdev,
|
|
||||||
this->hidden->ev,
|
|
||||||
output,
|
|
||||||
left_gain,
|
|
||||||
right_gain );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Carsten Griwodz
|
|
||||||
griff@kom.tu-darmstadt.de
|
|
||||||
|
|
||||||
based on linux/SDL_dspaudio.h by Sam Lantinga
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_UMSaudio_h
|
|
||||||
#define _SDL_UMSaudio_h
|
|
||||||
|
|
||||||
#include <UMS/UMSAudioDevice.h>
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData
|
|
||||||
{
|
|
||||||
/* Pointer to the (open) UMS audio device */
|
|
||||||
Environment* ev;
|
|
||||||
UMSAudioDevice umsdev;
|
|
||||||
|
|
||||||
/* Raw mixing buffer */
|
|
||||||
UMSAudioTypes_Buffer playbuf;
|
|
||||||
UMSAudioTypes_Buffer fillbuf;
|
|
||||||
|
|
||||||
long bytesPerSample;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* _SDL_UMSaudio_h */
|
|
||||||
|
|
||||||
|
|
@ -1,322 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#include <mmsystem.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "SDL_dibaudio.h"
|
|
||||||
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
|
|
||||||
#include "win_ce_semaphore.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void DIB_ThreadInit(_THIS);
|
|
||||||
static void DIB_WaitAudio(_THIS);
|
|
||||||
static Uint8 *DIB_GetAudioBuf(_THIS);
|
|
||||||
static void DIB_PlayAudio(_THIS);
|
|
||||||
static void DIB_WaitDone(_THIS);
|
|
||||||
static void DIB_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = DIB_OpenAudio;
|
|
||||||
this->ThreadInit = DIB_ThreadInit;
|
|
||||||
this->WaitAudio = DIB_WaitAudio;
|
|
||||||
this->PlayAudio = DIB_PlayAudio;
|
|
||||||
this->GetAudioBuf = DIB_GetAudioBuf;
|
|
||||||
this->WaitDone = DIB_WaitDone;
|
|
||||||
this->CloseAudio = DIB_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap WAVEOUT_bootstrap = {
|
|
||||||
"waveout", "Win95/98/NT/2000 WaveOut",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* The Win32 callback for filling the WAVE device */
|
|
||||||
static void CALLBACK FillSound(HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
|
|
||||||
DWORD dwParam1, DWORD dwParam2)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this = (SDL_AudioDevice *)dwInstance;
|
|
||||||
|
|
||||||
/* Only service "buffer done playing" messages */
|
|
||||||
if ( uMsg != WOM_DONE )
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Signal that we are done playing a buffer */
|
|
||||||
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
|
|
||||||
ReleaseSemaphoreCE(audio_sem, 1, NULL);
|
|
||||||
#else
|
|
||||||
ReleaseSemaphore(audio_sem, 1, NULL);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SetMMerror(char *function, MMRESULT code)
|
|
||||||
{
|
|
||||||
size_t len;
|
|
||||||
char errbuf[MAXERRORLENGTH];
|
|
||||||
#ifdef _WIN32_WCE
|
|
||||||
wchar_t werrbuf[MAXERRORLENGTH];
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
|
|
||||||
len = SDL_strlen(errbuf);
|
|
||||||
|
|
||||||
#ifdef _WIN32_WCE
|
|
||||||
/* UNICODE version */
|
|
||||||
waveOutGetErrorText(code, werrbuf, MAXERRORLENGTH-len);
|
|
||||||
WideCharToMultiByte(CP_ACP,0,werrbuf,-1,errbuf+len,MAXERRORLENGTH-len,NULL,NULL);
|
|
||||||
#else
|
|
||||||
waveOutGetErrorText(code, errbuf+len, (UINT)(MAXERRORLENGTH-len));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SDL_SetError("%s",errbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set high priority for the audio thread */
|
|
||||||
static void DIB_ThreadInit(_THIS)
|
|
||||||
{
|
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DIB_WaitAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Wait for an audio chunk to finish */
|
|
||||||
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
|
|
||||||
WaitForSemaphoreCE(audio_sem, INFINITE);
|
|
||||||
#else
|
|
||||||
WaitForSingleObject(audio_sem, INFINITE);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint8 *DIB_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
Uint8 *retval;
|
|
||||||
|
|
||||||
retval = (Uint8 *)(wavebuf[next_buffer].lpData);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DIB_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Queue it up */
|
|
||||||
waveOutWrite(sound, &wavebuf[next_buffer], sizeof(wavebuf[0]));
|
|
||||||
next_buffer = (next_buffer+1)%NUM_BUFFERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DIB_WaitDone(_THIS)
|
|
||||||
{
|
|
||||||
int i, left;
|
|
||||||
|
|
||||||
do {
|
|
||||||
left = NUM_BUFFERS;
|
|
||||||
for ( i=0; i<NUM_BUFFERS; ++i ) {
|
|
||||||
if ( wavebuf[i].dwFlags & WHDR_DONE ) {
|
|
||||||
--left;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( left > 0 ) {
|
|
||||||
SDL_Delay(100);
|
|
||||||
}
|
|
||||||
} while ( left > 0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
void DIB_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* Close up audio */
|
|
||||||
if ( audio_sem ) {
|
|
||||||
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
|
|
||||||
CloseSynchHandle(audio_sem);
|
|
||||||
#else
|
|
||||||
CloseHandle(audio_sem);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if ( sound ) {
|
|
||||||
waveOutClose(sound);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clean up mixing buffers */
|
|
||||||
for ( i=0; i<NUM_BUFFERS; ++i ) {
|
|
||||||
if ( wavebuf[i].dwUser != 0xFFFF ) {
|
|
||||||
waveOutUnprepareHeader(sound, &wavebuf[i],
|
|
||||||
sizeof(wavebuf[i]));
|
|
||||||
wavebuf[i].dwUser = 0xFFFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Free raw mixing buffer */
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
SDL_free(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int DIB_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
MMRESULT result;
|
|
||||||
int i;
|
|
||||||
WAVEFORMATEX waveformat;
|
|
||||||
|
|
||||||
/* Initialize the wavebuf structures for closing */
|
|
||||||
sound = NULL;
|
|
||||||
audio_sem = NULL;
|
|
||||||
for ( i = 0; i < NUM_BUFFERS; ++i )
|
|
||||||
wavebuf[i].dwUser = 0xFFFF;
|
|
||||||
mixbuf = NULL;
|
|
||||||
|
|
||||||
/* Set basic WAVE format parameters */
|
|
||||||
SDL_memset(&waveformat, 0, sizeof(waveformat));
|
|
||||||
waveformat.wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
|
|
||||||
/* Determine the audio parameters from the AudioSpec */
|
|
||||||
switch ( spec->format & 0xFF ) {
|
|
||||||
case 8:
|
|
||||||
/* Unsigned 8 bit audio data */
|
|
||||||
spec->format = AUDIO_U8;
|
|
||||||
waveformat.wBitsPerSample = 8;
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
/* Signed 16 bit audio data */
|
|
||||||
spec->format = AUDIO_S16;
|
|
||||||
waveformat.wBitsPerSample = 16;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
waveformat.nChannels = spec->channels;
|
|
||||||
waveformat.nSamplesPerSec = spec->freq;
|
|
||||||
waveformat.nBlockAlign =
|
|
||||||
waveformat.nChannels * (waveformat.wBitsPerSample/8);
|
|
||||||
waveformat.nAvgBytesPerSec =
|
|
||||||
waveformat.nSamplesPerSec * waveformat.nBlockAlign;
|
|
||||||
|
|
||||||
/* Check the buffer size -- minimum of 1/4 second (word aligned) */
|
|
||||||
if ( spec->samples < (spec->freq/4) )
|
|
||||||
spec->samples = ((spec->freq/4)+3)&~3;
|
|
||||||
|
|
||||||
/* Update the fragment size as size in bytes */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
result = waveOutOpen(&sound, WAVE_MAPPER, &waveformat,
|
|
||||||
(DWORD_PTR)FillSound, (DWORD_PTR)this, CALLBACK_FUNCTION);
|
|
||||||
if ( result != MMSYSERR_NOERROR ) {
|
|
||||||
SetMMerror("waveOutOpen()", result);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SOUND_DEBUG
|
|
||||||
/* Check the sound device we retrieved */
|
|
||||||
{
|
|
||||||
WAVEOUTCAPS caps;
|
|
||||||
|
|
||||||
result = waveOutGetDevCaps((UINT)sound, &caps, sizeof(caps));
|
|
||||||
if ( result != MMSYSERR_NOERROR ) {
|
|
||||||
SetMMerror("waveOutGetDevCaps()", result);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
printf("Audio device: %s\n", caps.szPname);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Create the audio buffer semaphore */
|
|
||||||
#if defined(_WIN32_WCE) && (_WIN32_WCE < 300)
|
|
||||||
audio_sem = CreateSemaphoreCE(NULL, NUM_BUFFERS-1, NUM_BUFFERS, NULL);
|
|
||||||
#else
|
|
||||||
audio_sem = CreateSemaphore(NULL, NUM_BUFFERS-1, NUM_BUFFERS, NULL);
|
|
||||||
#endif
|
|
||||||
if ( audio_sem == NULL ) {
|
|
||||||
SDL_SetError("Couldn't create semaphore");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the sound buffers */
|
|
||||||
mixbuf = (Uint8 *)SDL_malloc(NUM_BUFFERS*spec->size);
|
|
||||||
if ( mixbuf == NULL ) {
|
|
||||||
SDL_SetError("Out of memory");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
for ( i = 0; i < NUM_BUFFERS; ++i ) {
|
|
||||||
SDL_memset(&wavebuf[i], 0, sizeof(wavebuf[i]));
|
|
||||||
wavebuf[i].lpData = (LPSTR) &mixbuf[i*spec->size];
|
|
||||||
wavebuf[i].dwBufferLength = spec->size;
|
|
||||||
wavebuf[i].dwFlags = WHDR_DONE;
|
|
||||||
result = waveOutPrepareHeader(sound, &wavebuf[i],
|
|
||||||
sizeof(wavebuf[i]));
|
|
||||||
if ( result != MMSYSERR_NOERROR ) {
|
|
||||||
SetMMerror("waveOutPrepareHeader()", result);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ready to go! */
|
|
||||||
next_buffer = 0;
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_lowaudio_h
|
|
||||||
#define _SDL_lowaudio_h
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
#define NUM_BUFFERS 2 /* -- Don't lower this! */
|
|
||||||
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
HWAVEOUT sound;
|
|
||||||
HANDLE audio_sem;
|
|
||||||
Uint8 *mixbuf; /* The raw allocated mixing buffer */
|
|
||||||
WAVEHDR wavebuf[NUM_BUFFERS]; /* Wave audio fragments */
|
|
||||||
int next_buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define sound (this->hidden->sound)
|
|
||||||
#define audio_sem (this->hidden->audio_sem)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define wavebuf (this->hidden->wavebuf)
|
|
||||||
#define next_buffer (this->hidden->next_buffer)
|
|
||||||
|
|
||||||
#endif /* _SDL_lowaudio_h */
|
|
||||||
|
|
@ -1,705 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* Allow access to a raw mixing buffer */
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_audio.h"
|
|
||||||
#include "../SDL_audio_c.h"
|
|
||||||
#include "SDL_dx5audio.h"
|
|
||||||
|
|
||||||
/* Define this if you want to use DirectX 6 DirectSoundNotify interface */
|
|
||||||
//#define USE_POSITION_NOTIFY
|
|
||||||
|
|
||||||
/* DirectX function pointers for audio */
|
|
||||||
HRESULT (WINAPI *DSoundCreate)(LPGUID, LPDIRECTSOUND *, LPUNKNOWN);
|
|
||||||
|
|
||||||
/* Audio driver functions */
|
|
||||||
static int DX5_OpenAudio(_THIS, SDL_AudioSpec *spec);
|
|
||||||
static void DX5_ThreadInit(_THIS);
|
|
||||||
static void DX5_WaitAudio_BusyWait(_THIS);
|
|
||||||
#ifdef USE_POSITION_NOTIFY
|
|
||||||
static void DX6_WaitAudio_EventWait(_THIS);
|
|
||||||
#endif
|
|
||||||
static void DX5_PlayAudio(_THIS);
|
|
||||||
static Uint8 *DX5_GetAudioBuf(_THIS);
|
|
||||||
static void DX5_WaitDone(_THIS);
|
|
||||||
static void DX5_CloseAudio(_THIS);
|
|
||||||
|
|
||||||
/* Audio driver bootstrap functions */
|
|
||||||
|
|
||||||
static int Audio_Available(void)
|
|
||||||
{
|
|
||||||
HINSTANCE DSoundDLL;
|
|
||||||
int dsound_ok;
|
|
||||||
|
|
||||||
/* Version check DSOUND.DLL (Is DirectX okay?) */
|
|
||||||
dsound_ok = 0;
|
|
||||||
DSoundDLL = LoadLibrary(TEXT("DSOUND.DLL"));
|
|
||||||
if ( DSoundDLL != NULL ) {
|
|
||||||
/* We just use basic DirectSound, we're okay */
|
|
||||||
/* Yay! */
|
|
||||||
/* Unfortunately, the sound drivers on NT have
|
|
||||||
higher latencies than the audio buffers used
|
|
||||||
by many SDL applications, so there are gaps
|
|
||||||
in the audio - it sounds terrible. Punt for now.
|
|
||||||
*/
|
|
||||||
OSVERSIONINFO ver;
|
|
||||||
ver.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
|
|
||||||
GetVersionEx(&ver);
|
|
||||||
switch (ver.dwPlatformId) {
|
|
||||||
case VER_PLATFORM_WIN32_NT:
|
|
||||||
if ( ver.dwMajorVersion > 4 ) {
|
|
||||||
/* Win2K */
|
|
||||||
dsound_ok = 1;
|
|
||||||
} else {
|
|
||||||
/* WinNT */
|
|
||||||
dsound_ok = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Win95 or Win98 */
|
|
||||||
dsound_ok = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
/* Now check for DirectX 5 or better - otherwise
|
|
||||||
* we will fail later in DX5_OpenAudio without a chance
|
|
||||||
* to fall back to the DIB driver. */
|
|
||||||
if (dsound_ok) {
|
|
||||||
/* DirectSoundCaptureCreate was added in DX5 */
|
|
||||||
if (!GetProcAddress(DSoundDLL, TEXT("DirectSoundCaptureCreate")))
|
|
||||||
dsound_ok = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
/* Clean up.. */
|
|
||||||
FreeLibrary(DSoundDLL);
|
|
||||||
}
|
|
||||||
return(dsound_ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Functions for loading the DirectX functions dynamically */
|
|
||||||
static HINSTANCE DSoundDLL = NULL;
|
|
||||||
|
|
||||||
static void DX5_Unload(void)
|
|
||||||
{
|
|
||||||
if ( DSoundDLL != NULL ) {
|
|
||||||
FreeLibrary(DSoundDLL);
|
|
||||||
DSoundCreate = NULL;
|
|
||||||
DSoundDLL = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static int DX5_Load(void)
|
|
||||||
{
|
|
||||||
int status;
|
|
||||||
|
|
||||||
DX5_Unload();
|
|
||||||
DSoundDLL = LoadLibrary(TEXT("DSOUND.DLL"));
|
|
||||||
if ( DSoundDLL != NULL ) {
|
|
||||||
DSoundCreate = (void *)GetProcAddress(DSoundDLL,
|
|
||||||
TEXT("DirectSoundCreate"));
|
|
||||||
}
|
|
||||||
if ( DSoundDLL && DSoundCreate ) {
|
|
||||||
status = 0;
|
|
||||||
} else {
|
|
||||||
DX5_Unload();
|
|
||||||
status = -1;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Audio_DeleteDevice(SDL_AudioDevice *device)
|
|
||||||
{
|
|
||||||
DX5_Unload();
|
|
||||||
SDL_free(device->hidden);
|
|
||||||
SDL_free(device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
|
|
||||||
{
|
|
||||||
SDL_AudioDevice *this;
|
|
||||||
|
|
||||||
/* Load DirectX */
|
|
||||||
if ( DX5_Load() < 0 ) {
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize all variables that we clean on shutdown */
|
|
||||||
this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
|
|
||||||
if ( this ) {
|
|
||||||
SDL_memset(this, 0, (sizeof *this));
|
|
||||||
this->hidden = (struct SDL_PrivateAudioData *)
|
|
||||||
SDL_malloc((sizeof *this->hidden));
|
|
||||||
}
|
|
||||||
if ( (this == NULL) || (this->hidden == NULL) ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
if ( this ) {
|
|
||||||
SDL_free(this);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
SDL_memset(this->hidden, 0, (sizeof *this->hidden));
|
|
||||||
|
|
||||||
/* Set the function pointers */
|
|
||||||
this->OpenAudio = DX5_OpenAudio;
|
|
||||||
this->ThreadInit = DX5_ThreadInit;
|
|
||||||
this->WaitAudio = DX5_WaitAudio_BusyWait;
|
|
||||||
this->PlayAudio = DX5_PlayAudio;
|
|
||||||
this->GetAudioBuf = DX5_GetAudioBuf;
|
|
||||||
this->WaitDone = DX5_WaitDone;
|
|
||||||
this->CloseAudio = DX5_CloseAudio;
|
|
||||||
|
|
||||||
this->free = Audio_DeleteDevice;
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioBootStrap DSOUND_bootstrap = {
|
|
||||||
"dsound", "Win95/98/2000 DirectSound",
|
|
||||||
Audio_Available, Audio_CreateDevice
|
|
||||||
};
|
|
||||||
|
|
||||||
static void SetDSerror(const char *function, int code)
|
|
||||||
{
|
|
||||||
static const char *error;
|
|
||||||
static char errbuf[1024];
|
|
||||||
|
|
||||||
errbuf[0] = 0;
|
|
||||||
switch (code) {
|
|
||||||
case E_NOINTERFACE:
|
|
||||||
error =
|
|
||||||
"Unsupported interface\n-- Is DirectX 5.0 or later installed?";
|
|
||||||
break;
|
|
||||||
case DSERR_ALLOCATED:
|
|
||||||
error = "Audio device in use";
|
|
||||||
break;
|
|
||||||
case DSERR_BADFORMAT:
|
|
||||||
error = "Unsupported audio format";
|
|
||||||
break;
|
|
||||||
case DSERR_BUFFERLOST:
|
|
||||||
error = "Mixing buffer was lost";
|
|
||||||
break;
|
|
||||||
case DSERR_CONTROLUNAVAIL:
|
|
||||||
error = "Control requested is not available";
|
|
||||||
break;
|
|
||||||
case DSERR_INVALIDCALL:
|
|
||||||
error = "Invalid call for the current state";
|
|
||||||
break;
|
|
||||||
case DSERR_INVALIDPARAM:
|
|
||||||
error = "Invalid parameter";
|
|
||||||
break;
|
|
||||||
case DSERR_NODRIVER:
|
|
||||||
error = "No audio device found";
|
|
||||||
break;
|
|
||||||
case DSERR_OUTOFMEMORY:
|
|
||||||
error = "Out of memory";
|
|
||||||
break;
|
|
||||||
case DSERR_PRIOLEVELNEEDED:
|
|
||||||
error = "Caller doesn't have priority";
|
|
||||||
break;
|
|
||||||
case DSERR_UNSUPPORTED:
|
|
||||||
error = "Function not supported";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SDL_snprintf(errbuf, SDL_arraysize(errbuf),
|
|
||||||
"%s: Unknown DirectSound error: 0x%x",
|
|
||||||
function, code);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( ! errbuf[0] ) {
|
|
||||||
SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: %s", function, error);
|
|
||||||
}
|
|
||||||
SDL_SetError("%s", errbuf);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DirectSound needs to be associated with a window */
|
|
||||||
static HWND mainwin = NULL;
|
|
||||||
/* */
|
|
||||||
void DX5_SoundFocus(HWND hwnd)
|
|
||||||
{
|
|
||||||
mainwin = hwnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DX5_ThreadInit(_THIS)
|
|
||||||
{
|
|
||||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DX5_WaitAudio_BusyWait(_THIS)
|
|
||||||
{
|
|
||||||
DWORD status;
|
|
||||||
DWORD cursor, junk;
|
|
||||||
HRESULT result;
|
|
||||||
|
|
||||||
/* Semi-busy wait, since we have no way of getting play notification
|
|
||||||
on a primary mixing buffer located in hardware (DirectX 5.0)
|
|
||||||
*/
|
|
||||||
result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, &junk, &cursor);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
if ( result == DSERR_BUFFERLOST ) {
|
|
||||||
IDirectSoundBuffer_Restore(mixbuf);
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ( (cursor/mixlen) == lastchunk ) {
|
|
||||||
/* FIXME: find out how much time is left and sleep that long */
|
|
||||||
SDL_Delay(1);
|
|
||||||
|
|
||||||
/* Try to restore a lost sound buffer */
|
|
||||||
IDirectSoundBuffer_GetStatus(mixbuf, &status);
|
|
||||||
if ( (status&DSBSTATUS_BUFFERLOST) ) {
|
|
||||||
IDirectSoundBuffer_Restore(mixbuf);
|
|
||||||
IDirectSoundBuffer_GetStatus(mixbuf, &status);
|
|
||||||
if ( (status&DSBSTATUS_BUFFERLOST) ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( ! (status&DSBSTATUS_PLAYING) ) {
|
|
||||||
result = IDirectSoundBuffer_Play(mixbuf, 0, 0, DSBPLAY_LOOPING);
|
|
||||||
if ( result == DS_OK ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
SetDSerror("DirectSound Play", result);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find out where we are playing */
|
|
||||||
result = IDirectSoundBuffer_GetCurrentPosition(mixbuf,
|
|
||||||
&junk, &cursor);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_POSITION_NOTIFY
|
|
||||||
static void DX6_WaitAudio_EventWait(_THIS)
|
|
||||||
{
|
|
||||||
DWORD status;
|
|
||||||
HRESULT result;
|
|
||||||
|
|
||||||
/* Try to restore a lost sound buffer */
|
|
||||||
IDirectSoundBuffer_GetStatus(mixbuf, &status);
|
|
||||||
if ( (status&DSBSTATUS_BUFFERLOST) ) {
|
|
||||||
IDirectSoundBuffer_Restore(mixbuf);
|
|
||||||
IDirectSoundBuffer_GetStatus(mixbuf, &status);
|
|
||||||
if ( (status&DSBSTATUS_BUFFERLOST) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( ! (status&DSBSTATUS_PLAYING) ) {
|
|
||||||
result = IDirectSoundBuffer_Play(mixbuf, 0, 0, DSBPLAY_LOOPING);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
SetDSerror("DirectSound Play", result);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
WaitForSingleObject(audio_event, INFINITE);
|
|
||||||
}
|
|
||||||
#endif /* USE_POSITION_NOTIFY */
|
|
||||||
|
|
||||||
static void DX5_PlayAudio(_THIS)
|
|
||||||
{
|
|
||||||
/* Unlock the buffer, allowing it to play */
|
|
||||||
if ( locked_buf ) {
|
|
||||||
IDirectSoundBuffer_Unlock(mixbuf, locked_buf, mixlen, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static Uint8 *DX5_GetAudioBuf(_THIS)
|
|
||||||
{
|
|
||||||
DWORD cursor, junk;
|
|
||||||
HRESULT result;
|
|
||||||
DWORD rawlen;
|
|
||||||
|
|
||||||
/* Figure out which blocks to fill next */
|
|
||||||
locked_buf = NULL;
|
|
||||||
result = IDirectSoundBuffer_GetCurrentPosition(mixbuf, &junk, &cursor);
|
|
||||||
if ( result == DSERR_BUFFERLOST ) {
|
|
||||||
IDirectSoundBuffer_Restore(mixbuf);
|
|
||||||
result = IDirectSoundBuffer_GetCurrentPosition(mixbuf,
|
|
||||||
&junk, &cursor);
|
|
||||||
}
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
SetDSerror("DirectSound GetCurrentPosition", result);
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
cursor /= mixlen;
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
/* Detect audio dropouts */
|
|
||||||
{ DWORD spot = cursor;
|
|
||||||
if ( spot < lastchunk ) {
|
|
||||||
spot += NUM_BUFFERS;
|
|
||||||
}
|
|
||||||
if ( spot > lastchunk+1 ) {
|
|
||||||
fprintf(stderr, "Audio dropout, missed %d fragments\n",
|
|
||||||
(spot - (lastchunk+1)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
lastchunk = cursor;
|
|
||||||
cursor = (cursor+1)%NUM_BUFFERS;
|
|
||||||
cursor *= mixlen;
|
|
||||||
|
|
||||||
/* Lock the audio buffer */
|
|
||||||
result = IDirectSoundBuffer_Lock(mixbuf, cursor, mixlen,
|
|
||||||
(LPVOID *)&locked_buf, &rawlen, NULL, &junk, 0);
|
|
||||||
if ( result == DSERR_BUFFERLOST ) {
|
|
||||||
IDirectSoundBuffer_Restore(mixbuf);
|
|
||||||
result = IDirectSoundBuffer_Lock(mixbuf, cursor, mixlen,
|
|
||||||
(LPVOID *)&locked_buf, &rawlen, NULL, &junk, 0);
|
|
||||||
}
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
SetDSerror("DirectSound Lock", result);
|
|
||||||
return(NULL);
|
|
||||||
}
|
|
||||||
return(locked_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DX5_WaitDone(_THIS)
|
|
||||||
{
|
|
||||||
Uint8 *stream;
|
|
||||||
|
|
||||||
/* Wait for the playing chunk to finish */
|
|
||||||
stream = this->GetAudioBuf(this);
|
|
||||||
if ( stream != NULL ) {
|
|
||||||
SDL_memset(stream, silence, mixlen);
|
|
||||||
this->PlayAudio(this);
|
|
||||||
}
|
|
||||||
this->WaitAudio(this);
|
|
||||||
|
|
||||||
/* Stop the looping sound buffer */
|
|
||||||
IDirectSoundBuffer_Stop(mixbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void DX5_CloseAudio(_THIS)
|
|
||||||
{
|
|
||||||
if ( sound != NULL ) {
|
|
||||||
if ( mixbuf != NULL ) {
|
|
||||||
/* Clean up the audio buffer */
|
|
||||||
IDirectSoundBuffer_Release(mixbuf);
|
|
||||||
mixbuf = NULL;
|
|
||||||
}
|
|
||||||
if ( audio_event != NULL ) {
|
|
||||||
CloseHandle(audio_event);
|
|
||||||
audio_event = NULL;
|
|
||||||
}
|
|
||||||
IDirectSound_Release(sound);
|
|
||||||
sound = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_PRIMARY_BUFFER
|
|
||||||
/* This function tries to create a primary audio buffer, and returns the
|
|
||||||
number of audio chunks available in the created buffer.
|
|
||||||
*/
|
|
||||||
static int CreatePrimary(LPDIRECTSOUND sndObj, HWND focus,
|
|
||||||
LPDIRECTSOUNDBUFFER *sndbuf, WAVEFORMATEX *wavefmt, Uint32 chunksize)
|
|
||||||
{
|
|
||||||
HRESULT result;
|
|
||||||
DSBUFFERDESC format;
|
|
||||||
DSBCAPS caps;
|
|
||||||
int numchunks;
|
|
||||||
|
|
||||||
/* Try to set primary mixing privileges */
|
|
||||||
result = IDirectSound_SetCooperativeLevel(sndObj, focus,
|
|
||||||
DSSCL_WRITEPRIMARY);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
SetDSerror("DirectSound SetCooperativeLevel", result);
|
|
||||||
#endif
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to create the primary buffer */
|
|
||||||
SDL_memset(&format, 0, sizeof(format));
|
|
||||||
format.dwSize = sizeof(format);
|
|
||||||
format.dwFlags=(DSBCAPS_PRIMARYBUFFER|DSBCAPS_GETCURRENTPOSITION2);
|
|
||||||
format.dwFlags |= DSBCAPS_STICKYFOCUS;
|
|
||||||
#ifdef USE_POSITION_NOTIFY
|
|
||||||
format.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY;
|
|
||||||
#endif
|
|
||||||
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
SetDSerror("DirectSound CreateSoundBuffer", result);
|
|
||||||
#endif
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check the size of the fragment buffer */
|
|
||||||
SDL_memset(&caps, 0, sizeof(caps));
|
|
||||||
caps.dwSize = sizeof(caps);
|
|
||||||
result = IDirectSoundBuffer_GetCaps(*sndbuf, &caps);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
SetDSerror("DirectSound GetCaps", result);
|
|
||||||
#endif
|
|
||||||
IDirectSoundBuffer_Release(*sndbuf);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
if ( (chunksize > caps.dwBufferBytes) ||
|
|
||||||
((caps.dwBufferBytes%chunksize) != 0) ) {
|
|
||||||
/* The primary buffer size is not a multiple of 'chunksize'
|
|
||||||
-- this hopefully doesn't happen when 'chunksize' is a
|
|
||||||
power of 2.
|
|
||||||
*/
|
|
||||||
IDirectSoundBuffer_Release(*sndbuf);
|
|
||||||
SDL_SetError(
|
|
||||||
"Primary buffer size is: %d, cannot break it into chunks of %d bytes\n",
|
|
||||||
caps.dwBufferBytes, chunksize);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
numchunks = (caps.dwBufferBytes/chunksize);
|
|
||||||
|
|
||||||
/* Set the primary audio format */
|
|
||||||
result = IDirectSoundBuffer_SetFormat(*sndbuf, wavefmt);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
SetDSerror("DirectSound SetFormat", result);
|
|
||||||
#endif
|
|
||||||
IDirectSoundBuffer_Release(*sndbuf);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
return(numchunks);
|
|
||||||
}
|
|
||||||
#endif /* USE_PRIMARY_BUFFER */
|
|
||||||
|
|
||||||
/* This function tries to create a secondary audio buffer, and returns the
|
|
||||||
number of audio chunks available in the created buffer.
|
|
||||||
*/
|
|
||||||
static int CreateSecondary(LPDIRECTSOUND sndObj, HWND focus,
|
|
||||||
LPDIRECTSOUNDBUFFER *sndbuf, WAVEFORMATEX *wavefmt, Uint32 chunksize)
|
|
||||||
{
|
|
||||||
const int numchunks = 8;
|
|
||||||
HRESULT result;
|
|
||||||
DSBUFFERDESC format;
|
|
||||||
LPVOID pvAudioPtr1, pvAudioPtr2;
|
|
||||||
DWORD dwAudioBytes1, dwAudioBytes2;
|
|
||||||
|
|
||||||
/* Try to set primary mixing privileges */
|
|
||||||
if ( focus ) {
|
|
||||||
result = IDirectSound_SetCooperativeLevel(sndObj,
|
|
||||||
focus, DSSCL_PRIORITY);
|
|
||||||
} else {
|
|
||||||
result = IDirectSound_SetCooperativeLevel(sndObj,
|
|
||||||
GetDesktopWindow(), DSSCL_NORMAL);
|
|
||||||
}
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
SetDSerror("DirectSound SetCooperativeLevel", result);
|
|
||||||
#endif
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to create the secondary buffer */
|
|
||||||
SDL_memset(&format, 0, sizeof(format));
|
|
||||||
format.dwSize = sizeof(format);
|
|
||||||
format.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
|
||||||
#ifdef USE_POSITION_NOTIFY
|
|
||||||
format.dwFlags |= DSBCAPS_CTRLPOSITIONNOTIFY;
|
|
||||||
#endif
|
|
||||||
if ( ! focus ) {
|
|
||||||
format.dwFlags |= DSBCAPS_GLOBALFOCUS;
|
|
||||||
} else {
|
|
||||||
format.dwFlags |= DSBCAPS_STICKYFOCUS;
|
|
||||||
}
|
|
||||||
format.dwBufferBytes = numchunks*chunksize;
|
|
||||||
if ( (format.dwBufferBytes < DSBSIZE_MIN) ||
|
|
||||||
(format.dwBufferBytes > DSBSIZE_MAX) ) {
|
|
||||||
SDL_SetError("Sound buffer size must be between %d and %d",
|
|
||||||
DSBSIZE_MIN/numchunks, DSBSIZE_MAX/numchunks);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
format.dwReserved = 0;
|
|
||||||
format.lpwfxFormat = wavefmt;
|
|
||||||
result = IDirectSound_CreateSoundBuffer(sndObj, &format, sndbuf, NULL);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
SetDSerror("DirectSound CreateSoundBuffer", result);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
IDirectSoundBuffer_SetFormat(*sndbuf, wavefmt);
|
|
||||||
|
|
||||||
/* Silence the initial audio buffer */
|
|
||||||
result = IDirectSoundBuffer_Lock(*sndbuf, 0, format.dwBufferBytes,
|
|
||||||
(LPVOID *)&pvAudioPtr1, &dwAudioBytes1,
|
|
||||||
(LPVOID *)&pvAudioPtr2, &dwAudioBytes2,
|
|
||||||
DSBLOCK_ENTIREBUFFER);
|
|
||||||
if ( result == DS_OK ) {
|
|
||||||
if ( wavefmt->wBitsPerSample == 8 ) {
|
|
||||||
SDL_memset(pvAudioPtr1, 0x80, dwAudioBytes1);
|
|
||||||
} else {
|
|
||||||
SDL_memset(pvAudioPtr1, 0x00, dwAudioBytes1);
|
|
||||||
}
|
|
||||||
IDirectSoundBuffer_Unlock(*sndbuf,
|
|
||||||
(LPVOID)pvAudioPtr1, dwAudioBytes1,
|
|
||||||
(LPVOID)pvAudioPtr2, dwAudioBytes2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* We're ready to go */
|
|
||||||
return(numchunks);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function tries to set position notify events on the mixing buffer */
|
|
||||||
#ifdef USE_POSITION_NOTIFY
|
|
||||||
static int CreateAudioEvent(_THIS)
|
|
||||||
{
|
|
||||||
LPDIRECTSOUNDNOTIFY notify;
|
|
||||||
DSBPOSITIONNOTIFY *notify_positions;
|
|
||||||
int i, retval;
|
|
||||||
HRESULT result;
|
|
||||||
|
|
||||||
/* Default to fail on exit */
|
|
||||||
retval = -1;
|
|
||||||
notify = NULL;
|
|
||||||
|
|
||||||
/* Query for the interface */
|
|
||||||
result = IDirectSoundBuffer_QueryInterface(mixbuf,
|
|
||||||
&IID_IDirectSoundNotify, (void *)¬ify);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate the notify structures */
|
|
||||||
notify_positions = (DSBPOSITIONNOTIFY *)SDL_malloc(NUM_BUFFERS*
|
|
||||||
sizeof(*notify_positions));
|
|
||||||
if ( notify_positions == NULL ) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the notify event */
|
|
||||||
audio_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
||||||
if ( audio_event == NULL ) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set up the notify structures */
|
|
||||||
for ( i=0; i<NUM_BUFFERS; ++i ) {
|
|
||||||
notify_positions[i].dwOffset = i*mixlen;
|
|
||||||
notify_positions[i].hEventNotify = audio_event;
|
|
||||||
}
|
|
||||||
result = IDirectSoundNotify_SetNotificationPositions(notify,
|
|
||||||
NUM_BUFFERS, notify_positions);
|
|
||||||
if ( result == DS_OK ) {
|
|
||||||
retval = 0;
|
|
||||||
}
|
|
||||||
done:
|
|
||||||
if ( notify != NULL ) {
|
|
||||||
IDirectSoundNotify_Release(notify);
|
|
||||||
}
|
|
||||||
return(retval);
|
|
||||||
}
|
|
||||||
#endif /* USE_POSITION_NOTIFY */
|
|
||||||
|
|
||||||
static int DX5_OpenAudio(_THIS, SDL_AudioSpec *spec)
|
|
||||||
{
|
|
||||||
HRESULT result;
|
|
||||||
WAVEFORMATEX waveformat;
|
|
||||||
|
|
||||||
/* Set basic WAVE format parameters */
|
|
||||||
SDL_memset(&waveformat, 0, sizeof(waveformat));
|
|
||||||
waveformat.wFormatTag = WAVE_FORMAT_PCM;
|
|
||||||
|
|
||||||
/* Determine the audio parameters from the AudioSpec */
|
|
||||||
switch ( spec->format & 0xFF ) {
|
|
||||||
case 8:
|
|
||||||
/* Unsigned 8 bit audio data */
|
|
||||||
spec->format = AUDIO_U8;
|
|
||||||
silence = 0x80;
|
|
||||||
waveformat.wBitsPerSample = 8;
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
/* Signed 16 bit audio data */
|
|
||||||
spec->format = AUDIO_S16;
|
|
||||||
silence = 0x00;
|
|
||||||
waveformat.wBitsPerSample = 16;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SDL_SetError("Unsupported audio format");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
waveformat.nChannels = spec->channels;
|
|
||||||
waveformat.nSamplesPerSec = spec->freq;
|
|
||||||
waveformat.nBlockAlign =
|
|
||||||
waveformat.nChannels * (waveformat.wBitsPerSample/8);
|
|
||||||
waveformat.nAvgBytesPerSec =
|
|
||||||
waveformat.nSamplesPerSec * waveformat.nBlockAlign;
|
|
||||||
|
|
||||||
/* Update the fragment size as size in bytes */
|
|
||||||
SDL_CalculateAudioSpec(spec);
|
|
||||||
|
|
||||||
/* Open the audio device */
|
|
||||||
result = DSoundCreate(NULL, &sound, NULL);
|
|
||||||
if ( result != DS_OK ) {
|
|
||||||
SetDSerror("DirectSoundCreate", result);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Create the audio buffer to which we write */
|
|
||||||
NUM_BUFFERS = -1;
|
|
||||||
#ifdef USE_PRIMARY_BUFFER
|
|
||||||
if ( mainwin ) {
|
|
||||||
NUM_BUFFERS = CreatePrimary(sound, mainwin, &mixbuf,
|
|
||||||
&waveformat, spec->size);
|
|
||||||
}
|
|
||||||
#endif /* USE_PRIMARY_BUFFER */
|
|
||||||
if ( NUM_BUFFERS < 0 ) {
|
|
||||||
NUM_BUFFERS = CreateSecondary(sound, mainwin, &mixbuf,
|
|
||||||
&waveformat, spec->size);
|
|
||||||
if ( NUM_BUFFERS < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
fprintf(stderr, "Using secondary audio buffer\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_SOUND
|
|
||||||
else
|
|
||||||
fprintf(stderr, "Using primary audio buffer\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The buffer will auto-start playing in DX5_WaitAudio() */
|
|
||||||
lastchunk = 0;
|
|
||||||
mixlen = spec->size;
|
|
||||||
|
|
||||||
#ifdef USE_POSITION_NOTIFY
|
|
||||||
/* See if we can use DirectX 6 event notification */
|
|
||||||
if ( CreateAudioEvent(this) == 0 ) {
|
|
||||||
this->WaitAudio = DX6_WaitAudio_EventWait;
|
|
||||||
} else {
|
|
||||||
this->WaitAudio = DX5_WaitAudio_BusyWait;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef _SDL_lowaudio_h
|
|
||||||
#define _SDL_lowaudio_h
|
|
||||||
|
|
||||||
#include "directx.h"
|
|
||||||
|
|
||||||
#include "../SDL_sysaudio.h"
|
|
||||||
|
|
||||||
/* Hidden "this" pointer for the video functions */
|
|
||||||
#define _THIS SDL_AudioDevice *this
|
|
||||||
|
|
||||||
/* The DirectSound objects */
|
|
||||||
struct SDL_PrivateAudioData {
|
|
||||||
LPDIRECTSOUND sound;
|
|
||||||
LPDIRECTSOUNDBUFFER mixbuf;
|
|
||||||
int NUM_BUFFERS;
|
|
||||||
int mixlen, silence;
|
|
||||||
DWORD lastchunk;
|
|
||||||
Uint8 *locked_buf;
|
|
||||||
HANDLE audio_event;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Old variable names */
|
|
||||||
#define sound (this->hidden->sound)
|
|
||||||
#define mixbuf (this->hidden->mixbuf)
|
|
||||||
#define NUM_BUFFERS (this->hidden->NUM_BUFFERS)
|
|
||||||
#define mixlen (this->hidden->mixlen)
|
|
||||||
#define silence (this->hidden->silence)
|
|
||||||
#define lastchunk (this->hidden->lastchunk)
|
|
||||||
#define locked_buf (this->hidden->locked_buf)
|
|
||||||
#define audio_event (this->hidden->audio_event)
|
|
||||||
|
|
||||||
#endif /* _SDL_lowaudio_h */
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
|
|
||||||
#ifndef _directx_h
|
|
||||||
#define _directx_h
|
|
||||||
|
|
||||||
/* Include all of the DirectX 5.0 headers and adds any necessary tweaks */
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#include <mmsystem.h>
|
|
||||||
#ifndef WIN32
|
|
||||||
#define WIN32
|
|
||||||
#endif
|
|
||||||
#undef WINNT
|
|
||||||
|
|
||||||
/* Far pointers don't exist in 32-bit code */
|
|
||||||
#ifndef FAR
|
|
||||||
#define FAR
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Error codes not yet included in Win32 API header files */
|
|
||||||
#ifndef MAKE_HRESULT
|
|
||||||
#define MAKE_HRESULT(sev,fac,code) \
|
|
||||||
((HRESULT)(((unsigned long)(sev)<<31) | ((unsigned long)(fac)<<16) | ((unsigned long)(code))))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef S_OK
|
|
||||||
#define S_OK (HRESULT)0x00000000L
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SUCCEEDED
|
|
||||||
#define SUCCEEDED(x) ((HRESULT)(x) >= 0)
|
|
||||||
#endif
|
|
||||||
#ifndef FAILED
|
|
||||||
#define FAILED(x) ((HRESULT)(x)<0)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef E_FAIL
|
|
||||||
#define E_FAIL (HRESULT)0x80000008L
|
|
||||||
#endif
|
|
||||||
#ifndef E_NOINTERFACE
|
|
||||||
#define E_NOINTERFACE (HRESULT)0x80004002L
|
|
||||||
#endif
|
|
||||||
#ifndef E_OUTOFMEMORY
|
|
||||||
#define E_OUTOFMEMORY (HRESULT)0x8007000EL
|
|
||||||
#endif
|
|
||||||
#ifndef E_INVALIDARG
|
|
||||||
#define E_INVALIDARG (HRESULT)0x80070057L
|
|
||||||
#endif
|
|
||||||
#ifndef E_NOTIMPL
|
|
||||||
#define E_NOTIMPL (HRESULT)0x80004001L
|
|
||||||
#endif
|
|
||||||
#ifndef REGDB_E_CLASSNOTREG
|
|
||||||
#define REGDB_E_CLASSNOTREG (HRESULT)0x80040154L
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Severity codes */
|
|
||||||
#ifndef SEVERITY_ERROR
|
|
||||||
#define SEVERITY_ERROR 1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Error facility codes */
|
|
||||||
#ifndef FACILITY_WIN32
|
|
||||||
#define FACILITY_WIN32 7
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef FIELD_OFFSET
|
|
||||||
#define FIELD_OFFSET(type, field) ((LONG)&(((type *)0)->field))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* DirectX headers (if it isn't included, I haven't tested it yet)
|
|
||||||
*/
|
|
||||||
/* We need these defines to mark what version of DirectX API we use */
|
|
||||||
#define DIRECTDRAW_VERSION 0x0700
|
|
||||||
#define DIRECTSOUND_VERSION 0x0500
|
|
||||||
#define DIRECTINPUT_VERSION 0x0500
|
|
||||||
|
|
||||||
#include <ddraw.h>
|
|
||||||
#include <dsound.h>
|
|
||||||
#include <dinput.h>
|
|
||||||
|
|
||||||
#endif /* _directx_h */
|
|
||||||
|
|
@ -1,660 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Carsten Griwodz
|
|
||||||
griff@kom.tu-darmstadt.de
|
|
||||||
|
|
||||||
based on linux/SDL_syscdrom.c by Sam Lantinga
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_AIX
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
/*#define DEBUG_CDROM 1*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/devinfo.h>
|
|
||||||
#include <sys/mntctl.h>
|
|
||||||
#include <sys/statfs.h>
|
|
||||||
#include <sys/vmount.h>
|
|
||||||
#include <fstab.h>
|
|
||||||
#include <sys/scdisk.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
static dev_t SDL_cdmode[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDioctl(int id, int command, void *arg);
|
|
||||||
|
|
||||||
/* Check a drive to see if it is a CD-ROM */
|
|
||||||
static int CheckDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int is_cd;
|
|
||||||
int cdfd;
|
|
||||||
int ret;
|
|
||||||
struct devinfo info;
|
|
||||||
|
|
||||||
/* If it doesn't exist, return -1 */
|
|
||||||
if ( stat(drive, stbuf) < 0 ) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it does exist, verify that it's an available CD-ROM */
|
|
||||||
is_cd = 0;
|
|
||||||
if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
|
|
||||||
cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
|
|
||||||
if ( cdfd >= 0 ) {
|
|
||||||
ret = SDL_SYS_CDioctl( cdfd, IOCINFO, &info );
|
|
||||||
if ( ret < 0 ) {
|
|
||||||
/* Some kind of error */
|
|
||||||
is_cd = 0;
|
|
||||||
} else {
|
|
||||||
if ( info.devtype == DD_CDROM ) {
|
|
||||||
is_cd = 1;
|
|
||||||
} else {
|
|
||||||
is_cd = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(cdfd);
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Could not open drive %s (%s)\n", drive, strerror(errno));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return is_cd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds < MAX_DRIVES ) {
|
|
||||||
/* Check to make sure it's not already in our list.
|
|
||||||
This can happen when we see a drive via symbolic link.
|
|
||||||
*/
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
if ( stbuf->st_rdev == SDL_cdmode[i] ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add this drive to our list */
|
|
||||||
i = SDL_numcds;
|
|
||||||
SDL_cdlist[i] = SDL_strdup(drive);
|
|
||||||
if ( SDL_cdlist[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_cdmode[i] = stbuf->st_rdev;
|
|
||||||
++SDL_numcds;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void CheckMounts()
|
|
||||||
{
|
|
||||||
char* buffer;
|
|
||||||
int bufsz;
|
|
||||||
struct vmount* ptr;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
buffer = (char*)SDL_malloc(10);
|
|
||||||
bufsz = 10;
|
|
||||||
if ( buffer==NULL )
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Could not allocate 10 bytes in aix/SDL_syscdrom.c:CheckMounts\n" );
|
|
||||||
exit ( -10 );
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
/* mntctrl() returns an array of all mounted filesystems */
|
|
||||||
ret = mntctl ( MCTL_QUERY, bufsz, buffer );
|
|
||||||
if ( ret == 0 )
|
|
||||||
{
|
|
||||||
/* Buffer was too small, realloc. */
|
|
||||||
bufsz = *(int*)buffer; /* Required size is in first word. */
|
|
||||||
/* (whatever a word is in AIX 4.3.3) */
|
|
||||||
/* int seems to be OK in 32bit mode. */
|
|
||||||
SDL_free(buffer);
|
|
||||||
buffer = (char*)SDL_malloc(bufsz);
|
|
||||||
if ( buffer==NULL )
|
|
||||||
{
|
|
||||||
fprintf(stderr,
|
|
||||||
"Could not allocate %d bytes in aix/SDL_syscdrom.c:CheckMounts\n",
|
|
||||||
bufsz );
|
|
||||||
exit ( -10 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( ret < 0 )
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Error reading vmount structures\n");
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while ( ret == 0 );
|
|
||||||
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf ( stderr, "Read %d vmount structures\n",ret );
|
|
||||||
#endif
|
|
||||||
ptr = (struct vmount*)buffer;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
switch(ptr->vmt_gfstype)
|
|
||||||
{
|
|
||||||
case MNT_CDROM :
|
|
||||||
{
|
|
||||||
struct stat stbuf;
|
|
||||||
char* text;
|
|
||||||
|
|
||||||
text = (char*)ptr + ptr->vmt_data[VMT_OBJECT].vmt_off;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Checking mount path: %s mounted on %s\n",
|
|
||||||
text, (char*)ptr + ptr->vmt_data[VMT_STUB].vmt_off );
|
|
||||||
#endif
|
|
||||||
if ( CheckDrive( text, &stbuf) > 0)
|
|
||||||
{
|
|
||||||
AddDrive( text, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ptr = (struct vmount*)((char*)ptr + ptr->vmt_length);
|
|
||||||
ret--;
|
|
||||||
}
|
|
||||||
while ( ret > 0 );
|
|
||||||
|
|
||||||
free ( buffer );
|
|
||||||
}
|
|
||||||
|
|
||||||
static int CheckNonmounts()
|
|
||||||
{
|
|
||||||
#ifdef _THREAD_SAFE
|
|
||||||
AFILE_t fsFile = NULL;
|
|
||||||
int passNo = 0;
|
|
||||||
int ret;
|
|
||||||
struct fstab entry;
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
ret = setfsent_r( &fsFile, &passNo );
|
|
||||||
if ( ret != 0 ) return -1;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
ret = getfsent_r ( &entry, &fsFile, &passNo );
|
|
||||||
if ( ret == 0 ) {
|
|
||||||
char* l = SDL_strrchr(entry.fs_spec,'/');
|
|
||||||
if ( l != NULL ) {
|
|
||||||
if ( !SDL_strncmp("cd",++l,2) ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr,
|
|
||||||
"Found unmounted CD ROM drive with device name %s\n",
|
|
||||||
entry.fs_spec);
|
|
||||||
#endif
|
|
||||||
if ( CheckDrive( entry.fs_spec, &stbuf) > 0)
|
|
||||||
{
|
|
||||||
AddDrive( entry.fs_spec, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while ( ret == 0 );
|
|
||||||
ret = endfsent_r ( &fsFile );
|
|
||||||
if ( ret != 0 ) return -1;
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
struct fstab* entry;
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
setfsent();
|
|
||||||
do
|
|
||||||
{
|
|
||||||
entry = getfsent();
|
|
||||||
if ( entry != NULL ) {
|
|
||||||
char* l = SDL_strrchr(entry->fs_spec,'/');
|
|
||||||
if ( l != NULL ) {
|
|
||||||
if ( !SDL_strncmp("cd",++l,2) ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr,"Found unmounted CD ROM drive with device name %s", entry->fs_spec);
|
|
||||||
#endif
|
|
||||||
if ( CheckDrive( entry->fs_spec, &stbuf) > 0)
|
|
||||||
{
|
|
||||||
AddDrive( entry->fs_spec, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while ( entry != NULL );
|
|
||||||
endfsent();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
char *SDLcdrom;
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Look in the environment for our CD-ROM drive list */
|
|
||||||
SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
|
|
||||||
if ( SDLcdrom != NULL ) {
|
|
||||||
char *cdpath, *delim;
|
|
||||||
size_t len = SDL_strlen(SDLcdrom)+1;
|
|
||||||
cdpath = SDL_stack_alloc(char, len);
|
|
||||||
if ( cdpath != NULL ) {
|
|
||||||
SDL_strlcpy(cdpath, SDLcdrom, len);
|
|
||||||
SDLcdrom = cdpath;
|
|
||||||
do {
|
|
||||||
delim = SDL_strchr(SDLcdrom, ':');
|
|
||||||
if ( delim ) {
|
|
||||||
*delim++ = '\0';
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom);
|
|
||||||
#endif
|
|
||||||
if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
|
|
||||||
AddDrive(SDLcdrom, &stbuf);
|
|
||||||
}
|
|
||||||
if ( delim ) {
|
|
||||||
SDLcdrom = delim;
|
|
||||||
} else {
|
|
||||||
SDLcdrom = NULL;
|
|
||||||
}
|
|
||||||
} while ( SDLcdrom );
|
|
||||||
SDL_stack_free(cdpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckMounts();
|
|
||||||
CheckNonmounts();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* General ioctl() CD-ROM command function */
|
|
||||||
static int SDL_SYS_CDioctl(int id, int command, void *arg)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = ioctl(id, command, arg);
|
|
||||||
if ( retval < 0 ) {
|
|
||||||
SDL_SetError("ioctl() error: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
char* lastsl;
|
|
||||||
char* cdromname;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We found /dev/cd? drives and that is in our list. But we can
|
|
||||||
* open only the /dev/rcd? versions of those devices for Audio CD.
|
|
||||||
*/
|
|
||||||
len = SDL_strlen(SDL_cdlist[drive])+2;
|
|
||||||
cdromname = (char*)SDL_malloc(len);
|
|
||||||
SDL_strlcpy(cdromname,SDL_cdlist[drive],len);
|
|
||||||
lastsl = SDL_strrchr(cdromname,'/');
|
|
||||||
if (lastsl) {
|
|
||||||
*lastsl = 0;
|
|
||||||
SDL_strlcat(cdromname,"/r",len);
|
|
||||||
lastsl = SDL_strrchr(SDL_cdlist[drive],'/');
|
|
||||||
if (lastsl) {
|
|
||||||
lastsl++;
|
|
||||||
SDL_strlcat(cdromname,lastsl,len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Should open drive %s, opening %s\n", SDL_cdlist[drive], cdromname);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Use exclusive access. Don't use SC_DIAGNOSTICS as xmcd does because they
|
|
||||||
* require root priviledges, and we don't want that. SC_SINGLE provides
|
|
||||||
* exclusive access with less trouble.
|
|
||||||
*/
|
|
||||||
fd = openx(cdromname, O_RDONLY, NULL, SC_SINGLE);
|
|
||||||
if ( fd < 0 )
|
|
||||||
{
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Could not open drive %s (%s)\n", cdromname, strerror(errno));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
struct mode_form_op cdMode;
|
|
||||||
int ret;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
cdMode.action = CD_GET_MODE;
|
|
||||||
ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode);
|
|
||||||
if ( ret < 0 ) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Could not get drive mode for %s (%s)\n",
|
|
||||||
cdromname, strerror(errno));
|
|
||||||
} else {
|
|
||||||
switch(cdMode.cd_mode_form) {
|
|
||||||
case CD_MODE1 :
|
|
||||||
fprintf(stderr,
|
|
||||||
"Drive mode for %s is %s\n",
|
|
||||||
cdromname, "CD-ROM Data Mode 1");
|
|
||||||
break;
|
|
||||||
case CD_MODE2_FORM1 :
|
|
||||||
fprintf(stderr,
|
|
||||||
"Drive mode for %s is %s\n",
|
|
||||||
cdromname, "CD-ROM XA Data Mode 2 Form 1");
|
|
||||||
break;
|
|
||||||
case CD_MODE2_FORM2 :
|
|
||||||
fprintf(stderr,
|
|
||||||
"Drive mode for %s is %s\n",
|
|
||||||
cdromname, "CD-ROM XA Data Mode 2 Form 2");
|
|
||||||
break;
|
|
||||||
case CD_DA :
|
|
||||||
fprintf(stderr,
|
|
||||||
"Drive mode for %s is %s\n",
|
|
||||||
cdromname, "CD-DA");
|
|
||||||
break;
|
|
||||||
default :
|
|
||||||
fprintf(stderr,
|
|
||||||
"Drive mode for %s is %s\n",
|
|
||||||
cdromname, "unknown");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cdMode.action = CD_CHG_MODE;
|
|
||||||
cdMode.cd_mode_form = CD_DA;
|
|
||||||
ret = SDL_SYS_CDioctl(fd, DK_CD_MODE, &cdMode);
|
|
||||||
if ( ret < 0 ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr,
|
|
||||||
"Could not set drive mode for %s (%s)\n",
|
|
||||||
cdromname, strerror(errno));
|
|
||||||
#endif
|
|
||||||
SDL_SetError("ioctl() error: Could not set CD drive mode, %s",
|
|
||||||
strerror(errno));
|
|
||||||
} else {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr,
|
|
||||||
"Drive mode for %s set to CD_DA\n",
|
|
||||||
cdromname);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_free(cdromname);
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
struct cd_audio_cmd cmd;
|
|
||||||
struct cd_audio_cmd entry;
|
|
||||||
int i;
|
|
||||||
int okay;
|
|
||||||
|
|
||||||
cmd.audio_cmds = CD_TRK_INFO_AUDIO;
|
|
||||||
cmd.msf_flag = FALSE;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
cdrom->numtracks = cmd.indexing.track_index.last_track
|
|
||||||
- cmd.indexing.track_index.first_track+1;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read all the track TOC entries */
|
|
||||||
for ( i=0; i<=cdrom->numtracks; ++i ) {
|
|
||||||
if ( i == cdrom->numtracks ) {
|
|
||||||
cdrom->track[i].id = 0xAA;;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].id = cmd.indexing.track_index.first_track+i;
|
|
||||||
}
|
|
||||||
entry.audio_cmds = CD_GET_TRK_MSF;
|
|
||||||
entry.indexing.track_msf.track = cdrom->track[i].id;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &entry) < 0 ) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].type = 0; /* don't know how to detect 0x04 data track */
|
|
||||||
cdrom->track[i].offset = MSF_TO_FRAMES(
|
|
||||||
entry.indexing.track_msf.mins,
|
|
||||||
entry.indexing.track_msf.secs,
|
|
||||||
entry.indexing.track_msf.frames);
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if ( i > 0 ) {
|
|
||||||
cdrom->track[i-1].length = cdrom->track[i].offset
|
|
||||||
- cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( i == (cdrom->numtracks+1) ) {
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
struct cd_audio_cmd cmd;
|
|
||||||
cmd.audio_cmds = CD_INFO_AUDIO;
|
|
||||||
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd) < 0 ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "ioctl failed in SDL_SYS_CDStatus (%s)\n", SDL_GetError());
|
|
||||||
#endif
|
|
||||||
status = CD_ERROR;
|
|
||||||
} else {
|
|
||||||
switch (cmd.status) {
|
|
||||||
case CD_NO_AUDIO:
|
|
||||||
case CD_COMPLETED:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
case CD_PLAY_AUDIO:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case CD_PAUSE_AUDIO:
|
|
||||||
status = CD_PAUSED;
|
|
||||||
break;
|
|
||||||
case CD_NOT_VALID:
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "cdStatus failed with CD_NOT_VALID\n");
|
|
||||||
#endif
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
case CD_STATUS_ERROR:
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "cdStatus failed with CD_STATUS_ERROR\n");
|
|
||||||
#endif
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "cdStatus failed with unknown error\n");
|
|
||||||
#endif
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( position ) {
|
|
||||||
if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
|
|
||||||
*position = MSF_TO_FRAMES( cmd.indexing.info_audio.current_mins,
|
|
||||||
cmd.indexing.info_audio.current_secs,
|
|
||||||
cmd.indexing.info_audio.current_frames);
|
|
||||||
} else {
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
struct cd_audio_cmd cmd;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* My CD Rom is muted by default. I think I read that this is new with
|
|
||||||
* AIX 4.3. SDL does not change the volume, so I need a kludge. Maybe
|
|
||||||
* its better to do this elsewhere?
|
|
||||||
*/
|
|
||||||
cmd.audio_cmds = CD_PLAY_AUDIO | CD_SET_VOLUME;
|
|
||||||
cmd.msf_flag = TRUE;
|
|
||||||
FRAMES_TO_MSF(start,
|
|
||||||
&cmd.indexing.msf.first_mins,
|
|
||||||
&cmd.indexing.msf.first_secs,
|
|
||||||
&cmd.indexing.msf.first_frames);
|
|
||||||
FRAMES_TO_MSF(start+length,
|
|
||||||
&cmd.indexing.msf.last_mins,
|
|
||||||
&cmd.indexing.msf.last_secs,
|
|
||||||
&cmd.indexing.msf.last_frames);
|
|
||||||
cmd.volume_type = CD_VOLUME_ALL;
|
|
||||||
cmd.all_channel_vol = 255; /* This is a uchar. What is a good value? No docu! */
|
|
||||||
cmd.out_port_0_sel = CD_AUDIO_CHNL_0;
|
|
||||||
cmd.out_port_1_sel = CD_AUDIO_CHNL_1;
|
|
||||||
cmd.out_port_2_sel = CD_AUDIO_CHNL_2;
|
|
||||||
cmd.out_port_3_sel = CD_AUDIO_CHNL_3;
|
|
||||||
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
|
|
||||||
cmd.indexing.msf.first_mins,
|
|
||||||
cmd.indexing.msf.first_secs,
|
|
||||||
cmd.indexing.msf.first_frames,
|
|
||||||
cmd.indexing.msf.last_mins,
|
|
||||||
cmd.indexing.msf.last_secs,
|
|
||||||
cmd.indexing.msf.last_frames);
|
|
||||||
#endif
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
struct cd_audio_cmd cmd;
|
|
||||||
cmd.audio_cmds = CD_PAUSE_AUDIO;
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
struct cd_audio_cmd cmd;
|
|
||||||
cmd.audio_cmds = CD_RESUME_AUDIO;
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
struct cd_audio_cmd cmd;
|
|
||||||
cmd.audio_cmds = CD_STOP_AUDIO;
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, DKAUDIO, &cmd));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, DKEJECT, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
close(cdrom->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_AIX */
|
|
||||||
|
|
@ -1,410 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_BEOS
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control on BeOS
|
|
||||||
(not completely implemented yet)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <scsi.h>
|
|
||||||
#include <Directory.h>
|
|
||||||
#include <Entry.h>
|
|
||||||
#include <Path.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
extern "C" {
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Constants to help us get at the SCSI table-of-contents info */
|
|
||||||
#define CD_NUMTRACKS(toc) toc.toc_data[3]
|
|
||||||
#define CD_TRACK(toc, track) (&toc.toc_data[6+(track)*8])
|
|
||||||
#define CD_TRACK_N(toc, track) CD_TRACK(toc, track)[0]
|
|
||||||
#define CD_TRACK_M(toc, track) CD_TRACK(toc, track)[3]
|
|
||||||
#define CD_TRACK_S(toc, track) CD_TRACK(toc, track)[4]
|
|
||||||
#define CD_TRACK_F(toc, track) CD_TRACK(toc, track)[5]
|
|
||||||
|
|
||||||
/* Constants to help us get at the SCSI position info */
|
|
||||||
#define POS_TRACK(pos) pos.position[6]
|
|
||||||
#define POS_ABS_M(pos) pos.position[9]
|
|
||||||
#define POS_ABS_S(pos) pos.position[10]
|
|
||||||
#define POS_ABS_F(pos) pos.position[11]
|
|
||||||
#define POS_REL_M(pos) pos.position[13]
|
|
||||||
#define POS_REL_S(pos) pos.position[14]
|
|
||||||
#define POS_REL_F(pos) pos.position[15]
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
int try_dir(const char *directory);
|
|
||||||
|
|
||||||
|
|
||||||
/* Check a drive to see if it is a CD-ROM */
|
|
||||||
static int CheckDrive(char *drive)
|
|
||||||
{
|
|
||||||
struct stat stbuf;
|
|
||||||
int is_cd, cdfd;
|
|
||||||
device_geometry info;
|
|
||||||
|
|
||||||
/* If it doesn't exist, return -1 */
|
|
||||||
if ( stat(drive, &stbuf) < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it does exist, verify that it's an available CD-ROM */
|
|
||||||
is_cd = 0;
|
|
||||||
cdfd = open(drive, 0);
|
|
||||||
if ( cdfd >= 0 ) {
|
|
||||||
if ( ioctl(cdfd, B_GET_GEOMETRY, &info) == B_NO_ERROR ) {
|
|
||||||
if ( info.device_type == B_CD ) {
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(cdfd);
|
|
||||||
} else {
|
|
||||||
/* This can happen when the drive is open .. (?) */;
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
return(is_cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
size_t len;
|
|
||||||
|
|
||||||
if ( SDL_numcds < MAX_DRIVES ) {
|
|
||||||
/* Add this drive to our list */
|
|
||||||
i = SDL_numcds;
|
|
||||||
len = SDL_strlen(drive)+1;
|
|
||||||
SDL_cdlist[i] = (char *)SDL_malloc(len);
|
|
||||||
if ( SDL_cdlist[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_strlcpy(SDL_cdlist[i], drive, len);
|
|
||||||
++SDL_numcds;
|
|
||||||
#ifdef CDROM_DEBUG
|
|
||||||
fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* IDE bus scanning magic */
|
|
||||||
enum {
|
|
||||||
IDE_GET_DEVICES_INFO = B_DEVICE_OP_CODES_END + 50,
|
|
||||||
};
|
|
||||||
struct ide_ctrl_info {
|
|
||||||
bool ide_0_present;
|
|
||||||
bool ide_0_master_present;
|
|
||||||
bool ide_0_slave_present;
|
|
||||||
int ide_0_master_type;
|
|
||||||
int ide_0_slave_type;
|
|
||||||
bool ide_1_present;
|
|
||||||
bool ide_1_master_present;
|
|
||||||
bool ide_1_slave_present;
|
|
||||||
int ide_1_master_type;
|
|
||||||
int ide_1_slave_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
char *SDLcdrom;
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Look in the environment for our CD-ROM drive list */
|
|
||||||
SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
|
|
||||||
if ( SDLcdrom != NULL ) {
|
|
||||||
char *cdpath, *delim;
|
|
||||||
size_t len = SDL_strlen(SDLcdrom)+1;
|
|
||||||
cdpath = SDL_stack_alloc(char, len);
|
|
||||||
if ( cdpath != NULL ) {
|
|
||||||
SDL_strlcpy(cdpath, SDLcdrom, len);
|
|
||||||
SDLcdrom = cdpath;
|
|
||||||
do {
|
|
||||||
delim = SDL_strchr(SDLcdrom, ':');
|
|
||||||
if ( delim ) {
|
|
||||||
*delim++ = '\0';
|
|
||||||
}
|
|
||||||
if ( CheckDrive(SDLcdrom) > 0 ) {
|
|
||||||
AddDrive(SDLcdrom);
|
|
||||||
}
|
|
||||||
if ( delim ) {
|
|
||||||
SDLcdrom = delim;
|
|
||||||
} else {
|
|
||||||
SDLcdrom = NULL;
|
|
||||||
}
|
|
||||||
} while ( SDLcdrom );
|
|
||||||
SDL_stack_free(cdpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the system for CD-ROM drives */
|
|
||||||
try_dir("/dev/disk");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int try_dir(const char *directory)
|
|
||||||
{
|
|
||||||
BDirectory dir;
|
|
||||||
dir.SetTo(directory);
|
|
||||||
if(dir.InitCheck() != B_NO_ERROR) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
dir.Rewind();
|
|
||||||
BEntry entry;
|
|
||||||
while(dir.GetNextEntry(&entry) >= 0) {
|
|
||||||
BPath path;
|
|
||||||
const char *name;
|
|
||||||
entry_ref e;
|
|
||||||
|
|
||||||
if(entry.GetPath(&path) != B_NO_ERROR)
|
|
||||||
continue;
|
|
||||||
name = path.Path();
|
|
||||||
|
|
||||||
if(entry.GetRef(&e) != B_NO_ERROR)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(entry.IsDirectory()) {
|
|
||||||
if(SDL_strcmp(e.name, "floppy") == 0)
|
|
||||||
continue; /* ignore floppy (it is not silent) */
|
|
||||||
int devfd = try_dir(name);
|
|
||||||
if(devfd >= 0)
|
|
||||||
return devfd;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int devfd;
|
|
||||||
device_geometry g;
|
|
||||||
|
|
||||||
if(SDL_strcmp(e.name, "raw") != 0)
|
|
||||||
continue; /* ignore partitions */
|
|
||||||
|
|
||||||
devfd = open(name, O_RDONLY);
|
|
||||||
if(devfd < 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(ioctl(devfd, B_GET_GEOMETRY, &g, sizeof(g)) >= 0) {
|
|
||||||
if(g.device_type == B_CD)
|
|
||||||
{
|
|
||||||
AddDrive(strdup(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
close(devfd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return B_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* General ioctl() CD-ROM command function */
|
|
||||||
static int SDL_SYS_CDioctl(int index, int command, void *arg)
|
|
||||||
{
|
|
||||||
int okay;
|
|
||||||
int fd;
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
fd = open(SDL_cdlist[index], 0);
|
|
||||||
if ( fd >= 0 ) {
|
|
||||||
if ( ioctl(fd, command, arg) == B_NO_ERROR ) {
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
return(drive);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
scsi_toc toc;
|
|
||||||
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, B_SCSI_GET_TOC, &toc) == 0 ) {
|
|
||||||
cdrom->numtracks = CD_NUMTRACKS(toc);
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
for ( i=0; i<=cdrom->numtracks; ++i ) {
|
|
||||||
cdrom->track[i].id = CD_TRACK_N(toc, i);
|
|
||||||
/* FIXME: How do we tell on BeOS? */
|
|
||||||
cdrom->track[i].type = SDL_AUDIO_TRACK;
|
|
||||||
cdrom->track[i].offset = MSF_TO_FRAMES(
|
|
||||||
CD_TRACK_M(toc, i),
|
|
||||||
CD_TRACK_S(toc, i),
|
|
||||||
CD_TRACK_F(toc, i));
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if ( i > 0 ) {
|
|
||||||
cdrom->track[i-1].length =
|
|
||||||
cdrom->track[i].offset-
|
|
||||||
cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
} else {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
int fd;
|
|
||||||
int cur_frame;
|
|
||||||
scsi_position pos;
|
|
||||||
|
|
||||||
fd = open(SDL_cdlist[cdrom->id], 0);
|
|
||||||
cur_frame = 0;
|
|
||||||
if ( fd >= 0 ) {
|
|
||||||
if ( ioctl(fd, B_SCSI_GET_POSITION, &pos) == B_NO_ERROR ) {
|
|
||||||
cur_frame = MSF_TO_FRAMES(
|
|
||||||
POS_ABS_M(pos), POS_ABS_S(pos), POS_ABS_F(pos));
|
|
||||||
}
|
|
||||||
if ( ! pos.position[1] || (pos.position[1] >= 0x13) ||
|
|
||||||
((pos.position[1] == 0x12) && (!pos.position[6])) ) {
|
|
||||||
status = CD_STOPPED;
|
|
||||||
} else
|
|
||||||
if ( pos.position[1] == 0x11 ) {
|
|
||||||
status = CD_PLAYING;
|
|
||||||
} else {
|
|
||||||
status = CD_PAUSED;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
} else {
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
}
|
|
||||||
if ( position ) {
|
|
||||||
*position = cur_frame;
|
|
||||||
}
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
int okay;
|
|
||||||
int fd;
|
|
||||||
scsi_play_position pos;
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
fd = open(SDL_cdlist[cdrom->id], 0);
|
|
||||||
if ( fd >= 0 ) {
|
|
||||||
FRAMES_TO_MSF(start, &pos.start_m, &pos.start_s, &pos.start_f);
|
|
||||||
FRAMES_TO_MSF(start+length, &pos.end_m, &pos.end_s, &pos.end_f);
|
|
||||||
if ( ioctl(fd, B_SCSI_PLAY_POSITION, &pos) == B_NO_ERROR ) {
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_PAUSE_AUDIO, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_RESUME_AUDIO, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_STOP_AUDIO, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, B_SCSI_EJECT, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
close(cdrom->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_BEOS */
|
|
||||||
|
|
@ -1,542 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_BSDI
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Functions for system-level CD-ROM audio control for BSD/OS 4.x
|
|
||||||
* This started life out as a copy of the freebsd/SDL_cdrom.c file but was
|
|
||||||
* heavily modified. Works for standard (MMC) SCSI and ATAPI CDrom drives.
|
|
||||||
*
|
|
||||||
* Steven Schultz - sms@to.gd-es.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <err.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include </sys/dev/scsi/scsi.h>
|
|
||||||
#include </sys/dev/scsi/scsi_ioctl.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The msf_to_frame and frame_to_msf were yanked from libcdrom and inlined
|
|
||||||
* here so that -lcdrom doesn't have to be dragged in for something so simple.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define FRAMES_PER_SECOND 75
|
|
||||||
#define FRAMES_PER_MINUTE (FRAMES_PER_SECOND * 60)
|
|
||||||
|
|
||||||
int
|
|
||||||
msf_to_frame(int minute, int second, int frame)
|
|
||||||
{
|
|
||||||
return(minute * FRAMES_PER_MINUTE + second * FRAMES_PER_SECOND + frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
frame_to_msf(int frame, int *minp, int *secp, int *framep)
|
|
||||||
{
|
|
||||||
*minp = frame / FRAMES_PER_MINUTE;
|
|
||||||
*secp = (frame % FRAMES_PER_MINUTE) / FRAMES_PER_SECOND;
|
|
||||||
*framep = frame % FRAMES_PER_SECOND;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
static dev_t SDL_cdmode[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
typedef struct scsi_cdb cdb_t;
|
|
||||||
|
|
||||||
static int scsi_cmd(int fd,
|
|
||||||
struct scsi_cdb *cdb,
|
|
||||||
int cdblen,
|
|
||||||
int rw,
|
|
||||||
caddr_t data,
|
|
||||||
int datalen,
|
|
||||||
struct scsi_user_cdb *sus)
|
|
||||||
{
|
|
||||||
int scsistatus;
|
|
||||||
unsigned char *cp;
|
|
||||||
struct scsi_user_cdb suc;
|
|
||||||
|
|
||||||
/* safety checks */
|
|
||||||
if (!cdb) return(-1);
|
|
||||||
if (rw != SUC_READ && rw != SUC_WRITE) return(-1);
|
|
||||||
|
|
||||||
suc.suc_flags = rw;
|
|
||||||
suc.suc_cdblen = cdblen;
|
|
||||||
bcopy(cdb, suc.suc_cdb, cdblen);
|
|
||||||
suc.suc_datalen = datalen;
|
|
||||||
suc.suc_data = data;
|
|
||||||
suc.suc_timeout = 10; /* 10 secs max for TUR or SENSE */
|
|
||||||
if (ioctl(fd, SCSIRAWCDB, &suc) == -1)
|
|
||||||
return(-11);
|
|
||||||
scsistatus = suc.suc_sus.sus_status;
|
|
||||||
cp = suc.suc_sus.sus_sense;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If a place to copy the sense data back to has been provided then the
|
|
||||||
* caller is responsible for checking the errors and printing any information
|
|
||||||
* out if the status was not successful.
|
|
||||||
*/
|
|
||||||
if (scsistatus != 0 && !sus)
|
|
||||||
{
|
|
||||||
fprintf(stderr,"scsistatus = %x cmd = %x\n",
|
|
||||||
scsistatus, cdb[0]);
|
|
||||||
fprintf(stderr, "sense %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
|
|
||||||
cp[0], cp[1], cp[2], cp[3], cp[4], cp[5],
|
|
||||||
cp[6], cp[7], cp[8], cp[9], cp[10], cp[11],
|
|
||||||
cp[12], cp[13], cp[14], cp[15]);
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
if (sus)
|
|
||||||
bcopy(&suc, sus, sizeof (struct scsi_user_cdb));
|
|
||||||
if (scsistatus)
|
|
||||||
return(1); /* Return non-zero for unsuccessful status */
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* request vendor brand and model */
|
|
||||||
unsigned char *Inquiry(int fd)
|
|
||||||
{
|
|
||||||
static struct scsi_cdb6 cdb =
|
|
||||||
{
|
|
||||||
0x12,
|
|
||||||
0, 0, 0,
|
|
||||||
56,
|
|
||||||
0
|
|
||||||
};
|
|
||||||
static unsigned char Inqbuffer[56];
|
|
||||||
|
|
||||||
if (scsi_cmd(fd, (cdb_t *)&cdb, 6, SUC_READ, Inqbuffer,
|
|
||||||
sizeof(Inqbuffer), 0))
|
|
||||||
return("\377");
|
|
||||||
return(Inqbuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ADD_SENSECODE 12
|
|
||||||
#define ADD_SC_QUALIFIER 13
|
|
||||||
|
|
||||||
int TestForMedium(int fd)
|
|
||||||
{
|
|
||||||
int sts, asc, ascq;
|
|
||||||
struct scsi_user_cdb sus;
|
|
||||||
static struct scsi_cdb6 cdb =
|
|
||||||
{
|
|
||||||
CMD_TEST_UNIT_READY, /* command */
|
|
||||||
0, /* reserved */
|
|
||||||
0, /* reserved */
|
|
||||||
0, /* reserved */
|
|
||||||
0, /* reserved */
|
|
||||||
0 /* reserved */
|
|
||||||
};
|
|
||||||
|
|
||||||
again: sts = scsi_cmd(fd, (cdb_t *)&cdb, 6, SUC_READ, 0, 0, &sus);
|
|
||||||
asc = sus.suc_sus.sus_sense[ADD_SENSECODE];
|
|
||||||
ascq = sus.suc_sus.sus_sense[ADD_SC_QUALIFIER];
|
|
||||||
if (asc == 0x3a && ascq == 0x0) /* no medium */
|
|
||||||
return(0);
|
|
||||||
if (asc == 0x28 && ascq == 0x0) /* medium changed */
|
|
||||||
goto again;
|
|
||||||
if (asc == 0x4 && ascq == 0x1 ) /* coming ready */
|
|
||||||
{
|
|
||||||
sleep(2);
|
|
||||||
goto again;
|
|
||||||
}
|
|
||||||
return(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check a drive to see if it is a CD-ROM */
|
|
||||||
static int CheckDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int is_cd = 0, cdfd;
|
|
||||||
char *p;
|
|
||||||
|
|
||||||
/* If it doesn't exist, return -1 */
|
|
||||||
if ( stat(drive, stbuf) < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it does exist, verify that it's an available CD-ROM */
|
|
||||||
cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
|
|
||||||
if ( cdfd >= 0 ) {
|
|
||||||
p = Inquiry(cdfd);
|
|
||||||
if (*p == TYPE_ROM)
|
|
||||||
is_cd = 1;
|
|
||||||
close(cdfd);
|
|
||||||
}
|
|
||||||
return(is_cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds < MAX_DRIVES ) {
|
|
||||||
/* Check to make sure it's not already in our list.
|
|
||||||
This can happen when we see a drive via symbolic link.
|
|
||||||
*/
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
if ( stbuf->st_rdev == SDL_cdmode[i] ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add this drive to our list */
|
|
||||||
i = SDL_numcds;
|
|
||||||
SDL_cdlist[i] = SDL_strdup(drive);
|
|
||||||
if ( SDL_cdlist[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_cdmode[i] = stbuf->st_rdev;
|
|
||||||
++SDL_numcds;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
/* checklist: /dev/rsr?c */
|
|
||||||
static char *checklist[] = {
|
|
||||||
"?0 rsr?", NULL
|
|
||||||
};
|
|
||||||
char *SDLcdrom;
|
|
||||||
int i, j, exists;
|
|
||||||
char drive[32];
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Look in the environment for our CD-ROM drive list */
|
|
||||||
SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
|
|
||||||
if ( SDLcdrom != NULL ) {
|
|
||||||
char *cdpath, *delim;
|
|
||||||
size_t len = SDL_strlen(SDLcdrom)+1;
|
|
||||||
cdpath = SDL_stack_alloc(char, len);
|
|
||||||
if ( cdpath != NULL ) {
|
|
||||||
SDL_strlcpy(cdpath, SDLcdrom, len);
|
|
||||||
SDLcdrom = cdpath;
|
|
||||||
do {
|
|
||||||
delim = SDL_strchr(SDLcdrom, ':');
|
|
||||||
if ( delim ) {
|
|
||||||
*delim++ = '\0';
|
|
||||||
}
|
|
||||||
if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
|
|
||||||
AddDrive(SDLcdrom, &stbuf);
|
|
||||||
}
|
|
||||||
if ( delim ) {
|
|
||||||
SDLcdrom = delim;
|
|
||||||
} else {
|
|
||||||
SDLcdrom = NULL;
|
|
||||||
}
|
|
||||||
} while ( SDLcdrom );
|
|
||||||
SDL_stack_free(cdpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the system for CD-ROM drives */
|
|
||||||
for ( i=0; checklist[i]; ++i ) {
|
|
||||||
if ( checklist[i][0] == '?' ) {
|
|
||||||
char *insert;
|
|
||||||
exists = 1;
|
|
||||||
for ( j=checklist[i][1]; exists; ++j ) {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%sc", &checklist[i][3]);
|
|
||||||
insert = SDL_strchr(drive, '?');
|
|
||||||
if ( insert != NULL ) {
|
|
||||||
*insert = j;
|
|
||||||
}
|
|
||||||
switch (CheckDrive(drive, &stbuf)) {
|
|
||||||
/* Drive exists and is a CD-ROM */
|
|
||||||
case 1:
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
break;
|
|
||||||
/* Drive exists, but isn't a CD-ROM */
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
/* Drive doesn't exist */
|
|
||||||
case -1:
|
|
||||||
exists = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
|
|
||||||
if ( CheckDrive(drive, &stbuf) > 0 ) {
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
return(open(SDL_cdlist[drive], O_RDONLY | O_NONBLOCK | O_EXCL, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
u_char cdb[10], buf[4], *p, *toc;
|
|
||||||
struct scsi_user_cdb sus;
|
|
||||||
int i, sts, first_track, last_track, ntracks, toc_size;
|
|
||||||
|
|
||||||
bzero(cdb, sizeof (cdb));
|
|
||||||
cdb[0] = 0x43; /* Read TOC */
|
|
||||||
cdb[1] = 0x2; /* MSF */
|
|
||||||
cdb[8] = 4; /* size TOC header */
|
|
||||||
sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, buf, 4, &sus);
|
|
||||||
if (sts < 0)
|
|
||||||
return(-1);
|
|
||||||
first_track = buf[2];
|
|
||||||
last_track = buf[3];
|
|
||||||
ntracks = last_track - first_track + 1;
|
|
||||||
cdrom->numtracks = ntracks;
|
|
||||||
toc_size = 4 + (ntracks + 1) * 8;
|
|
||||||
toc = (u_char *)SDL_malloc(toc_size);
|
|
||||||
if (toc == NULL)
|
|
||||||
return(-1);
|
|
||||||
bzero(cdb, sizeof (cdb));
|
|
||||||
cdb[0] = 0x43;
|
|
||||||
cdb[1] = 0x2;
|
|
||||||
cdb[6] = first_track;
|
|
||||||
cdb[7] = toc_size >> 8;
|
|
||||||
cdb[8] = toc_size & 0xff;
|
|
||||||
sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, toc, toc_size,
|
|
||||||
&sus);
|
|
||||||
if (sts < 0)
|
|
||||||
{
|
|
||||||
SDL_free(toc);
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0, p = toc+4; i <= ntracks; i++, p+= 8)
|
|
||||||
{
|
|
||||||
if (i == ntracks)
|
|
||||||
cdrom->track[i].id = 0xAA; /* Leadout */
|
|
||||||
else
|
|
||||||
cdrom->track[i].id = first_track + i;
|
|
||||||
if (p[1] & 0x20)
|
|
||||||
cdrom->track[i].type = SDL_DATA_TRACK;
|
|
||||||
else
|
|
||||||
cdrom->track[i].type = SDL_AUDIO_TRACK;
|
|
||||||
cdrom->track[i].offset = msf_to_frame(p[5], p[6], p[7]);
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if (i > 0)
|
|
||||||
cdrom->track[i-1].length = cdrom->track[i].offset -
|
|
||||||
cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
SDL_free(toc);
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
u_char cdb[10], buf[16];
|
|
||||||
int sts;
|
|
||||||
struct scsi_user_cdb sus;
|
|
||||||
|
|
||||||
bzero(cdb, sizeof (cdb));
|
|
||||||
cdb[0] = 0x42; /* read subq */
|
|
||||||
cdb[1] = 0x2; /* MSF */
|
|
||||||
cdb[2] = 0x40; /* q channel */
|
|
||||||
cdb[3] = 1; /* current pos */
|
|
||||||
cdb[7] = sizeof (buf) >> 8;
|
|
||||||
cdb[8] = sizeof (buf) & 0xff;
|
|
||||||
sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, buf, sizeof (buf),
|
|
||||||
&sus);
|
|
||||||
if (sts < 0)
|
|
||||||
return(-1);
|
|
||||||
if (sts)
|
|
||||||
{
|
|
||||||
if (TestForMedium(cdrom->id) == 0)
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
else
|
|
||||||
status = CD_ERROR;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (buf[1])
|
|
||||||
{
|
|
||||||
case 0x11:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case 0x12:
|
|
||||||
status = CD_PAUSED;
|
|
||||||
break;
|
|
||||||
case 0x13:
|
|
||||||
case 0x14:
|
|
||||||
case 0x15:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (position)
|
|
||||||
{
|
|
||||||
if ( status == CD_PLAYING || (status == CD_PAUSED) )
|
|
||||||
*position = msf_to_frame(buf[9], buf[10], buf[11]);
|
|
||||||
else
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
u_char cdb[10];
|
|
||||||
int sts, minute, second, frame, eminute, esecond, eframe;
|
|
||||||
struct scsi_user_cdb sus;
|
|
||||||
|
|
||||||
bzero(cdb, sizeof(cdb));
|
|
||||||
cdb[0] = 0x47; /* Play */
|
|
||||||
frame_to_msf(start, &minute, &second, &frame);
|
|
||||||
frame_to_msf(start + length, &eminute, &esecond, &eframe);
|
|
||||||
cdb[3] = minute;
|
|
||||||
cdb[4] = second;
|
|
||||||
cdb[5] = frame;
|
|
||||||
cdb[6] = eminute;
|
|
||||||
cdb[7] = esecond;
|
|
||||||
cdb[8] = eframe;
|
|
||||||
sts = scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, 0, 0, &sus);
|
|
||||||
return(sts);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
pauseresume(SDL_CD *cdrom, int flag)
|
|
||||||
{
|
|
||||||
u_char cdb[10];
|
|
||||||
struct scsi_user_cdb sus;
|
|
||||||
|
|
||||||
bzero(cdb, sizeof (cdb));
|
|
||||||
cdb[0] = 0x4b;
|
|
||||||
cdb[8] = flag & 0x1;
|
|
||||||
return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 10, SUC_READ, 0, 0, &sus));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(pauseresume(cdrom, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(pauseresume(cdrom, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
u_char cdb[6];
|
|
||||||
struct scsi_user_cdb sus;
|
|
||||||
|
|
||||||
bzero(cdb, sizeof (cdb));
|
|
||||||
cdb[0] = 0x1b; /* stop */
|
|
||||||
cdb[1] = 1; /* immediate */
|
|
||||||
return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 6, SUC_READ, 0, 0, &sus));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
u_char cdb[6];
|
|
||||||
struct scsi_user_cdb sus;
|
|
||||||
|
|
||||||
bzero(cdb, sizeof (cdb));
|
|
||||||
cdb[0] = 0x1b; /* stop */
|
|
||||||
cdb[1] = 1; /* immediate */
|
|
||||||
cdb[4] = 2; /* eject */
|
|
||||||
return(scsi_cmd(cdrom->id, (cdb_t *)cdb, 6, SUC_READ, 0, 0, &sus));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
close(cdrom->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_BSDI */
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_DC
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
#include <dc/cdrom.h>
|
|
||||||
#include <dc/spu.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return "/cd";
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
return(drive);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define TRACK_CDDA 0
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
CDROM_TOC toc;
|
|
||||||
int ret,i;
|
|
||||||
|
|
||||||
ret = cdrom_read_toc(&toc,0);
|
|
||||||
if (ret!=ERR_OK) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cdrom->numtracks = TOC_TRACK(toc.last)-TOC_TRACK(toc.first)+1;
|
|
||||||
for(i=0;i<cdrom->numtracks;i++) {
|
|
||||||
unsigned long entry = toc.entry[i];
|
|
||||||
cdrom->track[i].id = i+1;
|
|
||||||
cdrom->track[i].type = (TOC_CTRL(toc.entry[i])==TRACK_CDDA)?SDL_AUDIO_TRACK:SDL_DATA_TRACK;
|
|
||||||
cdrom->track[i].offset = TOC_LBA(entry)-150;
|
|
||||||
cdrom->track[i].length = TOC_LBA((i+1<toc.last)?toc.entry[i+1]:toc.leadout_sector)-TOC_LBA(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
int ret,dc_status,disc_type;
|
|
||||||
|
|
||||||
ret = cdrom_get_status(&dc_status,&disc_type);
|
|
||||||
if (ret!=ERR_OK) return CD_ERROR;
|
|
||||||
|
|
||||||
switch(dc_status) {
|
|
||||||
// case CD_STATUS_BUSY:
|
|
||||||
case CD_STATUS_PAUSED:
|
|
||||||
return CD_PAUSED;
|
|
||||||
case CD_STATUS_STANDBY:
|
|
||||||
return CD_STOPPED;
|
|
||||||
case CD_STATUS_PLAYING:
|
|
||||||
return CD_PLAYING;
|
|
||||||
// case CD_STATUS_SEEKING:
|
|
||||||
// case CD_STATUS_SCANING:
|
|
||||||
case CD_STATUS_OPEN:
|
|
||||||
case CD_STATUS_NO_DISC:
|
|
||||||
return CD_TRAYEMPTY;
|
|
||||||
default:
|
|
||||||
return CD_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
int ret = cdrom_cdda_play(start-150,start-150+length,1,CDDA_SECTORS);
|
|
||||||
return ret==ERR_OK?0:-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
int ret=cdrom_cdda_pause();
|
|
||||||
return ret==ERR_OK?0:-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
int ret=cdrom_cdda_resume();
|
|
||||||
return ret==ERR_OK?0:-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
int ret=cdrom_spin_down();
|
|
||||||
return ret==ERR_OK?0:-1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_DC */
|
|
||||||
|
|
@ -1,406 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_FREEBSD
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/cdio.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
static dev_t SDL_cdmode[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
/* Some ioctl() errno values which occur when the tray is empty */
|
|
||||||
#define ERRNO_TRAYEMPTY(errno) \
|
|
||||||
((errno == EIO) || (errno == ENOENT) || (errno == EINVAL))
|
|
||||||
|
|
||||||
/* Check a drive to see if it is a CD-ROM */
|
|
||||||
static int CheckDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int is_cd, cdfd;
|
|
||||||
struct ioc_read_subchannel info;
|
|
||||||
|
|
||||||
/* If it doesn't exist, return -1 */
|
|
||||||
if ( stat(drive, stbuf) < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it does exist, verify that it's an available CD-ROM */
|
|
||||||
is_cd = 0;
|
|
||||||
if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
|
|
||||||
cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
|
|
||||||
if ( cdfd >= 0 ) {
|
|
||||||
info.address_format = CD_MSF_FORMAT;
|
|
||||||
info.data_format = CD_CURRENT_POSITION;
|
|
||||||
info.data_len = 0;
|
|
||||||
info.data = NULL;
|
|
||||||
/* Under Linux, EIO occurs when a disk is not present.
|
|
||||||
This isn't 100% reliable, so we use the USE_MNTENT
|
|
||||||
code above instead.
|
|
||||||
*/
|
|
||||||
if ( (ioctl(cdfd, CDIOCREADSUBCHANNEL, &info) == 0) ||
|
|
||||||
ERRNO_TRAYEMPTY(errno) ) {
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
close(cdfd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(is_cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds < MAX_DRIVES ) {
|
|
||||||
/* Check to make sure it's not already in our list.
|
|
||||||
This can happen when we see a drive via symbolic link.
|
|
||||||
*/
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
if ( stbuf->st_rdev == SDL_cdmode[i] ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add this drive to our list */
|
|
||||||
i = SDL_numcds;
|
|
||||||
SDL_cdlist[i] = SDL_strdup(drive);
|
|
||||||
if ( SDL_cdlist[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_cdmode[i] = stbuf->st_rdev;
|
|
||||||
++SDL_numcds;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
/* checklist: /dev/cdrom,/dev/cd?c /dev/acd?c
|
|
||||||
/dev/matcd?c /dev/mcd?c /dev/scd?c */
|
|
||||||
static char *checklist[] = {
|
|
||||||
"cdrom", "?0 cd?", "?0 acd?", "?0 matcd?", "?0 mcd?", "?0 scd?",NULL
|
|
||||||
};
|
|
||||||
char *SDLcdrom;
|
|
||||||
int i, j, exists;
|
|
||||||
char drive[32];
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Look in the environment for our CD-ROM drive list */
|
|
||||||
SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
|
|
||||||
if ( SDLcdrom != NULL ) {
|
|
||||||
char *cdpath, *delim;
|
|
||||||
size_t len = SDL_strlen(SDLcdrom)+1;
|
|
||||||
cdpath = SDL_stack_alloc(char, len);
|
|
||||||
if ( cdpath != NULL ) {
|
|
||||||
SDL_strlcpy(cdpath, SDLcdrom, len);
|
|
||||||
SDLcdrom = cdpath;
|
|
||||||
do {
|
|
||||||
delim = SDL_strchr(SDLcdrom, ':');
|
|
||||||
if ( delim ) {
|
|
||||||
*delim++ = '\0';
|
|
||||||
}
|
|
||||||
if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
|
|
||||||
AddDrive(SDLcdrom, &stbuf);
|
|
||||||
}
|
|
||||||
if ( delim ) {
|
|
||||||
SDLcdrom = delim;
|
|
||||||
} else {
|
|
||||||
SDLcdrom = NULL;
|
|
||||||
}
|
|
||||||
} while ( SDLcdrom );
|
|
||||||
SDL_stack_free(cdpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the system for CD-ROM drives */
|
|
||||||
for ( i=0; checklist[i]; ++i ) {
|
|
||||||
if ( checklist[i][0] == '?' ) {
|
|
||||||
char *insert;
|
|
||||||
exists = 1;
|
|
||||||
for ( j=checklist[i][1]; exists; ++j ) {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%sc", &checklist[i][3]);
|
|
||||||
insert = SDL_strchr(drive, '?');
|
|
||||||
if ( insert != NULL ) {
|
|
||||||
*insert = j;
|
|
||||||
}
|
|
||||||
switch (CheckDrive(drive, &stbuf)) {
|
|
||||||
/* Drive exists and is a CD-ROM */
|
|
||||||
case 1:
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
break;
|
|
||||||
/* Drive exists, but isn't a CD-ROM */
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
/* Drive doesn't exist */
|
|
||||||
case -1:
|
|
||||||
exists = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
|
|
||||||
if ( CheckDrive(drive, &stbuf) > 0 ) {
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* General ioctl() CD-ROM command function */
|
|
||||||
static int SDL_SYS_CDioctl(int id, int command, void *arg)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = ioctl(id, command, arg);
|
|
||||||
if ( retval < 0 ) {
|
|
||||||
SDL_SetError("ioctl() error: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
return(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
return(open(SDL_cdlist[drive], (O_RDONLY|O_EXCL|O_NONBLOCK), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
struct ioc_toc_header toc;
|
|
||||||
int i, okay;
|
|
||||||
struct ioc_read_toc_entry entry;
|
|
||||||
struct cd_toc_entry data;
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0 ) {
|
|
||||||
cdrom->numtracks = toc.ending_track-toc.starting_track+1;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
/* Read all the track TOC entries */
|
|
||||||
for ( i=0; i<=cdrom->numtracks; ++i ) {
|
|
||||||
if ( i == cdrom->numtracks ) {
|
|
||||||
cdrom->track[i].id = 0xAA; /* CDROM_LEADOUT */
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].id = toc.starting_track+i;
|
|
||||||
}
|
|
||||||
entry.starting_track = cdrom->track[i].id;
|
|
||||||
entry.address_format = CD_MSF_FORMAT;
|
|
||||||
entry.data_len = sizeof(data);
|
|
||||||
entry.data = &data;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCENTRYS,
|
|
||||||
&entry) < 0 ) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].type = data.control;
|
|
||||||
cdrom->track[i].offset = MSF_TO_FRAMES(
|
|
||||||
data.addr.msf.minute,
|
|
||||||
data.addr.msf.second,
|
|
||||||
data.addr.msf.frame);
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if ( i > 0 ) {
|
|
||||||
cdrom->track[i-1].length =
|
|
||||||
cdrom->track[i].offset-
|
|
||||||
cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( i == (cdrom->numtracks+1) ) {
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
struct ioc_toc_header toc;
|
|
||||||
struct ioc_read_subchannel info;
|
|
||||||
struct cd_sub_channel_info data;
|
|
||||||
|
|
||||||
info.address_format = CD_MSF_FORMAT;
|
|
||||||
info.data_format = CD_CURRENT_POSITION;
|
|
||||||
info.track = 0;
|
|
||||||
info.data_len = sizeof(data);
|
|
||||||
info.data = &data;
|
|
||||||
if ( ioctl(cdrom->id, CDIOCREADSUBCHANNEL, &info) < 0 ) {
|
|
||||||
if ( ERRNO_TRAYEMPTY(errno) ) {
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
} else {
|
|
||||||
status = CD_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (data.header.audio_status) {
|
|
||||||
case CD_AS_AUDIO_INVALID:
|
|
||||||
case CD_AS_NO_STATUS:
|
|
||||||
/* Try to determine if there's a CD available */
|
|
||||||
if (ioctl(cdrom->id,CDIOREADTOCHEADER,&toc)==0)
|
|
||||||
status = CD_STOPPED;
|
|
||||||
else
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
break;
|
|
||||||
case CD_AS_PLAY_COMPLETED:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
case CD_AS_PLAY_IN_PROGRESS:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case CD_AS_PLAY_PAUSED:
|
|
||||||
status = CD_PAUSED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( position ) {
|
|
||||||
if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
|
|
||||||
*position = MSF_TO_FRAMES(
|
|
||||||
data.what.position.absaddr.msf.minute,
|
|
||||||
data.what.position.absaddr.msf.second,
|
|
||||||
data.what.position.absaddr.msf.frame);
|
|
||||||
} else {
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
struct ioc_play_msf playtime;
|
|
||||||
|
|
||||||
FRAMES_TO_MSF(start,
|
|
||||||
&playtime.start_m, &playtime.start_s, &playtime.start_f);
|
|
||||||
FRAMES_TO_MSF(start+length,
|
|
||||||
&playtime.end_m, &playtime.end_s, &playtime.end_f);
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
|
|
||||||
playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
|
|
||||||
playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
|
|
||||||
#endif
|
|
||||||
ioctl(cdrom->id, CDIOCSTART, 0);
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCPLAYMSF, &playtime));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCPAUSE, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCRESUME, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCSTOP, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCEJECT, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
close(cdrom->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_FREEBSD */
|
|
||||||
|
|
@ -1,564 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_LINUX
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
#include <string.h> /* For strerror() */
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#ifdef __LINUX__
|
|
||||||
#ifdef HAVE_LINUX_VERSION_H
|
|
||||||
/* linux 2.6.9 workaround */
|
|
||||||
#include <linux/version.h>
|
|
||||||
#if LINUX_VERSION_CODE == KERNEL_VERSION(2,6,9)
|
|
||||||
#include <asm/types.h>
|
|
||||||
#define __le64 __u64
|
|
||||||
#define __le32 __u32
|
|
||||||
#define __le16 __u16
|
|
||||||
#define __be64 __u64
|
|
||||||
#define __be32 __u32
|
|
||||||
#define __be16 __u16
|
|
||||||
#endif /* linux 2.6.9 workaround */
|
|
||||||
#endif /* HAVE_LINUX_VERSION_H */
|
|
||||||
#include <linux/cdrom.h>
|
|
||||||
#endif
|
|
||||||
#ifdef __SVR4
|
|
||||||
#include <sys/cdio.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Define this to use the alternative getmntent() code */
|
|
||||||
#ifndef __SVR4
|
|
||||||
#define USE_MNTENT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_MNTENT
|
|
||||||
#if defined(__USLC__)
|
|
||||||
#include <sys/mntent.h>
|
|
||||||
#else
|
|
||||||
#include <mntent.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef _PATH_MNTTAB
|
|
||||||
#ifdef MNTTAB
|
|
||||||
#define _PATH_MNTTAB MNTTAB
|
|
||||||
#else
|
|
||||||
#define _PATH_MNTTAB "/etc/fstab"
|
|
||||||
#endif
|
|
||||||
#endif /* !_PATH_MNTTAB */
|
|
||||||
|
|
||||||
#ifndef _PATH_MOUNTED
|
|
||||||
#define _PATH_MOUNTED "/etc/mtab"
|
|
||||||
#endif /* !_PATH_MOUNTED */
|
|
||||||
|
|
||||||
#ifndef MNTTYPE_CDROM
|
|
||||||
#define MNTTYPE_CDROM "iso9660"
|
|
||||||
#endif
|
|
||||||
#ifndef MNTTYPE_SUPER
|
|
||||||
#define MNTTYPE_SUPER "supermount"
|
|
||||||
#endif
|
|
||||||
#endif /* USE_MNTENT */
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
static dev_t SDL_cdmode[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
/* Some ioctl() errno values which occur when the tray is empty */
|
|
||||||
#ifndef ENOMEDIUM
|
|
||||||
#define ENOMEDIUM ENOENT
|
|
||||||
#endif
|
|
||||||
#define ERRNO_TRAYEMPTY(errno) \
|
|
||||||
((errno == EIO) || (errno == ENOENT) || \
|
|
||||||
(errno == EINVAL) || (errno == ENOMEDIUM))
|
|
||||||
|
|
||||||
/* Check a drive to see if it is a CD-ROM */
|
|
||||||
static int CheckDrive(char *drive, char *mnttype, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int is_cd, cdfd;
|
|
||||||
struct cdrom_subchnl info;
|
|
||||||
|
|
||||||
/* If it doesn't exist, return -1 */
|
|
||||||
if ( stat(drive, stbuf) < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it does exist, verify that it's an available CD-ROM */
|
|
||||||
is_cd = 0;
|
|
||||||
if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
|
|
||||||
cdfd = open(drive, (O_RDONLY|O_NONBLOCK), 0);
|
|
||||||
if ( cdfd >= 0 ) {
|
|
||||||
info.cdsc_format = CDROM_MSF;
|
|
||||||
/* Under Linux, EIO occurs when a disk is not present.
|
|
||||||
*/
|
|
||||||
if ( (ioctl(cdfd, CDROMSUBCHNL, &info) == 0) ||
|
|
||||||
ERRNO_TRAYEMPTY(errno) ) {
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
close(cdfd);
|
|
||||||
}
|
|
||||||
#ifdef USE_MNTENT
|
|
||||||
/* Even if we can't read it, it might be mounted */
|
|
||||||
else if ( mnttype && (SDL_strcmp(mnttype, MNTTYPE_CDROM) == 0) ) {
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
return(is_cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds < MAX_DRIVES ) {
|
|
||||||
/* Check to make sure it's not already in our list.
|
|
||||||
This can happen when we see a drive via symbolic link.
|
|
||||||
*/
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
if ( stbuf->st_rdev == SDL_cdmode[i] ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add this drive to our list */
|
|
||||||
i = SDL_numcds;
|
|
||||||
SDL_cdlist[i] = SDL_strdup(drive);
|
|
||||||
if ( SDL_cdlist[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_cdmode[i] = stbuf->st_rdev;
|
|
||||||
++SDL_numcds;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_MNTENT
|
|
||||||
static void CheckMounts(const char *mtab)
|
|
||||||
{
|
|
||||||
FILE *mntfp;
|
|
||||||
struct mntent *mntent;
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
mntfp = setmntent(mtab, "r");
|
|
||||||
if ( mntfp != NULL ) {
|
|
||||||
char *tmp;
|
|
||||||
char *mnt_type;
|
|
||||||
size_t mnt_type_len;
|
|
||||||
char *mnt_dev;
|
|
||||||
size_t mnt_dev_len;
|
|
||||||
|
|
||||||
while ( (mntent=getmntent(mntfp)) != NULL ) {
|
|
||||||
mnt_type_len = SDL_strlen(mntent->mnt_type) + 1;
|
|
||||||
mnt_type = SDL_stack_alloc(char, mnt_type_len);
|
|
||||||
if (mnt_type == NULL)
|
|
||||||
continue; /* maybe you'll get lucky next time. */
|
|
||||||
|
|
||||||
mnt_dev_len = SDL_strlen(mntent->mnt_fsname) + 1;
|
|
||||||
mnt_dev = SDL_stack_alloc(char, mnt_dev_len);
|
|
||||||
if (mnt_dev == NULL) {
|
|
||||||
SDL_stack_free(mnt_type);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_strlcpy(mnt_type, mntent->mnt_type, mnt_type_len);
|
|
||||||
SDL_strlcpy(mnt_dev, mntent->mnt_fsname, mnt_dev_len);
|
|
||||||
|
|
||||||
/* Handle "supermount" filesystem mounts */
|
|
||||||
if ( SDL_strcmp(mnt_type, MNTTYPE_SUPER) == 0 ) {
|
|
||||||
tmp = SDL_strstr(mntent->mnt_opts, "fs=");
|
|
||||||
if ( tmp ) {
|
|
||||||
SDL_stack_free(mnt_type);
|
|
||||||
mnt_type = SDL_strdup(tmp + SDL_strlen("fs="));
|
|
||||||
if ( mnt_type ) {
|
|
||||||
tmp = SDL_strchr(mnt_type, ',');
|
|
||||||
if ( tmp ) {
|
|
||||||
*tmp = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tmp = SDL_strstr(mntent->mnt_opts, "dev=");
|
|
||||||
if ( tmp ) {
|
|
||||||
SDL_stack_free(mnt_dev);
|
|
||||||
mnt_dev = SDL_strdup(tmp + SDL_strlen("dev="));
|
|
||||||
if ( mnt_dev ) {
|
|
||||||
tmp = SDL_strchr(mnt_dev, ',');
|
|
||||||
if ( tmp ) {
|
|
||||||
*tmp = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( SDL_strcmp(mnt_type, MNTTYPE_CDROM) == 0 ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Checking mount path from %s: %s mounted on %s of %s\n",
|
|
||||||
mtab, mnt_dev, mntent->mnt_dir, mnt_type);
|
|
||||||
#endif
|
|
||||||
if (CheckDrive(mnt_dev, mnt_type, &stbuf) > 0) {
|
|
||||||
AddDrive(mnt_dev, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_stack_free(mnt_dev);
|
|
||||||
SDL_stack_free(mnt_type);
|
|
||||||
}
|
|
||||||
endmntent(mntfp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif /* USE_MNTENT */
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
/* checklist: /dev/cdrom, /dev/hd?, /dev/scd? /dev/sr? */
|
|
||||||
static char *checklist[] = {
|
|
||||||
"cdrom", "?a hd?", "?0 scd?", "?0 sr?", NULL
|
|
||||||
};
|
|
||||||
char *SDLcdrom;
|
|
||||||
int i, j, exists;
|
|
||||||
char drive[32];
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Look in the environment for our CD-ROM drive list */
|
|
||||||
SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
|
|
||||||
if ( SDLcdrom != NULL ) {
|
|
||||||
char *cdpath, *delim;
|
|
||||||
size_t len = SDL_strlen(SDLcdrom)+1;
|
|
||||||
cdpath = SDL_stack_alloc(char, len);
|
|
||||||
if ( cdpath != NULL ) {
|
|
||||||
SDL_strlcpy(cdpath, SDLcdrom, len);
|
|
||||||
SDLcdrom = cdpath;
|
|
||||||
do {
|
|
||||||
delim = SDL_strchr(SDLcdrom, ':');
|
|
||||||
if ( delim ) {
|
|
||||||
*delim++ = '\0';
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Checking CD-ROM drive from SDL_CDROM: %s\n", SDLcdrom);
|
|
||||||
#endif
|
|
||||||
if ( CheckDrive(SDLcdrom, NULL, &stbuf) > 0 ) {
|
|
||||||
AddDrive(SDLcdrom, &stbuf);
|
|
||||||
}
|
|
||||||
if ( delim ) {
|
|
||||||
SDLcdrom = delim;
|
|
||||||
} else {
|
|
||||||
SDLcdrom = NULL;
|
|
||||||
}
|
|
||||||
} while ( SDLcdrom );
|
|
||||||
SDL_stack_free(cdpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_MNTENT
|
|
||||||
/* Check /dev/cdrom first :-) */
|
|
||||||
if (CheckDrive("/dev/cdrom", NULL, &stbuf) > 0) {
|
|
||||||
AddDrive("/dev/cdrom", &stbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now check the currently mounted CD drives */
|
|
||||||
CheckMounts(_PATH_MOUNTED);
|
|
||||||
|
|
||||||
/* Finally check possible mountable drives in /etc/fstab */
|
|
||||||
CheckMounts(_PATH_MNTTAB);
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
#endif /* USE_MNTENT */
|
|
||||||
|
|
||||||
/* Scan the system for CD-ROM drives.
|
|
||||||
Not always 100% reliable, so use the USE_MNTENT code above first.
|
|
||||||
*/
|
|
||||||
for ( i=0; checklist[i]; ++i ) {
|
|
||||||
if ( checklist[i][0] == '?' ) {
|
|
||||||
char *insert;
|
|
||||||
exists = 1;
|
|
||||||
for ( j=checklist[i][1]; exists; ++j ) {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
|
|
||||||
insert = SDL_strchr(drive, '?');
|
|
||||||
if ( insert != NULL ) {
|
|
||||||
*insert = j;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Checking possible CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
switch (CheckDrive(drive, NULL, &stbuf)) {
|
|
||||||
/* Drive exists and is a CD-ROM */
|
|
||||||
case 1:
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
break;
|
|
||||||
/* Drive exists, but isn't a CD-ROM */
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
/* Drive doesn't exist */
|
|
||||||
case -1:
|
|
||||||
exists = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Checking possible CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
if ( CheckDrive(drive, NULL, &stbuf) > 0 ) {
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* General ioctl() CD-ROM command function */
|
|
||||||
static int SDL_SYS_CDioctl(int id, int command, void *arg)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = ioctl(id, command, arg);
|
|
||||||
if ( retval < 0 ) {
|
|
||||||
SDL_SetError("ioctl() error: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
return(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
return(open(SDL_cdlist[drive], (O_RDONLY|O_NONBLOCK), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
struct cdrom_tochdr toc;
|
|
||||||
int i, okay;
|
|
||||||
struct cdrom_tocentry entry;
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc) == 0 ) {
|
|
||||||
cdrom->numtracks = toc.cdth_trk1-toc.cdth_trk0+1;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
/* Read all the track TOC entries */
|
|
||||||
for ( i=0; i<=cdrom->numtracks; ++i ) {
|
|
||||||
if ( i == cdrom->numtracks ) {
|
|
||||||
cdrom->track[i].id = CDROM_LEADOUT;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].id = toc.cdth_trk0+i;
|
|
||||||
}
|
|
||||||
entry.cdte_track = cdrom->track[i].id;
|
|
||||||
entry.cdte_format = CDROM_MSF;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCENTRY,
|
|
||||||
&entry) < 0 ) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if ( entry.cdte_ctrl & CDROM_DATA_TRACK ) {
|
|
||||||
cdrom->track[i].type = SDL_DATA_TRACK;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].type = SDL_AUDIO_TRACK;
|
|
||||||
}
|
|
||||||
cdrom->track[i].offset = MSF_TO_FRAMES(
|
|
||||||
entry.cdte_addr.msf.minute,
|
|
||||||
entry.cdte_addr.msf.second,
|
|
||||||
entry.cdte_addr.msf.frame);
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if ( i > 0 ) {
|
|
||||||
cdrom->track[i-1].length =
|
|
||||||
cdrom->track[i].offset-
|
|
||||||
cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( i == (cdrom->numtracks+1) ) {
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
struct cdrom_tochdr toc;
|
|
||||||
struct cdrom_subchnl info;
|
|
||||||
|
|
||||||
info.cdsc_format = CDROM_MSF;
|
|
||||||
if ( ioctl(cdrom->id, CDROMSUBCHNL, &info) < 0 ) {
|
|
||||||
if ( ERRNO_TRAYEMPTY(errno) ) {
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
} else {
|
|
||||||
status = CD_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (info.cdsc_audiostatus) {
|
|
||||||
case CDROM_AUDIO_INVALID:
|
|
||||||
case CDROM_AUDIO_NO_STATUS:
|
|
||||||
/* Try to determine if there's a CD available */
|
|
||||||
if (ioctl(cdrom->id, CDROMREADTOCHDR, &toc)==0)
|
|
||||||
status = CD_STOPPED;
|
|
||||||
else
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_COMPLETED:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_PLAY:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_PAUSED:
|
|
||||||
/* Workaround buggy CD-ROM drive */
|
|
||||||
if ( info.cdsc_trk == CDROM_LEADOUT ) {
|
|
||||||
status = CD_STOPPED;
|
|
||||||
} else {
|
|
||||||
status = CD_PAUSED;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( position ) {
|
|
||||||
if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
|
|
||||||
*position = MSF_TO_FRAMES(
|
|
||||||
info.cdsc_absaddr.msf.minute,
|
|
||||||
info.cdsc_absaddr.msf.second,
|
|
||||||
info.cdsc_absaddr.msf.frame);
|
|
||||||
} else {
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
struct cdrom_msf playtime;
|
|
||||||
|
|
||||||
FRAMES_TO_MSF(start,
|
|
||||||
&playtime.cdmsf_min0, &playtime.cdmsf_sec0, &playtime.cdmsf_frame0);
|
|
||||||
FRAMES_TO_MSF(start+length,
|
|
||||||
&playtime.cdmsf_min1, &playtime.cdmsf_sec1, &playtime.cdmsf_frame1);
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
|
|
||||||
playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
|
|
||||||
playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
|
|
||||||
#endif
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDROMPLAYMSF, &playtime));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDROMPAUSE, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDROMRESUME, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDROMSTOP, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDROMEJECT, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
close(cdrom->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_LINUX */
|
|
||||||
|
|
@ -1,525 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_MACOS
|
|
||||||
|
|
||||||
/* MacOS functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
#include <Devices.h>
|
|
||||||
#include <Files.h>
|
|
||||||
#include <LowMem.h> /* Use entry table macros, not functions in InterfaceLib */
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
#include "SDL_syscdrom_c.h"
|
|
||||||
|
|
||||||
/* Added by Matt Slot */
|
|
||||||
#if !defined(LMGetUnitTableEntryCount)
|
|
||||||
#define LMGetUnitTableEntryCount() *(short *)0x01D2
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 26
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static long SDL_cdversion = 0;
|
|
||||||
static struct {
|
|
||||||
short dRefNum;
|
|
||||||
short driveNum;
|
|
||||||
long frames;
|
|
||||||
char name[256];
|
|
||||||
Boolean hasAudio;
|
|
||||||
} SDL_cdlist[MAX_DRIVES];
|
|
||||||
static StringPtr gDriverName = "\p.AppleCD";
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
static short SDL_SYS_ShortToBCD(short value)
|
|
||||||
{
|
|
||||||
return((value % 10) + (value / 10) * 0x10); /* Convert value to BCD */
|
|
||||||
}
|
|
||||||
|
|
||||||
static short SDL_SYS_BCDToShort(short value)
|
|
||||||
{
|
|
||||||
return((value % 0x10) + (value / 0x10) * 10); /* Convert value from BCD */
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
SInt16 dRefNum = 0;
|
|
||||||
SInt16 first, last;
|
|
||||||
|
|
||||||
SDL_numcds = 0;
|
|
||||||
|
|
||||||
/* Check that the software is available */
|
|
||||||
if (Gestalt(kGestaltAudioCDSelector, &SDL_cdversion) ||
|
|
||||||
!SDL_cdversion) return(0);
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Walk the list, count each AudioCD driver, and save the refnums */
|
|
||||||
first = -1;
|
|
||||||
last = 0 - LMGetUnitTableEntryCount();
|
|
||||||
for(dRefNum = first; dRefNum >= last; dRefNum--) {
|
|
||||||
Str255 driverName;
|
|
||||||
StringPtr namePtr;
|
|
||||||
DCtlHandle deviceEntry;
|
|
||||||
|
|
||||||
deviceEntry = GetDCtlEntry(dRefNum);
|
|
||||||
if (! deviceEntry) continue;
|
|
||||||
|
|
||||||
/* Is this an .AppleCD ? */
|
|
||||||
namePtr = (*deviceEntry)->dCtlFlags & (1L << dRAMBased) ?
|
|
||||||
((StringPtr) ((DCtlPtr) deviceEntry)->dCtlDriver + 18) :
|
|
||||||
((StringPtr) (*deviceEntry)->dCtlDriver + 18);
|
|
||||||
BlockMoveData(namePtr, driverName, namePtr[0]+1);
|
|
||||||
if (driverName[0] > gDriverName[0]) driverName[0] = gDriverName[0];
|
|
||||||
if (! EqualString(driverName, gDriverName, false, false)) continue;
|
|
||||||
|
|
||||||
/* Record the basic info for each drive */
|
|
||||||
SDL_cdlist[SDL_numcds].dRefNum = dRefNum;
|
|
||||||
BlockMoveData(namePtr + 1, SDL_cdlist[SDL_numcds].name, namePtr[0]);
|
|
||||||
SDL_cdlist[SDL_numcds].name[namePtr[0]] = 0;
|
|
||||||
SDL_cdlist[SDL_numcds].hasAudio = false;
|
|
||||||
SDL_numcds++;
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int get_drivenum(int drive)
|
|
||||||
{
|
|
||||||
QHdr *driveQ = GetDrvQHdr();
|
|
||||||
DrvQEl *driveElem;
|
|
||||||
|
|
||||||
/* Update the drive number */
|
|
||||||
SDL_cdlist[drive].driveNum = 0;
|
|
||||||
if ( driveQ->qTail ) {
|
|
||||||
driveQ->qTail->qLink = 0;
|
|
||||||
}
|
|
||||||
for ( driveElem=(DrvQEl *)driveQ->qHead; driveElem;
|
|
||||||
driveElem = (DrvQEl *)driveElem->qLink ) {
|
|
||||||
if ( driveElem->dQRefNum == SDL_cdlist[drive].dRefNum ) {
|
|
||||||
SDL_cdlist[drive].driveNum = driveElem->dQDrive;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(SDL_cdlist[drive].driveNum);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
return(drive);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
CDCntrlParam cdpb;
|
|
||||||
CDTrackData tracks[SDL_MAX_TRACKS];
|
|
||||||
long i, leadout;
|
|
||||||
|
|
||||||
/* Get the number of tracks on the CD by examining the TOC */
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kReadTOC;
|
|
||||||
cdpb.csParam.words[0] = kGetTrackRange;
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
cdrom->numtracks =
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) -
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS )
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
cdrom->status = CD_STOPPED;
|
|
||||||
cdrom->cur_track = 0; /* Apparently these are set elsewhere */
|
|
||||||
cdrom->cur_frame = 0; /* Apparently these are set elsewhere */
|
|
||||||
|
|
||||||
|
|
||||||
/* Get the lead out area of the CD by examining the TOC */
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kReadTOC;
|
|
||||||
cdpb.csParam.words[0] = kGetLeadOutArea;
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
leadout = MSF_TO_FRAMES(
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]),
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]),
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.bytes[2]));
|
|
||||||
|
|
||||||
/* Get an array of track locations by examining the TOC */
|
|
||||||
SDL_memset(tracks, 0, sizeof(tracks));
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kReadTOC;
|
|
||||||
cdpb.csParam.words[0] = kGetTrackEntries; /* Type of Query */
|
|
||||||
* ((long *) (cdpb.csParam.words+1)) = (long) tracks;
|
|
||||||
cdpb.csParam.words[3] = cdrom->numtracks * sizeof(tracks[0]);
|
|
||||||
* ((char *) (cdpb.csParam.words+4)) = 1; /* First track */
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read all the track TOC entries */
|
|
||||||
SDL_cdlist[cdrom->id].hasAudio = false;
|
|
||||||
for ( i=0; i<cdrom->numtracks; ++i )
|
|
||||||
{
|
|
||||||
cdrom->track[i].id = i+1;
|
|
||||||
if (tracks[i].entry.control & kDataTrackMask)
|
|
||||||
cdrom->track[i].type = SDL_DATA_TRACK;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cdrom->track[i].type = SDL_AUDIO_TRACK;
|
|
||||||
SDL_cdlist[SDL_numcds].hasAudio = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
cdrom->track[i].offset = MSF_TO_FRAMES(
|
|
||||||
SDL_SYS_BCDToShort(tracks[i].entry.min),
|
|
||||||
SDL_SYS_BCDToShort(tracks[i].entry.min),
|
|
||||||
SDL_SYS_BCDToShort(tracks[i].entry.frame));
|
|
||||||
cdrom->track[i].length = MSF_TO_FRAMES(
|
|
||||||
SDL_SYS_BCDToShort(tracks[i+1].entry.min),
|
|
||||||
SDL_SYS_BCDToShort(tracks[i+1].entry.min),
|
|
||||||
SDL_SYS_BCDToShort(tracks[i+1].entry.frame)) -
|
|
||||||
cdrom->track[i].offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Apparently SDL wants a fake last entry */
|
|
||||||
cdrom->track[i].offset = leadout;
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDCntrlParam cdpb;
|
|
||||||
CDstatus status = CD_ERROR;
|
|
||||||
Boolean spinning = false;
|
|
||||||
|
|
||||||
if (position) *position = 0;
|
|
||||||
|
|
||||||
/* Get the number of tracks on the CD by examining the TOC */
|
|
||||||
if ( ! get_drivenum(cdrom->id) ) {
|
|
||||||
return(CD_TRAYEMPTY);
|
|
||||||
}
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kReadTOC;
|
|
||||||
cdpb.csParam.words[0] = kGetTrackRange;
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(CD_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
cdrom->numtracks =
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.bytes[1]) -
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.bytes[0]) + 1;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS )
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
cdrom->cur_track = 0; /* Apparently these are set elsewhere */
|
|
||||||
cdrom->cur_frame = 0; /* Apparently these are set elsewhere */
|
|
||||||
|
|
||||||
|
|
||||||
if (1 || SDL_cdlist[cdrom->id].hasAudio) {
|
|
||||||
/* Get the current playback status */
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kAudioStatus;
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(cdpb.csParam.cd.status) {
|
|
||||||
case kStatusPlaying:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
spinning = true;
|
|
||||||
break;
|
|
||||||
case kStatusPaused:
|
|
||||||
status = CD_PAUSED;
|
|
||||||
spinning = true;
|
|
||||||
break;
|
|
||||||
case kStatusMuted:
|
|
||||||
status = CD_PLAYING; /* What should I do here? */
|
|
||||||
spinning = true;
|
|
||||||
break;
|
|
||||||
case kStatusDone:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
spinning = true;
|
|
||||||
break;
|
|
||||||
case kStatusStopped:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
spinning = false;
|
|
||||||
break;
|
|
||||||
case kStatusError:
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
spinning = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spinning && position) *position = MSF_TO_FRAMES(
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.cd.minute),
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.cd.second),
|
|
||||||
SDL_SYS_BCDToShort(cdpb.csParam.cd.frame));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
status = CD_ERROR; /* What should I do here? */
|
|
||||||
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
CDCntrlParam cdpb;
|
|
||||||
|
|
||||||
/* Pause the current audio playback to avoid audible artifacts */
|
|
||||||
if ( SDL_SYS_CDPause(cdrom) < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Specify the AudioCD playback mode */
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kSetPlayMode;
|
|
||||||
cdpb.csParam.bytes[0] = false; /* Repeat? */
|
|
||||||
cdpb.csParam.bytes[1] = kPlayModeSequential; /* Play mode */
|
|
||||||
/* ¥¥¥ÊTreat as soft error, NEC Drive doesnt support this call ¥¥¥ */
|
|
||||||
PBControlSync((ParmBlkPtr) &cdpb);
|
|
||||||
|
|
||||||
#if 1
|
|
||||||
/* Specify the end of audio playback */
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kAudioStop;
|
|
||||||
cdpb.csParam.words[0] = kBlockPosition; /* Position Mode */
|
|
||||||
*(long *) (cdpb.csParam.words + 1) = start+length-1; /* Search Address */
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Specify the start of audio playback, and start it */
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kAudioPlay;
|
|
||||||
cdpb.csParam.words[0] = kBlockPosition; /* Position Mode */
|
|
||||||
*(long *) (cdpb.csParam.words + 1) = start+1; /* Search Address */
|
|
||||||
cdpb.csParam.words[3] = false; /* Stop address? */
|
|
||||||
cdpb.csParam.words[4] = kStereoPlayMode; /* Audio Play Mode */
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
/* Specify the end of audio playback */
|
|
||||||
FRAMES_TO_MSF(start+length, &m, &s, &f);
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kAudioStop;
|
|
||||||
cdpb.csParam.words[0] = kTrackPosition; /* Position Mode */
|
|
||||||
cdpb.csParam.words[1] = 0; /* Search Address (hiword)*/
|
|
||||||
cdpb.csParam.words[2] = /* Search Address (loword)*/
|
|
||||||
SDL_SYS_ShortToBCD(cdrom->numtracks);
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Specify the start of audio playback, and start it */
|
|
||||||
FRAMES_TO_MSF(start, &m, &s, &f);
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kAudioPlay;
|
|
||||||
cdpb.csParam.words[0] = kTrackPosition; /* Position Mode */
|
|
||||||
cdpb.csParam.words[1] = 0; /* Search Address (hiword)*/
|
|
||||||
cdpb.csParam.words[2] = SDL_SYS_ShortToBCD(1); /* Search Address (loword)*/
|
|
||||||
cdpb.csParam.words[3] = false; /* Stop address? */
|
|
||||||
cdpb.csParam.words[4] = kStereoPlayMode; /* Audio Play Mode */
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
CDCntrlParam cdpb;
|
|
||||||
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kAudioPause;
|
|
||||||
cdpb.csParam.words[0] = 0; /* Pause/Continue Flag (hiword) */
|
|
||||||
cdpb.csParam.words[1] = 1; /* Pause/Continue Flag (loword) */
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
CDCntrlParam cdpb;
|
|
||||||
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kAudioPause;
|
|
||||||
cdpb.csParam.words[0] = 0; /* Pause/Continue Flag (hiword) */
|
|
||||||
cdpb.csParam.words[1] = 0; /* Pause/Continue Flag (loword) */
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
CDCntrlParam cdpb;
|
|
||||||
|
|
||||||
SDL_memset(&cdpb, 0, sizeof(cdpb));
|
|
||||||
cdpb.ioVRefNum = SDL_cdlist[cdrom->id].driveNum;
|
|
||||||
cdpb.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cdpb.csCode = kAudioStop;
|
|
||||||
cdpb.csParam.words[0] = 0; /* Position Mode */
|
|
||||||
cdpb.csParam.words[1] = 0; /* Search Address (hiword) */
|
|
||||||
cdpb.csParam.words[2] = 0; /* Search Address (loword) */
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cdpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
Boolean disk = false;
|
|
||||||
QHdr *driveQ = GetDrvQHdr();
|
|
||||||
DrvQEl *driveElem;
|
|
||||||
HParamBlockRec hpb;
|
|
||||||
ParamBlockRec cpb;
|
|
||||||
|
|
||||||
for ( driveElem = (DrvQEl *) driveQ->qHead; driveElem; driveElem =
|
|
||||||
(driveElem) ? ((DrvQEl *) driveElem->qLink) :
|
|
||||||
((DrvQEl *) driveQ->qHead) ) {
|
|
||||||
if ( driveQ->qTail ) {
|
|
||||||
driveQ->qTail->qLink = 0;
|
|
||||||
}
|
|
||||||
if ( driveElem->dQRefNum != SDL_cdlist[cdrom->id].dRefNum ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Does drive contain mounted volume? If not, skip */
|
|
||||||
SDL_memset(&hpb, 0, sizeof(hpb));
|
|
||||||
hpb.volumeParam.ioVRefNum = driveElem->dQDrive;
|
|
||||||
if ( PBHGetVInfoSync(&hpb) != noErr ) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if ( (UnmountVol(0, driveElem->dQDrive) == noErr) &&
|
|
||||||
(Eject(0, driveElem->dQDrive) == noErr) ) {
|
|
||||||
driveElem = 0; /* Clear pointer to reset our loop */
|
|
||||||
disk = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If no disk is present, just eject the tray */
|
|
||||||
if (! disk) {
|
|
||||||
SDL_memset(&cpb, 0, sizeof(cpb));
|
|
||||||
cpb.cntrlParam.ioVRefNum = 0; /* No Drive */
|
|
||||||
cpb.cntrlParam.ioCRefNum = SDL_cdlist[cdrom->id].dRefNum;
|
|
||||||
cpb.cntrlParam.csCode = kEjectTheDisc;
|
|
||||||
if ( PBControlSync((ParmBlkPtr)&cpb) != noErr ) {
|
|
||||||
SDL_SetError("PBControlSync() failed");
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
while(SDL_numcds--)
|
|
||||||
SDL_memset(SDL_cdlist + SDL_numcds, 0, sizeof(SDL_cdlist[0]));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_MACOS */
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* This is the MacOS specific header for the SDL CD-ROM API
|
|
||||||
Contributed by Matt Slot
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* AppleCD Control calls */
|
|
||||||
#define kVerifyTheDisc 5 /* Returns noErr if there is disc inserted */
|
|
||||||
#define kEjectTheDisc 7 /* Eject disc from drive */
|
|
||||||
#define kUserEject 80 /* Enable/disable the CD-ROM eject button */
|
|
||||||
#define kReadTOC 100 /* Extract various TOC information from the disc */
|
|
||||||
#define kReadQ 101 /* Extract Q subcode info for the current track */
|
|
||||||
#define kAudioTrackSearch 103 /* Start playback from the indicated position */
|
|
||||||
#define kAudioPlay 104 /* Start playback from the indicated position */
|
|
||||||
#define kAudioPause 105 /* Pause/continue the playback */
|
|
||||||
#define kAudioStop 106 /* Stop playback at the indicated position */
|
|
||||||
#define kAudioStatus 107 /* Return audio play status */
|
|
||||||
#define kAudioControl 109 /* Set the output volume for the audio channels */
|
|
||||||
#define kReadAudioVolume 112 /* Get the output volume for the audio channels */
|
|
||||||
#define kSetTrackList 122 /* Set the track program for the audio CD to play */
|
|
||||||
#define kGetTrackList 123 /* Get the track program the audio CD is playing */
|
|
||||||
#define kGetTrackIndex 124 /* Get the track index the audio CD is playing */
|
|
||||||
#define kSetPlayMode 125 /* Set the audio tracks play mode */
|
|
||||||
#define kGetPlayMode 126 /* Get the audio tracks play mode */
|
|
||||||
|
|
||||||
/* AppleCD Status calls */
|
|
||||||
#define kGetDriveType 96 /* Get the type of the physical CD-ROM drive */
|
|
||||||
#define kWhoIsThere 97 /* Get a bitmap of SCSI IDs the driver controls */
|
|
||||||
#define kGetBlockSize 98 /* Get current block size of the CD-ROM drive */
|
|
||||||
|
|
||||||
/* AppleCD other constants */
|
|
||||||
#define kBlockPosition 0 /* Position at the specified logical block number */
|
|
||||||
#define kAbsMSFPosition 1 /* Position at the specified Min/Sec/Frame (in BCD) */
|
|
||||||
#define kTrackPosition 2 /* Position at the specified track number (in BCD) */
|
|
||||||
#define kIndexPosition 3 /* Position at the nth track in program (in BCD) */
|
|
||||||
|
|
||||||
#define kMutedPlayMode 0 /* Play the audio track with no output */
|
|
||||||
#define kStereoPlayMode 9 /* Play the audio track in normal stereo */
|
|
||||||
|
|
||||||
#define kControlFieldMask 0x0D /* Bits 3,2,0 in the nibble */
|
|
||||||
#define kDataTrackMask 0x04 /* Indicates Data Track */
|
|
||||||
|
|
||||||
#define kGetTrackRange 1 /* Query TOC for track numbers */
|
|
||||||
#define kGetLeadOutArea 2 /* Query TOC for "Lead Out" end of audio data */
|
|
||||||
#define kGetTrackEntries 3 /* Query TOC for track starts and data types */
|
|
||||||
|
|
||||||
#define kStatusPlaying 0 /* Audio Play operation in progress */
|
|
||||||
#define kStatusPaused 1 /* CD-ROM device in Hold Track ("Pause") state */
|
|
||||||
#define kStatusMuted 2 /* MUTING-ON operation in progress */
|
|
||||||
#define kStatusDone 3 /* Audio Play completed */
|
|
||||||
#define kStatusError 4 /* Error occurred during audio play operation */
|
|
||||||
#define kStatusStopped 5 /* Audio play operation not requested */
|
|
||||||
|
|
||||||
#define kPlayModeSequential 0 /* Play tracks in order */
|
|
||||||
#define kPlayModeShuffled 1 /* Play tracks randomly */
|
|
||||||
#define kPlayModeProgrammed 2 /* Use custom playlist */
|
|
||||||
|
|
||||||
/* AppleCD Gestalt selectors */
|
|
||||||
#define kGestaltAudioCDSelector 'aucd'
|
|
||||||
#define kDriverVersion52 0x00000520
|
|
||||||
#define kDriverVersion51 0x00000510
|
|
||||||
#define kDriverVersion50 0x00000500
|
|
||||||
|
|
||||||
/* Drive type constants */
|
|
||||||
#define kDriveAppleCD_SC 1
|
|
||||||
#define kDriveAppleCD_SCPlus_or_150 2
|
|
||||||
#define kDriveAppleCD_300_or_300Plus 3
|
|
||||||
|
|
||||||
/* Misc constants */
|
|
||||||
#define kFirstSCSIDevice -33
|
|
||||||
#define kLastSCSIDevice -40
|
|
||||||
|
|
||||||
#if PRAGMA_STRUCT_ALIGN
|
|
||||||
#pragma options align=mac68k
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* AppleCD driver parameter block */
|
|
||||||
typedef struct CDCntrlParam {
|
|
||||||
QElemPtr qLink;
|
|
||||||
short qType;
|
|
||||||
short ioTrap;
|
|
||||||
Ptr ioCmdAddr;
|
|
||||||
IOCompletionUPP ioCompletion;
|
|
||||||
OSErr ioResult;
|
|
||||||
StringPtr ioNamePtr;
|
|
||||||
short ioVRefNum;
|
|
||||||
short ioCRefNum;
|
|
||||||
short csCode;
|
|
||||||
|
|
||||||
union {
|
|
||||||
long longs[6];
|
|
||||||
short words[11];
|
|
||||||
unsigned char bytes[22];
|
|
||||||
struct {
|
|
||||||
unsigned char status;
|
|
||||||
unsigned char play;
|
|
||||||
unsigned char control;
|
|
||||||
unsigned char minute;
|
|
||||||
unsigned char second;
|
|
||||||
unsigned char frame;
|
|
||||||
} cd;
|
|
||||||
} csParam;
|
|
||||||
|
|
||||||
} CDCntrlParam, *CDCntrlParamPtr;
|
|
||||||
|
|
||||||
typedef union CDTrackData {
|
|
||||||
long value; /* Treat as a longword value */
|
|
||||||
struct {
|
|
||||||
unsigned char reserved : 4; /* Unused by AppleCD driver */
|
|
||||||
unsigned char control : 4; /* Track flags (data track?) */
|
|
||||||
unsigned char min; /* Start of track (BCD) */
|
|
||||||
unsigned char sec; /* Start of track (BCD) */
|
|
||||||
unsigned char frame; /* Start of track (BCD) */
|
|
||||||
} entry; /* Broken into fields */
|
|
||||||
} CDTrackData, *CDTrackPtr;
|
|
||||||
|
|
||||||
#if PRAGMA_STRUCT_ALIGN
|
|
||||||
#pragma options align=reset
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,360 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
|
|
||||||
This file based on Apple sample code. We haven't changed the file name,
|
|
||||||
so if you want to see the original search for it on apple.com/developer
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
#include "SDL_endian.h"
|
|
||||||
|
|
||||||
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
AudioFilePlayer.cpp
|
|
||||||
*/
|
|
||||||
#include "AudioFilePlayer.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
void ThrowResult (OSStatus result, const char* str)
|
|
||||||
{
|
|
||||||
SDL_SetError ("Error: %s %d", str, result);
|
|
||||||
throw result;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
|
|
||||||
{
|
|
||||||
if (!inDesc) {
|
|
||||||
printf ("Can't print a NULL desc!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
printf ("- - - - - - - - - - - - - - - - - - - -\n");
|
|
||||||
printf (" Sample Rate:%f\n", inDesc->mSampleRate);
|
|
||||||
printf (" Format ID:%s\n", (char*)&inDesc->mFormatID);
|
|
||||||
printf (" Format Flags:%lX\n", inDesc->mFormatFlags);
|
|
||||||
printf (" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
|
|
||||||
printf (" Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
|
|
||||||
printf (" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
|
|
||||||
printf (" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
|
|
||||||
printf (" Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
|
|
||||||
printf ("- - - - - - - - - - - - - - - - - - - -\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit *inDestUnit)
|
|
||||||
{
|
|
||||||
/*if (afp->mConnected) throw static_cast<OSStatus>(-1);*/ /* can't set dest if already engaged */
|
|
||||||
if (afp->mConnected)
|
|
||||||
return 0 ;
|
|
||||||
|
|
||||||
SDL_memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit));
|
|
||||||
|
|
||||||
OSStatus result = noErr;
|
|
||||||
|
|
||||||
|
|
||||||
/* we can "down" cast a component instance to a component */
|
|
||||||
ComponentDescription desc;
|
|
||||||
result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0);
|
|
||||||
if (result) return 0; /*THROW_RESULT("GetComponentInfo")*/
|
|
||||||
|
|
||||||
/* we're going to use this to know which convert routine to call
|
|
||||||
a v1 audio unit will have a type of 'aunt'
|
|
||||||
a v2 audio unit will have one of several different types. */
|
|
||||||
if (desc.componentType != kAudioUnitType_Output) {
|
|
||||||
result = badComponentInstance;
|
|
||||||
/*THROW_RESULT("BAD COMPONENT")*/
|
|
||||||
if (result) return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the input format of the audio unit. */
|
|
||||||
result = AudioUnitSetProperty (*inDestUnit,
|
|
||||||
kAudioUnitProperty_StreamFormat,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
0,
|
|
||||||
&afp->mFileDescription,
|
|
||||||
sizeof (afp->mFileDescription));
|
|
||||||
/*THROW_RESULT("AudioUnitSetProperty")*/
|
|
||||||
if (result) return 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon)
|
|
||||||
{
|
|
||||||
afp->mNotifier = inNotifier;
|
|
||||||
afp->mRefCon = inRefCon;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp)
|
|
||||||
{
|
|
||||||
return afp->mConnected;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp)
|
|
||||||
{
|
|
||||||
return afp->mPlayUnit;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFilePlayer_Print(AudioFilePlayer *afp)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
|
|
||||||
printf ("- - - - - - - - - - - - - - \n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame)
|
|
||||||
{
|
|
||||||
SInt64 position = frame * 2352;
|
|
||||||
|
|
||||||
afp->mStartFrame = frame;
|
|
||||||
afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp)
|
|
||||||
{
|
|
||||||
return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame)
|
|
||||||
{
|
|
||||||
SInt64 position = frame * 2352;
|
|
||||||
|
|
||||||
afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position);
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_AudioFilePlayer(AudioFilePlayer *afp)
|
|
||||||
{
|
|
||||||
if (afp != NULL)
|
|
||||||
{
|
|
||||||
afp->Disconnect(afp);
|
|
||||||
|
|
||||||
if (afp->mAudioFileManager) {
|
|
||||||
delete_AudioFileManager(afp->mAudioFileManager);
|
|
||||||
afp->mAudioFileManager = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (afp->mForkRefNum) {
|
|
||||||
FSCloseFork (afp->mForkRefNum);
|
|
||||||
afp->mForkRefNum = 0;
|
|
||||||
}
|
|
||||||
SDL_free(afp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int AudioFilePlayer_Connect(AudioFilePlayer *afp)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0));
|
|
||||||
#endif
|
|
||||||
if (!afp->mConnected)
|
|
||||||
{
|
|
||||||
if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* set the render callback for the file data to be supplied to the sound converter AU */
|
|
||||||
afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc;
|
|
||||||
afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager;
|
|
||||||
|
|
||||||
OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
|
|
||||||
kAudioUnitProperty_SetRenderCallback,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
0,
|
|
||||||
&afp->mInputCallback,
|
|
||||||
sizeof(afp->mInputCallback));
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioUnitSetProperty")*/
|
|
||||||
afp->mConnected = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* warning noted, now please go away ;-) */
|
|
||||||
/* #warning This should redirect the calling of notification code to some other thread */
|
|
||||||
static void AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus)
|
|
||||||
{
|
|
||||||
if (afp->mNotifier) {
|
|
||||||
(*afp->mNotifier) (afp->mRefCon, inStatus);
|
|
||||||
} else {
|
|
||||||
SDL_SetError ("Notification posted with no notifier in place");
|
|
||||||
|
|
||||||
if (inStatus == kAudioFilePlay_FileIsFinished)
|
|
||||||
afp->Disconnect(afp);
|
|
||||||
else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
|
|
||||||
afp->Disconnect(afp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFilePlayer_Disconnect (AudioFilePlayer *afp)
|
|
||||||
{
|
|
||||||
#if DEBUG
|
|
||||||
printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0));
|
|
||||||
#endif
|
|
||||||
if (afp->mConnected)
|
|
||||||
{
|
|
||||||
afp->mConnected = 0;
|
|
||||||
|
|
||||||
afp->mInputCallback.inputProc = 0;
|
|
||||||
afp->mInputCallback.inputProcRefCon = 0;
|
|
||||||
OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
|
|
||||||
kAudioUnitProperty_SetRenderCallback,
|
|
||||||
kAudioUnitScope_Input,
|
|
||||||
0,
|
|
||||||
&afp->mInputCallback,
|
|
||||||
sizeof(afp->mInputCallback));
|
|
||||||
if (result)
|
|
||||||
SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
|
|
||||||
|
|
||||||
afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
UInt32 offset;
|
|
||||||
UInt32 blockSize;
|
|
||||||
} SSNDData;
|
|
||||||
|
|
||||||
static int AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize)
|
|
||||||
{
|
|
||||||
ContainerChunk chunkHeader;
|
|
||||||
ChunkHeader chunk;
|
|
||||||
SSNDData ssndData;
|
|
||||||
|
|
||||||
OSErr result;
|
|
||||||
HFSUniStr255 dfName;
|
|
||||||
ByteCount actual;
|
|
||||||
SInt64 offset;
|
|
||||||
|
|
||||||
/* Open the data fork of the input file */
|
|
||||||
result = FSGetDataForkName(&dfName);
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")*/
|
|
||||||
|
|
||||||
result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum);
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")*/
|
|
||||||
|
|
||||||
/* Read the file header, and check if it's indeed an AIFC file */
|
|
||||||
result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual);
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
|
|
||||||
|
|
||||||
if (SDL_SwapBE32(chunkHeader.ckID) != 'FORM') {
|
|
||||||
result = -1;
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");*/
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SDL_SwapBE32(chunkHeader.formType) != 'AIFC') {
|
|
||||||
result = -1;
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Search for the SSND chunk. We ignore all compression etc. information
|
|
||||||
in other chunks. Of course that is kind of evil, but for now we are lazy
|
|
||||||
and rely on the cdfs to always give us the same fixed format.
|
|
||||||
TODO: Parse the COMM chunk we currently skip to fill in mFileDescription.
|
|
||||||
*/
|
|
||||||
offset = 0;
|
|
||||||
do {
|
|
||||||
result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual);
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
|
|
||||||
|
|
||||||
chunk.ckID = SDL_SwapBE32(chunk.ckID);
|
|
||||||
chunk.ckSize = SDL_SwapBE32(chunk.ckSize);
|
|
||||||
|
|
||||||
/* Skip the chunk data */
|
|
||||||
offset = chunk.ckSize;
|
|
||||||
} while (chunk.ckID != 'SSND');
|
|
||||||
|
|
||||||
/* Read the header of the SSND chunk. After this, we are positioned right
|
|
||||||
at the start of the audio data. */
|
|
||||||
result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual);
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
|
|
||||||
|
|
||||||
ssndData.offset = SDL_SwapBE32(ssndData.offset);
|
|
||||||
|
|
||||||
result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset);
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")*/
|
|
||||||
|
|
||||||
/* Data size */
|
|
||||||
*outFileDataSize = chunk.ckSize - ssndData.offset - 8;
|
|
||||||
|
|
||||||
/* File format */
|
|
||||||
afp->mFileDescription.mSampleRate = 44100;
|
|
||||||
afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
|
|
||||||
afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
|
|
||||||
afp->mFileDescription.mBytesPerPacket = 4;
|
|
||||||
afp->mFileDescription.mFramesPerPacket = 1;
|
|
||||||
afp->mFileDescription.mBytesPerFrame = 4;
|
|
||||||
afp->mFileDescription.mChannelsPerFrame = 2;
|
|
||||||
afp->mFileDescription.mBitsPerChannel = 16;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef)
|
|
||||||
{
|
|
||||||
SInt64 fileDataSize = 0;
|
|
||||||
|
|
||||||
AudioFilePlayer *afp = (AudioFilePlayer *) SDL_malloc(sizeof (AudioFilePlayer));
|
|
||||||
if (afp == NULL)
|
|
||||||
return NULL;
|
|
||||||
SDL_memset(afp, '\0', sizeof (*afp));
|
|
||||||
|
|
||||||
#define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(SetDestination);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(SetNotifier);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(SetStartFrame);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(SetStopFrame);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(Connect);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(Disconnect);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(DoNotification);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(IsConnected);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(GetDestUnit);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(Print);
|
|
||||||
SET_AUDIOFILEPLAYER_METHOD(OpenFile);
|
|
||||||
#undef SET_AUDIOFILEPLAYER_METHOD
|
|
||||||
|
|
||||||
if (!afp->OpenFile (afp, inFileRef, &fileDataSize))
|
|
||||||
{
|
|
||||||
SDL_free(afp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we want about 4 seconds worth of data for the buffer */
|
|
||||||
int bytesPerSecond = (UInt32) (4 * afp->mFileDescription.mSampleRate * afp->mFileDescription.mBytesPerFrame);
|
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
printf("File format:\n");
|
|
||||||
PrintStreamDesc (&afp->mFileDescription);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
|
|
||||||
fileDataSize,
|
|
||||||
bytesPerSecond);
|
|
||||||
if (afp->mAudioFileManager == NULL)
|
|
||||||
{
|
|
||||||
delete_AudioFilePlayer(afp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return afp;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,178 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
|
|
||||||
This file based on Apple sample code. We haven't changed the file name,
|
|
||||||
so if you want to see the original search for it on apple.com/developer
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
AudioFilePlayer.h
|
|
||||||
*/
|
|
||||||
#ifndef __AudioFilePlayer_H__
|
|
||||||
#define __AudioFilePlayer_H__
|
|
||||||
|
|
||||||
#include <CoreServices/CoreServices.h>
|
|
||||||
|
|
||||||
#include <AudioUnit/AudioUnit.h>
|
|
||||||
#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050
|
|
||||||
#include <AudioUnit/AUNTComponent.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1050)
|
|
||||||
typedef SInt16 FSIORefNum;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "SDL_error.h"
|
|
||||||
|
|
||||||
const char* AudioFilePlayerErrorStr (OSStatus error);
|
|
||||||
|
|
||||||
/*
|
|
||||||
void ThrowResult (OSStatus result, const char *str);
|
|
||||||
|
|
||||||
#define THROW_RESULT(str) \
|
|
||||||
if (result) { \
|
|
||||||
ThrowResult (result, str); \
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
typedef void (*AudioFilePlayNotifier)(void *inRefCon,
|
|
||||||
OSStatus inStatus);
|
|
||||||
|
|
||||||
enum {
|
|
||||||
kAudioFilePlayErr_FilePlayUnderrun = -10000,
|
|
||||||
kAudioFilePlay_FileIsFinished = -10001,
|
|
||||||
kAudioFilePlay_PlayerIsUninitialized = -10002
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct S_AudioFileManager;
|
|
||||||
|
|
||||||
#pragma mark __________ AudioFilePlayer
|
|
||||||
typedef struct S_AudioFilePlayer
|
|
||||||
{
|
|
||||||
/*public:*/
|
|
||||||
int (*SetDestination)(struct S_AudioFilePlayer *afp, AudioUnit *inDestUnit);
|
|
||||||
void (*SetNotifier)(struct S_AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon);
|
|
||||||
void (*SetStartFrame)(struct S_AudioFilePlayer *afp, int frame); /* seek in the file */
|
|
||||||
int (*GetCurrentFrame)(struct S_AudioFilePlayer *afp); /* get the current frame position */
|
|
||||||
void (*SetStopFrame)(struct S_AudioFilePlayer *afp, int frame); /* set limit in the file */
|
|
||||||
int (*Connect)(struct S_AudioFilePlayer *afp);
|
|
||||||
void (*Disconnect)(struct S_AudioFilePlayer *afp);
|
|
||||||
void (*DoNotification)(struct S_AudioFilePlayer *afp, OSStatus inError);
|
|
||||||
int (*IsConnected)(struct S_AudioFilePlayer *afp);
|
|
||||||
AudioUnit (*GetDestUnit)(struct S_AudioFilePlayer *afp);
|
|
||||||
void (*Print)(struct S_AudioFilePlayer *afp);
|
|
||||||
|
|
||||||
/*private:*/
|
|
||||||
AudioUnit mPlayUnit;
|
|
||||||
FSIORefNum mForkRefNum;
|
|
||||||
|
|
||||||
AURenderCallbackStruct mInputCallback;
|
|
||||||
|
|
||||||
AudioStreamBasicDescription mFileDescription;
|
|
||||||
|
|
||||||
int mConnected;
|
|
||||||
|
|
||||||
struct S_AudioFileManager* mAudioFileManager;
|
|
||||||
|
|
||||||
AudioFilePlayNotifier mNotifier;
|
|
||||||
void* mRefCon;
|
|
||||||
|
|
||||||
int mStartFrame;
|
|
||||||
|
|
||||||
#pragma mark __________ Private_Methods
|
|
||||||
|
|
||||||
int (*OpenFile)(struct S_AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileSize);
|
|
||||||
} AudioFilePlayer;
|
|
||||||
|
|
||||||
|
|
||||||
AudioFilePlayer *new_AudioFilePlayer(const FSRef *inFileRef);
|
|
||||||
void delete_AudioFilePlayer(AudioFilePlayer *afp);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark __________ AudioFileManager
|
|
||||||
typedef struct S_AudioFileManager
|
|
||||||
{
|
|
||||||
/*public:*/
|
|
||||||
/* this method should NOT be called by an object of this class
|
|
||||||
as it is called by the parent's Disconnect() method */
|
|
||||||
void (*Disconnect)(struct S_AudioFileManager *afm);
|
|
||||||
int (*DoConnect)(struct S_AudioFileManager *afm);
|
|
||||||
OSStatus (*Read)(struct S_AudioFileManager *afm, char *buffer, ByteCount *len);
|
|
||||||
const char* (*GetFileBuffer)(struct S_AudioFileManager *afm);
|
|
||||||
const AudioFilePlayer *(*GetParent)(struct S_AudioFileManager *afm);
|
|
||||||
void (*SetPosition)(struct S_AudioFileManager *afm, SInt64 pos); /* seek/rewind in the file */
|
|
||||||
int (*GetByteCounter)(struct S_AudioFileManager *afm); /* return actual bytes streamed to audio hardware */
|
|
||||||
void (*SetEndOfFile)(struct S_AudioFileManager *afm, SInt64 pos); /* set the "EOF" (will behave just like it reached eof) */
|
|
||||||
|
|
||||||
/*protected:*/
|
|
||||||
AudioFilePlayer* mParent;
|
|
||||||
SInt16 mForkRefNum;
|
|
||||||
SInt64 mAudioDataOffset;
|
|
||||||
|
|
||||||
char* mFileBuffer;
|
|
||||||
|
|
||||||
int mByteCounter;
|
|
||||||
|
|
||||||
int mReadFromFirstBuffer;
|
|
||||||
int mLockUnsuccessful;
|
|
||||||
int mIsEngaged;
|
|
||||||
|
|
||||||
int mNumTimesAskedSinceFinished;
|
|
||||||
|
|
||||||
|
|
||||||
void* mTmpBuffer;
|
|
||||||
UInt32 mBufferSize;
|
|
||||||
UInt32 mBufferOffset;
|
|
||||||
/*public:*/
|
|
||||||
UInt32 mChunkSize;
|
|
||||||
SInt64 mFileLength;
|
|
||||||
SInt64 mReadFilePosition;
|
|
||||||
int mWriteToFirstBuffer;
|
|
||||||
int mFinishedReadingData;
|
|
||||||
|
|
||||||
/*protected:*/
|
|
||||||
OSStatus (*Render)(struct S_AudioFileManager *afm, AudioBufferList *ioData);
|
|
||||||
OSStatus (*GetFileData)(struct S_AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize);
|
|
||||||
void (*AfterRender)(struct S_AudioFileManager *afm);
|
|
||||||
|
|
||||||
/*public:*/
|
|
||||||
/*static*/
|
|
||||||
OSStatus (*FileInputProc)(void *inRefCon,
|
|
||||||
AudioUnitRenderActionFlags *ioActionFlags,
|
|
||||||
const AudioTimeStamp *inTimeStamp,
|
|
||||||
UInt32 inBusNumber,
|
|
||||||
UInt32 inNumberFrames,
|
|
||||||
AudioBufferList *ioData);
|
|
||||||
} AudioFileManager;
|
|
||||||
|
|
||||||
|
|
||||||
AudioFileManager *new_AudioFileManager (AudioFilePlayer *inParent,
|
|
||||||
SInt16 inForkRefNum,
|
|
||||||
SInt64 inFileLength,
|
|
||||||
UInt32 inChunkSize);
|
|
||||||
|
|
||||||
void delete_AudioFileManager(AudioFileManager *afm);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
@ -1,610 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
|
|
||||||
This file based on Apple sample code. We haven't changed the file name,
|
|
||||||
so if you want to see the original search for it on apple.com/developer
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
||||||
AudioFileManager.cpp
|
|
||||||
*/
|
|
||||||
#include "AudioFilePlayer.h"
|
|
||||||
#include <mach/mach.h> /* used for setting policy of thread */
|
|
||||||
#include "SDLOSXCAGuard.h"
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
/*#include <list>*/
|
|
||||||
|
|
||||||
/*typedef void *FileData;*/
|
|
||||||
typedef struct S_FileData
|
|
||||||
{
|
|
||||||
AudioFileManager *obj;
|
|
||||||
struct S_FileData *next;
|
|
||||||
} FileData;
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct S_FileReaderThread {
|
|
||||||
/*public:*/
|
|
||||||
SDLOSXCAGuard* (*GetGuard)(struct S_FileReaderThread *frt);
|
|
||||||
void (*AddReader)(struct S_FileReaderThread *frt);
|
|
||||||
void (*RemoveReader)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
|
|
||||||
int (*TryNextRead)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
|
|
||||||
|
|
||||||
int mThreadShouldDie;
|
|
||||||
|
|
||||||
/*private:*/
|
|
||||||
/*typedef std::list<AudioFileManager*> FileData;*/
|
|
||||||
|
|
||||||
SDLOSXCAGuard *mGuard;
|
|
||||||
UInt32 mThreadPriority;
|
|
||||||
|
|
||||||
int mNumReaders;
|
|
||||||
FileData *mFileData;
|
|
||||||
|
|
||||||
|
|
||||||
void (*ReadNextChunk)(struct S_FileReaderThread *frt);
|
|
||||||
int (*StartFixedPriorityThread)(struct S_FileReaderThread *frt);
|
|
||||||
/*static*/
|
|
||||||
UInt32 (*GetThreadBasePriority)(pthread_t inThread);
|
|
||||||
/*static*/
|
|
||||||
void* (*DiskReaderEntry)(void *inRefCon);
|
|
||||||
} FileReaderThread;
|
|
||||||
|
|
||||||
|
|
||||||
static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt)
|
|
||||||
{
|
|
||||||
return frt->mGuard;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* returns 1 if succeeded */
|
|
||||||
static int FileReaderThread_TryNextRead (FileReaderThread *frt, AudioFileManager* inItem)
|
|
||||||
{
|
|
||||||
int didLock = 0;
|
|
||||||
int succeeded = 0;
|
|
||||||
if (frt->mGuard->Try(frt->mGuard, &didLock))
|
|
||||||
{
|
|
||||||
/*frt->mFileData.push_back (inItem);*/
|
|
||||||
/* !!! FIXME: this could be faster with a "tail" member. --ryan. */
|
|
||||||
FileData *i = frt->mFileData;
|
|
||||||
FileData *prev = NULL;
|
|
||||||
|
|
||||||
FileData *newfd = (FileData *) SDL_malloc(sizeof (FileData));
|
|
||||||
newfd->obj = inItem;
|
|
||||||
newfd->next = NULL;
|
|
||||||
|
|
||||||
while (i != NULL) { prev = i; i = i->next; }
|
|
||||||
if (prev == NULL)
|
|
||||||
frt->mFileData = newfd;
|
|
||||||
else
|
|
||||||
prev->next = newfd;
|
|
||||||
|
|
||||||
frt->mGuard->Notify(frt->mGuard);
|
|
||||||
succeeded = 1;
|
|
||||||
|
|
||||||
if (didLock)
|
|
||||||
frt->mGuard->Unlock(frt->mGuard);
|
|
||||||
}
|
|
||||||
|
|
||||||
return succeeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FileReaderThread_AddReader(FileReaderThread *frt)
|
|
||||||
{
|
|
||||||
if (frt->mNumReaders == 0)
|
|
||||||
{
|
|
||||||
frt->mThreadShouldDie = 0;
|
|
||||||
frt->StartFixedPriorityThread (frt);
|
|
||||||
}
|
|
||||||
frt->mNumReaders++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FileReaderThread_RemoveReader (FileReaderThread *frt, AudioFileManager* inItem)
|
|
||||||
{
|
|
||||||
if (frt->mNumReaders > 0)
|
|
||||||
{
|
|
||||||
int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
|
|
||||||
|
|
||||||
/*frt->mFileData.remove (inItem);*/
|
|
||||||
FileData *i = frt->mFileData;
|
|
||||||
FileData *prev = NULL;
|
|
||||||
while (i != NULL)
|
|
||||||
{
|
|
||||||
FileData *next = i->next;
|
|
||||||
if (i->obj != inItem)
|
|
||||||
prev = i;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (prev == NULL)
|
|
||||||
frt->mFileData = next;
|
|
||||||
else
|
|
||||||
prev->next = next;
|
|
||||||
SDL_free(i);
|
|
||||||
}
|
|
||||||
i = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (--frt->mNumReaders == 0) {
|
|
||||||
frt->mThreadShouldDie = 1;
|
|
||||||
frt->mGuard->Notify(frt->mGuard); /* wake up thread so it will quit */
|
|
||||||
frt->mGuard->Wait(frt->mGuard); /* wait for thread to die */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int FileReaderThread_StartFixedPriorityThread (FileReaderThread *frt)
|
|
||||||
{
|
|
||||||
pthread_attr_t theThreadAttrs;
|
|
||||||
pthread_t pThread;
|
|
||||||
|
|
||||||
OSStatus result = pthread_attr_init(&theThreadAttrs);
|
|
||||||
if (result) return 0; /*THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")*/
|
|
||||||
|
|
||||||
result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
|
|
||||||
if (result) return 0; /*THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")*/
|
|
||||||
|
|
||||||
result = pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt);
|
|
||||||
if (result) return 0; /*THROW_RESULT("pthread_create - Create and start the thread.")*/
|
|
||||||
|
|
||||||
pthread_attr_destroy(&theThreadAttrs);
|
|
||||||
|
|
||||||
/* we've now created the thread and started it
|
|
||||||
we'll now set the priority of the thread to the nominated priority
|
|
||||||
and we'll also make the thread fixed */
|
|
||||||
thread_extended_policy_data_t theFixedPolicy;
|
|
||||||
thread_precedence_policy_data_t thePrecedencePolicy;
|
|
||||||
SInt32 relativePriority;
|
|
||||||
|
|
||||||
/* make thread fixed */
|
|
||||||
theFixedPolicy.timeshare = 0; /* set to 1 for a non-fixed thread */
|
|
||||||
result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
|
|
||||||
if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")*/
|
|
||||||
/* set priority */
|
|
||||||
/* precedency policy's "importance" value is relative to spawning thread's priority */
|
|
||||||
relativePriority = frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self());
|
|
||||||
|
|
||||||
thePrecedencePolicy.importance = relativePriority;
|
|
||||||
result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
|
|
||||||
if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread priority.")*/
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static UInt32 FileReaderThread_GetThreadBasePriority (pthread_t inThread)
|
|
||||||
{
|
|
||||||
thread_basic_info_data_t threadInfo;
|
|
||||||
policy_info_data_t thePolicyInfo;
|
|
||||||
unsigned int count;
|
|
||||||
|
|
||||||
/* get basic info */
|
|
||||||
count = THREAD_BASIC_INFO_COUNT;
|
|
||||||
thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count);
|
|
||||||
|
|
||||||
switch (threadInfo.policy) {
|
|
||||||
case POLICY_TIMESHARE:
|
|
||||||
count = POLICY_TIMESHARE_INFO_COUNT;
|
|
||||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count);
|
|
||||||
return thePolicyInfo.ts.base_priority;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case POLICY_FIFO:
|
|
||||||
count = POLICY_FIFO_INFO_COUNT;
|
|
||||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count);
|
|
||||||
if (thePolicyInfo.fifo.depressed) {
|
|
||||||
return thePolicyInfo.fifo.depress_priority;
|
|
||||||
} else {
|
|
||||||
return thePolicyInfo.fifo.base_priority;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case POLICY_RR:
|
|
||||||
count = POLICY_RR_INFO_COUNT;
|
|
||||||
thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count);
|
|
||||||
if (thePolicyInfo.rr.depressed) {
|
|
||||||
return thePolicyInfo.rr.depress_priority;
|
|
||||||
} else {
|
|
||||||
return thePolicyInfo.rr.base_priority;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *FileReaderThread_DiskReaderEntry (void *inRefCon)
|
|
||||||
{
|
|
||||||
FileReaderThread *frt = (FileReaderThread *)inRefCon;
|
|
||||||
frt->ReadNextChunk(frt);
|
|
||||||
#if DEBUG
|
|
||||||
printf ("finished with reading file\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FileReaderThread_ReadNextChunk (FileReaderThread *frt)
|
|
||||||
{
|
|
||||||
OSStatus result;
|
|
||||||
ByteCount dataChunkSize;
|
|
||||||
AudioFileManager* theItem = 0;
|
|
||||||
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
{ /* this is a scoped based lock */
|
|
||||||
int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
|
|
||||||
|
|
||||||
if (frt->mThreadShouldDie) {
|
|
||||||
frt->mGuard->Notify(frt->mGuard);
|
|
||||||
if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*if (frt->mFileData.empty())*/
|
|
||||||
if (frt->mFileData == NULL)
|
|
||||||
{
|
|
||||||
frt->mGuard->Wait(frt->mGuard);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* kill thread */
|
|
||||||
if (frt->mThreadShouldDie) {
|
|
||||||
|
|
||||||
frt->mGuard->Notify(frt->mGuard);
|
|
||||||
if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*theItem = frt->mFileData.front();*/
|
|
||||||
/*frt->mFileData.pop_front();*/
|
|
||||||
theItem = NULL;
|
|
||||||
if (frt->mFileData != NULL)
|
|
||||||
{
|
|
||||||
FileData *next = frt->mFileData->next;
|
|
||||||
theItem = frt->mFileData->obj;
|
|
||||||
SDL_free(frt->mFileData);
|
|
||||||
frt->mFileData = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
|
|
||||||
dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
|
|
||||||
else
|
|
||||||
dataChunkSize = theItem->mChunkSize;
|
|
||||||
|
|
||||||
/* this is the exit condition for the thread */
|
|
||||||
if (dataChunkSize <= 0) {
|
|
||||||
theItem->mFinishedReadingData = 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* construct pointer */
|
|
||||||
char* writePtr = (char *) (theItem->GetFileBuffer(theItem) +
|
|
||||||
(theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
|
|
||||||
|
|
||||||
/* read data */
|
|
||||||
result = theItem->Read(theItem, writePtr, &dataChunkSize);
|
|
||||||
if (result != noErr && result != eofErr) {
|
|
||||||
AudioFilePlayer *afp = (AudioFilePlayer *) theItem->GetParent(theItem);
|
|
||||||
afp->DoNotification(afp, result);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dataChunkSize != theItem->mChunkSize)
|
|
||||||
{
|
|
||||||
writePtr += dataChunkSize;
|
|
||||||
|
|
||||||
/* can't exit yet.. we still have to pass the partial buffer back */
|
|
||||||
SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize));
|
|
||||||
}
|
|
||||||
|
|
||||||
theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */
|
|
||||||
|
|
||||||
if (result == eofErr)
|
|
||||||
theItem->mReadFilePosition = theItem->mFileLength;
|
|
||||||
else
|
|
||||||
theItem->mReadFilePosition += dataChunkSize; /* increment count */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_FileReaderThread(FileReaderThread *frt)
|
|
||||||
{
|
|
||||||
if (frt != NULL)
|
|
||||||
{
|
|
||||||
delete_SDLOSXCAGuard(frt->mGuard);
|
|
||||||
SDL_free(frt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FileReaderThread *new_FileReaderThread ()
|
|
||||||
{
|
|
||||||
FileReaderThread *frt = (FileReaderThread *) SDL_malloc(sizeof (FileReaderThread));
|
|
||||||
if (frt == NULL)
|
|
||||||
return NULL;
|
|
||||||
SDL_memset(frt, '\0', sizeof (*frt));
|
|
||||||
|
|
||||||
frt->mGuard = new_SDLOSXCAGuard();
|
|
||||||
if (frt->mGuard == NULL)
|
|
||||||
{
|
|
||||||
SDL_free(frt);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
|
|
||||||
SET_FILEREADERTHREAD_METHOD(GetGuard);
|
|
||||||
SET_FILEREADERTHREAD_METHOD(AddReader);
|
|
||||||
SET_FILEREADERTHREAD_METHOD(RemoveReader);
|
|
||||||
SET_FILEREADERTHREAD_METHOD(TryNextRead);
|
|
||||||
SET_FILEREADERTHREAD_METHOD(ReadNextChunk);
|
|
||||||
SET_FILEREADERTHREAD_METHOD(StartFixedPriorityThread);
|
|
||||||
SET_FILEREADERTHREAD_METHOD(GetThreadBasePriority);
|
|
||||||
SET_FILEREADERTHREAD_METHOD(DiskReaderEntry);
|
|
||||||
#undef SET_FILEREADERTHREAD_METHOD
|
|
||||||
|
|
||||||
frt->mThreadPriority = 62;
|
|
||||||
return frt;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static FileReaderThread *sReaderThread;
|
|
||||||
|
|
||||||
|
|
||||||
static int AudioFileManager_DoConnect (AudioFileManager *afm)
|
|
||||||
{
|
|
||||||
if (!afm->mIsEngaged)
|
|
||||||
{
|
|
||||||
OSStatus result;
|
|
||||||
|
|
||||||
/*afm->mReadFilePosition = 0;*/
|
|
||||||
afm->mFinishedReadingData = 0;
|
|
||||||
|
|
||||||
afm->mNumTimesAskedSinceFinished = 0;
|
|
||||||
afm->mLockUnsuccessful = 0;
|
|
||||||
|
|
||||||
ByteCount dataChunkSize;
|
|
||||||
|
|
||||||
if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
|
|
||||||
dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
|
|
||||||
else
|
|
||||||
dataChunkSize = afm->mChunkSize;
|
|
||||||
|
|
||||||
result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
|
|
||||||
if (result) return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read")*/
|
|
||||||
|
|
||||||
afm->mReadFilePosition += dataChunkSize;
|
|
||||||
|
|
||||||
afm->mWriteToFirstBuffer = 0;
|
|
||||||
afm->mReadFromFirstBuffer = 1;
|
|
||||||
|
|
||||||
sReaderThread->AddReader(sReaderThread);
|
|
||||||
|
|
||||||
afm->mIsEngaged = 1;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
else
|
|
||||||
throw static_cast<OSStatus>(-1); */ /* thread has already been started */
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFileManager_Disconnect (AudioFileManager *afm)
|
|
||||||
{
|
|
||||||
if (afm->mIsEngaged)
|
|
||||||
{
|
|
||||||
sReaderThread->RemoveReader (sReaderThread, afm);
|
|
||||||
afm->mIsEngaged = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static OSStatus AudioFileManager_Read(AudioFileManager *afm, char *buffer, ByteCount *len)
|
|
||||||
{
|
|
||||||
return FSReadFork (afm->mForkRefNum,
|
|
||||||
fsFromStart,
|
|
||||||
afm->mReadFilePosition + afm->mAudioDataOffset,
|
|
||||||
*len,
|
|
||||||
buffer,
|
|
||||||
len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static OSStatus AudioFileManager_GetFileData (AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize)
|
|
||||||
{
|
|
||||||
if (afm->mFinishedReadingData)
|
|
||||||
{
|
|
||||||
++afm->mNumTimesAskedSinceFinished;
|
|
||||||
*inOutDataSize = 0;
|
|
||||||
*inOutData = 0;
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
|
|
||||||
#if DEBUG
|
|
||||||
printf ("* * * * * * * Can't keep up with reading file\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
afm->mParent->DoNotification (afm->mParent, kAudioFilePlayErr_FilePlayUnderrun);
|
|
||||||
*inOutDataSize = 0;
|
|
||||||
*inOutData = 0;
|
|
||||||
} else {
|
|
||||||
*inOutDataSize = afm->mChunkSize;
|
|
||||||
*inOutData = afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer + afm->mChunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
|
|
||||||
|
|
||||||
afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
|
|
||||||
|
|
||||||
return noErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFileManager_AfterRender (AudioFileManager *afm)
|
|
||||||
{
|
|
||||||
if (afm->mNumTimesAskedSinceFinished > 0)
|
|
||||||
{
|
|
||||||
int didLock = 0;
|
|
||||||
SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
|
|
||||||
if (guard->Try(guard, &didLock)) {
|
|
||||||
afm->mParent->DoNotification (afm->mParent, kAudioFilePlay_FileIsFinished);
|
|
||||||
if (didLock)
|
|
||||||
guard->Unlock(guard);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (afm->mLockUnsuccessful)
|
|
||||||
afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFileManager_SetPosition (AudioFileManager *afm, SInt64 pos)
|
|
||||||
{
|
|
||||||
if (pos < 0 || pos >= afm->mFileLength) {
|
|
||||||
SDL_SetError ("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
|
|
||||||
(unsigned int)pos, (unsigned int)afm->mFileLength);
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
afm->mReadFilePosition = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AudioFileManager_SetEndOfFile (AudioFileManager *afm, SInt64 pos)
|
|
||||||
{
|
|
||||||
if (pos <= 0 || pos > afm->mFileLength) {
|
|
||||||
SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
|
|
||||||
pos = afm->mFileLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
afm->mFileLength = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *AudioFileManager_GetFileBuffer(AudioFileManager *afm)
|
|
||||||
{
|
|
||||||
return afm->mFileBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AudioFilePlayer *AudioFileManager_GetParent(AudioFileManager *afm)
|
|
||||||
{
|
|
||||||
return afm->mParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int AudioFileManager_GetByteCounter(AudioFileManager *afm)
|
|
||||||
{
|
|
||||||
return afm->mByteCounter;
|
|
||||||
}
|
|
||||||
|
|
||||||
static OSStatus AudioFileManager_FileInputProc (void *inRefCon,
|
|
||||||
AudioUnitRenderActionFlags *ioActionFlags,
|
|
||||||
const AudioTimeStamp *inTimeStamp,
|
|
||||||
UInt32 inBusNumber,
|
|
||||||
UInt32 inNumberFrames,
|
|
||||||
AudioBufferList *ioData)
|
|
||||||
{
|
|
||||||
AudioFileManager* afm = (AudioFileManager*)inRefCon;
|
|
||||||
return afm->Render(afm, ioData);
|
|
||||||
}
|
|
||||||
|
|
||||||
static OSStatus AudioFileManager_Render (AudioFileManager *afm, AudioBufferList *ioData)
|
|
||||||
{
|
|
||||||
OSStatus result = noErr;
|
|
||||||
AudioBuffer *abuf;
|
|
||||||
UInt32 i;
|
|
||||||
|
|
||||||
for (i = 0; i < ioData->mNumberBuffers; i++) {
|
|
||||||
abuf = &ioData->mBuffers[i];
|
|
||||||
if (afm->mBufferOffset >= afm->mBufferSize) {
|
|
||||||
result = afm->GetFileData(afm, &afm->mTmpBuffer, &afm->mBufferSize);
|
|
||||||
if (result) {
|
|
||||||
SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
|
|
||||||
afm->mParent->DoNotification(afm->mParent, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
afm->mBufferOffset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (abuf->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
|
|
||||||
abuf->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
|
|
||||||
abuf->mData = (char *)afm->mTmpBuffer + afm->mBufferOffset;
|
|
||||||
afm->mBufferOffset += abuf->mDataByteSize;
|
|
||||||
|
|
||||||
afm->mByteCounter += abuf->mDataByteSize;
|
|
||||||
afm->AfterRender(afm);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void delete_AudioFileManager (AudioFileManager *afm)
|
|
||||||
{
|
|
||||||
if (afm != NULL) {
|
|
||||||
if (afm->mFileBuffer) {
|
|
||||||
free(afm->mFileBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_free(afm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
AudioFileManager *new_AudioFileManager(AudioFilePlayer *inParent,
|
|
||||||
SInt16 inForkRefNum,
|
|
||||||
SInt64 inFileLength,
|
|
||||||
UInt32 inChunkSize)
|
|
||||||
{
|
|
||||||
AudioFileManager *afm;
|
|
||||||
|
|
||||||
if (sReaderThread == NULL)
|
|
||||||
{
|
|
||||||
sReaderThread = new_FileReaderThread();
|
|
||||||
if (sReaderThread == NULL)
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
afm = (AudioFileManager *) SDL_malloc(sizeof (AudioFileManager));
|
|
||||||
if (afm == NULL)
|
|
||||||
return NULL;
|
|
||||||
SDL_memset(afm, '\0', sizeof (*afm));
|
|
||||||
|
|
||||||
#define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(Disconnect);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(DoConnect);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(Read);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(GetFileBuffer);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(GetParent);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(SetPosition);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(GetByteCounter);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(SetEndOfFile);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(Render);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(GetFileData);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(AfterRender);
|
|
||||||
SET_AUDIOFILEMANAGER_METHOD(FileInputProc);
|
|
||||||
#undef SET_AUDIOFILEMANAGER_METHOD
|
|
||||||
|
|
||||||
afm->mParent = inParent;
|
|
||||||
afm->mForkRefNum = inForkRefNum;
|
|
||||||
afm->mBufferSize = inChunkSize;
|
|
||||||
afm->mBufferOffset = inChunkSize;
|
|
||||||
afm->mChunkSize = inChunkSize;
|
|
||||||
afm->mFileLength = inFileLength;
|
|
||||||
afm->mFileBuffer = (char*) SDL_malloc(afm->mChunkSize * 2);
|
|
||||||
FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset);
|
|
||||||
assert (afm->mFileBuffer != NULL);
|
|
||||||
return afm;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,636 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#include "CDPlayer.h"
|
|
||||||
#include "AudioFilePlayer.h"
|
|
||||||
#include "SDLOSXCAGuard.h"
|
|
||||||
|
|
||||||
/* we're exporting these functions into C land for SDL_syscdrom.c */
|
|
||||||
/*extern "C" {*/
|
|
||||||
|
|
||||||
/*///////////////////////////////////////////////////////////////////////////
|
|
||||||
Constants
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
#define kAudioCDFilesystemID (UInt16)(('J' << 8) | 'H') /* 'JH'; this avoids compiler warning */
|
|
||||||
|
|
||||||
/* XML PList keys */
|
|
||||||
#define kRawTOCDataString "Format 0x02 TOC Data"
|
|
||||||
#define kSessionsString "Sessions"
|
|
||||||
#define kSessionTypeString "Session Type"
|
|
||||||
#define kTrackArrayString "Track Array"
|
|
||||||
#define kFirstTrackInSessionString "First Track"
|
|
||||||
#define kLastTrackInSessionString "Last Track"
|
|
||||||
#define kLeadoutBlockString "Leadout Block"
|
|
||||||
#define kDataKeyString "Data"
|
|
||||||
#define kPointKeyString "Point"
|
|
||||||
#define kSessionNumberKeyString "Session Number"
|
|
||||||
#define kStartBlockKeyString "Start Block"
|
|
||||||
|
|
||||||
/*///////////////////////////////////////////////////////////////////////////
|
|
||||||
Globals
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
#pragma mark -- Globals --
|
|
||||||
|
|
||||||
static int playBackWasInit = 0;
|
|
||||||
static AudioUnit theUnit;
|
|
||||||
static AudioFilePlayer* thePlayer = NULL;
|
|
||||||
static CDPlayerCompletionProc completionProc = NULL;
|
|
||||||
static SDL_mutex *apiMutex = NULL;
|
|
||||||
static SDL_sem *callbackSem;
|
|
||||||
static SDL_CD* theCDROM;
|
|
||||||
|
|
||||||
/*///////////////////////////////////////////////////////////////////////////
|
|
||||||
Prototypes
|
|
||||||
//////////////////////////////////////////////////////////////////////////*/
|
|
||||||
|
|
||||||
#pragma mark -- Prototypes --
|
|
||||||
|
|
||||||
static OSStatus CheckInit ();
|
|
||||||
|
|
||||||
static void FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus);
|
|
||||||
|
|
||||||
static int RunCallBackThread (void* inRefCon);
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -- Public Functions --
|
|
||||||
|
|
||||||
void Lock ()
|
|
||||||
{
|
|
||||||
if (!apiMutex) {
|
|
||||||
apiMutex = SDL_CreateMutex();
|
|
||||||
}
|
|
||||||
SDL_mutexP(apiMutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unlock ()
|
|
||||||
{
|
|
||||||
SDL_mutexV(apiMutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes)
|
|
||||||
{
|
|
||||||
int volumeIndex;
|
|
||||||
int cdVolumeCount = 0;
|
|
||||||
OSStatus result = noErr;
|
|
||||||
|
|
||||||
for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++)
|
|
||||||
{
|
|
||||||
FSVolumeRefNum actualVolume;
|
|
||||||
FSVolumeInfo volumeInfo;
|
|
||||||
|
|
||||||
memset (&volumeInfo, 0, sizeof(volumeInfo));
|
|
||||||
|
|
||||||
result = FSGetVolumeInfo (kFSInvalidVolumeRefNum,
|
|
||||||
volumeIndex,
|
|
||||||
&actualVolume,
|
|
||||||
kFSVolInfoFSInfo,
|
|
||||||
&volumeInfo,
|
|
||||||
NULL,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (result == noErr)
|
|
||||||
{
|
|
||||||
if (volumeInfo.filesystemID == kAudioCDFilesystemID) /* It's an audio CD */
|
|
||||||
{
|
|
||||||
if (volumes != NULL && cdVolumeCount < numVolumes)
|
|
||||||
volumes[cdVolumeCount] = actualVolume;
|
|
||||||
|
|
||||||
cdVolumeCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* I'm commenting this out because it seems to be harmless */
|
|
||||||
/*SDL_SetError ("DetectAudioCDVolumes: FSGetVolumeInfo returned %d", result);*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return cdVolumeCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD)
|
|
||||||
{
|
|
||||||
HFSUniStr255 dataForkName;
|
|
||||||
OSStatus theErr;
|
|
||||||
FSIORefNum forkRefNum;
|
|
||||||
SInt64 forkSize;
|
|
||||||
Ptr forkData = 0;
|
|
||||||
ByteCount actualRead;
|
|
||||||
CFDataRef dataRef = 0;
|
|
||||||
CFPropertyListRef propertyListRef = 0;
|
|
||||||
FSRefParam fsRefPB;
|
|
||||||
FSRef tocPlistFSRef;
|
|
||||||
FSRef rootRef;
|
|
||||||
const char* error = "Unspecified Error";
|
|
||||||
const UniChar uniName[] = { '.','T','O','C','.','p','l','i','s','t' };
|
|
||||||
|
|
||||||
theErr = FSGetVolumeInfo(theVolume, 0, 0, kFSVolInfoNone, 0, 0, &rootRef);
|
|
||||||
if(theErr != noErr) {
|
|
||||||
error = "FSGetVolumeInfo";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_memset(&fsRefPB, '\0', sizeof (fsRefPB));
|
|
||||||
|
|
||||||
/* get stuff from .TOC.plist */
|
|
||||||
fsRefPB.ref = &rootRef;
|
|
||||||
fsRefPB.newRef = &tocPlistFSRef;
|
|
||||||
fsRefPB.nameLength = sizeof (uniName) / sizeof (uniName[0]);
|
|
||||||
fsRefPB.name = uniName;
|
|
||||||
fsRefPB.textEncodingHint = kTextEncodingUnknown;
|
|
||||||
|
|
||||||
theErr = PBMakeFSRefUnicodeSync (&fsRefPB);
|
|
||||||
if(theErr != noErr) {
|
|
||||||
error = "PBMakeFSRefUnicodeSync";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load and parse the TOC XML data */
|
|
||||||
|
|
||||||
theErr = FSGetDataForkName (&dataForkName);
|
|
||||||
if (theErr != noErr) {
|
|
||||||
error = "FSGetDataForkName";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
theErr = FSOpenFork (&tocPlistFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum);
|
|
||||||
if (theErr != noErr) {
|
|
||||||
error = "FSOpenFork";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
theErr = FSGetForkSize (forkRefNum, &forkSize);
|
|
||||||
if (theErr != noErr) {
|
|
||||||
error = "FSGetForkSize";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate some memory for the XML data */
|
|
||||||
forkData = NewPtr (forkSize);
|
|
||||||
if(forkData == NULL) {
|
|
||||||
error = "NewPtr";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
theErr = FSReadFork (forkRefNum, fsFromStart, 0 /* offset location */, forkSize, forkData, &actualRead);
|
|
||||||
if(theErr != noErr) {
|
|
||||||
error = "FSReadFork";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
dataRef = CFDataCreate (kCFAllocatorDefault, (UInt8 *)forkData, forkSize);
|
|
||||||
if(dataRef == 0) {
|
|
||||||
error = "CFDataCreate";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
propertyListRef = CFPropertyListCreateFromXMLData (kCFAllocatorDefault,
|
|
||||||
dataRef,
|
|
||||||
kCFPropertyListImmutable,
|
|
||||||
NULL);
|
|
||||||
if (propertyListRef == NULL) {
|
|
||||||
error = "CFPropertyListCreateFromXMLData";
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now we got the Property List in memory. Parse it. */
|
|
||||||
|
|
||||||
/* First, make sure the root item is a CFDictionary. If not, release and bail. */
|
|
||||||
if(CFGetTypeID(propertyListRef)== CFDictionaryGetTypeID())
|
|
||||||
{
|
|
||||||
CFDictionaryRef dictRef = (CFDictionaryRef)propertyListRef;
|
|
||||||
|
|
||||||
CFDataRef theRawTOCDataRef;
|
|
||||||
CFArrayRef theSessionArrayRef;
|
|
||||||
CFIndex numSessions;
|
|
||||||
CFIndex index;
|
|
||||||
|
|
||||||
/* This is how we get the Raw TOC Data */
|
|
||||||
theRawTOCDataRef = (CFDataRef)CFDictionaryGetValue (dictRef, CFSTR(kRawTOCDataString));
|
|
||||||
|
|
||||||
/* Get the session array info. */
|
|
||||||
theSessionArrayRef = (CFArrayRef)CFDictionaryGetValue (dictRef, CFSTR(kSessionsString));
|
|
||||||
|
|
||||||
/* Find out how many sessions there are. */
|
|
||||||
numSessions = CFArrayGetCount (theSessionArrayRef);
|
|
||||||
|
|
||||||
/* Initialize the total number of tracks to 0 */
|
|
||||||
theCD->numtracks = 0;
|
|
||||||
|
|
||||||
/* Iterate over all sessions, collecting the track data */
|
|
||||||
for(index = 0; index < numSessions; index++)
|
|
||||||
{
|
|
||||||
CFDictionaryRef theSessionDict;
|
|
||||||
CFNumberRef leadoutBlock;
|
|
||||||
CFArrayRef trackArray;
|
|
||||||
CFIndex numTracks;
|
|
||||||
CFIndex trackIndex;
|
|
||||||
UInt32 value = 0;
|
|
||||||
|
|
||||||
theSessionDict = (CFDictionaryRef) CFArrayGetValueAtIndex (theSessionArrayRef, index);
|
|
||||||
leadoutBlock = (CFNumberRef) CFDictionaryGetValue (theSessionDict, CFSTR(kLeadoutBlockString));
|
|
||||||
|
|
||||||
trackArray = (CFArrayRef)CFDictionaryGetValue (theSessionDict, CFSTR(kTrackArrayString));
|
|
||||||
|
|
||||||
numTracks = CFArrayGetCount (trackArray);
|
|
||||||
|
|
||||||
for(trackIndex = 0; trackIndex < numTracks; trackIndex++) {
|
|
||||||
|
|
||||||
CFDictionaryRef theTrackDict;
|
|
||||||
CFNumberRef trackNumber;
|
|
||||||
CFNumberRef sessionNumber;
|
|
||||||
CFNumberRef startBlock;
|
|
||||||
CFBooleanRef isDataTrack;
|
|
||||||
UInt32 value;
|
|
||||||
|
|
||||||
theTrackDict = (CFDictionaryRef) CFArrayGetValueAtIndex (trackArray, trackIndex);
|
|
||||||
|
|
||||||
trackNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kPointKeyString));
|
|
||||||
sessionNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kSessionNumberKeyString));
|
|
||||||
startBlock = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kStartBlockKeyString));
|
|
||||||
isDataTrack = (CFBooleanRef) CFDictionaryGetValue (theTrackDict, CFSTR(kDataKeyString));
|
|
||||||
|
|
||||||
/* Fill in the SDL_CD struct */
|
|
||||||
int idx = theCD->numtracks++;
|
|
||||||
|
|
||||||
CFNumberGetValue (trackNumber, kCFNumberSInt32Type, &value);
|
|
||||||
theCD->track[idx].id = value;
|
|
||||||
|
|
||||||
CFNumberGetValue (startBlock, kCFNumberSInt32Type, &value);
|
|
||||||
theCD->track[idx].offset = value;
|
|
||||||
|
|
||||||
theCD->track[idx].type = (isDataTrack == kCFBooleanTrue) ? SDL_DATA_TRACK : SDL_AUDIO_TRACK;
|
|
||||||
|
|
||||||
/* Since the track lengths are not stored in .TOC.plist we compute them. */
|
|
||||||
if (trackIndex > 0) {
|
|
||||||
theCD->track[idx-1].length = theCD->track[idx].offset - theCD->track[idx-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compute the length of the last track */
|
|
||||||
CFNumberGetValue (leadoutBlock, kCFNumberSInt32Type, &value);
|
|
||||||
|
|
||||||
theCD->track[theCD->numtracks-1].length =
|
|
||||||
value - theCD->track[theCD->numtracks-1].offset;
|
|
||||||
|
|
||||||
/* Set offset to leadout track */
|
|
||||||
theCD->track[theCD->numtracks].offset = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
theErr = 0;
|
|
||||||
goto cleanup;
|
|
||||||
bail:
|
|
||||||
SDL_SetError ("ReadTOCData: %s returned %d", error, theErr);
|
|
||||||
theErr = -1;
|
|
||||||
cleanup:
|
|
||||||
|
|
||||||
if (propertyListRef != NULL)
|
|
||||||
CFRelease(propertyListRef);
|
|
||||||
if (dataRef != NULL)
|
|
||||||
CFRelease(dataRef);
|
|
||||||
if (forkData != NULL)
|
|
||||||
DisposePtr(forkData);
|
|
||||||
|
|
||||||
FSCloseFork (forkRefNum);
|
|
||||||
|
|
||||||
return theErr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks)
|
|
||||||
{
|
|
||||||
OSStatus result = -1;
|
|
||||||
FSIterator iterator;
|
|
||||||
ItemCount actualObjects;
|
|
||||||
FSRef rootDirectory;
|
|
||||||
FSRef ref;
|
|
||||||
HFSUniStr255 nameStr;
|
|
||||||
|
|
||||||
result = FSGetVolumeInfo (theVolume,
|
|
||||||
0,
|
|
||||||
NULL,
|
|
||||||
kFSVolInfoFSInfo,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
&rootDirectory);
|
|
||||||
|
|
||||||
if (result != noErr) {
|
|
||||||
SDL_SetError ("ListTrackFiles: FSGetVolumeInfo returned %d", result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = FSOpenIterator (&rootDirectory, kFSIterateFlat, &iterator);
|
|
||||||
if (result == noErr) {
|
|
||||||
do
|
|
||||||
{
|
|
||||||
result = FSGetCatalogInfoBulk (iterator, 1, &actualObjects,
|
|
||||||
NULL, kFSCatInfoNone, NULL, &ref, NULL, &nameStr);
|
|
||||||
if (result == noErr) {
|
|
||||||
|
|
||||||
CFStringRef name;
|
|
||||||
name = CFStringCreateWithCharacters (NULL, nameStr.unicode, nameStr.length);
|
|
||||||
|
|
||||||
/* Look for .aiff extension */
|
|
||||||
if (CFStringHasSuffix (name, CFSTR(".aiff")) ||
|
|
||||||
CFStringHasSuffix (name, CFSTR(".cdda"))) {
|
|
||||||
|
|
||||||
/* Extract the track id from the filename */
|
|
||||||
int trackID = 0, i = 0;
|
|
||||||
while (i < nameStr.length && !isdigit(nameStr.unicode[i])) {
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
while (i < nameStr.length && isdigit(nameStr.unicode[i])) {
|
|
||||||
trackID = 10 * trackID +(nameStr.unicode[i] - '0');
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG_CDROM
|
|
||||||
printf("Found AIFF for track %d: '%s'\n", trackID,
|
|
||||||
CFStringGetCStringPtr (name, CFStringGetSystemEncoding()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Track ID's start at 1, but we want to start at 0 */
|
|
||||||
trackID--;
|
|
||||||
|
|
||||||
assert(0 <= trackID && trackID <= SDL_MAX_TRACKS);
|
|
||||||
|
|
||||||
if (trackID < numTracks)
|
|
||||||
memcpy (&trackFiles[trackID], &ref, sizeof(FSRef));
|
|
||||||
}
|
|
||||||
CFRelease (name);
|
|
||||||
}
|
|
||||||
} while(noErr == result);
|
|
||||||
FSCloseIterator (iterator);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int LoadFile (const FSRef *ref, int startFrame, int stopFrame)
|
|
||||||
{
|
|
||||||
int error = -1;
|
|
||||||
|
|
||||||
if (CheckInit () < 0)
|
|
||||||
goto bail;
|
|
||||||
|
|
||||||
/* release any currently playing file */
|
|
||||||
if (ReleaseFile () < 0)
|
|
||||||
goto bail;
|
|
||||||
|
|
||||||
#if DEBUG_CDROM
|
|
||||||
printf ("LoadFile: %d %d\n", startFrame, stopFrame);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*try {*/
|
|
||||||
|
|
||||||
/* create a new player, and attach to the audio unit */
|
|
||||||
|
|
||||||
thePlayer = new_AudioFilePlayer(ref);
|
|
||||||
if (thePlayer == NULL) {
|
|
||||||
SDL_SetError ("LoadFile: Could not create player");
|
|
||||||
return -3; /*throw (-3);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!thePlayer->SetDestination(thePlayer, &theUnit))
|
|
||||||
goto bail;
|
|
||||||
|
|
||||||
if (startFrame >= 0)
|
|
||||||
thePlayer->SetStartFrame (thePlayer, startFrame);
|
|
||||||
|
|
||||||
if (stopFrame >= 0 && stopFrame > startFrame)
|
|
||||||
thePlayer->SetStopFrame (thePlayer, stopFrame);
|
|
||||||
|
|
||||||
/* we set the notifier later */
|
|
||||||
/*thePlayer->SetNotifier(thePlayer, FilePlayNotificationHandler, NULL);*/
|
|
||||||
|
|
||||||
if (!thePlayer->Connect(thePlayer))
|
|
||||||
goto bail;
|
|
||||||
|
|
||||||
#if DEBUG_CDROM
|
|
||||||
thePlayer->Print(thePlayer);
|
|
||||||
fflush (stdout);
|
|
||||||
#endif
|
|
||||||
/*}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
goto bail;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
error = 0;
|
|
||||||
|
|
||||||
bail:
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ReleaseFile ()
|
|
||||||
{
|
|
||||||
int error = -1;
|
|
||||||
|
|
||||||
/* (Don't see any way that the original C++ code could throw here.) --ryan. */
|
|
||||||
/*try {*/
|
|
||||||
if (thePlayer != NULL) {
|
|
||||||
|
|
||||||
thePlayer->Disconnect(thePlayer);
|
|
||||||
|
|
||||||
delete_AudioFilePlayer(thePlayer);
|
|
||||||
|
|
||||||
thePlayer = NULL;
|
|
||||||
}
|
|
||||||
/*}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
goto bail;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
error = 0;
|
|
||||||
|
|
||||||
/* bail: */
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PlayFile ()
|
|
||||||
{
|
|
||||||
OSStatus result = -1;
|
|
||||||
|
|
||||||
if (CheckInit () < 0)
|
|
||||||
goto bail;
|
|
||||||
|
|
||||||
/*try {*/
|
|
||||||
|
|
||||||
// start processing of the audio unit
|
|
||||||
result = AudioOutputUnitStart (theUnit);
|
|
||||||
if (result) goto bail; //THROW_RESULT("PlayFile: AudioOutputUnitStart")
|
|
||||||
|
|
||||||
/*}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
goto bail;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
result = 0;
|
|
||||||
|
|
||||||
bail:
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PauseFile ()
|
|
||||||
{
|
|
||||||
OSStatus result = -1;
|
|
||||||
|
|
||||||
if (CheckInit () < 0)
|
|
||||||
goto bail;
|
|
||||||
|
|
||||||
/*try {*/
|
|
||||||
|
|
||||||
/* stop processing the audio unit */
|
|
||||||
result = AudioOutputUnitStop (theUnit);
|
|
||||||
if (result) goto bail; /*THROW_RESULT("PauseFile: AudioOutputUnitStop")*/
|
|
||||||
/*}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
goto bail;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
result = 0;
|
|
||||||
bail:
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
assert(thePlayer != NULL);
|
|
||||||
|
|
||||||
theCDROM = cdrom;
|
|
||||||
completionProc = proc;
|
|
||||||
thePlayer->SetNotifier (thePlayer, FilePlayNotificationHandler, cdrom);
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetCurrentFrame ()
|
|
||||||
{
|
|
||||||
int frame;
|
|
||||||
|
|
||||||
if (thePlayer == NULL)
|
|
||||||
frame = 0;
|
|
||||||
else
|
|
||||||
frame = thePlayer->GetCurrentFrame (thePlayer);
|
|
||||||
|
|
||||||
return frame;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -- Private Functions --
|
|
||||||
|
|
||||||
static OSStatus CheckInit ()
|
|
||||||
{
|
|
||||||
if (playBackWasInit)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
OSStatus result = noErr;
|
|
||||||
|
|
||||||
/* Create the callback semaphore */
|
|
||||||
callbackSem = SDL_CreateSemaphore(0);
|
|
||||||
|
|
||||||
/* Start callback thread */
|
|
||||||
SDL_CreateThread(RunCallBackThread, NULL);
|
|
||||||
|
|
||||||
{ /*try {*/
|
|
||||||
ComponentDescription desc;
|
|
||||||
|
|
||||||
desc.componentType = kAudioUnitType_Output;
|
|
||||||
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
|
|
||||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
||||||
desc.componentFlags = 0;
|
|
||||||
desc.componentFlagsMask = 0;
|
|
||||||
|
|
||||||
Component comp = FindNextComponent (NULL, &desc);
|
|
||||||
if (comp == NULL) {
|
|
||||||
SDL_SetError ("CheckInit: FindNextComponent returned NULL");
|
|
||||||
if (result) return -1; //throw(internalComponentErr);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = OpenAComponent (comp, &theUnit);
|
|
||||||
if (result) return -1; //THROW_RESULT("CheckInit: OpenAComponent")
|
|
||||||
|
|
||||||
// you need to initialize the output unit before you set it as a destination
|
|
||||||
result = AudioUnitInitialize (theUnit);
|
|
||||||
if (result) return -1; //THROW_RESULT("CheckInit: AudioUnitInitialize")
|
|
||||||
|
|
||||||
|
|
||||||
playBackWasInit = true;
|
|
||||||
}
|
|
||||||
/*catch (...)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus)
|
|
||||||
{
|
|
||||||
if (inStatus == kAudioFilePlay_FileIsFinished) {
|
|
||||||
|
|
||||||
/* notify non-CA thread to perform the callback */
|
|
||||||
SDL_SemPost(callbackSem);
|
|
||||||
|
|
||||||
} else if (inStatus == kAudioFilePlayErr_FilePlayUnderrun) {
|
|
||||||
|
|
||||||
SDL_SetError ("CDPlayer Notification: buffer underrun");
|
|
||||||
} else if (inStatus == kAudioFilePlay_PlayerIsUninitialized) {
|
|
||||||
|
|
||||||
SDL_SetError ("CDPlayer Notification: player is uninitialized");
|
|
||||||
} else {
|
|
||||||
|
|
||||||
SDL_SetError ("CDPlayer Notification: unknown error %ld", inStatus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int RunCallBackThread (void *param)
|
|
||||||
{
|
|
||||||
for (;;) {
|
|
||||||
|
|
||||||
SDL_SemWait(callbackSem);
|
|
||||||
|
|
||||||
if (completionProc && theCDROM) {
|
|
||||||
#if DEBUG_CDROM
|
|
||||||
printf ("callback!\n");
|
|
||||||
#endif
|
|
||||||
(*completionProc)(theCDROM);
|
|
||||||
} else {
|
|
||||||
#if DEBUG_CDROM
|
|
||||||
printf ("callback?\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#if DEBUG_CDROM
|
|
||||||
printf ("thread dying now...\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*}; // extern "C" */
|
|
||||||
|
|
@ -1,69 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef __CDPlayer__H__
|
|
||||||
#define __CDPlayer__H__ 1
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <Carbon/Carbon.h>
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
#include <AudioUnit/AudioUnit.h>
|
|
||||||
|
|
||||||
#include "SDL.h"
|
|
||||||
#include "SDL_thread.h"
|
|
||||||
#include "SDL_mutex.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef void (*CDPlayerCompletionProc)(SDL_CD *cdrom) ;
|
|
||||||
|
|
||||||
void Lock ();
|
|
||||||
|
|
||||||
void Unlock();
|
|
||||||
|
|
||||||
int LoadFile (const FSRef *ref, int startFrame, int endFrame); /* pass -1 to do nothing */
|
|
||||||
|
|
||||||
int ReleaseFile ();
|
|
||||||
|
|
||||||
int PlayFile ();
|
|
||||||
|
|
||||||
int PauseFile ();
|
|
||||||
|
|
||||||
void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom);
|
|
||||||
|
|
||||||
int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD);
|
|
||||||
|
|
||||||
int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks);
|
|
||||||
|
|
||||||
int DetectAudioCDVolumes (FSVolumeRefNum *volumes, int numVolumes);
|
|
||||||
|
|
||||||
int GetCurrentFrame ();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* __CD_Player__H__ */
|
|
||||||
|
|
@ -1,199 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
Note: This file hasn't been modified so technically we have to keep the disclaimer :-(
|
|
||||||
|
|
||||||
Copyright: © Copyright 2002 Apple Computer, Inc. All rights reserved.
|
|
||||||
|
|
||||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
|
|
||||||
("Apple") in consideration of your agreement to the following terms, and your
|
|
||||||
use, installation, modification or redistribution of this Apple software
|
|
||||||
constitutes acceptance of these terms. If you do not agree with these terms,
|
|
||||||
please do not use, install, modify or redistribute this Apple software.
|
|
||||||
|
|
||||||
In consideration of your agreement to abide by the following terms, and subject
|
|
||||||
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
|
|
||||||
copyrights in this original Apple software (the "Apple Software"), to use,
|
|
||||||
reproduce, modify and redistribute the Apple Software, with or without
|
|
||||||
modifications, in source and/or binary forms; provided that if you redistribute
|
|
||||||
the Apple Software in its entirety and without modifications, you must retain
|
|
||||||
this notice and the following text and disclaimers in all such redistributions of
|
|
||||||
the Apple Software. Neither the name, trademarks, service marks or logos of
|
|
||||||
Apple Computer, Inc. may be used to endorse or promote products derived from the
|
|
||||||
Apple Software without specific prior written permission from Apple. Except as
|
|
||||||
expressly stated in this notice, no other rights or licenses, express or implied,
|
|
||||||
are granted by Apple herein, including but not limited to any patent rights that
|
|
||||||
may be infringed by your derivative works or by other works in which the Apple
|
|
||||||
Software may be incorporated.
|
|
||||||
|
|
||||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
|
|
||||||
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
|
|
||||||
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
|
||||||
COMBINATION WITH YOUR PRODUCTS.
|
|
||||||
|
|
||||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
|
|
||||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
||||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
|
||||||
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
|
|
||||||
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
|
|
||||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
/*=============================================================================
|
|
||||||
CAGuard.cp
|
|
||||||
|
|
||||||
=============================================================================*/
|
|
||||||
|
|
||||||
/*=============================================================================
|
|
||||||
Includes
|
|
||||||
=============================================================================*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
*/
|
|
||||||
#include "SDL_stdinc.h"
|
|
||||||
|
|
||||||
/*#define NDEBUG 1*/
|
|
||||||
/*
|
|
||||||
#include <assert.h>
|
|
||||||
*/
|
|
||||||
#define assert(X)
|
|
||||||
|
|
||||||
|
|
||||||
#include "SDLOSXCAGuard.h"
|
|
||||||
|
|
||||||
/*#warning Need a try-based Locker too*/
|
|
||||||
/*=============================================================================
|
|
||||||
SDLOSXCAGuard
|
|
||||||
=============================================================================*/
|
|
||||||
|
|
||||||
static int SDLOSXCAGuard_Lock(SDLOSXCAGuard *cag)
|
|
||||||
{
|
|
||||||
int theAnswer = 0;
|
|
||||||
|
|
||||||
if(pthread_self() != cag->mOwner)
|
|
||||||
{
|
|
||||||
OSStatus theError = pthread_mutex_lock(&cag->mMutex);
|
|
||||||
(void)theError;
|
|
||||||
assert(theError == 0);
|
|
||||||
cag->mOwner = pthread_self();
|
|
||||||
theAnswer = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return theAnswer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SDLOSXCAGuard_Unlock(SDLOSXCAGuard *cag)
|
|
||||||
{
|
|
||||||
OSStatus theError;
|
|
||||||
assert(pthread_self() == cag->mOwner);
|
|
||||||
|
|
||||||
cag->mOwner = 0;
|
|
||||||
theError = pthread_mutex_unlock(&cag->mMutex);
|
|
||||||
(void)theError;
|
|
||||||
assert(theError == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDLOSXCAGuard_Try (SDLOSXCAGuard *cag, int *outWasLocked)
|
|
||||||
{
|
|
||||||
int theAnswer = 0;
|
|
||||||
*outWasLocked = 0;
|
|
||||||
|
|
||||||
if (pthread_self() == cag->mOwner) {
|
|
||||||
theAnswer = 1;
|
|
||||||
*outWasLocked = 0;
|
|
||||||
} else {
|
|
||||||
OSStatus theError = pthread_mutex_trylock(&cag->mMutex);
|
|
||||||
if (theError == 0) {
|
|
||||||
cag->mOwner = pthread_self();
|
|
||||||
theAnswer = 1;
|
|
||||||
*outWasLocked = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return theAnswer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SDLOSXCAGuard_Wait(SDLOSXCAGuard *cag)
|
|
||||||
{
|
|
||||||
OSStatus theError;
|
|
||||||
assert(pthread_self() == cag->mOwner);
|
|
||||||
|
|
||||||
cag->mOwner = 0;
|
|
||||||
|
|
||||||
theError = pthread_cond_wait(&cag->mCondVar, &cag->mMutex);
|
|
||||||
(void)theError;
|
|
||||||
assert(theError == 0);
|
|
||||||
cag->mOwner = pthread_self();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SDLOSXCAGuard_Notify(SDLOSXCAGuard *cag)
|
|
||||||
{
|
|
||||||
OSStatus theError = pthread_cond_signal(&cag->mCondVar);
|
|
||||||
(void)theError;
|
|
||||||
assert(theError == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SDLOSXCAGuard *new_SDLOSXCAGuard(void)
|
|
||||||
{
|
|
||||||
OSStatus theError;
|
|
||||||
SDLOSXCAGuard *cag = (SDLOSXCAGuard *) SDL_malloc(sizeof (SDLOSXCAGuard));
|
|
||||||
if (cag == NULL)
|
|
||||||
return NULL;
|
|
||||||
SDL_memset(cag, '\0', sizeof (*cag));
|
|
||||||
|
|
||||||
#define SET_SDLOSXCAGUARD_METHOD(m) cag->m = SDLOSXCAGuard_##m
|
|
||||||
SET_SDLOSXCAGUARD_METHOD(Lock);
|
|
||||||
SET_SDLOSXCAGUARD_METHOD(Unlock);
|
|
||||||
SET_SDLOSXCAGUARD_METHOD(Try);
|
|
||||||
SET_SDLOSXCAGUARD_METHOD(Wait);
|
|
||||||
SET_SDLOSXCAGUARD_METHOD(Notify);
|
|
||||||
#undef SET_SDLOSXCAGUARD_METHOD
|
|
||||||
|
|
||||||
theError = pthread_mutex_init(&cag->mMutex, NULL);
|
|
||||||
(void)theError;
|
|
||||||
assert(theError == 0);
|
|
||||||
|
|
||||||
theError = pthread_cond_init(&cag->mCondVar, NULL);
|
|
||||||
(void)theError;
|
|
||||||
assert(theError == 0);
|
|
||||||
|
|
||||||
cag->mOwner = 0;
|
|
||||||
return cag;
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag)
|
|
||||||
{
|
|
||||||
if (cag != NULL)
|
|
||||||
{
|
|
||||||
pthread_mutex_destroy(&cag->mMutex);
|
|
||||||
pthread_cond_destroy(&cag->mCondVar);
|
|
||||||
SDL_free(cag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/*
|
|
||||||
Note: This file hasn't been modified so technically we have to keep the disclaimer :-(
|
|
||||||
|
|
||||||
|
|
||||||
Copyright: © Copyright 2002 Apple Computer, Inc. All rights reserved.
|
|
||||||
|
|
||||||
Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc.
|
|
||||||
("Apple") in consideration of your agreement to the following terms, and your
|
|
||||||
use, installation, modification or redistribution of this Apple software
|
|
||||||
constitutes acceptance of these terms. If you do not agree with these terms,
|
|
||||||
please do not use, install, modify or redistribute this Apple software.
|
|
||||||
|
|
||||||
In consideration of your agreement to abide by the following terms, and subject
|
|
||||||
to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
|
|
||||||
copyrights in this original Apple software (the "Apple Software"), to use,
|
|
||||||
reproduce, modify and redistribute the Apple Software, with or without
|
|
||||||
modifications, in source and/or binary forms; provided that if you redistribute
|
|
||||||
the Apple Software in its entirety and without modifications, you must retain
|
|
||||||
this notice and the following text and disclaimers in all such redistributions of
|
|
||||||
the Apple Software. Neither the name, trademarks, service marks or logos of
|
|
||||||
Apple Computer, Inc. may be used to endorse or promote products derived from the
|
|
||||||
Apple Software without specific prior written permission from Apple. Except as
|
|
||||||
expressly stated in this notice, no other rights or licenses, express or implied,
|
|
||||||
are granted by Apple herein, including but not limited to any patent rights that
|
|
||||||
may be infringed by your derivative works or by other works in which the Apple
|
|
||||||
Software may be incorporated.
|
|
||||||
|
|
||||||
The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
|
|
||||||
WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
|
|
||||||
WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
|
||||||
COMBINATION WITH YOUR PRODUCTS.
|
|
||||||
|
|
||||||
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
|
|
||||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
|
|
||||||
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
||||||
ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
|
||||||
OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
|
|
||||||
(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
|
|
||||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
/*=============================================================================
|
|
||||||
CAGuard.h
|
|
||||||
|
|
||||||
=============================================================================*/
|
|
||||||
#if !defined(__CAGuard_h__)
|
|
||||||
#define __CAGuard_h__
|
|
||||||
|
|
||||||
/*=============================================================================
|
|
||||||
Includes
|
|
||||||
=============================================================================*/
|
|
||||||
|
|
||||||
#include <CoreAudio/CoreAudioTypes.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
|
|
||||||
/*=============================================================================
|
|
||||||
CAGuard
|
|
||||||
|
|
||||||
This is your typical mutex with signalling implemented via pthreads.
|
|
||||||
Lock() will return true if and only if the guard is locked on that call.
|
|
||||||
A thread that already has the guard will receive 'false' if it locks it
|
|
||||||
again. Use of the stack-based CAGuard::Locker class is highly recommended
|
|
||||||
to properly manage the recursive nesting. The Wait calls with timeouts
|
|
||||||
will return true if and only if the timeout period expired. They will
|
|
||||||
return false if they receive notification any other way.
|
|
||||||
=============================================================================*/
|
|
||||||
|
|
||||||
typedef struct S_SDLOSXCAGuard
|
|
||||||
{
|
|
||||||
|
|
||||||
/* Construction/Destruction */
|
|
||||||
/*public:*/
|
|
||||||
/* Actions */
|
|
||||||
/*public:*/
|
|
||||||
int (*Lock)(struct S_SDLOSXCAGuard *cag);
|
|
||||||
void (*Unlock)(struct S_SDLOSXCAGuard *cag);
|
|
||||||
int (*Try)(struct S_SDLOSXCAGuard *cag, int *outWasLocked); /* returns true if lock is free, false if not */
|
|
||||||
void (*Wait)(struct S_SDLOSXCAGuard *cag);
|
|
||||||
void (*Notify)(struct S_SDLOSXCAGuard *cag);
|
|
||||||
|
|
||||||
/* Implementation */
|
|
||||||
/*protected:*/
|
|
||||||
pthread_mutex_t mMutex;
|
|
||||||
pthread_cond_t mCondVar;
|
|
||||||
pthread_t mOwner;
|
|
||||||
} SDLOSXCAGuard;
|
|
||||||
|
|
||||||
SDLOSXCAGuard *new_SDLOSXCAGuard(void);
|
|
||||||
void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
@ -1,514 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_MACOSX
|
|
||||||
|
|
||||||
#include "SDL_syscdrom_c.h"
|
|
||||||
|
|
||||||
#pragma mark -- Globals --
|
|
||||||
|
|
||||||
static FSRef** tracks;
|
|
||||||
static FSVolumeRefNum* volumes;
|
|
||||||
static CDstatus status;
|
|
||||||
static int nextTrackFrame;
|
|
||||||
static int nextTrackFramesRemaining;
|
|
||||||
static int fakeCD;
|
|
||||||
static int currentTrack;
|
|
||||||
static int didReadTOC;
|
|
||||||
static int cacheTOCNumTracks;
|
|
||||||
static int currentDrive; /* Only allow 1 drive in use at a time */
|
|
||||||
|
|
||||||
#pragma mark -- Prototypes --
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName (int drive);
|
|
||||||
static int SDL_SYS_CDOpen (int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC (SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay (SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause (SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume (SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop (SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject (SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose (SDL_CD *cdrom);
|
|
||||||
|
|
||||||
#pragma mark -- Helper Functions --
|
|
||||||
|
|
||||||
/* Read a list of tracks from the volume */
|
|
||||||
static int LoadTracks (SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
/* Check if tracks are already loaded */
|
|
||||||
if ( tracks[cdrom->id] != NULL )
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Allocate memory for tracks */
|
|
||||||
tracks[cdrom->id] = (FSRef*) SDL_calloc (1, sizeof(**tracks) * cdrom->numtracks);
|
|
||||||
if (tracks[cdrom->id] == NULL) {
|
|
||||||
SDL_OutOfMemory ();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load tracks */
|
|
||||||
if (ListTrackFiles (volumes[cdrom->id], tracks[cdrom->id], cdrom->numtracks) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Find a file for a given start frame and length */
|
|
||||||
static FSRef* GetFileForOffset (SDL_CD *cdrom, int start, int length, int *outStartFrame, int *outStopFrame)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < cdrom->numtracks; i++) {
|
|
||||||
|
|
||||||
if (cdrom->track[i].offset <= start &&
|
|
||||||
start < (cdrom->track[i].offset + cdrom->track[i].length))
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i == cdrom->numtracks)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
currentTrack = i;
|
|
||||||
|
|
||||||
*outStartFrame = start - cdrom->track[i].offset;
|
|
||||||
|
|
||||||
if ((*outStartFrame + length) < cdrom->track[i].length) {
|
|
||||||
*outStopFrame = *outStartFrame + length;
|
|
||||||
length = 0;
|
|
||||||
nextTrackFrame = -1;
|
|
||||||
nextTrackFramesRemaining = -1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*outStopFrame = -1;
|
|
||||||
length -= cdrom->track[i].length - *outStartFrame;
|
|
||||||
nextTrackFrame = cdrom->track[i+1].offset;
|
|
||||||
nextTrackFramesRemaining = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &tracks[cdrom->id][i];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Setup another file for playback, or stop playback (called from another thread) */
|
|
||||||
static void CompletionProc (SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
|
|
||||||
Lock ();
|
|
||||||
|
|
||||||
if (nextTrackFrame > 0 && nextTrackFramesRemaining > 0) {
|
|
||||||
|
|
||||||
/* Load the next file to play */
|
|
||||||
int startFrame, stopFrame;
|
|
||||||
FSRef *file;
|
|
||||||
|
|
||||||
PauseFile ();
|
|
||||||
ReleaseFile ();
|
|
||||||
|
|
||||||
file = GetFileForOffset (cdrom, nextTrackFrame,
|
|
||||||
nextTrackFramesRemaining, &startFrame, &stopFrame);
|
|
||||||
|
|
||||||
if (file == NULL) {
|
|
||||||
status = CD_STOPPED;
|
|
||||||
Unlock ();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadFile (file, startFrame, stopFrame);
|
|
||||||
|
|
||||||
SetCompletionProc (CompletionProc, cdrom);
|
|
||||||
|
|
||||||
PlayFile ();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
/* Release the current file */
|
|
||||||
PauseFile ();
|
|
||||||
ReleaseFile ();
|
|
||||||
status = CD_STOPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
Unlock ();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark -- Driver Functions --
|
|
||||||
|
|
||||||
/* Initialize */
|
|
||||||
int SDL_SYS_CDInit (void)
|
|
||||||
{
|
|
||||||
/* Initialize globals */
|
|
||||||
volumes = NULL;
|
|
||||||
tracks = NULL;
|
|
||||||
status = CD_STOPPED;
|
|
||||||
nextTrackFrame = -1;
|
|
||||||
nextTrackFramesRemaining = -1;
|
|
||||||
fakeCD = SDL_FALSE;
|
|
||||||
currentTrack = -1;
|
|
||||||
didReadTOC = SDL_FALSE;
|
|
||||||
cacheTOCNumTracks = -1;
|
|
||||||
currentDrive = -1;
|
|
||||||
|
|
||||||
/* Fill in function pointers */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Read the list of "drives"
|
|
||||||
|
|
||||||
This is currently a hack that infers drives from
|
|
||||||
mounted audio CD volumes, rather than
|
|
||||||
actual CD-ROM devices - which means it may not
|
|
||||||
act as expected sometimes.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Find out how many cd volumes are mounted */
|
|
||||||
SDL_numcds = DetectAudioCDVolumes (NULL, 0);
|
|
||||||
|
|
||||||
/*
|
|
||||||
If there are no volumes, fake a cd device
|
|
||||||
so tray empty can be reported.
|
|
||||||
*/
|
|
||||||
if (SDL_numcds == 0) {
|
|
||||||
|
|
||||||
fakeCD = SDL_TRUE;
|
|
||||||
SDL_numcds = 1;
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate space for volumes */
|
|
||||||
volumes = (FSVolumeRefNum*) SDL_calloc (1, sizeof(*volumes) * SDL_numcds);
|
|
||||||
if (volumes == NULL) {
|
|
||||||
SDL_OutOfMemory ();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate space for tracks */
|
|
||||||
tracks = (FSRef**) SDL_calloc (1, sizeof(*tracks) * (SDL_numcds + 1));
|
|
||||||
if (tracks == NULL) {
|
|
||||||
SDL_OutOfMemory ();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark the end of the tracks array */
|
|
||||||
tracks[ SDL_numcds ] = (FSRef*)-1;
|
|
||||||
|
|
||||||
/*
|
|
||||||
Redetect, now save all volumes for later
|
|
||||||
Update SDL_numcds just in case it changed
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
int numVolumes = SDL_numcds;
|
|
||||||
|
|
||||||
SDL_numcds = DetectAudioCDVolumes (volumes, numVolumes);
|
|
||||||
|
|
||||||
/* If more cds suddenly show up, ignore them */
|
|
||||||
if (SDL_numcds > numVolumes) {
|
|
||||||
SDL_SetError ("Some CD's were added but they will be ignored");
|
|
||||||
SDL_numcds = numVolumes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Shutdown and cleanup */
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
ReleaseFile();
|
|
||||||
|
|
||||||
if (volumes != NULL)
|
|
||||||
free (volumes);
|
|
||||||
|
|
||||||
if (tracks != NULL) {
|
|
||||||
|
|
||||||
FSRef **ptr;
|
|
||||||
for (ptr = tracks; *ptr != (FSRef*)-1; ptr++)
|
|
||||||
if (*ptr != NULL)
|
|
||||||
free (*ptr);
|
|
||||||
|
|
||||||
free (tracks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the Unix disk name of the volume */
|
|
||||||
static const char *SDL_SYS_CDName (int drive)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* !!! FIXME: PBHGetVolParmsSync() is gone in 10.6,
|
|
||||||
* !!! FIXME: replaced with FSGetVolumeParms(), which
|
|
||||||
* !!! FIXME: isn't available before 10.5. :/
|
|
||||||
*/
|
|
||||||
return "Mac OS X CD-ROM Device";
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
OSStatus err = noErr;
|
|
||||||
HParamBlockRec pb;
|
|
||||||
GetVolParmsInfoBuffer volParmsInfo;
|
|
||||||
|
|
||||||
if (fakeCD)
|
|
||||||
return "Fake CD-ROM Device";
|
|
||||||
|
|
||||||
pb.ioParam.ioNamePtr = NULL;
|
|
||||||
pb.ioParam.ioVRefNum = volumes[drive];
|
|
||||||
pb.ioParam.ioBuffer = (Ptr)&volParmsInfo;
|
|
||||||
pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo);
|
|
||||||
err = PBHGetVolParmsSync(&pb);
|
|
||||||
|
|
||||||
if (err != noErr) {
|
|
||||||
SDL_SetError ("PBHGetVolParmsSync returned %d", err);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return volParmsInfo.vMDeviceID;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open the "device" */
|
|
||||||
static int SDL_SYS_CDOpen (int drive)
|
|
||||||
{
|
|
||||||
/* Only allow 1 device to be open */
|
|
||||||
if (currentDrive >= 0) {
|
|
||||||
SDL_SetError ("Only one cdrom is supported");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
currentDrive = drive;
|
|
||||||
|
|
||||||
return drive;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the table of contents */
|
|
||||||
static int SDL_SYS_CDGetTOC (SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
if (fakeCD) {
|
|
||||||
SDL_SetError (kErrorFakeDevice);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (didReadTOC) {
|
|
||||||
cdrom->numtracks = cacheTOCNumTracks;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ReadTOCData (volumes[cdrom->id], cdrom);
|
|
||||||
didReadTOC = SDL_TRUE;
|
|
||||||
cacheTOCNumTracks = cdrom->numtracks;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
if (position) {
|
|
||||||
int trackFrame;
|
|
||||||
|
|
||||||
Lock ();
|
|
||||||
trackFrame = GetCurrentFrame ();
|
|
||||||
Unlock ();
|
|
||||||
|
|
||||||
*position = cdrom->track[currentTrack].offset + trackFrame;
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start playback */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
int startFrame, stopFrame;
|
|
||||||
FSRef *ref;
|
|
||||||
|
|
||||||
if (fakeCD) {
|
|
||||||
SDL_SetError (kErrorFakeDevice);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock();
|
|
||||||
|
|
||||||
if (LoadTracks (cdrom) < 0)
|
|
||||||
return -2;
|
|
||||||
|
|
||||||
if (PauseFile () < 0)
|
|
||||||
return -3;
|
|
||||||
|
|
||||||
if (ReleaseFile () < 0)
|
|
||||||
return -4;
|
|
||||||
|
|
||||||
ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame);
|
|
||||||
if (ref == NULL) {
|
|
||||||
SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length);
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LoadFile (ref, startFrame, stopFrame) < 0)
|
|
||||||
return -6;
|
|
||||||
|
|
||||||
SetCompletionProc (CompletionProc, cdrom);
|
|
||||||
|
|
||||||
if (PlayFile () < 0)
|
|
||||||
return -7;
|
|
||||||
|
|
||||||
status = CD_PLAYING;
|
|
||||||
|
|
||||||
Unlock();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause playback */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
if (fakeCD) {
|
|
||||||
SDL_SetError (kErrorFakeDevice);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock ();
|
|
||||||
|
|
||||||
if (PauseFile () < 0) {
|
|
||||||
Unlock ();
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = CD_PAUSED;
|
|
||||||
|
|
||||||
Unlock ();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume playback */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
if (fakeCD) {
|
|
||||||
SDL_SetError (kErrorFakeDevice);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock ();
|
|
||||||
|
|
||||||
if (PlayFile () < 0) {
|
|
||||||
Unlock ();
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = CD_PLAYING;
|
|
||||||
|
|
||||||
Unlock ();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop playback */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
if (fakeCD) {
|
|
||||||
SDL_SetError (kErrorFakeDevice);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock ();
|
|
||||||
|
|
||||||
if (PauseFile () < 0) {
|
|
||||||
Unlock ();
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReleaseFile () < 0) {
|
|
||||||
Unlock ();
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = CD_STOPPED;
|
|
||||||
|
|
||||||
Unlock ();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM (Unmount the volume) */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
OSStatus err;
|
|
||||||
pid_t dissenter;
|
|
||||||
|
|
||||||
if (fakeCD) {
|
|
||||||
SDL_SetError (kErrorFakeDevice);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lock ();
|
|
||||||
|
|
||||||
if (PauseFile () < 0) {
|
|
||||||
Unlock ();
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReleaseFile () < 0) {
|
|
||||||
Unlock ();
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = CD_STOPPED;
|
|
||||||
|
|
||||||
/* Eject the volume */
|
|
||||||
err = FSEjectVolumeSync(volumes[cdrom->id], kNilOptions, &dissenter);
|
|
||||||
|
|
||||||
if (err != noErr) {
|
|
||||||
Unlock ();
|
|
||||||
SDL_SetError ("PBUnmountVol returned %d", err);
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
|
|
||||||
/* Invalidate volume and track info */
|
|
||||||
volumes[cdrom->id] = 0;
|
|
||||||
free (tracks[cdrom->id]);
|
|
||||||
tracks[cdrom->id] = NULL;
|
|
||||||
|
|
||||||
Unlock ();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
currentDrive = -1;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_MACOSX */
|
|
||||||
|
|
@ -1,136 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
/* This is the Mac OS X / CoreAudio specific header for the SDL CD-ROM API
|
|
||||||
Contributed by Darrell Walisser and Max Horn
|
|
||||||
*/
|
|
||||||
|
|
||||||
/***********************************************************************************
|
|
||||||
Implementation Notes
|
|
||||||
*********************
|
|
||||||
|
|
||||||
This code has several limitations currently (all of which are proabaly fixable):
|
|
||||||
|
|
||||||
1. A CD-ROM device is inferred from a mounted cdfs volume, so device 0 is
|
|
||||||
not necessarily the first CD-ROM device on the system. (Somewhat easy to fix
|
|
||||||
by useing the device name from the volume id's to reorder the volumes)
|
|
||||||
|
|
||||||
2. You can only open and control 1 CD-ROM device at a time. (Challenging to fix,
|
|
||||||
due to extensive code restructuring)
|
|
||||||
|
|
||||||
3. The status reported by SDL_CDStatus only changes to from CD_PLAYING to CD_STOPPED in
|
|
||||||
1-second intervals (because the audio is buffered in 1-second chunks) If
|
|
||||||
the audio data is less than 1 second, the remainder is filled with silence.
|
|
||||||
|
|
||||||
If you need to play sequences back-to-back that are less that 1 second long,
|
|
||||||
use the frame position to determine when to play the next sequence, instead
|
|
||||||
of SDL_CDStatus.
|
|
||||||
|
|
||||||
This may be possible to fix with a clever usage of the AudioUnit API.
|
|
||||||
|
|
||||||
4. When new volumes are inserted, our volume information is not updated. The only way
|
|
||||||
to refresh this information is to reinit the CD-ROM subsystem of SDL. To fix this,
|
|
||||||
one would probably have to fix point 1 above first, then figure out how to register
|
|
||||||
for a notification when new media is mounted in order to perform an automatic
|
|
||||||
rescan for cdfs volumes.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
So, here comes a description of how this all works.
|
|
||||||
|
|
||||||
< Initializing >
|
|
||||||
|
|
||||||
To get things rolling, we have to locate mounted volumes that contain
|
|
||||||
audio (since nearly all Macs don't have analog audio-in on the sound card).
|
|
||||||
That's easy, since these volumes have a flag that indicates this special
|
|
||||||
filesystem. See DetectAudioCDVolumes() in CDPlayer.cpp for this code.
|
|
||||||
|
|
||||||
Next, we parse the invisible .TOC.plist in the root of the volume, which gets us
|
|
||||||
the track information (number, offset, length, leadout, etc). See ReadTOCData() in
|
|
||||||
CDPlayer.cpp for the skinny on this.
|
|
||||||
|
|
||||||
|
|
||||||
< The Playback Loop >
|
|
||||||
|
|
||||||
Now come the tricky parts. Let's start with basic audio playback. When a frame
|
|
||||||
range to play is requested, we must first find the .aiff files on the volume,
|
|
||||||
hopefully in the right order. Since these files all begin with a number "1 Audio Track",
|
|
||||||
etc, this is used to determine the correct track order.
|
|
||||||
|
|
||||||
Once all files are determined, we have to find what file corresponds to the start
|
|
||||||
and length parameter to SDL_SYS_CDPlay(). Again, this is quite simple by walking the
|
|
||||||
cdrom's track list. At this point, we also save the offset to the next track and frames
|
|
||||||
remaining, if we're going to have to play another file after the first one. See
|
|
||||||
GetFileForOffset() for this code.
|
|
||||||
|
|
||||||
At this point we have all info needed to start playback, so we hand off to the LoadFile()
|
|
||||||
function, which proceeds to do its magic and plays back the file.
|
|
||||||
|
|
||||||
When the file is finished playing, CompletionProc() is invoked, at which time we can
|
|
||||||
play the next file if the previously saved next track and frames remaining
|
|
||||||
indicates that we should.
|
|
||||||
|
|
||||||
|
|
||||||
< Magic >
|
|
||||||
|
|
||||||
OK, so it's not really magic, but since I don't fully understand all the hidden details it
|
|
||||||
seems like it to me ;-) The API's involved are the AudioUnit and AudioFile API's. These
|
|
||||||
appear to be an extension of CoreAudio for creating modular playback and f/x entities.
|
|
||||||
The important thing is that CPU usage is very low and reliability is very high. You'd
|
|
||||||
be hard-pressed to find a way to stutter the playback with other CPU-intensive tasks.
|
|
||||||
|
|
||||||
One part of this magic is that it uses multiple threads, which carries the usual potential
|
|
||||||
for disaster if not handled carefully. Playback currently requires 4 additional threads:
|
|
||||||
1. The coreaudio runloop thread
|
|
||||||
2. The coreaudio device i/o thread
|
|
||||||
3. The file streaming thread
|
|
||||||
4. The notification/callback thread
|
|
||||||
|
|
||||||
The first 2 threads are necessary evil - CoreAudio creates this no matter what the situation
|
|
||||||
is (even the SDL sound implementation creates theses suckers). The last two are are created
|
|
||||||
by us.
|
|
||||||
|
|
||||||
The file is streamed from disk using a threaded double-buffer approach.
|
|
||||||
This way, the high latency operation of reading from disk can be performed without interrupting
|
|
||||||
the real-time device thread (which amounts to avoiding dropouts). The device thread grabs the
|
|
||||||
buffer that isn't being read and sends it to the CoreAudio mixer where it eventually gets
|
|
||||||
to the sound card.
|
|
||||||
|
|
||||||
The device thread posts a notification when the file streaming thread is out of data. This
|
|
||||||
notification must be handled in a separate thread to avoid potential deadlock in the
|
|
||||||
device thread. That's where the notification thread comes in. This thread is signaled
|
|
||||||
whenever a notification needs to be processed, so another file can be played back if need be.
|
|
||||||
|
|
||||||
The API in CDPlayer.cpp contains synchronization because otherwise both the notification thread
|
|
||||||
and main thread (or another other thread using the SDL CD api) can potentially call it at the same time.
|
|
||||||
|
|
||||||
************************************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
#include "CDPlayer.h"
|
|
||||||
|
|
||||||
#define kErrorFakeDevice "Error: Cannot proceed since we're faking a CD-ROM device. Reinit the CD-ROM subsystem to scan for new volumes."
|
|
||||||
|
|
||||||
|
|
@ -1,317 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_MINT
|
|
||||||
|
|
||||||
/*
|
|
||||||
Atari MetaDOS CD-ROM functions
|
|
||||||
|
|
||||||
Patrice Mandin
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
#include <mint/cdromio.h>
|
|
||||||
#include <mint/metados.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
/* Some ioctl() errno values which occur when the tray is empty */
|
|
||||||
#ifndef ENOMEDIUM
|
|
||||||
#define ENOMEDIUM ENOENT
|
|
||||||
#endif
|
|
||||||
#define ERRNO_TRAYEMPTY(errno) \
|
|
||||||
((errno == EIO) || (errno == ENOENT) || \
|
|
||||||
(errno == EINVAL) || (errno == ENOMEDIUM))
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 32
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char device[3]; /* Physical device letter + ':' + '\0' */
|
|
||||||
metaopen_t metaopen; /* Infos on opened drive */
|
|
||||||
} metados_drive_t;
|
|
||||||
|
|
||||||
static metados_drive_t metados_drives[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDioctl(int id, int command, void *arg);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
metainit_t metainit={0,0,0,0};
|
|
||||||
metaopen_t metaopen;
|
|
||||||
int i, handle;
|
|
||||||
struct cdrom_subchnl info;
|
|
||||||
|
|
||||||
Metainit(&metainit);
|
|
||||||
if (metainit.version == NULL) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "MetaDOS not installed\n");
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (metainit.drives_map == 0) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "No MetaDOS devices present\n");
|
|
||||||
#endif
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_numcds = 0;
|
|
||||||
|
|
||||||
for (i='A'; i<='Z'; i++) {
|
|
||||||
metados_drives[SDL_numcds].device[0] = 0;
|
|
||||||
metados_drives[SDL_numcds].device[1] = ':';
|
|
||||||
metados_drives[SDL_numcds].device[2] = 0;
|
|
||||||
|
|
||||||
if (metainit.drives_map & (1<<(i-'A'))) {
|
|
||||||
handle = Metaopen(i, &metaopen);
|
|
||||||
if (handle == 0) {
|
|
||||||
|
|
||||||
info.cdsc_format = CDROM_MSF;
|
|
||||||
if ( (Metaioctl(i, METADOS_IOCTL_MAGIC, CDROMSUBCHNL, &info) == 0) || ERRNO_TRAYEMPTY(errno) ) {
|
|
||||||
metados_drives[SDL_numcds].device[0] = i;
|
|
||||||
++SDL_numcds;
|
|
||||||
}
|
|
||||||
|
|
||||||
Metaclose(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(metados_drives[drive].device);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
int handle;
|
|
||||||
|
|
||||||
handle = Metaopen(metados_drives[drive].device[0], &(metados_drives[drive].metaopen));
|
|
||||||
if (handle == 0) {
|
|
||||||
return drive;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
Metaclose(metados_drives[cdrom->id].device[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDioctl(int id, int command, void *arg)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = Metaioctl(metados_drives[id].device[0], METADOS_IOCTL_MAGIC, command, arg);
|
|
||||||
if ( retval < 0 ) {
|
|
||||||
SDL_SetError("ioctl() error: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
return(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
int i,okay;
|
|
||||||
struct cdrom_tochdr toc;
|
|
||||||
struct cdrom_tocentry entry;
|
|
||||||
|
|
||||||
/* Use standard ioctl() */
|
|
||||||
if (SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc)<0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cdrom->numtracks = toc.cdth_trk1-toc.cdth_trk0+1;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read all the track TOC entries */
|
|
||||||
okay=1;
|
|
||||||
for ( i=0; i<=cdrom->numtracks; ++i ) {
|
|
||||||
if ( i == cdrom->numtracks ) {
|
|
||||||
cdrom->track[i].id = CDROM_LEADOUT;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].id = toc.cdth_trk0+i;
|
|
||||||
}
|
|
||||||
entry.cdte_track = cdrom->track[i].id;
|
|
||||||
entry.cdte_format = CDROM_MSF;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCENTRY, &entry) < 0 ) {
|
|
||||||
okay=0;
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if ( entry.cdte_ctrl & CDROM_DATA_TRACK ) {
|
|
||||||
cdrom->track[i].type = SDL_DATA_TRACK;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].type = SDL_AUDIO_TRACK;
|
|
||||||
}
|
|
||||||
cdrom->track[i].offset = MSF_TO_FRAMES(
|
|
||||||
entry.cdte_addr.msf.minute,
|
|
||||||
entry.cdte_addr.msf.second,
|
|
||||||
entry.cdte_addr.msf.frame);
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if ( i > 0 ) {
|
|
||||||
cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
struct cdrom_tochdr toc;
|
|
||||||
struct cdrom_subchnl info;
|
|
||||||
|
|
||||||
info.cdsc_format = CDROM_MSF;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, CDROMSUBCHNL, &info) < 0 ) {
|
|
||||||
if ( ERRNO_TRAYEMPTY(errno) ) {
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
} else {
|
|
||||||
status = CD_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (info.cdsc_audiostatus) {
|
|
||||||
case CDROM_AUDIO_INVALID:
|
|
||||||
case CDROM_AUDIO_NO_STATUS:
|
|
||||||
/* Try to determine if there's a CD available */
|
|
||||||
if (SDL_SYS_CDioctl(cdrom->id, CDROMREADTOCHDR, &toc)==0) {
|
|
||||||
status = CD_STOPPED;
|
|
||||||
} else {
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_COMPLETED:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_PLAY:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_PAUSED:
|
|
||||||
/* Workaround buggy CD-ROM drive */
|
|
||||||
if ( info.cdsc_trk == CDROM_LEADOUT ) {
|
|
||||||
status = CD_STOPPED;
|
|
||||||
} else {
|
|
||||||
status = CD_PAUSED;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( position ) {
|
|
||||||
if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
|
|
||||||
*position = MSF_TO_FRAMES(
|
|
||||||
info.cdsc_absaddr.msf.minute,
|
|
||||||
info.cdsc_absaddr.msf.second,
|
|
||||||
info.cdsc_absaddr.msf.frame);
|
|
||||||
} else {
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
struct cdrom_msf playtime;
|
|
||||||
|
|
||||||
FRAMES_TO_MSF(start,
|
|
||||||
&playtime.cdmsf_min0, &playtime.cdmsf_sec0, &playtime.cdmsf_frame0);
|
|
||||||
FRAMES_TO_MSF(start+length,
|
|
||||||
&playtime.cdmsf_min1, &playtime.cdmsf_sec1, &playtime.cdmsf_frame1);
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
|
|
||||||
playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
|
|
||||||
playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return SDL_SYS_CDioctl(cdrom->id, CDROMPLAYMSF, &playtime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return SDL_SYS_CDioctl(cdrom->id, CDROMPAUSE, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return SDL_SYS_CDioctl(cdrom->id, CDROMRESUME, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return SDL_SYS_CDioctl(cdrom->id, CDROMSTOP, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return SDL_SYS_CDioctl(cdrom->id, CDROMEJECT, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_MINT */
|
|
||||||
|
|
@ -1,416 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_OPENBSD
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <sys/cdio.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
static dev_t SDL_cdmode[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
/* Some ioctl() errno values which occur when the tray is empty */
|
|
||||||
#define ERRNO_TRAYEMPTY(errno) \
|
|
||||||
((errno == EIO) || (errno == ENOENT) || (errno == EINVAL) || \
|
|
||||||
(errno == ENODEV))
|
|
||||||
|
|
||||||
/* Check a drive to see if it is a CD-ROM */
|
|
||||||
static int CheckDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int is_cd, cdfd;
|
|
||||||
struct ioc_read_subchannel info;
|
|
||||||
|
|
||||||
/* If it doesn't exist, return -1 */
|
|
||||||
if ( stat(drive, stbuf) < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it does exist, verify that it's an available CD-ROM */
|
|
||||||
is_cd = 0;
|
|
||||||
if ( S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode) ) {
|
|
||||||
cdfd = open(drive, (O_RDONLY|O_EXCL|O_NONBLOCK), 0);
|
|
||||||
if ( cdfd >= 0 ) {
|
|
||||||
info.address_format = CD_MSF_FORMAT;
|
|
||||||
info.data_format = CD_CURRENT_POSITION;
|
|
||||||
info.data_len = 0;
|
|
||||||
info.data = NULL;
|
|
||||||
/* Under Linux, EIO occurs when a disk is not present.
|
|
||||||
This isn't 100% reliable, so we use the USE_MNTENT
|
|
||||||
code above instead.
|
|
||||||
*/
|
|
||||||
if ( (ioctl(cdfd, CDIOCREADSUBCHANNEL, &info) == 0) ||
|
|
||||||
ERRNO_TRAYEMPTY(errno) ) {
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
close(cdfd);
|
|
||||||
}
|
|
||||||
else if (ERRNO_TRAYEMPTY(errno))
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
return(is_cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds < MAX_DRIVES ) {
|
|
||||||
/* Check to make sure it's not already in our list.
|
|
||||||
This can happen when we see a drive via symbolic link.
|
|
||||||
*/
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
if ( stbuf->st_rdev == SDL_cdmode[i] ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add this drive to our list */
|
|
||||||
i = SDL_numcds;
|
|
||||||
SDL_cdlist[i] = SDL_strdup(drive);
|
|
||||||
if ( SDL_cdlist[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_cdmode[i] = stbuf->st_rdev;
|
|
||||||
++SDL_numcds;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
static char *checklist[] = {
|
|
||||||
#if defined(__OPENBSD__)
|
|
||||||
"?0 cd?c", "cdrom", NULL
|
|
||||||
#elif defined(__NETBSD__)
|
|
||||||
"?0 cd?d", "?0 cd?c", "cdrom", NULL
|
|
||||||
#else
|
|
||||||
"?0 cd?c", "?0 acd?c", "cdrom", NULL
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
char *SDLcdrom;
|
|
||||||
int i, j, exists;
|
|
||||||
char drive[32];
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Look in the environment for our CD-ROM drive list */
|
|
||||||
SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
|
|
||||||
if ( SDLcdrom != NULL ) {
|
|
||||||
char *cdpath, *delim;
|
|
||||||
size_t len = SDL_strlen(SDLcdrom)+1;
|
|
||||||
cdpath = SDL_stack_alloc(char, len);
|
|
||||||
if ( cdpath != NULL ) {
|
|
||||||
SDL_strlcpy(cdpath, SDLcdrom, len);
|
|
||||||
SDLcdrom = cdpath;
|
|
||||||
do {
|
|
||||||
delim = SDL_strchr(SDLcdrom, ':');
|
|
||||||
if ( delim ) {
|
|
||||||
*delim++ = '\0';
|
|
||||||
}
|
|
||||||
if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
|
|
||||||
AddDrive(SDLcdrom, &stbuf);
|
|
||||||
}
|
|
||||||
if ( delim ) {
|
|
||||||
SDLcdrom = delim;
|
|
||||||
} else {
|
|
||||||
SDLcdrom = NULL;
|
|
||||||
}
|
|
||||||
} while ( SDLcdrom );
|
|
||||||
SDL_stack_free(cdpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the system for CD-ROM drives */
|
|
||||||
for ( i=0; checklist[i]; ++i ) {
|
|
||||||
if ( checklist[i][0] == '?' ) {
|
|
||||||
char *insert;
|
|
||||||
exists = 1;
|
|
||||||
for ( j=checklist[i][1]; exists; ++j ) {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
|
|
||||||
insert = SDL_strchr(drive, '?');
|
|
||||||
if ( insert != NULL ) {
|
|
||||||
*insert = j;
|
|
||||||
}
|
|
||||||
switch (CheckDrive(drive, &stbuf)) {
|
|
||||||
/* Drive exists and is a CD-ROM */
|
|
||||||
case 1:
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
break;
|
|
||||||
/* Drive exists, but isn't a CD-ROM */
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
/* Drive doesn't exist */
|
|
||||||
case -1:
|
|
||||||
exists = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
|
|
||||||
if ( CheckDrive(drive, &stbuf) > 0 ) {
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* General ioctl() CD-ROM command function */
|
|
||||||
static int SDL_SYS_CDioctl(int id, int command, void *arg)
|
|
||||||
{
|
|
||||||
int retval;
|
|
||||||
|
|
||||||
retval = ioctl(id, command, arg);
|
|
||||||
if ( retval < 0 ) {
|
|
||||||
SDL_SetError("ioctl() error: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
return(retval);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
return(open(SDL_cdlist[drive], (O_RDONLY|O_EXCL|O_NONBLOCK), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
struct ioc_toc_header toc;
|
|
||||||
int i, okay;
|
|
||||||
struct ioc_read_toc_entry entry;
|
|
||||||
struct cd_toc_entry data;
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCHEADER, &toc) == 0 ) {
|
|
||||||
cdrom->numtracks = toc.ending_track-toc.starting_track+1;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
/* Read all the track TOC entries */
|
|
||||||
for ( i=0; i<=cdrom->numtracks; ++i ) {
|
|
||||||
if ( i == cdrom->numtracks ) {
|
|
||||||
cdrom->track[i].id = 0xAA; /* CDROM_LEADOUT */
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].id = toc.starting_track+i;
|
|
||||||
}
|
|
||||||
entry.starting_track = cdrom->track[i].id;
|
|
||||||
entry.address_format = CD_MSF_FORMAT;
|
|
||||||
entry.data_len = sizeof(data);
|
|
||||||
entry.data = &data;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, CDIOREADTOCENTRYS,
|
|
||||||
&entry) < 0 ) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].type = data.control;
|
|
||||||
cdrom->track[i].offset = MSF_TO_FRAMES(
|
|
||||||
data.addr.msf.minute,
|
|
||||||
data.addr.msf.second,
|
|
||||||
data.addr.msf.frame);
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if ( i > 0 ) {
|
|
||||||
cdrom->track[i-1].length =
|
|
||||||
cdrom->track[i].offset-
|
|
||||||
cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( i == (cdrom->numtracks+1) ) {
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
struct ioc_toc_header toc;
|
|
||||||
struct ioc_read_subchannel info;
|
|
||||||
struct cd_sub_channel_info data;
|
|
||||||
|
|
||||||
info.address_format = CD_MSF_FORMAT;
|
|
||||||
info.data_format = CD_CURRENT_POSITION;
|
|
||||||
info.track = 0;
|
|
||||||
info.data_len = sizeof(data);
|
|
||||||
info.data = &data;
|
|
||||||
if ( ioctl(cdrom->id, CDIOCREADSUBCHANNEL, &info) < 0 ) {
|
|
||||||
if ( ERRNO_TRAYEMPTY(errno) ) {
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
} else {
|
|
||||||
status = CD_ERROR;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch (data.header.audio_status) {
|
|
||||||
case CD_AS_AUDIO_INVALID:
|
|
||||||
case CD_AS_NO_STATUS:
|
|
||||||
/* Try to determine if there's a CD available */
|
|
||||||
if (ioctl(cdrom->id,CDIOREADTOCHEADER,&toc)==0)
|
|
||||||
status = CD_STOPPED;
|
|
||||||
else
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
break;
|
|
||||||
case CD_AS_PLAY_COMPLETED:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
case CD_AS_PLAY_IN_PROGRESS:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case CD_AS_PLAY_PAUSED:
|
|
||||||
status = CD_PAUSED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( position ) {
|
|
||||||
if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
|
|
||||||
*position = MSF_TO_FRAMES(
|
|
||||||
data.what.position.absaddr.msf.minute,
|
|
||||||
data.what.position.absaddr.msf.second,
|
|
||||||
data.what.position.absaddr.msf.frame);
|
|
||||||
} else {
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
struct ioc_play_msf playtime;
|
|
||||||
|
|
||||||
FRAMES_TO_MSF(start,
|
|
||||||
&playtime.start_m, &playtime.start_s, &playtime.start_f);
|
|
||||||
FRAMES_TO_MSF(start+length,
|
|
||||||
&playtime.end_m, &playtime.end_s, &playtime.end_f);
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
|
|
||||||
playtime.start_m, playtime.start_s, playtime.start_f,
|
|
||||||
playtime.end_m, playtime.end_s, playtime.end_f);
|
|
||||||
#endif
|
|
||||||
ioctl(cdrom->id, CDIOCSTART, 0);
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCPLAYMSF, &playtime));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCPAUSE, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCRESUME, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCSTOP, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
SDL_SYS_CDioctl(cdrom->id, CDIOCALLOW, 0);
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, CDIOCEJECT, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
close(cdrom->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_OPENBSD */
|
|
||||||
|
|
@ -1,393 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_OS2
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
#define INCL_MCIOS2
|
|
||||||
#include <os2.h>
|
|
||||||
#include <os2me.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
/* Size of MCI result buffer (in bytes) */
|
|
||||||
#define MCI_CMDRETBUFSIZE 128
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
//static dev_t SDL_cdmode[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
/* MCI Timing Functions */
|
|
||||||
#define MCI_MMTIMEPERSECOND 3000
|
|
||||||
#define FRAMESFROMMM(mmtime) (((mmtime)*CD_FPS)/MCI_MMTIMEPERSECOND)
|
|
||||||
|
|
||||||
|
|
||||||
/* Ready for MCI CDAudio Devices */
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
int i; /* generig counter */
|
|
||||||
MCI_SYSINFO_PARMS msp; /* Structure to MCI SysInfo parameters */
|
|
||||||
CHAR SysInfoRet[MCI_CMDRETBUFSIZE]; /* Buffer for MCI Command result */
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Get the number of CD ROMs in the System */
|
|
||||||
/* Clean SysInfo structure */
|
|
||||||
SDL_memset(&msp, 0x00, sizeof(MCI_SYSINFO_PARMS));
|
|
||||||
/* Prepare structure to Ask Numer of Audio CDs */
|
|
||||||
msp.usDeviceType = MCI_DEVTYPE_CD_AUDIO; /* CD Audio Type */
|
|
||||||
msp.pszReturn = (PSZ)&SysInfoRet; /* Return Structure */
|
|
||||||
msp.ulRetSize = MCI_CMDRETBUFSIZE; /* Size of ret struct */
|
|
||||||
if (LOUSHORT(mciSendCommand(0,MCI_SYSINFO, MCI_SYSINFO_QUANTITY | MCI_WAIT, (PVOID)&msp, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
|
|
||||||
SDL_numcds = atoi(SysInfoRet);
|
|
||||||
if (SDL_numcds > MAX_DRIVES) SDL_numcds = MAX_DRIVES; /* Limit maximum CD number */
|
|
||||||
|
|
||||||
/* Get and Add their system name to the SDL_cdlist */
|
|
||||||
msp.pszReturn = (PSZ)&SysInfoRet; /* Return Structure */
|
|
||||||
msp.ulRetSize = MCI_CMDRETBUFSIZE; /* Size of ret struct */
|
|
||||||
msp.usDeviceType = MCI_DEVTYPE_CD_AUDIO; /* CD Audio Type */
|
|
||||||
for (i=0; i<SDL_numcds; i++)
|
|
||||||
{
|
|
||||||
msp.ulNumber = i+1;
|
|
||||||
mciSendCommand(0,MCI_SYSINFO, MCI_SYSINFO_NAME | MCI_WAIT,&msp, 0);
|
|
||||||
SDL_cdlist[i] = SDL_strdup(SysInfoRet);
|
|
||||||
if ( SDL_cdlist[i] == NULL )
|
|
||||||
{
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Return CDAudio System Dependent Device Name - Ready for MCI*/
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open CDAudio Device - Ready for MCI */
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
MCI_OPEN_PARMS mop;
|
|
||||||
MCI_SET_PARMS msp;
|
|
||||||
MCI_GENERIC_PARMS mgp;
|
|
||||||
|
|
||||||
/* Open the device */
|
|
||||||
mop.hwndCallback = (HWND)NULL; // None
|
|
||||||
mop.usDeviceID = (USHORT)NULL; // Will be returned.
|
|
||||||
mop.pszDeviceType = (PSZ)SDL_cdlist[drive]; // CDAudio Device
|
|
||||||
if (LOUSHORT(mciSendCommand(0,MCI_OPEN,MCI_WAIT,&mop, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
|
|
||||||
/* Set time format */
|
|
||||||
msp.hwndCallback = (HWND)NULL; // None
|
|
||||||
msp.ulTimeFormat = MCI_FORMAT_MSF; // Minute : Second : Frame structure
|
|
||||||
msp.ulSpeedFormat = (ULONG)NULL; // No change
|
|
||||||
msp.ulAudio = (ULONG)NULL; // No Channel
|
|
||||||
msp.ulLevel = (ULONG)NULL; // No Volume
|
|
||||||
msp.ulOver = (ULONG)NULL; // No Delay
|
|
||||||
msp.ulItem = (ULONG)NULL; // No item
|
|
||||||
msp.ulValue = (ULONG)NULL; // No value for item flag
|
|
||||||
if (LOUSHORT(mciSendCommand(mop.usDeviceID,MCI_SET,MCI_WAIT | MCI_SET_TIME_FORMAT,&msp, 0)) == MCIERR_SUCCESS) return (mop.usDeviceID);
|
|
||||||
/* Error setting time format? - Close opened device */
|
|
||||||
mgp.hwndCallback = (HWND)NULL; // None
|
|
||||||
mciSendCommand(mop.usDeviceID,MCI_CLOSE,MCI_WAIT,&mgp, 0);
|
|
||||||
return(CD_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD Table Of Contents - Ready for MCI */
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
MCI_TOC_PARMS mtp;
|
|
||||||
MCI_STATUS_PARMS msp;
|
|
||||||
MCI_TOC_REC * mtr;
|
|
||||||
INT i;
|
|
||||||
|
|
||||||
/* Correction because MCI cannot read TOC while CD is playing (it'll stop!) */
|
|
||||||
if (cdrom->status == CD_PLAYING || cdrom->status == CD_PAUSED) return 0;
|
|
||||||
|
|
||||||
/* Get Number of Tracks */
|
|
||||||
msp.hwndCallback = (HWND)NULL; /* None */
|
|
||||||
msp.ulReturn = (ULONG)NULL; /* We want this information */
|
|
||||||
msp.ulItem = MCI_STATUS_NUMBER_OF_TRACKS;
|
|
||||||
msp.ulValue = (ULONG)NULL; /* No additional information */
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) return(CD_ERROR);
|
|
||||||
cdrom->numtracks = msp.ulReturn;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS )
|
|
||||||
{
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
/* Alocate space for TOC data */
|
|
||||||
mtr = (MCI_TOC_REC *)SDL_malloc(cdrom->numtracks*sizeof(MCI_TOC_REC));
|
|
||||||
if ( mtr == NULL )
|
|
||||||
{
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
/* Get TOC from CD */
|
|
||||||
mtp.pBuf = mtr;
|
|
||||||
mtp.ulBufSize = cdrom->numtracks*sizeof(MCI_TOC_REC);
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_GETTOC,MCI_WAIT,&mtp, 0)) != MCIERR_SUCCESS)
|
|
||||||
{
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
SDL_free(mtr);
|
|
||||||
return(CD_ERROR);
|
|
||||||
}
|
|
||||||
/* Fill SDL Tracks Structure */
|
|
||||||
for (i=0; i<cdrom->numtracks; i++)
|
|
||||||
{
|
|
||||||
/* Set Track ID */
|
|
||||||
cdrom->track[i].id = (mtr+i)->TrackNum;
|
|
||||||
/* Set Track Type */
|
|
||||||
msp.hwndCallback = (HWND)NULL; /* None */
|
|
||||||
msp.ulReturn = (ULONG)NULL; /* We want this information */
|
|
||||||
msp.ulItem = MCI_CD_STATUS_TRACK_TYPE;
|
|
||||||
msp.ulValue = (ULONG)((mtr+i)->TrackNum); /* Track Number? */
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_TRACK | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS)
|
|
||||||
{
|
|
||||||
SDL_free(mtr);
|
|
||||||
return (CD_ERROR);
|
|
||||||
}
|
|
||||||
if (msp.ulReturn==MCI_CD_TRACK_AUDIO) cdrom->track[i].type = SDL_AUDIO_TRACK;
|
|
||||||
else cdrom->track[i].type = SDL_DATA_TRACK;
|
|
||||||
/* Set Track Length - values from MCI are in MMTIMEs - 3000 MMTIME = 1 second */
|
|
||||||
cdrom->track[i].length = FRAMESFROMMM((mtr+i)->ulEndAddr - (mtr+i)->ulStartAddr);
|
|
||||||
/* Set Track Offset */
|
|
||||||
cdrom->track[i].offset = FRAMESFROMMM((mtr+i)->ulStartAddr);
|
|
||||||
}
|
|
||||||
SDL_free(mtr);
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Get CD-ROM status - Ready for MCI */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
MCI_STATUS_PARMS msp;
|
|
||||||
|
|
||||||
/* Get Status from MCI */
|
|
||||||
msp.hwndCallback = (HWND)NULL; /* None */
|
|
||||||
msp.ulReturn = (ULONG)NULL; /* We want this information */
|
|
||||||
msp.ulItem = MCI_STATUS_MODE;
|
|
||||||
msp.ulValue = (ULONG)NULL; /* No additional information */
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) status = CD_ERROR;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch(msp.ulReturn)
|
|
||||||
{
|
|
||||||
case MCI_MODE_NOT_READY:
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
break;
|
|
||||||
case MCI_MODE_PAUSE:
|
|
||||||
status = CD_PAUSED;
|
|
||||||
break;
|
|
||||||
case MCI_MODE_PLAY:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case MCI_MODE_STOP:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
/* These cases should not occour */
|
|
||||||
case MCI_MODE_RECORD:
|
|
||||||
case MCI_MODE_SEEK:
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Determine position */
|
|
||||||
if (position != NULL) /* The SDL $&$&%# CDROM call sends NULL pointer here! */
|
|
||||||
{
|
|
||||||
if ((status == CD_PLAYING) || (status == CD_PAUSED))
|
|
||||||
{
|
|
||||||
/* Get Position */
|
|
||||||
msp.hwndCallback = (HWND)NULL; /* None */
|
|
||||||
msp.ulReturn = (ULONG)NULL; /* We want this information */
|
|
||||||
msp.ulItem = MCI_STATUS_POSITION;
|
|
||||||
msp.ulValue = (ULONG)NULL; /* No additiona info */
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) != MCIERR_SUCCESS) return (CD_ERROR);
|
|
||||||
/* Convert from MSF (format selected in the Open process) to Frames (format that will be returned) */
|
|
||||||
*position = MSF_TO_FRAMES(MSF_MINUTE(msp.ulReturn),MSF_SECOND(msp.ulReturn),MSF_FRAME(msp.ulReturn));
|
|
||||||
}
|
|
||||||
else *position = 0;
|
|
||||||
}
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play - Ready for MCI */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
MCI_GENERIC_PARMS mgp;
|
|
||||||
MCI_STATUS_PARMS msp;
|
|
||||||
MCI_PLAY_PARMS mpp;
|
|
||||||
ULONG min,sec,frm;
|
|
||||||
|
|
||||||
/* Start MSF */
|
|
||||||
FRAMES_TO_MSF(start, &min, &sec, &frm);
|
|
||||||
MSF_MINUTE(mpp.ulFrom) = min;
|
|
||||||
MSF_SECOND(mpp.ulFrom) = sec;
|
|
||||||
MSF_FRAME(mpp.ulFrom) = frm;
|
|
||||||
/* End MSF */
|
|
||||||
FRAMES_TO_MSF(start+length, &min, &sec, &frm);
|
|
||||||
MSF_MINUTE(mpp.ulTo) = min;
|
|
||||||
MSF_SECOND(mpp.ulTo) = sec;
|
|
||||||
MSF_FRAME(mpp.ulTo) = frm;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Trying to play from %d:%d:%d to %d:%d:%d\n",
|
|
||||||
playtime.cdmsf_min0, playtime.cdmsf_sec0, playtime.cdmsf_frame0,
|
|
||||||
playtime.cdmsf_min1, playtime.cdmsf_sec1, playtime.cdmsf_frame1);
|
|
||||||
#endif
|
|
||||||
/* Verifies if it is paused first... and if it is, unpause before stopping it. */
|
|
||||||
msp.hwndCallback = (HWND)NULL; /* None */
|
|
||||||
msp.ulReturn = (ULONG)NULL; /* We want this information */
|
|
||||||
msp.ulItem = MCI_STATUS_MODE;
|
|
||||||
msp.ulValue = (ULONG)NULL; /* No additional information */
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) == MCIERR_SUCCESS)
|
|
||||||
{
|
|
||||||
if (msp.ulReturn == MCI_MODE_PAUSE)
|
|
||||||
{
|
|
||||||
mgp.hwndCallback = (HWND)NULL; // None
|
|
||||||
mciSendCommand(cdrom->id,MCI_RESUME,0,&mgp, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Now play it. */
|
|
||||||
mpp.hwndCallback = (HWND)NULL; // We do not want the info. temp
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_PLAY,MCI_FROM | MCI_TO,&mpp, 0)) == MCIERR_SUCCESS) return 0;
|
|
||||||
return (CD_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play - Ready for MCI */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
MCI_GENERIC_PARMS mgp;
|
|
||||||
|
|
||||||
mgp.hwndCallback = (HWND)NULL; // None
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_PAUSE,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
|
|
||||||
return(CD_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play - Ready for MCI */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
MCI_GENERIC_PARMS mgp;
|
|
||||||
|
|
||||||
mgp.hwndCallback = (HWND)NULL; // None
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_RESUME,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
|
|
||||||
return(CD_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play - Ready for MCI */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
MCI_GENERIC_PARMS mgp;
|
|
||||||
MCI_STATUS_PARMS msp;
|
|
||||||
|
|
||||||
/* Verifies if it is paused first... and if it is, unpause before stopping it. */
|
|
||||||
msp.hwndCallback = (HWND)NULL; /* None */
|
|
||||||
msp.ulReturn = (ULONG)NULL; /* We want this information */
|
|
||||||
msp.ulItem = MCI_STATUS_MODE;
|
|
||||||
msp.ulValue = (ULONG)NULL; /* No additional information */
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STATUS,MCI_WAIT | MCI_STATUS_ITEM,&msp, 0)) == MCIERR_SUCCESS)
|
|
||||||
{
|
|
||||||
if (msp.ulReturn == MCI_MODE_PAUSE)
|
|
||||||
{
|
|
||||||
mgp.hwndCallback = (HWND)NULL; // None
|
|
||||||
mciSendCommand(cdrom->id,MCI_RESUME,0,&mgp, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Now stops the media */
|
|
||||||
mgp.hwndCallback = (HWND)NULL; // None
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_STOP,MCI_WAIT,&mgp, 0)) == MCIERR_SUCCESS) return 0;
|
|
||||||
return(CD_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM - Ready for MCI */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
MCI_SET_PARMS msp;
|
|
||||||
|
|
||||||
msp.hwndCallback = (HWND)NULL; // None
|
|
||||||
msp.ulTimeFormat = (ULONG)NULL; // No change
|
|
||||||
msp.ulSpeedFormat = (ULONG)NULL; // No change
|
|
||||||
msp.ulAudio = (ULONG)NULL; // No Channel
|
|
||||||
msp.ulLevel = (ULONG)NULL; // No Volume
|
|
||||||
msp.ulOver = (ULONG)NULL; // No Delay
|
|
||||||
msp.ulItem = (ULONG)NULL; // No item
|
|
||||||
msp.ulValue = (ULONG)NULL; // No value for item flag
|
|
||||||
if (LOUSHORT(mciSendCommand(cdrom->id,MCI_SET,MCI_WAIT | MCI_SET_DOOR_OPEN,&msp, 0)) == MCIERR_SUCCESS) return 0;
|
|
||||||
return(CD_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle - Ready for MCI */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
MCI_GENERIC_PARMS mgp;
|
|
||||||
|
|
||||||
mgp.hwndCallback = (HWND)NULL; // None
|
|
||||||
mciSendCommand(cdrom->id,MCI_CLOSE,MCI_WAIT,&mgp, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Finalize CDROM Subsystem - Ready for MCI */
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 )
|
|
||||||
{
|
|
||||||
for ( i=0; i<SDL_numcds; ++i )
|
|
||||||
{
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_OS2 */
|
|
||||||
|
|
@ -1,444 +0,0 @@
|
||||||
/*
|
|
||||||
Tru64 audio module for SDL (Simple DirectMedia Layer)
|
|
||||||
Copyright (C) 2003
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Library General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Library General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Library General Public
|
|
||||||
License along with this library; if not, write to the Free
|
|
||||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_OSF
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
/* #define DEBUG_CDROM 1 */
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <io/cam/cdrom.h>
|
|
||||||
#include <io/cam/rzdisk.h>
|
|
||||||
#include <io/common/devgetinfo.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
static dev_t SDL_cdmode[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
/* Check a drive to see if it is a CD-ROM */
|
|
||||||
/* Caution!! Not tested. */
|
|
||||||
static int CheckDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int cdfd, is_cd = 0;
|
|
||||||
struct mode_sel_sns_params msp;
|
|
||||||
struct inquiry_info inq;
|
|
||||||
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
char *devtype[] = {"Disk", "Tape", "Printer", "Processor", "WORM",
|
|
||||||
"CD-ROM", "Scanner", "Optical", "Changer", "Comm", "Unknown"};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bzero(&msp, sizeof(msp));
|
|
||||||
bzero(&inq, sizeof(inq));
|
|
||||||
|
|
||||||
/* If it doesn't exist, return -1 */
|
|
||||||
if ( stat(drive, stbuf) < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( (cdfd = open(drive, (O_RDWR|O_NDELAY), 0)) >= 0 ) {
|
|
||||||
msp.msp_addr = (caddr_t) &inq;
|
|
||||||
msp.msp_pgcode = 0;
|
|
||||||
msp.msp_pgctrl = 0;
|
|
||||||
msp.msp_length = sizeof(inq);
|
|
||||||
msp.msp_setps = 0;
|
|
||||||
|
|
||||||
if ( ioctl(cdfd, SCSI_GET_INQUIRY_DATA, &msp) )
|
|
||||||
return (0);
|
|
||||||
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Device Type: %s\n", devtype[inq.perfdt]);
|
|
||||||
fprintf(stderr, "Vendor: %.8s\n", inq.vndrid);
|
|
||||||
fprintf(stderr, "Product: %.8s\n", inq.prodid);
|
|
||||||
fprintf(stderr, "Revision: %.8s\n", inq.revlvl);
|
|
||||||
#endif
|
|
||||||
if ( inq.perfdt == DTYPE_RODIRECT )
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(is_cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds < MAX_DRIVES ) {
|
|
||||||
/* Check to make sure it's not already in our list.
|
|
||||||
* This can happen when we see a drive via symbolic link.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
if ( stbuf->st_rdev == SDL_cdmode[i] ) {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Duplicate drive detected: %s == %s\n", drive, SDL_cdlist[i]);
|
|
||||||
#endif
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add this drive to our list */
|
|
||||||
i = SDL_numcds;
|
|
||||||
SDL_cdlist[i] = SDL_strdup(drive);
|
|
||||||
if ( SDL_cdlist[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_cdmode[i] = stbuf->st_rdev;
|
|
||||||
++SDL_numcds;
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
/* checklist:
|
|
||||||
*
|
|
||||||
* Tru64 5.X (/dev/rdisk/cdrom?c)
|
|
||||||
* dir: /dev/rdisk, name: cdrom
|
|
||||||
*
|
|
||||||
* Digital UNIX 4.0X (/dev/rrz?c)
|
|
||||||
* dir: /dev, name: rrz
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
struct {
|
|
||||||
char *dir;
|
|
||||||
char *name;
|
|
||||||
} checklist[] = {
|
|
||||||
{"/dev/rdisk", "cdrom"},
|
|
||||||
{"/dev", "rrz"},
|
|
||||||
{NULL, NULL}};
|
|
||||||
char drive[32];
|
|
||||||
char *SDLcdrom;
|
|
||||||
int i, j, exists;
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
|
|
||||||
/* Look in the environment for our CD-ROM drive list */
|
|
||||||
SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
|
|
||||||
if ( SDLcdrom != NULL ) {
|
|
||||||
char *cdpath, *delim;
|
|
||||||
size_t len = SDL_strlen(SDLcdrom)+1;
|
|
||||||
cdpath = SDL_stack_alloc(char, len);
|
|
||||||
if ( cdpath != NULL ) {
|
|
||||||
SDL_strlcpy(cdpath, SDLcdrom, len);
|
|
||||||
SDLcdrom = cdpath;
|
|
||||||
do {
|
|
||||||
delim = SDL_strchr(SDLcdrom, ':');
|
|
||||||
if ( delim ) {
|
|
||||||
*delim++ = '\0';
|
|
||||||
}
|
|
||||||
if ( CheckDrive(SDLcdrom, &stbuf) > 0 ) {
|
|
||||||
AddDrive(SDLcdrom, &stbuf);
|
|
||||||
}
|
|
||||||
if ( delim ) {
|
|
||||||
SDLcdrom = delim;
|
|
||||||
} else {
|
|
||||||
SDLcdrom = NULL;
|
|
||||||
}
|
|
||||||
} while ( SDLcdrom );
|
|
||||||
SDL_stack_free(cdpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Scan the system for CD-ROM drives */
|
|
||||||
for ( i = 0; checklist[i].dir; ++i) {
|
|
||||||
DIR *devdir;
|
|
||||||
struct dirent *devent;
|
|
||||||
int name_len;
|
|
||||||
|
|
||||||
devdir = opendir(checklist[i].dir);
|
|
||||||
if (devdir) {
|
|
||||||
name_len = SDL_strlen(checklist[i].name);
|
|
||||||
while (devent = readdir(devdir))
|
|
||||||
if (SDL_memcmp(checklist[i].name, devent->d_name, name_len) == 0)
|
|
||||||
if (devent->d_name[devent->d_namlen-1] == 'c') {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "%s/%s", checklist[i].dir, devent->d_name);
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "Try to add drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
if ( CheckDrive(drive, &stbuf) > 0 )
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
}
|
|
||||||
closedir(devdir);
|
|
||||||
} else {
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr, "cannot open dir: %s\n", checklist[i].dir);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
/* O_RDWR: To use ioctl(fd, SCSI_STOP_UNIT) */
|
|
||||||
return(open(SDL_cdlist[drive], (O_RDWR|O_NDELAY), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
struct cd_toc toc;
|
|
||||||
struct cd_toc_header hdr;
|
|
||||||
struct cd_toc_entry *cdte;
|
|
||||||
int i;
|
|
||||||
int okay = 0;
|
|
||||||
if ( ioctl(cdrom->id, CDROM_TOC_HEADER, &hdr) ) {
|
|
||||||
fprintf(stderr,"ioctl error CDROM_TOC_HEADER\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
cdrom->numtracks = hdr.th_ending_track - hdr.th_starting_track + 1;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr,"hdr.th_data_len1 = %d\n", hdr.th_data_len1);
|
|
||||||
fprintf(stderr,"hdr.th_data_len0 = %d\n", hdr.th_data_len0);
|
|
||||||
fprintf(stderr,"hdr.th_starting_track = %d\n", hdr.th_starting_track);
|
|
||||||
fprintf(stderr,"hdr.th_ending_track = %d\n", hdr.th_ending_track);
|
|
||||||
fprintf(stderr,"cdrom->numtracks = %d\n", cdrom->numtracks);
|
|
||||||
#endif
|
|
||||||
toc.toc_address_format = CDROM_LBA_FORMAT;
|
|
||||||
toc.toc_starting_track = 0;
|
|
||||||
toc.toc_alloc_length = (hdr.th_data_len1 << 8) +
|
|
||||||
hdr.th_data_len0 + sizeof(hdr);
|
|
||||||
if ( (toc.toc_buffer = alloca(toc.toc_alloc_length)) == NULL) {
|
|
||||||
fprintf(stderr,"cannot allocate toc.toc_buffer\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bzero (toc.toc_buffer, toc.toc_alloc_length);
|
|
||||||
if (ioctl(cdrom->id, CDROM_TOC_ENTRYS, &toc)) {
|
|
||||||
fprintf(stderr,"ioctl error CDROM_TOC_ENTRYS\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
cdte =(struct cd_toc_entry *) ((char *) toc.toc_buffer + sizeof(hdr));
|
|
||||||
for (i=0; i <= cdrom->numtracks; ++i) {
|
|
||||||
if (i == cdrom->numtracks ) {
|
|
||||||
cdrom->track[i].id = 0xAA;;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].id = hdr.th_starting_track + i;
|
|
||||||
}
|
|
||||||
|
|
||||||
cdrom->track[i].type =
|
|
||||||
cdte[i].te_control & CDROM_DATA_TRACK;
|
|
||||||
cdrom->track[i].offset =
|
|
||||||
cdte[i].te_absaddr.lba.addr3 << 24 |
|
|
||||||
cdte[i].te_absaddr.lba.addr2 << 16 |
|
|
||||||
cdte[i].te_absaddr.lba.addr1 << 8 |
|
|
||||||
cdte[i].te_absaddr.lba.addr0;
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if ( i > 0 ) {
|
|
||||||
cdrom->track[i - 1].length =
|
|
||||||
cdrom->track[i].offset -
|
|
||||||
cdrom->track[i - 1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
for (i = 0; i <= cdrom->numtracks; i++) {
|
|
||||||
fprintf(stderr,"toc_entry[%d].te_track_number = %d\n",
|
|
||||||
i,cdte[i].te_track_number);
|
|
||||||
fprintf(stderr,"cdrom->track[%d].id = %d\n", i,cdrom->track[i].id);
|
|
||||||
fprintf(stderr,"cdrom->track[%d].type = %x\n", i,cdrom->track[i].type);
|
|
||||||
fprintf(stderr,"cdrom->track[%d].offset = %d\n", i,cdrom->track[i].offset);
|
|
||||||
fprintf(stderr,"cdrom->track[%d].length = %d\n", i,cdrom->track[i].length);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if ( i == (cdrom->numtracks+1) ) {
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
struct cd_sub_channel sc;
|
|
||||||
struct cd_subc_channel_data scd;
|
|
||||||
|
|
||||||
sc.sch_address_format = CDROM_LBA_FORMAT;
|
|
||||||
sc.sch_data_format = CDROM_CURRENT_POSITION;
|
|
||||||
sc.sch_track_number = 0;
|
|
||||||
sc.sch_alloc_length = sizeof(scd);
|
|
||||||
sc.sch_buffer = (caddr_t)&scd;
|
|
||||||
if ( ioctl(cdrom->id, CDROM_READ_SUBCHANNEL, &sc) ) {
|
|
||||||
status = CD_ERROR;
|
|
||||||
fprintf(stderr,"ioctl error CDROM_READ_SUBCHANNEL \n");
|
|
||||||
} else {
|
|
||||||
switch (scd.scd_header.sh_audio_status) {
|
|
||||||
case AS_AUDIO_INVALID:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
case AS_PLAY_IN_PROGRESS:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case AS_PLAY_PAUSED:
|
|
||||||
status = CD_PAUSED;
|
|
||||||
break;
|
|
||||||
case AS_PLAY_COMPLETED:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
case AS_PLAY_ERROR:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
case AS_NO_STATUS:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#ifdef DEBUG_CDROM
|
|
||||||
fprintf(stderr,"scd.scd_header.sh_audio_status = %x\n",
|
|
||||||
scd.scd_header.sh_audio_status);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
if (position) {
|
|
||||||
if (status == CD_PLAYING || (status == CD_PAUSED) ) {
|
|
||||||
*position =
|
|
||||||
scd.scd_position_data.scp_absaddr.lba.addr3 << 24 |
|
|
||||||
scd.scd_position_data.scp_absaddr.lba.addr2 << 16 |
|
|
||||||
scd.scd_position_data.scp_absaddr.lba.addr1 << 8 |
|
|
||||||
scd.scd_position_data.scp_absaddr.lba.addr0;
|
|
||||||
} else {
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Play MSF
|
|
||||||
*/
|
|
||||||
struct cd_play_audio_msf msf;
|
|
||||||
int end;
|
|
||||||
|
|
||||||
bzero(&msf, sizeof(msf));
|
|
||||||
end = start +length;
|
|
||||||
FRAMES_TO_MSF(start + 150, /* LBA = 4500*M + 75*S + F - 150 */
|
|
||||||
&msf.msf_starting_M_unit,
|
|
||||||
&msf.msf_starting_S_unit,
|
|
||||||
&msf.msf_starting_F_unit);
|
|
||||||
FRAMES_TO_MSF(end + 150, /* LBA = 4500*M + 75*S + F - 150 */
|
|
||||||
&msf.msf_ending_M_unit,
|
|
||||||
&msf.msf_ending_S_unit,
|
|
||||||
&msf.msf_ending_F_unit);
|
|
||||||
|
|
||||||
return(ioctl(cdrom->id, CDROM_PLAY_AUDIO_MSF, &msf));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(ioctl(cdrom->id, CDROM_PAUSE_PLAY));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(ioctl(cdrom->id, CDROM_RESUME_PLAY));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(ioctl(cdrom->id, SCSI_STOP_UNIT));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(ioctl(cdrom->id, CDROM_EJECT_CADDY));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
close(cdrom->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_OSF */
|
|
||||||
|
|
@ -1,551 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_QNX
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/cdrom.h>
|
|
||||||
#include <sys/dcmd_cam.h>
|
|
||||||
|
|
||||||
#include "SDL_timer.h"
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect */
|
|
||||||
#define MAX_DRIVES 16
|
|
||||||
|
|
||||||
#define QNX_CD_OPENMODE O_RDONLY | O_EXCL
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
static dev_t SDL_cdmode[MAX_DRIVES];
|
|
||||||
static int SDL_cdopen[MAX_DRIVES];
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
/* Check a drive to see if it is a CD-ROM */
|
|
||||||
static int CheckDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int is_cd, cdfd;
|
|
||||||
cam_devinfo_t dinfo;
|
|
||||||
int devctlret=0;
|
|
||||||
|
|
||||||
int atapi;
|
|
||||||
int removable;
|
|
||||||
int cdb10;
|
|
||||||
|
|
||||||
/* If it doesn't exist, return -1 */
|
|
||||||
if (stat(drive, stbuf) < 0)
|
|
||||||
{
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If it does exist, verify that it's an available CD-ROM */
|
|
||||||
is_cd = 0;
|
|
||||||
|
|
||||||
if (S_ISCHR(stbuf->st_mode) || S_ISBLK(stbuf->st_mode))
|
|
||||||
{
|
|
||||||
cdfd = open(drive, QNX_CD_OPENMODE);
|
|
||||||
if ( cdfd >= 0 )
|
|
||||||
{
|
|
||||||
devctlret=devctl(cdfd, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
|
|
||||||
|
|
||||||
if (devctlret==EOK)
|
|
||||||
{
|
|
||||||
atapi=dinfo.flags & DEV_ATAPI;
|
|
||||||
removable=dinfo.flags & DEV_REMOVABLE;
|
|
||||||
cdb10=dinfo.flags & DEV_CDB_10; /* I'm not sure about that flag */
|
|
||||||
|
|
||||||
/* in the near future need to add more checks for splitting cdroms from other devices */
|
|
||||||
if ((atapi)&&(removable))
|
|
||||||
{
|
|
||||||
is_cd = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(cdfd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(is_cd);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive, struct stat *stbuf)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (SDL_numcds < MAX_DRIVES)
|
|
||||||
{
|
|
||||||
/* Check to make sure it's not already in our list.
|
|
||||||
This can happen when we see a drive via symbolic link. */
|
|
||||||
|
|
||||||
for (i=0; i<SDL_numcds; ++i)
|
|
||||||
{
|
|
||||||
if (stbuf->st_rdev == SDL_cdmode[i])
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add this drive to our list */
|
|
||||||
|
|
||||||
i = SDL_numcds;
|
|
||||||
SDL_cdlist[i] = SDL_strdup(drive);
|
|
||||||
if (SDL_cdlist[i] == NULL)
|
|
||||||
{
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SDL_cdmode[i] = stbuf->st_rdev;
|
|
||||||
++SDL_numcds;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
/* checklist: /dev/cdrom, /dev/cd?, /dev/scd? */
|
|
||||||
static char *checklist[]={"cdrom", "?0 cd?", "?1 cd?", "?0 scd?", NULL};
|
|
||||||
|
|
||||||
char *SDLcdrom;
|
|
||||||
int i, j, exists;
|
|
||||||
char drive[32];
|
|
||||||
struct stat stbuf;
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* clearing device open status */
|
|
||||||
for (i=0; i<MAX_DRIVES; i++)
|
|
||||||
{
|
|
||||||
SDL_cdopen[i]=0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Look in the environment for our CD-ROM drive list */
|
|
||||||
SDLcdrom = SDL_getenv("SDL_CDROM"); /* ':' separated list of devices */
|
|
||||||
if ( SDLcdrom != NULL )
|
|
||||||
{
|
|
||||||
char *cdpath, *delim;
|
|
||||||
size_t len = SDL_strlen(SDLcdrom)+1;
|
|
||||||
cdpath = SDL_stack_alloc(char, len);
|
|
||||||
if (cdpath != NULL)
|
|
||||||
{
|
|
||||||
SDL_strlcpy(cdpath, SDLcdrom, len);
|
|
||||||
SDLcdrom = cdpath;
|
|
||||||
do {
|
|
||||||
delim = SDL_strchr(SDLcdrom, ':');
|
|
||||||
if (delim)
|
|
||||||
{
|
|
||||||
*delim++ = '\0';
|
|
||||||
}
|
|
||||||
if (CheckDrive(SDLcdrom, &stbuf) > 0)
|
|
||||||
{
|
|
||||||
AddDrive(SDLcdrom, &stbuf);
|
|
||||||
}
|
|
||||||
if (delim)
|
|
||||||
{
|
|
||||||
SDLcdrom = delim;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SDLcdrom = NULL;
|
|
||||||
}
|
|
||||||
} while (SDLcdrom);
|
|
||||||
SDL_stack_free(cdpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we found our drives, there's nothing left to do */
|
|
||||||
if (SDL_numcds > 0)
|
|
||||||
{
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Scan the system for CD-ROM drives */
|
|
||||||
for ( i=0; checklist[i]; ++i )
|
|
||||||
{
|
|
||||||
if (checklist[i][0] == '?')
|
|
||||||
{
|
|
||||||
char* insert;
|
|
||||||
exists = 1;
|
|
||||||
|
|
||||||
for ( j=checklist[i][1]; exists; ++j )
|
|
||||||
{
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", &checklist[i][3]);
|
|
||||||
insert = SDL_strchr(drive, '?');
|
|
||||||
if (insert != NULL)
|
|
||||||
{
|
|
||||||
*insert = j;
|
|
||||||
}
|
|
||||||
switch (CheckDrive(drive, &stbuf))
|
|
||||||
{
|
|
||||||
/* Drive exists and is a CD-ROM */
|
|
||||||
case 1:
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
break;
|
|
||||||
/* Drive exists, but isn't a CD-ROM */
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
/* Drive doesn't exist */
|
|
||||||
case -1:
|
|
||||||
exists = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "/dev/%s", checklist[i]);
|
|
||||||
if (CheckDrive(drive, &stbuf) > 0)
|
|
||||||
{
|
|
||||||
AddDrive(drive, &stbuf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
int handle;
|
|
||||||
|
|
||||||
handle=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
|
|
||||||
|
|
||||||
if (handle>0)
|
|
||||||
{
|
|
||||||
SDL_cdopen[drive]=handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
cdrom_read_toc_t toc;
|
|
||||||
int i, okay;
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL) == 0)
|
|
||||||
{
|
|
||||||
cdrom->numtracks = toc.last_track - toc.first_track + 1;
|
|
||||||
if (cdrom->numtracks > SDL_MAX_TRACKS)
|
|
||||||
{
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
/* Read all the track TOC entries */
|
|
||||||
for (i=0; i<=cdrom->numtracks; ++i)
|
|
||||||
{
|
|
||||||
if (i == cdrom->numtracks)
|
|
||||||
{
|
|
||||||
cdrom->track[i].id = CDROM_LEADOUT;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cdrom->track[i].id = toc.first_track+i;
|
|
||||||
}
|
|
||||||
|
|
||||||
cdrom->track[i].type = toc.toc_entry[i].control_adr & 0x0F;
|
|
||||||
cdrom->track[i].offset = toc.toc_entry[i].addr.lba;
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
cdrom->track[i-1].length = cdrom->track[i].offset-cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i == (cdrom->numtracks+1))
|
|
||||||
{
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
|
|
||||||
cdrom_read_toc_t toc;
|
|
||||||
cdrom_subch_data_t info;
|
|
||||||
cam_devinfo_t dinfo;
|
|
||||||
|
|
||||||
int devctlret=0;
|
|
||||||
int drive=-1;
|
|
||||||
int i;
|
|
||||||
int eagaincnt=0;
|
|
||||||
|
|
||||||
/* check media presence before read subchannel call, some cdroms can lockups */
|
|
||||||
/* if no media, while calling read subchannel functions. */
|
|
||||||
devctlret=devctl(cdrom->id, DCMD_CAM_DEVINFO, &dinfo, sizeof(cam_devinfo_t), NULL);
|
|
||||||
|
|
||||||
if (devctlret==EOK)
|
|
||||||
{
|
|
||||||
if ((dinfo.flags & DEV_NO_MEDIA)!=0)
|
|
||||||
{
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
if (position)
|
|
||||||
{
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
return (status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if media exists, then do other stuff */
|
|
||||||
|
|
||||||
SDL_memset(&info, 0x00, sizeof(info));
|
|
||||||
info.subch_command.data_format = CDROM_SUBCH_CURRENT_POSITION;
|
|
||||||
|
|
||||||
do {
|
|
||||||
devctlret=devctl(cdrom->id, DCMD_CAM_CDROMSUBCHNL, &info, sizeof(info), NULL);
|
|
||||||
if (devctlret==EIO)
|
|
||||||
{
|
|
||||||
/* big workaround for media change, handle is unusable after that,
|
|
||||||
that bug was found in QNX 6.2, 6.2.1 is not released yet. */
|
|
||||||
|
|
||||||
for (i=0; i<MAX_DRIVES; i++)
|
|
||||||
{
|
|
||||||
if (SDL_cdopen[i]==cdrom->id)
|
|
||||||
{
|
|
||||||
drive=i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (drive==-1)
|
|
||||||
{
|
|
||||||
/* that cannot happen, but ... */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
close(cdrom->id);
|
|
||||||
cdrom->id=open(SDL_cdlist[drive], QNX_CD_OPENMODE);
|
|
||||||
devctlret=EAGAIN;
|
|
||||||
}
|
|
||||||
if (devctlret==EAGAIN)
|
|
||||||
{
|
|
||||||
eagaincnt++;
|
|
||||||
}
|
|
||||||
if (eagaincnt==2)
|
|
||||||
{
|
|
||||||
/* workaround for broken cdroms, which can return always EAGAIN when its not ready, */
|
|
||||||
/* that mean errornous media or just no media avail */
|
|
||||||
devctlret=ENXIO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} while ((devctlret==EAGAIN)||(devctlret==ESTALE));
|
|
||||||
|
|
||||||
if (devctlret != 0)
|
|
||||||
{
|
|
||||||
if (devctlret==ENXIO)
|
|
||||||
{
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = CD_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (info.current_position.header.audio_status)
|
|
||||||
{
|
|
||||||
case CDROM_AUDIO_INVALID:
|
|
||||||
case CDROM_AUDIO_NO_STATUS:
|
|
||||||
/* Try to determine if there's a CD available */
|
|
||||||
if (devctl(cdrom->id, DCMD_CAM_CDROMREADTOC, &toc, sizeof(toc), NULL)==0)
|
|
||||||
status = CD_STOPPED;
|
|
||||||
else
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_COMPLETED:
|
|
||||||
status = CD_STOPPED;
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_PLAY:
|
|
||||||
status = CD_PLAYING;
|
|
||||||
break;
|
|
||||||
case CDROM_AUDIO_PAUSED:
|
|
||||||
/* Workaround buggy CD-ROM drive */
|
|
||||||
if (info.current_position.data_format == CDROM_LEADOUT)
|
|
||||||
{
|
|
||||||
status = CD_STOPPED;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = CD_PAUSED;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position)
|
|
||||||
{
|
|
||||||
if (status==CD_PLAYING || (status==CD_PAUSED))
|
|
||||||
{
|
|
||||||
*position = MSF_TO_FRAMES(info.current_position.addr.msf.minute,
|
|
||||||
info.current_position.addr.msf.second,
|
|
||||||
info.current_position.addr.msf.frame);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
cdrom_playmsf_t playtime;
|
|
||||||
|
|
||||||
FRAMES_TO_MSF(start, &playtime.start_minute, &playtime.start_second, &playtime.start_frame);
|
|
||||||
FRAMES_TO_MSF(start+length, &playtime.end_minute, &playtime.end_second, &playtime.end_frame);
|
|
||||||
|
|
||||||
if (devctl(cdrom->id, DCMD_CAM_CDROMPLAYMSF, &playtime, sizeof(playtime), NULL) != 0)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
if (devctl(cdrom->id, DCMD_CAM_CDROMPAUSE, NULL, 0, NULL)!=0)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
if (devctl(cdrom->id, DCMD_CAM_CDROMRESUME, NULL, 0, NULL)!=0)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
if (devctl(cdrom->id, DCMD_CAM_CDROMSTOP, NULL, 0, NULL)!=0)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
if (devctl(cdrom->id, DCMD_CAM_EJECT_MEDIA, NULL, 0, NULL)!=0)
|
|
||||||
{
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i=0; i<MAX_DRIVES; i++)
|
|
||||||
{
|
|
||||||
if (SDL_cdopen[i]==cdrom->id)
|
|
||||||
{
|
|
||||||
SDL_cdopen[i]=0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(cdrom->id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (SDL_numcds > 0)
|
|
||||||
{
|
|
||||||
for (i=0; i<SDL_numcds; ++i)
|
|
||||||
{
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_QNX */
|
|
||||||
|
|
@ -1,386 +0,0 @@
|
||||||
/*
|
|
||||||
SDL - Simple DirectMedia Layer
|
|
||||||
Copyright (C) 1997-2012 Sam Lantinga
|
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
|
||||||
modify it under the terms of the GNU Lesser General Public
|
|
||||||
License as published by the Free Software Foundation; either
|
|
||||||
version 2.1 of the License, or (at your option) any later version.
|
|
||||||
|
|
||||||
This library 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
|
|
||||||
Lesser General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public
|
|
||||||
License along with this library; if not, write to the Free Software
|
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Sam Lantinga
|
|
||||||
slouken@libsdl.org
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifdef SDL_CDROM_WIN32
|
|
||||||
|
|
||||||
/* Functions for system-level CD-ROM audio control */
|
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#include <windows.h>
|
|
||||||
#include <mmsystem.h>
|
|
||||||
|
|
||||||
#include "SDL_cdrom.h"
|
|
||||||
#include "../SDL_syscdrom.h"
|
|
||||||
|
|
||||||
/* This really broken?? */
|
|
||||||
#define BROKEN_MCI_PAUSE /* Pausing actually stops play -- Doh! */
|
|
||||||
|
|
||||||
/* The maximum number of CD-ROM drives we'll detect (Don't change!) */
|
|
||||||
#define MAX_DRIVES 26
|
|
||||||
|
|
||||||
/* A list of available CD-ROM drives */
|
|
||||||
static char *SDL_cdlist[MAX_DRIVES];
|
|
||||||
static MCIDEVICEID SDL_mciID[MAX_DRIVES];
|
|
||||||
#ifdef BROKEN_MCI_PAUSE
|
|
||||||
static int SDL_paused[MAX_DRIVES];
|
|
||||||
#endif
|
|
||||||
static int SDL_CD_end_position;
|
|
||||||
|
|
||||||
/* The system-dependent CD control functions */
|
|
||||||
static const char *SDL_SYS_CDName(int drive);
|
|
||||||
static int SDL_SYS_CDOpen(int drive);
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom);
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position);
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length);
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom);
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom);
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom);
|
|
||||||
|
|
||||||
|
|
||||||
/* Add a CD-ROM drive to our list of valid drives */
|
|
||||||
static void AddDrive(char *drive)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds < MAX_DRIVES ) {
|
|
||||||
/* Add this drive to our list */
|
|
||||||
i = SDL_numcds;
|
|
||||||
SDL_cdlist[i] = SDL_strdup(drive);
|
|
||||||
if ( SDL_cdlist[i] == NULL ) {
|
|
||||||
SDL_OutOfMemory();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
++SDL_numcds;
|
|
||||||
#ifdef CDROM_DEBUG
|
|
||||||
fprintf(stderr, "Added CD-ROM drive: %s\n", drive);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int SDL_SYS_CDInit(void)
|
|
||||||
{
|
|
||||||
/* checklist: Drive 'A' - 'Z' */
|
|
||||||
int i;
|
|
||||||
char drive[4];
|
|
||||||
|
|
||||||
/* Fill in our driver capabilities */
|
|
||||||
SDL_CDcaps.Name = SDL_SYS_CDName;
|
|
||||||
SDL_CDcaps.Open = SDL_SYS_CDOpen;
|
|
||||||
SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC;
|
|
||||||
SDL_CDcaps.Status = SDL_SYS_CDStatus;
|
|
||||||
SDL_CDcaps.Play = SDL_SYS_CDPlay;
|
|
||||||
SDL_CDcaps.Pause = SDL_SYS_CDPause;
|
|
||||||
SDL_CDcaps.Resume = SDL_SYS_CDResume;
|
|
||||||
SDL_CDcaps.Stop = SDL_SYS_CDStop;
|
|
||||||
SDL_CDcaps.Eject = SDL_SYS_CDEject;
|
|
||||||
SDL_CDcaps.Close = SDL_SYS_CDClose;
|
|
||||||
|
|
||||||
/* Scan the system for CD-ROM drives */
|
|
||||||
for ( i='A'; i<='Z'; ++i ) {
|
|
||||||
SDL_snprintf(drive, SDL_arraysize(drive), "%c:\\", i);
|
|
||||||
if ( GetDriveType(drive) == DRIVE_CDROM ) {
|
|
||||||
AddDrive(drive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_memset(SDL_mciID, 0, sizeof(SDL_mciID));
|
|
||||||
return(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* General ioctl() CD-ROM command function */
|
|
||||||
static int SDL_SYS_CDioctl(int id, UINT msg, DWORD flags, void *arg)
|
|
||||||
{
|
|
||||||
MCIERROR mci_error;
|
|
||||||
|
|
||||||
mci_error = mciSendCommand(SDL_mciID[id], msg, flags, (DWORD_PTR)arg);
|
|
||||||
if ( mci_error ) {
|
|
||||||
char error[256];
|
|
||||||
|
|
||||||
mciGetErrorString(mci_error, error, 256);
|
|
||||||
SDL_SetError("mciSendCommand() error: %s", error);
|
|
||||||
}
|
|
||||||
return(!mci_error ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *SDL_SYS_CDName(int drive)
|
|
||||||
{
|
|
||||||
return(SDL_cdlist[drive]);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDOpen(int drive)
|
|
||||||
{
|
|
||||||
MCI_OPEN_PARMS mci_open;
|
|
||||||
MCI_SET_PARMS mci_set;
|
|
||||||
char device[3];
|
|
||||||
DWORD flags;
|
|
||||||
|
|
||||||
/* Open the requested device */
|
|
||||||
mci_open.lpstrDeviceType = (LPCSTR) MCI_DEVTYPE_CD_AUDIO;
|
|
||||||
device[0] = *SDL_cdlist[drive];
|
|
||||||
device[1] = ':';
|
|
||||||
device[2] = '\0';
|
|
||||||
mci_open.lpstrElementName = device;
|
|
||||||
flags =
|
|
||||||
(MCI_OPEN_TYPE|MCI_OPEN_SHAREABLE|MCI_OPEN_TYPE_ID|MCI_OPEN_ELEMENT);
|
|
||||||
if ( SDL_SYS_CDioctl(0, MCI_OPEN, flags, &mci_open) < 0 ) {
|
|
||||||
flags &= ~MCI_OPEN_SHAREABLE;
|
|
||||||
if ( SDL_SYS_CDioctl(0, MCI_OPEN, flags, &mci_open) < 0 ) {
|
|
||||||
return(-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_mciID[drive] = mci_open.wDeviceID;
|
|
||||||
|
|
||||||
/* Set the minute-second-frame time format */
|
|
||||||
mci_set.dwTimeFormat = MCI_FORMAT_MSF;
|
|
||||||
SDL_SYS_CDioctl(drive, MCI_SET, MCI_SET_TIME_FORMAT, &mci_set);
|
|
||||||
|
|
||||||
#ifdef BROKEN_MCI_PAUSE
|
|
||||||
SDL_paused[drive] = 0;
|
|
||||||
#endif
|
|
||||||
return(drive);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int SDL_SYS_CDGetTOC(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
MCI_STATUS_PARMS mci_status;
|
|
||||||
int i, okay;
|
|
||||||
DWORD flags;
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
mci_status.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
|
|
||||||
flags = MCI_STATUS_ITEM | MCI_WAIT;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) == 0 ) {
|
|
||||||
cdrom->numtracks = mci_status.dwReturn;
|
|
||||||
if ( cdrom->numtracks > SDL_MAX_TRACKS ) {
|
|
||||||
cdrom->numtracks = SDL_MAX_TRACKS;
|
|
||||||
}
|
|
||||||
/* Read all the track TOC entries */
|
|
||||||
flags = MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT;
|
|
||||||
for ( i=0; i<cdrom->numtracks; ++i ) {
|
|
||||||
cdrom->track[i].id = i+1;
|
|
||||||
mci_status.dwTrack = cdrom->track[i].id;
|
|
||||||
#ifdef MCI_CDA_STATUS_TYPE_TRACK
|
|
||||||
mci_status.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
|
|
||||||
&mci_status) < 0 ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ( mci_status.dwReturn == MCI_CDA_TRACK_AUDIO ) {
|
|
||||||
cdrom->track[i].type = SDL_AUDIO_TRACK;
|
|
||||||
} else {
|
|
||||||
cdrom->track[i].type = SDL_DATA_TRACK;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
cdrom->track[i].type = SDL_AUDIO_TRACK;
|
|
||||||
#endif
|
|
||||||
mci_status.dwItem = MCI_STATUS_POSITION;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
|
|
||||||
&mci_status) < 0 ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
cdrom->track[i].offset = MSF_TO_FRAMES(
|
|
||||||
MCI_MSF_MINUTE(mci_status.dwReturn),
|
|
||||||
MCI_MSF_SECOND(mci_status.dwReturn),
|
|
||||||
MCI_MSF_FRAME(mci_status.dwReturn));
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
if ( i > 0 ) {
|
|
||||||
cdrom->track[i-1].length =
|
|
||||||
cdrom->track[i].offset-
|
|
||||||
cdrom->track[i-1].offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( i == cdrom->numtracks ) {
|
|
||||||
mci_status.dwTrack = cdrom->track[i - 1].id;
|
|
||||||
mci_status.dwItem = MCI_STATUS_LENGTH;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
|
|
||||||
&mci_status) == 0 ) {
|
|
||||||
cdrom->track[i - 1].length = MSF_TO_FRAMES(
|
|
||||||
MCI_MSF_MINUTE(mci_status.dwReturn),
|
|
||||||
MCI_MSF_SECOND(mci_status.dwReturn),
|
|
||||||
MCI_MSF_FRAME(mci_status.dwReturn));
|
|
||||||
/* compute lead-out offset */
|
|
||||||
cdrom->track[i].offset = cdrom->track[i - 1].offset +
|
|
||||||
cdrom->track[i - 1].length;
|
|
||||||
cdrom->track[i].length = 0;
|
|
||||||
okay = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get CD-ROM status */
|
|
||||||
static CDstatus SDL_SYS_CDStatus(SDL_CD *cdrom, int *position)
|
|
||||||
{
|
|
||||||
CDstatus status;
|
|
||||||
MCI_STATUS_PARMS mci_status;
|
|
||||||
DWORD flags;
|
|
||||||
|
|
||||||
flags = MCI_STATUS_ITEM | MCI_WAIT;
|
|
||||||
mci_status.dwItem = MCI_STATUS_MODE;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) < 0 ) {
|
|
||||||
status = CD_ERROR;
|
|
||||||
} else {
|
|
||||||
switch (mci_status.dwReturn) {
|
|
||||||
case MCI_MODE_NOT_READY:
|
|
||||||
case MCI_MODE_OPEN:
|
|
||||||
status = CD_TRAYEMPTY;
|
|
||||||
break;
|
|
||||||
case MCI_MODE_STOP:
|
|
||||||
#ifdef BROKEN_MCI_PAUSE
|
|
||||||
if ( SDL_paused[cdrom->id] ) {
|
|
||||||
status = CD_PAUSED;
|
|
||||||
} else {
|
|
||||||
status = CD_STOPPED;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
status = CD_STOPPED;
|
|
||||||
#endif /* BROKEN_MCI_PAUSE */
|
|
||||||
break;
|
|
||||||
case MCI_MODE_PLAY:
|
|
||||||
#ifdef BROKEN_MCI_PAUSE
|
|
||||||
if ( SDL_paused[cdrom->id] ) {
|
|
||||||
status = CD_PAUSED;
|
|
||||||
} else {
|
|
||||||
status = CD_PLAYING;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
status = CD_PLAYING;
|
|
||||||
#endif /* BROKEN_MCI_PAUSE */
|
|
||||||
break;
|
|
||||||
case MCI_MODE_PAUSE:
|
|
||||||
status = CD_PAUSED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = CD_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( position ) {
|
|
||||||
if ( status == CD_PLAYING || (status == CD_PAUSED) ) {
|
|
||||||
mci_status.dwItem = MCI_STATUS_POSITION;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags,
|
|
||||||
&mci_status) == 0 ) {
|
|
||||||
*position = MSF_TO_FRAMES(
|
|
||||||
MCI_MSF_MINUTE(mci_status.dwReturn),
|
|
||||||
MCI_MSF_SECOND(mci_status.dwReturn),
|
|
||||||
MCI_MSF_FRAME(mci_status.dwReturn));
|
|
||||||
} else {
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
*position = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Start play */
|
|
||||||
static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length)
|
|
||||||
{
|
|
||||||
MCI_PLAY_PARMS mci_play;
|
|
||||||
int m, s, f;
|
|
||||||
DWORD flags;
|
|
||||||
|
|
||||||
flags = MCI_FROM | MCI_TO | MCI_NOTIFY;
|
|
||||||
mci_play.dwCallback = 0;
|
|
||||||
FRAMES_TO_MSF(start, &m, &s, &f);
|
|
||||||
mci_play.dwFrom = MCI_MAKE_MSF(m, s, f);
|
|
||||||
FRAMES_TO_MSF(start+length, &m, &s, &f);
|
|
||||||
mci_play.dwTo = MCI_MAKE_MSF(m, s, f);
|
|
||||||
SDL_CD_end_position = mci_play.dwTo;
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, MCI_PLAY, flags, &mci_play));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Pause play */
|
|
||||||
static int SDL_SYS_CDPause(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
#ifdef BROKEN_MCI_PAUSE
|
|
||||||
SDL_paused[cdrom->id] = 1;
|
|
||||||
#endif
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, MCI_PAUSE, MCI_WAIT, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resume play */
|
|
||||||
static int SDL_SYS_CDResume(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
#ifdef BROKEN_MCI_PAUSE
|
|
||||||
MCI_STATUS_PARMS mci_status;
|
|
||||||
int okay;
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
okay = 0;
|
|
||||||
/* Play from the current play position to the end position set earlier */
|
|
||||||
flags = MCI_STATUS_ITEM | MCI_WAIT;
|
|
||||||
mci_status.dwItem = MCI_STATUS_POSITION;
|
|
||||||
if ( SDL_SYS_CDioctl(cdrom->id, MCI_STATUS, flags, &mci_status) == 0 ) {
|
|
||||||
MCI_PLAY_PARMS mci_play;
|
|
||||||
|
|
||||||
flags = MCI_FROM | MCI_TO | MCI_NOTIFY;
|
|
||||||
mci_play.dwCallback = 0;
|
|
||||||
mci_play.dwFrom = mci_status.dwReturn;
|
|
||||||
mci_play.dwTo = SDL_CD_end_position;
|
|
||||||
if (SDL_SYS_CDioctl(cdrom->id,MCI_PLAY,flags,&mci_play) == 0) {
|
|
||||||
okay = 1;
|
|
||||||
SDL_paused[cdrom->id] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return(okay ? 0 : -1);
|
|
||||||
#else
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, MCI_RESUME, MCI_WAIT, NULL));
|
|
||||||
#endif /* BROKEN_MCI_PAUSE */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Stop play */
|
|
||||||
static int SDL_SYS_CDStop(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, MCI_STOP, MCI_WAIT, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Eject the CD-ROM */
|
|
||||||
static int SDL_SYS_CDEject(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
return(SDL_SYS_CDioctl(cdrom->id, MCI_SET, MCI_SET_DOOR_OPEN, NULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Close the CD-ROM handle */
|
|
||||||
static void SDL_SYS_CDClose(SDL_CD *cdrom)
|
|
||||||
{
|
|
||||||
SDL_SYS_CDioctl(cdrom->id, MCI_CLOSE, MCI_WAIT, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_SYS_CDQuit(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if ( SDL_numcds > 0 ) {
|
|
||||||
for ( i=0; i<SDL_numcds; ++i ) {
|
|
||||||
SDL_free(SDL_cdlist[i]);
|
|
||||||
SDL_cdlist[i] = NULL;
|
|
||||||
}
|
|
||||||
SDL_numcds = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* SDL_CDROM_WIN32 */
|
|
||||||
|
|
@ -1,438 +0,0 @@
|
||||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
|
||||||
Version 2, June 1991
|
|
||||||
|
|
||||||
Copyright (C) 1991 Free Software Foundation, Inc.
|
|
||||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
[This is the first released version of the library GPL. It is
|
|
||||||
numbered 2 because it goes with version 2 of the ordinary GPL.]
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
Licenses are intended to guarantee your freedom to share and change
|
|
||||||
free software--to make sure the software is free for all its users.
|
|
||||||
|
|
||||||
This license, the Library General Public License, applies to some
|
|
||||||
specially designated Free Software Foundation software, and to any
|
|
||||||
other libraries whose authors decide to use it. You can use it for
|
|
||||||
your libraries, too.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom, not
|
|
||||||
price. Our General Public Licenses are designed to make sure that you
|
|
||||||
have the freedom to distribute copies of free software (and charge for
|
|
||||||
this service if you wish), that you receive source code or can get it
|
|
||||||
if you want it, that you can change the software or use pieces of it
|
|
||||||
in new free programs; and that you know you can do these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
anyone to deny you these rights or to ask you to surrender the rights.
|
|
||||||
These restrictions translate to certain responsibilities for you if
|
|
||||||
you distribute copies of the library, or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of the library, whether gratis
|
|
||||||
or for a fee, you must give the recipients all the rights that we gave
|
|
||||||
you. You must make sure that they, too, receive or can get the source
|
|
||||||
code. If you link a program with the library, you must provide
|
|
||||||
complete object files to the recipients so that they can relink them
|
|
||||||
with the library, after making changes to the library and recompiling
|
|
||||||
it. And you must show them these terms so they know their rights.
|
|
||||||
|
|
||||||
Our method of protecting your rights has two steps: (1) copyright
|
|
||||||
the library, and (2) offer you this license which gives you legal
|
|
||||||
permission to copy, distribute and/or modify the library.
|
|
||||||
|
|
||||||
Also, for each distributor's protection, we want to make certain
|
|
||||||
that everyone understands that there is no warranty for this free
|
|
||||||
library. If the library is modified by someone else and passed on, we
|
|
||||||
want its recipients to know that what they have is not the original
|
|
||||||
version, so that any problems introduced by others will not reflect on
|
|
||||||
the original authors' reputations.
|
|
||||||
|
|
||||||
Finally, any free program is threatened constantly by software
|
|
||||||
patents. We wish to avoid the danger that companies distributing free
|
|
||||||
software will individually obtain patent licenses, thus in effect
|
|
||||||
transforming the program into proprietary software. To prevent this,
|
|
||||||
we have made it clear that any patent must be licensed for everyone's
|
|
||||||
free use or not licensed at all.
|
|
||||||
|
|
||||||
Most GNU software, including some libraries, is covered by the ordinary
|
|
||||||
GNU General Public License, which was designed for utility programs. This
|
|
||||||
license, the GNU Library General Public License, applies to certain
|
|
||||||
designated libraries. This license is quite different from the ordinary
|
|
||||||
one; be sure to read it in full, and don't assume that anything in it is
|
|
||||||
the same as in the ordinary license.
|
|
||||||
|
|
||||||
The reason we have a separate public license for some libraries is that
|
|
||||||
they blur the distinction we usually make between modifying or adding to a
|
|
||||||
program and simply using it. Linking a program with a library, without
|
|
||||||
changing the library, is in some sense simply using the library, and is
|
|
||||||
analogous to running a utility program or application program. However, in
|
|
||||||
a textual and legal sense, the linked executable is a combined work, a
|
|
||||||
derivative of the original library, and the ordinary General Public License
|
|
||||||
treats it as such.
|
|
||||||
|
|
||||||
Because of this blurred distinction, using the ordinary General
|
|
||||||
Public License for libraries did not effectively promote software
|
|
||||||
sharing, because most developers did not use the libraries. We
|
|
||||||
concluded that weaker conditions might promote sharing better.
|
|
||||||
|
|
||||||
However, unrestricted linking of non-free programs would deprive the
|
|
||||||
users of those programs of all benefit from the free status of the
|
|
||||||
libraries themselves. This Library General Public License is intended to
|
|
||||||
permit developers of non-free programs to use free libraries, while
|
|
||||||
preserving your freedom as a user of such programs to change the free
|
|
||||||
libraries that are incorporated in them. (We have not seen how to achieve
|
|
||||||
this as regards changes in header files, but we have achieved it as regards
|
|
||||||
changes in the actual functions of the Library.) The hope is that this
|
|
||||||
will lead to faster development of free libraries.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow. Pay close attention to the difference between a
|
|
||||||
"work based on the library" and a "work that uses the library". The
|
|
||||||
former contains code derived from the library, while the latter only
|
|
||||||
works together with the library.
|
|
||||||
|
|
||||||
Note that it is possible for a library to be covered by the ordinary
|
|
||||||
General Public License rather than by this special one.
|
|
||||||
|
|
||||||
GNU LIBRARY GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License Agreement applies to any software library which
|
|
||||||
contains a notice placed by the copyright holder or other authorized
|
|
||||||
party saying it may be distributed under the terms of this Library
|
|
||||||
General Public License (also called "this License"). Each licensee is
|
|
||||||
addressed as "you".
|
|
||||||
|
|
||||||
A "library" means a collection of software functions and/or data
|
|
||||||
prepared so as to be conveniently linked with application programs
|
|
||||||
(which use some of those functions and data) to form executables.
|
|
||||||
|
|
||||||
The "Library", below, refers to any such software library or work
|
|
||||||
which has been distributed under these terms. A "work based on the
|
|
||||||
Library" means either the Library or any derivative work under
|
|
||||||
copyright law: that is to say, a work containing the Library or a
|
|
||||||
portion of it, either verbatim or with modifications and/or translated
|
|
||||||
straightforwardly into another language. (Hereinafter, translation is
|
|
||||||
included without limitation in the term "modification".)
|
|
||||||
|
|
||||||
"Source code" for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For a library, complete source code means
|
|
||||||
all the source code for all modules it contains, plus any associated
|
|
||||||
interface definition files, plus the scripts used to control compilation
|
|
||||||
and installation of the library.
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running a program using the Library is not restricted, and output from
|
|
||||||
such a program is covered only if its contents constitute a work based
|
|
||||||
on the Library (independent of the use of the Library in a tool for
|
|
||||||
writing it). Whether that is true depends on what the Library does
|
|
||||||
and what the program that uses the Library does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Library's
|
|
||||||
complete source code as you receive it, in any medium, provided that
|
|
||||||
you conspicuously and appropriately publish on each copy an
|
|
||||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
|
||||||
all the notices that refer to this License and to the absence of any
|
|
||||||
warranty; and distribute a copy of this License along with the
|
|
||||||
Library.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy,
|
|
||||||
and you may at your option offer warranty protection in exchange for a
|
|
||||||
fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Library or any portion
|
|
||||||
of it, thus forming a work based on the Library, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The modified work must itself be a software library.
|
|
||||||
|
|
||||||
b) You must cause the files modified to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
c) You must cause the whole of the work to be licensed at no
|
|
||||||
charge to all third parties under the terms of this License.
|
|
||||||
|
|
||||||
d) If a facility in the modified Library refers to a function or a
|
|
||||||
table of data to be supplied by an application program that uses
|
|
||||||
the facility, other than as an argument passed when the facility
|
|
||||||
is invoked, then you must make a good faith effort to ensure that,
|
|
||||||
in the event an application does not supply such function or
|
|
||||||
table, the facility still operates, and performs whatever part of
|
|
||||||
its purpose remains meaningful.
|
|
||||||
|
|
||||||
(For example, a function in a library to compute square roots has
|
|
||||||
a purpose that is entirely well-defined independent of the
|
|
||||||
application. Therefore, Subsection 2d requires that any
|
|
||||||
application-supplied function or table used by this function must
|
|
||||||
be optional: if the application does not supply it, the square
|
|
||||||
root function must still compute square roots.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Library,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Library, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote
|
|
||||||
it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Library.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Library
|
|
||||||
with the Library (or with a work based on the Library) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
|
||||||
License instead of this License to a given copy of the Library. To do
|
|
||||||
this, you must alter all the notices that refer to this License, so
|
|
||||||
that they refer to the ordinary GNU General Public License, version 2,
|
|
||||||
instead of to this License. (If a newer version than version 2 of the
|
|
||||||
ordinary GNU General Public License has appeared, then you can specify
|
|
||||||
that version instead if you wish.) Do not make any other change in
|
|
||||||
these notices.
|
|
||||||
|
|
||||||
Once this change is made in a given copy, it is irreversible for
|
|
||||||
that copy, so the ordinary GNU General Public License applies to all
|
|
||||||
subsequent copies and derivative works made from that copy.
|
|
||||||
|
|
||||||
This option is useful when you wish to copy part of the code of
|
|
||||||
the Library into a program that is not a library.
|
|
||||||
|
|
||||||
4. You may copy and distribute the Library (or a portion or
|
|
||||||
derivative of it, under Section 2) in object code or executable form
|
|
||||||
under the terms of Sections 1 and 2 above provided that you accompany
|
|
||||||
it with the complete corresponding machine-readable source code, which
|
|
||||||
must be distributed under the terms of Sections 1 and 2 above on a
|
|
||||||
medium customarily used for software interchange.
|
|
||||||
|
|
||||||
If distribution of object code is made by offering access to copy
|
|
||||||
from a designated place, then offering equivalent access to copy the
|
|
||||||
source code from the same place satisfies the requirement to
|
|
||||||
distribute the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
5. A program that contains no derivative of any portion of the
|
|
||||||
Library, but is designed to work with the Library by being compiled or
|
|
||||||
linked with it, is called a "work that uses the Library". Such a
|
|
||||||
work, in isolation, is not a derivative work of the Library, and
|
|
||||||
therefore falls outside the scope of this License.
|
|
||||||
|
|
||||||
However, linking a "work that uses the Library" with the Library
|
|
||||||
creates an executable that is a derivative of the Library (because it
|
|
||||||
contains portions of the Library), rather than a "work that uses the
|
|
||||||
library". The executable is therefore covered by this License.
|
|
||||||
Section 6 states terms for distribution of such executables.
|
|
||||||
|
|
||||||
When a "work that uses the Library" uses material from a header file
|
|
||||||
that is part of the Library, the object code for the work may be a
|
|
||||||
derivative work of the Library even though the source code is not.
|
|
||||||
Whether this is true is especially significant if the work can be
|
|
||||||
linked without the Library, or if the work is itself a library. The
|
|
||||||
threshold for this to be true is not precisely defined by law.
|
|
||||||
|
|
||||||
If such an object file uses only numerical parameters, data
|
|
||||||
structure layouts and accessors, and small macros and small inline
|
|
||||||
functions (ten lines or less in length), then the use of the object
|
|
||||||
file is unrestricted, regardless of whether it is legally a derivative
|
|
||||||
work. (Executables containing this object code plus portions of the
|
|
||||||
Library will still fall under Section 6.)
|
|
||||||
|
|
||||||
Otherwise, if the work is a derivative of the Library, you may
|
|
||||||
distribute the object code for the work under the terms of Section 6.
|
|
||||||
Any executables containing that work also fall under Section 6,
|
|
||||||
whether or not they are linked directly with the Library itself.
|
|
||||||
|
|
||||||
6. As an exception to the Sections above, you may also compile or
|
|
||||||
link a "work that uses the Library" with the Library to produce a
|
|
||||||
work containing portions of the Library, and distribute that work
|
|
||||||
under terms of your choice, provided that the terms permit
|
|
||||||
modification of the work for the customer's own use and reverse
|
|
||||||
engineering for debugging such modifications.
|
|
||||||
|
|
||||||
You must give prominent notice with each copy of the work that the
|
|
||||||
Library is used in it and that the Library and its use are covered by
|
|
||||||
this License. You must supply a copy of this License. If the work
|
|
||||||
during execution displays copyright notices, you must include the
|
|
||||||
copyright notice for the Library among them, as well as a reference
|
|
||||||
directing the user to the copy of this License. Also, you must do one
|
|
||||||
of these things:
|
|
||||||
|
|
||||||
a) Accompany the work with the complete corresponding
|
|
||||||
machine-readable source code for the Library including whatever
|
|
||||||
changes were used in the work (which must be distributed under
|
|
||||||
Sections 1 and 2 above); and, if the work is an executable linked
|
|
||||||
with the Library, with the complete machine-readable "work that
|
|
||||||
uses the Library", as object code and/or source code, so that the
|
|
||||||
user can modify the Library and then relink to produce a modified
|
|
||||||
executable containing the modified Library. (It is understood
|
|
||||||
that the user who changes the contents of definitions files in the
|
|
||||||
Library will not necessarily be able to recompile the application
|
|
||||||
to use the modified definitions.)
|
|
||||||
|
|
||||||
b) Accompany the work with a written offer, valid for at
|
|
||||||
least three years, to give the same user the materials
|
|
||||||
specified in Subsection 6a, above, for a charge no more
|
|
||||||
than the cost of performing this distribution.
|
|
||||||
|
|
||||||
c) If distribution of the work is made by offering access to copy
|
|
||||||
from a designated place, offer equivalent access to copy the above
|
|
||||||
specified materials from the same place.
|
|
||||||
|
|
||||||
d) Verify that the user has already received a copy of these
|
|
||||||
materials or that you have already sent this user a copy.
|
|
||||||
|
|
||||||
For an executable, the required form of the "work that uses the
|
|
||||||
Library" must include any data and utility programs needed for
|
|
||||||
reproducing the executable from it. However, as a special exception,
|
|
||||||
the source code distributed need not include anything that is normally
|
|
||||||
distributed (in either source or binary form) with the major
|
|
||||||
components (compiler, kernel, and so on) of the operating system on
|
|
||||||
which the executable runs, unless that component itself accompanies
|
|
||||||
the executable.
|
|
||||||
|
|
||||||
It may happen that this requirement contradicts the license
|
|
||||||
restrictions of other proprietary libraries that do not normally
|
|
||||||
accompany the operating system. Such a contradiction means you cannot
|
|
||||||
use both them and the Library together in an executable that you
|
|
||||||
distribute.
|
|
||||||
|
|
||||||
7. You may place library facilities that are a work based on the
|
|
||||||
Library side-by-side in a single library together with other library
|
|
||||||
facilities not covered by this License, and distribute such a combined
|
|
||||||
library, provided that the separate distribution of the work based on
|
|
||||||
the Library and of the other library facilities is otherwise
|
|
||||||
permitted, and provided that you do these two things:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work
|
|
||||||
based on the Library, uncombined with any other library
|
|
||||||
facilities. This must be distributed under the terms of the
|
|
||||||
Sections above.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library of the fact
|
|
||||||
that part of it is a work based on the Library, and explaining
|
|
||||||
where to find the accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
8. You may not copy, modify, sublicense, link with, or distribute
|
|
||||||
the Library except as expressly provided under this License. Any
|
|
||||||
attempt otherwise to copy, modify, sublicense, link with, or
|
|
||||||
distribute the Library is void, and will automatically terminate your
|
|
||||||
rights under this License. However, parties who have received copies,
|
|
||||||
or rights, from you under this License will not have their licenses
|
|
||||||
terminated so long as such parties remain in full compliance.
|
|
||||||
|
|
||||||
9. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Library or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Library (or any work based on the
|
|
||||||
Library), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Library or works based on it.
|
|
||||||
|
|
||||||
10. Each time you redistribute the Library (or any work based on the
|
|
||||||
Library), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute, link with or modify the Library
|
|
||||||
subject to these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties to
|
|
||||||
this License.
|
|
||||||
|
|
||||||
11. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Library at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Library by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Library.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under any
|
|
||||||
particular circumstance, the balance of the section is intended to apply,
|
|
||||||
and the section as a whole is intended to apply in other circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
12. If the distribution and/or use of the Library is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Library under this License may add
|
|
||||||
an explicit geographical distribution limitation excluding those countries,
|
|
||||||
so that distribution is permitted only in or among countries not thus
|
|
||||||
excluded. In such case, this License incorporates the limitation as if
|
|
||||||
written in the body of this License.
|
|
||||||
|
|
||||||
13. The Free Software Foundation may publish revised and/or new
|
|
||||||
versions of the Library General Public License from time to time.
|
|
||||||
Such new versions will be similar in spirit to the present version,
|
|
||||||
but may differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Library
|
|
||||||
specifies a version number of this License which applies to it and
|
|
||||||
"any later version", you have the option of following the terms and
|
|
||||||
conditions either of that version or of any later version published by
|
|
||||||
the Free Software Foundation. If the Library does not specify a
|
|
||||||
license version number, you may choose any version ever published by
|
|
||||||
the Free Software Foundation.
|
|
||||||
|
|
||||||
14. If you wish to incorporate parts of the Library into other free
|
|
||||||
programs whose distribution conditions are incompatible with these,
|
|
||||||
write to the author to ask for permission. For software which is
|
|
||||||
copyrighted by the Free Software Foundation, write to the Free
|
|
||||||
Software Foundation; we sometimes make exceptions for this. Our
|
|
||||||
decision will be guided by the two goals of preserving the free status
|
|
||||||
of all derivatives of our free software and of promoting the sharing
|
|
||||||
and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
|
||||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
||||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
|
||||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
|
||||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
|
||||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
|
||||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
|
||||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
|
||||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
|
||||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
|
||||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
|
||||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
|
||||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
|
||||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
|
||||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
||||||
DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
/*
|
|
||||||
Header definitions for the MMX routines for the HERMES library
|
|
||||||
Copyright (c) 1998 Christian Nentwich (c.nentwich@cs.ucl.ac.uk)
|
|
||||||
This source code is licensed under the GNU LGPL
|
|
||||||
|
|
||||||
Please refer to the file COPYING.LIB contained in the distribution for
|
|
||||||
licensing conditions
|
|
||||||
*/
|
|
||||||
#include "SDL_config.h"
|
|
||||||
|
|
||||||
#ifndef __HERMES_HEAD_MMX__
|
|
||||||
#define __HERMES_HEAD_MMX__
|
|
||||||
|
|
||||||
|
|
||||||
/* If you cannot stand ifdefs, then please do not look into this file, it's
|
|
||||||
going to end your life :) */
|
|
||||||
|
|
||||||
#ifdef X86_ASSEMBLER
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void STACKCALL ConvertMMX(HermesConverterInterface *);
|
|
||||||
|
|
||||||
void STACKCALL ClearMMX_32(HermesClearInterface *);
|
|
||||||
void STACKCALL ClearMMX_24(HermesClearInterface *);
|
|
||||||
void STACKCALL ClearMMX_16(HermesClearInterface *);
|
|
||||||
void STACKCALL ClearMMX_8(HermesClearInterface *);
|
|
||||||
|
|
||||||
void ConvertMMXpII32_24RGB888();
|
|
||||||
void ConvertMMXpII32_16RGB565();
|
|
||||||
void ConvertMMXpII32_16BGR565();
|
|
||||||
void ConvertMMXpII32_16RGB555();
|
|
||||||
void ConvertMMXpII32_16BGR565();
|
|
||||||
void ConvertMMXpII32_16BGR555();
|
|
||||||
|
|
||||||
void ConvertMMXp32_16RGB555();
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Fix the underscore business with ELF compilers */
|
|
||||||
|
|
||||||
#if (defined(__ELF__) && defined(__GNUC__)) || defined(__SUNPRO_C)
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern void _ConvertMMX(HermesConverterInterface *);
|
|
||||||
extern void _ConvertMMXpII32_24RGB888();
|
|
||||||
extern void _ConvertMMXpII32_16RGB565();
|
|
||||||
extern void _ConvertMMXpII32_16BGR565();
|
|
||||||
extern void _ConvertMMXpII32_16RGB555();
|
|
||||||
extern void _ConvertMMXpII32_16BGR555();
|
|
||||||
|
|
||||||
#define ConvertMMX _ConvertMMX
|
|
||||||
#define ConvertMMXpII32_24RGB888 _ConvertMMXpII32_24RGB888
|
|
||||||
#define ConvertMMXpII32_16RGB565 _ConvertMMXpII32_16RGB565
|
|
||||||
#define ConvertMMXpII32_16BGR565 _ConvertMMXpII32_16BGR565
|
|
||||||
#define ConvertMMXpII32_16RGB555 _ConvertMMXpII32_16RGB555
|
|
||||||
#define ConvertMMXpII32_16BGR555 _ConvertMMXpII32_16BGR555
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* ELF and GNUC */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Make it work with Watcom */
|
|
||||||
#ifdef __WATCOMC__
|
|
||||||
#pragma warning 601 9
|
|
||||||
|
|
||||||
#pragma aux ConvertMMX "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
|
|
||||||
#pragma aux ClearMMX_32 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
#pragma aux ClearMMX_24 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
#pragma aux ClearMMX_16 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
#pragma aux ClearMMX_8 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
|
|
||||||
#pragma aux ConvertMMXpII32_24RGB888 "_*"
|
|
||||||
#pragma aux ConvertMMXpII32_16RGB565 "_*"
|
|
||||||
#pragma aux ConvertMMXpII32_16BGR565 "_*"
|
|
||||||
#pragma aux ConvertMMXpII32_16RGB555 "_*"
|
|
||||||
#pragma aux ConvertMMXpII32_16BGR555 "_*"
|
|
||||||
#pragma aux ConvertMMXp32_16RGB555 "_*"
|
|
||||||
|
|
||||||
#endif /* WATCOM */
|
|
||||||
|
|
||||||
#endif /* X86_ASSEMBLER */
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
||||||
/*
|
|
||||||
Header definitions for the x86 routines for the HERMES library
|
|
||||||
Copyright (c) 1998 Christian Nentwich (brn@eleet.mcb.at)
|
|
||||||
This source code is licensed under the GNU LGPL
|
|
||||||
|
|
||||||
Please refer to the file COPYING.LIB contained in the distribution for
|
|
||||||
licensing conditions
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef __HERMES_HEAD_X86__
|
|
||||||
#define __HERMES_HEAD_X86__
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef X86_ASSEMBLER
|
|
||||||
|
|
||||||
/* If you can't stand IFDEFS, then close your eyes now, please :) */
|
|
||||||
|
|
||||||
/* Ok, we start with normal function definitions */
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
void STACKCALL ConvertX86(HermesConverterInterface *);
|
|
||||||
void STACKCALL ClearX86_32(HermesClearInterface *);
|
|
||||||
void STACKCALL ClearX86_24(HermesClearInterface *);
|
|
||||||
void STACKCALL ClearX86_16(HermesClearInterface *);
|
|
||||||
void STACKCALL ClearX86_8(HermesClearInterface *);
|
|
||||||
|
|
||||||
int STACKCALL Hermes_X86_CPU();
|
|
||||||
|
|
||||||
void ConvertX86p32_32BGR888();
|
|
||||||
void ConvertX86p32_32RGBA888();
|
|
||||||
void ConvertX86p32_32BGRA888();
|
|
||||||
void ConvertX86p32_24RGB888();
|
|
||||||
void ConvertX86p32_24BGR888();
|
|
||||||
void ConvertX86p32_16RGB565();
|
|
||||||
void ConvertX86p32_16BGR565();
|
|
||||||
void ConvertX86p32_16RGB555();
|
|
||||||
void ConvertX86p32_16BGR555();
|
|
||||||
void ConvertX86p32_8RGB332();
|
|
||||||
|
|
||||||
void ConvertX86p16_32RGB888();
|
|
||||||
void ConvertX86p16_32BGR888();
|
|
||||||
void ConvertX86p16_32RGBA888();
|
|
||||||
void ConvertX86p16_32BGRA888();
|
|
||||||
void ConvertX86p16_24RGB888();
|
|
||||||
void ConvertX86p16_24BGR888();
|
|
||||||
void ConvertX86p16_16BGR565();
|
|
||||||
void ConvertX86p16_16RGB555();
|
|
||||||
void ConvertX86p16_16BGR555();
|
|
||||||
void ConvertX86p16_8RGB332();
|
|
||||||
|
|
||||||
void CopyX86p_4byte();
|
|
||||||
void CopyX86p_3byte();
|
|
||||||
void CopyX86p_2byte();
|
|
||||||
void CopyX86p_1byte();
|
|
||||||
|
|
||||||
void ConvertX86pI8_32();
|
|
||||||
void ConvertX86pI8_24();
|
|
||||||
void ConvertX86pI8_16();
|
|
||||||
|
|
||||||
extern int ConvertX86p16_32RGB888_LUT_X86[512];
|
|
||||||
extern int ConvertX86p16_32BGR888_LUT_X86[512];
|
|
||||||
extern int ConvertX86p16_32RGBA888_LUT_X86[512];
|
|
||||||
extern int ConvertX86p16_32BGRA888_LUT_X86[512];
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Now fix up the ELF underscore problem */
|
|
||||||
|
|
||||||
#if (defined(__ELF__) && defined(__GNUC__)) || defined(__SUNPRO_C)
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern int _Hermes_X86_CPU();
|
|
||||||
|
|
||||||
extern void _ConvertX86(HermesConverterInterface *);
|
|
||||||
|
|
||||||
extern void _ConvertX86p32_32BGR888();
|
|
||||||
extern void _ConvertX86p32_32RGBA888();
|
|
||||||
extern void _ConvertX86p32_32BGRA888();
|
|
||||||
extern void _ConvertX86p32_24RGB888();
|
|
||||||
extern void _ConvertX86p32_24BGR888();
|
|
||||||
extern void _ConvertX86p32_16RGB565();
|
|
||||||
extern void _ConvertX86p32_16BGR565();
|
|
||||||
extern void _ConvertX86p32_16RGB555();
|
|
||||||
extern void _ConvertX86p32_16BGR555();
|
|
||||||
extern void _ConvertX86p32_8RGB332();
|
|
||||||
|
|
||||||
extern void _ConvertX86p16_16BGR565();
|
|
||||||
extern void _ConvertX86p16_16RGB555();
|
|
||||||
extern void _ConvertX86p16_16BGR555();
|
|
||||||
extern void _ConvertX86p16_8RGB332();
|
|
||||||
|
|
||||||
|
|
||||||
#define Hermes_X86_CPU _Hermes_X86_CPU
|
|
||||||
|
|
||||||
#define ConvertX86 _ConvertX86
|
|
||||||
|
|
||||||
#define ConvertX86p32_32BGR888 _ConvertX86p32_32BGR888
|
|
||||||
#define ConvertX86p32_32RGBA888 _ConvertX86p32_32RGBA888
|
|
||||||
#define ConvertX86p32_32BGRA888 _ConvertX86p32_32BGRA888
|
|
||||||
#define ConvertX86p32_24RGB888 _ConvertX86p32_24RGB888
|
|
||||||
#define ConvertX86p32_24BGR888 _ConvertX86p32_24BGR888
|
|
||||||
#define ConvertX86p32_16RGB565 _ConvertX86p32_16RGB565
|
|
||||||
#define ConvertX86p32_16BGR565 _ConvertX86p32_16BGR565
|
|
||||||
#define ConvertX86p32_16RGB555 _ConvertX86p32_16RGB555
|
|
||||||
#define ConvertX86p32_16BGR555 _ConvertX86p32_16BGR555
|
|
||||||
#define ConvertX86p32_8RGB332 _ConvertX86p32_8RGB332
|
|
||||||
|
|
||||||
#define ConvertX86p16_16BGR565 _ConvertX86p16_16BGR565
|
|
||||||
#define ConvertX86p16_16RGB555 _ConvertX86p16_16RGB555
|
|
||||||
#define ConvertX86p16_16BGR555 _ConvertX86p16_16BGR555
|
|
||||||
#define ConvertX86p16_8RGB332 _ConvertX86p16_8RGB332
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* ELF & GNU */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Make it run with WATCOM C */
|
|
||||||
#ifdef __WATCOMC__
|
|
||||||
#pragma warning 601 9
|
|
||||||
|
|
||||||
#pragma aux Hermes_X86_CPU "_*"
|
|
||||||
|
|
||||||
#pragma aux ConvertX86 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
#pragma aux ClearX86_32 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
#pragma aux ClearX86_24 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
#pragma aux ClearX86_16 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
#pragma aux ClearX86_8 "_*" modify [EAX EBX ECX EDX ESI EDI]
|
|
||||||
|
|
||||||
#pragma aux ConvertX86p32_32BGR888 "_*"
|
|
||||||
#pragma aux ConvertX86p32_32RGBA888 "_*"
|
|
||||||
#pragma aux ConvertX86p32_32BGRA888 "_*"
|
|
||||||
#pragma aux ConvertX86p32_24RGB888 "_*"
|
|
||||||
#pragma aux ConvertX86p32_24BGR888 "_*"
|
|
||||||
#pragma aux ConvertX86p32_16RGB565 "_*"
|
|
||||||
#pragma aux ConvertX86p32_16BGR565 "_*"
|
|
||||||
#pragma aux ConvertX86p32_16RGB555 "_*"
|
|
||||||
#pragma aux ConvertX86p32_16BGR555 "_*"
|
|
||||||
#pragma aux ConvertX86p32_8RGB332 "_*"
|
|
||||||
|
|
||||||
#pragma aux ConvertX86p16_32RGB888 "_*"
|
|
||||||
#pragma aux ConvertX86p16_32BGR888 "_*"
|
|
||||||
#pragma aux ConvertX86p16_32RGBA888 "_*"
|
|
||||||
#pragma aux ConvertX86p16_32BGRA888 "_*"
|
|
||||||
#pragma aux ConvertX86p16_24RGB888 "_*"
|
|
||||||
#pragma aux ConvertX86p16_24BGR888 "_*"
|
|
||||||
#pragma aux ConvertX86p16_16BGR565 "_*"
|
|
||||||
#pragma aux ConvertX86p16_16RGB555 "_*"
|
|
||||||
#pragma aux ConvertX86p16_16BGR555 "_*"
|
|
||||||
#pragma aux ConvertX86p16_8RGB332 "_*"
|
|
||||||
|
|
||||||
#pragma aux CopyX86p_4byte "_*"
|
|
||||||
#pragma aux CopyX86p_3byte "_*"
|
|
||||||
#pragma aux CopyX86p_2byte "_*"
|
|
||||||
#pragma aux CopyX86p_1byte "_*"
|
|
||||||
|
|
||||||
#pragma aux ConvertX86pI8_32 "_*"
|
|
||||||
#pragma aux ConvertX86pI8_24 "_*"
|
|
||||||
#pragma aux ConvertX86pI8_16 "_*"
|
|
||||||
|
|
||||||
#pragma aux ConvertX86p16_32RGB888_LUT_X86 "_*"
|
|
||||||
#pragma aux ConvertX86p16_32BGR888_LUT_X86 "_*"
|
|
||||||
#pragma aux ConvertX86p16_32RGBA888_LUT_X86 "_*"
|
|
||||||
#pragma aux ConvertX86p16_32BGRA888_LUT_X86 "_*"
|
|
||||||
|
|
||||||
#endif /* __WATCOMC__ */
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* X86_ASSEMBLER */
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
HERMES 1.2.4 (c)1998 Christian Nentwich (brn) (c.nentwich@cs.ucl.ac.uk)
|
|
||||||
and quite a few assembler routines (c) Glenn Fielder (gaffer@gaffer.org)
|
|
||||||
|
|
||||||
This library and all the files enclosed in this package are free software
|
|
||||||
under the terms of the GNU Library General Public License (LGPL). Please
|
|
||||||
refer to the included file COPYING.LIB for the exact terms.
|
|
||||||
----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
This is a stripped down version of HERMES, including only the x86 assembler
|
|
||||||
converters, for use with Simple DirectMedia Layer.
|
|
||||||
|
|
||||||
The full HERMES library is available at: http://hermes.terminal.at/
|
|
||||||
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
; Some common macros for hermes nasm code
|
|
||||||
|
|
||||||
%macro SDL_FUNC 1
|
|
||||||
%ifdef HIDDEN_VISIBILITY
|
|
||||||
GLOBAL %1:function hidden
|
|
||||||
%else
|
|
||||||
GLOBAL %1
|
|
||||||
%endif
|
|
||||||
%endmacro
|
|
||||||
|
|
@ -1,74 +0,0 @@
|
||||||
;
|
|
||||||
; mmx format converter main loops for HERMES
|
|
||||||
; Some routines Copyright (c) 1998 Christian Nentwich (c.nentwich@cs.ucl.ac.uk)
|
|
||||||
; This source code is licensed under the GNU LGPL
|
|
||||||
;
|
|
||||||
; Please refer to the file COPYING.LIB contained in the distribution for
|
|
||||||
; licensing conditions
|
|
||||||
;
|
|
||||||
|
|
||||||
BITS 32
|
|
||||||
|
|
||||||
%include "common.inc"
|
|
||||||
|
|
||||||
SDL_FUNC _ConvertMMX
|
|
||||||
|
|
||||||
SECTION .text
|
|
||||||
|
|
||||||
;; _ConvertMMX:
|
|
||||||
;; [ESP+8] ConverterInfo*
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; ConverterInfo (ebp+..)
|
|
||||||
;; 0: void *s_pixels
|
|
||||||
;; 4: int s_width
|
|
||||||
;; 8: int s_height
|
|
||||||
;; 12: int s_add
|
|
||||||
;; 16: void *d_pixels
|
|
||||||
;; 20: int d_width
|
|
||||||
;; 24: int d_height
|
|
||||||
;; 28: int d_add
|
|
||||||
;; 32: void (*converter_function)()
|
|
||||||
;; 36: int32 *lookup
|
|
||||||
|
|
||||||
_ConvertMMX:
|
|
||||||
push ebp
|
|
||||||
mov ebp,esp
|
|
||||||
|
|
||||||
; Save the registers used by the blitters, necessary for optimized code
|
|
||||||
pusha
|
|
||||||
|
|
||||||
mov eax,[ebp+8]
|
|
||||||
|
|
||||||
cmp dword [eax+4],BYTE 0
|
|
||||||
je endconvert
|
|
||||||
|
|
||||||
mov ebp,eax
|
|
||||||
|
|
||||||
mov esi,[ebp+0]
|
|
||||||
mov edi,[ebp+16]
|
|
||||||
|
|
||||||
y_loop:
|
|
||||||
mov ecx,[ebp+4]
|
|
||||||
|
|
||||||
call [ebp+32]
|
|
||||||
|
|
||||||
add esi,[ebp+12]
|
|
||||||
add edi,[ebp+28]
|
|
||||||
|
|
||||||
dec dword [ebp+8]
|
|
||||||
jnz y_loop
|
|
||||||
|
|
||||||
|
|
||||||
; Restore the registers used by the blitters, necessary for optimized code
|
|
||||||
popa
|
|
||||||
|
|
||||||
pop ebp
|
|
||||||
|
|
||||||
endconvert:
|
|
||||||
emms
|
|
||||||
|
|
||||||
ret
|
|
||||||
|
|
||||||
%ifidn __OUTPUT_FORMAT__,elf32
|
|
||||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
||||||
%endif
|
|
||||||
|
|
@ -1,405 +0,0 @@
|
||||||
;
|
|
||||||
; pII-optimised MMX format converters for HERMES
|
|
||||||
; Copyright (c) 1998 Christian Nentwich (c.nentwich@cs.ucl.ac.uk)
|
|
||||||
; and (c) 1999 Jonathan Matthew (jmatthew@uq.net.au)
|
|
||||||
; This source code is licensed under the GNU LGPL
|
|
||||||
;
|
|
||||||
; Please refer to the file COPYING.LIB contained in the distribution for
|
|
||||||
; licensing conditions
|
|
||||||
;
|
|
||||||
; COPYRIGHT NOTICE
|
|
||||||
;
|
|
||||||
; This file partly contains code that is (c) Intel Corporation, specifically
|
|
||||||
; the mode detection routine, and the converter to 15 bit (8 pixel
|
|
||||||
; conversion routine from the mmx programming tutorial pages).
|
|
||||||
;
|
|
||||||
;
|
|
||||||
; These routines aren't exactly pII optimised - it's just that as they
|
|
||||||
; are, they're terrible on p5 MMXs, but less so on pIIs. Someone needs to
|
|
||||||
; optimise them for p5 MMXs..
|
|
||||||
|
|
||||||
BITS 32
|
|
||||||
|
|
||||||
%include "common.inc"
|
|
||||||
|
|
||||||
SDL_FUNC _ConvertMMXpII32_24RGB888
|
|
||||||
SDL_FUNC _ConvertMMXpII32_16RGB565
|
|
||||||
SDL_FUNC _ConvertMMXpII32_16BGR565
|
|
||||||
SDL_FUNC _ConvertMMXpII32_16RGB555
|
|
||||||
SDL_FUNC _ConvertMMXpII32_16BGR555
|
|
||||||
|
|
||||||
;; Macros for conversion routines
|
|
||||||
|
|
||||||
%macro _push_immq_mask 1
|
|
||||||
push dword %1
|
|
||||||
push dword %1
|
|
||||||
%endmacro
|
|
||||||
|
|
||||||
%macro load_immq 2
|
|
||||||
_push_immq_mask %2
|
|
||||||
movq %1, [esp]
|
|
||||||
%endmacro
|
|
||||||
|
|
||||||
%macro pand_immq 2
|
|
||||||
_push_immq_mask %2
|
|
||||||
pand %1, [esp]
|
|
||||||
%endmacro
|
|
||||||
|
|
||||||
%define CLEANUP_IMMQ_LOADS(num) \
|
|
||||||
add esp, byte 8 * num
|
|
||||||
|
|
||||||
%define mmx32_rgb888_mask 00ffffffh
|
|
||||||
%define mmx32_rgb565_b 000000f8h
|
|
||||||
%define mmx32_rgb565_g 0000fc00h
|
|
||||||
%define mmx32_rgb565_r 00f80000h
|
|
||||||
|
|
||||||
%define mmx32_rgb555_rb 00f800f8h
|
|
||||||
%define mmx32_rgb555_g 0000f800h
|
|
||||||
%define mmx32_rgb555_mul 20000008h
|
|
||||||
%define mmx32_bgr555_mul 00082000h
|
|
||||||
|
|
||||||
SECTION .text
|
|
||||||
|
|
||||||
_ConvertMMXpII32_24RGB888:
|
|
||||||
|
|
||||||
; set up mm6 as the mask, mm7 as zero
|
|
||||||
load_immq mm6, mmx32_rgb888_mask
|
|
||||||
CLEANUP_IMMQ_LOADS(1)
|
|
||||||
pxor mm7, mm7
|
|
||||||
|
|
||||||
mov edx, ecx ; save ecx
|
|
||||||
and ecx, 0fffffffch ; clear lower two bits
|
|
||||||
jnz .L1
|
|
||||||
jmp .L2
|
|
||||||
|
|
||||||
.L1:
|
|
||||||
|
|
||||||
movq mm0, [esi] ; A R G B a r g b
|
|
||||||
pand mm0, mm6 ; 0 R G B 0 r g b
|
|
||||||
movq mm1, [esi+8] ; A R G B a r g b
|
|
||||||
pand mm1, mm6 ; 0 R G B 0 r g b
|
|
||||||
|
|
||||||
movq mm2, mm0 ; 0 R G B 0 r g b
|
|
||||||
punpckhdq mm2, mm7 ; 0 0 0 0 0 R G B
|
|
||||||
punpckldq mm0, mm7 ; 0 0 0 0 0 r g b
|
|
||||||
psllq mm2, 24 ; 0 0 R G B 0 0 0
|
|
||||||
por mm0, mm2 ; 0 0 R G B r g b
|
|
||||||
|
|
||||||
movq mm3, mm1 ; 0 R G B 0 r g b
|
|
||||||
psllq mm3, 48 ; g b 0 0 0 0 0 0
|
|
||||||
por mm0, mm3 ; g b R G B r g b
|
|
||||||
|
|
||||||
movq mm4, mm1 ; 0 R G B 0 r g b
|
|
||||||
punpckhdq mm4, mm7 ; 0 0 0 0 0 R G B
|
|
||||||
punpckldq mm1, mm7 ; 0 0 0 0 0 r g b
|
|
||||||
psrlq mm1, 16 ; 0 0 0 R G B 0 r
|
|
||||||
psllq mm4, 8 ; 0 0 0 0 R G B 0
|
|
||||||
por mm1, mm4 ; 0 0 0 0 R G B r
|
|
||||||
|
|
||||||
movq [edi], mm0
|
|
||||||
add esi, BYTE 16
|
|
||||||
movd [edi+8], mm1
|
|
||||||
add edi, BYTE 12
|
|
||||||
sub ecx, BYTE 4
|
|
||||||
jnz .L1
|
|
||||||
|
|
||||||
.L2:
|
|
||||||
mov ecx, edx
|
|
||||||
and ecx, BYTE 3
|
|
||||||
jz .L4
|
|
||||||
.L3:
|
|
||||||
mov al, [esi]
|
|
||||||
mov bl, [esi+1]
|
|
||||||
mov dl, [esi+2]
|
|
||||||
mov [edi], al
|
|
||||||
mov [edi+1], bl
|
|
||||||
mov [edi+2], dl
|
|
||||||
add esi, BYTE 4
|
|
||||||
add edi, BYTE 3
|
|
||||||
dec ecx
|
|
||||||
jnz .L3
|
|
||||||
.L4:
|
|
||||||
retn
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ConvertMMXpII32_16RGB565:
|
|
||||||
|
|
||||||
; set up masks
|
|
||||||
load_immq mm5, mmx32_rgb565_b
|
|
||||||
load_immq mm6, mmx32_rgb565_g
|
|
||||||
load_immq mm7, mmx32_rgb565_r
|
|
||||||
CLEANUP_IMMQ_LOADS(3)
|
|
||||||
|
|
||||||
mov edx, ecx
|
|
||||||
shr ecx, 2
|
|
||||||
jnz .L1
|
|
||||||
jmp .L2 ; not necessary at the moment, but doesn't hurt (much)
|
|
||||||
|
|
||||||
.L1:
|
|
||||||
movq mm0, [esi] ; argb
|
|
||||||
movq mm1, mm0 ; argb
|
|
||||||
pand mm0, mm6 ; 00g0
|
|
||||||
movq mm3, mm1 ; argb
|
|
||||||
pand mm1, mm5 ; 000b
|
|
||||||
pand mm3, mm7 ; 0r00
|
|
||||||
pslld mm1, 2 ; 0 0 000000bb bbb00000
|
|
||||||
por mm0, mm1 ; 0 0 ggggggbb bbb00000
|
|
||||||
psrld mm0, 5 ; 0 0 00000ggg gggbbbbb
|
|
||||||
|
|
||||||
movq mm4, [esi+8] ; argb
|
|
||||||
movq mm2, mm4 ; argb
|
|
||||||
pand mm4, mm6 ; 00g0
|
|
||||||
movq mm1, mm2 ; argb
|
|
||||||
pand mm2, mm5 ; 000b
|
|
||||||
pand mm1, mm7 ; 0r00
|
|
||||||
pslld mm2, 2 ; 0 0 000000bb bbb00000
|
|
||||||
por mm4, mm2 ; 0 0 ggggggbb bbb00000
|
|
||||||
psrld mm4, 5 ; 0 0 00000ggg gggbbbbb
|
|
||||||
|
|
||||||
packuswb mm3, mm1 ; R 0 r 0
|
|
||||||
packssdw mm0, mm4 ; as above.. ish
|
|
||||||
por mm0, mm3 ; done.
|
|
||||||
movq [edi], mm0
|
|
||||||
|
|
||||||
add esi, 16
|
|
||||||
add edi, 8
|
|
||||||
dec ecx
|
|
||||||
jnz .L1
|
|
||||||
|
|
||||||
.L2:
|
|
||||||
mov ecx, edx
|
|
||||||
and ecx, BYTE 3
|
|
||||||
jz .L4
|
|
||||||
.L3:
|
|
||||||
mov al, [esi]
|
|
||||||
mov bh, [esi+1]
|
|
||||||
mov ah, [esi+2]
|
|
||||||
shr al, 3
|
|
||||||
and eax, 0F81Fh ; BYTE?
|
|
||||||
shr ebx, 5
|
|
||||||
and ebx, 07E0h ; BYTE?
|
|
||||||
add eax, ebx
|
|
||||||
mov [edi], al
|
|
||||||
mov [edi+1], ah
|
|
||||||
add esi, BYTE 4
|
|
||||||
add edi, BYTE 2
|
|
||||||
dec ecx
|
|
||||||
jnz .L3
|
|
||||||
|
|
||||||
.L4:
|
|
||||||
retn
|
|
||||||
|
|
||||||
|
|
||||||
_ConvertMMXpII32_16BGR565:
|
|
||||||
|
|
||||||
load_immq mm5, mmx32_rgb565_r
|
|
||||||
load_immq mm6, mmx32_rgb565_g
|
|
||||||
load_immq mm7, mmx32_rgb565_b
|
|
||||||
CLEANUP_IMMQ_LOADS(3)
|
|
||||||
|
|
||||||
mov edx, ecx
|
|
||||||
shr ecx, 2
|
|
||||||
jnz .L1
|
|
||||||
jmp .L2
|
|
||||||
|
|
||||||
.L1:
|
|
||||||
movq mm0, [esi] ; a r g b
|
|
||||||
movq mm1, mm0 ; a r g b
|
|
||||||
pand mm0, mm6 ; 0 0 g 0
|
|
||||||
movq mm3, mm1 ; a r g b
|
|
||||||
pand mm1, mm5 ; 0 r 0 0
|
|
||||||
pand mm3, mm7 ; 0 0 0 b
|
|
||||||
|
|
||||||
psllq mm3, 16 ; 0 b 0 0
|
|
||||||
psrld mm1, 14 ; 0 0 000000rr rrr00000
|
|
||||||
por mm0, mm1 ; 0 0 ggggggrr rrr00000
|
|
||||||
psrld mm0, 5 ; 0 0 00000ggg gggrrrrr
|
|
||||||
|
|
||||||
movq mm4, [esi+8] ; a r g b
|
|
||||||
movq mm2, mm4 ; a r g b
|
|
||||||
pand mm4, mm6 ; 0 0 g 0
|
|
||||||
movq mm1, mm2 ; a r g b
|
|
||||||
pand mm2, mm5 ; 0 r 0 0
|
|
||||||
pand mm1, mm7 ; 0 0 0 b
|
|
||||||
|
|
||||||
psllq mm1, 16 ; 0 b 0 0
|
|
||||||
psrld mm2, 14 ; 0 0 000000rr rrr00000
|
|
||||||
por mm4, mm2 ; 0 0 ggggggrr rrr00000
|
|
||||||
psrld mm4, 5 ; 0 0 00000ggg gggrrrrr
|
|
||||||
|
|
||||||
packuswb mm3, mm1 ; BBBBB000 00000000 bbbbb000 00000000
|
|
||||||
packssdw mm0, mm4 ; 00000GGG GGGRRRRR 00000GGG GGGRRRRR
|
|
||||||
por mm0, mm3 ; BBBBBGGG GGGRRRRR bbbbbggg gggrrrrr
|
|
||||||
movq [edi], mm0
|
|
||||||
|
|
||||||
add esi, BYTE 16
|
|
||||||
add edi, BYTE 8
|
|
||||||
dec ecx
|
|
||||||
jnz .L1
|
|
||||||
|
|
||||||
.L2:
|
|
||||||
and edx, BYTE 3
|
|
||||||
jz .L4
|
|
||||||
.L3:
|
|
||||||
mov al, [esi+2]
|
|
||||||
mov bh, [esi+1]
|
|
||||||
mov ah, [esi]
|
|
||||||
shr al, 3
|
|
||||||
and eax, 0F81Fh ; BYTE ?
|
|
||||||
shr ebx, 5
|
|
||||||
and ebx, 07E0h ; BYTE ?
|
|
||||||
add eax, ebx
|
|
||||||
mov [edi], al
|
|
||||||
mov [edi+1], ah
|
|
||||||
add esi, BYTE 4
|
|
||||||
add edi, BYTE 2
|
|
||||||
dec edx
|
|
||||||
jnz .L3
|
|
||||||
|
|
||||||
.L4:
|
|
||||||
retn
|
|
||||||
|
|
||||||
_ConvertMMXpII32_16BGR555:
|
|
||||||
|
|
||||||
; the 16BGR555 converter is identical to the RGB555 one,
|
|
||||||
; except it uses a different multiplier for the pmaddwd
|
|
||||||
; instruction. cool huh.
|
|
||||||
|
|
||||||
load_immq mm7, mmx32_bgr555_mul
|
|
||||||
jmp _convert_bgr555_cheat
|
|
||||||
|
|
||||||
; This is the same as the Intel version.. they obviously went to
|
|
||||||
; much more trouble to expand/coil the loop than I did, so theirs
|
|
||||||
; would almost certainly be faster, even if only a little.
|
|
||||||
; I did rename 'mmx32_rgb555_add' to 'mmx32_rgb555_mul', which is
|
|
||||||
; (I think) a more accurate name..
|
|
||||||
_ConvertMMXpII32_16RGB555:
|
|
||||||
|
|
||||||
load_immq mm7, mmx32_rgb555_mul
|
|
||||||
_convert_bgr555_cheat:
|
|
||||||
load_immq mm6, mmx32_rgb555_g
|
|
||||||
CLEANUP_IMMQ_LOADS(2)
|
|
||||||
|
|
||||||
mov edx,ecx ; Save ecx
|
|
||||||
|
|
||||||
and ecx,DWORD 0fffffff8h ; clear lower three bits
|
|
||||||
jnz .L_OK
|
|
||||||
jmp near .L2
|
|
||||||
|
|
||||||
.L_OK:
|
|
||||||
|
|
||||||
movq mm2,[esi+8]
|
|
||||||
|
|
||||||
movq mm0,[esi]
|
|
||||||
movq mm3,mm2
|
|
||||||
|
|
||||||
pand_immq mm3, mmx32_rgb555_rb
|
|
||||||
movq mm1,mm0
|
|
||||||
|
|
||||||
pand_immq mm1, mmx32_rgb555_rb
|
|
||||||
pmaddwd mm3,mm7
|
|
||||||
|
|
||||||
CLEANUP_IMMQ_LOADS(2)
|
|
||||||
|
|
||||||
pmaddwd mm1,mm7
|
|
||||||
pand mm2,mm6
|
|
||||||
|
|
||||||
.L1:
|
|
||||||
movq mm4,[esi+24]
|
|
||||||
pand mm0,mm6
|
|
||||||
|
|
||||||
movq mm5,[esi+16]
|
|
||||||
por mm3,mm2
|
|
||||||
|
|
||||||
psrld mm3,6
|
|
||||||
por mm1,mm0
|
|
||||||
|
|
||||||
movq mm0,mm4
|
|
||||||
psrld mm1,6
|
|
||||||
|
|
||||||
pand_immq mm0, mmx32_rgb555_rb
|
|
||||||
packssdw mm1,mm3
|
|
||||||
|
|
||||||
movq mm3,mm5
|
|
||||||
pmaddwd mm0,mm7
|
|
||||||
|
|
||||||
pand_immq mm3, mmx32_rgb555_rb
|
|
||||||
pand mm4,mm6
|
|
||||||
|
|
||||||
movq [edi],mm1
|
|
||||||
pmaddwd mm3,mm7
|
|
||||||
|
|
||||||
add esi,BYTE 32
|
|
||||||
por mm4,mm0
|
|
||||||
|
|
||||||
pand mm5,mm6
|
|
||||||
psrld mm4,6
|
|
||||||
|
|
||||||
movq mm2,[esi+8]
|
|
||||||
por mm5,mm3
|
|
||||||
|
|
||||||
movq mm0,[esi]
|
|
||||||
psrld mm5,6
|
|
||||||
|
|
||||||
movq mm3,mm2
|
|
||||||
movq mm1,mm0
|
|
||||||
|
|
||||||
pand_immq mm3, mmx32_rgb555_rb
|
|
||||||
packssdw mm5,mm4
|
|
||||||
|
|
||||||
pand_immq mm1, mmx32_rgb555_rb
|
|
||||||
pand mm2,mm6
|
|
||||||
|
|
||||||
CLEANUP_IMMQ_LOADS(4)
|
|
||||||
|
|
||||||
movq [edi+8],mm5
|
|
||||||
pmaddwd mm3,mm7
|
|
||||||
|
|
||||||
pmaddwd mm1,mm7
|
|
||||||
add edi,BYTE 16
|
|
||||||
|
|
||||||
sub ecx,BYTE 8
|
|
||||||
jz .L2
|
|
||||||
jmp .L1
|
|
||||||
|
|
||||||
|
|
||||||
.L2:
|
|
||||||
mov ecx,edx
|
|
||||||
|
|
||||||
and ecx,BYTE 7
|
|
||||||
jz .L4
|
|
||||||
|
|
||||||
.L3:
|
|
||||||
mov ebx,[esi]
|
|
||||||
add esi,BYTE 4
|
|
||||||
|
|
||||||
mov eax,ebx
|
|
||||||
mov edx,ebx
|
|
||||||
|
|
||||||
shr eax,3
|
|
||||||
shr edx,6
|
|
||||||
|
|
||||||
and eax,BYTE 0000000000011111b
|
|
||||||
and edx, 0000001111100000b
|
|
||||||
|
|
||||||
shr ebx,9
|
|
||||||
|
|
||||||
or eax,edx
|
|
||||||
|
|
||||||
and ebx, 0111110000000000b
|
|
||||||
|
|
||||||
or eax,ebx
|
|
||||||
|
|
||||||
mov [edi],ax
|
|
||||||
add edi,BYTE 2
|
|
||||||
|
|
||||||
dec ecx
|
|
||||||
jnz .L3
|
|
||||||
|
|
||||||
.L4:
|
|
||||||
retn
|
|
||||||
|
|
||||||
%ifidn __OUTPUT_FORMAT__,elf32
|
|
||||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
||||||
%endif
|
|
||||||
|
|
@ -1,75 +0,0 @@
|
||||||
;
|
|
||||||
; x86 format converters for HERMES
|
|
||||||
; Some routines Copyright (c) 1998 Christian Nentwich (brn@eleet.mcb.at)
|
|
||||||
; This source code is licensed under the GNU LGPL
|
|
||||||
;
|
|
||||||
; Please refer to the file COPYING.LIB contained in the distribution for
|
|
||||||
; licensing conditions
|
|
||||||
;
|
|
||||||
; Most routines are (c) Glenn Fiedler (ptc@gaffer.org), used with permission
|
|
||||||
;
|
|
||||||
|
|
||||||
BITS 32
|
|
||||||
|
|
||||||
%include "common.inc"
|
|
||||||
|
|
||||||
SDL_FUNC _ConvertX86
|
|
||||||
|
|
||||||
SECTION .text
|
|
||||||
|
|
||||||
;; _ConvertX86:
|
|
||||||
;; [ESP+8] ConverterInfo*
|
|
||||||
;; --------------------------------------------------------------------------
|
|
||||||
;; ConverterInfo (ebp+..)
|
|
||||||
;; 0: void *s_pixels
|
|
||||||
;; 4: int s_width
|
|
||||||
;; 8: int s_height
|
|
||||||
;; 12: int s_add
|
|
||||||
;; 16: void *d_pixels
|
|
||||||
;; 20: int d_width
|
|
||||||
;; 24: int d_height
|
|
||||||
;; 28: int d_add
|
|
||||||
;; 32: void (*converter_function)()
|
|
||||||
;; 36: int32 *lookup
|
|
||||||
|
|
||||||
_ConvertX86:
|
|
||||||
push ebp
|
|
||||||
mov ebp,esp
|
|
||||||
|
|
||||||
; Save the registers used by the blitters, necessary for optimized code
|
|
||||||
pusha
|
|
||||||
|
|
||||||
mov eax,[ebp+8]
|
|
||||||
|
|
||||||
cmp dword [eax+4],BYTE 0
|
|
||||||
je endconvert
|
|
||||||
|
|
||||||
mov ebp,eax
|
|
||||||
|
|
||||||
mov esi,[ebp+0]
|
|
||||||
mov edi,[ebp+16]
|
|
||||||
|
|
||||||
y_loop:
|
|
||||||
mov ecx,[ebp+4]
|
|
||||||
|
|
||||||
call [ebp+32]
|
|
||||||
|
|
||||||
add esi,[ebp+12]
|
|
||||||
add edi,[ebp+28]
|
|
||||||
|
|
||||||
dec dword [ebp+8]
|
|
||||||
jnz y_loop
|
|
||||||
|
|
||||||
; Restore the registers used by the blitters, necessary for optimized code
|
|
||||||
popa
|
|
||||||
|
|
||||||
pop ebp
|
|
||||||
|
|
||||||
endconvert:
|
|
||||||
ret
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
%ifidn __OUTPUT_FORMAT__,elf32
|
|
||||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
||||||
%endif
|
|
||||||
|
|
@ -1,490 +0,0 @@
|
||||||
;
|
|
||||||
; x86 format converters for HERMES
|
|
||||||
; Copyright (c) 1998 Glenn Fielder (gaffer@gaffer.org)
|
|
||||||
; This source code is licensed under the GNU LGPL
|
|
||||||
;
|
|
||||||
; Please refer to the file COPYING.LIB contained in the distribution for
|
|
||||||
; licensing conditions
|
|
||||||
;
|
|
||||||
; Routines adjusted for Hermes by Christian Nentwich (brn@eleet.mcb.at)
|
|
||||||
; Used with permission.
|
|
||||||
;
|
|
||||||
|
|
||||||
BITS 32
|
|
||||||
|
|
||||||
%include "common.inc"
|
|
||||||
|
|
||||||
SDL_FUNC _ConvertX86p16_16BGR565
|
|
||||||
SDL_FUNC _ConvertX86p16_16RGB555
|
|
||||||
SDL_FUNC _ConvertX86p16_16BGR555
|
|
||||||
SDL_FUNC _ConvertX86p16_8RGB332
|
|
||||||
|
|
||||||
EXTERN _ConvertX86
|
|
||||||
|
|
||||||
SECTION .text
|
|
||||||
|
|
||||||
_ConvertX86p16_16BGR565:
|
|
||||||
|
|
||||||
; check short
|
|
||||||
cmp ecx,BYTE 16
|
|
||||||
ja .L3
|
|
||||||
|
|
||||||
|
|
||||||
.L1: ; short loop
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
shr eax,11
|
|
||||||
and eax,BYTE 11111b
|
|
||||||
and ebx,11111100000b
|
|
||||||
shl edx,11
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
dec ecx
|
|
||||||
jnz .L1
|
|
||||||
.L2:
|
|
||||||
retn
|
|
||||||
|
|
||||||
.L3: ; head
|
|
||||||
mov eax,edi
|
|
||||||
and eax,BYTE 11b
|
|
||||||
jz .L4
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
shr eax,11
|
|
||||||
and eax,BYTE 11111b
|
|
||||||
and ebx,11111100000b
|
|
||||||
shl edx,11
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
dec ecx
|
|
||||||
|
|
||||||
.L4: ; save count
|
|
||||||
push ecx
|
|
||||||
|
|
||||||
; unroll twice
|
|
||||||
shr ecx,1
|
|
||||||
|
|
||||||
; point arrays to end
|
|
||||||
lea esi,[esi+ecx*4]
|
|
||||||
lea edi,[edi+ecx*4]
|
|
||||||
|
|
||||||
; negative counter
|
|
||||||
neg ecx
|
|
||||||
jmp SHORT .L6
|
|
||||||
|
|
||||||
.L5: mov [edi+ecx*4-4],eax
|
|
||||||
.L6: mov eax,[esi+ecx*4]
|
|
||||||
|
|
||||||
mov ebx,[esi+ecx*4]
|
|
||||||
and eax,07E007E0h
|
|
||||||
|
|
||||||
mov edx,[esi+ecx*4]
|
|
||||||
and ebx,0F800F800h
|
|
||||||
|
|
||||||
shr ebx,11
|
|
||||||
and edx,001F001Fh
|
|
||||||
|
|
||||||
shl edx,11
|
|
||||||
add eax,ebx
|
|
||||||
|
|
||||||
add eax,edx
|
|
||||||
inc ecx
|
|
||||||
|
|
||||||
jnz .L5
|
|
||||||
|
|
||||||
mov [edi+ecx*4-4],eax
|
|
||||||
|
|
||||||
; tail
|
|
||||||
pop ecx
|
|
||||||
and ecx,BYTE 1
|
|
||||||
jz .L7
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
shr eax,11
|
|
||||||
and eax,BYTE 11111b
|
|
||||||
and ebx,11111100000b
|
|
||||||
shl edx,11
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
|
|
||||||
.L7:
|
|
||||||
retn
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ConvertX86p16_16RGB555:
|
|
||||||
|
|
||||||
; check short
|
|
||||||
cmp ecx,BYTE 32
|
|
||||||
ja .L3
|
|
||||||
|
|
||||||
|
|
||||||
.L1: ; short loop
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
shr ebx,1
|
|
||||||
and ebx, 0111111111100000b
|
|
||||||
and eax,BYTE 0000000000011111b
|
|
||||||
add eax,ebx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
dec ecx
|
|
||||||
jnz .L1
|
|
||||||
.L2:
|
|
||||||
retn
|
|
||||||
|
|
||||||
.L3: ; head
|
|
||||||
mov eax,edi
|
|
||||||
and eax,BYTE 11b
|
|
||||||
jz .L4
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
shr ebx,1
|
|
||||||
and ebx, 0111111111100000b
|
|
||||||
and eax,BYTE 0000000000011111b
|
|
||||||
add eax,ebx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
dec ecx
|
|
||||||
|
|
||||||
.L4: ; save ebp
|
|
||||||
push ebp
|
|
||||||
|
|
||||||
; save count
|
|
||||||
push ecx
|
|
||||||
|
|
||||||
; unroll four times
|
|
||||||
shr ecx,2
|
|
||||||
|
|
||||||
; point arrays to end
|
|
||||||
lea esi,[esi+ecx*8]
|
|
||||||
lea edi,[edi+ecx*8]
|
|
||||||
|
|
||||||
; negative counter
|
|
||||||
xor ebp,ebp
|
|
||||||
sub ebp,ecx
|
|
||||||
|
|
||||||
.L5: mov eax,[esi+ebp*8] ; agi?
|
|
||||||
mov ecx,[esi+ebp*8+4]
|
|
||||||
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,ecx
|
|
||||||
|
|
||||||
and eax,0FFC0FFC0h
|
|
||||||
and ecx,0FFC0FFC0h
|
|
||||||
|
|
||||||
shr eax,1
|
|
||||||
and ebx,001F001Fh
|
|
||||||
|
|
||||||
shr ecx,1
|
|
||||||
and edx,001F001Fh
|
|
||||||
|
|
||||||
add eax,ebx
|
|
||||||
add ecx,edx
|
|
||||||
|
|
||||||
mov [edi+ebp*8],eax
|
|
||||||
mov [edi+ebp*8+4],ecx
|
|
||||||
|
|
||||||
inc ebp
|
|
||||||
jnz .L5
|
|
||||||
|
|
||||||
; tail
|
|
||||||
pop ecx
|
|
||||||
.L6: and ecx,BYTE 11b
|
|
||||||
jz .L7
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
shr ebx,1
|
|
||||||
and ebx, 0111111111100000b
|
|
||||||
and eax,BYTE 0000000000011111b
|
|
||||||
add eax,ebx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
dec ecx
|
|
||||||
jmp SHORT .L6
|
|
||||||
|
|
||||||
.L7: pop ebp
|
|
||||||
retn
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ConvertX86p16_16BGR555:
|
|
||||||
|
|
||||||
; check short
|
|
||||||
cmp ecx,BYTE 16
|
|
||||||
ja .L3
|
|
||||||
|
|
||||||
|
|
||||||
.L1: ; short loop
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
shr eax,11
|
|
||||||
and eax,BYTE 11111b
|
|
||||||
shr ebx,1
|
|
||||||
and ebx,1111100000b
|
|
||||||
shl edx,10
|
|
||||||
and edx,0111110000000000b
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
dec ecx
|
|
||||||
jnz .L1
|
|
||||||
.L2:
|
|
||||||
retn
|
|
||||||
|
|
||||||
.L3: ; head
|
|
||||||
mov eax,edi
|
|
||||||
and eax,BYTE 11b
|
|
||||||
jz .L4
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
shr eax,11
|
|
||||||
and eax,BYTE 11111b
|
|
||||||
shr ebx,1
|
|
||||||
and ebx,1111100000b
|
|
||||||
shl edx,10
|
|
||||||
and edx,0111110000000000b
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
dec ecx
|
|
||||||
|
|
||||||
.L4: ; save count
|
|
||||||
push ecx
|
|
||||||
|
|
||||||
; unroll twice
|
|
||||||
shr ecx,1
|
|
||||||
|
|
||||||
; point arrays to end
|
|
||||||
lea esi,[esi+ecx*4]
|
|
||||||
lea edi,[edi+ecx*4]
|
|
||||||
|
|
||||||
; negative counter
|
|
||||||
neg ecx
|
|
||||||
jmp SHORT .L6
|
|
||||||
|
|
||||||
.L5: mov [edi+ecx*4-4],eax
|
|
||||||
.L6: mov eax,[esi+ecx*4]
|
|
||||||
|
|
||||||
shr eax,1
|
|
||||||
mov ebx,[esi+ecx*4]
|
|
||||||
|
|
||||||
and eax,03E003E0h
|
|
||||||
mov edx,[esi+ecx*4]
|
|
||||||
|
|
||||||
and ebx,0F800F800h
|
|
||||||
|
|
||||||
shr ebx,11
|
|
||||||
and edx,001F001Fh
|
|
||||||
|
|
||||||
shl edx,10
|
|
||||||
add eax,ebx
|
|
||||||
|
|
||||||
add eax,edx
|
|
||||||
inc ecx
|
|
||||||
|
|
||||||
jnz .L5
|
|
||||||
|
|
||||||
mov [edi+ecx*4-4],eax
|
|
||||||
|
|
||||||
; tail
|
|
||||||
pop ecx
|
|
||||||
and ecx,BYTE 1
|
|
||||||
jz .L7
|
|
||||||
mov al,[esi]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
shr eax,11
|
|
||||||
and eax,BYTE 11111b
|
|
||||||
shr ebx,1
|
|
||||||
and ebx,1111100000b
|
|
||||||
shl edx,10
|
|
||||||
and edx,0111110000000000b
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
mov [edi+1],ah
|
|
||||||
add esi,BYTE 2
|
|
||||||
add edi,BYTE 2
|
|
||||||
|
|
||||||
.L7:
|
|
||||||
retn
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
_ConvertX86p16_8RGB332:
|
|
||||||
|
|
||||||
; check short
|
|
||||||
cmp ecx,BYTE 16
|
|
||||||
ja .L3
|
|
||||||
|
|
||||||
|
|
||||||
.L1: ; short loop
|
|
||||||
mov al,[esi+0]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
and eax,BYTE 11000b ; blue
|
|
||||||
shr eax,3
|
|
||||||
and ebx,11100000000b ; green
|
|
||||||
shr ebx,6
|
|
||||||
and edx,1110000000000000b ; red
|
|
||||||
shr edx,8
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
add esi,BYTE 2
|
|
||||||
inc edi
|
|
||||||
dec ecx
|
|
||||||
jnz .L1
|
|
||||||
.L2:
|
|
||||||
retn
|
|
||||||
|
|
||||||
.L3: mov eax,edi
|
|
||||||
and eax,BYTE 11b
|
|
||||||
jz .L4
|
|
||||||
mov al,[esi+0]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
and eax,BYTE 11000b ; blue
|
|
||||||
shr eax,3
|
|
||||||
and ebx,11100000000b ; green
|
|
||||||
shr ebx,6
|
|
||||||
and edx,1110000000000000b ; red
|
|
||||||
shr edx,8
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
add esi,BYTE 2
|
|
||||||
inc edi
|
|
||||||
dec ecx
|
|
||||||
jmp SHORT .L3
|
|
||||||
|
|
||||||
.L4: ; save ebp
|
|
||||||
push ebp
|
|
||||||
|
|
||||||
; save count
|
|
||||||
push ecx
|
|
||||||
|
|
||||||
; unroll 4 times
|
|
||||||
shr ecx,2
|
|
||||||
|
|
||||||
; prestep
|
|
||||||
mov dl,[esi+0]
|
|
||||||
mov bl,[esi+1]
|
|
||||||
mov dh,[esi+2]
|
|
||||||
|
|
||||||
.L5: shl edx,16
|
|
||||||
mov bh,[esi+3]
|
|
||||||
|
|
||||||
shl ebx,16
|
|
||||||
mov dl,[esi+4]
|
|
||||||
|
|
||||||
mov dh,[esi+6]
|
|
||||||
mov bl,[esi+5]
|
|
||||||
|
|
||||||
and edx,00011000000110000001100000011000b
|
|
||||||
mov bh,[esi+7]
|
|
||||||
|
|
||||||
ror edx,16+3
|
|
||||||
mov eax,ebx ; setup eax for reds
|
|
||||||
|
|
||||||
and ebx,00000111000001110000011100000111b
|
|
||||||
and eax,11100000111000001110000011100000b ; reds
|
|
||||||
|
|
||||||
ror ebx,16-2
|
|
||||||
add esi,BYTE 8
|
|
||||||
|
|
||||||
ror eax,16
|
|
||||||
add edi,BYTE 4
|
|
||||||
|
|
||||||
add eax,ebx
|
|
||||||
mov bl,[esi+1] ; greens
|
|
||||||
|
|
||||||
add eax,edx
|
|
||||||
mov dl,[esi+0] ; blues
|
|
||||||
|
|
||||||
mov [edi-4],eax
|
|
||||||
mov dh,[esi+2]
|
|
||||||
|
|
||||||
dec ecx
|
|
||||||
jnz .L5
|
|
||||||
|
|
||||||
; check tail
|
|
||||||
pop ecx
|
|
||||||
and ecx,BYTE 11b
|
|
||||||
jz .L7
|
|
||||||
|
|
||||||
.L6: ; tail
|
|
||||||
mov al,[esi+0]
|
|
||||||
mov ah,[esi+1]
|
|
||||||
mov ebx,eax
|
|
||||||
mov edx,eax
|
|
||||||
and eax,BYTE 11000b ; blue
|
|
||||||
shr eax,3
|
|
||||||
and ebx,11100000000b ; green
|
|
||||||
shr ebx,6
|
|
||||||
and edx,1110000000000000b ; red
|
|
||||||
shr edx,8
|
|
||||||
add eax,ebx
|
|
||||||
add eax,edx
|
|
||||||
mov [edi],al
|
|
||||||
add esi,BYTE 2
|
|
||||||
inc edi
|
|
||||||
dec ecx
|
|
||||||
jnz .L6
|
|
||||||
|
|
||||||
.L7: pop ebp
|
|
||||||
retn
|
|
||||||
|
|
||||||
%ifidn __OUTPUT_FORMAT__,elf32
|
|
||||||
section .note.GNU-stack noalloc noexec nowrite progbits
|
|
||||||
%endif
|
|
||||||
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue