Skip to content

Commit dd05827

Browse files
committed
Allow changing selected instruments even on '0' patterns.
Bugfixes: After page load, no longer chops off the first bit of audio. Fixed mod instrument indexes getting messed up when adding new instruments. Next bar mod now should not cause audible crackling. Making a new mod pattern and setting the instrument immediately should now actually properly set that instrument. High pass filters and some other circumstances could break custom chip instruments, that should now be fixed.
1 parent 44e7bdb commit dd05827

File tree

5 files changed

+48
-17
lines changed

5 files changed

+48
-17
lines changed

editor/ExportPrompt.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,8 +316,8 @@ export class ExportPrompt implements Prompt {
316316
}
317317
}
318318

319-
this.synth.warmUpSynthesizer(this._doc.song);
320319
this.synth.computeLatestModValues();
320+
this.synth.warmUpSynthesizer(this._doc.song);
321321

322322
this.sampleFrames = this.synth.getTotalSamples(this._enableIntro.checked, this._enableOutro.checked, this.synth.loopRepeatCount);
323323
// Compute how many UI updates will need to run to determine how many

editor/Selection.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,14 @@ export class Selection {
142142
if (digit == "0") digit = "10";
143143
this.instrumentDigits += digit;
144144
var parsed = parseInt(this.instrumentDigits);
145-
var pattern: Pattern | null = this._doc.getCurrentPattern();
146-
if (pattern != null && parsed != 0 && parsed <= this._doc.song.channels[this._doc.channel].instruments.length) {
145+
//var pattern: Pattern | null = this._doc.getCurrentPattern();
146+
if (parsed != 0 && parsed <= this._doc.song.channels[this._doc.channel].instruments.length) {
147147
this.selectInstrument(parsed - 1);
148148
return;
149149
}
150150
this.instrumentDigits = digit;
151151
parsed = parseInt(this.instrumentDigits);
152-
if (pattern != null && parsed != 0 && parsed <= this._doc.song.channels[this._doc.channel].instruments.length) {
152+
if (parsed != 0 && parsed <= this._doc.song.channels[this._doc.channel].instruments.length) {
153153
this.selectInstrument(parsed - 1);
154154
return;
155155
}
@@ -795,7 +795,8 @@ export class Selection {
795795
this._changeInstrument.append(new ChangeSetPatternInstruments(this._doc, channelIndex, instruments, pattern));
796796
}
797797
}
798-
this._doc.record(this._changeInstrument, canReplaceLastChange);
798+
if (!this._changeInstrument.isNoop())
799+
this._doc.record(this._changeInstrument, canReplaceLastChange);
799800
}
800801
} else {
801802
const canReplaceLastChange: boolean = this._doc.lastChangeWas(this._changeInstrument);

editor/SongEditor.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3155,51 +3155,61 @@ export class SongEditor {
31553155
case 48: // 0
31563156
if (canPlayNotes) break;
31573157
this._doc.selection.nextDigit("0", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3158+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31583159
event.preventDefault();
31593160
break;
31603161
case 49: // 1
31613162
if (canPlayNotes) break;
31623163
this._doc.selection.nextDigit("1", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3164+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31633165
event.preventDefault();
31643166
break;
31653167
case 50: // 2
31663168
if (canPlayNotes) break;
31673169
this._doc.selection.nextDigit("2", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3170+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31683171
event.preventDefault();
31693172
break;
31703173
case 51: // 3
31713174
if (canPlayNotes) break;
31723175
this._doc.selection.nextDigit("3", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3176+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31733177
event.preventDefault();
31743178
break;
31753179
case 52: // 4
31763180
if (canPlayNotes) break;
31773181
this._doc.selection.nextDigit("4", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3182+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31783183
event.preventDefault();
31793184
break;
31803185
case 53: // 5
31813186
if (canPlayNotes) break;
31823187
this._doc.selection.nextDigit("5", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3188+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31833189
event.preventDefault();
31843190
break;
31853191
case 54: // 6
31863192
if (canPlayNotes) break;
31873193
this._doc.selection.nextDigit("6", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3194+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31883195
event.preventDefault();
31893196
break;
31903197
case 55: // 7
31913198
if (canPlayNotes) break;
31923199
this._doc.selection.nextDigit("7", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3200+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31933201
event.preventDefault();
31943202
break;
31953203
case 56: // 8
31963204
if (canPlayNotes) break;
31973205
this._doc.selection.nextDigit("8", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3206+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
31983207
event.preventDefault();
31993208
break;
32003209
case 57: // 9
32013210
if (canPlayNotes) break;
32023211
this._doc.selection.nextDigit("9", needControlForShortcuts != (event.shiftKey || event.ctrlKey || event.metaKey), event.altKey);
3212+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], this._doc.getCurrentInstrument(), ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
32033213
event.preventDefault();
32043214
break;
32053215
default:
@@ -3471,6 +3481,7 @@ export class SongEditor {
34713481
if (this._doc.channel >= this._doc.song.pitchChannelCount + this._doc.song.noiseChannelCount) {
34723482
this._piano.forceRender();
34733483
}
3484+
this._renderInstrumentBar(this._doc.song.channels[this._doc.channel], index, ColorConfig.getChannelColor(this._doc.song, this._doc.channel));
34743485
}
34753486

34763487
this.refocusStage();

editor/changes.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2167,7 +2167,7 @@ export class ChangeAddChannelInstrument extends Change {
21672167
let modInstrument: number = instrument.modInstruments[mod];
21682168
let modChannel: number = instrument.modChannels[mod];
21692169

2170-
if (modInstrument >= doc.song.channels[channelIndex].instruments.length && modChannel == doc.channel) {
2170+
if (modChannel == doc.channel && modInstrument >= doc.song.channels[modChannel].instruments.length-1 ) {
21712171
instrument.modInstruments[mod]++;
21722172
}
21732173
}
@@ -2214,12 +2214,12 @@ export class ChangeRemoveChannelInstrument extends Change {
22142214
let modChannel: number = instrument.modChannels[mod];
22152215

22162216
if (modChannel == doc.channel) {
2217-
// Boundary checking - check if setting was 'all' or 'active' previously
2218-
if (modInstrument > doc.song.channels[channelIndex].instruments.length) {
2217+
// Boundary checking - check if setting was previously higher index
2218+
if (modInstrument > removedIndex) {
22192219
instrument.modInstruments[mod]--;
22202220
}
22212221
// Boundary checking - check if setting was set to the last instrument before splice
2222-
else if (modInstrument == doc.song.channels[channelIndex].instruments.length) {
2222+
else if (modInstrument == removedIndex) {
22232223
instrument.modInstruments[mod] = 0;
22242224
instrument.modulators[mod] = 0;
22252225
}

synth/synth.ts

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6225,6 +6225,10 @@ export class Synth {
62256225

62266226
}
62276227
}
6228+
var dummyArray = new Float32Array(1);
6229+
this.isPlayingSong = true;
6230+
this.synthesize(dummyArray, dummyArray, 1, true);
6231+
this.isPlayingSong = false;
62286232
}
62296233

62306234
public computeLatestModValues(): void {
@@ -6827,10 +6831,10 @@ export class Synth {
68276831

68286832
public play(): void {
68296833
if (this.isPlayingSong) return;
6834+
this.computeLatestModValues();
6835+
this.warmUpSynthesizer(this.song);
68306836
this.isPlayingSong = true;
68316837
this.activateAudio();
6832-
this.warmUpSynthesizer(this.song);
6833-
this.computeLatestModValues();
68346838
}
68356839

68366840
public pause(): void {
@@ -7031,6 +7035,7 @@ export class Synth {
70317035
this.part = 0;
70327036
this.tick = 0;
70337037
this.tickSampleCountdown = samplesPerTick;
7038+
this.isAtStartOfTick = true;
70347039

70357040
if (this.loopRepeatCount != 0 && this.bar == this.song.loopStart + this.song.loopLength) {
70367041
this.bar = this.song.loopStart;
@@ -7121,6 +7126,8 @@ export class Synth {
71217126
const limitDecay: number = 1.0 - Math.pow(0.5, 4.0 / this.samplesPerSecond);
71227127
const limitRise: number = 1.0 - Math.pow(0.5, 4000.0 / this.samplesPerSecond);
71237128
let limit: number = +this.limit;
7129+
let skippedBars: number[] = [];
7130+
let firstSkippedBufferIndex: number = -1;
71247131

71257132
let bufferIndex: number = 0;
71267133
while (bufferIndex < outputBufferLength && !ended) {
@@ -7132,7 +7139,7 @@ export class Synth {
71327139
const samplesLeftInTick: number = Math.ceil(this.tickSampleCountdown);
71337140
const runLength: number = Math.min(samplesLeftInTick, samplesLeftInBuffer);
71347141
const runEnd: number = bufferIndex + runLength;
7135-
7142+
71367143
// Handle mod synth
71377144
if (this.isPlayingSong || this.renderingSong) {
71387145
for (let channelIndex: number = song.pitchChannelCount + song.noiseChannelCount; channelIndex < song.getChannelCount(); channelIndex++) {
@@ -7154,8 +7161,20 @@ export class Synth {
71547161

71557162
// Handle next bar mods if they were set
71567163
if (this.wantToSkip) {
7164+
// Unable to continue, as we have skipped back to a previously visited bar without generating new samples, which means we are infinitely skipping.
7165+
// In this case processing will return before the designated number of samples are processed. In other words, silence will be generated.
7166+
let barVisited: boolean = skippedBars.includes(this.bar);
7167+
if (barVisited && bufferIndex == firstSkippedBufferIndex)
7168+
return;
7169+
if (firstSkippedBufferIndex == -1) {
7170+
firstSkippedBufferIndex = bufferIndex;
7171+
}
7172+
if (!barVisited)
7173+
skippedBars.push(this.bar);
7174+
71577175
this.wantToSkip = false;
71587176
this.skipBar();
7177+
continue;
71597178
}
71607179

71617180
for (let channelIndex: number = 0; channelIndex < song.pitchChannelCount + song.noiseChannelCount; channelIndex++) {
@@ -8393,7 +8412,6 @@ export class Synth {
83938412
}
83948413
}
83958414

8396-
83978415
if (instrument.type == InstrumentType.drumset && tone.drumsetPitch == null) {
83988416
// It's possible that the note will change while the user is editing it,
83998417
// but the tone's pitches don't get updated because the tone has already
@@ -8844,7 +8862,8 @@ export class Synth {
88448862
const wave: Float32Array = instrumentState.wave!;
88458863
const volumeScale = instrumentState.volumeScale;
88468864

8847-
const waveLength: number = wave.length - 1; // The first sample is duplicated at the end, don't double-count it.
8865+
// For all but aliasing custom chip, the first sample is duplicated at the end, so don't double-count it.
8866+
const waveLength: number = (aliases && instrumentState.type == InstrumentType.customChipWave) ? wave.length : wave.length - 1;
88488867

88498868
const unisonSign: number = tone.specialIntervalExpressionMult * instrumentState.unison!.sign;
88508869
if (instrumentState.unison!.voices == 1 && !instrumentState.chord!.customInterval) tone.phases[1] = tone.phases[0];
@@ -8910,14 +8929,14 @@ export class Synth {
89108929
inputSample = waveA + waveB * unisonSign;
89118930
}
89128931

8913-
const sample: number = applyFilters(inputSample, initialFilterInput1, initialFilterInput2, filterCount, filters);
8932+
const sample: number = applyFilters(inputSample * volumeScale, initialFilterInput1, initialFilterInput2, filterCount, filters);
89148933
initialFilterInput2 = initialFilterInput1;
8915-
initialFilterInput1 = inputSample;
8934+
initialFilterInput1 = inputSample * volumeScale;
89168935

89178936
phaseDeltaA *= phaseDeltaScaleA;
89188937
phaseDeltaB *= phaseDeltaScaleB;
89198938

8920-
const output: number = sample * expression * volumeScale;
8939+
const output: number = sample * expression;
89218940
expression += expressionDelta;
89228941

89238942
data[sampleIndex] += output;

0 commit comments

Comments
 (0)