summaryrefslogtreecommitdiffstats
path: root/src/audio
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio')
-rw-r--r--src/audio/AudioLogic.cpp196
-rw-r--r--src/audio/AudioManager.cpp9
-rw-r--r--src/audio/AudioManager.h2
-rw-r--r--src/audio/oal/stream.cpp809
-rw-r--r--src/audio/oal/stream.h10
-rw-r--r--src/audio/sampman.h6
-rw-r--r--src/audio/sampman_oal.cpp85
7 files changed, 913 insertions, 204 deletions
diff --git a/src/audio/AudioLogic.cpp b/src/audio/AudioLogic.cpp
index 94ca67de..976bbaec 100644
--- a/src/audio/AudioLogic.cpp
+++ b/src/audio/AudioLogic.cpp
@@ -3038,109 +3038,109 @@ cAudioManager::ProcessPedOneShots(cPedParams &params)
switch (sound) {
case SOUND_STEP_START:
case SOUND_STEP_END:
- if (!params.m_pPed->bIsLooking) {
- emittingVol = m_anRandomTable[3] % 15 + 45;
- if (FindPlayerPed() != m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity)
- emittingVol /= 2;
- maxDist = 400.f;
- switch (params.m_pPed->m_nSurfaceTouched) {
- case SURFACE_GRASS:
- sampleIndex = m_anRandomTable[1] % 5 + SFX_FOOTSTEP_GRASS_1;
- break;
- case SURFACE_GRAVEL:
- case SURFACE_MUD_DRY:
- sampleIndex = m_anRandomTable[4] % 5 + SFX_FOOTSTEP_GRAVEL_1;
- break;
- case SURFACE_CAR:
- case SURFACE_GARAGE_DOOR:
- case SURFACE_CAR_PANEL:
- case SURFACE_THICK_METAL_PLATE:
- case SURFACE_SCAFFOLD_POLE:
- case SURFACE_LAMP_POST:
- case SURFACE_FIRE_HYDRANT:
- case SURFACE_GIRDER:
- case SURFACE_METAL_CHAIN_FENCE:
- case SURFACE_CONTAINER:
- case SURFACE_NEWS_VENDOR:
- sampleIndex = m_anRandomTable[0] % 5 + SFX_FOOTSTEP_METAL_1;
- break;
- case SURFACE_SAND:
- sampleIndex = (m_anRandomTable[4] & 3) + SFX_FOOTSTEP_SAND_1;
- break;
- case SURFACE_WATER:
- sampleIndex = (m_anRandomTable[3] & 3) + SFX_FOOTSTEP_WATER_1;
- break;
- case SURFACE_WOOD_CRATES:
- case SURFACE_WOOD_BENCH:
- case SURFACE_WOOD_SOLID:
- sampleIndex = m_anRandomTable[2] % 5 + SFX_FOOTSTEP_WOOD_1;
- break;
- case SURFACE_HEDGE:
- sampleIndex = m_anRandomTable[2] % 5 + SFX_COL_VEG_1;
- break;
- default:
- sampleIndex = m_anRandomTable[2] % 5 + SFX_FOOTSTEP_CONCRETE_1;
- break;
- }
- m_sQueueSample.m_nSampleIndex = sampleIndex;
- m_sQueueSample.m_nBankIndex = SFX_BANK_0;
- m_sQueueSample.m_nCounter = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] - 28;
- m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex);
- m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 17);
- switch (params.m_pPed->m_nMoveState) {
- case PEDMOVE_WALK:
- emittingVol /= 4;
- m_sQueueSample.m_nFrequency = 9 * m_sQueueSample.m_nFrequency / 10;
- break;
- case PEDMOVE_RUN:
- emittingVol /= 2;
- m_sQueueSample.m_nFrequency = 11 * m_sQueueSample.m_nFrequency / 10;
- break;
- case PEDMOVE_SPRINT:
- m_sQueueSample.m_nFrequency = 12 * m_sQueueSample.m_nFrequency / 10;
- break;
- default:
- break;
- }
- m_sQueueSample.m_nReleasingVolumeModificator = 5;
- m_sQueueSample.m_fSpeedMultiplier = 0.0f;
- m_sQueueSample.m_fSoundIntensity = 20.0f;
- m_sQueueSample.m_nLoopCount = 1;
- m_sQueueSample.m_nLoopStart = 0;
- m_sQueueSample.m_nLoopEnd = -1;
- m_sQueueSample.m_nEmittingVolume = emittingVol;
- m_sQueueSample.m_bIs2D = false;
- m_sQueueSample.m_bReleasingSoundFlag = true;
- m_sQueueSample.m_bRequireReflection = true;
+ if (params.m_pPed->bIsInTheAir)
+ continue;
+ emittingVol = m_anRandomTable[3] % 15 + 45;
+ if (FindPlayerPed() != m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_pEntity)
+ emittingVol /= 2;
+ maxDist = 400.f;
+ switch (params.m_pPed->m_nSurfaceTouched) {
+ case SURFACE_GRASS:
+ sampleIndex = m_anRandomTable[1] % 5 + SFX_FOOTSTEP_GRASS_1;
+ break;
+ case SURFACE_GRAVEL:
+ case SURFACE_MUD_DRY:
+ sampleIndex = m_anRandomTable[4] % 5 + SFX_FOOTSTEP_GRAVEL_1;
+ break;
+ case SURFACE_CAR:
+ case SURFACE_GARAGE_DOOR:
+ case SURFACE_CAR_PANEL:
+ case SURFACE_THICK_METAL_PLATE:
+ case SURFACE_SCAFFOLD_POLE:
+ case SURFACE_LAMP_POST:
+ case SURFACE_FIRE_HYDRANT:
+ case SURFACE_GIRDER:
+ case SURFACE_METAL_CHAIN_FENCE:
+ case SURFACE_CONTAINER:
+ case SURFACE_NEWS_VENDOR:
+ sampleIndex = m_anRandomTable[0] % 5 + SFX_FOOTSTEP_METAL_1;
+ break;
+ case SURFACE_SAND:
+ sampleIndex = (m_anRandomTable[4] & 3) + SFX_FOOTSTEP_SAND_1;
+ break;
+ case SURFACE_WATER:
+ sampleIndex = (m_anRandomTable[3] & 3) + SFX_FOOTSTEP_WATER_1;
+ break;
+ case SURFACE_WOOD_CRATES:
+ case SURFACE_WOOD_BENCH:
+ case SURFACE_WOOD_SOLID:
+ sampleIndex = m_anRandomTable[2] % 5 + SFX_FOOTSTEP_WOOD_1;
+ break;
+ case SURFACE_HEDGE:
+ sampleIndex = m_anRandomTable[2] % 5 + SFX_COL_VEG_1;
+ break;
+ default:
+ sampleIndex = m_anRandomTable[2] % 5 + SFX_FOOTSTEP_CONCRETE_1;
+ break;
}
+ m_sQueueSample.m_nSampleIndex = sampleIndex;
+ m_sQueueSample.m_nBankIndex = SFX_BANK_0;
+ m_sQueueSample.m_nCounter = m_asAudioEntities[m_sQueueSample.m_nEntityIndex].m_awAudioEvent[i] - SOUND_STEP_START + 1;
+ m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex);
+ m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 17);
+ switch (params.m_pPed->m_nMoveState) {
+ case PEDMOVE_WALK:
+ emittingVol /= 4;
+ m_sQueueSample.m_nFrequency = 9 * m_sQueueSample.m_nFrequency / 10;
+ break;
+ case PEDMOVE_RUN:
+ emittingVol /= 2;
+ m_sQueueSample.m_nFrequency = 11 * m_sQueueSample.m_nFrequency / 10;
+ break;
+ case PEDMOVE_SPRINT:
+ m_sQueueSample.m_nFrequency = 12 * m_sQueueSample.m_nFrequency / 10;
+ break;
+ default:
+ break;
+ }
+ m_sQueueSample.m_nReleasingVolumeModificator = 5;
+ m_sQueueSample.m_fSpeedMultiplier = 0.0f;
+ m_sQueueSample.m_fSoundIntensity = 20.0f;
+ m_sQueueSample.m_nLoopCount = 1;
+ m_sQueueSample.m_nLoopStart = 0;
+ m_sQueueSample.m_nLoopEnd = -1;
+ m_sQueueSample.m_nEmittingVolume = emittingVol;
+ m_sQueueSample.m_bIs2D = false;
+ m_sQueueSample.m_bReleasingSoundFlag = true;
+ m_sQueueSample.m_bRequireReflection = true;
break;
case SOUND_FALL_LAND:
case SOUND_FALL_COLLAPSE:
- if (!ped->bIsLooking) {
- maxDist = SQR(30);
- emittingVol = m_anRandomTable[3] % 20 + 80;
- if (ped->m_nSurfaceTouched == SURFACE_WATER) {
- m_sQueueSample.m_nSampleIndex = (m_anRandomTable[3] & 3) + SFX_FOOTSTEP_WATER_1;
- } else if (sound == SOUND_FALL_LAND) {
- m_sQueueSample.m_nSampleIndex = SFX_BODY_LAND;
- } else {
- m_sQueueSample.m_nSampleIndex = SFX_BODY_LAND_AND_FALL;
- }
- m_sQueueSample.m_nBankIndex = SFX_BANK_0;
- m_sQueueSample.m_nCounter = 1;
- m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex);
- m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 17);
- m_sQueueSample.m_nReleasingVolumeModificator = 2;
- m_sQueueSample.m_fSpeedMultiplier = 0.0f;
- m_sQueueSample.m_fSoundIntensity = 30.0f;
- m_sQueueSample.m_nLoopCount = 1;
- m_sQueueSample.m_nLoopStart = 0;
- m_sQueueSample.m_nLoopEnd = -1;
- m_sQueueSample.m_nEmittingVolume = emittingVol;
- m_sQueueSample.m_bIs2D = false;
- m_sQueueSample.m_bReleasingSoundFlag = true;
- m_sQueueSample.m_bRequireReflection = true;
+ if (ped->bIsInTheAir)
+ continue;
+ maxDist = SQR(30);
+ emittingVol = m_anRandomTable[3] % 20 + 80;
+ if (ped->m_nSurfaceTouched == SURFACE_WATER) {
+ m_sQueueSample.m_nSampleIndex = (m_anRandomTable[3] & 3) + SFX_FOOTSTEP_WATER_1;
+ } else if (sound == SOUND_FALL_LAND) {
+ m_sQueueSample.m_nSampleIndex = SFX_BODY_LAND;
+ } else {
+ m_sQueueSample.m_nSampleIndex = SFX_BODY_LAND_AND_FALL;
}
+ m_sQueueSample.m_nBankIndex = SFX_BANK_0;
+ m_sQueueSample.m_nCounter = 1;
+ m_sQueueSample.m_nFrequency = SampleManager.GetSampleBaseFrequency(m_sQueueSample.m_nSampleIndex);
+ m_sQueueSample.m_nFrequency += RandomDisplacement(m_sQueueSample.m_nFrequency / 17);
+ m_sQueueSample.m_nReleasingVolumeModificator = 2;
+ m_sQueueSample.m_fSpeedMultiplier = 0.0f;
+ m_sQueueSample.m_fSoundIntensity = 30.0f;
+ m_sQueueSample.m_nLoopCount = 1;
+ m_sQueueSample.m_nLoopStart = 0;
+ m_sQueueSample.m_nLoopEnd = -1;
+ m_sQueueSample.m_nEmittingVolume = emittingVol;
+ m_sQueueSample.m_bIs2D = false;
+ m_sQueueSample.m_bReleasingSoundFlag = true;
+ m_sQueueSample.m_bRequireReflection = true;
break;
case SOUND_FIGHT_PUNCH_33:
m_sQueueSample.m_nSampleIndex = SFX_FIGHT_1;
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
index 868f1b65..e1b5be1d 100644
--- a/src/audio/AudioManager.cpp
+++ b/src/audio/AudioManager.cpp
@@ -17,12 +17,15 @@ const int channels = ARRAY_SIZE(cAudioManager::m_asActiveSamples);
const int policeChannel = channels + 1;
const int allChannels = channels + 2;
+#define SPEED_OF_SOUND 343.f
+#define TIME_SPENT 50
+
cAudioManager::cAudioManager()
{
m_bIsInitialised = false;
- field_1 = 1;
- m_fSpeedOfSound = 6.86f;
- m_nTimeSpent = 50;
+ m_bReverb = true;
+ m_fSpeedOfSound = SPEED_OF_SOUND / TIME_SPENT;
+ m_nTimeSpent = TIME_SPENT;
m_nActiveSamples = NUM_SOUNDS_SAMPLES_SLOTS;
m_nActiveSampleQueue = 1;
ClearRequestedQueue();
diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h
index 9fe2f4ef..2f86ee98 100644
--- a/src/audio/AudioManager.h
+++ b/src/audio/AudioManager.h
@@ -185,7 +185,7 @@ class cAudioManager
{
public:
bool m_bIsInitialised;
- uint8 field_1; // unused
+ bool m_bReverb; // unused
bool m_bFifthFrameFlag;
uint8 m_nActiveSamples;
uint8 field_4; // unused
diff --git a/src/audio/oal/stream.cpp b/src/audio/oal/stream.cpp
index 90e90dd8..ccb17577 100644
--- a/src/audio/oal/stream.cpp
+++ b/src/audio/oal/stream.cpp
@@ -4,22 +4,395 @@
#include "stream.h"
#include "sampman.h"
-#ifdef AUDIO_OPUS
-#include <opusfile.h>
-#else
#ifdef _WIN32
+#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
#ifndef _WIN32
#include "crossplatform.h"
#endif
-#ifndef AUDIO_OPUS
+/*
+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.
+Each stream now has two sources - one panned to the left and one to the right,
+and uses two separate buffers to store data for each individual channel.
+For that we also have to reshuffle all decoded PCM stereo data from LRLRLRLR to
+LLLLRRRR (handled by CSortStereoBuffer).
+*/
+
+class CSortStereoBuffer
+{
+ uint16* PcmBuf;
+ size_t BufSize;
+public:
+ CSortStereoBuffer() : PcmBuf(nil), BufSize(0) {}
+ ~CSortStereoBuffer()
+ {
+ if (PcmBuf)
+ free(PcmBuf);
+ }
+
+ uint16* GetBuffer(size_t size)
+ {
+ if (size == 0) return nil;
+ if (!PcmBuf)
+ {
+ BufSize = size;
+ PcmBuf = (uint16*)malloc(BufSize);
+ }
+ else if (BufSize < size)
+ {
+ BufSize = size;
+ PcmBuf = (uint16*)realloc(PcmBuf, size);
+ }
+ return PcmBuf;
+ }
+
+ void SortStereo(void* buf, size_t size)
+ {
+ uint16* InBuf = (uint16*)buf;
+ uint16* OutBuf = GetBuffer(size);
+
+ if (!OutBuf) return;
+
+ size_t rightStart = size / 4;
+ for (size_t i = 0; i < size / 4; i++)
+ {
+ OutBuf[i] = InBuf[i*2];
+ OutBuf[i+rightStart] = InBuf[i*2+1];
+ }
+
+ memcpy(InBuf, OutBuf, size);
+ }
+
+};
+
+CSortStereoBuffer SortStereoBuffer;
+
+class CImaADPCMDecoder
+{
+ const uint16 StepTable[89] = {
+ 7, 8, 9, 10, 11, 12, 13, 14,
+ 16, 17, 19, 21, 23, 25, 28, 31,
+ 34, 37, 41, 45, 50, 55, 60, 66,
+ 73, 80, 88, 97, 107, 118, 130, 143,
+ 157, 173, 190, 209, 230, 253, 279, 307,
+ 337, 371, 408, 449, 494, 544, 598, 658,
+ 724, 796, 876, 963, 1060, 1166, 1282, 1411,
+ 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
+ 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
+ 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
+ 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794,
+ 32767
+ };
+
+ int16 Sample, StepIndex;
+
+public:
+ CImaADPCMDecoder()
+ {
+ Init(0, 0);
+ }
+
+ void Init(int16 _Sample, int16 _StepIndex)
+ {
+ Sample = _Sample;
+ StepIndex = _StepIndex;
+ }
+
+ void Decode(uint8 *inbuf, int16 *_outbuf, size_t size)
+ {
+ int16* outbuf = _outbuf;
+ for (size_t i = 0; i < size; i++)
+ {
+ *(outbuf++) = DecodeSample(inbuf[i] & 0xF);
+ *(outbuf++) = DecodeSample(inbuf[i] >> 4);
+ }
+ }
+
+ int16 DecodeSample(uint8 adpcm)
+ {
+ uint16 step = StepTable[StepIndex];
+
+ if (adpcm & 4)
+ StepIndex += ((adpcm & 3) + 1) * 2;
+ else
+ StepIndex--;
+
+ StepIndex = clamp(StepIndex, 0, 88);
+
+ int delta = step >> 3;
+ if (adpcm & 1) delta += step >> 2;
+ if (adpcm & 2) delta += step >> 1;
+ if (adpcm & 4) delta += step;
+ if (adpcm & 8) delta = -delta;
+
+ int newSample = Sample + delta;
+ Sample = clamp(newSample, -32768, 32767);
+ return Sample;
+ }
+};
+
+class CWavFile : public IDecoder
+{
+ enum
+ {
+ WAVEFMT_PCM = 1,
+ WAVEFMT_IMA_ADPCM = 0x11,
+ WAVEFMT_XBOX_ADPCM = 0x69,
+ };
+
+ struct tDataHeader
+ {
+ uint32 ID;
+ uint32 Size;
+ };
+
+ struct tFormatHeader
+ {
+ uint16 AudioFormat;
+ uint16 NumChannels;
+ uint32 SampleRate;
+ uint32 ByteRate;
+ uint16 BlockAlign;
+ uint16 BitsPerSample;
+ uint16 extra[2]; // adpcm only
+
+ tFormatHeader() { memset(this, 0, sizeof(*this)); }
+ };
+
+ FILE *m_pFile;
+ bool m_bIsOpen;
+
+ tFormatHeader m_FormatHeader;
+
+ uint32 m_DataStartOffset; // TODO: 64 bit?
+ uint32 m_nSampleCount;
+ uint32 m_nSamplesPerBlock;
+
+ // ADPCM things
+ uint8 *m_pAdpcmBuffer;
+ int16 **m_ppPcmBuffers;
+ CImaADPCMDecoder *m_pAdpcmDecoders;
+
+ void Close()
+ {
+ if (m_pFile) {
+ fclose(m_pFile);
+ m_pFile = nil;
+ }
+ delete[] m_pAdpcmBuffer;
+ delete[] m_ppPcmBuffers;
+ delete[] m_pAdpcmDecoders;
+ }
+
+ uint32 GetCurrentSample() const
+ {
+ // TODO: 64 bit?
+ uint32 FilePos = ftell(m_pFile);
+ if (FilePos <= m_DataStartOffset)
+ return 0;
+ return (FilePos - m_DataStartOffset) / m_FormatHeader.BlockAlign * m_nSamplesPerBlock;
+ }
+
+public:
+ CWavFile(const char* path) : m_bIsOpen(false), m_DataStartOffset(0), m_nSampleCount(0), m_nSamplesPerBlock(0), m_pAdpcmBuffer(nil), m_ppPcmBuffers(nil), m_pAdpcmDecoders(nil)
+ {
+ m_pFile = fopen(path, "rb");
+ if (!m_pFile) return;
+
+#define CLOSE_ON_ERROR(op)\
+ if (op) { \
+ Close(); \
+ return; \
+ }
+
+ tDataHeader DataHeader;
+
+ CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
+ CLOSE_ON_ERROR(DataHeader.ID != 'FFIR');
+
+ // TODO? validate filesizes
+
+ int WAVE;
+ CLOSE_ON_ERROR(fread(&WAVE, 4, 1, m_pFile) == 0);
+ CLOSE_ON_ERROR(WAVE != 'EVAW')
+ CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
+ CLOSE_ON_ERROR(DataHeader.ID != ' tmf');
+
+ CLOSE_ON_ERROR(fread(&m_FormatHeader, Min(DataHeader.Size, sizeof(tFormatHeader)), 1, m_pFile) == 0);
+ CLOSE_ON_ERROR(DataHeader.Size > sizeof(tFormatHeader));
+
+ switch (m_FormatHeader.AudioFormat)
+ {
+ case WAVEFMT_XBOX_ADPCM:
+ m_FormatHeader.AudioFormat = WAVEFMT_IMA_ADPCM;
+ case WAVEFMT_IMA_ADPCM:
+ m_nSamplesPerBlock = (m_FormatHeader.BlockAlign / m_FormatHeader.NumChannels - 4) * 2 + 1;
+ m_pAdpcmBuffer = new uint8[m_FormatHeader.BlockAlign];
+ m_ppPcmBuffers = new int16*[m_FormatHeader.NumChannels];
+ m_pAdpcmDecoders = new CImaADPCMDecoder[m_FormatHeader.NumChannels];
+ break;
+ case WAVEFMT_PCM:
+ m_nSamplesPerBlock = 1;
+ if (m_FormatHeader.BitsPerSample != 16)
+ {
+ debug("Unsupported PCM (%d bits), only signed 16-bit is supported (%s)\n", m_FormatHeader.BitsPerSample, path);
+ Close();
+ return;
+ }
+ break;
+ default:
+ debug("Unsupported wav format 0x%x (%s)\n", m_FormatHeader.AudioFormat, path);
+ Close();
+ return;
+ }
+
+ while (true) {
+ CLOSE_ON_ERROR(fread(&DataHeader, sizeof(DataHeader), 1, m_pFile) == 0);
+ if (DataHeader.ID == 'atad')
+ break;
+ fseek(m_pFile, DataHeader.Size, SEEK_CUR);
+ // TODO? validate data size
+ // maybe check if there no extreme custom headers that might break this
+ }
+
+ m_DataStartOffset = ftell(m_pFile);
+ m_nSampleCount = DataHeader.Size / m_FormatHeader.BlockAlign * m_nSamplesPerBlock;
+
+ m_bIsOpen = true;
+#undef CLOSE_ON_ERROR
+ }
+
+ ~CWavFile()
+ {
+ Close();
+ }
+
+ bool IsOpened()
+ {
+ return m_bIsOpen;
+ }
+
+ uint32 GetSampleSize()
+ {
+ return sizeof(uint16);
+ }
+
+ uint32 GetSampleCount()
+ {
+ return m_nSampleCount;
+ }
+
+ uint32 GetSampleRate()
+ {
+ return m_FormatHeader.SampleRate;
+ }
+
+ uint32 GetChannels()
+ {
+ return m_FormatHeader.NumChannels;
+ }
+
+ void Seek(uint32 milliseconds)
+ {
+ if (!IsOpened()) return;
+ fseek(m_pFile, m_DataStartOffset + ms2samples(milliseconds) / m_nSamplesPerBlock * m_FormatHeader.BlockAlign, SEEK_SET);
+ }
+
+ uint32 Tell()
+ {
+ if (!IsOpened()) return 0;
+ return samples2ms(GetCurrentSample());
+ }
+
+#define SAMPLES_IN_LINE (8)
+
+ uint32 Decode(void* buffer)
+ {
+ if (!IsOpened()) return 0;
+
+ if (m_FormatHeader.AudioFormat == WAVEFMT_PCM)
+ {
+ // just read the file and sort the samples
+ uint32 size = fread(buffer, 1, GetBufferSize(), m_pFile);
+ if (m_FormatHeader.NumChannels == 2)
+ SortStereoBuffer.SortStereo(buffer, size);
+ return size;
+ }
+ else if (m_FormatHeader.AudioFormat == WAVEFMT_IMA_ADPCM)
+ {
+ // trim the buffer size if we're at the end of our file
+ uint32 nMaxSamples = GetBufferSamples() / m_FormatHeader.NumChannels;
+ uint32 nSamplesLeft = m_nSampleCount - GetCurrentSample();
+ nMaxSamples = Min(nMaxSamples, nSamplesLeft);
+
+ // align sample count to our block
+ nMaxSamples = nMaxSamples / m_nSamplesPerBlock * m_nSamplesPerBlock;
+
+ // count the size of output buffer
+ uint32 OutBufSizePerChannel = nMaxSamples * GetSampleSize();
+ uint32 OutBufSize = OutBufSizePerChannel * m_FormatHeader.NumChannels;
+
+ // calculate the pointers to individual channel buffers
+ for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
+ m_ppPcmBuffers[i] = (int16*)((int8*)buffer + OutBufSizePerChannel * i);
+
+ uint32 samplesRead = 0;
+ while (samplesRead < nMaxSamples)
+ {
+ // read the file
+ uint8 *pAdpcmBuf = m_pAdpcmBuffer;
+ if (fread(m_pAdpcmBuffer, 1, m_FormatHeader.BlockAlign, m_pFile) == 0)
+ return 0;
+
+ // get the first sample in adpcm block and initialise the decoder(s)
+ for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
+ {
+ int16 Sample = *(int16*)pAdpcmBuf;
+ pAdpcmBuf += sizeof(int16);
+ int16 Step = *(int16*)pAdpcmBuf;
+ pAdpcmBuf += sizeof(int16);
+ m_pAdpcmDecoders[i].Init(Sample, Step);
+ *(m_ppPcmBuffers[i]) = Sample;
+ m_ppPcmBuffers[i]++;
+ }
+ samplesRead++;
+
+ // decode the rest of the block
+ for (uint32 s = 1; s < m_nSamplesPerBlock; s += SAMPLES_IN_LINE)
+ {
+ for (uint32 i = 0; i < m_FormatHeader.NumChannels; i++)
+ {
+ m_pAdpcmDecoders[i].Decode(pAdpcmBuf, m_ppPcmBuffers[i], SAMPLES_IN_LINE / 2);
+ pAdpcmBuf += SAMPLES_IN_LINE / 2;
+ m_ppPcmBuffers[i] += SAMPLES_IN_LINE;
+ }
+ samplesRead += SAMPLES_IN_LINE;
+ }
+ }
+ return OutBufSize;
+ }
+ return 0;
+ }
+};
+
+#ifdef AUDIO_OAL_USE_SNDFILE
class CSndFile : public IDecoder
{
SNDFILE *m_pfSound;
@@ -81,9 +454,18 @@ public:
uint32 Decode(void *buffer)
{
if ( !IsOpened() ) return 0;
- return sf_read_short(m_pfSound, (short *)buffer, GetBufferSamples()) * GetSampleSize();
+
+ size_t size = sf_read_short(m_pfSound, (short*)buffer, GetBufferSamples()) * GetSampleSize();
+ if (GetChannels()==2)
+ SortStereoBuffer.SortStereo(buffer, size);
+ return size;
}
};
+#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
{
@@ -101,6 +483,9 @@ public:
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;
@@ -176,10 +561,251 @@ public:
assert("We can't handle audio files more then 2 GB yet :shrug:" && (size < UINT32_MAX));
#endif
if (err != MPG123_OK && err != MPG123_DONE) return 0;
+ if (GetChannels() == 2)
+ SortStereoBuffer.SortStereo(buffer, size);
return (uint32)size;
}
};
-#else
+
+#endif
+#define VAG_LINE_SIZE (0x10)
+#define VAG_SAMPLES_IN_LINE (28)
+
+class CVagDecoder
+{
+ const double f[5][2] = { { 0.0, 0.0 },
+ { 60.0 / 64.0, 0.0 },
+ { 115.0 / 64.0, -52.0 / 64.0 },
+ { 98.0 / 64.0, -55.0 / 64.0 },
+ { 122.0 / 64.0, -60.0 / 64.0 } };
+
+ double s_1;
+ double s_2;
+public:
+ CVagDecoder()
+ {
+ ResetState();
+ }
+
+ void ResetState()
+ {
+ s_1 = s_2 = 0.0;
+ }
+
+ static short quantize(double sample)
+ {
+ int a = int(sample + 0.5);
+ return short(clamp(a, -32768, 32767));
+ }
+
+ void Decode(void* _inbuf, int16* _outbuf, size_t size)
+ {
+ uint8* inbuf = (uint8*)_inbuf;
+ int16* outbuf = _outbuf;
+ size &= ~(VAG_LINE_SIZE - 1);
+
+ while (size > 0) {
+ double samples[VAG_SAMPLES_IN_LINE];
+
+ int predict_nr, shift_factor, flags;
+ predict_nr = *(inbuf++);
+ shift_factor = predict_nr & 0xf;
+ predict_nr >>= 4;
+ flags = *(inbuf++);
+ if (flags == 7) // TODO: ignore?
+ break;
+ for (int i = 0; i < VAG_SAMPLES_IN_LINE; i += 2) {
+ int d = *(inbuf++);
+ int16 s = int16((d & 0xf) << 12);
+ samples[i] = (double)(s >> shift_factor);
+ s = int16((d & 0xf0) << 8);
+ samples[i + 1] = (double)(s >> shift_factor);
+ }
+
+ for (int i = 0; i < VAG_SAMPLES_IN_LINE; i++) {
+ samples[i] = samples[i] + s_1 * f[predict_nr][0] + s_2 * f[predict_nr][1];
+ s_2 = s_1;
+ s_1 = samples[i];
+ *(outbuf++) = quantize(samples[i] + 0.5);
+ }
+ size -= VAG_LINE_SIZE;
+ }
+ }
+};
+
+#define VB_BLOCK_SIZE (0x2000)
+#define NUM_VAG_LINES_IN_BLOCK (VB_BLOCK_SIZE / VAG_LINE_SIZE)
+#define NUM_VAG_SAMPLES_IN_BLOCK (NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE)
+
+class CVbFile : public IDecoder
+{
+ FILE *m_pFile;
+ CVagDecoder *m_pVagDecoders;
+
+ size_t m_FileSize;
+ size_t m_nNumberOfBlocks;
+
+ uint32 m_nSampleRate;
+ uint8 m_nChannels;
+ bool m_bBlockRead;
+ uint16 m_LineInBlock;
+ size_t m_CurrentBlock;
+
+ uint8 **m_ppVagBuffers; // buffers that cache actual ADPCM file data
+ int16 **m_ppPcmBuffers;
+
+ void ReadBlock(int32 block = -1)
+ {
+ // just read next block if -1
+ if (block != -1)
+ fseek(m_pFile, block * m_nChannels * VB_BLOCK_SIZE, SEEK_SET);
+
+ for (int i = 0; i < m_nChannels; i++)
+ fread(m_ppVagBuffers[i], VB_BLOCK_SIZE, 1, m_pFile);
+ m_bBlockRead = true;
+ }
+
+public:
+ CVbFile(const char* path, uint32 nSampleRate = 32000, uint8 nChannels = 2) : m_nSampleRate(nSampleRate), m_nChannels(nChannels), m_pVagDecoders(nil), m_ppVagBuffers(nil), m_ppPcmBuffers(nil),
+ m_FileSize(0), m_nNumberOfBlocks(0), m_bBlockRead(false), m_LineInBlock(0), m_CurrentBlock(0)
+ {
+ m_pFile = fopen(path, "rb");
+ if (!m_pFile) return;
+
+ fseek(m_pFile, 0, SEEK_END);
+ m_FileSize = ftell(m_pFile);
+ fseek(m_pFile, 0, SEEK_SET);
+
+ m_nNumberOfBlocks = m_FileSize / (nChannels * VB_BLOCK_SIZE);
+ m_pVagDecoders = new CVagDecoder[nChannels];
+ m_ppVagBuffers = new uint8*[nChannels];
+ m_ppPcmBuffers = new int16*[nChannels];
+ for (uint8 i = 0; i < nChannels; i++)
+ m_ppVagBuffers[i] = new uint8[VB_BLOCK_SIZE];
+ }
+
+ ~CVbFile()
+ {
+ if (m_pFile)
+ {
+ fclose(m_pFile);
+
+ delete[] m_pVagDecoders;
+ for (int i = 0; i < m_nChannels; i++)
+ delete[] m_ppVagBuffers[i];
+ delete[] m_ppVagBuffers;
+ delete[] m_ppPcmBuffers;
+ }
+ }
+
+ bool IsOpened()
+ {
+ return m_pFile != nil;
+ }
+
+ uint32 GetSampleSize()
+ {
+ return sizeof(uint16);
+ }
+
+ uint32 GetSampleCount()
+ {
+ if (!IsOpened()) return 0;
+ return m_nNumberOfBlocks * NUM_VAG_LINES_IN_BLOCK * VAG_SAMPLES_IN_LINE;
+ }
+
+ uint32 GetSampleRate()
+ {
+ return m_nSampleRate;
+ }
+
+ uint32 GetChannels()
+ {
+ return m_nChannels;
+ }
+
+ void Seek(uint32 milliseconds)
+ {
+ if (!IsOpened()) return;
+ uint32 samples = ms2samples(milliseconds);
+
+ // find the block of our sample
+ uint32 block = samples / NUM_VAG_SAMPLES_IN_BLOCK;
+ if (block > m_nNumberOfBlocks)
+ {
+ samples = 0;
+ block = 0;
+ }
+ if (block != m_CurrentBlock)
+ m_bBlockRead = false;
+
+ // find a line of our sample within our block
+ uint32 remainingSamples = samples - block * NUM_VAG_SAMPLES_IN_BLOCK;
+ uint32 newLine = remainingSamples / VAG_SAMPLES_IN_LINE / VAG_LINE_SIZE;
+
+ if (m_CurrentBlock != block || m_LineInBlock != newLine)
+ {
+ m_CurrentBlock = block;
+ m_LineInBlock = newLine;
+ for (uint32 i = 0; i < GetChannels(); i++)
+ m_pVagDecoders[i].ResetState();
+ }
+
+ }
+
+ uint32 Tell()
+ {
+ if (!IsOpened()) return 0;
+ uint32 pos = (m_CurrentBlock * NUM_VAG_LINES_IN_BLOCK + m_LineInBlock) * VAG_SAMPLES_IN_LINE;
+ return samples2ms(pos);
+ }
+
+ uint32 Decode(void* buffer)
+ {
+ if (!IsOpened()) return 0;
+
+ if (m_CurrentBlock >= m_nNumberOfBlocks) return 0;
+
+ // cache current ADPCM block
+ if (!m_bBlockRead)
+ ReadBlock(m_CurrentBlock);
+
+ // trim the buffer size if we're at the end of our file
+ int numberOfRequiredLines = GetBufferSamples() / m_nChannels / VAG_SAMPLES_IN_LINE;
+ int numberOfRemainingLines = (m_nNumberOfBlocks - m_CurrentBlock) * NUM_VAG_LINES_IN_BLOCK - m_LineInBlock;
+ int bufSizePerChannel = Min(numberOfRequiredLines, numberOfRemainingLines) * VAG_SAMPLES_IN_LINE * GetSampleSize();
+
+ // calculate the pointers to individual channel buffers
+ for (uint32 i = 0; i < m_nChannels; i++)
+ m_ppPcmBuffers[i] = (int16*)((int8*)buffer + bufSizePerChannel * i);
+
+ int size = 0;
+ while (size < bufSizePerChannel)
+ {
+ // decode the VAG lines
+ for (uint32 i = 0; i < m_nChannels; i++)
+ {
+ m_pVagDecoders[i].Decode(m_ppVagBuffers[i] + m_LineInBlock * VAG_LINE_SIZE, m_ppPcmBuffers[i], VAG_LINE_SIZE);
+ m_ppPcmBuffers[i] += VAG_SAMPLES_IN_LINE;
+ }
+ size += VAG_SAMPLES_IN_LINE * GetSampleSize();
+ m_LineInBlock++;
+
+ // block is over, read the next block
+ if (m_LineInBlock >= NUM_VAG_LINES_IN_BLOCK)
+ {
+ m_CurrentBlock++;
+ if (m_CurrentBlock >= m_nNumberOfBlocks) // end of file
+ break;
+ m_LineInBlock = 0;
+ ReadBlock();
+ }
+ }
+
+ return bufSizePerChannel * m_nChannels;
+ }
+};
+#ifdef AUDIO_OAL_USE_OPUS
class COpusFile : public IDecoder
{
OggOpusFile *m_FileH;
@@ -267,6 +893,9 @@ public:
if (size < 0)
return 0;
+ if (GetChannels() == 2)
+ SortStereoBuffer.SortStereo(buffer, size * m_nChannels * GetSampleSize());
+
return size * m_nChannels * GetSampleSize();
}
};
@@ -274,20 +903,20 @@ public:
void CStream::Initialise()
{
-#ifndef AUDIO_OPUS
+#ifdef AUDIO_OAL_USE_MPG123
mpg123_init();
#endif
}
void CStream::Terminate()
{
-#ifndef AUDIO_OPUS
+#ifdef AUDIO_OAL_USE_MPG123
mpg123_exit();
#endif
}
-CStream::CStream(char *filename, ALuint &source, ALuint (&buffers)[NUM_STREAMBUFFERS]) :
- m_alSource(source),
+CStream::CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate) :
+ m_pAlSources(sources),
m_alBuffers(buffers),
m_pBuffer(nil),
m_bPaused(false),
@@ -314,13 +943,20 @@ CStream::CStream(char *filename, ALuint &source, ALuint (&buffers)[NUM_STREAMBUF
DEV("Stream %s\n", m_aFilename);
-#ifndef AUDIO_OPUS
- if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".mp3")], ".mp3"))
- m_pSoundFile = new CMP3File(m_aFilename);
- else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".wav")], ".wav"))
+ if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".wav")], ".wav"))
+#ifdef AUDIO_OAL_USE_SNDFILE
m_pSoundFile = new CSndFile(m_aFilename);
#else
- if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".opus")], ".opus"))
+ 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
+ else if (!strcasecmp(&m_aFilename[strlen(m_aFilename) - strlen(".opus")], ".opus"))
m_pSoundFile = new COpusFile(m_aFilename);
#endif
else
@@ -368,7 +1004,7 @@ void CStream::Delete()
bool CStream::HasSource()
{
- return m_alSource != AL_NONE;
+ return (m_pAlSources[0] != AL_NONE) && (m_pAlSources[1] != AL_NONE);
}
bool CStream::IsOpened()
@@ -382,9 +1018,10 @@ bool CStream::IsPlaying()
if ( !m_bPaused )
{
- ALint sourceState;
- alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
- if ( m_bActive || sourceState == AL_PLAYING )
+ ALint sourceState[2];
+ alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState[0]);
+ alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
+ if ( m_bActive || sourceState[0] == AL_PLAYING || sourceState[1] == AL_PLAYING)
return true;
}
@@ -395,9 +1032,12 @@ void CStream::Pause()
{
if ( !HasSource() ) return;
ALint sourceState = AL_PAUSED;
- alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
- if (sourceState != AL_PAUSED )
- alSourcePause(m_alSource);
+ alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
+ if (sourceState != AL_PAUSED)
+ alSourcePause(m_pAlSources[0]);
+ alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
+ if (sourceState != AL_PAUSED)
+ alSourcePause(m_pAlSources[1]);
}
void CStream::SetPause(bool bPause)
@@ -419,19 +1059,21 @@ void CStream::SetPause(bool bPause)
void CStream::SetPitch(float pitch)
{
if ( !HasSource() ) return;
- alSourcef(m_alSource, AL_PITCH, pitch);
+ alSourcef(m_pAlSources[0], AL_PITCH, pitch);
+ alSourcef(m_pAlSources[1], AL_PITCH, pitch);
}
void CStream::SetGain(float gain)
{
if ( !HasSource() ) return;
- alSourcef(m_alSource, AL_GAIN, gain);
+ alSourcef(m_pAlSources[0], AL_GAIN, gain);
+ alSourcef(m_pAlSources[1], AL_GAIN, gain);
}
-void CStream::SetPosition(float x, float y, float z)
+void CStream::SetPosition(int i, float x, float y, float z)
{
if ( !HasSource() ) return;
- alSource3f(m_alSource, AL_POSITION, x, y, z);
+ alSource3f(m_pAlSources[i], AL_POSITION, x, y, z);
}
void CStream::SetVolume(uint32 nVol)
@@ -442,8 +1084,13 @@ void CStream::SetVolume(uint32 nVol)
void CStream::SetPan(uint8 nPan)
{
+ m_nPan = clamp((int8)nPan - 63, 0, 63);
+ SetPosition(0, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f)));
+
+ m_nPan = clamp((int8)nPan + 64, 64, 127);
+ SetPosition(1, (m_nPan - 63) / 64.0f, 0.0f, Sqrt(1.0f - SQR((m_nPan - 63) / 64.0f)));
+
m_nPan = nPan;
- SetPosition((nPan - 63)/64.0f, 0.0f, Sqrt(1.0f-SQR((nPan-63)/64.0f)));
}
void CStream::SetPosMS(uint32 nPos)
@@ -460,10 +1107,10 @@ uint32 CStream::GetPosMS()
ALint offset;
//alGetSourcei(m_alSource, AL_SAMPLE_OFFSET, &offset);
- alGetSourcei(m_alSource, AL_BYTE_OFFSET, &offset);
+ alGetSourcei(m_pAlSources[0], AL_BYTE_OFFSET, &offset);
return m_pSoundFile->Tell()
- - m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS-1)) / m_pSoundFile->GetChannels()
+ - m_pSoundFile->samples2ms(m_pSoundFile->GetBufferSamples() * (NUM_STREAMBUFFERS/2-1)) / m_pSoundFile->GetChannels()
+ m_pSoundFile->samples2ms(offset/m_pSoundFile->GetSampleSize()) / m_pSoundFile->GetChannels();
}
@@ -473,33 +1120,41 @@ uint32 CStream::GetLengthMS()
return m_pSoundFile->GetLength();
}
-bool CStream::FillBuffer(ALuint alBuffer)
+bool CStream::FillBuffer(ALuint *alBuffer)
{
if ( !HasSource() )
return false;
if ( !IsOpened() )
return false;
- if ( !(alBuffer != AL_NONE && alIsBuffer(alBuffer)) )
+ if ( !(alBuffer[0] != AL_NONE && alIsBuffer(alBuffer[0])) )
+ return false;
+ if ( !(alBuffer[1] != AL_NONE && alIsBuffer(alBuffer[1])) )
return false;
uint32 size = m_pSoundFile->Decode(m_pBuffer);
if( size == 0 )
return false;
+
+ uint32 channelSize = size / m_pSoundFile->GetChannels();
- alBufferData(alBuffer, m_pSoundFile->GetChannels() == 1 ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16,
- m_pBuffer, size, m_pSoundFile->GetSampleRate());
-
+ alBufferData(alBuffer[0], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate());
+ // TODO: use just one buffer if we play mono
+ if (m_pSoundFile->GetChannels() == 1)
+ alBufferData(alBuffer[1], AL_FORMAT_MONO16, m_pBuffer, channelSize, m_pSoundFile->GetSampleRate());
+ else
+ alBufferData(alBuffer[1], AL_FORMAT_MONO16, (uint8*)m_pBuffer + channelSize, channelSize, m_pSoundFile->GetSampleRate());
return true;
}
int32 CStream::FillBuffers()
{
int32 i = 0;
- for ( i = 0; i < NUM_STREAMBUFFERS; i++ )
+ for ( i = 0; i < NUM_STREAMBUFFERS/2; i++ )
{
- if ( !FillBuffer(m_alBuffers[i]) )
+ if ( !FillBuffer(&m_alBuffers[i*2]) )
break;
- alSourceQueueBuffers(m_alSource, 1, &m_alBuffers[i]);
+ alSourceQueueBuffers(m_pAlSources[0], 1, &m_alBuffers[i*2]);
+ alSourceQueueBuffers(m_pAlSources[1], 1, &m_alBuffers[i*2+1]);
}
return i;
@@ -508,13 +1163,16 @@ int32 CStream::FillBuffers()
void CStream::ClearBuffers()
{
if ( !HasSource() ) return;
-
- ALint buffersQueued;
- alGetSourcei(m_alSource, AL_BUFFERS_QUEUED, &buffersQueued);
+
+ ALint buffersQueued[2];
+ alGetSourcei(m_pAlSources[0], AL_BUFFERS_QUEUED, &buffersQueued[0]);
+ alGetSourcei(m_pAlSources[1], AL_BUFFERS_QUEUED, &buffersQueued[1]);
ALuint value;
- while (buffersQueued--)
- alSourceUnqueueBuffers(m_alSource, 1, &value);
+ while (buffersQueued[0]--)
+ alSourceUnqueueBuffers(m_pAlSources[0], 1, &value);
+ while (buffersQueued[1]--)
+ alSourceUnqueueBuffers(m_pAlSources[1], 1, &value);
}
bool CStream::Setup()
@@ -522,7 +1180,6 @@ bool CStream::Setup()
if ( IsOpened() )
{
m_pSoundFile->Seek(0);
- alSourcei(m_alSource, AL_SOURCE_RELATIVE, AL_TRUE);
//SetPosition(0.0f, 0.0f, 0.0f);
SetPitch(1.0f);
//SetPan(m_nPan);
@@ -538,17 +1195,29 @@ void CStream::SetPlay(bool state)
if ( state )
{
ALint sourceState = AL_PLAYING;
- alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
+ alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
if (sourceState != AL_PLAYING )
- alSourcePlay(m_alSource);
+ alSourcePlay(m_pAlSources[0]);
+
+ sourceState = AL_PLAYING;
+ alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
+ if (sourceState != AL_PLAYING)
+ alSourcePlay(m_pAlSources[1]);
+
m_bActive = true;
}
else
{
ALint sourceState = AL_STOPPED;
- alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
- if (sourceState != AL_STOPPED )
- alSourceStop(m_alSource);
+ alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState);
+ if (sourceState != AL_STOPPED)
+ alSourceStop(m_pAlSources[0]);
+
+ sourceState = AL_STOPPED;
+ alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState);
+ if (sourceState != AL_STOPPED)
+ alSourceStop(m_pAlSources[1]);
+
m_bActive = false;
}
}
@@ -579,35 +1248,51 @@ void CStream::Update()
if ( !m_bPaused )
{
- ALint sourceState;
- ALint buffersProcessed = 0;
+ ALint sourceState[2];
+ ALint buffersProcessed[2] = { 0, 0 };
- alGetSourcei(m_alSource, AL_SOURCE_STATE, &sourceState);
- alGetSourcei(m_alSource, AL_BUFFERS_PROCESSED, &buffersProcessed);
+ // Relying a lot on left buffer states in here
+
+ do
+ {
+ //alSourcef(m_pAlSources[0], AL_ROLLOFF_FACTOR, 0.0f);
+ alGetSourcei(m_pAlSources[0], AL_SOURCE_STATE, &sourceState[0]);
+ alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed[0]);
+ //alSourcef(m_pAlSources[1], AL_ROLLOFF_FACTOR, 0.0f);
+ alGetSourcei(m_pAlSources[1], AL_SOURCE_STATE, &sourceState[1]);
+ alGetSourcei(m_pAlSources[1], AL_BUFFERS_PROCESSED, &buffersProcessed[1]);
+ } while (buffersProcessed[0] != buffersProcessed[1]);
ALint looping = AL_FALSE;
- alGetSourcei(m_alSource, AL_LOOPING, &looping);
+ alGetSourcei(m_pAlSources[0], AL_LOOPING, &looping);
if ( looping == AL_TRUE )
{
TRACE("stream set looping");
- alSourcei(m_alSource, AL_LOOPING, AL_TRUE);
+ alSourcei(m_pAlSources[0], AL_LOOPING, AL_TRUE);
+ alSourcei(m_pAlSources[1], AL_LOOPING, AL_TRUE);
}
+
+ assert(buffersProcessed[0] == buffersProcessed[1]);
- while( buffersProcessed-- )
+ while( buffersProcessed[0]-- )
{
- ALuint buffer;
+ ALuint buffer[2];
- alSourceUnqueueBuffers(m_alSource, 1, &buffer);
+ alSourceUnqueueBuffers(m_pAlSources[0], 1, &buffer[0]);
+ alSourceUnqueueBuffers(m_pAlSources[1], 1, &buffer[1]);
- if ( m_bActive && FillBuffer(buffer) )
- alSourceQueueBuffers(m_alSource, 1, &buffer);
+ if (m_bActive && FillBuffer(buffer))
+ {
+ alSourceQueueBuffers(m_pAlSources[0], 1, &buffer[0]);
+ alSourceQueueBuffers(m_pAlSources[1], 1, &buffer[1]);
+ }
}
- if ( sourceState != AL_PLAYING )
+ if ( sourceState[0] != AL_PLAYING )
{
- alGetSourcei(m_alSource, AL_BUFFERS_PROCESSED, &buffersProcessed);
- SetPlay(buffersProcessed!=0);
+ alGetSourcei(m_pAlSources[0], AL_BUFFERS_PROCESSED, &buffersProcessed[0]);
+ SetPlay(buffersProcessed[0]!=0);
}
}
}
diff --git a/src/audio/oal/stream.h b/src/audio/oal/stream.h
index 2476abcc..bcbc5e54 100644
--- a/src/audio/oal/stream.h
+++ b/src/audio/oal/stream.h
@@ -3,7 +3,7 @@
#ifdef AUDIO_OAL
#include <AL/al.h>
-#define NUM_STREAMBUFFERS 4
+#define NUM_STREAMBUFFERS 8
class IDecoder
{
@@ -57,7 +57,7 @@ public:
class CStream
{
char m_aFilename[128];
- ALuint &m_alSource;
+ ALuint *m_pAlSources;
ALuint (&m_alBuffers)[NUM_STREAMBUFFERS];
bool m_bPaused;
@@ -73,20 +73,20 @@ class CStream
IDecoder *m_pSoundFile;
bool HasSource();
- void SetPosition(float x, float y, float z);
+ void SetPosition(int i, float x, float y, float z);
void SetPitch(float pitch);
void SetGain(float gain);
void Pause();
void SetPlay(bool state);
- bool FillBuffer(ALuint alBuffer);
+ bool FillBuffer(ALuint *alBuffer);
int32 FillBuffers();
void ClearBuffers();
public:
static void Initialise();
static void Terminate();
- CStream(char *filename, ALuint &source, ALuint (&buffers)[NUM_STREAMBUFFERS]);
+ CStream(char *filename, ALuint *sources, ALuint (&buffers)[NUM_STREAMBUFFERS], uint32 overrideSampleRate = 32000);
~CStream();
void Delete();
diff --git a/src/audio/sampman.h b/src/audio/sampman.h
index 2284d385..72c3eb7f 100644
--- a/src/audio/sampman.h
+++ b/src/audio/sampman.h
@@ -218,7 +218,7 @@ extern uint32 BankStartOffset[MAX_SFX_BANKS];
extern int defaultProvider;
#endif
-#ifdef AUDIO_OPUS
+#if defined(OPUS_AUDIO_PATHS)
static char StreamedNameTable[][25] = {
"AUDIO\\HEAD.OPUS", "AUDIO\\CLASS.OPUS", "AUDIO\\KJAH.OPUS", "AUDIO\\RISE.OPUS", "AUDIO\\LIPS.OPUS", "AUDIO\\GAME.OPUS",
"AUDIO\\MSX.OPUS", "AUDIO\\FLASH.OPUS", "AUDIO\\CHAT.OPUS", "AUDIO\\HEAD.OPUS", "AUDIO\\POLICE.OPUS", "AUDIO\\CITY.OPUS",
@@ -254,9 +254,9 @@ static char StreamedNameTable[][25] = {
"AUDIO\\door_2.OPUS", "AUDIO\\door_3.OPUS", "AUDIO\\door_4.OPUS", "AUDIO\\door_5.OPUS", "AUDIO\\door_6.OPUS", "AUDIO\\t3_a.OPUS",
"AUDIO\\t3_b.OPUS", "AUDIO\\t3_c.OPUS", "AUDIO\\k1_b.OPUS", "AUDIO\\cat1.OPUS"};
#else
+#if defined(PS2_AUDIO_PATHS)
static char StreamedNameTable[][25]=
{
-#ifdef PS2_AUDIO
"AUDIO\\MUSIC\\HEAD.VB",
"AUDIO\\MUSIC\\CLASS.VB",
"AUDIO\\MUSIC\\KJAH.VB",
@@ -353,6 +353,8 @@ static char StreamedNameTable[][25]=
"AUDIO\\MUSIC\\MISCOM.VB",
"AUDIO\\MUSIC\\END.VB",
#else
+static char StreamedNameTable[][25] =
+{
"AUDIO\\HEAD.WAV",
"AUDIO\\CLASS.WAV",
"AUDIO\\KJAH.WAV",
diff --git a/src/audio/sampman_oal.cpp b/src/audio/sampman_oal.cpp
index eec5ca5f..bb7f0aac 100644
--- a/src/audio/sampman_oal.cpp
+++ b/src/audio/sampman_oal.cpp
@@ -30,7 +30,7 @@
#include "MusicManager.h"
#include "Frontend.h"
#include "Timer.h"
-#ifdef AUDIO_OPUS
+#ifdef AUDIO_OAL_USE_OPUS
#include <opusfile.h>
#endif
@@ -83,7 +83,7 @@ char SampleBankDescFilename[] = "audio/sfx.SDT";
char SampleBankDataFilename[] = "audio/sfx.RAW";
FILE *fpSampleDescHandle;
-#ifdef AUDIO_OPUS
+#ifdef OPUS_SFX
OggOpusFile *fpSampleDataHandle;
#else
FILE *fpSampleDataHandle;
@@ -102,7 +102,7 @@ CChannel aChannel[MAXCHANNELS+MAX2DCHANNELS];
uint8 nChannelVolume[MAXCHANNELS+MAX2DCHANNELS];
uint32 nStreamLength[TOTAL_STREAMED_SOUNDS];
-ALuint ALStreamSources[MAX_STREAMS];
+ALuint ALStreamSources[MAX_STREAMS][2];
ALuint ALStreamBuffers[MAX_STREAMS][NUM_STREAMBUFFERS];
struct tMP3Entry
@@ -245,9 +245,9 @@ release_existing()
if (stream)
stream->ProviderTerm();
- alDeleteSources(1, &ALStreamSources[i]);
alDeleteBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
}
+ alDeleteSources(MAX_STREAMS*2, ALStreamSources[0]);
CChannel::DestroyChannels();
@@ -287,7 +287,10 @@ set_new_provider(int index)
//TODO:
_maxSamples = MAXCHANNELS;
- ALCint attr[] = {ALC_FREQUENCY,MAX_FREQ,0};
+ ALCint attr[] = {ALC_FREQUENCY,MAX_FREQ,
+ ALC_MONO_SOURCES, MAX_STREAMS * 2 + MAXCHANNELS,
+ 0,
+ };
ALDevice = alcOpenDevice(providers[index].id);
ASSERT(ALDevice != NULL);
@@ -319,11 +322,17 @@ set_new_provider(int index)
alGenAuxiliaryEffectSlots(1, &ALEffectSlot);
alGenEffects(1, &ALEffect);
}
-
+
+ alGenSources(MAX_STREAMS*2, ALStreamSources[0]);
for ( int32 i = 0; i < MAX_STREAMS; i++ )
{
- alGenSources(1, &ALStreamSources[i]);
- alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
+ alGenBuffers(NUM_STREAMBUFFERS, ALStreamBuffers[i]);
+ alSourcei(ALStreamSources[i][0], AL_SOURCE_RELATIVE, AL_TRUE);
+ alSource3f(ALStreamSources[i][0], AL_POSITION, 0.0f, 0.0f, 0.0f);
+ alSourcef(ALStreamSources[i][0], AL_GAIN, 1.0f);
+ alSourcei(ALStreamSources[i][1], AL_SOURCE_RELATIVE, AL_TRUE);
+ alSource3f(ALStreamSources[i][1], AL_POSITION, 0.0f, 0.0f, 0.0f);
+ alSourcef(ALStreamSources[i][1], AL_GAIN, 1.0f);
CStream *stream = aStream[i];
if (stream)
@@ -384,6 +393,12 @@ set_new_provider(int index)
return false;
}
+static bool
+IsThisTrackAt16KHz(uint32 track)
+{
+ return track == STREAMED_SOUND_RADIO_CHAT;
+}
+
cSampleManager::cSampleManager(void)
{
;
@@ -956,33 +971,37 @@ cSampleManager::Initialise(void)
#ifdef AUDIO_CACHE
FILE *cacheFile = fcaseopen("audio\\sound.cache", "rb");
if (cacheFile) {
+ debug("Loadind audio cache (If game crashes around here, then your cache is corrupted, remove audio/sound.cache)\n");
fread(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile);
fclose(cacheFile);
} else
-#endif
{
-
- for ( int32 i = 0; i < TOTAL_STREAMED_SOUNDS; i++ )
- {
- aStream[0] = new CStream(StreamedNameTable[i], ALStreamSources[0], ALStreamBuffers[0]);
-
- if ( aStream[0] && aStream[0]->IsOpened() )
- {
+ 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()) {
uint32 tatalms = aStream[0]->GetLengthMS();
delete aStream[0];
aStream[0] = NULL;
-
+
nStreamLength[i] = tatalms;
- }
- else
+ } else
USERERROR("Can't open '%s'\n", StreamedNameTable[i]);
}
#ifdef AUDIO_CACHE
cacheFile = fcaseopen("audio\\sound.cache", "wb");
- fwrite(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile);
- fclose(cacheFile);
-#endif
+ if(cacheFile) {
+ debug("Saving audio cache\n");
+ fwrite(nStreamLength, sizeof(uint32), TOTAL_STREAMED_SOUNDS, cacheFile);
+ fclose(cacheFile);
+ } else {
+ debug("Cannot save audio cache\n");
+ }
}
+#endif
{
if ( !InitialiseSampleBanks() )
@@ -1199,7 +1218,7 @@ cSampleManager::LoadSampleBank(uint8 nBank)
return false;
}
-#ifdef AUDIO_OPUS
+#ifdef OPUS_SFX
int samplesRead = 0;
int samplesSize = nSampleBankSize[nBank] / 2;
op_pcm_seek(fpSampleDataHandle, 0);
@@ -1312,7 +1331,7 @@ cSampleManager::LoadPedComment(uint32 nComment)
}
}
-#ifdef AUDIO_OPUS
+#ifdef OPUS_SFX
int samplesRead = 0;
int samplesSize = m_aSamples[nComment].nSize / 2;
op_pcm_seek(fpSampleDataHandle, m_aSamples[nComment].nOffset / 2);
@@ -1648,7 +1667,7 @@ cSampleManager::PreloadStreamedFile(uint8 nFile, uint8 nStream)
strcpy(filename, StreamedNameTable[nFile]);
- CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
ASSERT(stream != NULL);
aStream[nStream] = stream;
@@ -1723,7 +1742,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
nFile = 0;
strcat(filename, StreamedNameTable[nFile]);
- CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
ASSERT(stream != NULL);
aStream[nStream] = stream;
@@ -1747,12 +1766,12 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
}
if (mp3->pLinkPath != NULL)
- aStream[nStream] = new CStream(mp3->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ aStream[nStream] = new CStream(mp3->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
else {
strcpy(filename, _mp3DirectoryPath);
strcat(filename, mp3->aFilename);
- aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ aStream[nStream] = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
}
if (aStream[nStream]->IsOpened()) {
@@ -1779,7 +1798,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
{
nFile = 0;
strcat(filename, StreamedNameTable[nFile]);
- CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ CStream* stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
ASSERT(stream != NULL);
aStream[nStream] = stream;
@@ -1803,7 +1822,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
}
if (e->pLinkPath != NULL)
- aStream[nStream] = new CStream(e->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ aStream[nStream] = new CStream(e->pLinkPath, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
else {
strcpy(filename, _mp3DirectoryPath);
strcat(filename, e->aFilename);
@@ -1836,7 +1855,7 @@ cSampleManager::StartStreamedFile(uint8 nFile, uint32 nPos, uint8 nStream)
strcpy(filename, StreamedNameTable[nFile]);
- CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream]);
+ CStream *stream = new CStream(filename, ALStreamSources[nStream], ALStreamBuffers[nStream], IsThisTrackAt16KHz(nFile) ? 16000 : 32000);
ASSERT(stream != NULL);
aStream[nStream] = stream;
@@ -1959,7 +1978,7 @@ cSampleManager::InitialiseSampleBanks(void)
fpSampleDescHandle = fcaseopen(SampleBankDescFilename, "rb");
if ( fpSampleDescHandle == NULL )
return false;
-#ifndef AUDIO_OPUS
+#ifndef OPUS_SFX
fpSampleDataHandle = fcaseopen(SampleBankDataFilename, "rb");
if ( fpSampleDataHandle == NULL )
{
@@ -1977,7 +1996,7 @@ cSampleManager::InitialiseSampleBanks(void)
fpSampleDataHandle = op_open_file(SampleBankDataFilename, &e);
#endif
fread(m_aSamples, sizeof(tSample), TOTAL_AUDIO_SAMPLES, fpSampleDescHandle);
-#ifdef AUDIO_OPUS
+#ifdef OPUS_SFX
int32 _nSampleDataEndOffset = m_aSamples[TOTAL_AUDIO_SAMPLES - 1].nOffset + m_aSamples[TOTAL_AUDIO_SAMPLES - 1].nSize;
#endif
fclose(fpSampleDescHandle);