diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml index d3064cad..0846e849 100644 --- a/.github/workflows/ci-windows.yml +++ b/.github/workflows/ci-windows.yml @@ -67,7 +67,7 @@ jobs: - uses: actions/checkout@v4 - name: create build dir run: mkdir build - - uses: actions/cache@v2 + - uses: actions/cache@v4 id: dep-cache with: path: C:/vcpkg/packages diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 54918cff..98d90b22 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -53,13 +53,13 @@ jobs: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - name: create build dir run: mkdir build - - uses: actions/cache@v2 + - uses: actions/cache@v4 id: dep-cache with: path: C:/vcpkg/packages key: ${{ runner.os }}-x64-windows-dep-cache-${{ hashFiles('**/*') }} restore-keys: ${{ runner.os }}-x64-windows-dep-cache - - uses: actions/cache@v2 + - uses: actions/cache@v4 id: sonar-cache with: path: | diff --git a/.gitignore b/.gitignore index cd28babb..5380ca54 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ **/out /CMakeSettings.json /.vs +/.vscode /build *.bak sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/version.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 555645ad..5de1f0b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ if(DEFINED ENV{VCPKG_ROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) endif() -project ("sdl2-hyper-sonic-drivers" VERSION 0.18.0 DESCRIPTION "SDL2 based Hyper-Sonic Drivers for emulating old soundcards") +project ("sdl2-hyper-sonic-drivers" VERSION 0.18.1 DESCRIPTION "SDL2 based Hyper-Sonic Drivers for emulating old soundcards") include (TestBigEndian) TEST_BIG_ENDIAN(IS_BIG_ENDIAN) if(IS_BIG_ENDIAN) diff --git a/sdl2-hyper-sonic-drivers/examples/CMakeLists.txt b/sdl2-hyper-sonic-drivers/examples/CMakeLists.txt index 7019e32c..d1ca6022 100644 --- a/sdl2-hyper-sonic-drivers/examples/CMakeLists.txt +++ b/sdl2-hyper-sonic-drivers/examples/CMakeLists.txt @@ -87,6 +87,18 @@ macro_example( NONE ) +macro_example( + EXE ADLPlay + FILE "adl-play.cpp" + DEPS hyper-sonic-drivers-static + LINKS ${LIB_SDL2main} hyper-sonic-drivers-static spdlog::spdlog SDL2::SDL2 + FIXTURES + "../test/fixtures/DUNE0.ADL" + #"../test/fixtures/EOBSOUND.ADL" + #"../test/fixtures/LOREINTR.ADL" + NONE +) + macro_example( EXE PCMExample FILE "pcm-example.cpp" diff --git a/sdl2-hyper-sonic-drivers/examples/adl-play.cpp b/sdl2-hyper-sonic-drivers/examples/adl-play.cpp new file mode 100644 index 00000000..8e16df7e --- /dev/null +++ b/sdl2-hyper-sonic-drivers/examples/adl-play.cpp @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#include + +#include +#include +#include +#include + +using namespace HyperSonicDrivers; + +using hardware::opl::OPLFactory; +using hardware::opl::OplEmulator; +using hardware::opl::OplType; +using utils::delayMillis; +using files::westwood::ADLFile; +using drivers::westwood::ADLDriver; + + +/** + * @brief Play an ADL file using a chosen OPL emulator/type until the user exits. + * + * Plays the given ADL file through an OPL device constructed from the specified + * emulator and OPL type. The function creates an ADLDriver bound to the + * Music channel group and enters an SDL event-driven loop that starts playback + * (initial track index is 5) and responds to key presses to control playback. + * + * Behavior: + * - Selects an OPL device based on `type`: + * - OPL2 -> Adlib + Opl + * - DUAL_OPL2-> SbPro + Opl + * - OPL3 -> SbPro2 + Opl + * - If the provided mixer is not ready, logs an error and returns immediately. + * - Starts playing the current track when not already playing. + * - Key controls (during playback): + * - ESC: stop all channels and return (exit function) + * - RIGHT: stop channels and advance to the next track (wraps to 0) + * - LEFT: stop channels and go to the previous track (wraps to last) + * + * This function blocks until ESC is pressed. + * + * @param emu OPL emulator selection used when constructing the device. + * @param type OPL hardware type used to choose the concrete device implementation. + * @param filename Path to the ADL file to load and play. + */ +void adl_play(const OplEmulator emu, const OplType type, std::shared_ptr mixer, const std::string& filename) +{ + using devices::make_device; + using utils::ILogger; + + auto adlFile = std::make_shared(filename); + std::shared_ptr device; + switch (type) + { + using enum OplType; + + case OPL2: + device = make_device(mixer, emu); + break; + case DUAL_OPL2: + device = make_device(mixer, emu); + break; + case OPL3: + device = make_device(mixer, emu); + break; + + } + + uint8_t track = 0; + + ADLDriver adlDrv(device, audio::mixer::eChannelGroup::Music); + adlDrv.setADLFile(adlFile); + + if(!mixer->isReady()) { + spdlog::error("mixer not ready yet.."); + return; + } + + do + { + if (!adlDrv.isPlaying()) + { + adlDrv.play(track); + ILogger::instance->info(fmt::format("Playing track: {}/{}", static_cast(track), adlFile->getNumTracks()), ILogger::eCategory::Application); + } + //delayMillis(1000); + SDL_Event e; + while (SDL_WaitEventTimeout(&e, 100)) + { + if (e.type == SDL_QUIT) + { + adlDrv.stopAllChannels(); + return; + } + else if (e.type == SDL_KEYDOWN) + { + switch (e.key.keysym.sym) + { + case SDLK_ESCAPE: + { + adlDrv.stopAllChannels(); + return; + } + case SDLK_RIGHT: + { + adlDrv.stopAllChannels(); + track++; + if (track >= adlFile->getNumTracks()) + track = 0; + + break; + } + case SDLK_LEFT: + { + adlDrv.stopAllChannels(); + if (track > 0) + track--; + else + track = adlFile->getNumTracks() - 1; + + break; + } + } + } + } + + } while (true); +} + +/** + * @brief Program entry point for the ADL playback demo using SDL2 and OPL emulation. + * + * Initializes SDL video, creates a minimal SDL window, and starts an SDL-based audio mixer. + * Iterates over configured OPL emulator and OPL type combinations, prints a colored header + * for each pair, and invokes adl_play to run the ADL playback demo for "DUNE0.ADL". + * Cleans up SDL resources before exiting. + * + * Return codes: + * - 0 : Normal exit after running demos and cleanup. + * - 1 : Mixer initialization failed. + * - -1 : SDL video initialization failed. + * - -2 : SDL window creation failed. + */ +int main(int argc, char* argv[]) +{ + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) + return -1; + + auto pWin = SDL_CreateWindow("for Keyboard Input...", 0, 0, 320, 200, 0); + if (!pWin) + { + SDL_Quit(); + return -2; + } + + + auto mixer = audio::make_mixer(8, 44100, 1024); + if (!mixer->init()) + { + spdlog::error("can't init mixer"); + SDL_DestroyWindow(pWin); + SDL_Quit(); + return 1; + } + + + const std::map emus = { + { OplEmulator::DOS_BOX, "DOS_BOX" }, + //{ OplEmulator::MAME, "MAME" }, + //{ OplEmulator::NUKED, "NUKED" }, + //{ OplEmulator::WOODY, "WOODY" }, + }; + + const std::map types = { + {OplType::OPL2, "OPL2"}, + //{OplType::DUAL_OPL2, "DUAL_OPL2"}, + //{OplType::OPL3, "OPL3"}, + }; + + const std::string m = "##### {} {} #####"; + + spdlog::set_level(spdlog::level::info); + HyperSonicDrivers::utils::ILogger::instance->setLevelAll(HyperSonicDrivers::utils::ILogger::eLevel::Info); + for (const auto& emu : emus) + { + for (const auto& type : types) + { + using enum fmt::color; + + for (const auto& c : { white_smoke, yellow, aqua, + lime_green, blue_violet, indian_red }) { + spdlog::info(fmt::format(fg(c), m, emu.second, type.second)); + } + + try + { + adl_play(emu.first, type.first, mixer, "DUNE0.ADL"); + //adl_test(emu.first, type.first, mixer, "EOBSOUND.ADL", 1); + //adl_test(emu.first, type.first, mixer, "LOREINTR.ADL", 3); + } + catch (const std::exception& e) + { + spdlog::default_logger()->error(e.what()); + } + } + } + + SDL_DestroyWindow(pWin); + SDL_Quit(); + return 0; +} diff --git a/sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp b/sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp index 616ecc26..39115f7a 100644 --- a/sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp +++ b/sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp @@ -1,8 +1,10 @@ -// sdl2-hyper-sonic-drivers.cpp : Defines the entry point for the application. -// TODO: delete this file and its target, this is kinda scratch pad +// sdl2-hyper-sonic-drivers.cpp : Defines the entry point for the application. +// TODO: delete this file and its target, this is kind of scratch pad #include #include +#include +#include #include @@ -336,6 +338,16 @@ void testMT32() } +/** + * @brief Load a WAV fixture, append it to itself, and play the result to completion. + * + * Initializes an SDL2 mixer, loads "test/fixtures/test_renderer_adlib_mame2.wav" into two sound + * objects, concatenates them into a single PCM sound, and plays that combined sound via a + * PCMDriver. The function blocks, polling until playback finishes. + * + * @note This function is a test utility: it uses a hard-coded test fixture path and performs + * synchronous waiting while playback is active. + */ void pcm_sound_append() { auto mixer = audio::make_mixer(8, 44100, 1024); @@ -356,6 +368,24 @@ void pcm_sound_append() } } +/** + * @brief Test harness that plays Westwood ADL music tracks through an Adlib/OPL driver using SDL2. + * + * This routine initializes an SDL2-backed audio mixer and an Adlib/OPL device, loads a Westwood + * ADL file (fixed to "adl/KYRA1A.ADL"), and uses drivers::westwood::ADLDriver to play each + * track (tracks are iterated from index 1 to getNumTracks()-1). Each track is played up to three + * times. The function creates a small SDL window and pumps SDL events while waiting for playback + * to finish; pressing Escape or receiving SDL_QUIT will abort the test, and pressing Return stops + * the current track. After completing playback or aborting, the SDL window is destroyed. + * + * Side effects: + * - Initializes SDL event and video subsystems and creates an SDL window. + * - Initializes audio mixer and constructs Adlib/OPL device and ADL driver. + * - Blocks while tracks are playing (polls events and sleeps). + * + * Notes: + * - File name and iteration counts are hard-coded for this test harness. + */ void adldune2filestest() { auto mixer = audio::make_mixer(8, 44100, 1024); @@ -412,60 +442,75 @@ void adldune2filestest() } QUIT: SDL_DestroyWindow(window); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + SDL_QuitSubSystem(SDL_INIT_EVENTS); } -void vocdune2filestest() -{ - auto mixer = audio::make_mixer(8, 44100, 1024); - mixer->init(); - - auto device = devices::make_device(mixer); - drivers::PCMDriver drv(mixer); - - SDL_InitSubSystem(SDL_INIT_EVENTS); - SDL_InitSubSystem(SDL_INIT_VIDEO); - - auto window = SDL_CreateWindow("a", 0, 0, 320, 200, 0); - - for (auto const& dir_entry : std::filesystem::directory_iterator{ "voc/" }) - { - const std::string fn = dir_entry.path().string(); - utils::ILogger::instance->info(std::format("opening file: {}", fn), utils::ILogger::eCategory::Application); - auto vocf = std::make_shared(fn); - - for (int j = 0; j < 10; j++) - { - utils::ILogger::instance->info(std::format("times: {}", j), utils::ILogger::eCategory::Application); - - drv.play(vocf->getSound()); - while (drv.isPlaying()) - { - //utils::delayMillis(200); - SDL_Event e; - while (SDL_PollEvent(&e)) - switch (e.type) - { - case SDL_QUIT: - goto QUIT; - case SDL_KEYDOWN: - //case SDL_KEYUP: - if (e.key.keysym.sym == SDLK_ESCAPE) - goto QUIT; - if (e.key.keysym.sym == SDLK_RETURN) - drv.stop(); - break; - - default: - std::cout << "event: " << e.type << std::endl; - } - } - } - utils::delayMillis(1000); - } -QUIT: - SDL_DestroyWindow(window); -} - +//void vocdune2filestest() +//{ +// auto mixer = audio::make_mixer(8, 44100, 1024); +// mixer->init(); +// +// auto device = devices::make_device(mixer); +// drivers::PCMDriver drv(mixer); +// +// SDL_InitSubSystem(SDL_INIT_EVENTS); +// SDL_InitSubSystem(SDL_INIT_VIDEO); +// +// auto window = SDL_CreateWindow("a", 0, 0, 320, 200, 0); +// +// for (auto const& dir_entry : std::filesystem::directory_iterator{ "voc/" }) +// { +// const std::string fn = dir_entry.path().string(); +// utils::ILogger::instance->info(std::format("opening file: {}", fn), utils::ILogger::eCategory::Application); +// auto vocf = std::make_shared(fn); +// +// for (int j = 0; j < 10; j++) +// { +// utils::ILogger::instance->info(std::format("times: {}", j), utils::ILogger::eCategory::Application); +// +// drv.play(vocf->getSound()); +// while (drv.isPlaying()) +// { +// //utils::delayMillis(200); +// SDL_Event e; +// while (SDL_PollEvent(&e)) +// switch (e.type) +// { +// case SDL_QUIT: +// goto QUIT; +// case SDL_KEYDOWN: +// //case SDL_KEYUP: +// if (e.key.keysym.sym == SDLK_ESCAPE) +// goto QUIT; +// if (e.key.keysym.sym == SDLK_RETURN) +// drv.stop(); +// break; +// +// default: +// std::cout << "event: " << e.type << std::endl; +// } +// } +// } +// utils::delayMillis(1000); +// } +//QUIT: +// SDL_DestroyWindow(window); +//} + +/** + * @brief Program entry point that runs selected test routines. + * + * The main function serves as the test harness entry. It dispatches to various + * SDL2/HyperSonicDrivers test routines; currently it invokes adldune2filestest() + * and then returns immediately. The function performs global initialization + * and teardown only for the test routines it calls (handled by those routines). + * + * Note: many test calls are present but commented out; uncommenting changes the + * executed test sequence. + * + * @return int Process exit code (0 on normal completion). + */ int main(int argc, char* argv[]) { //newMixerTest(); diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/audio/converters/IRateConverter.hpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/audio/converters/IRateConverter.hpp index 6bf0a2ca..5889f713 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/audio/converters/IRateConverter.hpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/audio/converters/IRateConverter.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp index b5154614..5cf07c71 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include #include diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp index 777c9980..a410dba3 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp @@ -22,7 +22,7 @@ namespace HyperSonicDrivers::drivers explicit IAudioDriver(const std::shared_ptr& device); virtual ~IAudioDriver() = default; - virtual void play(const uint8_t track) noexcept = 0; + virtual void play(const uint16_t track) noexcept = 0; virtual void stop() noexcept = 0; // TODO: it might not be required diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp index e7074947..bb04aff5 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp @@ -163,7 +163,7 @@ namespace HyperSonicDrivers::drivers // m_isPlaying = true; //} - void MIDDriver::play(const uint8_t track) noexcept + void MIDDriver::play(const uint16_t track) noexcept { if (m_midi == nullptr) return; diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp index a0950016..8b1508a3 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp @@ -37,7 +37,7 @@ namespace HyperSonicDrivers::drivers // this restore the default MidiDriver (scummvm::MidiAdlib, MT32) bool resetBankOP2() noexcept; - void play(const uint8_t track = 0) noexcept override; + void play(const uint16_t track = 0) noexcept override; void stop() noexcept override; void pause() noexcept; diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp index 6451c4c9..a35b2686 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -31,7 +32,7 @@ namespace HyperSonicDrivers::drivers::westwood IAudioDriver(opl), m_opl(opl->getOpl()) { - memset(m_channels.data(), 0, sizeof(m_channels)); + std::memset(m_channels.data(), 0, sizeof(m_channels)); hardware::TimerCallBack cb = std::bind(&ADLDriver::onCallback, this); auto p = std::make_shared(cb); @@ -82,7 +83,7 @@ namespace HyperSonicDrivers::drivers::westwood resetAdLibState_(); } - void ADLDriver::startSound_(const uint8_t track, const uint8_t volume) + void ADLDriver::startSound_(const uint16_t track, const uint8_t volume) { uint8_t* trackData = getProgram_(track); if (trackData == nullptr) { @@ -231,7 +232,7 @@ namespace HyperSonicDrivers::drivers::westwood } } - void ADLDriver::play(const uint8_t track) noexcept + void ADLDriver::play(const uint16_t track) noexcept { std::scoped_lock lock(m_mutex); @@ -557,7 +558,7 @@ namespace HyperSonicDrivers::drivers::westwood logD(std::format("initChannel({})", (long)(&channel - m_channels.data()))); const int8_t backupEL2 = channel.opExtraLevel2; - memset(&channel, 0, sizeof(Channel)); + std::memset(&channel, 0, sizeof(Channel)); channel.opExtraLevel2 = backupEL2; channel.tempo = 0xFF; diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp index aba97f91..a07c0e64 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp @@ -53,13 +53,13 @@ namespace HyperSonicDrivers::drivers::westwood void setOplMusicVolume(const uint8_t volume); void setOplSfxVolume(const uint8_t volume); - void play(const uint8_t track) noexcept override; + void play(const uint16_t track) noexcept override; void stop() noexcept override; bool isPlaying() const noexcept override; private: void initDriver_(); - void startSound_(const uint8_t track, const uint8_t volume); + void startSound_(const uint16_t track, const uint8_t volume); std::shared_ptr m_adl_file = nullptr; diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp index 65894631..b704347d 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp @@ -91,6 +91,27 @@ namespace HyperSonicDrivers::files::westwood V3_DATA_HEADER_SIZE }; + /** + * @brief Construct an ADLFile by loading and parsing a Westwood ADL file from disk. + * + * Reads the file header, track offsets, instrument offsets and raw data payload + * according to the detected ADL version (V1/V2/V3), validates sizes, computes + * counts for tracks/offsets, then closes the file and converts absolute offsets + * into data-relative offsets (using 0xFFFF as the sentinel for invalid entries). + * + * The constructor: + * - validates the file meets the minimum size for an ADL file, + * - determines the on-disk ADL version and associated metadata, + * - reads version-appropriate header entries, + * - reads track and instrument offset tables, + * - reads the raw data payload and stores its size/header size, + * - sets m_num_tracks from the version metadata and computes the number of + * valid track/instrument offsets, + * - closes the underlying file handle, + * - adjusts offsets to be relative to the in-memory data payload (and maps 0 -> 0xFFFF). + * + * @param filename Path to the ADL file to open and parse. + */ ADLFile::ADLFile(const std::string& filename) : File(filename) { assertValid_(size() >= FILE_SIZE_MIN); @@ -111,7 +132,7 @@ namespace HyperSonicDrivers::files::westwood readDataFromFile_(m_meta_version.data_offset, m_meta_version.data_header_size); - m_num_tracks = count_loop_(0, m_header); + m_num_tracks = m_meta_version.num_headers; m_num_track_offsets = count_loop_(m_meta_version.offset_start, m_track_offsets); m_num_instrument_offsets = count_loop_(m_meta_version.offset_start, m_instrument_offsets); @@ -128,37 +149,57 @@ namespace HyperSonicDrivers::files::westwood return m_version; } - int ADLFile::getNumTracks() const noexcept + /** + * @brief Return the number of tracks (subsongs) in the ADL file. + * + * The value is the internally stored track count determined at file load + * from the version-specific metadata. For some V3 files the in-memory + * header is known to be incorrect, so this returned count reflects the + * metadata-derived value rather than any inferred count from the header. + * + * @return int Number of tracks (subsongs). + */ + uint16_t ADLFile::getNumTracks() const noexcept { return m_num_tracks; } - int ADLFile::getNumTrackOffsets() const noexcept + /** + * @brief Return the number of program (track) offsets present in the ADL file. + * + * This value is determined during construction from the file's version-specific + * metadata and offset tables. It represents how many valid track/program + * offset entries are available (sentinel/invalid entries are counted according + * to the parsed offset table). + * + * @return int Number of track/program offsets. + */ + uint16_t ADLFile::getNumTrackOffsets() const noexcept { return m_num_track_offsets; } - int ADLFile::getNumInstrumentOffsets() const noexcept + uint16_t ADLFile::getNumInstrumentOffsets() const noexcept { return m_num_instrument_offsets; } - uint8_t ADLFile::getTrack(const int track) const + uint16_t ADLFile::getTrack(const uint16_t track) const { return m_header.at(track); } - uint16_t ADLFile::getTrackOffset(const int programId) const + uint16_t ADLFile::getTrackOffset(const uint16_t programId) const { return m_track_offsets.at(programId); } - uint16_t ADLFile::getInstrumentOffset(const int instrument) const + uint16_t ADLFile::getInstrumentOffset(const uint16_t instrument) const { return m_instrument_offsets.at(instrument); } - uint16_t ADLFile::getProgramOffset(const int progId, const PROG_TYPE prog_type) const + uint16_t ADLFile::getProgramOffset(const uint16_t progId, const PROG_TYPE prog_type) const { switch (prog_type) { @@ -259,18 +300,29 @@ namespace HyperSonicDrivers::files::westwood } } + /** + * @brief Read and populate the in-memory header table from the open file. + * + * Resizes ADLFile::m_header to header_size and fills it by invoking the supplied + * read callback header_size times. The read callback is expected to return the + * next header entry (as a 16-bit value) each time it is called. After reading, + * the method validates that the header vector length matches header_size. + * + * @param header_size Number of header entries to read and store into m_header. + * @param read A zero-argument callable that returns the next header entry as uint16_t. + * The caller is responsible for using the correct byte-width and endianness + * for the target ADL version. + */ void ADLFile::readHeaderFromFile_(const int header_size, std::function read) { m_header.resize(header_size); for (int i = 0; i < header_size; i++) - { m_header[i] = read(); - } assertValid_(m_header.size() == header_size); } - void ADLFile::readOffsetsFromFile_(const int num_offsets, std::vector& vec, const int offset_start) const noexcept + void ADLFile::readOffsetsFromFile_(const int num_offsets, std::vector& vec, const int offset_start) const { assertValid_(tell() == offset_start); vec.resize(num_offsets); @@ -280,14 +332,14 @@ namespace HyperSonicDrivers::files::westwood assertValid_(vec.size() == num_offsets); } - void ADLFile::readDataFromFile_(const int data_offsets, const int data_heder_size) + void ADLFile::readDataFromFile_(const int data_offsets, const int data_header_size) { m_dataSize = size() - data_offsets; assertValid_(m_dataSize > 0); assertValid_(tell() == data_offsets); m_data.reset(new uint8_t[m_dataSize]); read(m_data.get(), m_dataSize); - m_dataHeaderSize = data_heder_size; + m_dataHeaderSize = data_header_size; } void ADLFile::adjust_offsets_(std::vector& vec) diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp index 88df632c..50c20bf7 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp @@ -37,14 +37,14 @@ namespace HyperSonicDrivers::files::westwood uint8_t getVersion() const noexcept; - int getNumTracks() const noexcept; - int getNumTrackOffsets() const noexcept; - int getNumInstrumentOffsets() const noexcept; + uint16_t getNumTracks() const noexcept; + uint16_t getNumTrackOffsets() const noexcept; + uint16_t getNumInstrumentOffsets() const noexcept; - uint8_t getTrack(const int track) const; - uint16_t getTrackOffset(const int programId) const; - uint16_t getInstrumentOffset(const int instrument) const; - uint16_t getProgramOffset(const int progId, const PROG_TYPE prog_type) const; + uint16_t getTrack(const uint16_t track) const; + uint16_t getTrackOffset(const uint16_t programId) const; + uint16_t getInstrumentOffset(const uint16_t instrument) const; + uint16_t getProgramOffset(const uint16_t progId, const PROG_TYPE prog_type) const; uint32_t getDataSize() const noexcept; std::shared_ptr getData() const noexcept; @@ -59,10 +59,10 @@ namespace HyperSonicDrivers::files::westwood void detectVersion_(); void readHeaderFromFile_(const int header_size, std::function read); - void readOffsetsFromFile_(const int num_offsets, std::vector& vec, const int offset_start) const noexcept; - void readDataFromFile_(const int data_offsets, const int data_heder_size); + void readOffsetsFromFile_(const int num_offsets, std::vector& vec, const int offset_start) const; + void readDataFromFile_(const int data_offsets, const int data_header_size); - std::vector m_header; + std::vector m_header; std::vector m_track_offsets; std::vector m_instrument_offsets; std::shared_ptr m_data; @@ -72,9 +72,9 @@ namespace HyperSonicDrivers::files::westwood template int count_loop_(const int num_offs, const std::vector& vec); void adjust_offsets_(std::vector& vec); - int m_num_tracks = -1; - int m_num_track_offsets = -1; - int m_num_instrument_offsets = -1; + uint16_t m_num_tracks = 0; + uint16_t m_num_track_offsets = 0; + uint16_t m_num_instrument_offsets = 0; }; template @@ -82,7 +82,7 @@ namespace HyperSonicDrivers::files::westwood { int tot = 0; constexpr T max_ = std::numeric_limits::max(); - for (int i = 0; i < vec.size(); ++i) + for (size_t i = 0; i < vec.size(); ++i) { if (vec[i] >= offs_start && vec[i] < max_) { ++tot; diff --git a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/hardware/opl/mame/MameOPL3.cpp b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/hardware/opl/mame/MameOPL3.cpp index 7dace502..49a71924 100644 --- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/hardware/opl/mame/MameOPL3.cpp +++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/hardware/opl/mame/MameOPL3.cpp @@ -1,4 +1,5 @@ #include +#include namespace HyperSonicDrivers::hardware::opl::mame { diff --git a/sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp b/sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp index 56213cd2..12094124 100644 --- a/sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp +++ b/sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp @@ -8,7 +8,7 @@ namespace HyperSonicDrivers::drivers { public: IAudioDriverMock(const std::shared_ptr& device) : IAudioDriver(device) {} - void play(const uint8_t track) noexcept override {}; + void play(const uint16_t track) noexcept override {}; void stop() noexcept override {}; bool isPlaying() const noexcept override { return false; }; }; diff --git a/sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp b/sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp index d425daa4..b2aff2fd 100644 --- a/sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp +++ b/sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp @@ -20,7 +20,7 @@ namespace HyperSonicDrivers::files::westwood { ADLFile f("../fixtures/EOBSOUND.ADL"); EXPECT_EQ(f.getVersion(), 1); - EXPECT_EQ(f.getNumTracks(), 15); + EXPECT_EQ(f.getNumTracks(), 120); EXPECT_EQ(f.getNumTrackOffsets(), 42); EXPECT_EQ(f.getNumInstrumentOffsets(), 40); EXPECT_EQ(f.getDataSize(), 13019 - 600); @@ -33,7 +33,7 @@ namespace HyperSonicDrivers::files::westwood { ADLFileMock f("../fixtures/DUNE19.ADL"); EXPECT_EQ(f.getVersion(), 2); - EXPECT_EQ(f.getNumTracks(), 49); + EXPECT_EQ(f.getNumTracks(), 120); EXPECT_EQ(f.getNumTrackOffsets(), 72); EXPECT_EQ(f.getNumInstrumentOffsets(), 71); EXPECT_EQ(f.parentSize(), 7257); @@ -56,7 +56,7 @@ namespace HyperSonicDrivers::files::westwood ADLFileMock f("../fixtures/DUNE0.ADL"); EXPECT_EQ(f.getVersion(), 2); - EXPECT_EQ(f.getNumTracks(), 18); + EXPECT_EQ(f.getNumTracks(), 120); EXPECT_EQ(f.getNumTrackOffsets(), 52); EXPECT_EQ(f.getNumInstrumentOffsets(), 63); EXPECT_EQ(f.parentSize(), 14473); @@ -74,11 +74,19 @@ namespace HyperSonicDrivers::files::westwood EXPECT_EQ(chan, 9); } + /** + * @brief Tests ADLFile parsing for a version 3 ADL fixture (LOREINTR.ADL). + * + * Verifies that the file is recognized as version 3, reports the expected + * counts (tracks, track offsets, instrument offsets) and data size, and that + * program offsets for a track resolve to the corresponding track and + * instrument offsets. Also checks that the first track's data byte equals 9. + */ TEST(ADLFile, ADLv3) { ADLFile f("../fixtures/LOREINTR.ADL"); EXPECT_EQ(f.getVersion(), 3); - EXPECT_EQ(f.getNumTracks(), 30); + EXPECT_EQ(f.getNumTracks(), 250); EXPECT_EQ(f.getNumTrackOffsets(), 58); EXPECT_EQ(f.getNumInstrumentOffsets(), 71); EXPECT_EQ(f.getDataSize(), 13812 - 2000); diff --git a/sonar-project.properties b/sonar-project.properties index c2893529..22a4629a 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,5 +1,5 @@ sonar.organization=raffaello-github -sonar.projectKey=Raffaello_sdl2-sonic-drivers +sonar.projectKey=Raffaello_sdl2-hyper-sonic-drivers sonar.sources=sdl2-hyper-sonic-drivers/src,sdl2-hyper-sonic-drivers/examples sonar.tests=sdl2-hyper-sonic-drivers/test