View Issue Details

IDProjectCategoryView StatusLast Update
0001902OpenMPTFeature Requestpublic2025-06-26 04:23
Reportertwi Assigned To 
PrioritylowSeverityfeatureReproducibilityalways
Status newResolutionopen 
Platformx64OSWindowsOS Version10
Product VersionOpenMPT 1.33.00.* (current testing) 
Summary0001902: Use imported soundfont instruments' panning if it is present
Description

When importing a soundfont and associated MIDI the other day (i needed both the ability to disable sample interpolation and to export individual tracks to wave) i noticed there was some panning missing on one of the instruments. Initially i assumed there were some channel pan events that were being overwritten by some delay events that would have overlapped them, but eventually i noticed in the sample editor that the samples that were panned in the original had their panning values set away from center but their planning flag remained off. I simply checked "Set Pan" and they were then correct.

The provided patch automatically sets the panning flag for an imported MIDI bank's samples, but only if they have an nPan value that is not equal to 128 (center). It also alters the SF2→DLS conversion function to provide regions it creates with a default pan value of 128 as i discovered an issue where unpanned regions took on the panning of some panned region in the list (the last, but then the first in some later iterations of the soundfont...) unless i gave them an explicit pan of 0 in a soundfont editor.

I can't attach it, but i made an example soundfont for testing. It's a modification of the GM.DLS piano which has its samples set to pan left to right based on location on the keyboard https://drive.google.com/drive/folders/1-1IAYZSg_2-DqYUmUJd9Ywx19MjIOgBD?usp=sharing

I'm not terribly familiar with the intricacies of these formats beyond what i learned while creating this patch, so while i did my best, it's very possible that my patch is missing some nuances in the conversion, particularly with the separate values used for percussion instruments. Also, i'm not sure what the desired behavior should be when there is a discontinuous region, that is to say, two regions linked to the same wave data. Currently it appears to take on the panning of the first occurrence of the sample in the list. (try importing ...dup_region.sf2 and compare with its values in a soundfont editor. swap the pan values around and import again.) Though i don't know of any soundfonts that have such regions, let alone with differing pan values.

It would take non-negligibly more work, and likely me figuring out how to work with the MFC stuff, but if this behavior isn't what's desired, there could instead be a dialogue on import informing the user of the panned samples if any are detected and giving them the option to automatically set the flags, or to set manually if/as they wish. it seems relatively rare that this field gets used in soundfonts anyway, so it would be unlikely to pop up too often.

Steps To Reproduce
  • Create a blank module.
  • Import the example .sf2, or a .sf2 that you know contains panned regions, as an instrument.
  • Audition it with the keyboard on latest and observe that the samples do not sound panned. Open sample editor and observe that the pan value for each sample is different than 32, but that "Set Pan" is not checked for them. (In the case of the example, also observe that PIANO64, the middle sample, is set to pan 48 whereas in any soundfont editor/player it will show as unset pan and play in stereo center).
TagsNo tags attached.
Attached Files
Dlsbank.cpp.patch (1,637 bytes)   
Index: soundlib/Dlsbank.cpp
===================================================================
--- soundlib/Dlsbank.cpp	(revision 23607)
+++ soundlib/Dlsbank.cpp	(working copy)
@@ -1293,6 +1293,7 @@
 
 				// Region Default Values
 				int32 regionAttn = 0;
+				int16 regionPan = 128;
 				// Load Generators
 				sf2info.instBags.Seek(ibagcnt * sizeof(SFInstBag));
 				SFInstBag bags[2];
@@ -1300,6 +1301,7 @@
 				sf2info.instGens.Seek(bags[0].wGenNdx * sizeof(SFInstGenList));
 				uint16 lastOp = SF2_GEN_SAMPLEID;
 				int32 loopStart = 0, loopEnd = 0;
+
 				if(!sf2info.instGens.ReadVector(instrGenerators, bags[1].wGenNdx - bags[0].wGenNdx))
 					break;
 				for(const auto &gen : instrGenerators)
@@ -1329,7 +1331,7 @@
 						{
 							int32 pan = static_cast<int16>(value);
 							pan = std::clamp(Util::muldivr(pan + 500, 256, 1000), 0, 256);
-							rgn.panning = static_cast<int16>(pan);
+							regionPan = static_cast<int16>(pan);
 							pDlsEnv->defaultPan = mpt::saturate_cast<uint8>(pan);
 						}
 						break;
@@ -1393,6 +1395,7 @@
 				int32 linearVol = DLS32BitRelativeGainToLinear(((instrAttenuation + regionAttn) * 65536) / 10) / 256;
 				Limit(linearVol, 16, 256);
 				rgn.usVolume = static_cast<uint16>(linearVol);
+				rgn.panning = regionPan;
 
 				if(lastOp != SF2_GEN_SAMPLEID && nRgn == 0)
 					globalZone = rgn;
@@ -1911,6 +1914,7 @@
 			sample.nGlobalVol = (uint8)(lVolume / 4);	// 0-64
 		}
 		sample.nPan = GetPanning(nIns, nRgn);
+		if(sample.nPan != 128) sample.uFlags |= CHN_PANNING;
 
 		sample.Convert(MOD_TYPE_IT, sndFile.GetType());
 		sample.PrecomputeLoops(sndFile, false);
Dlsbank.cpp.patch (1,637 bytes)   
Has the bug occurred in previous versions?
Tested code revision (in case you know it)

Activities

Saga Musix

Saga Musix

2025-06-24 22:07

administrator   ~0006404

I'm not sure this is a good idea. Maybe I'm wrong but from my understanding, panning in soundfonts would normally be additive, i.e. if the soundfont patch is panned somewhat to the left and the MIDI channel is panned center, then the result would be panned somewhat to the left, but if the MIDI channel is panned a bit to the right then the result would be a centered channel again. This is not how panning in modules (MPTM files in particular works): If an instrument/sample has panning enabled, it will completely override the channel panning that may have previously been set. So in a MIDI import, this would render MIDI CC 10 (pan position) useless for such instruments. This is the reason why panning is imported for reference purposes, but not enabled by default.

A more viable approach might be to import this setting as a panning envelope, since those are additive (i.e. they don't overwrite the channel panning).

twi

twi

2025-06-26 04:05

reporter   ~0006405

Last edited: 2025-06-26 04:18

Your understanding of inst/sample pan settings, pan envelopes, and the way soundfont instruments affect panning on the midi channel they're played is all correct as far as i've tested (with the nuance that in mptm's case, if the channel then switches to an unpanned instrument/sample, it will go right back to the previously set panning, something that was a bit ambiguous in the wording). I didn't really give myself the full picture of how exactly these things interacted because at the time that felt a little bit beside the point of what i was interested in. I wasn't too enthused about this exact solution either but it was all i could come up with within the bounds of what i could see was doable.

It sounds like the right solution to this would take more than a tweak then, a pretty substantial addition/rework to the way instrument envelopes work in fact, since as far as i can tell there can only be one volume/pitch/pan/filter envelope per instrument, and this setting would have to be per region. More work and change than i really would be willing to ask for in service of something that it seems few would actually use/care about, though i'm always in favor of closer accuracy for imported instruments and greater flexibility for custom instruments as long as it doesn't make the UI needlessly complicated. Maybe i'll try to muck around in the code at some point and try to come up with an agreeable solution, though i can't say it would necessarily be happening anytime soon.

EDIT: just to note for my own reference if nothing else, the panning in soundfonts goes like this: a region's pan fully overrides the instrument's if both are set, but an instrument's (and a region's; if a region's pan is unset but the global value is it just gets filled in, so to speak) is additive to the preset in the same manner it is to the channel's pan (i.e, +20 and -20 would cancel out). So it would actually likely not be too hard to import an instrument's global panning value to the pan envelope, but i don't know that that would really be of use to anyone.

Issue History

Date Modified Username Field Change
2025-06-24 21:05 twi New Issue
2025-06-24 21:05 twi File Added: Dlsbank.cpp.patch
2025-06-24 21:11 twi Has the bug occurred in previous versions? All previous versions as far as i know =>
2025-06-24 22:07 Saga Musix Note Added: 0006404
2025-06-26 04:05 twi Note Added: 0006405
2025-06-26 04:16 twi Note Edited: 0006405
2025-06-26 04:17 twi Note Edited: 0006405
2025-06-26 04:18 twi Note Edited: 0006405
2025-06-26 04:23 twi Priority normal => low
2025-06-26 04:23 twi Severity tweak => feature
2025-06-26 04:23 twi Summary Flag imported soundfonts' panned samples to use their pan values => Use imported soundfont instruments' panning if it is present