'beet write' - bpm quirk?

I use AcousticBrainz to grab the bpm (among other tags) when I do an import (config.yaml below). When I perform a beet write, I see the following for some songs:

# beet -vv write -p
user configuration: /config/config.yaml
data directory: /config
plugin paths: 
Sending event: pluginload
inline: adding item field multidisc
library database: /config/library.db
library directory: /music
Sending event: library_opened
52nd Street - Dance Classics, Volume 53 & 54 - Tell Me How It Feels
  **bpm: 118 -> 118.323051453**

If I run beet write again, the same songs are updates with the same bpm. I’ve checked the file properties and it shows the correct bpm:

I can run beet write 10 times, and it will still try to update the bpm for the same songs. Any idea why this happens? Below is my config.

# Beets configuration --------------------------------------------------------

directory: /music

import:
    # write metadata to music files
    write: yes

    # move imported files from source to the music directory
    move: no
    copy: yes
    delete: no

    # use auto-tagging where possible
    # do not require confirmation on strong matches
    autotag: yes
    timid: no

    resume: ask
    incremental: no
    none_rec_action: ask
    log: /config/beet.log

    quiet: no  # enable with command line option
    quiet_fallback: skip
    default_action: apply

    singletons: no
    languages: [en]
    detail: no
    flat: no

# use the release-date of the original (first) release of an album?
original_date: no

# on multi-disk releases, assign track numbers for the whole album.
# If "per disk", make sure tracknames do not collide ("paths" setting).
per_disc_numbering: yes

# files matching these patterns are deleted from source after import
clutter: ["Thumbs.DB", ".DS_Store", "*.m3u", ".pls", "*.jpg", "*.nfo", "*.sfv", "*.jpeg", "*.png", "*.URL", "*.url", "*.ini", "*.txt"]

# files/directories matching one of these patterns are ignored during import
ignore: [".*", "*~", "System Volume Information"]

# Paths ----------------------------------------------------------------------

# Paths and filenames for music files
# relative to music directory
paths:
    default: Albums/%asciify{%the{$albumartist}}/($year) %asciify{$album}%aunique{}/%if{$multidisc,$disc - }$track - %asciify{$title}
    albumtype:soundtrack: Soundtracks/($year) %asciify{$album}/%if{$multidisc,$disc - }$track - %asciify{$title}
    comp: Various Artists/%asciify{$album}%aunique{}/%if{$multidisc,$disc - }$track - %asciify{$title}
    singleton: Non-Album/%asciify{%the{$artist}}/%asciify{$artist} - %asciify{$title}
 
item_fields:
    multidisc: 1 if disctotal > 1 else 0
    
# replace special characters in generated filenames
replace:
    '^\.': _
    '[\x00-\x1f]': _
    '[?]': ''
    '[:]': ' -'
    '[<]': (
    '[>]': )
    '["]': ''
    '[\*\|]': _
    '[\xE8-\xEB]': e
    '[\xEC-\xEF]': i
    '[\xE2-\xE6]': a
    '[\xF2-\xF6]': o
    '[\xF8]': o
    '\.$': ''
    '\s+$': ''
    '[_]': ' '
    '[\\\/]': '-'
    '[.]': ''

path_sep_replace: '-'

# filename for the album art
art_filename: cover  # results in "cover.jpg"

max_filename_length: 0  # unlimited


# General --------------------------------------------------------------------

# use mutliple threads during import
threaded: no
timeout: 5.0
verbose: no


# User Interface -------------------------------------------------------------

color: yes
list_format_item: %upper{$artist} - $album - $track. $title
list_format_album: %upper{$albumartist} - $album
time_format: '%Y-%m-%d %H:%M:%S'
terminal_encoding: utf8

ui:
    color: yes
    colors:
        text_success: green
        text_warning: yellow
        text_error: red
        text_highlight: red
        text_highlight_minor: lightgray
        action_default: turquoise
        action: blue


# Auto Tagger ----------------------------------------------------------------

match:
    strong_rec_thresh: 0.1      # match 90% or better for auto import
    medium_rec_thresh: 0.25
    rec_gap_thresh: 0.25
    max_rec:
        missing_tracks: medium
        unmatched_tracks: medium
    distance_weights:
        source: 2.0
        artist: 3.0
        album: 3.0
        media: 1.0
        mediums: 1.0
        #year: 1.0
        country: 0.5
        #label: 0.5
        catalognum: 0.5
        albumdisambig: 0.5
        album_id: 5.0
        tracks: 2.0
        missing_tracks: 0.9
        unmatched_tracks: 0.6
        track_title: 3.0
        track_artist: 2.0
        track_index: 1.0
        track_length: 2.0
        track_id: 5.0
    preferred:
        countries: []
        media: []
        original_year: no
    ignored: []
    track_length_grace: 10
    track_length_max: 30


# Plugins --------------------------------------------------------------------

plugins: [
    acousticbrainz ,
    discogs,
    lyrics,
    fetchart,
    embedart,
    lastgenre,
    scrub,
    missing,
    duplicates,
    the,
    convert,
    zero,
    replaygain,
    chroma,
    permissions,
    edit,
    ftintitle,
    inline
]
pluginpath: []


# Plugins Config -------------------------------------------------------------

convert:
    auto: yes
    ffmpeg: /usr/bin/ffmpeg
    opts: -ab 320k -ac 2 -ar 48000
    max_bitrate: 320
    never_convert_lossy_files: yes
    threads: 4

chroma:
    auto: no

edit:
    itemfields:
    - track
    - title
    - artist
    - albumartist
    - album
    - genre
    - label
    - original_year
    - bpm
    albumfields: album albumartist
    ignore_fields: id path
    
lyrics:
    auto: yes
    fallback: ''
    google_API_key: AIzaSyC2ellggMdicoJoPz7S41kcbh0zNm1yeO8

echonest_tempo:
    auto: yes

lastgenre:
    auto: no
    force: yes
    #whitelist: /config/genres.txt
    #canoncical: /config/genres-tree.yaml
    canoncical: no
    fallback: ''
    count: 3
    prefer_specific: no
    min_weight: 10
    source: album

fetchart:
    auto: yes
    cautious: yes
    minwidth: 300
    maxwidth: 1000 
    enforce_ratio: yes
    sources:
        - filesystem
        - coverart: release
        - itunes
        - coverart: releasegroup
        - '*'
    google_key: AIzaSyC2ellggMdicoJoPz7S41kcbh0zNm1yeO8
    google_engine: 001442825323518660753:hrh5ch1gjzm
    store_source: no
    cover_names:
    - cover
    - front
    - art
    - album
    - folder
    - albumart

embedart:
    auto: yes
    compare_threshold: 0
    ifempty: no
    remove_art_file: no
    maxwidth: 0

replaygain:
    auto: yes
    backend: gstreamer
    overwrite: yes
    albumgain: yes

scrub:
    auto: yes
    
acousticbrainz:
    auto: yes
    force: no
    tags: []

missing:
    format: $albumartist - $album - $track - $title
    count: no
    total: no

duplicates:
    checksum: ''
    
the:
    auto: yes
    a: yes
    format: '{0}, {1}'
    strip: no
    patterns: []
    the: yes
    
zero:
    fields: comments
    update_database: yes
    auto: yes
    keep_fields: []

It appears that this may be a known issue from 2014 (also another similar issue). There doesn’t seem to be a fix at the moment, sorry!

2 Likes

Thanks for the info. Glad it’s not something I’m doing wrong.

Problem with BPM is that many DJs would like to have this one or two decimals but in ID3v2 it’ſ still defined as an integer. There are programs that (illegally) write BPM values with decimals into the TBPM frame but that’s definitely non-standard and might pose problems.

This has been a point of discussion for many years, and still no solution. One could use a non-standard TXXX frame for that, but that’d break compatibility with most other software.

Personally, I’m also for using 2 decimals (and also use software that’s much more exact), but I don’t because it’d break existing standards.

Probably best to maybe calculate and store as a float in the DB and convert to an integer on write? Then again, this could violate beets’ principle that everything should be possible to be recreated just by reading tags …