Add new audio objects for loading sounds

This commit is contained in:
Ted John
2022-05-09 18:34:38 +01:00
parent 697fa7c436
commit a2e6691ac2
46 changed files with 1583 additions and 1152 deletions

View File

@@ -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

View File

@@ -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;

View File

@@ -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()

View File

@@ -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

View 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

View File

@@ -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

View File

@@ -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;

View File

@@ -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" />

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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();

View File

@@ -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()

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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