FightNome 如何讓錄製的語音提示與拳擊訓練 App 節拍同步
Lining up recorded voice callouts with a beat in a boxing training app
FightNome 説明其拳擊訓練 App 如何將錄製的語音提示對齊節拍,讓提示詞落在節拍上。訓練速度介乎 74 至 128 BPM,音樂以 90 BPM 錄製,再按各段速度調整;每個提示會在節拍前 40 至 60 ms 播放。App 以單字錄音組合提示,並提供預設連擊及隨機反應模式。
I work on FightNome. This post is about a feature of our own app.
FightNome calls strikes out loud while you shadowbox or work pads. You hear "jab", you throw a jab. For that to feel right, the word has to land on the beat itself. Here's how we think about that problem, in plain terms.
The beat grid
Everything hangs off the tempo. One beat lasts 60000 / BPM milliseconds. Our workouts run from 74 to 128 BPM, so the spacing between beats changes a lot:
| BPM | Beat length |
|---|---|
| 74 | about 811 ms |
| 90 | about 667 ms |
| 128 | about 469 ms |
The music bed is a single track recorded at 90 BPM. It gets time-stretched to whatever tempo a section uses, so the grid and the music always agree.
One word, one file
Every callout is a separate recording of a person saying one word: jab, cross, hook, upper, body, teep, slip, roll and so on. There are 42 of them in the iOS project at the moment. It's a real voice, not text to speech.
One word per file keeps the model simple. A combination is a list of words placed on beats, so changing a combo means changing the list and nothing else.
Starting early
A spoken word has an onset, and the part you recognize it by arrives slightly after the file starts playing. A file that starts exactly on the beat would sound late. So each callout starts 40 to 60 ms before its beat, and the word lands on the tick.
In pseudocode (a sketch of the idea, not our source):
beat_ms = 60000 / bpm
play_at = beat_index * beat_ms - head_start_ms # 40 to 60 ms
That head start is small next to a beat. At 74 BPM it's between 5 and 7 percent of a beat. At 128 BPM it's between 9 and 13 percent.
Two ways to place the words
In combo mode the words repeat a sequence you built, strike by strike. Placement is predictable, and the editor lets a strike take half of its window ("Quick Strike x2"). At 128 BPM half a beat is about 234 ms, a short slot for any word.
In reaction mode the app picks words at random, from 6 to 45 calls a minute. At 45 calls a minute you get one call every 1.33 seconds, which at 90 BPM is every two beats. At 6 calls a minute it's one every ten seconds. The same grid rules apply, and only the choice of word changes.
What we're still unsure about
Output delay differs between phone speakers, wired earbuds and Bluetooth headphones, and any fixed head start is a compromise across those. If you've dealt with output latency in a simple way, I'd like to hear how.
If you want to try it
FightNome is a paid app: FightNome Pro is $8.99 a month or $49.99 a year (App Store US prices), and the yearly plan has a 3-day trial. There's no free tier. It runs on iOS 26 and up and on Google Play. Search "FightNome" in either store, or go to https://fightnome.app.
I used AI assistance to draft this post and edited it myself.
來源:DEV Community · dev.to
