From b190b9ca2c4c0339a1da97ac84aaa0d50ff23706 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Sun, 13 Apr 2014 13:11:50 +0200 Subject: Add MusicXML to frequencies CSV program --- src/mxml2csv.go | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 src/mxml2csv.go (limited to 'src') diff --git a/src/mxml2csv.go b/src/mxml2csv.go new file mode 100644 index 0000000..94ea5d0 --- /dev/null +++ b/src/mxml2csv.go @@ -0,0 +1,188 @@ +package main + +import ( + "encoding/csv" + "encoding/xml" + "flag" + "fmt" + "io/ioutil" + "math" + "os" +) + +const APP_VERSION = "0.1" + +var versionFlag *bool = flag.Bool("v", false, "Print the version number.") +var inputFlag *string = flag.String("i", "", "The MusicXML file.") +var outputFlag *string = flag.String("o", "", "The CSV file to generate.") +var partFlag *int = flag.Int("p", 0, "The part to process.") + +func init() { + flag.Parse() + + if *versionFlag { + fmt.Println("Version:", APP_VERSION) + return + } + + if *outputFlag == "" { + *outputFlag = *inputFlag + ".csv" + } +} + +/***** MusicXML structure *****/ + +type XMLFile struct { + Parts []Part `xml:"part"` +} + +type Part struct { + Measures []Measure `xml:"measure"` +} + +type Measure struct { + Attributes Attributes `xml:"attributes"` + Directions []Direction `xml:"direction"` + Notes []Note `xml:"note"` +} + +type Attributes struct { + Divisions int `xml:"divisions"` +} + +type Direction struct { + Sound Sound `xml:"sound"` +} + +type Sound struct { + Tempo float64 `xml:"tempo,attr"` +} + +type Note struct { + Pitch Pitch `xml:"pitch"` + Duration int `xml:"duration"` +} + +type Pitch struct { + Step string `xml:"step"` + Alter int `xml:"alter"` + Octave int `xml:"octave"` +} + +/***** Note conversion *****/ +const REFERENCE_PITCH float64 = 440 + +var pitchOffset = map[string]int{ + "C": -9, + "D": -7, + "E": -5, + "F": -4, + "G": -2, + "A": +0, + "B": +2, +} + +func calcFrequency(pitch Pitch) float64 { + if pitch.Step == "" { + return 0 + } + return REFERENCE_PITCH * float64(pitch.Octave-3) * math.Pow(2, float64(pitchOffset[pitch.Step]+pitch.Alter)/12) +} + +/***** Division calculation *****/ + +const DEFAULT_DIVISION = 24 + +func readDivisions(part Part) int { + for _, measure := range part.Measures { + if measure.Attributes.Divisions != 0 { + return measure.Attributes.Divisions + } + } + return DEFAULT_DIVISION +} + +/***** Tempo calculation *****/ + +const DEFAULT_TEMPO = 120 + +func readTempo(part Part) float64 { + for _, measure := range part.Measures { + for _, direction := range measure.Directions { + if direction.Sound.Tempo != 0 { + return direction.Sound.Tempo + } + } + } + return DEFAULT_TEMPO +} + +func calcDuration(noteDuration int, divisions int, tempo float64) float64 { + return 60000 / tempo * float64(noteDuration) / float64(divisions) +} + +/***** Main program *****/ + +func main() { + + // read and parse MusicXML file + + inputFile, err := os.Open(*inputFlag) + if err != nil { + fmt.Println("Error opening MusicXML file:", err) + return + } + defer inputFile.Close() + + data, err := ioutil.ReadAll(inputFile) + if err != nil { + fmt.Println("Error reading MusicXML file:", err) + return + } + + var parsed XMLFile + xml.Unmarshal(data, &parsed) + + fmt.Println("Parsed data:", parsed) + + // create and write to CSV file + + outputFile, err := os.Create(*outputFlag) + if err != nil { + fmt.Println("Error creating CSV file:", err) + return + } + defer outputFile.Close() + + writer := csv.NewWriter(outputFile) + defer writer.Flush() + + if *partFlag >= len(parsed.Parts) { + fmt.Println("Error: part", *partFlag, "does not exist.") + return + } + part := parsed.Parts[*partFlag] + + tempo := readTempo(part) + fmt.Println("Tempo:", tempo) + + divisions := readDivisions(part) + fmt.Println("Divisions:", divisions) + + for _, measure := range part.Measures { + for _, note := range measure.Notes { + pitch := calcFrequency(note.Pitch) + duration := calcDuration(note.Duration, divisions, tempo) + line := []string{fmt.Sprint(pitch), fmt.Sprint(duration)} + fmt.Println(line) + err := writer.Write(line) + if err != nil { + fmt.Println("Error writing in CSV file:", err) + return + } + } + } + + fmt.Println("Done.") + +} -- cgit v1.2.3