Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Dead Slap #1742

Merged
merged 1 commit into from
Nov 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/exporter/GpifWriter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -740,7 +740,10 @@ export class GpifWriter {
if (beat.slashed) {
beatNode.addElement('Slashed');
}
if (!beat.isRest) {
if (beat.deadSlapped) {
beatNode.addElement('DeadSlapped');
}
if (beat.notes.length > 0) {
beatNode.addElement('Notes').innerText = beat.notes.map(n => n.id).join(' ');
}

Expand Down
1 change: 1 addition & 0 deletions src/generated/model/BeatCloner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class BeatCloner {
clone.tap = original.tap;
clone.text = original.text;
clone.slashed = original.slashed;
clone.deadSlapped = original.deadSlapped;
clone.brushType = original.brushType;
clone.brushDuration = original.brushDuration;
clone.tupletDenominator = original.tupletDenominator;
Expand Down
4 changes: 4 additions & 0 deletions src/generated/model/BeatSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class BeatSerializer {
o.set("tap", obj.tap);
o.set("text", obj.text);
o.set("slashed", obj.slashed);
o.set("deadslapped", obj.deadSlapped);
o.set("brushtype", obj.brushType as number);
o.set("brushduration", obj.brushDuration);
o.set("tupletdenominator", obj.tupletDenominator);
Expand Down Expand Up @@ -140,6 +141,9 @@ export class BeatSerializer {
case "slashed":
obj.slashed = v! as boolean;
return true;
case "deadslapped":
obj.deadSlapped = v! as boolean;
return true;
case "brushtype":
obj.brushType = JsonHelper.parseEnum<BrushType>(v, BrushType)!;
return true;
Expand Down
8 changes: 6 additions & 2 deletions src/importer/AlphaTexImporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ export class AlphaTexImporter extends ScoreImporter {
this._score.tracks[track].applyLyrics(lyrics);
}
for (const [sustainPedal, beat] of this._sustainPedalToBeat) {
if(sustainPedal.ratioPosition === 0) {
if (sustainPedal.ratioPosition === 0) {
const duration = beat.voice.bar.masterBar.calculateDuration();
sustainPedal.ratioPosition = beat.playbackStart / duration;
}
Expand Down Expand Up @@ -1601,7 +1601,7 @@ export class AlphaTexImporter extends ScoreImporter {
beat.voice.bar.sustainPedals.push(sustainPedal);
this._sy = this.newSy();
return true;
} else if (syData === 'spe') {
} else if (syData === 'spe') {
const sustainPedal = new SustainPedalMarker();
sustainPedal.pedalType = SustainPedalMarkerType.Up;
sustainPedal.ratioPosition = 1;
Expand All @@ -1613,6 +1613,10 @@ export class AlphaTexImporter extends ScoreImporter {
beat.slashed = true;
this._sy = this.newSy();
return true;
} else if (syData === 'ds') {
beat.deadSlapped = true;
this._sy = this.newSy();
return true;
} else {
// string didn't match any beat effect syntax
return false;
Expand Down
3 changes: 3 additions & 0 deletions src/importer/GpifParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,9 @@ export class GpifParser {
case 'Slashed':
beat.slashed = true;
break;
case 'DeadSlapped':
beat.deadSlapped = true;
break;
}
}
}
Expand Down
49 changes: 35 additions & 14 deletions src/midi/MidiFileGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { Settings } from '@src/Settings';
import { Logger } from '@src/Logger';
import { SynthConstants } from '@src/synth/SynthConstants';
import { PercussionMapper } from '@src/model/PercussionMapper';
import { DynamicValue } from '@src/model';

export class MidiNoteDuration {
public noteOnly: number = 0;
Expand Down Expand Up @@ -206,7 +207,12 @@ export class MidiFileGenerator {
return Math.max(value, -1) + 1;
}

private generateMasterBar(masterBar: MasterBar, previousMasterBar: MasterBar | null, currentTick: number, currentTempo: number): void {
private generateMasterBar(
masterBar: MasterBar,
previousMasterBar: MasterBar | null,
currentTick: number,
currentTempo: number
): void {
// time signature
if (
!previousMasterBar ||
Expand All @@ -225,20 +231,20 @@ export class MidiFileGenerator {

// tempo
if (masterBar.tempoAutomations.length > 0) {
if(masterBar.tempoAutomations[0].ratioPosition > 0) {
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(currentTick, currentTempo))
if (masterBar.tempoAutomations[0].ratioPosition > 0) {
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(currentTick, currentTempo));
}

for (const automation of masterBar.tempoAutomations) {
const tick = currentTick + masterBarDuration * automation.ratioPosition;
this._handler.addTempo(tick, automation.value);
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(tick, automation.value))
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(tick, automation.value));
}
} else if (!previousMasterBar) {
this._handler.addTempo(currentTick, masterBar.score.tempo);
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(currentTick, masterBar.score.tempo))
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(currentTick, masterBar.score.tempo));
} else {
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(currentTick, currentTempo))
masterBarLookup.tempoChanges.push(new MasterBarTickLookupTempoChange(currentTick, currentTempo));
}

masterBarLookup.masterBar = masterBar;
Expand Down Expand Up @@ -299,12 +305,7 @@ export class MidiFileGenerator {

private _currentTripletFeel: TripletFeelDurations | null = null;

private generateBeat(
beat: Beat,
barStartTick: number,
realBar: Bar,
tempoOnBeatStart: number
): void {
private generateBeat(beat: Beat, barStartTick: number, realBar: Bar, tempoOnBeatStart: number): void {
let beatStart: number = beat.playbackStart;
let audioDuration: number = beat.playbackDuration;
const masterBarDuration = beat.voice.bar.masterBar.calculateDuration();
Expand All @@ -331,7 +332,7 @@ export class MidiFileGenerator {
if (realBar === beat.voice.bar) {
this.tickLookup.addBeat(beat, beatStart, audioDuration);
} else {
// in case of simile marks where we repeat we also register
// in case of simile marks where we repeat we also register
this.tickLookup.addBeat(beat, 0, audioDuration);
}

Expand All @@ -341,6 +342,8 @@ export class MidiFileGenerator {
}
if (beat.isRest) {
this._handler.addRest(track.index, barStartTick + beatStart, track.playbackInfo.primaryChannel);
} else if (beat.deadSlapped) {
this.generateDeadSlap(beat, barStartTick + beatStart);
} else {
let brushInfo = this.getBrushInfo(beat);
for (const n of beat.notes) {
Expand Down Expand Up @@ -453,6 +456,24 @@ export class MidiFileGenerator {
return durations;
}

private generateDeadSlap(beat: Beat, beatStart: number): void {
// we generate dead-slap as 64th note on all strings (0 fret)
const deadSlapDuration = MidiUtils.toTicks(Duration.SixtyFourth);
const staff = beat.voice.bar.staff;
if (staff.tuning.length > 0) {
for (const t of staff.tuning) {
this._handler.addNote(
staff.track.index,
beatStart,
deadSlapDuration,
t,
MidiUtils.dynamicToVelocity(DynamicValue.F as number),
staff.track.playbackInfo.primaryChannel
);
}
}
}

private generateNote(
note: Note,
beatStart: number,
Expand Down
7 changes: 6 additions & 1 deletion src/model/Beat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ export class Beat {
public duration: Duration = Duration.Quarter;

public get isRest(): boolean {
return this.isEmpty || this.notes.length === 0;
return this.isEmpty || (!this.deadSlapped && this.notes.length === 0);
}

/**
Expand Down Expand Up @@ -246,6 +246,11 @@ export class Beat {
*/
public slashed: boolean = false;

/**
* Whether this beat should rendered and played as "dead slapped".
*/
public deadSlapped: boolean = false;

/**
* Gets or sets the brush type applied to the notes of this beat.
*/
Expand Down
6 changes: 5 additions & 1 deletion src/rendering/LineBarRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ export abstract class LineBarRenderer extends BarRendererBase {
return false;
}

if(beat.deadSlapped) {
return false;
}

// we don't have an X-position: cannot paint a flag
if (!h.hasBeatLineX(beat)) {
return false;
Expand Down Expand Up @@ -540,7 +544,7 @@ export abstract class LineBarRenderer extends BarRendererBase {
protected paintBar(cx: number, cy: number, canvas: ICanvas, h: BeamingHelper): void {
for (let i: number = 0, j: number = h.beats.length; i < j; i++) {
let beat: Beat = h.beats[i];
if (!h.hasBeatLineX(beat)) {
if (!h.hasBeatLineX(beat) || beat.deadSlapped) {
continue;
}

Expand Down
37 changes: 37 additions & 0 deletions src/rendering/glyphs/DeadSlappedBeatGlyph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ICanvas } from '@src/platform';
import { Glyph } from './Glyph';
import { LineBarRenderer } from '../LineBarRenderer';

export class DeadSlappedBeatGlyph extends Glyph {
public constructor() {
super(0, 0);
}

public override doLayout(): void {
this.width = 26 * this.scale;
}

public override paint(cx: number, cy: number, canvas: ICanvas): void {
const renderer = (this.renderer as LineBarRenderer);
const crossHeight = renderer.getLineHeight(renderer.heightLineCount - 1);

const staffTop = renderer.getLineY(0);
const staffHeight = renderer.getLineHeight(renderer.drawnLineCount - 1);

// center X on staff
const centerY = (staffTop + staffHeight / 2) - (crossHeight / 2);


const lw = canvas.lineWidth;
canvas.lineWidth = this.scale * 2;
canvas.moveTo(cx + this.x, cy + centerY);
canvas.lineTo(cx + this.x + this.width, cy + centerY + crossHeight);

canvas.moveTo(cx + this.x, cy + centerY + crossHeight);
canvas.lineTo(cx + this.x + this.width, cy + centerY);

canvas.stroke();

canvas.lineWidth = lw;
}
}
Loading
Loading