Taggerscript + Plugin: Separating performers by instrument

Tags: #<Tag:0x00007f4199ed3a00>

I wanted to share a taggerscript to address issues that many people likely share with the default Picard behavior for saving artist performance relationships:

  1. My preferred MP3 players cannot read TMCL and IPLS frames
  2. I find the default parentheses formatting to be extremely noisy and difficult to read, so I’d prefer to separate the performers by instrument into dedicated fields.

Here’s a demonstration:

Before:
image
After:
image

For the current version of the script, see this post. Hope others enjoy this as much as I will.

It used to be a pretty simple script that just requires the “Instruments” plugin by David Mandelberg, but it has a variety of bugs due to the way MusicBrainz and Picard work. The bug-filled original script is posted here so the simplicity of the concept can be seen:

$foreach(%_instruments%,
  $set(_currentInstrument,%_loop_value%)

  $if($eq(%_currentInstrument%,other instruments),,
    $noop(Standardize instrument names to be used in tag field names)
    $set(_standardizedInstrument,$lower(%_currentInstrument%))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,drums \(drum set\),drums))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,membranophone,drums))

    $noop(Set custom field)
    $set(_currentCustomPerformerField,performer %_standardizedInstrument%)
    $set(_performerPattern,%_standardizedInstrument%)
    $setmulti(%_currentCustomPerformerField%,$performer(%_currentInstrument%,	),	)
  
    $noop(keep list of instruments so the tags can be scripted)
    $copymerge(_standardizedInstruments,_standardizedInstrument)
  )
)

$foreach($unique(%_standardizedInstruments%),
$set(_standardizedInstrument,%_loop_value%)
$copymerge(instruments,_standardizedInstrument)
)

$unset(performer:*)

Unfortunately, the quirks of MusicBrainz and Picard require more complexity. Again, see this post for the updated version of the script.

5 Likes

Hi! I made an account specially to reply to you, your script works very well, I was wondering if it would be possible to achieve something similar with the rest of the IPLS metadata (producer, engineer, etc), perhaps tweaking your original script? I really know close to nothing about scripting, I’m just trying to make foobar2000 display the IPLS field one way or another :confused:

1 Like

The original script has a variety of issues due to the way MusicBrainz and Picard work. In order to address all the issues I am observing in my library, the script becomes much more complicated. Due to the complexity, I will be posting my script as-is, without removing any of the proprietary personal tags that are present.

The updated script currently has additional requirements:

  1. You still need the “Instruments” plugin by David Mandelberg.
  2. You now need an additional plugin, rperformer, so exact match RegEx can be used. This is scheduled for Picard 2.7.
  3. You will need to manually apply this patch to rperformer if you use the colon in a pre-existing performer field. This bug fix is scheduled for Picard 2.7.

The script:

$noop("instruments" and "additional instruments" ends up in performer)
$unset(performer)

$set(_instrumentsFieldName,instruments)
  $noop(Picard is case sensitive, so MP3 TXXX tags must be manually handled)
  $if($eq_any($upper(%_extension%),MP3),$set(_instrumentsFieldName,$upper(%_instrumentsFieldName%)))

$noop(I like to indicate whether these are track-level, release-level, or 'reduction by ear' credits. You need to manually fill this in.)
$set(_instrumentsPrecisionField,instruments precision)
  $noop(Picard is case sensitive, so MP3 TXXX tags must be manually handled)
  $if($eq_any($upper(%_extension%),MP3),$set(_instrumentsPrecisionField,$upper(%_instrumentsPrecisionField%)))



$foreach(%_instruments%,
  $set(_currentInstrument,%_loop_value%)

  $noop(Ignore misuse of instrument 0a06dd9a-92d6-4891-a699-2b116a3d3f37)
  $if($eq($lower(%_currentInstrument%),other instruments),$set(_skipInstrument,1))
  $if($eq($lower(%_currentInstrument%),all other instruments),$set(_skipInstrument,1))
  $if($eq($lower(%_currentInstrument%),all other instruments & programming),$set(_skipInstrument,1))

  $if($eq(%_skipInstrument%,1),,
    $noop(Standardize instrument names to be used in tag field names)
    $set(_standardizedInstrument,$lower(%_currentInstrument%))
    $noop(Transliterate the field name for non-ASCII instruments)
      $noop(e.g., bandoneón)
      $set(_standardizedInstrument,$replace(%_standardizedInstrument%,ó,o))
      $set(_standardizedInstrument,$replace(%_standardizedInstrument%,二胡,erhu))
      $set(_standardizedInstrument,$replace(%_standardizedInstrument%,尺八,shakuhachi))
      $set(_standardizedInstrument,$replace(%_standardizedInstrument%,三味線,shamisen))
      $set(_standardizedInstrument,$replace(%_standardizedInstrument%,鍵盤ハーモニカ,melodica))

    $noop(Migrate legacy data)
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,drums \(drum set\),drums))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,membranophone,drums))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,guitar family,guitar))

    $noop(Standardize credited names)
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,1st violin,violin))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,2nd violin,violin))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,3rd violin,violin))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,vocals,vocal))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,lead vocal,vocal))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,backing chorus,background vocal))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,fantastic chorus,background vocal))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,chorus,background vocal))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,backing vocal,background vocal))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,rapper,rap))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,guitars,guitar))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,e.guitar,electric guitar))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,a.guitar,acoustic guitar))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,w.bass,wood bass))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,a.saxophone,alto saxophone))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,b.saxophone,baritone saxophone))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,t.saxophone,tenor saxophone))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,pf,piano))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,keyboards,keyboard))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,synthesizers,synthesizer))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,percussions,percussion))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,mini moog,minimoog))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,blues harp,harmonica))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,prophet-5,analog synthesizer))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,fagotto,bassoon))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,fagott,bassoon))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,tsugarushamisen,shamisen))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,cor anglais,oboe))
    $set(_standardizedInstrument,$replace(%_standardizedInstrument%,english horn,oboe))


    $noop(Set custom field's name)
    $set(_currentCustomPerformerField,performer %_standardizedInstrument%)
      $noop(Picard is case sensitive, so MP3 TXXX tags must be manually handled)
      $if($eq($upper(%_extension%),MP3),$set(_currentCustomPerformerField,$upper(%_currentCustomPerformerField%)))

    $noop(If I set the instruments using 'reduction by ear', then don't download non-applicable performers.)
    $set(_existingInstruments,$get(%_instrumentsFieldName%))
    $set(_instruments_precision,$get(%_instrumentsPrecisionField%))
    $if($eq(%_instruments_precision%,reduction by ear),
      $if($find(%_existingInstruments%,%_standardizedInstrument%),,
        $set(_skipInstrument,1)
      )
    )

    $if($eq(%_skipInstrument%,1),,
      $noop(Performer prefixes)
      $foreach(; additional ; guest ; solo ; guest solo ,
        $set(_currentInstrumentRegex,^\(.*\, \)?\(.* and \)?%_loop_value%$replace($replace(%_currentInstrument%,\(,\\\(),\),\\\))\(\, .*\)?\( and .*\)?\$)
        $setmulti(_currentInstrumentMains,$rperformer(%_currentInstrumentRegex%,	),	)
        $foreach($unique(%_currentInstrumentMains%),
          $set(_performer,%_loop_value%)

          $noop(Standardize the musician names here, since it's not really possible to work on the performer names elsewhere)

          $noop(Undo select fancy punctuation)
          $set(_performer,$replace($get(_performer),‐,-))
          $set(_performer,$replace($get(_performer),“,"))
          $set(_performer,$replace($get(_performer),”,"))
          $set(_performer,$replace($get(_performer),’,'))
  
          $noop(Remove character from the vocal in CV credited names)
          $if($or($find(%_performer%,\(CV:),$find(%_performer%,\(CV.)),
            $set(_performer,$rreplace(%_performer%,\(.*\)\\\(CV[.:]\(.*\)\\\),\\2))
          )
          $noop(Remove character from the vocal in Geneon-style starring credited names)
          $if($find(%_performer%, starring ),
            $set(_performer,$rreplace(%_performer%,\(.*\) starring \(.*\),\\2))
          )
          $noop(Remove BEMANI Sound Team)
          $if($find(%_performer%,BEMANI Sound Team),
            $set(_performer,$rreplace(%_performer%,BEMANI Sound Team ["“”]\(.*\)["“”],\\1))
          )
          $noop(Remove Konami Amusement)
          $if($find(%_performer%,Konami Amusement),
            $set(_performer,$rreplace(%_performer%,Konami Amusement \\\(\(.*\)\\\),\\1))
          )
  
          $copymerge(%_currentCustomPerformerField%,_performer)
        )$noop(Close foreach performer in this performer prefix)
      )$noop(Close foreach performer prefix)
  
      $noop(keep list of instruments so the tags can be scripted)
      $copymerge(_standardizedInstruments,_standardizedInstrument)
    )
  )

  $set(_skipInstrument,)
)$noop(Close foreach _instruments)

$noop(Save instruments, removing any local instruments that MusicBrainz doesn't know about)
$delete(%_instrumentsFieldName%)
$foreach($unique(%_standardizedInstruments%),
  $set(_standardizedInstrument,%_loop_value%)
  $copymerge(%_instrumentsFieldName%,_standardizedInstrument)

  $noop(Flatten M4A performer tags: Mutagen writes multi-value tags that can't be read correctly by foobar2000, Mp3tag, etc.)
  $if($eq($upper(%_extension%),M4A),
    $noop(Set custom field's name)
    $set(_currentCustomPerformerField,performer %_standardizedInstrument%)
    $set(%_currentCustomPerformerField%,$join($get(%_currentCustomPerformerField%),; ))
  )
)
$noop(Flatten M4A instruments tag: Mutagen writes multi-value tags that can't be read correctly by foobar2000, Mp3tag, etc.)
$if($eq($upper(%_extension%),M4A),
  $set(instruments,$join(%instruments%,; ))
)

$unset(performer:*)

$noop(I like to indicate whether these are track-level, release-level, or 'reduction by ear' credits. You need to manually fill this in.)
$set(_instruments_precision,$get(%_instrumentsPrecisionField%))
$if($and($gt($lenmulti(%_instruments%),0),$eq(0,$len(%_instruments_precision%))),
$set(%_instrumentsPrecisionField%,?)
)

Picard is case sensitive, so this is actually very easy:

$if($eq($upper(%_extension%),MP3),
$set(ARRANGER,%arranger%)
$unset(arranger)
)

In my testing, this creates a TXXX ARRANGER field rather than an INVOLVED PEOPLE entry.

I don’t think it really clicked for me that in order to get a unified view of data in foobar2000, you need to intentionally NOT have a unified view in Picard (since field names are case-sensitive). I’ll have to rewrite all of my userscripts to change the capitalization based on file extension…

1 Like

I have updated the script to address a variety of bugs/features from MusicBrainz data and Picard.

1 Like