Files
mcpe/platforms/android/SoundSystemSL.cpp
iProgramInCpp dafc28e01d * Android Native: Add SoundSystemSL!
This gives the native Android build sound support!
2023-12-23 00:51:42 +02:00

242 lines
6.0 KiB
C++

/********************************************************************
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<SLObjectItf> 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)
{
}