// ParseNotesToChord takes a collection of notes (e.g. "CEG") plus the base octave // and returns a sound of them all being played together. func ParseNotesToChord(notes string, base uint) s.Sound { asSounds := make([]s.Sound, 0, len(notes)) var sound s.Sound for at := 0; at < len(notes); { sound, at = noteToSound(notes, at, base) asSounds = append(asSounds, sound) } return s.SumSounds(asSounds...) }
// A chord of notes in the treble clef, 0 = B, then notes up and down (e.g. -4 = E, 4 = F) // in the proper key (Db major), with +/- 0.5 signifying a sharp or flat. func notesT(quaverCount float64, notes []float64) s.Sound { sounds := make([]s.Sound, len(notes), len(notes)) for i, note := range notes { sounds[i] = noteTMidi(note, quaverCount) } return s.SumSounds(sounds...) }
func SampleNormalSum() s.Sound { // Includes: TimedSound and MidiToSound return s.SumSounds( s.NewTimedSound(u.MidiToSound(55), 333), s.NewTimedSound(u.MidiToSound(59), 333), s.NewTimedSound(u.MidiToSound(62), 333), s.NewTimedSound(u.MidiToSound(65), 333), s.NewTimedSound(u.MidiToSound(67), 333), ) }
// ParseChord converts a chord string (e.g. "G#sus4") and base octave into a // Sound that contains the notes in the chord. func ParseChord(chord string, base uint) s.Sound { baseMidi, at := noteToMidi(chord, 0) baseMidi += 12 * int(base+1) modifier := chord[at:] var offsets []int // A subset of these, converted to integer notation: // https://en.wikibooks.org/wiki/Music_Theory/Complete_List_of_Chord_Patterns switch modifier { case "": offsets = []int{0, 4, 7} case "5": offsets = []int{0, 7} case "m": offsets = []int{0, 3, 7} case "dom": fallthrough case "7": offsets = []int{0, 4, 7, 10} case "M7": offsets = []int{0, 4, 7, 11} case "m7": offsets = []int{0, 3, 7, 10} case "6": offsets = []int{0, 4, 7, 9} case "m6": offsets = []int{0, 3, 7, 9} case "dim": offsets = []int{0, 3, 6} case "sus4": offsets = []int{0, 5, 7} case "sus2": offsets = []int{0, 2, 7} case "aug": offsets = []int{0, 4, 8} default: panic("Unsupported chord modifier: " + modifier) } asSounds := make([]s.Sound, len(offsets), len(offsets)) for i, offset := range offsets { asSounds[i] = MidiToSound(offset + baseMidi) } return s.SumSounds(asSounds...) }
// GuitarChord converts a standard guitar representation (e.g. "2x0232") // into the sound of those notes being played, assuming standard tuning. func GuitarChord(chord string) s.Sound { // Standard guitar tuning: EADGBE stringMidi := []int{40, 45, 50, 55, 59, 64} noteMidi := []int{} for i, fret := range chord { if '0' <= fret && fret <= '9' { offset, _ := strconv.Atoi(fmt.Sprintf("%c", fret)) noteMidi = append(noteMidi, stringMidi[i]+offset) } } asSounds := make([]s.Sound, len(noteMidi), len(noteMidi)) for i, offset := range noteMidi { asSounds[i] = MidiToSound(offset) } return s.SumSounds(asSounds...) }
// Shephard tones func shephardTones() s.Sound { octaves := 5 base, mid := 110.0, 155.563491861 tones := 2 * octaves bases := make([]float64, tones, tones) for i := 0; i < octaves; i++ { bases[2*i] = base * float64(unsafeShift(i)) bases[2*i+1] = mid * float64(unsafeShift(i)) } secondsPerOctave := 10 maxHz := bases[0] * float64(unsafeShift(octaves)) downOctaves := 1.0 / float64(unsafeShift(octaves)) samplesPerOctave := int(secondsPerOctave * s.CyclesPerSecond) octavesPerSample := 1.0 / float64(samplesPerOctave) channels := make([]chan []float64, tones, tones) for i := 0; i < tones; i++ { channels[i] = make(chan []float64) } go func() { for { for sample := 0; sample < octaves*samplesPerOctave; sample++ { for i := 0; i < tones; i++ { hz := bases[i] * math.Pow(2.0, float64(sample)*octavesPerSample) if hz >= maxHz { hz *= downOctaves } channels[i] <- []float64{hz, gaussianAmplitude(hz, bases[0], maxHz)} } } } }() sounds := make([]s.Sound, tones, tones) for i, v := range channels { sounds[i] = s.NewHzFromChannelWithAmplitude(v) } return s.SumSounds(sounds...) }
func main() { runtime.GOMAXPROCS(2) fmt.Printf("Open the serial cable...\n") port, err := serial.OpenPort(&serial.Config{Name: findArduino(), Baud: 9600}) if err != nil { log.Fatal(err) } time.Sleep(1 * time.Second) fmt.Printf("Generate the tone definition...\n") player := &Player{} toPlay := s.SumSounds( s.NewHzFromChannel(player.sampledToneGenerator()), s.NewSineWave(hzC/2.0), ) buf := make([]byte, 128) startTime, readCount := time.Now(), 0 for { if _, err := port.Read(buf); err != nil { if readCount == 0 { startTime = time.Now() player.Start(toPlay) } readCount++ player.currentValue = float64(buf[0]) / 256.0 if readCount%100000 == 0 { fmt.Printf("Value = %f\n", player.currentValue) } if readCount%1000000 == 0 { seconds := time.Since(startTime).Seconds() fmt.Printf("Read %d in %f seconds, at a rate of %f Hz\n", readCount, seconds, float64(readCount)/seconds) } } } }