| commit | 82381d34e8166050531919b070131117bb1e13ee |
|---|
Pulsefield is the app side of a "play the music around you" rhythm game. The intended experience is not to open a local beatmap player, choose a file, and press play. The intended experience is that music is already playing in the room, Pulsefield listens through the microphone, locks onto the track and playback position, asks Pulsefield/Pulsefield-model for a real-time 4-key mania chart, and lets the player follow the surrounding music as the chart streams in.
This repository contains the SwiftUI/Xcode client: microphone capture, recognition, local reference resolution, ambient sync, gameplay rendering, and the websocket client that talks to the model endpoint. Pulsefield/Pulsefield-model is the companion inference side that produces hit-object tokens for the live chart stream.
The core loop is:
- Music plays outside the app.
- Pulsefield captures a short microphone window.
- Recognition identifies the track.
- The app resolves that track to a local reference audio asset.
- Ambient sync aligns the live microphone stream to the reference playback position.
- The app sends the audio path and locked reference time to
Pulsefield/Pulsefield-model. - The model streams hit-object tokens back over websocket.
- Pulsefield renders a 4-key mania chart that stays aligned with the music happening around the player.
The local library and manual beatmap loading paths exist to support and verify this loop. They are not the main product pitch.
PulsefieldMacandPulsefieldiOSboth launchPulsefieldWorkbenchView.- macOS debug builds expose the real end-to-end prototype through
Recognition Sync Flow, available from the toolbar orDebug > Open Recognition Sync Flow. - The recognition sync flow connects microphone capture, ACRCloud recognition, local reference resolution, ambient sync, and the model websocket at
ws://localhost:8765. - The workbench also has
LibraryandPlaytabs, but those are support surfaces for indexing reference audio and validating gameplay rendering. - Shared UI lives in
PulsefieldUI; domain models and services live inPulsefieldCore.
- Request real microphone permission through AVFoundation.
- Capture growing microphone clips and submit them to the ACRCloud Identification API.
- Normalize ACRCloud matches into Pulsefield recognition snapshots and canonical track queries.
- Resolve the recognized track against the local library and require confirmation when the match is ambiguous.
- Build an ambient sync reference index from the selected local asset.
- Run live microphone feature extraction and Sonalign alignment until a final lock is reached.
- Send the selected audio path and locked reference time to the configured inference websocket.
- Receive and buffer hit-object tokens from the inference endpoint for streaming readiness diagnostics.
- Render a dark 4-lane mania play scene with live lane input, judgement feedback, combo, accuracy, score, and chart metadata.
- Support keyboard input on macOS and lane touch input on touch platforms.
- Configure scroll speed, audio and visual offsets, judge difficulty, and macOS key bindings.
- Stream hit objects through the gameplay model rather than treating the chart as static UI state.
- Index local reference audio in SQLite so recognized public tracks can be matched to playable local assets.
- Extract audio duration and common metadata with AVFoundation.
- Track file identity with SHA-256, file size, bookmarks, and index status.
- Resolve a recognized or manually entered canonical track to local assets using ISRC, title, artist, album, duration, and fuzzy filename evidence.
- Load
.osubeatmaps and separate audio files as a renderer/gameplay validation path. - Record ambient sync fixtures from indexed local assets on macOS debug builds.
PulsefieldACRCloudDebugCLIcan capture microphone audio or clip fixture audio, call the official ACRCloud filescan CLI, and print the normalized result.RecognitionAppModelandRecognitionDashboardViewremain as the mock recognition/beatmap handoff seam used by tests and previews.CascadingRecognitionServicemodels the provider fallback contract, but the production ShazamKit provider is not implemented.BeatmapGenerationProvidingexists for the older reserved beatmap handoff, but the live product direction is the model websocket token stream.
Apps/PulsefieldMac- macOS app entry point and debug recognition window wiring.Apps/PulsefieldiOS- iOS app entry point.Sources/PulsefieldCore- domain models, local library services, recognition clients, ambient sync engine, mania4k session model, and inference websocket client.Sources/PulsefieldUI- SwiftUI workbench, library, recognition, ambient fixture, and mania4k play surfaces.Tests/PulsefieldCoreTests- focused unit tests for core model transitions, parsing, local resolution, ambient sync, inference tokens, and gameplay behavior.Tools/ACRCloudDebugCLI- debug-only command line recognition helper.docs- design notes, current-status writeups, and execution plans.
- Xcode with a Swift 6 compiler.
- XcodeGen
2.45.0or newer. - macOS 14.0 and iOS 17.0 are the configured deployment targets.
- Optional for recognition debugging: ACRCloud credentials and, for filescan CLI flows, the
acrcloudcommand. - Optional for the full live-chart loop: the companion
Pulsefield/Pulsefield-modelwebsocket service.
xcodegen generate
open Pulsefield.xcodeprojBuild the PulsefieldMac scheme for macOS or the PulsefieldiOS scheme for an iOS simulator.
xcodebuild -scheme PulsefieldMac -destination 'platform=macOS' test
xcodebuild -scheme PulsefieldiOS -destination 'generic/platform=iOS Simulator' build CODE_SIGNING_ALLOWED=NO
xcodebuild -scheme PulsefieldACRCloudDebugCLI -destination 'platform=macOS' buildPulsefieldCore consumes Sonalign 0.1.0
as an exact SwiftPM dependency in project.yml. Xcode downloads the released Apple
XCFramework and verifies its pinned checksum. Building the app needs neither a local
Sonalign checkout nor a Rust toolchain.
AmbientSyncSessionEngine uses Sonalign for the default V2 live and fixture replay
paths. Capture, decoding, resampling, PCEN extraction and recorded-time propagation
remain in Pulsefield. Each native session is serialized on its processing worker;
processing errors invalidate the session and propagate to the existing failure path.
The Swift engine remains the comparison oracle and the backend for V1 or custom
configurations that Sonalign's current Swift API cannot express. Diagnostics absent
from the released API remain unavailable.
The optional parity tool installs the released Rust CLI from crates.io and compares it with the Swift oracle using identical feature windows:
cargo install sonalign --version 0.1.0 --locked --root .build/sonalign
python3 Tools/check_sonalign_parity.py --suites syntheticOmit --suites synthetic to include the local private fixture corpus. CI runs the
synthetic comparison. To upgrade Sonalign, update the exact version in project.yml,
the CLI version in the parity tool/workflow, regenerate the Xcode project and resolve
packages, then run the app tests and parity comparison before committing the new pin.
The macOS debug recognition window loads values from the process environment or from the first .env file it finds in the current working directory or project root.
For the live Identification API flow, set:
ACRCLOUD_IDENTIFICATION_HOST=identify-ap-southeast-1.acrcloud.com
ACRCLOUD_ACCESS_KEY=...
ACRCLOUD_ACCESS_SECRET=...For filescan CLI debugging, set:
ACRCLOUD_ACCESS_TOKEN=...
ACRCLOUD_CLI=acrcloud
ACRCLOUD_FILESCAN_REGION=eu-west-1
ACRCLOUD_FILESCAN_BUCKETS=23
ACRCLOUD_FILESCAN_ENGINE=1
ACRCLOUD_FILESCAN_AUDIO_TYPE=recordedThe filescan helper expects the official CLI to be installed and available on PATH:
pip install acrcloud-cliInferenceEndpointWebSocketClient connects to ws://localhost:8765 by default. That endpoint is expected to be served by the companion Pulsefield/Pulsefield-model process during the live-chart prototype. The app imports the local PulsefieldProtocol Swift package and sends binary protobuf Envelope websocket frames:
- a
readypayload before endpoint preparation - an
audiopayload with the selected local audio path, sync source, difficulty, and route - a
reference_timepayload after ambient sync reaches a final lock - a
stop_sessionpayload when the debug session is stopped
Incoming hit_object_token and end_of_stream envelope payloads are decoded through the shared Swift package, parsed into mania4k hit objects, and buffered until the stream has enough ready-window coverage for rendering diagnostics. JSON is reserved for endpoint debugging and logs, not the reference transport. The generated token stream is the intended chart source for "play the music around you"; it is not yet wired into the normal Play tab as the primary user-facing play path.
- ShazamKit is still design-only and not wired into the app target.
- The generated token stream from
Pulsefield/Pulsefield-modelis not yet wired into the mainPlaytab as the default gameplay source. - The normal
Playtab still plays imported.osucharts for renderer validation, not as the main product experience. - The recognition sync window is debug-only and expects developer-supplied ACRCloud credentials.
- The UI is prototype-grade and does not include account sync, packaging, onboarding, or app-store signing work.