Skip to content

Commit ce5e641

Browse files
committed
add quick n dirty version of counter, make consecutive hits configurable
1 parent 67e5615 commit ce5e641

4 files changed

Lines changed: 31 additions & 12 deletions

File tree

index.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ <h4>Blastbeat Detection Visualizer</h4>
5555
<span id="audioPath"></span> <span id="snareFreq"></span> <span id="bassdrumFreq"></span>
5656
</div>
5757
<div id="controls">
58+
<div id="seenCount">Blastbeat count: 0</div>
5859
<div id="currentTime">0:00:000</div>
5960
<div id="mobile-controls">
6061
<button id="playPauseBtn">Play/Pause</button>
@@ -90,6 +91,7 @@ <h4>Blastbeat Detection Visualizer</h4>
9091
document.getElementById('waveform').innerHTML = ""
9192
document.getElementById('spectrogram').innerHTML = ""
9293
document.getElementById('currentTime').textContent = "0:00:000"
94+
document.getElementById('seenCount').textContent = "Blastbeat count: 0"
9395
}
9496

9597
const lw = document.getElementById('loadingWaveform')
@@ -123,6 +125,8 @@ <h4>Blastbeat Detection Visualizer</h4>
123125

124126
wavesurfer.load(audioFile)
125127

128+
let seenCount = 0
129+
126130
wavesurfer.on('ready', () => {
127131
lw.style.display = 'none'
128132
document.getElementById('explainer').style.display = 'none'
@@ -145,12 +149,18 @@ <h4>Blastbeat Detection Visualizer</h4>
145149
const ms = Math.floor((t % 1) * 1000)
146150
document.getElementById('currentTime').textContent =
147151
`${m}:${s.toString().padStart(2, '0')}:${ms.toString().padStart(3, '0')}`
152+
const newCount = intervals.filter(b => t > b.start_time).length
153+
if (newCount !== seenCount) {
154+
seenCount = newCount
155+
document.getElementById('seenCount').textContent = `Blastbeat count: ${seenCount}`
156+
}
148157
})
149158

150159
document.getElementById('waveform').addEventListener('wheel', handleWheelZoom, { passive: false })
151160
document.getElementById('spectrogram').addEventListener('wheel', handleWheelZoom, { passive: false })
152161
}
153162

163+
154164
async function loadZip(zipFile) {
155165
const zip = await JSZip.loadAsync(zipFile)
156166
const jsonFile = Object.keys(zip.files).find(n => n.endsWith('.json'))

pipeline.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ def parse_range(row, start, end):
1818
)
1919

2020

21+
def parse_int(row, key):
22+
return int(row[key]) if row.get(key) else None
23+
24+
2125
def load_rows(csv_path):
2226
with open(csv_path, newline="") as f:
2327
return [
@@ -64,6 +68,7 @@ def load_rows(csv_path):
6468
"snare_range": parse_range(
6569
row, "snare_drum_range_start", "snare_drum_range_end"
6670
),
71+
"min_consecutive_hits": parse_int(row, "min_consecutive_hits"),
6772
}.items()
6873
if v is not None
6974
}

processing.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,24 @@ def get_sections_labeled_by_percussion_content_from_audio(
103103
return results
104104

105105

106-
def identify_blastbeats(sections: list[LabeledSection]) -> list[tuple[int, int]]:
107-
min_hits = 8 # primitive approach: 4 snares+bass in a series at least
108-
start_idx = 0
106+
def identify_blastbeats(
107+
sections: list[LabeledSection], min_hits
108+
) -> list[tuple[int, int]]:
109+
blastbeat_start_idx = 0
109110
hits = 0
110111
results = []
111112

112-
for i, s in enumerate(sections):
113-
if s.snare_present and s.bass_drum_present:
113+
# Caveman approach: consider it as blast beat if a given number of consecutive labeled sections contain snare & bassdrum
114+
for i, section in enumerate(sections):
115+
if section.snare_present and section.bass_drum_present:
114116
if hits == 0:
115-
start_idx = i
117+
blastbeat_start_idx = i
116118
hits += 1
117119
else:
118120
if hits >= min_hits:
119-
results.append((sections[start_idx].start_idx, s.start_idx))
121+
results.append(
122+
(sections[blastbeat_start_idx].start_idx, section.start_idx)
123+
)
120124
hits = 0
121125

122126
return results
@@ -177,6 +181,7 @@ def process_song(
177181
step_size_in_seconds=0.15,
178182
bass_drum_range=(10, 100),
179183
snare_range=(170, 600),
184+
min_consecutive_hits=8,
180185
):
181186
print("Separating drum track...")
182187
(time, audio_data, sample_rate), drumtrack_path = extract_drums(file_path)
@@ -203,7 +208,7 @@ def process_song(
203208
peak_detection_band_width,
204209
peak_detection_min_area_threshold,
205210
)
206-
blastbeat_intervals = identify_blastbeats(labeled_sections)
211+
blastbeat_intervals = identify_blastbeats(labeled_sections, min_consecutive_hits)
207212

208213
print("Exporting result...")
209214
save_result(
@@ -215,7 +220,7 @@ def process_song(
215220
import argparse
216221
import webbrowser
217222

218-
OPEN_BROWSER_AFTER_PROCESSING = False
223+
OPEN_BROWSER_AFTER_PROCESSING = True
219224

220225
parser = argparse.ArgumentParser()
221226
parser.add_argument("--file", type=str)
@@ -244,6 +249,5 @@ def process_song(
244249
webbrowser.open(f"file://{Path(__file__).parent.resolve()}/index.html")
245250

246251
# TODO:
247-
# - experiment with compression of drum track before fft?
248252
# - investigate false positives in all songs & tune parameters
249253
# - make min consecutive hits configurable

test_processing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def test_identify_blasts_1():
1515
LabeledSection(10, 11, False, False),
1616
]
1717
expected = [(2, 10)]
18-
actual = identify_blastbeats(input)
18+
actual = identify_blastbeats(input, 8)
1919

2020
assert actual == expected
2121

@@ -32,6 +32,6 @@ def test_identify_blasts_2():
3232
LabeledSection(8, 9, True, True),
3333
]
3434
expected = []
35-
actual = identify_blastbeats(input)
35+
actual = identify_blastbeats(input, 8)
3636

3737
assert actual == expected

0 commit comments

Comments
 (0)