arousal and valence to grouping

justb72's Avatar


20 Feb, 2016 04:28 PM

Hi there,

I want to have the arousal and valence values to be written in the grouping field.

I already searched everywhere and the only thing coming near is "TagsToGrouping.groovy".

Although I am not a developer I understand a tiny little bit of programming - so if the tags can be copied to grouping then arousal and valence can be copied there as well.

  1. Is there already a solution for this topic? If so, please provide me with the necessary information.
  2. If not it would probably be necessary to modify the "TagsToGrouping.groovy" to "MoodToGrouping.groovy" and place that file in the plugin directory. I already found two mood-methods getArousal() and getValence() which should deliver the information I need.
    But as I am no programmer I have some difficulties in rewriting the code. I think the topic can be narrowed on this part of the code:
    ` def void process(AudioSong song, int index) {

        Set<String> tags = song.getTags()
        if (tags != null && !tags.isEmpty()) {
            final StringBuilder sb = new StringBuilder()
            for (s in tags) {
            final String content = sb.toString().endsWith(",") ? sb.substring(0, sb.length() - 1) : sb.toString()
   "Writing tags (${content}) to ${song.getName()}"

Any suggestions how this can be done?

Cheers, Björn

  1. Support Staff 1 Posted by hendrik on 21 Feb, 2016 04:15 AM

    hendrik's Avatar

    Hey Björn,

    1. No.
    2. Yes—you're exactly right. From the top of my head, this should work (untested):

      [...] Mood mood = song.getMood() if (mood != null) { song.setGrouping("valence=${mood.getValence()}, arousal=${mood.getArousal()}") } [...]

    If you're calling the file MoodToGrouping.groovy, you should call the class MoodToGrouping. Also, you may need to add an import statement for the Mood class. I.e.

    import com.tagtraum.audiokern.mood.Mood

    If you get this to work, please attach the resulting beaTlet to this discussion, so that other may benefit. Thank you!

    Hope this helps,


  2. 2 Posted by justb72 on 21 Feb, 2016 09:25 AM

    justb72's Avatar

    Thanks Hendrik,
    what is the best way to test my plugin?
    I don't want to mess my whole library.

    Cheers Björn

  3. 3 Posted by justb72 on 21 Feb, 2016 10:37 AM

    justb72's Avatar

    I modified the class as suggested:

    class MoodToGrouping extends LibraryBatchAction {
    class MoodWriter implements EachSongProcessor {
        static log = LoggerFactory.getLogger("MoodToGrouping.groovy")
        // Called once, before processing starts.
        def void startProcessing(int count) {
   "Starting processing..."
        // Called for each song.
        def void process(AudioSong song, int index) {
        Mood mood = song.getMood() 
        if (mood != null) {
            // write arousal and valence into the grouping field 
                song.setGrouping("${mood.getArousal()}, ${mood.getValence()}") 
      "Writing mood (${mood.getArousal()}, ${mood.getValence()}) to ${song.getName()}"

    You think this is correct?

  4. Support Staff 4 Posted by hendrik on 21 Feb, 2016 11:53 AM

    hendrik's Avatar

    The MoodWriter part looks good to me. You can test it by not calling setGrouping and just doing log statements.

    Anyhow. In the MoodToGrouping class you have to make sure that you adjust the factory method to create a MoodWriter instance.


    // Factory method that creates the processor for each song.
    def EachSongProcessor createEachSongProcessor() {
        new MoodWriter()


  5. Support Staff 5 Posted by hendrik on 01 Mar, 2016 10:44 AM

    hendrik's Avatar

    Hey Björn,

    just wanted to follow-up: Did you get this to work satisfactorily? If not, what's not working? If you did get it to work, would you attach the source file (as attachment, not inline)?



  6. 6 Posted by justb72 on 01 Mar, 2016 09:59 PM

    justb72's Avatar

    Hey Hendrik,

    indeed I succeeded.
    With your help it was quite easy to read the arousal and valence out of the mood and to write the values in the grouping field.
    But then I discovered, that I would not be able to sort my tracks based on the arousal because the lowest arousal values range from -90 to 90 and the grouping field is of text type and therefore iTunes and beatunes would sort it in the wrong way.

    So I rewrote the code, so it would write -90 of arousal to a00 and +90 of arousal to a20. Same with valence (v00 to v20).
    I am sure you can write the code much better (probably extracting the functionality to transfer the mood values in a seperate function) - but it works for me.

    Additional information on how I get my dj-tool Traktor DJ to display the arousal and valence values:
    In the tool mp3tag I have written an "action" to copy the grouping tag to the label tag - something like:
    format tag field "PUBLISHER": %contentgroup%

    So now I am finally able to sort my tracks in my dj-tool according to the arousal of the tracks. This is a big advantage for me.

    Thank you again for your great support.

  7. Support Staff 7 Posted by hendrik on 02 Mar, 2016 07:39 AM

    hendrik's Avatar

    Very cool! :-)

    And thanks for posting the code. Hopefully someone else finds this useful.

    BTW: In beaTunes 4.5.9, you can translate mood values into verbal descriptions with the following method:

        List<String> keywords = AbstractSongTableModel.toMoodKeywords(getApplication(), mood);
        StringBuilder sb = new StringBuilder();
        for (int i=0; i<keywords.size(); i++) {
            if (i+1<keywords.size()) sb.append(", ");
        String description = sb.toString();

    For this to work, you first need to import AbstractSongTableModel:

    import com.tagtraum.beatunes. AbstractSongTableModel

    Just as additional info—the method in AbstractSongTableModel is defined like this:

    public static List<String> toMoodKeywords(final BeaTunes application, final Mood mood) {
        if (mood == null) return Collections.emptyList();
        final Mood category = mood.getCategory();
        final Mood labeledMood = mood.getClosestLabeledMood();
        final String categoryLabel = application.localize(category.getLabel());
        final String moodLabel = application.localize(labeledMood.getLabel());
        final String very;
        if (mood.getStrength() > 80) {
            very = application.localize("very") + " ";
        } else {
            very = "";
        if (!categoryLabel.equals(moodLabel)) {
            return Arrays.asList(very + categoryLabel, moodLabel);
        } else {
            return Collections.singletonList(very + categoryLabel);



  8. 8 Posted by justb72 on 04 Mar, 2016 05:28 PM

    justb72's Avatar

    Just discovered that I attached a version containing errors.
    Find attached the latest version.

  9. Support Staff 9 Posted by hendrik on 06 Mar, 2016 07:21 PM

    hendrik's Avatar

    Great. Thanks!

Reply to this discussion

Internal reply

Formatting help / Preview (switch to plain text) No formatting (switch to Markdown)

Attaching KB article:


Attached Files

You can attach files up to 10MB

If you don't have an account yet, we need to confirm you're human and not a machine trying to post spam.

Keyboard shortcuts


? Show this help
ESC Blurs the current field

Comment Form

r Focus the comment reply box
^ + ↩ Submit the comment

You can use Command ⌘ instead of Control ^ on Mac