src/audio/openbsd/SDL_openbsdaudio.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 17 Nov 2002 18:59:10 +0000
changeset 543 522e5202014d
parent 297 f6ffac90895c
child 769 b8d311d90021
permissions -rw-r--r--
*** empty log message ***
slouken@37
     1
/*
slouken@37
     2
    SDL - Simple DirectMedia Layer
slouken@297
     3
    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
slouken@37
     4
slouken@37
     5
    This library is free software; you can redistribute it and/or
slouken@37
     6
    modify it under the terms of the GNU Library General Public
slouken@37
     7
    License as published by the Free Software Foundation; either
slouken@37
     8
    version 2 of the License, or (at your option) any later version.
slouken@37
     9
slouken@37
    10
    This library is distributed in the hope that it will be useful,
slouken@37
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@37
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@37
    13
    Library General Public License for more details.
slouken@37
    14
slouken@37
    15
    You should have received a copy of the GNU Library General Public
slouken@37
    16
    License along with this library; if not, write to the Free
slouken@37
    17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
slouken@37
    18
slouken@37
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@37
    21
*/
slouken@37
    22
slouken@37
    23
/*
slouken@37
    24
 * Driver for native OpenBSD audio(4).
slouken@37
    25
 * vedge@vedge.com.ar.
slouken@37
    26
 */
slouken@37
    27
slouken@37
    28
#include <stdlib.h>
slouken@37
    29
#include <stdio.h>
slouken@37
    30
#include <string.h>
slouken@37
    31
#include <errno.h>
slouken@37
    32
#include <unistd.h>
slouken@37
    33
#include <fcntl.h>
slouken@37
    34
#include <signal.h>
slouken@37
    35
#include <sys/time.h>
slouken@37
    36
#include <sys/ioctl.h>
slouken@37
    37
#include <sys/stat.h>
slouken@37
    38
#include <sys/types.h>
slouken@37
    39
#include <sys/audioio.h>
slouken@37
    40
slouken@37
    41
#include "SDL_audio.h"
slouken@37
    42
#include "SDL_error.h"
slouken@37
    43
#include "SDL_audiomem.h"
slouken@37
    44
#include "SDL_audio_c.h"
slouken@37
    45
#include "SDL_timer.h"
slouken@37
    46
#include "SDL_audiodev_c.h"
slouken@37
    47
#include "SDL_openbsdaudio.h"
slouken@37
    48
slouken@37
    49
/* The tag name used by OpenBSD audio */
slouken@37
    50
#define OBSD_DRIVER_NAME         "openbsd"
slouken@37
    51
slouken@37
    52
/* Open the audio device for playback, and don't block if busy */
slouken@37
    53
/* #define USE_BLOCKING_WRITES */
slouken@37
    54
slouken@37
    55
/* Use timer for synchronization */
slouken@37
    56
/* #define USE_TIMER_SYNC */
slouken@37
    57
slouken@37
    58
/* #define DEBUG_AUDIO */
slouken@37
    59
/* #define DEBUG_AUDIO_STREAM */
slouken@37
    60
slouken@37
    61
#ifdef USE_BLOCKING_WRITES
slouken@37
    62
#define OPEN_FLAGS	O_WRONLY
slouken@37
    63
#else
slouken@37
    64
#define OPEN_FLAGS	(O_WRONLY|O_NONBLOCK)
slouken@37
    65
#endif
slouken@37
    66
slouken@37
    67
/* Audio driver functions */
slouken@37
    68
static void OBSD_WaitAudio(_THIS);
slouken@37
    69
static int OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec);
slouken@37
    70
static void OBSD_PlayAudio(_THIS);
slouken@37
    71
static Uint8 *OBSD_GetAudioBuf(_THIS);
slouken@37
    72
static void OBSD_CloseAudio(_THIS);
slouken@37
    73
slouken@37
    74
#ifdef DEBUG_AUDIO
slouken@37
    75
static void OBSD_Status(_THIS);
slouken@37
    76
#endif
slouken@37
    77
slouken@37
    78
/* Audio driver bootstrap functions */
slouken@37
    79
slouken@37
    80
static int
slouken@37
    81
Audio_Available(void)
slouken@37
    82
{
slouken@37
    83
    int fd;
slouken@37
    84
    int available;
slouken@37
    85
slouken@37
    86
    available = 0;
slouken@37
    87
    fd = SDL_OpenAudioPath(NULL, 0, OPEN_FLAGS, 0);
slouken@37
    88
    if(fd >= 0) {
slouken@37
    89
	available = 1;
slouken@37
    90
	close(fd);
slouken@37
    91
    }
slouken@37
    92
    return(available);
slouken@37
    93
}
slouken@37
    94
slouken@37
    95
static void
slouken@37
    96
Audio_DeleteDevice(SDL_AudioDevice *device)
slouken@37
    97
{
slouken@37
    98
    free(device->hidden);
slouken@37
    99
    free(device);
slouken@37
   100
}
slouken@37
   101
slouken@37
   102
static SDL_AudioDevice
slouken@37
   103
*Audio_CreateDevice(int devindex)
slouken@37
   104
{
slouken@37
   105
    SDL_AudioDevice *this;
slouken@37
   106
slouken@37
   107
    /* Initialize all variables that we clean on shutdown */
slouken@37
   108
    this = (SDL_AudioDevice*)malloc(sizeof(SDL_AudioDevice));
slouken@37
   109
    if(this) {
slouken@37
   110
	memset(this, 0, (sizeof *this));
slouken@37
   111
	this->hidden =
slouken@37
   112
	    (struct SDL_PrivateAudioData*)malloc((sizeof *this->hidden));
slouken@37
   113
    }
slouken@37
   114
    if((this == NULL) || (this->hidden == NULL)) {
slouken@37
   115
	SDL_OutOfMemory();
slouken@37
   116
	if(this) free(this);
slouken@37
   117
	return(0);
slouken@37
   118
    }
slouken@37
   119
    memset(this->hidden, 0, (sizeof *this->hidden));
slouken@37
   120
    audio_fd = -1;
slouken@37
   121
slouken@37
   122
    /* Set the function pointers */
slouken@37
   123
    this->OpenAudio = OBSD_OpenAudio;
slouken@37
   124
    this->WaitAudio = OBSD_WaitAudio;
slouken@37
   125
    this->PlayAudio = OBSD_PlayAudio;
slouken@37
   126
    this->GetAudioBuf = OBSD_GetAudioBuf;
slouken@37
   127
    this->CloseAudio = OBSD_CloseAudio;
slouken@37
   128
slouken@37
   129
    this->free = Audio_DeleteDevice;
slouken@37
   130
    
slouken@37
   131
    return this;
slouken@37
   132
}
slouken@37
   133
slouken@121
   134
AudioBootStrap OPENBSD_AUDIO_bootstrap = {
slouken@37
   135
	OBSD_DRIVER_NAME, "Native OpenBSD audio",
slouken@37
   136
	Audio_Available, Audio_CreateDevice
slouken@37
   137
};
slouken@37
   138
slouken@37
   139
/* This function waits until it is possible to write a full sound buffer */
slouken@37
   140
static void
slouken@37
   141
OBSD_WaitAudio(_THIS)
slouken@37
   142
{
slouken@94
   143
	/* Check to see if the thread-parent process is still alive */
slouken@94
   144
	{ static int cnt = 0;
slouken@94
   145
		/* Note that this only works with thread implementations 
slouken@94
   146
		   that use a different process id for each thread.
slouken@94
   147
		*/
slouken@94
   148
		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
slouken@94
   149
			if ( kill(parent, 0) < 0 ) {
slouken@94
   150
				this->enabled = 0;
slouken@94
   151
			}
slouken@94
   152
		}
slouken@94
   153
	}
slouken@37
   154
slouken@94
   155
#ifndef USE_BLOCKING_WRITES /* Not necessary when using blocking writes */
slouken@94
   156
	/* See if we need to use timed audio synchronization */
slouken@94
   157
	if ( frame_ticks ) {
slouken@94
   158
		/* Use timer for general audio synchronization */
slouken@94
   159
		Sint32 ticks;
slouken@94
   160
slouken@94
   161
		ticks = ((Sint32)(next_frame - SDL_GetTicks()))-FUDGE_TICKS;
slouken@94
   162
		if ( ticks > 0 ) {
slouken@94
   163
			SDL_Delay(ticks);
slouken@94
   164
		}
slouken@94
   165
	} else {
slouken@94
   166
		/* Use select() for audio synchronization */
slouken@94
   167
		fd_set fdset;
slouken@94
   168
		struct timeval timeout;
slouken@94
   169
slouken@94
   170
		FD_ZERO(&fdset);
slouken@94
   171
		FD_SET(audio_fd, &fdset);
slouken@94
   172
		timeout.tv_sec = 10;
slouken@94
   173
		timeout.tv_usec = 0;
slouken@94
   174
#ifdef DEBUG_AUDIO
slouken@94
   175
		fprintf(stderr, "Waiting for audio to get ready\n");
slouken@94
   176
#endif
slouken@94
   177
		if ( select(audio_fd+1, NULL, &fdset, NULL, &timeout) <= 0 ) {
slouken@94
   178
			const char *message =
slouken@94
   179
			"Audio timeout - buggy audio driver? (disabled)";
slouken@94
   180
			/* In general we should never print to the screen,
slouken@94
   181
			   but in this case we have no other way of letting
slouken@94
   182
			   the user know what happened.
slouken@94
   183
			*/
slouken@94
   184
			fprintf(stderr, "SDL: %s\n", message);
slouken@94
   185
			this->enabled = 0;
slouken@94
   186
			/* Don't try to close - may hang */
slouken@94
   187
			audio_fd = -1;
slouken@94
   188
#ifdef DEBUG_AUDIO
slouken@94
   189
			fprintf(stderr, "Done disabling audio\n");
slouken@94
   190
#endif
slouken@94
   191
		}
slouken@94
   192
#ifdef DEBUG_AUDIO
slouken@94
   193
		fprintf(stderr, "Ready!\n");
slouken@94
   194
#endif
slouken@37
   195
	}
slouken@37
   196
#endif /* !USE_BLOCKING_WRITES */
slouken@37
   197
}
slouken@37
   198
slouken@37
   199
static void
slouken@37
   200
OBSD_PlayAudio(_THIS)
slouken@37
   201
{
slouken@94
   202
	int written, p=0;
slouken@37
   203
slouken@94
   204
	/* Write the audio data, checking for EAGAIN on broken audio drivers */
slouken@94
   205
	do {
slouken@94
   206
		written = write(audio_fd, &mixbuf[p], mixlen-p);
slouken@94
   207
		if (written>0)
slouken@94
   208
		   p += written;
slouken@94
   209
		if (written == -1 && errno != 0 && errno != EAGAIN && errno != EINTR)
slouken@94
   210
		{
slouken@94
   211
		   /* Non recoverable error has occurred. It should be reported!!! */
slouken@94
   212
		   perror("audio");
slouken@94
   213
		   break;
slouken@94
   214
		}
slouken@37
   215
slouken@94
   216
		if ( p < written || ((written < 0) && ((errno == 0) || (errno == EAGAIN))) ) {
slouken@94
   217
			SDL_Delay(1);	/* Let a little CPU time go by */
slouken@94
   218
		}
slouken@94
   219
	} while ( p < written );
slouken@37
   220
slouken@94
   221
	/* If timer synchronization is enabled, set the next write frame */
slouken@94
   222
	if ( frame_ticks ) {
slouken@94
   223
		next_frame += frame_ticks;
slouken@94
   224
	}
slouken@37
   225
slouken@94
   226
	/* If we couldn't write, assume fatal error for now */
slouken@94
   227
	if ( written < 0 ) {
slouken@94
   228
		this->enabled = 0;
slouken@94
   229
	}
slouken@94
   230
#ifdef DEBUG_AUDIO
slouken@94
   231
	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
slouken@37
   232
#endif
slouken@37
   233
}
slouken@37
   234
slouken@37
   235
static Uint8
slouken@37
   236
*OBSD_GetAudioBuf(_THIS)
slouken@37
   237
{
slouken@37
   238
    return(mixbuf);
slouken@37
   239
}
slouken@37
   240
slouken@37
   241
static void
slouken@37
   242
OBSD_CloseAudio(_THIS)
slouken@37
   243
{
slouken@37
   244
    if(mixbuf != NULL) {
slouken@37
   245
	SDL_FreeAudioMem(mixbuf);
slouken@37
   246
	mixbuf = NULL;
slouken@37
   247
    }
slouken@37
   248
    if(audio_fd >= 0) {
slouken@37
   249
	close(audio_fd);
slouken@37
   250
	audio_fd = -1;
slouken@37
   251
    }
slouken@37
   252
}
slouken@37
   253
slouken@37
   254
#ifdef DEBUG_AUDIO
slouken@37
   255
void
slouken@37
   256
OBSD_Status(_THIS)
slouken@37
   257
{
slouken@37
   258
    audio_info_t info;
slouken@37
   259
slouken@37
   260
    if(ioctl(audio_fd, AUDIO_GETINFO, &info) < 0) {
slouken@37
   261
	fprintf(stderr,"AUDIO_GETINFO failed.\n");
slouken@37
   262
	return;
slouken@37
   263
    }
slouken@37
   264
slouken@37
   265
    fprintf(stderr,"
slouken@37
   266
[play/record info]
slouken@37
   267
buffer size	:   %d bytes
slouken@37
   268
sample rate	:   %i Hz
slouken@37
   269
channels	:   %i
slouken@37
   270
precision	:   %i-bit
slouken@37
   271
encoding	:   0x%x
slouken@37
   272
seek		:   %i
slouken@37
   273
sample count	:   %i
slouken@37
   274
EOF count	:   %i
slouken@37
   275
paused		:   %s
slouken@37
   276
error occured	:   %s
slouken@37
   277
waiting		:   %s
slouken@37
   278
active		:   %s
slouken@37
   279
",
slouken@37
   280
    info.play.buffer_size,
slouken@37
   281
    info.play.sample_rate,
slouken@37
   282
    info.play.channels,
slouken@37
   283
    info.play.precision,
slouken@37
   284
    info.play.encoding,
slouken@37
   285
    info.play.seek,
slouken@37
   286
    info.play.samples,
slouken@37
   287
    info.play.eof,
slouken@37
   288
    info.play.pause ? "yes" : "no",
slouken@37
   289
    info.play.error ? "yes" : "no",
slouken@37
   290
    info.play.waiting ? "yes" : "no",
slouken@37
   291
    info.play.active ? "yes": "no");
slouken@37
   292
slouken@37
   293
    fprintf(stderr,"
slouken@37
   294
[audio info]
slouken@37
   295
monitor_gain	:   %i
slouken@37
   296
hw block size	:   %d bytes
slouken@37
   297
hi watermark	:   %i
slouken@37
   298
lo watermark	:   %i
slouken@37
   299
audio mode	:   %s
slouken@37
   300
",  
slouken@37
   301
    info.monitor_gain,
slouken@37
   302
    info.blocksize,
slouken@37
   303
    info.hiwat, info.lowat,
slouken@37
   304
    (info.mode == AUMODE_PLAY) ? "PLAY"
slouken@37
   305
    : (info.mode = AUMODE_RECORD) ? "RECORD"
slouken@37
   306
    : (info.mode == AUMODE_PLAY_ALL ? "PLAY_ALL"
slouken@543
   307
    : "?"));
slouken@37
   308
}
slouken@37
   309
#endif /* DEBUG_AUDIO */
slouken@37
   310
slouken@37
   311
static int
slouken@37
   312
OBSD_OpenAudio(_THIS, SDL_AudioSpec *spec)
slouken@37
   313
{
slouken@37
   314
    char audiodev[64];
slouken@131
   315
    Uint16 format;
slouken@37
   316
    audio_info_t info;
slouken@37
   317
slouken@37
   318
    AUDIO_INITINFO(&info);
slouken@37
   319
    
slouken@37
   320
    /* Calculate the final parameters for this audio specification */
slouken@37
   321
    SDL_CalculateAudioSpec(spec);
slouken@37
   322
slouken@37
   323
#ifdef USE_TIMER_SYNC
slouken@37
   324
    frame_ticks = 0.0;
slouken@37
   325
#endif
slouken@37
   326
slouken@37
   327
    /* Open the audio device */
slouken@37
   328
    audio_fd = SDL_OpenAudioPath(audiodev, sizeof(audiodev), OPEN_FLAGS, 0);
slouken@37
   329
    if(audio_fd < 0) {
slouken@37
   330
	SDL_SetError("Couldn't open %s: %s", audiodev, strerror(errno));
slouken@37
   331
	return(-1);
slouken@37
   332
    }
slouken@37
   333
    
slouken@37
   334
    /* Set to play mode */
slouken@37
   335
    info.mode = AUMODE_PLAY;
slouken@37
   336
    if(ioctl(audio_fd, AUDIO_SETINFO, &info) < 0) {
slouken@37
   337
	SDL_SetError("Couldn't put device into play mode");
slouken@37
   338
	return(-1);
slouken@37
   339
    }
slouken@37
   340
    
slouken@37
   341
    mixbuf = NULL;
slouken@131
   342
    AUDIO_INITINFO(&info);
slouken@131
   343
    for (format = SDL_FirstAudioFormat(spec->format); 
slouken@131
   344
    	format; format = SDL_NextAudioFormat())
slouken@37
   345
    {
slouken@131
   346
	switch(format) {
slouken@131
   347
	case AUDIO_U8:
slouken@131
   348
	    info.play.encoding = AUDIO_ENCODING_ULINEAR;
slouken@131
   349
	    info.play.precision = 8;
slouken@131
   350
	    break;
slouken@131
   351
	case AUDIO_S8:
slouken@131
   352
	    info.play.encoding = AUDIO_ENCODING_SLINEAR;
slouken@131
   353
	    info.play.precision = 8;
slouken@131
   354
	    break;
slouken@131
   355
	case AUDIO_S16LSB:
slouken@131
   356
	    info.play.encoding = AUDIO_ENCODING_SLINEAR_LE;
slouken@131
   357
	    info.play.precision = 16;
slouken@131
   358
	    break;
slouken@131
   359
	case AUDIO_S16MSB:
slouken@131
   360
	    info.play.encoding = AUDIO_ENCODING_SLINEAR_BE;
slouken@131
   361
	    info.play.precision = 16;
slouken@131
   362
	    break;
slouken@131
   363
	case AUDIO_U16LSB:
slouken@131
   364
	    info.play.encoding = AUDIO_ENCODING_ULINEAR_LE;
slouken@131
   365
	    info.play.precision = 16;
slouken@131
   366
	    break;
slouken@131
   367
	case AUDIO_U16MSB:
slouken@131
   368
	    info.play.encoding = AUDIO_ENCODING_ULINEAR_BE;
slouken@131
   369
	    info.play.precision = 16;
slouken@131
   370
	    break;
slouken@131
   371
	default:
slouken@131
   372
	    continue;
slouken@37
   373
	}
slouken@131
   374
	if (ioctl(audio_fd, AUDIO_SETINFO, &info) == 0)
slouken@131
   375
	    break;
slouken@37
   376
    }
slouken@37
   377
slouken@131
   378
    if(!format) {
slouken@37
   379
	SDL_SetError("No supported encoding for 0x%x", spec->format);
slouken@37
   380
	return(-1);
slouken@37
   381
    }
slouken@37
   382
slouken@131
   383
    spec->format = format;
slouken@37
   384
slouken@131
   385
    AUDIO_INITINFO(&info);
slouken@37
   386
    info.play.channels = spec->channels;
slouken@131
   387
    if (ioctl(audio_fd, AUDIO_SETINFO, &info) == -1)
slouken@131
   388
    	spec->channels = 1;
slouken@131
   389
    AUDIO_INITINFO(&info);
slouken@37
   390
    info.play.sample_rate = spec->freq;
slouken@131
   391
    (void)ioctl(audio_fd, AUDIO_SETINFO, &info);
slouken@131
   392
    (void)ioctl(audio_fd, AUDIO_GETINFO, &info);
slouken@131
   393
    spec->freq  = info.play.sample_rate;
slouken@37
   394
    /* Allocate mixing buffer */
slouken@37
   395
    mixlen = spec->size;
slouken@37
   396
    mixbuf = (Uint8*)SDL_AllocAudioMem(mixlen);
slouken@37
   397
    if(mixbuf == NULL) {
slouken@37
   398
	return(-1);
slouken@37
   399
    }
slouken@37
   400
    memset(mixbuf, spec->silence, spec->size);
slouken@37
   401
    
slouken@37
   402
    /* Get the parent process id (we're the parent of the audio thread) */
slouken@37
   403
    parent = getpid();
slouken@37
   404
slouken@37
   405
#ifdef DEBUG_AUDIO
slouken@37
   406
    OBSD_Status(this);
slouken@37
   407
#endif
slouken@37
   408
slouken@37
   409
    /* We're ready to rock and roll. :-) */
slouken@37
   410
    return(0);
slouken@37
   411
}