-
Notifications
You must be signed in to change notification settings - Fork 0
/
midi.go
49 lines (39 loc) · 1.71 KB
/
midi.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package sound
import (
"math"
"time"
)
// make a Note from a Midi note number, a time.Duration and a volume percentage.
// duration set to the closest whole number of the source's Period(), shorter than the length
func NewMidiNote(noteNumber int8, length time.Duration, volume float64) Note {
return NewNote(NewMidiTone(noteNumber, volume), length)
}
// make a Tone, a continuous Sine wave, from a Midi-note number and a volume.
func NewMidiTone(noteNumber int8, volume float64) Tone {
return NewTone(PeriodFromMilliHz(FrequencyMilliHzMidi(noteNumber)), volume)
}
// Tone made from a Sound, sped-up or slowed down, to fit a Midi note period, and looped.
// put simply, it is a Tone made from a sample of a single cycle.
// samples can come from a file, using PCM decode function, which can be a standard wave encoded instrument sample.
func NewSampledMidiTone(noteNumber int8, sample Sound, volume float64) Tone {
return NewSampledTone(PeriodFromMilliHz(FrequencyMilliHzMidi(noteNumber)), sample, volume)
}
const baseNoteNumber = 69
const baseFrequency = 440000 // mHz
// frequency as an int, in 1/1000 of a Hz units
func FrequencyMilliHzMidi(noteNumber int8) uint {
return uint(baseFrequency * math.Pow(2, float64(noteNumber-baseNoteNumber)/12))
}
// frequency as in int, in 1/1000 of a Hz units
func FrequencyMilliHz(octave, semiNote int8) uint {
return FrequencyMilliHzMidi(MidiNoteNumber(octave, semiNote))
}
func MidiNoteNumber(octave, semiNote int8) int8 {
return (octave+1)*12 + semiNote
}
func MidiNote(octave, semiNote string) int8 {
return (octaveNumber[octave]+1)*12 + semitoneNumber[semiNote]
}
func NameFromMidiNoteNumber(noteNumber int8) string {
return semitonePrefixes[noteNumber/12] + semitones[noteNumber%12]
}