/******************************************************************** Minecraft: Pocket Edition - Decompilation Project Copyright (C) 2023 iProgramInCpp The following code is licensed under the BSD 1 clause license. SPDX-License-Identifier: BSD-1-Clause ********************************************************************/ #include "SoundSystemSL.hpp" #include "common/Utils.hpp" #define C_MAX_SOUNDS 4 // TODO: Use other types of mutexes later. SLObjectItf SoundSystemSL::objEngine; std::vector SoundSystemSL::toRemove; pthread_mutex_t SoundSystemSL::toRemoveMutex; SoundSystemSL::SoundSystemSL() { m_listenerX = 0.0f; m_listenerY = 0.0f; m_listenerZ = 0.0f; m_soundCount = 0; m_3dLocationItf = nullptr; m_bAvailable = true; init(); } SoundSystemSL::~SoundSystemSL() { pthread_mutex_unlock(&toRemoveMutex); for (SLSoundList::iterator it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { (**it)->Destroy(*it); } (*m_slOutputMix)->Destroy(m_slOutputMix); if (objEngine) { (*objEngine)->Destroy(objEngine); objEngine = nullptr; } } void SoundSystemSL::init() { toRemove.clear(); // some weird stuff if (objEngine) (*objEngine)->Destroy(objEngine); SLEngineOption option; option.feature = SL_ENGINEOPTION_THREADSAFE; option.data = true; checkErr(slCreateEngine(&objEngine, 1, &option, 0, nullptr, nullptr)); if (checkErr((*objEngine)->Realize(objEngine, false))) { m_bAvailable = false; return; } checkErr((*objEngine)->GetInterface(objEngine, SL_IID_ENGINE, &m_slEngine)); checkErr((*m_slEngine)->CreateOutputMix(m_slEngine, &m_slOutputMix, 0, nullptr, nullptr)); checkErr((*m_slOutputMix)->Realize(m_slOutputMix, false)); } bool SoundSystemSL::checkErr(SLresult res) { if (res == SL_RESULT_SUCCESS) return false; LOG_E("OpenSL error: %d\n", res); return true; } void SoundSystemSL::removePlayer(SLAndroidSimpleBufferQueueItf caller, void* context) { pthread_mutex_lock(&toRemoveMutex); toRemove.push_back((SLObjectItf) context); pthread_mutex_unlock(&toRemoveMutex); } void SoundSystemSL::removeStoppedSounds() { pthread_mutex_lock(&toRemoveMutex); m_tempToRemove = toRemove; toRemove.clear(); pthread_mutex_unlock(&toRemoveMutex); for (int i = 0; i < m_tempToRemove.size(); i++) { for (SLSoundList::iterator it = m_playingSounds.begin(); it != m_playingSounds.end(); ++it) { if (*it == m_tempToRemove[i]) { m_playingSounds.erase(it); break; } } (*m_tempToRemove[i])->Destroy(m_tempToRemove[i]); m_soundCount--; } } void SoundSystemSL::setListenerPos(float x, float y, float z) { if (!m_3dLocationItf) { m_listenerX = x; m_listenerY = y; m_listenerZ = z; return; } SLVec3D vec; vec.x = int(1000.0f * x); vec.y = int(1000.0f * y); vec.z = int(1000.0f * z); checkErr((*m_3dLocationItf)->SetLocationCartesian( m_3dLocationItf, &vec )); } void SoundSystemSL::setListenerAngle(float yaw, float pitch) { if (!m_3dLocationItf) return; checkErr((*m_3dLocationItf)->SetOrientationAngles( m_3dLocationItf, int(180000.0f * yaw), 0, 0 )); } void SoundSystemSL::playAt(const SoundDesc &sound, float x, float y, float z, float volume, float pitch) { removeStoppedSounds(); if (m_soundCount >= C_MAX_SOUNDS) return; SLDataSource dataSource; SLDataSink dataSink; SLDataLocator_AndroidSimpleBufferQueue dataSourceLocator; SLDataFormat_PCM dataSourceFormat; SLDataLocator_OutputMix dataSinkLocator; SLObjectItf pAudioPlayer; SLPlayItf pPlayItf; SLVolumeItf pVolumeItf; SLmillibel maxVolume; SLAndroidSimpleBufferQueueItf pBufferQueueItf; static SLInterfaceID static_iid_array[2]; static SLboolean static_bool_array[2]; dataSource.pLocator = &dataSourceLocator; dataSource.pFormat = &dataSourceFormat; dataSink.pLocator = &dataSinkLocator; dataSink.pFormat = nullptr; // TODO: Nicer initialization based on initializer lists :) dataSourceLocator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; dataSourceLocator.numBuffers = 2; dataSinkLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; dataSinkLocator.outputMix = m_slOutputMix; dataSourceFormat.numChannels = sound.m_header.m_channels; dataSourceFormat.channelMask = sound.m_header.m_channels == 1 ? 4 : 3; dataSourceFormat.samplesPerSec = 1000 * sound.m_header.m_sample_rate; dataSourceFormat.formatType = SL_DATAFORMAT_PCM; dataSourceFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; dataSourceFormat.bitsPerSample = 8 * sound.m_header.m_bytes_per_sample; dataSourceFormat.containerSize = 8 * sound.m_header.m_bytes_per_sample; static_iid_array[0] = SL_IID_BUFFERQUEUE; static_iid_array[1] = SL_IID_VOLUME; static_bool_array[0] = true; static_bool_array[1] = true; checkErr((*m_slEngine)->CreateAudioPlayer( m_slEngine, &pAudioPlayer, &dataSource, &dataSink, 2, static_iid_array, static_bool_array )); checkErr((*pAudioPlayer)->Realize(pAudioPlayer, false)); checkErr((*pAudioPlayer)->GetInterface(pAudioPlayer, SL_IID_PLAY, &pPlayItf)); checkErr((*pAudioPlayer)->GetInterface(pAudioPlayer, SL_IID_VOLUME, &pVolumeItf)); checkErr((*pVolumeItf)->GetMaxVolumeLevel(pVolumeItf, &maxVolume)); SLmillibel remappedVolume = int(float(maxVolume) + (1.0f - volume) * -2000.0f); LOG_I("min: %d, max: %d, current: %d (%f)\n", SL_MILLIBEL_MIN, maxVolume, remappedVolume, volume); checkErr((*pVolumeItf)->SetVolumeLevel(pVolumeItf, remappedVolume)); checkErr((*pAudioPlayer)->GetInterface(pAudioPlayer, SL_IID_BUFFERQUEUE, &pBufferQueueItf)); checkErr((*pBufferQueueItf)->RegisterCallback(pBufferQueueItf, removePlayer, (void*) pAudioPlayer)); checkErr((*pBufferQueueItf)->Enqueue(pBufferQueueItf, sound.m_pData, sound.field_4)); checkErr((*pPlayItf)->SetPlayState(pPlayItf, SL_PLAYSTATE_PLAYING)); m_playingSounds.push_back(pAudioPlayer); m_soundCount++; } // Confirmed Stubs void SoundSystemSL::destroy() { } void SoundSystemSL::load(const std::string &sound) { } void SoundSystemSL::play(const std::string &sound) { } void SoundSystemSL::pause(const std::string &sound) { } void SoundSystemSL::stop(const std::string &sound) { }