Browse Source

Implement PS2 audio via SPU

This give us 2MB of dedicated audio memory and support for compressed
audio at runtime which is a huge boost to the already limited system
memory
ps2
Anders Jenbo 3 years ago
parent
commit
246c6f9e66
  1. 7
      CMake/platforms/ps2.cmake
  2. 10
      Source/CMakeLists.txt
  3. 33
      Source/engine/sound.cpp
  4. 18
      Source/storm/storm_svid.cpp
  5. 92
      Source/utils/soundsample.cpp
  6. 47
      Source/utils/soundsample.h

7
CMake/platforms/ps2.cmake

@ -1,5 +1,4 @@
set(NONET ON)
set(NOSOUND ON)
set(DISABLE_DEMOMODE ON)
set(ASAN OFF)
set(UBSAN OFF)
@ -14,4 +13,10 @@ set(NOEXIT ON)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/threads-stub")
set(BUILD_ASSETS_MPQ OFF)
set(UNPACKED_MPQS ON)
# -fmerge-all-constants saves ~4 KiB
set(_extra_flags "-fmerge-all-constants -fipa-pta")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_extra_flags}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${_extra_flags}")

10
Source/CMakeLists.txt

@ -196,8 +196,10 @@ else()
list(APPEND libdevilutionx_SRCS
effects.cpp
engine/sound.cpp
utils/push_aulib_decoder.cpp
utils/soundsample.cpp)
if(NOT PS2)
list(APPEND libdevilutionx_SRCS utils/push_aulib_decoder.cpp)
endif()
endif()
if(NOT NONET)
@ -279,7 +281,11 @@ if(NOT NONET)
endif()
if(NOT NOSOUND)
target_link_libraries(libdevilutionx PUBLIC SDL_audiolib::SDL_audiolib)
if(PS2)
target_link_libraries(libdevilutionx PUBLIC audsrv)
else()
target_link_libraries(libdevilutionx PUBLIC SDL_audiolib::SDL_audiolib)
endif()
endif()
if(NOT NONET AND NOT DISABLE_ZERO_TIER)

33
Source/engine/sound.cpp

@ -38,6 +38,7 @@ namespace {
SoundSample music;
#ifndef PS2
std::string GetMp3Path(const char *path)
{
std::string mp3Path = path;
@ -45,15 +46,33 @@ std::string GetMp3Path(const char *path)
mp3Path.replace(dot + 1, mp3Path.size() - (dot + 1), "mp3");
return mp3Path;
}
#else
std::string GetAdpPath(const char *path)
{
std::string adpPath = path;
const std::string::size_type dot = adpPath.find_last_of('.');
adpPath.replace(dot + 1, adpPath.size() - (dot + 1), "adp");
return adpPath;
}
#endif
bool LoadAudioFile(const char *path, bool stream, bool errorDialog, SoundSample &result)
{
std::string filePath;
#ifndef PS2
bool isMp3 = true;
AssetRef ref = FindAsset(GetMp3Path(path).c_str());
filePath = GetMp3Path(path);
AssetRef ref = FindAsset(filePath.c_str());
if (!ref.ok()) {
ref = FindAsset(path);
filePath = path;
ref = FindAsset(filePath.c_str());
isMp3 = false;
}
#else
bool isMp3 = false;
filePath = GetAdpPath(path);
AssetRef ref = FindAsset(filePath.c_str());
#endif
if (!ref.ok())
ErrDlg("Audio file not found", StrCat(path, "\n", SDL_GetError(), "\n"), __FILE__, __LINE__);
@ -70,7 +89,7 @@ bool LoadAudioFile(const char *path, bool stream, bool errorDialog, SoundSample
#endif
if (stream) {
if (result.SetChunkStream(path, isMp3, /*logErrors=*/true) != 0) {
if (result.SetChunkStream(filePath, isMp3, /*logErrors=*/true) != 0) {
if (errorDialog) {
ErrDlg("Failed to load audio file", StrCat(path, "\n", SDL_GetError(), "\n"), __FILE__, __LINE__);
}
@ -118,10 +137,12 @@ SoundSample *DuplicateSound(const SoundSample &sound)
it = duplicateSounds.end();
--it;
}
#ifndef PS2
result->SetFinishCallback([it]([[maybe_unused]] Aulib::Stream &stream) {
const std::lock_guard<SdlMutex> lock(*duplicateSoundsMutex);
duplicateSounds.erase(it);
});
#endif
return result;
}
@ -209,6 +230,7 @@ void snd_init()
sgOptions.Audio.musicVolume.SetValue(CapVolume(*sgOptions.Audio.musicVolume));
gbMusicOn = *sgOptions.Audio.musicVolume > VOLUME_MIN;
#ifndef PS2
// Initialize the SDL_audiolib library. Set the output sample rate to
// 22kHz, the audio format to 16-bit signed, use 2 output channels
// (stereo), and a 2KiB output buffer.
@ -220,14 +242,19 @@ void snd_init()
Aulib::sampleRate(), Aulib::channelCount(), Aulib::frameSize(), Aulib::sampleFormat());
duplicateSoundsMutex.emplace();
#else
audsrv_set_volume(MAX_VOLUME);
#endif
gbSndInited = true;
}
void snd_deinit()
{
if (gbSndInited) {
#ifndef PS2
Aulib::quit();
duplicateSoundsMutex = std::nullopt;
#endif
}
gbSndInited = false;

18
Source/storm/storm_svid.cpp

@ -6,15 +6,19 @@
#include <SmackerDecoder.h>
#ifndef PS2
#ifndef NOSOUND
#include "utils/push_aulib_decoder.h"
#endif
#endif
#include "engine/assets.hpp"
#include "engine/dx.h"
#include "engine/palette.h"
#include "options.h"
#ifndef PS2
#include "utils/aulib.hpp"
#endif
#include "utils/display.h"
#include "utils/log.hpp"
#include "utils/sdl_compat.h"
@ -24,12 +28,14 @@
namespace devilution {
namespace {
#ifndef PS2
#ifndef NOSOUND
std::optional<Aulib::Stream> SVidAudioStream;
PushAulibDecoder *SVidAudioDecoder;
std::uint8_t SVidAudioDepth;
std::unique_ptr<int16_t[]> SVidAudioBuffer;
#endif
#endif
uint32_t SVidWidth, SVidHeight;
double SVidFrameEnd;
@ -109,12 +115,14 @@ void TrySetVideoModeToSVidForSDL1()
}
#endif
#ifndef PS2
#ifndef NOSOUND
bool HasAudio()
{
return SVidAudioStream && SVidAudioStream->isPlaying();
}
#endif
#endif
bool SVidLoadNextFrame()
{
@ -247,6 +255,7 @@ bool SVidPlayBegin(const char *filename, int flags)
return false;
}
#ifndef PS2
#ifndef NOSOUND
const bool enableAudio = (flags & 0x1000000) == 0;
@ -276,6 +285,7 @@ bool SVidPlayBegin(const char *filename, int flags)
SVidAudioDecoder = nullptr;
}
}
#endif
#endif
SVidFrameLength = 1000000.0 / Smacker_GetFrameRate(SVidHandle);
@ -337,6 +347,7 @@ bool SVidPlayContinue()
return SVidLoadNextFrame(); // Skip video and audio if the system is to slow
}
#ifndef PS2
#ifndef NOSOUND
if (HasAudio()) {
std::int16_t *buf = SVidAudioBuffer.get();
@ -347,6 +358,7 @@ bool SVidPlayContinue()
SVidAudioDecoder->PushSamples(reinterpret_cast<const std::uint8_t *>(buf), len);
}
}
#endif
#endif
if (SDL_GetTicks() * 1000.0 >= SVidFrameEnd) {
@ -366,12 +378,14 @@ bool SVidPlayContinue()
void SVidPlayEnd()
{
#ifndef PS2
#ifndef NOSOUND
if (HasAudio()) {
SVidAudioStream = std::nullopt;
SVidAudioDecoder = nullptr;
SVidAudioBuffer = nullptr;
}
#endif
#endif
if (SVidHandle.isValid)
@ -402,18 +416,22 @@ void SVidPlayEnd()
void SVidMute()
{
#ifndef PS2
#ifndef NOSOUND
if (SVidAudioStream)
SVidAudioStream->mute();
#endif
#endif
}
void SVidUnmute()
{
#ifndef PS2
#ifndef NOSOUND
if (SVidAudioStream)
SVidAudioStream->unmute();
#endif
#endif
}
} // namespace devilution

92
Source/utils/soundsample.cpp

@ -4,8 +4,10 @@
#include <cmath>
#include <utility>
#ifndef PS2
#include <Aulib/DecoderDrmp3.h>
#include <Aulib/DecoderDrwav.h>
#endif
#include <SDL.h>
#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
@ -15,7 +17,9 @@
#include "engine/assets.hpp"
#include "options.h"
#ifndef PS2
#include "utils/aulib.hpp"
#endif
#include "utils/log.hpp"
#include "utils/math.h"
#include "utils/stubs.h"
@ -57,6 +61,7 @@ float PanLogToLinear(int logPan)
return copysign(1.F - factor, static_cast<float>(logPan));
}
#ifndef PS2
std::unique_ptr<Aulib::Decoder> CreateDecoder(bool isMp3)
{
if (isMp3)
@ -72,6 +77,7 @@ std::unique_ptr<Aulib::Stream> CreateStream(SDL_RWops *handle, bool isMp3)
auto resampler = CreateAulibResampler(decoder->getRate());
return std::make_unique<Aulib::Stream>(handle, std::move(decoder), std::move(resampler), /*closeRw=*/true);
}
#endif
/**
* @brief Converts log volume passed in into linear volume.
@ -92,9 +98,15 @@ float VolumeLogToLinear(int logVolume, int logMin, int logMax)
void SoundSample::Release()
{
stream_ = nullptr;
#ifndef PS2
file_data_ = nullptr;
file_data_size_ = 0;
#else
if (stream_ != nullptr) // We are the owner
audsrv_free_adpcm(sampleId_);
sampleId_ = nullptr;
#endif
stream_ = nullptr;
}
/**
@ -102,15 +114,37 @@ void SoundSample::Release()
*/
bool SoundSample::IsPlaying()
{
#ifndef PS2
return stream_ && stream_->isPlaying();
#else
if (channel_ == -1)
return false;
return audsrv_is_adpcm_playing(channel_, sampleId_) != 0;
#endif
}
bool SoundSample::Play(int numIterations)
{
#ifndef PS2
if (!stream_->play(numIterations)) {
LogError(LogCategory::Audio, "Aulib::Stream::play (from SoundSample::Play): {}", SDL_GetError());
return false;
}
#else
if (IsStreaming()) {
return true;
}
int channel = audsrv_ch_play_adpcm(-1, sampleId_);
printf("channel %d\n", channel);
if (channel < 0) {
LogError(LogCategory::Audio, "audsrv_ch_play_adpcm (from SoundSample::Play): {}", channel);
return false;
}
channel_ = channel;
audsrv_adpcm_set_volume_and_pan(channel_, volume_, pan_);
#endif
return true;
}
@ -123,6 +157,7 @@ int SoundSample::SetChunkStream(std::string filePath, bool isMp3, bool logErrors
return -1;
}
file_path_ = std::move(filePath);
#ifndef PS2
isMp3_ = isMp3;
stream_ = CreateStream(handle, isMp3);
if (!stream_->open()) {
@ -131,11 +166,13 @@ int SoundSample::SetChunkStream(std::string filePath, bool isMp3, bool logErrors
LogError(LogCategory::Audio, "Aulib::Stream::open (from SoundSample::SetChunkStream) for {}: {}", file_path_, SDL_GetError());
return -1;
}
#endif
return 0;
}
int SoundSample::SetChunk(ArraySharedPtr<std::uint8_t> fileData, std::size_t dwBytes, bool isMp3)
{
#ifndef PS2
isMp3_ = isMp3;
file_data_ = std::move(fileData);
file_data_size_ = dwBytes;
@ -151,25 +188,76 @@ int SoundSample::SetChunk(ArraySharedPtr<std::uint8_t> fileData, std::size_t dwB
LogError(LogCategory::Audio, "Aulib::Stream::open (from SoundSample::SetChunk): {}", SDL_GetError());
return -1;
}
#else
stream_ = std::make_unique<audsrv_adpcm_t>();
int success = audsrv_load_adpcm(stream_.get(), fileData.get(), dwBytes);
if (success != 0) {
LogError(LogCategory::Audio, "audsrv_load_adpcm (from SoundSample::SetChunk): {}", success);
return -1;
}
sampleId_ = stream_.get();
#endif
return 0;
}
void SoundSample::SetVolume(int logVolume, int logMin, int logMax)
{
#ifndef PS2
stream_->setVolume(VolumeLogToLinear(logVolume, logMin, logMax));
#else
volume_ = VolumeLogToLinear(logVolume, logMin, logMax) * MAX_VOLUME;
if (channel_ == -1)
return;
audsrv_adpcm_set_volume_and_pan(channel_, volume_, pan_);
#endif
}
void SoundSample::SetStereoPosition(int logPan)
{
#ifndef PS2
stream_->setStereoPosition(PanLogToLinear(logPan));
#else
pan_ = PanLogToLinear(logPan) * 100;
if (channel_ == -1)
return;
audsrv_adpcm_set_volume_and_pan(channel_, volume_, pan_);
#endif
}
int SoundSample::GetLength() const
{
#ifndef PS2
if (!stream_)
return 0;
return std::chrono::duration_cast<std::chrono::milliseconds>(stream_->duration()).count();
#else
size_t size = 0;
int pitch = 0;
if (stream_ != nullptr) {
size = stream_->size;
pitch = stream_->pitch;
} else if (!file_path_.empty()) {
AssetHandle handle = OpenAsset(file_path_.c_str(), size);
if (handle.ok()) {
size -= 16;
uint32_t buffer[3];
if (handle.read(buffer, sizeof(buffer))) {
pitch = buffer[2];
}
}
}
if (pitch == 0)
return 0;
uint64_t microSamples = size;
microSamples *= 56 * 1000;
return microSamples / (pitch * 375);
#endif
}
} // namespace devilution

47
Source/utils/soundsample.h

@ -4,7 +4,11 @@
#include <cstdint>
#include <memory>
#ifdef PS2
#include <audsrv.h>
#else
#include <Aulib/Stream.h>
#endif
#include "engine/sound_defs.hpp"
#include "utils/stdcompat/shared_ptr_array.hpp"
@ -28,10 +32,12 @@ public:
// Returns 0 on success.
int SetChunkStream(std::string filePath, bool isMp3, bool logErrors = true);
#ifndef PS2
void SetFinishCallback(Aulib::Stream::Callback &&callback)
{
stream_->setFinishCallback(std::forward<Aulib::Stream::Callback>(callback));
}
#endif
/**
* @brief Sets the sample's WAV, FLAC, or Ogg/Vorbis data.
@ -44,14 +50,25 @@ public:
[[nodiscard]] bool IsStreaming() const
{
#ifndef PS2
return file_data_ == nullptr;
#else
return sampleId_ == nullptr;
#endif
}
int DuplicateFrom(const SoundSample &other)
{
#ifndef PS2
if (other.IsStreaming())
return SetChunkStream(other.file_path_, other.isMp3_);
return SetChunk(other.file_data_, other.file_data_size_, other.isMp3_);
#else
if (other.IsStreaming())
return SetChunkStream(other.file_path_, false);
sampleId_ = other.sampleId_;
return 0;
#endif
}
/**
@ -74,7 +91,13 @@ public:
*/
void Stop()
{
#ifndef PS2
stream_->stop();
#else
/** Hack: Implement way to stop sounds in PS2SDK */
if (channel_ != -1)
audsrv_adpcm_set_volume_and_pan(channel_, 0, pan_);
#endif
}
void SetVolume(int logVolume, int logMin, int logMax);
@ -82,12 +105,22 @@ public:
void Mute()
{
#ifndef PS2
stream_->mute();
#else
if (channel_ != -1)
audsrv_adpcm_set_volume_and_pan(channel_, 0, pan_);
#endif
}
void Unmute()
{
#ifndef PS2
stream_->unmute();
#else
if (channel_ != -1)
audsrv_adpcm_set_volume_and_pan(channel_, volume_, pan_);
#endif
}
/**
@ -96,16 +129,24 @@ public:
int GetLength() const;
private:
// Set for streaming audio to allow for duplicating it:
std::string file_path_;
#ifndef PS2
// Non-streaming audio fields:
ArraySharedPtr<std::uint8_t> file_data_;
std::size_t file_data_size_;
// Set for streaming audio to allow for duplicating it:
std::string file_path_;
bool isMp3_;
std::unique_ptr<Aulib::Stream> stream_;
#else
int channel_ = -1;
int pan_ = 0;
int volume_ = 100;
audsrv_adpcm_t *sampleId_ = nullptr;
std::unique_ptr<audsrv_adpcm_t> stream_;
#endif
};
} // namespace devilution

Loading…
Cancel
Save