You've already forked OpenRCT2-Unity
mirror of
https://github.com/izzy2lost/OpenRCT2-Unity.git
synced 2026-03-10 12:38:22 -07:00
Add new audio objects for loading sounds
This commit is contained in:
@@ -1952,8 +1952,6 @@ STR_2738 :Title screen music:
|
||||
STR_2739 :None
|
||||
STR_2740 :RollerCoaster Tycoon 1
|
||||
STR_2741 :RollerCoaster Tycoon 2
|
||||
STR_2742 :css50.dat not found
|
||||
STR_2743 :Copy ‘data/css17.dat’ from your RCT1 installation to ‘data/css50.dat’ in your RCT2 installation, or make sure the path to RCT1 in the Advanced tab is correct.
|
||||
STR_2749 :My new scenario
|
||||
# New strings used in the cheats window previously these were ???
|
||||
STR_2750 :Move all items to top
|
||||
@@ -2994,7 +2992,7 @@ STR_5832 :Show height as generic units instead of measurement format set unde
|
||||
STR_5833 :Changes what date format is used
|
||||
STR_5834 :Select which audio device OpenRCT2 will use
|
||||
STR_5835 :Mute the game if the window loses focus
|
||||
STR_5836 :Select music to use on the main menu.{NEWLINE}Selecting RCT1 theme requires that you copy ‘data/css17.dat’ from your RCT1 game folder to ‘data/css50.dat’ in your RCT2 folder, or set the path to RCT1 in the Miscellaneous tab.
|
||||
STR_5836 :Select music to use on the main menu.
|
||||
STR_5837 :Create and manage custom UI themes
|
||||
STR_5838 :Show a separate button for the finance window in the toolbar
|
||||
STR_5839 :Show a separate button for the research and development window in the toolbar
|
||||
|
||||
@@ -42,7 +42,6 @@ namespace OpenRCT2::Audio
|
||||
bool _stopping = false;
|
||||
bool _done = true;
|
||||
bool _deleteondone = false;
|
||||
bool _deletesourceondone = false;
|
||||
|
||||
public:
|
||||
AudioChannelImpl()
|
||||
@@ -59,10 +58,6 @@ namespace OpenRCT2::Audio
|
||||
speex_resampler_destroy(_resampler);
|
||||
_resampler = nullptr;
|
||||
}
|
||||
if (_deletesourceondone)
|
||||
{
|
||||
delete _source;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] IAudioSource* GetSource() const override
|
||||
@@ -214,11 +209,6 @@ namespace OpenRCT2::Audio
|
||||
_deleteondone = value;
|
||||
}
|
||||
|
||||
void SetDeleteSourceOnDone(bool value) override
|
||||
{
|
||||
_deletesourceondone = value;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool IsPlaying() const override
|
||||
{
|
||||
return !_done;
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
#include "AudioContext.h"
|
||||
|
||||
#include "../SDLException.h"
|
||||
#include "AudioMixer.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <memory>
|
||||
#include <openrct2/audio/AudioContext.h>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include <openrct2/common.h>
|
||||
#include <openrct2/core/String.hpp>
|
||||
|
||||
@@ -21,7 +24,7 @@ namespace OpenRCT2::Audio
|
||||
class AudioContext final : public IAudioContext
|
||||
{
|
||||
private:
|
||||
IAudioMixer* _audioMixer = nullptr;
|
||||
std::unique_ptr<AudioMixer> _audioMixer;
|
||||
|
||||
public:
|
||||
AudioContext()
|
||||
@@ -30,18 +33,17 @@ namespace OpenRCT2::Audio
|
||||
{
|
||||
SDLException::Throw("SDL_Init(SDL_INIT_AUDIO)");
|
||||
}
|
||||
_audioMixer = AudioMixer::Create();
|
||||
_audioMixer = std::make_unique<AudioMixer>();
|
||||
}
|
||||
|
||||
~AudioContext() override
|
||||
{
|
||||
delete _audioMixer;
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
}
|
||||
|
||||
IAudioMixer* GetMixer() override
|
||||
{
|
||||
return _audioMixer;
|
||||
return _audioMixer.get();
|
||||
}
|
||||
|
||||
std::vector<std::string> GetOutputDevices() override
|
||||
@@ -65,14 +67,42 @@ namespace OpenRCT2::Audio
|
||||
_audioMixer->Init(szDeviceName);
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromCSS(const std::string& path, uint32_t index) override
|
||||
{
|
||||
auto& format = _audioMixer->GetFormat();
|
||||
return AddSource(AudioSource::CreateMemoryFromCSS1(path, index, &format));
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(const std::string& path) override
|
||||
{
|
||||
return AudioSource::CreateStreamFromWAV(path);
|
||||
return AddSource(AudioSource::CreateStreamFromWAV(path));
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromCSS(std::unique_ptr<IStream> stream, uint32_t index) override
|
||||
{
|
||||
auto rw = StreamToSDL2(std::move(stream));
|
||||
if (rw == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& format = _audioMixer->GetFormat();
|
||||
return AddSource(AudioSource::CreateMemoryFromCSS1(rw, index, &format));
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) override
|
||||
{
|
||||
return AudioSource::CreateStreamFromWAV(std::move(stream));
|
||||
constexpr size_t STREAM_MIN_SIZE = 2 * 1024 * 1024; // 2 MiB
|
||||
auto loadIntoRAM = stream->GetLength() < STREAM_MIN_SIZE;
|
||||
auto rw = StreamToSDL2(std::move(stream));
|
||||
if (rw == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AddSource(
|
||||
loadIntoRAM ? AudioSource::CreateMemoryFromWAV(rw, &_audioMixer->GetFormat())
|
||||
: AudioSource::CreateStreamFromWAV(rw));
|
||||
}
|
||||
|
||||
void StartTitleMusic() override
|
||||
@@ -120,6 +150,44 @@ namespace OpenRCT2::Audio
|
||||
void StopVehicleSounds() override
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
IAudioSource* AddSource(std::unique_ptr<ISDLAudioSource> source)
|
||||
{
|
||||
return _audioMixer->AddSource(std::move(source));
|
||||
}
|
||||
|
||||
static SDL_RWops* StreamToSDL2(std::unique_ptr<IStream> stream)
|
||||
{
|
||||
auto rw = SDL_AllocRW();
|
||||
if (rw == nullptr)
|
||||
return nullptr;
|
||||
*rw = {};
|
||||
|
||||
rw->type = SDL_RWOPS_UNKNOWN;
|
||||
rw->hidden.unknown.data1 = stream.release();
|
||||
rw->seek = [](SDL_RWops* ctx, Sint64 offset, int whence) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
ptr->Seek(offset, whence);
|
||||
return static_cast<Sint64>(ptr->GetPosition());
|
||||
};
|
||||
rw->read = [](SDL_RWops* ctx, void* buf, size_t size, size_t maxnum) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
return static_cast<size_t>(ptr->TryRead(buf, size * maxnum) / size);
|
||||
};
|
||||
rw->size = [](SDL_RWops* ctx) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
return static_cast<Sint64>(ptr->GetLength());
|
||||
};
|
||||
rw->close = [](SDL_RWops* ctx) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
delete ptr;
|
||||
ctx->hidden.unknown.data1 = nullptr;
|
||||
delete ctx;
|
||||
return 0;
|
||||
};
|
||||
return rw;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<IAudioContext> CreateAudioContext()
|
||||
|
||||
@@ -50,6 +50,7 @@ namespace OpenRCT2::Audio
|
||||
|
||||
struct ISDLAudioSource : public IAudioSource
|
||||
{
|
||||
[[nodiscard]] virtual bool IsReleased() const abstract;
|
||||
[[nodiscard]] virtual AudioFormat GetFormat() const abstract;
|
||||
};
|
||||
|
||||
@@ -62,11 +63,13 @@ namespace OpenRCT2::Audio
|
||||
|
||||
namespace AudioSource
|
||||
{
|
||||
IAudioSource* CreateMemoryFromCSS1(const std::string& path, size_t index, const AudioFormat* targetFormat = nullptr);
|
||||
IAudioSource* CreateMemoryFromWAV(const std::string& path, const AudioFormat* targetFormat = nullptr);
|
||||
IAudioSource* CreateStreamFromWAV(const std::string& path);
|
||||
IAudioSource* CreateStreamFromWAV(SDL_RWops* rw);
|
||||
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream);
|
||||
std::unique_ptr<ISDLAudioSource> CreateMemoryFromCSS1(
|
||||
const std::string& path, size_t index, const AudioFormat* targetFormat = nullptr);
|
||||
std::unique_ptr<ISDLAudioSource> CreateMemoryFromCSS1(
|
||||
SDL_RWops* rw, size_t index, const AudioFormat* targetFormat = nullptr);
|
||||
std::unique_ptr<ISDLAudioSource> CreateMemoryFromWAV(SDL_RWops* rw, const AudioFormat* targetFormat = nullptr);
|
||||
std::unique_ptr<ISDLAudioSource> CreateStreamFromWAV(const std::string& path);
|
||||
std::unique_ptr<ISDLAudioSource> CreateStreamFromWAV(SDL_RWops* rw);
|
||||
} // namespace AudioSource
|
||||
|
||||
namespace AudioChannel
|
||||
@@ -74,11 +77,6 @@ namespace OpenRCT2::Audio
|
||||
ISDLAudioChannel* Create();
|
||||
}
|
||||
|
||||
namespace AudioMixer
|
||||
{
|
||||
IAudioMixer* Create();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<IAudioContext> CreateAudioContext();
|
||||
|
||||
} // namespace OpenRCT2::Audio
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
84
src/openrct2-ui/audio/AudioMixer.h
Normal file
84
src/openrct2-ui/audio/AudioMixer.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2022 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <cstdint>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <openrct2/Context.h>
|
||||
#include <openrct2/audio/AudioChannel.h>
|
||||
#include <openrct2/audio/AudioMixer.h>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include <openrct2/audio/audio.h>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
class AudioMixer final : public IAudioMixer
|
||||
{
|
||||
private:
|
||||
std::vector<std::unique_ptr<ISDLAudioSource>> _sources;
|
||||
IAudioSource* _nullSource = nullptr;
|
||||
|
||||
SDL_AudioDeviceID _deviceId = 0;
|
||||
AudioFormat _format = {};
|
||||
std::list<ISDLAudioChannel*> _channels;
|
||||
float _volume = 1.0f;
|
||||
float _adjustSoundVolume = 0.0f;
|
||||
float _adjustMusicVolume = 0.0f;
|
||||
uint8_t _settingSoundVolume = 0xFF;
|
||||
uint8_t _settingMusicVolume = 0xFF;
|
||||
|
||||
std::vector<uint8_t> _channelBuffer;
|
||||
std::vector<uint8_t> _convertBuffer;
|
||||
std::vector<uint8_t> _effectBuffer;
|
||||
|
||||
std::mutex _mutex;
|
||||
|
||||
public:
|
||||
AudioMixer();
|
||||
~AudioMixer() override;
|
||||
void Init(const char* device) override;
|
||||
void Close() override;
|
||||
void Lock() override;
|
||||
void Unlock() override;
|
||||
IAudioChannel* Play(IAudioSource* source, int32_t loop, bool deleteondone) override;
|
||||
void Stop(IAudioChannel* channel) override;
|
||||
void SetVolume(float volume) override;
|
||||
ISDLAudioSource* AddSource(std::unique_ptr<ISDLAudioSource> source);
|
||||
|
||||
const AudioFormat& GetFormat() const;
|
||||
|
||||
private:
|
||||
void GetNextAudioChunk(uint8_t* dst, size_t length);
|
||||
void UpdateAdjustedSound();
|
||||
void MixChannel(ISDLAudioChannel* channel, uint8_t* data, size_t length);
|
||||
void RemoveReleasedSources();
|
||||
|
||||
/**
|
||||
* Resample the given buffer into _effectBuffer.
|
||||
* Assumes that srcBuffer is the same format as _format.
|
||||
*/
|
||||
size_t ApplyResample(
|
||||
ISDLAudioChannel* channel, const void* srcBuffer, int32_t srcSamples, int32_t dstSamples, int32_t inRate,
|
||||
int32_t outRate);
|
||||
void ApplyPan(const IAudioChannel* channel, void* buffer, size_t len, size_t sampleSize);
|
||||
int32_t ApplyVolume(const IAudioChannel* channel, void* buffer, size_t len);
|
||||
static void EffectPanS16(const IAudioChannel* channel, int16_t* data, int32_t length);
|
||||
static void EffectPanU8(const IAudioChannel* channel, uint8_t* data, int32_t length);
|
||||
static void EffectFadeS16(int16_t* data, int32_t length, int32_t startvolume, int32_t endvolume);
|
||||
static void EffectFadeU8(uint8_t* data, int32_t length, int32_t startvolume, int32_t endvolume);
|
||||
bool Convert(SDL_AudioCVT* cvt, const void* src, size_t len);
|
||||
};
|
||||
} // namespace OpenRCT2::Audio
|
||||
@@ -28,11 +28,26 @@ namespace OpenRCT2::Audio
|
||||
SDL_RWops* _rw = nullptr;
|
||||
uint64_t _dataBegin = 0;
|
||||
uint64_t _dataLength = 0;
|
||||
bool _released{};
|
||||
|
||||
public:
|
||||
~FileAudioSource() override
|
||||
{
|
||||
Unload();
|
||||
Release();
|
||||
}
|
||||
|
||||
bool IsReleased() const override
|
||||
{
|
||||
return _released;
|
||||
}
|
||||
|
||||
void Release() override
|
||||
{
|
||||
if (!_released)
|
||||
{
|
||||
Unload();
|
||||
_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetLength() const override
|
||||
@@ -181,55 +196,23 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
};
|
||||
|
||||
IAudioSource* AudioSource::CreateStreamFromWAV(const std::string& path)
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateStreamFromWAV(const std::string& path)
|
||||
{
|
||||
IAudioSource* source = nullptr;
|
||||
SDL_RWops* rw = SDL_RWFromFile(path.c_str(), "rb");
|
||||
auto* rw = SDL_RWFromFile(path.c_str(), "rb");
|
||||
if (rw != nullptr)
|
||||
{
|
||||
return AudioSource::CreateStreamFromWAV(rw);
|
||||
}
|
||||
return source;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IAudioSource* AudioSource::CreateStreamFromWAV(SDL_RWops* rw)
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateStreamFromWAV(SDL_RWops* rw)
|
||||
{
|
||||
auto source = new FileAudioSource();
|
||||
auto source = std::make_unique<FileAudioSource>();
|
||||
if (!source->LoadWAV(rw))
|
||||
{
|
||||
delete source;
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
IAudioSource* AudioSource::CreateStreamFromWAV(std::unique_ptr<IStream> stream)
|
||||
{
|
||||
auto rw = new SDL_RWops();
|
||||
*rw = {};
|
||||
rw->type = SDL_RWOPS_UNKNOWN;
|
||||
rw->hidden.unknown.data1 = stream.release();
|
||||
rw->seek = [](SDL_RWops* ctx, Sint64 offset, int whence) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
ptr->Seek(offset, whence);
|
||||
return static_cast<Sint64>(ptr->GetPosition());
|
||||
};
|
||||
rw->read = [](SDL_RWops* ctx, void* buf, size_t size, size_t maxnum) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
return static_cast<size_t>(ptr->TryRead(buf, size * maxnum) / size);
|
||||
};
|
||||
rw->size = [](SDL_RWops* ctx) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
return static_cast<Sint64>(ptr->GetLength());
|
||||
};
|
||||
rw->close = [](SDL_RWops* ctx) {
|
||||
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
|
||||
delete ptr;
|
||||
ctx->hidden.unknown.data1 = nullptr;
|
||||
delete ctx;
|
||||
return 0;
|
||||
};
|
||||
return CreateStreamFromWAV(rw);
|
||||
}
|
||||
|
||||
} // namespace OpenRCT2::Audio
|
||||
|
||||
@@ -27,8 +27,9 @@ namespace OpenRCT2::Audio
|
||||
private:
|
||||
AudioFormat _format = {};
|
||||
std::vector<uint8_t> _data;
|
||||
uint8_t* _dataSDL = nullptr;
|
||||
size_t _length = 0;
|
||||
uint8_t* _dataSDL{};
|
||||
size_t _length{};
|
||||
bool _released{};
|
||||
|
||||
const uint8_t* GetData()
|
||||
{
|
||||
@@ -38,7 +39,21 @@ namespace OpenRCT2::Audio
|
||||
public:
|
||||
~MemoryAudioSource() override
|
||||
{
|
||||
Unload();
|
||||
Release();
|
||||
}
|
||||
|
||||
bool IsReleased() const override
|
||||
{
|
||||
return _released;
|
||||
}
|
||||
|
||||
void Release() override
|
||||
{
|
||||
if (!_released)
|
||||
{
|
||||
Unload();
|
||||
_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetLength() const override
|
||||
@@ -67,87 +82,63 @@ namespace OpenRCT2::Audio
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
bool LoadWAV(const utf8* path)
|
||||
bool LoadWAV(SDL_RWops* rw)
|
||||
{
|
||||
log_verbose("MemoryAudioSource::LoadWAV(%s)", path);
|
||||
|
||||
Unload();
|
||||
|
||||
bool result = false;
|
||||
SDL_RWops* rw = SDL_RWFromFile(path, "rb");
|
||||
if (rw != nullptr)
|
||||
SDL_AudioSpec audiospec = {};
|
||||
uint32_t audioLen;
|
||||
SDL_AudioSpec* spec = SDL_LoadWAV_RW(rw, false, &audiospec, &_dataSDL, &audioLen);
|
||||
if (spec != nullptr)
|
||||
{
|
||||
SDL_AudioSpec audiospec = {};
|
||||
uint32_t audioLen;
|
||||
SDL_AudioSpec* spec = SDL_LoadWAV_RW(rw, false, &audiospec, &_dataSDL, &audioLen);
|
||||
if (spec != nullptr)
|
||||
{
|
||||
_format.freq = spec->freq;
|
||||
_format.format = spec->format;
|
||||
_format.channels = spec->channels;
|
||||
_length = audioLen;
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Error loading %s, unsupported WAV format", path);
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Error loading %s", path);
|
||||
_format.freq = spec->freq;
|
||||
_format.format = spec->format;
|
||||
_format.channels = spec->channels;
|
||||
_length = audioLen;
|
||||
result = true;
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LoadCSS1(const utf8* path, size_t index)
|
||||
bool LoadCSS1(SDL_RWops* rw, size_t index)
|
||||
{
|
||||
log_verbose("MemoryAudioSource::LoadCSS1(%s, %d)", path, index);
|
||||
|
||||
Unload();
|
||||
|
||||
bool result = false;
|
||||
SDL_RWops* rw = SDL_RWFromFile(path, "rb");
|
||||
if (rw != nullptr)
|
||||
bool result{};
|
||||
uint32_t numSounds{};
|
||||
SDL_RWread(rw, &numSounds, sizeof(numSounds), 1);
|
||||
if (index < numSounds)
|
||||
{
|
||||
uint32_t numSounds{};
|
||||
SDL_RWread(rw, &numSounds, sizeof(numSounds), 1);
|
||||
if (index < numSounds)
|
||||
SDL_RWseek(rw, index * 4, RW_SEEK_CUR);
|
||||
|
||||
uint32_t pcmOffset{};
|
||||
SDL_RWread(rw, &pcmOffset, sizeof(pcmOffset), 1);
|
||||
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
|
||||
|
||||
uint32_t pcmSize{};
|
||||
SDL_RWread(rw, &pcmSize, sizeof(pcmSize), 1);
|
||||
_length = pcmSize;
|
||||
|
||||
WaveFormatEx waveFormat{};
|
||||
SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1);
|
||||
_format.freq = waveFormat.frequency;
|
||||
_format.format = AUDIO_S16LSB;
|
||||
_format.channels = waveFormat.channels;
|
||||
|
||||
try
|
||||
{
|
||||
SDL_RWseek(rw, index * 4, RW_SEEK_CUR);
|
||||
|
||||
uint32_t pcmOffset{};
|
||||
SDL_RWread(rw, &pcmOffset, sizeof(pcmOffset), 1);
|
||||
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
|
||||
|
||||
uint32_t pcmSize{};
|
||||
SDL_RWread(rw, &pcmSize, sizeof(pcmSize), 1);
|
||||
_length = pcmSize;
|
||||
|
||||
WaveFormatEx waveFormat{};
|
||||
SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1);
|
||||
_format.freq = waveFormat.frequency;
|
||||
_format.format = AUDIO_S16LSB;
|
||||
_format.channels = waveFormat.channels;
|
||||
|
||||
try
|
||||
{
|
||||
_data.resize(_length);
|
||||
SDL_RWread(rw, _data.data(), _length, 1);
|
||||
result = true;
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
log_verbose("Unable to allocate data");
|
||||
}
|
||||
_data.resize(_length);
|
||||
SDL_RWread(rw, _data.data(), _length, 1);
|
||||
result = true;
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
log_verbose("Unable to allocate data");
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Unable to load %s", path);
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -195,44 +186,58 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
};
|
||||
|
||||
IAudioSource* AudioSource::CreateMemoryFromCSS1(const std::string& path, size_t index, const AudioFormat* targetFormat)
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateMemoryFromCSS1(
|
||||
const std::string& path, size_t index, const AudioFormat* targetFormat)
|
||||
{
|
||||
auto source = new MemoryAudioSource();
|
||||
if (source->LoadCSS1(path.c_str(), index))
|
||||
log_verbose("AudioSource::CreateMemoryFromCSS1(%s, %d)", path.c_str(), index);
|
||||
SDL_RWops* rw = SDL_RWFromFile(path.c_str(), "rb");
|
||||
if (rw != nullptr)
|
||||
{
|
||||
return CreateMemoryFromCSS1(rw, index, targetFormat);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Unable to load %s", path.c_str());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateMemoryFromCSS1(
|
||||
SDL_RWops* rw, size_t index, const AudioFormat* targetFormat)
|
||||
{
|
||||
auto source = std::make_unique<MemoryAudioSource>();
|
||||
if (source->LoadCSS1(rw, index))
|
||||
{
|
||||
if (targetFormat != nullptr && source->GetFormat() != *targetFormat)
|
||||
{
|
||||
if (!source->Convert(targetFormat))
|
||||
{
|
||||
delete source;
|
||||
source = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete source;
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
IAudioSource* AudioSource::CreateMemoryFromWAV(const std::string& path, const AudioFormat* targetFormat)
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateMemoryFromWAV(SDL_RWops* rw, const AudioFormat* targetFormat)
|
||||
{
|
||||
auto source = new MemoryAudioSource();
|
||||
if (source->LoadWAV(path.c_str()))
|
||||
auto source = std::make_unique<MemoryAudioSource>();
|
||||
if (source->LoadWAV(rw))
|
||||
{
|
||||
if (targetFormat != nullptr && source->GetFormat() != *targetFormat)
|
||||
{
|
||||
if (!source->Convert(targetFormat))
|
||||
{
|
||||
SafeDelete(source);
|
||||
source = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete source;
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<ItemGroup>
|
||||
<ClInclude Include="audio\AudioContext.h" />
|
||||
<ClInclude Include="audio\AudioFormat.h" />
|
||||
<ClInclude Include="audio\AudioMixer.h" />
|
||||
<ClInclude Include="CursorRepository.h" />
|
||||
<ClInclude Include="drawing\BitmapReader.h" />
|
||||
<ClInclude Include="drawing\engines\DrawingEngineFactory.hpp" />
|
||||
@@ -119,7 +120,7 @@
|
||||
<ClCompile Include="scripting\CustomMenu.cpp" />
|
||||
<ClCompile Include="scripting\CustomWindow.cpp" />
|
||||
<ClCompile Include="scripting\UiExtensions.cpp" />
|
||||
<ClCompile Include="SDLException.cpp" />
|
||||
<ClCompile Include="SDLException.cpp" />
|
||||
<ClCompile Include="TextComposition.cpp" />
|
||||
<ClCompile Include="title\TitleSequencePlayer.cpp" />
|
||||
<ClCompile Include="Ui.cpp" />
|
||||
|
||||
@@ -1343,18 +1343,35 @@ private:
|
||||
Dropdown::SetChecked(OpenRCT2::Audio::GetCurrentDeviceIndex(), true);
|
||||
break;
|
||||
case WIDX_TITLE_MUSIC_DROPDOWN:
|
||||
uint32_t numItems = 4;
|
||||
|
||||
for (size_t i = 0; i < numItems; i++)
|
||||
{
|
||||
if (gConfigGeneral.rct1_path.empty())
|
||||
{
|
||||
gDropdownItems[i].Format = STR_DROPDOWN_MENU_LABEL;
|
||||
gDropdownItems[i].Args = TitleMusicNames[i];
|
||||
// Only show None and RCT2
|
||||
int32_t numItems{};
|
||||
gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL;
|
||||
gDropdownItems[numItems++].Args = TitleMusicNames[0];
|
||||
gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL;
|
||||
gDropdownItems[numItems++].Args = TitleMusicNames[2];
|
||||
ShowDropdown(widget, numItems);
|
||||
if (gConfigSound.title_music == TitleMusicKind::None)
|
||||
Dropdown::SetChecked(0, true);
|
||||
else if (gConfigSound.title_music == TitleMusicKind::Rct2)
|
||||
Dropdown::SetChecked(1, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Show None, RCT1, RCT2 and random
|
||||
int32_t numItems{};
|
||||
for (auto musicName : TitleMusicNames)
|
||||
{
|
||||
gDropdownItems[numItems].Format = STR_DROPDOWN_MENU_LABEL;
|
||||
gDropdownItems[numItems++].Args = musicName;
|
||||
}
|
||||
ShowDropdown(widget, numItems);
|
||||
Dropdown::SetChecked(EnumValue(gConfigSound.title_music), true);
|
||||
}
|
||||
|
||||
ShowDropdown(widget, numItems);
|
||||
|
||||
Dropdown::SetChecked(gConfigSound.title_music, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1384,21 +1401,24 @@ private:
|
||||
Invalidate();
|
||||
break;
|
||||
case WIDX_TITLE_MUSIC_DROPDOWN:
|
||||
if ((dropdownIndex == 1 || dropdownIndex == 3) && !File::Exists(context_get_path_legacy(PATH_ID_CSS50)))
|
||||
{
|
||||
auto titleMusic = static_cast<TitleMusicKind>(dropdownIndex);
|
||||
if (gConfigGeneral.rct1_path.empty() && dropdownIndex != 0)
|
||||
{
|
||||
context_show_error(STR_OPTIONS_MUSIC_ERR_CSS50_NOT_FOUND, STR_OPTIONS_MUSIC_ERR_CSS50_NOT_FOUND_HINT, {});
|
||||
}
|
||||
else
|
||||
{
|
||||
gConfigSound.title_music = static_cast<int8_t>(dropdownIndex);
|
||||
config_save_default();
|
||||
Invalidate();
|
||||
titleMusic = TitleMusicKind::Rct2;
|
||||
}
|
||||
|
||||
gConfigSound.title_music = titleMusic;
|
||||
config_save_default();
|
||||
Invalidate();
|
||||
|
||||
OpenRCT2::Audio::StopTitleMusic();
|
||||
if (dropdownIndex != 0)
|
||||
if (titleMusic != TitleMusicKind::None)
|
||||
{
|
||||
OpenRCT2::Audio::PlayTitleMusic();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1440,6 +1460,16 @@ private:
|
||||
return { 500, 0 };
|
||||
}
|
||||
|
||||
rct_string_id GetTitleMusicName()
|
||||
{
|
||||
auto index = EnumValue(gConfigSound.title_music);
|
||||
if (index < 0 || static_cast<size_t>(index) >= std::size(TitleMusicNames))
|
||||
{
|
||||
index = EnumValue(TitleMusicKind::None);
|
||||
}
|
||||
return TitleMusicNames[index];
|
||||
}
|
||||
|
||||
void AudioPrepareDraw()
|
||||
{
|
||||
// Sound device
|
||||
@@ -1469,7 +1499,7 @@ private:
|
||||
auto ft = Formatter::Common();
|
||||
ft.Add<char*>(audioDeviceName);
|
||||
|
||||
widgets[WIDX_TITLE_MUSIC].text = TitleMusicNames[gConfigSound.title_music];
|
||||
widgets[WIDX_TITLE_MUSIC].text = GetTitleMusicName();
|
||||
|
||||
SetCheckboxValue(WIDX_SOUND_CHECKBOX, gConfigSound.sound_enabled);
|
||||
SetCheckboxValue(WIDX_MASTER_SOUND_CHECKBOX, gConfigSound.master_sound_enabled);
|
||||
|
||||
@@ -320,50 +320,6 @@ namespace OpenRCT2
|
||||
context_open_window(WC_SAVE_PROMPT);
|
||||
}
|
||||
|
||||
std::string GetPathLegacy(int32_t pathId) override
|
||||
{
|
||||
static constexpr const char* const LegacyFileNames[PATH_ID_END] = {
|
||||
nullptr, nullptr, "css1.dat", "css2.dat", "css4.dat", "css5.dat", "css6.dat", "css7.dat",
|
||||
"css8.dat", "css9.dat", "css11.dat", "css12.dat", "css13.dat", "css14.dat", "css15.dat", "css3.dat",
|
||||
"css17.dat", "css18.dat", "css19.dat", "css20.dat", "css21.dat", "css22.dat", nullptr, "css23.dat",
|
||||
"css24.dat", "css25.dat", "css26.dat", "css27.dat", "css28.dat", "css29.dat", "css30.dat", "css31.dat",
|
||||
"css32.dat", "css33.dat", "css34.dat", "css35.dat", "css36.dat", "css37.dat", "css38.dat", "CUSTOM1.WAV",
|
||||
"CUSTOM2.WAV", "css39.dat", "css40.dat", "css41.dat", nullptr, "css42.dat", "css43.dat", "css44.dat",
|
||||
"css45.dat", "css46.dat", "css50.dat",
|
||||
};
|
||||
|
||||
std::string result;
|
||||
if (pathId == PATH_ID_CSS50)
|
||||
{
|
||||
if (!(_env->GetDirectoryPath(DIRBASE::RCT1).empty()))
|
||||
{
|
||||
auto dataPath = _env->GetDirectoryPath(DIRBASE::RCT1, DIRID::DATA);
|
||||
result = Path::ResolveCasing(Path::Combine(dataPath, u8"css17.dat"));
|
||||
|
||||
if (!File::Exists(result))
|
||||
{
|
||||
auto rct1Path = _env->GetDirectoryPath(DIRBASE::RCT1);
|
||||
result = Path::ResolveCasing(Path::Combine(rct1Path, u8"RCTdeluxe_install", u8"Data", u8"css17.dat"));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto dataPath = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA);
|
||||
result = Path::ResolveCasing(Path::Combine(dataPath, u8"css50.dat"));
|
||||
}
|
||||
}
|
||||
else if (pathId >= 0 && pathId < PATH_ID_END)
|
||||
{
|
||||
auto fileName = LegacyFileNames[pathId];
|
||||
if (fileName != nullptr)
|
||||
{
|
||||
auto dataPath = _env->GetDirectoryPath(DIRBASE::RCT2, DIRID::DATA);
|
||||
result = Path::Combine(dataPath, fileName);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Initialise() final override
|
||||
{
|
||||
if (_initialised)
|
||||
@@ -1553,14 +1509,6 @@ void context_quit()
|
||||
GetContext()->Quit();
|
||||
}
|
||||
|
||||
const utf8* context_get_path_legacy(int32_t pathId)
|
||||
{
|
||||
static utf8 result[MAX_PATH];
|
||||
auto path = GetContext()->GetPathLegacy(pathId);
|
||||
String::Set(result, sizeof(result), path.c_str());
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ContextOpenCommonFileDialog(utf8* outFilename, OpenRCT2::Ui::FileDialogDesc& desc, size_t outSize)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -160,10 +160,6 @@ namespace OpenRCT2
|
||||
|
||||
virtual bool HasNewVersionInfo() const abstract;
|
||||
virtual const NewVersionInfo* GetNewVersionInfo() const abstract;
|
||||
/**
|
||||
* This is deprecated, use IPlatformEnvironment.
|
||||
*/
|
||||
virtual std::string GetPathLegacy(int32_t pathId) abstract;
|
||||
|
||||
virtual void SetTimeScale(float newScale) abstract;
|
||||
virtual float GetTimeScale() const abstract;
|
||||
@@ -191,66 +187,6 @@ namespace
|
||||
constexpr float GAME_MIN_TIME_SCALE = 0.1f;
|
||||
constexpr float GAME_MAX_TIME_SCALE = 5.0f;
|
||||
|
||||
/**
|
||||
* Legacy get_file_path IDs.
|
||||
* Remove when context_get_path_legacy is removed.
|
||||
*/
|
||||
enum
|
||||
{
|
||||
PATH_ID_G1,
|
||||
PATH_ID_PLUGIN,
|
||||
PATH_ID_CSS1,
|
||||
PATH_ID_CSS2,
|
||||
PATH_ID_CSS4,
|
||||
PATH_ID_CSS5,
|
||||
PATH_ID_CSS6,
|
||||
PATH_ID_CSS7,
|
||||
PATH_ID_CSS8,
|
||||
PATH_ID_CSS9,
|
||||
PATH_ID_CSS11,
|
||||
PATH_ID_CSS12,
|
||||
PATH_ID_CSS13,
|
||||
PATH_ID_CSS14,
|
||||
PATH_ID_CSS15,
|
||||
PATH_ID_CSS3,
|
||||
PATH_ID_CSS17,
|
||||
PATH_ID_CSS18,
|
||||
PATH_ID_CSS19,
|
||||
PATH_ID_CSS20,
|
||||
PATH_ID_CSS21,
|
||||
PATH_ID_CSS22,
|
||||
PATH_ID_SCORES,
|
||||
PATH_ID_CSS23,
|
||||
PATH_ID_CSS24,
|
||||
PATH_ID_CSS25,
|
||||
PATH_ID_CSS26,
|
||||
PATH_ID_CSS27,
|
||||
PATH_ID_CSS28,
|
||||
PATH_ID_CSS29,
|
||||
PATH_ID_CSS30,
|
||||
PATH_ID_CSS31,
|
||||
PATH_ID_CSS32,
|
||||
PATH_ID_CSS33,
|
||||
PATH_ID_CSS34,
|
||||
PATH_ID_CSS35,
|
||||
PATH_ID_CSS36,
|
||||
PATH_ID_CSS37,
|
||||
PATH_ID_CSS38,
|
||||
PATH_ID_CUSTOM1,
|
||||
PATH_ID_CUSTOM2,
|
||||
PATH_ID_CSS39,
|
||||
PATH_ID_CSS40,
|
||||
PATH_ID_CSS41,
|
||||
PATH_ID_SIXFLAGS_MAGICMOUNTAIN,
|
||||
PATH_ID_CSS42,
|
||||
PATH_ID_CSS43,
|
||||
PATH_ID_CSS44,
|
||||
PATH_ID_CSS45,
|
||||
PATH_ID_CSS46,
|
||||
PATH_ID_CSS50,
|
||||
PATH_ID_END,
|
||||
};
|
||||
|
||||
void context_init();
|
||||
void context_setcurrentcursor(CursorID cursor);
|
||||
void context_update_cursor_scale();
|
||||
@@ -283,7 +219,6 @@ void context_update_map_tooltip();
|
||||
void context_handle_input();
|
||||
void context_input_handle_keyboard(bool isTitle);
|
||||
void context_quit();
|
||||
const utf8* context_get_path_legacy(int32_t pathId);
|
||||
bool context_load_park_from_file(const utf8* path);
|
||||
bool context_load_park_from_stream(void* stream);
|
||||
bool ContextOpenCommonFileDialog(utf8* outFilename, OpenRCT2::Ui::FileDialogDesc& desc, size_t outSize);
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Editor
|
||||
|
||||
// Unload objects first, the repository is re-populated which owns the objects.
|
||||
auto& objectManager = context->GetObjectManager();
|
||||
objectManager.UnloadAll();
|
||||
objectManager.UnloadAllTransient();
|
||||
|
||||
// Scan objects if necessary
|
||||
const auto& localisationService = context->GetLocalisationService();
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "../interface/Viewport.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../localisation/StringIds.h"
|
||||
#include "../object/AudioObject.h"
|
||||
#include "../object/ObjectManager.h"
|
||||
#include "../ride/Ride.h"
|
||||
#include "../ride/RideAudio.h"
|
||||
#include "../ui/UiContext.h"
|
||||
@@ -42,6 +44,8 @@ namespace OpenRCT2::Audio
|
||||
|
||||
static std::vector<std::string> _audioDevices;
|
||||
static int32_t _currentAudioDevice = -1;
|
||||
static ObjectEntryIndex _soundsAudioObjectEntryIndex = OBJECT_ENTRY_INDEX_NULL;
|
||||
static ObjectEntryIndex _titleAudioObjectEntryIndex = OBJECT_ENTRY_INDEX_NULL;
|
||||
|
||||
bool gGameSoundsOff = false;
|
||||
int32_t gVolumeAdjustZoom = 0;
|
||||
@@ -51,77 +55,6 @@ namespace OpenRCT2::Audio
|
||||
|
||||
VehicleSound gVehicleSoundList[MaxVehicleSounds];
|
||||
|
||||
// clang-format off
|
||||
static int32_t SoundVolumeAdjust[RCT2SoundCount] =
|
||||
{
|
||||
0, // LiftClassic
|
||||
0, // TrackFrictionClassicWood
|
||||
0, // FrictionClassic
|
||||
0, // Scream1
|
||||
0, // Click1
|
||||
0, // Click2
|
||||
0, // PlaceItem
|
||||
0, // Scream2
|
||||
0, // Scream3
|
||||
0, // Scream4
|
||||
0, // Scream5
|
||||
0, // Scream6
|
||||
0, // LiftFrictionWheels
|
||||
-400, // Purchase
|
||||
0, // Crash
|
||||
0, // LayingOutWater
|
||||
0, // Water1
|
||||
0, // Water2
|
||||
0, // TrainWhistle
|
||||
0, // TrainDeparting
|
||||
-1000, // WaterSplash
|
||||
0, // GoKartEngine
|
||||
-800, // RideLaunch1
|
||||
-1700, // RideLaunch2
|
||||
-700, // Cough1
|
||||
-700, // Cough2
|
||||
-700, // Cough3
|
||||
-700, // Cough4
|
||||
0, // Rain
|
||||
0, // Thunder1
|
||||
0, // Thunder2
|
||||
0, // TrackFrictionTrain
|
||||
0, // TrackFrictionWater
|
||||
0, // BalloonPop
|
||||
-700, // MechanicFix
|
||||
0, // Scream7
|
||||
-2500, // ToiletFlush original value: -1000
|
||||
0, // Click3
|
||||
0, // Quack
|
||||
0, // NewsItem
|
||||
0, // WindowOpen
|
||||
-900, // Laugh1
|
||||
-900, // Laugh2
|
||||
-900, // Laugh3
|
||||
0, // Applause
|
||||
-600, // HauntedHouseScare
|
||||
-700, // HauntedHouseScream1
|
||||
-700, // HauntedHouseScream2
|
||||
-2550, // BlockBrakeClose
|
||||
-2900, // BlockBrakeRelease
|
||||
0, // Error
|
||||
-3400, // BrakeRelease
|
||||
0, // LiftArrow
|
||||
0, // LiftWood
|
||||
0, // TrackFrictionWood
|
||||
0, // LiftWildMouse
|
||||
0, // LiftBM
|
||||
0, // TrackFrictionBM
|
||||
0, // Scream8
|
||||
0, // Tram
|
||||
-2000, // DoorOpen
|
||||
-2700, // DoorClose
|
||||
-700 // Portcullis
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static AudioParams GetParametersFromLocation(SoundId soundId, const CoordsXYZ& location);
|
||||
|
||||
bool IsAvailable()
|
||||
{
|
||||
if (_currentAudioDevice == -1)
|
||||
@@ -155,6 +88,14 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto baseAudio = objManager.LoadObject(AudioObjectIdentifiers::Rct2Base);
|
||||
if (baseAudio != nullptr)
|
||||
{
|
||||
_soundsAudioObjectEntryIndex = objManager.GetLoadedObjectEntryIndex(baseAudio);
|
||||
}
|
||||
objManager.LoadObject(AudioObjectIdentifiers::Rct2Circus);
|
||||
}
|
||||
|
||||
void PopulateDevices()
|
||||
@@ -180,25 +121,13 @@ namespace OpenRCT2::Audio
|
||||
_audioDevices = devices;
|
||||
}
|
||||
|
||||
void Play3D(SoundId soundId, const CoordsXYZ& loc)
|
||||
{
|
||||
if (!IsAvailable())
|
||||
return;
|
||||
|
||||
AudioParams params = GetParametersFromLocation(soundId, loc);
|
||||
if (params.in_range)
|
||||
{
|
||||
Play(soundId, params.volume, params.pan);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the audio parameters to use when playing the specified sound at a virtual location.
|
||||
* @param soundId The sound effect to be played.
|
||||
* @param location The location at which the sound effect is to be played.
|
||||
* @return The audio parameters to be used when playing this sound effect.
|
||||
*/
|
||||
static AudioParams GetParametersFromLocation(SoundId soundId, const CoordsXYZ& location)
|
||||
static AudioParams GetParametersFromLocation(AudioObject* obj, uint32_t sampleIndex, const CoordsXYZ& location)
|
||||
{
|
||||
int32_t volumeDown = 0;
|
||||
AudioParams params;
|
||||
@@ -222,8 +151,10 @@ namespace OpenRCT2::Audio
|
||||
{
|
||||
int16_t vx = pos2.x - viewport->viewPos.x;
|
||||
params.pan = viewport->pos.x + viewport->zoom.ApplyInversedTo(vx);
|
||||
params.volume = SoundVolumeAdjust[static_cast<uint8_t>(soundId)]
|
||||
+ ((viewport->zoom.ApplyTo(-1024) - 1) * (1 << volumeDown)) + 1;
|
||||
|
||||
auto sampleModifier = obj->GetSampleModifier(sampleIndex);
|
||||
auto viewModifier = ((viewport->zoom.ApplyTo(-1024) - 1) * (1 << volumeDown)) + 1;
|
||||
params.volume = sampleModifier + viewModifier;
|
||||
|
||||
if (!viewport->Contains(pos2) || params.volume < -10000)
|
||||
{
|
||||
@@ -236,11 +167,16 @@ namespace OpenRCT2::Audio
|
||||
return params;
|
||||
}
|
||||
|
||||
void Play(SoundId soundId, int32_t volume, int32_t pan)
|
||||
AudioObject* GetBaseAudioObject()
|
||||
{
|
||||
if (gGameSoundsOff)
|
||||
return;
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto baseAudioObject = static_cast<AudioObject*>(
|
||||
objManager.GetLoadedObject(ObjectType::Audio, _soundsAudioObjectEntryIndex));
|
||||
return baseAudioObject;
|
||||
}
|
||||
|
||||
static void Play(IAudioSource* audioSource, int32_t volume, int32_t pan)
|
||||
{
|
||||
int32_t mixerPan = 0;
|
||||
if (pan != AUDIO_PLAY_AT_CENTRE)
|
||||
{
|
||||
@@ -249,7 +185,62 @@ namespace OpenRCT2::Audio
|
||||
mixerPan = ((x2 / screenWidth) - 0x8000) >> 4;
|
||||
}
|
||||
|
||||
Mixer_Play_Effect(soundId, MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(mixerPan), 1, 1);
|
||||
Mixer_Play_Effect(audioSource, MIXER_LOOP_NONE, DStoMixerVolume(volume), DStoMixerPan(mixerPan), 1, 1);
|
||||
}
|
||||
|
||||
void Play3D(SoundId soundId, const CoordsXYZ& loc)
|
||||
{
|
||||
if (!IsAvailable())
|
||||
return;
|
||||
|
||||
// Get sound from base object
|
||||
auto baseAudioObject = GetBaseAudioObject();
|
||||
if (baseAudioObject != nullptr)
|
||||
{
|
||||
auto params = GetParametersFromLocation(baseAudioObject, EnumValue(soundId), loc);
|
||||
if (params.in_range)
|
||||
{
|
||||
auto source = baseAudioObject->GetSample(EnumValue(soundId));
|
||||
if (source != nullptr)
|
||||
{
|
||||
Play(source, params.volume, params.pan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Play(SoundId soundId, int32_t volume, int32_t pan)
|
||||
{
|
||||
if (!IsAvailable())
|
||||
return;
|
||||
|
||||
// Get sound from base object
|
||||
auto baseAudioObject = GetBaseAudioObject();
|
||||
if (baseAudioObject != nullptr)
|
||||
{
|
||||
auto source = baseAudioObject->GetSample(EnumValue(soundId));
|
||||
if (source != nullptr)
|
||||
{
|
||||
Play(source, volume, pan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ObjectEntryDescriptor GetTitleMusicDescriptor()
|
||||
{
|
||||
switch (gConfigSound.title_music)
|
||||
{
|
||||
default:
|
||||
return {};
|
||||
case TitleMusicKind::Rct1:
|
||||
return ObjectEntryDescriptor(ObjectType::Audio, AudioObjectIdentifiers::Rct1Title);
|
||||
case TitleMusicKind::Rct2:
|
||||
return ObjectEntryDescriptor(ObjectType::Audio, AudioObjectIdentifiers::Rct2Title);
|
||||
case TitleMusicKind::Random:
|
||||
return ObjectEntryDescriptor(
|
||||
ObjectType::Audio,
|
||||
(util_rand() & 1) ? AudioObjectIdentifiers::Rct1Title : AudioObjectIdentifiers::Rct2Title);
|
||||
}
|
||||
}
|
||||
|
||||
void PlayTitleMusic()
|
||||
@@ -265,26 +256,24 @@ namespace OpenRCT2::Audio
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t pathId;
|
||||
switch (gConfigSound.title_music)
|
||||
// Load title sequence audio object
|
||||
auto descriptor = GetTitleMusicDescriptor();
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto audioObject = static_cast<AudioObject*>(objManager.LoadObject(descriptor));
|
||||
if (audioObject != nullptr)
|
||||
{
|
||||
case 1:
|
||||
pathId = PATH_ID_CSS50;
|
||||
break;
|
||||
case 2:
|
||||
pathId = PATH_ID_CSS17;
|
||||
break;
|
||||
case 3:
|
||||
pathId = (util_rand() & 1) ? PATH_ID_CSS50 : PATH_ID_CSS17;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
_titleAudioObjectEntryIndex = objManager.GetLoadedObjectEntryIndex(audioObject);
|
||||
|
||||
gTitleMusicChannel = Mixer_Play_Music(pathId, MIXER_LOOP_INFINITE, true);
|
||||
if (gTitleMusicChannel != nullptr)
|
||||
{
|
||||
Mixer_Channel_SetGroup(gTitleMusicChannel, OpenRCT2::Audio::MixerGroup::TitleMusic);
|
||||
// Play first sample from object
|
||||
auto source = audioObject->GetSample(0);
|
||||
if (source != nullptr)
|
||||
{
|
||||
gTitleMusicChannel = Mixer_Play_Music(source, MIXER_LOOP_INFINITE, true);
|
||||
if (gTitleMusicChannel != nullptr)
|
||||
{
|
||||
Mixer_Channel_SetGroup(gTitleMusicChannel, MixerGroup::TitleMusic);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,6 +313,18 @@ namespace OpenRCT2::Audio
|
||||
Mixer_Stop_Channel(gTitleMusicChannel);
|
||||
gTitleMusicChannel = nullptr;
|
||||
}
|
||||
|
||||
// Unload the audio object
|
||||
if (_titleAudioObjectEntryIndex != OBJECT_ENTRY_INDEX_NULL)
|
||||
{
|
||||
auto& objManager = GetContext()->GetObjectManager();
|
||||
auto obj = objManager.GetLoadedObject(ObjectType::Audio, _titleAudioObjectEntryIndex);
|
||||
if (obj != nullptr)
|
||||
{
|
||||
objManager.UnloadObjects({ obj->GetDescriptor() });
|
||||
}
|
||||
_titleAudioObjectEntryIndex = OBJECT_ENTRY_INDEX_NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void StopWeatherSound()
|
||||
|
||||
@@ -58,8 +58,6 @@ namespace OpenRCT2::Audio
|
||||
virtual bool DeleteOnDone() const abstract;
|
||||
virtual void SetDeleteOnDone(bool value) abstract;
|
||||
|
||||
virtual void SetDeleteSourceOnDone(bool value) abstract;
|
||||
|
||||
virtual bool IsPlaying() const abstract;
|
||||
|
||||
virtual void Play(IAudioSource* source, int32_t loop = 0) abstract;
|
||||
|
||||
@@ -34,6 +34,8 @@ namespace OpenRCT2::Audio
|
||||
virtual std::vector<std::string> GetOutputDevices() abstract;
|
||||
virtual void SetOutputDevice(const std::string& deviceName) abstract;
|
||||
|
||||
virtual IAudioSource* CreateStreamFromCSS(const std::string& path, uint32_t index) abstract;
|
||||
virtual IAudioSource* CreateStreamFromCSS(std::unique_ptr<IStream> stream, uint32_t index) abstract;
|
||||
virtual IAudioSource* CreateStreamFromWAV(const std::string& path) abstract;
|
||||
virtual IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) abstract;
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include "../Context.h"
|
||||
#include "../config/Config.h"
|
||||
#include "../object/AudioObject.h"
|
||||
#include "../object/ObjectManager.h"
|
||||
#include "AudioChannel.h"
|
||||
#include "AudioContext.h"
|
||||
#include "AudioSource.h"
|
||||
@@ -37,31 +39,43 @@ void Mixer_Init(const char* device)
|
||||
audioContext->SetOutputDevice(std::string(device));
|
||||
}
|
||||
|
||||
void* Mixer_Play_Effect(SoundId id, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone)
|
||||
IAudioChannel* Mixer_Play_Effect(SoundId id, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone)
|
||||
{
|
||||
if (gConfigSound.sound_enabled)
|
||||
{
|
||||
// Get sound from base object
|
||||
auto baseAudioObject = GetBaseAudioObject();
|
||||
if (baseAudioObject != nullptr)
|
||||
{
|
||||
auto source = baseAudioObject->GetSample(EnumValue(id));
|
||||
if (source != nullptr)
|
||||
{
|
||||
return Mixer_Play_Effect(source, loop, volume, pan, rate, deleteondone);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IAudioChannel* Mixer_Play_Effect(
|
||||
IAudioSource* source, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone)
|
||||
{
|
||||
IAudioChannel* channel = nullptr;
|
||||
if (gConfigSound.sound_enabled)
|
||||
{
|
||||
if (static_cast<uint32_t>(id) >= RCT2SoundCount)
|
||||
IAudioMixer* mixer = GetMixer();
|
||||
if (mixer != nullptr)
|
||||
{
|
||||
log_error("Tried to play an invalid sound id. %i", id);
|
||||
}
|
||||
else
|
||||
{
|
||||
IAudioMixer* mixer = GetMixer();
|
||||
if (mixer != nullptr)
|
||||
mixer->Lock();
|
||||
channel = mixer->Play(source, loop, deleteondone != 0);
|
||||
if (channel != nullptr)
|
||||
{
|
||||
mixer->Lock();
|
||||
IAudioSource* source = mixer->GetSoundSource(id);
|
||||
channel = mixer->Play(source, loop, deleteondone != 0, false);
|
||||
if (channel != nullptr)
|
||||
{
|
||||
channel->SetVolume(volume);
|
||||
channel->SetPan(pan);
|
||||
channel->SetRate(rate);
|
||||
}
|
||||
mixer->Unlock();
|
||||
channel->SetVolume(volume);
|
||||
channel->SetPan(pan);
|
||||
channel->SetRate(rate);
|
||||
channel->UpdateOldVolume();
|
||||
}
|
||||
mixer->Unlock();
|
||||
}
|
||||
}
|
||||
return channel;
|
||||
@@ -140,7 +154,7 @@ template<typename T> static void* PlayMusic(T&& src, int32_t loop)
|
||||
if (stream == nullptr)
|
||||
return nullptr;
|
||||
|
||||
auto* channel = mixer->Play(stream, loop, false, true);
|
||||
auto* channel = mixer->Play(stream, loop, false);
|
||||
if (channel == nullptr)
|
||||
{
|
||||
delete stream;
|
||||
@@ -151,36 +165,15 @@ template<typename T> static void* PlayMusic(T&& src, int32_t loop)
|
||||
return channel;
|
||||
}
|
||||
|
||||
void* Mixer_Play_Music(int32_t pathId, int32_t loop, int32_t streaming)
|
||||
IAudioChannel* Mixer_Play_Music(IAudioSource* source, int32_t loop, int32_t streaming)
|
||||
{
|
||||
IAudioChannel* channel = nullptr;
|
||||
IAudioMixer* mixer = GetMixer();
|
||||
if (mixer != nullptr)
|
||||
auto* mixer = GetMixer();
|
||||
if (mixer == nullptr)
|
||||
{
|
||||
if (streaming)
|
||||
{
|
||||
const utf8* path = context_get_path_legacy(pathId);
|
||||
|
||||
auto audioContext = GetContext()->GetAudioContext();
|
||||
IAudioSource* source = audioContext->CreateStreamFromWAV(path);
|
||||
if (source != nullptr)
|
||||
{
|
||||
channel = mixer->Play(source, loop, false, true);
|
||||
if (channel == nullptr)
|
||||
{
|
||||
delete source;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (mixer->LoadMusic(pathId))
|
||||
{
|
||||
IAudioSource* source = mixer->GetMusicSource(pathId);
|
||||
channel = mixer->Play(source, MIXER_LOOP_INFINITE, false, false);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* channel = mixer->Play(source, loop, false);
|
||||
if (channel != nullptr)
|
||||
{
|
||||
channel->SetGroup(MixerGroup::RideMusic);
|
||||
@@ -188,11 +181,6 @@ void* Mixer_Play_Music(int32_t pathId, int32_t loop, int32_t streaming)
|
||||
return channel;
|
||||
}
|
||||
|
||||
void* Mixer_Play_Music(const char* path, int32_t loop)
|
||||
{
|
||||
return PlayMusic(path, loop);
|
||||
}
|
||||
|
||||
void* Mixer_Play_Music(std::unique_ptr<IStream> stream, int32_t loop)
|
||||
{
|
||||
return PlayMusic(std::move(stream), loop);
|
||||
|
||||
@@ -43,13 +43,9 @@ namespace OpenRCT2::Audio
|
||||
virtual void Close() abstract;
|
||||
virtual void Lock() abstract;
|
||||
virtual void Unlock() abstract;
|
||||
virtual IAudioChannel* Play(IAudioSource* source, int32_t loop, bool deleteondone, bool deletesourceondone) abstract;
|
||||
virtual IAudioChannel* Play(IAudioSource* source, int32_t loop, bool deleteondone) abstract;
|
||||
virtual void Stop(IAudioChannel* channel) abstract;
|
||||
virtual bool LoadMusic(size_t pathid) abstract;
|
||||
virtual void SetVolume(float volume) abstract;
|
||||
|
||||
virtual IAudioSource* GetSoundSource(SoundId id) abstract;
|
||||
virtual IAudioSource* GetMusicSource(int32_t id) abstract;
|
||||
};
|
||||
} // namespace OpenRCT2::Audio
|
||||
|
||||
@@ -61,8 +57,10 @@ namespace OpenRCT2::Audio
|
||||
#endif
|
||||
|
||||
void Mixer_Init(const char* device);
|
||||
void* Mixer_Play_Effect(
|
||||
OpenRCT2::Audio::IAudioChannel* Mixer_Play_Effect(
|
||||
OpenRCT2::Audio::SoundId id, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone);
|
||||
OpenRCT2::Audio::IAudioChannel* Mixer_Play_Effect(
|
||||
OpenRCT2::Audio::IAudioSource* source, int32_t loop, int32_t volume, float pan, double rate, int32_t deleteondone);
|
||||
void Mixer_Stop_Channel(void* channel);
|
||||
void Mixer_Channel_Volume(void* channel, int32_t volume);
|
||||
void Mixer_Channel_Pan(void* channel, float pan);
|
||||
@@ -71,8 +69,7 @@ int32_t Mixer_Channel_IsPlaying(void* channel);
|
||||
uint64_t Mixer_Channel_GetOffset(void* channel);
|
||||
int32_t Mixer_Channel_SetOffset(void* channel, uint64_t offset);
|
||||
void Mixer_Channel_SetGroup(void* channel, OpenRCT2::Audio::MixerGroup group);
|
||||
void* Mixer_Play_Music(int32_t pathId, int32_t loop, int32_t streaming);
|
||||
void* Mixer_Play_Music(const char* path, int32_t loop);
|
||||
OpenRCT2::Audio::IAudioChannel* Mixer_Play_Music(OpenRCT2::Audio::IAudioSource* source, int32_t loop, int32_t streaming);
|
||||
void* Mixer_Play_Music(std::unique_ptr<OpenRCT2::IStream> stream, int32_t loop);
|
||||
void Mixer_SetVolume(float volume);
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ namespace OpenRCT2::Audio
|
||||
{
|
||||
virtual ~IAudioSource() = default;
|
||||
|
||||
virtual void Release() abstract;
|
||||
virtual uint64_t GetLength() const abstract;
|
||||
// virtual AudioFormat GetFormat() abstract;
|
||||
virtual size_t Read(void* dst, uint64_t offset, size_t len) abstract;
|
||||
};
|
||||
|
||||
|
||||
@@ -26,6 +26,16 @@ namespace OpenRCT2::Audio
|
||||
{
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromCSS(const std::string& /* path */, uint32_t /* index */) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromCSS(std::unique_ptr<IStream> /* stream */, uint32_t /* index */) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(const std::string& /*path*/) override
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user