src/audio/SDL_audio.c
author Sam Lantinga
Sat, 21 Aug 2004 12:27:02 +0000
changeset 942 41a59de7f2ed
parent 936 84f930aebaeb
child 955 d74fbf56f2f6
permissions -rw-r--r--
Here are patches for SDL12 and SDL_mixer for 4 or 6 channel
surround sound on Linux using the Alsa driver. To use them, naturally
you need a sound card that will do 4 or 6 channels and probably also a
recent version of the Alsa drivers and library. Since the only SDL
output driver that knows about surround sound is the Alsa driver,
you���ll want to choose it, using:

export SDL_AUDIODRIVER=alsa

There are no syntactic changes to the programming API. No new
library calls, no differences in arguments.

There are two semantic changes:

(1) For library calls with number of channels as an argument, formerly
you could use only 1 or 2 for the number of channels. Now you
can also use 4 or 6.

(2) The two "left" and "right" arguments to Mix_SetPanning, for the
case of 4 or 6 channels, no longer simply control the volumes of
the left and right channels. Now the "left" argument is converted
to an angle and Mix_SetPosition is called, and the "right" argu-
ment is ignored.

With two exceptions, so far as I know, the modified SDL12 and
SDL_mixer work the same way as the original versions, when opened for
1 or 2 channel output. The two exceptions are bugs which I fixed.
Well, the first, anyway, is a bug for sure. When rate conversions up
or down by a factor of two are applied (in src/audio/SDL_audiocvt.c),
streams with different numbers of channels (that is, mono and stereo)
are treated the same way: either each sample is copied or every other
sample is omitted. This is ok for mono, but for stereo, it is frames
that should be copied or omitted, where by "frame" I mean a portion of
the stream containing one sample for each channel. (In the SDL source,
confusingly, sometimes frames are called "samples".) So for these
rate conversions, stereo streams have to be treated differently, and
they are, in my modified version.

The other problem that might be characterized as a bug arises
when SDL_mixer is passed a multichannel chunk which does not have an
integral number of frames. Due to the way the effect_position code
loops over frames, when the chunk ends with a partial frame, memory
outside the chunk buffer will be accessed. In the case of stereo,
it���s possible that because malloc may give more memory than requested,
this potential problem never actually causes a segment fault. I don���t
know. For 6 channel chunks, I do know, and it does cause segment
faults.


If SDL_mixer is passed defective chunks and this causes a segment
fault, arguably, that���s not a bug in SDL_mixer. Still, whether or not
it counts as a bug, it���s easy to protect against, so why not? I added
code in mixer.c to discard any partial frame at the end of a chunk.

Then what about when SDL or SDL_mixer is opened for 4 or 6 chan-
nel output? What happens with the parts of the current library
designed for stereo? I don���t know whether I���ve covered all the bases,
but I���ve tried:

(1) For playing 2 channel waves, or other cases where SDL knows it has
to match up a 2 channel source with a 4 or 6 channel output, I���ve
added code in SDL_audiocvt.c to make the necessary conversions.

(2) For playing midis using timidity, I���ve converted timidity to do 4
or 6 channel output, upon request.

(3) For playing mods using mikmod, I put ad hoc code in music.c to
convert the stereo output that mikmod produces to 4 or 6 chan-
nels. Obviously it would be better to change the mikmod code to
mix down into 4 or 6 channels, but I have a hard time following
the code in mikmod, so I didn���t do that.

(4) For playing mp3s, I put ad hoc code in smpeg to copy channels in
the case when 4 or 6 channel output is needed.

(5) There seems to be no problem with .ogg files - stereo .oggs can be
up converted as .wavs are.

(6) The effect_position code in SDL_mixer is now generalized to in-
clude the cases of 4 and 6 channel streams.

I���ve done a very limited amount of compatibility testing for some
of the games using SDL I happen to have. For details, see the file
TESTS.

I���ve put into a separate archive, Surround-SDL-testfiles.tgz, a
couple of 6 channel wave files for testing and a 6 channel ogg file.
If you have the right hardware and version of Alsa, you should be able
to play the wave files with the Alsa utility aplay (and hear all
channels, except maybe lfe, for chan-id.wav, since it���s rather faint).
Don���t expect aplay to give good sound, though. There���s something
wrong with the current version of aplay.

The canyon.ogg file is to test loading of 6 channel oggs. After
patching and compiling, you can play it with playmus. (My version of
ogg123 will not play it, and I had to patch mplayer to get it to play
6 channel oggs.)

Greg Lee <greg@ling.lll.hawaii.edu>
Thus, July 1, 2004
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@769
     3
    Copyright (C) 1997-2004 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@0
     6
    modify it under the terms of the GNU Library General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@0
     8
    version 2 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@0
    13
    Library General Public License for more details.
slouken@0
    14
slouken@0
    15
    You should have received a copy of the GNU Library General Public
slouken@0
    16
    License along with this library; if not, write to the Free
slouken@0
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@0
    22
slouken@0
    23
#ifdef SAVE_RCSID
slouken@0
    24
static char rcsid =
slouken@0
    25
 "@(#) $Id$";
slouken@0
    26
#endif
slouken@0
    27
slouken@0
    28
/* Allow access to a raw mixing buffer */
slouken@0
    29
#include <stdlib.h>
slouken@0
    30
#include <stdio.h>
slouken@0
    31
#include <string.h>
slouken@0
    32
slouken@0
    33
#include "SDL.h"
slouken@0
    34
#include "SDL_audio.h"
slouken@0
    35
#include "SDL_timer.h"
slouken@0
    36
#include "SDL_error.h"
slouken@0
    37
#include "SDL_audio_c.h"
slouken@0
    38
#include "SDL_audiomem.h"
slouken@0
    39
#include "SDL_sysaudio.h"
slouken@0
    40
slouken@0
    41
/* Available audio drivers */
slouken@0
    42
static AudioBootStrap *bootstrap[] = {
slouken@121
    43
#ifdef OPENBSD_AUDIO_SUPPORT
slouken@121
    44
	&OPENBSD_AUDIO_bootstrap,
slouken@94
    45
#endif
slouken@0
    46
#ifdef OSS_SUPPORT
slouken@0
    47
	&DSP_bootstrap,
slouken@0
    48
	&DMA_bootstrap,
slouken@0
    49
#endif
slouken@0
    50
#ifdef ALSA_SUPPORT
slouken@0
    51
	&ALSA_bootstrap,
slouken@0
    52
#endif
slouken@663
    53
#ifdef QNXNTOAUDIO_SUPPORT
slouken@663
    54
	&QNXNTOAUDIO_bootstrap,
slouken@663
    55
#endif
slouken@148
    56
#ifdef SUNAUDIO_SUPPORT
slouken@148
    57
	&SUNAUDIO_bootstrap,
slouken@148
    58
#endif
slouken@148
    59
#ifdef DMEDIA_SUPPORT
slouken@148
    60
	&DMEDIA_bootstrap,
slouken@35
    61
#endif
slouken@0
    62
#ifdef ARTSC_SUPPORT
slouken@0
    63
	&ARTSC_bootstrap,
slouken@0
    64
#endif
slouken@0
    65
#ifdef ESD_SUPPORT
slouken@0
    66
	&ESD_bootstrap,
slouken@0
    67
#endif
slouken@0
    68
#ifdef NAS_SUPPORT
slouken@0
    69
	&NAS_bootstrap,
slouken@0
    70
#endif
slouken@0
    71
#ifdef ENABLE_DIRECTX
slouken@0
    72
	&DSOUND_bootstrap,
slouken@0
    73
#endif
slouken@0
    74
#ifdef ENABLE_WINDIB
slouken@0
    75
	&WAVEOUT_bootstrap,
slouken@0
    76
#endif
slouken@0
    77
#ifdef __BEOS__
slouken@0
    78
	&BAUDIO_bootstrap,
slouken@0
    79
#endif
slouken@936
    80
#ifdef MACOSX
slouken@935
    81
	&COREAUDIO_bootstrap,
slouken@935
    82
#endif
slouken@0
    83
#if defined(macintosh) || TARGET_API_MAC_CARBON
slouken@0
    84
	&SNDMGR_bootstrap,
slouken@0
    85
#endif
slouken@0
    86
#ifdef _AIX
slouken@0
    87
	&Paud_bootstrap,
slouken@0
    88
#endif
slouken@21
    89
#ifdef ENABLE_AHI
slouken@21
    90
	&AHI_bootstrap,
slouken@21
    91
#endif
slouken@654
    92
#ifdef MMEAUDIO_SUPPORT
slouken@654
    93
	&MMEAUDIO_bootstrap,
slouken@654
    94
#endif
slouken@398
    95
#ifdef MINTAUDIO_SUPPORT
patmandin@644
    96
	&MINTAUDIO_GSXB_bootstrap,
patmandin@644
    97
	&MINTAUDIO_MCSN_bootstrap,
patmandin@644
    98
	&MINTAUDIO_STFA_bootstrap,
patmandin@644
    99
	&MINTAUDIO_XBIOS_bootstrap,
patmandin@644
   100
	&MINTAUDIO_DMA8_bootstrap,
slouken@398
   101
#endif
slouken@68
   102
#ifdef DISKAUD_SUPPORT
slouken@68
   103
	&DISKAUD_bootstrap,
slouken@68
   104
#endif
slouken@509
   105
#ifdef ENABLE_DC
slouken@509
   106
	&DCAUD_bootstrap,
slouken@509
   107
#endif
slouken@630
   108
#ifdef DRENDERER_SUPPORT
slouken@630
   109
	&DRENDERER_bootstrap,
slouken@630
   110
#endif
slouken@0
   111
	NULL
slouken@0
   112
};
slouken@0
   113
SDL_AudioDevice *current_audio = NULL;
slouken@0
   114
slouken@0
   115
/* Various local functions */
slouken@0
   116
int SDL_AudioInit(const char *driver_name);
slouken@0
   117
void SDL_AudioQuit(void);
slouken@0
   118
slouken@21
   119
#ifdef ENABLE_AHI
slouken@21
   120
static int audio_configured = 0;
slouken@21
   121
#endif
slouken@0
   122
slouken@0
   123
/* The general mixing thread function */
slouken@0
   124
int SDL_RunAudio(void *audiop)
slouken@0
   125
{
slouken@0
   126
	SDL_AudioDevice *audio = (SDL_AudioDevice *)audiop;
slouken@0
   127
	Uint8 *stream;
slouken@0
   128
	int    stream_len;
slouken@0
   129
	void  *udata;
slouken@0
   130
	void (*fill)(void *userdata,Uint8 *stream, int len);
slouken@0
   131
	int    silence;
slouken@21
   132
#ifdef ENABLE_AHI
slouken@21
   133
	int started = 0;
slouken@21
   134
slouken@21
   135
/* AmigaOS NEEDS that the audio driver is opened in the thread that uses it! */
slouken@21
   136
slouken@21
   137
	D(bug("Task audio started audio struct:<%lx>...\n",audiop));
slouken@21
   138
slouken@21
   139
	D(bug("Before Openaudio..."));
slouken@21
   140
	if(audio->OpenAudio(audio, &audio->spec)==-1)
slouken@21
   141
	{
slouken@21
   142
		D(bug("Open audio failed...\n"));
slouken@21
   143
		return(-1);
slouken@21
   144
	}
slouken@21
   145
	D(bug("OpenAudio...OK\n"));
slouken@21
   146
#endif
slouken@0
   147
slouken@0
   148
	/* Perform any thread setup */
slouken@0
   149
	if ( audio->ThreadInit ) {
slouken@0
   150
		audio->ThreadInit(audio);
slouken@0
   151
	}
slouken@0
   152
	audio->threadid = SDL_ThreadID();
slouken@0
   153
slouken@0
   154
	/* Set up the mixing function */
slouken@0
   155
	fill  = audio->spec.callback;
slouken@0
   156
	udata = audio->spec.userdata;
slouken@21
   157
slouken@21
   158
#ifdef ENABLE_AHI
slouken@21
   159
	audio_configured = 1;
slouken@21
   160
slouken@21
   161
	D(bug("Audio configured... Checking for conversion\n"));
slouken@21
   162
	SDL_mutexP(audio->mixer_lock);
slouken@21
   163
	D(bug("Semaphore obtained...\n"));
slouken@21
   164
#endif
slouken@21
   165
slouken@0
   166
	if ( audio->convert.needed ) {
slouken@0
   167
		if ( audio->convert.src_format == AUDIO_U8 ) {
slouken@0
   168
			silence = 0x80;
slouken@0
   169
		} else {
slouken@0
   170
			silence = 0;
slouken@0
   171
		}
slouken@0
   172
		stream_len = audio->convert.len;
slouken@0
   173
	} else {
slouken@0
   174
		silence = audio->spec.silence;
slouken@0
   175
		stream_len = audio->spec.size;
slouken@0
   176
	}
slouken@0
   177
	stream = audio->fake_stream;
slouken@0
   178
slouken@21
   179
#ifdef ENABLE_AHI
slouken@21
   180
	SDL_mutexV(audio->mixer_lock);
slouken@21
   181
	D(bug("Entering audio loop...\n"));
slouken@21
   182
#endif
slouken@21
   183
slouken@21
   184
slouken@0
   185
	/* Loop, filling the audio buffers */
slouken@0
   186
	while ( audio->enabled ) {
slouken@0
   187
slouken@0
   188
		/* Wait for new current buffer to finish playing */
slouken@0
   189
		if ( stream == audio->fake_stream ) {
slouken@0
   190
			SDL_Delay((audio->spec.samples*1000)/audio->spec.freq);
slouken@0
   191
		} else {
slouken@21
   192
#ifdef ENABLE_AHI
slouken@21
   193
			if ( started > 1 )
slouken@21
   194
#endif
slouken@0
   195
			audio->WaitAudio(audio);
slouken@0
   196
		}
slouken@0
   197
slouken@0
   198
		/* Fill the current buffer with sound */
slouken@0
   199
		if ( audio->convert.needed ) {
slouken@0
   200
			if ( audio->convert.buf ) {
slouken@0
   201
				stream = audio->convert.buf;
slouken@0
   202
			} else {
slouken@0
   203
				continue;
slouken@0
   204
			}
slouken@0
   205
		} else {
slouken@0
   206
			stream = audio->GetAudioBuf(audio);
slouken@0
   207
			if ( stream == NULL ) {
slouken@0
   208
				stream = audio->fake_stream;
slouken@0
   209
			}
slouken@0
   210
		}
slouken@0
   211
		memset(stream, silence, stream_len);
slouken@0
   212
slouken@0
   213
		if ( ! audio->paused ) {
slouken@0
   214
			SDL_mutexP(audio->mixer_lock);
slouken@0
   215
			(*fill)(udata, stream, stream_len);
slouken@0
   216
			SDL_mutexV(audio->mixer_lock);
slouken@0
   217
		}
slouken@0
   218
slouken@0
   219
		/* Convert the audio if necessary */
slouken@0
   220
		if ( audio->convert.needed ) {
slouken@0
   221
			SDL_ConvertAudio(&audio->convert);
slouken@0
   222
			stream = audio->GetAudioBuf(audio);
slouken@0
   223
			if ( stream == NULL ) {
slouken@0
   224
				stream = audio->fake_stream;
slouken@0
   225
			}
slouken@0
   226
			memcpy(stream, audio->convert.buf,
slouken@0
   227
			               audio->convert.len_cvt);
slouken@0
   228
		}
slouken@0
   229
slouken@0
   230
		/* Ready current buffer for play and change current buffer */
slouken@0
   231
		if ( stream != audio->fake_stream ) {
slouken@0
   232
			audio->PlayAudio(audio);
slouken@21
   233
#ifdef ENABLE_AHI
slouken@21
   234
/* AmigaOS don't have to wait the first time audio is played! */
slouken@21
   235
			started++;
slouken@21
   236
#endif
slouken@0
   237
		}
slouken@0
   238
	}
slouken@0
   239
	/* Wait for the audio to drain.. */
slouken@0
   240
	if ( audio->WaitDone ) {
slouken@0
   241
		audio->WaitDone(audio);
slouken@0
   242
	}
slouken@21
   243
slouken@21
   244
#ifdef ENABLE_AHI
slouken@21
   245
	D(bug("WaitAudio...Done\n"));
slouken@21
   246
slouken@21
   247
	audio->CloseAudio(audio);
slouken@21
   248
slouken@21
   249
	D(bug("CloseAudio..Done, subtask exiting...\n"));
slouken@21
   250
	audio_configured = 0;
slouken@21
   251
#endif
slouken@0
   252
	return(0);
slouken@0
   253
}
slouken@0
   254
slouken@322
   255
static void SDL_LockAudio_Default(SDL_AudioDevice *audio)
slouken@322
   256
{
slouken@322
   257
	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
slouken@322
   258
		return;
slouken@322
   259
	}
slouken@322
   260
	SDL_mutexP(audio->mixer_lock);
slouken@322
   261
}
slouken@322
   262
slouken@322
   263
static void SDL_UnlockAudio_Default(SDL_AudioDevice *audio)
slouken@322
   264
{
slouken@322
   265
	if ( audio->thread && (SDL_ThreadID() == audio->threadid) ) {
slouken@322
   266
		return;
slouken@322
   267
	}
slouken@322
   268
	SDL_mutexV(audio->mixer_lock);
slouken@322
   269
}
slouken@322
   270
slouken@0
   271
int SDL_AudioInit(const char *driver_name)
slouken@0
   272
{
slouken@0
   273
	SDL_AudioDevice *audio;
slouken@0
   274
	int i = 0, idx;
slouken@0
   275
slouken@0
   276
	/* Check to make sure we don't overwrite 'current_audio' */
slouken@0
   277
	if ( current_audio != NULL ) {
slouken@0
   278
		SDL_AudioQuit();
slouken@0
   279
	}
slouken@0
   280
slouken@0
   281
	/* Select the proper audio driver */
slouken@0
   282
	audio = NULL;
slouken@0
   283
	idx = 0;
slouken@0
   284
#ifdef unix
slouken@0
   285
	if ( (driver_name == NULL) && (getenv("ESPEAKER") != NULL) ) {
slouken@0
   286
		/* Ahem, we know that if ESPEAKER is set, user probably wants
slouken@0
   287
		   to use ESD, but don't start it if it's not already running.
slouken@0
   288
		   This probably isn't the place to do this, but... Shh! :)
slouken@0
   289
		 */
slouken@0
   290
		for ( i=0; bootstrap[i]; ++i ) {
slouken@0
   291
			if ( strcmp(bootstrap[i]->name, "esd") == 0 ) {
slouken@0
   292
				const char *esd_no_spawn;
slouken@0
   293
slouken@0
   294
				/* Don't start ESD if it's not running */
slouken@0
   295
				esd_no_spawn = getenv("ESD_NO_SPAWN");
slouken@0
   296
				if ( esd_no_spawn == NULL ) {
slouken@0
   297
					putenv("ESD_NO_SPAWN=1");
slouken@0
   298
				}
slouken@0
   299
				if ( bootstrap[i]->available() ) {
slouken@0
   300
					audio = bootstrap[i]->create(0);
slouken@0
   301
					break;
slouken@0
   302
				}
slouken@0
   303
#ifdef linux	/* No unsetenv() on most platforms */
slouken@0
   304
				if ( esd_no_spawn == NULL ) {
slouken@0
   305
					unsetenv("ESD_NO_SPAWN");
slouken@0
   306
				}
slouken@0
   307
#endif
slouken@0
   308
			}
slouken@0
   309
		}
slouken@0
   310
	}
slouken@0
   311
#endif /* unix */
slouken@0
   312
	if ( audio == NULL ) {
slouken@0
   313
		if ( driver_name != NULL ) {
slouken@0
   314
#if 0	/* This will be replaced with a better driver selection API */
slouken@0
   315
			if ( strrchr(driver_name, ':') != NULL ) {
slouken@0
   316
				idx = atoi(strrchr(driver_name, ':')+1);
slouken@0
   317
			}
slouken@0
   318
#endif
slouken@0
   319
			for ( i=0; bootstrap[i]; ++i ) {
slouken@0
   320
				if (strncmp(bootstrap[i]->name, driver_name,
slouken@0
   321
				            strlen(bootstrap[i]->name)) == 0) {
slouken@0
   322
					if ( bootstrap[i]->available() ) {
slouken@0
   323
						audio=bootstrap[i]->create(idx);
slouken@0
   324
						break;
slouken@0
   325
					}
slouken@0
   326
				}
slouken@0
   327
			}
slouken@0
   328
		} else {
slouken@0
   329
			for ( i=0; bootstrap[i]; ++i ) {
slouken@0
   330
				if ( bootstrap[i]->available() ) {
slouken@0
   331
					audio = bootstrap[i]->create(idx);
slouken@0
   332
					if ( audio != NULL ) {
slouken@0
   333
						break;
slouken@0
   334
					}
slouken@0
   335
				}
slouken@0
   336
			}
slouken@0
   337
		}
slouken@0
   338
		if ( audio == NULL ) {
slouken@0
   339
			SDL_SetError("No available audio device");
slouken@0
   340
#if 0 /* Don't fail SDL_Init() if audio isn't available.
slouken@0
   341
         SDL_OpenAudio() will handle it at that point.  *sigh*
slouken@0
   342
       */
slouken@0
   343
			return(-1);
slouken@0
   344
#endif
slouken@0
   345
		}
slouken@0
   346
	}
slouken@0
   347
	current_audio = audio;
slouken@0
   348
	if ( current_audio ) {
slouken@0
   349
		current_audio->name = bootstrap[i]->name;
slouken@322
   350
		if ( !current_audio->LockAudio && !current_audio->UnlockAudio ) {
slouken@322
   351
			current_audio->LockAudio = SDL_LockAudio_Default;
slouken@322
   352
			current_audio->UnlockAudio = SDL_UnlockAudio_Default;
slouken@322
   353
		}
slouken@0
   354
	}
slouken@0
   355
	return(0);
slouken@0
   356
}
slouken@0
   357
slouken@0
   358
char *SDL_AudioDriverName(char *namebuf, int maxlen)
slouken@0
   359
{
slouken@0
   360
	if ( current_audio != NULL ) {
slouken@0
   361
		strncpy(namebuf, current_audio->name, maxlen-1);
slouken@0
   362
		namebuf[maxlen-1] = '\0';
slouken@0
   363
		return(namebuf);
slouken@0
   364
	}
slouken@0
   365
	return(NULL);
slouken@0
   366
}
slouken@0
   367
slouken@0
   368
int SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
slouken@0
   369
{
slouken@0
   370
	SDL_AudioDevice *audio;
slouken@0
   371
slouken@0
   372
	/* Start up the audio driver, if necessary */
slouken@0
   373
	if ( ! current_audio ) {
slouken@0
   374
		if ( (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) ||
slouken@0
   375
		     (current_audio == NULL) ) {
slouken@0
   376
			return(-1);
slouken@0
   377
		}
slouken@0
   378
	}
slouken@0
   379
	audio = current_audio;
slouken@0
   380
slouken@262
   381
	if (audio->opened) {
slouken@262
   382
		SDL_SetError("Audio device is already opened");
slouken@262
   383
		return(-1);
slouken@262
   384
	}
slouken@262
   385
slouken@0
   386
	/* Verify some parameters */
slouken@0
   387
	if ( desired->callback == NULL ) {
slouken@0
   388
		SDL_SetError("SDL_OpenAudio() passed a NULL callback");
slouken@0
   389
		return(-1);
slouken@0
   390
	}
slouken@0
   391
	switch ( desired->channels ) {
slouken@0
   392
	    case 1:	/* Mono */
slouken@0
   393
	    case 2:	/* Stereo */
slouken@942
   394
	    case 4:	/* surround */
slouken@942
   395
	    case 6:	/* surround with center and lfe */
slouken@0
   396
		break;
slouken@0
   397
	    default:
slouken@0
   398
		SDL_SetError("1 (mono) and 2 (stereo) channels supported");
slouken@0
   399
		return(-1);
slouken@0
   400
	}
slouken@0
   401
slouken@630
   402
#if defined(macintosh) || defined(__riscos__)
slouken@0
   403
	/* FIXME: Need to implement PPC interrupt asm for SDL_LockAudio() */
slouken@0
   404
#else
slouken@398
   405
#if defined(__MINT__) && !defined(ENABLE_THREADS)
slouken@398
   406
	/* Uses interrupt driven audio, without thread */
slouken@398
   407
#else
slouken@0
   408
	/* Create a semaphore for locking the sound buffers */
slouken@0
   409
	audio->mixer_lock = SDL_CreateMutex();
slouken@0
   410
	if ( audio->mixer_lock == NULL ) {
slouken@0
   411
		SDL_SetError("Couldn't create mixer lock");
slouken@0
   412
		SDL_CloseAudio();
slouken@0
   413
		return(-1);
slouken@0
   414
	}
slouken@398
   415
#endif /* __MINT__ */
slouken@398
   416
#endif /* macintosh */
slouken@0
   417
slouken@0
   418
	/* Calculate the silence and size of the audio specification */
slouken@0
   419
	SDL_CalculateAudioSpec(desired);
slouken@0
   420
slouken@0
   421
	/* Open the audio subsystem */
slouken@0
   422
	memcpy(&audio->spec, desired, sizeof(audio->spec));
slouken@0
   423
	audio->convert.needed = 0;
slouken@0
   424
	audio->enabled = 1;
slouken@0
   425
	audio->paused  = 1;
slouken@21
   426
slouken@21
   427
#ifndef ENABLE_AHI
slouken@21
   428
slouken@21
   429
/* AmigaOS opens audio inside the main loop */
slouken@0
   430
	audio->opened = audio->OpenAudio(audio, &audio->spec)+1;
slouken@21
   431
slouken@0
   432
	if ( ! audio->opened ) {
slouken@0
   433
		SDL_CloseAudio();
slouken@0
   434
		return(-1);
slouken@0
   435
	}
slouken@21
   436
#else
slouken@21
   437
	D(bug("Locking semaphore..."));
slouken@21
   438
	SDL_mutexP(audio->mixer_lock);
slouken@21
   439
slouken@21
   440
	audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
slouken@21
   441
	D(bug("Created thread...\n"));
slouken@21
   442
slouken@21
   443
	if ( audio->thread == NULL ) {
slouken@21
   444
		SDL_mutexV(audio->mixer_lock);
slouken@21
   445
		SDL_CloseAudio();
slouken@21
   446
		SDL_SetError("Couldn't create audio thread");
slouken@21
   447
		return(-1);
slouken@21
   448
	}
slouken@21
   449
slouken@21
   450
	while(!audio_configured)
slouken@21
   451
		SDL_Delay(100);
slouken@21
   452
#endif
slouken@0
   453
slouken@0
   454
	/* If the audio driver changes the buffer size, accept it */
slouken@0
   455
	if ( audio->spec.samples != desired->samples ) {
slouken@0
   456
		desired->samples = audio->spec.samples;
slouken@0
   457
		SDL_CalculateAudioSpec(desired);
slouken@0
   458
	}
slouken@0
   459
slouken@0
   460
	/* Allocate a fake audio memory buffer */
slouken@0
   461
	audio->fake_stream = SDL_AllocAudioMem(audio->spec.size);
slouken@0
   462
	if ( audio->fake_stream == NULL ) {
slouken@0
   463
		SDL_CloseAudio();
slouken@0
   464
		SDL_OutOfMemory();
slouken@0
   465
		return(-1);
slouken@0
   466
	}
slouken@0
   467
slouken@0
   468
	/* See if we need to do any conversion */
slouken@808
   469
	if ( obtained != NULL ) {
slouken@808
   470
		memcpy(obtained, &audio->spec, sizeof(audio->spec));
slouken@808
   471
	} else if ( desired->freq != audio->spec.freq ||
slouken@808
   472
                    desired->format != audio->spec.format ||
slouken@808
   473
	            desired->channels != audio->spec.channels ) {
slouken@808
   474
		/* Build an audio conversion block */
slouken@808
   475
		if ( SDL_BuildAudioCVT(&audio->convert,
slouken@808
   476
			desired->format, desired->channels,
slouken@808
   477
					desired->freq,
slouken@808
   478
			audio->spec.format, audio->spec.channels,
slouken@808
   479
					audio->spec.freq) < 0 ) {
slouken@808
   480
			SDL_CloseAudio();
slouken@808
   481
			return(-1);
slouken@0
   482
		}
slouken@808
   483
		if ( audio->convert.needed ) {
slouken@808
   484
			audio->convert.len = desired->size;
slouken@808
   485
			audio->convert.buf =(Uint8 *)SDL_AllocAudioMem(
slouken@808
   486
			   audio->convert.len*audio->convert.len_mult);
slouken@808
   487
			if ( audio->convert.buf == NULL ) {
slouken@0
   488
				SDL_CloseAudio();
slouken@808
   489
				SDL_OutOfMemory();
slouken@0
   490
				return(-1);
slouken@0
   491
			}
slouken@0
   492
		}
slouken@0
   493
	}
slouken@0
   494
slouken@21
   495
#ifndef ENABLE_AHI
slouken@0
   496
	/* Start the audio thread if necessary */
slouken@0
   497
	switch (audio->opened) {
slouken@0
   498
		case  1:
slouken@0
   499
			/* Start the audio thread */
slouken@0
   500
			audio->thread = SDL_CreateThread(SDL_RunAudio, audio);
slouken@0
   501
			if ( audio->thread == NULL ) {
slouken@0
   502
				SDL_CloseAudio();
slouken@0
   503
				SDL_SetError("Couldn't create audio thread");
slouken@0
   504
				return(-1);
slouken@0
   505
			}
slouken@0
   506
			break;
slouken@0
   507
slouken@0
   508
		default:
slouken@0
   509
			/* The audio is now playing */
slouken@0
   510
			break;
slouken@0
   511
	}
slouken@21
   512
#else
slouken@21
   513
	SDL_mutexV(audio->mixer_lock);
slouken@21
   514
	D(bug("SDL_OpenAudio USCITA...\n"));
slouken@21
   515
slouken@21
   516
#endif
slouken@21
   517
slouken@0
   518
	return(0);
slouken@0
   519
}
slouken@0
   520
slouken@0
   521
SDL_audiostatus SDL_GetAudioStatus(void)
slouken@0
   522
{
slouken@0
   523
	SDL_AudioDevice *audio = current_audio;
slouken@0
   524
	SDL_audiostatus status;
slouken@0
   525
slouken@0
   526
	status = SDL_AUDIO_STOPPED;
slouken@0
   527
	if ( audio && audio->enabled ) {
slouken@0
   528
		if ( audio->paused ) {
slouken@0
   529
			status = SDL_AUDIO_PAUSED;
slouken@0
   530
		} else {
slouken@0
   531
			status = SDL_AUDIO_PLAYING;
slouken@0
   532
		}
slouken@0
   533
	}
slouken@0
   534
	return(status);
slouken@0
   535
}
slouken@0
   536
slouken@0
   537
void SDL_PauseAudio (int pause_on)
slouken@0
   538
{
slouken@0
   539
	SDL_AudioDevice *audio = current_audio;
slouken@0
   540
slouken@0
   541
	if ( audio ) {
slouken@0
   542
		audio->paused = pause_on;
slouken@0
   543
	}
slouken@0
   544
}
slouken@0
   545
slouken@0
   546
void SDL_LockAudio (void)
slouken@0
   547
{
slouken@0
   548
	SDL_AudioDevice *audio = current_audio;
slouken@0
   549
slouken@0
   550
	/* Obtain a lock on the mixing buffers */
slouken@322
   551
	if ( audio && audio->LockAudio ) {
slouken@322
   552
		audio->LockAudio(audio);
slouken@0
   553
	}
slouken@0
   554
}
slouken@0
   555
slouken@0
   556
void SDL_UnlockAudio (void)
slouken@0
   557
{
slouken@0
   558
	SDL_AudioDevice *audio = current_audio;
slouken@0
   559
slouken@0
   560
	/* Release lock on the mixing buffers */
slouken@322
   561
	if ( audio && audio->UnlockAudio ) {
slouken@322
   562
		audio->UnlockAudio(audio);
slouken@0
   563
	}
slouken@0
   564
}
slouken@0
   565
slouken@0
   566
void SDL_CloseAudio (void)
slouken@0
   567
{
slouken@0
   568
	SDL_QuitSubSystem(SDL_INIT_AUDIO);
slouken@0
   569
}
slouken@0
   570
slouken@0
   571
void SDL_AudioQuit(void)
slouken@0
   572
{
slouken@0
   573
	SDL_AudioDevice *audio = current_audio;
slouken@0
   574
slouken@0
   575
	if ( audio ) {
slouken@0
   576
		audio->enabled = 0;
slouken@0
   577
		if ( audio->thread != NULL ) {
slouken@0
   578
			SDL_WaitThread(audio->thread, NULL);
slouken@0
   579
		}
slouken@0
   580
		if ( audio->mixer_lock != NULL ) {
slouken@0
   581
			SDL_DestroyMutex(audio->mixer_lock);
slouken@0
   582
		}
slouken@0
   583
		if ( audio->fake_stream != NULL ) {
slouken@0
   584
			SDL_FreeAudioMem(audio->fake_stream);
slouken@0
   585
		}
slouken@0
   586
		if ( audio->convert.needed ) {
slouken@0
   587
			SDL_FreeAudioMem(audio->convert.buf);
slouken@21
   588
slouken@0
   589
		}
slouken@21
   590
#ifndef ENABLE_AHI
slouken@0
   591
		if ( audio->opened ) {
slouken@0
   592
			audio->CloseAudio(audio);
slouken@0
   593
			audio->opened = 0;
slouken@0
   594
		}
slouken@21
   595
#endif
slouken@0
   596
		/* Free the driver data */
slouken@0
   597
		audio->free(audio);
slouken@0
   598
		current_audio = NULL;
slouken@0
   599
	}
slouken@0
   600
}
slouken@0
   601
slouken@0
   602
#define NUM_FORMATS	6
slouken@0
   603
static int format_idx;
slouken@0
   604
static int format_idx_sub;
slouken@0
   605
static Uint16 format_list[NUM_FORMATS][NUM_FORMATS] = {
slouken@0
   606
 { AUDIO_U8, AUDIO_S8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
slouken@0
   607
 { AUDIO_S8, AUDIO_U8, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB },
slouken@0
   608
 { AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_U8, AUDIO_S8 },
slouken@0
   609
 { AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_U8, AUDIO_S8 },
slouken@0
   610
 { AUDIO_U16LSB, AUDIO_U16MSB, AUDIO_S16LSB, AUDIO_S16MSB, AUDIO_U8, AUDIO_S8 },
slouken@0
   611
 { AUDIO_U16MSB, AUDIO_U16LSB, AUDIO_S16MSB, AUDIO_S16LSB, AUDIO_U8, AUDIO_S8 },
slouken@0
   612
};
slouken@0
   613
slouken@0
   614
Uint16 SDL_FirstAudioFormat(Uint16 format)
slouken@0
   615
{
slouken@0
   616
	for ( format_idx=0; format_idx < NUM_FORMATS; ++format_idx ) {
slouken@0
   617
		if ( format_list[format_idx][0] == format ) {
slouken@0
   618
			break;
slouken@0
   619
		}
slouken@0
   620
	}
slouken@0
   621
	format_idx_sub = 0;
slouken@0
   622
	return(SDL_NextAudioFormat());
slouken@0
   623
}
slouken@0
   624
slouken@0
   625
Uint16 SDL_NextAudioFormat(void)
slouken@0
   626
{
slouken@0
   627
	if ( (format_idx == NUM_FORMATS) || (format_idx_sub == NUM_FORMATS) ) {
slouken@0
   628
		return(0);
slouken@0
   629
	}
slouken@0
   630
	return(format_list[format_idx][format_idx_sub++]);
slouken@0
   631
}
slouken@0
   632
slouken@0
   633
void SDL_CalculateAudioSpec(SDL_AudioSpec *spec)
slouken@0
   634
{
slouken@0
   635
	switch (spec->format) {
slouken@0
   636
		case AUDIO_U8:
slouken@0
   637
			spec->silence = 0x80;
slouken@0
   638
			break;
slouken@0
   639
		default:
slouken@0
   640
			spec->silence = 0x00;
slouken@0
   641
			break;
slouken@0
   642
	}
slouken@0
   643
	spec->size = (spec->format&0xFF)/8;
slouken@0
   644
	spec->size *= spec->channels;
slouken@0
   645
	spec->size *= spec->samples;
slouken@0
   646
}