Skip to content

Commit 8411ead

Browse files
committed
v0.7.1: handle mono & warps
- if the next "beat" happens within the boundary of the current fingerprint, skip it - also record the indices of beats that are actually used so folks using the plots as lookup against the beatmap aren't completely destroyed by this change - convert stereo to mono for analysis: use max(L, R) instead of L channel only
1 parent b35a9d9 commit 8411ead

File tree

1 file changed

+24
-11
lines changed

1 file changed

+24
-11
lines changed

nine-or-null/nine_or_null/__init__.py

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
_VERSION = '0.7.0'
1+
_VERSION = '0.7.1'
22

33
from collections.abc import Container
44
import csv
@@ -226,6 +226,7 @@ def plot_fingerprint(fingerprint, target_axes, **kwargs):
226226
frequencies_kHz = fingerprint['frequencies']
227227
acc = fingerprint['freq_domain']
228228
digest = fingerprint['beat_digest']
229+
beat_indices = fingerprint['beat_indices']
229230
sync_bias = fingerprint['bias_result']
230231
post_kernel_flat = fingerprint['convolution']
231232
post_kernel = fingerprint['post_kernel']
@@ -236,7 +237,8 @@ def plot_fingerprint(fingerprint, target_axes, **kwargs):
236237
hide_yticks = kwargs.get('hide_yticks', False)
237238

238239
edge_discard = 5 # TODO: pull in from calling function I guess
239-
digest_axis = np.arange(digest.shape[0])
240+
if beat_indices is None:
241+
beat_indices = np.arange(digest.shape[0])
240242

241243
time_ticks = np.hstack((
242244
np.arange(0, times_ms[ 0], -10),
@@ -261,8 +263,8 @@ def plot_fingerprint(fingerprint, target_axes, **kwargs):
261263
post_kernel_flat[edge_discard:-edge_discard].max()
262264
),
263265
(
264-
digest.shape[0] * 0.2,
265-
digest.shape[0] * 0.8
266+
beat_indices.min() * 0.9 + beat_indices.max() * 0.1,
267+
beat_indices.min() * 0.1 + beat_indices.max() * 0.9
266268
))
267269

268270
# Accumulator in frequency domain
@@ -282,12 +284,12 @@ def plot_fingerprint(fingerprint, target_axes, **kwargs):
282284
# Digest in beat domain
283285
ax = target_axes[1]
284286
ax.clear()
285-
pcm = ax.pcolormesh(times_ms, digest_axis, digest)
287+
pcm = ax.pcolormesh(times_ms, beat_indices, digest)
286288
pcm.set_clim(np.percentile(digest[:], 10), np.percentile(digest[:], 90))
287289
ax.set_ylabel('Beat Index')
288290
ax.set_xlabel('Time [msec]')
289291
ax.plot(times_ms + magic_offset_ms, post_kernel_over_beat, 'w-')
290-
ax.plot(beatindex_line, digest_axis, 'r-')
292+
ax.plot(beatindex_line, beat_indices, 'r-')
291293
ax.set_xticks(time_ticks)
292294
if hide_yticks:
293295
ax.set_yticks([])
@@ -303,10 +305,10 @@ def plot_fingerprint(fingerprint, target_axes, **kwargs):
303305
ax.plot(times_ms + magic_offset_ms, post_kernel_over_freq, 'w-')
304306
ax.plot(frequency_line, frequencies_kHz, 'r-')
305307
else: # kernel_target == KernelTarget.DIGEST
306-
pcm = ax.pcolormesh(times_ms, digest_axis, post_kernel)
308+
pcm = ax.pcolormesh(times_ms, beat_indices, post_kernel)
307309
ax.set_ylabel('Beat Index')
308310
ax.plot(times_ms + magic_offset_ms, post_kernel_over_beat, 'w-')
309-
ax.plot(beatindex_line, digest_axis, 'r-')
311+
ax.plot(beatindex_line, beat_indices, 'r-')
310312
pcm.set_clim(np.percentile(post_kernel[:], 3), np.percentile(post_kernel[:], 97))
311313
ax.set_xlabel('Time [msec]')
312314
ax.set_xticks(time_ticks)
@@ -326,6 +328,7 @@ def get_full_title(base_simfile):
326328
def check_sync_bias(simfile_dir, base_simfile, chart_index=None, report_path=None, save_plots=True, show_intermediate_plots=False, **kwargs):
327329
fingerprint = {
328330
'beat_digest': None, # Beat digest fingerprint (beat index vs. time)
331+
'beat_indices': None, # Beat indices that contributed to the digest
329332
'freq_domain': None, # Accumulation in frequency domain (frequency vs. time)
330333
'post_kernel': None, # Post-kernel
331334
'bias_result': None, # Scalar value result of the bias analysis
@@ -364,7 +367,9 @@ def check_sync_bias(simfile_dir, base_simfile, chart_index=None, report_path=Non
364367
# Account for stereo audio and normalize
365368
# https://stackoverflow.com/questions/53633177/how-to-read-a-mp3-audio-file-into-a-numpy-array-save-a-numpy-array-to-mp3
366369
if audio.channels == 2:
367-
audio_data = audio_data.reshape((-1, 2))
370+
#audio_data = audio_data.reshape((-1, 2)).sum(1) * 0.5 # Reshape to stereo and average the two channels
371+
#audio_data = audio_data.reshape((-1, 2))[:, 0].flatten() # Pull mono only
372+
audio_data = audio_data.reshape((-1, 2)).max(1) # Reshape to stereo and average the two channels
368373
audio_data = audio_data / 2**15
369374

370375
###################################################################
@@ -400,7 +405,7 @@ def check_sync_bias(simfile_dir, base_simfile, chart_index=None, report_path=Non
400405
if full_spectrogram:
401406
# print(f'audio: {audio_data.shape}, nperseg: {nperseg}, noverlap: {noverlap}, actual_step: {actual_step}, n_spectral_taps: {n_time_taps}, n_freq_taps: {n_freq_taps}')
402407
frequencies, times, spectrogram = signal.spectrogram(
403-
audio_data[:, 0], # Mono left channel please
408+
audio_data[:],
404409
fs=audio.frame_rate,
405410
window='hann',
406411
nperseg=nperseg,
@@ -433,6 +438,8 @@ def check_sync_bias(simfile_dir, base_simfile, chart_index=None, report_path=Non
433438
# For each beat in the song that has a full
434439
# fingerprint's width of surrounding audio data:
435440
b = 0
441+
beat_indices = []
442+
t_last = -np.inf
436443
while True:
437444
t = engine.time_at(b)
438445
b += 1
@@ -442,6 +449,10 @@ def check_sync_bias(simfile_dir, base_simfile, chart_index=None, report_path=Non
442449
if (t > audio.duration_seconds):
443450
# Too late
444451
break
452+
if (t - t_last < fingerprint_ms * 1e-3):
453+
# Too soon
454+
continue
455+
t_last = t
445456

446457
# Because the spectrogram doesn't "start" until a full window is in view,
447458
# it has an inherent offset that amounts to half a window.
@@ -467,7 +478,7 @@ def check_sync_bias(simfile_dir, base_simfile, chart_index=None, report_path=Non
467478
# print(f't_sample: {t_sample_s}:{t_sample_f}')
468479

469480
frequencies, times, spectrogram = signal.spectrogram(
470-
audio_data[t_sample_s:t_sample_f, 0], # Mono left channel please
481+
audio_data[t_sample_s:t_sample_f],
471482
fs=audio.frame_rate,
472483
window='hann',
473484
nperseg=nperseg,
@@ -486,6 +497,7 @@ def check_sync_bias(simfile_dir, base_simfile, chart_index=None, report_path=Non
486497
# Accumulate, and add to digest
487498
acc += spfilt[:n_freq_taps, :]
488499
digest = np.vstack([digest, np.sum(spfilt, axis=0)])
500+
beat_indices.append(b-1)
489501

490502

491503
###################################################################
@@ -543,6 +555,7 @@ def check_sync_bias(simfile_dir, base_simfile, chart_index=None, report_path=Non
543555
chart_tag = ' ' + slot_abbreviation(chart['STEPSTYPE'], chart['DIFFICULTY'], chart_index=chart_index, paradigm=guess_paradigm(sync_bias_ms, **kwargs))
544556
fingerprint['sample_rate'] = audio.frame_rate
545557
fingerprint['beat_digest'] = digest
558+
fingerprint['beat_indices'] = np.array(beat_indices)
546559
fingerprint['freq_domain'] = acc
547560
fingerprint['post_kernel'] = post_kernel
548561
fingerprint['convolution'] = post_kernel_flat

0 commit comments

Comments
 (0)