src/audio/dsp/SDL_dspaudio.c
author Sam Lantinga
Fri, 12 Nov 2004 21:39:04 +0000
changeset 968 4675910b0b7b
parent 960 eec28a5278be
child 1037 c5dedfdb4e42
permissions -rw-r--r--
Date: Mon, 11 Oct 2004 15:17:27 +0300 (EEST)
From: Hannu Savolainen
Subject: Re: SDL uses obsolete OSS features

I did some work on getting OSS to work better with SDL. There have been
some problems with select which should be fixed now.

I'm having some problems in understanding what is the purpose of the
DSP_WaitAudio() routine. I added a return to the very beginning of this
routine and commendted out the define for USE_BLOCKING_WRITES. At least
lbreakout2 seems to work as well as earlier. The latencies are the same.

An ordinary blocking write does exactly the same thing than DSP_WaitAudio
does. So I would recommend using the USE_BLOCKING_WRITES approach and
removing everything from the DSP_WaitAudio routine. Also enabling
USE_BLOCKING_WRITES makes it possible to simplify DSP_PlayAudio() because
you don't need to handle the partial writes (the do-while loop).

Attached is a patch against SDL-1.2.7. After these changes SDL will use
OSS as it's designed to be used (make it as simple as possible). This code
should work with all OSS implementations because it uses only the very
fundamental features that have been there since the jurassic times.
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@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
#ifdef SAVE_RCSID
slouken@0
    27
static char rcsid =
slouken@0
    28
 "@(#) $Id$";
slouken@0
    29
#endif
slouken@0
    30
slouken@0
    31
/* Allow access to a raw mixing buffer */
slouken@0
    32
slouken@0
    33
#include <stdlib.h>
slouken@0
    34
#include <stdio.h>
slouken@0
    35
#include <string.h>
slouken@0
    36
#include <errno.h>
slouken@0
    37
#include <unistd.h>
slouken@0
    38
#include <fcntl.h>
slouken@0
    39
#include <signal.h>
slouken@0
    40
#include <sys/time.h>
slouken@0
    41
#include <sys/ioctl.h>
slouken@0
    42
#include <sys/stat.h>
slouken@94
    43
#ifdef OSS_USE_SOUNDCARD_H
slouken@94
    44
/* This is installed on some systems */
slouken@94
    45
#include <soundcard.h>
slouken@94
    46
#else
slouken@94
    47
/* This is recommended by OSS */
slouken@0
    48
#include <sys/soundcard.h>
slouken@94
    49
#endif
slouken@0
    50
slouken@0
    51
#include "SDL_audio.h"
slouken@0
    52
#include "SDL_error.h"
slouken@0
    53
#include "SDL_audiomem.h"
slouken@0
    54
#include "SDL_audio_c.h"
slouken@0
    55
#include "SDL_timer.h"
slouken@0
    56
#include "SDL_audiodev_c.h"
slouken@0
    57
#include "SDL_dspaudio.h"
slouken@0
    58
slouken@0
    59
/* The tag name used by DSP audio */
slouken@0
    60
#define DSP_DRIVER_NAME         "dsp"
slouken@0
    61
slouken@0
    62
/* Open the audio device for playback, and don't block if busy */
slouken@0
    63
#define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
slouken@0
    64
slouken@0
    65
/* Audio driver functions */
slouken@0
    66
static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec);
slouken@0
    67
static void DSP_WaitAudio(_THIS);
slouken@0
    68
static void DSP_PlayAudio(_THIS);
slouken@0
    69
static Uint8 *DSP_GetAudioBuf(_THIS);
slouken@0
    70
static void DSP_CloseAudio(_THIS);
slouken@0
    71
slouken@0
    72
/* Audio driver bootstrap functions */
slouken@0
    73
slouken@0
    74
static int Audio_Available(void)
slouken@0
    75
{
slouken@0
    76
	int fd;
slouken@0
    77
	int available;
slouken@0
    78
slouken@0
    79
	available = 0;
slouken@0
    80
	fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
slouken@0
    81
	if ( fd >= 0 ) {
slouken@0
    82
		available = 1;
slouken@0
    83
		close(fd);
slouken@0
    84
	}
slouken@0
    85
	return(available);
slouken@0
    86
}
slouken@0
    87
slouken@0
    88
static void Audio_DeleteDevice(SDL_AudioDevice *device)
slouken@0
    89
{
slouken@0
    90
	free(device->hidden);
slouken@0
    91
	free(device);
slouken@0
    92
}
slouken@0
    93
slouken@0
    94
static SDL_AudioDevice *Audio_CreateDevice(int devindex)
slouken@0
    95
{
slouken@0
    96
	SDL_AudioDevice *this;
slouken@0
    97
slouken@0
    98
	/* Initialize all variables that we clean on shutdown */
slouken@0
    99
	this = (SDL_AudioDevice *)malloc(sizeof(SDL_AudioDevice));
slouken@0
   100
	if ( this ) {
slouken@0
   101
		memset(this, 0, (sizeof *this));
slouken@0
   102
		this->hidden = (struct SDL_PrivateAudioData *)
slouken@0
   103
				malloc((sizeof *this->hidden));
slouken@0
   104
	}
slouken@0
   105
	if ( (this == NULL) || (this->hidden == NULL) ) {
slouken@0
   106
		SDL_OutOfMemory();
slouken@0
   107
		if ( this ) {
slouken@0
   108
			free(this);
slouken@0
   109
		}
slouken@0
   110
		return(0);
slouken@0
   111
	}
slouken@0
   112
	memset(this->hidden, 0, (sizeof *this->hidden));
slouken@0
   113
	audio_fd = -1;
slouken@0
   114
slouken@0
   115
	/* Set the function pointers */
slouken@0
   116
	this->OpenAudio = DSP_OpenAudio;
slouken@0
   117
	this->WaitAudio = DSP_WaitAudio;
slouken@0
   118
	this->PlayAudio = DSP_PlayAudio;
slouken@0
   119
	this->GetAudioBuf = DSP_GetAudioBuf;
slouken@0
   120
	this->CloseAudio = DSP_CloseAudio;
slouken@0
   121
slouken@0
   122
	this->free = Audio_DeleteDevice;
slouken@0
   123
slouken@0
   124
	return this;
slouken@0
   125
}
slouken@0
   126
slouken@0
   127
AudioBootStrap DSP_bootstrap = {
slouken@0
   128
	DSP_DRIVER_NAME, "OSS /dev/dsp standard audio",
slouken@0
   129
	Audio_Available, Audio_CreateDevice
slouken@0
   130
};
slouken@0
   131
slouken@0
   132
/* This function waits until it is possible to write a full sound buffer */
slouken@0
   133
static void DSP_WaitAudio(_THIS)
slouken@0
   134
{
slouken@968
   135
	/* Not needed at all since OSS handles waiting automagically */
slouken@0
   136
}
slouken@0
   137
slouken@0
   138
static void DSP_PlayAudio(_THIS)
slouken@0
   139
{
slouken@968
   140
	if (write(audio_fd, mixbuf, mixlen)==-1)
slouken@968
   141
	{
slouken@968
   142
		perror("Audio write");
slouken@968
   143
		this->enabled = 0;
slouken@0
   144
	}
slouken@0
   145
slouken@0
   146
#ifdef DEBUG_AUDIO
slouken@968
   147
	fprintf(stderr, "Wrote %d bytes of audio data\n", mixlen);
slouken@0
   148
#endif
slouken@0
   149
}
slouken@0
   150
slouken@0
   151
static Uint8 *DSP_GetAudioBuf(_THIS)
slouken@0
   152
{
slouken@0
   153
	return(mixbuf);
slouken@0
   154
}
slouken@0
   155
slouken@0
   156
static void DSP_CloseAudio(_THIS)
slouken@0
   157
{
slouken@0
   158
	if ( mixbuf != NULL ) {
slouken@0
   159
		SDL_FreeAudioMem(mixbuf);
slouken@0
   160
		mixbuf = NULL;
slouken@0
   161
	}
slouken@0
   162
	if ( audio_fd >= 0 ) {
slouken@0
   163
		close(audio_fd);
slouken@0
   164
		audio_fd = -1;
slouken@0
   165
	}
slouken@0
   166
}
slouken@0
   167
slouken@968
   168
static int DSP_OpenAudio(_THIS, SDL_AudioSpec *spec)
slouken@0
   169
{
slouken@968
   170
	char audiodev[1024];
slouken@968
   171
	int format;
slouken@0
   172
	int value;
slouken@968
   173
	int frag_spec;
slouken@968
   174
	Uint16 test_format;
slouken@0
   175
slouken@968
   176
	/* Open the audio device */
slouken@968
   177
	audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
slouken@0
   178
	if ( audio_fd < 0 ) {
slouken@0
   179
		SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
slouken@0
   180
		return(-1);
slouken@0
   181
	}
slouken@968
   182
	mixbuf = NULL;
slouken@968
   183
slouken@968
   184
	/* Make the file descriptor use blocking writes with fcntl() */
slouken@968
   185
	{ long flags;
slouken@968
   186
		flags = fcntl(audio_fd, F_GETFL);
slouken@968
   187
		flags &= ~O_NONBLOCK;
slouken@968
   188
		if ( fcntl(audio_fd, F_SETFL, flags) < 0 ) {
slouken@968
   189
			SDL_SetError("Couldn't set audio blocking mode");
slouken@968
   190
			return(-1);
slouken@968
   191
		}
slouken@968
   192
	}
slouken@968
   193
slouken@968
   194
	/* Get a list of supported hardware formats */
slouken@968
   195
	if ( ioctl(audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0 ) {
slouken@968
   196
		perror("SNDCTL_DSP_GETFMTS");
slouken@968
   197
		SDL_SetError("Couldn't get audio format list");
slouken@968
   198
		return(-1);
slouken@968
   199
	}
slouken@968
   200
slouken@968
   201
	/* Try for a closest match on audio format */
slouken@968
   202
	format = 0;
slouken@968
   203
	for ( test_format = SDL_FirstAudioFormat(spec->format);
slouken@968
   204
						! format && test_format; ) {
slouken@968
   205
#ifdef DEBUG_AUDIO
slouken@968
   206
		fprintf(stderr, "Trying format 0x%4.4x\n", test_format);
slouken@968
   207
#endif
slouken@968
   208
		switch ( test_format ) {
slouken@968
   209
			case AUDIO_U8:
slouken@968
   210
				if ( value & AFMT_U8 ) {
slouken@968
   211
					format = AFMT_U8;
slouken@968
   212
				}
slouken@968
   213
				break;
slouken@968
   214
			case AUDIO_S16LSB:
slouken@968
   215
				if ( value & AFMT_S16_LE ) {
slouken@968
   216
					format = AFMT_S16_LE;
slouken@968
   217
				}
slouken@968
   218
				break;
slouken@968
   219
			case AUDIO_S16MSB:
slouken@968
   220
				if ( value & AFMT_S16_BE ) {
slouken@968
   221
					format = AFMT_S16_BE;
slouken@968
   222
				}
slouken@968
   223
				break;
slouken@968
   224
#if 0
slouken@968
   225
/*
slouken@968
   226
 * These formats are not used by any real life systems so they are not 
slouken@968
   227
 * needed here.
slouken@968
   228
 */
slouken@968
   229
			case AUDIO_S8:
slouken@968
   230
				if ( value & AFMT_S8 ) {
slouken@968
   231
					format = AFMT_S8;
slouken@968
   232
				}
slouken@968
   233
				break;
slouken@968
   234
			case AUDIO_U16LSB:
slouken@968
   235
				if ( value & AFMT_U16_LE ) {
slouken@968
   236
					format = AFMT_U16_LE;
slouken@968
   237
				}
slouken@968
   238
				break;
slouken@968
   239
			case AUDIO_U16MSB:
slouken@968
   240
				if ( value & AFMT_U16_BE ) {
slouken@968
   241
					format = AFMT_U16_BE;
slouken@968
   242
				}
slouken@968
   243
				break;
slouken@968
   244
#endif
slouken@968
   245
			default:
slouken@968
   246
				format = 0;
slouken@968
   247
				break;
slouken@968
   248
		}
slouken@968
   249
		if ( ! format ) {
slouken@968
   250
			test_format = SDL_NextAudioFormat();
slouken@968
   251
		}
slouken@968
   252
	}
slouken@968
   253
	if ( format == 0 ) {
slouken@968
   254
		SDL_SetError("Couldn't find any hardware audio formats");
slouken@968
   255
		return(-1);
slouken@968
   256
	}
slouken@968
   257
	spec->format = test_format;
slouken@968
   258
slouken@968
   259
	/* Set the audio format */
slouken@968
   260
	value = format;
slouken@968
   261
	if ( (ioctl(audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) ||
slouken@968
   262
						(value != format) ) {
slouken@968
   263
		perror("SNDCTL_DSP_SETFMT");
slouken@968
   264
		SDL_SetError("Couldn't set audio format");
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@968
   273
		return(-1);
slouken@968
   274
	}
slouken@968
   275
	spec->channels = value;
slouken@968
   276
slouken@968
   277
	/* Set the DSP frequency */
slouken@968
   278
	value = spec->freq;
slouken@968
   279
	if ( ioctl(audio_fd, SNDCTL_DSP_SPEED, &value) < 0 ) {
slouken@968
   280
		perror("SNDCTL_DSP_SPEED");
slouken@968
   281
		SDL_SetError("Couldn't set audio frequency");
slouken@968
   282
		return(-1);
slouken@968
   283
	}
slouken@968
   284
	spec->freq = value;
slouken@0
   285
slouken@0
   286
	/* Calculate the final parameters for this audio specification */
slouken@0
   287
	SDL_CalculateAudioSpec(spec);
slouken@0
   288
slouken@0
   289
	/* Determine the power of two of the fragment size */
slouken@0
   290
	for ( frag_spec = 0; (0x01<<frag_spec) < spec->size; ++frag_spec );
slouken@0
   291
	if ( (0x01<<frag_spec) != spec->size ) {
slouken@0
   292
		SDL_SetError("Fragment size must be a power of two");
slouken@0
   293
		return(-1);
slouken@0
   294
	}
slouken@0
   295
	frag_spec |= 0x00020000;	/* two fragments, for low latency */
slouken@0
   296
slouken@0
   297
	/* Set the audio buffering parameters */
slouken@0
   298
#ifdef DEBUG_AUDIO
slouken@0
   299
	fprintf(stderr, "Requesting %d fragments of size %d\n",
slouken@0
   300
		(frag_spec >> 16), 1<<(frag_spec&0xFFFF));
slouken@0
   301
#endif
slouken@0
   302
	if ( ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0 ) {
slouken@968
   303
		perror("SNDCTL_DSP_SETFRAGMENT");
slouken@0
   304
		fprintf(stderr, "Warning: Couldn't set audio fragment size\n");
slouken@0
   305
	}
slouken@0
   306
#ifdef DEBUG_AUDIO
slouken@0
   307
	{ audio_buf_info info;
slouken@0
   308
	  ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info);
slouken@0
   309
	  fprintf(stderr, "fragments = %d\n", info.fragments);
slouken@0
   310
	  fprintf(stderr, "fragstotal = %d\n", info.fragstotal);
slouken@0
   311
	  fprintf(stderr, "fragsize = %d\n", info.fragsize);
slouken@0
   312
	  fprintf(stderr, "bytes = %d\n", info.bytes);
slouken@0
   313
	}
slouken@0
   314
#endif
slouken@0
   315
slouken@0
   316
	/* Allocate mixing buffer */
slouken@0
   317
	mixlen = spec->size;
slouken@0
   318
	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
slouken@0
   319
	if ( mixbuf == NULL ) {
slouken@0
   320
		return(-1);
slouken@0
   321
	}
slouken@0
   322
	memset(mixbuf, spec->silence, spec->size);
slouken@0
   323
slouken@0
   324
	/* Get the parent process id (we're the parent of the audio thread) */
slouken@0
   325
	parent = getpid();
slouken@0
   326
slouken@0
   327
	/* We're ready to rock and roll. :-) */
slouken@0
   328
	return(0);
slouken@0
   329
}