Skip to main content
Glama
emicklei

melrōse musical expression player

by emicklei
fraction_map.go3.19 kB
package op import ( "bytes" "fmt" "strconv" "strings" "github.com/emicklei/melrose/core" "github.com/emicklei/melrose/notify" ) type FractionMap struct { fraction core.HasValue target core.Sequenceable } func NewFractionMap(fraction core.HasValue, target core.Sequenceable) FractionMap { return FractionMap{fraction: fraction, target: target} } func (f FractionMap) Storex() string { var b bytes.Buffer fmt.Fprintf(&b, "fractionmap(%s,%s)", core.Storex(f.fraction), core.Storex(f.target)) return b.String() } // S is part of core.Sequenceable func (f FractionMap) S() core.Sequence { frac := core.String(f.fraction) if len(frac) == 0 { notify.Warnf("invalid fraction type detected, %v", f.fraction) return core.EmptySequence } mapping, err := parseIndexFractions(frac) if err != nil { notify.Warnf("invalid fraction mapping detected, %v", err) return core.EmptySequence } if len(mapping) == 0 { return core.EmptySequence } source := f.target.S().Notes target := [][]core.Note{} for _, entry := range mapping { if entry.at <= 0 || entry.at > len(source) { // invalid offset, skip continue } eachGroup := source[entry.at-1] // at is one-based newGroup := []core.Note{} for _, eachNote := range eachGroup { newGroup = append(newGroup, eachNote.WithFraction(float32(1.0/float32(entry.inverseFraction)), entry.dotted)) } target = append(target, newGroup) } return core.Sequence{Notes: target} } type int2fractionAndDotted struct { at int inverseFraction int dotted bool } // 1:1 2:.2 3:8. // 1 .2 8 func parseIndexFractions(s string) (m []int2fractionAndDotted, err error) { entries := strings.Fields(strings.ReplaceAll(s, ",", " ")) for i, each := range entries { var ik = i + 1 var iv int var err error var dotted bool if strings.Contains(each, ":") { kv := strings.Split(each, ":") ik, err = strconv.Atoi(strings.TrimSpace(kv[0])) if err != nil { return m, err } rh := strings.TrimSpace(kv[1]) dotted = strings.Contains(rh, ".") if dotted { rh = strings.Replace(rh, ".", "", -1) } iv, err = strconv.Atoi(rh) if err != nil { return m, err } } else { rh := each dotted = strings.Contains(each, ".") if dotted { rh = strings.Replace(each, ".", "", -1) } iv, err = strconv.Atoi(rh) if err != nil { return m, err } } if ik < 1 { return m, fmt.Errorf("index must be >= 1, got %d", ik) } // TODO move this to Note.ValidateFraction if !core.ContainsInt([]int{1, 2, 4, 8, 16}, iv) { return m, fmt.Errorf("fraction must be in [1,2,4,8,16], got %d", iv) } m = append(m, int2fractionAndDotted{at: ik, inverseFraction: iv, dotted: dotted}) } return } // Return a new FractionMap in which any occurrences of "from" are replaced by "to". func (f FractionMap) Replaced(from, to core.Sequenceable) core.Sequenceable { if core.IsIdenticalTo(f, from) { return to } if core.IsIdenticalTo(f.target, from) { return FractionMap{target: to, fraction: f.fraction} } if rep, ok := f.target.(core.Replaceable); ok { return FractionMap{target: rep.Replaced(from, to), fraction: f.fraction} } return f }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/emicklei/melrose'

If you have feedback or need assistance with the MCP directory API, please join our Discord server