feat: added an option to let user choose the vertical resolution of the spectrogram, also fixed a bug that peak of pitch in the spectrogram has been mapped to the start of the note bin instead of the center
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { encodeWav } from './KGOfflineRenderer';
|
||||
import { encodeWav, getOfflineTrackGain, getOfflineTrackVolumeDb } from './KGOfflineRenderer';
|
||||
|
||||
/**
|
||||
* Create a minimal AudioBuffer-like object for testing.
|
||||
@@ -145,3 +145,22 @@ describe('encodeWav', () => {
|
||||
expect(view.getUint32(40, true)).toBe(0); // data size = 0
|
||||
});
|
||||
});
|
||||
|
||||
describe('offline track volume conversion', () => {
|
||||
it('treats 0 dB as unity gain instead of silence', () => {
|
||||
expect(getOfflineTrackVolumeDb(0, false)).toBe(0);
|
||||
expect(getOfflineTrackGain(0, false)).toBe(1);
|
||||
});
|
||||
|
||||
it('converts negative dB values to linear gain', () => {
|
||||
expect(getOfflineTrackVolumeDb(-6, false)).toBe(-6);
|
||||
expect(getOfflineTrackGain(-6, false)).toBeCloseTo(Math.pow(10, -6 / 20), 6);
|
||||
});
|
||||
|
||||
it('silences muted tracks and floor-level volumes', () => {
|
||||
expect(getOfflineTrackVolumeDb(0, true)).toBe(-Infinity);
|
||||
expect(getOfflineTrackGain(0, true)).toBe(0);
|
||||
expect(getOfflineTrackVolumeDb(-60, false)).toBe(-Infinity);
|
||||
expect(getOfflineTrackGain(-60, false)).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { KGProject } from '../KGProject';
|
||||
import type { KGMidiNote } from '../midi/KGMidiNote';
|
||||
import type { KGAudioRegion } from '../region/KGAudioRegion';
|
||||
import { FLUIDR3_INSTRUMENT_MAP } from '../../constants/generalMidiConstants';
|
||||
import { AUDIO_INTERFACE_CONSTANTS } from '../../constants/coreConstants';
|
||||
import { pitchToNoteNameString } from '../../util/midiUtil';
|
||||
import { KGToneBuffersPool } from './KGToneBuffersPool';
|
||||
import { KGToneSamplerFactory } from './KGToneSamplerFactory';
|
||||
@@ -254,9 +255,8 @@ export class KGOfflineRenderer {
|
||||
});
|
||||
});
|
||||
|
||||
// Apply volume
|
||||
const volumeDb = trackInfo.volume > 0 ? 20 * Math.log10(trackInfo.volume) : -Infinity;
|
||||
sampler.volume.value = volumeDb;
|
||||
// Track volumes are stored in dB across the app, with 0 meaning unity gain.
|
||||
sampler.volume.value = getOfflineTrackVolumeDb(trackInfo.volume, trackInfo.muted);
|
||||
sampler.connect(masterGain);
|
||||
|
||||
// Schedule all notes for this track
|
||||
@@ -288,7 +288,7 @@ export class KGOfflineRenderer {
|
||||
for (const trackInfo of audioTrackData) {
|
||||
if (!shouldPlay(trackInfo, hasSoloedTracks)) continue;
|
||||
|
||||
const trackGain = new Tone.Gain(trackInfo.volume);
|
||||
const trackGain = new Tone.Gain(getOfflineTrackGain(trackInfo.volume, trackInfo.muted));
|
||||
trackGain.connect(masterGain);
|
||||
|
||||
for (const regionInfo of trackInfo.regions) {
|
||||
@@ -418,6 +418,16 @@ function shouldPlay(trackInfo: { muted: boolean; solo: boolean }, hasSoloedTrack
|
||||
return true;
|
||||
}
|
||||
|
||||
export function getOfflineTrackVolumeDb(volumeDb: number, muted: boolean): number {
|
||||
const isSilent = muted || volumeDb <= AUDIO_INTERFACE_CONSTANTS.MIN_TRACK_VOLUME_DB;
|
||||
return isSilent ? -Infinity : volumeDb;
|
||||
}
|
||||
|
||||
export function getOfflineTrackGain(volumeDb: number, muted: boolean): number {
|
||||
const effectiveVolumeDb = getOfflineTrackVolumeDb(volumeDb, muted);
|
||||
return Number.isFinite(effectiveVolumeDb) ? Math.pow(10, effectiveVolumeDb / 20) : 0;
|
||||
}
|
||||
|
||||
// ===== WAV ENCODER =====
|
||||
|
||||
/**
|
||||
|
||||
@@ -71,6 +71,7 @@ interface AppConfig {
|
||||
};
|
||||
editor: {
|
||||
playhead_update_frequency: number;
|
||||
spectrogram_height_resolution: 1 | 3 | 5;
|
||||
};
|
||||
chatbox: {
|
||||
default_open: boolean;
|
||||
@@ -243,7 +244,8 @@ export class ConfigManager {
|
||||
},
|
||||
},
|
||||
editor: {
|
||||
playhead_update_frequency: 10
|
||||
playhead_update_frequency: 10,
|
||||
spectrogram_height_resolution: 3
|
||||
},
|
||||
chatbox: {
|
||||
default_open: true
|
||||
@@ -607,4 +609,4 @@ export class ConfigManager {
|
||||
console.error('Error disposing ConfigManager:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user