This commit is contained in:
erorcun 2021-08-15 12:38:38 +01:00 committed by GitHub
commit e80b71fa5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 3596 additions and 2127 deletions

View File

@ -47,7 +47,6 @@ jobs:
mv ./bin/${{matrix.platform}}/${{matrix.buildtype}}/re3.pdb ./gamefiles/
- name: Move dynamic dependencies to gamefiles
run: |
mv ./vendor/mpg123/dist/Win64/libmpg123-0.dll ./gamefiles/
mv ./vendor/openal-soft/dist/Win64/OpenAL32.dll ./gamefiles/
- name: Upload artifact to actions
uses: actions/upload-artifact@v2

View File

@ -48,7 +48,6 @@ jobs:
- if: contains(matrix.platform, 'oal')
name: Move dynamic dependencies to gamefiles
run: |
mv ./vendor/mpg123/dist/Win32/libmpg123-0.dll ./gamefiles/
mv ./vendor/openal-soft/dist/Win32/OpenAL32.dll ./gamefiles/
- name: Upload artifact to actions
uses: actions/upload-artifact@v2

View File

@ -1,34 +0,0 @@
# - Find Miles SDK
# Find the Miles SDK header + import library
#
# MilesSDK_INCLUDE_DIR - Where to find mss.h
# MilesSDK_LIBRARIES - List of libraries when using MilesSDK.
# MilesSDK_FOUND - True if Miles SDK found.
# MilesSDK::MilesSDK - Imported library of Miles SDK
find_path(MilesSDK_INCLUDE_DIR mss.h
PATHS "${MilesSDK_DIR}"
PATH_SUFFIXES include
)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
set(_miles_sdk_libname mss64)
else()
set(_miles_sdk_libname mss32)
endif()
find_library(MilesSDK_LIBRARIES NAMES ${_miles_sdk_libname}
PATHS "${MilesSDK_DIR}"
PATH_SUFFIXES lib
)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MilesSDK DEFAULT_MSG MilesSDK_LIBRARIES MilesSDK_INCLUDE_DIR)
if(NOT TARGET MilesSDK::MilesSDK)
add_library(MilesSDK::MilesSDK UNKNOWN IMPORTED)
set_target_properties(MilesSDK::MilesSDK PROPERTIES
IMPORTED_LOCATION "${MilesSDK_LIBRARIES}"
INTERFACE_INCLUDE_DIRECTORIES "${MilesSDK_INCLUDE_DIR}"
)
endif()

View File

@ -1,38 +0,0 @@
# - Find mpg123
# Find the native mpg123 includes and library
#
# mpg123_INCLUDE_DIR - Where to find mpg123.h
# mpg123_LIBRARIES - List of libraries when using mpg123.
# mpg123_CFLAGS - Compile options to use mpg123
# mpg123_FOUND - True if mpg123 found.
# MPG123::libmpg123 - Imported library of libmpg123
find_package(PkgConfig QUIET)
if(PKG_CONFIG_FOUND)
pkg_search_module(PKG_MPG123 mpg123)
endif()
find_path(mpg123_INCLUDE_DIR mpg123.h
HINTS ${PKG_MPG123_INCLUDE_DIRS}
PATHS "${mpg123_DIR}"
PATH_SUFFIXES include
)
find_library(mpg123_LIBRARIES NAMES mpg123 mpg123-0 libmpg123-0
HINTS ${PKG_MPG123_LIBRARIES}
PATHS "${mpg123_DIR}"
PATH_SUFFIXES lib
)
set(mpg123_CFLAGS "${PKG_MPG123_CFLAGS_OTHER}" CACHE STRING "CFLAGS of mpg123")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(mpg123 DEFAULT_MSG mpg123_LIBRARIES mpg123_INCLUDE_DIR)
if(NOT TARGET MPG123::libmpg123)
add_library(__libmpg123 INTERFACE)
target_compile_options(__libmpg123 INTERFACE ${mpg123_CFLAGS})
target_include_directories(__libmpg123 INTERFACE ${mpg123_INCLUDE_DIR})
target_link_libraries(__libmpg123 INTERFACE ${mpg123_LIBRARIES})
add_library(MPG123::libmpg123 ALIAS __libmpg123)
endif()

View File

@ -305,16 +305,6 @@
<SETTING><NAME>FrameworkPath</NAME><VALUE>false</VALUE></SETTING>
<SETTING><NAME>HostFlags</NAME><VALUE>All</VALUE></SETTING>
</SETTING>
<SETTING>
<SETTING><NAME>SearchPath</NAME>
<SETTING><NAME>Path</NAME><VALUE>..\src\extras</VALUE></SETTING>
<SETTING><NAME>PathFormat</NAME><VALUE>Windows</VALUE></SETTING>
<SETTING><NAME>PathRoot</NAME><VALUE>Project</VALUE></SETTING>
</SETTING>
<SETTING><NAME>Recursive</NAME><VALUE>true</VALUE></SETTING>
<SETTING><NAME>FrameworkPath</NAME><VALUE>false</VALUE></SETTING>
<SETTING><NAME>HostFlags</NAME><VALUE>All</VALUE></SETTING>
</SETTING>
</SETTING>
<SETTING><NAME>SystemSearchPaths</NAME>
<SETTING>
@ -6554,16 +6544,6 @@
<SETTING><NAME>FrameworkPath</NAME><VALUE>false</VALUE></SETTING>
<SETTING><NAME>HostFlags</NAME><VALUE>All</VALUE></SETTING>
</SETTING>
<SETTING>
<SETTING><NAME>SearchPath</NAME>
<SETTING><NAME>Path</NAME><VALUE>..\src\extras</VALUE></SETTING>
<SETTING><NAME>PathFormat</NAME><VALUE>Windows</VALUE></SETTING>
<SETTING><NAME>PathRoot</NAME><VALUE>Project</VALUE></SETTING>
</SETTING>
<SETTING><NAME>Recursive</NAME><VALUE>true</VALUE></SETTING>
<SETTING><NAME>FrameworkPath</NAME><VALUE>false</VALUE></SETTING>
<SETTING><NAME>HostFlags</NAME><VALUE>All</VALUE></SETTING>
</SETTING>
</SETTING>
<SETTING><NAME>SystemSearchPaths</NAME>
<SETTING>
@ -13869,8 +13849,6 @@
<PATHFORMAT>Windows</PATHFORMAT>
</FILEREF>
</GROUP>
<GROUP><NAME>extras</NAME>
</GROUP>
<GROUP><NAME>math</NAME>
<FILEREF>
<TARGETNAME>Debug</TARGETNAME>

View File

@ -21,15 +21,6 @@ class Re3Conan(ConanFile):
"with_libsndfile": False,
"with_opus": False,
# "libsndfile:with_external_libs": False,
# "mpg123:flexible_resampling": False,
# "mpg123:network": False,
# "mpg123:icy": False,
# "mpg123:id3v2": False,
# "mpg123:ieeefloat": False,
# "mpg123:layer1": False,
# "mpg123:layer2": False,
# "mpg123:layer3": False,
# "mpg123:moreinfo": False,
# "sdl2:vulkan": False,
# "sdl2:opengl": True,
# "sdl2:sdl2main": True,
@ -49,11 +40,9 @@ class Re3Conan(ConanFile):
def requirements(self):
self.requires("librw/{}".format(self.version))
self.requires("mpg123/1.26.4")
if self.options.audio == "openal":
self.requires("openal/1.21.0")
elif self.options.audio == "miles":
self.requires("miles-sdk/{}".format(self.version))
if self.options.with_libsndfile:
self.requires("libsndfile/1.0.30")
if self.options.with_opus:

View File

@ -257,6 +257,7 @@ project "re3"
files { addSrcFiles("src/audio/oal") }
files { addSrcFiles("src/buildings") }
files { addSrcFiles("src/collision") }
files { addSrcFiles("src/external") }
files { addSrcFiles("src/control") }
files { addSrcFiles("src/core") }
files { addSrcFiles("src/entities") }
@ -286,6 +287,7 @@ project "re3"
includedirs { "src/audio/oal" }
includedirs { "src/buildings" }
includedirs { "src/collision" }
includedirs { "src/external" }
includedirs { "src/control" }
includedirs { "src/core" }
includedirs { "src/entities" }
@ -318,8 +320,8 @@ project "re3"
filter "platforms:*mss"
defines { "AUDIO_MSS" }
includedirs { "vendor/milessdk/include" }
libdirs { "vendor/milessdk/lib" }
includedirs { "src/external/milessdk" }
libdirs { "src/external/milessdk" }
if _OPTIONS["with-opus"] then
filter "platforms:win*"
@ -367,27 +369,24 @@ project "re3"
filter "platforms:win*oal"
includedirs { "vendor/openal-soft/include" }
includedirs { "vendor/libsndfile/include" }
includedirs { "vendor/mpg123/include" }
includedirs { "src/external/libsndfile/include" }
filter "platforms:win-x86*oal"
libdirs { "vendor/mpg123/lib/Win32" }
libdirs { "vendor/libsndfile/lib/Win32" }
libdirs { "src/external/libsndfile/lib/Win32" }
libdirs { "vendor/openal-soft/libs/Win32" }
filter "platforms:win-amd64*oal"
libdirs { "vendor/mpg123/lib/Win64" }
libdirs { "vendor/libsndfile/lib/Win64" }
libdirs { "src/external/libsndfile/lib/Win64" }
libdirs { "vendor/openal-soft/libs/Win64" }
filter "platforms:linux*oal"
links { "openal", "mpg123", "sndfile", "pthread", "X11" }
links { "openal", "sndfile", "pthread", "X11" }
filter "platforms:bsd*oal"
links { "openal", "mpg123", "sndfile", "pthread", "X11" }
links { "openal", "sndfile", "pthread", "X11" }
filter "platforms:macosx*oal"
links { "openal", "mpg123", "sndfile", "pthread" }
links { "openal", "sndfile", "pthread" }
filter "platforms:macosx-arm64-*oal"
includedirs { "/opt/homebrew/opt/openal-soft/include" }

View File

@ -64,10 +64,6 @@ elseif(${PROJECT}_AUDIO STREQUAL "MSS")
target_link_libraries(${EXECUTABLE} PRIVATE MilesSDK::MilesSDK)
endif()
find_package(mpg123 REQUIRED)
target_link_libraries(${EXECUTABLE} PRIVATE
MPG123::libmpg123
)
if(${PROJECT}_WITH_OPUS)
find_package(opusfile REQUIRED)
target_link_libraries(${EXECUTABLE} PRIVATE

View File

@ -6,47 +6,46 @@
#ifdef AUDIO_OAL_USE_SNDFILE
#pragma comment( lib, "libsndfile-1.lib" )
#endif
#ifdef AUDIO_OAL_USE_MPG123
#pragma comment( lib, "libmpg123-0.lib" )
#endif
#endif
#ifdef AUDIO_OAL_USE_SNDFILE
#include <sndfile.h>
#endif
#ifdef AUDIO_OAL_USE_MPG123
#include <mpg123.h>
#endif
#ifdef AUDIO_OAL_USE_OPUS
#include <opusfile.h>
#endif
#define MINIMP3_IMPLEMENTATION
#include "minimp3_ex.h"
#include <queue>
#include <utility>
#ifdef MULTITHREADED_AUDIO
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#define XPLAT_PTHREAD
#include "crossplatform.h"
#include "MusicManager.h"
#include "stream.h"
std::thread gAudioThread;
std::mutex gAudioThreadQueueMutex;
std::condition_variable gAudioThreadCv;
pthread_t gAudioThread;
pthread_mutex_t gAudioThreadQueueMutex;
pthread_cond_t gAudioThreadCv;
bool gAudioThreadTerm = false;
std::queue<CStream*> gStreamsToProcess; // values are not unique, we will handle that ourself
std::queue<std::pair<IDecoder*, void*>> gStreamsToClose;
#else
#include "stream.h"
#endif
#include "sampman.h"
#else
#include "stream.h"
#ifndef _WIN32
#include "crossplatform.h"
#endif
#endif // not MULTITHREADED_AUDIO
#include "sampman.h"
/*
As we ran onto an issue of having different volume levels for mono streams
and stereo streams we are now handling all the stereo panning ourselves.
@ -61,7 +60,7 @@ class CSortStereoBuffer
uint16* PcmBuf;
size_t BufSize;
//#ifdef MULTITHREADED_AUDIO
// std::mutex Mutex;
// pthread_mutex_t Mutex;
//#endif
public:
@ -91,12 +90,17 @@ public:
void SortStereo(void* buf, size_t size)
{
//#ifdef MULTITHREADED_AUDIO
// std::lock_guard<std::mutex> lock(Mutex);
// pthread_mutex_lock(&Mutex);
//#endif
uint16* InBuf = (uint16*)buf;
uint16* OutBuf = GetBuffer(size);
if (!OutBuf) return;
if (!OutBuf) {
//#ifdef MULTITHREADED_AUDIO
// pthread_mutex_unlock(&Mutex);
//#endif
return;
}
size_t rightStart = size / 4;
for (size_t i = 0; i < size / 4; i++)
@ -106,6 +110,9 @@ public:
}
memcpy(InBuf, OutBuf, size);
//#ifdef MULTITHREADED_AUDIO
// pthread_mutex_unlock(&Mutex);
//#endif
}
};
@ -500,68 +507,35 @@ public:
};
#endif
#ifdef AUDIO_OAL_USE_MPG123
class CMP3File : public IDecoder
{
protected:
mpg123_handle *m_pMH;
mp3dec_ex_t m_handle;
mp3dec_frame_info_t m_frameInfo;
bool m_bOpened;
uint32 m_nRate;
uint32 m_nChannels;
const char* m_pPath;
bool m_bFileNotOpenedYet;
public:
CMP3File(const char *path) :
m_pMH(nil),
m_bOpened(false),
m_nRate(0),
m_nChannels(0),
m_pPath(path),
m_bFileNotOpenedYet(false)
m_nChannels(0)
{
m_pMH = mpg123_new(nil, nil);
if ( m_pMH )
int res = mp3dec_ex_open(&m_handle, path, MP3D_SEEK_TO_SAMPLE);
if (res == 0)
{
mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);
m_bOpened = true;
m_bFileNotOpenedYet = true;
// It's possible to move this to audioFileOpsThread(), but effect isn't noticable + probably not compatible with our current cutscene audio handling
#if 1
FileOpen();
#endif
m_nRate = m_handle.info.hz;
m_nChannels = m_handle.info.channels;
}
}
void FileOpen()
{
if(!m_bFileNotOpenedYet) return;
long rate = 0;
int channels = 0;
int encoding = 0;
m_bOpened = mpg123_open(m_pMH, m_pPath) == MPG123_OK
&& mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;
m_nRate = rate;
m_nChannels = channels;
if(IsOpened()) {
mpg123_format_none(m_pMH);
mpg123_format(m_pMH, rate, channels, encoding);
}
m_bFileNotOpenedYet = false;
}
~CMP3File()
{
if ( m_pMH )
{
mpg123_close(m_pMH);
mpg123_delete(m_pMH);
m_pMH = nil;
}
mp3dec_ex_close(&m_handle);
}
bool IsOpened()
@ -571,13 +545,13 @@ public:
uint32 GetSampleSize()
{
return sizeof(uint16);
return sizeof(mp3d_sample_t); // uint16
}
uint32 GetSampleCount()
{
if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
return mpg123_length(m_pMH);
if ( !IsOpened() ) return 0;
return m_handle.samples;
}
uint32 GetSampleRate()
@ -592,33 +566,35 @@ public:
void Seek(uint32 milliseconds)
{
if ( !IsOpened() || m_bFileNotOpenedYet ) return;
mpg123_seek(m_pMH, ms2samples(milliseconds), SEEK_SET);
if ( !IsOpened() ) return;
mp3dec_ex_seek(&m_handle, ms2samples(milliseconds));
}
uint32 Tell()
{
if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
return samples2ms(mpg123_tell(m_pMH));
if ( !IsOpened() ) return 0;
return samples2ms(m_handle.cur_sample);
}
uint32 Decode(void *buffer)
{
if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
size_t size;
int err = mpg123_read(m_pMH, (unsigned char *)buffer, GetBufferSize(), &size);
if ( !IsOpened() ) return 0;
size_t read_samples = mp3dec_ex_read(&m_handle, (mp3d_sample_t*)buffer, GetBufferSamples());
if (read_samples == 0)
return 0;
size_t read_bytes = read_samples * GetSampleSize();
#if defined(__LP64__) || defined(_WIN64)
assert("We can't handle audio files more then 2 GB yet :shrug:" && (size < UINT32_MAX));
assert("We can't handle this big audio files yet :shrug:" && (read_bytes < UINT32_MAX));
#endif
if (err != MPG123_OK && err != MPG123_DONE) return 0;
if (GetChannels() == 2)
SortStereoBuffer.SortStereo(buffer, size);
return (uint32)size;
SortStereoBuffer.SortStereo(buffer, read_bytes);
return (uint32)read_bytes;
}
};
#endif
#define VAG_LINE_SIZE (0x10)
#define VAG_SAMPLES_IN_LINE (28)
@ -1013,28 +989,33 @@ CStream::FlagAsToBeProcessed(bool close)
if (!close && MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE)
return;
gAudioThreadQueueMutex.lock();
pthread_mutex_lock(&gAudioThreadQueueMutex);
if (close)
gStreamsToClose.push(std::pair<IDecoder*, void*>(m_pSoundFile ? m_pSoundFile : nil, m_pBuffer ? m_pBuffer : nil));
else
gStreamsToProcess.push(this);
gAudioThreadQueueMutex.unlock();
pthread_mutex_unlock(&gAudioThreadQueueMutex);
gAudioThreadCv.notify_one();
pthread_cond_broadcast(&gAudioThreadCv);
}
void audioFileOpsThread()
void* audioFileOpsThread(void* arg)
{
do
{
CStream *stream;
{
// Just a semaphore
std::unique_lock<std::mutex> queueMutex(gAudioThreadQueueMutex);
gAudioThreadCv.wait(queueMutex, [] { return gStreamsToProcess.size() > 0 || gStreamsToClose.size() > 0 || gAudioThreadTerm; });
if (gAudioThreadTerm)
return;
pthread_mutex_lock(&gAudioThreadQueueMutex);
while (gStreamsToProcess.size() == 0 && gStreamsToClose.size() == 0 && !gAudioThreadTerm)
pthread_cond_wait(&gAudioThreadCv, &gAudioThreadQueueMutex);
if (gAudioThreadTerm) {
pthread_mutex_unlock(&gAudioThreadQueueMutex);
return nil;
}
if (!gStreamsToClose.empty()) {
auto streamToClose = gStreamsToClose.front();
@ -1051,19 +1032,22 @@ void audioFileOpsThread()
if (!gStreamsToProcess.empty()) {
stream = gStreamsToProcess.front();
gStreamsToProcess.pop();
} else
} else {
pthread_mutex_unlock(&gAudioThreadQueueMutex);
continue;
}
pthread_mutex_unlock(&gAudioThreadQueueMutex);
}
std::unique_lock<std::mutex> lock(stream->m_mutex);
pthread_mutex_lock(&stream->m_mutex);
std::pair<ALuint, ALuint> buffers, *lastBufAddr;
bool insertBufsAfterCheck = false;
do {
if (!stream->IsOpened()) {
if (!stream->IsOpened())
break;
}
if (stream->m_bReset)
break;
@ -1082,9 +1066,9 @@ void audioFileOpsThread()
if (stream->m_bDoSeek) {
stream->m_bDoSeek = false;
int pos = stream->m_SeekPos;
lock.unlock();
pthread_mutex_unlock(&stream->m_mutex);
stream->m_pSoundFile->Seek(pos);
lock.lock();
pthread_mutex_lock(&stream->m_mutex);
continue; // let's do the checks again, make sure we didn't miss anything while Seeking
}
@ -1097,12 +1081,12 @@ void audioFileOpsThread()
if (!stream->m_fillBuffers.empty()) {
lastBufAddr = &stream->m_fillBuffers.front();
buffers = *lastBufAddr;
lock.unlock();
pthread_mutex_unlock(&stream->m_mutex);
ALuint alBuffers[2] = {buffers.first, buffers.second}; // left - right
bool filled = stream->FillBuffer(alBuffers);
lock.lock();
pthread_mutex_lock(&stream->m_mutex);
// Make sure queue isn't touched after we released mutex
if (!stream->m_fillBuffers.empty() && lastBufAddr == &stream->m_fillBuffers.front()) {
@ -1115,32 +1099,33 @@ void audioFileOpsThread()
} while (true);
pthread_mutex_unlock(&stream->m_mutex);
} while(true);
return nil;
}
#endif
void CStream::Initialise()
{
#ifdef AUDIO_OAL_USE_MPG123
mpg123_init();
#endif
#ifdef MULTITHREADED_AUDIO
gAudioThread = std::thread(audioFileOpsThread);
pthread_mutex_init(&gAudioThreadQueueMutex, NULL);
pthread_cond_init(&gAudioThreadCv, NULL);
pthread_create(&gAudioThread, NULL, &audioFileOpsThread, NULL);
#endif
}
void CStream::Terminate()
{
#ifdef AUDIO_OAL_USE_MPG123
mpg123_exit();
#endif
#ifdef MULTITHREADED_AUDIO
gAudioThreadQueueMutex.lock();
pthread_mutex_lock(&gAudioThreadQueueMutex);
gAudioThreadTerm = true;
gAudioThreadQueueMutex.unlock();
pthread_mutex_unlock(&gAudioThreadQueueMutex);
gAudioThreadCv.notify_one();
gAudioThread.join();
pthread_cond_broadcast(&gAudioThreadCv);
void *status;
pthread_join(gAudioThread, &status);
#endif
}
@ -1163,6 +1148,9 @@ CStream::CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]) :
m_nLoopCount(1)
{
#ifdef MULTITHREADED_AUDIO
pthread_mutex_init(&m_mutex, NULL);
#endif
}
bool CStream::Open(const char* filename, uint32 overrideSampleRate)
@ -1170,7 +1158,7 @@ bool CStream::Open(const char* filename, uint32 overrideSampleRate)
if (IsOpened()) return false;
#ifdef MULTITHREADED_AUDIO
std::unique_lock<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
m_bDoSeek = false;
m_SeekPos = 0;
@ -1205,10 +1193,8 @@ bool CStream::Open(const char* filename, uint32 overrideSampleRate)
#else
m_pSoundFile = new CWavFile(m_aFilename);
#endif
#ifdef AUDIO_OAL_USE_MPG123
else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".mp3")], ".mp3"))
m_pSoundFile = new CMP3File(m_aFilename);
#endif
else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".vb")], ".VB"))
m_pSoundFile = new CVbFile(m_aFilename, overrideSampleRate);
#ifdef AUDIO_OAL_USE_OPUS
@ -1235,9 +1221,13 @@ bool CStream::Open(const char* filename, uint32 overrideSampleRate)
}
#ifdef MULTITHREADED_AUDIO
m_bIExist = true;
pthread_mutex_unlock(&m_mutex);
#endif
return true;
}
#ifdef MULTITHREADED_AUDIO
pthread_mutex_unlock(&m_mutex);
#endif
return false;
}
@ -1252,13 +1242,15 @@ void CStream::Close()
#ifdef MULTITHREADED_AUDIO
{
std::lock_guard<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
Stop();
ClearBuffers();
m_bIExist = false;
std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, mutex is acquired
pthread_mutex_unlock(&m_mutex);
}
FlagAsToBeProcessed(true);
@ -1309,11 +1301,14 @@ bool CStream::IsPlaying()
return true;
#ifdef MULTITHREADED_AUDIO
std::lock_guard<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
// Streams are designed in such a way that m_fillBuffers and m_queueBuffers will be *always* filled if audio is playing, and mutex is acquired
if (!m_fillBuffers.empty() || !m_queueBuffers.emptyNts())
if (!m_fillBuffers.empty() || !m_queueBuffers.emptyNts()) {
pthread_mutex_unlock(&m_mutex);
return true;
}
pthread_mutex_unlock(&m_mutex);
#endif
}
@ -1391,7 +1386,7 @@ void CStream::SetPosMS(uint32 nPos)
if ( !IsOpened() ) return;
#ifdef MULTITHREADED_AUDIO
std::lock_guard<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired
@ -1405,7 +1400,10 @@ void CStream::SetPosMS(uint32 nPos)
m_pSoundFile->Seek(nPos);
}
ClearBuffers();
#ifdef MULTITHREADED_AUDIO
pthread_mutex_unlock(&m_mutex);
#endif
// adding to gStreamsToProcess not needed, someone always calls Start() / BuffersShouldBeFilled() after SetPosMS
}
@ -1518,7 +1516,7 @@ bool CStream::Setup(bool imSureQueueIsEmpty, bool lock)
{
#ifdef MULTITHREADED_AUDIO
if (lock)
m_mutex.lock();
pthread_mutex_lock(&m_mutex);
#endif
if (!imSureQueueIsEmpty) {
@ -1534,7 +1532,7 @@ bool CStream::Setup(bool imSureQueueIsEmpty, bool lock)
}
if (lock)
m_mutex.unlock();
pthread_mutex_unlock(&m_mutex);
#else
m_pSoundFile->Seek(0);
#endif
@ -1593,10 +1591,15 @@ void CStream::Start()
if ( !HasSource() ) return;
#ifdef MULTITHREADED_AUDIO
std::lock_guard<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // TSness not required, second thread always access it when stream mutex acquired
#endif
BuffersShouldBeFilled();
#ifdef MULTITHREADED_AUDIO
pthread_mutex_unlock(&m_mutex);
#endif
}
void CStream::Stop()
@ -1658,7 +1661,7 @@ void CStream::Update()
if (m_nLoopCount != 1 && m_bActive && totalBuffers[0] == 0)
{
#ifdef MULTITHREADED_AUDIO
std::lock_guard<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
if (m_fillBuffers.empty() && m_queueBuffers.emptyNts()) // we already acquired stream mutex, which is enough for second thread. thus Nts variant
#endif
@ -1668,6 +1671,9 @@ void CStream::Update()
if (m_nLoopCount != 0)
m_nLoopCount--;
}
#ifdef MULTITHREADED_AUDIO
pthread_mutex_unlock(&m_mutex);
#endif
}
else
{
@ -1689,7 +1695,7 @@ void CStream::Update()
if (m_bActive && buffersProcessed[1])
{
#ifdef MULTITHREADED_AUDIO
m_mutex.lock();
pthread_mutex_lock(&m_mutex);
#endif
while (!tempFillBuffer.empty()) {
auto elem = tempFillBuffer.front();
@ -1697,7 +1703,7 @@ void CStream::Update()
buffersQueuedButNotStarted = BufferShouldBeFilledAndQueued(&elem);
}
#ifdef MULTITHREADED_AUDIO
m_mutex.unlock();
pthread_mutex_unlock(&m_mutex);
FlagAsToBeProcessed();
#endif
@ -1721,7 +1727,7 @@ void CStream::ProviderInit()
SetLoopCount(m_nLoopCount);
SetPosMS(m_nPosBeforeReset);
#ifdef MULTITHREADED_AUDIO
std::unique_lock<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
#endif
if(m_bActive)
BuffersShouldBeFilled();
@ -1733,17 +1739,20 @@ void CStream::ProviderInit()
} else {
#ifdef MULTITHREADED_AUDIO
std::unique_lock<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
#endif
m_bReset = false;
}
#ifdef MULTITHREADED_AUDIO
pthread_mutex_unlock(&m_mutex);
#endif
}
}
void CStream::ProviderTerm()
{
#ifdef MULTITHREADED_AUDIO
std::lock_guard<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
// unlike Close() we will reuse this stream, so clearing queues are important.
std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
@ -1754,6 +1763,9 @@ void CStream::ProviderTerm()
Stop();
ClearBuffers();
#ifdef MULTITHREADED_AUDIO
pthread_mutex_unlock(&m_mutex);
#endif
}
#endif

View File

@ -59,26 +59,32 @@ public:
template <typename T> class tsQueue
{
public:
tsQueue() : count(0) { }
tsQueue() : count(0) {
pthread_mutex_init(&m_mutex, NULL);
}
void push(const T &value)
{
std::lock_guard<std::mutex> lock(m_mutex);
pthread_mutex_lock(&m_mutex);
m_queue.push(value);
count++;
pthread_mutex_unlock(&m_mutex);
}
bool peekPop(T *retVal)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (count == 0)
return false;
bool peekPop(T *retVal)
{
pthread_mutex_lock(&m_mutex);
if (count == 0) {
pthread_mutex_unlock(&m_mutex);
return false;
}
*retVal = m_queue.front();
m_queue.pop();
count--;
return true;
}
*retVal = m_queue.front();
m_queue.pop();
count--;
pthread_mutex_unlock(&m_mutex);
return true;
}
void swapNts(tsQueue<T> &replaceWith)
{
@ -89,9 +95,11 @@ public:
/*
void swapTs(tsQueue<T> &replaceWith)
{
std::lock_guard<std::mutex> lock(m_mutex);
std::lock_guard<std::mutex> lock2(replaceWith.m_mutex);
pthread_mutex_lock(&m_mutex);
pthread_mutex_lock(&replaceWith.m_mutex);
swapNts(replaceWith);
pthread_mutex_unlock(&replaceWith.m_mutex);
pthread_mutex_unlock(&m_mutex);
}
*/
@ -103,14 +111,16 @@ public:
/*
bool emptyTs()
{
std::lock_guard<std::mutex> lock(m_mutex);
return emptyNts();
pthread_mutex_lock(&m_mutex);
bool isEmpty = emptyNts();
pthread_mutex_unlock(&m_mutex);
return isEmpty;
}
*/
std::queue<T> m_queue;
int count;
mutable std::mutex m_mutex;
pthread_mutex_t m_mutex;
};
#endif
class CStream
@ -124,10 +134,9 @@ class CStream
public:
#ifdef MULTITHREADED_AUDIO
std::mutex m_mutex;
pthread_mutex_t m_mutex;
std::queue<std::pair<ALuint, ALuint>> m_fillBuffers; // left and right buffer
tsQueue<std::pair<ALuint, ALuint>> m_queueBuffers;
// std::condition_variable m_closeCv;
bool m_bDoSeek;
uint32 m_SeekPos;
bool m_bIExist;

View File

@ -27,6 +27,10 @@
#endif
#include "common.h"
#ifdef MULTITHREADED_AUDIO
#define XPLAT_PTHREAD
#include <queue>
#endif
#include "crossplatform.h"
#include "sampman.h"
@ -36,11 +40,6 @@
#include "oal/channel.h"
#include <utility>
#ifdef MULTITHREADED_AUDIO
#include <mutex>
#include <queue>
#include <condition_variable>
#endif
#include "oal/stream.h"
#include "AudioManager.h"

View File

@ -428,9 +428,8 @@ enum Config {
#define PS2_AUDIO_CHANNELS // increases the maximum number of audio channels to PS2 value of 44 (PC has 28 originally)
#define PS2_AUDIO_PATHS // changes audio paths for cutscenes and radio to PS2 paths (needs vbdec on MSS builds)
//#define AUDIO_OAL_USE_SNDFILE // use libsndfile to decode WAVs instead of our internal decoder
#define AUDIO_OAL_USE_MPG123 // use mpg123 to support mp3 files
#define PAUSE_RADIO_IN_FRONTEND // pause radio when game is paused
#define MULTITHREADED_AUDIO // for streams. requires C++11 or later
#define MULTITHREADED_AUDIO // for streams
#ifdef AUDIO_OPUS
#define AUDIO_OAL_USE_OPUS // enable support of opus files

1855
src/external/minimp3.h vendored Normal file

File diff suppressed because it is too large Load Diff

1394
src/external/minimp3_ex.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -279,3 +279,129 @@ char* casepath(char const* path, bool checkPathFirst)
return out;
}
#endif
// pthread wrapper for Windows
#if defined _MSC_VER
#include <Windows.h>
typedef CRITICAL_SECTION pthread_mutex_t;
typedef void pthread_mutexattr_t;
typedef void pthread_condattr_t;
typedef void pthread_rwlockattr_t;
typedef HANDLE pthread_t;
typedef CONDITION_VARIABLE pthread_cond_t;
int pthread_create(pthread_t *thread, void *attr, void * (__cdecl *start_routine) (void *), void *arg)
{
if (thread == NULL || start_routine == NULL)
return 1;
*thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) start_routine, arg, 0, NULL);
if (*thread == NULL)
return 1;
return 0;
}
int pthread_join(pthread_t thread, void **value_ptr)
{
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
return 0;
}
int pthread_detach(pthread_t thread)
{
return CloseHandle(thread) ? 0 : 1;
}
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
{
if (mutex == NULL)
return 1;
InitializeCriticalSection(mutex);
return 0;
}
int pthread_mutex_destroy(pthread_mutex_t *mutex)
{
if (mutex == NULL)
return 1;
DeleteCriticalSection(mutex);
return 0;
}
int pthread_mutex_lock(pthread_mutex_t *mutex)
{
if (mutex == NULL)
return 1;
EnterCriticalSection(mutex);
return 0;
}
int pthread_mutex_unlock(pthread_mutex_t *mutex)
{
if (mutex == NULL)
return 1;
LeaveCriticalSection(mutex);
return 0;
}
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr)
{
if (cond == NULL)
return 1;
InitializeConditionVariable(cond);
return 0;
}
int pthread_cond_destroy(pthread_cond_t *cond)
{
/* Windows does not have a destroy for conditionals */
return 0;
}
static DWORD timespec_to_ms(const struct timespec *abstime)
{
DWORD t;
if (abstime == NULL)
return INFINITE;
t = ((abstime->tv_sec - time(NULL)) * 1000) + (abstime->tv_nsec / 1000000);
if (t < 0)
t = 1;
return t;
}
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
const struct timespec *abstime)
{
if (cond == NULL || mutex == NULL)
return 1;
if (!SleepConditionVariableCS(cond, mutex, timespec_to_ms(abstime)))
return 1;
return 0;
}
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
{
return pthread_cond_timedwait(cond, mutex, NULL);
}
int pthread_cond_signal(pthread_cond_t *cond)
{
if (cond == NULL)
return 1;
WakeConditionVariable(cond);
return 0;
}
int pthread_cond_broadcast(pthread_cond_t *cond)
{
if (cond == NULL)
return 1;
WakeAllConditionVariable(cond);
return 0;
}
#endif

View File

@ -30,7 +30,16 @@ enum eWinVersion
extern DWORD _dwOperatingSystemVersion;
#define fcaseopen fopen
#define caserename rename
#else
#if defined _MSC_VER && _MSC_VER < 1900
struct timespec {
time_t tv_sec;
long tv_nsec;
};
#endif
#else // ifndef _WIN32
char *strupr(char *str);
char *strlwr(char *str);
@ -157,3 +166,37 @@ bool FindNextFile(HANDLE, WIN32_FIND_DATA*);
void FileTimeToSystemTime(time_t*, SYSTEMTIME*);
void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int);
#endif
#ifdef XPLAT_PTHREAD
#ifdef _MSC_VER
// pthread wrapper for Windows
#include <Windows.h>
typedef CRITICAL_SECTION pthread_mutex_t;
typedef void pthread_mutexattr_t;
typedef void pthread_condattr_t;
typedef void pthread_rwlockattr_t;
typedef HANDLE pthread_t;
typedef CONDITION_VARIABLE pthread_cond_t;
int pthread_create(pthread_t *thread, void *attr, void *(*start_routine)(void *), void *arg);
int pthread_join(pthread_t thread, void **value_ptr);
int pthread_detach(pthread_t);
int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *attr);
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
#else
#include <pthread.h>
#endif
#endif

Binary file not shown.

Binary file not shown.

View File

@ -1,159 +0,0 @@
/*
libmpg123: MPEG Audio Decoder library
separate header just for audio format definitions not tied to
library code
copyright 1995-2020 by the mpg123 project
free software under the terms of the LGPL 2.1
see COPYING and AUTHORS files in distribution or http://mpg123.org
*/
#ifndef MPG123_ENC_H
#define MPG123_ENC_H
/** \file fmt123.h Audio format definitions. */
/** \defgroup mpg123_enc mpg123 PCM sample encodings
* These are definitions for audio formats used by libmpg123 and
* libout123.
*
* @{
*/
/** An enum over all sample types possibly known to mpg123.
* The values are designed as bit flags to allow bitmasking for encoding
* families.
* This is also why the enum is not used as type for actual encoding variables,
* plain integers (at least 16 bit, 15 bit being used) cover the possible
* combinations of these flags.
*
* Note that (your build of) libmpg123 does not necessarily support all these.
* Usually, you can expect the 8bit encodings and signed 16 bit.
* Also 32bit float will be usual beginning with mpg123-1.7.0 .
* What you should bear in mind is that (SSE, etc) optimized routines may be
* absent for some formats. We do have SSE for 16, 32 bit and float, though.
* 24 bit integer is done via postprocessing of 32 bit output -- just cutting
* the last byte, no rounding, even. If you want better, do it yourself.
*
* All formats are in native byte order. If you need different endinaness, you
* can simply postprocess the output buffers (libmpg123 wouldn't do anything
* else). The macro MPG123_SAMPLESIZE() can be helpful there.
*/
enum mpg123_enc_enum
{
/* 0000 0000 0000 1111 Some 8 bit integer encoding. */
MPG123_ENC_8 = 0x00f
/* 0000 0000 0100 0000 Some 16 bit integer encoding. */
, MPG123_ENC_16 = 0x040
/* 0100 0000 0000 0000 Some 24 bit integer encoding. */
, MPG123_ENC_24 = 0x4000
/* 0000 0001 0000 0000 Some 32 bit integer encoding. */
, MPG123_ENC_32 = 0x100
/* 0000 0000 1000 0000 Some signed integer encoding. */
, MPG123_ENC_SIGNED = 0x080
/* 0000 1110 0000 0000 Some float encoding. */
, MPG123_ENC_FLOAT = 0xe00
/* 0000 0000 1101 0000 signed 16 bit */
, MPG123_ENC_SIGNED_16 = (MPG123_ENC_16|MPG123_ENC_SIGNED|0x10)
/* 0000 0000 0110 0000 unsigned 16 bit */
, MPG123_ENC_UNSIGNED_16 = (MPG123_ENC_16|0x20)
/* 0000 0000 0000 0001 unsigned 8 bit */
, MPG123_ENC_UNSIGNED_8 = 0x01
/* 0000 0000 1000 0010 signed 8 bit */
, MPG123_ENC_SIGNED_8 = (MPG123_ENC_SIGNED|0x02)
/* 0000 0000 0000 0100 ulaw 8 bit */
, MPG123_ENC_ULAW_8 = 0x04
/* 0000 0000 0000 1000 alaw 8 bit */
, MPG123_ENC_ALAW_8 = 0x08
/* 0001 0001 1000 0000 signed 32 bit */
, MPG123_ENC_SIGNED_32 = MPG123_ENC_32|MPG123_ENC_SIGNED|0x1000
/* 0010 0001 0000 0000 unsigned 32 bit */
, MPG123_ENC_UNSIGNED_32 = MPG123_ENC_32|0x2000
/* 0101 0000 1000 0000 signed 24 bit */
, MPG123_ENC_SIGNED_24 = MPG123_ENC_24|MPG123_ENC_SIGNED|0x1000
/* 0110 0000 0000 0000 unsigned 24 bit */
, MPG123_ENC_UNSIGNED_24 = MPG123_ENC_24|0x2000
/* 0000 0010 0000 0000 32bit float */
, MPG123_ENC_FLOAT_32 = 0x200
/* 0000 0100 0000 0000 64bit float */
, MPG123_ENC_FLOAT_64 = 0x400
/* Any possibly known encoding from the list above. */
, MPG123_ENC_ANY = ( MPG123_ENC_SIGNED_16 | MPG123_ENC_UNSIGNED_16
| MPG123_ENC_UNSIGNED_8 | MPG123_ENC_SIGNED_8
| MPG123_ENC_ULAW_8 | MPG123_ENC_ALAW_8
| MPG123_ENC_SIGNED_32 | MPG123_ENC_UNSIGNED_32
| MPG123_ENC_SIGNED_24 | MPG123_ENC_UNSIGNED_24
| MPG123_ENC_FLOAT_32 | MPG123_ENC_FLOAT_64 )
};
/** Get size of one PCM sample with given encoding.
* This is included both in libmpg123 and libout123. Both offer
* an API function to provide the macro results from library
* compile-time, not that of you application. This most likely
* does not matter as I do not expect any fresh PCM sample
* encoding to appear. But who knows? Perhaps the encoding type
* will be abused for funny things in future, not even plain PCM.
* And, by the way: Thomas really likes the ?: operator.
* \param enc the encoding (mpg123_enc_enum value)
* \return size of one sample in bytes
*/
#define MPG123_SAMPLESIZE(enc) ( \
(enc) < 1 \
? 0 \
: ( (enc) & MPG123_ENC_8 \
? 1 \
: ( (enc) & MPG123_ENC_16 \
? 2 \
: ( (enc) & MPG123_ENC_24 \
? 3 \
: ( ( (enc) & MPG123_ENC_32 \
|| (enc) == MPG123_ENC_FLOAT_32 ) \
? 4 \
: ( (enc) == MPG123_ENC_FLOAT_64 \
? 8 \
: 0 \
) ) ) ) ) )
/** Representation of zero in differing encodings.
* This exists to define proper silence in various encodings without
* having to link to libsyn123 to do actual conversions at runtime.
* You have to handle big/little endian order yourself, though.
* This takes the shortcut that any signed encoding has a zero with
* all-zero bits. Unsigned linear encodings just have the highest bit set
* (2^(n-1) for n bits), while the nonlinear 8-bit ones are special.
* \param enc the encoding (mpg123_enc_enum value)
* \param siz bytes per sample (return value of MPG123_SAMPLESIZE(enc))
* \param off byte (octet) offset counted from LSB
* \return unsigned byte value for the designated octet
*/
#define MPG123_ZEROSAMPLE(enc, siz, off) ( \
(enc) == MPG123_ENC_ULAW_8 \
? (off == 0 ? 0xff : 0x00) \
: ( (enc) == MPG123_ENC_ALAW_8 \
? (off == 0 ? 0xd5 : 0x00) \
: ( (((enc) & (MPG123_ENC_SIGNED|MPG123_ENC_FLOAT)) || (siz) != ((off)+1)) \
? 0x00 \
: 0x80 \
) ) )
/** Structure defining an audio format.
* Providing the members as individual function arguments to define a certain
* output format is easy enough. This struct makes is more comfortable to deal
* with a list of formats.
* Negative values for the members might be used to communicate use of default
* values.
*/
struct mpg123_fmt
{
long rate; /**< sampling rate in Hz */
int channels; /**< channel count */
/** encoding code, can be single value or bitwise or of members of
* mpg123_enc_enum */
int encoding;
};
/* @} */
#endif

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.