summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/animation/AnimBlendAssociation.h2
-rw-r--r--src/animation/AnimBlendClumpData.cpp1
-rw-r--r--src/audio/AudioManager.cpp2
-rw-r--r--src/audio/oal/stream.cpp565
-rw-r--r--src/audio/oal/stream.h90
-rw-r--r--src/audio/sampman_oal.cpp159
-rw-r--r--src/control/CarCtrl.cpp2
-rw-r--r--src/core/Frontend.cpp98
-rw-r--r--src/core/Frontend.h29
-rw-r--r--src/core/Game.cpp6
-rw-r--r--src/core/IniFile.cpp4
-rw-r--r--src/core/IniFile.h3
-rw-r--r--src/core/MenuScreensCustom.cpp27
-rw-r--r--src/core/config.h9
-rw-r--r--src/core/re3.cpp26
-rw-r--r--src/entities/Physical.cpp4
-rw-r--r--src/extras/frontendoption.h2
-rw-r--r--src/peds/Population.cpp8
19 files changed, 808 insertions, 231 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 35b7ec11..28090d7e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -39,7 +39,7 @@ target_compile_definitions(${EXECUTABLE}
PRIVATE
$<IF:$<CONFIG:DEBUG>,DEBUG,NDEBUG>
LIBRW
- ${PROJECT}_NO_AUTOLINK
+ CMAKE_NO_AUTOLINK
)
if(LIBRW_PLATFORM_D3D9)
diff --git a/src/animation/AnimBlendAssociation.h b/src/animation/AnimBlendAssociation.h
index 80927da2..45720b6f 100644
--- a/src/animation/AnimBlendAssociation.h
+++ b/src/animation/AnimBlendAssociation.h
@@ -35,7 +35,7 @@ public:
CAnimBlendLink link;
- int numNodes; // taken from CAnimBlendClumpData::numFrames
+ int32 numNodes; // taken from CAnimBlendClumpData::numFrames
// NB: Order of these depends on order of nodes in Clump this was built from
CAnimBlendNode *nodes;
CAnimBlendHierarchy *hierarchy;
diff --git a/src/animation/AnimBlendClumpData.cpp b/src/animation/AnimBlendClumpData.cpp
index 702ee811..b333a449 100644
--- a/src/animation/AnimBlendClumpData.cpp
+++ b/src/animation/AnimBlendClumpData.cpp
@@ -3,7 +3,6 @@
#include "AnimBlendClumpData.h"
#include "MemoryMgr.h"
-
CAnimBlendClumpData::CAnimBlendClumpData(void)
{
numFrames = 0;
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
index 2e391349..a113cc93 100644
--- a/src/audio/AudioManager.cpp
+++ b/src/audio/AudioManager.cpp
@@ -993,4 +993,4 @@ cAudioManager::ComputeEmittingVolume(uint8 emittingVolume, float intensity, floa
return (quatIntensity - (dist - diffIntensity)) * (float)emittingVolume / quatIntensity;
return emittingVolume;
}
-#endif \ No newline at end of file
+#endif
diff --git a/src/audio/oal/stream.cpp b/src/audio/oal/stream.cpp
index 5d3ff08e..6afe8e30 100644
--- a/src/audio/oal/stream.cpp
+++ b/src/audio/oal/stream.cpp
@@ -1,10 +1,8 @@
#include "common.h"
#ifdef AUDIO_OAL
-#include "stream.h"
-#include "sampman.h"
-#if defined _MSC_VER && !defined RE3_NO_AUTOLINK
+#if defined _MSC_VER && !defined CMAKE_NO_AUTOLINK
#ifdef AUDIO_OAL_USE_SNDFILE
#pragma comment( lib, "libsndfile-1.lib" )
#endif
@@ -22,6 +20,29 @@
#include <opusfile.h>
#endif
+#include <queue>
+#include <utility>
+
+#ifdef MULTITHREADED_AUDIO
+#include <iostream>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+#include "MusicManager.h"
+#include "stream.h"
+
+std::thread gAudioThread;
+std::mutex gAudioThreadQueueMutex;
+std::condition_variable 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"
+
#ifndef _WIN32
#include "crossplatform.h"
#endif
@@ -39,6 +60,10 @@ class CSortStereoBuffer
{
uint16* PcmBuf;
size_t BufSize;
+//#ifdef MULTITHREADED_AUDIO
+// std::mutex Mutex;
+//#endif
+
public:
CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {}
~CSortStereoBuffer()
@@ -65,6 +90,9 @@ public:
void SortStereo(void* buf, size_t size)
{
+//#ifdef MULTITHREADED_AUDIO
+// std::lock_guard<std::mutex> lock(Mutex);
+//#endif
uint16* InBuf = (uint16*)buf;
uint16* OutBuf = GetBuffer(size);
@@ -279,6 +307,10 @@ public:
#undef CLOSE_ON_ERROR
}
+ void FileOpen()
+ {
+ }
+
~CWavFile()
{
Close();
@@ -289,6 +321,7 @@ public:
return m_bIsOpen;
}
+
uint32 GetSampleSize()
{
return sizeof(uint16);
@@ -405,6 +438,10 @@ public:
m_pfSound = sf_open(path, SFM_READ, &m_soundInfo);
}
+ void FileOpen()
+ {
+ }
+
~CSndFile()
{
if ( m_pfSound )
@@ -464,46 +501,59 @@ public:
#endif
#ifdef AUDIO_OAL_USE_MPG123
-// fuzzy seek eliminates stutter when playing ADF but spams errors a lot (nothing breaks though)
-#define MP3_USE_FUZZY_SEEK
class CMP3File : public IDecoder
{
+protected:
mpg123_handle *m_pMH;
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_nChannels(0),
+ m_pPath(path),
+ m_bFileNotOpenedYet(false)
{
m_pMH = mpg123_new(nil, nil);
if ( m_pMH )
{
-#ifdef MP3_USE_FUZZY_SEEK
- mpg123_param(m_pMH, MPG123_FLAGS, MPG123_FUZZY | MPG123_SEEKBUFFER | MPG123_GAPLESS | MPG123_QUIET, 0.0);
-#endif
- long rate = 0;
- int channels = 0;
- int encoding = 0;
-
- m_bOpened = mpg123_open(m_pMH, path) == MPG123_OK
- && mpg123_getformat(m_pMH, &rate, &channels, &encoding) == MPG123_OK;
+ mpg123_param(m_pMH, MPG123_FLAGS, MPG123_SEEKBUFFER | MPG123_GAPLESS, 0.0);
- m_nRate = rate;
- m_nChannels = channels;
-
- if ( IsOpened() )
- {
- mpg123_format_none(m_pMH);
- mpg123_format(m_pMH, rate, channels, encoding);
- }
+ 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
}
}
+ 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 )
@@ -526,7 +576,7 @@ public:
uint32 GetSampleCount()
{
- if ( !IsOpened() ) return 0;
+ if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
return mpg123_length(m_pMH);
}
@@ -542,19 +592,19 @@ public:
void Seek(uint32 milliseconds)
{
- if ( !IsOpened() ) return;
+ if ( !IsOpened() || m_bFileNotOpenedYet ) return;
mpg123_seek(m_pMH, ms2samples(milliseconds), SEEK_SET);
}
uint32 Tell()
{
- if ( !IsOpened() ) return 0;
+ if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
return samples2ms(mpg123_tell(m_pMH));
}
uint32 Decode(void *buffer)
{
- if ( !IsOpened() ) return 0;
+ if ( !IsOpened() || m_bFileNotOpenedYet ) return 0;
size_t size;
int err = mpg123_read(m_pMH, (unsigned char *)buffer, GetBufferSize(), &size);
@@ -685,6 +735,10 @@ public:
m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE];
}
+ void FileOpen()
+ {
+ }
+
~CVbFile()
{
if (m_pFile)
@@ -837,6 +891,10 @@ public:
m_bOpened = true;
}
}
+
+ void FileOpen()
+ {
+ }
~COpusFile()
{
@@ -902,11 +960,173 @@ public:
};
#endif
+
+// For multi-thread: Someone always acquire stream's mutex before entering here
+void
+CStream::BuffersShouldBeFilled()
+{
+#ifdef MULTITHREADED_AUDIO
+ if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
+ std::queue<std::pair<ALuint, ALuint>> tempQueue;
+ for(int i = 0; i < NUM_STREAMBUFFERS / 2; i++) {
+ tempQueue.push(std::pair<ALuint, ALuint>(m_alBuffers[i * 2], m_alBuffers[i * 2 + 1]));
+ }
+ m_fillBuffers.swap(tempQueue);
+
+ FlagAsToBeProcessed();
+
+ m_bActive = true; // to allow Update() to queue the filled buffers & play
+ return;
+ }
+ std::queue<std::pair<ALuint, ALuint>>().swap(m_fillBuffers);
+#endif
+ if ( FillBuffers() != 0 )
+ {
+ SetPlay(true);
+ }
+}
+
+// returns whether it's queued (not on multi-thread)
+bool
+CStream::BufferShouldBeFilledAndQueued(std::pair<ALuint, ALuint>* bufs)
+{
+#ifdef MULTITHREADED_AUDIO
+ if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE)
+ m_fillBuffers.push(*bufs);
+ else
+#endif
+ {
+ ALuint alBuffers[2] = {(*bufs).first, (*bufs).second}; // left - right
+ if (FillBuffer(alBuffers)) {
+ alSourceQueueBuffers(m_pAlSources[0], 1, &alBuffers[0]);
+ alSourceQueueBuffers(m_pAlSources[1], 1, &alBuffers[1]);
+ return true;
+ }
+ }
+ return false;
+}
+
+#ifdef MULTITHREADED_AUDIO
+void
+CStream::FlagAsToBeProcessed(bool close)
+{
+ if (!close && MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE)
+ return;
+
+ gAudioThreadQueueMutex.lock();
+ if (close)
+ gStreamsToClose.push(std::pair<IDecoder*, void*>(m_pSoundFile ? m_pSoundFile : nil, m_pBuffer ? m_pBuffer : nil));
+ else
+ gStreamsToProcess.push(this);
+
+ gAudioThreadQueueMutex.unlock();
+
+ gAudioThreadCv.notify_one();
+}
+
+void audioFileOpsThread()
+{
+ 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;
+
+ if (!gStreamsToClose.empty()) {
+ auto streamToClose = gStreamsToClose.front();
+ gStreamsToClose.pop();
+ if (streamToClose.first) { // pSoundFile
+ delete streamToClose.first;
+ }
+
+ if (streamToClose.second) { // pBuffer
+ free(streamToClose.second);
+ }
+ }
+
+ if (!gStreamsToProcess.empty()) {
+ stream = gStreamsToProcess.front();
+ gStreamsToProcess.pop();
+ } else
+ continue;
+ }
+
+ std::unique_lock<std::mutex> lock(stream->m_mutex);
+
+ std::pair<ALuint, ALuint> buffers, *lastBufAddr;
+ bool insertBufsAfterCheck = false;
+
+ do {
+ if (!stream->IsOpened()) {
+ break;
+ }
+
+ if (stream->m_bReset)
+ break;
+
+ // We gave up this idea for now
+ /*
+ stream->m_pSoundFile->FileOpen();
+
+ // Deffered allocation, do it now
+ if (stream->m_pBuffer == nil) {
+ stream->m_pBuffer = malloc(stream->m_pSoundFile->GetBufferSize());
+ ASSERT(stream->m_pBuffer != nil);
+ }
+ */
+
+ if (stream->m_bDoSeek) {
+ stream->m_bDoSeek = false;
+ int pos = stream->m_SeekPos;
+ lock.unlock();
+ stream->m_pSoundFile->Seek(pos);
+ lock.lock();
+
+ continue; // let's do the checks again, make sure we didn't miss anything while Seeking
+ }
+
+ if (insertBufsAfterCheck) {
+ stream->m_queueBuffers.push(buffers);
+ insertBufsAfterCheck = false;
+ }
+
+ if (!stream->m_fillBuffers.empty()) {
+ lastBufAddr = &stream->m_fillBuffers.front();
+ buffers = *lastBufAddr;
+ lock.unlock();
+
+ ALuint alBuffers[2] = {buffers.first, buffers.second}; // left - right
+ bool filled = stream->FillBuffer(alBuffers);
+
+ lock.lock();
+
+ // Make sure queue isn't touched after we released mutex
+ if (!stream->m_fillBuffers.empty() && lastBufAddr == &stream->m_fillBuffers.front()) {
+ stream->m_fillBuffers.pop();
+ if (filled)
+ insertBufsAfterCheck = true; // Also make sure stream's properties aren't changed. So make one more pass, and push it to m_queueBuffers only if it pass checks again.
+ }
+ } else
+ break;
+
+ } while (true);
+
+ } while(true);
+}
+#endif
+
void CStream::Initialise()
{
#ifdef AUDIO_OAL_USE_MPG123
mpg123_init();
#endif
+#ifdef MULTITHREADED_AUDIO
+ gAudioThread = std::thread(audioFileOpsThread);
+#endif
}
void CStream::Terminate()
@@ -914,14 +1134,27 @@ void CStream::Terminate()
#ifdef AUDIO_OAL_USE_MPG123
mpg123_exit();
#endif
+#ifdef MULTITHREADED_AUDIO
+ gAudioThreadQueueMutex.lock();
+ gAudioThreadTerm = true;
+ gAudioThreadQueueMutex.unlock();
+
+ gAudioThreadCv.notify_one();
+ gAudioThread.join();
+#endif
}
-CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate) :
+CStream::CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]) :
m_pAlSources(sources),
m_alBuffers(buffers),
m_pBuffer(nil),
m_bPaused(false),
m_bActive(false),
+#ifdef MULTITHREADED_AUDIO
+ m_bIExist(false),
+ m_bDoSeek(false),
+ m_SeekPos(0),
+#endif
m_pSoundFile(nil),
m_bReset(false),
m_nVolume(0),
@@ -930,6 +1163,27 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU
m_nLoopCount(1)
{
+}
+
+bool CStream::Open(const char* filename, uint32 overrideSampleRate)
+{
+ if (IsOpened()) return false;
+
+#ifdef MULTITHREADED_AUDIO
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ m_bDoSeek = false;
+ m_SeekPos = 0;
+#endif
+
+ m_bPaused = false;
+ m_bActive = false;
+ m_bReset = false;
+ m_nVolume = 0;
+ m_nPan = 0;
+ m_nPosBeforeReset = 0;
+ m_nLoopCount = 1;
+
// Be case-insensitive on linux (from https://github.com/OneSadCookie/fcaseopen/)
#if !defined(_WIN32)
char *real = casepath(filename);
@@ -964,44 +1218,67 @@ CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBU
else
m_pSoundFile = nil;
- if ( IsOpened() )
+ if ( m_pSoundFile && m_pSoundFile->IsOpened() )
{
- m_pBuffer = malloc(m_pSoundFile->GetBufferSize());
- ASSERT(m_pBuffer!=nil);
-
- DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec());
- DEV("SampleCount: %d\n", m_pSoundFile->GetSampleCount());
- DEV("SampleRate: %d\n", m_pSoundFile->GetSampleRate());
- DEV("Channels: %d\n", m_pSoundFile->GetChannels());
- DEV("Buffer Samples: %d\n", m_pSoundFile->GetBufferSamples());
- DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate())));
- DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60);
-
- return;
+ uint32 bufSize = m_pSoundFile->GetBufferSize();
+ if(bufSize != 0) { // Otherwise it's deferred
+ m_pBuffer = malloc(bufSize);
+ ASSERT(m_pBuffer != nil);
+
+ DEV("AvgSamplesPerSec: %d\n", m_pSoundFile->GetAvgSamplesPerSec());
+ DEV("SampleCount: %d\n", m_pSoundFile->GetSampleCount());
+ DEV("SampleRate: %d\n", m_pSoundFile->GetSampleRate());
+ DEV("Channels: %d\n", m_pSoundFile->GetChannels());
+ DEV("Buffer Samples: %d\n", m_pSoundFile->GetBufferSamples());
+ DEV("Buffer sec: %f\n", (float(m_pSoundFile->GetBufferSamples()) / float(m_pSoundFile->GetChannels())/ float(m_pSoundFile->GetSampleRate())));
+ DEV("Length MS: %02d:%02d\n", (m_pSoundFile->GetLength() / 1000) / 60, (m_pSoundFile->GetLength() / 1000) % 60);
+ }
+#ifdef MULTITHREADED_AUDIO
+ m_bIExist = true;
+#endif
+ return true;
}
+ return false;
}
CStream::~CStream()
{
- Delete();
+ assert(!IsOpened());
}
-void CStream::Delete()
+void CStream::Close()
{
+ if(!IsOpened()) return;
+
+#ifdef MULTITHREADED_AUDIO
+ {
+ std::lock_guard<std::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
+ }
+
+ FlagAsToBeProcessed(true);
+#else
+
Stop();
ClearBuffers();
-
+
if ( m_pSoundFile )
{
delete m_pSoundFile;
m_pSoundFile = nil;
}
-
+
if ( m_pBuffer )
{
free(m_pBuffer);
m_pBuffer = nil;
}
+#endif
}
bool CStream::HasSource()
@@ -1009,9 +1286,14 @@ bool CStream::HasSource()
return (m_pAlSources[0] != AL_NONE) && (m_pAlSources[1] != AL_NONE);
}
+// m_bIExist only written in main thread, thus mutex is not needed on main thread
bool CStream::IsOpened()
{
+#ifdef MULTITHREADED_AUDIO
+ return m_bIExist;
+#else
return m_pSoundFile && m_pSoundFile->IsOpened();
+#endif
}
bool CStream::IsPlaying()
@@ -1025,6 +1307,14 @@ bool CStream::IsPlaying()
alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
if (sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING)
return true;
+
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::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())
+ return true;
+#endif
}
return false;
@@ -1099,8 +1389,24 @@ void CStream::SetPan(uint8 nPan)
void CStream::SetPosMS(uint32 nPos)
{
if ( !IsOpened() ) return;
- m_pSoundFile->Seek(nPos);
+
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::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
+
+ if (MusicManager.m_nMusicMode != MUSICMODE_CUTSCENE) {
+ m_bDoSeek = true;
+ m_SeekPos = nPos;
+ } else
+#endif
+ {
+ m_pSoundFile->Seek(nPos);
+ }
ClearBuffers();
+
+ // adding to gStreamsToProcess not needed, someone always calls Start() / BuffersShouldBeFilled() after SetPosMS
}
uint32 CStream::GetPosMS()
@@ -1108,10 +1414,16 @@ uint32 CStream::GetPosMS()
if ( !HasSource() ) return 0;
if ( !IsOpened() ) return 0;
+ // Deferred init causes division by zero
+ if (m_pSoundFile->GetChannels() == 0)
+ return 0;
+
ALint offset;
//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset);
alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset);
+ //std::lock_guard<std::mutex> lock(m_mutex);
+
return m_pSoundFile->Tell()
- m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels()
+ m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels();
@@ -1125,6 +1437,7 @@ uint32 CStream::GetLengthMS()
bool CStream::FillBuffer(ALuint *alBuffer)
{
+#ifndef MULTITHREADED_AUDIO
if ( !HasSource() )
return false;
if ( !IsOpened() )
@@ -1133,7 +1446,8 @@ bool CStream::FillBuffer(ALuint *alBuffer)
return false;
if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) )
return false;
-
+#endif
+
uint32 size = m_pSoundFile->Decode(m_pBuffer);
if( size == 0 )
return false;
@@ -1149,6 +1463,26 @@ bool CStream::FillBuffer(ALuint *alBuffer)
return true;
}
+#ifdef MULTITHREADED_AUDIO
+bool CStream::QueueBuffers()
+{
+ bool buffersQueued = false;
+ std::pair<ALuint, ALuint> buffers;
+ while (m_queueBuffers.peekPop(&buffers)) // beware: m_queueBuffers is tsQueue
+ {
+ ALuint leftBuf = buffers.first;
+ ALuint rightBuf = buffers.second;
+
+ alSourceQueueBuffers(m_pAlSources[0], 1, &leftBuf);
+ alSourceQueueBuffers(m_pAlSources[1], 1, &rightBuf);
+
+ buffersQueued = true;
+ }
+ return buffersQueued;
+}
+#endif
+
+// Only used in single-threaded audio or cutscene audio
int32 CStream::FillBuffers()
{
int32 i = 0;
@@ -1178,17 +1512,33 @@ void CStream::ClearBuffers()
alSourceUnqueueBuffers(m_pAlSources[1], 1, &value);
}
-bool CStream::Setup(bool imSureQueueIsEmpty)
+bool CStream::Setup(bool imSureQueueIsEmpty, bool lock)
{
if ( IsOpened() )
{
- alSourcei(m_pAlSources[0], AL_LOOPING, AL_FALSE);
- alSourcei(m_pAlSources[1], AL_LOOPING, AL_FALSE);
+#ifdef MULTITHREADED_AUDIO
+ if (lock)
+ m_mutex.lock();
+#endif
+
if (!imSureQueueIsEmpty) {
- SetPlay(false);
+ Stop();
ClearBuffers();
}
+#ifdef MULTITHREADED_AUDIO
+ if (MusicManager.m_nMusicMode == MUSICMODE_CUTSCENE) {
+ m_pSoundFile->Seek(0);
+ } else {
+ m_bDoSeek = true;
+ m_SeekPos = 0;
+ }
+
+ if (lock)
+ m_mutex.unlock();
+#else
m_pSoundFile->Seek(0);
+#endif
+
//SetPosition(0.0f, 0.0f, 0.0f);
SetPitch(1.0f);
//SetPan(m_nPan);
@@ -1241,8 +1591,12 @@ void CStream::SetPlay(bool state)
void CStream::Start()
{
if ( !HasSource() ) return;
- if ( FillBuffers() != 0 )
- SetPlay(true);
+
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::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();
}
void CStream::Stop()
@@ -1264,9 +1618,23 @@ void CStream::Update()
if ( !m_bPaused )
{
- ALint totalBuffers[2] = { 0, 0 };
- ALint buffersProcessed[2] = { 0, 0 };
+ bool buffersQueuedAndStarted = false;
+ bool buffersQueuedButNotStarted = false;
+#ifdef MULTITHREADED_AUDIO
+ // Put it in here because we need totalBuffers after queueing to decide when to loop audio
+ if (m_bActive)
+ {
+ buffersQueuedAndStarted = QueueBuffers();
+ if(buffersQueuedAndStarted) {
+ SetPlay(true);
+ }
+ }
+#endif
+
+ ALint totalBuffers[2] = {0, 0};
+ ALint buffersProcessed[2] = {0, 0};
+
// Relying a lot on left buffer states in here
do
@@ -1278,44 +1646,66 @@ void CStream::Update()
alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &totalBuffers[1]);
alGetSourcei(m_pAlSources[1], AL_BUFFERS_PROCESSED, &buffersProcessed[1]);
} while (buffersProcessed[0] != buffersProcessed[1]);
-
+
assert(buffersProcessed[0] == buffersProcessed[1]);
// Correcting OpenAL concepts here:
// AL_BUFFERS_QUEUED = Number of *all* buffers in queue, including processed, processing and pending
// AL_BUFFERS_PROCESSED = Index of the buffer being processing right now. Buffers coming after that(have greater index) are pending buffers.
// which means: totalBuffers[0] - buffersProcessed[0] = pending buffers
-
- bool buffersRefilled = false;
-
+
// We should wait queue to be cleared to loop track, because position calculation relies on queue.
if (m_nLoopCount != 1 && m_bActive && totalBuffers[0] == 0)
{
- Setup(true);
- buffersRefilled = FillBuffers() != 0;
- if (m_nLoopCount != 0)
- m_nLoopCount--;
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::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
+ {
+ Setup(true, false);
+ BuffersShouldBeFilled(); // will also call SetPlay(true)
+ if (m_nLoopCount != 0)
+ m_nLoopCount--;
+ }
}
else
{
- while( buffersProcessed[0]-- )
+ static std::queue<std::pair<ALuint, ALuint>> tempFillBuffer;
+
+ while ( buffersProcessed[0]-- )
{
ALuint buffer[2];
alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]);
alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]);
-
- if (m_bActive && FillBuffer(buffer))
+
+ if (m_bActive)
{
- buffersRefilled = true;
- alSourceQueueBuffers(m_pAlSources[0], 1, &buffer[0]);
- alSourceQueueBuffers(m_pAlSources[1], 1, &buffer[1]);
+ tempFillBuffer.push(std::pair<ALuint, ALuint>(buffer[0], buffer[1]));
}
}
+
+ if (m_bActive && buffersProcessed[1])
+ {
+#ifdef MULTITHREADED_AUDIO
+ m_mutex.lock();
+#endif
+ while (!tempFillBuffer.empty()) {
+ auto elem = tempFillBuffer.front();
+ tempFillBuffer.pop();
+ buffersQueuedButNotStarted = BufferShouldBeFilledAndQueued(&elem);
+ }
+#ifdef MULTITHREADED_AUDIO
+ m_mutex.unlock();
+ FlagAsToBeProcessed();
+#endif
+
+ }
}
- // Two reasons: 1-Source may be starved to audio and stopped itself, 2- We're already waiting it to starve and die for looping track!
- if (m_bActive && (buffersRefilled || (totalBuffers[1] - buffersProcessed[1] != 0)))
+ // Source may be starved to audio and stopped itself
+ if (m_bActive && !buffersQueuedAndStarted && (buffersQueuedButNotStarted || (totalBuffers[1] - buffersProcessed[1] != 0)))
SetPlay(true);
}
}
@@ -1324,28 +1714,45 @@ void CStream::ProviderInit()
{
if ( m_bReset )
{
- if ( Setup(true) )
+ if ( Setup(true, false) ) // lock not needed, thread can't process streams with m_bReset set
{
SetPan(m_nPan);
SetVolume(m_nVolume);
SetLoopCount(m_nLoopCount);
SetPosMS(m_nPosBeforeReset);
- if (m_bActive)
- FillBuffers();
- SetPlay(m_bActive);
- if ( m_bPaused )
+#ifdef MULTITHREADED_AUDIO
+ std::unique_lock<std::mutex> lock(m_mutex);
+#endif
+ if(m_bActive)
+ BuffersShouldBeFilled();
+
+ if (m_bPaused)
Pause();
+
+ m_bReset = false;
+
+ } else {
+#ifdef MULTITHREADED_AUDIO
+ std::unique_lock<std::mutex> lock(m_mutex);
+#endif
+ m_bReset = false;
}
-
- m_bReset = false;
}
}
void CStream::ProviderTerm()
{
+#ifdef MULTITHREADED_AUDIO
+ std::lock_guard<std::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);
+ tsQueue<std::pair<ALuint, ALuint>>().swapNts(m_queueBuffers); // stream mutex is already acquired, thus Nts variant
+#endif
m_bReset = true;
m_nPosBeforeReset = GetPosMS();
-
+
+ Stop();
ClearBuffers();
}
diff --git a/src/audio/oal/stream.h b/src/audio/oal/stream.h
index 9a2a2fbe..f0456925 100644
--- a/src/audio/oal/stream.h
+++ b/src/audio/oal/stream.h
@@ -11,6 +11,7 @@ public:
virtual ~IDecoder() { }
virtual bool IsOpened() = 0;
+ virtual void FileOpen() = 0;
virtual uint32 GetSampleSize() = 0;
virtual uint32 GetSampleCount() = 0;
@@ -48,12 +49,70 @@ public:
uint32 GetLength()
{
+ FileOpen(); // abort deferred init, we need length now - game has to cache audio file sizes
return float(GetSampleCount()) * 1000.0f / float(GetSampleRate());
}
virtual uint32 Decode(void *buffer) = 0;
};
+#ifdef MULTITHREADED_AUDIO
+template <typename T> class tsQueue
+{
+public:
+ tsQueue() : count(0) { }
+
+ void push(const T &value)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_queue.push(value);
+ count++;
+ }
+
+ bool peekPop(T *retVal)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ if (count == 0)
+ return false;
+
+ *retVal = m_queue.front();
+ m_queue.pop();
+ count--;
+ return true;
+ }
+
+ void swapNts(tsQueue<T> &replaceWith)
+ {
+ m_queue.swap(replaceWith.m_queue);
+ replaceWith.count = count;
+ }
+
+ /*
+ void swapTs(tsQueue<T> &replaceWith)
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ std::lock_guard<std::mutex> lock2(replaceWith.m_mutex);
+ swapNts(replaceWith);
+ }
+ */
+
+ bool emptyNts()
+ {
+ return count == 0;
+ }
+ /*
+ bool emptyTs()
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ return emptyNts();
+ }
+ */
+
+ std::queue<T> m_queue;
+ int count;
+ mutable std::mutex m_mutex;
+};
+#endif
class CStream
{
char m_aFilename[128];
@@ -63,6 +122,17 @@ class CStream
bool m_bPaused;
bool m_bActive;
+public:
+#ifdef MULTITHREADED_AUDIO
+ std::mutex 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;
+#endif
+
void *m_pBuffer;
bool m_bReset;
@@ -72,7 +142,14 @@ class CStream
int32 m_nLoopCount;
IDecoder *m_pSoundFile;
-
+
+ void BuffersShouldBeFilled(); // all
+ bool BufferShouldBeFilledAndQueued(std::pair<ALuint, ALuint>*); // two (left-right)
+#ifdef MULTITHREADED_AUDIO
+ void FlagAsToBeProcessed(bool close = false);
+ bool QueueBuffers();
+#endif
+
bool HasSource();
void SetPosition(int i, float x, float y, float z);
void SetPitch(float pitch);
@@ -81,15 +158,17 @@ class CStream
void SetPlay(bool state);
bool FillBuffer(ALuint *alBuffer);
- int32 FillBuffers();
+ int32 FillBuffers();
void ClearBuffers();
-public:
+//public:
static void Initialise();
static void Terminate();
- CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate = 32000);
+ CStream(ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS]);
~CStream();
void Delete();
+ bool Open(const char *filename, uint32 overrideSampleRate = 32000);
+ void Close();
bool IsOpened();
bool IsPlaying();
@@ -100,12 +179,11 @@ public:
uint32 GetPosMS();
uint32 GetLengthMS();
- bool Setup(bool imSureQueueIsEmpty = false);
+ bool Setup(bool imSureQueueIsEmpty = false, bool lock = true);
void Start();
void Stop();
void Update(void);
void SetLoopCount(int32);
-
void ProviderInit();
void ProviderTerm();
diff --git a/src/audio/sampman_oal.cpp b/src/audio/sampman_oal.cpp
index 7fb84965..fdd449f7 100644
--- a/src/audio/sampman_oal.cpp
+++ b/src/audio/sampman_oal.cpp
@@ -34,6 +34,13 @@
#include "oal/oal_utils.h"
#include "oal/aldlist.h"
#include "oal/channel.h"
+
+#include <utility>
+#ifdef MULTITHREADED_AUDIO
+#include <mutex>
+#include <queue>
+#include <condition_variable>
+#endif
#include "oal/stream.h"
#include "AudioManager.h"
@@ -515,14 +522,11 @@ _FindMP3s(void)
continue;
}
}
- aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]);
-
- if (aStream[0] && aStream[0]->IsOpened())
+ if (aStream[0] && aStream[0]->Open(filepath))
{
total_ms = aStream[0]->GetLengthMS();
- delete aStream[0];
- aStream[0] = NULL;
-
+ aStream[0]->Close();
+
OutputDebugString(fd.cFileName);
_pMP3List = new tMP3Entry;
@@ -573,14 +577,13 @@ _FindMP3s(void)
else
bShortcut = FALSE;
- aStream[0] = new CStream(filepath, ALStreamSources[0], ALStreamBuffers[0]);
-
- if (aStream[0] && aStream[0]->IsOpened())
+ if (aStream[0] && aStream[0]->Open(filepath))
{
total_ms = aStream[0]->GetLengthMS();
- delete aStream[0];
- aStream[0] = NULL;
+ aStream[0]->Close();
+ OutputDebugString(fd.cFileName);
+
pList->pNext = new tMP3Entry;
tMP3Entry *e = pList->pNext;
@@ -732,6 +735,10 @@ cSampleManager::Initialise(void)
return TRUE;
EFXInit();
+
+ for(int i = 0; i < MAX_STREAMS; i++)
+ aStream[i] = new CStream(ALStreamSources[i], ALStreamBuffers[i]);
+
CStream::Initialise();
{
@@ -885,14 +892,12 @@ cSampleManager::Initialise(void)
debug("Cannot load audio cache\n");
#endif
- for(int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++) {
- aStream[0] = new CStream(StreamedNameTable[i], ALStreamSources[0], ALStreamBuffers[0], IsThisTrackAt16KHz(i) ? 16000 : 32000);
-
- if(aStream[0] && aStream[0]->IsOpened()) {
+ for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
+ {
+ if ( aStream[0] && aStream[0]->Open(StreamedNameTable[i], IsThisTrackAt16KHz(i) ? 16000 : 32000) )
+ {
uint32 tatalms = aStream[0]->GetLengthMS();
- delete aStream[0];
- aStream[0] = NULL;
-
+ aStream[0]->Close();
nStreamLength[i] = tatalms;
} else
USERERROR("Can't open '%s'\n", StreamedNameTable[i]);
@@ -934,12 +939,13 @@ cSampleManager::Initialise(void)
{
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
- aStream[i] = NULL;
+ aStream[i]->Close();
+
nStreamVolume[i] = 100;
nStreamPan[i] = 63;
}
}
-
+
{
_bSampmanInitialised = TRUE;
@@ -1021,15 +1027,8 @@ void
cSampleManager::Terminate(void)
{
for (int32 i = 0; i < MAX_STREAMS; i++)
- {
- CStream *stream = aStream[i];
- if (stream)
- {
- delete stream;
- aStream[i] = NULL;
- }
- }
-
+ aStream[i]->Close();
+
for ( int32 i = 0; i < NUM_CHANNELS; i++ )
aChannel[i].Term();
@@ -1079,6 +1078,9 @@ cSampleManager::Terminate(void)
CStream::Terminate();
+ for(int32 i = 0; i < MAX_STREAMS; i++)
+ delete aStream[i];
+
if ( nSampleBankMemoryStartAddress[SFX_BANK_0] != 0 )
{
free((void *)nSampleBankMemoryStartAddress[SFX_BANK_0]);
@@ -1605,22 +1607,16 @@ cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
if ( nFile < TOTAL_STREAMED_SOUNDS )
{
- if ( aStream[nStream] )
- {
- delete aStream[nStream];
- aStream[nStream] = NULL;
- }
-
+ CStream *stream = aStream[nStream];
+
+ stream->Close();
+
strcpy(filename, StreamedNameTable[nFile]);
- CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
- ASSERT(stream != NULL);
-
- aStream[nStream] = stream;
+ stream->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
if ( !stream->Setup() )
{
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
}
}
}
@@ -1632,7 +1628,7 @@ cSampleManager::PauseStream(bool8 nPauseFlag, uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
stream->SetPause(nPauseFlag != FALSE);
}
@@ -1645,12 +1641,9 @@ cSampleManager::StartPreloadedStreamedFile(uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
- if ( stream->IsOpened() )
- {
- stream->Start();
- }
+ stream->Start();
}
}
@@ -1664,11 +1657,8 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
if ( nFile >= TOTAL_STREAMED_SOUNDS )
return FALSE;
- if ( aStream[nStream] )
- {
- delete aStream[nStream];
- aStream[nStream] = NULL;
- }
+ aStream[nStream]->Close();
+
if ( nFile == STREAMED_SOUND_RADIO_MP3_PLAYER )
{
do
@@ -1684,12 +1674,10 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
if(!_GetMP3PosFromStreamPos(&position, &e) && !e) {
nFile = 0;
strcpy(filename, StreamedNameTable[nFile]);
-
- CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
-
- aStream[nStream] = stream;
- if (stream->Setup()) {
+ CStream *stream = aStream[nStream];
+ stream->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
+ if ( stream->Setup() ) {
if (position != 0)
stream->SetPosMS(position);
@@ -1697,19 +1685,18 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
return TRUE;
} else {
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
}
return FALSE;
} else {
- if ( e->pLinkPath != NULL )
- aStream[nStream] = new CStream(e->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
+ if (e->pLinkPath != NULL)
+ aStream[nStream]->Open(e->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
else {
strcpy(filename, _mp3DirectoryPath);
strcat(filename, e->aFilename);
-
- aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+
+ aStream[nStream]->Open(filename);
}
if (aStream[nStream]->Setup()) {
@@ -1721,8 +1708,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
_bIsMp3Active = TRUE;
return TRUE;
} else {
- delete aStream[nStream];
- aStream[nStream] = NULL;
+ aStream[nStream]->Close();
}
// fall through, start playing from another song
}
@@ -1741,9 +1727,8 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
_bIsMp3Active = 0;
strcpy(filename, StreamedNameTable[nFile]);
- CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
-
- aStream[nStream] = stream;
+ CStream* stream = aStream[nStream];
+ stream->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
if (stream->Setup()) {
if (position != 0)
@@ -1753,19 +1738,17 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
return TRUE;
} else {
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
}
return FALSE;
}
}
- if(mp3->pLinkPath != NULL)
- aStream[nStream] = new CStream(mp3->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
+ if (mp3->pLinkPath != NULL)
+ aStream[nStream]->Open(mp3->pLinkPath, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
else {
strcpy(filename, _mp3DirectoryPath);
strcat(filename, mp3->aFilename);
-
- aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ aStream[nStream]->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
}
if (aStream[nStream]->Setup()) {
@@ -1775,8 +1758,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
#endif
return TRUE;
} else {
- delete aStream[nStream];
- aStream[nStream] = NULL;
+ aStream[nStream]->Close();
}
}
@@ -1788,9 +1770,9 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
}
strcpy(filename, StreamedNameTable[nFile]);
- CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
+ CStream *stream = aStream[nStream];
- aStream[nStream] = stream;
+ aStream[nStream]->Open(filename, IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
if ( stream->Setup() ) {
if (position != 0)
@@ -1800,8 +1782,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
return TRUE;
} else {
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
}
return FALSE;
}
@@ -1813,14 +1794,10 @@ cSampleManager::StopStreamedFile(uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
- {
- delete stream;
- aStream[nStream] = NULL;
+ stream->Close();
- if ( nStream == 0 )
- _bIsMp3Active = FALSE;
- }
+ if ( nStream == 0 )
+ _bIsMp3Active = FALSE;
}
int32
@@ -1830,7 +1807,7 @@ cSampleManager::GetStreamedFilePosition(uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
if ( _bIsMp3Active )
{
@@ -1868,7 +1845,7 @@ cSampleManager::SetStreamedVolumeAndPan(uint8 nVolume, uint8 nPan, uint8 nEffect
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
if ( nEffectFlag )
stream->SetVolume(m_nEffectsFadeVolume*nVolume*m_nEffectsVolume >> 14);
@@ -1894,7 +1871,7 @@ cSampleManager::IsStreamPlaying(uint8 nStream)
CStream *stream = aStream[nStream];
- if ( stream )
+ if ( stream->IsOpened() )
{
if ( stream->IsPlaying() )
return TRUE;
@@ -1910,7 +1887,7 @@ cSampleManager::Service(void)
{
CStream *stream = aStream[i];
- if ( stream )
+ if ( stream->IsOpened() )
stream->Update();
}
int refCount = CChannel::channelsThatNeedService;
diff --git a/src/control/CarCtrl.cpp b/src/control/CarCtrl.cpp
index 0516e214..35580053 100644
--- a/src/control/CarCtrl.cpp
+++ b/src/control/CarCtrl.cpp
@@ -77,7 +77,7 @@ int32 CCarCtrl::NumRandomCars;
int32 CCarCtrl::NumParkedCars;
int32 CCarCtrl::NumPermanentCars;
int8 CCarCtrl::CountDownToCarsAtStart;
-int32 CCarCtrl::MaxNumberOfCarsInUse = 12;
+int32 CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS;
uint32 CCarCtrl::LastTimeLawEnforcerCreated;
uint32 CCarCtrl::LastTimeFireTruckCreated;
uint32 CCarCtrl::LastTimeAmbulanceCreated;
diff --git a/src/core/Frontend.cpp b/src/core/Frontend.cpp
index 7ff80697..ecb893b4 100644
--- a/src/core/Frontend.cpp
+++ b/src/core/Frontend.cpp
@@ -336,6 +336,7 @@ const char* MenuFilenames[][2] = {
CFont::SetScale(MENU_X(SMALLESTTEXT_X_SCALE), MENU_Y(SMALLESTTEXT_Y_SCALE)); \
CFont::SetFontStyle(FONT_LOCALE(FONT_BANK));
+// value must be between 0.0-1.0
#define ProcessSlider(value, increaseAction, decreaseAction, hoverStartX, hoverEndX) \
do { \
lastActiveBarX = DisplaySlider(MENU_X_RIGHT_ALIGNED(MENUSLIDER_X + columnWidth), MENU_Y(bitAboveNextItemY), MENU_Y(smallestSliderBar), MENU_Y(usableLineHeight), MENU_X(MENUSLIDER_UNK), value); \
@@ -489,7 +490,7 @@ CMenuManager::ThingsToDoBeforeGoingBack()
option.m_CFODynamic->buttonPressFunc(FEOPTION_ACTION_FOCUSLOSS);
if (option.m_Action == MENUACTION_CFO_SELECT && option.m_CFOSelect->onlyApplyOnEnter && option.m_CFOSelect->lastSavedValue != option.m_CFOSelect->displayedValue)
- option.m_CFOSelect->displayedValue = *option.m_CFO->value = option.m_CFOSelect->lastSavedValue;
+ option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue;
if (aScreens[m_nCurrScreen].returnPrevPageFunc) {
aScreens[m_nCurrScreen].returnPrevPageFunc();
@@ -898,29 +899,29 @@ CMenuManager::CheckSliderMovement(int value)
{
switch (aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action) {
case MENUACTION_BRIGHTNESS:
- m_PrefsBrightness += value * (512/16);
+ m_PrefsBrightness += value * (512/MENUSLIDER_LOGICAL_BARS);
m_PrefsBrightness = Clamp(m_PrefsBrightness, 0, 511);
break;
case MENUACTION_DRAWDIST:
if(value > 0)
- m_PrefsLOD += ((1.8f - 0.8f) / 16.0f);
+ m_PrefsLOD += ((1.8f - 0.8f) / MENUSLIDER_LOGICAL_BARS);
else
- m_PrefsLOD -= ((1.8f - 0.8f) / 16.0f);
+ m_PrefsLOD -= ((1.8f - 0.8f) / MENUSLIDER_LOGICAL_BARS);
m_PrefsLOD = Clamp(m_PrefsLOD, 0.8f, 1.8f);
CRenderer::ms_lodDistScale = m_PrefsLOD;
break;
case MENUACTION_MUSICVOLUME:
- m_PrefsMusicVolume += value * (128/16);
+ m_PrefsMusicVolume += value * (128/MENUSLIDER_LOGICAL_BARS);
m_PrefsMusicVolume = Clamp(m_PrefsMusicVolume, 0, 127);
DMAudio.SetMusicMasterVolume(m_PrefsMusicVolume);
break;
case MENUACTION_SFXVOLUME:
- m_PrefsSfxVolume += value * (128/16);
+ m_PrefsSfxVolume += value * (128/MENUSLIDER_LOGICAL_BARS);
m_PrefsSfxVolume = Clamp(m_PrefsSfxVolume, 0, 127);
DMAudio.SetEffectsMasterVolume(m_PrefsSfxVolume);
break;
case MENUACTION_MOUSESENS:
- TheCamera.m_fMouseAccelHorzntl += value * 1.0f/200.0f/15.0f; // ???
+ TheCamera.m_fMouseAccelHorzntl += value * 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps
TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f/3200.0f, 1.0f/200.0f);
#ifdef FIX_BUGS
TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f;
@@ -928,6 +929,20 @@ CMenuManager::CheckSliderMovement(int value)
TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl;
#endif
break;
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ case MENUACTION_CFO_SLIDER:
+ {
+ CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption];
+ float oldValue = *(float*)option.m_CFOSlider->value;
+ *(float*)option.m_CFOSlider->value += value * ((option.m_CFOSlider->max - option.m_CFOSlider->min) / MENUSLIDER_LOGICAL_BARS);
+ *(float*)option.m_CFOSlider->value = Clamp(*(float*)option.m_CFOSlider->value, option.m_CFOSlider->min, option.m_CFOSlider->max);
+
+ if (*(float*)option.m_CFOSlider->value != oldValue && option.m_CFOSlider->changeFunc)
+ option.m_CFOSlider->changeFunc(oldValue, *(float*)option.m_CFOSlider->value);
+
+ break;
+ }
+#endif
default:
return;
}
@@ -1001,10 +1016,10 @@ CMenuManager::DisplaySlider(float x, float y, float mostLeftBarSize, float mostR
int lastActiveBarX = 0;
float curBarX = 0.0f;
float spacing = SCREEN_SCALE_X(10.0f);
- for (int i = 0; i < 16; i++) {
- curBarX = i * rectSize/16.0f + x;
+ for (int i = 0; i < MENUSLIDER_BARS; i++) {
+ curBarX = i * rectSize/MENUSLIDER_BARS + x;
- if (i / 16.0f + 1 / 32.0f < progress) {
+ if (i / (float)MENUSLIDER_BARS + 1 / (MENUSLIDER_BARS * 2.f) < progress) {
color = CRGBA(SLIDERON_COLOR.r, SLIDERON_COLOR.g, SLIDERON_COLOR.b, FadeIn(255));
lastActiveBarX = curBarX;
} else
@@ -1012,7 +1027,7 @@ CMenuManager::DisplaySlider(float x, float y, float mostLeftBarSize, float mostR
maxBarHeight = Max(mostLeftBarSize, mostRightBarSize);
- float curBarFreeSpace = ((16 - i) * mostLeftBarSize + i * mostRightBarSize) / 16.0f;
+ float curBarFreeSpace = ((MENUSLIDER_BARS - i) * mostLeftBarSize + i * mostRightBarSize) / (float)MENUSLIDER_BARS;
float left = curBarX;
float top = y + maxBarHeight - curBarFreeSpace;
float right = spacing + curBarX;
@@ -1595,10 +1610,10 @@ CMenuManager::Draw()
// If that was previously selected option, restore it to default value.
// if (m_nCurrOption != lastSelectedOpt && lastSelectedOpt == i)
- option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *option.m_CFO->value;
+ option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *(int8*)option.m_CFO->value;
} else {
- if (option.m_CFOSelect->displayedValue != *option.m_CFO->value)
+ if (option.m_CFOSelect->displayedValue != *(int8*)option.m_CFO->value)
SetHelperText(1); // Enter to apply
else if (m_nHelperTextMsgId == 1)
ResetHelperText(); // Applied
@@ -1606,8 +1621,8 @@ CMenuManager::Draw()
}
// To whom manipulate option.m_CFO->value of select options externally (like RestoreDef functions)
- if (*option.m_CFO->value != option.m_CFOSelect->lastSavedValue)
- option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *option.m_CFO->value;
+ if (*(int8*)option.m_CFO->value != option.m_CFOSelect->lastSavedValue)
+ option.m_CFOSelect->displayedValue = option.m_CFOSelect->lastSavedValue = *(int8*)option.m_CFO->value;
if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0)
option.m_CFOSelect->displayedValue = 0;
@@ -1799,6 +1814,12 @@ CMenuManager::Draw()
case MENUACTION_MOUSESENS:
ProcessSlider(TheCamera.m_fMouseAccelHorzntl * 200.0f, HOVEROPTION_INCREASE_MOUSESENS, HOVEROPTION_DECREASE_MOUSESENS, MENU_X_LEFT_ALIGNED(200.0f), SCREEN_WIDTH);
break;
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ case MENUACTION_CFO_SLIDER:
+ CMenuScreenCustom::CMenuEntry &option = aScreens[m_nCurrScreen].m_aEntries[i];
+ ProcessSlider((*(float*)option.m_CFOSlider->value - option.m_CFOSlider->min) / (option.m_CFOSlider->max - option.m_CFOSlider->min), HOVEROPTION_INCREASE_CFO_SLIDER, HOVEROPTION_DECREASE_CFO_SLIDER, MENU_X_LEFT_ALIGNED(170.0f), SCREEN_WIDTH);
+ break;
+#endif
}
// Needed after the bug fix in Font.cpp
@@ -4477,7 +4498,7 @@ CMenuManager::ProcessButtonPresses(void)
#ifndef TIDY_UP_PBP
switch (m_nHoverOption) {
case HOVEROPTION_INCREASE_BRIGHTNESS:
- m_PrefsBrightness = m_PrefsBrightness + 32;
+ m_PrefsBrightness = m_PrefsBrightness + (512 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsBrightness < 0) {
m_PrefsBrightness = 0;
}
@@ -4487,7 +4508,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_DECREASE_BRIGHTNESS:
- m_PrefsBrightness = m_PrefsBrightness - 32;
+ m_PrefsBrightness = m_PrefsBrightness - (512 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsBrightness < 0) {
m_PrefsBrightness = 0;
}
@@ -4497,25 +4518,25 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_INCREASE_DRAWDIST:
- m_PrefsLOD = m_PrefsLOD + (1.0f / 16);
+ m_PrefsLOD = m_PrefsLOD + (1.0f / MENUSLIDER_LOGICAL_BARS);
m_PrefsLOD = min(1.8f, m_PrefsLOD);
CRenderer::ms_lodDistScale = m_PrefsLOD;
SaveSettings();
break;
case HOVEROPTION_DECREASE_DRAWDIST:
- m_PrefsLOD = m_PrefsLOD - (1.0f / 16);
+ m_PrefsLOD = m_PrefsLOD - (1.0f / MENUSLIDER_LOGICAL_BARS);
m_PrefsLOD = max(0.8f, m_PrefsLOD);
CRenderer::ms_lodDistScale = m_PrefsLOD;
SaveSettings();
break;
case HOVEROPTION_INCREASE_MUSICVOLUME:
- m_PrefsMusicVolume = m_PrefsMusicVolume + 8;
+ m_PrefsMusicVolume = m_PrefsMusicVolume + (128 / MENUSLIDER_LOGICAL_BARS);
m_PrefsMusicVolume = Clamp(m_PrefsMusicVolume, 0, 127);
DMAudio.SetMusicMasterVolume(uchar)(m_PrefsMusicVolume);
SaveSettings();
break;
case HOVEROPTION_DECREASE_MUSICVOLUME:
- m_PrefsMusicVolume = m_PrefsMusicVolume - 8;
+ m_PrefsMusicVolume = m_PrefsMusicVolume - (128 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsMusicVolume < 0) {
m_PrefsMusicVolume = 0;
}
@@ -4526,7 +4547,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_INCREASE_SFXVOLUME:
- m_PrefsSFXVolume = m_PrefsSFXVolume + 8;
+ m_PrefsSFXVolume = m_PrefsSFXVolume + (128 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsSFXVolume < 0) {
m_PrefsSFXVolume = 0;
}
@@ -4537,7 +4558,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_DECREASE_SFXVOLUME:
- m_PrefsSFXVolume = m_PrefsSFXVolume - 8;
+ m_PrefsSFXVolume = m_PrefsSFXVolume - (128 / MENUSLIDER_LOGICAL_BARS);
if (m_PrefsSFXVolume < 0) {
m_PrefsSFXVolume = 0;
}
@@ -4548,7 +4569,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_INCREASE_MOUSESENS:
- TheCamera.m_fMouseAccelHorzntl += (1.0f / 3000);
+ TheCamera.m_fMouseAccelHorzntl += 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps
TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f / 3200, 1.0f / 200);
#ifdef FIX_BUGS
TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f;
@@ -4558,7 +4579,7 @@ CMenuManager::ProcessButtonPresses(void)
SaveSettings();
break;
case HOVEROPTION_DECREASE_MOUSESENS:
- TheCamera.m_fMouseAccelHorzntl -= (1.0f / 3000);
+ TheCamera.m_fMouseAccelHorzntl -= 1.0f/200.0f/15.0f; // probably because diving it to 15 instead of 16(MENUSLIDER_LOGICAL_BARS) had more accurate steps
TheCamera.m_fMouseAccelHorzntl = Clamp(TheCamera.m_fMouseAccelHorzntl, 1.0f / 3200, 1.0f / 200);
#ifdef FIX_BUGS
TheCamera.m_fMouseAccelVertical = TheCamera.m_fMouseAccelHorzntl + 0.0005f;
@@ -4575,6 +4596,9 @@ CMenuManager::ProcessButtonPresses(void)
case HOVEROPTION_INCREASE_MUSICVOLUME:
case HOVEROPTION_INCREASE_SFXVOLUME:
case HOVEROPTION_INCREASE_MOUSESENS:
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ case HOVEROPTION_INCREASE_CFO_SLIDER:
+#endif
CheckSliderMovement(1);
break;
case HOVEROPTION_DECREASE_BRIGHTNESS:
@@ -4582,6 +4606,9 @@ CMenuManager::ProcessButtonPresses(void)
case HOVEROPTION_DECREASE_MUSICVOLUME:
case HOVEROPTION_DECREASE_SFXVOLUME:
case HOVEROPTION_DECREASE_MOUSESENS:
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ case HOVEROPTION_DECREASE_CFO_SLIDER:
+#endif
CheckSliderMovement(-1);
break;
}
@@ -4612,7 +4639,11 @@ CMenuManager::ProcessButtonPresses(void)
|| CPad::GetPad(0)->GetAnaloguePadLeftJustUp() || CPad::GetPad(0)->GetAnaloguePadRightJustUp()
|| CPad::GetPad(0)->GetMouseWheelUpJustDown() || CPad::GetPad(0)->GetMouseWheelDownJustDown()) {
int option = aScreens[m_nCurrScreen].m_aEntries[m_nCurrOption].m_Action;
- if (option == MENUACTION_BRIGHTNESS || option == MENUACTION_DRAWDIST)
+ if (option == MENUACTION_BRIGHTNESS || option == MENUACTION_DRAWDIST
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ || option == MENUACTION_CFO_SLIDER
+#endif
+ )
DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0);
else if (option == MENUACTION_SFXVOLUME)
DMAudio.PlayFrontEndSound(SOUND_FRONTEND_AUDIO_TEST, 0);
@@ -4775,7 +4806,12 @@ CMenuManager::ProcessButtonPresses(void)
} else if (option != MENUACTION_CHANGEMENU && option != MENUACTION_BRIGHTNESS && option != MENUACTION_DRAWDIST
&& option != MENUACTION_MUSICVOLUME && option != MENUACTION_SFXVOLUME
&& option != MENUACTION_CHECKSAVE && option != MENUACTION_UNK24
- && option != MENUACTION_MOUSESENS && option != MENUACTION_SCREENRES) {
+ && option != MENUACTION_MOUSESENS && option != MENUACTION_SCREENRES
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ && option != MENUACTION_CFO_SLIDER
+#endif
+ )
+ {
DMAudio.PlayFrontEndSound(SOUND_FRONTEND_MENU_SETTING_CHANGE, 0);
}
@@ -5166,9 +5202,9 @@ CMenuManager::ProcessButtonPresses(void)
if (option.m_CFOSelect->displayedValue >= option.m_CFOSelect->numRightTexts || option.m_CFOSelect->displayedValue < 0)
option.m_CFOSelect->displayedValue = 0;
}
- int8 oldValue = *option.m_CFO->value;
+ int8 oldValue = *(int8*)option.m_CFO->value;
- *option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue;
+ *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue;
// Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO
// if (option.m_CFOSelect->save)
@@ -5412,9 +5448,9 @@ CMenuManager::ProcessButtonPresses(void)
option.m_CFOSelect->displayedValue = option.m_CFOSelect->numRightTexts - 1;
}
if (!option.m_CFOSelect->onlyApplyOnEnter) {
- int8 oldValue = *option.m_CFO->value;
+ int8 oldValue = *(int8*)option.m_CFO->value;
- *option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue;
+ *(int8*)option.m_CFO->value = option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue;
// Now everything is saved in .ini, and LOAD_INI_SETTINGS is fundamental for CFO
// if (option.m_CFOSelect->save)
diff --git a/src/core/Frontend.h b/src/core/Frontend.h
index 5c3523ab..32e5ef9d 100644
--- a/src/core/Frontend.h
+++ b/src/core/Frontend.h
@@ -25,6 +25,9 @@
#define MENUSLIDER_X 256.0f
#define MENUSLIDER_UNK 256.0f
+#define MENUSLIDER_BARS 16
+#define MENUSLIDER_LOGICAL_BARS MENUSLIDER_BARS
+
#define BIGTEXT_X_SCALE 0.75f // For FONT_HEADING
#define BIGTEXT_Y_SCALE 0.9f
#define MEDIUMTEXT_X_SCALE 0.55f // For FONT_HEADING
@@ -256,6 +259,7 @@ enum eMenuScreen
enum eMenuAction
{
#ifdef CUSTOM_FRONTEND_OPTIONS
+ MENUACTION_CFO_SLIDER = -3,
MENUACTION_CFO_SELECT = -2,
MENUACTION_CFO_DYNAMIC = -1,
#endif
@@ -424,6 +428,10 @@ enum eCheckHover
HOVEROPTION_DECREASE_SFXVOLUME,
HOVEROPTION_INCREASE_MOUSESENS,
HOVEROPTION_DECREASE_MOUSESENS,
+#ifdef CUSTOM_FRONTEND_OPTIONS
+ HOVEROPTION_INCREASE_CFO_SLIDER,
+ HOVEROPTION_DECREASE_CFO_SLIDER,
+#endif
HOVEROPTION_NOT_HOVERING,
};
@@ -493,7 +501,7 @@ struct CCustomScreenLayout {
struct CCFO
{
- int8 *value;
+ void *value;
const char *saveCat;
const char *save;
};
@@ -524,6 +532,24 @@ struct CCFOSelect : CCFO
}
};
+// Value is float in here
+struct CCFOSlider : CCFO
+{
+ ChangeFuncFloat changeFunc;
+ float min;
+ float max;
+
+ CCFOSlider() {};
+ CCFOSlider(float* value, const char* saveCat, const char* save, float min, float max, ChangeFuncFloat changeFunc = nil){
+ this->value = value;
+ this->saveCat = saveCat;
+ this->save = save;
+ this->changeFunc = changeFunc;
+ this->min = min;
+ this->max = max;
+ }
+};
+
struct CCFODynamic : CCFO
{
DrawFunc drawFunc;
@@ -555,6 +581,7 @@ struct CMenuScreenCustom
CCFO *m_CFO; // for initializing
CCFOSelect *m_CFOSelect;
CCFODynamic *m_CFODynamic;
+ CCFOSlider *m_CFOSlider;
};
int32 m_SaveSlot; // eSaveSlot
int32 m_TargetMenu; // eMenuScreen
diff --git a/src/core/Game.cpp b/src/core/Game.cpp
index f6156a4c..b3dd1eda 100644
--- a/src/core/Game.cpp
+++ b/src/core/Game.cpp
@@ -409,7 +409,11 @@ bool CGame::Initialise(const char* datFile)
#endif
#ifndef GTA_PS2
- CIniFile::LoadIniFile();
+#ifdef PED_CAR_DENSITY_SLIDERS
+ // Load density values from gta3.ini only if our re3.ini have them 1.f
+ if (CIniFile::PedNumberMultiplier == 1.f && CIniFile::CarNumberMultiplier == 1.f)
+#endif
+ CIniFile::LoadIniFile();
#endif
currLevel = LEVEL_INDUSTRIAL;
diff --git a/src/core/IniFile.cpp b/src/core/IniFile.cpp
index df01b440..524632fe 100644
--- a/src/core/IniFile.cpp
+++ b/src/core/IniFile.cpp
@@ -23,6 +23,6 @@ void CIniFile::LoadIniFile()
CarNumberMultiplier = Min(3.0f, Max(0.5f, CarNumberMultiplier));
CFileMgr::CloseFile(f);
}
- CPopulation::MaxNumberOfPedsInUse = 25.0f * PedNumberMultiplier;
- CCarCtrl::MaxNumberOfCarsInUse = 12.0f * CarNumberMultiplier;
+ CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * PedNumberMultiplier;
+ CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CarNumberMultiplier;
} \ No newline at end of file
diff --git a/src/core/IniFile.h b/src/core/IniFile.h
index 1e30c4de..30dc8c21 100644
--- a/src/core/IniFile.h
+++ b/src/core/IniFile.h
@@ -1,5 +1,8 @@
#pragma once
+#define DEFAULT_MAX_NUMBER_OF_PEDS 25.0f
+#define DEFAULT_MAX_NUMBER_OF_CARS 12.0f
+
class CIniFile
{
public:
diff --git a/src/core/MenuScreensCustom.cpp b/src/core/MenuScreensCustom.cpp
index 6e23f76a..033ed9b9 100644
--- a/src/core/MenuScreensCustom.cpp
+++ b/src/core/MenuScreensCustom.cpp
@@ -26,6 +26,9 @@
#include "ModelInfo.h"
#include "Pad.h"
#include "ControllerConfig.h"
+#include "IniFile.h"
+#include "CarCtrl.h"
+#include "Population.h"
// Menu screens array is at the bottom of the file.
@@ -63,6 +66,15 @@
#define DUALPASS_SELECTOR
#endif
+#ifdef PED_CAR_DENSITY_SLIDERS
+ // 0.2f - 3.4f makes it possible to have 1.0f somewhere inbetween
+ #define DENSITY_SLIDERS \
+ MENUACTION_CFO_SLIDER, "FEM_PED", { new CCFOSlider(&CIniFile::PedNumberMultiplier, "Display", "PedDensity", 0.2f, 3.4f, PedDensityChange) }, \
+ MENUACTION_CFO_SLIDER, "FEM_CAR", { new CCFOSlider(&CIniFile::CarNumberMultiplier, "Display", "CarDensity", 0.2f, 3.4f, CarDensityChange) },
+#else
+ #define DENSITY_SLIDERS
+#endif
+
#ifdef NO_ISLAND_LOADING
#define ISLAND_LOADING_SELECTOR MENUACTION_CFO_SELECT, "FEM_ISL", { new CCFOSelect((int8*)&CMenuManager::m_PrefsIslandLoading, "Graphics", "IslandLoading", islandLoadingOpts, ARRAY_SIZE(islandLoadingOpts), true, IslandLoadingAfterChange) },
#else
@@ -145,6 +157,9 @@ void RestoreDefDisplay(int8 action) {
#ifdef FREE_CAM
TheCamera.bFreeCam = false;
#endif
+ #ifdef PED_CAR_DENSITY_SLIDERS
+ CIniFile::LoadIniFile();
+ #endif
#ifdef GRAPHICS_MENU_OPTIONS // otherwise Frontend will handle those
CMenuManager::m_PrefsBrightness = 256;
CMenuManager::m_PrefsLOD = 1.2f;
@@ -195,6 +210,16 @@ void IslandLoadingAfterChange(int8 before, int8 after) {
}
#endif
+#ifdef PED_CAR_DENSITY_SLIDERS
+void PedDensityChange(float before, float after) {
+ CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * after;
+}
+
+void CarDensityChange(float before, float after) {
+ CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * after;
+}
+#endif
+
#ifndef MULTISAMPLING
void GraphicsGoBack() {
}
@@ -423,6 +448,7 @@ CMenuScreenCustom aScreens[MENUPAGES] = {
{ "FET_DIS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil,
MENUACTION_BRIGHTNESS, "FED_BRI", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
MENUACTION_DRAWDIST, "FEM_LOD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
+ DENSITY_SLIDERS
MENUACTION_FRAMESYNC, "FEM_VSC", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
MENUACTION_FRAMELIMIT, "FEM_FRM", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
#ifndef EXTENDED_COLOURFILTER
@@ -447,6 +473,7 @@ CMenuScreenCustom aScreens[MENUPAGES] = {
{ "FET_DIS", MENUPAGE_OPTIONS, MENUPAGE_OPTIONS, nil, nil,
MENUACTION_BRIGHTNESS, "FED_BRI", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
MENUACTION_DRAWDIST, "FEM_LOD", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
+ DENSITY_SLIDERS
CUTSCENE_BORDERS_TOGGLE
FREE_CAM_TOGGLE
MENUACTION_SUBTITLES, "FED_SUB", { nil, SAVESLOT_NONE, MENUPAGE_DISPLAY_SETTINGS },
diff --git a/src/core/config.h b/src/core/config.h
index 00d7a938..a8b83577 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -341,6 +341,7 @@ enum Config {
# define CUTSCENE_BORDERS_SWITCH
# define MULTISAMPLING // adds MSAA option
# define INVERT_LOOK_FOR_PAD // add bInvertLook4Pad from VC
+# define PED_CAR_DENSITY_SLIDERS
# endif
#endif
@@ -401,6 +402,7 @@ enum Config {
//#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
#ifdef AUDIO_OPUS
#define AUDIO_OAL_USE_OPUS // enable support of opus files
@@ -511,6 +513,7 @@ enum Config {
#undef CUTSCENE_BORDERS_SWITCH
#undef MULTISAMPLING
#undef INVERT_LOOK_FOR_PAD
+#undef PED_CAR_DENSITY_SLIDERS
#undef USE_DEBUG_SCRIPT_LOADER
#undef USE_MEASUREMENTS_IN_METERS
@@ -528,7 +531,11 @@ enum Config {
#undef CANCELLABLE_CAR_ENTER
#undef IMPROVED_CAMERA
#undef FREE_CAM
+
#undef RADIO_SCROLL_TO_PREV_STATION
-#undef BIG_IMG
+#undef AUDIO_CACHE
#undef PS2_AUDIO_CHANNELS
+#undef PAUSE_RADIO_IN_FRONTEND
+#undef MULTITHREADED_AUDIO
+#undef BIG_IMG
#endif
diff --git a/src/core/re3.cpp b/src/core/re3.cpp
index fe0347d9..ef045eed 100644
--- a/src/core/re3.cpp
+++ b/src/core/re3.cpp
@@ -41,6 +41,9 @@
#include "Camera.h"
#include "MBlur.h"
#include "ControllerConfig.h"
+#include "CarCtrl.h"
+#include "Population.h"
+#include "IniFile.h"
#ifdef DETECT_JOYSTICK_MENU
#include "crossplatform.h"
@@ -533,22 +536,29 @@ bool LoadINISettings()
// CFO check
if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) {
- // CFO only supports saving uint8 right now
-
// Migrate from old .ini to new .ini
- if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, option.m_CFO->value))
+ // Old values can only be int8, new ones can contain float if it is slider
+ if (migrate && ReadIniIfExists("FrontendOptions", option.m_CFO->save, (int8*)option.m_CFO->value))
cfg.remove("FrontendOptions", option.m_CFO->save);
+ else if (option.m_Action == MENUACTION_CFO_SLIDER)
+ ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (float*)option.m_CFO->value);
else
- ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, option.m_CFO->value);
+ ReadIniIfExists(option.m_CFO->saveCat, option.m_CFO->save, (int8*)option.m_CFO->value);
if (option.m_Action == MENUACTION_CFO_SELECT) {
- option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *option.m_CFO->value;
+ option.m_CFOSelect->lastSavedValue = option.m_CFOSelect->displayedValue = *(int8*)option.m_CFO->value;
}
}
}
}
#endif
+ // Fetched in above block, but needs evaluation
+#ifdef PED_CAR_DENSITY_SLIDERS
+ CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS * CIniFile::PedNumberMultiplier;
+ CCarCtrl::MaxNumberOfCarsInUse = DEFAULT_MAX_NUMBER_OF_CARS * CIniFile::CarNumberMultiplier;
+#endif
+
return true;
}
@@ -623,8 +633,10 @@ void SaveINISettings()
break;
if (option.m_Action < MENUACTION_NOTHING && option.m_CFO->save) {
- // Beware: CFO only supports saving uint8 right now
- StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *option.m_CFO->value);
+ if (option.m_Action == MENUACTION_CFO_SLIDER)
+ StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(float*)option.m_CFO->value);
+ else
+ StoreIni(option.m_CFO->saveCat, option.m_CFO->save, *(int8*)option.m_CFO->value);
}
}
}
diff --git a/src/entities/Physical.cpp b/src/entities/Physical.cpp
index 182ff46b..591fb501 100644
--- a/src/entities/Physical.cpp
+++ b/src/entities/Physical.cpp
@@ -480,6 +480,10 @@ CPhysical::ApplySpringDampening(float damping, CVector &springDir, CVector &poin
{
float speedA = DotProduct(speed, springDir);
float speedB = DotProduct(GetSpeed(point), springDir);
+#ifdef FIX_BUGS
+ if (speedB == 0.0f)
+ return true;
+#endif
float step = Min(CTimer::GetTimeStep(), 3.0f);
float impulse = -damping * (speedA + speedB)/2.0f * m_fMass * step * 0.53f;
diff --git a/src/extras/frontendoption.h b/src/extras/frontendoption.h
index 8b64335a..a571170f 100644
--- a/src/extras/frontendoption.h
+++ b/src/extras/frontendoption.h
@@ -40,6 +40,8 @@ typedef void (*ReturnPrevPageFunc)();
typedef void (*ChangeFunc)(int8 before, int8 after); // called after updating the value.
// only called on enter if onlyApplyOnEnter set, otherwise called on every value change
+typedef void (*ChangeFuncFloat)(float before, float after); // called after updating the value.
+
// for dynamic options
typedef wchar* (*DrawFunc)(bool* disabled, bool userHovering); // you must return a pointer for right text.
// you can also set *disabled if you want to gray it out.
diff --git a/src/peds/Population.cpp b/src/peds/Population.cpp
index ace6d37c..1d2a5798 100644
--- a/src/peds/Population.cpp
+++ b/src/peds/Population.cpp
@@ -55,7 +55,7 @@ bool CPopulation::ms_bGivePedsWeapons;
int32 CPopulation::m_AllRandomPedsThisType = -1;
float CPopulation::PedDensityMultiplier = 1.0f;
uint32 CPopulation::ms_nTotalMissionPeds;
-int32 CPopulation::MaxNumberOfPedsInUse = 25;
+int32 CPopulation::MaxNumberOfPedsInUse = DEFAULT_MAX_NUMBER_OF_PEDS;
uint32 CPopulation::ms_nNumCivMale;
uint32 CPopulation::ms_nNumCivFemale;
uint32 CPopulation::ms_nNumCop;
@@ -1122,12 +1122,6 @@ CPopulation::ManagePopulation(void)
}
float dist = (ped->GetPosition() - playerPos).Magnitude2D();
-#ifdef SQUEEZE_PERFORMANCE
- if (dist > 50.f)
- ped->bUsesCollision = false;
- else
- ped->bUsesCollision = true;
-#endif
bool pedIsFarAway = false;
if (PedCreationDistMultiplier() * (PED_REMOVE_DIST_SPECIAL * TheCamera.GenerationDistMultiplier) < dist