mixxxdj / mixxx

Mixxx is Free DJ software that gives you everything you need to perform live mixes.
http://mixxx.org
Other
4.43k stars 1.27k forks source link

Mixxx palette Hotcue colors non-ideal #11045

Open Swiftb0y opened 1 year ago

Swiftb0y commented 1 year ago

It seems a common way of encoding an led color as midi is by taking the most significant two bits and packing that into the lower six bits of the velocity byte. Our color palette largely works for that, though the colors for green and purple are not ideal. I'd like to propose to slightly correct those colors so when they're truncated for sending to the controller, the appropriate bits are set.

Concretely, I'd like to change the green from #32be44 to #32c044 and purple from #af00cc to #c000cc. I believe both changes are imperceptible enough to go unnoticed by the average user and also do not degrade the experience for people with color vision deficiencies. green comparision purple comparision

daschuer commented 1 year ago

It looks like you want to use integer truncation instead of rounding. If we round our palette properly to 6 bit will look like this: #32be44 becomes with 0x1f dither #1fdf5f The proposed alternative color will become the same #af00cc becomes with 0x1f dither #df1fdf The proposed new colors will lead to the same colors on the controller.

Te user can define any color and they should not be limited to 6 bits. Instead of limiting the source colors to two bits per channel, you may just provide a rounding function for that purpose. I think that is perfectly OK, because the RGB LED color space has probably nothing to do with sRGB of the displays anyway. And in many cases the controller colors needs to be tweaked anyway.

Swiftb0y commented 1 year ago

It's not clear to me what you are suggesting exactly. I agree that the user should not be limited in what colors they choose, they take responsibility if the colors they choose look bad on their hardware, but our in-built palette should look good out of the box. The colors I propose also lead to the same result when truncating, but are "less invasive". I'm already implementing the suggested rounding function by uniformly truncating. What other kind of rounding are you suggesting instead? I don't want to do color space transformations in JS...

daschuer commented 1 year ago

You propose to change "0xbe" to "0xc0" in the green value. It is "1011.1110" and "1100.000" both are "11" after rounding and truncating to two bits. Something likeround(value/64.0) So what is the difference, that the proposed change achieves?

Swiftb0y commented 1 year ago

right, I tried rounding, the problem with that is that suddenly the lower bits of the other channels get rounded up and become significant enough to completely change the color to be unrecognizable compared to what its supposed to look like.

daschuer commented 1 year ago

Which color is affected?

Swiftb0y commented 1 year ago

So here are a couple impressions: These are the first 8 colors from the mixxx color palette Screenshot from 2022-11-08 10-29-27


The first code yields terrible results.

var downsample = function (n) {
    return Math.round(n / 64);
}

IMG_20221108_102723

Scaling by a different amount also doesn't yield great results either

var downsample = function(n) {
    var precision = 3 / 255;
    return Math.round(n * precision);
}

IMG_20221108_102942


This aint great either and also is also wrong.

var downsample = function(n) {
    var precision = 4 / 255;
    return Math.round(n * precision);
}

IMG_20221108_103227


The truncation yields the best results (apart from the faint green purple).

var downsample = function(n) {
    return (n & 0xC0) >> 6;
}

IMG_20221108_103513

daschuer commented 1 year ago

Ah I see Math.round(n / 64) is wrong because it does round up to 4, which is not a two bit value. So I think the (n & 0xC0) >> 6; version is correct.

It is hard to judge from a picture, but the related picture looks good.

Here is what it means with values:

Truncation + 0x1f dither

#C50A08 L42 -> #DF1f1f L49 #32BE44 L68 -> #1f9f5f L57 proposed #1fdf5f L78 #42D4F4 L78 -> #5fdfdf L81 #F8D200 L84 -> #dfdf1f L86 #0044FF L38 -> #005fdf L42 #AF00CC L43 -> #9f1fdf L43 proposed #df1fdf L54 #FCA6D7 L78 -> #df9fdf L73 #F2F2FF L95 -> #dfdfdf L88

On my monitor the proposed colours do longer have an optimal distance to their neighbours.

I have added the lightness numbers as well. You see that all colours have a different value. From the picture it looks like the resulting lightness is different. If you aim that they are all at the same brightness on your controller, you need to adjust them anyway. It is a hardware specific issue that needs to be fixed in the mapping IMHO.

That was by the way the reason to not use hardware specific RGB codes in our fist version of the colour feature. So I propose to match the Mixxx RGB values to set of good looking colours. on your device.

Swiftb0y commented 1 year ago

Ah I see Math.round(n / 64) is wrong because it does round up to 4, which is not a two bit value.

Right, but even rounding with n * (3/255) doesn't work because rounding is the wrong solution here.

On my monitor the proposed colours do longer have an optimal distance to their neighbours.

Optimal distance in what terms? IIRC we optimized the colors to be usable for people with color vision deficiencies, which I don't think is related to your metric.

It is hard to judge from a picture, but the related picture looks good.

The picture is non-ideal, as I said, the purple and green are both just 0b10 instead of 0b11 in their primary channel. It's very visible in person.

If you aim that they are all at the same brightness on your controller, you need to adjust them anyway. It is a hardware specific issue that needs to be fixed in the mapping IMHO.

I somewhat agree, but as far as I can tell, serato controllers have this standardized color schema, and this issue does not apply to the hardware directly but to the schema. So this fix likely applies to all recent serato controllers.

That was by the way the reason to not use hardware specific RGB codes in our fist version of the colour feature. So I propose to match the Mixxx RGB values to set of good looking colours. on your device.

As outlined, the issue is likely not specific for my hardware but to all serato controllers.

I don't understand why these slight changes to our color palette would be so severe.

daschuer commented 1 year ago

The problem with this issue it that it describes a solution and original not a problem which would be better. I am not certain if I already fully understand the issue. Did I get it right that the underlying issue can be summarized as
"green and purple look faint on my Serato controller"

Unfortunately, I cannot confirm it from the picture (n & 0xC0) >> 6; (Even though you cannot judge colors from picture taken by a digital camera)

Changing the pallet color is strictly speaking an breaking API change. The pallet colors are picked to look good on a sRGB Monitor after a few loops of optimization. They are not intended to pass to the controller in the first place. The normal way is to use the ColorMapper to match them to a set of good looking colors on the controller.

For me this is a solution for your controller which supports only 64 colors, with the majority of them too faint. You can select a set of good looking colors and the mapping script will work reasonable for all cures, from the Mixxx palette and imported from third party tools.

I wonder how Serato DJ Pro itself solves the problem. Their greens are even darker than the one in the Mixxx palette. Ideal the a track imported from Serato should result to the same color on the button when loaded via Mixxx or via Serato.

grafik

Swiftb0y commented 1 year ago

Did I get it right that the underlying issue can be summarized as "green and purple look faint on my Serato controller"

Sort of, I am hypothesizing that its rather "green and purple look faint all Serato controllers which implement this color encoding scheme", which is why I am proposing to make the adjustment in mixxx rather than the mapping script of my specific controller.

Unfortunately, I cannot confirm it from the picture (n & 0xC0) >> 6; (Even though you cannot judge colors from picture taken by a digital camera)

I'll try to retake the picture under better conditions when I get home later today. The original picture is so overexposed that the difference in lightness is only really noticeable because the actual bright colors have some bloom to them while green and purple don't.

Changing the pallet color is strictly speaking an breaking API change. The pallet colors are picked to look good on a sRGB Monitor after a few loops of optimization.

So are they part of the API or strictly cosmetic?

The normal way is to use the ColorMapper to match them to a set of good looking colors on the controller.

Not really, we specifically designed the API so controller also gets access to the full color if it wants. For example when you can send the color in higher resolution using sysex messages (consult the novation launchpad programmers manual for an example). The ColorMapper on the other hand was created for controller that only have a limited palette with fixed IDs, (novation launchpad also is capable of that, or the Roland DJ 505 is also an example).

I wonder how Serato DJ Pro itself solves the problem. Their greens are even darker than the one in the Mixxx palette. Ideal the a track imported from Serato should result to the same color on the button when loaded via Mixxx or via Serato.

I assume they have a fixed palette which manufactures match to their hardware, that was also the very first implementation of hotcue colors in mixxx but we figured out pretty soon that that approach was too limited. I may look into what colors serato exports and how they look when imported into mixxx.

Swiftb0y commented 1 year ago

I would also like to request input from @ronso0 here since IIRC they came up with the current colors in the palette.

daschuer commented 1 year ago

rather than the mapping script of my specific controller.

For me this is a controller issue, because your controller supports only 64 colors, where only a subset of them are fully saturated and not faint. Maybe the good looking colors are the 18 that are ins the Serato palette.

I'll try to retake the picture under better conditions

Not necessary, I trust you that you see the issue.

So are they are kind of part of the API ..

because they are the input of the ColorMapper. But this is only a minor detail, that should not prevent a solution.

we specifically designed the API so controller also gets access to the full color if it wants.

your controller cannot do it.

The main issue with this solution is that it is only a band aid for two colors in the MIxxx hotcue color palette. As a general purpose solution.

If we assume that Serato has a fixed palette. We can be quite sure that the hardware is optimized for these. So a general purpose solution here would be to use the color mapper to map these colors. This will make any color good looking.

Swiftb0y commented 1 year ago

For me this is a controller issue, because your controller supports only 64 colors, where only a subset of them are fully saturated and not faint.

Right, but I argue that this applies to a significant portion of serato controllers. Not just my hardware.

So are they are kind of part of the API .. because they are the input of the ColorMapper. But this is only a minor detail, that should not prevent a solution.

This is not what the color mapper was designed for IMO! ColorMapper is not just a glorified LUT. You're supposed to give it an approximation of the colors supported by your controller and it chooses the closest matching one. Hardcoding specific colors of the mixxx-inbuilt color-palettes is exactly what we wanted to avoid.

your controller cannot do it.

No controller likely can because our colors have 8-bit of precision per channel but even if you send each channel in a single "byte" per sysex, you only have 7-bits.

The problem is that our palette does not work well with downsampling its colors! Which is an issue unrelated to my specific hardware.

If we assume that Serato has a fixed palette. We can be quite sure that the hardware is optimized for these. So a general purpose solution here would be to use the color mapper to map these colors. This will make any color good looking.

Well it will only make the colors good looking which I select, to make appropriate use of the entire palette, I would map all colors, which would be built programmatically from the color schema, so then we're back to the beginning where the color mapper would select a suboptimal color because our palette is suboptimal.

Swiftb0y commented 1 year ago

fyi autogenerating the colormapper palette upfront fixes green and purple, but now pink is being interpreted as white...

daschuer commented 1 year ago

Do you have access to a Serato installation?

ronso0 commented 1 year ago

I would also like to request input from @ronso0 here since IIRC they came up with the current colors in the palette.

he/him is fine : )

IIRC I gave my 2ct when @Holzhaus refined the Mixxx track color palette. Which I think works well in the GUI.

Besides that, I rarely use hotcue colors, and never on controllers, simply beause mine only have red LEDs (and blue pitch center indicators : ) so I can't judge the issue your discussing here.

No controller likely can because our colors have 8-bit of precision per channel but even if you send each channel in a single "byte" per sysex, you only have 7-bits.

The problem is that our palette does not work well with downsampling its colors! Which is an issue unrelated to my specific hardware.

If that is the issue we should indeed simply adjust the palette.

Swiftb0y commented 1 year ago

Do you have access to a Serato installation?

Yes.

daschuer commented 1 year ago

Can you please create one or two files with cues of all colors? I am curious if our Serato palette is correct in terms of file tags or if we have only picked the visual appearance in the skin.

Swiftb0y commented 1 year ago

I forgot about it yesterday and I'm not sure when I'll get around to it. Feel free to ping me if I don't get to it until Monday.

daschuer commented 1 year ago

Ok, no stress.

For reference, here is our original discussion where we have optimized our palette, and verified it on various devices. It took some interaction to come to our current palette. I am afraid that your proposal leads to a regression, while it is only a band aid for our default palette.

https://github.com/mixxxdj/mixxx/pull/2031

Swiftb0y commented 1 year ago

looks good (assuming the user selected the appropriate color palette in the settings). Screenshot from 2022-11-14 21-06-36 I also tested these colors on the controller and it results in about 6 distinct colors at full brightness. The same colors interpreted through ColorMapper don't always result in the full brightness (nor do they yield the best matching color), but the color variety is higher.

daschuer commented 1 year ago

Are the cues of the loaded track created by Serato? In LateNight Pale Moon the cue buttons do not show exactly the palette color, but I can confirm that I got the same colors creating the Track with Mixxx. In case this track was not created with Serato, can you create one and take the same screenshot with Deere or Shade?

Swiftb0y commented 1 year ago

Are the cues of the loaded track created by Serato?

Yes. The screenshot is for colors 9-16.

daschuer commented 1 year ago

Ah I see. I should have looked up our code before. In case of Serato there is already a mapping form the pure colors in the track metadata (and the controller) to the colors used in the GUI and in the Mixxx control objects: https://github.com/mixxxdj/mixxx/blob/67a3e3f7978341210885e6bd5fd93d9f926c0174/src/util/color/predefinedcolorpalettes.cpp#L77

I have just checked if the kSeratoDJProHotcueColorPalette matches the pure colors from kSeratoTrackMetadataHotcueColorPalette if it is rounded as proposed.

GUI Serato Mixxx #C02626 #CC0000 #CC0000 #DB4E27 #CC4400 #CC4400 #F8821A #CC8800 #CC8800 #FAC313 #CCCC00 #CCCC00 #4EB648 #88CC00 #448844 #006838 #44CC00 #004400 #1FAD26 #00CC00 #008800 #8DC63F #00CC44 #88CC00 #2B3673 #00CC88 #000044 #1DBEBD #00CCCC #008888 #0F88CA #0088CC #0088CC #16308B #0044CC #000088 #173BA2 #0000CC #000088 #5C3F97 #4400CC #440088 #6823B6 #8800CC #440088 #CE359E #CC00CC #CC0088 #DC1D49 #CC0088 #CC0044 #C71136 #CC0044 #CC0000

Unfortunately some cues leads to to the same colors in Mixxx and while in case of Serato there is always one color 0xCC for full brightness this is not the case for the resulting colors of Mixxx. This means some cues have a different color on the controller when it played via Mixxx. This must not happen IMHO.

Conclusion, the simple rounding of Display colors does not work. Since the color Mapper does also not fully work in the current state, I guess you need to tweak it a bit to follow the Serato mapping.

Except white, I expect that the color mapper filled with the kSeratoTrackMetadataHotcueColorPalette works perfectly for the Mixxx cue colors because they have a good distance. So you probably only need to add white or a non dazzling gray that is than picked for white.

Swiftb0y commented 1 year ago

You only confused me even more now. To me this is another indicator that our color palettes are suboptimal.

daschuer commented 1 year ago

What is the point of confusion?

The last post is only about the Serato colors. We aim to follow Serato, so there is not much a point of optimizing colors in this case.

daschuer commented 1 year ago

Instead of adding the half value 0x1F, I have adjusted the rounding to match the Serato metadata values: controller_value = ((value >> 6) << 6) + ((value >> 6) << 2),

Swiftb0y commented 1 year ago

So what are you suggesting? Should we change the mixxx color palette? Or should I change my mapping (well knowing that many others that map their serato controller encounter the same issue).

daschuer commented 1 year ago

I think we need a special color mapper for all serato controllers. Requirements:

Problems:

Idea: (Not verified)

This should work for all serato cues. If this results in unique colors with the Mixxx paette needs to be checked. If not we may invent additional intermediate target colors for the mapper with a fixed mapping in the lookup table to fix that issue.

What do you think?

Swiftb0y commented 1 year ago

I can't really give proper judgement here. Especially since this discussion now conflates two different topics (the colors of the tracks exported from serato and the colors produced on serato hardware with the mixxx color palette) each other. As the former case is very niche, I don't think it makes much sense to care about. This issue is only supposed to address the latter one (since I feel like its the most common case). Moreover, as I mentioned previously, the colors shown on the controller when produced by serato are actually worse than what mixxx already does of the box, so I don't think it makes sense to spend effort there to improve that.

As I already mentioned, the truncation would likely work best for many serato controllers if our color palette was just chosen with the truncation in mind.

daschuer commented 1 year ago

Moreover, as I mentioned previously, the colors shown on the controller when produced by serato are actually worse than what mixxx already does of the box, so I don't think it makes sense to spend effort there to improve that.

Do you mean the colors on the controller look bad if you connect a controller to Serato using the shipped Serato mapping and and create cues with Serato?

Swiftb0y commented 1 year ago

Well they don't look bad, they just highlight that the controller doesn't really distinguish between many colors of the palette. See these screenshots (they are supposed to contain the entire serato palette). IMG_20221114_204745 IMG_20221114_204810 IMG_20221114_204820

daschuer commented 1 year ago

That is interesting. Thank you.

I see the following:

Conclusion: Serato sends the fully saturated matadat colors to the controller. One of RGB color is always 0x00

It confirms my table above, playing the same track with Serato shows the Serato colum while playing it with Mixxx it will show the Mixxx column using a simple trunkation: https://github.com/mixxxdj/mixxx/issues/11045#issuecomment-1314561112

For the Mixxx palette it means via truncation: #32be44 becomes #008844 #af00cc becomes #8800cc #fca6d7 becomes #cc88cc

#8800cc is already a pure color, the other two are not a Serato color, either too bright or too dark.

daschuer commented 1 year ago

This shows possible results of a Serato color mapper following Serato's ideas: Color mapper using the Serato GUI colors:

mixxx -------- trunc ------- Serato GUI Serato Meta #C50A08 #CC0000 #C71136 #CC0044 #32BE44 #008844 #4EB648 #88CC00 #42D4F4 #44CCCC #1DBEBD #00CCCC #F8D200 #CCCC00 #FAC313 #CCCC00 #0044FF #0044CC #173BA2 #0000CC #AF00CC #8800CC #CE359E #CC00CC #FCA6D7 #CC88CC #CE359E #CC00CC #F2F2FF #CCCCCC #8DC63F #00CC44

Tweaked version mixxx -------- trunc ------- Serato GUI Serato Meta #C50A08 #CC0000 #C71136 #CC0044 #32BE44 #008844 #4EB648 #88CC00 #42D4F4 #44CCCC #1DBEBD #00CCCC #F8D200 #CCCC00 #FAC313 #CCCC00 #0044FF #0044CC #173BA2 #0000CC #AF00CC #8800CC #8800CC #8800CC #FCA6D7 #CC88CC #CC4488 #CC4488 #F2F2FF #CCCCCC #8DC63F #888888

daschuer commented 1 year ago

This has been produced with: https://github.com/daschuer/mixxx/tree/serato-cue-test

Conclusion:

The alternative proposed solution to change the GUI colors for the Mixxx palette does only help for the Mixxx palette.

I think the source of this issue is that our color CO caries the GUI colors and not the stored colors. We can do the same for the Traktor and Rekordbox colors.

What do you think?

Swiftb0y commented 1 year ago

I disagree. The ColorMapper is supposed to decouple the hardware from the actual palette colors. I strongly object to any proposal that involves forming any kind of dependency between the colors available in mixxx and the colors that can be displayed by the controller (this is also the reason for my previous outrage where you suggested our palette colors are part of our API). The controller is simply supposed to declare what colors it can display and ColorMapper chooses the closest matching one.

I'm still getting confused why you relate this to the way serato cue colors are being written into the metadata or how they are translated when imported.

Swiftb0y commented 1 year ago

I think we've been talking past each other for the last 7 days. So let me rephrase my problem: When truncating our palette colors, green and purple both result in subideal brightness. To fix this, I proposed to make them slightly lighter so they play better when truncated.

What exactly is the problem you are talking about?

daschuer commented 1 year ago

We have two problems:

  1. The proposed solution is only a band aid for some colors, and for a series of a controllers of one manufacturer. Especially the change of purple is a regression for me. The change of green is marginal and would be Ok.
  2. Your controller has only 64 colors where only 16 are considered as usefull. The rest is either to dark or too bright. The color Mapper solves the mapping of all colors to one of the suitable colors. The proposed solution allows also a perfect roundtrip for the Serato case. This is for me a good evidence that it is a good idea.

Did you look into the linked branch? IMHO it solves the problem entirely. If not please describe the case where it fails.

Swiftb0y commented 1 year ago

The proposed solution is only a band aid for some colors, and for a series of a controllers of one manufacturer. Especially the change of purple is a regression for me. The change of green is marginal and would be Ok.

I somewhat disagree with you. The notion of truncating the colors for sending to the controller is universal across controller hardware. It seems to be that truncating to two bits is common, but hardware could truncate more or less bits. Since two bits is common, my fix is two make sure our default color palette saturates the two upper bits in their dominant channel.

Especially the change of purple is a regression for me.

Can you elaborate on? As I said, controllers are not supposed to have a direct dependency on palette colors because those may be subject to change.

Your controller has only 64 colors where only 16 are considered as usefull. The rest is either to dark or too bright.

I disagree with the assumption that only a subset of the colors are useful. It all depends on whats done with colors, so it depends on the palette being used.

The proposed solution allows also a perfect roundtrip for the Serato case.

Roundtrip from/through what exactly?

Did you look into the linked branch?

I'll try to ASAP.

Swiftb0y commented 1 year ago

I read the code in your branch and I don't know what to do with it to be honest. I don't understand why you truncate like you do, I don't understand why you are converting a color from the mixxx palette to a stored serato metadata color with a function that was only supposed to be used in the serato palette (judging by its name and usage examples).

Whatever issue your code solves, I consider it a non-issue.

daschuer commented 1 year ago

I somewhat disagree with you. The notion of truncating the colors for sending to the controller is universal across controller hardware.

I can confirm that. The question is however how the device manufacturer recreates the missing bits. We control this using a color mapper populated with the manufacturer full resolution color.

Since two bits is common, my fix is two make sure our default color palette saturates the two upper bits in their dominant channel.

This is true for green, but our purple has already a main channel fully saturated. We also have intentionally picked colors with different saturation/brightness to make them optimal distinguishable an human recognisable/nameable. We have explicitly not choosen fully saturated colors for the GUI.

Can you elaborate on? Due to the proposed change purple and pink are moved closer together purple is no longer the best purple.

I disagree with the assumption that only a subset of the colors are useful.

Oh, this was my takeway from your posts. For my understanding all not fully saturated colors are not suitable for you. So why you dismiss green green than?

Roundtrip from/through what exactly?

After migration to from Serato to Mixxx and using the trunkated colors, all you cues are looking different on the controller. Some cue buttons are quite dark and colors are even swapped see https://github.com/mixxxdj/mixxx/issues/11045#issuecomment-1314561112 If you recolor the cues to other Sesto colors or Mixxx colors they look bad after going back to serato. This is due the differen color domains at Serato controller/GUI/metadata.

Whatever issue your code solves, I consider it a non-issue.

That is an unfair judgement, without understanding the code.

We have a fixed table to turn GUI colors into controller colors for Serato. The proposed trunkation does not work, so we use a lookup table in the existing code.

My code now matches all colors from our GUI domain to the closest Srato color also from the GUI domain. Than it uses the lookup table to get the matching Controller color.

Since you think there are more colors usefull, we may add them to this model as well. Currently at least white and pink are missing.

Swiftb0y commented 1 year ago

This is true for green, but our purple has already a main channel fully saturated.

Well since purple is a secondary color, its "main channel" really rather meant both the red and the blue channel (and the red channel is inadequately saturated).

Oh, this was my takeway from your posts. For my understanding all not fully saturated colors are not suitable for you. So why you dismiss green green than?

The issue I have with the green (as well as the purple) is that they both seem like they're fully saturated, but aren't when truncated. The less saturated/dimmer colors are still useful on the controller (for example when the color palette has a green which is really that dimm (the serato palette is an example of that )).

After migration to from Serato to Mixxx and using the trunkated colors, all you cues are looking different on the controller. Some cue buttons are quite dark and colors are even swapped

I don't think thats really a problem since as I pointed out (see the three pictures), these colors already look worse when they come from serato compared to when they come from mixxx.

That is an unfair judgement, without understanding the code.

I apologize for the tone.

We have a fixed table to turn GUI colors into controller colors for Serato. The proposed truncation does not work, so we use a lookup table in the existing code.

I don't really understand this. IIUC we convert the exported serato colors to the gui serato colors on import. When those are truncated, the colors produced by the controller are also non-ideal? I'd say thats then also an issue with the serato color palette. Creating a special mapping that irons out these deficiencies doesn't really help, nor does it work really in the general case where a library contains tracks with colors from different palettes.

The problem with the serato gui colors is really a problem in their palette (that is evident by the fact that even serato outputs three different shades of green as the exact same color to the controller). Because of that, I don't see any reason why we should try to perfect this niche case when even serato hardware coupled with serato software creates worse results.