From 6fd71185cd6dc200203ab6c483e49791826f8bf1 Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 4 Jul 2023 19:37:31 -0400 Subject: [PATCH] dsp: Updated for new SDL3 audio API. --- CMakeLists.txt | 1 - src/audio/SDL_audiodev.c | 14 +-- src/audio/SDL_audiodev_c.h | 2 +- src/audio/dsp/SDL_dspaudio.c | 212 ++++++++++++++++++----------------- src/audio/dsp/SDL_dspaudio.h | 2 - 5 files changed, 115 insertions(+), 116 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b745b1bbaa..0af3be5528 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -353,7 +353,6 @@ set_option(SDL_CLANG_TIDY "Run clang-tidy static analysis" OFF) set(SDL_VENDOR_INFO "" CACHE STRING "Vendor name and/or version to add to SDL_REVISION") -set(SDL_OSS OFF) set(SDL_JACK OFF) set(SDL_SNDIO OFF) diff --git a/src/audio/SDL_audiodev.c b/src/audio/SDL_audiodev.c index 50bcddf86c..9d73fbb55c 100644 --- a/src/audio/SDL_audiodev.c +++ b/src/audio/SDL_audiodev.c @@ -45,13 +45,13 @@ #define SDL_PATH_DEV_AUDIO "/dev/audio" #endif -static void test_device(const int iscapture, const char *fname, int flags, int (*test)(int fd)) +static void test_device(const SDL_bool iscapture, const char *fname, int flags, SDL_bool (*test)(int fd)) { struct stat sb; if ((stat(fname, &sb) == 0) && (S_ISCHR(sb.st_mode))) { const int audio_fd = open(fname, flags | O_CLOEXEC, 0); if (audio_fd >= 0) { - const int okay = test(audio_fd); + const SDL_bool okay = test(audio_fd); close(audio_fd); if (okay) { static size_t dummyhandle = 0; @@ -63,18 +63,18 @@ static void test_device(const int iscapture, const char *fname, int flags, int ( * information, making this information inaccessible at * enumeration time */ - SDL_AddAudioDevice(iscapture, fname, 0, 0, 0, (void *)(uintptr_t)dummyhandle, ); + SDL_AddAudioDevice(iscapture, fname, NULL, (void *)(uintptr_t)dummyhandle); } } } } -static int test_stub(int fd) +static SDL_bool test_stub(int fd) { - return 1; + return SDL_TRUE; } -static void SDL_EnumUnixAudioDevices_Internal(const int iscapture, const int classic, int (*test)(int)) +static void SDL_EnumUnixAudioDevices_Internal(const SDL_bool iscapture, const SDL_bool classic, SDL_bool (*test)(int)) { const int flags = iscapture ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT; const char *audiodev; @@ -116,7 +116,7 @@ static void SDL_EnumUnixAudioDevices_Internal(const int iscapture, const int cla } } -void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int)) +void SDL_EnumUnixAudioDevices(const SDL_bool classic, SDL_bool (*test)(int)) { SDL_EnumUnixAudioDevices_Internal(SDL_TRUE, classic, test); SDL_EnumUnixAudioDevices_Internal(SDL_FALSE, classic, test); diff --git a/src/audio/SDL_audiodev_c.h b/src/audio/SDL_audiodev_c.h index 370172c9e1..6301d07c54 100644 --- a/src/audio/SDL_audiodev_c.h +++ b/src/audio/SDL_audiodev_c.h @@ -36,6 +36,6 @@ #define OPEN_FLAGS_INPUT (O_RDONLY | O_NONBLOCK) #endif -extern void SDL_EnumUnixAudioDevices(const int classic, int (*test)(int)); +extern void SDL_EnumUnixAudioDevices(const SDL_bool classic, SDL_bool (*test)(int)); #endif /* SDL_audiodev_c_h_ */ diff --git a/src/audio/dsp/SDL_dspaudio.c b/src/audio/dsp/SDL_dspaudio.c index 88c053e8d6..e45df1b04b 100644 --- a/src/audio/dsp/SDL_dspaudio.c +++ b/src/audio/dsp/SDL_dspaudio.c @@ -20,9 +20,9 @@ */ #include "SDL_internal.h" -#ifdef SDL_AUDIO_DRIVER_OSS +// !!! FIXME: clean out perror and fprintf calls in here. -/* Allow access to a raw mixing buffer */ +#ifdef SDL_AUDIO_DRIVER_OSS #include /* For perror() */ #include /* For strerror() */ @@ -38,82 +38,69 @@ #include "../SDL_audio_c.h" #include "../SDL_audiodev_c.h" +#include "../../SDL_utils_c.h" #include "SDL_dspaudio.h" -static void DSP_DetectDevices(void) +static void DSP_DetectDevices(SDL_AudioDevice **default_output, SDL_AudioDevice **default_capture) { - SDL_EnumUnixAudioDevices(0, NULL); + SDL_EnumUnixAudioDevices(SDL_FALSE, NULL); } -static void DSP_CloseDevice(SDL_AudioDevice *_this) +static void DSP_CloseDevice(SDL_AudioDevice *device) { - if (_this->hidden->audio_fd >= 0) { - close(_this->hidden->audio_fd); - } - SDL_free(_this->hidden->mixbuf); - SDL_free(_this->hidden); -} - -static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname) -{ - SDL_bool iscapture = _this->iscapture; - const int flags = ((iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); - int format = 0; - int value; - int frag_spec; - SDL_AudioFormat test_format; - const SDL_AudioFormat *closefmts; - - /* We don't care what the devname is...we'll try to open anything. */ - /* ...but default to first name in the list... */ - if (devname == NULL) { - devname = SDL_GetAudioDeviceName(0, iscapture); - if (devname == NULL) { - return SDL_SetError("No such audio device"); + if (device->hidden) { + if (device->hidden->audio_fd >= 0) { + close(device->hidden->audio_fd); } + SDL_free(device->hidden->mixbuf); + SDL_free(device->hidden); + } +} + +static int DSP_OpenDevice(SDL_AudioDevice *device) +{ + // 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 (device->spec.channels > 8) { + device->spec.channels = 8; + } else if (device->spec.channels > 4) { + device->spec.channels = 4; + } else if (device->spec.channels > 2) { + device->spec.channels = 2; } - /* 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 (_this->spec.channels > 8) { - _this->spec.channels = 8; - } else if (_this->spec.channels > 4) { - _this->spec.channels = 4; - } else if (_this->spec.channels > 2) { - _this->spec.channels = 2; - } - - /* Initialize all variables that we clean on shutdown */ - _this->hidden = (struct SDL_PrivateAudioData *) SDL_malloc(sizeof(*_this->hidden)); - if (_this->hidden == NULL) { + // Initialize all variables that we clean on shutdown + device->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, sizeof(*device->hidden)); + if (device->hidden == NULL) { return SDL_OutOfMemory(); } - SDL_zerop(_this->hidden); - /* Open the audio device */ - _this->hidden->audio_fd = open(devname, flags | O_CLOEXEC, 0); - if (_this->hidden->audio_fd < 0) { - return SDL_SetError("Couldn't open %s: %s", devname, strerror(errno)); + // Open the audio device; we hardcode the device path in `device->name` for lack of better info, so use that. + const int flags = ((device->iscapture) ? OPEN_FLAGS_INPUT : OPEN_FLAGS_OUTPUT); + device->hidden->audio_fd = open(device->name, flags | O_CLOEXEC, 0); + if (device->hidden->audio_fd < 0) { + return SDL_SetError("Couldn't open %s: %s", device->name, strerror(errno)); } - /* Make the file descriptor use blocking i/o with fcntl() */ + // Make the file descriptor use blocking i/o with fcntl() { - long ctlflags; - ctlflags = fcntl(_this->hidden->audio_fd, F_GETFL); - ctlflags &= ~O_NONBLOCK; - if (fcntl(_this->hidden->audio_fd, F_SETFL, ctlflags) < 0) { + const long ctlflags = fcntl(device->hidden->audio_fd, F_GETFL) & ~O_NONBLOCK; + if (fcntl(device->hidden->audio_fd, F_SETFL, ctlflags) < 0) { return SDL_SetError("Couldn't set audio blocking mode"); } } - /* Get a list of supported hardware formats */ - if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { + // Get a list of supported hardware formats + int value; + if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_GETFMTS, &value) < 0) { perror("SNDCTL_DSP_GETFMTS"); return SDL_SetError("Couldn't get audio format list"); } /* Try for a closest match on audio format */ - closefmts = SDL_ClosestAudioFormats(_this->spec.format); + int format = 0; + SDL_AudioFormat test_format; + const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format); while ((test_format = *(closefmts++)) != 0) { #ifdef DEBUG_AUDIO fprintf(stderr, "Trying format 0x%4.4x\n", test_format); @@ -134,17 +121,7 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname) format = AFMT_S16_BE; } break; -#if 0 -/* - * These formats are not used by any real life systems so they are not - * needed here. - */ - case SDL_AUDIO_S8: - if (value & AFMT_S8) { - format = AFMT_S8; - } - break; -#endif + default: continue; } @@ -153,40 +130,43 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname) if (format == 0) { return SDL_SetError("Couldn't find any hardware audio formats"); } - _this->spec.format = test_format; + device->spec.format = test_format; - /* Set the audio format */ + // Set the audio format value = format; - if ((ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || + if ((ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFMT, &value) < 0) || (value != format)) { perror("SNDCTL_DSP_SETFMT"); return SDL_SetError("Couldn't set audio format"); } - /* Set the number of channels of output */ - value = _this->spec.channels; - if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) { + // Set the number of channels of output + value = device->spec.channels; + if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_CHANNELS, &value) < 0) { perror("SNDCTL_DSP_CHANNELS"); return SDL_SetError("Cannot set the number of channels"); } - _this->spec.channels = value; + device->spec.channels = value; - /* Set the DSP frequency */ - value = _this->spec.freq; - if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { + // Set the DSP frequency + value = device->spec.freq; + if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SPEED, &value) < 0) { perror("SNDCTL_DSP_SPEED"); return SDL_SetError("Couldn't set audio frequency"); } - _this->spec.freq = value; + device->spec.freq = value; /* Calculate the final parameters for this audio specification */ - SDL_CalculateAudioSpec(&_this->spec); + SDL_UpdatedAudioDeviceFormat(device); - /* Determine the power of two of the fragment size */ - for (frag_spec = 0; (0x01U << frag_spec) < _this->spec.size; ++frag_spec) { - } - if ((0x01U << frag_spec) != _this->spec.size) { - return SDL_SetError("Fragment size must be a power of two"); + /* Determine the power of two of the fragment size + Since apps don't control this in SDL3, and this driver only accepts 8, 16 + bit formats and 1, 2, 4, 8 channels, this should always be a power of 2 already. */ + SDL_assert(SDL_powerof2(device->buffer_size) == device->buffer_size); + + int frag_spec = 0; + while ((0x01U << frag_spec) < device->buffer_size) { + frag_spec++; } frag_spec |= 0x00020000; /* two fragments, for low latency */ @@ -195,13 +175,13 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname) fprintf(stderr, "Requesting %d fragments of size %d\n", (frag_spec >> 16), 1 << (frag_spec & 0xFFFF)); #endif - if (ioctl(_this->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { + if (ioctl(device->hidden->audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag_spec) < 0) { perror("SNDCTL_DSP_SETFRAGMENT"); } #ifdef DEBUG_AUDIO { audio_buf_info info; - ioctl(_this->hidden->audio_fd, SNDCTL_DSP_GETOSPACE, &info); + ioctl(device->hidden->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); @@ -210,44 +190,64 @@ static int DSP_OpenDevice(SDL_AudioDevice *_this, const char *devname) #endif /* Allocate mixing buffer */ - if (!iscapture) { - _this->hidden->mixlen = _this->spec.size; - _this->hidden->mixbuf = (Uint8 *)SDL_malloc(_this->hidden->mixlen); - if (_this->hidden->mixbuf == NULL) { + if (!device->iscapture) { + device->hidden->mixbuf = (Uint8 *)SDL_malloc(device->buffer_size); + if (device->hidden->mixbuf == NULL) { return SDL_OutOfMemory(); } - SDL_memset(_this->hidden->mixbuf, _this->spec.silence, _this->spec.size); + SDL_memset(device->hidden->mixbuf, device->silence_value, device->buffer_size); } - /* We're ready to rock and roll. :-) */ - return 0; + return 0; // We're ready to rock and roll. :-) } -static void DSP_PlayDevice(SDL_AudioDevice *_this) +static void DSP_WaitDevice(SDL_AudioDevice *device) { - struct SDL_PrivateAudioData *h = _this->hidden; - if (write(h->audio_fd, h->mixbuf, h->mixlen) == -1) { + const unsigned long ioctlreq = device->iscapture ? SNDCTL_DSP_GETISPACE : SNDCTL_DSP_GETOSPACE; + struct SDL_PrivateAudioData *h = device->hidden; + + while (!SDL_AtomicGet(&device->shutdown)) { + audio_buf_info info; + const int rc = ioctl(h->audio_fd, ioctlreq, &info); + if ((rc < 0) && (errno != EAGAIN)) { // Hmm, not much we can do - abort + fprintf(stderr, "dsp WaitDevice ioctl failed (unrecoverable): %s\n", strerror(errno)); + SDL_AudioDeviceDisconnected(device); + return; + } else if (info.bytes < device->buffer_size) { +//SDL_Log("DSP NEED=%d HAVE=%d", (int) device->buffer_size, (int) info.bytes); + SDL_Delay(10); + } else { +//SDL_Log("DSP NEED=%d HAVE=%d", (int) device->buffer_size, (int) info.bytes); + break; /* ready to go! */ + } + } +} + +static void DSP_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) +{ + struct SDL_PrivateAudioData *h = device->hidden; + if (write(h->audio_fd, buffer, buflen) == -1) { perror("Audio write"); - SDL_OpenedAudioDeviceDisconnected(_this); + SDL_AudioDeviceDisconnected(device); } #ifdef DEBUG_AUDIO fprintf(stderr, "Wrote %d bytes of audio data\n", h->mixlen); #endif } -static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *_this) +static Uint8 *DSP_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) { - return _this->hidden->mixbuf; + return device->hidden->mixbuf; } -static int DSP_CaptureFromDevice(SDL_AudioDevice *_this, void *buffer, int buflen) +static int DSP_CaptureFromDevice(SDL_AudioDevice *device, void *buffer, int buflen) { - return (int)read(_this->hidden->audio_fd, buffer, buflen); + return (int)read(device->hidden->audio_fd, buffer, buflen); } -static void DSP_FlushCapture(SDL_AudioDevice *_this) +static void DSP_FlushCapture(SDL_AudioDevice *device) { - struct SDL_PrivateAudioData *h = _this->hidden; + struct SDL_PrivateAudioData *h = device->hidden; audio_buf_info info; if (ioctl(h->audio_fd, SNDCTL_DSP_GETISPACE, &info) == 0) { while (info.bytes > 0) { @@ -263,17 +263,17 @@ static void DSP_FlushCapture(SDL_AudioDevice *_this) } static SDL_bool InitTimeDevicesExist = SDL_FALSE; -static int look_for_devices_test(int fd) +static SDL_bool look_for_devices_test(int fd) { InitTimeDevicesExist = SDL_TRUE; /* note that _something_ exists. */ /* Don't add to the device list, we're just seeing if any devices exist. */ - return 0; + return SDL_FALSE; } static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl) { InitTimeDevicesExist = SDL_FALSE; - SDL_EnumUnixAudioDevices(0, look_for_devices_test); + SDL_EnumUnixAudioDevices(SDL_FALSE, look_for_devices_test); if (!InitTimeDevicesExist) { SDL_SetError("dsp: No such audio device"); return SDL_FALSE; /* maybe try a different backend. */ @@ -282,9 +282,11 @@ static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl) /* Set the function pointers */ impl->DetectDevices = DSP_DetectDevices; impl->OpenDevice = DSP_OpenDevice; + impl->WaitDevice = DSP_WaitDevice; impl->PlayDevice = DSP_PlayDevice; impl->GetDeviceBuf = DSP_GetDeviceBuf; impl->CloseDevice = DSP_CloseDevice; + impl->WaitCaptureDevice = DSP_WaitDevice; impl->CaptureFromDevice = DSP_CaptureFromDevice; impl->FlushCapture = DSP_FlushCapture; @@ -295,7 +297,7 @@ static SDL_bool DSP_Init(SDL_AudioDriverImpl *impl) } AudioBootStrap DSP_bootstrap = { - "dsp", "OSS /dev/dsp standard audio", DSP_Init, SDL_FALSE + "dsp", "Open Sound System (/dev/dsp)", DSP_Init, SDL_FALSE }; #endif /* SDL_AUDIO_DRIVER_OSS */ diff --git a/src/audio/dsp/SDL_dspaudio.h b/src/audio/dsp/SDL_dspaudio.h index e5bf29eddc..abe0c347df 100644 --- a/src/audio/dsp/SDL_dspaudio.h +++ b/src/audio/dsp/SDL_dspaudio.h @@ -32,8 +32,6 @@ struct SDL_PrivateAudioData /* Raw mixing buffer */ Uint8 *mixbuf; - int mixlen; }; -#define FUDGE_TICKS 10 /* The scheduler overhead ticks per frame */ #endif /* SDL_dspaudio_h_ */