* Android Native: Add SoundSystemSL!

This gives the native Android build sound support!
This commit is contained in:
iProgramInCpp
2023-12-23 00:51:42 +02:00
parent a3c9800bd8
commit dafc28e01d
4 changed files with 310 additions and 5 deletions

View File

@@ -5,12 +5,11 @@
The following code is licensed under the BSD 1 clause license.
SPDX-License-Identifier: BSD-1-Clause
********************************************************************/
#define WIN32_LEAN_AND_MEAN
#include <fstream>
#include <sstream>
#include "AppPlatform_android.hpp"
#include "SoundSystemSL.hpp"
#include "client/player/input/Mouse.hpp"
#include "stb_image.h"
@@ -171,9 +170,8 @@ SoundSystem* const AppPlatform_android::getSoundSystem() const
void AppPlatform_android::initSoundSystem()
{
// TODO: SoundSystemSL!
if (!m_pSoundSystem)
m_pSoundSystem = new SoundSystem();
m_pSoundSystem = new SoundSystemSL();
else
LOG_E("Trying to initialize SoundSystem more than once!");
}

View File

@@ -0,0 +1,241 @@
/********************************************************************
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)
{
}

View File

@@ -0,0 +1,65 @@
/********************************************************************
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
********************************************************************/
#pragma once
#include <vector>
#include <list>
#define USE_PTHREAD_MUTEX
#ifdef USE_PTHREAD_MUTEX
#include <pthread.h>
#else
#error TODO: Implement support for other types of mutexes, such
#error as C++11 mutexes. Although I'm pretty sure this will only
#error be included for Android. -iProgram
#endif
#include <SLES/OpenSLES.h>
#ifdef ANDROID
#include <SLES/OpenSLES_Android.h>
#endif
#include "client/sound/SoundSystem.hpp"
#include "client/sound/SoundData.hpp"
typedef std::list<SLObjectItf> SLSoundList;
class SoundSystemSL : public SoundSystem
{
public:
SoundSystemSL();
~SoundSystemSL();
void init();
void destroy();
bool checkErr(SLresult res);
void removeStoppedSounds();
void setListenerPos(float x, float y, float z) override;
void setListenerAngle(float yaw, float pitch) override;
void load(const std::string& sound) override;
void play(const std::string& sound) override;
void pause(const std::string& sound) override;
void stop(const std::string& sound) override;
void playAt(const SoundDesc& sound, float x, float y, float z, float volume, float pitch) override;
static void removePlayer(SLAndroidSimpleBufferQueueItf caller, void* context);
private:
SLSoundList m_playingSounds;
SLEngineItf m_slEngine;
SL3DLocationItf m_3dLocationItf;
SLObjectItf m_slOutputMix;
float m_listenerX;
float m_listenerY;
float m_listenerZ;
int m_soundCount;
bool m_bAvailable;
std::vector<SLObjectItf> m_tempToRemove;
static std::vector<SLObjectItf> toRemove;
static SLObjectItf objEngine;
static pthread_mutex_t toRemoveMutex;
};

View File

@@ -12,6 +12,7 @@ set(USE_NATIVE_ANDROID TRUE)
add_library(reminecraftpe SHARED
"${MC_ROOT}/platforms/android/android_native_app_glue.c"
"${MC_ROOT}/platforms/android/AppPlatform_android.cpp"
"${MC_ROOT}/platforms/android/SoundSystemSL.cpp"
"${MC_ROOT}/platforms/android/main.cpp"
)
@@ -24,7 +25,7 @@ add_subdirectory("${MC_ROOT}/thirdparty/stb_image" stb_image)
target_link_libraries(reminecraftpe stb_image)
# Extra Dependencies
target_link_libraries(reminecraftpe android)
target_link_libraries(reminecraftpe android OpenSLES)
# Check for the presence of some optional asset based features.
if(EXISTS "${MC_ROOT}/game/assets/gui/background/panorama_0.png")