Files
AyeZeeBB 48a9cb8f5b Music: Spotify, YouTube Music and External Audio through the in-game player
Three outside sources can now be played through the game's own music
player. Each has its own row in the music screen, shown only when it is
set up, with tiles that behave like the game's playlists: track name,
cover, pause, next, and pressing a row to start that track.

How the sound gets in
- The game's song decoder is hooked. While one of these tiles is on, the
  audio of the game song behind it (its "carrier") is replaced, block by
  block, with the outside source's audio. The game's volume, fades and
  pausing still apply.
- Rows are songs of our own registered with the game's music registry
  (copies of a real song's asset with their own artist and title), so no
  game song is borrowed or renamed to list a track.

Spotify
- The game is a Spotify Connect device ("ReSkate"), built on librespot
  and linked into ReSkate.dll (SpotifyHelper/, Rust). Audio, track info
  and controls come straight from Spotify. Needs Premium.
- Optional sign-in through the Web API lists the player's playlists and
  Liked Songs as tiles with their tracks.
- librespot can no longer end the process (it does so on a free account
  and on playback errors); only its thread is stopped.

YouTube Music
- Videos and playlists added by link. yt-dlp is fetched on request into
  <Local AppData>\ReSkate\bin and checked against its published sums;
  decoding is done in the DLL, so no ffmpeg.
- Private and age-restricted songs: cookies.txt or a browser's sign-in,
  with QuickJS fetched as the JavaScript runtime yt-dlp then needs
  (Node.js is used instead when installed).

External Audio
- Any app's sound, captured per process from Windows, with the app's own
  output turned down while it plays through the game.

Setup page
- "spotify setup" opens http://127.0.0.1:8421/: device status, sign-ins,
  YouTube links and tools, the external app, loudness. Loopback only;
  changes are accepted from its own page only.

Also
- Speakers in the world play the tile's audio; grabbing one keeps the
  player on their tile.
- Playlists added while the game runs are sent to the music screen live.
- MUSIC-SETUP.md: setup guide for players.

Building the Spotify device and the YouTube decoder needs cargo; without
it ReSkate.dll builds without them.
2026-10-12 03:16:36 -05:00

40 lines
2.6 KiB
C++

#pragma once
#include "Engine/Game/Build/fingerprint.h"
#include <cstddef>
#include <cstdint>
// The game's sound engine (EA::Audio::Core): the plugin that plays a streamed sound file
// ("SndPlayer1"), which is what the music player's songs come out of.
namespace dingosdk::game::build::v20260929::audio_core {
// SndPlayer1's processing routine: (player, mixer) -> nonzero when it produced a block.
inline constexpr Fingerprint stream_player_process{0x13de2b0, {0x40,0x53,0x41,0x55,0x48,0x83,0xec,0x58,0x80,0xb9,0x24,0x03,0x00,0x00,0x00,0x4c,0x8d,0x91,0x23,0x03,0x00,0x00,0x4c,0x89,0x54,0x24,0x20,0x4c,0x8b,0xea,0x48,0x8b}};
// SndPlayer1's command routine: (player, command, arguments). Command 4 asks it to play a
// file, whose name is the string at arguments + 0x18.
inline constexpr Fingerprint stream_player_command{0x13e2190, {0x40,0x53,0x55,0x56,0x57,0x41,0x56,0x48,0x83,0xec,0x20,0x4c,0x8b,0x71,0x18,0x49,0x8b,0xf0,0x48,0x8b,0xf9,0x83,0xfa,0x04,0x0f,0x85,0x13,0x01,0x00,0x00,0x48,0x8b}};
inline constexpr int command_play = 4;
inline constexpr std::size_t command_play_name = 0x18;
// The player.
inline constexpr std::size_t player_block_frames = 0x318; // u16: the most frames of one block
inline constexpr std::size_t player_voices = 0x31c; // u16: where its voices begin
inline constexpr std::size_t player_voice = 0x321; // u8: the voice being played
inline constexpr std::size_t player_voice_count = 0x322; // u8
inline constexpr std::size_t voice_size = 0x168;
inline constexpr std::size_t voice_rate = 0x14; // float, Hz
inline constexpr std::size_t voice_frames = 0x18; // u32: the length of the file
inline constexpr std::size_t voice_state = 0x2e; // u8: 0 and 5 are not playing
inline constexpr std::size_t voice_channels = 0x2f; // u8
// The mixer. A block is written to the buffer at mixer_write, and the two are then exchanged:
// after the routine, what it wrote is the buffer at mixer_written.
inline constexpr std::size_t mixer_written = 0x10090;
inline constexpr std::size_t mixer_write = 0x10098;
inline constexpr std::size_t mixer_frames = 0x100bc; // u32: frames of the block
inline constexpr std::size_t mixer_rate = 0x100c0; // float
inline constexpr std::size_t mixer_channels = 0x100c8; // u8
// A buffer: 32-bit float samples, a channel after another.
inline constexpr std::size_t buffer_data = 0x8; // channel pointers, or the samples
inline constexpr std::size_t buffer_stride = 0x12; // u16: samples a channel, when together
inline constexpr std::size_t buffer_together = 0x15; // u8: nonzero when buffer_data is the samples
}