package main import "strings" type IntervalPair struct { Strings [2]int `json:"strings"` Name string `json:"name"` Semitones int `json:"semitones"` } type IntervalData struct { StringGroup []int `json:"string_group"` FretPositions []int `json:"fret_positions"` Intervals []IntervalPair `json:"intervals"` } var intervalNames = [12]string{ "P1", "m2", "M2", "m3", "M3", "P4", "TT", "P5", "m6", "M6", "m7", "M7", } func intervalName(semitones int) string { s := semitones % 12 if s < 0 { s += 12 } return intervalNames[s] } func generateIntervalPairs(cfg Config) []IntervalData { tuning := cfg.Tuning numStrings := len(tuning) var result []IntervalData for size := 2; size <= numStrings; size++ { stringGroups := combinationsOf(numStrings, size) for _, group := range stringGroups { fretProduct := cartesianFrets(size, 6) for _, frets := range fretProduct { var pairs []IntervalPair for i := 0; i < size; i++ { for j := i + 1; j < size; j++ { si, sj := group[i], group[j] fi, fj := frets[i], frets[j] sem := (NoteToSemitone[strings.TrimSpace(tuning[sj])] + fj - NoteToSemitone[strings.TrimSpace(tuning[si])] - fi + 120) % 12 pairs = append(pairs, IntervalPair{ Strings: [2]int{si, sj}, Name: intervalName(sem), Semitones: sem, }) } } result = append(result, IntervalData{ StringGroup: group, FretPositions: frets, Intervals: pairs, }) } } } return result } func combinationsOf(n, k int) [][]int { var results [][]int combo := make([]int, k) var gen func(start, depth int) gen = func(start, depth int) { if depth == k { cp := make([]int, k) copy(cp, combo) results = append(results, cp) return } for i := start; i < n; i++ { combo[depth] = i gen(i+1, depth+1) } } gen(0, 0) return results } func cartesianFrets(size, maxFret int) [][]int { total := 1 for i := 0; i < size; i++ { total *= maxFret } results := make([][]int, total) for i := 0; i < total; i++ { combo := make([]int, size) tmp := i for s := size - 1; s >= 0; s-- { combo[s] = tmp % maxFret tmp /= maxFret } results[i] = combo } return results }