Skip to content

Commit

Permalink
2 examples using realtime and offline rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
mattetti committed Jan 4, 2017
1 parent 30e69fc commit aed9f58
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 5 deletions.
10 changes: 5 additions & 5 deletions cmd/main.go → examples/offline/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import (
)

var (
freqFlag = flag.Float64("freq", 440, "frequency to generate")
biteDepthFlag = flag.Int("biteDepth", 16, "bit size to use when generating the auid file")
durationFlag = flag.Int("duration", 4, "duration of the generated file")
formatFlag = flag.String("format", "wav", "the audio format of the output file")
freqFlag = flag.Float64("freq", 440, "frequency to generate")
bitDepthFlag = flag.Int("bitDepth", 16, "bit size to use when generating the auid file")
durationFlag = flag.Int("duration", 4, "duration of the generated file")
formatFlag = flag.String("format", "wav", "the audio format of the output file")
)

func main() {
Expand All @@ -29,7 +29,7 @@ func main() {

freq := *freqFlag
fs := 44100
biteDepth := *biteDepthFlag
biteDepth := *bitDepthFlag

osc := generator.NewOsc(generator.WaveSine, float64(freq), fs)
// our osc generates values from -1 to 1, we need to go back to PCM scale
Expand Down
123 changes: 123 additions & 0 deletions examples/realtime/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// demo package simulating a realtime generation and processing.
// Start the example from your terminal and type a letter + enter.
package main

import (
"bufio"
"fmt"
"log"
"math"
"os"
"os/signal"

"github.com/go-audio/audio"
"github.com/go-audio/generator"
"github.com/go-audio/transforms"
"github.com/gordonklaus/portaudio"
)

func main() {
bufferSize := 512

This comment has been minimized.

Copy link
@egonelbre

egonelbre Jan 4, 2017

This number should ideally be 64 or 32. (See http://www.rossbencina.com/code/real-time-audio-programming-101-time-waits-for-nothing)

Starting at 64 we can more easily see all the overhead of communication.

buf := &audio.FloatBuffer{
Data: make([]float64, bufferSize),
Format: audio.FormatMono4410016bLE,
}
currentNote := 440.0
osc := generator.NewOsc(generator.WaveSine, currentNote, buf.Format.SampleRate)
osc.Amplitude = 1

sig := make(chan os.Signal, 1)
signal.Notify(sig, os.Interrupt, os.Kill)

gainControl := 0.0

This comment has been minimized.

Copy link
@egonelbre

egonelbre Jan 4, 2017

Not thread-safe.

currentVol := osc.Amplitude

fmt.Println(`This is a demo, press a key followed by enter, the played note should change.
Use the - and + keys follow by enter to decrease or increase the volume\nPress q or ctrl-c to exit.
Note that the sound will come out of your default sound card.`)

scanner := bufio.NewScanner(os.Stdin)
go func() {
for scanner.Scan() {
if len(scanner.Text()) > 0 {
k := scanner.Text()[0]
switch k {
case 'q':
sig <- os.Interrupt
case '+':
gainControl += 0.10
case '-':
gainControl -= 0.10

This comment has been minimized.

Copy link
@egonelbre

egonelbre Jan 4, 2017

Immediate changes in gain cause clicking. Most of the time you would want to smooth it out. At 0.1 increments, it's probably not that noticeable.

default:
v := float64(math.Abs(float64(int(k - 100))))
currentNote = 440.0 * math.Pow(2, (v)/12.0)
fmt.Printf("switching oscillator to %.2f Hz\n", currentNote)
if currentNote > 22000 {
currentNote = 440.0
}
osc.SetFreq(currentNote)
}
}
}
}()

// Audio output
portaudio.Initialize()
defer portaudio.Terminate()
out := make([]float32, bufferSize)
stream, err := portaudio.OpenDefaultStream(0, 1, 44100, len(out), &out)
if err != nil {
log.Fatal(err)
}
defer stream.Close()

if err := stream.Start(); err != nil {
log.Fatal(err)
}
defer stream.Stop()
for {

// populate the out buffer
if err := osc.Fill(buf); err != nil {
log.Printf("error filling up the buffer")
}
// apply vol control if needed (applied as a transform instead of a control
// on the osc)
if gainControl != 0 {
currentVol += gainControl
if currentVol < 0.1 {
currentVol = 0
}
if currentVol > 6 {
currentVol = 6
}
fmt.Printf("new vol %f.2", currentVol)
gainControl = 0
}
transforms.Gain(buf, currentVol)

f64ToF32Copy(buf.Data, out)

// write to the stream
if err := stream.Write(); err != nil {

This comment has been minimized.

Copy link
@egonelbre

egonelbre Jan 4, 2017

I'm not sure whether waiting here for completion is a good idea or would it be better to do the buffer swapping yourself. I have to lookup how should this be done in PortAudio.

The concern: when you start waiting here, you are wasting time that could be spent on calculating the next buffer. This means that you could be more likely to miss the deadline for that next buffer.

log.Printf("error writing to stream : %v\n", err)
}
select {
case <-sig:
fmt.Println("\tCiao!")
return
default:
}
}
}

// portaudio doesn't support float64 so we need to copy our data over to the
// destination buffer.
func f64ToF32Copy(src []float64, dst []float32) {

This comment has been minimized.

Copy link
@nigeltao

nigeltao Jan 4, 2017

The argument order for copy functions are usually dst then src, not src then dst. See the built-in copy function for a perfect example.

if len(src) > len(dst) {

This comment has been minimized.

Copy link
@nigeltao

nigeltao Jan 4, 2017

When this if condition triggers, you're lengthening the dst slice, but the longer dst is only a local variable, and isn't returned by any means. In such a case you're writing elements of a new dst slice that aren't viewable at all by the caller of that function.

dst = append(dst, make([]float32, len(src)-len(dst))...)
}
for i := 0; i < len(src); i++ {

This comment has been minimized.

Copy link
@nigeltao

nigeltao Jan 4, 2017

"for i := 0; i < len(src); i++" can be "for i := range src"

dst[i] = float32(src[i])
}
}

1 comment on commit aed9f58

@mattetti
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

both comments addressed, thanks

Please sign in to comment.