Refactor audio handling with range checks, buffer threshold, and lock fixes

- Added range checks for handle to avoid invalid index access in AudioOutOutput, AudioOutSetVolume, and AudioOutGetStatus.

- Added a constant AUDIO_STREAM_BUFFER_THRESHOLD for the buffer threshold (was previously a magic number).

- Set the freq parameter correctly in the SDL_AudioSpec structure in AudioOutOpen.

- Fixed locking issues in AudioOutOutput to avoid unlocking before it's locked.

- Removed tab spaces to fix format clang error
This commit is contained in:
Mikasa-san 2024-10-02 16:58:57 +04:00 committed by GitHub
parent d63b7fcc1d
commit 4a6922bcce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -17,165 +17,165 @@ namespace Audio {
constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold constexpr int AUDIO_STREAM_BUFFER_THRESHOLD = 65536; // Define constant for buffer threshold
int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq, int SDLAudio::AudioOutOpen(int type, u32 samples_num, u32 freq,
Libraries::AudioOut::OrbisAudioOutParamFormat format) { Libraries::AudioOut::OrbisAudioOutParamFormat format) {
using Libraries::AudioOut::OrbisAudioOutParamFormat; using Libraries::AudioOut::OrbisAudioOutParamFormat;
std::unique_lock lock{m_mutex}; std::unique_lock lock{m_mutex};
for (int id = 0; id < portsOut.size(); id++) { for (int id = 0; id < portsOut.size(); id++) {
auto& port = portsOut[id]; auto& port = portsOut[id];
if (!port.isOpen) { if (!port.isOpen) {
port.isOpen = true; port.isOpen = true;
port.type = type; port.type = type;
port.samples_num = samples_num; port.samples_num = samples_num;
port.freq = freq; port.freq = freq;
port.format = format; port.format = format;
SDL_AudioFormat sampleFormat; SDL_AudioFormat sampleFormat;
switch (format) { switch (format) {
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO: case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_MONO:
sampleFormat = SDL_AUDIO_S16; sampleFormat = SDL_AUDIO_S16;
port.channels_num = 1; port.channels_num = 1;
port.sample_size = 2; port.sample_size = 2;
break; break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO: case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_MONO:
sampleFormat = SDL_AUDIO_F32; sampleFormat = SDL_AUDIO_F32;
port.channels_num = 1; port.channels_num = 1;
port.sample_size = 4; port.sample_size = 4;
break; break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO: case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_STEREO:
sampleFormat = SDL_AUDIO_S16; sampleFormat = SDL_AUDIO_S16;
port.channels_num = 2; port.channels_num = 2;
port.sample_size = 2; port.sample_size = 2;
break; break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO: case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_STEREO:
sampleFormat = SDL_AUDIO_F32; sampleFormat = SDL_AUDIO_F32;
port.channels_num = 2; port.channels_num = 2;
port.sample_size = 4; port.sample_size = 4;
break; break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH: case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH:
sampleFormat = SDL_AUDIO_S16; sampleFormat = SDL_AUDIO_S16;
port.channels_num = 8; port.channels_num = 8;
port.sample_size = 2; port.sample_size = 2;
break; break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH: case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH:
sampleFormat = SDL_AUDIO_F32; sampleFormat = SDL_AUDIO_F32;
port.channels_num = 8; port.channels_num = 8;
port.sample_size = 4; port.sample_size = 4;
break; break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD: case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD:
sampleFormat = SDL_AUDIO_S16; sampleFormat = SDL_AUDIO_S16;
port.channels_num = 8; port.channels_num = 8;
port.sample_size = 2; port.sample_size = 2;
break; break;
case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD: case OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD:
sampleFormat = SDL_AUDIO_F32; sampleFormat = SDL_AUDIO_F32;
port.channels_num = 8; port.channels_num = 8;
port.sample_size = 4; port.sample_size = 4;
break; break;
default: default:
UNREACHABLE_MSG("Unknown format"); UNREACHABLE_MSG("Unknown format");
} }
for (int i = 0; i < port.channels_num; i++) { for (int i = 0; i < port.channels_num; i++) {
port.volume[i] = Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB; port.volume[i] = Libraries::AudioOut::SCE_AUDIO_OUT_VOLUME_0DB;
} }
SDL_AudioSpec fmt; SDL_AudioSpec fmt;
SDL_zero(fmt); SDL_zero(fmt);
fmt.format = sampleFormat; fmt.format = sampleFormat;
fmt.channels = port.channels_num; fmt.channels = port.channels_num;
fmt.freq = freq; // Set frequency from the argument fmt.freq = freq; // Set frequency from the argument
port.stream = port.stream =
SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL); SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &fmt, NULL, NULL);
SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port.stream)); SDL_ResumeAudioDevice(SDL_GetAudioStreamDevice(port.stream));
return id + 1; return id + 1;
} }
} }
return -1; // all ports are used return -1; // all ports are used
} }
s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) { s32 SDLAudio::AudioOutOutput(s32 handle, const void* ptr) {
if (handle < 1 || handle > portsOut.size()) { // Add handle range check if (handle < 1 || handle > portsOut.size()) { // Add handle range check
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
std::shared_lock lock{m_mutex}; if (ptr == nullptr) {
auto& port = portsOut[handle - 1]; return 0; // Nothing to output
if (!port.isOpen) { }
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
if (ptr == nullptr) {
return 0;
}
// TODO mixing channels std::shared_lock lock{m_mutex};
SDL_bool result = SDL_PutAudioStreamData( auto& port = portsOut[handle - 1];
port.stream, ptr, port.samples_num * port.sample_size * port.channels_num); if (!port.isOpen) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
}
lock.unlock(); // Unlock after the critical section const size_t data_size = port.samples_num * port.sample_size * port.channels_num;
// TODO find a correct value 8192 is estimated SDL_bool result = SDL_PutAudioStreamData(port.stream, ptr, data_size);
while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
SDL_Delay(0);
}
return result ? ORBIS_OK : -1; lock.unlock(); // Unlock only after necessary operations
while (SDL_GetAudioStreamAvailable(port.stream) > AUDIO_STREAM_BUFFER_THRESHOLD) {
SDL_Delay(0);
}
return result ? ORBIS_OK : -1;
} }
bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) { bool SDLAudio::AudioOutSetVolume(s32 handle, s32 bitflag, s32* volume) {
if (handle < 1 || handle > portsOut.size()) { // Add handle range check if (handle < 1 || handle > portsOut.size()) { // Add handle range check
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
using Libraries::AudioOut::OrbisAudioOutParamFormat; using Libraries::AudioOut::OrbisAudioOutParamFormat;
std::shared_lock lock{m_mutex}; std::shared_lock lock{m_mutex};
auto& port = portsOut[handle - 1]; auto& port = portsOut[handle - 1];
if (!port.isOpen) { if (!port.isOpen) {
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) { for (int i = 0; i < port.channels_num; i++, bitflag >>= 1u) {
auto bit = bitflag & 0x1u; auto bit = bitflag & 0x1u;
if (bit == 1) { if (bit == 1) {
int src_index = i; int src_index = i;
if (port.format == if (port.format ==
OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD || OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_FLOAT_8CH_STD ||
port.format == OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD) { port.format == OrbisAudioOutParamFormat::ORBIS_AUDIO_OUT_PARAM_FORMAT_S16_8CH_STD) {
switch (i) { switch (i) {
case 4: case 4:
src_index = 6; src_index = 6;
break; break;
case 5: case 5:
src_index = 7; src_index = 7;
break; break;
case 6: case 6:
src_index = 4; src_index = 4;
break; break;
case 7: case 7:
src_index = 5; src_index = 5;
break; break;
default: default:
break; break;
} }
} }
port.volume[i] = volume[src_index]; port.volume[i] = volume[src_index];
} }
} }
return true; return true;
} }
bool SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) { bool SDLAudio::AudioOutGetStatus(s32 handle, int* type, int* channels_num) {
if (handle < 1 || handle > portsOut.size()) { // Add handle range check if (handle < 1 || handle > portsOut.size()) { // Add handle range check
return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT; return ORBIS_AUDIO_OUT_ERROR_INVALID_PORT;
} }
std::shared_lock lock{m_mutex}; std::shared_lock lock{m_mutex};
auto& port = portsOut[handle - 1]; auto& port = portsOut[handle - 1];
*type = port.type; *type = port.type;
*channels_num = port.channels_num; *channels_num = port.channels_num;
return true; return true;
} }
} // namespace Audio } // namespace Audio