Skip to content

Commit

Permalink
Fix DLS Write for WINAMP
Browse files Browse the repository at this point in the history
  • Loading branch information
spessasus committed Nov 26, 2024
1 parent 4729a6a commit 23d4bd9
Show file tree
Hide file tree
Showing 23 changed files with 262 additions and 189 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "SpessaSynth",
"version": "3.23.5",
"version": "3.23.6",
"type": "module",
"scripts": {
"start": "node src/website/server/server.js",
Expand Down
8 changes: 4 additions & 4 deletions src/spessasynth_lib/midi_parser/rmidi_writer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { combineArrays, IndexedByteArray } from "../utils/indexed_array.js";
import { writeMIDIFile } from "./midi_writer.js";
import { writeRIFFOddSize } from "../soundfont/basic_soundfont/riff_chunk.js";
import { getStringBytes } from "../utils/byte_functions/string.js";
import { getStringBytes, getStringBytesZero } from "../utils/byte_functions/string.js";
import { messageTypes, midiControllers, MidiMessage } from "./midi_message.js";
import { DEFAULT_PERCUSSION } from "../synthetizer/synthetizer.js";
import { getGsOn } from "./midi_editor.js";
Expand Down Expand Up @@ -409,7 +409,7 @@ export function writeRMIDI(
minute: "numeric"
});
infoContent.push(
writeRIFFOddSize(RMIDINFOChunks.creationDate, getStringBytes(today), true)
writeRIFFOddSize(RMIDINFOChunks.creationDate, getStringBytesZero(today), true)
);
}
// comment
Expand Down Expand Up @@ -471,7 +471,7 @@ export function writeRMIDI(
// use midi copyright if possible
const copyright = mid.copyright.length > 0 ? mid.copyright : DEFAULT_COPYRIGHT;
infoContent.push(
writeRIFFOddSize(RMIDINFOChunks.copyright, getStringBytes(copyright))
writeRIFFOddSize(RMIDINFOChunks.copyright, getStringBytesZero(copyright))
);
}

Expand All @@ -488,7 +488,7 @@ export function writeRMIDI(
encoding = FORCED_ENCODING;
}
// encoding
infoContent.push(writeRIFFOddSize(RMIDINFOChunks.encoding, getStringBytes(encoding)));
infoContent.push(writeRIFFOddSize(RMIDINFOChunks.encoding, getStringBytesZero(encoding)));

// combine and write out
const infodata = combineArrays(infoContent);
Expand Down
6 changes: 4 additions & 2 deletions src/spessasynth_lib/soundfont/basic_soundfont/modulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,16 @@ export class Modulator
/**
* @param mod1 {Modulator}
* @param mod2 {Modulator}
* @param checkAmount {boolean}
* @returns {boolean}
*/
static isIdentical(mod1, mod2)
static isIdentical(mod1, mod2, checkAmount = false)
{
return (mod1.sourceEnum === mod2.sourceEnum)
&& (mod1.modulatorDestination === mod2.modulatorDestination)
&& (mod1.secondarySourceEnum === mod2.secondarySourceEnum)
&& (mod1.transformType === mod2.transformType);
&& (mod1.transformType === mod2.transformType)
&& (!checkAmount || (mod1.transformAmount === mod2.transformAmount));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export function writeRIFFOddSize(header, data, addZeroByte = false, isList = fal
if (addZeroByte)
{
const tempData = new Uint8Array(data.length + 1);
tempData.set(data, 0);
tempData.set(data);
data = tempData;
}
let offset = 8;
Expand Down
17 changes: 17 additions & 0 deletions src/spessasynth_lib/soundfont/basic_soundfont/write_dls/art2.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import { Generator, generatorTypes } from "../generator.js";
import { writeDword } from "../../../utils/byte_functions/little_endian.js";
import { consoleColors } from "../../../utils/other.js";
import { SpessaSynthInfo, SpessaSynthWarn } from "../../../utils/loggin.js";
import { Modulator } from "../modulator.js";
import {
DEFAULT_DLS_CHORUS,
DEFAULT_DLS_REVERB,
DLS_1_NO_VIBRATO_MOD,
DLS_1_NO_VIBRATO_PRESSURE
} from "../../dls/dls_sources.js";

const invalidGeneratorTypes = new Set([
generatorTypes.sampleModes,
Expand Down Expand Up @@ -108,6 +115,16 @@ export function writeArticulator(zone)
*/
const modulators = zone.modulators.reduce((arrs, m) =>
{
// do not write the default DLS modulators
if (
Modulator.isIdentical(m, DEFAULT_DLS_CHORUS, true) ||
Modulator.isIdentical(m, DEFAULT_DLS_REVERB, true) ||
Modulator.isIdentical(m, DLS_1_NO_VIBRATO_MOD, true) ||
Modulator.isIdentical(m, DLS_1_NO_VIBRATO_PRESSURE, true)
)
{
return arrs;
}
const art = getDLSArticulatorFromSf2Modulator(m);
if (art !== undefined)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { combineZones } from "./combine_zones.js";
import { writeRIFFOddSize } from "../riff_chunk.js";
import { writeDword } from "../../../utils/byte_functions/little_endian.js";
import { writeDLSRegion } from "./rgn2.js";
import { getStringBytes } from "../../../utils/byte_functions/string.js";
import { getStringBytesZero } from "../../../utils/byte_functions/string.js";
import { writeArticulator } from "./art2.js";
import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd } from "../../../utils/loggin.js";
import { consoleColors } from "../../../utils/other.js";
Expand Down Expand Up @@ -84,7 +84,7 @@ export function writeIns(preset)
// writeINFO
const inam = writeRIFFOddSize(
"INAM",
getStringBytes(preset.presetName)
getStringBytesZero(preset.presetName)
);
const info = writeRIFFOddSize(
"INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,16 +257,21 @@ export function getDLSArticulatorFromSf2Modulator(mod)
return undefined;
}
let source = getDLSSourceFromSf2Source(mod.sourceUsesCC, mod.sourceIndex);
let sourceCurve = mod.sourceCurveType;
let sourceTransformType = mod.sourceCurveType;
let sourceBipolar = mod.sourcePolarity;
let sourceDirection = mod.sourceDirection;
if (source === undefined)
{
SpessaSynthWarn(`Invalid source: ${mod.sourceIndex}, CC: ${mod.sourceUsesCC}`);
return undefined;
}
// attenuation is the opposite of gain. Invert
if (mod.modulatorDestination === generatorTypes.initialAttenuation)
{
sourceDirection = sourceDirection === 1 ? 0 : 1;
}
let control = getDLSSourceFromSf2Source(mod.secSrcUsesCC, mod.secSrcIndex);
let controlCurve = mod.secSrcCurveType;
let controlTransformType = mod.secSrcCurveType;
let controlBipolar = mod.secSrcPolarity;
let controlDirection = mod.secSrcDirection;
if (control === undefined)
Expand All @@ -288,12 +293,12 @@ export function getDLSArticulatorFromSf2Modulator(mod)
amt = specialCombo.amt;
// move source to control
control = source;
controlCurve = sourceCurve;
controlTransformType = sourceTransformType;
controlBipolar = sourceBipolar;
controlDirection = sourceDirection;

// set source as static as it's either: env, lfo or keynum
sourceCurve = modulatorCurveTypes.linear;
sourceTransformType = modulatorCurveTypes.linear;
sourceBipolar = specialCombo.isBipolar ? 1 : 0;
sourceDirection = 0;
source = specialCombo.source;
Expand All @@ -307,11 +312,12 @@ export function getDLSArticulatorFromSf2Modulator(mod)

// source curve type maps to desfont curve type in section 2.10, table 9
let transform = 0;
transform |= controlCurve << 4;
transform |= controlTransformType << 4;
transform |= controlBipolar << 8;
transform |= controlDirection << 9;

transform |= sourceCurve << 10;
// use use the source curve in output transform
transform |= sourceTransformType;
transform |= sourceBipolar << 14;
transform |= sourceDirection << 15;
return new Articulator(
Expand Down
55 changes: 30 additions & 25 deletions src/spessasynth_lib/soundfont/basic_soundfont/write_dls/rgn2.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ import { writeArticulator } from "./art2.js";
export function writeDLSRegion(zone, globalZone)
{
// region header
const rgnhData = new IndexedByteArray(14);
const rgnhData = new IndexedByteArray(12);
// keyRange
writeWord(rgnhData, Math.max(zone.keyRange.min, 0));
writeWord(rgnhData, zone.keyRange.max);
// velRange
writeWord(rgnhData, Math.max(zone.velRange.min, 0));
writeWord(rgnhData, zone.velRange.max);
// fusOptions
// fusOptions: 0 it seems
writeWord(rgnhData, 0);
// keyGroup (exclusive class)
const exclusive = zone.getGeneratorValue(generatorTypes.exclusiveClass, 0);
Expand Down Expand Up @@ -71,36 +71,41 @@ export function writeDLSRegion(zone, globalZone)
const wlnkData = new IndexedByteArray(12);
writeWord(wlnkData, 0); // fusOptions
writeWord(wlnkData, 0); // usPhaseGroup
let sampleType = 0;
switch (zone.sample.sampleType)
{
default:
case 1:
case 4:
// mono/left
sampleType = 0;
break;

case 2:
// right
sampleType = 1;
}
writeDword(wlnkData, sampleType); // ulChannel
// let sampleType = 0;
// switch (zone.sample.sampleType)
// {
// default:
// case 1:
// case 4:
// // mono/left
// sampleType = 0;
// break;
//
// case 2:
// // right
// sampleType = 1;
// }
// 1 means that the first bit is on so mono/left
writeDword(wlnkData, 1); // ulChannel
writeDword(wlnkData, this.samples.indexOf(zone.sample)); // ulTableIndex
const wlnk = writeRIFFOddSize(
"wlnk",
wlnkData
);

// art
const art2 = writeArticulator(zone);

const lar2 = writeRIFFOddSize(
"lar2",
art2,
false,
true
);
let lar2 = new IndexedByteArray(0);
if (zone.modulators.length + zone.generators.length > 0)
{
const art2 = writeArticulator(zone);

lar2 = writeRIFFOddSize(
"lar2",
art2,
false,
true
);
}

return writeRIFFOddSize(
"rgn2",
Expand Down
11 changes: 8 additions & 3 deletions src/spessasynth_lib/soundfont/basic_soundfont/write_dls/wave.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { combineArrays, IndexedByteArray } from "../../../utils/indexed_array.js
import { writeDword, writeWord } from "../../../utils/byte_functions/little_endian.js";
import { writeRIFFOddSize } from "../riff_chunk.js";
import { writeWavesample } from "./wsmp.js";
import { getStringBytes } from "../../../utils/byte_functions/string.js";
import { getStringBytesZero } from "../../../utils/byte_functions/string.js";
import { SpessaSynthInfo } from "../../../utils/loggin.js";
import { consoleColors } from "../../../utils/other.js";

Expand All @@ -23,14 +23,19 @@ export function writeDLSSample(sample)
"fmt ",
fmtData
);
let loop = 1;
if (sample.sampleLoopStartIndex + Math.abs(sample.getAudioData().length - sample.sampleLoopEndIndex) < 2)
{
loop = 0;
}
const wsmp = writeWavesample(
sample,
sample.samplePitch,
sample.samplePitchCorrection,
0,
sample.sampleLoopStartIndex,
sample.sampleLoopEndIndex,
1
loop
);
const audio = sample.getAudioData();
let data;
Expand Down Expand Up @@ -60,7 +65,7 @@ export function writeDLSSample(sample)

const inam = writeRIFFOddSize(
"INAM",
getStringBytes(sample.sampleName)
getStringBytesZero(sample.sampleName)
);
const info = writeRIFFOddSize(
"INFO",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { writeRIFFOddSize } from "../riff_chunk.js";
import { writeDword } from "../../../utils/byte_functions/little_endian.js";
import { combineArrays, IndexedByteArray } from "../../../utils/indexed_array.js";
import { writeLins } from "./lins.js";
import { getStringBytes, writeStringAsBytes } from "../../../utils/byte_functions/string.js";
import { getStringBytesZero, writeStringAsBytes } from "../../../utils/byte_functions/string.js";
import { writeWavePool } from "./wvpl.js";
import { SpessaSynthGroupCollapsed, SpessaSynthGroupEnd, SpessaSynthInfo } from "../../../utils/loggin.js";
import { consoleColors } from "../../../utils/other.js";
Expand Down Expand Up @@ -80,7 +80,8 @@ export function writeDLS()
infos.push(
writeRIFFOddSize(
info,
getStringBytes(data)
getStringBytesZero(data),
true
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export function writeWavesample(
// gain correction: Each unit of gain represents 1/655360 dB
const lGain = Math.floor(attenuationCb * -65536);
writeDword(wsmpData, lGain);
// fulOptions
writeDword(wsmpData, 0);
// fulOptions: has to be 2 according to all DLS files I have
writeDword(wsmpData, 2);

const loopSize = loopEnd - loopStart;
let ulLoopType = 0;
Expand Down
22 changes: 12 additions & 10 deletions src/spessasynth_lib/soundfont/dls/articulator_converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,15 @@ function checkForSpecialDLSCombo(source, destination)
* @param destination {number}
* @param value {number}
* @param transform {number}
* @param msg {string}
*/
function modulatorConverterDebug(
export function modulatorConverterDebug(
source,
control,
destination,
value,
transform
transform,
msg = "Attempting to convert the following DLS Articulator to SF2 Modulator:"
)
{
const type = Object.keys(DLSDestinations).find(k => DLSDestinations[k] === destination);
Expand All @@ -204,7 +206,7 @@ function modulatorConverterDebug(
const srcString = srcType ? srcType : source.toString(16);
const ctrlString = ctrlType ? ctrlType : control.toString(16);
console.debug(
`%cAttempting to convert the following DLS Articulator to SF2 Modulator:
`%c${msg}
Source: %c${srcString}%c
Control: %c${ctrlString}%c
Destination: %c${typeString}%c
Expand Down Expand Up @@ -240,6 +242,13 @@ export function getSF2ModulatorFromArticulator(
value
)
{
// modulatorConverterDebug(
// source,
// control,
// destination,
// value,
// transform
// );
// check for special combinations
const specialDestination = checkForSpecialDLSCombo(source, destination);
/**
Expand Down Expand Up @@ -320,13 +329,6 @@ export function getSF2ModulatorFromArticulator(
// special case: for attenuation, invert source (dls gain is the opposite of sf2 attenuation)
if (destinationGenerator === generatorTypes.initialAttenuation)
{
// modulatorConverterDebug(
// source,
// control,
// destination,
// value,
// transform
// );
// if the value is negative, the source shall be negative!
// why?
// Idk, it makes it work with ROCK.RMI and NOKIA_S30.dls
Expand Down
Loading

0 comments on commit 23d4bd9

Please sign in to comment.