Skip to content

review ADLFile #285

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Aug 23, 2025
Merged

review ADLFile #285

merged 17 commits into from
Aug 23, 2025

Conversation

Raffaello
Copy link
Owner

@Raffaello Raffaello commented Dec 31, 2023

Summary by CodeRabbit

  • New Features

    • ADLPlay example: browse and play ADL tracks with keyboard controls (ESC/LEFT/RIGHT).
    • PCM demo: concatenate and play WAVs for playback testing.
  • Documentation

    • Improved program-entry docs and clarified header comments.
  • Tests

    • Added ADLv3 fixture; updated ADL track-count expectations; added ADL playback test harness.
  • Chores

    • Project bumped to 0.18.1; CI cache action upgraded; SonarCloud project key updated.

@Raffaello Raffaello self-assigned this Dec 31, 2023
Copy link
Contributor

coderabbitai bot commented Dec 31, 2023

Walkthrough

Refactors ADL handling to use 16-bit counts/indices, adds ADL version/offset accessors, expands driver track parameter widths to uint16_t, adds an ADL playback example and SDL test harness changes (new tests and event loop), updates several header includes (cstring/atomic), bumps project version and Sonar key, upgrades GitHub Actions cache to v4, and updates .gitignore.

Changes

Cohort / File(s) Summary
ADL Westwood core
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp, .../ADLFile.cpp, sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp
ADLFile internals and API switched to uint16_t counts/indices; added getVersion() and new count/offset accessors; constructor/header/data reading refactored (new readHeaderFromFile_); tests updated for new counts and an ADLv3 fixture added.
Examples / ADL player
sdl2-hyper-sonic-drivers/examples/CMakeLists.txt, sdl2-hyper-sonic-drivers/examples/adl-play.cpp
Adds ADLPlay example executable and implementation: SDL window/mixer init, ADL file loading, OPL device selection, ADLDriver playback loop with SDL event controls and logging.
SDL test harness & main
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp
Added pcm_sound_append() and adldune2filestest() tests; integrates SDL window creation and event pumping for ADL playback; vocdune2filestest() removed/commented out; main() updated to dispatch ADL test (other tests commented). Also added includes (<format>, <filesystem>).
IAudio / Drivers
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp, .../MIDDriver.hpp, .../MIDDriver.cpp, .../drivers/westwood/ADLDriver.hpp, .../drivers/westwood/ADLDriver.cpp, sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp
Widened track parameter types from uint8_tuint16_t across IAudioDriver interface and implementations (MIDDriver, ADLDriver) and tests; ADLDriver internal helper signatures updated accordingly.
Memset / header fixes
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp, .../audio/converters/IRateConverter.hpp, .../devices/IDevice.hpp, .../hardware/opl/mame/MameOPL3.cpp
Replaced C-style memset usages with std::memset and added #include <cstring> where needed; added #include <atomic> in IDevice.hpp to support atomic members; other compile-time include fixes applied.
Tests updated
sdl2-hyper-sonic-drivers/test/...
Test expectations adjusted for ADL counts; ADLv3 fixture test added; IAudioDriver mock updated to accept uint16_t.
CI workflows
.github/workflows/ci-windows.yml, .github/workflows/sonarcloud.yml
Upgraded actions/cache@v2actions/cache@v4 in cache steps.
Project metadata & misc
sonar-project.properties, CMakeLists.txt, .gitignore
Sonar project key updated to Raffaello_sdl2-hyper-sonic-drivers; project version bumped 0.18.00.18.1; added .vscode to .gitignore.
Minor includes / docs
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp
Added <format> and <filesystem> includes; minor header comment wording change.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant App as ADLPlay / Test harness
  participant SDL as SDL (Window & Events)
  participant Mixer as Audio Mixer
  participant ADLFile as ADLFile
  participant OPL as OPL Device
  participant Driver as ADLDriver

  User->>App: launch example/test
  App->>SDL: init window & events
  App->>Mixer: init audio mixer
  App->>ADLFile: load ADL (read header/data)
  App->>OPL: create OPL device
  App->>Driver: construct ADLDriver (OPL + Mixer)
  loop playback
    SDL->>App: input (LEFT/RIGHT/ESC)
    App->>Driver: play(track) / stop
    Driver->>OPL: write programs / tones
    Driver->>Mixer: push audio frames
    Mixer-->>SDL: audio callback
  end
  User-->>SDL: ESC / quit
  App->>SDL: destroy window / quit subsystems
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

I nibble bytes and hop through code,
Offsets widened to a 16-bit road.
ADL sings softly, SDL opens the gate,
CI caches newer, version bumps — elate.
A rabbit drums fixes small and great. 🥕

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Status

Actionable comments generated: 0

Configuration used: CodeRabbit UI

Commits Files that changed from the base of the PR and between 2939550 and e73a9b3.
Files selected for processing (2)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2 hunks)
  • sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp (1 hunks)
Files skipped from review due to trivial changes (1)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp
Additional comments: 1
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp (1)
  • 59-59: The expected number of tracks has been updated from a previous unspecified value to 200. This is a significant change and should be verified to ensure that the ADLFile class is intended to support this many tracks. If this is a new feature, ensure that the implementation in ADLFile is capable of handling 200 tracks without issues.
Verification successful

The search results indicate that the getNumTracks method is indeed present in the ADLFile class and that there are several test cases with different expected values for the number of tracks, including the value 200 that was mentioned in the review comment. This suggests that the ADLFile class is expected to handle 200 tracks, at least in the context of testing.

Based on the information provided:

  • The ADLFile::getNumTracks method exists.
  • The test suite includes a test that expects 200 tracks, which aligns with the change mentioned in the review comment.

Therefore, the change in the test suite to expect 200 tracks from the ADLFile class appears to be consistent with the test cases present in the codebase.

Scripts Executed

The following scripts were executed for the analysis:


Script:

#!/bin/bash
# Verify that the ADLFile class has been updated to support 200 tracks.
rg --type cpp "getNumTracks" src/HyperSonicDrivers/files/westwood/ADLFile.cpp

Length of output: 166



Script:

#!/bin/bash
# Re-run the search for the getNumTracks method in the ADLFile.cpp file with the correct path.
rg --type cpp "getNumTracks" HyperSonicDrivers/files/westwood/ADLFile.cpp

Length of output: 158



Script:

#!/bin/bash
# Find the ADLFile.cpp file and search for the getNumTracks method.
fd ADLFile.cpp --exec rg "getNumTracks" {}

Length of output: 256

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (1)

4-8: Missing standard headers for used APIs: add and .

std::format and std::filesystem are used later; compilation will fail without these includes.

Apply this diff:

 #include <iostream>
 #include <memory>
+#include <format>
+#include <filesystem>
 
 #include <SDL2/SDL.h>
🧹 Nitpick comments (4)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/midi/IMidiChannelVoice.cpp (1)

18-21: Prefer std::clamp with widened arithmetic for clarity and safety.

Avoids template gymnastics on std::min<uint8_t> and makes the bounds explicit while keeping the intermediate computation in int.

Apply this diff:

-        return  std::min<uint8_t>(static_cast<uint8_t>(m_volume * m_channel->volume / 127), 127);
+        // clamp the product after widening to int, then narrow to uint8_t
+        return static_cast<uint8_t>(
+            std::clamp<int>(
+                (static_cast<int>(m_volume) * static_cast<int>(m_channel->volume)) / 127,
+                0, 127
+            )
+        );
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (3)

386-407: Event loop responsiveness: poll before sleeping and avoid noisy default logging.

Polling first reduces input latency; the default branch prints every event type and can spam logs.

Apply this diff:

-                while (drv.isPlaying())
-                {
-                    utils::delayMillis(200);
-                    SDL_Event e;
+                while (drv.isPlaying())
+                {
+                    SDL_Event e;
                     while (SDL_PollEvent(&e))
                         switch (e.type)
                         {
@@
                         default:
-                            std::cout << "event: " << e.type << std::endl;
+                            break; // ignore
                         }
+                    utils::delayMillis(10);
                 }

417-467: vocdune2filestest(): remove unused variable, add tiny sleep in loop, and ensure SDL cleanup.

  • device is unused.
  • Tight loop may spin hot while audio plays; add a small delay.
  • Add SDL_QuitSubSystem on exit, like the ADL test.

Apply this diff:

-    auto device = devices::make_device<devices::Adlib, devices::Opl>(mixer);
     drivers::PCMDriver drv(mixer);
@@
-            while (drv.isPlaying())
+            while (drv.isPlaying())
             {
-                //utils::delayMillis(200);
+                utils::delayMillis(5);
                 SDL_Event e;
                 while (SDL_PollEvent(&e))
@@
 QUIT:
     SDL_DestroyWindow(window);
+    SDL_QuitSubSystem(SDL_INIT_VIDEO);
+    SDL_QuitSubSystem(SDL_INIT_EVENTS);

478-481: Main calls adldune2filestest(): consider gating test harness under a build option.

Keeps test code out of production binaries and avoids accidental UI popups.

Would you like a small CMake option (e.g., -DBUILD_DRIVER_TESTS=ON) plus ifdef guards to compile/enable these entry points only in test builds?

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e73a9b3 and f704728.

📒 Files selected for processing (3)
  • sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (4 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/midi/IMidiChannelVoice.cpp (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp
🧰 Additional context used
🪛 GitHub Check: Codacy Static Code Analysis
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/midi/IMidiChannelVoice.cpp

[warning] 3-3: sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/midi/IMidiChannelVoice.cpp#L3
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.

sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp

[warning] 39-39: sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp#L39
Include file: <HyperSonicDrivers/files/VOCFile.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (6)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/midi/IMidiChannelVoice.cpp (2)

3-3: Good switch to for std::min.

Header choice is correct for the current implementation.


3-3: Codacy warning “ not found” looks spurious.

This is a standard header. If the warning persists, it’s likely a toolchain configuration issue on Codacy rather than a code problem.

sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (4)

134-135: Mix_Hook vs Mix_HookMusic: confirm target SDL_mixer API.

This block is commented-out, but “Mix_Hook” is not part of SDL_mixer 2.x public API. If you plan to enable this path, double-check against your SDL_mixer version and headers.

Would you like me to cross-check the exact function name against the version you’re targeting and propose a compatible snippet?


39-39: Include path verified

  • The header VOCFile.hpp is present at src/HyperSonicDrivers/files/VOCFile.hpp, matching the include directive.
  • No changes are needed; the build system should resolve this path correctly.

359-415: Build configuration already enforces C++20

The sdl2-hyper-sonic-drivers/CMakeLists.txt includes:

set(CMAKE_CXX_STANDARD 20)

which ensures the code is compiled with C++20 support, covering the usage of std::format. No further changes are required.


359-415: adldune2filestest(): tighten initialization, remove dead loop, ensure cleanup, and confirm track indexing

  • Add #include <format> at the top to resolve uses of std::format.
  • Guard mixer startup: check the return of mixer->init() (and optionally mixer->isReady()) and log+return on failure.
  • Eliminate the redundant for (int f = 0; f <= 0; f++) loop—just set fn directly.
  • After destroying the SDL window, call:
    • SDL_QuitSubSystem(SDL_INIT_VIDEO);
    • SDL_QuitSubSystem(SDL_INIT_EVENTS);
  • Verify whether tracks are 0-based. If so, change
    for (int i = 1; i < static_cast<int>(adlf->getNumTracks()); i++)
    to
    for (int i = 0; i < static_cast<int>(adlf->getNumTracks()); ++i)
    // or at least document why track 0 is skipped

Suggested diff:

+#include <format>
 void adldune2filestest()
 {
     auto mixer = audio::make_mixer<audio::sdl2::Mixer>(8, 44100, 1024);
-    mixer->init();
+    if (!mixer->init() || !mixer->isReady()) {
+        utils::ILogger::instance->error("Mixer initialization failed",
+            utils::ILogger::eCategory::Application);
+        return;
+    }
@@
-    for (int f = 0; f <= 0; f++)
-    {
-        const std::string fn = "adl/KYRA1A.ADL";
+    // Play a single ADL file
+    const std::string fn = "adl/KYRA1A.ADL";
     utils::ILogger::instance->info(std::format("opening file: {}", fn),
         utils::ILogger::eCategory::Application);
@@
-    for (int i = 1; i < static_cast<int>(adlf->getNumTracks()); i++)
+    // TODO: confirm indexing; if tracks are 0-based, start at 0
+    for (int i = 1; i < static_cast<int>(adlf->getNumTracks()); ++i)
     {
         …
     }
@@
     SDL_DestroyWindow(window);
+    SDL_QuitSubSystem(SDL_INIT_VIDEO);
+    SDL_QuitSubSystem(SDL_INIT_EVENTS);
 }

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (3)

70-71: Use fmt consistently or include

std::format requires <format> and C++20 library support. Given you're already using fmt, prefer fmt::format throughout or add the missing header and ensure C++20 is enabled.

Option A (preferred): keep using fmt::format everywhere (already included via fmt/color.h).
Option B: if you keep std::format, add:

-#include <fmt/color.h>
+#include <fmt/color.h>
+#include <format>

73-75: Avoid busy-spin: wait for events with a short timeout

The outer loop runs continuously and SDL_PollEvent can return false, causing a tight spin that burns a core. Use SDL_WaitEventTimeout or add a tiny delay.

Minimal change:

-        SDL_Event e;
-        while (SDL_PollEvent(&e))
+        SDL_Event e;
+        while (SDL_PollEvent(&e))
         {
             ...
         }
+        SDL_Delay(1); // prevent busy loop when no events

Or preferred:

-        SDL_Event e;
-        while (SDL_PollEvent(&e))
+        SDL_Event e;
+        while (SDL_WaitEventTimeout(&e, 10))
         {
             ...
         }

73-104: Graceful termination on window close

Consider handling SDL_QUIT to allow closing the window to stop playback without using Esc.

-        while (SDL_PollEvent(&e))
+        while (SDL_PollEvent(&e))
         {
-            if (e.type == SDL_KEYDOWN)
+            if (e.type == SDL_QUIT) {
+                adlDrv.stop();
+                return;
+            } else if (e.type == SDL_KEYDOWN)
             {
                 switch (e.key.keysym.sym)
                 {
sdl2-hyper-sonic-drivers/examples/CMakeLists.txt (1)

90-101: Linking SDL2 for event loop is correct; consider copying SDL runtime if needed

Adding SDL2::SDL2 to ADLPlay is appropriate since the example uses SDL event APIs. If you plan to run the installed example standalone, you may also need to copy SDL2 runtime alongside the executable on some platforms (Windows/macOS).

Example (Windows-only, optional) to copy SDL2 DLL next to the exe:

 add_dependencies(${EXAMPLE_EXE} ${EXAMPLE_DEPS}) 
 target_link_libraries(${EXAMPLE_EXE} ${EXAMPLE_LINKS})
 if(${EXAMPLE_COPY_LIB})
@@
 endif()
+if(WIN32)
+  add_custom_command(TARGET ${EXAMPLE_EXE} POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E copy_if_different
+            $<TARGET_FILE:SDL2::SDL2>
+            $<TARGET_FILE_DIR:${EXAMPLE_EXE}>
+  )
+endif()
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f704728 and f373a4c.

📒 Files selected for processing (2)
  • sdl2-hyper-sonic-drivers/examples/CMakeLists.txt (1 hunks)
  • sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Codacy Static Code Analysis

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)

168-171: Fix ADLFile to correctly support 16-bit track IDs for v3

The root issue is that even if you widen the getTrack API, the underlying container m_header is still std::vector<uint8_t>, so any values ≥256 are truncated as they’re read. We need to store full 16-bit values and expose them in the public API.

Please apply these mandatory refactors:

• sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp

  • Change the header storage to 16 bits
  • Widen the getTrack declaration
--- a/src/HyperSonicDrivers/files/westwood/ADLFile.hpp
+++ b/src/HyperSonicDrivers/files/westwood/ADLFile.hpp
@@
-        // TODO: This is wrong for v3 as it is uint16_t instead!
-        std::vector<uint8_t> m_header;
+        // Store full 16-bit IDs so v3 entries ≥256 aren’t lost
+        std::vector<uint16_t> m_header;
@@
-        uint8_t getTrack(const int track) const;
+        uint16_t getTrack(const int track) const;

• sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp

  • Widen the getTrack definition
--- a/src/HyperSonicDrivers/files/westwood/ADLFile.cpp
+++ b/src/HyperSonicDrivers/files/westwood/ADLFile.cpp
@@
-    uint8_t ADLFile::getTrack(const int track) const
+    uint16_t ADLFile::getTrack(const int track) const
     {
         return m_header.at(track);
     }

• sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp

  • Ensure readHeaderFromFile_ still works, since it already reads 16-bit values via the read() callback

No other call sites need changing: callers in ADLDriver already assign to uint16_t, and tests compare to integer literals (<256) or to 0xFF/0xFFFF, which remain correct.

♻️ Duplicate comments (4)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp (1)

23-23: Clarify semantics: getNumTracks now returns header capacity, not “actual subsongs”.

The new expectations (120/250) mirror m_meta_version.num_headers. That’s fine, but several headers contain many empty/invalid entries, so iterating 0..getNumTracks()-1 will hit empty tracks. Please confirm all call sites that loop by getNumTracks() either:

  • filter out invalid entries (0xFF for v1/v2, 0xFFFF for v3), or
  • switch to a separate accessor that returns only “valid/occupied” headers.

Consider adding a companion test that asserts selected indices map to valid program IDs (< getNumTrackOffsets()) to prevent regressions.

Example test additions:

@@ TEST(ADLFile, ADLv2)
-        EXPECT_EQ(f.getTrack(2), 3);
+        EXPECT_EQ(f.getTrack(2), 3);
+        // Guard: header program id must be within available offsets
+        EXPECT_LT(static_cast<int>(f.getTrack(2)), f.getNumTrackOffsets());

@@ TEST(ADLFile, ADLv3)
         EXPECT_EQ(f.getVersion(), 3);
         EXPECT_EQ(f.getNumTracks(), 250);
+        // v3 IDs are 16-bit; ensure the chosen entry maps to a valid offset range
+        EXPECT_LT(static_cast<int>(f.getTrack(0)), f.getNumTrackOffsets());

Also applies to: 36-36, 59-59, 81-81

sdl2-hyper-sonic-drivers/examples/adl-play.cpp (3)

70-71: std::format vs fmt::format mismatch; include or standardize.

You use fmt elsewhere; std::format requires and C++20 library support. For consistency, switch to fmt::format.

-            ILogger::instance->info(std::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()), ILogger::eCategory::Application);
+            ILogger::instance->info(fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()), ILogger::eCategory::Application);

85-101: Handle LEFT/RIGHT by playing immediately and logging; avoid deferred restart.

Currently you stop channels and adjust index, but rely on the outer loop to restart playback. Play immediately for snappier UX and log consistently.

                 case SDLK_RIGHT:
                 {
-                    adlDrv.stopAllChannels();
-                    track++;
-                    if (track >= adlFile->getNumTracks())
-                        track = 0;
+                    const auto numTracks = adlFile->getNumTracks();
+                    track = (track + 1) % numTracks;
+                    adlDrv.stopAllChannels();
+                    adlDrv.play(track);
+                    HyperSonicDrivers::utils::ILogger::instance->info(
+                        fmt::format("Playing track: {}/{}", static_cast<int>(track), numTracks),
+                        HyperSonicDrivers::utils::ILogger::eCategory::Application
+                    );
 
                     break;
                 }
                 case SDLK_LEFT:
                 {
-                    adlDrv.stopAllChannels();
-                    if (track > 0)
-                        track--;
-                    else
-                        track = adlFile->getNumTracks() - 1;
+                    const auto numTracks = adlFile->getNumTracks();
+                    track = (track + numTracks - 1) % numTracks;
+                    adlDrv.stopAllChannels();
+                    adlDrv.play(track);
+                    HyperSonicDrivers::utils::ILogger::instance->info(
+                        fmt::format("Playing track: {}/{}", static_cast<int>(track), numTracks),
+                        HyperSonicDrivers::utils::ILogger::eCategory::Application
+                    );
+                    break;
                 }

111-114: Initialize SDL events/audio for reliability on all platforms.

SDL_KEYDOWN is more reliable with SDL_INIT_EVENTS; some backends also expect SDL_INIT_AUDIO when using custom mixers. You already create a window; consider:

-    if (SDL_Init(SDL_INIT_VIDEO) != 0)
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_AUDIO) != 0)
         return -1;
🧹 Nitpick comments (8)
.github/workflows/sonarcloud.yml (1)

56-71: Good move to actions/cache@v4; consider aligning other core actions.

Cache bump looks right. For consistency and maintenance, also consider upgrading:

  • actions/checkout@v2 → @v4
  • actions/setup-java@v1 → @v4

Optional, but it avoids older runner behavior and deprecation churn.

Suggested minimal change:

-      - uses: actions/checkout@v2
+      - uses: actions/checkout@v4
@@
-      - name: Set up JDK 17
-        uses: actions/setup-java@v1
+      - name: Set up JDK 17
+        uses: actions/setup-java@v4
         with:
           java-version: 17
+          distribution: temurin
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (4)

114-124: Track count = header capacity; consider exposing “occupied tracks” as well.

m_num_tracks = m_meta_version.num_headers aligns with updated tests, but it blurs capacity vs. actual content. Suggest keeping this as-is for compatibility and adding an additional computed count (e.g., getNumOccupiedTracks()) that scans m_header for entries < m_meta_version.num_track_offsets (and != 0xFF/0xFFFF). This helps UIs/players skip empties without hardcoding sentinels.

Sketch:

+int ADLFile::getNumOccupiedTracks() const noexcept
+{
+    const auto maxId = m_meta_version.num_track_offsets;
+    int last = -1;
+    for (int i = 0; i < m_meta_version.num_headers; ++i) {
+        const auto id = m_header[i];
+        if (id < maxId) last = i;
+    }
+    return last + 1; // 0..last inclusive
+}

140-156: Remove stale commented logic or gate behind a debug helper.

The commented “find last subsong” block is obsolete and can mislead future readers. Either delete or move into a small local debug helper (disabled by default). Keeps implementation intent clear.


284-289: Make header reading explicit by width; fix typos.

The TODO accurately captures the need. Two small, actionable tweaks:

  • Provide width via a template rather than passing a reader function; it’s faster and clearer.
  • Typos: “greter” → “greater”.

Example:

-void ADLFile::readHeaderFromFile_(const int header_size, std::function<uint16_t()> read)
+template<typename T>
+void ADLFile::readHeaderFromFile_(const int header_size)
 {
-    m_header.resize(header_size);
-    // TODO: pass the byte size instead of a function read and read at once,
-    //       and do read in LE when is greter than 1, basically if version 3
-    for (int i = 0; i < header_size; i++)
-    {
-        m_header[i] = read();
-    }
+    m_header.resize(header_size);
+    for (int i = 0; i < header_size; i++) {
+        if constexpr (sizeof(T) == 1) m_header[i] = readU8();
+        else                          m_header[i] = readLE16();
+    }
     assertValid_(m_header.size() == header_size);
 }

And call with:

  • readHeaderFromFile_<uint8_t>(m_meta_version.num_headers) for v1/v2
  • readHeaderFromFile_<uint16_t>(...) for v3

307-315: Typo: parameter name “data_heder_size”.

Minor readability nit: rename to data_header_size.

-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;
@@
-    m_dataHeaderSize = data_heder_size;
+    m_dataHeaderSize = data_header_size;
 }
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (3)

55-55: Use a larger signed index for tracks and fix the stale comment.

uint8_t silently wraps and the comment says “starting from 2” while the value is 5. Prefer size_t or int and start from 0 unless there’s a reason.

-    uint8_t track = 5; // starting from 2
+    size_t track = 0; // 0-based start

65-72: Optional: auto-advance when a song ends.

As written, when a track finishes, you replay the same track. Consider auto-advancing:

-        if (!adlDrv.isPlaying())
-        {
-            adlDrv.play(track);
-            ILogger::instance->info(fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()), ILogger::eCategory::Application);
-        }
+        if (!adlDrv.isPlaying()) {
+            const auto n = adlFile->getNumTracks();
+            track = (track + 1) % n;
+            adlDrv.play(track);
+            ILogger::instance->info(
+                fmt::format("Playing track: {}/{}", static_cast<int>(track), n),
+                ILogger::eCategory::Application
+            );
+        }

96-102: Add an explicit break after the LEFT case.

Not required since it’s the last case, but adding break avoids future fall-through bugs if more cases are appended.

                 case SDLK_LEFT:
                 {
                     adlDrv.stopAllChannels();
                     if (track > 0)
                         track--;
                     else
                         track = adlFile->getNumTracks() - 1;
+                    break;
                 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f373a4c and 5088807.

📒 Files selected for processing (6)
  • .github/workflows/ci-windows.yml (1 hunks)
  • .github/workflows/sonarcloud.yml (1 hunks)
  • sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (3 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (2 hunks)
  • sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp
🧰 Additional context used
🧬 Code graph analysis (1)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)
  • ADLFile (94-133)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (3)
  • ADLFile (35-35)
  • ADLFile (36-36)
  • track (44-44)
🪛 GitHub Check: Codacy Static Code Analysis
sdl2-hyper-sonic-drivers/examples/adl-play.cpp

[warning] 1-1: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L1
Include file: <HyperSonicDrivers/audio/sdl2/Mixer.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 2-2: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L2
Include file: <HyperSonicDrivers/hardware/opl/OPL.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 3-3: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L3
Include file: <HyperSonicDrivers/hardware/opl/OPLFactory.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 4-4: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L4
Include file: <HyperSonicDrivers/utils/algorithms.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 5-5: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L5
Include file: <HyperSonicDrivers/files/westwood/ADLFile.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 6-6: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L6
Include file: <HyperSonicDrivers/drivers/westwood/ADLDriver.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 7-7: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L7
Include file: <HyperSonicDrivers/utils/ILogger.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 8-8: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L8
Include file: <HyperSonicDrivers/devices/Adlib.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 9-9: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L9
Include file: <HyperSonicDrivers/devices/SbPro.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 10-10: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L10
Include file: <HyperSonicDrivers/devices/SbPro2.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 12-12: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L12
Include file: <spdlog/spdlog.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 13-13: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L13
Include file: <fmt/color.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 15-15: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L15
Include file: <SDL2/SDL.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 17-17: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L17
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 18-18: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L18
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 19-19: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L19
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 20-20: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L20
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Build Windows (x64-windows, Release, 2022)
  • GitHub Check: Build Windows (x64-windows, Debug, 2022)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
🔇 Additional comments (1)
.github/workflows/ci-windows.yml (1)

70-76: actions/cache@v4 upgrade LGTM.

Cache key/restore-keys are kept stable across the matrix and scoped to vcpkg packages; this is a straightforward, correct bump.

coderabbitai bot added a commit that referenced this pull request Aug 23, 2025
Docstrings generation was requested by @Raffaello.

* #285 (comment)

The following files were modified:

* `sdl2-hyper-sonic-drivers/examples/adl-play.cpp`
* `sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp`
* `sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp`
* `sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp`
Copy link
Contributor

coderabbitai bot commented Aug 23, 2025

Note

Generated docstrings for this pull request at #286

Docstrings generation was requested by @Raffaello.

* #285 (comment)

The following files were modified:

* `sdl2-hyper-sonic-drivers/examples/adl-play.cpp`
* `sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp`
* `sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp`
* `sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp`

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@Raffaello Raffaello added this to the AdLib / SoundBlaster Pro milestone Aug 23, 2025
@github-project-automation github-project-automation bot moved this to In progress in Audio Files Aug 23, 2025
@Raffaello Raffaello added example related to how to use the library file I/O file related ci continus integration labels Aug 23, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (1)

351-367: Harden pcm_sound_append: check init result and remove unused variable

mixer->init() is unchecked and s1b is never used.

Apply this diff:

 void pcm_sound_append()
 {
     auto mixer = audio::make_mixer<audio::sdl2::Mixer>(8, 44100, 1024);
     files::WAVFile w1("test/fixtures/test_renderer_adlib_mame2.wav");
-    
-    mixer->init();
+    if (!mixer->init()) {
+        std::cerr << "Mixer init failed\n";
+        return;
+    }
     auto s1 = w1.getSound();
-    auto s1b = w1.getSound();
+    // second copy was unused; keep a single read
     //auto s2a = utils::makeMono(s1);
     //auto s2b = utils::makeStereo(s1);
     auto s2 = utils::append(s1, s1);
     drivers::PCMDriver drv(mixer);
 
     drv.play(s2);
     while (drv.isPlaying())
     {
         utils::delayMillis(100);
     }
 }
♻️ Duplicate comments (2)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (2)

81-83: Fix track navigation: remove double-advance risk and play immediately on arrow keys

Track is uint8_t and you only trigger a play when the current song ends. This causes sluggish UX and risks wrap/overflow issues. Use a larger index and play immediately after adjusting.

Apply this diff:

-    uint8_t track = 5; // starting from 2
+    size_t track = 0; // start from first track (0-based)
@@
-    do
+    // start initial track
+    adlDrv.play(track);
+    HyperSonicDrivers::utils::ILogger::instance->info(
+        fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()),
+        HyperSonicDrivers::utils::ILogger::eCategory::Application
+    );
+
+    do
     {
-        if (!adlDrv.isPlaying())
-        {
-            adlDrv.play(track);
-            ILogger::instance->info(std::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()), ILogger::eCategory::Application);
-        }
+        if (!adlDrv.isPlaying()) {
+            const auto numTracks = adlFile->getNumTracks();
+            track = (track + 1) % numTracks; // auto-advance on song end
+            adlDrv.play(track);
+            HyperSonicDrivers::utils::ILogger::instance->info(
+                fmt::format("Playing track: {}/{}", static_cast<int>(track), numTracks),
+                HyperSonicDrivers::utils::ILogger::eCategory::Application
+            );
+        }
@@
-                case SDLK_RIGHT:
+                case SDLK_RIGHT:
                 {
-                    adlDrv.stopAllChannels();
-                    track++;
-                    if (track >= adlFile->getNumTracks())
-                        track = 0;
+                    const auto numTracks = adlFile->getNumTracks();
+                    track = (track + 1) % numTracks;
+                    adlDrv.stopAllChannels();
+                    adlDrv.play(track);
+                    HyperSonicDrivers::utils::ILogger::instance->info(
+                        fmt::format("Playing track: {}/{}", static_cast<int>(track), numTracks),
+                        HyperSonicDrivers::utils::ILogger::eCategory::Application
+                    );
                     break;
                 }
@@
-                case SDLK_LEFT:
+                case SDLK_LEFT:
                 {
-                    adlDrv.stopAllChannels();
-                    if (track > 0)
-                        track--;
-                    else
-                        track = adlFile->getNumTracks() - 1;
+                    const auto numTracks = adlFile->getNumTracks();
+                    track = (track + numTracks - 1) % numTracks;
+                    adlDrv.stopAllChannels();
+                    adlDrv.play(track);
+                    HyperSonicDrivers::utils::ILogger::instance->info(
+                        fmt::format("Playing track: {}/{}", static_cast<int>(track), numTracks),
+                        HyperSonicDrivers::utils::ILogger::eCategory::Application
+                    );
                     break;
                 }

Note: this uses fmt::format; add #include <fmt/format.h> or keep std::format and include .

Also applies to: 91-97, 111-127


96-97: std::format without header; prefer fmt for consistency

Same issue as the other file: add or switch to fmt::format.

Apply:

-            ILogger::instance->info(std::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()), ILogger::eCategory::Application);
+            ILogger::instance->info(fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()), ILogger::eCategory::Application);

And include <fmt/format.h> (or <format> if you want to keep std::format).

🧹 Nitpick comments (6)
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (3)

400-440: Confirm intent: skipping track 0 in ADL playback

Tracks iterate from 1 to getNumTracks()-1. If track 0 is valid (and some fixtures show it is), you’re skipping it.

If unintentional, change the loop to start from 0. If intentional, add a comment explaining why track 0 is skipped.

-        for (int i = 1; i < adlf->getNumTracks(); i++)
+        for (int i = 0; i < adlf->getNumTracks(); i++)

545-600: Unreachable code after return: remove or #if 0

There’s a large block after return 0 that never executes, which increases maintenance cost and confuses readers.

Either remove it or wrap with a compile-time guard:

-    return 0;
+    return 0;
+#if 0
     SDL_Init(SDL_INIT_TIMER | SDL_INIT_AUDIO);
     ...
     return 0;
 #endif

134-135: Commented example uses Mix_Hook; other blocks use Mix_HookMusic

To avoid confusion when someone copy-pastes, keep the API consistent across examples or annotate which SDL_mixer version each block targets.

Add a brief comment indicating the intended SDL_mixer version or unify all examples to the same API (e.g., Mix_HookMusic for SDL_mixer 2).

sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp (1)

23-23: Clarify semantics of getNumTracks and add a guard test for V3 header >255

getNumTracks now reflects metadata.num_headers (120 or 250), not “used” tracks. That’s fine, but it hides cases where header entries may be 0xFF or point past available offsets.

Consider adding a V3-specific test that asserts at least one header entry is >255 (if true for your fixtures), to catch potential truncation in getTrack for V3. I can draft it if you confirm a suitable fixture index.

Add something like:

// Ensure V3 header values can exceed 255 (guards against uint8_t truncation)
const auto t = f.getTrack(200); // pick an index known >255 in LOREINTR
EXPECT_GT(t, 255);
EXPECT_LT(t, f.getNumTrackOffsets());

Also applies to: 36-36, 59-59, 89-89

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)

361-369: Typo: data_heder_size → data_header_size

Minor but worth fixing for readability.

-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_dataHeaderSize = data_heder_size;
+    m_dataHeaderSize = data_header_size;

338-349: Avoid per-element std::function overhead in readHeaderFromFile_

Passing a std::function and invoking it in a loop adds overhead and inhibits inlining. A templated reader or a simple branch (v3 vs others) that bulk-reads then converts would be simpler and faster.

Sketch:

if (m_version == 3) {
    m_header.resize(header_size);
    for (int i=0;i<header_size;++i) m_header[i] = readLE16();
} else {
    std::vector<uint8_t> tmp(header_size);
    read(tmp.data(), header_size);
    m_header.resize(header_size);
    for (int i=0;i<header_size;++i) m_header[i] = tmp[i];
}

This also guarantees LE reads for v3.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5088807 and e6bb4a9.

📒 Files selected for processing (4)
  • sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1 hunks)
  • sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (6 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (4 hunks)
  • sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/files/westwood/TestADLFile.cpp (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (3)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/audio/IMixer.hpp (2)
  • make_mixer (100-103)
  • make_mixer (100-100)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp (2)
  • make_device (62-65)
  • make_device (62-62)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/utils/algorithms.hpp (2)
  • delayMillis (50-53)
  • delayMillis (50-50)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (1)
  • header_size (61-61)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/File.cpp (2)
  • read (44-50)
  • read (44-44)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)
  • ADLFile (115-154)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (3)
  • ADLFile (35-35)
  • ADLFile (36-36)
  • track (44-44)
🪛 GitHub Check: Codacy Static Code Analysis
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp

[warning] 39-39: sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp#L39
Include file: <HyperSonicDrivers/files/VOCFile.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.

sdl2-hyper-sonic-drivers/examples/adl-play.cpp

[warning] 1-1: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L1
Include file: <HyperSonicDrivers/audio/sdl2/Mixer.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 2-2: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L2
Include file: <HyperSonicDrivers/hardware/opl/OPL.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 3-3: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L3
Include file: <HyperSonicDrivers/hardware/opl/OPLFactory.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 4-4: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L4
Include file: <HyperSonicDrivers/utils/algorithms.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 5-5: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L5
Include file: <HyperSonicDrivers/files/westwood/ADLFile.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 6-6: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L6
Include file: <HyperSonicDrivers/drivers/westwood/ADLDriver.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 7-7: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L7
Include file: <HyperSonicDrivers/utils/ILogger.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 8-8: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L8
Include file: <HyperSonicDrivers/devices/Adlib.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 9-9: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L9
Include file: <HyperSonicDrivers/devices/SbPro.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 10-10: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L10
Include file: <HyperSonicDrivers/devices/SbPro2.hpp> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 12-12: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L12
Include file: <spdlog/spdlog.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 13-13: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L13
Include file: <fmt/color.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 15-15: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L15
Include file: <SDL2/SDL.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 17-17: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L17
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 18-18: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L18
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 19-19: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L19
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.


[warning] 20-20: sdl2-hyper-sonic-drivers/examples/adl-play.cpp#L20
Include file: not found. Please note: Cppcheck does not need standard library headers to get proper results.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
🔇 Additional comments (1)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)

115-134: Good: strict file layout validation and offset adjustment

Validating minimum size, version-dependent offsets, and then converting absolute to data-relative offsets with 0→0xFFFF sentinel is clear and safe.

Also applies to: 151-154

Copy link
Contributor

coderabbitai bot commented Aug 23, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

Copy link
Contributor

coderabbitai bot commented Aug 23, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)

187-190: V3 header truncation risk: widen getTrack() result to 16-bit

getTrack returns uint8_t but V3 header entries are 16-bit; values >255 will be truncated. This breaks program lookups for v3.

Apply in this file:

-    uint8_t ADLFile::getTrack(const int track) const
+    uint16_t ADLFile::getTrack(const int track) const
     {
         return m_header.at(track);
     }

And in ADLFile.hpp (outside this file), update both the member and declaration:

-std::vector<uint8_t> m_header; // TODO: wrong for v3
+std::vector<uint16_t> m_header;

-uint8_t getTrack(const int track) const;
+uint16_t getTrack(const int track) const;

If you want a follow-up, I can propagate type fixes across call sites and tests.


230-249: Bounds/sentinel check before decoding instrument bytes

If getInstrumentOffset returns 0xFFFF (invalid) or an offset near the end, pointer arithmetic and reads will go OOB.

Apply:

-        const uint8_t* instr = m_data.get() + getInstrumentOffset(instrument);
+        const uint16_t off = getInstrumentOffset(instrument);
+        assertValid_(off != 0xFFFF);
+        // We read up to instr[10], ensure at least 11 bytes are available.
+        assertValid_(static_cast<uint32_t>(off) + 11 <= m_dataSize);
+        const uint8_t* instr = m_data.get() + off;
♻️ Duplicate comments (1)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1)

82-99: Track navigation: use a wider index, start playback once, auto-advance on end, and play immediately on arrow keys

  • uint8_t can wrap; prefer size_t.
  • Current loop restarts the same track when it ends instead of advancing.
  • RIGHT/LEFT only adjust the index; playback resumes after a timeout tick, adding latency.

Apply this focused patch:

-    uint8_t track = 0;
+    std::size_t track = 0;
@@
-    do
+    // Precompute and validate track count
+    const auto numTracks = static_cast<std::size_t>(adlFile->getNumTracks());
+    if (numTracks == 0) {
+        spdlog::warn("No tracks in ADL file: {}", filename);
+        return;
+    }
+
+    // Start initial track
+    adlDrv.play(track);
+    ILogger::instance->info(
+        fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()),
+        ILogger::eCategory::Application
+    );
+
+    do
     {
-        if (!adlDrv.isPlaying())
-        {
-            adlDrv.play(track);
-            ILogger::instance->info(fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()), ILogger::eCategory::Application);
-        }
+        if (!adlDrv.isPlaying()) {
+            track = (track + 1) % numTracks; // auto-advance
+            adlDrv.play(track);
+            ILogger::instance->info(
+                fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()),
+                ILogger::eCategory::Application
+            );
+        }
@@
-                case SDLK_RIGHT:
+                case SDLK_RIGHT:
                 {
-                    adlDrv.stopAllChannels();
-                    track++;
-                    if (track >= adlFile->getNumTracks())
-                        track = 0;
+                    adlDrv.stopAllChannels();
+                    track = (track + 1) % numTracks;
+                    adlDrv.play(track);
+                    ILogger::instance->info(
+                        fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()),
+                        ILogger::eCategory::Application
+                    );
                     break;
                 }
@@
-                case SDLK_LEFT:
+                case SDLK_LEFT:
                 {
-                    adlDrv.stopAllChannels();
-                    if (track > 0)
-                        track--;
-                    else
-                        track = adlFile->getNumTracks() - 1;
+                    adlDrv.stopAllChannels();
+                    track = (track + numTracks - 1) % numTracks;
+                    adlDrv.play(track);
+                    ILogger::instance->info(
+                        fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()),
+                        ILogger::eCategory::Application
+                    );
                     break;
                 }

Also applies to: 117-135

🧹 Nitpick comments (8)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (4)

39-40: Doc comment and code disagree on initial track index

The comment says “initial track index is 5” but the code starts from 0. Align one of them.

Apply either:

  • Update the comment to 0, or
  • Initialize to 5 in code.

If you prefer code change:

-    uint8_t track = 0;
+    uint8_t track = 5;

66-80: Guard against unhandled OPL types (device may remain null)

If an unexpected OplType is passed, device stays null and subsequent usage is undefined. Add a default case and fail fast.

Apply:

     switch (type)
     {
         using enum OplType;
@@
     case OPL3:
         device = make_device<devices::SbPro2, devices::Opl>(mixer, emu);
         break;
 
+    default:
+        spdlog::error("Unhandled OPL type: {}", static_cast<int>(type));
+        return;
     }

159-160: Initialize SDL audio too for portability

Some SDL backends require SDL_INIT_AUDIO for mixer/device init to succeed reliably.

Apply:

-    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0)
+    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_AUDIO) != 0)
         return -1;

87-90: Minor: early-return path logs but doesn’t indicate which mixer

Consider adding the mixer type or filename for easier diagnostics; optional.

Example:

-        spdlog::error("mixer not ready yet..");
+        spdlog::error("Mixer not ready (type: SDL2). Cannot play '{}'.", filename);
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (4)

259-273: Iterate by number of entries, not header size in bytes

Using V1_HEADER_SIZE as the loop bound happens to work (120), but it’s a byte size constant. Use V1_NUM_HEADER for clarity and to avoid accidental misuse if constants change.

Apply:

-        for (int i = 0; i < V1_HEADER_SIZE; i++)
+        for (int i = 0; i < V1_NUM_HEADER; i++)
         {
             uint16_t v = readLE16();

316-327: readHeaderFromFile_: consider passing element width instead of std::function

std::function<uint16_t()> in a tight loop has overhead and hides byte-width semantics. Passing an element-size and reading inline (u8 vs le16) would be simpler and faster.

Illustrative approach:

  • Signature: readHeaderFromFile_(int num_entries, int elem_bytes)
  • Body: branch once on elem_bytes and fill m_header[i] using readU8() or readLE16().

329-337: Offset-table read: small clarity nit

assertValid_(tell() == offset_start); is great. Consider a short comment stating that offset_start is absolute (from file start) per version metadata.


339-347: Fix inconsistent parameter naming in ADLFile.hpp

The declaration of readDataFromFile_ in ADLFile.hpp uses a typo (data_heder_size), while the definition in ADLFile.cpp uses data_header_size. Although C++ doesn’t require parameter names to match, aligning them helps prevent confusion and improves IDE navigation.

• File: sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp
• Line: 63

Proposed diff:

--- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp
+++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp
@@ -63,1 +63,1 @@
-        void readDataFromFile_(const int data_offsets, const int data_heder_size);
+        void readDataFromFile_(const int data_offsets, const int data_header_size);

This change harmonizes the parameter name between declaration and definition.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e6bb4a9 and a049e63.

📒 Files selected for processing (3)
  • sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1 hunks)
  • sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (4 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp
🧰 Additional context used
🧬 Code graph analysis (2)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)
  • ADLFile (115-145)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (3)
  • ADLFile (35-35)
  • ADLFile (36-36)
  • track (44-44)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (2)
  • header_size (61-61)
  • data_offsets (63-63)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/File.cpp (8)
  • read (44-50)
  • read (44-44)
  • size (25-28)
  • size (25-25)
  • assertValid_ (127-136)
  • assertValid_ (127-127)
  • tell (30-33)
  • tell (30-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Windows (x64-windows, Debug, 2022)
  • GitHub Check: Build Windows (x64-windows, Release, 2022)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
🔇 Additional comments (1)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)

135-145: Deriving counts from metadata: LGTM

Computing counts from m_meta_version and using count_loop_ for offsets is clean and version-agnostic.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (2)

391-398: Check mixer->init() result to avoid running with an uninitialized audio backend

If init fails, subsequent device/driver usage will misbehave.

 void adldune2filestest()
 {
     auto mixer = audio::make_mixer<audio::sdl2::Mixer>(8, 44100, 1024);
-    mixer->init();
+    if (!mixer->init()) {
+        utils::ILogger::instance->error("Mixer init failed", utils::ILogger::eCategory::Application);
+        return;
+    }

400-402: Guard SDL window creation; clean up subsystems on failure

SDL_CreateWindow can return null; handle it and quit subsystems to avoid leaks.

-    auto window = SDL_CreateWindow("a", 0, 0, 320, 200, 0);
+    auto window = SDL_CreateWindow("a", 0, 0, 320, 200, 0);
+    if (!window) {
+        utils::ILogger::instance->error(std::string("SDL_CreateWindow failed: ") + SDL_GetError(), utils::ILogger::eCategory::Application);
+        SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
+        return;
+    }
@@
-QUIT:
-    SDL_DestroyWindow(window);
+QUIT:
+    SDL_DestroyWindow(window);
     SDL_QuitSubSystem(SDL_INIT_VIDEO);
     SDL_QuitSubSystem(SDL_INIT_EVENTS);

Also applies to: 444-447

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)

226-250: Guard against invalid instrument offsets and out-of-bounds reads

If an instrument offset is 0xFFFF (invalid) or points near the end of the buffer, dereferencing m_data+offset will be undefined behavior.

Add sanity checks:

 hardware::opl::OPL2instrument_t ADLFile::getInstrument(const int instrument) const
 {
     using hardware::opl::OPL2instrument_t;
-
-    const uint8_t* instr = m_data.get() + getInstrumentOffset(instrument);
+    const uint16_t off = getInstrumentOffset(instrument);
+    assertValid_(off != 0xFFFF);
+    // We read up to instr[10]; ensure at least 11 bytes available
+    assertValid_(static_cast<uint32_t>(off) + 11u <= m_dataSize);
+    const uint8_t* instr = m_data.get() + off;

316-327: Critical Fix: Prevent 16-bit Header Truncation in ADLFile (V3)

The current implementation of readHeaderFromFile_ reads 16-bit values for version 3 but stores them in a std::vector<uint8_t>, truncating any value > 255. Likewise, getTrack() returns an 8-bit value, masking true track IDs and leading to incorrect lookups for V3 ADL files. To eliminate this risk, we must widen the container and accessor to 16 bits.

• In src/HyperSonicDrivers/files/westwood/ADLFile.hpp:
– Change the member declaration at line 65
diff - std::vector<uint8_t> m_header; // TODO: This is wrong for v3 as it is uint16_t instead! + std::vector<uint16_t> m_header; // Store full 16-bit entries for V3 headers
– Update the accessor prototype at line 44
diff - uint8_t getTrack(const int track) const; + uint16_t getTrack(const int track) const;

• In src/HyperSonicDrivers/files/westwood/ADLFile.cpp:
– Widen the method signature at the definition (was line 187)
diff - uint8_t ADLFile::getTrack(const int track) const + uint16_t ADLFile::getTrack(const int track) const { return m_header.at(track); }

• Verify that readHeaderFromFile_’s loop remains correct—no per-element change needed since it already calls a std::function<uint16_t()> read and simply stores into the widened m_header.

• Update all call-sites and tests that consume getTrack():
– In ADLDriver.cpp (line 242) you can now directly assign to uint16_t soundId without truncation.
– Adjust any TestADLFile.cpp assertions to compare against uint16_t values (GTest’s EXPECT_EQ will handle mixed integral types, but updating literals to 0xFFu or casting to uint16_t clarifies intent).

By making these changes, V3 headers will be fully preserved, and downstream logic will work with the correct track IDs.

♻️ Duplicate comments (2)
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (1)

6-8: Includes for and —good fix to unbreak builds

Adding these headers resolves the previously flagged compile issues around std::format usage and std::filesystem iteration.

sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1)

82-101: Make track index type wider, auto-advance on song end, and play immediately on LEFT/RIGHT

  • Use size_t (or int) instead of uint8_t to avoid 8-bit wrap concerns.
  • Auto-advance to next track when a song ends.
  • Start playback immediately when navigating with arrow keys for responsive UX.
-    uint8_t track = 0;
+    size_t track = 0;
@@
-        if (!adlDrv.isPlaying())
-        {
-            adlDrv.play(track);
-            ILogger::instance->info(fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()), ILogger::eCategory::Application);
-        }
+        if (!adlDrv.isPlaying()) {
+            const auto numTracks = static_cast<size_t>(adlFile->getNumTracks());
+            track = (track + 1) % numTracks; // auto-advance
+            adlDrv.play(static_cast<int>(track));
+            ILogger::instance->info(
+                fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()),
+                ILogger::eCategory::Application
+            );
+        }
@@
-                case SDLK_RIGHT:
-                {
-                    adlDrv.stopAllChannels();
-                    track++;
-                    if (track >= adlFile->getNumTracks())
-                        track = 0;
-
-                    break;
-                }
+                case SDLK_RIGHT: {
+                    const auto numTracks = static_cast<size_t>(adlFile->getNumTracks());
+                    track = (track + 1) % numTracks;
+                    adlDrv.stopAllChannels();
+                    adlDrv.play(static_cast<int>(track));
+                    ILogger::instance->info(
+                        fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()),
+                        ILogger::eCategory::Application
+                    );
+                    break;
+                }
@@
-                case SDLK_LEFT:
-                {
-                    adlDrv.stopAllChannels();
-                    if (track > 0)
-                        track--;
-                    else
-                        track = adlFile->getNumTracks() - 1;
-
-                    break;
-                }
+                case SDLK_LEFT: {
+                    const auto numTracks = static_cast<size_t>(adlFile->getNumTracks());
+                    track = (track + numTracks - 1) % numTracks;
+                    adlDrv.stopAllChannels();
+                    adlDrv.play(static_cast<int>(track));
+                    ILogger::instance->info(
+                        fmt::format("Playing track: {}/{}", static_cast<int>(track), adlFile->getNumTracks()),
+                        ILogger::eCategory::Application
+                    );
+                    break;
+                }

Also applies to: 110-136

🧹 Nitpick comments (5)
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (2)

341-350: Harden mixer init and drop the unused variable in pcm_sound_append()

  • mixer->init() is unchecked; bail out if it fails.
  • s1b is never used; remove it to silence warnings.

Apply:

 void pcm_sound_append()
 {
     auto mixer = audio::make_mixer<audio::sdl2::Mixer>(8, 44100, 1024);
     files::WAVFile w1("test/fixtures/test_renderer_adlib_mame2.wav");
     
-    mixer->init();
+    if (!mixer->init()) {
+        spdlog::error("Mixer init failed");
+        return;
+    }
     auto s1 = w1.getSound();
-    auto s1b = w1.getSound();
+    // auto s1b = w1.getSound(); // unused
     //auto s2a = utils::makeMono(s1);
     //auto s2b = utils::makeStereo(s1);
     auto s2 = utils::append(s1, s1);
     drivers::PCMDriver drv(mixer);

Also applies to: 351-369


402-442: Remove the no-op outer loop (for f=0..0) and simplify file selection

The loop executes exactly once; flatten for clarity.

-    for (int f = 0; f <= 0; f++)
-    {
-        //const std::string fn = "adl/DUNE" + std::to_string(f) + ".ADL";
-        const std::string fn = "adl/KYRA1A.ADL";
+    {
+        const std::string fn = "adl/KYRA1A.ADL";
         utils::ILogger::instance->info(std::format("opening file: {}", fn), utils::ILogger::eCategory::Application);
         auto adlf = std::make_shared<files::westwood::ADLFile>(fn);
         drv.setADLFile(adlf);
         for (int i = 1; i < adlf->getNumTracks(); i++)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1)

33-41: Doc mismatch: comment says “initial track index is 5” but code starts at 0

Keep docs accurate to avoid confusion.

- * (initial track index is 5) and responds to key presses to control playback.
+ * Starts from track index 0 and responds to key presses to control playback.
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)

339-347: Minor: set m_dataHeaderSize before bounds assertions elsewhere depend on it

No functional issue here, but if future code adds checks during read, you might want m_dataHeaderSize set earlier. Optional.

-    read(m_data.get(), m_dataSize);
-    m_dataHeaderSize = data_header_size;
+    m_dataHeaderSize = data_header_size;
+    read(m_data.get(), m_dataSize);

252-301: Version detection logic is clever but brittle; consider explicit signature checks

Current heuristic relies on header entry magnitudes vs V3_HEADER_SIZE. Consider augmenting with additional invariants (e.g., minimum offset monotonicity, sentinel patterns) to reduce false positives across malformed inputs. Optional for now.

Happy to propose a dedicated detector that peeks both header and early offsets with clear invariants.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e6bb4a9 and f4c764a.

📒 Files selected for processing (4)
  • sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1 hunks)
  • sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (4 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (5 hunks)
  • sonar-project.properties (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
sdl2-hyper-sonic-drivers/examples/adl-play.cpp (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)
  • ADLFile (115-145)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (3)
  • ADLFile (35-35)
  • ADLFile (36-36)
  • track (44-44)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/File.cpp (8)
  • read (44-50)
  • read (44-44)
  • size (25-28)
  • size (25-25)
  • assertValid_ (127-136)
  • assertValid_ (127-127)
  • tell (30-33)
  • tell (30-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
  • GitHub Check: Build Windows (x64-windows, Release, 2022)
  • GitHub Check: Build Windows (x64-windows, Debug, 2022)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
🔇 Additional comments (4)
sonar-project.properties (1)

2-2: SonarCloud projectKey update verified
No lingering references to the old key were found in the repository. Your sonar.projectKey is correctly set to Raffaello_sdl2-hyper-sonic-drivers in sonar-project.properties, and the ripgrep check returned no instances of the previous key.

• All occurrences of sonar.projectKey now point to the new value.
• No matches for Raffaello_sdl2-sonic-drivers were detected.

Please ensure that:

  • Your SonarCloud project’s key matches Raffaello_sdl2-hyper-sonic-drivers.
  • Any CI/CD secrets or workflow inputs (e.g. SONAR_PROJECT_KEY) are updated to this new key.
sdl2-hyper-sonic-drivers/sdl2-hyper-sonic-drivers.cpp (1)

409-411: Verify track indexing (0-based vs 1-based) for ADLDriver::play

This loop skips track 0 by starting from 1; adl-play example uses 0-based indexing. Confirm intended behavior to avoid off-by-one or missing the first subsong.

Would you like me to scan ADLDriver and tests to confirm the expected indexing convention and generate a patch accordingly?

sdl2-hyper-sonic-drivers/examples/adl-play.cpp (1)

157-177: Solid: SDL init, window creation checks, and mixer failure cleanup

Init/teardown paths are handled correctly; returning proper codes on failure is appreciated.

Also applies to: 221-224

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)

115-121: Constructor flow/read ordering looks sound; deriving track count from metadata is correct

Validating sizes, reading header/offsets/data, then deriving counts from version metadata removes the prior reliance on the (problematic) header count. Good progression.

Also applies to: 133-145

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (4)

63-63: Typo in parameter name: data_heder_sizedata_header_size

Minor spelling fix for clarity and consistency with m_dataHeaderSize.

-        void readDataFromFile_(const int data_offsets, const int data_heder_size);
+        void readDataFromFile_(const int data_offsets, const int data_header_size);

65-65: Good change: header stored as 16-bit words; clarify unit in related APIs

Switching m_header to std::vector<uint16_t> aligns with 16-bit fields in the format. To avoid ambiguity, document whether readHeaderFromFile_’s header_size argument is a count of 16-bit words or bytes. Consider renaming to header_word_count (header only) in a future refactor for self-documenting intent.


75-78: Keep member types consistent with the (proposed) unsigned getters

If you adopt unsigned return types for the offset counts, consider aligning the members as well and dropping negative sentinels (or use std::optional<uint16_t>).

-        uint16_t m_num_tracks = 0;
-        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;       // consider std::optional<uint16_t>
+        uint16_t m_num_instrument_offsets = 0;  // consider std::optional<uint16_t>

If you keep int members intentionally for a tri-state (unknown/known-zero/known-nonzero), please add a brief comment documenting the sentinel semantics.

Also, ensure any assignment from counting helpers to m_num_tracks clamps safely to UINT16_MAX to avoid silent narrowing.


81-86: Avoid signed/unsigned comparison in loop

vec.size() returns size_t; iterating with int i risks signed/unsigned warnings and potential UB if vec.size() exceeds INT_MAX. Use std::size_t for the index.

-        for (int i = 0; i < vec.size(); ++i)
+        for (std::size_t i = 0; i < vec.size(); ++i)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f4c764a and 53ce2bc.

📒 Files selected for processing (2)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (5 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
  • GitHub Check: Build Windows (x64-windows, Release, 2022)
  • GitHub Check: Build Windows (x64-windows, Debug, 2022)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
🔇 Additional comments (1)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (1)

40-42: It looks like attempts to display the file contents didn’t return output. To ensure we correctly inspect the backing member types, please verify manually that in ADLFile.hpp the fields m_num_track_offsets and m_num_instrument_offsets are declared as int. If they’re indeed signed, consider aligning them to uint16_t (or another unsigned type matching the file format) alongside changing the getters.

Since the automated search didn’t reliably show callers depending on negative sentinels, please manually review:

  • All uses of getNumTrackOffsets() and getNumInstrumentOffsets() to ensure no code relies on negative return values.
  • The declarations of backing fields m_num_track_offsets and m_num_instrument_offsets for type consistency.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (6)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)

226-250: Guard against invalid instrument offsets (0xFFFF) and OOB reads

If an instrument entry is invalid (0xFFFF after adjustment) or too close to the end of the data blob, this performs an out‑of‑bounds read. Add sentinel and bounds checks before dereferencing.

     hardware::opl::OPL2instrument_t ADLFile::getInstrument(const int instrument) const
     {
         using hardware::opl::OPL2instrument_t;
-
-        const uint8_t* instr = m_data.get() + getInstrumentOffset(instrument);
+        const uint16_t off = getInstrumentOffset(static_cast<uint16_t>(instrument));
+        // Each instrument reads at least 11 bytes (highest index used below is 10)
+        constexpr uint32_t kInstrSizeBytes = 11;
+        if (off == 0xFFFF) {
+            assertValid_(false); // invalid instrument index
+        }
+        assertValid_(off + kInstrSizeBytes <= m_dataSize);
+        const uint8_t* instr = m_data.get() + off;
         return OPL2instrument_t(
             {
                 .trem_vibr_1 = instr[0],
                 .att_dec_1 = instr[7],
                 .sust_rel_1 = instr[9],
                 .wave_1 = instr[3],
                 .scale_1 = 0, // ???
                 .level_1 = instr[5],
                 .feedback = instr[2],
                 .trem_vibr_2 = instr[1],
                 .att_dec_2 = instr[8],
                 .sust_rel_2 = instr[10],
                 .wave_2 = instr[4],
                 .scale_2 = 0, // ???
                 .level_2 = instr[6],
                 .unused = 0,
                 .basenote = 0 /// ???
             }
         );
     }

If you prefer not to assert on invalid indices, we can return a default‑constructed OPL2instrument_t instead.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp (2)

179-197: SMPTE division parsing is incorrect; high byte must be treated as signed

When the MSB is set, the SMPTE FPS byte is signed (e.g., -24, -25, -29, -30). Current code masks with 0x7FFF and right-shifts, yielding a positive number and making the switch cases on negative constants unreachable.

Apply this fix to read the signed SMPTE value correctly:

-        if (m_midi->division & 0x8000)
+        if (m_midi->division & 0x8000)
         {
             // ticks per frame
-            int smpte = (m_midi->division & 0x7FFF) >> 8;
-            int ticksPerFrame = m_midi->division & 0xFF;
+            const int8_t smpte = static_cast<int8_t>(m_midi->division >> 8);
+            const int ticksPerFrame = m_midi->division & 0xFF;
             switch (smpte)
             {
             case -24:
             case -25:
             case -29:
             case -30:
                 logW("SMPTE not implemented yet");
                 break;
             default:
                 logW(std::format("Division SMPTE not known = {}", smpte));
             }

205-213: Thread-safety: data races on m_force_stop/m_paused/m_isPlaying

processTrack reads m_force_stop and m_paused while play/stop/pause/resume write them without synchronization. This is UB under the C++ memory model.

Minimal safe options:

  • Make m_force_stop, m_paused, and m_isPlaying std::atomic.
  • Or guard all reads/writes with a mutex consistently.
    Optionally switch to jthread stop_token to avoid custom flags; request_stop() in stop(), and check token in processTrack.
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (1)

260-267: Bug: QueueEntry.id is uint8_t — truncates 16-bit track IDs and breaks retry logic

The PR widens track/sound IDs to 16-bit, but QueueEntry still stores id as uint8_t. When the queue is full or when retrying a skipped sound, IDs > 255 will be truncated, leading to playing the wrong program and misleading logs.

Apply this diff to carry full 16-bit IDs:

 struct QueueEntry
 {
-    QueueEntry() : data(0), id(0), volume(0) {}
-    QueueEntry(uint8_t* ptr, uint8_t track, uint8_t vol) : data(ptr), id(track), volume(vol) {}
+    QueueEntry() : data(nullptr), id(0), volume(0) {}
+    QueueEntry(uint8_t* ptr, uint16_t track, uint8_t vol) : data(ptr), id(track), volume(vol) {}
     uint8_t* data;
-    uint8_t id;
+    uint16_t id;
     uint8_t volume;
 };

Follow-up in ADLDriver.cpp is minimal (constructor call adapts automatically); see suggested tweak there for clarity.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp (2)

85-103: Propagate 16-bit IDs through the queue and improve ring-buffer increment readability

With startSound_(uint16_t, ...), ensure QueueEntry.id is also uint16_t (see header fix). Additionally, the ring-buffer increment uses a terse idiom.

Apply this readability tweak (functionally equivalent):

-        m_programQueue[m_programQueueEnd] = QueueEntry(trackData, track, volume);
-        ++m_programQueueEnd &= 15;
+        m_programQueue[m_programQueueEnd] = QueueEntry(trackData, track, volume);
+        m_programQueueEnd = (m_programQueueEnd + 1) & 0x0F;

234-249: Mandatory refactor: Widen QueueEntry.id to match the new uint16_t track type

The grep shows that while you’ve widened the public APIs (play() and startSound_()) to accept a uint16_t track, the internal queue still stores track in an 8-bit field—truncating any value ≥256. You need to update ADLDriver.hpp so the QueueEntry uses a 16-bit id.

– ADLDriver.hpp (around line 261):
• Change uint8_t id;uint16_t id;
• Change the constructor from QueueEntry(uint8_t* ptr, uint8_t track, uint8_t vol)QueueEntry(uint8_t* ptr, uint16_t track, uint8_t vol)

Suggested diff:

--- a/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp
+++ b/sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp
@@ struct QueueEntry {
-    uint8_t id;
+    uint16_t id;
@@
-    QueueEntry(uint8_t* ptr, uint8_t track, uint8_t vol)
-        : data(ptr), id(track), volume(vol) {}
+    QueueEntry(uint8_t* ptr, uint16_t track, uint8_t vol)
+        : data(ptr), id(track), volume(vol) {}

After this change, rebuild and test to ensure no further 8-bit truncations occur when queuing tracks.

♻️ Duplicate comments (1)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)

325-333: Re: noexcept on readOffsetsFromFile_

This implementation uses assertValid_ and can throw; combined with noexcept in the declaration leads to terminate on error.

See the header diff to remove noexcept.

🧹 Nitpick comments (7)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (2)

70-70: Prefer a narrower unsigned type for m_dataHeaderSize

m_dataHeaderSize holds the header prefix within the data blob (≤ ~2 KiB for all versions). Using int invites sign/width mismatches when subtracting from uint16_t offsets.

Minimal change:

-        int m_dataHeaderSize = 0;
+        uint16_t m_dataHeaderSize = 0;

72-74: Name/contract mismatch in count_loop_: clarify parameter and use an unsigned return

The parameter represents the minimum valid offset (offset_start), not a “num_offs.” Also, the value returned is a count that fits in uint16_t for ADL; returning int is unnecessary.

Proposed tweaks (update declaration and definition):

-        template<typename T>
-        int count_loop_(const int num_offs, const std::vector<T>& vec);
+        template<typename T>
+        uint16_t count_loop_(const int offset_start, const std::vector<T>& vec);

And in the definition:

-    int ADLFile::count_loop_(const int offs_start, const std::vector<T>& vec)
+    uint16_t ADLFile::count_loop_(const int offset_start, const std::vector<T>& vec)
     {
-        int tot = 0;
+        uint16_t tot = 0;
         constexpr T max_ = std::numeric_limits<T>::max();
-        for (size_t i = 0; i < vec.size(); ++i)
+        for (size_t i = 0; i < vec.size(); ++i)
         {
-            if (vec[i] >= offs_start && vec[i] < max_) {
+            if (vec[i] >= offset_start && vec[i] < max_) {
                 ++tot;
             }
         }
         return tot;
     }

Also applies to: 80-87

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (3)

202-214: Make default branch explicitly unreachable

The enum covers both cases; the default is defensive but could mask future issues. Prefer asserting unreachable.

-        default:
-            // unreachable code
-            return 0xFFFF;
+        default:
+            assertValid_(false); // unreachable
+            return 0xFFFF;

259-273: Use entry count constant instead of byte size in v3 detection loop

The loop reads 16‑bit entries but uses V1_HEADER_SIZE (bytes) as the iteration bound. It happens to be 120 which matches V1_NUM_HEADER, but using the proper “number of entries” constant improves readability and avoids accidental drift.

-        for (int i = 0; i < V1_HEADER_SIZE; i++)
+        for (int i = 0; i < V1_NUM_HEADER; ++i)

335-343: m_dataSize width and upper‑bound check

size() returns uintmax_t; truncating to uint32_t is fine for ADL but add a guard to make the intent explicit, or widen m_dataSize to size_t.

Option A (guard and keep uint32_t):

-        m_dataSize = size() - data_offsets;
+        const auto total = size();
+        assertValid_(total >= static_cast<uintmax_t>(data_offsets));
+        const auto remaining = total - static_cast<uintmax_t>(data_offsets);
+        assertValid_(remaining <= std::numeric_limits<uint32_t>::max());
+        m_dataSize = static_cast<uint32_t>(remaining);

Option B (widen the member type to size_t in the header and here).

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp (1)

205-209: Remove redundant acquire() in play()
The call to m_device->acquire(this) after stop() is effectively a no-op, since:

  • IDevice::acquire(owner)
    • on first call: runs init(), sets m_acquired = true and m_owner = owner.
    • on subsequent calls with the same owner: returns isOwned(owner) (always true) without side-effects.
  • MIDDriver::stop() does not call release(), so the device remains acquired by this driver.

Keeping the extra acquire() is safe but redundant. To simplify, you can remove the check and early return:

• File: src/HyperSonicDrivers/drivers/MIDDriver.cpp
Lines: 205–209

-        stop();
-        if (!m_device->acquire(this)) {
-            return;
-        }
+        stop();
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp (1)

271-289: Nit: logging typo and missing label in getProgram_() debug

Minor, but makes logs easier to parse.

-        logD(std::format("calling getProgram(prodIg={}){}", progId, offset));
+        logD(std::format("calling getProgram(progId={}) offset={}", progId, offset));
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0f6f3eb and 614cba6.

📒 Files selected for processing (8)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp (2 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (5 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (3 hunks)
  • sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (5)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1)
  • track (25-25)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (2)
  • track (56-56)
  • track (62-62)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (1)
  • track (44-44)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp (1)
  • track (11-11)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/MIDDriverMock.hpp (2)
  • track (19-22)
  • track (19-19)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp (3)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1)
  • track (25-25)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (2)
  • track (40-40)
  • track (52-52)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (2)
  • track (56-56)
  • track (62-62)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (4)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1)
  • track (25-25)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (2)
  • track (40-40)
  • track (52-52)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (1)
  • track (44-44)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp (1)
  • track (11-11)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp (4)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1)
  • track (25-25)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (2)
  • track (40-40)
  • track (52-52)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (1)
  • track (44-44)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp (2)
  • play (166-213)
  • play (166-166)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (4)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (2)
  • track (40-40)
  • track (52-52)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (2)
  • track (56-56)
  • track (62-62)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (1)
  • track (44-44)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp (1)
  • track (11-11)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (3)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1)
  • track (25-25)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (2)
  • track (40-40)
  • track (52-52)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (4)
  • track (56-56)
  • track (62-62)
  • progId (70-70)
  • progId (73-73)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp (6)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp (2)
  • play (234-249)
  • play (234-234)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1)
  • track (25-25)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (2)
  • track (40-40)
  • track (52-52)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (2)
  • track (56-56)
  • track (62-62)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp (1)
  • track (11-11)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/MIDDriverMock.hpp (2)
  • track (19-22)
  • track (19-19)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (6)
  • track (44-44)
  • programId (45-45)
  • instrument (46-46)
  • instrument (52-52)
  • progId (47-47)
  • data_offsets (63-63)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/File.cpp (8)
  • size (25-28)
  • size (25-25)
  • assertValid_ (127-136)
  • assertValid_ (127-127)
  • tell (30-33)
  • tell (30-30)
  • read (44-50)
  • read (44-44)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
  • GitHub Check: Build Windows (x64-windows, Debug, 2022)
  • GitHub Check: Build Windows (x64-windows, Release, 2022)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
🔇 Additional comments (10)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1)

25-25: API widening to 16‑bit track index looks good and consistent with ADL/MID changes

Signature aligns with the 16‑bit track/offset model used across the PR. No issues spotted here.

sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp (1)

11-11: Test mock kept in sync with interface

The override now matches IAudioDriver::play(uint16_t). Looks correct.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (1)

40-47: Public API updated to 16-bit — verified safe at call sites

I’ve confirmed that all ADLFile accessors now return uint16_t and align with the on-disk format; any implicit conversions back to int (in tests) or comparisons against 8-bit sentinels still behave correctly:

• ADLFile.hpp / ADLFile.cpp

  • getTrack, getTrackOffset, getInstrumentOffset, getProgramOffset all return uint16_t and index into vectors of matching element width.
  • Internally, for legacy ADLv<3 files, headers store uint8_t values but are returned as uint16_t, so comparing against 0xFF (255) still works; for ADLv3, 16-bit format uses 0xFFFF sentinel.

• Call sites:

  • Tests frequently assign to int track2 = f.getTrack(2) or use auto; converting uint16_tint is safe (no truncation).
  • No instances of narrowing to uint8_t remain.
  • Drivers compare returned values (soundId) to both 0xFF and 0xFFFF as intended.

There are no lingering assumptions about 8-bit returns nor unsafe truncations. I’m happy to approve these changes.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (3)

94-114: Constructor doc is clear and matches the implemented flow

Helpful overview of the parsing pipeline. No action required.


115-145: Construction flow looks correct; metadata‑driven sizing is a good improvement

The sequence (detect → read header via callback → read offsets → read data → compute counts → close → adjust offsets) is sound, and setting m_num_tracks from metadata avoids relying on potentially inconsistent v3 headers.


187-190: getTrack widened to 16‑bit — good

Directly returns the widened header entry. Using at() preserves bounds checks.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (1)

40-40: play(uint16_t) override — consistent and non‑breaking

Matches IAudioDriver’s widened signature; default remains 0. Good.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.cpp (1)

166-176: API widening to uint16_t looks good and is consistent with IAudioDriver

Signature change aligns with IAudioDriver::play(uint16_t). The bounds check against numTracks is preserved.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (2)

56-56: Public API widened to uint16_t: OK

play(uint16_t) matches IAudioDriver and related changes across the codebase.


62-63: Internal API widened to uint16_t: OK but verify downstream usage

startSound_(uint16_t, uint8_t) matches the broader 16-bit track IDs work.

Please ensure all call sites (e.g., QueueEntry construction) propagate 16-bit IDs without truncation (see next comment).

Comment on lines 61 to 64
void readHeaderFromFile_(const int header_size, std::function<uint16_t()> read);
void readOffsetsFromFile_(const int num_offsets, std::vector<uint16_t>& vec, const int offset_start) const noexcept;
void readDataFromFile_(const int data_offsets, const int data_heder_size);
void readDataFromFile_(const int data_offsets, const int data_header_size);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

noexcept on readOffsetsFromFile_ conflicts with throwing asserts

readOffsetsFromFile_ is marked noexcept but calls assertValid_(), which throws. Any violation will std::terminate instead of surfacing a diagnostic. Drop noexcept here (and in the definition) to preserve error reporting.

Apply this diff in the header and mirror it in the cpp definition:

-        void readOffsetsFromFile_(const int num_offsets, std::vector<uint16_t>& vec, const int offset_start) const noexcept;
+        void readOffsetsFromFile_(const int num_offsets, std::vector<uint16_t>& vec, const int offset_start) const;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void readHeaderFromFile_(const int header_size, std::function<uint16_t()> read);
void readOffsetsFromFile_(const int num_offsets, std::vector<uint16_t>& vec, const int offset_start) const noexcept;
void readDataFromFile_(const int data_offsets, const int data_heder_size);
void readDataFromFile_(const int data_offsets, const int data_header_size);
void readHeaderFromFile_(const int header_size, std::function<uint16_t()> read);
void readOffsetsFromFile_(const int num_offsets, std::vector<uint16_t>& vec, const int offset_start) const;
void readDataFromFile_(const int data_offsets, const int data_header_size);
🤖 Prompt for AI Agents
In sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp
around lines 61-64, the method readOffsetsFromFile_ is declared noexcept but
calls assertValid_() which can throw; remove the noexcept specifier from the
declaration and update the corresponding definition in the .cpp to also drop
noexcept so thrown errors produce diagnostics instead of causing std::terminate.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (3)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (2)

62-64: Dropping noexcept from readOffsetsFromFile_ avoids terminate-on-assert.

readOffsetsFromFile_ previously conflicted with throwing asserts; removing noexcept here (and in the cpp) fixes that. Thank you for addressing the earlier concern.


65-65: m_header widened to std::vector<uint16_t> fixes V3 truncation.

This resolves the v3 WORD-width header issue and aligns with the new getTrack return type.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (1)

325-333: noexcept removal mirrored here — correct.

readOffsetsFromFile_ asserts and reads from the stream; removing noexcept avoids std::terminate in error paths and matches the header change.

🧹 Nitpick comments (4)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (2)

75-77: Unify count types to avoid needless narrowing.

Members are uint16_t but count_loop_ returns int; while safe given current maxima, returning uint16_t removes narrowing and clarifies intent.

Apply this diff in the header and the corresponding template definition below:

-        template<typename T>
-        int count_loop_(const int num_offs, const std::vector<T>& vec);
+        template<typename T>
+        uint16_t count_loop_(const int offs_start, const std::vector<T>& vec) noexcept;

And in the definition:

-    template<typename T>
-    int ADLFile::count_loop_(const int offs_start, const std::vector<T>& vec)
+    template<typename T>
+    uint16_t ADLFile::count_loop_(const int offs_start, const std::vector<T>& vec) noexcept
     {
-        int tot = 0;
+        uint16_t tot = 0;
         constexpr T max_ = std::numeric_limits<T>::max();
         for (size_t i = 0; i < vec.size(); ++i)
         {
             if (vec[i] >= offs_start && vec[i] < max_) {
                 ++tot;
             }
         }
         return tot;
     }

81-93: Consider std::count_if for clarity (optional).

Minor readability tweak: replace the indexed loop with a standard algorithm.

Apply this diff (and add include at the top of the header if not present):

-        int tot = 0;
-        constexpr T max_ = std::numeric_limits<T>::max();
-        for (size_t i = 0; i < vec.size(); ++i)
-        {
-            if (vec[i] >= offs_start && vec[i] < max_) {
-                ++tot;
-            }
-        }
-        return tot;
+        constexpr T max_ = std::numeric_limits<T>::max();
+        return static_cast<uint16_t>(std::count_if(
+            vec.begin(), vec.end(),
+            [&](const T v){ return v >= offs_start && v < max_; }
+        ));

Additional change outside this range:

#include <algorithm> // at the top of this header
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)

152-166: Doc return type says int but function returns uint16_t.

Align the doc with the function signature.

Apply this diff:

-     * @return int Number of tracks (subsongs).
+     * @return uint16_t Number of tracks (subsongs).

167-181: Doc return type says int but function returns uint16_t.

Same nit for getNumTrackOffsets().

Apply this diff:

-     * @return int Number of track/program offsets.
+     * @return uint16_t Number of track/program offsets.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 614cba6 and 305d18a.

📒 Files selected for processing (2)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (5 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (4)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/IAudioDriver.hpp (1)
  • track (25-25)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/MIDDriver.hpp (2)
  • track (40-40)
  • track (52-52)
sdl2-hyper-sonic-drivers/test/HyperSonicDrivers/drivers/TestIAudioDriver.cpp (1)
  • track (11-11)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.hpp (4)
  • track (56-56)
  • track (62-62)
  • progId (70-70)
  • progId (73-73)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (9)
  • track (44-44)
  • programId (45-45)
  • instrument (46-46)
  • instrument (52-52)
  • progId (47-47)
  • header_size (61-61)
  • num_offsets (62-62)
  • vec (74-74)
  • data_offsets (63-63)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/File.cpp (8)
  • read (44-50)
  • read (44-44)
  • assertValid_ (127-136)
  • assertValid_ (127-127)
  • size (25-28)
  • size (25-25)
  • tell (30-33)
  • tell (30-30)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Build Windows (x64-windows, Release, 2022)
  • GitHub Check: Build Windows (x64-windows, Debug, 2022)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
🔇 Additional comments (6)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.hpp (2)

40-43: Switching count getters to uint16_t is correct and consistent with 16‑bit ADL formats.

Good alignment with the 16‑bit widening across the driver stack; noexcept is appropriate for these trivial accessors.


44-48: Public getters updated to 16‑bit indices — consistent surface.

Signatures for getTrack/get{Track,Instrument,Program}Offset now uniformly take/return uint16_t, matching the 16‑bit on-disk tables and other driver interfaces (IAudioDriver/MIDDriver/ADLDriver). Looks good.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/files/westwood/ADLFile.cpp (4)

94-114: Constructor documentation and responsibilities are clear.

Nice high-level description of parsing steps and the offset normalization to data-relative indices.


135-138: Track count now sourced from version metadata — confirm semantic intent.

m_num_tracks = m_meta_version.num_headers reports the maximum track slots (120 for v1/v2, 250 for v3), not the number of “populated” header entries. If UI or driver logic previously relied on a smaller “actual tracks” count, this broadening may expose empty/unused slots to callers. If intended, fine; otherwise consider counting only valid header entries for v1/v2 (and keeping the metadata value for v3 where header reliability is doubtful).

Would you like me to add an assertion or a helper like hasTrack(i) to gate access to unused slots?


303-324: readHeaderFromFile_ helper is clean and version-agnostic.

Good separation of concerns by passing the width-aware reader via std::function.


335-343: readDataFromFile_ is robust; minor naming fix carried through.

m_dataHeaderSize capture is correct; assertions ensure position and size validity before allocation and read.

Comment on lines +197 to 200
uint16_t ADLFile::getInstrumentOffset(const uint16_t instrument) const
{
return m_instrument_offsets.at(instrument);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Guard against invalid instrument offsets before dereferencing in getInstrument.

Offsets with sentinel 0xFFFF are possible; using them directly as m_data + 0xFFFF causes out-of-bounds reads. Add a simple validity check in getInstrument().

Apply this diff to add bounds checks:

 hardware::opl::OPL2instrument_t ADLFile::getInstrument(const int instrument) const
 {
     using hardware::opl::OPL2instrument_t;
-
-    const uint8_t* instr = m_data.get() + getInstrumentOffset(instrument);
+    const uint16_t off = getInstrumentOffset(static_cast<uint16_t>(instrument));
+    // 11 bytes are read below; ensure the offset is valid and not the sentinel.
+    assertValid_(off != 0xFFFF);
+    assertValid_(static_cast<uint32_t>(off) + 11u <= m_dataSize);
+    const uint8_t* instr = m_data.get() + off;
     return OPL2instrument_t(
         {
             .trem_vibr_1 = instr[0],
             .att_dec_1 = instr[7],
             .sust_rel_1 = instr[9],
             .wave_1 = instr[3],
             .scale_1 = 0, // ???
             .level_1 = instr[5],
             .feedback = instr[2],
             .trem_vibr_2 = instr[1],
             .att_dec_2 = instr[8],
             .sust_rel_2 = instr[10],
             .wave_2 = instr[4],
             .scale_2 = 0, // ???
             .level_2 = instr[6],
             .unused = 0,
             .basenote = 0 /// ???
         }
     );
 }

Optionally, consider returning a default-initialized instrument or throwing a more explicit exception if the offset is invalid.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uint16_t ADLFile::getInstrumentOffset(const uint16_t instrument) const
{
return m_instrument_offsets.at(instrument);
}
hardware::opl::OPL2instrument_t ADLFile::getInstrument(const int instrument) const
{
using hardware::opl::OPL2instrument_t;
const uint16_t off = getInstrumentOffset(static_cast<uint16_t>(instrument));
// 11 bytes are read below; ensure the offset is valid and not the sentinel.
assertValid_(off != 0xFFFF);
assertValid_(static_cast<uint32_t>(off) + 11u <= m_dataSize);
const uint8_t* instr = m_data.get() + off;
return OPL2instrument_t(
{
.trem_vibr_1 = instr[0],
.att_dec_1 = instr[7],
.sust_rel_1 = instr[9],
.wave_1 = instr[3],
.scale_1 = 0, // ???
.level_1 = instr[5],
.feedback = instr[2],
.trem_vibr_2 = instr[1],
.att_dec_2 = instr[8],
.sust_rel_2 = instr[10],
.wave_2 = instr[4],
.scale_2 = 0, // ???
.level_2 = instr[6],
.unused = 0,
.basenote = 0 /// ???
}
);
}

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp (2)

37-39: Bug: std::atomic used without .load(); pointer comparison is invalid.

Both accessors read atomic values without .load(), and comparing std::atomic<T*> directly to a raw pointer is ill-formed. Use explicit loads (with appropriate memory order). Also fix the double space in the parameter type.

Apply:

-            inline bool isAcquired() const noexcept { return m_acquired; }
-            inline bool isOwned(const  drivers::IAudioDriver* owner) const noexcept { return m_owner == owner; }
+            inline bool isAcquired() const noexcept { return m_acquired.load(std::memory_order_acquire); }
+            inline bool isOwned(const drivers::IAudioDriver* owner) const noexcept {
+                return m_owner.load(std::memory_order_acquire) == owner;
+            }

Follow-up:

  • Ensure the corresponding stores in acquire()/release() use memory_order_release (or seq_cst) to pair with these loads.

40-42: Enforce atomic ownership with proper memory ordering

The current implementation uses plain reads of the atomics (m_acquired and m_owner) and a simple pointer comparison in isOwned(), which incurs data races. You must:

• Implement acquire() using compare_exchange_strong(expected, owner, std::memory_order_acq_rel) to claim the device only when it’s free, and return false otherwise.
• Implement release() by first checking ownership via an atomic load, then storing nullptr with std::memory_order_release.
• Replace all direct uses of m_acquired and m_owner == owner with explicit atomic operations and the correct memory orders:

– In IDevice.hpp:
• Line 37:
diff - inline bool isAcquired() const noexcept { return m_acquired; } + inline bool isAcquired() const noexcept { + return m_acquired.load(std::memory_order_acquire); + }
• Line 38:
diff - inline bool isOwned(const drivers::IAudioDriver* owner) const noexcept { return m_owner == owner; } + inline bool isOwned(const drivers::IAudioDriver* owner) const noexcept { + return m_owner.load(std::memory_order_acquire) == owner; + }

– In IDevice.cpp:
• Replace the fast path in acquire():
diff bool IDevice::acquire(drivers::IAudioDriver* owner) { - if (!m_acquired) - { - if (!init()) - return false; - m_acquired = true; - m_owner = owner; - return true; - } - return false; + drivers::IAudioDriver* expected = nullptr; + if (m_owner.compare_exchange_strong(expected, owner, std::memory_order_acq_rel)) { + return true; + } + return false; }
• Implement release() as:
cpp bool IDevice::release(const drivers::IAudioDriver* owner) { if (m_owner.load(std::memory_order_acquire) != owner) { return false; } m_owner.store(nullptr, std::memory_order_release); return true; }

• Review the repository for any remaining plain uses of m_acquired or m_owner without .load()/.store() (e.g. via the earlier rg command) and correct them likewise.

These changes eliminate races between audio drivers and ensure that ownership hand-off has the correct acquire/release semantics.

🧹 Nitpick comments (5)
.gitignore (1)

4-4: Clarify the ignore intent for VS Code settings directory.

'/.vscode' ignores only the top-level path and doesn't signal it's a directory. If that’s intended, you're fine. Otherwise, consider one of these tweaks:

  • Root-only and explicit directory:
-/.vscode
+/.vscode/
  • Ignore at any depth:
-/.vscode
+.vscode/

If you plan to share workspace tasks/launch configs, consider committing specific files like .vscode/extensions.json, tasks.json, and launch.json and ignore the rest.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/audio/converters/IRateConverter.hpp (2)

55-59: Prefer std::fill_n over std::memset for type safety and portability

Zeroing a typed buffer is clearer and avoids relying on object-representation assumptions. std::fill_n also keeps you header-only without needing .

Apply this diff:

-        virtual size_t drain(int16_t* obuf, uint32_t osamp, const uint16_t vol)
+        virtual size_t drain(int16_t* obuf, uint32_t osamp, const uint16_t vol)
         {
-            std::memset(obuf, 0, sizeof(int16_t) * osamp);
+            std::fill_n(obuf, osamp, int16_t{0});
             return osamp;
         }

If you adopt this, you can remove the now-unused include to keep the header’s dependencies minimal.


55-56: Unused parameter in default implementation

vol is not used in the base drain; consider marking it [[maybe_unused]] to avoid warnings while preserving the signature for overrides.

Apply this diff:

-        virtual size_t drain(int16_t* obuf, uint32_t osamp, const uint16_t vol)
+        virtual size_t drain(int16_t* obuf, uint32_t osamp, const uint16_t /*[[maybe_unused]] vol*/)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp (2)

57-60: Avoid duplicated ownership state (m_acquired vs m_owner).

m_acquired duplicates information that can be derived from m_owner != nullptr, creating a risk of divergence. Prefer a single source of truth.

Minimal refactor (header-only):

-            std::atomic<bool> m_acquired = false;
-            std::atomic<drivers::IAudioDriver*> m_owner = nullptr;
+            std::atomic<drivers::IAudioDriver*> m_owner{nullptr};

Then derive the accessor from m_owner:

-            inline bool isAcquired() const noexcept { return m_acquired.load(std::memory_order_acquire); }
+            inline bool isAcquired() const noexcept {
+                return m_owner.load(std::memory_order_acquire) != nullptr;
+            }

If you keep both fields, add a strong invariant in acquire()/release() so they can’t get out of sync.


62-66: Prefer perfect forwarding and static (or implicit) upcast; avoid dynamic_pointer_cast overhead.

dynamic_pointer_cast is for downcasts and adds RTTI cost. Here T should derive from To; use static cast or rely on implicit conversion, and forward constructor args.

Apply:

-template<class T, class To = devices::IDevice, typename... Args>
-std::shared_ptr<To> make_device(Args... args)
-{
-    return std::dynamic_pointer_cast<To>(std::make_shared<T>(args...));
-}
+template<class T, class To = devices::IDevice, typename... Args>
+std::shared_ptr<To> make_device(Args&&... args)
+{
+    static_assert(std::is_base_of_v<To, T>, "T must derive from To");
+    return std::static_pointer_cast<To>(
+        std::make_shared<T>(std::forward<Args>(args)...)
+    );
+}

Note: This requires including <type_traits> and at the top of the file.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 305d18a and 5fcf3f7.

📒 Files selected for processing (5)
  • .gitignore (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/audio/converters/IRateConverter.hpp (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp (1 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp (5 hunks)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/hardware/opl/mame/MameOPL3.cpp (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/hardware/opl/mame/MameOPL3.cpp
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/drivers/westwood/ADLDriver.cpp
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
  • GitHub Check: Build Windows (x64-windows, Release, 2022)
  • GitHub Check: Build Windows (x64-windows, Debug, 2022)
  • GitHub Check: SonarCloud Analysis (x64-windows, Debug)
🔇 Additional comments (2)
sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/audio/converters/IRateConverter.hpp (1)

7-7: Include added for std::memset — LGTM

Adding is appropriate here to make std::memset visible from this header. No API impact.

sdl2-hyper-sonic-drivers/src/HyperSonicDrivers/devices/IDevice.hpp (1)

5-5: Good fix: add for std::atomic members.

This resolves compilation relying on transitive includes and matches the atomic members below.

@Raffaello Raffaello merged commit d59d5b6 into master Aug 23, 2025
8 of 9 checks passed
@Raffaello Raffaello deleted the refactor branch August 23, 2025 15:45
@github-project-automation github-project-automation bot moved this from In progress to Done in Audio Files Aug 23, 2025
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
10.9% Duplication on New Code (required ≤ 3%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ci continus integration example related to how to use the library file I/O file related
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

1 participant