mirror of
				https://github.com/Ryujinx/SDL.git
				synced 2025-11-04 12:44:56 +00:00 
			
		
		
		
	audio: Fix locking in backends that manage their own callback threads.
Otherwise you might get a race where an app pauses the device, but the audio callback still manages to run after the pause is in place.
This commit is contained in:
		
							parent
							
								
									a95f5a792c
								
							
						
					
					
						commit
						dc62fec5e9
					
				| 
						 | 
					@ -522,8 +522,12 @@ static void
 | 
				
			||||||
outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
 | 
					outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
 | 
					    SDL_AudioDevice *this = (SDL_AudioDevice *) inUserData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SDL_LockMutex(this->mixer_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (SDL_AtomicGet(&this->hidden->shutdown)) {
 | 
					    if (SDL_AtomicGet(&this->hidden->shutdown)) {
 | 
				
			||||||
        return;  /* don't do anything. */
 | 
					        SDL_UnlockMutex(this->mixer_lock);
 | 
				
			||||||
 | 
					        return;  /* don't do anything, since we don't even want to enqueue this buffer again. */
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
 | 
					    if (!SDL_AtomicGet(&this->enabled) || SDL_AtomicGet(&this->paused)) {
 | 
				
			||||||
| 
						 | 
					@ -536,10 +540,8 @@ outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffe
 | 
				
			||||||
        while (remaining > 0) {
 | 
					        while (remaining > 0) {
 | 
				
			||||||
            if (SDL_AudioStreamAvailable(this->stream) == 0) {
 | 
					            if (SDL_AudioStreamAvailable(this->stream) == 0) {
 | 
				
			||||||
                /* Generate the data */
 | 
					                /* Generate the data */
 | 
				
			||||||
                SDL_LockMutex(this->mixer_lock);
 | 
					 | 
				
			||||||
                (*this->callbackspec.callback)(this->callbackspec.userdata,
 | 
					                (*this->callbackspec.callback)(this->callbackspec.userdata,
 | 
				
			||||||
                                               this->hidden->buffer, this->hidden->bufferSize);
 | 
					                                               this->hidden->buffer, this->hidden->bufferSize);
 | 
				
			||||||
                SDL_UnlockMutex(this->mixer_lock);
 | 
					 | 
				
			||||||
                this->hidden->bufferOffset = 0;
 | 
					                this->hidden->bufferOffset = 0;
 | 
				
			||||||
                SDL_AudioStreamPut(this->stream, this->hidden->buffer, this->hidden->bufferSize);
 | 
					                SDL_AudioStreamPut(this->stream, this->hidden->buffer, this->hidden->bufferSize);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -565,10 +567,8 @@ outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffe
 | 
				
			||||||
            UInt32 len;
 | 
					            UInt32 len;
 | 
				
			||||||
            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
 | 
					            if (this->hidden->bufferOffset >= this->hidden->bufferSize) {
 | 
				
			||||||
                /* Generate the data */
 | 
					                /* Generate the data */
 | 
				
			||||||
                SDL_LockMutex(this->mixer_lock);
 | 
					 | 
				
			||||||
                (*this->callbackspec.callback)(this->callbackspec.userdata,
 | 
					                (*this->callbackspec.callback)(this->callbackspec.userdata,
 | 
				
			||||||
                            this->hidden->buffer, this->hidden->bufferSize);
 | 
					                            this->hidden->buffer, this->hidden->bufferSize);
 | 
				
			||||||
                SDL_UnlockMutex(this->mixer_lock);
 | 
					 | 
				
			||||||
                this->hidden->bufferOffset = 0;
 | 
					                this->hidden->bufferOffset = 0;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -587,6 +587,8 @@ outputCallback(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffe
 | 
				
			||||||
    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
 | 
					    AudioQueueEnqueueBuffer(this->hidden->audioQueue, inBuffer, 0, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
 | 
					    inBuffer->mAudioDataByteSize = inBuffer->mAudioDataBytesCapacity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SDL_UnlockMutex(this->mixer_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,6 +28,11 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <emscripten/emscripten.h>
 | 
					#include <emscripten/emscripten.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* !!! FIXME: this currently expects that the audio callback runs in the main thread,
 | 
				
			||||||
 | 
					   !!! FIXME:  in intervals when the application isn't running, but that may not be
 | 
				
			||||||
 | 
					   !!! FIXME:  true always once pthread support becomes widespread. Revisit this code
 | 
				
			||||||
 | 
					   !!! FIXME:  at some point and see what needs to be done for that! */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
FeedAudioDevice(_THIS, const void *buf, const int buflen)
 | 
					FeedAudioDevice(_THIS, const void *buf, const int buflen)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,39 +49,40 @@ FillSound(void *device, void *stream, size_t len,
 | 
				
			||||||
    SDL_AudioDevice *audio = (SDL_AudioDevice *) device;
 | 
					    SDL_AudioDevice *audio = (SDL_AudioDevice *) device;
 | 
				
			||||||
    SDL_AudioCallback callback = audio->callbackspec.callback;
 | 
					    SDL_AudioCallback callback = audio->callbackspec.callback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SDL_LockMutex(audio->mixer_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Only do something if audio is enabled */
 | 
					    /* Only do something if audio is enabled */
 | 
				
			||||||
    if (!SDL_AtomicGet(&audio->enabled) || SDL_AtomicGet(&audio->paused)) {
 | 
					    if (!SDL_AtomicGet(&audio->enabled) || SDL_AtomicGet(&audio->paused)) {
 | 
				
			||||||
        if (audio->stream) {
 | 
					        if (audio->stream) {
 | 
				
			||||||
            SDL_AudioStreamClear(audio->stream);
 | 
					            SDL_AudioStreamClear(audio->stream);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        SDL_memset(stream, audio->spec.silence, len);
 | 
					        SDL_memset(stream, audio->spec.silence, len);
 | 
				
			||||||
        return;
 | 
					    } else {
 | 
				
			||||||
    }
 | 
					        SDL_assert(audio->spec.size == len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SDL_assert(audio->spec.size == len);
 | 
					        if (audio->stream == NULL) {  /* no conversion necessary. */
 | 
				
			||||||
 | 
					            callback(audio->callbackspec.userdata, (Uint8 *) stream, len);
 | 
				
			||||||
 | 
					        } else {  /* streaming/converting */
 | 
				
			||||||
 | 
					            const int stream_len = audio->callbackspec.size;
 | 
				
			||||||
 | 
					            const int ilen = (int) len;
 | 
				
			||||||
 | 
					            while (SDL_AudioStreamAvailable(audio->stream) < ilen) {
 | 
				
			||||||
 | 
					                callback(audio->callbackspec.userdata, audio->work_buffer, stream_len);
 | 
				
			||||||
 | 
					                if (SDL_AudioStreamPut(audio->stream, audio->work_buffer, stream_len) == -1) {
 | 
				
			||||||
 | 
					                    SDL_AudioStreamClear(audio->stream);
 | 
				
			||||||
 | 
					                    SDL_AtomicSet(&audio->enabled, 0);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (audio->stream == NULL) {  /* no conversion necessary. */
 | 
					            const int got = SDL_AudioStreamGet(audio->stream, stream, ilen);
 | 
				
			||||||
        SDL_LockMutex(audio->mixer_lock);
 | 
					            SDL_assert((got < 0) || (got == ilen));
 | 
				
			||||||
        callback(audio->callbackspec.userdata, (Uint8 *) stream, len);
 | 
					            if (got != ilen) {
 | 
				
			||||||
        SDL_UnlockMutex(audio->mixer_lock);
 | 
					                SDL_memset(stream, audio->spec.silence, len);
 | 
				
			||||||
    } else {  /* streaming/converting */
 | 
					 | 
				
			||||||
        const int stream_len = audio->callbackspec.size;
 | 
					 | 
				
			||||||
        const int ilen = (int) len;
 | 
					 | 
				
			||||||
        while (SDL_AudioStreamAvailable(audio->stream) < ilen) {
 | 
					 | 
				
			||||||
            callback(audio->callbackspec.userdata, audio->work_buffer, stream_len);
 | 
					 | 
				
			||||||
            if (SDL_AudioStreamPut(audio->stream, audio->work_buffer, stream_len) == -1) {
 | 
					 | 
				
			||||||
                SDL_AudioStreamClear(audio->stream);
 | 
					 | 
				
			||||||
                SDL_AtomicSet(&audio->enabled, 0);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        const int got = SDL_AudioStreamGet(audio->stream, stream, ilen);
 | 
					 | 
				
			||||||
        SDL_assert((got < 0) || (got == ilen));
 | 
					 | 
				
			||||||
        if (got != ilen) {
 | 
					 | 
				
			||||||
            SDL_memset(stream, audio->spec.silence, len);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SDL_UnlockMutex(audio->mixer_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void
 | 
					static void
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,8 +49,8 @@ static void nacl_audio_callback(void* stream, uint32_t buffer_size, PP_TimeDelta
 | 
				
			||||||
    const int len = (int) buffer_size;
 | 
					    const int len = (int) buffer_size;
 | 
				
			||||||
    SDL_AudioDevice* _this = (SDL_AudioDevice*) data;
 | 
					    SDL_AudioDevice* _this = (SDL_AudioDevice*) data;
 | 
				
			||||||
    SDL_AudioCallback callback = _this->callbackspec.callback;
 | 
					    SDL_AudioCallback callback = _this->callbackspec.callback;
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    SDL_LockMutex(private->mutex);  /* !!! FIXME: is this mutex necessary? */
 | 
					    SDL_LockMutex(_this->mixer_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /* Only do something if audio is enabled */
 | 
					    /* Only do something if audio is enabled */
 | 
				
			||||||
    if (!SDL_AtomicGet(&_this->enabled) || SDL_AtomicGet(&_this->paused)) {
 | 
					    if (!SDL_AtomicGet(&_this->enabled) || SDL_AtomicGet(&_this->paused)) {
 | 
				
			||||||
| 
						 | 
					@ -58,34 +58,31 @@ static void nacl_audio_callback(void* stream, uint32_t buffer_size, PP_TimeDelta
 | 
				
			||||||
            SDL_AudioStreamClear(_this->stream);
 | 
					            SDL_AudioStreamClear(_this->stream);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        SDL_memset(stream, _this->spec.silence, len);
 | 
					        SDL_memset(stream, _this->spec.silence, len);
 | 
				
			||||||
        return;
 | 
					    } else {
 | 
				
			||||||
    }
 | 
					        SDL_assert(_this->spec.size == len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SDL_assert(_this->spec.size == len);
 | 
					        if (_this->stream == NULL) {  /* no conversion necessary. */
 | 
				
			||||||
 | 
					            callback(_this->callbackspec.userdata, stream, len);
 | 
				
			||||||
 | 
					        } else {  /* streaming/converting */
 | 
				
			||||||
 | 
					            const int stream_len = _this->callbackspec.size;
 | 
				
			||||||
 | 
					            while (SDL_AudioStreamAvailable(_this->stream) < len) {
 | 
				
			||||||
 | 
					                callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
 | 
				
			||||||
 | 
					                if (SDL_AudioStreamPut(_this->stream, _this->work_buffer, stream_len) == -1) {
 | 
				
			||||||
 | 
					                    SDL_AudioStreamClear(_this->stream);
 | 
				
			||||||
 | 
					                    SDL_AtomicSet(&_this->enabled, 0);
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (_this->stream == NULL) {  /* no conversion necessary. */
 | 
					            const int got = SDL_AudioStreamGet(_this->stream, stream, len);
 | 
				
			||||||
        SDL_LockMutex(_this->mixer_lock);
 | 
					            SDL_assert((got < 0) || (got == len));
 | 
				
			||||||
        callback(_this->callbackspec.userdata, stream, len);
 | 
					            if (got != len) {
 | 
				
			||||||
        SDL_UnlockMutex(_this->mixer_lock);
 | 
					                SDL_memset(stream, _this->spec.silence, len);
 | 
				
			||||||
    } else {  /* streaming/converting */
 | 
					 | 
				
			||||||
        const int stream_len = _this->callbackspec.size;
 | 
					 | 
				
			||||||
        while (SDL_AudioStreamAvailable(_this->stream) < len) {
 | 
					 | 
				
			||||||
            callback(_this->callbackspec.userdata, _this->work_buffer, stream_len);
 | 
					 | 
				
			||||||
            if (SDL_AudioStreamPut(_this->stream, _this->work_buffer, stream_len) == -1) {
 | 
					 | 
				
			||||||
                SDL_AudioStreamClear(_this->stream);
 | 
					 | 
				
			||||||
                SDL_AtomicSet(&_this->enabled, 0);
 | 
					 | 
				
			||||||
                break;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        const int got = SDL_AudioStreamGet(_this->stream, stream, len);
 | 
					 | 
				
			||||||
        SDL_assert((got < 0) || (got == len));
 | 
					 | 
				
			||||||
        if (got != len) {
 | 
					 | 
				
			||||||
            SDL_memset(stream, _this->spec.silence, len);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SDL_UnlockMutex(private->mutex);
 | 
					    SDL_UnlockMutex(_this->mixer_lock);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void NACLAUDIO_CloseDevice(SDL_AudioDevice *device) {
 | 
					static void NACLAUDIO_CloseDevice(SDL_AudioDevice *device) {
 | 
				
			||||||
| 
						 | 
					@ -94,7 +91,6 @@ static void NACLAUDIO_CloseDevice(SDL_AudioDevice *device) {
 | 
				
			||||||
    SDL_PrivateAudioData *hidden = (SDL_PrivateAudioData *) device->hidden;
 | 
					    SDL_PrivateAudioData *hidden = (SDL_PrivateAudioData *) device->hidden;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    ppb_audio->StopPlayback(hidden->audio);
 | 
					    ppb_audio->StopPlayback(hidden->audio);
 | 
				
			||||||
    SDL_DestroyMutex(hidden->mutex);
 | 
					 | 
				
			||||||
    core->ReleaseResource(hidden->audio);
 | 
					    core->ReleaseResource(hidden->audio);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,7 +105,6 @@ NACLAUDIO_OpenDevice(_THIS, const char *devname) {
 | 
				
			||||||
        return SDL_OutOfMemory();
 | 
					        return SDL_OutOfMemory();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    private->mutex = SDL_CreateMutex();
 | 
					 | 
				
			||||||
    _this->spec.freq = 44100;
 | 
					    _this->spec.freq = 44100;
 | 
				
			||||||
    _this->spec.format = AUDIO_S16LSB;
 | 
					    _this->spec.format = AUDIO_S16LSB;
 | 
				
			||||||
    _this->spec.channels = 2;
 | 
					    _this->spec.channels = 2;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,8 +34,7 @@
 | 
				
			||||||
#define private _this->hidden
 | 
					#define private _this->hidden
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef struct SDL_PrivateAudioData {
 | 
					typedef struct SDL_PrivateAudioData {
 | 
				
			||||||
  SDL_mutex* mutex;
 | 
					    PP_Resource audio;
 | 
				
			||||||
  PP_Resource audio;
 | 
					 | 
				
			||||||
} SDL_PrivateAudioData;
 | 
					} SDL_PrivateAudioData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif /* SDL_naclaudio_h_ */
 | 
					#endif /* SDL_naclaudio_h_ */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -910,6 +910,7 @@ output_callback(void *data)
 | 
				
			||||||
     * and run the callback with the work buffer to keep the callback
 | 
					     * and run the callback with the work buffer to keep the callback
 | 
				
			||||||
     * firing regularly in case the audio is being used as a timer.
 | 
					     * firing regularly in case the audio is being used as a timer.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					    SDL_LockMutex(this->mixer_lock);
 | 
				
			||||||
    if (!SDL_AtomicGet(&this->paused)) {
 | 
					    if (!SDL_AtomicGet(&this->paused)) {
 | 
				
			||||||
        if (SDL_AtomicGet(&this->enabled)) {
 | 
					        if (SDL_AtomicGet(&this->enabled)) {
 | 
				
			||||||
            dst = spa_buf->datas[0].data;
 | 
					            dst = spa_buf->datas[0].data;
 | 
				
			||||||
| 
						 | 
					@ -919,18 +920,13 @@ output_callback(void *data)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!this->stream) {
 | 
					        if (!this->stream) {
 | 
				
			||||||
            SDL_LockMutex(this->mixer_lock);
 | 
					 | 
				
			||||||
            this->callbackspec.callback(this->callbackspec.userdata, dst, this->callbackspec.size);
 | 
					            this->callbackspec.callback(this->callbackspec.userdata, dst, this->callbackspec.size);
 | 
				
			||||||
            SDL_UnlockMutex(this->mixer_lock);
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            int got;
 | 
					            int got;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /* Fire the callback until we have enough to fill a buffer */
 | 
					            /* Fire the callback until we have enough to fill a buffer */
 | 
				
			||||||
            while (SDL_AudioStreamAvailable(this->stream) < this->spec.size) {
 | 
					            while (SDL_AudioStreamAvailable(this->stream) < this->spec.size) {
 | 
				
			||||||
                SDL_LockMutex(this->mixer_lock);
 | 
					 | 
				
			||||||
                this->callbackspec.callback(this->callbackspec.userdata, this->work_buffer, this->callbackspec.size);
 | 
					                this->callbackspec.callback(this->callbackspec.userdata, this->work_buffer, this->callbackspec.size);
 | 
				
			||||||
                SDL_UnlockMutex(this->mixer_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                SDL_AudioStreamPut(this->stream, this->work_buffer, this->callbackspec.size);
 | 
					                SDL_AudioStreamPut(this->stream, this->work_buffer, this->callbackspec.size);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -940,6 +936,7 @@ output_callback(void *data)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        SDL_memset(spa_buf->datas[0].data, this->spec.silence, this->spec.size);
 | 
					        SDL_memset(spa_buf->datas[0].data, this->spec.silence, this->spec.size);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    SDL_UnlockMutex(this->mixer_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    spa_buf->datas[0].chunk->offset = 0;
 | 
					    spa_buf->datas[0].chunk->offset = 0;
 | 
				
			||||||
    spa_buf->datas[0].chunk->stride = this->hidden->stride;
 | 
					    spa_buf->datas[0].chunk->stride = this->hidden->stride;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue