diff --git a/bpm_finder.py b/bpm_finder.py index bdd524f..c80ad51 100644 --- a/bpm_finder.py +++ b/bpm_finder.py @@ -45,84 +45,78 @@ def argmax(signal): return max_index -def findBPMinRange(corrected_autocorrelation, minBPM, maxBPM, duration, fineAdjustRecursion=3): +def find_bpm_in_range(corrected_autocorrelation, min_bpm, max_bpm, duration, fine_adjust_recursion=3): length = len(corrected_autocorrelation) - firstOffset = int(60/maxBPM/duration*length) - firstIndex = length//2 + firstOffset - lastOffset = int(60/minBPM/duration*length) - lastIndex = length//2 + lastOffset + first_offset = int(60/max_bpm/duration*length) + first_index = length//2 + first_offset + last_offset = int(60/min_bpm/duration*length) + last_index = length//2 + last_offset - interestingPart = corrected_autocorrelation[firstIndex:lastIndex] + interesting_part = corrected_autocorrelation[first_index:last_index] n = 1 # uneven integer, not choosing one leads to worse results - ddinterestingPart = np.concatenate([np.zeros(n), np.diff(np.diff(interestingPart, n), n), np.zeros(n)])/(n**2) # dd is the second derivative - - indexBeatLength = argmax(interestingPart) + firstOffset - indexBeatLength_dd = argmax(-ddinterestingPart) + firstOffset - BPM = length/indexBeatLength*60/duration - BPM_dd = length/indexBeatLength_dd*60/duration - - def temposAreSimilar(a, b): - if abs(a - b) <= 1: - return True - if abs(2 * a - b) <= 1: - return True - if abs(a - 2 * b) <= 1: - return True - return False + dd_interesting_part = np.concatenate([np.zeros(n), np.diff(np.diff(interesting_part, n), n), np.zeros(n)])/(n**2) # dd is the second derivative + + index_beat_length = argmax(interesting_part) + first_offset + index_beat_length_dd = argmax(-dd_interesting_part) + first_offset + bpm = length/index_beat_length*60/duration + bpm_dd = length/index_beat_length_dd*60/duration + + def tempos_are_similar(a, b): + return abs(a - b) <= 1 or abs(2 * a - b) <= 1 or abs(a - 2 * b) <= 1 if config.DEBUG_BPM_FINDER: - xRange = range(firstOffset, lastOffset) - plt.plot(xRange, interestingPart) - plt.plot(xRange, -ddinterestingPart) - xRange = range(lastOffset) - plt.plot(xRange, corrected_autocorrelation[length//2: length//2 + lastOffset]) - plt.scatter(indexBeatLength, interestingPart[indexBeatLength - firstOffset], label=str(BPM) + ' bpm') - plt.scatter(indexBeatLength_dd, -ddinterestingPart[indexBeatLength_dd - firstOffset], label=str(BPM_dd) + ' bpm') + x_range = range(first_offset, last_offset) + plt.plot(x_range, interesting_part) + plt.plot(x_range, -dd_interesting_part) + x_range = range(last_offset) + plt.plot(x_range, corrected_autocorrelation[length//2: length//2 + last_offset]) + plt.scatter(index_beat_length, interesting_part[index_beat_length - first_offset], label=str(bpm) + ' bpm') + plt.scatter(index_beat_length_dd, -dd_interesting_part[index_beat_length_dd - first_offset], label=str(bpm_dd) + ' bpm') plt.legend() plt.show() - if not temposAreSimilar(indexBeatLength, indexBeatLength_dd): + if not tempos_are_similar(index_beat_length, index_beat_length_dd): # Compare the quality of the findings by comparing the autocorrelation for 2, 3 and 4 beats beats = np.array([2, 3, 4]) scores = np.zeros(len(beats)) scores_dd = np.zeros(len(beats)) for i, n in enumerate(beats): - scores[i] = corrected_autocorrelation[length//2 + n*indexBeatLength] - scores_dd[i] = corrected_autocorrelation[length//2 + n*indexBeatLength_dd] + scores[i] = corrected_autocorrelation[length//2 + n*index_beat_length] + scores_dd[i] = corrected_autocorrelation[length//2 + n*index_beat_length_dd] if sum(scores) > sum(scores_dd): - rough_BPM = BPM + rough_bpm = bpm else: - rough_BPM = BPM_dd + rough_bpm = bpm_dd if config.DEBUG_BPM_FINDER: print(' Non-trivial rhythm') - print(' index:', indexBeatLength, indexBeatLength_dd) - print(' bpm:', BPM, BPM_dd) - - xRange = range(lastOffset*2) - plt.plot(xRange, corrected_autocorrelation[length//2: length//2 + lastOffset*2]) - plt.scatter(indexBeatLength, interestingPart[indexBeatLength - firstOffset], label=str(BPM) + ' bpm') - plt.scatter(indexBeatLength_dd, -ddinterestingPart[indexBeatLength_dd - firstOffset], label=str(BPM_dd) + ' bpm') - plt.scatter(beats*indexBeatLength, scores, label=str(BPM) + ' bpm beats') - plt.scatter(beats*indexBeatLength_dd, scores_dd, label=str(BPM_dd) + ' bpm beats') + print(' index:', index_beat_length, index_beat_length_dd) + print(' bpm:', bpm, bpm_dd) + + x_range = range(last_offset*2) + plt.plot(x_range, corrected_autocorrelation[length//2: length//2 + last_offset*2]) + plt.scatter(index_beat_length, interesting_part[index_beat_length - first_offset], label=str(bpm) + ' bpm') + plt.scatter(index_beat_length_dd, -dd_interesting_part[index_beat_length_dd - first_offset], label=str(bpm_dd) + ' bpm') + plt.scatter(beats*index_beat_length, scores, label=str(bpm) + ' bpm beats') + plt.scatter(beats*index_beat_length_dd, scores_dd, label=str(bpm_dd) + ' bpm beats') plt.legend() plt.show() else: - rough_BPM = BPM + rough_bpm = bpm if config.DEBUG_BPM_FINDER: - print(' Tempos match: ', BPM, BPM_dd) + print(' Tempos match: ', bpm, bpm_dd) - if fineAdjustRecursion and (60/rough_BPM < duration/5): + if fine_adjust_recursion and (60/rough_bpm < duration/5): variation = 0.08 if config.DEBUG_BPM_FINDER: - print('', fineAdjustRecursion, 'BPM-Range', rough_BPM*0.5*(1-variation), rough_BPM*0.5*(1+variation)) - return 2*findBPMinRange(corrected_autocorrelation, rough_BPM*0.5*0.95, rough_BPM*0.5*1.05, duration, fineAdjustRecursion-1) + print('', fine_adjust_recursion, 'bpm-Range', rough_bpm*0.5*(1-variation), rough_bpm*0.5*(1+variation)) + return 2*find_bpm_in_range(corrected_autocorrelation, rough_bpm*0.5*0.95, rough_bpm*0.5*1.05, duration, fine_adjust_recursion-1) else: - return rough_BPM + return rough_bpm -def getBPM(song, duration): +def get_bpm(song, duration): # Simplify signal mono_signal= np.mean(song, 1) # subsample @@ -133,7 +127,7 @@ def getBPM(song, duration): # tempo / autocorrelation autocorrelation = np.correlate(subsampled, subsampled, 'same') corrected_autocorr = correct_autocorrelation(autocorrelation, len_subsampled) - BPM = findBPMinRange(corrected_autocorr, config.BPM_FINDER_MINIMUM, config.BPM_FINDER_MAXIMUM, duration, fineAdjustRecursion=3) + bpm = find_bpm_in_range(corrected_autocorr, config.BPM_FINDER_MINIMUM, config.BPM_FINDER_MAXIMUM, duration, fine_adjust_recursion=3) if config.DEBUG_BPM_FINDER: - print('BPM:', BPM) - return BPM + print('bpm:', bpm) + return bpm diff --git a/level.py b/level.py index 17b964b..73c6f39 100644 --- a/level.py +++ b/level.py @@ -21,7 +21,7 @@ def __init__(self, song: pygame.mixer.Sound, song_name): self.blocks = [] # Find tempo of the song - BPM = bpm_finder.getBPM(self.array, self.duration) + bpm = bpm_finder.get_bpm(self.array, self.duration) def adjust_frequency_to_aim(frequency, aim): while frequency < aim * 0.66: @@ -29,7 +29,7 @@ def adjust_frequency_to_aim(frequency, aim): while frequency > aim * 1.33: frequency /= 2 return frequency - blocks_per_sec = adjust_frequency_to_aim(BPM/60., config.BLOCKS_PER_SECOND_AIM) + blocks_per_sec = adjust_frequency_to_aim(bpm/60., config.BLOCKS_PER_SECOND_AIM) print('Blocks per second: ', blocks_per_sec, 'that\'s', blocks_per_sec*60, 'bpm') # Build map from audio @@ -45,11 +45,11 @@ def adjust_frequency_to_aim(frequency, aim): self.blocks *= config.HEIGHT_LEVELS/maximum self.blocks = self.blocks.clip(min=0, max=config.HEIGHT_LEVELS) # Clear space at the beginning of the song - start_Blocks = int(config.DISP_HEI/config.VELOCITY_X*blocks_per_sec) - for i in range(start_Blocks-start_Blocks*2//3): + start_blocks = int(config.DISP_HEI/config.VELOCITY_X*blocks_per_sec) + for i in range(start_blocks-start_blocks*2//3): self.blocks[i] = 0 # free plain - for i in range(start_Blocks-start_Blocks*2//3, start_Blocks): # ramping up to normal map - self.blocks[i] *= float(i)/start_Blocks*3/2-0.5 + for i in range(start_blocks-start_blocks*2//3, start_blocks): # ramping up to normal map + self.blocks[i] *= float(i)/start_blocks*3/2-0.5 # Quantize blocks self.blocks = np.round(self.blocks) diff --git a/main.pyw b/main.py similarity index 95% rename from main.pyw rename to main.py index a01ddbf..6fa88e2 100644 --- a/main.pyw +++ b/main.py @@ -8,7 +8,6 @@ import colors import config from config import DISP_WID, DISP_HEI, BASE_FPS -from pydub import AudioSegment # INITIALIZE PYGAME @@ -62,24 +61,9 @@ def pager(length, cut): return [slice(i, min(i + cut, length)) for i in range(0, length, cut)] -def transform(path, new_format, replace=False, return_path=False): - audio = AudioSegment.from_file(path) - name = os.path.basename(path).split('.')[0] + '.' + new_format - new_path = os.path.join(os.path.dirname(path), name) - audio.export(new_path, new_format) - if replace: - os.remove(path) - if return_path: - return new_path - - SONGS = [] # [song title, path] add_songs_in_folder(os.path.join(PATH, 'assets', 'songs'), SONGS) -##for song in SONGS: -## if song[0].split('.')[-1].lower() != 'ogg': -## song[1] = transform(song[1], 'ogg', True, True) - # GAME LOOP state = 'start' diff --git a/obstacle.py b/obstacle.py index cd15c29..ea73c7b 100644 --- a/obstacle.py +++ b/obstacle.py @@ -5,8 +5,8 @@ class Obstacle(pygame.Rect): def __init__(self, initial_x, y, width, height): """A Rectangle that will move with time and has an initial position""" - pygame.Rect.__init__(self, initial_x, y, width, height) + super().__init__(initial_x, y, width, height) self.initial_x = initial_x - def updateOffset(self, offset): + def update_offset(self, offset): self.x = self.initial_x + offset diff --git a/player.py b/player.py index c0f5d9b..f931e17 100644 --- a/player.py +++ b/player.py @@ -62,7 +62,7 @@ def update(self, timePassed): self.collide = False offset = int(-timePassed * self.vel_x) for obstacle in self.level.obstacles: - obstacle.updateOffset(offset) + obstacle.update_offset(offset) if self.rect.colliderect(obstacle): self.damage() self.collide = True