func genMelodyArpeggio(ctx sound.Context, scale music.Scale, n int) (stream chan float64) { var parts []chan float64 root := scale.Root for i := 0; i < n; i++ { if i+1 < n && rand.Float64() < 0.01 { from := scale.Root nextNote(&scale, root) to := scale.Root nextNote(&scale, root) part := genSlideEnvelope(ctx, from, to, time.Second/4) parts = append(parts, part) } else { note := scale.Root nextNote(&scale, root) part := ctx.TakeDuration(ctx.Const(note.Frequency()), time.Second/8, false) parts = append(parts, part) } } return ctx.Append(parts...) }
func FrequencyEnvelope(ctx sound.Context) (stream chan float64) { var parts []chan float64 for _, note := range Notes { part := ctx.TakeDuration(ctx.Const(note.Frequency()), NoteDuration, false) parts = append(parts, part) } return ctx.Append(parts...) }
func FilterFormant(ctx sound.Context, input chan float64, centre, q, gain float64) (output chan float64) { lowerCutoff := (centre - (centre / q)) / 0.772 upperCutoff := (centre + (centre / q)) / 1.29 input, formant := ctx.Fork2(input) formant = filter.Chebyshev(ctx, formant, filter.HighPass, lowerCutoff, 0.5, 2) formant = filter.Chebyshev(ctx, formant, filter.LowPass, upperCutoff, 0.5, 2) formant = ctx.Mul(formant, ctx.Const(gain-1.0)) return ctx.Add(input, formant) }
func playBassSynth(ctx sound.Context, freqInput chan float64) (stream chan float64) { return ctx.Add( ctx.Mul( ctx.Square( freqInput, ctx.Const(0.8), ), ), ) }
func Generate(ctx sound.Context) (left, right chan float64) { stream := ctx.TakeDuration( ctx.Mul( ctx.Sine( FrequencyEnvelope(ctx), ), ctx.Const(0.7), ), NoteDuration*8, true, // Wait for a zero crossing ) return ctx.Fork2(stream) }
func Generate(ctx sound.Context) (left, right chan float64) { treble := SequenceTreble(ctx).Play() bass := SequenceBass(ctx).Play() trebleLeft, trebleRight := ctx.Fork2(treble) bassLeft, bassRight := ctx.Fork2(bass) left = ctx.TakeDuration( ctx.Add( ctx.Mul(trebleLeft, ctx.Const(0.3)), ctx.Mul(bassLeft, ctx.Const(0.4)), ), NoteDuration*NumBars*3+time.Second*2, true, ) right = ctx.TakeDuration( ctx.Add( ctx.Mul(trebleRight, ctx.Const(0.4)), ctx.Mul(bassRight, ctx.Const(0.3)), ), NoteDuration*NumBars*3+time.Second*2, true, ) return left, right }
func PlayBassNote(ctx sound.Context, freqInput chan float64, duration time.Duration) (stream chan float64) { return ctx.TakeDuration( ctx.Add( ctx.Mul( ctx.Square( freqInput, ctx.Const(0.8), ), ), ), duration, true, ) }
func SequenceBass(ctx sound.Context) (seq *sound.Sequencer) { melody := GenerateBassMelody() seq = sound.NewSequencer(ctx) var pos time.Duration for i := 0; i < NumBars; i++ { if rand.Float64() < 0.05 { freqInput := ctx.Const((<-melody).Frequency()) note := PlayBassNote(ctx, freqInput, NoteDuration*3) seq.Add(pos, note) } else if rand.Float64() < 0.05 { freqInput1, freqInput2 := ctx.Fork2(ctx.Const((<-melody).Frequency())) note1 := PlayBassNote(ctx, freqInput1, NoteDuration) note2 := PlayBassNote(ctx, freqInput2, NoteDuration) seq.Add(pos, note1) seq.Add(pos+NoteDuration*2, note2) } else { freqInput := ctx.Const((<-melody).Frequency()) note := PlayBassNote(ctx, freqInput, NoteDuration) seq.Add(pos, note) } pos += NoteDuration * 3 } return seq }
func SequenceTreble(ctx sound.Context) (seq *sound.Sequencer) { melody := GenerateTrebleMelody() seq = sound.NewSequencer(ctx) var pos time.Duration for i := 0; i < NumBars*3; i++ { var freqInput chan float64 var duration time.Duration if rand.Float64() < 0.01 { from := (<-melody).Frequency() to := (<-melody).Frequency() freqInput = ctx.LinearEnvelope( from, NoteDuration*1/5, from, NoteDuration*8/5, to, NoteDuration*1/5+time.Millisecond, to, ) duration = NoteDuration * 2 } else { freqInput = ctx.Const((<-melody).Frequency()) duration = NoteDuration } note := PlayTrebleNote(ctx, freqInput, duration) seq.Add(pos, note) pos += duration } return seq }
// input should be finite and preferably short (e.g. one cycle of a triangle wave) func KarplusStrong(ctx sound.Context, input chan float64, delaySamples uint, cutoff float64, decay float64) (output chan float64) { feedback := make(chan float64, ctx.StreamBufferSize) // Mix the input with the feedback. output = ctx.Add(input, feedback) // Fork off a copy of the output. output, outputCopy := ctx.Fork2(output) // The copy is first passed through a delay line... outputCopy = delay(ctx, outputCopy, delaySamples) // ...then filtered... //outputCopy = filter.Chebyshev(ctx, outputCopy, filter.LowPass, cutoff, 0.5, 2) outputCopy = filter.RC(ctx, outputCopy, filter.LowPass, cutoff) // ...and finally attenuated slightly. outputCopy = ctx.Mul(outputCopy, ctx.Const(decay)) // The filtered output copy is fed back into the system. go pipe(outputCopy, feedback) return output }
func playMelodySynth(ctx sound.Context, freqInput chan float64) (stream chan float64) { freqInput1, freqInput2 := ctx.Fork2(freqInput) return ctx.Add( ctx.Mul( ctx.Square( freqInput1, ctx.Const(0.5), ), ctx.Const(0.75), ), ctx.Mul( ctx.Saw( ctx.Mul(freqInput2, ctx.Const(2)), ), ctx.Const(0.25), ), ) }
func PlayTrebleNote(ctx sound.Context, freqInput chan float64, duration time.Duration) (stream chan float64) { freqInput1, freqInput2 := ctx.Fork2(freqInput) return ctx.TakeDuration( ctx.Add( ctx.Mul( ctx.Square( freqInput1, ctx.Const(0.5), ), ctx.Const(0.75), ), ctx.Mul( ctx.Saw( ctx.Mul(freqInput2, ctx.Const(2)), ), ctx.Const(0.25), ), ), duration, true, ) }
func KarplusStrongSaw(ctx sound.Context, frequency, cutoff, decay float64) (output chan float64) { delaySamples := uint((1.0 / frequency) * ctx.SampleRate) wave := ctx.Saw(ctx.Const(frequency)) input := ctx.Take(wave, delaySamples, true) return KarplusStrong(ctx, input, delaySamples, cutoff, decay) }
func Generate(ctx sound.Context) (left, right chan float64) { rand.Seed(time.Now().UnixNano()) melodyParts := make(chan chan float64) bassParts := make(chan chan float64) go func() { for { var octave int x := rand.Float64() if x < 0.3 { octave = 4 } else { octave = 5 } root := music.MakeNote(music.D, octave) scale := music.Scale{Root: root, Intervals: music.HarmonicMinor} var n int x = rand.Float64() if x < 0.2 { n = 3 } else if x < 0.4 { n = 6 } else if x < 0.6 { n = 12 } else if x < 0.8 { n = 18 } else { n = 24 } melodyParts <- genMelodyArpeggio(ctx, scale, n) } }() go func() { for { var octave int x := rand.Float64() if x < 0.3 { octave = 3 } else { octave = 2 } root := music.MakeNote(music.D, octave) scale := music.Scale{Root: root, Intervals: music.HarmonicMinor} var n int x = rand.Float64() if x < 0.2 { n = 3 } else if x < 0.4 { n = 6 } else if x < 0.6 { n = 9 } else if x < 0.8 { n = 12 } else { n = 18 } bassParts <- genBassArpeggio(ctx, scale, n) } }() melody := playMelodySynth(ctx, ctx.AppendStream(melodyParts)) bass := playBassSynth(ctx, ctx.AppendStream(bassParts)) melodyLeft, melodyRight := ctx.Fork2(melody) bassLeft, bassRight := ctx.Fork2(bass) left = ctx.TakeDuration( ctx.Add( ctx.Mul(melodyLeft, ctx.Const(0.4)), ctx.Mul(bassLeft, ctx.Const(0.6)), ), time.Second*300, true, ) right = ctx.TakeDuration( ctx.Add( ctx.Mul(melodyRight, ctx.Const(0.6)), ctx.Mul(bassRight, ctx.Const(0.4)), ), time.Second*300, true, ) return left, right }
func genBassArpeggio(ctx sound.Context, scale music.Scale, n int) (stream chan float64) { var parts []chan float64 root := scale.Root for i := 0; i < n; i++ { note := scale.Root nextNote(&scale, root) if rand.Float64() < 0.05 { part := ctx.TakeDuration( ctx.Mul( ctx.Const(note.Frequency()), ctx.Add( // Add a slight vibrato effect ctx.Mul( ctx.Sine(ctx.Const(24)), ctx.Const(0.01), ), ctx.Const(1.0), ), ), time.Second*3/8, false, ) parts = append(parts, part) } else if rand.Float64() < 0.05 { part1 := ctx.TakeDuration( ctx.Const(note.Frequency()), time.Second/8, false, ) part2 := ctx.TakeDuration( ctx.Const(note.Frequency()), time.Second/8, false, ) parts = append(parts, part1) parts = append(parts, ctx.TakeDuration(ctx.Silence(), time.Second/8, false)) parts = append(parts, part2) } else { part := ctx.TakeDuration( ctx.Const(note.Frequency()), time.Second/8, false, ) parts = append(parts, part) parts = append(parts, ctx.TakeDuration(ctx.Silence(), time.Second/4, false)) } } return ctx.Append(parts...) }