From 10962dca23ded300a54cf4d043b541e3e6f0548e Mon Sep 17 00:00:00 2001 From: pszsh Date: Wed, 4 Mar 2026 04:45:29 -0800 Subject: [PATCH] This might be the final version, it works perfectly as far as I can tell at this point. --- app.go | 75 +- config.go | 11 +- debug.go | 42 +- debug.sh | 21 +- density.go | 249 ++++ fingerings.go | 98 +- frontend/dist/app.js | 255 +++- frontend/dist/chords-default.css | 4 +- frontend/dist/chords-light.css | 4 +- frontend/dist/index.html | 48 +- frontend/dist/layout.css | 389 +++++++ frontend/dist/sets-editor.js | 403 +++++++ frontend/dist/shapes.js | 345 +++++- frontend/dist/vendor/WebAudioFontPlayer.js | 1222 ++++++++++++++++++++ frontend/dist/vendor/guitar_sample.js | 46 + release.go | 11 + scoresets.go | 54 + shapes.go | 151 ++- shift_test.go | 89 ++ static/vectors | 2 +- 20 files changed, 3317 insertions(+), 202 deletions(-) create mode 100644 density.go create mode 100644 frontend/dist/sets-editor.js create mode 100644 frontend/dist/vendor/WebAudioFontPlayer.js create mode 100644 frontend/dist/vendor/guitar_sample.js create mode 100644 release.go create mode 100644 scoresets.go create mode 100644 shift_test.go diff --git a/app.go b/app.go index feb38f7..34109af 100644 --- a/app.go +++ b/app.go @@ -7,9 +7,11 @@ import ( ) type App struct { - ctx context.Context - config Config - configPath string + ctx context.Context + config Config + configPath string + scoreSets ScoreSetsData + scoreSetsPath string } func NewApp() *App { @@ -19,35 +21,62 @@ func NewApp() *App { func (a *App) startup(ctx context.Context) { a.ctx = ctx a.loadConfig() + a.loadScoreSets() +} + +func dataDir() string { + home, _ := os.UserHomeDir() + dir := filepath.Join(home, "web-tuner") + os.MkdirAll(dir, 0755) + return dir } func (a *App) loadConfig() { - exe, _ := os.Executable() - dir := filepath.Dir(exe) - - candidates := []string{ - filepath.Join(dir, "config.json"), - "config.json", - } - - for _, path := range candidates { - cfg, err := LoadConfig(path) - if err == nil { - a.config = cfg - a.configPath = path - return + a.configPath = filepath.Join(dataDir(), "config.json") + cfg, err := LoadConfig(a.configPath) + if err == nil { + if cfg.RangeDown == 0 && cfg.RangeUp == 0 { + cfg.RangeDown = 7 + cfg.RangeUp = 7 } + a.config = cfg + return } - - a.configPath = "config.json" a.config = Config{ Instrument: "guitar", Tuning: []string{"E", "A", "D", "G", "B", "E"}, Frets: 4, MaxFingers: 4, + RangeDown: 7, + RangeUp: 7, } } +func (a *App) loadScoreSets() { + a.scoreSetsPath = filepath.Join(dataDir(), "scoresets.json") + data, err := LoadScoreSets(a.scoreSetsPath) + if err == nil { + a.scoreSets = data + return + } + a.scoreSets = DefaultScoreSetsData() +} + +func (a *App) GetScoreSets() ScoreSetsData { + return a.scoreSets +} + +func (a *App) SaveScoreSets(data ScoreSetsData) error { + if len(data.Sets) == 0 { + data = DefaultScoreSetsData() + } + if data.Selected < 0 || data.Selected >= len(data.Sets) { + data.Selected = 0 + } + a.scoreSets = data + return SaveScoreSets(a.scoreSetsPath, data) +} + func (a *App) GetConfig() Config { return a.config } @@ -96,3 +125,11 @@ func (a *App) GetDefaultShapes() []ShapeDefinition { func (a *App) FindShapeTunings(query ShapeQuery, companions []ShapeDefinition) ([]TuningCandidate, error) { return findTuningsForShape(query, companions) } + +func (a *App) IdentifyShape(frets []int) string { + return identifyShape(frets, a.config) +} + +func (a *App) FindDensityTunings(query ShapeQuery) ([]DensityCandidate, error) { + return findDensityTunings(query, a.config.Frets, a.config.MaxFingers) +} diff --git a/config.go b/config.go index 8e47034..5fe7459 100644 --- a/config.go +++ b/config.go @@ -7,10 +7,13 @@ import ( ) type Config struct { - Instrument string `json:"instrument"` - Tuning []string `json:"tuning"` - Frets int `json:"frets"` - MaxFingers int `json:"max_fingers"` + Instrument string `json:"instrument"` + Tuning []string `json:"tuning"` + Frets int `json:"frets"` + MaxFingers int `json:"max_fingers"` + BaselineShift int `json:"baseline_shift"` + RangeDown int `json:"range_down"` + RangeUp int `json:"range_up"` } func LoadConfig(path string) (Config, error) { diff --git a/debug.go b/debug.go index 2b85983..f45e023 100644 --- a/debug.go +++ b/debug.go @@ -2,9 +2,45 @@ package main -import "log" +import ( + "fmt" + "log" + "os" + "path/filepath" + "time" +) + +var debugLogger *log.Logger func init() { - log.SetFlags(log.Ltime | log.Lshortfile) - log.Println("[debug] debug build active") + home, _ := os.UserHomeDir() + logDir := filepath.Join(home, "web-tuner") + os.MkdirAll(logDir, 0755) + logPath := filepath.Join(logDir, "debug.log") + + f, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) + if err != nil { + debugLogger = log.New(os.Stderr, "[DEBUG] ", log.Ltime|log.Lshortfile) + debugLogger.Printf("Could not open %s: %v — logging to stderr", logPath, err) + return + } + + debugLogger = log.New(f, "[DEBUG] ", log.Ltime|log.Lshortfile) + debugLogger.Printf("=== web-tuner debug session started %s ===", time.Now().Format(time.RFC3339)) + debugLogger.Printf("Log file: %s", logPath) + fmt.Fprintf(os.Stderr, "[web-tuner] Debug logging to %s\n", logPath) +} + +func debugLog(format string, args ...any) { + if debugLogger != nil { + debugLogger.Printf(format, args...) + } +} + +func (a *App) JSDebugLog(msg string) { + debugLog("[JS] %s", msg) +} + +func (a *App) IsDebug() bool { + return true } diff --git a/debug.sh b/debug.sh index 4e260e7..2786993 100755 --- a/debug.sh +++ b/debug.sh @@ -1,10 +1,25 @@ #!/bin/bash -set -euo pipefail +set -e +pkill -f "web-tuner" 2>/dev/null || true +sleep 0.5 export CC=/usr/bin/clang export CXX=/usr/bin/clang++ export SDKROOT=$(xcrun --show-sdk-path) +export CGO_ENABLED=1 export PATH="$HOME/go/bin:$PATH" -echo "Starting dev build with debug tag..." -wails dev -tags debug -loglevel debug +WAILS=$(command -v wails || echo "$HOME/go/bin/wails") + +echo "Building web-tuner (DEBUG)..." +"$WAILS" build -skipbindings -tags debug + +echo "" +echo "Debug build complete. Logs will be written to ~/web-tuner/debug.log" +echo "Launching..." +open build/bin/web-tuner.app + +sleep 1 +echo "" +echo "=== Tailing ~/web-tuner/debug.log (Ctrl-C to stop) ===" +tail -f ~/web-tuner/debug.log 2>/dev/null || echo "Waiting for log file..." diff --git a/density.go b/density.go new file mode 100644 index 0000000..8b5216f --- /dev/null +++ b/density.go @@ -0,0 +1,249 @@ +package main + +import ( + "fmt" + "sort" + "sync" +) + +type DensityChord struct { + Chord string `json:"chord"` + Root string `json:"root"` + Quality string `json:"quality"` + Frets []int `json:"frets"` + Fingers int `json:"fingers"` + Notes []string `json:"notes"` +} + +type DensityCandidate struct { + Tuning []string `json:"tuning"` + TuningMIDI []int `json:"tuning_midi"` + Root string `json:"root"` + Chord string `json:"chord"` + Chords []DensityChord `json:"chords"` + ValidChords int `json:"valid_chords"` + MajMinCount int `json:"maj_min_count"` + HighCompat bool `json:"high_compat"` + AvgFingers float64 `json:"avg_fingers"` +} + +func findDensityTunings(query ShapeQuery, maxFret, maxFingers int) ([]DensityCandidate, error) { + tunings, err := findCandidateTunings(query) + if err != nil { + return nil, err + } + + nStrings := len(query.Shape.Frets) + results := make([]DensityCandidate, len(tunings)) + + var wg sync.WaitGroup + for idx, ct := range tunings { + wg.Add(1) + go func(i int, t candidateTuning) { + defer wg.Done() + + chords := scoreTuningDensity(t.tuningMIDI, maxFret, maxFingers) + + tuningNames := make([]string, nStrings) + for s := 0; s < nStrings; s++ { + tuningNames[s] = midiToNoteName(t.tuningMIDI[s]) + } + + majMin := 0 + totalFingers := 0 + for _, c := range chords { + if c.Quality == "major" || c.Quality == "minor" { + majMin++ + } + totalFingers += c.Fingers + } + + avg := 0.0 + if len(chords) > 0 { + avg = float64(totalFingers) / float64(len(chords)) + } + + results[i] = DensityCandidate{ + Tuning: tuningNames, + TuningMIDI: t.tuningMIDI, + Root: SemitoneToNote[t.root], + Chord: SemitoneToNote[t.root] + " " + query.TargetQuality, + Chords: chords, + ValidChords: len(chords), + MajMinCount: majMin, + HighCompat: majMin > 5, + AvgFingers: avg, + } + }(idx, ct) + } + wg.Wait() + + sort.Slice(results, func(i, j int) bool { + if results[i].ValidChords != results[j].ValidChords { + return results[i].ValidChords > results[j].ValidChords + } + return results[i].AvgFingers < results[j].AvgFingers + }) + + return results, nil +} + +func scoreTuningDensity(tuningMIDI []int, maxFret, maxFingers int) []DensityChord { + nStrings := len(tuningMIDI) + defs := GetChordDefinitions() + + type chordDef struct { + quality string + intervals []int + } + var allDefs []chordDef + for _, cat := range defs { + for name, ivs := range cat { + allDefs = append(allDefs, chordDef{name, ivs}) + } + } + + type chordKey struct { + root int + quality string + } + best := make(map[chordKey]DensityChord) + + optCount := maxFret + 2 + totalCombinations := 1 + for i := 0; i < nStrings; i++ { + totalCombinations *= optCount + } + + fingering := make([]string, nStrings) + for combo := 0; combo < totalCombinations; combo++ { + tmp := combo + for s := nStrings - 1; s >= 0; s-- { + val := tmp % optCount + tmp /= optCount + if val == maxFret+1 { + fingering[s] = "x" + } else { + fingering[s] = fmt.Sprintf("%d", val) + } + } + + if !isValidMuteConfig(fingering) { + continue + } + fingers := countEffectiveFingers(fingering, nStrings) + if fingers > maxFingers { + continue + } + + var soundedPCs []int + frets := make([]int, nStrings) + notes := make([]string, nStrings) + for s := 0; s < nStrings; s++ { + if fingering[s] == "x" { + frets[s] = -1 + notes[s] = "x" + continue + } + fn := atoi(fingering[s]) + frets[s] = fn + midi := tuningMIDI[s] + fn + pc := midi % 12 + soundedPCs = append(soundedPCs, pc) + notes[s] = midiToNoteName(midi) + } + + if len(soundedPCs) < 2 { + continue + } + + uniquePCs := uniqueInts(soundedPCs) + + // try each unique PC as root, match against chord dictionary + type candidate struct { + root int + quality string + size int + bassIdx int + } + var candidates []candidate + for ri, root := range uniquePCs { + ivSet := make(map[int]bool) + for _, pc := range soundedPCs { + ivSet[(pc-root+12)%12] = true + } + for _, cd := range allDefs { + if len(ivSet) != len(cd.intervals) { + continue + } + ok := true + for _, iv := range cd.intervals { + if !ivSet[iv] { + ok = false + break + } + } + if ok { + candidates = append(candidates, candidate{root, cd.quality, len(cd.intervals), ri}) + } + } + } + + if len(candidates) == 0 { + continue + } + + // pick simplest: fewest intervals, bass preference + pick := candidates[0] + for _, c := range candidates[1:] { + if c.size < pick.size || (c.size == pick.size && c.bassIdx < pick.bassIdx) { + pick = c + } + } + + ck := chordKey{pick.root, pick.quality} + existing, exists := best[ck] + if !exists || fingers < existing.Fingers { + fretsCopy := make([]int, nStrings) + copy(fretsCopy, frets) + notesCopy := make([]string, nStrings) + copy(notesCopy, notes) + + best[ck] = DensityChord{ + Chord: SemitoneToNote[pick.root] + " " + pick.quality, + Root: SemitoneToNote[pick.root], + Quality: pick.quality, + Frets: fretsCopy, + Fingers: fingers, + Notes: notesCopy, + } + } + } + + result := make([]DensityChord, 0, len(best)) + for _, dc := range best { + result = append(result, dc) + } + + sort.Slice(result, func(i, j int) bool { + ri := qualityRank(result[i].Quality) + rj := qualityRank(result[j].Quality) + if ri != rj { + return ri < rj + } + return result[i].Fingers < result[j].Fingers + }) + + return result +} + +func qualityRank(q string) int { + switch q { + case "major": + return 0 + case "minor": + return 1 + default: + return 2 + } +} diff --git a/fingerings.go b/fingerings.go index a0818fb..e02ab79 100644 --- a/fingerings.go +++ b/fingerings.go @@ -249,7 +249,7 @@ func findChordFingerings(cfg Config) []ChordResult { if seen[k] { continue } - if countFingers(alt) > maxFingers { + if countEffectiveFingers(alt, len(alt)) > maxFingers { continue } seen[k] = true @@ -279,81 +279,35 @@ func isValidMuteConfig(fingering []string) bool { } func countEffectiveFingers(fingering []string, numStrings int) int { - type fretInfo struct { - fret int - strings []int - } - - frets := make(map[int][]int) + fretStrings := make(map[int][]int) for i, f := range fingering { if f == "x" || f == "0" { continue } fv := atoi(f) - frets[fv] = append(frets[fv], i) - } - - if len(frets) == 0 { - return 0 - } - - type fingerKey struct { - fret int - barre bool - } - used := make(map[fingerKey]bool) - - for fret, strings := range frets { - if len(strings) >= 2 { - start := strings[0] - end := strings[len(strings)-1] - for _, s := range strings { - if s < start { - start = s - } - if s > end { - end = s - } - } - if end-start <= 4 { - valid := true - for i := start; i <= end; i++ { - if fingering[i] == "x" { - continue - } - fv := atoi(fingering[i]) - if fv < fret { - valid = false - break - } - } - if valid { - used[fingerKey{fret, true}] = true - } - } - } - } - - for fret := range frets { - found := false - for k := range used { - if k.fret == fret { - found = true - break - } - } - if !found { - used[fingerKey{fret, false}] = true - } + fretStrings[fv] = append(fretStrings[fv], i) } count := 0 - for k := range used { - if k.barre { - count += 2 - } else { + for fret, strings := range fretStrings { + if len(strings) == 1 { count++ + continue } + groups := 1 + for i := 1; i < len(strings); i++ { + consecutive := true + for s := strings[i-1] + 1; s < strings[i]; s++ { + if fingering[s] == "x" || atoi(fingering[s]) < fret { + consecutive = false + break + } + } + if !consecutive { + groups++ + } + } + count += groups } return count } @@ -439,7 +393,7 @@ func generateMutedVariations(primary []string, tuning []string, intervals map[in if !isValidMuteConfig(test) { return } - if countFingers(test) > maxFingers { + if countEffectiveFingers(test, len(test)) > maxFingers { return } if isSameChord(test, tuning, intervals) { @@ -486,16 +440,6 @@ func isOpenChord(fingering []string) bool { return true } -func countFingers(fingering []string) int { - count := 0 - for _, f := range fingering { - if f != "x" && f != "0" { - count++ - } - } - return count -} - func fingeringKey(f []string) string { return strings.Join(f, ",") } diff --git a/frontend/dist/app.js b/frontend/dist/app.js index 55fcee9..f27ba15 100644 --- a/frontend/dist/app.js +++ b/frontend/dist/app.js @@ -1,3 +1,13 @@ +let _dbgEnabled = false; + +function dbg(...args) { + if (!_dbgEnabled) return; + const msg = args.map(a => typeof a === 'string' ? a : JSON.stringify(a)).join(' '); + try { window.go.main.App.JSDebugLog(msg); } catch (_) {} +} + +window.dbg = dbg; + document.addEventListener('DOMContentLoaded', () => { const navLinks = document.querySelectorAll('.nav-link'); const views = document.querySelectorAll('.view'); @@ -12,6 +22,9 @@ document.addEventListener('DOMContentLoaded', () => { const tuningGrid = document.getElementById('tuning-grid'); const fretsInput = document.getElementById('frets-input'); const fingersInput = document.getElementById('fingers-input'); + const baselineShiftInput = document.getElementById('baseline-shift'); + const rangeDownInput = document.getElementById('range-down'); + const rangeUpInput = document.getElementById('range-up'); const loading = document.getElementById('chord-loading'); const allNotes = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']; @@ -35,21 +48,149 @@ document.addEventListener('DOMContentLoaded', () => { window.currentTuningMIDI = standardMIDI.slice(); let polySynth = null; - window.playChord = function(midiNotes) { - Tone.start(); - if (!polySynth) { - polySynth = new Tone.PolySynth(Tone.Synth, {maxPolyphony: 8}).toDestination(); - polySynth.set({ - oscillator: {type: 'triangle'}, - envelope: {attack: 0.01, decay: 0.3, sustain: 0.4, release: 1.0} + let audioFilter = null; + let audioGain = null; + let audioProfile = 15; + + const volSlider = document.getElementById('audio-vol'); + const profileSlider = document.getElementById('audio-profile'); + const strumSlider = document.getElementById('audio-strum'); + const engineSelect = document.getElementById('audio-engine'); + + // --- PluckSynth engine (Karplus-Strong) --- + let pluckSynths = null; + let pluckFilter = null; + let pluckGain = null; + + function buildPluck() { + if (!pluckFilter) { + pluckFilter = new Tone.Filter({ type: 'lowpass', rolloff: -12 }).toDestination(); + pluckGain = new Tone.Gain(volSlider.value / 100).connect(pluckFilter); + } + if (!pluckSynths) { + pluckSynths = []; + for (let i = 0; i < 6; i++) { + pluckSynths.push(new Tone.PluckSynth().connect(pluckGain)); + } + } + const t = audioProfile / 100; + pluckFilter.frequency.value = 1000 + t * 6000; + pluckFilter.Q.value = 1.0; + pluckSynths.forEach(ps => { + ps.set({ + attackNoise: 1, + dampening: 1000 + t * 6000, + resonance: 0.9 + t * 0.09 }); + }); + } + + // --- WebAudioFont engine --- + let wafPlayer = null; + let wafContext = null; + let wafGain = null; + let wafFilter = null; + let guitarPreset = null; + + function initWAF() { + if (wafPlayer) return; + wafContext = Tone.context.rawContext; + wafGain = wafContext.createGain(); + wafGain.gain.value = volSlider.value / 100; + wafFilter = wafContext.createBiquadFilter(); + wafFilter.type = 'lowpass'; + wafFilter.frequency.value = 5000; + wafGain.connect(wafFilter); + wafFilter.connect(wafContext.destination); + wafPlayer = new WebAudioFontPlayer(); + guitarPreset = _tone_0250_SoundBlasterOld_sf2; + wafPlayer.adjustPreset(wafContext, guitarPreset); + } + + function applyWAFTone() { + if (!wafFilter) return; + const t = audioProfile / 100; + wafFilter.frequency.value = 800 + t * 8000; + } + + // --- Synth engine (existing PolySynth) --- + function profilePartials(t) { + const p = []; + for (let n = 1; n <= 16; n++) { + const tri = (n % 2 === 1) ? 1 / (n * n) : 0; + const saw = 1 / n; + p.push(tri * (1 - t) + saw * t); + } + return p; + } + + function buildSynth() { + if (!audioFilter) { + audioFilter = new Tone.Filter({ type: 'lowpass', rolloff: -12 }).toDestination(); + audioGain = new Tone.Gain(volSlider.value / 100).connect(audioFilter); + } + if (!polySynth) { + polySynth = new Tone.PolySynth(Tone.Synth, { maxPolyphony: 8 }).connect(audioGain); } polySynth.releaseAll(); - const now = Tone.now(); - midiNotes.forEach((m, i) => { - const freq = 440 * Math.pow(2, (m - 69) / 12); - polySynth.triggerAttackRelease(freq, '1.5s', now + i * 0.03); + const t = audioProfile / 100; + audioFilter.frequency.value = 600 + t * 9400; + audioFilter.Q.value = 1.5 - t * 1.0; + polySynth.set({ + oscillator: { type: 'custom', partials: profilePartials(t) }, + envelope: { + attack: 0.045 - t * 0.04, + decay: 0.5 - t * 0.3, + sustain: 0.25 + t * 0.25, + release: 1.5 - t * 0.7 + } }); + } + + // --- Volume slider: update all engines --- + volSlider.addEventListener('input', () => { + const v = volSlider.value / 100; + if (audioGain) audioGain.gain.value = v; + if (pluckGain) pluckGain.gain.value = v; + if (wafGain) wafGain.gain.value = v; + }); + + // --- Tone slider: update active engine --- + profileSlider.addEventListener('input', () => { + audioProfile = parseInt(profileSlider.value); + const engine = engineSelect.value; + if (engine === 'pluck') buildPluck(); + else if (engine === 'sample') applyWAFTone(); + else buildSynth(); + }); + + // --- playChord: multi-engine with strum --- + window.playChord = function(midiNotes) { + Tone.start(); + const strum = parseInt(strumSlider.value) / 1000; + const engine = engineSelect.value; + const now = Tone.now(); + + if (engine === 'pluck') { + if (!pluckSynths) buildPluck(); + midiNotes.forEach((m, i) => { + const freq = 440 * Math.pow(2, (m - 69) / 12); + pluckSynths[i % pluckSynths.length].triggerAttack(freq, now + i * strum); + }); + } else if (engine === 'sample') { + initWAF(); + midiNotes.forEach((m, i) => { + wafPlayer.queueWaveTable(wafContext, wafGain, guitarPreset, + wafContext.currentTime + i * strum, m, 2.0); + }); + } else { + if (!polySynth) buildSynth(); + polySynth.releaseAll(); + midiNotes.forEach((m, i) => { + const freq = 440 * Math.pow(2, (m - 69) / 12); + polySynth.triggerAttackRelease(freq, '1.5s', now + i * strum); + }); + } }; const presets = { @@ -69,25 +210,30 @@ document.addEventListener('DOMContentLoaded', () => { let currentConfig = null; let chordsLoaded = false; let shapesInited = false; + let lastBaselineShift = 0; // --- Navigation --- + window.switchToView = function(target) { + navLinks.forEach(l => l.classList.remove('active')); + views.forEach(v => v.classList.remove('active')); + const navLink = document.querySelector('.nav-link[data-view="' + target + '"]'); + if (navLink) navLink.classList.add('active'); + document.getElementById('view-' + target).classList.add('active'); + filterSection.style.display = target === 'chords' ? '' : 'none'; + shapesSection.style.display = (target === 'shapes') ? '' : 'none'; + + if (target === 'chords' && !chordsLoaded) { + loadChords(); + } + if (target === 'shapes' && !shapesInited) { + shapesInited = true; + if (window.initShapeExplorer) window.initShapeExplorer(); + } + }; + navLinks.forEach(link => { link.addEventListener('click', () => { - const target = link.dataset.view; - navLinks.forEach(l => l.classList.remove('active')); - views.forEach(v => v.classList.remove('active')); - link.classList.add('active'); - document.getElementById('view-' + target).classList.add('active'); - filterSection.style.display = target === 'chords' ? '' : 'none'; - shapesSection.style.display = target === 'shapes' ? '' : 'none'; - - if (target === 'chords' && !chordsLoaded) { - loadChords(); - } - if (target === 'shapes' && !shapesInited) { - shapesInited = true; - if (window.initShapeExplorer) window.initShapeExplorer(); - } + window.switchToView(link.dataset.view); }); }); @@ -135,6 +281,10 @@ document.addEventListener('DOMContentLoaded', () => { function syncConfigPanel(cfg) { fretsInput.value = cfg.frets; fingersInput.value = cfg.max_fingers; + lastBaselineShift = cfg.baseline_shift || 0; + baselineShiftInput.value = lastBaselineShift; + rangeDownInput.value = cfg.range_down || 7; + rangeUpInput.value = cfg.range_up || 7; buildTuningGrid(cfg.tuning); let matched = false; @@ -156,14 +306,35 @@ document.addEventListener('DOMContentLoaded', () => { } }); - btnApply.addEventListener('click', () => { - if (!window.go) return; - const cfg = { + baselineShiftInput.addEventListener('input', () => { + const newShift = parseInt(baselineShiftInput.value) || 0; + const delta = newShift - lastBaselineShift; + if (delta === 0) return; + lastBaselineShift = newShift; + const selects = tuningGrid.querySelectorAll('select'); + selects.forEach(sel => { + const pc = allNotes.indexOf(sel.value); + if (pc < 0) return; + sel.value = allNotes[((pc + delta) % 12 + 12) % 12]; + }); + presetSelect.value = 'Custom'; + }); + + function readConfigFromPanel() { + return { instrument: currentConfig ? currentConfig.instrument : 'guitar', tuning: readTuningFromGrid(), frets: parseInt(fretsInput.value) || 4, - max_fingers: parseInt(fingersInput.value) || 4 + max_fingers: parseInt(fingersInput.value) || 4, + baseline_shift: parseInt(baselineShiftInput.value) || 0, + range_down: parseInt(rangeDownInput.value) || 0, + range_up: parseInt(rangeUpInput.value) || 0 }; + } + + btnApply.addEventListener('click', () => { + if (!window.go) return; + const cfg = readConfigFromPanel(); loading.style.display = ''; loading.textContent = 'Regenerating chords...'; @@ -182,7 +353,15 @@ document.addEventListener('DOMContentLoaded', () => { btnSave.addEventListener('click', () => { if (!window.go) return; - window.go.main.App.SaveConfig().then(() => { + const cfg = readConfigFromPanel(); + window.go.main.App.UpdateConfig(cfg).then(chords => { + currentConfig = cfg; + window.currentTuningMIDI = tuningNamesToMIDI(cfg.tuning); + if (chordsLoaded && window.buildChordCards) { + window.buildChordCards(chords || [], cfg.frets, cfg.tuning.length); + } + return window.go.main.App.SaveConfig(); + }).then(() => { btnSave.textContent = 'Saved'; setTimeout(() => { btnSave.textContent = 'Save'; }, 1500); }).catch(err => { @@ -201,6 +380,13 @@ document.addEventListener('DOMContentLoaded', () => { }); }); + window.setTuningFromExplorer = function(noteNames) { + const pcs = noteNames.map(n => n.replace(/[-]?\d+$/, '')); + buildTuningGrid(pcs); + presetSelect.value = 'Custom'; + btnApply.click(); + }; + // --- Load Chords --- function loadChords() { if (!window.go) { @@ -223,6 +409,13 @@ document.addEventListener('DOMContentLoaded', () => { populatePresets(); if (window.go && window.go.main && window.go.main.App) { + window.go.main.App.IsDebug().then(d => { + if (d) { + _dbgEnabled = true; + window.DEBUG = true; + dbg('=== Frontend debug logging active ==='); + } + }); window.go.main.App.GetConfig().then(cfg => { currentConfig = cfg; window.currentTuningMIDI = tuningNamesToMIDI(cfg.tuning); diff --git a/frontend/dist/chords-default.css b/frontend/dist/chords-default.css index a876b7b..3fec743 100644 --- a/frontend/dist/chords-default.css +++ b/frontend/dist/chords-default.css @@ -20,8 +20,8 @@ .fret { border: .5px solid var(--border, #444746); - border-left: 2px solid transparent; - border-right: 2px; + border-left: 2px; + border-right: 2px solid transparent; background: var(--bg-overlay, #282a2c); color: transparent; } diff --git a/frontend/dist/chords-light.css b/frontend/dist/chords-light.css index 1caf030..dea4ef5 100644 --- a/frontend/dist/chords-light.css +++ b/frontend/dist/chords-light.css @@ -24,8 +24,8 @@ .fret { border: .5px solid #0002; - border-left: 2px solid #fff; - border-right: 2px solid #0001; + border-left: 2px solid #0001; + border-right: 2px solid #fff; background: #fff; color: #fff; } diff --git a/frontend/dist/index.html b/frontend/dist/index.html index 27e48c4..379d42a 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -14,6 +14,19 @@

Web Tuner

+
+ + + + + + + +
+
@@ -90,6 +106,20 @@ +

Tuning Range

+ + +
+
+ + +
+
+ + +
+
+
@@ -147,13 +177,29 @@
+ +
+
+
+ +

Score Sets Editor

+
+
+
+
+
+
+
+ + + diff --git a/frontend/dist/layout.css b/frontend/dist/layout.css index 2d5b9aa..18ea5a1 100644 --- a/frontend/dist/layout.css +++ b/frontend/dist/layout.css @@ -58,6 +58,48 @@ body { --wails-draggable: no-drag; } +.audio-sliders { + display: flex; + align-items: center; + gap: 0.375rem; +} + +.audio-sliders label { + font-size: 0.6875rem; + color: var(--text-subtle); + user-select: none; +} + +.audio-sliders input[type="range"] { + width: 4rem; + height: 0.25rem; + -webkit-appearance: none; + appearance: none; + background: var(--border); + border-radius: 0.125rem; + outline: none; + cursor: pointer; +} + +.audio-sliders input[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + width: 0.75rem; + height: 0.75rem; + border-radius: 50%; + background: var(--accent); + cursor: pointer; +} + +.audio-sliders input[type="range"]::-webkit-slider-thumb:hover { + background: var(--accent-hover); +} + +.audio-sliders select { + font-size: 0.6875rem; + padding: 0.25rem 1.25rem 0.25rem 0.375rem; + background-position: right 0.375rem center; +} + .header-actions select, .header-actions button { font: inherit; @@ -441,6 +483,22 @@ body { border-color: var(--text-secondary); } +.set-selector-row { + display: flex; + gap: 0.375rem; + margin-bottom: 0.5rem; +} + +.set-selector-row select { + flex: 1; + min-width: 0; +} + +.set-selector-row .btn-small { + flex: 0; + padding: 0.375rem 0.5rem; +} + .shape-list { display: flex; flex-direction: column; @@ -704,6 +762,25 @@ body { color: var(--text-subtle); } +.density-explanation { + font-size: 0.8rem; + color: var(--text-subtle); + margin-bottom: 0.75rem; + line-height: 1.4; +} + +.finger-count { + display: inline-block; + font-size: 0.625rem; + font-weight: 600; + color: var(--text-subtle); + background: var(--bg-overlay); + border-radius: 0.25rem; + padding: 0.0625rem 0.3rem; + margin-left: 0.375rem; + vertical-align: middle; +} + .btn-load-more { display: block; width: 100%; @@ -751,6 +828,318 @@ body { background: var(--border); } +/* --- Sets Editor --- */ +.sets-editor { + display: flex; + flex-direction: column; + height: 100%; +} + +.sets-editor-header { + display: flex; + align-items: center; + gap: 1rem; + padding-bottom: 0.75rem; + border-bottom: 1px solid var(--border-light); + margin-bottom: 0.75rem; +} + +.sets-editor-header h2 { + font-size: 1rem; + font-weight: 500; + color: var(--text-primary); +} + +.sets-editor-body { + display: flex; + flex: 1; + min-height: 0; + gap: 1rem; +} + +.sets-editor-list { + width: 14rem; + flex-shrink: 0; + overflow-y: auto; + display: flex; + flex-direction: column; + gap: 0.25rem; +} + +.sets-editor-detail { + flex: 1; + overflow-y: auto; +} + +.se-set-item { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.375rem; + padding: 0.5rem 0.625rem; + border-radius: var(--radius-sm); + font-size: 0.8125rem; + cursor: pointer; + border: 1px solid transparent; + transition: all var(--transition); +} + +.se-set-item:hover { + background: var(--bg-surface); +} + +.se-set-item.selected { + background: var(--accent-dim); + border-color: rgba(138, 180, 248, 0.25); +} + +.se-set-name { + flex: 1; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--text-secondary); +} + +.se-set-badges { + display: flex; + gap: 0.25rem; + flex-shrink: 0; +} + +.se-badge { + font-size: 0.625rem; + font-weight: 600; + padding: 0.125rem 0.375rem; + border-radius: 0.25rem; + background: var(--bg-overlay); + color: var(--text-subtle); +} + +.se-badge-density { + background: var(--accent-dim); + color: var(--accent); +} + +.se-btn-new-set { + margin-top: 0.5rem; +} + +.se-detail-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.75rem; +} + +.se-detail-header h3 { + font-size: 0.9375rem; + font-weight: 500; + color: var(--text-primary); + flex: 1; +} + +.se-shape-list { + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.se-shape-row { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.375rem 0.5rem; + border-radius: var(--radius-sm); + border: 1px solid var(--border-light); + background: var(--bg-surface); +} + +.se-mini-fb { + flex-shrink: 0; +} + +.se-shape-info { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 0.125rem; +} + +.se-shape-name { + font-size: 0.8125rem; + color: var(--text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.se-shape-name.se-auto-name { + font-style: italic; + color: var(--text-subtle); +} + +.se-shape-frets { + font-size: 0.6875rem; + color: var(--text-subtle); +} + +.se-shape-actions { + display: flex; + gap: 0.25rem; + flex-shrink: 0; +} + +.se-shape-actions .btn-small { + flex: 0; + padding: 0.25rem 0.5rem; + font-size: 0.6875rem; +} + +.se-btn-danger { + border-color: #c0392b !important; + color: #e74c3c !important; +} + +.se-btn-danger:hover { + background: rgba(231, 76, 60, 0.15) !important; +} + +.se-btn-add-shape { + margin-top: 0.75rem; +} + +/* shape editor */ +.se-editor-header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +.se-editor-header h3 { + font-size: 0.9375rem; + font-weight: 500; + color: var(--text-primary); +} + +.se-editor-name-row { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 1rem; +} + +.se-editor-name-row label { + font-size: 0.75rem; + color: var(--text-subtle); + flex-shrink: 0; +} + +.se-editor-name-input { + font-family: inherit; + font-size: 0.8125rem; + padding: 0.375rem 0.5rem; + border-radius: var(--radius-sm); + border: 1px solid var(--border); + background: var(--bg-input); + color: var(--text-primary); + outline: none; + width: 14rem; + transition: all var(--transition); +} + +.se-editor-name-input:focus { + border-color: var(--text-secondary); +} + +.se-auto-label { + font-size: 0.75rem; + font-style: italic; + color: var(--text-subtle); +} + +/* clickable fretboard grid */ +.se-clickable-fb { + overflow-x: auto; + padding-bottom: 0.5rem; +} + +.se-fb-grid { + display: flex; + flex-direction: column; + gap: 0; + width: fit-content; +} + +.se-fb-header-row { + display: flex; +} + +.se-fb-fret-num { + width: 2.5rem; + text-align: center; + font-size: 0.625rem; + color: var(--text-subtle); + padding-bottom: 0.25rem; +} + +.se-fb-row { + display: flex; +} + +.se-fb-label { + width: 4.5rem; + font-size: 0.6875rem; + color: var(--text-subtle); + display: flex; + align-items: center; + flex-shrink: 0; + padding-right: 0.25rem; +} + +.se-fb-cell { + width: 2.5rem; + height: 2rem; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + border: 1px solid var(--border-light); + font-size: 0.75rem; + font-weight: 600; + transition: background var(--transition); + position: relative; +} + +.se-fb-cell:hover { + background: var(--bg-overlay); +} + +.se-fb-nut { + border-right: 3px solid var(--text-subtle); +} + +.se-fb-cell.se-dot-active::after { + content: ''; + width: 0.75rem; + height: 0.75rem; + border-radius: 50%; + background: var(--accent); + display: block; +} + +.se-fb-cell.se-muted { + color: #e74c3c; + font-weight: 700; +} + +.se-fb-cell.se-open { + color: var(--text-secondary); + font-weight: 600; +} + /* --- Print --- */ @media print { .header, .sidebar { diff --git a/frontend/dist/sets-editor.js b/frontend/dist/sets-editor.js new file mode 100644 index 0000000..483894c --- /dev/null +++ b/frontend/dist/sets-editor.js @@ -0,0 +1,403 @@ +let _seListEl = document.getElementById('sets-editor-list'); +let _seDetailEl = document.getElementById('sets-editor-detail'); +let _seSelectedIdx = 0; +let _seEditingIdx = -1; +let _seFrets = []; +let _seNameInput = null; +let _seAutoLabel = null; + +function _seDbg() { + if (window.dbg) window.dbg.apply(null, ['[sets-editor]'].concat(Array.prototype.slice.call(arguments))); +} + +function _seData() { + return window.getScoreSetsData(); +} + +function _seDensity(set) { return set && set.type === 'density'; } + +document.getElementById('btn-back-explorer').addEventListener('click', function() { + _seEditingIdx = -1; + window.setScoreSetsData(_seData()); + window.switchToView('shapes'); +}); + +window.initSetsEditor = function() { + _seDbg('init'); + var data = _seData(); + _seSelectedIdx = data.selected; + _seEditingIdx = -1; + seRenderList(); + seRenderDetail(); +}; + +function seSelectSet(i) { + _seDbg('selectSet', i); + _seSelectedIdx = i; + _seEditingIdx = -1; + seRenderList(); + seRenderDetail(); +} + +function seNewSet() { + _seDbg('newSet'); + var name = prompt('New score set name:'); + if (!name || !name.trim()) return; + var data = _seData(); + data.sets.push({ name: name.trim(), type: '', shapes: [] }); + _seSelectedIdx = data.sets.length - 1; + _seEditingIdx = -1; + window.persistSets(); + seRenderList(); + seRenderDetail(); +} + +function seRenderList() { + _seListEl.innerHTML = ''; + var data = _seData(); + data.sets.forEach(function(set, i) { + var item = document.createElement('div'); + item.className = 'se-set-item' + (i === _seSelectedIdx ? ' selected' : ''); + var nm = document.createElement('span'); + nm.className = 'se-set-name'; + nm.textContent = set.name; + var badges = document.createElement('span'); + badges.className = 'se-set-badges'; + var ct = document.createElement('span'); + ct.className = 'se-badge'; + ct.textContent = set.shapes.length; + badges.appendChild(ct); + if (_seDensity(set)) { + var db = document.createElement('span'); + db.className = 'se-badge se-badge-density'; + db.textContent = 'density'; + badges.appendChild(db); + } + item.appendChild(nm); + item.appendChild(badges); + item.setAttribute('onclick', 'seSelectSet(' + i + ')'); + _seListEl.appendChild(item); + }); + var btn = document.createElement('button'); + btn.className = 'btn-small se-btn-new-set'; + btn.textContent = 'New Set'; + btn.setAttribute('onclick', 'seNewSet()'); + _seListEl.appendChild(btn); +} + +function seRenameSet() { + _seDbg('rename'); + var set = _seData().sets[_seSelectedIdx]; + if (!set) return; + var name = prompt('Rename set:', set.name); + if (!name || !name.trim()) return; + set.name = name.trim(); + window.persistSets(); + seRenderList(); + seRenderDetail(); +} + +function seDeleteSet() { + _seDbg('deleteSet'); + var data = _seData(); + var set = data.sets[_seSelectedIdx]; + if (!set || !confirm('Delete "' + set.name + '"?')) return; + data.sets.splice(_seSelectedIdx, 1); + if (_seSelectedIdx >= data.sets.length) _seSelectedIdx = data.sets.length - 1; + data.selected = _seSelectedIdx; + window.persistSets(); + seRenderList(); + seRenderDetail(); +} + +function seMoveShape(i, dir) { + _seDbg('move', i, dir); + var set = _seData().sets[_seSelectedIdx]; + if (!set) return; + var j = i + dir; + if (j < 0 || j >= set.shapes.length) return; + var tmp = set.shapes[i]; + set.shapes[i] = set.shapes[j]; + set.shapes[j] = tmp; + window.persistSets(); + seRenderDetail(); +} + +function seEditShape(i) { + _seDbg('edit', i); + _seEditingIdx = i; + seRenderDetail(); +} + +function seDupShape(i) { + _seDbg('dup', i); + var set = _seData().sets[_seSelectedIdx]; + if (!set) return; + var orig = set.shapes[i]; + set.shapes.splice(i + 1, 0, { name: '', frets: orig.frets.slice() }); + window.persistSets(); + seRenderDetail(); +} + +function seDelShape(i) { + _seDbg('del', i); + var set = _seData().sets[_seSelectedIdx]; + if (!set) return; + set.shapes.splice(i, 1); + window.persistSets(); + seRenderDetail(); +} + +function seAddShape() { + _seDbg('add'); + var set = _seData().sets[_seSelectedIdx]; + if (!set) return; + set.shapes.push({ name: '', frets: [0, 0, 0, 0, 0, 0] }); + _seEditingIdx = set.shapes.length - 1; + window.persistSets(); + seRenderDetail(); +} + +function seRenderDetail() { + _seDetailEl.innerHTML = ''; + var data = _seData(); + var set = data.sets[_seSelectedIdx]; + if (!set) return; + + if (_seEditingIdx >= 0) { + seRenderShapeEditor(set); + return; + } + + var density = _seDensity(set); + var header = document.createElement('div'); + header.className = 'se-detail-header'; + var h3 = document.createElement('h3'); + h3.textContent = set.name; + header.appendChild(h3); + + if (!density) { + var btnR = document.createElement('button'); + btnR.className = 'btn-small'; + btnR.textContent = 'Rename'; + btnR.setAttribute('onclick', 'seRenameSet()'); + header.appendChild(btnR); + if (data.sets.length > 1) { + var btnD = document.createElement('button'); + btnD.className = 'btn-small se-btn-danger'; + btnD.textContent = 'Delete Set'; + btnD.setAttribute('onclick', 'seDeleteSet()'); + header.appendChild(btnD); + } + } + _seDetailEl.appendChild(header); + + var listEl = document.createElement('div'); + listEl.className = 'se-shape-list'; + + set.shapes.forEach(function(shape, i) { + var row = document.createElement('div'); + row.className = 'se-shape-row'; + + var fb = document.createElement('div'); + fb.className = 'fretboard alternative-fretboard se-mini-fb'; + var fingering = shape.frets.map(function(f) { return f === -1 ? 'x' : String(f); }); + var mx = Math.max.apply(null, shape.frets.filter(function(f) { return f >= 0; }).concat([1])); + window.renderSingleFretboard(fb, fingering, Math.max(mx, 4)); + row.appendChild(fb); + + var info = document.createElement('div'); + info.className = 'se-shape-info'; + var nameEl = document.createElement('span'); + nameEl.className = 'se-shape-name'; + if (shape.name) { + nameEl.textContent = shape.name; + } else { + nameEl.classList.add('se-auto-name'); + nameEl.textContent = 'detecting...'; + _seAutoDetect(shape.frets).then(function(n) { nameEl.textContent = n || '(unknown)'; }); + } + info.appendChild(nameEl); + var fretsEl = document.createElement('span'); + fretsEl.className = 'se-shape-frets'; + fretsEl.textContent = shape.frets.map(function(f) { return f === -1 ? 'x' : f; }).join(' '); + info.appendChild(fretsEl); + row.appendChild(info); + + if (!density) { + var acts = document.createElement('div'); + acts.className = 'se-shape-actions'; + if (i > 0) { + var up = document.createElement('button'); + up.className = 'btn-small'; + up.textContent = '\u25B2'; + up.title = 'Move up'; + up.setAttribute('onclick', 'seMoveShape(' + i + ',-1)'); + acts.appendChild(up); + } + if (i < set.shapes.length - 1) { + var dn = document.createElement('button'); + dn.className = 'btn-small'; + dn.textContent = '\u25BC'; + dn.title = 'Move down'; + dn.setAttribute('onclick', 'seMoveShape(' + i + ',1)'); + acts.appendChild(dn); + } + var ed = document.createElement('button'); + ed.className = 'btn-small'; + ed.textContent = 'edit'; + ed.setAttribute('onclick', 'seEditShape(' + i + ')'); + acts.appendChild(ed); + var dp = document.createElement('button'); + dp.className = 'btn-small'; + dp.textContent = 'dup'; + dp.title = 'Duplicate'; + dp.setAttribute('onclick', 'seDupShape(' + i + ')'); + acts.appendChild(dp); + var dl = document.createElement('button'); + dl.className = 'btn-small se-btn-danger'; + dl.textContent = 'del'; + dl.setAttribute('onclick', 'seDelShape(' + i + ')'); + acts.appendChild(dl); + row.appendChild(acts); + } + listEl.appendChild(row); + }); + + _seDetailEl.appendChild(listEl); + + if (!density) { + var btnA = document.createElement('button'); + btnA.className = 'btn-small se-btn-add-shape'; + btnA.textContent = 'Add Shape'; + btnA.setAttribute('onclick', 'seAddShape()'); + _seDetailEl.appendChild(btnA); + } +} + +function seShapeDone() { + _seDbg('done'); + var set = _seData().sets[_seSelectedIdx]; + if (!set) return; + var shape = set.shapes[_seEditingIdx]; + if (!shape) return; + shape.frets = _seFrets.slice(); + shape.name = _seNameInput ? _seNameInput.value.trim() : ''; + _seEditingIdx = -1; + window.persistSets(); + seRenderList(); + seRenderDetail(); +} + +function seFbCell(s, f) { + var cur = _seFrets[s]; + if (f === 0) { + _seFrets[s] = (cur === -1) ? 0 : -1; + } else { + _seFrets[s] = (cur === f) ? 0 : f; + } + _seBuildGrid(); + _seUpdateAutoName(); +} + +function _seBuildGrid() { + var wrap = document.getElementById('se-fb-wrap'); + if (!wrap) return; + wrap.innerHTML = ''; + var nf = 12; + var table = document.createElement('div'); + table.className = 'se-fb-grid'; + + var hdr = document.createElement('div'); + hdr.className = 'se-fb-header-row'; + var lbl = document.createElement('div'); + lbl.className = 'se-fb-label'; + hdr.appendChild(lbl); + for (var f = 0; f <= nf; f++) { + var c = document.createElement('div'); + c.className = 'se-fb-fret-num'; + c.textContent = f === 0 ? 'nut' : f; + hdr.appendChild(c); + } + table.appendChild(hdr); + + var labels = ['1 (low)', '2', '3', '4', '5', '6 (high)']; + for (var s = 0; s < 6; s++) { + var row = document.createElement('div'); + row.className = 'se-fb-row'; + var sl = document.createElement('div'); + sl.className = 'se-fb-label'; + sl.textContent = labels[s]; + row.appendChild(sl); + for (var f = 0; f <= nf; f++) { + var cell = document.createElement('div'); + cell.className = 'se-fb-cell'; + if (f === 0) cell.classList.add('se-fb-nut'); + var cur = _seFrets[s]; + if (f === 0) { + if (cur === -1) { cell.classList.add('se-muted'); cell.textContent = 'x'; } + else if (cur === 0) { cell.classList.add('se-open'); cell.textContent = 'o'; } + } else if (cur === f) { + cell.classList.add('se-dot-active'); + } + cell.setAttribute('onclick', 'seFbCell(' + s + ',' + f + ')'); + row.appendChild(cell); + } + table.appendChild(row); + } + wrap.appendChild(table); +} + +function _seUpdateAutoName() { + _seAutoDetect(_seFrets).then(function(n) { + if (_seAutoLabel) _seAutoLabel.textContent = n ? 'Detected: ' + n : ''; + }); +} + +function seRenderShapeEditor(set) { + var shape = set.shapes[_seEditingIdx]; + if (!shape) { _seEditingIdx = -1; seRenderDetail(); return; } + _seFrets = shape.frets.slice(); + _seDetailEl.innerHTML = ''; + + var header = document.createElement('div'); + header.className = 'se-editor-header'; + var btnDone = document.createElement('button'); + btnDone.className = 'btn-small btn-apply'; + btnDone.textContent = 'Done'; + btnDone.setAttribute('onclick', 'seShapeDone()'); + header.appendChild(btnDone); + var title = document.createElement('h3'); + title.textContent = 'Edit Shape'; + header.appendChild(title); + _seDetailEl.appendChild(header); + + var nameRow = document.createElement('div'); + nameRow.className = 'se-editor-name-row'; + var nl = document.createElement('label'); + nl.textContent = 'Name'; + _seNameInput = document.createElement('input'); + _seNameInput.type = 'text'; + _seNameInput.className = 'se-editor-name-input'; + _seNameInput.placeholder = 'Auto-detect name'; + _seNameInput.value = shape.name; + _seAutoLabel = document.createElement('span'); + _seAutoLabel.className = 'se-auto-label'; + nameRow.appendChild(nl); + nameRow.appendChild(_seNameInput); + nameRow.appendChild(_seAutoLabel); + _seDetailEl.appendChild(nameRow); + + var fbWrap = document.createElement('div'); + fbWrap.id = 'se-fb-wrap'; + fbWrap.className = 'se-clickable-fb'; + _seDetailEl.appendChild(fbWrap); + + _seBuildGrid(); + _seUpdateAutoName(); +} + +function _seAutoDetect(frets) { + if (!window.go || !window.go.main || !window.go.main.App) return Promise.resolve(''); + return window.go.main.App.IdentifyShape(frets).then(function(n) { return n || ''; }).catch(function() { return ''; }); +} diff --git a/frontend/dist/shapes.js b/frontend/dist/shapes.js index 18fec7e..1048512 100644 --- a/frontend/dist/shapes.js +++ b/frontend/dist/shapes.js @@ -13,17 +13,41 @@ const btnSearch = document.getElementById('btn-shape-search'); const resultsContainer = document.getElementById('shapes-results'); const loadingEl = document.getElementById('shapes-loading'); + const setSelect = document.getElementById('score-set-select'); const roots = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']; + let scoreSetsData = { sets: [], selected: 0 }; let shapes = []; let selectedIndex = 0; let editingIndex = -1; + function activeSet() { + return scoreSetsData.sets[scoreSetsData.selected]; + } + + function syncShapesRef() { + const set = activeSet(); + shapes = set ? set.shapes : []; + } + function init() { if (!window.go || !window.go.main || !window.go.main.App) return; - - window.go.main.App.GetDefaultShapes().then(defaults => { - shapes = defaults; + window.go.main.App.GetScoreSets().then(data => { + scoreSetsData = data; + if (!scoreSetsData.sets || scoreSetsData.sets.length === 0) { + scoreSetsData = { sets: [{ name: 'Standard Shapes', type: '', shapes: [] }], selected: 0 }; + window.go.main.App.GetDefaultShapes().then(defaults => { + scoreSetsData.sets[0].shapes = defaults; + syncShapesRef(); + renderSetSelector(); + renderShapeList(); + buildVoicingInputs(); + }); + return; + } + syncShapesRef(); + selectedIndex = 0; + renderSetSelector(); renderShapeList(); buildVoicingInputs(); }); @@ -55,6 +79,17 @@ btnEditorSave.addEventListener('click', saveEditor); btnEditorCancel.addEventListener('click', closeEditor); btnSearch.addEventListener('click', doSearch); + setSelect.addEventListener('change', () => { + if (window.dbg) window.dbg('[shapes] setSelect change:', setSelect.value); + if (setSelect.value === '__editor__') { + setSelect.value = scoreSetsData.selected; + if (window.dbg) window.dbg('[shapes] opening editor, sets:', scoreSetsData.sets.length); + window.switchToView('sets-editor'); + if (window.initSetsEditor) window.initSetsEditor(); + return; + } + switchSet(parseInt(setSelect.value)); + }); resultsContainer.addEventListener('click', (e) => { const item = e.target.closest('[data-chord-midi]'); @@ -63,6 +98,49 @@ }); } + function isDensitySet() { + const set = activeSet(); + return set && set.type === 'density'; + } + + function renderSetSelector() { + setSelect.innerHTML = ''; + scoreSetsData.sets.forEach((set, i) => { + const opt = document.createElement('option'); + opt.value = i; + opt.textContent = set.name; + if (i === scoreSetsData.selected) opt.selected = true; + setSelect.appendChild(opt); + }); + const sep = document.createElement('option'); + sep.disabled = true; + sep.textContent = '───'; + setSelect.appendChild(sep); + const editorOpt = document.createElement('option'); + editorOpt.value = '__editor__'; + editorOpt.textContent = 'Open Sets Editor'; + setSelect.appendChild(editorOpt); + } + + function switchSet(i) { + if (i < 0 || i >= scoreSetsData.sets.length) return; + scoreSetsData.selected = i; + syncShapesRef(); + selectedIndex = 0; + closeEditor(); + renderSetSelector(); + renderShapeList(); + buildVoicingInputs(); + persistSets(); + } + + function persistSets() { + if (!window.go) return; + const set = activeSet(); + if (set) set.shapes = shapes; + window.go.main.App.SaveScoreSets(scoreSetsData); + } + function renderShapeList() { shapeList.innerHTML = ''; shapes.forEach((s, i) => { @@ -80,6 +158,38 @@ const actions = document.createElement('span'); actions.className = 'shape-actions'; + if (i > 0) { + const upBtn = document.createElement('button'); + upBtn.textContent = '\u25B2'; + upBtn.title = 'Move up'; + upBtn.addEventListener('click', e => { + e.stopPropagation(); + [shapes[i - 1], shapes[i]] = [shapes[i], shapes[i - 1]]; + if (selectedIndex === i) selectedIndex = i - 1; + else if (selectedIndex === i - 1) selectedIndex = i; + renderShapeList(); + buildVoicingInputs(); + persistSets(); + }); + actions.appendChild(upBtn); + } + + if (i < shapes.length - 1) { + const downBtn = document.createElement('button'); + downBtn.textContent = '\u25BC'; + downBtn.title = 'Move down'; + downBtn.addEventListener('click', e => { + e.stopPropagation(); + [shapes[i], shapes[i + 1]] = [shapes[i + 1], shapes[i]]; + if (selectedIndex === i) selectedIndex = i + 1; + else if (selectedIndex === i + 1) selectedIndex = i; + renderShapeList(); + buildVoicingInputs(); + persistSets(); + }); + actions.appendChild(downBtn); + } + const editBtn = document.createElement('button'); editBtn.textContent = 'edit'; editBtn.addEventListener('click', e => { e.stopPropagation(); openEditor(i); }); @@ -92,6 +202,7 @@ if (selectedIndex >= shapes.length) selectedIndex = Math.max(0, shapes.length - 1); renderShapeList(); buildVoicingInputs(); + persistSets(); }); actions.appendChild(editBtn); @@ -191,27 +302,20 @@ closeEditor(); renderShapeList(); buildVoicingInputs(); + persistSets(); } function restoreDefaults() { if (!window.go) return; - const currentSearch = shapes[selectedIndex]; window.go.main.App.GetDefaultShapes().then(defaults => { - shapes = defaults; - // preserve search shape if custom - if (currentSearch) { - const exists = shapes.some(s => s.name === currentSearch.name); - if (!exists) { - shapes.push(currentSearch); - selectedIndex = shapes.length - 1; - } else { - selectedIndex = shapes.findIndex(s => s.name === currentSearch.name); - } - } else { - selectedIndex = 0; - } + shapes.length = 0; + defaults.forEach(s => shapes.push(s)); + const set = activeSet(); + if (set) set.shapes = shapes; + selectedIndex = 0; renderShapeList(); buildVoicingInputs(); + persistSets(); }); } @@ -230,21 +334,36 @@ shape: shape, target_quality: qualitySelect.value, target_root: parseInt(rootSelect.value), - voicing: voicing + voicing: voicing, + base_tuning: window.currentTuningMIDI ? window.currentTuningMIDI.slice() : [], + baseline_shift: parseInt(document.getElementById('baseline-shift').value) || 0, + range_down: parseInt(document.getElementById('range-down').value) || 0, + range_up: parseInt(document.getElementById('range-up').value) || 0, }; loadingEl.style.display = ''; loadingEl.textContent = 'Searching tunings...'; resultsContainer.innerHTML = ''; - window.go.main.App.FindShapeTunings(query, shapes).then(results => { - loadingEl.style.display = 'none'; - renderResults(results || [], shape.name); - }).catch(err => { - loadingEl.style.display = 'none'; - loadingEl.style.display = ''; - loadingEl.textContent = 'Error: ' + err; - }); + if (isDensitySet()) { + window.go.main.App.FindDensityTunings(query).then(results => { + loadingEl.style.display = 'none'; + renderDensityResults(results || []); + }).catch(err => { + loadingEl.style.display = 'none'; + loadingEl.style.display = ''; + loadingEl.textContent = 'Error: ' + err; + }); + } else { + window.go.main.App.FindShapeTunings(query, shapes).then(results => { + loadingEl.style.display = 'none'; + renderResults(results || [], shape.name); + }).catch(err => { + loadingEl.style.display = 'none'; + loadingEl.style.display = ''; + loadingEl.textContent = 'Error: ' + err; + }); + } } const PAGE_SIZE = 10; @@ -391,6 +510,170 @@ body.appendChild(grid); } + const setBtn = document.createElement('button'); + setBtn.className = 'btn-small'; + setBtn.textContent = 'Set to instrument tuning'; + setBtn.style.marginTop = '0.75rem'; + setBtn.addEventListener('click', () => { + if (window.setTuningFromExplorer) window.setTuningFromExplorer(tc.tuning); + }); + body.appendChild(setBtn); + + card.appendChild(header); + card.appendChild(body); + return card; + } + + function renderDensityResults(results) { + resultsContainer.innerHTML = ''; + + if (results.length === 0) { + const msg = document.createElement('div'); + msg.className = 'loading'; + msg.textContent = 'No tunings found.'; + resultsContainer.appendChild(msg); + return; + } + + const explain = document.createElement('p'); + explain.className = 'density-explanation'; + explain.textContent = 'Tunings ranked by total playable chords within current fret/finger settings. Expand a card to see all identified chords with their simplest fingerings.'; + resultsContainer.appendChild(explain); + + const compat = results.filter(r => r.high_compat).length; + const summary = document.createElement('div'); + summary.style.cssText = 'font-size:0.8rem;color:#888;margin-bottom:0.75rem;'; + summary.textContent = results.length.toLocaleString() + ' tuning' + (results.length !== 1 ? 's' : '') + ' found' + + (compat ? ' \u2014 ' + compat.toLocaleString() + ' highly compatible' : ''); + resultsContainer.appendChild(summary); + + const listEl = document.createElement('div'); + resultsContainer.appendChild(listEl); + + let shown = 0; + let loadMoreBtn = null; + + function renderPage() { + const end = Math.min(shown + PAGE_SIZE, results.length); + for (let idx = shown; idx < end; idx++) { + listEl.appendChild(buildDensityCard(results[idx])); + } + shown = end; + + if (shown < results.length) { + if (!loadMoreBtn) { + loadMoreBtn = document.createElement('button'); + loadMoreBtn.className = 'btn-load-more'; + loadMoreBtn.addEventListener('click', renderPage); + resultsContainer.appendChild(loadMoreBtn); + } + loadMoreBtn.textContent = 'Load more (' + (results.length - shown) + ' remaining)'; + } else if (loadMoreBtn) { + loadMoreBtn.remove(); + loadMoreBtn = null; + } + } + + renderPage(); + } + + function buildDensityCard(dc) { + const card = document.createElement('div'); + card.className = 'tuning-card'; + + const header = document.createElement('div'); + header.className = 'tuning-card-header'; + + const h3 = document.createElement('h3'); + h3.textContent = dc.chord; + + if (dc.high_compat) { + card.classList.add('high-compat'); + const star = document.createElement('span'); + star.className = 'compat-star'; + star.textContent = '\u2605'; + star.title = dc.maj_min_count + ' major/minor triads'; + h3.appendChild(star); + } + + const stats = document.createElement('span'); + stats.className = 'tuning-stats'; + stats.textContent = dc.valid_chords + ' chords, avg ' + dc.avg_fingers.toFixed(1) + ' fingers'; + + const notes = document.createElement('span'); + notes.className = 'tuning-notes'; + notes.textContent = dc.tuning.join(' '); + + const arrow = document.createElement('span'); + arrow.className = 'expand-icon'; + arrow.textContent = '\u25B6'; + + header.appendChild(h3); + header.appendChild(stats); + header.appendChild(notes); + header.appendChild(arrow); + + header.addEventListener('click', () => { + card.classList.toggle('expanded'); + }); + + const body = document.createElement('div'); + body.className = 'tuning-card-body'; + + if (dc.chords && dc.chords.length > 0) { + const grid = document.createElement('div'); + grid.className = 'companion-grid'; + + dc.chords.forEach(ch => { + const item = document.createElement('div'); + item.className = 'companion-item'; + + const chordLabel = document.createElement('div'); + chordLabel.className = 'companion-chord'; + chordLabel.textContent = ch.chord; + + const fingerBadge = document.createElement('span'); + fingerBadge.className = 'finger-count'; + fingerBadge.textContent = ch.fingers + 'f'; + chordLabel.appendChild(fingerBadge); + + item.appendChild(chordLabel); + + const fingering = ch.frets.map(f => f === -1 ? 'x' : String(f)); + const maxFretVal = Math.max(...ch.frets.filter(f => f >= 0), 1); + const fb = document.createElement('div'); + fb.className = 'fretboard alternative-fretboard'; + renderSingleFretboard(fb, fingering, Math.max(maxFretVal, 4)); + item.appendChild(fb); + + const midi = []; + for (let si = 0; si < dc.tuning_midi.length; si++) { + if (ch.frets[si] !== -1 && dc.tuning_midi[si] != null) { + midi.push(dc.tuning_midi[si] + ch.frets[si]); + } + } + if (midi.length) item.dataset.chordMidi = JSON.stringify(midi); + + const notesDiv = document.createElement('div'); + notesDiv.className = 'companion-notes'; + notesDiv.textContent = (ch.notes || []).join(' '); + item.appendChild(notesDiv); + + grid.appendChild(item); + }); + + body.appendChild(grid); + } + + const setBtn = document.createElement('button'); + setBtn.className = 'btn-small'; + setBtn.textContent = 'Set to instrument tuning'; + setBtn.style.marginTop = '0.75rem'; + setBtn.addEventListener('click', () => { + if (window.setTuningFromExplorer) window.setTuningFromExplorer(dc.tuning); + }); + body.appendChild(setBtn); + card.appendChild(header); card.appendChild(body); return card; @@ -502,4 +785,14 @@ } window.initShapeExplorer = init; + window.renderSingleFretboard = renderSingleFretboard; + window.getScoreSetsData = () => scoreSetsData; + window.setScoreSetsData = (data) => { + scoreSetsData = data; + syncShapesRef(); + renderSetSelector(); + renderShapeList(); + buildVoicingInputs(); + }; + window.persistSets = persistSets; })(); diff --git a/frontend/dist/vendor/WebAudioFontPlayer.js b/frontend/dist/vendor/WebAudioFontPlayer.js new file mode 100644 index 0000000..d9feb11 --- /dev/null +++ b/frontend/dist/vendor/WebAudioFontPlayer.js @@ -0,0 +1,1222 @@ +'use strict'; +var WebAudioFontChannel = /** @class */ (function () { + function WebAudioFontChannel(audioContext) { + this.audioContext = audioContext; + this.input = audioContext.createGain(); + this.band32 = this.bandEqualizer(this.input, 32); + this.band64 = this.bandEqualizer(this.band32, 64); + this.band128 = this.bandEqualizer(this.band64, 128); + this.band256 = this.bandEqualizer(this.band128, 256); + this.band512 = this.bandEqualizer(this.band256, 512); + this.band1k = this.bandEqualizer(this.band512, 1024); + this.band2k = this.bandEqualizer(this.band1k, 2048); + this.band4k = this.bandEqualizer(this.band2k, 4096); + this.band8k = this.bandEqualizer(this.band4k, 8192); + this.band16k = this.bandEqualizer(this.band8k, 16384); + this.output = audioContext.createGain(); + this.band16k.connect(this.output); + } + WebAudioFontChannel.prototype.bandEqualizer = function (from, frequency) { + var filter = this.audioContext.createBiquadFilter(); + filter.frequency.setTargetAtTime(frequency, 0, 0.0001); + filter.type = "peaking"; + filter.gain.setTargetAtTime(0, 0, 0.0001); + filter.Q.setTargetAtTime(1.0, 0, 0.0001); + from.connect(filter); + return filter; + }; + ; + return WebAudioFontChannel; +}()); +var WebAudioFontLoader = /** @class */ (function () { + function WebAudioFontLoader(player) { + this.cached = []; + this.instrumentKeyArray = []; + this.instrumentNamesArray = []; + this.choosenInfos = []; + this.drumNamesArray = []; + this.drumKeyArray = []; + this.instrumentTitles = function () { + if (this.instrumentNamesArray.length == 0) { + var insNames = []; + insNames[0] = "Acoustic Grand Piano: Piano"; + insNames[1] = "Bright Acoustic Piano: Piano"; + insNames[2] = "Electric Grand Piano: Piano"; + insNames[3] = "Honky-tonk Piano: Piano"; + insNames[4] = "Electric Piano 1: Piano"; + insNames[5] = "Electric Piano 2: Piano"; + insNames[6] = "Harpsichord: Piano"; + insNames[7] = "Clavinet: Piano"; + insNames[8] = "Celesta: Chromatic Percussion"; + insNames[9] = "Glockenspiel: Chromatic Percussion"; + insNames[10] = "Music Box: Chromatic Percussion"; + insNames[11] = "Vibraphone: Chromatic Percussion"; + insNames[12] = "Marimba: Chromatic Percussion"; + insNames[13] = "Xylophone: Chromatic Percussion"; + insNames[14] = "Tubular Bells: Chromatic Percussion"; + insNames[15] = "Dulcimer: Chromatic Percussion"; + insNames[16] = "Drawbar Organ: Organ"; + insNames[17] = "Percussive Organ: Organ"; + insNames[18] = "Rock Organ: Organ"; + insNames[19] = "Church Organ: Organ"; + insNames[20] = "Reed Organ: Organ"; + insNames[21] = "Accordion: Organ"; + insNames[22] = "Harmonica: Organ"; + insNames[23] = "Tango Accordion: Organ"; + insNames[24] = "Acoustic Guitar (nylon): Guitar"; + insNames[25] = "Acoustic Guitar (steel): Guitar"; + insNames[26] = "Electric Guitar (jazz): Guitar"; + insNames[27] = "Electric Guitar (clean): Guitar"; + insNames[28] = "Electric Guitar (muted): Guitar"; + insNames[29] = "Overdriven Guitar: Guitar"; + insNames[30] = "Distortion Guitar: Guitar"; + insNames[31] = "Guitar Harmonics: Guitar"; + insNames[32] = "Acoustic Bass: Bass"; + insNames[33] = "Electric Bass (finger): Bass"; + insNames[34] = "Electric Bass (pick): Bass"; + insNames[35] = "Fretless Bass: Bass"; + insNames[36] = "Slap Bass 1: Bass"; + insNames[37] = "Slap Bass 2: Bass"; + insNames[38] = "Synth Bass 1: Bass"; + insNames[39] = "Synth Bass 2: Bass"; + insNames[40] = "Violin: Strings"; + insNames[41] = "Viola: Strings"; + insNames[42] = "Cello: Strings"; + insNames[43] = "Contrabass: Strings"; + insNames[44] = "Tremolo Strings: Strings"; + insNames[45] = "Pizzicato Strings: Strings"; + insNames[46] = "Orchestral Harp: Strings"; + insNames[47] = "Timpani: Strings"; + insNames[48] = "String Ensemble 1: Ensemble"; + insNames[49] = "String Ensemble 2: Ensemble"; + insNames[50] = "Synth Strings 1: Ensemble"; + insNames[51] = "Synth Strings 2: Ensemble"; + insNames[52] = "Choir Aahs: Ensemble"; + insNames[53] = "Voice Oohs: Ensemble"; + insNames[54] = "Synth Choir: Ensemble"; + insNames[55] = "Orchestra Hit: Ensemble"; + insNames[56] = "Trumpet: Brass"; + insNames[57] = "Trombone: Brass"; + insNames[58] = "Tuba: Brass"; + insNames[59] = "Muted Trumpet: Brass"; + insNames[60] = "French Horn: Brass"; + insNames[61] = "Brass Section: Brass"; + insNames[62] = "Synth Brass 1: Brass"; + insNames[63] = "Synth Brass 2: Brass"; + insNames[64] = "Soprano Sax: Reed"; + insNames[65] = "Alto Sax: Reed"; + insNames[66] = "Tenor Sax: Reed"; + insNames[67] = "Baritone Sax: Reed"; + insNames[68] = "Oboe: Reed"; + insNames[69] = "English Horn: Reed"; + insNames[70] = "Bassoon: Reed"; + insNames[71] = "Clarinet: Reed"; + insNames[72] = "Piccolo: Pipe"; + insNames[73] = "Flute: Pipe"; + insNames[74] = "Recorder: Pipe"; + insNames[75] = "Pan Flute: Pipe"; + insNames[76] = "Blown bottle: Pipe"; + insNames[77] = "Shakuhachi: Pipe"; + insNames[78] = "Whistle: Pipe"; + insNames[79] = "Ocarina: Pipe"; + insNames[80] = "Lead 1 (square): Synth Lead"; + insNames[81] = "Lead 2 (sawtooth): Synth Lead"; + insNames[82] = "Lead 3 (calliope): Synth Lead"; + insNames[83] = "Lead 4 (chiff): Synth Lead"; + insNames[84] = "Lead 5 (charang): Synth Lead"; + insNames[85] = "Lead 6 (voice): Synth Lead"; + insNames[86] = "Lead 7 (fifths): Synth Lead"; + insNames[87] = "Lead 8 (bass + lead): Synth Lead"; + insNames[88] = "Pad 1 (new age): Synth Pad"; + insNames[89] = "Pad 2 (warm): Synth Pad"; + insNames[90] = "Pad 3 (polysynth): Synth Pad"; + insNames[91] = "Pad 4 (choir): Synth Pad"; + insNames[92] = "Pad 5 (bowed): Synth Pad"; + insNames[93] = "Pad 6 (metallic): Synth Pad"; + insNames[94] = "Pad 7 (halo): Synth Pad"; + insNames[95] = "Pad 8 (sweep): Synth Pad"; + insNames[96] = "FX 1 (rain): Synth Effects"; + insNames[97] = "FX 2 (soundtrack): Synth Effects"; + insNames[98] = "FX 3 (crystal): Synth Effects"; + insNames[99] = "FX 4 (atmosphere): Synth Effects"; + insNames[100] = "FX 5 (brightness): Synth Effects"; + insNames[101] = "FX 6 (goblins): Synth Effects"; + insNames[102] = "FX 7 (echoes): Synth Effects"; + insNames[103] = "FX 8 (sci-fi): Synth Effects"; + insNames[104] = "Sitar: Ethnic"; + insNames[105] = "Banjo: Ethnic"; + insNames[106] = "Shamisen: Ethnic"; + insNames[107] = "Koto: Ethnic"; + insNames[108] = "Kalimba: Ethnic"; + insNames[109] = "Bagpipe: Ethnic"; + insNames[110] = "Fiddle: Ethnic"; + insNames[111] = "Shanai: Ethnic"; + insNames[112] = "Tinkle Bell: Percussive"; + insNames[113] = "Agogo: Percussive"; + insNames[114] = "Steel Drums: Percussive"; + insNames[115] = "Woodblock: Percussive"; + insNames[116] = "Taiko Drum: Percussive"; + insNames[117] = "Melodic Tom: Percussive"; + insNames[118] = "Synth Drum: Percussive"; + insNames[119] = "Reverse Cymbal: Percussive"; + insNames[120] = "Guitar Fret Noise: Sound effects"; + insNames[121] = "Breath Noise: Sound effects"; + insNames[122] = "Seashore: Sound effects"; + insNames[123] = "Bird Tweet: Sound effects"; + insNames[124] = "Telephone Ring: Sound effects"; + insNames[125] = "Helicopter: Sound effects"; + insNames[126] = "Applause: Sound effects"; + insNames[127] = "Gunshot: Sound effects"; + this.instrumentNamesArray = insNames; + } + return this.instrumentNamesArray; + }; + this.player = player; + } + WebAudioFontLoader.prototype.startLoad = function (audioContext, filePath, variableName) { + if (window[variableName]) { + return; + } + for (var i = 0; i < this.cached.length; i++) { + if (this.cached[i].variableName == variableName) { + return; + } + } + this.cached.push({ + filePath: filePath, + variableName: variableName + }); + var r = document.createElement('script'); + r.setAttribute("type", "text/javascript"); + r.setAttribute("src", filePath); + document.getElementsByTagName("head")[0].appendChild(r); + this.decodeAfterLoading(audioContext, variableName); + }; + ; + WebAudioFontLoader.prototype.decodeAfterLoading = function (audioContext, variableName) { + var me = this; + this.waitOrFinish(variableName, function () { + me.player.adjustPreset(audioContext, window[variableName]); + }); + }; + ; + WebAudioFontLoader.prototype.waitOrFinish = function (variableName, onFinish) { + if (window[variableName]) { + onFinish(); + } + else { + var me = this; + setTimeout(function () { + me.waitOrFinish(variableName, onFinish); + }, 111); + } + }; + ; + WebAudioFontLoader.prototype.loaded = function (variableName) { + if (!(window[variableName])) { + return false; + } + var preset = window[variableName]; + for (var i = 0; i < preset.zones.length; i++) { + if (!(preset.zones[i].buffer)) { + return false; + } + } + return true; + }; + ; + WebAudioFontLoader.prototype.progress = function () { + if (this.cached.length > 0) { + for (var k = 0; k < this.cached.length; k++) { + if (!this.loaded(this.cached[k].variableName)) { + return k / this.cached.length; + } + } + return 1; + } + else { + return 1; + } + }; + ; + WebAudioFontLoader.prototype.waitLoad = function (onFinish) { + var me = this; + if (this.progress() >= 1) { + onFinish(); + } + else { + setTimeout(function () { + me.waitLoad(onFinish); + }, 333); + } + }; + ; + WebAudioFontLoader.prototype.instrumentKeys = function () { + if (this.instrumentKeyArray.length == 0) { + this.instrumentKeyArray = [ + '0000_JCLive_sf2_file', '0000_Aspirin_sf2_file', '0000_Chaos_sf2_file', '0000_FluidR3_GM_sf2_file', '0000_GeneralUserGS_sf2_file', '0000_SBLive_sf2', '0000_SoundBlasterOld_sf2', + '0001_FluidR3_GM_sf2_file', '0001_GeneralUserGS_sf2_file', '0002_GeneralUserGS_sf2_file', '0003_GeneralUserGS_sf2_file', '0010_Aspirin_sf2_file', '0010_Chaos_sf2_file', '0010_FluidR3_GM_sf2_file', + '0010_GeneralUserGS_sf2_file', '0010_JCLive_sf2_file', '0010_SBLive_sf2', '0010_SoundBlasterOld_sf2', '0011_Aspirin_sf2_file', '0011_FluidR3_GM_sf2_file', '0011_GeneralUserGS_sf2_file', + '0012_GeneralUserGS_sf2_file', '0020_Aspirin_sf2_file', '0020_Chaos_sf2_file', '0020_FluidR3_GM_sf2_file', '0020_GeneralUserGS_sf2_file', '0020_JCLive_sf2_file', '0020_SBLive_sf2', + '0020_SoundBlasterOld_sf2', '0021_Aspirin_sf2_file', '0021_GeneralUserGS_sf2_file', '0022_Aspirin_sf2_file', '0030_Aspirin_sf2_file', '0030_Chaos_sf2_file', '0030_FluidR3_GM_sf2_file', + '0030_GeneralUserGS_sf2_file', '0030_JCLive_sf2_file', '0030_SBLive_sf2', '0030_SoundBlasterOld_sf2', '0031_Aspirin_sf2_file', '0031_FluidR3_GM_sf2_file', '0031_GeneralUserGS_sf2_file', + '0031_SoundBlasterOld_sf2', '0040_Aspirin_sf2_file', '0040_Chaos_sf2_file', '0040_FluidR3_GM_sf2_file', '0040_GeneralUserGS_sf2_file', '0040_JCLive_sf2_file', '0040_SBLive_sf2', + '0040_SoundBlasterOld_sf2', '0041_FluidR3_GM_sf2_file', '0041_GeneralUserGS_sf2_file', '0041_SoundBlasterOld_sf2', '0042_GeneralUserGS_sf2_file', '0043_GeneralUserGS_sf2_file', + '0044_GeneralUserGS_sf2_file', '0045_GeneralUserGS_sf2_file', '0046_GeneralUserGS_sf2_file', '0050_Aspirin_sf2_file', '0050_Chaos_sf2_file', '0050_FluidR3_GM_sf2_file', + '0050_GeneralUserGS_sf2_file', '0050_JCLive_sf2_file', '0050_SBLive_sf2', '0050_SoundBlasterOld_sf2', '0051_FluidR3_GM_sf2_file', '0051_GeneralUserGS_sf2_file', '0052_GeneralUserGS_sf2_file', + '0053_GeneralUserGS_sf2_file', '0054_GeneralUserGS_sf2_file', '0060_Aspirin_sf2_file', '0060_Chaos_sf2_file', '0060_FluidR3_GM_sf2_file', '0060_GeneralUserGS_sf2_file', '0060_JCLive_sf2_file', + '0060_SBLive_sf2', '0060_SoundBlasterOld_sf2', '0061_Aspirin_sf2_file', '0061_GeneralUserGS_sf2_file', '0061_SoundBlasterOld_sf2', '0062_GeneralUserGS_sf2_file', '0070_Aspirin_sf2_file', + '0070_Chaos_sf2_file', '0070_FluidR3_GM_sf2_file', '0070_GeneralUserGS_sf2_file', '0070_JCLive_sf2_file', '0070_SBLive_sf2', '0070_SoundBlasterOld_sf2', '0071_GeneralUserGS_sf2_file', + '0080_Aspirin_sf2_file', '0080_Chaos_sf2_file', '0080_FluidR3_GM_sf2_file', '0080_GeneralUserGS_sf2_file', '0080_JCLive_sf2_file', '0080_SBLive_sf2', '0080_SoundBlasterOld_sf2', + '0081_FluidR3_GM_sf2_file', '0081_GeneralUserGS_sf2_file', '0081_SoundBlasterOld_sf2', '0090_Aspirin_sf2_file', '0090_Chaos_sf2_file', '0090_FluidR3_GM_sf2_file', '0090_GeneralUserGS_sf2_file', + '0090_JCLive_sf2_file', '0090_SBLive_sf2', '0090_SoundBlasterOld_sf2', '0091_SoundBlasterOld_sf2', '0100_Aspirin_sf2_file', '0100_Chaos_sf2_file', '0100_FluidR3_GM_sf2_file', + '0100_GeneralUserGS_sf2_file', '0100_JCLive_sf2_file', '0100_SBLive_sf2', '0100_SoundBlasterOld_sf2', '0101_GeneralUserGS_sf2_file', '0101_SoundBlasterOld_sf2', '0110_Aspirin_sf2_file', + '0110_Chaos_sf2_file', '0110_FluidR3_GM_sf2_file', '0110_GeneralUserGS_sf2_file', '0110_JCLive_sf2_file', '0110_SBLive_sf2', '0110_SoundBlasterOld_sf2', '0111_FluidR3_GM_sf2_file', + '0120_Aspirin_sf2_file', '0120_Chaos_sf2_file', '0120_FluidR3_GM_sf2_file', '0120_GeneralUserGS_sf2_file', '0120_JCLive_sf2_file', '0120_SBLive_sf2', '0120_SoundBlasterOld_sf2', + '0121_FluidR3_GM_sf2_file', '0121_GeneralUserGS_sf2_file', '0130_Aspirin_sf2_file', '0130_Chaos_sf2_file', '0130_FluidR3_GM_sf2_file', '0130_GeneralUserGS_sf2_file', '0130_JCLive_sf2_file', + '0130_SBLive_sf2', '0130_SoundBlasterOld_sf2', '0131_FluidR3_GM_sf2_file', '0140_Aspirin_sf2_file', '0140_Chaos_sf2_file', '0140_FluidR3_GM_sf2_file', '0140_GeneralUserGS_sf2_file', + '0140_JCLive_sf2_file', '0140_SBLive_sf2', '0140_SoundBlasterOld_sf2', '0141_FluidR3_GM_sf2_file', '0141_GeneralUserGS_sf2_file', '0142_GeneralUserGS_sf2_file', '0143_GeneralUserGS_sf2_file', + '0150_Aspirin_sf2_file', '0150_Chaos_sf2_file', '0150_FluidR3_GM_sf2_file', '0150_GeneralUserGS_sf2_file', '0150_JCLive_sf2_file', '0150_SBLive_sf2', '0150_SoundBlasterOld_sf2', + '0151_FluidR3_GM_sf2_file', '0160_Aspirin_sf2_file', '0160_Chaos_sf2_file', '0160_FluidR3_GM_sf2_file', '0160_GeneralUserGS_sf2_file', '0160_JCLive_sf2_file', '0160_SBLive_sf2', + '0160_SoundBlasterOld_sf2', '0161_Aspirin_sf2_file', '0161_FluidR3_GM_sf2_file', '0161_SoundBlasterOld_sf2', '0170_Aspirin_sf2_file', '0170_Chaos_sf2_file', '0170_FluidR3_GM_sf2_file', + '0170_GeneralUserGS_sf2_file', '0170_JCLive_sf2_file', '0170_SBLive_sf2', '0170_SoundBlasterOld_sf2', '0171_FluidR3_GM_sf2_file', '0171_GeneralUserGS_sf2_file', '0172_FluidR3_GM_sf2_file', + '0180_Aspirin_sf2_file', '0180_Chaos_sf2_file', '0180_FluidR3_GM_sf2_file', '0180_GeneralUserGS_sf2_file', '0180_JCLive_sf2_file', '0180_SBLive_sf2', '0180_SoundBlasterOld_sf2', + '0181_Aspirin_sf2_file', '0181_GeneralUserGS_sf2_file', '0181_SoundBlasterOld_sf2', '0190_Aspirin_sf2_file', '0190_Chaos_sf2_file', '0190_FluidR3_GM_sf2_file', '0190_GeneralUserGS_sf2_file', + '0190_JCLive_sf2_file', '0190_SBLive_sf2', '0190_SoundBlasterOld_sf2', '0191_Aspirin_sf2_file', '0191_GeneralUserGS_sf2_file', '0191_SoundBlasterOld_sf2', '0200_Aspirin_sf2_file', + '0200_Chaos_sf2_file', '0200_FluidR3_GM_sf2_file', '0200_GeneralUserGS_sf2_file', '0200_JCLive_sf2_file', '0200_SBLive_sf2', '0200_SoundBlasterOld_sf2', '0201_Aspirin_sf2_file', + '0201_FluidR3_GM_sf2_file', '0201_GeneralUserGS_sf2_file', '0201_SoundBlasterOld_sf2', '0210_Aspirin_sf2_file', '0210_Chaos_sf2_file', '0210_FluidR3_GM_sf2_file', '0210_GeneralUserGS_sf2_file', + '0210_JCLive_sf2_file', '0210_SBLive_sf2', '0210_SoundBlasterOld_sf2', '0211_Aspirin_sf2_file', '0211_FluidR3_GM_sf2_file', '0211_GeneralUserGS_sf2_file', '0211_SoundBlasterOld_sf2', + '0212_GeneralUserGS_sf2_file', '0220_Aspirin_sf2_file', '0220_Chaos_sf2_file', '0220_FluidR3_GM_sf2_file', '0220_GeneralUserGS_sf2_file', '0220_JCLive_sf2_file', '0220_SBLive_sf2', + '0220_SoundBlasterOld_sf2', '0221_FluidR3_GM_sf2_file', '0230_Aspirin_sf2_file', '0230_Chaos_sf2_file', '0230_FluidR3_GM_sf2_file', '0230_GeneralUserGS_sf2_file', '0230_JCLive_sf2_file', + '0230_SBLive_sf2', '0230_SoundBlasterOld_sf2', '0231_FluidR3_GM_sf2_file', '0231_GeneralUserGS_sf2_file', '0231_JCLive_sf2_file', '0231_SoundBlasterOld_sf2', '0232_FluidR3_GM_sf2_file', + '0233_FluidR3_GM_sf2_file', '0240_Aspirin_sf2_file', '0240_Chaos_sf2_file', '0240_FluidR3_GM_sf2_file', '0240_GeneralUserGS_sf2_file', '0240_JCLive_sf2_file', '0240_LK_Godin_Nylon_SF2_file', + '0240_SBLive_sf2', '0240_SoundBlasterOld_sf2', '0241_GeneralUserGS_sf2_file', '0241_JCLive_sf2_file', '0242_JCLive_sf2_file', '0243_JCLive_sf2_file', '0253_Acoustic_Guitar_sf2_file', + '0250_Aspirin_sf2_file', '0250_Chaos_sf2_file', '0250_FluidR3_GM_sf2_file', '0250_GeneralUserGS_sf2_file', '0250_JCLive_sf2_file', '0250_LK_AcousticSteel_SF2_file', '0250_SBLive_sf2', + '0250_SoundBlasterOld_sf2', '0251_Acoustic_Guitar_sf2_file', '0251_GeneralUserGS_sf2_file', '0252_Acoustic_Guitar_sf2_file', '0252_GeneralUserGS_sf2_file', '0253_Acoustic_Guitar_sf2_file', + '0253_GeneralUserGS_sf2_file', '0254_Acoustic_Guitar_sf2_file', '0254_GeneralUserGS_sf2_file', '0255_GeneralUserGS_sf2_file', '0260_Aspirin_sf2_file', '0260_Chaos_sf2_file', + '0260_FluidR3_GM_sf2_file', '0260_GeneralUserGS_sf2_file', '0260_JCLive_sf2_file', '0260_SBLive_sf2', '0260_SoundBlasterOld_sf2', '0260_Stratocaster_sf2_file', '0261_GeneralUserGS_sf2_file', + '0261_SoundBlasterOld_sf2', '0261_Stratocaster_sf2_file', '0262_Stratocaster_sf2_file', '0270_Aspirin_sf2_file', '0270_Chaos_sf2_file', '0270_FluidR3_GM_sf2_file', '0270_GeneralUserGS_sf2_file', + '0270_Gibson_Les_Paul_sf2_file', '0270_JCLive_sf2_file', '0270_SBAWE32_sf2_file', '0270_SBLive_sf2', '0270_SoundBlasterOld_sf2', '0270_Stratocaster_sf2_file', '0271_GeneralUserGS_sf2_file', + '0271_Stratocaster_sf2_file', '0272_Stratocaster_sf2_file', '0280_Aspirin_sf2_file', '0280_Chaos_sf2_file', '0280_FluidR3_GM_sf2_file', '0280_GeneralUserGS_sf2_file', '0280_JCLive_sf2_file', + '0280_LesPaul_sf2', '0280_LesPaul_sf2_file', '0280_SBAWE32_sf2_file', '0280_SBLive_sf2', '0280_SoundBlasterOld_sf2', '0281_Aspirin_sf2_file', '0281_FluidR3_GM_sf2_file', + '0281_GeneralUserGS_sf2_file', '0282_FluidR3_GM_sf2_file', '0282_GeneralUserGS_sf2_file', '0283_GeneralUserGS_sf2_file', '0290_Aspirin_sf2_file', '0290_Chaos_sf2_file', '0290_FluidR3_GM_sf2_file', + '0290_GeneralUserGS_sf2_file', '0290_JCLive_sf2_file', '0290_LesPaul_sf2', '0290_LesPaul_sf2_file', '0290_SBAWE32_sf2_file', '0290_SBLive_sf2', '0290_SoundBlasterOld_sf2', '0291_Aspirin_sf2_file', + '0291_LesPaul_sf2', '0291_LesPaul_sf2_file', '0291_SBAWE32_sf2_file', '0291_SoundBlasterOld_sf2', '0292_Aspirin_sf2_file', '0292_LesPaul_sf2', '0292_LesPaul_sf2_file', '0300_Aspirin_sf2_file', + '0300_Chaos_sf2_file', '0300_FluidR3_GM_sf2_file', '0300_GeneralUserGS_sf2_file', '0300_JCLive_sf2_file', '0300_LesPaul_sf2', '0300_LesPaul_sf2_file', '0300_SBAWE32_sf2_file', '0300_SBLive_sf2', + '0300_SoundBlasterOld_sf2', '0301_Aspirin_sf2_file', '0301_FluidR3_GM_sf2_file', '0301_GeneralUserGS_sf2_file', '0301_JCLive_sf2_file', '0301_LesPaul_sf2', '0301_LesPaul_sf2_file', + '0302_Aspirin_sf2_file', '0302_GeneralUserGS_sf2_file', '0302_JCLive_sf2_file', '0303_Aspirin_sf2_file', '0304_Aspirin_sf2_file', '0310_Aspirin_sf2_file', '0310_Chaos_sf2_file', + '0310_FluidR3_GM_sf2_file', '0310_GeneralUserGS_sf2_file', '0310_JCLive_sf2_file', '0310_LesPaul_sf2', '0310_LesPaul_sf2_file', '0310_SBAWE32_sf2_file', '0310_SBLive_sf2', + '0310_SoundBlasterOld_sf2', '0311_FluidR3_GM_sf2_file', '0311_GeneralUserGS_sf2_file', '0320_Aspirin_sf2_file', '0320_Chaos_sf2_file', '0320_FluidR3_GM_sf2_file', '0320_GeneralUserGS_sf2_file', + '0320_JCLive_sf2_file', '0320_SBLive_sf2', '0320_SoundBlasterOld_sf2', '0321_GeneralUserGS_sf2_file', '0322_GeneralUserGS_sf2_file', '0330_Aspirin_sf2_file', '0330_Chaos_sf2_file', + '0330_FluidR3_GM_sf2_file', '0330_GeneralUserGS_sf2_file', '0330_JCLive_sf2_file', '0330_SBLive_sf2', '0330_SoundBlasterOld_sf2', '0331_GeneralUserGS_sf2_file', '0332_GeneralUserGS_sf2_file', + '0340_Aspirin_sf2_file', '0340_Chaos_sf2_file', '0340_FluidR3_GM_sf2_file', '0340_GeneralUserGS_sf2_file', '0340_JCLive_sf2_file', '0340_SBLive_sf2', '0340_SoundBlasterOld_sf2', + '0341_Aspirin_sf2_file', '0341_GeneralUserGS_sf2_file', '0350_Aspirin_sf2_file', '0350_Chaos_sf2_file', '0350_FluidR3_GM_sf2_file', '0350_GeneralUserGS_sf2_file', '0350_JCLive_sf2_file', + '0350_SBLive_sf2', '0350_SoundBlasterOld_sf2', '0351_GeneralUserGS_sf2_file', '0360_Aspirin_sf2_file', '0360_Chaos_sf2_file', '0360_FluidR3_GM_sf2_file', '0360_GeneralUserGS_sf2_file', + '0360_JCLive_sf2_file', '0360_SBLive_sf2', '0360_SoundBlasterOld_sf2', '0361_GeneralUserGS_sf2_file', '0370_Aspirin_sf2_file', '0370_Chaos_sf2_file', '0370_FluidR3_GM_sf2_file', + '0370_GeneralUserGS_sf2_file', '0370_JCLive_sf2_file', '0370_SBLive_sf2', '0370_SoundBlasterOld_sf2', '0371_GeneralUserGS_sf2_file', '0372_GeneralUserGS_sf2_file', + '0385_GeneralUserGS_sf2_file', + '0380_Aspirin_sf2_file', + '0380_Chaos_sf2_file', + '0380_FluidR3_GM_sf2_file', + '0380_GeneralUserGS_sf2_file', + '0380_JCLive_sf2_file', + '0380_SBLive_sf2', + '0380_SoundBlasterOld_sf2', + '0381_FluidR3_GM_sf2_file', + '0381_GeneralUserGS_sf2_file', + '0382_FluidR3_GM_sf2_file', + '0382_GeneralUserGS_sf2_file', + '0383_GeneralUserGS_sf2_file', + '0384_GeneralUserGS_sf2_file', + '0386_GeneralUserGS_sf2_file', + '0387_GeneralUserGS_sf2_file', + '0390_Aspirin_sf2_file', '0390_Chaos_sf2_file', '0390_FluidR3_GM_sf2_file', + '0390_GeneralUserGS_sf2_file', '0390_JCLive_sf2_file', '0390_SBLive_sf2', '0390_SoundBlasterOld_sf2', '0391_FluidR3_GM_sf2_file', + '0391_GeneralUserGS_sf2_file', '0391_SoundBlasterOld_sf2', '0392_FluidR3_GM_sf2_file', '0392_GeneralUserGS_sf2_file', + '0393_GeneralUserGS_sf2_file', '0400_Aspirin_sf2_file', '0400_Chaos_sf2_file', '0400_FluidR3_GM_sf2_file', '0400_GeneralUserGS_sf2_file', + '0400_JCLive_sf2_file', '0400_SBLive_sf2', '0400_SoundBlasterOld_sf2', '0401_Aspirin_sf2_file', '0401_FluidR3_GM_sf2_file', + '0401_GeneralUserGS_sf2_file', '0402_GeneralUserGS_sf2_file', '0410_Aspirin_sf2_file', '0410_Chaos_sf2_file', '0410_FluidR3_GM_sf2_file', + '0410_GeneralUserGS_sf2_file', '0410_JCLive_sf2_file', '0410_SBLive_sf2', '0410_SoundBlasterOld_sf2', '0411_FluidR3_GM_sf2_file', + '0420_Aspirin_sf2_file', '0420_Chaos_sf2_file', '0420_FluidR3_GM_sf2_file', '0420_GeneralUserGS_sf2_file', '0420_JCLive_sf2_file', + '0420_SBLive_sf2', '0420_SoundBlasterOld_sf2', '0421_FluidR3_GM_sf2_file', '0421_GeneralUserGS_sf2_file', '0430_Aspirin_sf2_file', + '0430_Chaos_sf2_file', '0430_FluidR3_GM_sf2_file', '0430_GeneralUserGS_sf2_file', '0430_JCLive_sf2_file', '0430_SBLive_sf2', + '0430_SoundBlasterOld_sf2', '0431_FluidR3_GM_sf2_file', '0440_Aspirin_sf2_file', '0440_Chaos_sf2_file', '0440_FluidR3_GM_sf2_file', + '0440_GeneralUserGS_sf2_file', '0440_JCLive_sf2_file', '0440_SBLive_sf2', + '0440_SoundBlasterOld_sf2', '0441_GeneralUserGS_sf2_file', '0442_GeneralUserGS_sf2_file', '0450_Aspirin_sf2_file', '0450_Chaos_sf2_file', + '0450_FluidR3_GM_sf2_file', + '0450_GeneralUserGS_sf2_file', '0450_JCLive_sf2_file', '0450_SBLive_sf2', '0450_SoundBlasterOld_sf2', '0451_FluidR3_GM_sf2_file', '0460_Aspirin_sf2_file', + '0460_Chaos_sf2_file', '0460_FluidR3_GM_sf2_file', '0460_GeneralUserGS_sf2_file', '0460_JCLive_sf2_file', '0460_SBLive_sf2', '0460_SoundBlasterOld_sf2', + '0461_FluidR3_GM_sf2_file', '0470_Aspirin_sf2_file', '0470_Chaos_sf2_file', '0470_FluidR3_GM_sf2_file', '0470_GeneralUserGS_sf2_file', '0470_JCLive_sf2_file', + '0470_SBLive_sf2', '0470_SoundBlasterOld_sf2', '0471_FluidR3_GM_sf2_file', '0471_GeneralUserGS_sf2_file', '0480_Aspirin_sf2_file', '0480_Chaos_sf2_file', + '0480_FluidR3_GM_sf2_file', '0480_GeneralUserGS_sf2_file', '0480_JCLive_sf2_file', '0480_SBLive_sf2', '0480_SoundBlasterOld_sf2', '04810_GeneralUserGS_sf2_file', + '04811_GeneralUserGS_sf2_file', '04812_GeneralUserGS_sf2_file', '04813_GeneralUserGS_sf2_file', '04814_GeneralUserGS_sf2_file', '04815_GeneralUserGS_sf2_file', '04816_GeneralUserGS_sf2_file', + '04817_GeneralUserGS_sf2_file', '0481_Aspirin_sf2_file', '0481_FluidR3_GM_sf2_file', '0481_GeneralUserGS_sf2_file', '0482_Aspirin_sf2_file', '0482_GeneralUserGS_sf2_file', + '0483_GeneralUserGS_sf2_file', '0484_GeneralUserGS_sf2_file', '0485_GeneralUserGS_sf2_file', '0486_GeneralUserGS_sf2_file', '0487_GeneralUserGS_sf2_file', '0488_GeneralUserGS_sf2_file', + '0489_GeneralUserGS_sf2_file', '0490_Aspirin_sf2_file', '0490_Chaos_sf2_file', '0490_FluidR3_GM_sf2_file', '0490_GeneralUserGS_sf2_file', '0490_JCLive_sf2_file', '0490_SBLive_sf2', + '0490_SoundBlasterOld_sf2', '0491_GeneralUserGS_sf2_file', '0492_GeneralUserGS_sf2_file', '0500_Aspirin_sf2_file', '0500_Chaos_sf2_file', '0500_FluidR3_GM_sf2_file', '0500_GeneralUserGS_sf2_file', + '0500_JCLive_sf2_file', '0500_SBLive_sf2', '0500_SoundBlasterOld_sf2', '0501_FluidR3_GM_sf2_file', '0501_GeneralUserGS_sf2_file', '0502_FluidR3_GM_sf2_file', '0502_GeneralUserGS_sf2_file', + '0503_FluidR3_GM_sf2_file', '0504_FluidR3_GM_sf2_file', '0505_FluidR3_GM_sf2_file', '0510_Aspirin_sf2_file', '0510_Chaos_sf2_file', '0510_FluidR3_GM_sf2_file', '0510_GeneralUserGS_sf2_file', + '0510_JCLive_sf2_file', '0510_SBLive_sf2', '0510_SoundBlasterOld_sf2', '0511_GeneralUserGS_sf2_file', '0511_SoundBlasterOld_sf2', '0520_Aspirin_sf2_file', '0520_Chaos_sf2_file', + '0520_FluidR3_GM_sf2_file', '0520_GeneralUserGS_sf2_file', '0520_JCLive_sf2_file', '0520_SBLive_sf2', '0520_Soul_Ahhs_sf2_file', '0520_SoundBlasterOld_sf2', '0521_FluidR3_GM_sf2_file', + '0521_Soul_Ahhs_sf2_file', '0521_SoundBlasterOld_sf2', '0522_Soul_Ahhs_sf2_file', '0530_Aspirin_sf2_file', '0530_Chaos_sf2_file', '0530_FluidR3_GM_sf2_file', '0530_GeneralUserGS_sf2_file', + '0530_JCLive_sf2_file', '0530_SBLive_sf2', '0530_Soul_Ahhs_sf2_file', '0530_SoundBlasterOld_sf2', '0531_FluidR3_GM_sf2_file', '0531_GeneralUserGS_sf2_file', '0531_JCLive_sf2_file', + '0531_SoundBlasterOld_sf2', '0540_Aspirin_sf2_file', '0540_Chaos_sf2_file', '0540_FluidR3_GM_sf2_file', '0540_GeneralUserGS_sf2_file', '0540_JCLive_sf2_file', '0540_SBLive_sf2', + '0540_SoundBlasterOld_sf2', '0541_FluidR3_GM_sf2_file', '0550_Aspirin_sf2_file', '0550_Chaos_sf2_file', '0550_FluidR3_GM_sf2_file', '0550_GeneralUserGS_sf2_file', '0550_JCLive_sf2_file', + '0550_SBLive_sf2', '0550_SoundBlasterOld_sf2', '0551_Aspirin_sf2_file', '0551_FluidR3_GM_sf2_file', '0560_Aspirin_sf2_file', '0560_Chaos_sf2_file', '0560_FluidR3_GM_sf2_file', + '0560_GeneralUserGS_sf2_file', '0560_JCLive_sf2_file', '0560_SBLive_sf2', '0560_SoundBlasterOld_sf2', '0570_Aspirin_sf2_file', '0570_Chaos_sf2_file', '0570_FluidR3_GM_sf2_file', + '0570_GeneralUserGS_sf2_file', '0570_JCLive_sf2_file', '0570_SBLive_sf2', '0570_SoundBlasterOld_sf2', '0571_GeneralUserGS_sf2_file', '0580_Aspirin_sf2_file', '0580_Chaos_sf2_file', + '0580_FluidR3_GM_sf2_file', '0580_GeneralUserGS_sf2_file', '0580_JCLive_sf2_file', '0580_SBLive_sf2', '0580_SoundBlasterOld_sf2', '0581_GeneralUserGS_sf2_file', '0590_Aspirin_sf2_file', + '0590_Chaos_sf2_file', '0590_FluidR3_GM_sf2_file', '0590_GeneralUserGS_sf2_file', '0590_JCLive_sf2_file', '0590_SBLive_sf2', '0590_SoundBlasterOld_sf2', '0591_GeneralUserGS_sf2_file', + '0600_Aspirin_sf2_file', '0600_Chaos_sf2_file', '0600_FluidR3_GM_sf2_file', '0600_GeneralUserGS_sf2_file', '0600_JCLive_sf2_file', '0600_SBLive_sf2', '0600_SoundBlasterOld_sf2', + '0601_FluidR3_GM_sf2_file', '0601_GeneralUserGS_sf2_file', '0602_GeneralUserGS_sf2_file', '0603_GeneralUserGS_sf2_file', '0610_Aspirin_sf2_file', '0610_Chaos_sf2_file', '0610_FluidR3_GM_sf2_file', + '0610_GeneralUserGS_sf2_file', '0610_JCLive_sf2_file', '0610_SBLive_sf2', '0610_SoundBlasterOld_sf2', '0611_GeneralUserGS_sf2_file', '0612_GeneralUserGS_sf2_file', '0613_GeneralUserGS_sf2_file', + '0614_GeneralUserGS_sf2_file', '0615_GeneralUserGS_sf2_file', '0620_Aspirin_sf2_file', '0620_Chaos_sf2_file', '0620_FluidR3_GM_sf2_file', '0620_GeneralUserGS_sf2_file', '0620_JCLive_sf2_file', + '0620_SBLive_sf2', '0620_SoundBlasterOld_sf2', '0621_Aspirin_sf2_file', '0621_FluidR3_GM_sf2_file', '0621_GeneralUserGS_sf2_file', '0622_FluidR3_GM_sf2_file', '0622_GeneralUserGS_sf2_file', + '0630_Aspirin_sf2_file', '0630_Chaos_sf2_file', '0630_FluidR3_GM_sf2_file', '0630_GeneralUserGS_sf2_file', '0630_JCLive_sf2_file', '0630_SBLive_sf2', '0630_SoundBlasterOld_sf2', + '0631_Aspirin_sf2_file', '0631_FluidR3_GM_sf2_file', '0631_GeneralUserGS_sf2_file', '0632_FluidR3_GM_sf2_file', '0633_FluidR3_GM_sf2_file', '0640_Aspirin_sf2_file', '0640_Chaos_sf2_file', + '0640_FluidR3_GM_sf2_file', '0640_GeneralUserGS_sf2_file', '0640_JCLive_sf2_file', '0640_SBLive_sf2', '0640_SoundBlasterOld_sf2', '0641_FluidR3_GM_sf2_file', '0650_Aspirin_sf2_file', + '0650_Chaos_sf2_file', '0650_FluidR3_GM_sf2_file', '0650_GeneralUserGS_sf2_file', '0650_JCLive_sf2_file', '0650_SBLive_sf2', '0650_SoundBlasterOld_sf2', '0651_Aspirin_sf2_file', + '0651_FluidR3_GM_sf2_file', '0660_Aspirin_sf2_file', '0660_Chaos_sf2_file', '0660_FluidR3_GM_sf2_file', '0660_GeneralUserGS_sf2_file', '0660_JCLive_sf2_file', '0660_SBLive_sf2', + '0660_SoundBlasterOld_sf2', '0661_FluidR3_GM_sf2_file', '0661_GeneralUserGS_sf2_file', '0670_Aspirin_sf2_file', '0670_Chaos_sf2_file', '0670_FluidR3_GM_sf2_file', '0670_GeneralUserGS_sf2_file', + '0670_JCLive_sf2_file', '0670_SBLive_sf2', '0670_SoundBlasterOld_sf2', '0671_FluidR3_GM_sf2_file', '0680_Aspirin_sf2_file', '0680_Chaos_sf2_file', '0680_FluidR3_GM_sf2_file', + '0680_GeneralUserGS_sf2_file', '0680_JCLive_sf2_file', '0680_SBLive_sf2', '0680_SoundBlasterOld_sf2', '0681_FluidR3_GM_sf2_file', '0690_Aspirin_sf2_file', '0690_Chaos_sf2_file', + '0690_FluidR3_GM_sf2_file', '0690_GeneralUserGS_sf2_file', '0690_JCLive_sf2_file', '0690_SBLive_sf2', '0690_SoundBlasterOld_sf2', '0691_FluidR3_GM_sf2_file', '0700_Aspirin_sf2_file', + '0700_Chaos_sf2_file', '0700_FluidR3_GM_sf2_file', '0700_GeneralUserGS_sf2_file', '0700_JCLive_sf2_file', '0700_SBLive_sf2', '0700_SoundBlasterOld_sf2', '0701_FluidR3_GM_sf2_file', + '0701_GeneralUserGS_sf2_file', '0710_Aspirin_sf2_file', '0710_Chaos_sf2_file', '0710_FluidR3_GM_sf2_file', '0710_GeneralUserGS_sf2_file', '0710_JCLive_sf2_file', '0710_SBLive_sf2', + '0710_SoundBlasterOld_sf2', '0711_FluidR3_GM_sf2_file', '0720_Aspirin_sf2_file', '0720_Chaos_sf2_file', '0720_FluidR3_GM_sf2_file', '0720_GeneralUserGS_sf2_file', '0720_JCLive_sf2_file', + '0720_SBLive_sf2', '0720_SoundBlasterOld_sf2', '0721_FluidR3_GM_sf2_file', '0721_SoundBlasterOld_sf2', '0730_Aspirin_sf2_file', '0730_Chaos_sf2_file', '0730_FluidR3_GM_sf2_file', + '0730_GeneralUserGS_sf2_file', '0730_JCLive_sf2_file', '0730_SBLive_sf2', '0730_SoundBlasterOld_sf2', '0731_Aspirin_sf2_file', '0731_FluidR3_GM_sf2_file', '0731_SoundBlasterOld_sf2', + '0740_Aspirin_sf2_file', '0740_Chaos_sf2_file', '0740_FluidR3_GM_sf2_file', '0740_GeneralUserGS_sf2_file', '0740_JCLive_sf2_file', '0740_SBLive_sf2', '0740_SoundBlasterOld_sf2', + '0741_GeneralUserGS_sf2_file', '0750_Aspirin_sf2_file', '0750_Chaos_sf2_file', '0750_FluidR3_GM_sf2_file', '0750_GeneralUserGS_sf2_file', '0750_JCLive_sf2_file', '0750_SBLive_sf2', + '0750_SoundBlasterOld_sf2', '0751_Aspirin_sf2_file', '0751_FluidR3_GM_sf2_file', '0751_GeneralUserGS_sf2_file', '0751_SoundBlasterOld_sf2', '0760_Aspirin_sf2_file', '0760_Chaos_sf2_file', + '0760_FluidR3_GM_sf2_file', '0760_GeneralUserGS_sf2_file', '0760_JCLive_sf2_file', '0760_SBLive_sf2', '0760_SoundBlasterOld_sf2', '0761_FluidR3_GM_sf2_file', '0761_GeneralUserGS_sf2_file', + '0761_SoundBlasterOld_sf2', '0762_GeneralUserGS_sf2_file', '0770_Aspirin_sf2_file', '0770_Chaos_sf2_file', '0770_FluidR3_GM_sf2_file', '0770_GeneralUserGS_sf2_file', '0770_JCLive_sf2_file', + '0770_SBLive_sf2', '0770_SoundBlasterOld_sf2', '0771_FluidR3_GM_sf2_file', '0771_GeneralUserGS_sf2_file', '0772_GeneralUserGS_sf2_file', '0780_Aspirin_sf2_file', '0780_Chaos_sf2_file', + '0780_FluidR3_GM_sf2_file', '0780_GeneralUserGS_sf2_file', '0780_JCLive_sf2_file', '0780_SBLive_sf2', '0780_SoundBlasterOld_sf2', '0781_GeneralUserGS_sf2_file', '0790_Aspirin_sf2_file', + '0790_Chaos_sf2_file', '0790_FluidR3_GM_sf2_file', '0790_GeneralUserGS_sf2_file', '0790_JCLive_sf2_file', '0790_SBLive_sf2', '0790_SoundBlasterOld_sf2', '0791_GeneralUserGS_sf2_file', + '0800_Aspirin_sf2_file', '0800_Chaos_sf2_file', '0800_FluidR3_GM_sf2_file', '0800_GeneralUserGS_sf2_file', '0800_JCLive_sf2_file', '0800_SBLive_sf2', '0800_SoundBlasterOld_sf2', + '0801_FluidR3_GM_sf2_file', '0801_GeneralUserGS_sf2_file', '0810_Aspirin_sf2_file', '0810_Chaos_sf2_file', '0810_FluidR3_GM_sf2_file', '0810_GeneralUserGS_sf2_file', '0810_JCLive_sf2_file', + '0810_SBLive_sf2', '0810_SoundBlasterOld_sf2', '0811_Aspirin_sf2_file', '0811_GeneralUserGS_sf2_file', '0811_SoundBlasterOld_sf2', '0820_Aspirin_sf2_file', '0820_Chaos_sf2_file', + '0820_FluidR3_GM_sf2_file', '0820_GeneralUserGS_sf2_file', '0820_JCLive_sf2_file', '0820_SBLive_sf2', '0820_SoundBlasterOld_sf2', '0821_FluidR3_GM_sf2_file', '0821_GeneralUserGS_sf2_file', + '0821_SoundBlasterOld_sf2', '0822_GeneralUserGS_sf2_file', '0823_GeneralUserGS_sf2_file', '0830_Aspirin_sf2_file', '0830_Chaos_sf2_file', '0830_FluidR3_GM_sf2_file', '0830_GeneralUserGS_sf2_file', + '0830_JCLive_sf2_file', '0830_SBLive_sf2', '0830_SoundBlasterOld_sf2', '0831_FluidR3_GM_sf2_file', '0831_GeneralUserGS_sf2_file', '0831_SoundBlasterOld_sf2', '0840_Aspirin_sf2_file', + '0840_Chaos_sf2_file', '0840_FluidR3_GM_sf2_file', '0840_GeneralUserGS_sf2_file', '0840_JCLive_sf2_file', '0840_SBLive_sf2', '0840_SoundBlasterOld_sf2', '0841_Aspirin_sf2_file', + '0841_Chaos_sf2_file', '0841_FluidR3_GM_sf2_file', '0841_GeneralUserGS_sf2_file', '0841_JCLive_sf2_file', '0841_SoundBlasterOld_sf2', '0842_FluidR3_GM_sf2_file', '0850_Aspirin_sf2_file', + '0850_Chaos_sf2_file', '0850_FluidR3_GM_sf2_file', '0850_GeneralUserGS_sf2_file', '0850_JCLive_sf2_file', '0850_SBLive_sf2', '0850_SoundBlasterOld_sf2', '0851_FluidR3_GM_sf2_file', + '0851_GeneralUserGS_sf2_file', '0851_JCLive_sf2_file', '0851_SoundBlasterOld_sf2', '0860_Aspirin_sf2_file', '0860_Chaos_sf2_file', '0860_FluidR3_GM_sf2_file', '0860_GeneralUserGS_sf2_file', + '0860_JCLive_sf2_file', '0860_SBLive_sf2', '0860_SoundBlasterOld_sf2', '0861_Aspirin_sf2_file', '0861_FluidR3_GM_sf2_file', '0861_SoundBlasterOld_sf2', '0870_Aspirin_sf2_file', + '0870_Chaos_sf2_file', '0870_FluidR3_GM_sf2_file', '0870_GeneralUserGS_sf2_file', '0870_JCLive_sf2_file', '0870_SBLive_sf2', '0870_SoundBlasterOld_sf2', '0871_GeneralUserGS_sf2_file', + '0872_GeneralUserGS_sf2_file', '0873_GeneralUserGS_sf2_file', '0880_Aspirin_sf2_file', '0880_Chaos_sf2_file', '0880_FluidR3_GM_sf2_file', '0880_GeneralUserGS_sf2_file', '0880_JCLive_sf2_file', + '0880_SBLive_sf2', '0880_SoundBlasterOld_sf2', '0881_Aspirin_sf2_file', '0881_FluidR3_GM_sf2_file', '0881_GeneralUserGS_sf2_file', '0881_SoundBlasterOld_sf2', '0882_Aspirin_sf2_file', + '0882_FluidR3_GM_sf2_file', '0882_GeneralUserGS_sf2_file', '0883_GeneralUserGS_sf2_file', '0884_GeneralUserGS_sf2_file', '0885_GeneralUserGS_sf2_file', '0886_GeneralUserGS_sf2_file', + '0887_GeneralUserGS_sf2_file', '0888_GeneralUserGS_sf2_file', '0889_GeneralUserGS_sf2_file', '0890_Aspirin_sf2_file', '0890_Chaos_sf2_file', '0890_FluidR3_GM_sf2_file', + '0890_GeneralUserGS_sf2_file', '0890_JCLive_sf2_file', '0890_SBLive_sf2', '0890_SoundBlasterOld_sf2', '0891_Aspirin_sf2_file', '0891_FluidR3_GM_sf2_file', '0891_GeneralUserGS_sf2_file', + '0900_Aspirin_sf2_file', '0900_Chaos_sf2_file', '0900_FluidR3_GM_sf2_file', '0900_GeneralUserGS_sf2_file', '0900_JCLive_sf2_file', '0900_SBLive_sf2', '0900_SoundBlasterOld_sf2', + '0901_Aspirin_sf2_file', '0901_FluidR3_GM_sf2_file', '0901_GeneralUserGS_sf2_file', '0901_SoundBlasterOld_sf2', '0910_Aspirin_sf2_file', '0910_Chaos_sf2_file', '0910_FluidR3_GM_sf2_file', + '0910_GeneralUserGS_sf2_file', '0910_JCLive_sf2_file', '0910_SBLive_sf2', '0910_SoundBlasterOld_sf2', '0911_Aspirin_sf2_file', '0911_GeneralUserGS_sf2_file', '0911_JCLive_sf2_file', + '0911_SoundBlasterOld_sf2', '0920_Aspirin_sf2_file', '0920_Chaos_sf2_file', '0920_FluidR3_GM_sf2_file', '0920_GeneralUserGS_sf2_file', '0920_JCLive_sf2_file', '0920_SBLive_sf2', + '0920_SoundBlasterOld_sf2', '0921_Aspirin_sf2_file', '0921_GeneralUserGS_sf2_file', '0921_SoundBlasterOld_sf2', '0930_Aspirin_sf2_file', '0930_Chaos_sf2_file', '0930_FluidR3_GM_sf2_file', + '0930_GeneralUserGS_sf2_file', '0930_JCLive_sf2_file', '0930_SBLive_sf2', '0930_SoundBlasterOld_sf2', '0931_Aspirin_sf2_file', '0931_FluidR3_GM_sf2_file', '0931_GeneralUserGS_sf2_file', + '0931_SoundBlasterOld_sf2', '0940_Aspirin_sf2_file', '0940_Chaos_sf2_file', '0940_FluidR3_GM_sf2_file', '0940_GeneralUserGS_sf2_file', '0940_JCLive_sf2_file', '0940_SBLive_sf2', + '0940_SoundBlasterOld_sf2', '0941_Aspirin_sf2_file', '0941_FluidR3_GM_sf2_file', '0941_GeneralUserGS_sf2_file', '0941_JCLive_sf2_file', '0950_Aspirin_sf2_file', '0950_Chaos_sf2_file', + '0950_FluidR3_GM_sf2_file', '0950_GeneralUserGS_sf2_file', '0950_JCLive_sf2_file', '0950_SBLive_sf2', '0950_SoundBlasterOld_sf2', '0951_FluidR3_GM_sf2_file', '0951_GeneralUserGS_sf2_file', + '0960_Aspirin_sf2_file', '0960_Chaos_sf2_file', '0960_FluidR3_GM_sf2_file', '0960_GeneralUserGS_sf2_file', '0960_JCLive_sf2_file', '0960_SBLive_sf2', '0960_SoundBlasterOld_sf2', + '0961_Aspirin_sf2_file', '0961_FluidR3_GM_sf2_file', '0961_GeneralUserGS_sf2_file', '0961_SoundBlasterOld_sf2', '0962_GeneralUserGS_sf2_file', '0970_Aspirin_sf2_file', '0970_Chaos_sf2_file', + '0970_FluidR3_GM_sf2_file', '0970_GeneralUserGS_sf2_file', '0970_JCLive_sf2_file', '0970_SBLive_sf2', '0970_SoundBlasterOld_sf2', '0971_FluidR3_GM_sf2_file', '0971_GeneralUserGS_sf2_file', + '0971_SoundBlasterOld_sf2', '0980_Aspirin_sf2_file', '0980_Chaos_sf2_file', '0980_FluidR3_GM_sf2_file', '0980_GeneralUserGS_sf2_file', '0980_JCLive_sf2_file', '0980_SBLive_sf2', + '0980_SoundBlasterOld_sf2', '0981_Aspirin_sf2_file', '0981_FluidR3_GM_sf2_file', '0981_GeneralUserGS_sf2_file', '0981_SoundBlasterOld_sf2', '0982_GeneralUserGS_sf2_file', + '0983_GeneralUserGS_sf2_file', '0984_GeneralUserGS_sf2_file', '0990_Aspirin_sf2_file', '0990_Chaos_sf2_file', '0990_FluidR3_GM_sf2_file', '0990_GeneralUserGS_sf2_file', '0990_JCLive_sf2_file', + '0990_SBLive_sf2', '0990_SoundBlasterOld_sf2', '0991_Aspirin_sf2_file', '0991_FluidR3_GM_sf2_file', '0991_GeneralUserGS_sf2_file', '0991_JCLive_sf2_file', '0991_SoundBlasterOld_sf2', + '0992_FluidR3_GM_sf2_file', '0992_JCLive_sf2_file', '0993_JCLive_sf2_file', '0994_JCLive_sf2_file', '1000_Aspirin_sf2_file', '1000_Chaos_sf2_file', '1000_FluidR3_GM_sf2_file', + '1000_GeneralUserGS_sf2_file', '1000_JCLive_sf2_file', '1000_SBLive_sf2', '1000_SoundBlasterOld_sf2', '1001_Aspirin_sf2_file', '1001_FluidR3_GM_sf2_file', '1001_GeneralUserGS_sf2_file', + '1001_JCLive_sf2_file', '1001_SoundBlasterOld_sf2', '1002_Aspirin_sf2_file', '1002_FluidR3_GM_sf2_file', '1002_GeneralUserGS_sf2_file', '1010_Aspirin_sf2_file', '1010_Chaos_sf2_file', + '1010_FluidR3_GM_sf2_file', '1010_GeneralUserGS_sf2_file', '1010_JCLive_sf2_file', '1010_SBLive_sf2', '1010_SoundBlasterOld_sf2', '1011_Aspirin_sf2_file', '1011_FluidR3_GM_sf2_file', + '1011_JCLive_sf2_file', '1012_Aspirin_sf2_file', '1020_Aspirin_sf2_file', '1020_Chaos_sf2_file', '1020_FluidR3_GM_sf2_file', '1020_GeneralUserGS_sf2_file', '1020_JCLive_sf2_file', + '1020_SBLive_sf2', '1020_SoundBlasterOld_sf2', '1021_Aspirin_sf2_file', '1021_FluidR3_GM_sf2_file', '1021_GeneralUserGS_sf2_file', '1021_JCLive_sf2_file', '1021_SoundBlasterOld_sf2', + '1022_GeneralUserGS_sf2_file', '1030_Aspirin_sf2_file', '1030_Chaos_sf2_file', '1030_FluidR3_GM_sf2_file', '1030_GeneralUserGS_sf2_file', '1030_JCLive_sf2_file', '1030_SBLive_sf2', + '1030_SoundBlasterOld_sf2', '1031_Aspirin_sf2_file', '1031_FluidR3_GM_sf2_file', '1031_GeneralUserGS_sf2_file', '1031_SoundBlasterOld_sf2', '1032_FluidR3_GM_sf2_file', '1040_Aspirin_sf2_file', + '1040_Chaos_sf2_file', '1040_FluidR3_GM_sf2_file', '1040_GeneralUserGS_sf2_file', '1040_JCLive_sf2_file', '1040_SBLive_sf2', '1040_SoundBlasterOld_sf2', '1041_FluidR3_GM_sf2_file', + '1041_GeneralUserGS_sf2_file', '1050_Aspirin_sf2_file', '1050_Chaos_sf2_file', '1050_FluidR3_GM_sf2_file', '1050_GeneralUserGS_sf2_file', '1050_JCLive_sf2_file', '1050_SBLive_sf2', + '1050_SoundBlasterOld_sf2', '1051_GeneralUserGS_sf2_file', '1060_Aspirin_sf2_file', '1060_Chaos_sf2_file', '1060_FluidR3_GM_sf2_file', '1060_GeneralUserGS_sf2_file', '1060_JCLive_sf2_file', + '1060_SBLive_sf2', '1060_SoundBlasterOld_sf2', '1061_FluidR3_GM_sf2_file', '1061_GeneralUserGS_sf2_file', '1061_SoundBlasterOld_sf2', '1070_Aspirin_sf2_file', '1070_Chaos_sf2_file', + '1070_FluidR3_GM_sf2_file', '1070_GeneralUserGS_sf2_file', '1070_JCLive_sf2_file', '1070_SBLive_sf2', '1070_SoundBlasterOld_sf2', '1071_FluidR3_GM_sf2_file', '1071_GeneralUserGS_sf2_file', + '1072_GeneralUserGS_sf2_file', '1073_GeneralUserGS_sf2_file', '1080_Aspirin_sf2_file', '1080_Chaos_sf2_file', '1080_FluidR3_GM_sf2_file', '1080_GeneralUserGS_sf2_file', '1080_JCLive_sf2_file', + '1080_SBLive_sf2', '1080_SoundBlasterOld_sf2', '1081_SoundBlasterOld_sf2', '1090_Aspirin_sf2_file', '1090_Chaos_sf2_file', '1090_FluidR3_GM_sf2_file', '1090_GeneralUserGS_sf2_file', + '1090_JCLive_sf2_file', '1090_SBLive_sf2', '1090_SoundBlasterOld_sf2', '1091_SoundBlasterOld_sf2', '1100_Aspirin_sf2_file', '1100_Chaos_sf2_file', '1100_FluidR3_GM_sf2_file', + '1100_GeneralUserGS_sf2_file', '1100_JCLive_sf2_file', '1100_SBLive_sf2', '1100_SoundBlasterOld_sf2', '1101_Aspirin_sf2_file', '1101_FluidR3_GM_sf2_file', '1101_GeneralUserGS_sf2_file', + '1102_GeneralUserGS_sf2_file', '1110_Aspirin_sf2_file', '1110_Chaos_sf2_file', '1110_FluidR3_GM_sf2_file', '1110_GeneralUserGS_sf2_file', '1110_JCLive_sf2_file', '1110_SBLive_sf2', + '1110_SoundBlasterOld_sf2', '1120_Aspirin_sf2_file', '1120_Chaos_sf2_file', '1120_FluidR3_GM_sf2_file', '1120_GeneralUserGS_sf2_file', '1120_JCLive_sf2_file', '1120_SBLive_sf2', + '1120_SoundBlasterOld_sf2', '1121_SoundBlasterOld_sf2', '1130_Aspirin_sf2_file', '1130_Chaos_sf2_file', '1130_FluidR3_GM_sf2_file', '1130_GeneralUserGS_sf2_file', '1130_JCLive_sf2_file', + '1130_SBLive_sf2', '1130_SoundBlasterOld_sf2', '1131_FluidR3_GM_sf2_file', '1131_SoundBlasterOld_sf2', '1140_Aspirin_sf2_file', '1140_Chaos_sf2_file', '1140_FluidR3_GM_sf2_file', + '1140_GeneralUserGS_sf2_file', '1140_JCLive_sf2_file', '1140_SBLive_sf2', '1140_SoundBlasterOld_sf2', '1141_FluidR3_GM_sf2_file', '1150_Aspirin_sf2_file', '1150_Chaos_sf2_file', + '1150_FluidR3_GM_sf2_file', '1150_GeneralUserGS_sf2_file', '1150_JCLive_sf2_file', '1150_SBLive_sf2', '1150_SoundBlasterOld_sf2', '1151_FluidR3_GM_sf2_file', '1151_GeneralUserGS_sf2_file', + '1152_FluidR3_GM_sf2_file', '1152_GeneralUserGS_sf2_file', '1160_Aspirin_sf2_file', '1160_Chaos_sf2_file', '1160_FluidR3_GM_sf2_file', '1160_GeneralUserGS_sf2_file', '1160_JCLive_sf2_file', + '1160_SBLive_sf2', '1160_SoundBlasterOld_sf2', '1161_FluidR3_GM_sf2_file', '1161_GeneralUserGS_sf2_file', '1161_SoundBlasterOld_sf2', '1162_FluidR3_GM_sf2_file', '1162_GeneralUserGS_sf2_file', + '1163_FluidR3_GM_sf2_file', '1170_Aspirin_sf2_file', '1170_Chaos_sf2_file', '1170_FluidR3_GM_sf2_file', '1170_GeneralUserGS_sf2_file', '1170_JCLive_sf2_file', '1170_SBLive_sf2', + '1170_SoundBlasterOld_sf2', '1171_FluidR3_GM_sf2_file', '1171_GeneralUserGS_sf2_file', '1172_FluidR3_GM_sf2_file', '1173_FluidR3_GM_sf2_file', '1180_Aspirin_sf2_file', '1180_Chaos_sf2_file', + '1180_FluidR3_GM_sf2_file', '1180_GeneralUserGS_sf2_file', '1180_JCLive_sf2_file', '1180_SBLive_sf2', '1180_SoundBlasterOld_sf2', '1181_FluidR3_GM_sf2_file', '1181_GeneralUserGS_sf2_file', + '1181_SoundBlasterOld_sf2', '1190_Aspirin_sf2_file', '1190_Chaos_sf2_file', '1190_FluidR3_GM_sf2_file', '1190_GeneralUserGS_sf2_file', '1190_JCLive_sf2_file', '1190_SBLive_sf2', + '1190_SoundBlasterOld_sf2', '1191_GeneralUserGS_sf2_file', '1192_GeneralUserGS_sf2_file', '1193_GeneralUserGS_sf2_file', '1194_GeneralUserGS_sf2_file', '1200_Aspirin_sf2_file', + '1200_Chaos_sf2_file', '1200_FluidR3_GM_sf2_file', '1200_GeneralUserGS_sf2_file', '1200_JCLive_sf2_file', '1200_SBLive_sf2', '1200_SoundBlasterOld_sf2', '1201_Aspirin_sf2_file', + '1201_GeneralUserGS_sf2_file', '1202_GeneralUserGS_sf2_file', '1210_Aspirin_sf2_file', '1210_Chaos_sf2_file', '1210_FluidR3_GM_sf2_file', '1210_GeneralUserGS_sf2_file', '1210_JCLive_sf2_file', + '1210_SBLive_sf2', '1210_SoundBlasterOld_sf2', '1211_Aspirin_sf2_file', '1211_GeneralUserGS_sf2_file', '1212_GeneralUserGS_sf2_file', '1220_Aspirin_sf2_file', '1220_Chaos_sf2_file', + '1220_FluidR3_GM_sf2_file', '1220_GeneralUserGS_sf2_file', '1220_JCLive_sf2_file', '1220_SBLive_sf2', '1220_SoundBlasterOld_sf2', '1221_Aspirin_sf2_file', '1221_GeneralUserGS_sf2_file', + '1221_JCLive_sf2_file', '1222_Aspirin_sf2_file', '1222_GeneralUserGS_sf2_file', '1223_Aspirin_sf2_file', '1223_GeneralUserGS_sf2_file', '1224_Aspirin_sf2_file', '1224_GeneralUserGS_sf2_file', + '1225_GeneralUserGS_sf2_file', '1226_GeneralUserGS_sf2_file', '1230_Aspirin_sf2_file', '1230_Chaos_sf2_file', '1230_FluidR3_GM_sf2_file', '1230_GeneralUserGS_sf2_file', '1230_JCLive_sf2_file', + '1230_SBLive_sf2', '1230_SoundBlasterOld_sf2', '1231_Aspirin_sf2_file', '1231_GeneralUserGS_sf2_file', '1232_Aspirin_sf2_file', '1232_GeneralUserGS_sf2_file', '1233_GeneralUserGS_sf2_file', + '1234_GeneralUserGS_sf2_file', '1240_Aspirin_sf2_file', '1240_Chaos_sf2_file', '1240_FluidR3_GM_sf2_file', '1240_GeneralUserGS_sf2_file', '1240_JCLive_sf2_file', '1240_SBLive_sf2', + '1240_SoundBlasterOld_sf2', '1241_Aspirin_sf2_file', '1241_GeneralUserGS_sf2_file', '1242_Aspirin_sf2_file', '1242_GeneralUserGS_sf2_file', '1243_Aspirin_sf2_file', '1243_GeneralUserGS_sf2_file', + '1244_Aspirin_sf2_file', '1244_GeneralUserGS_sf2_file', '1250_Aspirin_sf2_file', '1250_Chaos_sf2_file', '1250_FluidR3_GM_sf2_file', '1250_GeneralUserGS_sf2_file', '1250_JCLive_sf2_file', + '1250_SBLive_sf2', '1250_SoundBlasterOld_sf2', '1251_Aspirin_sf2_file', '1251_FluidR3_GM_sf2_file', '1251_GeneralUserGS_sf2_file', '1252_Aspirin_sf2_file', '1252_FluidR3_GM_sf2_file', + '1252_GeneralUserGS_sf2_file', '1253_Aspirin_sf2_file', '1253_GeneralUserGS_sf2_file', '1254_Aspirin_sf2_file', '1254_GeneralUserGS_sf2_file', '1255_Aspirin_sf2_file', + '1255_GeneralUserGS_sf2_file', '1256_Aspirin_sf2_file', '1256_GeneralUserGS_sf2_file', '1257_Aspirin_sf2_file', '1257_GeneralUserGS_sf2_file', '1258_Aspirin_sf2_file', + '1258_GeneralUserGS_sf2_file', '1259_GeneralUserGS_sf2_file', '1260_Aspirin_sf2_file', '1260_Chaos_sf2_file', '1260_FluidR3_GM_sf2_file', '1260_GeneralUserGS_sf2_file', '1260_JCLive_sf2_file', + '1260_SBLive_sf2', '1260_SoundBlasterOld_sf2', '1261_Aspirin_sf2_file', '1261_GeneralUserGS_sf2_file', '1262_Aspirin_sf2_file', '1262_GeneralUserGS_sf2_file', '1263_Aspirin_sf2_file', + '1263_GeneralUserGS_sf2_file', '1264_Aspirin_sf2_file', '1264_GeneralUserGS_sf2_file', '1265_Aspirin_sf2_file', '1265_GeneralUserGS_sf2_file', '1270_Aspirin_sf2_file', '1270_Chaos_sf2_file', + '1270_FluidR3_GM_sf2_file', '1270_GeneralUserGS_sf2_file', '1270_JCLive_sf2_file', '1270_SBLive_sf2', '1270_SoundBlasterOld_sf2', '1271_Aspirin_sf2_file', '1271_GeneralUserGS_sf2_file', + '1272_Aspirin_sf2_file', '1272_GeneralUserGS_sf2_file', '1273_GeneralUserGS_sf2_file', '1274_GeneralUserGS_sf2_file' + ]; + } + return this.instrumentKeyArray; + }; + ; + WebAudioFontLoader.prototype.instrumentInfo = function (n) { + var key = this.instrumentKeys()[n]; + var p = 1 * parseInt(key.substring(0, 3)); + return { + variable: '_tone_' + key, + url: 'https://surikov.github.io/webaudiofontdata/sound/' + key + '.js', + title: this.instrumentTitles()[p], + pitch: -1 + }; + }; + ; + WebAudioFontLoader.prototype.findInstrument = function (program) { + if (this.choosenInfos.length == 0) { + this.choosenInfos = [ + [1, 2] //Accoustic Grand Piano + , + [2, 14] //Bright Accoustic Piano + , + [3, 25] //Electric Grand Piano + , + [4, 37] //Honky-Tonk Piano + , + [5, 48] //Electric Pino 1 + , + [6, 58] //Electric Piano 2 + , + [7, 70] //HarpsiChord Piano + , + [8, 83] //Cravinet + , + [9, 91] //Celesta + , + [10, 99] //Glockenspiel + , + [11, 107] //Music Box + , + [12, 118] //Vibraphone + , + [13, 127] // Marimba + , + [14, 136] // Xylophone + , + [15, 144] // Tubular Bells + , + [16, 152] // Dulcimer + , + [17, 164] // Drawbar Organ + , + [18, 170] // Percussive Organ + , + [19, 183] //Rock Organ + , + [20, 194] // Church Organ + , + [21, 205] //Reed Organ + , + [22, 215] //Accordion + , + [23, 228] // + , + [24, 241] // + , + [25, 254] // + , + [26, 263] // + , + [27, 277] // + , + [28, 296] // + , + [29, 308] // + , + [30, 319] // + , + [31, 350] // + , + [32, 356] // + , + [33, 369] // + , + [34, 379] // + , + [35, 385] // + , + [36, 399] // Fretless Bass + , + [37, 403] // Slap Bass 1 + , + [38, 412] // Slap Bass 2 + , + [39, 421] // Synth Bass 1 + , + [40, 438] // Synth Bass 2 + , + [41, 452] // Violin + , + [42, 461] // Viola + , + [43, 467] // Cello + , + [44, 477] // Contrabass + , + [45, 488] // Tremolo Strings + , + [46, 493] // Pizzicato Strings + , + [47, 501] // Orchestral Harp + , + [48, 511] // Timpani + , + [49, 518] // String Ensemble 1 + , + [50, 547] //String Ensemble 2 + ]; + } + for (var i = 0; i < this.instrumentKeys().length; i++) { + if (program == 1 * parseInt(this.instrumentKeys()[i].substring(0, 3))) { + return i; + } + } + console.log('program', program, 'not found'); + return 0; + }; + ; + WebAudioFontLoader.prototype.drumTitles = function () { + if (this.drumNamesArray.length == 0) { + var drumNames = []; + drumNames[35] = "Bass Drum 2"; + drumNames[36] = "Bass Drum 1"; + drumNames[37] = "Side Stick/Rimshot"; + drumNames[38] = "Snare Drum 1"; + drumNames[39] = "Hand Clap"; + drumNames[40] = "Snare Drum 2"; + drumNames[41] = "Low Tom 2"; + drumNames[42] = "Closed Hi-hat"; + drumNames[43] = "Low Tom 1"; + drumNames[44] = "Pedal Hi-hat"; + drumNames[45] = "Mid Tom 2"; + drumNames[46] = "Open Hi-hat"; + drumNames[47] = "Mid Tom 1"; + drumNames[48] = "High Tom 2"; + drumNames[49] = "Crash Cymbal 1"; + drumNames[50] = "High Tom 1"; + drumNames[51] = "Ride Cymbal 1"; + drumNames[52] = "Chinese Cymbal"; + drumNames[53] = "Ride Bell"; + drumNames[54] = "Tambourine"; + drumNames[55] = "Splash Cymbal"; + drumNames[56] = "Cowbell"; + drumNames[57] = "Crash Cymbal 2"; + drumNames[58] = "Vibra Slap"; + drumNames[59] = "Ride Cymbal 2"; + drumNames[60] = "High Bongo"; + drumNames[61] = "Low Bongo"; + drumNames[62] = "Mute High Conga"; + drumNames[63] = "Open High Conga"; + drumNames[64] = "Low Conga"; + drumNames[65] = "High Timbale"; + drumNames[66] = "Low Timbale"; + drumNames[67] = "High Agogo"; + drumNames[68] = "Low Agogo"; + drumNames[69] = "Cabasa"; + drumNames[70] = "Maracas"; + drumNames[71] = "Short Whistle"; + drumNames[72] = "Long Whistle"; + drumNames[73] = "Short Guiro"; + drumNames[74] = "Long Guiro"; + drumNames[75] = "Claves"; + drumNames[76] = "High Wood Block"; + drumNames[77] = "Low Wood Block"; + drumNames[78] = "Mute Cuica"; + drumNames[79] = "Open Cuica"; + drumNames[80] = "Mute Triangle"; + drumNames[81] = "Open Triangle"; + this.drumNamesArray = drumNames; + } + return this.drumNamesArray; + }; + ; + WebAudioFontLoader.prototype.drumKeys = function () { + if (this.drumKeyArray.length == 0) { + this.drumKeyArray = [ + //'35_0_SBLive_sf2' + '35_0_Chaos_sf2_file', + '35_12_JCLive_sf2_file', '35_16_JCLive_sf2_file', '35_18_JCLive_sf2_file', '35_4_Chaos_sf2_file', '36_0_SBLive_sf2', '36_12_JCLive_sf2_file', '36_16_JCLive_sf2_file', '36_18_JCLive_sf2_file', + '36_4_Chaos_sf2_file', '37_0_SBLive_sf2', '37_12_JCLive_sf2_file', '37_16_JCLive_sf2_file', '37_18_JCLive_sf2_file', '37_4_Chaos_sf2_file', '38_0_SBLive_sf2', '38_12_JCLive_sf2_file', + '38_16_JCLive_sf2_file', '38_18_JCLive_sf2_file', '38_4_Chaos_sf2_file', '39_0_SBLive_sf2', '39_12_JCLive_sf2_file', '39_16_JCLive_sf2_file', '39_18_JCLive_sf2_file', '39_4_Chaos_sf2_file', + '40_0_SBLive_sf2', '40_12_JCLive_sf2_file', '40_16_JCLive_sf2_file', '40_18_JCLive_sf2_file', '40_4_Chaos_sf2_file', '41_0_SBLive_sf2', '41_12_JCLive_sf2_file', '41_16_JCLive_sf2_file', + '41_18_JCLive_sf2_file', '41_4_Chaos_sf2_file', '42_0_SBLive_sf2', '42_12_JCLive_sf2_file', '42_16_JCLive_sf2_file', '42_18_JCLive_sf2_file', '42_4_Chaos_sf2_file', '43_0_SBLive_sf2', + '43_12_JCLive_sf2_file', '43_16_JCLive_sf2_file', '43_18_JCLive_sf2_file', '43_4_Chaos_sf2_file', '44_0_SBLive_sf2', '44_12_JCLive_sf2_file', '44_16_JCLive_sf2_file', '44_18_JCLive_sf2_file', + '44_4_Chaos_sf2_file', '45_0_SBLive_sf2', '45_12_JCLive_sf2_file', '45_16_JCLive_sf2_file', '45_18_JCLive_sf2_file', '45_4_Chaos_sf2_file', '46_0_SBLive_sf2', '46_12_JCLive_sf2_file', + '46_16_JCLive_sf2_file', '46_18_JCLive_sf2_file', '46_4_Chaos_sf2_file', '47_0_SBLive_sf2', '47_12_JCLive_sf2_file', '47_16_JCLive_sf2_file', '47_18_JCLive_sf2_file', '47_4_Chaos_sf2_file', + '48_0_SBLive_sf2', '48_12_JCLive_sf2_file', '48_16_JCLive_sf2_file', '48_18_JCLive_sf2_file', '48_4_Chaos_sf2_file', '49_0_SBLive_sf2', '49_12_JCLive_sf2_file', '49_16_JCLive_sf2_file', + '49_18_JCLive_sf2_file', '49_4_Chaos_sf2_file', '50_0_SBLive_sf2', '50_12_JCLive_sf2_file', '50_16_JCLive_sf2_file', '50_18_JCLive_sf2_file', '50_4_Chaos_sf2_file', '51_0_SBLive_sf2', + '51_12_JCLive_sf2_file', '51_16_JCLive_sf2_file', '51_18_JCLive_sf2_file', '51_4_Chaos_sf2_file', '52_0_SBLive_sf2', '52_12_JCLive_sf2_file', '52_16_JCLive_sf2_file', '52_18_JCLive_sf2_file', + '52_4_Chaos_sf2_file', '53_0_SBLive_sf2', '53_12_JCLive_sf2_file', '53_16_JCLive_sf2_file', '53_18_JCLive_sf2_file', '53_4_Chaos_sf2_file', '54_0_SBLive_sf2', '54_12_JCLive_sf2_file', + '54_16_JCLive_sf2_file', '54_18_JCLive_sf2_file', '54_4_Chaos_sf2_file', '55_0_SBLive_sf2', '55_12_JCLive_sf2_file', '55_16_JCLive_sf2_file', '55_18_JCLive_sf2_file', '55_4_Chaos_sf2_file', + '56_0_SBLive_sf2', '56_12_JCLive_sf2_file', '56_16_JCLive_sf2_file', '56_18_JCLive_sf2_file', '56_4_Chaos_sf2_file', '57_0_SBLive_sf2', '57_12_JCLive_sf2_file', '57_16_JCLive_sf2_file', + '57_18_JCLive_sf2_file', '57_4_Chaos_sf2_file', '58_0_SBLive_sf2', '58_12_JCLive_sf2_file', '58_16_JCLive_sf2_file', '58_18_JCLive_sf2_file', '58_4_Chaos_sf2_file', '59_0_SBLive_sf2', + '59_12_JCLive_sf2_file', '59_16_JCLive_sf2_file', '59_18_JCLive_sf2_file', '59_4_Chaos_sf2_file', '60_0_SBLive_sf2', '60_12_JCLive_sf2_file', '60_16_JCLive_sf2_file', '60_18_JCLive_sf2_file', + '60_4_Chaos_sf2_file', '61_0_SBLive_sf2', '61_12_JCLive_sf2_file', '61_16_JCLive_sf2_file', '61_18_JCLive_sf2_file', '61_4_Chaos_sf2_file', '62_0_SBLive_sf2', '62_12_JCLive_sf2_file', + '62_16_JCLive_sf2_file', '62_18_JCLive_sf2_file', '62_4_Chaos_sf2_file', '63_0_SBLive_sf2', '63_12_JCLive_sf2_file', '63_16_JCLive_sf2_file', '63_18_JCLive_sf2_file', '63_4_Chaos_sf2_file', + '64_0_SBLive_sf2', '64_12_JCLive_sf2_file', '64_16_JCLive_sf2_file', '64_18_JCLive_sf2_file', '64_4_Chaos_sf2_file', '65_0_SBLive_sf2', '65_12_JCLive_sf2_file', '65_16_JCLive_sf2_file', + '65_18_JCLive_sf2_file', '65_4_Chaos_sf2_file', '66_0_SBLive_sf2', '66_12_JCLive_sf2_file', '66_16_JCLive_sf2_file', '66_18_JCLive_sf2_file', '66_4_Chaos_sf2_file', '67_0_SBLive_sf2', + '67_12_JCLive_sf2_file', '67_16_JCLive_sf2_file', '67_18_JCLive_sf2_file', '67_4_Chaos_sf2_file', '68_0_SBLive_sf2', '68_12_JCLive_sf2_file', '68_16_JCLive_sf2_file', '68_18_JCLive_sf2_file', + '68_4_Chaos_sf2_file', '69_0_SBLive_sf2', '69_12_JCLive_sf2_file', '69_16_JCLive_sf2_file', '69_18_JCLive_sf2_file', '69_4_Chaos_sf2_file', '70_0_SBLive_sf2', '70_12_JCLive_sf2_file', + '70_16_JCLive_sf2_file', '70_18_JCLive_sf2_file', '70_4_Chaos_sf2_file', '71_0_SBLive_sf2', '71_12_JCLive_sf2_file', '71_16_JCLive_sf2_file', '71_18_JCLive_sf2_file', '71_4_Chaos_sf2_file', + '72_0_SBLive_sf2', '72_12_JCLive_sf2_file', '72_16_JCLive_sf2_file', '72_18_JCLive_sf2_file', '72_4_Chaos_sf2_file', '73_0_SBLive_sf2', '73_12_JCLive_sf2_file', '73_16_JCLive_sf2_file', + '73_18_JCLive_sf2_file', '73_4_Chaos_sf2_file', '74_0_SBLive_sf2', '74_12_JCLive_sf2_file', '74_16_JCLive_sf2_file', '74_18_JCLive_sf2_file', '74_4_Chaos_sf2_file', '75_0_SBLive_sf2', + '75_12_JCLive_sf2_file', '75_16_JCLive_sf2_file', '75_18_JCLive_sf2_file', '75_4_Chaos_sf2_file', '76_0_SBLive_sf2', '76_12_JCLive_sf2_file', '76_16_JCLive_sf2_file', '76_18_JCLive_sf2_file', + '76_4_Chaos_sf2_file', '77_0_SBLive_sf2', '77_12_JCLive_sf2_file', '77_16_JCLive_sf2_file', '77_18_JCLive_sf2_file', '77_4_Chaos_sf2_file', '78_0_SBLive_sf2', '78_12_JCLive_sf2_file', + '78_16_JCLive_sf2_file', '78_18_JCLive_sf2_file', '78_4_Chaos_sf2_file', '79_0_SBLive_sf2', '79_12_JCLive_sf2_file', '79_16_JCLive_sf2_file', '79_18_JCLive_sf2_file', '79_4_Chaos_sf2_file', + '80_0_SBLive_sf2', '80_12_JCLive_sf2_file', '80_16_JCLive_sf2_file', '80_18_JCLive_sf2_file', '80_4_Chaos_sf2_file', '81_0_SBLive_sf2', '81_12_JCLive_sf2_file', '81_16_JCLive_sf2_file', + '81_18_JCLive_sf2_file', '81_4_Chaos_sf2_file' + ]; + } + return this.drumKeyArray; + }; + ; + WebAudioFontLoader.prototype.drumInfo = function (n) { + var key = this.drumKeys()[n]; + var p = 1 * parseInt(key.substring(0, 2)); + return { + variable: '_drum_' + key, + url: 'https://surikov.github.io/webaudiofontdata/sound/128' + key + '.js', + pitch: p, + title: this.drumTitles()[p] + }; + }; + ; + WebAudioFontLoader.prototype.findDrum = function (nn) { + for (var i = 0; i < this.drumKeys().length; i++) { + if (nn == 1 * parseInt(this.drumKeys()[i].substring(0, 2))) { + return i; + } + } + return 0; + }; + return WebAudioFontLoader; +}()); +console.log('WebAudioFont Engine v3.0.04 GPL3'); +//docs +//npm link typescript +//npx typedoc player.ts otypes.ts channel.ts loader.ts reverberator.ts ticker.ts +var WebAudioFontPlayer = /** @class */ (function () { + function WebAudioFontPlayer() { + this.envelopes = []; + this.loader = new WebAudioFontLoader(this); + //onCacheFinish = null; + //onCacheProgress = null; + this.afterTime = 0.05; + this.nearZero = 0.000001; + this.adjustPreset = function (audioContext, preset) { + for (var i = 0; i < preset.zones.length; i++) { + this.adjustZone(audioContext, preset.zones[i]); + } + }; + this.adjustZone = function (audioContext, zone) { + if (zone.buffer) { + // + } + else { + zone.delay = 0; + if (zone.sample) { + var decoded = atob(zone.sample); + zone.buffer = audioContext.createBuffer(1, decoded.length / 2, zone.sampleRate); + var float32Array = zone.buffer.getChannelData(0); + var b1, b2, n; + for (var i = 0; i < decoded.length / 2; i++) { + b1 = decoded.charCodeAt(i * 2); + b2 = decoded.charCodeAt(i * 2 + 1); + if (b1 < 0) { + b1 = 256 + b1; + } + if (b2 < 0) { + b2 = 256 + b2; + } + n = b2 * 256 + b1; + if (n >= 65536 / 2) { + n = n - 65536; + } + float32Array[i] = n / 65536.0; + } + } + else { + if (zone.file) { + var datalen = zone.file.length; + var arraybuffer = new ArrayBuffer(datalen); + var view = new Uint8Array(arraybuffer); + var decoded = atob(zone.file); + var b; + for (var i = 0; i < decoded.length; i++) { + b = decoded.charCodeAt(i); + view[i] = b; + } + audioContext.decodeAudioData(arraybuffer, function (audioBuffer) { + zone.buffer = audioBuffer; + }); + } + } + zone.loopStart = this.numValue(zone.loopStart, 0); + zone.loopEnd = this.numValue(zone.loopEnd, 0); + zone.coarseTune = this.numValue(zone.coarseTune, 0); + zone.fineTune = this.numValue(zone.fineTune, 0); + zone.originalPitch = this.numValue(zone.originalPitch, 6000); + zone.sampleRate = this.numValue(zone.sampleRate, 44100); + zone.sustain = this.numValue(zone.originalPitch, 0); + } + }; + } + WebAudioFontPlayer.prototype.createChannel = function (audioContext) { + return new WebAudioFontChannel(audioContext); + }; + ; + WebAudioFontPlayer.prototype.createReverberator = function (audioContext) { + return new WebAudioFontReverberator(audioContext); + }; + ; + WebAudioFontPlayer.prototype.limitVolume = function (volume) { + if (volume) { + volume = 1.0 * volume; + } + else { + volume = 0.5; + } + return volume; + }; + ; + WebAudioFontPlayer.prototype.queueChord = function (audioContext, target, preset, when, pitches, duration, volume, slides) { + volume = this.limitVolume(volume); + var envelopes = []; + for (var i = 0; i < pitches.length; i++) { + var singleSlide = undefined; + if (slides) { + singleSlide = slides[i]; + } + var envlp = this.queueWaveTable(audioContext, target, preset, when, pitches[i], duration, volume - Math.random() * 0.01, singleSlide); + if (envlp) + envelopes.push(envlp); + } + return envelopes; + }; + ; + WebAudioFontPlayer.prototype.queueStrumUp = function (audioContext, target, preset, when, pitches, duration, volume, slides) { + pitches.sort(function (a, b) { + return b - a; + }); + return this.queueStrum(audioContext, target, preset, when, pitches, duration, volume, slides); + }; + ; + WebAudioFontPlayer.prototype.queueStrumDown = function (audioContext, target, preset, when, pitches, duration, volume, slides) { + pitches.sort(function (a, b) { + return a - b; + }); + return this.queueStrum(audioContext, target, preset, when, pitches, duration, volume, slides); + }; + ; + WebAudioFontPlayer.prototype.queueStrum = function (audioContext, target, preset, when, pitches, duration, volume, slides) { + volume = this.limitVolume(volume); + if (when < audioContext.currentTime) { + when = audioContext.currentTime; + } + var envelopes = []; + for (var i = 0; i < pitches.length; i++) { + var singleSlide = undefined; + if (slides) { + singleSlide = slides[i]; + } + var envlp = this.queueWaveTable(audioContext, target, preset, when + i * 0.01, pitches[i], duration, volume - Math.random() * 0.01, singleSlide); + if (envlp) + envelopes.push(envlp); + volume = 0.9 * volume; + } + return envelopes; + }; + ; + WebAudioFontPlayer.prototype.queueSnap = function (audioContext, target, preset, when, pitches, duration, volume, slides) { + volume = this.limitVolume(volume); + volume = 1.5 * (volume || 1.0); + duration = 0.05; + return this.queueChord(audioContext, target, preset, when, pitches, duration, volume, slides); + }; + ; + WebAudioFontPlayer.prototype.resumeContext = function (audioContext) { + try { + if (audioContext.state == 'suspended') { + console.log('audioContext.resume', audioContext); + audioContext.resume(); + } + } + catch (e) { + //don't care + } + }; + WebAudioFontPlayer.prototype.queueWaveTable = function (audioContext, target, preset, when, pitch, duration, volume, slides) { + this.resumeContext(audioContext); + volume = this.limitVolume(volume); + var zone = this.findZone(audioContext, preset, pitch); + if (zone) { + if (!(zone.buffer)) { + console.log('empty buffer ', zone); + return null; + } + var baseDetune = zone.originalPitch - 100.0 * zone.coarseTune - zone.fineTune; + var playbackRate = 1.0 * Math.pow(2, (100.0 * pitch - baseDetune) / 1200.0); + var startWhen = when; + if (startWhen < audioContext.currentTime) { + startWhen = audioContext.currentTime; + } + var waveDuration = duration + this.afterTime; + var loop = true; + if (zone.loopStart < 1 || zone.loopStart >= zone.loopEnd) { + loop = false; + } + if (!loop) { + if (waveDuration > zone.buffer.duration / playbackRate) { + waveDuration = zone.buffer.duration / playbackRate; + } + } + var envelope = this.findEnvelope(audioContext, target); + this.setupEnvelope(audioContext, envelope, zone, volume, startWhen, waveDuration, duration); + envelope.audioBufferSourceNode = audioContext.createBufferSource(); + envelope.audioBufferSourceNode.playbackRate.setValueAtTime(playbackRate, 0); + if (slides) { + if (slides.length > 0) { + envelope.audioBufferSourceNode.playbackRate.setValueAtTime(playbackRate, when); + for (var i = 0; i < slides.length; i++) { + var nextPitch = pitch + slides[i].delta; + var newPlaybackRate = 1.0 * Math.pow(2, (100.0 * nextPitch - baseDetune) / 1200.0); + var newWhen = when + slides[i].when; + envelope.audioBufferSourceNode.playbackRate.linearRampToValueAtTime(newPlaybackRate, newWhen); + } + } + } + envelope.audioBufferSourceNode.buffer = zone.buffer; + if (loop) { + envelope.audioBufferSourceNode.loop = true; + envelope.audioBufferSourceNode.loopStart = zone.loopStart / zone.sampleRate + ((zone.delay) ? zone.delay : 0); + envelope.audioBufferSourceNode.loopEnd = zone.loopEnd / zone.sampleRate + ((zone.delay) ? zone.delay : 0); + } + else { + envelope.audioBufferSourceNode.loop = false; + } + envelope.audioBufferSourceNode.connect(envelope); + envelope.audioBufferSourceNode.start(startWhen, zone.delay); + envelope.audioBufferSourceNode.stop(startWhen + waveDuration); + envelope.when = startWhen; + envelope.duration = waveDuration; + envelope.pitch = pitch; + envelope.preset = preset; + return envelope; + } + else { + return null; + } + }; + ; + WebAudioFontPlayer.prototype.noZeroVolume = function (n) { + if (n > this.nearZero) { + return n; + } + else { + return this.nearZero; + } + }; + ; + WebAudioFontPlayer.prototype.setupEnvelope = function (audioContext, envelope, zone, volume, when, sampleDuration, noteDuration) { + envelope.gain.setValueAtTime(this.noZeroVolume(0), audioContext.currentTime); + var lastTime = 0; + var lastVolume = 0; + var duration = noteDuration; + var zoneahdsr = zone.ahdsr; + if (sampleDuration < duration + this.afterTime) { + duration = sampleDuration - this.afterTime; + } + if (zoneahdsr) { + if (!(zoneahdsr.length > 0)) { + zoneahdsr = [{ + duration: 0, + volume: 1 + }, { + duration: 0.5, + volume: 1 + }, { + duration: 1.5, + volume: 0.5 + }, { + duration: 3, + volume: 0 + } + ]; + } + } + else { + zoneahdsr = [{ + duration: 0, + volume: 1 + }, { + duration: duration, + volume: 1 + } + ]; + } + var ahdsr = zoneahdsr; + envelope.gain.cancelScheduledValues(when); + envelope.gain.setValueAtTime(this.noZeroVolume(ahdsr[0].volume * volume), when); + for (var i = 0; i < ahdsr.length; i++) { + if (ahdsr[i].duration > 0) { + if (ahdsr[i].duration + lastTime > duration) { + var r = 1 - (ahdsr[i].duration + lastTime - duration) / ahdsr[i].duration; + var n = lastVolume - r * (lastVolume - ahdsr[i].volume); + envelope.gain.linearRampToValueAtTime(this.noZeroVolume(volume * n), when + duration); + break; + } + lastTime = lastTime + ahdsr[i].duration; + lastVolume = ahdsr[i].volume; + envelope.gain.linearRampToValueAtTime(this.noZeroVolume(volume * lastVolume), when + lastTime); + } + } + envelope.gain.linearRampToValueAtTime(this.noZeroVolume(0), when + duration + this.afterTime); + }; + ; + WebAudioFontPlayer.prototype.numValue = function (aValue, defValue) { + if (typeof aValue === "number") { + return aValue; + } + else { + return defValue; + } + }; + ; + WebAudioFontPlayer.prototype.findEnvelope = function (audioContext, target) { + var envelope = null; + for (var i = 0; i < this.envelopes.length; i++) { + var e = this.envelopes[i]; + if (e.target == target && audioContext.currentTime > e.when + e.duration + 0.001) { + try { + if (e.audioBufferSourceNode) { + e.audioBufferSourceNode.disconnect(); + e.audioBufferSourceNode.stop(0); + e.audioBufferSourceNode = null; + } + } + catch (x) { + //audioBufferSourceNode is dead already + } + envelope = e; + break; + } + } + if (!(envelope)) { + envelope = audioContext.createGain(); + envelope.target = target; + envelope.connect(target); + envelope.cancel = function () { + if (envelope && (envelope.when + envelope.duration > audioContext.currentTime)) { + envelope.gain.cancelScheduledValues(0); + envelope.gain.setTargetAtTime(0.00001, audioContext.currentTime, 0.1); + envelope.when = audioContext.currentTime + 0.00001; + envelope.duration = 0; + } + }; + this.envelopes.push(envelope); + } + return envelope; + }; + ; + WebAudioFontPlayer.prototype.findZone = function (audioContext, preset, pitch) { + var zone = null; + for (var i = preset.zones.length - 1; i >= 0; i--) { + zone = preset.zones[i]; + if (zone.keyRangeLow <= pitch && zone.keyRangeHigh + 1 >= pitch) { + break; + } + } + try { + if (zone) + this.adjustZone(audioContext, zone); + } + catch (ex) { + console.log('adjustZone', ex); + } + return zone; + }; + ; + WebAudioFontPlayer.prototype.cancelQueue = function (audioContext) { + for (var i = 0; i < this.envelopes.length; i++) { + var e = this.envelopes[i]; + e.gain.cancelScheduledValues(0); + e.gain.setValueAtTime(this.nearZero, audioContext.currentTime); + e.when = -1; + try { + if (e.audioBufferSourceNode) + e.audioBufferSourceNode.disconnect(); + } + catch (ex) { + console.log(ex); + } + } + }; + ; + return WebAudioFontPlayer; +}()); +var WebAudioFontReverberator = /** @class */ (function () { + function WebAudioFontReverberator(audioContext) { + this.irr = "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"; //http://www.openairlib.net/ + var me = this; + this.audioContext = audioContext; + this.output = audioContext.createGain(); + this.input = this.audioContext.createBiquadFilter(); + this.compressor = audioContext.createDynamicsCompressor(); + this.compressorWet = audioContext.createGain(); + this.compressorDry = audioContext.createGain(); + this.convolver = null; + this.convolverInput = audioContext.createGain(); + this.dry = audioContext.createGain(); + this.wet = audioContext.createGain(); + this.input.type = "lowpass"; + this.input.frequency.setTargetAtTime(18000, 0, 0.0001); + this.compressorWet.gain.setTargetAtTime(1.0, 0, 0.0001); + this.compressorDry.gain.setTargetAtTime(0.0, 0, 0.0001); + var threshold = -35; + var knee = 35; + var ratio = 8; + var attack = 0.02; + var release = 0.1; + this.compressor.threshold.setValueAtTime(threshold, 0.0001); //-100,0 + this.compressor.knee.setValueAtTime(knee, 0.0001); //0,40 + this.compressor.ratio.setValueAtTime(ratio, 0.0001); //2,20 + this.compressor.attack.setValueAtTime(attack, 0.0001); //0,1 + this.compressor.release.setValueAtTime(release, 0.0001); //0,1 + this.dry.gain.setTargetAtTime(0.6, 0, 0.0001); + this.wet.gain.setTargetAtTime(0.4, 0, 0.0001); + this.input.connect(this.compressorDry); + this.compressorDry.connect(this.convolverInput); + this.input.connect(this.compressorWet); + this.compressorWet.connect(this.compressor); + this.compressor.connect(this.convolverInput); + this.convolverInput.connect(this.dry); + this.dry.connect(this.output); + this.convolverInput.connect(this.wet); + var datalen = this.irr.length / 2; + this.irrArrayBuffer = new ArrayBuffer(datalen); + var view = new Uint8Array(this.irrArrayBuffer); + var decoded = atob(this.irr); + var b; + for (var i = 0; i < decoded.length; i++) { + b = decoded.charCodeAt(i); + view[i] = b; + } + this.audioContext.decodeAudioData(this.irrArrayBuffer, function (audioBuffer) { + var c = audioContext.createConvolver(); + c.buffer = audioBuffer; + me.wet.connect(c); + c.connect(me.output); + me.convolver = c; + console.log('convolver audioBuffer', audioBuffer); + }); + } + return WebAudioFontReverberator; +}()); +var WebAudioFontTicker = /** @class */ (function () { + function WebAudioFontTicker() { + this.stateStop = 1; + this.statePlay = 2; + this.stateEnd = 3; + this.state = this.stateStop; + this.stepDuration = 0.1; + this.lastPosition = 0; + } + WebAudioFontTicker.prototype.playLoop = function (player, audioContext, loopStart, loopPosition, loopEnd, queue) { + this.startTicks(audioContext, function (when, from, to) { + for (var i = 0; i < queue.length; i++) { + var note = queue[i]; + if (note.when >= from && note.when < to) { + var start = when + note.when - from; + player.queueWaveTable(audioContext, note.destination, note.preset, start, note.pitch, note.duration, note.volume, note.slides); + } + } + }, loopStart, loopPosition, loopEnd, function (at) { + player.cancelQueue(audioContext); + }); + }; + ; + WebAudioFontTicker.prototype.startTicks = function (audioContext, onTick, loopStart, loopPosition, loopEnd, onEnd) { + if (this.state == this.stateStop) { + this.state = this.statePlay; + this.tick(audioContext, audioContext.currentTime, onTick, loopStart, loopPosition, loopEnd, onEnd); + } + }; + ; + WebAudioFontTicker.prototype.tick = function (audioContext, nextAudioTime, onTick, loopStart, loopPosition, loopEnd, onEnd) { + this.lastPosition = loopPosition; + if (this.state == this.stateEnd) { + this.state = this.stateStop; + onEnd(loopPosition); + } + else { + if (this.state == this.statePlay) { + if (nextAudioTime - this.stepDuration < audioContext.currentTime) { + if (loopPosition + this.stepDuration < loopEnd) { + var from = loopPosition; + var to = loopPosition + this.stepDuration; + onTick(nextAudioTime, from, to); + loopPosition = to; + } + else { + var from = loopPosition; + var to = loopEnd; + onTick(nextAudioTime, from, to); + from = loopStart; + to = loopStart + this.stepDuration - (loopEnd - loopPosition); + if (to < loopEnd) { + onTick(nextAudioTime + (loopEnd - loopPosition), from, to); + loopPosition = to; + } + else { + loopPosition = loopEnd; + } + } + nextAudioTime = nextAudioTime + this.stepDuration; + if (nextAudioTime < audioContext.currentTime) { + nextAudioTime = audioContext.currentTime; + } + } + var me = this; + window.requestAnimationFrame(function (time) { + me.tick(audioContext, nextAudioTime, onTick, loopStart, loopPosition, loopEnd, onEnd); + }); + } + } + }; + WebAudioFontTicker.prototype.cancel = function () { + if (this.state == this.statePlay) { + this.state = this.stateEnd; + } + }; + ; + return WebAudioFontTicker; +}()); diff --git a/frontend/dist/vendor/guitar_sample.js b/frontend/dist/vendor/guitar_sample.js new file mode 100644 index 0000000..8dbc41e --- /dev/null +++ b/frontend/dist/vendor/guitar_sample.js @@ -0,0 +1,46 @@ +var _tone_0250_SoundBlasterOld_sf2={ + zones:[ + { + midi:25 + ,originalPitch:7400 + ,keyRangeLow:0 + ,keyRangeHigh:72 + ,loopStart:4800 + ,loopEnd:4877 + ,coarseTune:0 + ,fineTune:47 + ,sampleRate:44100 + ,ahdsr:true + ,sample:'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' + //_tone.acgtrg2 + } + ,{ + midi:25 + ,originalPitch:7600 + ,keyRangeLow:73 + ,keyRangeHigh:97 + ,loopStart:6168 + ,loopEnd:6236 + ,coarseTune:0 + ,fineTune:32 + ,sampleRate:44100 + ,ahdsr:true + ,sample:'AAAAAJ0AiwC2APEAGAEqAUEBaAGPAZYBqQHBAeIB+wHiAeMB9QH1AeUB3AHoAdIBwQGuAY8BdgFlAWIBMgEDAdMAvwCeAHwARQAtAAYAAQDN/8P/kP9Z/z7/LP8M//r+6P7W/rX+mP6c/qz+k/6W/nX+Xv5X/ln+Vf48/kb+O/5T/kj+SP5E/m/+WP7v/Q798PsU8nIG0geX/5wI+AJn/p4AbfmwBLf/LuyB9ILtsu8P8XXoAuZg6Jj7Avi9CRD3cwCG/8YM7xT1AR4ebgYWDbL9cw63FggQUCA2IEAhBRoHJ/QcySQWKHogqyabDZkiOgeyGYMg9ieoI4kYSxoODq4dtP5uAW/52vbeAaPVYeJm3mDgvdi71mLqxftBAqXpXerw8SMDe+bXv0rPIuQV4CPC6a7XqlvAoK1IhjyZUbo02o603ae6mpe1XbUerOKqUbwP1gi9bKpGtUvBY9B6z6rXEPBb+Db/mffKCNkRJSEmH4gdei/EO6U57UMeT2RqD2jMX/ZlJ3S6aXBlkEyLavJ69mdLYspSoWsJWSdnX1IrZttbRVqyVq9Se1CUTLlKF0eKRTlCYEC+PdYT7ihnDj4Ewd1VApIY5SGtET7ykPRg61X8TNYa3ifmpu6oy+CpvLdFsDrEVrdVuU3IXL2CxnCzIr3XugLOo7t1zM3Cd8dE0y3KQe9K35zpVuto5pf6b+uf4qjrKfER9jjws+5b6gX2//JN8KPp5/9jGp0MuRNFKRgzahJPFdj3mBGUGLQJ5e6j4cztoNOuwg27zNo2+sMDqvSM24nfTu9J6PTfgOrVAnv1NuqkygXX9OT15vbqEeut7ZEBT/Vc7BP4QvbQCXUEZv4uCx4DmxLLHRAVfySjFlwn4StdJecj6h+CJK0gHyc8IDofABiZHGAX1/uAD/QXUSHqEc83xCwQMdMgzA3aFJ8hLiRRB5r9fPEg5//MPbuwwArOwO4q7w7Xj9Gy0uHegs2h3prWTvde4AvJcr66vwDOCdzV1hfWrPRQ8Vz22+xg73UGNwNRC5AGmvt7GyYWjioaHx4pEzh2PydCcDuWSKJAMUREP2BQgEZDSTBGxETGMistvz/hMDNG9EtRUqBZTUzEP9MyE0Q8S+lFtDLoK3cgHQKp/D3bLubO+50SZwHv6qPqdeRX7Obb7tiU7Fr10d7BzYe1I8d23prMJM7f0xPojOtk0e/Z8NsC5DzwXNvS2rzflu098p/zvfKa+4IIDAD1Bh4FKhD//1YHSA4ZFeEKfBG0CUr6C/1m9BzvXPvQBPUKlhXiFR4QePdgAsINDBVWB4wMyPfF767bW8iJsIDFReXX5cbYyc4Zwx/VPr+gvZLIitQZ5HjK27ICtVfR8s5E2R/AT/P+7qfru++c31b/ufz3B9zzI/ohBn4SDh8GE9sl8CvYOmIt9j6TP7Y8vTmKP2tHhTl9UoI5XDPzMvQrvSIBGVwtQihDOxw8+zmAJV0d5jLbKAI2kyqEMOcYjhQP/LrbDd0O7cP+EPtb6mffv+B10YnPWMfay3Xo5tvXxVu3JbUb2sTFXsVsy9rbleQy3kPXLNRt7Ezt/erM2VHisfAMBaT8VgL6EgYeuyJ6IN0x8TG6L785KjLoM6xMxz9uMDAvajILJZUcGhp6JUApaEIwPYgwNCccM1syejnOOuJA1zZ2M0gi1f23+Dbs+gsVCqUH//zt9r3m3eyg2RvW3uwZ6e7zc8Umxk/Y19XC3JPNJuC75KrzAt5E2d3cbOgU7v/ZAM2y13vqCukP6XzpRQA4AeH93Q+XAGQOchSQA2IDHRKyHbkDWgC1+cb9w+jD6KHev+nC/lsJ0f5V9kv5ngDO/BMN0gnLDa4XJ/8u9m3Trcye1ujlZt9P8BTSENo80FrEosgwvLbcEuA2z4HAVMLcz4DaMdme1F7qbPgm+n3wWuot++YHPgif8WT1/wDyEysP1w+0JJQkiDVXPDUzKj1BTs0/BDccPghOa0iWOPcyvi4yKisfCQ/sBvQfrS41L5QfQyeGHcomuyvqJO8uMDMJMu4jTwRK7ffybOvg+r/7rvCG8l7b5t+tzJXGfdM23DncEcx7vwjBrsyC1bnJd9Hd4EfswOvz2ajdZu959i/t+du84OT5xfLYATcBfAXsHEsrzB4GKl04t0CBLJ0vuDcpRoU3WzYqIg8ntSk5DrUELABdLa4eGSvCHaQcZSKSKRokJymQLqY2GD2NEhgHefRT7qj9rvL+AjLwqvEQ5fbXoM3BzUzcWNdZ4GjGKMKSxKLR3s3MybXQ4eBX7LTZX9bd1xjrwey5zlrWXtSk3pbp/+lT4Sn7iAhbDG8E/xaRJOsaThNmGOQd/yixIAMQeAdMEf4KSeIv5jP06QPVDOsKLfsnDFsGdRIXEMAHSSiVKA8eMgc+7cbo7uZ85+PzTOrV7u7pRdrPzP/Ls9PB1UvhSdgby1/LD85B20rVU9Pg5azx9/eM5a7mGffdAU/vCva/4y/wrfwkBHD36QI+HMMe/CPiIUU/MTICO6svuDOuPRJH0jtmH/wzzC66FDgG/P6CEegh+yOjGxIcLhU8L4gelR2BLFM0TUeaJlEWqf4j+pT7pv3D/DH6+QVc9C/intyd2xLiweij5zndstrMz/DhG9mP27jdxOt6/cnuZeJ6+BXzGPvo94vqAuQn81b8Qvbp86cE2BWwD8kgex/WLvwpLTEkId0naToYOHUfkiR+JE8ZPAk67oz3EgZUENsZ8AFRB0AUkg+TEOMHNRoULk8rjBTf/YPpxesu7N3ny+Pw9mfuuOAq0Z7F6NJW0aDZgtPty3/HcstSyLbQ48L801PsJN7c38ne7egi5+H17+Ue3RfiDvCm8O7kg/tq+7QKtQ+WGvUgxiN9Mvgfyh1HMVMz1SrBJnUeNCcHFhH7m/fx7RcORRZXBwIJaAZIFCsP2AVfC0geGS/JJggSEPXe8Hzx2uk85U3w7PXH9GvdmNA0z2TX3tdr32zOi9nkyNDQzNQhw1nVTOIE7WDg2+1r45fxoPTN+w3mYung9cr0RfVd+qADQArjEmgmrCBgMak7MzVBLpc6LjsRRUs8bzJ8QOsrAS6eDIsCRxLQIUAlkxrbGQgigCQuH3wWPSItNVFCgDkgF+MJuwiT/d37vvXcAkkNzvxv7GLfkdt57t3ireae4Gzazd6U3BPOSdEs5IvkTu1m5hns3+YN9Tb5HOvY5XzvkOuG7PTtKvkv9Jr/fBNDC10eWihkIgQmAyO0JWgwKyjlK2skcSBEIzIL/vEn8uT8KBDVBccFZgOsECELgAXmBCoHIzDkKJcecP9l/PXw2e+J3mnnaPnB8cD1RMkn1ufRftlV20zRG8/m1djWHcNnxWrNRdp63tLgnuTr2PTwD+487xDi2eug7e/fte9R8UvwOvdYCK4Bnh0vHl0kBSkGH9YvySXAK1EyXyVxKSMmcSCQBdDzIfYkBeoKOAbHBTcMrQyKEfsCJgJtG2YwkyowHdMA3gTl9dPsBOtA6l8Dlvvf6/TWetny2KzrY9pr1APf9eMb17jMk8+k3CLhaO/T6dHkVfDU/f36yu1h+XX6Ae9M+c37X/ciCpYDHxGGGLgqKjI4Mdgw4D5VNDw8GT0cPWQ6bDglPeAk3xU+ABYPahNfE0wXBBEpGnMcEhfjBOkYGiS0Po0u5hp1Fdb/jQOo7XnrwfuDA+D8TexP1bzYu+pz4vbV39Z/5bjgqM3Wz6bKtdkZ5Cfoytul3p30D/O/5q7uuPDg5arxu+fT7P/2afRU/nj9FxLaI2kcZSaxJ3QtzyxNLa0x0SnmNRcrPTKoEc8D/f3eAdIEigrZBKUIDhdDB1ALPvhpEv0jPigjJQ4RcAQL/8vsteLs5jPx7/cc9/fSuM5k1QrjotEVyZPZj9Zs1x3HcMIVxNfWx+Gf2HzK9ure5U7m2+jQ4ffnq+gN4lbmheWk82L0fe0XA9oRdxuwHG4hqicHK9cvxidsLhYsyTILNBglHg8ZCL381QMcC7EB1Q6XDvEXrw4JAW4JThucJuU2vSF9HCwQkgPQ8wvyuu5zAlMK2faf3YXfwu4S5gLeVN5O5lDpluNa11vMHdm09G3k8+HV6Z/z8PWQ/Arv7/oB9dz47/U+69IFVPo5A938rxd6Hswniy2cJtQ5OzXeODsycDN3NhND9zJ5KIMYRwMoD5sCxAulCDgLqh5uDu4JtwLrCqAYcC7aJVcjmBtrBGUBQ+tg6wzsKQSTARDkhtzF4WHj+d9k12HZ+N6F46jfJcAQytbhz9wM36vaFt/37m7tou/u6bLsGPYu7A/p3O1E+13yUfhy/IIMNh28IUAgNibDL88xrjI0If8zATSIOL4vYBx4DLYITv6bB5/4YwSdER8PIwmBAOr6SgpvFzwfGyJxHCsPcv+L9bvhyd4o8JT+q+i34BfSDd+y1HPaLc5Gywvf+99lwnW8SM2OzSnexsua1vHXEuSE7VfdfejA54fxH+FL6Kruye8T9erw3/x+Degc1B3WIochXDhANGsnVC+kLqY/ETxaLN0i0w3FEMkJzwPYAAkUBBbJGhAOwwNIDqETTSMZKaIwDiZGGyMRsfwo6pP59AG7CCn03fGc6L/m1u9O5jzTnOoQ8w7gc9ezzGjgZN+E52XeBuBx69H02vT+6Y734PXy81vxcfLb9Xn9F/d7+isHMBKaJ3cgex7YN88yejl6KT0sLzuEPhc8si4qGFYWxxI4AXkD9P71EiYWXhGMAnoD9QayEdQZkSnYIjwgYxquAhfveOhL+A79W/ab84bnwtnu7TXiN88J3YTgdemC0q/MecyR0vHcLtsm1cbYy+jh6m7nk+tT7bjsDvEu5gLxXe+38iT1hPIj/swZ9xNgHAQd8CvHNTEo1yQXKT0ysDwwM9QcIRfQD6UH1PwT9qn74RC/CtAI7/QdBBP5aRDGFika7x9QG18V6PGp5uDsSvQ77pv88uP73CjnSN5/2nbK7t7I3x3c0dFYxyHNhdDG3jrR8NVd3M3lIeiM6xTq1+tX8+rnjfTw6svzPfxR7D7/sQWdGYMcvBy0JfE3GDP/MeInrCyvQzs/fTTlJEQdzxhFDwL/af2dCtca0RB4CykE9wLUDMMYZB3eIsArMC0TEtP0I/718WL9bAP69wrv0+7s7ynmCNxJ4Vrn4Oga5VDZzdOm1NzliNzI31Ti7eMw8Dr0ke55+Dzvh/hb+EDu1vom+rn4ofqO/VsRdx38Gf4l0yhkOjE4iC7uI1I9Aj7GQyouniUWIa4XKw6v9GwFygtPFIEQowUO/uIBAAxyFD8T6h6rNUIYMAiE+SPssPVo+FH6cOnK7OHpxeRA1+bWPdvj27jg2tm2ySDMNtJY0xPVI9hC0XzkI98K7JjpteNs7+LruOlp7RTt9vGV7+brJv7yBL8SmxSZGtsjszgzKH4kGCJLNrE6ljcDKEMcdCIAD0YALvVaAGYIoBK8CHIAyfYTAy0R9QC/F9wpdiUMHXEB2/V+7kH7zfpi8Kfu0fG77MLeo9uL2x7cd+Hi5fPPddQM0L/Qw9wh0ovc3dla34Hs++ux5+vw4uvG8Q3vYe5697Pw7/AV+Ln9EgzCGIsSgiJ6L9w3RisBJUctNTqjQvA0sSh3K9UhpBHyAuH7QQdnDscYNQ0t+SoI3Q15BRAPeCE/LOstMR19Bkb1mv83AOz9hfHq+x/5xewS6Fvjz91H6wjqQ+OV3y3VutuR3XTZiOT13DDhZfDi6Tn43+zf+ILzRfcw8xn+W/UT99b5P/c4Cw0NrhntFKYq9zPoMiAtLCN0M2E+PT35LlUwCSd+HzcMsf7P/w365RvxDAH+EgA6A/sB2f/3DV4Z1ycWJ2US7faX88T6dPdE7R/tqfb25sLrJdeG1aXeWtvM48rVtdDz0cjPnM/q153PMNnz2VbhvuXM6Bzo9O975eXvsPBK8Mzvt+2T7131YALNBvYL/hblJEstmy8cHiEnMzKDOJE2li2+L5clihaKEFf5wPXFDf0RGwb2/xkDPf9O/5oDDwuCHPwsZyRkC3T11f6m/HPyLfRH8CD1ovT04ovbSd8D2xru19mC3T7Y8tGr2aDUe9iT2O3dct606i3lxvKE8CzsWfLZ8lz5LPTd9TjwCvdQ/6gDMgv1EpwdoCwRNssoySo1LcA6Qz05NhY/Jy6DKXgmsAqb/OcG4RVKEPYM6QgoBsEDTQcZBTESGiZtNrUjMgZICdr+pQJj+pDxCfsC/nHwie0h2CnoCOfk5w/jZ96i2ILcT9gM2LLcddgQ5djgm+ac78nxhfBM7cTzzfWT+CH3ve7V8zX56vsmAhEKlQy6JKwmezBXJFAlvTKiLgQ4kzqDL4Uq9SyjF1b/EfxfBsAKYgn1CF7+EgGr/V7/S/poDagr/yWtFc8Gtvtm/yT7Kurv85zvTPmU6mbZmNvI3gvgReJ02MTW9tWH0lrVAtAF0y7ayNoB2pjkEeqI7BDopOvH6/j1afS47aTtrPGN9BL8sft9BOMRuR4XLAUjdyWoK48mdDNHOmoyCi7TMYAq8Qt6A/H//gQTDAsJZwk7/UIDQwKa9QL9qx77IfkrlRFfBfUHEfzn/c3tpPN8+l/6OuZP4k3dWOQw6UHeMuNz1oPcbtte1azW/Nnh4SnbTuQZ6fHwu++E8ErrRPn9+uP1wfdN7s373vsw/okDBgY7Hk4njCgQKhoxkCYIMxY8NTr8M+A6fDq4I1MVwgWTCpsHbxI4EAYFdQloDOX3V/5tBwsf1i7JIaQWTQ20BcwHG/dp8g389/v4+dToIOGO46jnyeUP5sPa4tzi3D/b19IG2cHb79ss3hrjkebs8uHr/urH75bzWfqb9LTuevKf9RL8J/um9RkPNhkMH3gmWylCJUUkgTYzMt0v/jWXOXcxFxygDj4GD/+SCUYPHP16DeYCPf+t9IjzgQoJHOEiOx2pD0YGuwj3+fby3/FR9OX86e41457edN2m5EXjddyA1praz9hD0tPRr9Sv1Izbi9eD3dvp7eeJ60/l4+wR9Ir0/vF36w7tovu19gPwx/zFDAAVXB29KX8fEyPxLEcxDC0WMR45mTxHKOof6w/s/WwOrgY9B8gJRQkaCeD66/CX/poMpR6ZJjYY3BVXDWwG2wAu81L5WftC/2DxMelU4CDnv+qt5nLeld8P4T/Z0t2x0yzaFN8h20vhe+RG7h3xkewW7r704vfY/znyf+7Q/sP8GvlR9XMJaQqpHaUnzSRPJDgsizLHMrsrhj1TP9831TSCHHMNjg+QCfoL2ggADW8SeQPT+RP2Z/+mEZYi9x21Hj8TXA/YB0X8Wvc1+ez9HP2s8bTkp+Ae7IvnR+FV4YLbG98q2xnVWdVz2P7YqNwo2RPne+ov6tDscuaF8x/6OPaj6Yrz9vso8/T0FvfE/ZUL3B5BHEwhaB3OMAwqaCTRNcYxWTuvOWknExfsDREIvQq0AK0GgRB8BTECdPDe8KEAMQ4aHPQZPxgcEMANdgDc+YPzjPfb/CT6FOqD3gLozuNt5Rbf89od3lnaJdcV1LTSntb21xLUteAB4HDqU+qs5Bjp9PU1+O/rH/C+943ymvil8/fzjf/zDjkgkRj0G7ws4yblKP0tbCz7OUQ+wTc1KKAVVRKTD3UD1AdaCw0R0gwB/oDxiPimA8kVQhvdHNkb/BSKEA4Cg//A9dT/PAX29hPuBOga6gXsNudN4hbkSt8z4RHbqtb23OXVk93J24TiOOcD8ZLpB+hQ9N76tfKs9iv38/RB/fn4C/h082gFAx36FYIbQyhKJVIuwyoNK5oyUTzIQY44cyCqHg4UKQ11BoAHlg/NFI4JC/tm8vL59wcnFIAZaBsdHSIT8w6TA7D3gPuXAlL+z/Yj6jXoseyV5YvnGuAf4SHhudnw2rXWkdaP1W7aF9go37TpZulk3wHv++9z75D1A+9l9SXv2/pN+Onm8vW4CXgMOxR/GngcRyjkI/AmdSUdLJE/BTr3LbEf3BlLD+UJPf6rBu4OQw/3BNXw3vG+9YIJxQt4FjIaehQsF7YHwPxl+Pz82v7U/a7uaOyI6OLnHumr4ALkz+AK3Q/cT9rY1gfUqtpj1XrZluf/5c7j6emG7Wzv1fI39O33Geqv/aj9bewN8Vv77wbCEisTgBxnIbEnRyqcI2slJjjqPhU7BS7tIvodoxK0CIcBeQ3EEoISLwJI8kn2jP0MB1US9ReZGBMfZRTbCAEBC/o0BgL/4v4688/tce3w7N/neeh/5Y7jAOGx4HfcTtbU4BjUnNkE5Q7lWOm25vPu0vDK7Q78E/id7Rj7VQFo9eT0KvDeAUMJVxF9F9Ua8ySGLJglWSEwLsw3mULxNikvfCXzHuwRgwX+BhMN7RcMD1/8p/Z39En9cgpMDlQUWB3eGe4UCwPI/jz/kgDrAB/5sPA/7tPrUOkW5nnn8OH633bjxddF2obccNLk1ErZbeG14sDfyOps6tLkrfe88gjsefQf9gr8PfB+7lfxXvsNBsMOiQ4fGdglCiMaINUdoCzNNos8/zGDKh8m0BdeD+T/eATREH4T8Qbd/DTviPMaAHMCGA2aEfkbCBuUCnIE2Pzg/0EBgv1p90zw1u916fLoMeqp4WTkRuby2MPfyduq2RrU6dOW4Tvff95H7ArmN+Y282DztfDT8Ff2gvx4+FvyavJp8LIBKQlRC8UR6x97JiojBh+2IuUw9DsnOfM1jC6AJhMeDQsXAwoPiBDcF3II6PoK9Pj46AGOBp0LKxkiIGYYIBDfAlsEVwMhBkv+tvlb+OfsoPFL7k/lVezz6JbjgeDx4d3hfNoS0UvlPNqV4iLr0uXm6E/t7vY18nPxOvQD/tr4Wfub9HnuE/m3AWYINAnPFnUgZCUDIcobACg0MPs5oTibM28tBSm7FQYJxAbHCHcWyBCABRf2IvHV+7v8CwH5ClQX5hw8FLQJTwAYAvQE6vso/gT4z+2C8hrrIOYZ6BfnhubT3JLd9OZV1c3VHdmL18jajuQ244biQeac70TyY+pN8nX05Pf8+Vf3Z+8g7gP6FP6GA04J5RRtIQYhExpnH+IjVDEaOToyRzVbLhojwBXdBR8G0AyrFOMRBv+v9Gv2pvlJ+8X/ygx9GdMaEhVcAlcHhAMGAJICZ/wR9NP2d+5b7Hnnquj57kvcBePy5trctdp12lnWPNk34Kjlt+Kb4ePt2PGH7UjwKfOa9sX6z/1p9BrydfMb+7IABANiCx0cayDNH+wdUx0zK8wzNDV3OOI1GTA2JuARBgtuBqgTHhlODboAT/f0+zD5jvtoBF8MqyGIGeEPNAubBzED1AiN/gP/Yvhc9oX1WuXP8JvukeWJ5KbpleG94VDe4tkA2HPapudp4nbgC+mE7jnxru0L8zzwC/vY/P75kPUg8e31X/xn/LABqxDYF3Aiexv4GNMi3ic9MpIzODYtNuItgiFeEIMEAQr9E9wUmgcC/eb55PQg+RT3UP6FFIwYQBS/EMYDLwdPAwYDYv+I9nv7lPXR573tbe5C5RDnx+RZ5DLfNOEG3TfUNNWS35Hirdwc4rvncuzH7rrtwOvv81P5nPqu+K/v9/Os9sz2sPvEAScOwhz+Gl8Ykxs3H1gr2CznM4I3sjJcMfAarg+zAoAPzRTzDn0IWPyO98T5bfPX80gIeQ22G98SugxRCHoDtQngAZ755v+b+zXuQvAf8GHpv+qo5m/peN/X5UvkLNrX1FLbBOOl3qThxuOL6TPxSu+77F7wofUr/tP89/Rz97f2QPf4+q/5CQdOFJ4bLhxsGUcezSPEKZQxOjSBOuc2xTHOGyIMMgxME68TUBWFBK//WAAt8x74XPc9CDcV+hfdFpsM2gWYD9MGQv6oA/oApPcy9Pfyoe9T6hPuH+oD5YPkX+oj4vrWctqt3XHg7+Dw4EPjg+7z7WDvsOv77Qn8XvtU+Db4o/TB94v3F/TW/L8F3BQJGdAW1xoMGpojLyfxLsEyJjeuOaUqNxSdDpoI6BKGFc0IvAbk/rL4a/Th7pb53QWJDyoaHQ21Bb4N/AeP/0wBDQF7+9H0vPNv8ODoyuxv65LkveGz6ArmYtrq2T/XwNwG3yPcQeCh4/jqf+/06ernpfNC90j6wPeW8835rPNa91rxMv0/CKkS+RfrFcgZXRp9I5QnTC0kMBZAgDQPKEIX3wdOE20SPBG1DJ0EUwO292PxJPTZ+H8FpRk4EVUM2g0mDSYGMAKXBjH/zPwm+HX3Le2l7jvxcOkg5YnpXeoQ5CTgENmy3F7fuN1N4g/foef+8G7tr+qd7Wrz7/1I+BT6Yvd7+8H4n/Nt9lj/ggtGFZEWShl5GBMdjSi9ImkumzhePOk4JSVwErcSRxHgFRsQ+guLC8z/HPfa9hnvIPtnDiITtg8WD0sQbwnYBg4HQwRm/nL+cftz8u7t+PQK7HTo/uip6ALrquNU3Xzcutub3Tfi19pg4iHqku0r7k7m1O+N9PH7jPfn9oX61vqs9TryMvWwAC4LrRGjGEMR+BnDIEsdSiYWK3s33D9HMEUhRBKoEDcUOg+yDf8PpgMo//34Ue3Q7tf+WgvZDIsOTw+9CuwGqQeyBan9c//g//fzXvA08uPul+o256LmsOrs5i3g89/d1dXeqtxf3ELcfODh60rrAeqt5sLuiPYG+Qj0Nvm5+WX7c/JL8Dv5pvzLDiwTTQ99GPMY/R5LHz4iNi4/PaE7/DGFHHIVZRdODn4T/hC9DA4HXQMH9d3sJPUTAvsIEw6eEjcOhAogC2AMsQCqAikGCfsU+OTzWPRo8RjrD+mu7cjojusZ463dhN6g3oDgQtyX3tjm3uwC7oDpLuml9CX3hPnA9e/7pv9D+Fr0LfWp84gIYwyGENMUlhUBHtAdTRzLJNcw1D5qPQ4qAiCCGeIRCxNtFOkNiA0sC5gAH/BW8AX34gB/BrcQ3hBTCG8O7wwFBfAB0gZVAGX8/PSy9Ej2G+pE7jbnJ+zv6srnzuHG3B/frt733KPawN8R5gDuQejB6LnqfPIV+UPxTPpJ/CP69Pv07wzxZ/pXBNcL1g/qD0ca7hk4G9QbeiDsNuI93zUyKpkgfhRxFEQUgA/CDBYREQpj+s7vuPHW9z/74goVD8EIxAzBDvQGigW7AkcFaQAs9vH4BfRQ8fLsWuk16WTrr+m25pve9d5F3lzdDts12Zjg8efp6OvpeuMb70jya/F39zz2J/sL/w71F/BF8v74NghNB9cOeRLSFzccKhioFuwnYTi0OYA2HisYHVEXPxjiEVcOghGrE1cIL/dI9lLyAvVCAtAM2ggRD/oNSg6bCdYDuArgA8j/SPx2+Or33/Jf7ojsLusX8Fbraudf4tbhROJj3wTa6d+d4RztnepD5jzuvO2R8zj33/TU+sAAVP0x+D7vnfVG//QE3wrhDmUSPh6bGFQUHhtKLIY1lDuBN4smqh6BGgoY1A7xDgUXQhFDA/b7ivTd7Pr7LgC+CCMJjAxdENsIQwdSBx0HJwIV/2n5tPkC9QjyEey46S3ue+u/6wnjR+FQ5C/dSd6r17jcquYq56blFesh50TvnvL+8DX34vepATL7kPJX8IT1m/zOBRMHEAv8F8wZOxMPE/EdiyfTN/k69TGsJNQeOB1NEjcM1hQIFdkLAAjG9wTwBPNR+IwCHgPSCeYPiQm9CfMGRAiyBaEBx/3o+oL43fZv7vfsDOvg7EbvUuVQ5jXiIuI94WrYz9ip4sTh++eB51/nH+tE7r3x+fKs89T9ngBd+LX0Yu+69uUAFAMiBMUQkBmvFOUUoxMbHQcrNzpPOmMuzCURJnQZtA9rFdESxxXiEFoF4/he8Sr27fyT/VkHHQ0jDvUKXQoqCgYKfgW6BCH9B/9k+lT26vLY6p3we++87BrrR+Qa5u3nkNv43IzcseFr5mDnxuhd6v3qePIH8ZnxevmW/n8AfPp18mzwx/yO/3H+MAnAEsoW8xRWE18SGB2ULSU8hDP6KwUufh4vGAgTwxF7FHwVHBCgA7jzo/TA9iP3k/67BeYMRgqkCC4L+wZ/CgUDOAKu/qT78Poh9UDtuu4j7Xbuie7B4uToDuaO4qXcHNqo3PXh8OMb51rnTuf97s/t6O638nP4pf6yALb2N+5Y91j61/okALAJPhI2FcwTaBHQDi0fajSQMqQy3TFMJ9ggMxZIFD0R8hTZFyIPPP5a9771EvUw98f9LwlSCB8LYgh/CQsL+QXsBbwBtvxpALD3FvUO8Anr8PEv7wPpTejB5wjnJ+Oe2mXcwNwZ4iTmlOQB5/HqJu6s60zxofAg+vUCrvxg873zkPiG96371/8BDbYPCRflFNsIuxMLJictZzTQMwcxKCgVH3EZ8RLDENIauxaNDef9Gfpa98/yJfqz/tsItAlqCRYK3QslCLQL8QLHAmUBuPxx/czz++3e8Xrxy+7o6q7oaev85+vhJ97n2nHg4OTY5KvkGOsR7OzswO+O7ZLzDAAuAZz50/Yq9iX5BPZu/HYEOgduFpoXkAvcDO0W3SOfLTAzJDUVL0EmiiKiFakRGhT5GmkWOwijAED56/R79Nf2NwDBB2gFcwvmBRoLWQprBUcFVACl/oEAcPiQ8M/v7O8p8Yfr3ugL6uroteaB4J7aadoe4g3hSeHj5lXnmOs+7fXp+es19wz+Kv2e9872NPgO8UD6GvtY/RIOcRW+EFkKBQ2iF/shJSxcNVAwJy+6Jw0gNxR8EFMZnRlKE10Kdf5Z+1P0BPNt+tD+8QaZCP4FXQrLCpkIGgrVAZUBkwO+/kT50PCD8qPyqPAc7MDrw+pm6w/prt5H3Jff3eGo4BPmROSp6/zuOOrl6q7ufPtZ/pH5Tf0I+VnzG/l++dH2vQS1D4oV2w0WC+kQFBXzJLktozOvMLYwXikUHg4SKhcDGPcaLBPtB/IDb/dg9vT0mffQAm0FYAbHCI4IpQqRDCMFjQPoAhUEQf8z9orzRPMV87vuhO5P6UTtru1e5SXfDN+e3sHhjuMS4Vno+uyM7bPpJee29hj5GfoQ/637GvWe+Un3GvYV+O0GpBIyDx8P5gpUDgwXaiT2LcgvojEFMwUl7BvXE/kVQBuGFqASBQqR/nr65PN/8tb6tv6sBC0GhQTjCB4L/QgfBIYBrATZAub7OPUX9MHxrfF674jp0+rf7kfpcOQD4P3aPeEy4AzghuEI5zDwFOid5ADuTfMq9Yr+H/sQ+TT3qPmX9T/xafy8CLAONxBjDIEKEw1uGGgmcihNMV40Wi9RJvQX2RZgGXQXLBqTEb0IWgLH90/1JPVm+S4B6wSmBLIF9AqfCzIJFwNZBWoGFAI9/B34m/Ny9Gn0FuzV7Pnuh+xI7XXkwt9n31bhnOLX3m/hYvBU62Hmg+sR7E70x/jr/gz6fvlQ/Ir5U/E59UX+lQkkDzsPCg17BnQRLBrgISQraTCBNuQuWyFfGzUYMxeJGwcXahLaClz+CPsH9LD1mfkwAgMDxwMhBmsL6QtPBVUFaAbRBB8CXv3v9Fr2ivb57hzwdusU7wXv/+pP5b3eSOAj5TfdNd1b6q3qLevn5/npXexM9Cb80/pB+G/+xPuC9s3wRvY4APUH5xBmDiIHkwuvDxMaXyHTJw02njOkKxMjlhnzGMIYzBjHGPERbgjF/7H50PP69ID6AgGAAZABmwcqC+YHcQU0BjkDiQayAVT4rPh49anzofB/7NXs6O5d7drsf98m4GLlCN3t3ILgb+jk6d/pUui85jnsf/gJ+Pf30vrN/i375vEO89f1fv48DMANIAsICHAJnRL/Fvcdcy3dMjoz5iqDIPMbfxhhGHEa0RgcEdgIlABf+Wz0nPWq/HcASv+fA+gJggd+CvMFwgPjCFkFQQCp+0v4fvcL9THvcfA361Hyg/G95qTj++UI4l/eXd4W5Njntev67BnlYuj68cv3Cvgc990AxP7j+CX23PC39wUDXwvlDkUJhwc7D6UOORd3IPYsyDSiMeUpniFuHAYYnBqzGkoYXxHVCHYBYfgz9JX3Bf6a+8EB/wM8BtMLlwbGBPYGWwfgBEUAoPnU+xz1F/Uf8Tzq+/Cg86jr+Of25R/myN6v3iHgeOFA6ZXtN+ht5HbpD/S19AD0zPxm/Y3+vvni8SDyK/erA3cNQQmuCP4JsQvFDpAVGSCVLr8ydzAMKfEgMhsQGdEazRkYGGgQ6wleANb0APi798L5Sv4Z/8QCoAkRBzcGPQX/BTwJTQFFAAb8vfds+Fr0QevJ7nnyRvBw6tvoKelj4Pngyd6I3THjDOpE7SrkSeTv7XvwvfIY90r58/9m/b73u/IW7ib7dQUfCjQI+Ai1CUwLgQ0GFOMiMy23MmIvfijvHzMc8xncGv4avRX9FDYIV/0m+mT3X/fP/Dz7sABfBWEHLQkUBLgHQQkqBbYFDQDF+sz8IvlI8fftffKz8zTtXO497Fzm1uTo4FTgSt2/5nTu0Og95cLpDewc8yLz0fZZ/dj+qADq927vjfL7/UcGSQmgCIkJqAuFCVENchXRIxQtZTOCLk4neSGAGr0dFRknGd8a+RElBkcA1Pef+Kb4uPj7/Av/fwXyB0ADswesBv0FEQj/Akn9Qf00/eX1WO4D8Q7zIu4a8OXsfeus5UTkueF22v3fq+mk6OXooOSV6DLtl+9c8wT2BPueAan+y/O07tPzU/5nBa0G8Ad1CigJ2gfoDL4UPyPLLY4wvS40JTwfCh/VGdAXpx1MF4cSPgbn/RL7qvdz+Gb5tPn+ArMEmgNvBzMFYgZvCC4HbwFN/UICmPpS8wvyIPJj8eDvH/Dj7lPpv+i85p/bQN8E4hnp7el35qbnnOgB7Rzx6vLi9Bz/QQJb/V/yc+/h9h//BQVEBf4KDwmZCJkIuQqMF2oiii3KMuQqdCTRI6gbOBrQGiobJRtoD5UGeP/f+c36IPej9or+PAHBAkMGxgSPBaMGIwuvAwAATAPe/zX5g/TF8vbylO898hHx5ep07nDps+IB3qzeYuUc6LfoHuhr56DoT+9M8AXxc/e0AOMCt/r28XvwMvq0/asEfgeYCYQK7QflBvwNBxXaI/gwDy4WK30njyDUHrgY0RstHjQY1xEoBQL/uP5L+NT1yfkf/TAB/wJ4BjMC2AX+CqEGTgI1AwUDev9a94D2OPNx7xf0uPCN7QrvFO4e6ebgYt314TvkCOeT6b/mkeZH6knuMO707075xAB8Aob2svET82T3sP5rA+UFVQuVBjQH6AeXCK4WlyVMLAIuWCnmJSYiUhqYGm4bExwIG3IM6ATdAR38Ofd39QP6+votAU8ESAEEAocJLgfDBTkBxQVjArr7vfvB89jx3/Li8h7vdO488JTv9+ac37jg4eA35dPn8ugb5gXoOeyh7DDuze9l/A4Dkf6e95ryd/PV+f79awMgCv4G/gn5B7IDYQw9F/AlAC2pK1krfCbvH/sdiRiLHZIfcBdfDcgGWgMc+0T4yvcs93X9uwNOAJYBXgQTCqEGJwPNBsgDfQESAGT59fO/8zP0c/JG7tbv9fOf7ZbmGeEp4cThkeZq6PLm4ucZ6XXs4Ox16pT01P1fAVX/7/Xk8/X1IPdrAEAFIQYhC+kIhgUMBeIK+BmYJSoqdi6uKAknaSGwGpcasB9eHtwVig0+Cef/Mv0L+YrzhPlN/t4BGv47AG8ImQXgBN8FwwM+A+MCYv7v93vzYfSb9HLuou6D8Rzzxesx5mDg6t8844XliuZv5ojmEOs068HnDe3787X9ugEk+tH32vPp8bz6jP6gApAIrggLCQ8DlgJWDi8XJyVQKmArVylRJ40ehhpMHKogORuQFlUPcgXoA7P8U/fl85r66gAV/VT+RgR1BWsFFwdEBIIEogTxA7T+TPbw9uD1w/Mg7kzxqfN28lXu8OS44TviQORC50Hm0OWj6/Pqg+k26qPrNflv/i//9P1X97fyivae+cP/+AMFCLMMKAVOA80E0AywGXUkQyl8K9Iq/CVoHNQcph4NHU4eWhWWDagHpgOy/R7zmPaL/QH8z/z9/0MCkgQNBnYFxwOaA1YGOgPq+2H33/fG9XHwZfB48PjzifPN62rlKuFM4r3muuT145Ppk+ny6yPoj+YR8Xz3Cf2kAXf6qvc+8x/2Avts/dkE2AoaCZAFcgI5BDEOkhmQI1snCC4GKvUhnh/vHLMdRB8iHY8VSgxsClQF3vhy9VT67fnT/ML8FQCVAb0EqwYLBDEDLwVhB2IBBPtW+l/3bPSV8bDuWPK49CjyY+194uLhyeXv40vkvuQw6Fbtr+i05ALr3u0T+Wb9cP8v+0v1lfQ496j3xP4OBlMJbAkyAx0CNwMMEAoYBSCBK3sr4yeIIs0edB3rHA4hHBx8EkUPrgwcAGz6G/dT+cr59/uy/R3+PgLyBAYGLwK+A/YHQgV8ACb9+Pn999L0qu+88YfxDfa29EnpeuRy5AnlK+Zr4r/kKO6q6UbpB+bc6VrxSPiS/zP/bPmo9xj22PUq+bL/mQesCXEJoAJyADEI1Q0cF6UixCqAK88mbyMfH4UcGyASIVcZ8xQIEdwK5wCp+pr4Sfix+gT8F/08/lcC5gU7BGYCIAU1By8FtwDi/Aj7bveK9ITwpvCS8rL2dPPY6S/kuuTO5QPlkOKC5Zrt8unR6Anm9+kf8aT4bP/8/oX5n/cb9sn1Mfmw/5gHvwlLCQAA' + //_tone.acgtrb3 + } + ,{ + midi:25 + ,originalPitch:11100 + ,keyRangeLow:98 + ,keyRangeHigh:127 + ,loopStart:2963 + ,loopEnd:5394 + ,coarseTune:0 + ,fineTune:-48 + ,sampleRate:44100 + ,ahdsr:true + ,sample:'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' + //_tone.kpianob5 + } + ] +}; diff --git a/release.go b/release.go new file mode 100644 index 0000000..89a7ef4 --- /dev/null +++ b/release.go @@ -0,0 +1,11 @@ +//go:build !debug + +package main + +func debugLog(string, ...any) {} + +func (a *App) JSDebugLog(string) {} + +func (a *App) IsDebug() bool { + return false +} diff --git a/scoresets.go b/scoresets.go new file mode 100644 index 0000000..a2209ea --- /dev/null +++ b/scoresets.go @@ -0,0 +1,54 @@ +package main + +import ( + "encoding/json" + "os" +) + +type ScoreSet struct { + Name string `json:"name"` + Type string `json:"type,omitempty"` + Shapes []ShapeDefinition `json:"shapes"` +} + +type ScoreSetsData struct { + Sets []ScoreSet `json:"sets"` + Selected int `json:"selected"` +} + +func DefaultScoreSetsData() ScoreSetsData { + return ScoreSetsData{ + Sets: []ScoreSet{ + {Name: "Standard Shapes", Shapes: DefaultShapes()}, + {Name: "Chord Density", Type: "density", Shapes: DefaultShapes()}, + }, + Selected: 0, + } +} + +func LoadScoreSets(path string) (ScoreSetsData, error) { + var data ScoreSetsData + raw, err := os.ReadFile(path) + if err != nil { + return data, err + } + err = json.Unmarshal(raw, &data) + if err != nil { + return data, err + } + if len(data.Sets) == 0 { + return DefaultScoreSetsData(), nil + } + if data.Selected < 0 || data.Selected >= len(data.Sets) { + data.Selected = 0 + } + return data, nil +} + +func SaveScoreSets(path string, data ScoreSetsData) error { + raw, err := json.MarshalIndent(data, "", " ") + if err != nil { + return err + } + return os.WriteFile(path, raw, 0644) +} diff --git a/shapes.go b/shapes.go index 9f5b440..179218a 100644 --- a/shapes.go +++ b/shapes.go @@ -18,6 +18,10 @@ type ShapeQuery struct { TargetQuality string `json:"target_quality"` TargetRoot int `json:"target_root"` // 0-11 or -1 for any Voicing []string `json:"voicing"` // per-string "" or "C4"/"60" + BaseTuning []int `json:"base_tuning"` // per-string MIDI for range center + BaselineShift int `json:"baseline_shift"` + RangeDown int `json:"range_down"` + RangeUp int `json:"range_up"` } type CompanionChord struct { @@ -112,10 +116,9 @@ func parsePitchInput(s string) (midi int, pc int, err error) { return midi, sem, nil } -func closestMIDIInRange(pc, standard int) (int, bool) { - lo := standard - 7 - hi := standard + 7 - // find MIDI note with pitch class pc closest to standard within range +func closestMIDIInRange(pc, standard, down, up int) (int, bool) { + lo := standard - down + hi := standard + up best := -1 bestDist := 999 for m := lo; m <= hi; m++ { @@ -136,14 +139,26 @@ func closestMIDIInRange(pc, standard int) (int, bool) { return best, best >= 0 } -func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]TuningCandidate, error) { +type candidateTuning struct { + tuningMIDI []int + root int +} + +func findCandidateTunings(query ShapeQuery) ([]candidateTuning, error) { shape := query.Shape.Frets nStrings := len(shape) if nStrings != 6 { return nil, fmt.Errorf("shape must have 6 strings") } - // resolve target intervals + rangeDown := query.RangeDown + rangeUp := query.RangeUp + + baseMIDI := standardMIDI + if len(query.BaseTuning) == nStrings { + baseMIDI = query.BaseTuning + } + var targetIntervals []int defs := GetChordDefinitions() found := false @@ -158,7 +173,6 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin return nil, fmt.Errorf("unknown quality: %s", query.TargetQuality) } - // parse voicing pins type pinInfo struct { midi int pc int @@ -174,7 +188,7 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin continue } if shape[i] == -1 { - continue // muted string, ignore voicing + continue } m, p, err := parsePitchInput(v) if err != nil { @@ -184,7 +198,6 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin } } - // determine candidate roots var roots []int if query.TargetRoot >= 0 && query.TargetRoot < 12 { roots = []int{query.TargetRoot} @@ -195,13 +208,7 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin } } - // chord pitch classes for each root - type tuningResult struct { - tuningPCs []int - tuningMIDI []int - root int - } - var results []tuningResult + var results []candidateTuning seen := make(map[string]bool) for _, root := range roots { @@ -210,7 +217,6 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin chordPCs[(root+iv)%12] = true } - // for each non-muted string, compute candidate open note PCs type stringCandidates struct { pcs []int } @@ -220,12 +226,11 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin for s := 0; s < nStrings; s++ { if shape[s] == -1 { muted[s] = true - candidates[s] = stringCandidates{[]int{standardMIDI[s] % 12}} + candidates[s] = stringCandidates{[]int{baseMIDI[s] % 12}} continue } if pins[s].pc >= 0 { - // pinned: open note = pinned_midi - shape_fret openPC := (pins[s].pc - shape[s] + 120) % 12 candidates[s] = stringCandidates{[]int{openPC}} continue @@ -239,7 +244,6 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin candidates[s] = stringCandidates{cands} } - // cartesian product indices := make([]int, nStrings) sizes := make([]int, nStrings) total := 1 @@ -260,7 +264,6 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin tuningPCs[s] = candidates[s].pcs[indices[s]] } - // check all chord tones present in voicing (non-muted strings) voicedPCs := make(map[int]bool) for s := 0; s < nStrings; s++ { if !muted[s] { @@ -279,7 +282,6 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin continue } - // resolve MIDI pitches with tension check tuningMIDI := make([]int, nStrings) valid := true for s := 0; s < nStrings; s++ { @@ -293,18 +295,16 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin valid = false break } - std := standardMIDI[s] - diff := openMIDI - std - if diff < 0 { - diff = -diff - } - if diff > 7 { + base := baseMIDI[s] + diff := openMIDI - base + if diff < -rangeDown || diff > rangeUp { valid = false break } tuningMIDI[s] = openMIDI } else { - m, ok := closestMIDIInRange(tuningPCs[s], standardMIDI[s]) + base := baseMIDI[s] + m, ok := closestMIDIInRange(tuningPCs[s], base, rangeDown, rangeUp) if !ok { valid = false break @@ -319,16 +319,23 @@ func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]Tunin key := fmt.Sprint(tuningMIDI) if !seen[key] { seen[key] = true - results = append(results, tuningResult{tuningPCs, tuningMIDI, root}) + results = append(results, candidateTuning{tuningMIDI, root}) } } } - deduped := results + return results, nil +} - // build output +func findTuningsForShape(query ShapeQuery, allShapes []ShapeDefinition) ([]TuningCandidate, error) { + candidates, err := findCandidateTunings(query) + if err != nil { + return nil, err + } + + nStrings := len(query.Shape.Frets) var output []TuningCandidate - for _, r := range deduped { + for _, r := range candidates { tuningNames := make([]string, nStrings) for s := 0; s < nStrings; s++ { tuningNames[s] = midiToNoteName(r.tuningMIDI[s]) @@ -516,6 +523,84 @@ func identifyCompanions(tuningMIDI []int, shapes []ShapeDefinition, searchShapeN return companions } +func identifyShape(frets []int, cfg Config) string { + nStrings := len(frets) + if nStrings != len(cfg.Tuning) { + return "" + } + + var soundedPCs []int + for s := 0; s < nStrings; s++ { + if frets[s] == -1 { + continue + } + sem, ok := NoteToSemitone[cfg.Tuning[s]] + if !ok { + continue + } + pc := (sem + frets[s]) % 12 + soundedPCs = append(soundedPCs, pc) + } + if len(soundedPCs) == 0 { + return "" + } + + uniquePCs := uniqueInts(soundedPCs) + + defs := GetChordDefinitions() + type chordDef struct { + quality string + intervals []int + } + var allDefs []chordDef + for _, cat := range defs { + for name, ivs := range cat { + allDefs = append(allDefs, chordDef{name, ivs}) + } + } + + type candidate struct { + root int + quality string + size int + bassIdx int + } + var candidates []candidate + for ri, root := range uniquePCs { + ivSet := make(map[int]bool) + for _, pc := range soundedPCs { + ivSet[(pc-root+12)%12] = true + } + for _, cd := range allDefs { + if len(ivSet) != len(cd.intervals) { + continue + } + ok := true + for _, iv := range cd.intervals { + if !ivSet[iv] { + ok = false + break + } + } + if ok { + candidates = append(candidates, candidate{root, cd.quality, len(cd.intervals), ri}) + } + } + } + + if len(candidates) == 0 { + return "" + } + + best := candidates[0] + for _, c := range candidates[1:] { + if c.size < best.size || (c.size == best.size && c.bassIdx < best.bassIdx) { + best = c + } + } + return SemitoneToNote[best.root] + " " + best.quality +} + func midiToNoteName(midi int) string { pc := midi % 12 octave := midi/12 - 1 diff --git a/shift_test.go b/shift_test.go new file mode 100644 index 0000000..33b906f --- /dev/null +++ b/shift_test.go @@ -0,0 +1,89 @@ +package main + +import ( + "testing" +) + +func TestStandardTuningAppearsForEMajor(t *testing.T) { + query := ShapeQuery{ + Shape: ShapeDefinition{"E major", []int{0, 2, 2, 1, 0, 0}}, + TargetQuality: "major", + TargetRoot: -1, + RangeDown: 7, + RangeUp: 7, + } + + results, err := findCandidateTunings(query) + if err != nil { + t.Fatal(err) + } + + std := []int{40, 45, 50, 55, 59, 64} + found := false + for _, r := range results { + match := true + for s := 0; s < 6; s++ { + if r.tuningMIDI[s] != std[s] { + match = false + break + } + } + if match { + found = true + break + } + } + if !found { + t.Error("Standard tuning not found for E major shape + major quality") + } +} + +func TestBStandardReturnsItself(t *testing.T) { + // B standard = 5 semitones down from E standard + bStd := []int{35, 40, 45, 50, 54, 59} // B1 E2 A2 D3 F#3 B3 + + query := ShapeQuery{ + Shape: ShapeDefinition{"E major", []int{0, 2, 2, 1, 0, 0}}, + TargetQuality: "major", + TargetRoot: -1, + BaseTuning: bStd, + RangeDown: 3, + RangeUp: 3, + } + + results, err := findCandidateTunings(query) + if err != nil { + t.Fatal(err) + } + + t.Logf("Total candidates: %d", len(results)) + found := false + for _, r := range results { + match := true + for s := 0; s < 6; s++ { + if r.tuningMIDI[s] != bStd[s] { + match = false + break + } + } + if match { + found = true + t.Logf("B standard found: root=%d midi=%v", r.root, r.tuningMIDI) + break + } + } + + if !found { + for i, r := range results { + if i >= 10 { + break + } + shifts := make([]int, 6) + for s := 0; s < 6; s++ { + shifts[s] = r.tuningMIDI[s] - bStd[s] + } + t.Logf(" root=%d midi=%v shifts=%v", r.root, r.tuningMIDI, shifts) + } + t.Error("B standard tuning not found when BaseTuning=B standard, range ±3") + } +} diff --git a/static/vectors b/static/vectors index ca6d522..512c578 160000 --- a/static/vectors +++ b/static/vectors @@ -1 +1 @@ -Subproject commit ca6d52252307de75ce9278bb828296780fcfafdc +Subproject commit 512c578c22b2cdc493aca89bb705806afc034359