├── LICENSE ├── README ├── demo ├── blank.go └── logging.go ├── midi.go ├── midi_errors.go ├── midi_functions.go ├── midi_functions_test.go ├── midi_interfaces.go ├── midi_lexer_test.go ├── midi_mocks_test.go ├── midi_structs.go ├── midi_test_helpers_test.go ├── midi_values.go ├── mocks.go ├── music.go ├── music_test.go └── states.dot /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Joe Wass 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A library for reading Midi files, written in Go. 2 | 3 | Copyright Joe Wass 2012 4 | joe@afandian.com 5 | http://blog.afandian.com 6 | 7 | Use of this source code is governed by the MIT license which can be found in the LICENSE file. 8 | 9 | This is a library to parse SMF MIDI files. It is fully unit tested. 10 | 11 | To use this library, write a callback object and pass it to the MidiLexer, along with a MIDI file. The Lexer will call events on the callback as they occur in the file. 12 | 13 | To install, run: 14 | go get "github.com/afandian/go-midi" 15 | 16 | To use it in your programs: 17 | import midi "github.com/afandian/go-midi" 18 | 19 | See the examples for how to use. 20 | 21 | References: 22 | 23 | http://faydoc.tripod.com/formats/mid.htm 24 | http://www.music.mcgill.ca/~ich/classes/mumt306/midiformat.pdf 25 | http://www.sonicspot.com/guide/midifiles.html 26 | http://www.indiana.edu/~emusic/etext/MIDI/chapter3_MIDI10.shtml 27 | http://home.roadrunner.com/~jgglatt/tech/midifile.htm -------------------------------------------------------------------------------- /demo/blank.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "midi" 5 | "os" 6 | ) 7 | 8 | type LoggingLexerCallback struct{} 9 | 10 | func (cbk LoggingLexerCallback) Header(header midi.HeaderData) {} 11 | func (cbk LoggingLexerCallback) Track(header midi.ChunkHeader) {} 12 | func (cbk LoggingLexerCallback) Began() {} 13 | func (cbk LoggingLexerCallback) Finished() {} 14 | func (cbk LoggingLexerCallback) ErrorReading() {} 15 | func (cbk LoggingLexerCallback) ErrorOpeningFile() {} 16 | func (cbk LoggingLexerCallback) Tempo(bpm uint32, microsecondsPerCrotchet uint32, time uint32) { 17 | 18 | } 19 | func (cbk LoggingLexerCallback) NoteOff(channel uint8, pitch uint8, velocity uint8, time uint32) { 20 | 21 | } 22 | func (cbk LoggingLexerCallback) NoteOn(channel uint8, pitch uint8, velocity uint8, time uint32) { 23 | } 24 | func (cbk LoggingLexerCallback) PolyphonicAfterTouch(channel uint8, pitch uint8, pressure uint8, time uint32) { 25 | 26 | } 27 | func (cbk LoggingLexerCallback) ControlChange(channel uint8, controller uint8, value uint8, time uint32) { 28 | 29 | } 30 | func (cbk LoggingLexerCallback) ProgramChange(channel uint8, program uint8, time uint32) { 31 | 32 | } 33 | func (cbk LoggingLexerCallback) ChannelAfterTouch(channel uint8, value uint8, time uint32) { 34 | 35 | } 36 | func (cbk LoggingLexerCallback) PitchWheel(channel uint8, value int16, absValue uint16, time uint32) { 37 | 38 | } 39 | func (cbk LoggingLexerCallback) TimeCodeQuarter(messageType uint8, values uint8, time uint32) { 40 | 41 | } 42 | func (cbk LoggingLexerCallback) SongPositionPointer(beats uint16, time uint32) { 43 | 44 | } 45 | func (cbk LoggingLexerCallback) SongSelect(song uint8, time uint32) { 46 | 47 | } 48 | func (cbk LoggingLexerCallback) Undefined1(time uint32) {} 49 | func (cbk LoggingLexerCallback) Undefined2(time uint32) {} 50 | func (cbk LoggingLexerCallback) TuneRequest(time uint32) {} 51 | func (cbk LoggingLexerCallback) TimingClock(time uint32) {} 52 | func (cbk LoggingLexerCallback) Undefined3(time uint32) {} 53 | func (cbk LoggingLexerCallback) Start(time uint32) {} 54 | func (cbk LoggingLexerCallback) Continue(time uint32) {} 55 | func (cbk LoggingLexerCallback) Stop(time uint32) {} 56 | func (cbk LoggingLexerCallback) Undefined4(time uint32) {} 57 | func (cbk LoggingLexerCallback) ActiveSensing(time uint32) {} 58 | func (cbk LoggingLexerCallback) Reset(time uint32) {} 59 | func (cbk LoggingLexerCallback) Done(time uint32) {} 60 | func (cbk LoggingLexerCallback) SequenceNumber(channel uint8, number uint16, numberGiven bool, time uint32) { 61 | 62 | } 63 | func (cbk LoggingLexerCallback) Text(channel uint8, text string, time uint32) { 64 | 65 | } 66 | func (cbk LoggingLexerCallback) CopyrightText(channel uint8, text string, time uint32) { 67 | 68 | } 69 | func (cbk LoggingLexerCallback) SequenceName(channel uint8, text string, time uint32) { 70 | 71 | } 72 | func (cbk LoggingLexerCallback) TrackInstrumentName(channel uint8, text string, time uint32) { 73 | 74 | } 75 | func (cbk LoggingLexerCallback) LyricText(channel uint8, text string, time uint32) { 76 | 77 | } 78 | func (cbk LoggingLexerCallback) MarkerText(channel uint8, text string, time uint32) { 79 | 80 | } 81 | func (cbk LoggingLexerCallback) CuePointText(channel uint8, text string, time uint32) { 82 | 83 | } 84 | func (cbk LoggingLexerCallback) EndOfTrack(channel uint8, time uint32) { 85 | 86 | } 87 | func (cbk LoggingLexerCallback) TimeSignature(numerator uint8, denomenator uint8, clocksPerClick uint8, demiSemiQuaverPerQuarter uint8, time uint32) { 88 | 89 | } 90 | func (cbk LoggingLexerCallback) KeySignature(key midi.ScaleDegree, mode midi.KeySignatureMode, sharpsOrFlats int8) { 91 | 92 | } 93 | 94 | func main() { 95 | 96 | var callback LoggingLexerCallback 97 | // loc := "/Users/joe/Downloads/102891.mid" 98 | // loc := "/Users/joe/Downloads/HOTELCAL.MID" 99 | 100 | loc := "/Users/joe/personal/backup/home/Websites/close site5/ttf/public_html_old-28-apr-2010/temporaryImages/1018.mid" 101 | var file, err = os.Open(loc) 102 | if err != nil { 103 | return 104 | } 105 | lexer := midi.NewMidiLexer(file, callback) 106 | lexer.Lex() 107 | } 108 | -------------------------------------------------------------------------------- /demo/logging.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "midi" 6 | "os" 7 | ) 8 | 9 | type LoggingLexerCallback struct{} 10 | 11 | func (cbk LoggingLexerCallback) Header(header midi.HeaderData) { fmt.Println("Header", header) } 12 | func (cbk LoggingLexerCallback) Track(header midi.ChunkHeader) { fmt.Println("Track", header) } 13 | func (cbk LoggingLexerCallback) Began() { fmt.Println("Began") } 14 | func (cbk LoggingLexerCallback) Finished() { fmt.Println("Finished") } 15 | func (cbk LoggingLexerCallback) ErrorReading() { fmt.Println("ErrorReading") } 16 | func (cbk LoggingLexerCallback) ErrorOpeningFile() { fmt.Println("ErrorOpeningFile") } 17 | func (cbk LoggingLexerCallback) Tempo(bpm uint32, microsecondsPerCrotchet uint32, time uint32) { 18 | fmt.Println("tempo", bpm, "bpm") 19 | } 20 | func (cbk LoggingLexerCallback) NoteOff(channel uint8, pitch uint8, velocity uint8, time uint32) { 21 | // fmt.Println("NoteOff", channel, pitch, velocity, time) 22 | 23 | for i := uint32(0); i < time/100; i++ { 24 | fmt.Println("") 25 | } 26 | 27 | for i := uint8(0); i < pitch; i++ { 28 | fmt.Print(" ") 29 | } 30 | fmt.Println("x") 31 | 32 | } 33 | func (cbk LoggingLexerCallback) NoteOn(channel uint8, pitch uint8, velocity uint8, time uint32) { 34 | // fmt.Println("NoteOn", channel, pitch, velocity, time) 35 | 36 | for i := uint32(0); i < time/100; i++ { 37 | fmt.Println("") 38 | } 39 | 40 | for i := uint8(0); i < pitch; i++ { 41 | fmt.Print(" ") 42 | } 43 | fmt.Println("*") 44 | } 45 | func (cbk LoggingLexerCallback) PolyphonicAfterTouch(channel uint8, pitch uint8, pressure uint8, time uint32) { 46 | fmt.Println("PolyphonicAfterTouch", channel, pitch, pressure, time) 47 | } 48 | func (cbk LoggingLexerCallback) ControlChange(channel uint8, controller uint8, value uint8, time uint32) { 49 | fmt.Println("ControlChange", channel, controller, value, time) 50 | } 51 | func (cbk LoggingLexerCallback) ProgramChange(channel uint8, program uint8, time uint32) { 52 | fmt.Println("ProgramChange", channel, program, time) 53 | } 54 | func (cbk LoggingLexerCallback) ChannelAfterTouch(channel uint8, value uint8, time uint32) { 55 | fmt.Println("ChannelAfterTouch", channel, value, time) 56 | } 57 | func (cbk LoggingLexerCallback) PitchWheel(channel uint8, value int16, absValue uint16, time uint32) { 58 | fmt.Println("PitchWheel", channel, value, absValue, time) 59 | } 60 | func (cbk LoggingLexerCallback) TimeCodeQuarter(messageType uint8, values uint8, time uint32) { 61 | fmt.Println("TimeCodeQuarter", messageType, values, time) 62 | } 63 | func (cbk LoggingLexerCallback) SongPositionPointer(beats uint16, time uint32) { 64 | fmt.Println("SongPositionPointer", beats, time) 65 | } 66 | func (cbk LoggingLexerCallback) SongSelect(song uint8, time uint32) { 67 | fmt.Println("SongSelect", song, time) 68 | } 69 | func (cbk LoggingLexerCallback) Undefined1(time uint32) { fmt.Println("Undefined1", time) } 70 | func (cbk LoggingLexerCallback) Undefined2(time uint32) { fmt.Println("Undefined2", time) } 71 | func (cbk LoggingLexerCallback) TuneRequest(time uint32) { fmt.Println("TuneRequest", time) } 72 | func (cbk LoggingLexerCallback) TimingClock(time uint32) { fmt.Println("TimingClock", time) } 73 | func (cbk LoggingLexerCallback) Undefined3(time uint32) { fmt.Println("Undefined3", time) } 74 | func (cbk LoggingLexerCallback) Start(time uint32) { fmt.Println("Start", time) } 75 | func (cbk LoggingLexerCallback) Continue(time uint32) { fmt.Println("Continue", time) } 76 | func (cbk LoggingLexerCallback) Stop(time uint32) { fmt.Println("Stop", time) } 77 | func (cbk LoggingLexerCallback) Undefined4(time uint32) { fmt.Println("Undefined4", time) } 78 | func (cbk LoggingLexerCallback) ActiveSensing(time uint32) { fmt.Println("ActiveSensing", time) } 79 | func (cbk LoggingLexerCallback) Reset(time uint32) { fmt.Println("Reset", time) } 80 | func (cbk LoggingLexerCallback) Done(time uint32) { fmt.Println("Done", time) } 81 | func (cbk LoggingLexerCallback) SequenceNumber(channel uint8, number uint16, numberGiven bool, time uint32) { 82 | fmt.Println("SequenceNumber", channel, number, numberGiven, time) 83 | } 84 | func (cbk LoggingLexerCallback) Text(channel uint8, text string, time uint32) { 85 | fmt.Println("Text", channel, text, time) 86 | } 87 | func (cbk LoggingLexerCallback) CopyrightText(channel uint8, text string, time uint32) { 88 | fmt.Println("CopyrightText", channel, text, time) 89 | } 90 | func (cbk LoggingLexerCallback) SequenceName(channel uint8, text string, time uint32) { 91 | fmt.Println("SequenceName", channel, text, time) 92 | } 93 | func (cbk LoggingLexerCallback) TrackInstrumentName(channel uint8, text string, time uint32) { 94 | fmt.Println("TrackInstrumentName", channel, text, time) 95 | } 96 | func (cbk LoggingLexerCallback) LyricText(channel uint8, text string, time uint32) { 97 | fmt.Println("LyricText", channel, text, time) 98 | } 99 | func (cbk LoggingLexerCallback) MarkerText(channel uint8, text string, time uint32) { 100 | fmt.Println("MarkerText", channel, text, time) 101 | } 102 | func (cbk LoggingLexerCallback) CuePointText(channel uint8, text string, time uint32) { 103 | fmt.Println("CuePointText", channel, text, time) 104 | } 105 | func (cbk LoggingLexerCallback) EndOfTrack(channel uint8, time uint32) { 106 | fmt.Println("EndOfTrack", channel, time) 107 | } 108 | func (cbk LoggingLexerCallback) TimeSignature(numerator uint8, denomenator uint8, clocksPerClick uint8, demiSemiQuaverPerQuarter uint8, time uint32) { 109 | fmt.Println("TimeSignature", numerator, denomenator, clocksPerClick, demiSemiQuaverPerQuarter, time) 110 | } 111 | func (cbk LoggingLexerCallback) KeySignature(key midi.ScaleDegree, mode midi.KeySignatureMode, sharpsOrFlats int8) { 112 | fmt.Println("KeySignature", key, mode, sharpsOrFlats) 113 | } 114 | 115 | func main() { 116 | fmt.Println("Logging Midi") 117 | var callback LoggingLexerCallback 118 | // var file, err = os.Open("/Users/joe/Downloads/102891.mid") 119 | var file, err = os.Open("/Users/joe/Downloads/HOTELCAL.MID") 120 | if err != nil { 121 | fmt.Println(err) 122 | return 123 | } 124 | lexer := midi.NewMidiLexer(file, callback) 125 | lexer.Lex() 126 | } 127 | -------------------------------------------------------------------------------- /midi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * The main file. 12 | * This contains the lexer, which does the job of scanning through the file. 13 | */ 14 | 15 | package midi 16 | 17 | import ( 18 | "io" 19 | ) 20 | 21 | // State of the MidiLexerCallback. 22 | const ( 23 | // At the start of the MIDI file. 24 | // Expect SMF Header chunk. 25 | ExpectHeader = iota 26 | 27 | // Expect a chunk. Any kind of chunk. Except MThd. 28 | // But really, anything other than MTrk would be weird. 29 | ExpectChunk = iota 30 | 31 | // We're in a Track, expect a track event. 32 | ExpectTrackEvent = iota 33 | 34 | // This has to happen sooner or later. 35 | Done = iota 36 | ) 37 | 38 | // MidiLexer is a Standard Midi File Lexer. 39 | // Pass this a ReadSeeker to a MIDI file and a callback that conforms to MidiLexerCallback 40 | // and it'll run over the file, calling events on the callback. 41 | type MidiLexer struct { 42 | callback MidiLexerCallback 43 | input io.ReadSeeker 44 | 45 | // State of the parser, as per the above constants. 46 | state int 47 | 48 | // The location of the next chunk header that we expect to find as an offset from 49 | // most recent Chunk header. 50 | nextChunkHeader int64 51 | } 52 | 53 | // Construct a new MidiLexer 54 | func NewMidiLexer(input io.ReadSeeker, callback MidiLexerCallback) *MidiLexer { 55 | return &MidiLexer{callback: callback, input: input, state: ExpectHeader} 56 | } 57 | 58 | // Lex starts the MidiLexer running. 59 | func (lexer *MidiLexer) Lex() error { 60 | if lexer.callback == nil { 61 | return NoCallback 62 | } 63 | 64 | if lexer.input == nil { 65 | return NoReadSeeker 66 | } 67 | 68 | var finished bool = false 69 | var err error 70 | 71 | for { 72 | finished, err = lexer.next() 73 | 74 | if err != nil { 75 | return err 76 | } 77 | 78 | if finished == true { 79 | return nil 80 | } 81 | } 82 | 83 | return nil 84 | } 85 | 86 | // next lexes the next item, calling appropriate callbacks. 87 | // Finished only set true when finished correctly. 88 | func (lexer *MidiLexer) next() (finished bool, err error) { 89 | 90 | // Default for return values. 91 | err = nil 92 | finished = false 93 | 94 | // The position in the file before the next lexing event happens. 95 | // Useful in some cases 96 | var currentPosition int64 97 | currentPosition, err = lexer.input.Seek(0, 1) 98 | 99 | if err != nil { 100 | return 101 | } 102 | 103 | // See comments for state values above. 104 | switch lexer.state { 105 | case ExpectHeader: 106 | { 107 | //fmt.Println("ExpectHeader") 108 | 109 | var chunkHeader ChunkHeader 110 | chunkHeader, err = parseChunkHeader(lexer.input) 111 | if chunkHeader.ChunkType != "MThd" { 112 | err = ExpectedMthd 113 | 114 | //fmt.Println("ChunkHeader error ", err) 115 | return 116 | } 117 | 118 | var header HeaderData 119 | header, err = parseHeaderData(lexer.input) 120 | 121 | if err != nil { 122 | //fmt.Println("HeaderData error ", err) 123 | return 124 | } 125 | 126 | lexer.callback.Began() 127 | 128 | lexer.callback.Header(header) 129 | 130 | lexer.state = ExpectChunk 131 | 132 | return 133 | } 134 | 135 | case ExpectChunk: 136 | { 137 | //fmt.Println("ExpectChunk") 138 | 139 | var chunkHeader ChunkHeader 140 | chunkHeader, err = parseChunkHeader(lexer.input) 141 | 142 | //fmt.Println("Got chunk header", chunkHeader) 143 | 144 | if err != nil { 145 | // If we expect a chunk and we hit the end of the file, that's not so unexpected after all. 146 | // The file has to end some time, and this is the correct boundary upon which to end it. 147 | if err == UnexpectedEndOfFile { 148 | lexer.state = Done 149 | 150 | // TODO TEST 151 | lexer.callback.Finished() 152 | 153 | finished = true 154 | err = nil 155 | return 156 | } 157 | 158 | //fmt.Println("Chunk header error ", err) 159 | return 160 | } 161 | 162 | lexer.callback.Track(chunkHeader) 163 | lexer.nextChunkHeader = int64(chunkHeader.Length) + currentPosition 164 | 165 | // If the header is of an unknown type, skip over it. 166 | if chunkHeader.ChunkType != "MTrk" { 167 | lexer.input.Seek(lexer.nextChunkHeader, 1) 168 | 169 | // Then we expect another chunk. 170 | lexer.state = ExpectChunk 171 | lexer.nextChunkHeader = 0 172 | } else { 173 | // We have a MTrk 174 | lexer.state = ExpectTrackEvent 175 | } 176 | 177 | return 178 | } 179 | 180 | case ExpectTrackEvent: 181 | { 182 | //fmt.Println("ExpectTrackEvent") 183 | 184 | // Removed because there is an event to say 'end of chunk'. 185 | // TODO: investigate. Could put this back for error cases. 186 | // // If we're at the end of the chunk, change the state. 187 | // if lexer.nextChunkHeader != 0 { 188 | 189 | // // The chunk should end exactly on the chunk boundary, really. 190 | // if currentPosition == lexer.nextChunkHeader { 191 | // lexer.state = ExpectChunk 192 | // return false, nil 193 | // } else if currentPosition > lexer.nextChunkHeader { 194 | // //fmt.Println("Chunk end error ", err) 195 | // return false, BadSizeChunk 196 | // } 197 | // } 198 | 199 | // Time Delta 200 | var time uint32 201 | time, err = parseVarLength(lexer.input) 202 | if err != nil { 203 | //fmt.Println("Time delta error ", err) 204 | return 205 | } 206 | 207 | // Message type, Message Channel 208 | var mType, channel uint8 209 | mType, channel, err = readStatusByte(lexer.input) 210 | 211 | //fmt.Println("Track Event Type ", mType) 212 | 213 | switch mType { 214 | // NoteOff 215 | case 0x8: 216 | { 217 | var pitch, velocity uint8 218 | pitch, velocity, err = parseTwoUint7(lexer.input) 219 | 220 | if err != nil { 221 | //fmt.Println("NoteOff error ", err) 222 | return 223 | } 224 | 225 | lexer.callback.NoteOff(channel, pitch, velocity, time) 226 | } 227 | 228 | // NoteOn 229 | case 0x9: 230 | { 231 | var pitch, velocity uint8 232 | pitch, velocity, err = parseTwoUint7(lexer.input) 233 | 234 | if err != nil { 235 | //fmt.Println("NoteOn error ", err) 236 | return 237 | } 238 | 239 | lexer.callback.NoteOn(channel, pitch, velocity, time) 240 | } 241 | 242 | // Polyphonic Key Pressure 243 | case 0xA: 244 | { 245 | var pitch, pressure uint8 246 | pitch, pressure, err = parseTwoUint7(lexer.input) 247 | 248 | if err != nil { 249 | return 250 | } 251 | 252 | lexer.callback.PolyphonicAfterTouch(channel, pitch, pressure, time) 253 | } 254 | 255 | // Control Change or Channel Mode Message 256 | case 0xB: 257 | { 258 | var controller, value uint8 259 | controller, value, err = parseTwoUint7(lexer.input) 260 | 261 | if err != nil { 262 | return 263 | } 264 | 265 | // TODO split this into ChannelMode for values [120, 127]? 266 | // TODO implement separate callbacks for each type of: 267 | // - All sound off 268 | // - Reset all controllers 269 | // - Local control 270 | // - All notes off 271 | // Only if required. http://www.midi.org/techspecs/midimessages.php 272 | 273 | // TODO TEST 274 | lexer.callback.ControlChange(channel, controller, value, time) 275 | return 276 | } 277 | 278 | // Program Change 279 | case 0xC: 280 | { 281 | var program uint8 282 | program, err = parseUint7(lexer.input) 283 | 284 | if err != nil { 285 | return 286 | } 287 | 288 | lexer.callback.ProgramChange(channel, program, time) 289 | return 290 | } 291 | 292 | // Channel Pressure 293 | case 0xD: 294 | { 295 | var value uint8 296 | value, err = parseUint7(lexer.input) 297 | 298 | if err != nil { 299 | return 300 | } 301 | 302 | lexer.callback.ChannelAfterTouch(channel, value, time) 303 | } 304 | 305 | // Pitch Wheel 306 | case 0xE: 307 | { 308 | // The value is a signed int (relative to centre), and absoluteValue is the actual value in the file. 309 | var value int16 310 | var absoluteValue uint16 311 | value, absoluteValue, err = parsePitchWheelValue(lexer.input) 312 | 313 | if err != nil { 314 | return 315 | } 316 | 317 | lexer.callback.PitchWheel(channel, value, absoluteValue, time) 318 | } 319 | 320 | // System Common and System Real-Time / Meta 321 | case 0xF: 322 | { 323 | // The 4-bit nibble called 'channel' isn't actually the channel in this case. 324 | switch channel { 325 | // Meta-events 326 | case 0xF: 327 | { 328 | var command uint8 329 | command, err = parseUint8(lexer.input) 330 | 331 | if err != nil { 332 | return 333 | } 334 | 335 | //fmt.Println("SystemCommon/RealTime command:", command) 336 | 337 | // TODO: If every one of these takes a length, then take this outside. 338 | // Will make for more more robust unknown types. 339 | switch command { 340 | 341 | // Sequence number 342 | case 0x00: 343 | { 344 | //fmt.Println("seq no") 345 | var length uint8 346 | length, err = parseUint8(lexer.input) 347 | 348 | if err != nil { 349 | return 350 | } 351 | 352 | // Zero length sequences allowed according to http://home.roadrunner.com/~jgglatt/tech/midifile/seq.htm 353 | if length == 0 { 354 | lexer.callback.SequenceNumber(channel, 0, false, time) 355 | 356 | return 357 | } 358 | 359 | // Otherwise length will be 2 to hold the uint16. 360 | var sequenceNumber uint16 361 | sequenceNumber, err = parseUint16(lexer.input) 362 | 363 | if err != nil { 364 | return 365 | } 366 | 367 | lexer.callback.SequenceNumber(channel, sequenceNumber, true, time) 368 | 369 | return 370 | } 371 | 372 | // Text event 373 | case 0x01: 374 | { 375 | //fmt.Println("Text") 376 | var text string 377 | text, err = parseText(lexer.input) 378 | //fmt.Println("text value", text, err) 379 | if err != nil { 380 | return 381 | } 382 | 383 | lexer.callback.Text(channel, text, time) 384 | 385 | return 386 | } 387 | 388 | // Copyright text event 389 | case 0x02: 390 | { 391 | //fmt.Println("Copyright") 392 | var text string 393 | text, err = parseText(lexer.input) 394 | 395 | if err != nil { 396 | return 397 | } 398 | 399 | lexer.callback.CopyrightText(channel, text, time) 400 | 401 | return 402 | } 403 | 404 | // Sequence or track name 405 | case 0x03: 406 | { 407 | var text string 408 | text, err = parseText(lexer.input) 409 | 410 | if err != nil { 411 | return 412 | } 413 | 414 | lexer.callback.SequenceName(channel, text, time) 415 | 416 | return 417 | 418 | } 419 | 420 | // Track instrument name 421 | case 0x04: 422 | { 423 | var text string 424 | text, err = parseText(lexer.input) 425 | 426 | if err != nil { 427 | return 428 | } 429 | 430 | lexer.callback.TrackInstrumentName(channel, text, time) 431 | 432 | return 433 | 434 | } 435 | 436 | // Lyric text 437 | case 0x05: 438 | { 439 | var text string 440 | text, err = parseText(lexer.input) 441 | 442 | if err != nil { 443 | return 444 | } 445 | 446 | lexer.callback.LyricText(channel, text, time) 447 | 448 | return 449 | } 450 | 451 | // Marker text 452 | case 0x06: 453 | { 454 | var text string 455 | text, err = parseText(lexer.input) 456 | 457 | if err != nil { 458 | return 459 | } 460 | 461 | lexer.callback.MarkerText(channel, text, time) 462 | 463 | return 464 | } 465 | 466 | // Cue point text 467 | case 0x07: 468 | { 469 | var text string 470 | text, err = parseText(lexer.input) 471 | 472 | if err != nil { 473 | return 474 | } 475 | 476 | lexer.callback.CuePointText(channel, text, time) 477 | 478 | return 479 | } 480 | 481 | case 0x20: 482 | { 483 | // Obsolete 'MIDI Channel' 484 | //fmt.Println("MIDI Channel obsolete") 485 | var length uint32 486 | length, err = parseVarLength(lexer.input) 487 | 488 | if err != nil { 489 | return 490 | } 491 | 492 | if length != 1 { 493 | err = UnexpectedEventLengthError{"Midi Channel Event expected length 1"} 494 | return 495 | } 496 | 497 | // This is the channel value. 498 | // Just forget this one. 499 | _, err = parseUint8(lexer.input) 500 | 501 | if err != nil { 502 | return 503 | } 504 | } 505 | 506 | case 0x21: 507 | { 508 | // Obsolete 'MIDI Port' 509 | //fmt.Println("MIDI PORT obsolete") 510 | var length uint32 511 | length, err = parseVarLength(lexer.input) 512 | 513 | if err != nil { 514 | return 515 | } 516 | 517 | if length != 1 { 518 | err = UnexpectedEventLengthError{"MIDI Port Event expected length 1"} 519 | return 520 | } 521 | 522 | // This is the port value. 523 | // Just forget this one. 524 | _, err = parseUint8(lexer.input) 525 | 526 | if err != nil { 527 | return 528 | } 529 | } 530 | 531 | // End of track 532 | case 0x2F: 533 | { 534 | var length uint32 535 | length, err = parseVarLength(lexer.input) 536 | 537 | if err != nil { 538 | return 539 | } 540 | 541 | if length != 0 { 542 | err = UnexpectedEventLengthError{"EndOfTrack expected length 0"} 543 | return 544 | } 545 | 546 | lexer.callback.EndOfTrack(channel, time) 547 | 548 | // Expect the next chunk event. 549 | lexer.state = ExpectChunk 550 | 551 | return false, nil 552 | } 553 | 554 | // Set tempo 555 | case 0x51: 556 | { 557 | // TODO TEST 558 | 559 | var length uint32 560 | length, err = parseVarLength(lexer.input) 561 | 562 | if err != nil { 563 | return 564 | } 565 | 566 | if length != 3 { 567 | err = UnexpectedEventLengthError{"Tempo expected length 3"} 568 | return 569 | } 570 | 571 | var microsecondsPerCrotchet uint32 572 | microsecondsPerCrotchet, err = parseUint24(lexer.input) 573 | 574 | if err != nil { 575 | return 576 | } 577 | 578 | // Also beats per minute 579 | var bpm uint32 580 | bpm = 60000000 / microsecondsPerCrotchet 581 | 582 | lexer.callback.Tempo(bpm, microsecondsPerCrotchet, time) 583 | } 584 | 585 | // Time signature 586 | case 0x58: 587 | { 588 | var length uint32 589 | length, err = parseVarLength(lexer.input) 590 | 591 | if err != nil { 592 | return 593 | } 594 | 595 | if length != 4 { 596 | err = UnexpectedEventLengthError{"TimeSignature expected length 4"} 597 | return 598 | } 599 | 600 | // TODO TEST 601 | var numerator uint8 602 | numerator, err = parseUint8(lexer.input) 603 | 604 | if err != nil { 605 | return 606 | } 607 | 608 | var denomenator uint8 609 | denomenator, err = parseUint8(lexer.input) 610 | 611 | if err != nil { 612 | return 613 | } 614 | 615 | var clocksPerClick uint8 616 | clocksPerClick, err = parseUint8(lexer.input) 617 | 618 | if err != nil { 619 | return 620 | } 621 | 622 | var demiSemiQuaverPerQuarter uint8 623 | demiSemiQuaverPerQuarter, err = parseUint8(lexer.input) 624 | 625 | if err != nil { 626 | return 627 | } 628 | 629 | //fmt.Println("TimeSignature event", numerator, denomenator, clocksPerClick, demiSemiQuaverPerQuarter, time) 630 | 631 | lexer.callback.TimeSignature(numerator, denomenator, clocksPerClick, demiSemiQuaverPerQuarter, time) 632 | 633 | return false, nil 634 | } 635 | 636 | // Key signature 637 | case 0x59: 638 | { 639 | // TODO TEST 640 | var length uint32 641 | var sharpsOrFlats int8 642 | var mode uint8 643 | 644 | length, err = parseVarLength(lexer.input) 645 | 646 | if err != nil { 647 | return 648 | } 649 | 650 | if length != 2 { 651 | err = UnexpectedEventLengthError{"KeySignature expected length 2"} 652 | return 653 | } 654 | 655 | // Signed int, positive is sharps, negative is flats. 656 | sharpsOrFlats, err = parseInt8(lexer.input) 657 | 658 | if err != nil { 659 | return 660 | } 661 | 662 | // Mode is Major or Minor. 663 | mode, err = parseUint8(lexer.input) 664 | 665 | if err != nil { 666 | return 667 | } 668 | 669 | key, resultMode := keySignatureFromSharpsOrFlats(sharpsOrFlats, mode) 670 | 671 | lexer.callback.KeySignature(key, resultMode, sharpsOrFlats) 672 | } 673 | 674 | // Sequencer specific info 675 | case 0x7F: 676 | { 677 | //fmt.Println("0x7F") 678 | } 679 | 680 | // Timing clock 681 | case 0xF8: 682 | { 683 | //fmt.Println("0xF8") 684 | } 685 | 686 | // Start current sequence 687 | case 0xFA: 688 | { 689 | //fmt.Println("0xFA") 690 | } 691 | 692 | // Continue stopped sequence where left off 693 | case 0xFB: 694 | { 695 | //fmt.Println("0xFB") 696 | } 697 | 698 | // Stop sequence 699 | case 0xFc: 700 | { 701 | //fmt.Println("0xFc") 702 | } 703 | default: 704 | //fmt.Println("Unrecognised meta command", command) 705 | } 706 | 707 | } 708 | 709 | default: 710 | //fmt.Println("Unrecognised message type", mType) 711 | } 712 | 713 | // 714 | } 715 | 716 | // This covers all cases. 717 | 718 | // Now we need to see if we're at the end of a Track Data chunk. 719 | default: 720 | { 721 | var length uint32 722 | length, err = parseVarLength(lexer.input) 723 | 724 | if err != nil { 725 | return 726 | } 727 | 728 | //fmt.Println("Type Unrecognised", mType, "length", length) 729 | 730 | // Read length of chunk 731 | for i := uint32(0); i < length; i++ { 732 | _, err = parseUint8(lexer.input) 733 | 734 | if err != nil { 735 | return 736 | } 737 | } 738 | } 739 | } 740 | 741 | } 742 | 743 | case Done: 744 | { 745 | // The event that raised this will already have returned false to say it's stopped ticking. 746 | // Just keep returning false. 747 | finished = true 748 | return 749 | } 750 | } 751 | 752 | return 753 | } 754 | -------------------------------------------------------------------------------- /midi_errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Errors that may arise during parsing. 12 | * The LexerCallback may recieve any of these. 13 | */ 14 | 15 | package midi 16 | 17 | // A load of Errors and single values for convenience. 18 | 19 | type UnexpectedEventLengthError struct { 20 | message string 21 | } 22 | 23 | func (e UnexpectedEventLengthError) Error() string { 24 | return e.message 25 | } 26 | 27 | type NoCallbackError struct{} 28 | 29 | func (e NoCallbackError) Error() string { 30 | return "No callback supplied" 31 | } 32 | 33 | var NoCallback = NoCallbackError{} 34 | 35 | type NoReadSeekerError struct{} 36 | 37 | func (e NoReadSeekerError) Error() string { 38 | return "No ReadSeeker input supplied" 39 | } 40 | 41 | var NoReadSeeker = NoReadSeekerError{} 42 | 43 | type VarLengthNotFoundError struct{} 44 | 45 | func (e VarLengthNotFoundError) Error() string { 46 | return "Variable length value not found where expected." 47 | } 48 | 49 | type UnexpectedEndOfFileError struct{} 50 | 51 | func (e UnexpectedEndOfFileError) Error() string { 52 | return "Unexpected End of File found." 53 | } 54 | 55 | var UnexpectedEndOfFile = UnexpectedEndOfFileError{} 56 | 57 | type UnsupportedSmfFormatError struct{} 58 | 59 | func (e UnsupportedSmfFormatError) Error() string { 60 | return "The SMF format was not expected." 61 | } 62 | 63 | var UnsupportedSmfFormat = UnsupportedSmfFormatError{} 64 | 65 | type ExpectedMthdError struct{} 66 | 67 | func (e ExpectedMthdError) Error() string { 68 | return "Expected SMF Midi header." 69 | } 70 | 71 | var ExpectedMthd = ExpectedMthdError{} 72 | 73 | type BadSizeChunkError struct{} 74 | 75 | func (e BadSizeChunkError) Error() string { 76 | return "Chunk was an unexpected size." 77 | } 78 | 79 | var BadSizeChunk = BadSizeChunkError{} 80 | -------------------------------------------------------------------------------- /midi_functions.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Functions for reading actual MIDI data in the various formats that crop up. 12 | */ 13 | 14 | package midi 15 | 16 | import ( 17 | // "fmt" 18 | "io" 19 | ) 20 | 21 | // parseUint32 parse a 4-byte 32 bit integer from a ReadSeeker. 22 | // It returns the 32-bit value and an error. 23 | func parseUint32(reader io.ReadSeeker) (uint32, error) { 24 | var buffer []byte = make([]byte, 4) 25 | num, err := reader.Read(buffer) 26 | 27 | // If we couldn't read 4 bytes, that's a problem. 28 | if num != 4 { 29 | return 0, UnexpectedEndOfFile 30 | } 31 | 32 | // If there was some other problem, that's also a problem. 33 | if err != nil { 34 | return 0, err 35 | } 36 | 37 | var value uint32 = 0x00 38 | value |= uint32(buffer[3]) << 0 39 | value |= uint32(buffer[2]) << 8 40 | value |= uint32(buffer[1]) << 16 41 | value |= uint32(buffer[0]) << 24 42 | 43 | return value, nil 44 | } 45 | 46 | // parseUint24 parse a 3-byte 24 bit integer from a ReadSeeker. 47 | // It returns the 32-bit value and an error. 48 | // TODO TEST 49 | func parseUint24(reader io.ReadSeeker) (uint32, error) { 50 | var buffer []byte = make([]byte, 3) 51 | num, err := reader.Read(buffer) 52 | 53 | // If we couldn't read 3 bytes, that's a problem. 54 | if num != 3 { 55 | return 0, UnexpectedEndOfFile 56 | } 57 | 58 | // If there was some other problem, that's also a problem. 59 | if err != nil { 60 | return 0, err 61 | } 62 | 63 | var value uint32 = 0x00 64 | value |= uint32(buffer[2]) << 0 65 | value |= uint32(buffer[1]) << 8 66 | value |= uint32(buffer[0]) << 16 67 | 68 | return value, nil 69 | } 70 | 71 | // parseUint16 parses a 2-byte 16 bit integer from a ReadSeeker. 72 | // It returns the 16-bit value and an error. 73 | func parseUint16(reader io.ReadSeeker) (uint16, error) { 74 | 75 | var buffer []byte = make([]byte, 2) 76 | num, err := reader.Read(buffer) 77 | 78 | // If we couldn't read 2 bytes, that's a problem. 79 | if num != 2 { 80 | return 0, UnexpectedEndOfFile 81 | } 82 | 83 | // If there was some other problem, that's also a problem. 84 | if err != nil { 85 | return 0, err 86 | } 87 | 88 | var value uint16 = 0x00 89 | value |= uint16(buffer[1]) << 0 90 | value |= uint16(buffer[0]) << 8 91 | 92 | return value, nil 93 | } 94 | 95 | // parseUint7 parses a 7-bit bit integer from a ReadSeeker, ignoring the high bit. 96 | // It returns the 8-bit value and an error. 97 | func parseUint7(reader io.ReadSeeker) (uint8, error) { 98 | 99 | var buffer []byte = make([]byte, 1) 100 | num, err := reader.Read(buffer) 101 | 102 | // If we couldn't read 1 bytes, that's a problem. 103 | if num != 1 { 104 | return 0, UnexpectedEndOfFile 105 | } 106 | 107 | // If there was some other problem, that's also a problem. 108 | if err != nil { 109 | return 0, err 110 | } 111 | 112 | return (buffer[0] & 0x7f), nil 113 | } 114 | 115 | // parseTwoUint7 parses two 7-bit bit integer stored in consecutive bytes from a ReadSeeker, ignoring the high bit in each. 116 | // It returns the 8-bit value and an error. 117 | func parseTwoUint7(reader io.ReadSeeker) (uint8, uint8, error) { 118 | 119 | var buffer []byte = make([]byte, 2) 120 | num, err := reader.Read(buffer) 121 | 122 | // If we couldn't read 2 bytes, that's a problem. 123 | if num != 2 { 124 | return 0, 0, UnexpectedEndOfFile 125 | } 126 | 127 | // If there was some other problem, that's also a problem. 128 | if err != nil { 129 | return 0, 0, err 130 | } 131 | 132 | return (buffer[0] & 0x7f), (buffer[1] & 0x7f), nil 133 | } 134 | 135 | // parseUint8 parses an 8-bit bit integer stored in a bytes from a ReadSeeker. 136 | // It returns a single uint8. 137 | func parseUint8(reader io.ReadSeeker) (uint8, error) { 138 | 139 | var buffer []byte = make([]byte, 1) 140 | num, err := reader.Read(buffer) 141 | 142 | // If we couldn't read 1 bytes, that's a problem. 143 | if num != 1 { 144 | return 0, UnexpectedEndOfFile 145 | } 146 | 147 | // If there was some other problem, that's also a problem. 148 | if err != nil { 149 | return 0, err 150 | } 151 | 152 | return uint8(buffer[0]), nil 153 | } 154 | 155 | // parseInt8 parses an 8-bit bit signedinteger stored in a bytes from a ReadSeeker. 156 | // It returns a single int8. 157 | func parseInt8(reader io.ReadSeeker) (int8, error) { 158 | 159 | var buffer []byte = make([]byte, 1) 160 | num, err := reader.Read(buffer) 161 | 162 | // If we couldn't read 1 bytes, that's a problem. 163 | if num != 1 { 164 | return 0, UnexpectedEndOfFile 165 | } 166 | 167 | // If there was some other problem, that's also a problem. 168 | if err != nil { 169 | return 0, err 170 | } 171 | 172 | return int8(buffer[0]), nil 173 | } 174 | 175 | // parsePitchWheelValue parses a 14-bit signed value, which becomes a signed int16. 176 | // The least significant 7 bits are stored in the first byte, the 7 most significant bites are stored in the second. 177 | // Return the signed value relative to the centre, and the absolute value. 178 | // This is tested in midi_lexer_test.go TestPitchWheel 179 | func parsePitchWheelValue(reader io.ReadSeeker) (relative int16, absolute uint16, err error) { 180 | 181 | var buffer []byte = make([]byte, 2) 182 | num, err := reader.Read(buffer) 183 | 184 | // If we couldn't read 2 bytes, that's a problem. 185 | if num != 2 { 186 | return 0, 0, UnexpectedEndOfFile 187 | } 188 | 189 | // If there was some other problem, that's also a problem. 190 | if err != nil { 191 | return 0, 0, err 192 | } 193 | 194 | var val uint16 = 0 195 | 196 | val = uint16((buffer[1])&0x7f) << 7 197 | val |= uint16(buffer[0]) & 0x7f 198 | // fmt.Println(val) 199 | 200 | // log.Println() 201 | // Turn into a signed value relative to the centre. 202 | relative = int16(val) - 0x2000 203 | 204 | return relative, val, nil 205 | } 206 | 207 | // parseVarLength parses a variable length value from a ReadSeeker. 208 | // It returns the [up to] 32-bit value and an error. 209 | func parseVarLength(reader io.ReadSeeker) (uint32, error) { 210 | 211 | // Single byte buffer to read byte by byte. 212 | var buffer []byte = make([]uint8, 1) 213 | 214 | // The number of bytes returned. 215 | // Should always be 1 unless we reach the EOF 216 | var num int = 1 217 | 218 | // Result value 219 | var result uint32 = 0x00 220 | 221 | // RTFM. 222 | var first = true 223 | for (first || (buffer[0]&0x80 == 0x80)) && (num > 0) { 224 | result = result << 7 225 | 226 | num, _ = reader.Read(buffer) 227 | result |= (uint32(buffer[0]) & 0x7f) 228 | first = false 229 | } 230 | 231 | if num == 0 && !first { 232 | return result, UnexpectedEndOfFile 233 | } 234 | 235 | return result, nil 236 | } 237 | 238 | // parseChunkHeader parses a chunk header from a ReadSeeker. 239 | // It returns the ChunkHeader struct as a value and an error. 240 | func parseChunkHeader(reader io.ReadSeeker) (ChunkHeader, error) { 241 | // fmt.Println("Parse Chunk Header") 242 | 243 | var chunk ChunkHeader 244 | 245 | var chunkTypeBuffer []byte = make([]byte, 4) 246 | num, err := reader.Read(chunkTypeBuffer) 247 | 248 | // fmt.Println("Buffer type", chunkTypeBuffer, "num", num) 249 | 250 | // If we couldn't read 4 bytes, that's a problem. 251 | if num != 4 { 252 | return chunk, UnexpectedEndOfFile 253 | } 254 | 255 | if err != nil { 256 | return chunk, err 257 | } 258 | 259 | chunk.Length, err = parseUint32(reader) 260 | chunk.ChunkType = string(chunkTypeBuffer) 261 | 262 | // parseUint32 might return an error. 263 | if err != nil { 264 | return chunk, err 265 | } 266 | 267 | return chunk, nil 268 | } 269 | 270 | // parseHeaderData parses SMF-header chunk header data. 271 | // It returns the ChunkHeader struct as a value and an error. 272 | func parseHeaderData(reader io.ReadSeeker) (HeaderData, error) { 273 | var headerData HeaderData 274 | // var buffer []byte = make([]byte, 2) 275 | var err error 276 | 277 | // Format 278 | headerData.Format, err = parseUint16(reader) 279 | 280 | if err != nil { 281 | return headerData, err 282 | } 283 | 284 | // Should be one of 0, 1, 2 285 | if headerData.Format > 2 { 286 | return headerData, UnsupportedSmfFormat 287 | } 288 | 289 | // Num tracks 290 | headerData.NumTracks, err = parseUint16(reader) 291 | 292 | if err != nil { 293 | return headerData, err 294 | } 295 | // Division 296 | var division uint16 297 | division, err = parseUint16(reader) 298 | 299 | // "If bit 15 of is zero, the bits 14 thru 0 represent the number 300 | // of delta time "ticks" which make up a quarter-note." 301 | if division&0x8000 == 0x0000 { 302 | headerData.TicksPerQuarterNote = division & 0x7FFF 303 | headerData.TimeFormat = MetricalTimeFormat 304 | } else { 305 | // TODO: Can't be bothered to implement this bit just now. 306 | // If you want it, write it! 307 | headerData.TimeFormatData = division & 0x7FFF 308 | headerData.TimeFormat = TimeCodeTimeFormat 309 | } 310 | 311 | if err != nil { 312 | return headerData, err 313 | } 314 | 315 | return headerData, nil 316 | } 317 | 318 | // readStatusByte reads the track event status byte and returns the type and channel 319 | func readStatusByte(reader io.ReadSeeker) (messageType uint8, messageChannel uint8, err error) { 320 | var buffer []byte = make([]byte, 1) 321 | num, err := reader.Read(buffer) 322 | 323 | // If we couldn't read 1 byte, that's a problem. 324 | if num != 1 { 325 | return 0, 0, UnexpectedEndOfFile 326 | } 327 | 328 | // If there was some other problem, that's also a problem. 329 | if err != nil { 330 | return 0, 0, err 331 | } 332 | 333 | messageType = (buffer[0] & 0xF0) >> 4 334 | messageChannel = buffer[0] & 0x0F 335 | 336 | return 337 | } 338 | 339 | func parseText(reader io.ReadSeeker) (string, error) { 340 | length, err := parseVarLength(reader) 341 | 342 | if err != nil { 343 | return "", err 344 | } 345 | 346 | var buffer []byte = make([]byte, length) 347 | 348 | num, err := reader.Read(buffer) 349 | 350 | // If we couldn't read the entire expected-length buffer, that's a problem. 351 | if num != int(length) { 352 | return "", UnexpectedEndOfFile 353 | } 354 | 355 | // If there was some other problem, that's also a problem. 356 | if err != nil { 357 | return "", err 358 | } 359 | 360 | // TODO: Data should be ASCII but might go up to 0xFF. 361 | // What will Go do? Try and decode UTF-8? 362 | return string(buffer), nil 363 | } 364 | -------------------------------------------------------------------------------- /midi_functions_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Tests for test_functions. 12 | * Make sure that each midi function works. 13 | */ 14 | 15 | package midi 16 | 17 | import ( 18 | "io" 19 | "testing" 20 | ) 21 | 22 | // Test that parseVarLength does what it should. 23 | // Example data taken from http://www.music.mcgill.ca/~ich/classes/mumt306/midiformat.pdf 24 | func TestVarLengthParser(t *testing.T) { 25 | expected := []uint32{ 26 | 0x00000000, 27 | 0x00000040, 28 | 0x0000007F, 29 | 0x00000080, 30 | 0x00002000, 31 | 0x00003FFF, 32 | 0x00004000, 33 | 0x00100000, 34 | 0x001FFFFF, 35 | 0x00200000, 36 | 0x08000000, 37 | 0x0FFFFFFF} 38 | 39 | input := [][]byte{ 40 | []byte{0x00}, 41 | []byte{0x40}, 42 | []byte{0x7F}, 43 | []byte{0x81, 0x00}, 44 | []byte{0xC0, 0x00}, 45 | []byte{0xFF, 0x7F}, 46 | []byte{0x81, 0x80, 0x00}, 47 | []byte{0xC0, 0x80, 0x00}, 48 | []byte{0xFF, 0xFF, 0x7F}, 49 | []byte{0x81, 0x80, 0x80, 0x00}, 50 | []byte{0xC0, 0x80, 0x80, 0x00}, 51 | []byte{0xFF, 0xFF, 0xFF, 0x7F}} 52 | 53 | var numItems = len(input) 54 | 55 | for i := 0; i < numItems; i++ { 56 | var reader = NewMockReadSeeker(&input[i]) 57 | var result, err = parseVarLength(reader) 58 | 59 | if result != expected[i] { 60 | t.Fatal("Expected ", expected[i], " got ", result) 61 | } 62 | 63 | if err != nil { 64 | t.Fatal("Expected no error got ", err) 65 | } 66 | } 67 | 68 | // Now we want to read past the end of a var length file. 69 | // We should get an UnexpectedEndOfFile error. 70 | 71 | // First read OK. 72 | var reader = NewMockReadSeeker(&input[0]) 73 | var _, err = parseVarLength(reader) 74 | if err != nil { 75 | t.Fatal("Expected no error got ", err) 76 | } 77 | 78 | // Second read not OK. 79 | _, err = parseVarLength(reader) 80 | if err != UnexpectedEndOfFile { 81 | t.Fatal("Expected End of file ") 82 | } 83 | } 84 | 85 | // Test for parseUint32 86 | func TestParse32Bit(t *testing.T) { 87 | expected := []uint32{ 88 | 0, 89 | 1, 90 | 4, 91 | 42, 92 | 429, 93 | 4294, 94 | 42949, 95 | 429496, 96 | 4294967, 97 | 42949672, 98 | 429496729, 99 | 4294967295, 100 | } 101 | 102 | input := [][]byte{ 103 | []byte{0x00, 0x00, 0x00, 0x00}, 104 | []byte{0x00, 0x00, 0x00, 0x01}, 105 | []byte{0x00, 0x00, 0x00, 0x04}, 106 | []byte{0x00, 0x00, 0x00, 0x2A}, 107 | []byte{0x00, 0x00, 0x01, 0xAD}, 108 | []byte{0x00, 0x00, 0x10, 0xC6}, 109 | []byte{0x00, 0x00, 0xA7, 0xC5}, 110 | []byte{0x00, 0x06, 0x8D, 0xB8}, 111 | []byte{0x00, 0x41, 0x89, 0x37}, 112 | []byte{0x02, 0x8F, 0x5C, 0x28}, 113 | []byte{0x19, 0x99, 0x99, 0x99}, 114 | []byte{0xFF, 0xFF, 0xFF, 0xFF}, 115 | } 116 | 117 | var numItems = len(input) 118 | 119 | for i := 0; i < numItems; i++ { 120 | var reader = NewMockReadSeeker(&input[i]) 121 | var result, err = parseUint32(reader) 122 | 123 | if result != expected[i] { 124 | t.Fatal("Expected ", expected[i], " got ", result) 125 | } 126 | 127 | if err != nil { 128 | t.Fatal("Expected no error got ", err) 129 | } 130 | } 131 | 132 | // Now we want to read past the end of a file. 133 | // We should get an UnexpectedEndOfFile error. 134 | 135 | // First read OK. 136 | var reader = NewMockReadSeeker(&input[0]) 137 | var _, err = parseUint32(reader) 138 | if err != nil { 139 | t.Fatal("Expected no error got ", err) 140 | } 141 | 142 | // Second read not OK. 143 | _, err = parseUint32(reader) 144 | if err != UnexpectedEndOfFile { 145 | t.Fatal("Expected End of file ") 146 | } 147 | } 148 | 149 | // Test for parseUint8 150 | func TestParse8Bit(t *testing.T) { 151 | expected := []uint8{ 152 | 0, 153 | 1, 154 | 4, 155 | 42, 156 | } 157 | 158 | input := [][]byte{ 159 | []byte{0x00}, 160 | []byte{0x01}, 161 | []byte{0x04}, 162 | []byte{0x2A}, 163 | } 164 | 165 | var numItems = len(input) 166 | 167 | for i := 0; i < numItems; i++ { 168 | var reader = NewMockReadSeeker(&input[i]) 169 | var result, err = parseUint8(reader) 170 | 171 | if result != expected[i] { 172 | t.Fatal("Expected ", expected[i], " got ", result) 173 | } 174 | 175 | if err != nil { 176 | t.Fatal("Expected no error got ", err) 177 | } 178 | } 179 | 180 | // Now we want to read past the end of a file. 181 | // We should get an UnexpectedEndOfFile error. 182 | 183 | // First read OK. 184 | var reader = NewMockReadSeeker(&input[0]) 185 | var _, err = parseUint8(reader) 186 | if err != nil { 187 | t.Fatal("Expected no error got ", err) 188 | } 189 | 190 | // Second read not OK. 191 | _, err = parseUint8(reader) 192 | if err != UnexpectedEndOfFile { 193 | t.Fatal("Expected End of file ") 194 | } 195 | } 196 | 197 | // Test for parseUint16 198 | func TestParse16Bit(t *testing.T) { 199 | expected := []uint16{ 200 | 0, 201 | 1, 202 | 4, 203 | 42, 204 | 429, 205 | 4294, 206 | 42949, 207 | } 208 | 209 | input := [][]byte{ 210 | []byte{0x00, 0x00}, 211 | []byte{0x00, 0x01}, 212 | []byte{0x00, 0x04}, 213 | []byte{0x00, 0x2A}, 214 | []byte{0x01, 0xAD}, 215 | []byte{0x10, 0xC6}, 216 | []byte{0xA7, 0xC5}, 217 | } 218 | 219 | var numItems = len(input) 220 | 221 | for i := 0; i < numItems; i++ { 222 | var reader = NewMockReadSeeker(&input[i]) 223 | var result, err = parseUint16(reader) 224 | 225 | if result != expected[i] { 226 | t.Fatal("Expected ", expected[i], " got ", result) 227 | } 228 | 229 | if err != nil { 230 | t.Fatal("Expected no error got ", err) 231 | } 232 | } 233 | 234 | // Now we want to read past the end of a file. 235 | // We should get an UnexpectedEndOfFile error. 236 | 237 | // First read OK. 238 | var reader = NewMockReadSeeker(&input[0]) 239 | var _, err = parseUint16(reader) 240 | if err != nil { 241 | t.Fatal("Expected no error got ", err) 242 | } 243 | 244 | // Second read not OK. 245 | _, err = parseUint16(reader) 246 | if err != UnexpectedEndOfFile { 247 | t.Fatal("Expected End of file ") 248 | } 249 | } 250 | 251 | // Test for parseChunkHeader. 252 | func TestParseChunkHeader(t *testing.T) { 253 | // Headers for two popular chunk types. 254 | 255 | // Length 6, as all MThds shoudl be 6 long. 256 | var MThd = []byte{0x4D, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06} 257 | 258 | // Arbitrary length 4294967 (parseUint32 is tested separately). 259 | var MTrk = []byte{0x4D, 0x54, 0x72, 0x6b, 0x00, 0x41, 0x89, 0x37} 260 | 261 | // Too short in the type word. 262 | var tooShort1 = []byte{0x4D, 0x54, 0x72} 263 | 264 | // To short in the length word. 265 | var tooShort2 = []byte{0x4D, 0x54, 0x72, 0x6b, 0x00, 0x41, 0x89} 266 | 267 | var header ChunkHeader 268 | var err error 269 | var reader io.ReadSeeker 270 | 271 | // Try for typical MIDI file header 272 | reader = NewMockReadSeeker(&MThd) 273 | header, err = parseChunkHeader(reader) 274 | 275 | if header.ChunkType != "MThd" { 276 | t.Fatal("Got ", header, " expected MThd") 277 | } 278 | 279 | if header.Length != 6 { 280 | t.Fatal("Got ", header, " expected 6") 281 | } 282 | 283 | if err != nil { 284 | t.Fatal("Got error ", err) 285 | } 286 | 287 | // Try for typical track header 288 | reader = NewMockReadSeeker(&MTrk) 289 | header, err = parseChunkHeader(reader) 290 | 291 | if header.ChunkType != "MTrk" { 292 | t.Fatal("Got ", header, " expected MTrk") 293 | } 294 | 295 | if header.Length != 4294967 { 296 | t.Fatal("Got ", header, " expected 4294967") 297 | } 298 | 299 | if err != nil { 300 | t.Fatal("Got error ", err) 301 | } 302 | 303 | // Now two incomplete headers. 304 | 305 | // Too short to parse the type 306 | reader = NewMockReadSeeker(&tooShort1) 307 | header, err = parseChunkHeader(reader) 308 | 309 | if err == nil { 310 | t.Fatal("Expected error for tooshort1") 311 | } 312 | 313 | // Too short to parse the length 314 | reader = NewMockReadSeeker(&tooShort2) 315 | header, err = parseChunkHeader(reader) 316 | 317 | if err == nil { 318 | t.Fatal("Expected error for tooshort 2") 319 | } 320 | } 321 | 322 | // Test for parseHeaderData. 323 | func TestParseHeaderData(t *testing.T) { 324 | var err error 325 | var data, expected HeaderData 326 | 327 | // Format: 1 328 | // Tracks: 2 329 | // Division: metrical 5 330 | var headerMetrical = NewMockReadSeeker(&[]byte{0x00, 0x01, 0x00, 0x02, 0x00, 0x05}) 331 | expected = HeaderData{ 332 | Format: 1, 333 | NumTracks: 2, 334 | TimeFormat: MetricalTimeFormat, 335 | TimeFormatData: 0x00, 336 | TicksPerQuarterNote: 5} 337 | 338 | data, err = parseHeaderData(headerMetrical) 339 | 340 | if err != nil { 341 | t.Fatal(err) 342 | } 343 | 344 | if data != expected { 345 | t.Fatal(data, " != ", expected) 346 | } 347 | 348 | // Format: 2 349 | // Tracks: 1 350 | // Division: timecode (actual data ignored for now) 351 | var headerTimecode = NewMockReadSeeker(&[]byte{0x00, 0x02, 0x00, 0x01, 0xFF, 0x05}) 352 | expected = HeaderData{ 353 | Format: 2, 354 | NumTracks: 1, 355 | TimeFormat: TimeCodeTimeFormat, 356 | TimeFormatData: 0x7F05, // Removed the top timecode type bit flag. 357 | TicksPerQuarterNote: 0} 358 | 359 | data, err = parseHeaderData(headerTimecode) 360 | 361 | if err != nil { 362 | t.Fatal(err) 363 | } 364 | 365 | if data != expected { 366 | t.Fatal(data, " != ", expected) 367 | } 368 | 369 | // Format: 3, which doesn't exist. 370 | var badFormat = NewMockReadSeeker(&[]byte{0x00, 0x03, 0x00, 0x01, 0xFF, 0x05}) 371 | data, err = parseHeaderData(badFormat) 372 | 373 | if err != UnsupportedSmfFormat { 374 | t.Fatal("Expected exception but got none") 375 | } 376 | 377 | // Too short in each field 378 | var tooShort1 = NewMockReadSeeker(&[]byte{0x00, 0x02, 0x00, 0x01, 0xFF}) 379 | data, err = parseHeaderData(tooShort1) 380 | 381 | if err != UnexpectedEndOfFile { 382 | t.Fatal("Expected exception but got ", err) 383 | } 384 | 385 | var tooShort2 = NewMockReadSeeker(&[]byte{0x00, 0x02, 0x00}) 386 | data, err = parseHeaderData(tooShort2) 387 | 388 | if err != UnexpectedEndOfFile { 389 | t.Fatal("Expected exception but got none") 390 | } 391 | 392 | var tooShort3 = NewMockReadSeeker(&[]byte{0x00}) 393 | data, err = parseHeaderData(tooShort3) 394 | 395 | if err != UnexpectedEndOfFile { 396 | t.Fatal("Expected exception but got none") 397 | } 398 | } 399 | 400 | // readStatusByte should read the status byte and return type and channel correctly. 401 | func TestReadStatusByte(t *testing.T) { 402 | expectedType := []uint8{1, 2, 3, 4} 403 | expectedChannel := []uint8{5, 4, 3, 2} 404 | 405 | input := [][]byte{ 406 | []byte{0x15}, 407 | []byte{0x24}, 408 | []byte{0x33}, 409 | []byte{0x42}, 410 | } 411 | 412 | var numItems = len(input) 413 | 414 | for i := 0; i < numItems; i++ { 415 | var reader = NewMockReadSeeker(&input[i]) 416 | var messageType, messageChannel, err = readStatusByte(reader) 417 | 418 | if messageType != expectedType[i] { 419 | t.Fatal("Expected type", expectedType[i], " got ", messageType) 420 | } 421 | 422 | if messageChannel != expectedChannel[i] { 423 | t.Fatal("Expected channel", expectedChannel[i], " got ", messageChannel) 424 | } 425 | 426 | if err != nil { 427 | t.Fatal("Expected no error got ", err) 428 | } 429 | } 430 | } 431 | 432 | // Test that parsePitchWheelValue works for a number of values. 433 | // It returns both unsigned absolute and signed relative values. 434 | func TestParsePitchWheelValue(t *testing.T) { 435 | 436 | // 0x200 should be centre, i.e. 0 relative. 437 | mockReadSeeker := NewMockReadSeeker(&[]byte{ 438 | 0x00, 0x40}) 439 | 440 | relative, absolute, err := parsePitchWheelValue(mockReadSeeker) 441 | 442 | assertNoError(err, t) 443 | assertInt16sEqual(relative, 0x00, t) 444 | assertUint16Equal(absolute, 0x2000, t) 445 | 446 | // 0x1234 encoded 447 | mockReadSeeker = NewMockReadSeeker(&[]byte{ 448 | 0x34, 0x24, // 0x1234 encoded 449 | }) 450 | 451 | relative, absolute, err = parsePitchWheelValue(mockReadSeeker) 452 | 453 | assertNoError(err, t) 454 | assertInt16sEqual(relative, -0xDCC, t) 455 | assertUint16Equal(absolute, 0x1234, t) 456 | } 457 | 458 | // Test that the parseString function works under normal circumstances. 459 | func TestParseString(t *testing.T) { 460 | var reader = NewMockReadSeeker(&[]uint8{0x10, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 461 | 462 | expectedResult := "joe@afandian.com" 463 | 464 | result, err := parseText(reader) 465 | 466 | assertNoError(err, t) 467 | 468 | assertStringsEqual(result, expectedResult, t) 469 | } 470 | 471 | // Test the parseString signals unexpected early end of file 472 | func TestParseBadString(t *testing.T) { 473 | var reader = NewMockReadSeeker(&[]uint8{0x11, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 474 | 475 | _, err := parseText(reader) 476 | 477 | assertError(err, UnexpectedEndOfFile, t) 478 | } 479 | -------------------------------------------------------------------------------- /midi_interfaces.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Interfaces. 12 | * Contains the MidiLexerCallback interface which you should implement in order to use the Lexer. 13 | */ 14 | 15 | package midi 16 | 17 | // MidiLexerCallback describes a callback object that should be passed to MidiLexer. It recieves the following method calls as the lexer finds them. 18 | type MidiLexerCallback interface { 19 | // Meta messages 20 | 21 | // Started reading a file. 22 | Began() 23 | 24 | // Finished reading the file. 25 | Finished() 26 | 27 | // There was an error when lexing. 28 | ErrorReading() 29 | 30 | // There was an error opening the file input. 31 | ErrorOpeningFile() 32 | 33 | // SMF header. 34 | Header(header HeaderData) 35 | 36 | // A chunk header (usually MTrk). 37 | Track(header ChunkHeader) 38 | 39 | // Midi in-track messages 40 | NoteOff(channel uint8, pitch uint8, velocity uint8, time uint32) 41 | NoteOn(channel uint8, pitch uint8, velocity uint8, time uint32) 42 | PolyphonicAfterTouch(channel uint8, pitch uint8, pressure uint8, time uint32) 43 | ControlChange(channel uint8, controller uint8, value uint8, time uint32) 44 | ProgramChange(channel uint8, program uint8, time uint32) 45 | ChannelAfterTouch(channel uint8, value uint8, time uint32) 46 | PitchWheel(channel uint8, value int16, absValue uint16, time uint32) 47 | TimeCodeQuarter(messageType uint8, values uint8, time uint32) 48 | SongPositionPointer(beats uint16, time uint32) 49 | SongSelect(song uint8, time uint32) 50 | Undefined1(time uint32) 51 | Undefined2(time uint32) 52 | TuneRequest(time uint32) 53 | TimingClock(time uint32) 54 | Undefined3(time uint32) 55 | Start(time uint32) 56 | Continue(time uint32) 57 | Stop(time uint32) 58 | Undefined4(time uint32) 59 | Tempo(bpm uint32, microsecondsPerCrotchet uint32, time uint32) 60 | ActiveSensing(time uint32) 61 | Reset(time uint32) 62 | 63 | // TODO remove, duplicated by Finished() 64 | Done(time uint32) 65 | 66 | // Meta Events 67 | 68 | SequenceNumber(channel uint8, number uint16, numberGiven bool, time uint32) 69 | Text(channel uint8, text string, time uint32) 70 | 71 | // The Key and Mode. Also the sharps (>0) or flats (<0) as per MIDI spec, in case you want to use it. 72 | KeySignature(key ScaleDegree, mode KeySignatureMode, sharpsOrFlats int8) 73 | CopyrightText(channel uint8, text string, time uint32) 74 | SequenceName(channel uint8, text string, time uint32) 75 | TrackInstrumentName(channel uint8, text string, time uint32) 76 | LyricText(channel uint8, text string, time uint32) 77 | MarkerText(channel uint8, text string, time uint32) 78 | CuePointText(channel uint8, text string, time uint32) 79 | EndOfTrack(channel uint8, time uint32) 80 | TimeSignature(numerator uint8, denomenator uint8, clocksPerClick uint8, demiSemiQuaverPerQuarter uint8, time uint32) 81 | } 82 | -------------------------------------------------------------------------------- /midi_lexer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Tests for lexer. 12 | * Check that the state transitions work fine and that the lexer can cope with real streams of MIDI data. 13 | */ 14 | 15 | package midi 16 | 17 | import ( 18 | "io" 19 | "testing" 20 | ) 21 | 22 | var lexer *MidiLexer 23 | var mockLexerCallback *CountingLexerCallback 24 | var mockReadSeeker io.ReadSeeker 25 | var finished bool 26 | var err error 27 | 28 | // Get clean values. 29 | func setupData(data *[]byte) { 30 | mockLexerCallback = new(CountingLexerCallback) 31 | mockReadSeeker = NewMockReadSeeker(data) 32 | } 33 | 34 | /* 35 | * Correct state transitions. 36 | */ 37 | 38 | // Start of file, consume header. 39 | // ExpectHeader -> ExpectChunk 40 | func TestLexerShouldExpectHeader(t *testing.T) { 41 | // Just enough for the header chunk 42 | 43 | mockLexerCallback = new(CountingLexerCallback) 44 | 45 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x4D, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0xC8}) 46 | 47 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 48 | 49 | // Pre: New file, ExpectHeader state. 50 | // Should be ready for header 51 | assertIntsEqual(lexer.state, ExpectHeader, t) 52 | 53 | finished, err = lexer.next() 54 | assertNoError(err, t) 55 | 56 | // Post: 57 | // not finished yet 58 | assertFalse(finished, t) 59 | 60 | // ExpectChunk state. 61 | assertIntsEqual(lexer.state, ExpectChunk, t) 62 | 63 | // Began() was called. 64 | assertIntsEqual(mockLexerCallback.began, 1, t) 65 | 66 | // Began() was called with the right values. 67 | assertIntsEqual(int(mockLexerCallback.header), 1, t) 68 | assertIntsEqual(int(mockLexerCallback.headerData.Format), 1, t) 69 | assertIntsEqual(int(mockLexerCallback.headerData.NumTracks), 2, t) 70 | 71 | if mockLexerCallback.headerData.TimeFormat != MetricalTimeFormat { 72 | t.Fatal("Was not MetricalTimeFormat") 73 | } 74 | 75 | assertIntsEqual(int(mockLexerCallback.headerData.TicksPerQuarterNote), 200, t) 76 | } 77 | 78 | // Expect a chunk, get an unrecognised type. Should skip to next. 79 | // ExpectChunk -> ExpectChunk 80 | func TestMidiLexerShouldSkipUnknownTrack(t *testing.T) { 81 | // Just enough for the header chunk 82 | 83 | mockLexerCallback = new(CountingLexerCallback) 84 | 85 | // Head of data stream is MThd, where the lexer will expect MTrk 86 | 87 | mockReadSeeker = NewMockReadSeeker(&[]byte{ /* start of unknown block, claims to be 2-long */ 0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x02, 0xCA, 0xFE /* Start of next block. */, 0x4D, 0x54, 0x68, 0x64, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0xC8}) 88 | 89 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 90 | 91 | // Pre: ExpectChunk 92 | // Should be ready for a chunk. 93 | lexer.state = ExpectChunk 94 | 95 | finished, err = lexer.next() 96 | assertNoError(err, t) 97 | 98 | // Post: 99 | // not finished yet 100 | assertFalse(finished, t) 101 | 102 | // ExpectChunk state. 103 | assertIntsEqual(lexer.state, ExpectChunk, t) 104 | 105 | // Reader should have jumped to position 10, the next block. 106 | var position, err = lexer.input.Seek(0, 1) 107 | assertNoError(err, t) 108 | assertIntsEqual(int(position), 10, t) 109 | } 110 | 111 | // Expect a chunk, get an end of file. Should end gracefully. 112 | // ExpectChunk -> Done 113 | func TestMidiLexerShouldReachEndOfFile(t *testing.T) { 114 | // Just enough for the header chunk 115 | 116 | mockLexerCallback = new(CountingLexerCallback) 117 | 118 | mockReadSeeker = NewMockReadSeeker(&[]byte{}) 119 | 120 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 121 | 122 | // Pre: ExpectChunk 123 | // Should be ready for a chunk. 124 | lexer.state = ExpectChunk 125 | 126 | finished, err = lexer.next() 127 | assertNoError(err, t) 128 | 129 | // Post: 130 | // finished 131 | assertTrue(finished, t) 132 | 133 | // ExpectChunk state. 134 | assertIntsEqual(lexer.state, Done, t) 135 | } 136 | 137 | // Expect a chunk, get MTrk. Should enter ExpectTrackEvent state. 138 | // ExpectChunk -> ExpectTrackEvent 139 | func TestMidiLexerShouldExpectTrackEvent(t *testing.T) { 140 | mockLexerCallback = new(CountingLexerCallback) 141 | 142 | // Head of data stream is MThd, where the lexer will expect MTrk 143 | 144 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x4D, 0x54, 0x72, 0x6B, 0x00, 0x00, 0x00, 0xEE, 0x00, 0x01, 0x00, 0x02, 0x00, 0xC8}) 145 | 146 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 147 | 148 | // Pre: ExpectChunk 149 | // Should be ready for a chunk. 150 | lexer.state = ExpectChunk 151 | 152 | finished, err = lexer.next() 153 | assertNoError(err, t) 154 | 155 | // Post: 156 | // not finished yet 157 | assertFalse(finished, t) 158 | 159 | // ExpectChunk state. 160 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 161 | 162 | // callback.Track should have been called. 163 | assertIntsEqual(mockLexerCallback.track, 1, t) 164 | assertIntsEqual(int(mockLexerCallback.chunkHeader.Length), 0xEE, t) 165 | } 166 | 167 | // Expect a chunk, get MTrk. 168 | // Should store reported track length and go back to ExpectChunk at end of chunk. 169 | // ExpectChunk -> ExpectTrackEvent 170 | func TestMidiLexerShouldHandleChunkLengths(t *testing.T) { 171 | // TODO 172 | } 173 | 174 | // Expect a chunk, get MTrk with a too-short length. 175 | // Should raise a BadSizeChunk error 176 | // ExpectChunk -> ExpectTrackEvent 177 | func TestMidiLexerShouldHandleChunkLengthError(t *testing.T) { 178 | // TODO 179 | } 180 | 181 | // Expect a track event, parse a NoteOff message. 182 | // ExpectTrackEvent -> ExpectTrackEvent 183 | func TestNoteOff(t *testing.T) { 184 | mockLexerCallback = new(CountingLexerCallback) 185 | 186 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x40, 0x85, 0x04, 0x03}) 187 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 188 | 189 | // Pre: ExpectChunk 190 | // Should be ready for a track event. 191 | lexer.state = ExpectTrackEvent 192 | 193 | finished, err = lexer.next() 194 | assertNoError(err, t) 195 | 196 | // Post: 197 | // not finished yet 198 | assertFalse(finished, t) 199 | 200 | // ExpectChunk state. 201 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 202 | 203 | // callback.Track should have been called. 204 | assertIntsEqual(mockLexerCallback.noteOff, 1, t) 205 | assertUint32Equal(mockLexerCallback.time, 0x40, t) 206 | assertUint8sEqual(mockLexerCallback.channel, 0x05, t) 207 | assertUint8sEqual(mockLexerCallback.pitch, 0x04, t) 208 | assertUint8sEqual(mockLexerCallback.velocity, 0x03, t) 209 | } 210 | 211 | // Expect a track event, parse a NoteOn message. 212 | // ExpectTrackEvent -> ExpectTrackEvent 213 | func TestNoteOn(t *testing.T) { 214 | mockLexerCallback = new(CountingLexerCallback) 215 | 216 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x40, 0x95, 0x04, 0x03}) 217 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 218 | 219 | // Pre: ExpectChunk 220 | // Should be ready for a track event. 221 | lexer.state = ExpectTrackEvent 222 | 223 | finished, err = lexer.next() 224 | assertNoError(err, t) 225 | 226 | // Post: 227 | // not finished yet 228 | assertFalse(finished, t) 229 | 230 | // ExpectChunk state. 231 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 232 | 233 | // callback.Track should have been called. 234 | assertIntsEqual(mockLexerCallback.noteOn, 1, t) 235 | assertUint32Equal(mockLexerCallback.time, 0x40, t) 236 | assertUint8sEqual(mockLexerCallback.channel, 0x05, t) 237 | assertUint8sEqual(mockLexerCallback.pitch, 0x04, t) 238 | assertUint8sEqual(mockLexerCallback.velocity, 0x03, t) 239 | } 240 | 241 | // Expect a track event, parse a NoteOn message. 242 | // ExpectTrackEvent -> ExpectTrackEvent 243 | func TestNotePolyphonicKeyPressure(t *testing.T) { 244 | mockLexerCallback = new(CountingLexerCallback) 245 | 246 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x40, 0xA7, 0x12, 0x34}) 247 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 248 | 249 | // Pre: ExpectChunk 250 | // Should be ready for a track event. 251 | lexer.state = ExpectTrackEvent 252 | 253 | finished, err = lexer.next() 254 | assertNoError(err, t) 255 | 256 | // Post: 257 | // not finished yet 258 | assertFalse(finished, t) 259 | 260 | // ExpectChunk state. 261 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 262 | 263 | // callback.Track should have been called. 264 | assertIntsEqual(mockLexerCallback.polyphonicAfterTouch, 1, t) 265 | assertUint32Equal(mockLexerCallback.time, 0x40, t) 266 | assertUint8sEqual(mockLexerCallback.channel, 0x07, t) 267 | assertUint8sEqual(mockLexerCallback.pitch, 0x12, t) 268 | assertUint8sEqual(mockLexerCallback.pressure, 0x34, t) 269 | } 270 | 271 | // Expect a track event, parse a message. 272 | // ExpectTrackEvent -> ExpectTrackEvent 273 | func TestProgramChange(t *testing.T) { 274 | // TODO 275 | } 276 | 277 | // Expect a track event, parse a message. 278 | // ExpectTrackEvent -> ExpectTrackEvent 279 | func TestChannelPressure(t *testing.T) { 280 | mockLexerCallback = new(CountingLexerCallback) 281 | 282 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x40, 0xD8, 0x56}) 283 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 284 | 285 | // Pre: ExpectChunk 286 | // Should be ready for a track event. 287 | lexer.state = ExpectTrackEvent 288 | 289 | finished, err = lexer.next() 290 | assertNoError(err, t) 291 | 292 | // Post: 293 | // not finished yet 294 | assertFalse(finished, t) 295 | 296 | // ExpectChunk state. 297 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 298 | 299 | // callback.Track should have been called. 300 | assertIntsEqual(mockLexerCallback.channelAfterTouch, 1, t) 301 | assertUint32Equal(mockLexerCallback.time, 0x40, t) 302 | assertUint8sEqual(mockLexerCallback.channel, 0x08, t) 303 | assertUint8sEqual(mockLexerCallback.pressure, 0x56, t) 304 | } 305 | 306 | // Expect a track event, parse a PitchWheel message. 307 | // ExpectTrackEvent -> ExpectTrackEvent 308 | func TestPitchWheel(t *testing.T) { 309 | mockLexerCallback = new(CountingLexerCallback) 310 | 311 | // Three sequential pitch wheel events. NB the value is 14-bit, 312 | // split over two bytes, little end first! 313 | mockReadSeeker = NewMockReadSeeker(&[]byte{ 314 | 0x10, 0xE9, 0x00, 0x40, // 0x2000 should be centre 315 | 0x20, 0xE8, 0x34, 0x24, // 0x1234 encoded 316 | 0x50, 0xE7, 0x00, 0x40}) 317 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 318 | 319 | /* 320 | * FIRST 321 | */ 322 | 323 | // Pre: ExpectChunk 324 | // Should be ready for a track event. 325 | lexer.state = ExpectTrackEvent 326 | 327 | finished, err = lexer.next() 328 | assertNoError(err, t) 329 | 330 | // Post: 331 | // not finished yet 332 | assertFalse(finished, t) 333 | 334 | // ExpectChunk state. 335 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 336 | 337 | // callback.Track should have been called. 338 | assertIntsEqual(mockLexerCallback.pitchWheel, 1, t) 339 | assertUint32Equal(mockLexerCallback.time, 0x10, t) 340 | assertUint8sEqual(mockLexerCallback.channel, 0x09, t) 341 | assertInt16sEqual(mockLexerCallback.pitchWheelValue, 0x00, t) 342 | assertUint16Equal(mockLexerCallback.pitchWheelValueAbsolute, 0x2000, t) 343 | 344 | /* 345 | * SECOND 346 | */ 347 | 348 | // Pre: ExpectChunk 349 | // Should be ready for a track event. 350 | lexer.state = ExpectTrackEvent 351 | 352 | finished, err = lexer.next() 353 | assertNoError(err, t) 354 | 355 | // Post: 356 | // not finished yet 357 | assertFalse(finished, t) 358 | 359 | // ExpectChunk state. 360 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 361 | 362 | // callback.Track should have been called. 363 | assertIntsEqual(mockLexerCallback.pitchWheel, 2, t) 364 | assertUint32Equal(mockLexerCallback.time, 0x20, t) 365 | assertUint8sEqual(mockLexerCallback.channel, 0x08, t) 366 | assertInt16sEqual(mockLexerCallback.pitchWheelValue, -0xDCC, t) 367 | assertUint16Equal(mockLexerCallback.pitchWheelValueAbsolute, 0x1234, t) 368 | 369 | /* 370 | * THIRD 371 | */ 372 | 373 | // Pre: ExpectChunk 374 | // Should be ready for a track event. 375 | lexer.state = ExpectTrackEvent 376 | 377 | finished, err = lexer.next() 378 | assertNoError(err, t) 379 | 380 | // Post: 381 | // not finished yet 382 | assertFalse(finished, t) 383 | 384 | // ExpectChunk state. 385 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 386 | 387 | // callback.Track should have been called. 388 | assertIntsEqual(mockLexerCallback.pitchWheel, 3, t) 389 | assertUint32Equal(mockLexerCallback.time, 0x50, t) 390 | assertUint8sEqual(mockLexerCallback.channel, 0x07, t) 391 | assertInt16sEqual(mockLexerCallback.pitchWheelValue, 0x00, t) 392 | assertUint16Equal(mockLexerCallback.pitchWheelValueAbsolute, 0x2000, t) 393 | } 394 | 395 | // Expect a track event, get SequenceNumber with no number. 396 | // ExpectTrackEvent -> ExpectTrackEvent 397 | func TestNilSequenceNumber(t *testing.T) { 398 | mockLexerCallback = new(CountingLexerCallback) 399 | 400 | // Three sequential pitch wheel events. NB the value is 14-bit, 401 | // split over two bytes, little end first! 402 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x00, 0x00}) 403 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 404 | 405 | // Pre: ExpectChunk 406 | // Should be ready for a track event. 407 | lexer.state = ExpectTrackEvent 408 | 409 | finished, err = lexer.next() 410 | assertNoError(err, t) 411 | 412 | // Post: 413 | // not finished yet 414 | assertFalse(finished, t) 415 | 416 | // ExpectChunk state. 417 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 418 | 419 | // callback.Track should have been called. 420 | assertIntsEqual(mockLexerCallback.sequenceNumber, 1, t) 421 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 422 | assertFalse(mockLexerCallback.sequenceNumberGiven, t) 423 | } 424 | 425 | // Expect a track event, get SequenceNumber with a number supplied. 426 | // ExpectTrackEvent -> ExpectTrackEvent 427 | func TestSequenceNumber(t *testing.T) { 428 | mockLexerCallback = new(CountingLexerCallback) 429 | 430 | // Three sequential pitch wheel events. NB the value is 14-bit, 431 | // split over two bytes, little end first! 432 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x00, 0x02, 0xA7, 0xC5}) 433 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 434 | 435 | // Pre: ExpectChunk 436 | // Should be ready for a track event. 437 | lexer.state = ExpectTrackEvent 438 | 439 | finished, err = lexer.next() 440 | assertNoError(err, t) 441 | 442 | // Post: 443 | // not finished yet 444 | assertFalse(finished, t) 445 | 446 | // ExpectChunk state. 447 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 448 | 449 | // callback.Track should have been called. 450 | assertIntsEqual(mockLexerCallback.sequenceNumber, 1, t) 451 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 452 | assertUint16Equal(mockLexerCallback.sequenceNumberValue, 42949, t) 453 | assertTrue(mockLexerCallback.sequenceNumberGiven, t) 454 | } 455 | 456 | // Expect a track event, get Text event. 457 | // ExpectTrackEvent -> ExpectTrackEvent 458 | func TestText(t *testing.T) { 459 | mockLexerCallback = new(CountingLexerCallback) 460 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x01, 0x10, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 461 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 462 | 463 | // Pre: ExpectChunk 464 | // Should be ready for a track event. 465 | lexer.state = ExpectTrackEvent 466 | 467 | finished, err = lexer.next() 468 | assertNoError(err, t) 469 | 470 | // Post: 471 | // not finished yet 472 | assertFalse(finished, t) 473 | 474 | // ExpectChunk state. 475 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 476 | 477 | // callback.Text should have been callbacked. 478 | assertIntsEqual(mockLexerCallback.text, 1, t) 479 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 480 | assertStringsEqual(mockLexerCallback.textValue, "joe@afandian.com", t) 481 | 482 | } 483 | 484 | // Expect a track event, get CopyrightText event. 485 | // ExpectTrackEvent -> ExpectTrackEvent 486 | func TestCopyrightText(t *testing.T) { 487 | mockLexerCallback = new(CountingLexerCallback) 488 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x02, 0x10, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 489 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 490 | 491 | // Pre: ExpectChunk 492 | // Should be ready for a track event. 493 | lexer.state = ExpectTrackEvent 494 | 495 | finished, err = lexer.next() 496 | assertNoError(err, t) 497 | 498 | // Post: 499 | // not finished yet 500 | assertFalse(finished, t) 501 | 502 | // ExpectChunk state. 503 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 504 | 505 | // callback.Text should have been callbacked. 506 | assertIntsEqual(mockLexerCallback.copyrightText, 1, t) 507 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 508 | assertStringsEqual(mockLexerCallback.textValue, "joe@afandian.com", t) 509 | } 510 | 511 | // Expect a track event, get SequenceName event. 512 | // ExpectTrackEvent -> ExpectTrackEvent 513 | func TestSequenceName(t *testing.T) { 514 | mockLexerCallback = new(CountingLexerCallback) 515 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x03, 0x10, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 516 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 517 | 518 | // Pre: ExpectChunk 519 | // Should be ready for a track event. 520 | lexer.state = ExpectTrackEvent 521 | 522 | finished, err = lexer.next() 523 | assertNoError(err, t) 524 | 525 | // Post: 526 | // not finished yet 527 | assertFalse(finished, t) 528 | 529 | // ExpectChunk state. 530 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 531 | 532 | // callback.Text should have been callbacked. 533 | assertIntsEqual(mockLexerCallback.sequenceName, 1, t) 534 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 535 | assertStringsEqual(mockLexerCallback.textValue, "joe@afandian.com", t) 536 | } 537 | 538 | // Expect a track event, get TrackInstrumentName event. 539 | // ExpectTrackEvent -> ExpectTrackEvent 540 | func TestTrackInstrumentName(t *testing.T) { 541 | mockLexerCallback = new(CountingLexerCallback) 542 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x04, 0x10, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 543 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 544 | 545 | // Pre: ExpectChunk 546 | // Should be ready for a track event. 547 | lexer.state = ExpectTrackEvent 548 | 549 | finished, err = lexer.next() 550 | assertNoError(err, t) 551 | 552 | // Post: 553 | // not finished yet 554 | assertFalse(finished, t) 555 | 556 | // ExpectChunk state. 557 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 558 | 559 | // callback.Text should have been callbacked. 560 | assertIntsEqual(mockLexerCallback.trackInstrumentName, 1, t) 561 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 562 | assertStringsEqual(mockLexerCallback.textValue, "joe@afandian.com", t) 563 | } 564 | 565 | // Expect a track event, get LyricText event. 566 | // ExpectTrackEvent -> ExpectTrackEvent 567 | func TestLyricText(t *testing.T) { 568 | mockLexerCallback = new(CountingLexerCallback) 569 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x05, 0x10, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 570 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 571 | 572 | // Pre: ExpectChunk 573 | // Should be ready for a track event. 574 | lexer.state = ExpectTrackEvent 575 | 576 | finished, err = lexer.next() 577 | assertNoError(err, t) 578 | 579 | // Post: 580 | // not finished yet 581 | assertFalse(finished, t) 582 | 583 | // ExpectChunk state. 584 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 585 | 586 | // callback.Text should have been callbacked. 587 | assertIntsEqual(mockLexerCallback.lyricText, 1, t) 588 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 589 | assertStringsEqual(mockLexerCallback.textValue, "joe@afandian.com", t) 590 | } 591 | 592 | // Expect a track event, get MarkerText event. 593 | // ExpectTrackEvent -> ExpectTrackEvent 594 | func TestMarkerText(t *testing.T) { 595 | mockLexerCallback = new(CountingLexerCallback) 596 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x06, 0x10, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 597 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 598 | 599 | // Pre: ExpectChunk 600 | // Should be ready for a track event. 601 | lexer.state = ExpectTrackEvent 602 | 603 | finished, err = lexer.next() 604 | assertNoError(err, t) 605 | 606 | // Post: 607 | // not finished yet 608 | assertFalse(finished, t) 609 | 610 | // ExpectChunk state. 611 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 612 | 613 | // callback.Text should have been callbacked. 614 | assertIntsEqual(mockLexerCallback.markerText, 1, t) 615 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 616 | assertStringsEqual(mockLexerCallback.textValue, "joe@afandian.com", t) 617 | } 618 | 619 | // Expect a track event, get CuePointText event. 620 | // ExpectTrackEvent -> ExpectTrackEvent 621 | func TestCuePointText(t *testing.T) { 622 | mockLexerCallback = new(CountingLexerCallback) 623 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x07, 0x10, 0x6A, 0x6F, 0x65, 0x40, 0x61, 0x66, 0x61, 0x6E, 0x64, 0x69, 0x61, 0x6E, 0x2E, 0x63, 0x6F, 0x6D}) 624 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 625 | 626 | // Pre: ExpectChunk 627 | // Should be ready for a track event. 628 | lexer.state = ExpectTrackEvent 629 | 630 | finished, err = lexer.next() 631 | assertNoError(err, t) 632 | 633 | // Post: 634 | // not finished yet 635 | assertFalse(finished, t) 636 | 637 | // ExpectChunk state. 638 | assertIntsEqual(lexer.state, ExpectTrackEvent, t) 639 | 640 | // callback.Text should have been callbacked. 641 | assertIntsEqual(mockLexerCallback.cuePointText, 1, t) 642 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 643 | assertStringsEqual(mockLexerCallback.textValue, "joe@afandian.com", t) 644 | } 645 | 646 | // Expect a track event, get EndOfTrack event. 647 | // ExpectTrackEvent -> ExpectChunk 648 | func TestEndOfTrack(t *testing.T) { 649 | mockLexerCallback = new(CountingLexerCallback) 650 | mockReadSeeker = NewMockReadSeeker(&[]byte{0x09, 0xFF, 0x2F, 0x00}) 651 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 652 | 653 | // Pre: ExpectChunk 654 | // Should be ready for a track event. 655 | lexer.state = ExpectTrackEvent 656 | 657 | finished, err = lexer.next() 658 | assertNoError(err, t) 659 | 660 | // Post: 661 | // not finished yet 662 | assertFalse(finished, t) 663 | 664 | // ExpectChunk state. 665 | // As this ends the track, we go back to ExpectChunk state ready for the next track. 666 | assertIntsEqual(lexer.state, ExpectChunk, t) 667 | 668 | // callback.Text should have been callbacked. 669 | assertIntsEqual(mockLexerCallback.endOfTrack, 1, t) 670 | assertUint32Equal(mockLexerCallback.time, 0x09, t) 671 | } 672 | 673 | /* 674 | * Exceptional state transitions. 675 | */ 676 | 677 | // Bad header chunk type at start of file should result in error 678 | func TestLexerShouldErrorBadHeader(t *testing.T) { 679 | 680 | // Just enough for the header chunk 681 | mockLexerCallback = new(CountingLexerCallback) 682 | 683 | mockReadSeeker = NewMockReadSeeker(&[]byte{0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0xC8}) 684 | 685 | lexer = NewMidiLexer(mockReadSeeker, mockLexerCallback) 686 | 687 | finished, err = lexer.next() 688 | 689 | assertError(err, ExpectedMthd, t) 690 | } 691 | -------------------------------------------------------------------------------- /midi_mocks_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Tests for test mocks. 12 | * Ensure that test mocks work correctly so we can trust tests. 13 | */ 14 | 15 | package midi 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | /* Tests for tests */ 22 | 23 | // The MockReadSeeker should behaves as a ReadSeeker should. 24 | func TestMockReadSeekerReads(t *testing.T) { 25 | var reader = NewMockReadSeeker(&[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}) 26 | 27 | // Buffer to read into. 28 | var data []byte = []byte{0x00, 0x00, 0x00} 29 | var count = 0 30 | 31 | // Start with empty data buffer 32 | assertBytesEqual(data, []byte{0x00, 0x00, 0x00}, t) 33 | 34 | // Now read into the 3-length buffer 35 | count, err := reader.Read(data) 36 | if count != 3 { 37 | t.Fatal("Count not 3 was ", count) 38 | } 39 | 40 | if err != nil { 41 | t.Fatal("Error not nil, was ", err) 42 | } 43 | 44 | assertBytesEqual(data, []byte{0x01, 0x02, 0x03}, t) 45 | 46 | // Read into it again to get the next 3 47 | count, err = reader.Read(data) 48 | if count != 3 { 49 | t.Fatal("Count not 3 was ", count) 50 | } 51 | 52 | if err != nil { 53 | t.Fatal("Error not nil, was ", err) 54 | } 55 | assertBytesEqual(data, []byte{0x04, 0x05, 0x06}, t) 56 | 57 | // Read again to get the last one. 58 | count, err = reader.Read(data) 59 | if count != 1 { 60 | t.Fatal("Count not 1 was ", count) 61 | } 62 | 63 | if err != nil { 64 | t.Fatal("Error not nil, was ", err) 65 | } 66 | 67 | // Data will still have the old data remaining 68 | assertBytesEqual(data, []byte{0x07, 0x05, 0x06}, t) 69 | 70 | // One more time, should be empty 71 | count, err = reader.Read(data) 72 | if count != 0 { 73 | t.Fatal("Count not 0 was ", count) 74 | } 75 | 76 | if err != nil { 77 | t.Fatal("Error not nil, was ", err) 78 | } 79 | } 80 | 81 | // The MockReadSeeker should behaves as a ReadSeeker should. 82 | func TestMockReadSeekerSeeks(t *testing.T) { 83 | var reader = NewMockReadSeeker(&[]byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}) 84 | var dataSize int64 = 7 85 | 86 | var count = 0 87 | 88 | // Single byte buffer. 89 | var byteBuffer []byte = []byte{0x00} 90 | 91 | /* 92 | * 0 - Relative to start of file 93 | */ 94 | 95 | // Seek from the start of the file to the last byte. 96 | sook, err := reader.Seek(dataSize-1, 0) 97 | 98 | if err != nil { 99 | t.Fatal(err) 100 | } 101 | 102 | if sook != dataSize-1 { 103 | t.Fatal("Expected to return ", dataSize-1, " got ", sook) 104 | } 105 | 106 | count, err = reader.Read(byteBuffer) 107 | 108 | if err != nil { 109 | t.Fatal(err) 110 | } 111 | 112 | if count != 1 { 113 | t.Fatal("Expected to read 1 byte, got ", count) 114 | } 115 | 116 | if byteBuffer[0] != 0x07 { 117 | t.Fatal("Expected 0x07 got ", byteBuffer) 118 | } 119 | 120 | // Seek from the start of the file to the 3rd byte. 121 | sook, err = reader.Seek(2, 0) 122 | 123 | if err != nil { 124 | t.Fatal(err) 125 | } 126 | 127 | if sook != 2 { 128 | t.Fatal("Expected to return ", 2, " got ", sook) 129 | } 130 | 131 | count, err = reader.Read(byteBuffer) 132 | 133 | if err != nil { 134 | t.Fatal(err) 135 | } 136 | 137 | if byteBuffer[0] != 0x03 { 138 | t.Fatal("Expected 0x03 got ", byteBuffer) 139 | } 140 | 141 | // Seek from the start of the file to the first byte. 142 | sook, err = reader.Seek(0, 0) 143 | 144 | if err != nil { 145 | t.Fatal(err) 146 | } 147 | 148 | if sook != 0 { 149 | t.Fatal("Expected to return ", 0, " got ", sook) 150 | } 151 | 152 | count, err = reader.Read(byteBuffer) 153 | 154 | if err != nil { 155 | t.Fatal(err) 156 | } 157 | 158 | if byteBuffer[0] != 0x01 { 159 | t.Fatal("Expected 0x01 got ", byteBuffer) 160 | } 161 | 162 | /* 163 | * 1 - Relative to current position 164 | */ 165 | 166 | // Seek from the current position to the same place. 167 | 168 | // Get in the middle 169 | sook, err = reader.Seek(4, 0) 170 | 171 | if err != nil { 172 | t.Fatal(err) 173 | } 174 | 175 | if sook != 4 { 176 | t.Fatal("Expected to return ", 4, " got ", sook) 177 | } 178 | 179 | // Seek same place relative to current. 180 | sook, err = reader.Seek(0, 1) 181 | 182 | if err != nil { 183 | t.Fatal(err) 184 | } 185 | 186 | if sook != 4 { 187 | t.Fatal("Expected to return ", 4, " got ", sook) 188 | } 189 | 190 | count, err = reader.Read(byteBuffer) 191 | 192 | if err != nil { 193 | t.Fatal(err) 194 | } 195 | 196 | if byteBuffer[0] != 0x05 { 197 | t.Fatal("Expected 0x05 got ", byteBuffer) 198 | } 199 | 200 | // Seek forward a byte 201 | sook, err = reader.Seek(1, 1) 202 | 203 | if err != nil { 204 | t.Fatal(err) 205 | } 206 | 207 | if sook != 6 { 208 | t.Fatal("Expected to return ", 6, " got ", sook) 209 | } 210 | 211 | count, err = reader.Read(byteBuffer) 212 | 213 | if err != nil { 214 | t.Fatal(err) 215 | } 216 | 217 | if byteBuffer[0] != 0x07 { 218 | t.Fatal("Expected 0x07 got ", byteBuffer) 219 | } 220 | 221 | /* 222 | * 2 - Relative to end of file 223 | */ 224 | 225 | // Seek from the current position to the same place. 226 | 227 | // Get to the end. 228 | // Seek same place relative to end 229 | sook, err = reader.Seek(0, 2) 230 | 231 | if err != nil { 232 | t.Fatal(err) 233 | } 234 | 235 | if sook != 7 { 236 | t.Fatal("Expected to return ", 7, " got ", sook) 237 | } 238 | 239 | count, err = reader.Read(byteBuffer) 240 | 241 | if err != nil { 242 | t.Fatal(err) 243 | } 244 | 245 | if byteBuffer[0] != 0x07 { 246 | t.Fatal("Expected 0x07 got ", byteBuffer) 247 | } 248 | 249 | // Seek back a byte 250 | sook, err = reader.Seek(1, 2) 251 | 252 | if err != nil { 253 | t.Fatal(err) 254 | } 255 | 256 | if sook != 6 { 257 | t.Fatal("Expected to return ", 6, " got ", sook) 258 | } 259 | 260 | count, err = reader.Read(byteBuffer) 261 | 262 | if err != nil { 263 | t.Fatal(err) 264 | } 265 | 266 | if byteBuffer[0] != 0x07 { 267 | t.Fatal("Expected 0x07 got ", byteBuffer) 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /midi_structs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Data structures passed to the Lexer callback. 12 | */ 13 | 14 | package midi 15 | 16 | // A chunk header 17 | type ChunkHeader struct { 18 | ChunkType string 19 | Length uint32 20 | } 21 | 22 | // Header data 23 | type HeaderData struct { 24 | Format uint16 25 | NumTracks uint16 26 | 27 | // One of MetricalTimeFormat or TimeCodeTimeFormat 28 | TimeFormat uint 29 | 30 | // Used if TimeCodeTimeFormat 31 | // Currently data is not un-packed. 32 | TimeFormatData uint16 33 | 34 | // Used if MetricalTimeFormat 35 | TicksPerQuarterNote uint16 36 | } 37 | -------------------------------------------------------------------------------- /midi_test_helpers_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Test helper functions. 12 | * Bits and pieces that don't appear to be in the test framework. 13 | */ 14 | 15 | package midi 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | // Helper assertions 22 | func assertHasFlag(value int, flag int, test *testing.T) { 23 | if value&flag == 0 { 24 | test.Fatal("Expected to find ", flag, " in ", value) 25 | } 26 | } 27 | 28 | // assertBytesEqual asserts that the given byte arrays or slices are equal. 29 | func assertBytesEqual(a []byte, b []byte, t *testing.T) { 30 | if len(a) != len(b) { 31 | t.Fatal("Two arrays not equal", a, b) 32 | } 33 | 34 | for i := 0; i < len(a); i++ { 35 | if a[i] != b[i] { 36 | t.Fatal("Two arrays not equal. At ", i, " was ", a[i], " vs ", b[i]) 37 | } 38 | } 39 | } 40 | 41 | // Assert uint16s equal 42 | func assertUint16Equal(a uint16, b uint16, test *testing.T) { 43 | if a != b { 44 | test.Fatal(a, " != ", b) 45 | } 46 | } 47 | 48 | // Assert uint16s equal 49 | func assertInt16sEqual(a int16, b int16, test *testing.T) { 50 | if a != b { 51 | test.Fatal(a, " != ", b) 52 | } 53 | } 54 | 55 | // Assert uint32s equal 56 | func assertUint32Equal(a uint32, b uint32, test *testing.T) { 57 | if a != b { 58 | test.Fatal(a, " != ", b) 59 | } 60 | } 61 | 62 | // Assert uint16s equal 63 | func assertIntsEqual(a int, b int, test *testing.T) { 64 | if a != b { 65 | test.Fatal(a, " != ", b) 66 | } 67 | } 68 | 69 | // Assert uint8s equal 70 | func assertUint8sEqual(a uint8, b uint8, test *testing.T) { 71 | if a != b { 72 | test.Fatal(a, " != ", b) 73 | } 74 | } 75 | 76 | func assertFalse(a bool, test *testing.T) { 77 | if a == true { 78 | test.Fatal("Should have been false") 79 | } 80 | } 81 | 82 | func assertTrue(a bool, test *testing.T) { 83 | if a != true { 84 | test.Fatal("Should have been true") 85 | } 86 | } 87 | 88 | func assertNoError(e error, test *testing.T) { 89 | if e != nil { 90 | test.Fatal("Should have been no error, was ", e) 91 | } 92 | } 93 | 94 | func assertError(e error, expected error, test *testing.T) { 95 | if e != expected { 96 | test.Fatal("Should have been ", expected, " was ", e) 97 | } 98 | } 99 | 100 | // Assert two strings are equal 101 | func assertStringsEqual(a string, b string, test *testing.T) { 102 | if a != b { 103 | test.Fatal(a, " != ", b) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /midi_values.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | // Constants and values. 11 | 12 | package midi 13 | 14 | // SMF format 15 | const ( 16 | SingleMultiTrackChannel = 0 17 | SimultaneousTracks = 1 18 | SequentialTracks = 2 19 | ) 20 | 21 | // Time code formats used in HeaderData.TimeFormat 22 | const ( 23 | MetricalTimeFormat = iota 24 | TimeCodeTimeFormat = iota 25 | ) 26 | 27 | // Supplied to KeySignature 28 | const ( 29 | MajorMode = 0 30 | MinorMode = 1 31 | ) 32 | 33 | type KeySignatureMode uint8 34 | 35 | const ( 36 | DegreeC = 0 37 | DegreeCs = 1 38 | DegreeDf = DegreeCs 39 | DegreeD = 2 40 | DegreeDs = 3 41 | DegreeEf = DegreeDs 42 | DegreeE = 4 43 | DegreeF = 5 44 | DegreeFs = 6 45 | DegreeGf = DegreeFs 46 | DegreeG = 7 47 | DegreeGs = 8 48 | DegreeAf = DegreeGs 49 | DegreeA = 9 50 | DegreeAs = 10 51 | DegreeBf = DegreeAs 52 | DegreeB = 11 53 | DegreeCf = DegreeB 54 | ) 55 | 56 | type ScaleDegree uint8 57 | -------------------------------------------------------------------------------- /mocks.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Mocks implementations for testing. 12 | * In order to check that the right calls are made to the LexerCallback by the Lexer, a mock callback is pssed to it during tests. 13 | */ 14 | 15 | package midi 16 | 17 | import ( 18 | "io" 19 | ) 20 | 21 | // A mock implementation of LexerCallback that does nothing. 22 | type MockLexerCallback struct{} 23 | 24 | func (*MockLexerCallback) Header(header HeaderData) {} 25 | func (*MockLexerCallback) Track(header ChunkHeader) {} 26 | func (*MockLexerCallback) Began() {} 27 | func (*MockLexerCallback) Finished() {} 28 | func (*MockLexerCallback) ErrorReading() {} 29 | func (*MockLexerCallback) ErrorOpeningFile() {} 30 | func (*MockLexerCallback) NoteOff(channel uint8, pitch uint8, velocity uint8, time uint32) {} 31 | func (*MockLexerCallback) NoteOn(channel uint8, pitch uint8, velocity uint8, time uint32) {} 32 | func (*MockLexerCallback) PolyphonicAfterTouch(channel uint8, pitch uint8, pressure uint8, time uint32) { 33 | } 34 | func (*MockLexerCallback) ControlChange(channel uint8, controller uint8, value uint8, time uint32) {} 35 | func (*MockLexerCallback) ProgramChange(channel uint8, program uint8, time uint32) {} 36 | func (*MockLexerCallback) ChannelAfterTouch(channel uint8, value uint8, time uint32) {} 37 | func (*MockLexerCallback) PitchWheel(channel uint8, value int16, absValue uint16, time uint32) {} 38 | func (*MockLexerCallback) TimeCodeQuarter(messageType uint8, values uint8, time uint32) {} 39 | func (*MockLexerCallback) Tempo(bpm uint32, microsecondsPerCrotchet uint32, time uint32) {} 40 | func (*MockLexerCallback) SongPositionPointer(beats uint16, time uint32) {} 41 | func (*MockLexerCallback) SongSelect(song uint8, time uint32) {} 42 | func (*MockLexerCallback) Undefined1(time uint32) {} 43 | func (*MockLexerCallback) Undefined2(time uint32) {} 44 | func (*MockLexerCallback) TuneRequest(time uint32) {} 45 | func (*MockLexerCallback) TimingClock(time uint32) {} 46 | func (*MockLexerCallback) Undefined3(time uint32) {} 47 | func (*MockLexerCallback) Start(time uint32) {} 48 | func (*MockLexerCallback) Continue(time uint32) {} 49 | func (*MockLexerCallback) Stop(time uint32) {} 50 | func (*MockLexerCallback) Undefined4(time uint32) {} 51 | func (*MockLexerCallback) ActiveSensing(time uint32) {} 52 | func (*MockLexerCallback) Reset(time uint32) {} 53 | func (*MockLexerCallback) Done(time uint32) {} 54 | 55 | func (*MockLexerCallback) SequenceNumber(channel uint8, number uint16, numberGiven bool, time uint32) { 56 | } 57 | func (*MockLexerCallback) Text(channel uint8, text string, time uint32) {} 58 | func (*MockLexerCallback) CopyrightText(channel uint8, text string, time uint32) {} 59 | func (*MockLexerCallback) SequenceName(channel uint8, text string, time uint32) {} 60 | func (*MockLexerCallback) TrackInstrumentName(channel uint8, text string, time uint32) {} 61 | func (*MockLexerCallback) LyricText(channel uint8, text string, time uint32) {} 62 | func (*MockLexerCallback) MarkerText(channel uint8, text string, time uint32) {} 63 | func (*MockLexerCallback) CuePointText(channel uint8, text string, time uint32) {} 64 | func (*MockLexerCallback) EndOfTrack(channel uint8, time uint32) {} 65 | func (*MockLexerCallback) TimeSignature(numerator uint8, denomenator uint8, clocksPerClick uint8, demiSemiQuaverPerQuarter uint8, time uint32) { 66 | } 67 | func (*MockLexerCallback) KeySignature(key ScaleDegree, mode KeySignatureMode, sharpsOrFlats int8) { 68 | 69 | } 70 | 71 | // A mock implementation of LexerCallback that counts each method call and stores the most recent values, 72 | // so that calls can be verified. 73 | type CountingLexerCallback struct { 74 | 75 | // Callback counts 76 | header int 77 | track int 78 | began int 79 | finished int 80 | errorReading int 81 | errorOpeningFile int 82 | noteOff int 83 | noteOn int 84 | polyphonicAfterTouch int 85 | controlChange int 86 | programChange int 87 | channelAfterTouch int 88 | pitchWheel int 89 | timeCodeQuarter int 90 | songPositionPointer int 91 | songSelect int 92 | undefined1 int 93 | undefined2 int 94 | tuneRequest int 95 | timingClock int 96 | undefined3 int 97 | start int 98 | continue_ int 99 | stop int 100 | undefined4 int 101 | activeSensing int 102 | reset int 103 | done int 104 | endOfTrack int 105 | text int 106 | copyrightText int 107 | sequenceName int 108 | trackInstrumentName int 109 | lyricText int 110 | markerText int 111 | cuePointText int 112 | sequenceNumber int 113 | tempo int 114 | 115 | // Most recent values 116 | headerData HeaderData 117 | chunkHeader ChunkHeader 118 | pitch uint8 119 | channel uint8 120 | time uint32 121 | velocity uint8 122 | pressure uint8 123 | textValue string 124 | 125 | pitchWheelValue int16 126 | pitchWheelValueAbsolute uint16 127 | sequenceNumberGiven bool 128 | sequenceNumberValue uint16 129 | 130 | // Time sig args 131 | numerator uint8 132 | denomenator uint8 133 | clocksPerClick uint8 134 | demiSemiQuaverPerQuarter uint8 135 | 136 | // Tempo 137 | 138 | microsecondsPerCrotchet uint32 139 | bpm uint32 140 | } 141 | 142 | func (cbk *CountingLexerCallback) Header(header HeaderData) { cbk.header++; cbk.headerData = header } 143 | func (cbk *CountingLexerCallback) Track(header ChunkHeader) { cbk.track++; cbk.chunkHeader = header } 144 | func (cbk *CountingLexerCallback) Began() { cbk.began++ } 145 | func (cbk *CountingLexerCallback) Finished() { cbk.finished++ } 146 | func (cbk *CountingLexerCallback) ErrorReading() { cbk.errorReading++ } 147 | func (cbk *CountingLexerCallback) ErrorOpeningFile() { cbk.errorOpeningFile++ } 148 | func (cbk *CountingLexerCallback) NoteOff(channel uint8, pitch uint8, velocity uint8, time uint32) { 149 | cbk.noteOff++ 150 | cbk.pitch = pitch 151 | cbk.channel = channel 152 | cbk.velocity = velocity 153 | cbk.time = time 154 | } 155 | func (cbk *CountingLexerCallback) NoteOn(channel uint8, pitch uint8, velocity uint8, time uint32) { 156 | cbk.noteOn++ 157 | cbk.pitch = pitch 158 | cbk.channel = channel 159 | cbk.velocity = velocity 160 | cbk.time = time 161 | } 162 | func (cbk *CountingLexerCallback) PolyphonicAfterTouch(channel uint8, pitch uint8, pressure uint8, time uint32) { 163 | cbk.polyphonicAfterTouch++ 164 | cbk.channel = channel 165 | cbk.pitch = pitch 166 | cbk.pressure = pressure 167 | cbk.time = time 168 | } 169 | 170 | func (cbk *CountingLexerCallback) Tempo(bpm uint32, microsecondsPerCrotchet uint32, time uint32) { 171 | // TODO USE 172 | cbk.tempo++ 173 | cbk.microsecondsPerCrotchet = microsecondsPerCrotchet 174 | cbk.bpm = bpm 175 | cbk.time = time 176 | } 177 | 178 | func (cbk *CountingLexerCallback) ControlChange(channel uint8, controller uint8, value uint8, time uint32) { 179 | cbk.controlChange++ 180 | } 181 | func (cbk *CountingLexerCallback) ProgramChange(channel uint8, program uint8, time uint32) { 182 | cbk.programChange++ 183 | } 184 | func (cbk *CountingLexerCallback) ChannelAfterTouch(channel uint8, pressure uint8, time uint32) { 185 | cbk.channelAfterTouch++ 186 | cbk.channel = channel 187 | cbk.pressure = pressure 188 | cbk.time = time 189 | } 190 | func (cbk *CountingLexerCallback) PitchWheel(channel uint8, value int16, absValue uint16, time uint32) { 191 | cbk.pitchWheel++ 192 | cbk.pitchWheelValue = value 193 | cbk.pitchWheelValueAbsolute = absValue 194 | cbk.time = time 195 | cbk.channel = channel 196 | } 197 | func (cbk *CountingLexerCallback) TimeCodeQuarter(messageType uint8, values uint8, time uint32) { 198 | cbk.timeCodeQuarter++ 199 | } 200 | func (cbk *CountingLexerCallback) SongPositionPointer(beats uint16, time uint32) { 201 | cbk.songPositionPointer++ 202 | } 203 | func (cbk *CountingLexerCallback) SongSelect(song uint8, time uint32) { cbk.songSelect++ } 204 | func (cbk *CountingLexerCallback) Undefined1(time uint32) { cbk.undefined1++ } 205 | func (cbk *CountingLexerCallback) Undefined2(time uint32) { cbk.undefined2++ } 206 | func (cbk *CountingLexerCallback) TuneRequest(time uint32) { cbk.tuneRequest++ } 207 | func (cbk *CountingLexerCallback) TimingClock(time uint32) { cbk.timingClock++ } 208 | func (cbk *CountingLexerCallback) Undefined3(time uint32) { cbk.undefined3++ } 209 | func (cbk *CountingLexerCallback) Start(time uint32) { cbk.start++ } 210 | func (cbk *CountingLexerCallback) Continue(time uint32) { cbk.continue_++ } 211 | func (cbk *CountingLexerCallback) Stop(time uint32) { cbk.stop++ } 212 | func (cbk *CountingLexerCallback) Undefined4(time uint32) { cbk.undefined4++ } 213 | func (cbk *CountingLexerCallback) ActiveSensing(time uint32) { cbk.activeSensing++ } 214 | func (cbk *CountingLexerCallback) Reset(time uint32) { cbk.reset++ } 215 | func (cbk *CountingLexerCallback) Done(time uint32) { cbk.done++ } 216 | 217 | func (cbk *CountingLexerCallback) SequenceNumber(channel uint8, number uint16, numberGiven bool, time uint32) { 218 | cbk.sequenceNumber++ 219 | cbk.time = time 220 | cbk.sequenceNumberValue = number 221 | cbk.sequenceNumberGiven = numberGiven 222 | } 223 | func (cbk *CountingLexerCallback) Text(channel uint8, text string, time uint32) { 224 | cbk.text++ 225 | cbk.textValue = text 226 | cbk.time = time 227 | } 228 | func (cbk *CountingLexerCallback) CopyrightText(channel uint8, text string, time uint32) { 229 | cbk.copyrightText++ 230 | cbk.textValue = text 231 | cbk.time = time 232 | } 233 | func (cbk *CountingLexerCallback) SequenceName(channel uint8, text string, time uint32) { 234 | cbk.sequenceName++ 235 | cbk.textValue = text 236 | cbk.time = time 237 | } 238 | func (cbk *CountingLexerCallback) TrackInstrumentName(channel uint8, text string, time uint32) { 239 | cbk.trackInstrumentName++ 240 | cbk.textValue = text 241 | cbk.time = time 242 | } 243 | func (cbk *CountingLexerCallback) LyricText(channel uint8, text string, time uint32) { 244 | cbk.lyricText++ 245 | cbk.textValue = text 246 | cbk.time = time 247 | } 248 | func (cbk *CountingLexerCallback) MarkerText(channel uint8, text string, time uint32) { 249 | cbk.markerText++ 250 | cbk.textValue = text 251 | cbk.time = time 252 | } 253 | func (cbk *CountingLexerCallback) CuePointText(channel uint8, text string, time uint32) { 254 | cbk.cuePointText++ 255 | cbk.textValue = text 256 | cbk.time = time 257 | } 258 | func (cbk *CountingLexerCallback) EndOfTrack(channel uint8, time uint32) { 259 | cbk.endOfTrack++ 260 | cbk.time = time 261 | } 262 | func (cbk *CountingLexerCallback) TimeSignature(numerator uint8, denomenator uint8, clocksPerClick uint8, demiSemiQuaverPerQuarter uint8, time uint32) { 263 | cbk.numerator = numerator 264 | cbk.denomenator = denomenator 265 | cbk.clocksPerClick = clocksPerClick 266 | cbk.demiSemiQuaverPerQuarter = demiSemiQuaverPerQuarter 267 | cbk.time = time 268 | } 269 | 270 | func (*CountingLexerCallback) KeySignature(key ScaleDegree, mode KeySignatureMode, sharpsOrFlats int8) { 271 | // TODO fill out when tests written. 272 | } 273 | 274 | // MockReadSeeker is a mock Reader and Seeker. Constructed with data, behaves as a file reader. 275 | // This is used to pass MIDI data to the Lexer and also to the MIDI value parsing functions. 276 | type MockReadSeeker struct { 277 | data *[]byte 278 | position int64 279 | } 280 | 281 | // NewMockReadSeeker creates a new MockReadSeeker object backed by the given byte array data. 282 | func NewMockReadSeeker(data *[]byte) *MockReadSeeker { 283 | return &MockReadSeeker{data: data} 284 | } 285 | 286 | // Read fills the given buffer, returning the number of bytes and an error. 287 | func (reader *MockReadSeeker) Read(p []byte) (n int, err error) { 288 | var amount = int64(len(p)) 289 | var maxAmount = int64(len(*reader.data)) - reader.position 290 | 291 | // Don't read past the end 292 | if amount > maxAmount { 293 | amount = maxAmount 294 | } 295 | 296 | copy(p, (*reader.data)[reader.position:reader.position+amount]) 297 | reader.position += amount 298 | return int(amount), nil 299 | } 300 | 301 | // Seek sets the offset for the next Read or Write to offset, interpreted according to the value of `whence`: 302 | // 0 means relative to the origin of the file, 1 means relative to the current offset, and 2 means relative to the end. 303 | // Seek returns the new offset and an Error, if any. 304 | func (reader *MockReadSeeker) Seek(offset int64, whence int) (ret int64, err error) { 305 | switch whence { 306 | case 0: 307 | { 308 | if offset > int64(len(*reader.data)) { 309 | return -1, io.EOF 310 | } 311 | 312 | reader.position = offset 313 | 314 | return reader.position, nil 315 | } 316 | case 1: 317 | { 318 | if offset+reader.position > int64(len(*reader.data)) { 319 | return -1, io.EOF 320 | } 321 | 322 | reader.position += offset 323 | 324 | return reader.position, nil 325 | } 326 | case 2: 327 | { 328 | if offset > int64(len(*reader.data)) { 329 | return -1, io.EOF 330 | } 331 | 332 | reader.position = int64(len(*reader.data)) - offset 333 | 334 | return reader.position, nil 335 | } 336 | } 337 | 338 | return 339 | } 340 | -------------------------------------------------------------------------------- /music.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Functions that deal with musical concepts. 12 | */ 13 | 14 | package midi 15 | 16 | // import "fmt" 17 | 18 | // Taking a signed number of sharps or flats (positive for sharps, negative for flats) and a mode (0 for major, 1 for minor) 19 | // decide the key signature. 20 | func keySignatureFromSharpsOrFlats(sharpsOrFlats int8, mode uint8) (key ScaleDegree, resultMode KeySignatureMode) { 21 | // 0 is C. 22 | var tmp int = int(DegreeC + sharpsOrFlats*7) 23 | 24 | // Relative Minor. 25 | if mode == MinorMode { 26 | tmp -= 3 27 | } 28 | 29 | // Clamp to Octave 0-11. 30 | for tmp < 0 { 31 | tmp += 12 32 | } 33 | 34 | tmp = tmp % 12 35 | 36 | resultMode = KeySignatureMode(mode) 37 | key = ScaleDegree(tmp) 38 | 39 | return 40 | } 41 | -------------------------------------------------------------------------------- /music_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Joe Wass. All rights reserved. 2 | // Use of this source code is governed by the MIT license 3 | // which can be found in the LICENSE file. 4 | 5 | // MIDI package 6 | // A package for reading Standard Midi Files, written in Go. 7 | // Joe Wass 2012 8 | // joe@afandian.com 9 | 10 | /* 11 | * Tests for lexer. 12 | * Check that the state transitions work fine and that the lexer can cope with real streams of MIDI data. 13 | */ 14 | 15 | package midi 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func keyTest(sharpsFlats int8, mode KeySignatureMode, expectedDegree ScaleDegree, expectedMode KeySignatureMode, message string, t *testing.T) { 22 | gotKey, gotMode := keySignatureFromSharpsOrFlats(sharpsFlats, uint8(mode)) 23 | if !((gotKey == expectedDegree) && (gotMode == expectedMode)) { 24 | t.Fatal("Fail", message, "key", gotKey, "e", expectedDegree, "mode", gotMode, "e", expectedMode) 25 | 26 | } 27 | } 28 | 29 | // Test lots of key signatures. 30 | // Might as well test every possible input. 31 | func TestKeySignatureTest(t *testing.T) { 32 | keyTest( 33 | 0, MajorMode, 34 | DegreeC, MajorMode, 35 | "C Major", t, 36 | ) 37 | 38 | keyTest( 39 | 0, MinorMode, 40 | DegreeA, MinorMode, 41 | "A Minor", t, 42 | ) 43 | 44 | // Sharps 45 | keyTest( 46 | 1, MajorMode, 47 | DegreeG, MajorMode, 48 | "G Major", t, 49 | ) 50 | 51 | keyTest( 52 | 1, MinorMode, 53 | DegreeE, MinorMode, 54 | "E Minor", t, 55 | ) 56 | 57 | keyTest( 58 | 2, MajorMode, 59 | DegreeD, MajorMode, 60 | "D Major", t, 61 | ) 62 | 63 | keyTest( 64 | 2, MinorMode, 65 | DegreeB, MinorMode, 66 | "B Minor", t, 67 | ) 68 | 69 | keyTest( 70 | 3, MajorMode, 71 | DegreeA, MajorMode, 72 | "A Major", t, 73 | ) 74 | 75 | keyTest( 76 | 3, MinorMode, 77 | DegreeFs, MinorMode, 78 | "Fs Minor", t, 79 | ) 80 | 81 | keyTest( 82 | 4, MajorMode, 83 | DegreeE, MajorMode, 84 | "E Major", t, 85 | ) 86 | 87 | keyTest( 88 | 4, MinorMode, 89 | DegreeCs, MinorMode, 90 | "Cs Minor", t, 91 | ) 92 | 93 | keyTest( 94 | 5, MajorMode, 95 | DegreeB, MajorMode, 96 | "B Major", t, 97 | ) 98 | 99 | keyTest( 100 | 5, MinorMode, 101 | DegreeGs, MinorMode, 102 | "Gs Minor", t, 103 | ) 104 | 105 | keyTest( 106 | 6, MajorMode, 107 | DegreeFs, MajorMode, 108 | "Fs Major", t, 109 | ) 110 | 111 | keyTest( 112 | 6, MinorMode, 113 | DegreeDs, MinorMode, 114 | "Ds Minor", t, 115 | ) 116 | 117 | keyTest( 118 | 7, MajorMode, 119 | DegreeCs, MajorMode, 120 | "Cs Major", t, 121 | ) 122 | 123 | keyTest( 124 | 7, MinorMode, 125 | DegreeAs, MinorMode, 126 | "As Minor", t, 127 | ) 128 | 129 | // Flats 130 | keyTest( 131 | -1, MajorMode, 132 | DegreeF, MajorMode, 133 | "F Major", t, 134 | ) 135 | 136 | keyTest( 137 | -1, MinorMode, 138 | DegreeD, MinorMode, 139 | "D Minor", t, 140 | ) 141 | 142 | keyTest( 143 | -2, MajorMode, 144 | DegreeBf, MajorMode, 145 | "Bf Major", t, 146 | ) 147 | 148 | keyTest( 149 | -2, MinorMode, 150 | DegreeG, MinorMode, 151 | "G Minor", t, 152 | ) 153 | 154 | keyTest( 155 | -3, MajorMode, 156 | DegreeEf, MajorMode, 157 | "Ef Major", t, 158 | ) 159 | 160 | keyTest( 161 | -3, MinorMode, 162 | DegreeC, MinorMode, 163 | "C Minor", t, 164 | ) 165 | 166 | keyTest( 167 | -4, MajorMode, 168 | DegreeAf, MajorMode, 169 | "Af Major", t, 170 | ) 171 | 172 | keyTest( 173 | -4, MinorMode, 174 | DegreeF, MinorMode, 175 | "F Minor", t, 176 | ) 177 | 178 | keyTest( 179 | -5, MajorMode, 180 | DegreeDf, MajorMode, 181 | "Df Major", t, 182 | ) 183 | 184 | keyTest( 185 | -5, MinorMode, 186 | DegreeBf, MinorMode, 187 | "Bf Minor", t, 188 | ) 189 | 190 | keyTest( 191 | -6, MajorMode, 192 | DegreeGf, MajorMode, 193 | "Gf Major", t, 194 | ) 195 | 196 | keyTest( 197 | -6, MinorMode, 198 | DegreeEf, MinorMode, 199 | "Ef Minor", t, 200 | ) 201 | 202 | keyTest( 203 | -7, MajorMode, 204 | DegreeCf, MajorMode, 205 | "Cf Major", t, 206 | ) 207 | 208 | keyTest( 209 | -7, MinorMode, 210 | DegreeAf, MinorMode, 211 | "Af Minor", t, 212 | ) 213 | } 214 | -------------------------------------------------------------------------------- /states.dot: -------------------------------------------------------------------------------- 1 | // States of the Lexer. Render this with graphviz if you want. 2 | digraph lexer_state { 3 | rankdir=LR; 4 | size="8,5" 5 | node [shape = doublecircle]; 6 | ExpectHeader; 7 | 8 | node [shape = circle]; 9 | ExpectChunk; 10 | 11 | ExpectHeader -> ExpectChunk [label="Began(), Header()"]; 12 | ExpectChunk -> ExpectChunk [label="Skip unrecognised chunk type"]; 13 | ExpectChunk -> ExpectTrackEvent [label="Track()"] 14 | 15 | ExpectTrackEvent -> ExpectTrackEvent [label="Track event"] 16 | 17 | ExpectTrackEvent -> ExpectChunk [label="End of chunk"] 18 | } --------------------------------------------------------------------------------