Implement EQ Preset MHOD (type 7)#3
Conversation
… mark the track for saving.
…f the preset object is null
|
Whoops - was unaware that renaming the branch would close the PR! |
|
Thank you so much for this! Before I review, would you mind taking a look at the diff? It looks like Git might have converted line endingings in your PR, making it have more changes than necessary. |
|
Ah, sorry about that, all sorted now! |
|
Thanks for cleaning that up! Before I start requesting changes I have a few questions:
|
|
Hey, no worries!
I frequently referenced the iPodLinux wiki's page on the iTunesDB file throughout my research, although they didn't seem to have any information on how the EQ preset was actually stored, other than that it was type 7 and likely a string. I then used iTunes to add an EQ preset to a few tracks - you can do this by right clicking on a track, pressing "Song Info", going to the "Options" tab, and there finding the "Equaliser" dropdown. I set one track on my iPod to "Accoustic", the first preset on the list (besides "None"). I've also verified that adding an EQ preset to a track via Clickwheel also shows up in iTunes.
Ah, sorry to disappoint, but according to (again, you guessed it) the iPodLinux wiki source 1 source 2, whilst iTunes does in fact save custom EQs to the iPod, no iPod was known to actually make use of it, at least, not when that article was written. This is corroborated by multiple forums, such as Reddit and Apple Discussions. (Note: the link mentioned in that comment no longer works, however, a copy was saved on the Wayback Machine here).
Well I did start by writing an If you'd prefer, I could remove |
… UnicodeMHOD and helper methods in EQPreset.cs instead
|
Something more like this perhaps? |
|
Okay finally had some time to sit down and do some digging! Turns out mhod 7 will also store the custom preset name. So, a built-in preset will have a value of the format: And a custom preset is just the string name of the preset: I think given this we should probably update the API to simply return a string value. For the built in formats we can create a string enum to make it easier for the built-in presets: public enum StandardEQPreset : string
{
Acoustic = "Acoustic",
BassBooster = "Bass Booster",
// ...And then update the songWithBuiltInPreset.EQPreset; // "Acoustic", StandardEQPreset.Acoustic
songWithCustomPreset.EQPreset; // "No Bass At All"The parser can use the presence of the magic string to determine if it should be parsed as a built-in preset integer value or whether it should just be treated as the string. |
|
Actually, thinking on it a little more I wonder if we just skip parsing all together and just treat it as a string mhod, but make the enum with the magic values as an easy way to set it? |
Nope! I'm somewhat confident that this isn't something that has ever worked given the amount of documentation saying as such. iTunes also didn't sync an
ahhh bummer :( I read a blog post that had a code sample showing it and I assumed it was available. Turns out the blog post was explaining that it would be nice if C# supported it, not that it was supported 😅
I think I'm mostly on board with this! Any thoughts on what the API would look like to assign a custom EQ name to a track in this case? // reading
track.EQPreset.Name; // "Acoustic"
track.EQPreset.ID; // 100
track.EQPreset.IsCustom; // false
track.EQPreset.Name: // "No Bass At All"
track.EQPreset.IsCustom; // true
// writing
var track = new NewTrack
{
EQPreset = /* set custom or built-in preset */I think |
Hah, I think we read the same blog post xD
Your example looks good to me.
I do wonder if a factory method might be clearer here though - for example So that would mean setting an EQPreset would look like: var track = new NewTrack
{
EQPreset = EQPreset.Accoustic
[...]
}Or var track = new NewTrack
{
EQPreset = EQPreset.Custom("No Bass At All")
[...]
} |
|
Also, what are your thoughts on using |
|
I think that API looks good: // assign built-in
var track = new NewTrack
{
EQPreset = EQPreset.Accoustic
/* ... */
}
// assign custom
var track = new NewTrack
{
EQPreset = EQPreset.Custom("No Bass")
/* ... */
}
// read
var name = track.EQPreset.Name;
var isCustom = track.EQPreset.IsCustom;As far as |


Intro
First off, I want to say a huge thank you for all the work put into this project - it has been incredibly useful to me!
I also want to note that C# is not a language I am overly confident in, so whilst I have tested these changes on my own iPod and can confirm that they work (at least on my 1st gen Mini), if you notice any non-standard code practices or oddities in how I've implemented things, please let me know and I will be happy to adjust them.
TLDR;
This PR adds support for Equalizer (EQ) presets and addresses a bug regarding track data saving.
Added the logic to parse and write MHOD type 7 objects. This allows the library to recognize and preserve Equalizer preset data within the iTunesDB.
Track.SetDataElement()bugCalling
Track.SetDataElement()withdata == nulldidn't set_isDirtytotrue. I fixed this by adding_isDirty = true;after_childSections.Remove(mhod);Background
I found out that iTunes can set per-song EQ presets and sync these to iPods, which then use them if their own EQ is set to "flat", and immediately checked to see if Clickwheel supported it. I saw that it didn't, so I dug further and found that iTunes stores the EQ preset in an MHOD of type 7.
I didn't find any further information on this though.
Slightly concerned, I grabbed my iPod, cloned this library and wrote some test code. Well, lo and behold, after writing some EQ data from iTunes, I was able to read it from Clickwheel as a string MHOD. iPodLinux confirmed that this should be the case, and it turned out that each of the EQ presets has an ID, from 100, up to 121. I carefully mapped each of these IDs to their names from iTunes, and built some code infrastructure around it. This is the resultant PR.
When testing this, I found that when clearing an EQ preset by setting it to null, the change wouldn't be reflected on my iPod. Looking into it, I saw the bug I mentioned in
SetDataElement, so I fixed that at the same time. If you'd rather, I can break that out into its own separate PR (literally just a one-line fix).