Skip to content

Commit

Permalink
Add chord_snap_threshold
Browse files Browse the repository at this point in the history
  • Loading branch information
Geomitron committed Jun 25, 2024
1 parent 139583f commit f318a0d
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 94 deletions.
5 changes: 5 additions & 0 deletions .changeset/smart-ducks-eat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"scan-chart": minor
---

Add chord_snap_threshold
34 changes: 7 additions & 27 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,16 @@ interface Chart {
/** Overrides the .mid note number for Star Power on 5-Fret Guitar. Valid values are 103 and 116. Only applies to .mid charts. */
multiplier_note?: number
/**
* For .mid charts, setting this causes any sustains shorter than the threshold (in number of ticks) to be reduced to length 0.
* For .mid charts, setting this causes any sustains not larger than the threshold (in number of ticks) to be reduced to length 0.
* By default, this happens to .mid sustains shorter than 1/12 step.
*/
sustain_cutoff_threshold?: number
/**
* Notes at or closer than this threshold (in number of ticks) will be merged into a chord.
* All note and modifier ticks are set to the tick of the earliest merged note.
* All note sustains are set to the length of the shortest merged note.
*/
chord_snap_threshold?: number
/**
* The amount of time that should be skipped from the beginning of the video background in milliseconds.
* A negative value will delay the start of the video by that many milliseconds.
Expand Down Expand Up @@ -309,32 +315,6 @@ type NoteType =
| 'blueTomOrCymbalMarker'
| 'greenTomOrCymbalMarker'

type FolderIssueType =
| 'noMetadata' // This chart doesn't have "song.ini"
| 'invalidIni' // .ini file is not named "song.ini"
| 'invalidMetadata' // "song.ini" doesn't have a "[Song]" section
| 'badIniLine' // This line in "song.ini" couldn't be parsed
| 'multipleIniFiles' // This chart has multiple .ini files
| 'noAlbumArt' // This chart doesn't have album art
| 'albumArtSize' // This chart's album art is not 500x500 or 512x512
| 'badAlbumArt' // This chart's album art couldn't be parsed
| 'multipleAlbumArt' // This chart has multiple album art files
| 'noAudio' // This chart doesn't have an audio file
| 'invalidAudio' // Audio file is not a valid audio stem name
| 'badAudio' // This chart's audio couldn't be parsed
| 'multipleAudio' // This chart has multiple audio files of the same stem
| 'noChart' // This chart doesn't have "notes.chart"/"notes.mid"
| 'invalidChart' // .chart/.mid file is not named "notes.chart"/"notes.mid"
| 'badChart' // This chart's .chart/.mid file couldn't be parsed
| 'multipleChart' // This chart has multiple .chart/.mid files
| 'badVideo' // This chart has a video background that will not work on Linux
| 'multipleVideo' // This chart has multiple video background files

type MetadataIssueType =
| 'missingValue' // Metadata is missing a required value
| 'invalidValue' // Metadata property was set to an unsupported value
| 'extraValue' // Metadata contains a property that should not be included

interface ParsedChart {
resolution: number
drumType: DrumType | null
Expand Down
50 changes: 40 additions & 10 deletions src/chart/chart-scanner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { md5 } from 'js-md5'
import * as _ from 'lodash'
import { base64url } from 'rfc4648'

import { defaultMetadata } from 'src/ini'
import { ChartIssueType, Difficulty, FolderIssueType, getInstrumentType, Instrument, instrumentTypes, NotesData } from '../interfaces'
import { getExtension, hasChartExtension, hasChartName, msToExactTime } from '../utils'
import { IniChartModifiers, NoteEvent, noteFlags, NoteType, noteTypes } from './note-parsing-interfaces'
Expand Down Expand Up @@ -454,7 +455,6 @@ function findChartIssues(
}
}

if (instrumentType !== instrumentTypes.drums) {
// brokenNote
{
for (let i = 1; i < track.noteEventGroups.length; i++) {
Expand All @@ -473,6 +473,7 @@ function findChartIssues(
}
}

if (instrumentType !== instrumentTypes.drums) {
// badSustainGap, babySustain
{
/** Sustain gaps at the end of notes already checked in the for loop. `startTime` is inclusive, `endTime` is exclusive. */
Expand Down Expand Up @@ -526,18 +527,47 @@ function typeCount(noteGroup: NoteEvent[], types: NoteType[]) {
}

function getChartHash(chartBytes: Uint8Array, iniChartModifiers: IniChartModifiers) {
const iniChartModifierSize = 4 + 1 + 4 + 4 + 1 + 1
const buffer = new ArrayBuffer(chartBytes.length + iniChartModifierSize)
const hashedIniModifiers = (
[
{ name: 'hopo_frequency', value: iniChartModifiers.hopo_frequency },
{ name: 'eighthnote_hopo', value: iniChartModifiers.eighthnote_hopo },
{ name: 'multiplier_note', value: iniChartModifiers.multiplier_note },
{ name: 'sustain_cutoff_threshold', value: iniChartModifiers.sustain_cutoff_threshold },
{ name: 'chord_snap_threshold', value: iniChartModifiers.chord_snap_threshold },
{ name: 'five_lane_drums', value: iniChartModifiers.five_lane_drums },
{ name: 'pro_drums', value: iniChartModifiers.pro_drums },
] as const
)
.filter(modifier => modifier.value !== defaultMetadata[modifier.name])
.map(modifier => ({
name: new TextEncoder().encode(modifier.name),
value: int32ToUint8Array(
typeof modifier.value === 'number' ? modifier.value
: modifier.value === true ? 1
: 0,
),
}))

const hashedIniModifiersLength = _.sumBy(hashedIniModifiers, modifier => modifier.name.length + modifier.value.length)
const buffer = new ArrayBuffer(chartBytes.length + hashedIniModifiersLength)
const uint8Array = new Uint8Array(buffer)
uint8Array.set(chartBytes)
const view = new DataView(buffer, chartBytes.length)

view.setInt32(0, iniChartModifiers.hopo_frequency)
view.setInt8(4, iniChartModifiers.eighthnote_hopo ? 1 : 0)
view.setInt32(5, iniChartModifiers.multiplier_note)
view.setInt32(9, iniChartModifiers.sustain_cutoff_threshold)
view.setInt8(13, iniChartModifiers.five_lane_drums ? 1 : 0)
view.setInt8(14, iniChartModifiers.pro_drums ? 1 : 0)
let offset = chartBytes.length
for (const modifier of hashedIniModifiers) {
uint8Array.set(modifier.name, offset)
offset += modifier.name.length
uint8Array.set(modifier.value, offset)
offset += modifier.value.length
}

return base64url.stringify(blake3(uint8Array))
}

function int32ToUint8Array(num: number) {
const buffer = new ArrayBuffer(4)
const view = new DataView(buffer)
view.setInt32(0, num, true)

return new Uint8Array(buffer)
}
1 change: 1 addition & 0 deletions src/chart/note-parsing-interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface IniChartModifiers {
eighthnote_hopo: boolean
multiplier_note: number
sustain_cutoff_threshold: number
chord_snap_threshold: number
five_lane_drums: boolean
pro_drums: boolean
}
Expand Down
Loading

0 comments on commit f318a0d

Please sign in to comment.