Example #1
1
// 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...)
}
Example #2
0
// 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...)
}
Example #3
0
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),
	)
}
Example #4
0
// 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...)
}
Example #5
0
// 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...)
}
Example #6
0
// 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...)
}
Example #7
0
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)
			}
		}
	}
}