src/audio/dsp/SDL_dspaudio.c
author Sam Lantinga
Thu, 16 Feb 2006 10:11:48 +0000
changeset 1361 19418e4422cb
parent 1358 c71e05b4dc2e
child 1402 d910939febfa
permissions -rw-r--r--
New configure-based build system. Still work in progress, but much improved
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@1312
     3
    Copyright (C) 1997-2006 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 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@1312
    13
    Lesser General Public License for more details.
slouken@0
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@968
    21
slouken@968
    22
    Modified in Oct 2004 by Hannu Savolainen 
slouken@968
    23
    hannu@opensound.com
slouken@0
    24
*/
slouken@0
    25
slouken@0
    26
/* Allow access to a raw mixing buffer */
slouken@0
    27
slouken@1341
    28
#include <stdio.h>	/* For perror() */
slouken@1341
    29
#include <string.h>	/* For strerror() */
slouken@0
    30
#include <errno.h>
slouken@0
    31
#include <unistd.h>
slouken@0
    32
#include <fcntl.h>
slouken@0
    33
#include <signal.h>
slouken@0
    34
#include <sys/time.h>
slouken@0
    35
#include <sys/ioctl.h>
slouken@0
    36
#include <sys/stat.h>
slouken@1361
    37
slouken@1361
    38
#include "SDL_config.h"
slouken@1361
    39
slouken@1361
    40
#if SDL_AUDIO_DRIVER_OSS_SOUNDCARD_H
slouken@94
    41
/* This is installed on some systems */
slouken@94
    42
#include <soundcard.h>
slouken@94
    43
#else
slouken@94
    44
/* This is recommended by OSS */
slouken@0
    45
#include <sys/soundcard.h>
slouken@94
    46
#endif
slouken@0
    47
slouken@1358
    48
#include "SDL_timer.h"
slouken@0
    49
#include "SDL_audio.h"
slouken@1361
    50
#include "../SDL_audiomem.h"
slouken@1361
    51
#include "../SDL_audio_c.h"
slouken@1361
    52
#include "../SDL_audiodev_c.h"
slouken@0
    53
#include "SDL_dspaudio.h"
slouken@0
    54
slouken@0
    55
/* The tag name used by DSP audio */
slouken@0
    56
#define DSP_DRIVER_NAME         "dsp"
slouken@0
    57
slouken@0
    58
/* Open the audio device for playback, and don't block if busy */
slouken@0
    59
#define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
slouken@0
    60
slouken@0
    61
/* Audio driver functions */
slouken@0
    62
static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
slouken@0
    63
static void DSP_WaitAudio(_THIS);
slouken@0
    64
static void DSP_PlayAudio(_THIS);
slouken@0
    65
static Uint8 *DSP_GetAudioBuf(_THIS);
slouken@0
    66
static void DSP_CloseAudio(_THIS);
slouken@0
    67
slouken@0
    68
/* Audio driver bootstrap functions */
slouken@0
    69
slouken@0
    70
static int Audio_Available(void)
slouken@0
    71
{
slouken@0
    72
	int fd;
slouken@0
    73
	int available;
slouken@0
    74
slouken@0
    75
	available = 0;
slouken@0
    76
	fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
slouken@0
    77
	if ( fd >= 0 ) {
slouken@0
    78
		available = 1;
slouken@0
    79
		close(fd);
slouken@0
    80
	}
slouken@0
    81
	return(available);
slouken@0
    82
}
slouken@0
    83
slouken@0
    84
static void Audio_DeleteDevice(SDL_AudioDevice *device)
slouken@0
    85
{
slouken@1336
    86
	SDL_free(device->hidden);
slouken@1336
    87
	SDL_free(device);
slouken@0
    88
}
slouken@0
    89
slouken@0
    90
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
slouken@0
    91
{
slouken@0
    92
	SDL_AudioDevice *this;
slouken@0
    93
slouken@0
    94
	/* Initialize all variables that we clean on shutdown */
slouken@1336
    95
	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
slouken@0
    96
	if ( this ) {
slouken@1336
    97
		SDL_memset(this, 0, (sizeof *this));
slouken@0
    98
		this->hidden = (struct SDL_PrivateAudioData *)
slouken@1336
    99
				SDL_malloc((sizeof *this->hidden));
slouken@0
   100
	}
slouken@0
   101
	if ( (this == NULL) || (this->hidden == NULL) ) {
slouken@0
   102
		SDL_OutOfMemory();
slouken@0
   103
		if ( this ) {
slouken@1336
   104
			SDL_free(this);
slouken@0
   105
		}
slouken@0
   106
		return(0);
slouken@0
   107
	}
slouken@1336
   108
	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
slouken@0
   109
	audio_fd = -1;
slouken@0
   110
slouken@0
   111
	/* Set the function pointers */
slouken@0
   112
	this->OpenAudio = DSP_OpenAudio;
slouken@0
   113
	this->WaitAudio = DSP_WaitAudio;
slouken@0
   114
	this->PlayAudio = DSP_PlayAudio;
slouken@0
   115
	this->GetAudioBuf = DSP_GetAudioBuf;
slouken@0
   116
	this->CloseAudio = DSP_CloseAudio;
slouken@0
   117
slouken@0
   118
	this->free = Audio_DeleteDevice;
slouken@0
   119
slouken@0
   120
	return this;
slouken@0
   121
}
slouken@0
   122
slouken@0
   123
AudioBootStrap DSP_bootstrap = {
slouken@0
   124
	DSP_DRIVER_NAME, "OSS /dev/dsp standard audio",
slouken@0
   125
	Audio_Available, Audio_CreateDevice
slouken@0
   126
};
slouken@0
   127
slouken@0
   128
/* This function waits until it is possible to write a full sound buffer */
slouken@0
   129
static void DSP_WaitAudio(_THIS)
slouken@0
   130
{
slouken@968
   131
	/* Not needed at all since OSS handles waiting automagically */
slouken@0
   132
}
slouken@0
   133
slouken@0
   134
static void DSP_PlayAudio(_THIS)
slouken@0
   135
{
slouken@968
   136
	if (write(audio_fd, mixbuf, mixlen)==-1)
slouken@968
   137
	{
slouken@968
   138
		perror("Audio write");
slouken@968
   139
		this->enabled = 0;
slouken@0
   140
	}
slouken@0
   141
slouken@0
   142
#ifdef DEBUG_AUDIO
slouken@968
   143
	fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
slouken@0
   144
#endif
slouken@0
   145
}
slouken@0
   146
slouken@0
   147
static Uint8 *DSP_GetAudioBuf(_THIS)
slouken@0
   148
{
slouken@0
   149
	return(mixbuf);
slouken@0
   150
}
slouken@0
   151
slouken@0
   152
static void DSP_CloseAudio(_THIS)
slouken@0
   153
{
slouken@0
   154
	if ( mixbuf != NULL ) {
slouken@0
   155
		SDL_FreeAudioMem(mixbuf);
slouken@0
   156
		mixbuf = NULL;
slouken@0
   157
	}
slouken@0
   158
	if ( audio_fd >= 0 ) {
slouken@0
   159
		close(audio_fd);
slouken@0
   160
		audio_fd = -1;
slouken@0
   161
	}
slouken@0
   162
}
slouken@0
   163
slouken@968
   164
static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
slouken@0
   165
{
slouken@968
   166
	char audiodev[1024];
slouken@968
   167
	int format;
slouken@0
   168
	int value;
slouken@968
   169
	int frag_spec;
slouken@968
   170
	Uint16 test_format;
slouken@0
   171
slouken@968
   172
	/* Open the audio device */
slouken@968
   173
	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
slouken@0
   174
	if ( audio_fd < 0 ) {
slouken@0
   175
		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
slouken@0
   176
		return(-1);
slouken@0
   177
	}
slouken@968
   178
	mixbuf = NULL;
slouken@968
   179
slouken@968
   180
	/* Make the file descriptor use blocking writes with fcntl() */
slouken@968
   181
	{ long flags;
slouken@968
   182
		flags = fcntl(audio_fd, F_GETFL);
slouken@968
   183
		flags &= ~O_NONBLOCK;
slouken@968
   184
		if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) {
slouken@968
   185
			SDL_SetError("Couldn't set audio blocking mode");
slouken@1037
   186
			DSP_CloseAudio(this);
slouken@968
   187
			return(-1);
slouken@968
   188
		}
slouken@968
   189
	}
slouken@968
   190
slouken@968
   191
	/* Get a list of supported hardware formats */
slouken@968
   192
	if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
slouken@968
   193
		perror("SNDCTL_DSP_GETFMTS");
slouken@968
   194
		SDL_SetError("Couldn't get audio format list");
slouken@1037
   195
		DSP_CloseAudio(this);
slouken@968
   196
		return(-1);
slouken@968
   197
	}
slouken@968
   198
slouken@968
   199
	/* Try for a closest match on audio format */
slouken@968
   200
	format = 0;
slouken@968
   201
	for ( test_format = SDL_FirstAudioFormat(spec->format);
slouken@968
   202
						! format && test_format; ) {
slouken@968
   203
#ifdef DEBUG_AUDIO
slouken@968
   204
		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
slouken@968
   205
#endif
slouken@968
   206
		switch ( test_format ) {
slouken@968
   207
			case AUDIO_U8:
slouken@968
   208
				if ( value & AFMT_U8 ) {
slouken@968
   209
					format = AFMT_U8;
slouken@968
   210
				}
slouken@968
   211
				break;
slouken@968
   212
			case AUDIO_S16LSB:
slouken@968
   213
				if ( value & AFMT_S16_LE ) {
slouken@968
   214
					format = AFMT_S16_LE;
slouken@968
   215
				}
slouken@968
   216
				break;
slouken@968
   217
			case AUDIO_S16MSB:
slouken@968
   218
				if ( value & AFMT_S16_BE ) {
slouken@968
   219
					format = AFMT_S16_BE;
slouken@968
   220
				}
slouken@968
   221
				break;
slouken@968
   222
#if 0
slouken@968
   223
/*
slouken@968
   224
 * These formats are not used by any real life systems so they are not 
slouken@968
   225
 * needed here.
slouken@968
   226
 */
slouken@968
   227
			case AUDIO_S8:
slouken@968
   228
				if ( value & AFMT_S8 ) {
slouken@968
   229
					format = AFMT_S8;
slouken@968
   230
				}
slouken@968
   231
				break;
slouken@968
   232
			case AUDIO_U16LSB:
slouken@968
   233
				if ( value & AFMT_U16_LE ) {
slouken@968
   234
					format = AFMT_U16_LE;
slouken@968
   235
				}
slouken@968
   236
				break;
slouken@968
   237
			case AUDIO_U16MSB:
slouken@968
   238
				if ( value & AFMT_U16_BE ) {
slouken@968
   239
					format = AFMT_U16_BE;
slouken@968
   240
				}
slouken@968
   241
				break;
slouken@968
   242
#endif
slouken@968
   243
			default:
slouken@968
   244
				format = 0;
slouken@968
   245
				break;
slouken@968
   246
		}
slouken@968
   247
		if ( ! format ) {
slouken@968
   248
			test_format = SDL_NextAudioFormat();
slouken@968
   249
		}
slouken@968
   250
	}
slouken@968
   251
	if ( format == 0 ) {
slouken@968
   252
		SDL_SetError("Couldn't find any hardware audio formats");
slouken@1037
   253
		DSP_CloseAudio(this);
slouken@968
   254
		return(-1);
slouken@968
   255
	}
slouken@968
   256
	spec->format = test_format;
slouken@968
   257
slouken@968
   258
	/* Set the audio format */
slouken@968
   259
	value = format;
slouken@968
   260
	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
slouken@968
   261
						(value != format) ) {
slouken@968
   262
		perror("SNDCTL_DSP_SETFMT");
slouken@968
   263
		SDL_SetError("Couldn't set audio format");
slouken@1037
   264
		DSP_CloseAudio(this);
slouken@968
   265
		return(-1);
slouken@968
   266
	}
slouken@968
   267
slouken@968
   268
	/* Set the number of channels of output */
slouken@968
   269
	value = spec->channels;
slouken@968
   270
	if ( ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0 ) {
slouken@968
   271
		perror("SNDCTL_DSP_CHANNELS");
slouken@968
   272
		SDL_SetError("Cannot set the number of channels");
slouken@1037
   273
		DSP_CloseAudio(this);
slouken@968
   274
		return(-1);
slouken@968
   275
	}
slouken@968
   276
	spec->channels = value;
slouken@968
   277
slouken@968
   278
	/* Set the DSP frequency */
slouken@968
   279
	value = spec->freq;
slouken@968
   280
	if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
slouken@968
   281
		perror("SNDCTL_DSP_SPEED");
slouken@968
   282
		SDL_SetError("Couldn't set audio frequency");
slouken@1037
   283
		DSP_CloseAudio(this);
slouken@968
   284
		return(-1);
slouken@968
   285
	}
slouken@968
   286
	spec->freq = value;
slouken@0
   287
slouken@0
   288
	/* Calculate the final parameters for this audio specification */
slouken@0
   289
	SDL_CalculateAudioSpec(spec);
slouken@0
   290
slouken@0
   291
	/* Determine the power of two of the fragment size */
slouken@0
   292
	for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
slouken@0
   293
	if ( (0x01<<frag_spec) != spec->size ) {
slouken@0
   294
		SDL_SetError("Fragment size must be a power of two");
slouken@1037
   295
		DSP_CloseAudio(this);
slouken@0
   296
		return(-1);
slouken@0
   297
	}
slouken@0
   298
	frag_spec |= 0x00020000;	/* two fragments, for low latency */
slouken@0
   299
slouken@0
   300
	/* Set the audio buffering parameters */
slouken@0
   301
#ifdef DEBUG_AUDIO
slouken@0
   302
	fprintf(stderr, "Requesting %d fragments of size %d\n",
slouken@0
   303
		(frag_spec >> 16), 1<<(frag_spec&0xFFFF));
slouken@0
   304
#endif
slouken@0
   305
	if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
slouken@968
   306
		perror("SNDCTL_DSP_SETFRAGMENT");
slouken@0
   307
	}
slouken@0
   308
#ifdef DEBUG_AUDIO
slouken@0
   309
	{ audio_buf_info info;
slouken@0
   310
	  ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
slouken@0
   311
	  fprintf(stderr, "fragments = %d\n", info.fragments);
slouken@0
   312
	  fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
slouken@0
   313
	  fprintf(stderr, "fragsize = %d\n", info.fragsize);
slouken@0
   314
	  fprintf(stderr, "bytes = %d\n", info.bytes);
slouken@0
   315
	}
slouken@0
   316
#endif
slouken@0
   317
slouken@0
   318
	/* Allocate mixing buffer */
slouken@0
   319
	mixlen = spec->size;
slouken@0
   320
	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
slouken@0
   321
	if ( mixbuf == NULL ) {
slouken@1281
   322
		DSP_CloseAudio(this);
slouken@0
   323
		return(-1);
slouken@0
   324
	}
slouken@1336
   325
	SDL_memset(mixbuf, spec->silence, spec->size);
slouken@0
   326
slouken@0
   327
	/* Get the parent process id (we're the parent of the audio thread) */
slouken@0
   328
	parent = getpid();
slouken@0
   329
slouken@0
   330
	/* We're ready to rock and roll. :-) */
slouken@0
   331
	return(0);
slouken@0
   332
}