diff --git a/beetsplug/bpm.py b/beetsplug/bpm.py new file mode 100644 index 000000000..977d59dcc --- /dev/null +++ b/beetsplug/bpm.py @@ -0,0 +1,87 @@ +# This file is part of beets. +# Copyright 2014, aroquen +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +"""Determine BPM by pressing a key to the rhythm.""" + +import time +import logging + +from beets import ui +from beets.plugins import BeetsPlugin + +log = logging.getLogger('beets') + + +def bpm(max_strokes): + """Returns average BPM (possibly of a playing song) + listening to Enter keystrokes. + """ + t0 = None + dt = [] + for i in range(max_strokes): + # Press enter to the rhythm... + s = raw_input() + if s == '': + t1 = time.time() + # Only start measuring at the second stroke + if t0: + dt.append(t1 - t0) + t0 = t1 + else: + break + + # Return average BPM + # bpm = (max_strokes-1) / sum(dt) * 60 + ave = sum([1.0 / dti * 60 for dti in dt]) / len(dt) + return ave + + +class BPMPlugin(BeetsPlugin): + + def __init__(self): + super(BPMPlugin, self).__init__() + self.config.add({ + u'max_strokes': 3, + u'overwrite': True, + }) + + def commands(self): + cmd = ui.Subcommand('bpm', + help='determine bpm of a song by pressing \ + a key to the rhythm') + cmd.func = self.command + return [cmd] + + def command(self, lib, opts, args): + self.get_bpm(lib.items(ui.decargs(args))) + + def get_bpm(self, items, write=False): + overwrite = self.config['overwrite'].get(bool) + if len(items) > 1: + raise ValueError('Can only get bpm of one song at time') + + item = items[0] + if item['bpm']: + log.info('Found bpm {0}'.format(item['bpm'])) + if not overwrite: + return + + log.info('Press Enter {0} times to the rhythm or Ctrl-D \ +to exit'.format(self.config['max_strokes'].get(int))) + new_bpm = bpm(self.config['max_strokes'].get(int)) + item['bpm'] = int(new_bpm) + if write: + item.try_write() + item.store() + log.info('Added new bpm {0}'.format(item['bpm'])) diff --git a/docs/plugins/bpm.rst b/docs/plugins/bpm.rst new file mode 100644 index 000000000..6359f9501 --- /dev/null +++ b/docs/plugins/bpm.rst @@ -0,0 +1,22 @@ +BPM Plugin +========== + +This ``bpm`` plugin allows to determine the bpm (beats per minute) of a song by recording the user's keystrokes. The rationale is that sometimes the bpm of a song cannot be obtained from the echonest database (via the ``echonest`` plugin) or it is simply plain wrong. Whenever you need to fix the bpm of a song manually, the ``bpm`` plugin comes to the rescue. + +Usage +------ + +First, enable the plugin ``bpm`` as described in :doc:`/plugins/index`. Then, suppose you want to set or modify the bpm of ````, where ```` is any valid query that matches the song of interest. Start playing it with your favorite media player, fire up a terminal and type:: + + beet bpm + +You'll be prompted to press Enter three times to the rhythm. This typically allows to determine the bpm within 5% accuracy. + +The plugin works best if you wrap it in a script that gets the playing song, for instance with ``mpc`` you can do something like:: + + beet bpm $(mpc |head -1|tr -d "-") + +Credit +------ + +This plugin is inspired by a similar feature present in the Banshee media player. diff --git a/docs/plugins/index.rst b/docs/plugins/index.rst index 7dc0fe799..613c9408d 100644 --- a/docs/plugins/index.rst +++ b/docs/plugins/index.rst @@ -60,6 +60,7 @@ by typing ``beet version``. keyfinder bucket importadded + bpm Autotagger Extensions --------------------- @@ -94,6 +95,7 @@ Metadata key from the audio. * :doc:`importadded`: Use file modification times for guessing the value for the `added` field in the database. +* :doc:`bpm`: Determine bpm from keystrokes .. _Acoustic Attributes: http://developer.echonest.com/acoustic-attributes.html .. _the Echo Nest: http://www.echonest.com