Panning & volume - beginner question

Started by TheRealByteraver, February 17, 2019, 11:15:13

Previous topic - Next topic

TheRealByteraver

In the mixing routine of my player I naively implement volume & panning as follows:

leftVolume   = ( volume * ( 255 - panning ) ) / 256
rightVolume = ( volume *            panning   ) / 256

left   Channel Mix Data +=  ( leftVolume   * sampleData ) / 64
right Channel Mix Data +=  ( rightVolume * sampleData ) / 64

Where volume is in a 0..64 range and panning in a 0..255 range. In practice the order of the divisions are a bit different to prevent loss of resolution (I use fixed point) but this is the general idea.

Now I always supposed this was "accurate enough", and how "most people do it", but it really isn't, right? Since volumes follow a logarithmic scale. According to http://weaselaudiolib.sourceforge.net/#panning_law FastTracker 2 uses a logarithmic curve.

Does anybody have some background information about this, like theory, formulas, lookup tables, how to properly implement volume, panning, and so on?


(Edit: corrected the formula, made a copy-paste error there initially )

Saga Musix

In general: You may want to read up on pan laws, the page you linked already gives you a small selection of different pan laws. There's a lot of literature about the whole topic. You may also want to have a look at this forum post.
There is no perfect answer to the question which pan law to use. Different pan laws are good for different usage scenarios, for example it might be important for some use cases that the levels remain constant when summing a stereo signal to mono in one case. This is why you will actually find the pan law to be configurable in many DAWs.

Now, if all you want is play existing mod music formats, linear pan law is the correct one 99% of the time, except for FT2 which uses square root pan law. For the same reason, even though linear volumes are really not very user-friendly, you will have to use them if you want to write a player for most (but not all) classic module formats.
» No support, bug reports, feature requests via private messages - they will not be answered. Use the forums and the issue tracker so that everyone can benefit from your post.

TheRealByteraver

Thanks for taking the time to reply, I should've searched this forum more extensively first. I might implement the square root thing for FT2 modules later. Right know I'm battling with the vibrato effect. The outcome of the fight is yet undetermined. Stay tuned  ;)

TheRealByteraver

I'm mostly having trouble to determine how to calculate the amplitude in non-linear period mode. I'm contemplating "cheating" and looking at ModPlug's source code  ;D. I'm not a musician so my experience with DAW's is non-existant. I suppose it's with these programs as with anything else in life. Put in the effort, reap the reward :)


Saga Musix

In non-linear mode, you are best off computing everything in periods rather than Hertz, as then pitch slides and vibratos are all just additions. Then convert back to Hertz when you are done with all calculations. Note that the vibrato is only a temporary addition on top of the computed note frequency.
» No support, bug reports, feature requests via private messages - they will not be answered. Use the forums and the issue tracker so that everyone can benefit from your post.

TheRealByteraver

@Saga: when I added the vibrato directly to the frequency it was too discrete, when I added it to the period the vibrato was too heavy. I suppose that my note-to-period calculation does not result in a classic Protracker-type period, that'll be the culprit probably.

Could you just confirm that, given a Vibrato command Hxy, where x is the vibrato speed and y the vibrato depth, the final period is calculated as follows:

             tempPeriod = Period - sin( ( ( ( x * vibratoTickCounter ) % 64 ) * 2 * pi ) / 64 ) * 2 * y

Assuming the vibrato waveform is the default sinus waveform and that we are using the same period scale as the ones stored in a .mod file.  VibratoTickCounter is increased on every tick and decreased with the value 64 whenever it goes over 64.

Also, when the waveform is not retriggered, the vibrato command has to be processed on tick 0 (where the note update happens) as well I guess, whenever it is at least a second contiguous vibrato command? Same question for the arpeggio command by the way, since I can't seem to get ode2pro.mod sound the same on my player as on MPT. It's like I'm missing notes or something.

Saga Musix

Most trackers/players don't directly calculate sin() but use lookup tables - "back then" because it was faster and nowadays simply so that you can easily achieve 100% precise playback that is identical to older trackers. This is because some of the tables contain rounding errors or simply plain wrong values. You will find these tables in a lot of format documentation or OpenMPT's Tables.cpp. Depending on which format you implement you may have to multiply the values from those tables by 4 because XM periods are four times as precise as MOD periods (so all values everywhere are multiplied by 4 essentially). Without knowing how the rest of your code looks like, it's hard to tell what you are doing wrong.

QuoteAlso, when the waveform is not retriggered, the vibrato command has to be processed on tick 0 (where the note update happens) as well I guess, whenever it is at least a second contiguous vibrato command?
This has nothing to do with the waveform retrigger but only when triggering new notes. In the MOD format (at least on ProTracker), vibrato, tremolo and arpeggio is never applied on the first tick so it will indeed sound a bit strange especially at low tempos. Lots of those old players were tuned for low CPU usage, not for being comprehensible to the musician. ;)
In the XM format it's different, there you will find vibrato and tremolo effects to be applied on all ticks.

Ode 2 Protracker exploits a lot of ProTracker peculiarities so it's a module that's hard to get right. It's definitely not the easiest one to start with. ;)
» No support, bug reports, feature requests via private messages - they will not be answered. Use the forums and the issue tracker so that everyone can benefit from your post.

TheRealByteraver

Hi Saga, thanks again for your elaborate answer, it is really helpful. I use a 32-value lookup table with half a sinus function in it (max value == 255). I took it from a document that said it "came straight from Protracker", so I recycled that to get "authentic" playback. My program does also have a s3m + xm loader so I am aware of the "4 times finer period" thing that was introduced by Scream tracker (not sure which version).

I tried to describe the vibrato effect in a "universal" way because I thought it might be more clear to more people that way. Also shorter than posting code.

I had no idea FT2 would also process vibrato & tremelo on tick 0 when Protracker doesn't, thanks for telling :)

ode2pro.mod: what can I say, it is kind of a cool challenge :D At least I got the nested pattern loop thing right so far. It's something ;)

TheRealByteraver

I think I nailed the vibrato command in the meantime. The formula was almost right, it should've been (sign change in red):

tempPeriod = Period + sin( ( ( ( x * vibratoTickCounter ) % 64 ) * 2 * pi ) / 64 ) * 2 * y

where x is the vibrato speed and y is the vibrato depth.

Apparently the frequency decreases first, then increases at the start of the vibrato, rather than the other way around. The problem was not with the amplitude (depth) but with the difference in the sign and the fact that I did not reset the period when the vibrato was finished. Now, tremelo ;)

Saga Musix

Cool to hear that you got it working. Once you tackle the IT format, you will notice that "first decrease then increase" does not always hold... ;) Sadly there is no universal way to handle most pattern effects, which is one of the reasons for the complexity in some of OpenMPT's replayer parts.
» No support, bug reports, feature requests via private messages - they will not be answered. Use the forums and the issue tracker so that everyone can benefit from your post.

TheRealByteraver

Well lo and behold the .IT format indeed has the vibrato's sign reversed! I wonder what the design decision was behind it. Given the perfectionist Jeffrey Lim is, I can't imagine that he broke with tradition by mistake. Thanks for the heads up!

About the .IT format: I have not written a loader yet, but I might indeed in the future, my mixer is built with NNA's in mind.

PS: thanks also for the "maintenance work" on this forum by the way.

Saga Musix

QuoteGiven the perfectionist Jeffrey Lim is, I can't imagine that he broke with tradition by mistake. Thanks for the heads up!
Well, IT is not (and was not meant to be) a perfect MOD or XM replayer in any way. It does many things in a better way while retaining reasonable compatibility. In fact, if you enable the "Old Effects" flag in IT files, the vibrato will be more like in XM (first going down then up), but it's not going for bit-for-bit accuracy there either.
» No support, bug reports, feature requests via private messages - they will not be answered. Use the forums and the issue tracker so that everyone can benefit from your post.

TheRealByteraver

So that is wat this "Old Effects" is about. I thought Impulse Tracker 2.x might have a different effect engine compared to Impulse tracker 1.x or something (I never used Impulse Tracker or any other tracker for that matter). So in reality, Impulse Tracker has a compatibility mode which is not perfect, and Modplug Tracker needs to emulate this mode?

I have to say, the longer I stare into the Abyss, the deeper it gets lol  ;D It is great fun though.

Saga Musix

QuoteSo in reality, Impulse Tracker has a compatibility mode which is not perfect, and Modplug Tracker needs to emulate this mode?
Well, I guess that is a bit of a harsh way to formulate it. As said, it was not the goal to be a perfect replayer for other formats, although IT was meant to be a good S3M player (although it doesn't emulate many of ST3's very quirky behaviours). In the end, "Old Effects" is just one out of many switches in module formats and certainly not the trickiest one to implement. :)
» No support, bug reports, feature requests via private messages - they will not be answered. Use the forums and the issue tracker so that everyone can benefit from your post.

TheRealByteraver

I found the info about the "Old Effects" in Impulse Trackers' documentation in the meantime. It seems quite doable indeed :)

I fixed ode2pro.mod arpeggio's in the meantime (I think). The issue was that, in my mind, arpeggio restarted the sample each time. On every tick. No idea why I thought so, but I did  ::) This effect is mostly used on chiptunes, were it'll make not that much difference, since the samples are miniscule anyway.

I have to thank Jogeir Liljedahl's slow-motion.mod for that discovery.