HydrogenAudio

Hosted Forums => foobar2000 => Development - (fb2k) => Topic started by: foosion on 2013-04-24 20:50:58

Title: Multi-formatting Tech Demo
Post by: foosion on 2013-04-24 20:50:58
I have found a way to handle Album List style %<field>% expression in title formatting scripts but without the restrictions of the post-processing approach used in the Album List component. In this topic I want to present my approach to fellow developers. Nonetheless, user comments are also welcome.

Introduction

If you are not familiar with Album List, here is a short introduction what this is all about. Otherwise you can skip this section. In some use cases (as in Album List) you want multiple results from a title formatting script if there are multi-value fields. For example, to list a song under each of its artists in Album List, you would use a format like this:
Code: [Select]
%<artist>%|%title%

In Album List the %<artist>% field will be expanded to a list of the song's artists which might look like
Quote
(Calexico,Iron and Wine)|Red Dust
with the characters delimiting the list highlighted in red. Note that the list encoding scheme here is used only for illustration and is different from the one actually used in Album List. In a post-processing step this single string will be expanded to a list of string:
Quote
Calexico|Red Dust
Iron and Wine|Red Dust

By interpreting each of the strings as a path in a hierarchical structure Album List would derive the following tree view:
Code: [Select]
+ Calexico
| + Red Dust
+ Iron and Wine
  + Red Dust

Suppose we wanted to group artists by their first letter, i.e.
Code: [Select]
+ C
| + Calexico
|   + Red Dust
+ I
  + Iron and Wine
    + Red Dust

In Album List we cannot do this because the $left() function - like all the other functions - is not aware of the list encoding. This is where my new approach comes in to play.

Multi-formatting algorithm

The multi-formatting algorithm is based on the idea that we only process one of the values of a multi-value field at a time. If %<artist>% returns only one artist without decorating it with additional list encoding characters, we can use any title formatting function on the result, even control flow functions like $if(). If there are multiple artist values, we just to run the title formatting script multiple times, once for each value. Sounds simple enough, right?

The foo_multiformat component implements this approach in the branch_formatter class. This class coordinates the repeated execution of a given title formatting script. It uses some helper classes for keeping track of the encountered multi-value fields and the remaining values. In essence, the code does a depth-first search through a tree where each inner node of the tree is a branch point created by %<field>%, $meta_branch_remap(field) or $meta_branch(field).

So what are the restrictions? The depth-first search requires a predictable behaviour of all used titleformat_hook implementations, so it can re-create the state corresponding to the last branch. In particular this means:

The implementation contains safety checks to detect inconsistencies between the current execution state and its log. Nothing bad (like crashes) should happen in the case of inconsistencies but the tree of possible value combinations may not be fully explored.

Documentation

The component adds a context menu command called "Multi-formatting Tech Demo". This command opens a window where title formatting scripts can be entered and evaluated. The window displays the results of the script in the lower part.

The component supports the following special fields and functions.

The following functions are provided to explore the limitations of the multi-formatting approach.

Examples:

I'll add further documentation as the need arises.

Links
Title: Multi-formatting Tech Demo
Post by: d125q on 2013-06-24 20:10:44
It would be awesome if Peter could comment on the plausibility of this being implemented.

Mad props for the work, foosion.
Title: Multi-formatting Tech Demo
Post by: db1989 on 2013-06-25 00:11:11
I agree. This is great stuff, and so it would be great if were able to be folded into the core or at least DUI. Thanks, foosion!
Title: Multi-formatting Tech Demo
Post by: psix on 2013-08-17 20:27:44
So, your approach would limit each tag to single a single branch? I.e. each subsequent %<tag>% would return the same single value?
I don't know what possible use there is to having multiple branches of the same tag, but I would still like to have the option.

The Album List titleformat wiki page (http://wiki.hydrogenaudio.org/index.php?title=Foobar2000:Titleformat_Album_List) says at the very bottom
"The value of a branching expression can be stored in variables and retrieved without loss." I don't think that is true.
It would seem to me that currently the branch value is not stored but the branching operation itself is. (If I am wrong, please point me to the correct usage.)
I'd prefer if one could use variables to "unbranch" or pin down the value, e.g. $puts(constant,%<tag>%), so that the variable value $get(constant) could then be used safely for e.g. string operations.

Using this approach, your first example should then be written something like "$upper($left($put(artist,%<artist>%),1))|$get(artist)|%title%"
whereas "$upper($left(%<artist>%,1))|%<artist>%|%title%" would still have two %artist% branches.
Apart from some possible nasty surprises for people who'd expect the variables to still iterate all the values, I don't think any functionality would be lost that way.
Title: Multi-formatting Tech Demo
Post by: foosion on 2013-08-19 19:30:14
So, your approach would limit each tag to single a single branch? I.e. each subsequent %<tag>% would return the same single value?
Correct, although I don't see it as a limitation. In title formatting, you generally get the same value of all occurrences of %tag%. My approach preserves that property for %<tag>%.

I don't know what possible use there is to having multiple branches of the same tag, but I would still like to have the option.
Technically, it would be an easy change to my code and I thought about which uses it might have. I could only come up with two kinds of use cases:
I then chose to go with the current approach for two reasons. Firstly, the performance of my approach would deteriorate in both cases. Secondly, as far as I know there are third-party components which provide greater control over the tree construction by using a different language. They thereby avoid the limitations of title formatting entirely.

The Album List titleformat wiki page (http://wiki.hydrogenaudio.org/index.php?title=Foobar2000:Titleformat_Album_List) says at the very bottom
"The value of a branching expression can be stored in variables and retrieved without loss." I don't think that is true.
It would seem to me that currently the branch value is not stored but the branching operation itself is. (If I am wrong, please point me to the correct usage.)
Yes, that is one way to visualise it.

I'd prefer if one could use variables to "unbranch" or pin down the value, e.g. $puts(constant,%<tag>%), so that the variable value $get(constant) could then be used safely for e.g. string operations.
I prefer to not use variables in title formatting because I find all the $puts and $get rather cumbersome. So I tried to optimise the most common use cases. Consider the following lines of code, starting with a version without branching:
Code: [Select]
$upper($left(%artist%,1))|%artist%|%title%
$upper($left(%<artist>%,1))|%<artist>%|%title%
$upper($left($put(artist,%<artist>%),1))|$get(artist)|%title%
There is also a usability perspective here. What does $get(artist) do? You won't know until you find the corresponding $put or $puts. What does %<artist>% do? Always the same thing, it returns the current artist value.

If needed, it would be easy to add a $meta_branch_ex(tag) function which would branch each time it is called.