func newStereoSine(freqL, freqR, sampleRate float64) *stereoSine { s := &stereoSine{nil, freqL / sampleRate, 0, freqR / sampleRate, 0} var err error s.Stream, err = portaudio.OpenDefaultStream(0, 2, sampleRate, 0, s.processAudio) chk(err) return s }
// NewSequencer creates and returns a pointer to a New Sequencer. // Returns an error if there is one encountered // During initializing portaudio, or the default stream func NewSequencer() (*Sequencer, error) { err := portaudio.Initialize() if err != nil { return nil, err } s := &Sequencer{ Timer: NewTimer(), Bar: 0, Beat: 0, } stream, err := portaudio.OpenDefaultStream( InputChannels, OutputChannels, float64(SampleRate), portaudio.FramesPerBufferUnspecified, s.ProcessAudio, ) if err != nil { return nil, err } s.Stream = stream return s, nil }
func (o *PortAudioOutput) Start(op OutputParams) error { var err error o.start.Do(func() { o.op = op MakeRecycleChannel(op) if o.stream == nil { o.stream, err = portaudio.OpenDefaultStream(0, o.channels, op.SampleRate, op.BufferSize, o) if err != nil { logger.Println("portaudio output creation failed", err) return } } err = o.stream.Start() if err != nil { logger.Println("portaudio output start failed", err) return } err = o.inputs[0].Start(op) if err != nil { logger.Println("input 0 start failed", err) o.stream.Stop() return } logger.Printf("portaudio Start %T %p", o.inputs[0], o.inputs[0]) o.stop = new(sync.Once) }) return err }
func main() { flag.Parse() sequencer := NewSequencer() for _, file := range flag.Args() { pattern, err := drum.DecodeFile(file) if err != nil { log.Fatal(err) } if err := sequencer.Add(pattern); err != nil { log.Fatal(err) } log.Print(pattern.String()) } portaudio.Initialize() defer portaudio.Terminate() stream, err := portaudio.OpenDefaultStream(0, 2, 44100, 0, func(o []int32) { sequencer.Read(o) }) if err != nil { log.Fatal(err) } defer stream.Close() stream.Start() defer stream.Stop() sequencer.Start() for { time.Sleep(time.Second) } }
func main() { sampleRate := 44100 blockSize := 205 * sampleRate / 8000 window := blockSize / 4 dt := dtmf.NewStandard(sampleRate, blockSize) lastKey := -1 keyCount := 0 samples := make([]float32, blockSize) if err := portaudio.Initialize(); err != nil { log.Fatalf("Initialize: %+v", err) } defer func() { if err := portaudio.Terminate(); err != nil { log.Fatalf("Terminate: %+v", err) } }() inputBuf := make([]float32, window) stream, err := portaudio.OpenDefaultStream(1, 0, float64(sampleRate), len(inputBuf), inputBuf) if err != nil { log.Fatalf("OpenDefaultStream: %+v", err) } defer stream.Close() if err := stream.Start(); err != nil { log.Fatalf("Start: %+v", err) } defer stream.Stop() fmt.Printf("%+v\n", stream.Info()) sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, os.Kill) for { if err := stream.Read(); err != nil { log.Fatalf("Read: %+v", err) } copy(samples, samples[window:]) copy(samples[len(samples)-len(inputBuf):], inputBuf) if k, t := dt.Feed(samples); k == lastKey && t > 0.0 { keyCount++ if keyCount == 10 { fmt.Printf("%c", dtmf.Keypad[k]) } } else { lastKey = k keyCount = 0 } select { case <-sig: fmt.Println() return default: } } }
// Runs the sampler, commencing output to an audio device. func (s *Sampler) Run() error { portaudio.Initialize() var err error s.stream, err = portaudio.OpenDefaultStream(0, 2, 44100, 0, s.processAudio) if err != nil { return err } return s.stream.Start() }
// Play will play the given pattern. // It will generate different sounds for each instrument // every time it is called. For simplification, those sounds // are just sine waves. func (p *Pattern) Play(playingTime time.Duration) error { // Initialize portaudio portaudio.Initialize() defer portaudio.Terminate() stream, err := portaudio.OpenDefaultStream(0, 1, sampleRate, 0, processAudio) if err != nil { return errors.New("could not open default stream") } defer stream.Close() // Create random tone map toneMap = make(map[byte]*tone) rand.Seed(time.Now().Unix()) for i := range p.instruments { var err error toneMap[i], err = newTone(rand.Float64()*600+300, sampleRate) if err != nil { return fmt.Errorf("could not create tone for instrument %v", i) } } stream.Start() defer stream.Stop() // Signal for stopping timeOut := time.After(playingTime) timePerStep := time.Duration(60/p.header.BPM*1000) * time.Millisecond ticker := time.NewTicker(timePerStep) currentStep := 0 // Play! for _ = range ticker.C { for i, instrument := range p.instruments { if instrument.Pattern[currentStep] == 0 { toneMap[i].playing = false } else { toneMap[i].playing = true } } currentStep++ if currentStep > 15 { currentStep = 0 } select { case <-timeOut: ticker.Stop() return nil default: } } return nil }
func newStereoSine(freqL, freqR, sampleRate float64) *stereoSine { s := &stereoSine{nil, // timestep, phase, pan freqL / sampleRate, 0, -1, freqR / sampleRate, 0, 1, 0, 0, make(chan bool)} var err error s.Stream, err = portaudio.OpenDefaultStream(0, 2, sampleRate, 0, s.processAudio) chk(err) return s }
func (a *app) OnInit() { var err error a.r, err = os.Open(*in) if err != nil { log.Fatalf("Unable to open file '%s': %s", *in, err) } var wm webm.WebM a.reader, err = webm.Parse(a.r, &wm) if err != nil { log.Fatal("Unable to parse file: ", err) } a.duration = wm.GetDuration() var vtrack *webm.TrackEntry if !*justaudio { vtrack = wm.FindFirstVideoTrack() } var vstream *webm.Stream if vtrack != nil { vstream = webm.NewStream(vtrack) a.fduration = vtrack.GetDefaultDuration() a.vchan = vstream.VideoChannel() } var atrack *webm.TrackEntry if !*justvideo { atrack = wm.FindFirstAudioTrack() } var astream *webm.Stream if atrack != nil { astream = webm.NewStream(atrack) } splitter := webm.NewSplitter(a.reader.Chan) splitter.Split(astream, vstream) a.steps = uint(0xffffffff) a.img = <-a.vchan a.pimg = a.img chk := func(err error) { if err != nil { panic(err) } } if atrack != nil { channels := int(atrack.Audio.Channels) a.aw = &AudioWriter{ch: astream.AudioChannel(), channels: channels, active: true} a.pastream, err = portaudio.OpenDefaultStream(0, channels, atrack.Audio.SamplingFrequency, 0, a.aw) chk(err) chk(a.pastream.Start()) } }
func main() { chk := func(err error) { if err != nil { panic(err) } } stream, err := portaudio.OpenDefaultStream(0, 2, sampleRate, 0, newStereoSine(256, 320, sampleRate)) chk(err) defer stream.Close() chk(stream.Start()) time.Sleep(2 * time.Second) chk(stream.Stop()) }
func main() { chk := func(err error) { if err != nil { panic(err) } } stream, err := portaudio.OpenDefaultStream(0, 1, 44100, 128, noiseGenerator{}) chk(err) defer stream.Close() chk(stream.Start()) time.Sleep(1e9) chk(stream.Stop()) }
func (i *PortAudioInput) Start(op OutputParams) error { var err error i.start.Do(func() { if i.stream == nil { i.stream, err = portaudio.OpenDefaultStream(len(i.outchans), 0, op.SampleRate, op.BufferSize, i) if err != nil { return } } i.stop = new(sync.Once) i.stream.Start() }) return err }
func main() { chk := func(err error) { if err != nil { panic(err) } } bufferSize := 4096 stream, err := portaudio.OpenDefaultStream(1, 1, 44100, bufferSize, &echoer{make([]float32, bufferSize)}) chk(err) defer stream.Close() chk(stream.Start()) time.Sleep(4e9) chk(stream.Stop()) }
func (s *sound) Play() error { chans := len(s.sample()) str, err := portaudio.OpenDefaultStream(0, chans, s.sampleRate, 0, s.processAudio) if err != nil { return err } if err = str.Start(); err != nil { return err } <-s.quit if err := str.Stop(); err != nil { return err } return str.Close() }
// InitAudio starts up PortAudio, creates a stream and // returns a pointer to an Audio struct, or an error. func InitAudio() (*Audio, error) { a := Audio{ tracks: make([]*Track, 0), } err := portaudio.Initialize() if err != nil { return nil, err } stream, err := portaudio.OpenDefaultStream( 0, 2, float64(44100), 0, a.playCallback, ) if err != nil { return nil, err } a.stream = stream a.stream.Start() return &a, nil }
func (m *mixer) mix(signalChan chan SoundGen) { var err error m.Stream, err = portaudio.OpenDefaultStream(0, 2, sampleRate, 0, m.processAudio) chk(err) defer m.Close() chk(m.Start()) defer m.Stop() for s := range signalChan { if len(m.signals) > 5 { //go func() { fmt.Println("yar!") }() // s m.signals[0]) { s.SilentStop() } go func(m *mixer) { fmt.Println("SILENCIO!") // m.signals[0].SilentStop() m.signals = m.signals[1:] }(m) // s m.signals[0]) { s.SilentStop() } } m.signals = append(m.signals, s) } }
func (e *Engine) Start() error { stream, err := portaudio.OpenDefaultStream(0, 1, waveHz, nSamples, e.processAudio) if err != nil { return err } errc := make(chan error) go func() { err = stream.Start() errc <- err if err != nil { return } <-e.done err = stream.Stop() if err == nil { err = stream.Close() } e.done <- err }() return <-errc }
func (pa *portAudio) player() { out := make([]int16, 2048*2) stream, err := portaudio.OpenDefaultStream( 0, 2, // audio.format.Channels, 44100, // float64(audio.format.SampleRate), len(out), &out, ) if err != nil { panic(err) } defer stream.Close() stream.Start() defer stream.Stop() for { // Decode the incoming data which is expected to be 2 channels and // delivered as int16 in []byte, hence we need to convert it. select { case audio := <-pa.buffer: if len(audio.frames) != 2048*2*2 { // panic("unexpected") // don't know if it's a panic or track just ended break } j := 0 for i := 0; i < len(audio.frames); i += 2 { out[j] = int16(audio.frames[i]) | int16(audio.frames[i+1])<<8 j++ } stream.Write() } } }
func NewPort(sampleRate, channels int) (Output, error) { // todo: fix race condition if portInitCount == 0 { portaudio.Initialize() } portInitCount++ p := port{ ch: make(chan []float32), } var err error p.st, err = portaudio.OpenDefaultStream(0, channels, float64(sampleRate), 1024, p.Fetch) if err != nil { p.Dispose() return nil, err } if err := p.st.Start(); err != nil { p.Dispose() return nil, err } return &p, nil }
func newMixer() (*mixer, error) { m := &mixer{ stream: nil, gain: 0.1, // TODO make mutable incoming: make(chan (<-chan []float32)), audio: make(chan chan []float32), quit: make(chan chan struct{}), } stream, err := portaudio.OpenDefaultStream(iChan, oChan, sRate, bufSz, m) if err != nil { return nil, err } if err := stream.Start(); err != nil { return nil, err } log.Printf("mixer: stream started") m.stream = stream go m.loop() return m, nil }
// Play is a blocking call which initializes the audio subsystem. It should // be called on a separate goroutine. Calling Stop will trigger Play to // return. func (m *Mixer) Play() { const ( ICHAN = 1 OCHAN = 1 ) m.mtx.Lock() defer m.mtx.Unlock() m.on = true stream, err := portaudio.OpenDefaultStream(ICHAN, OCHAN, SRATE, BUFSZ, m) if err != nil { panic(fmt.Sprintf("open: %s", err)) } defer stream.Close() if err = stream.Start(); err != nil { panic(fmt.Sprintf("start: %s", err)) } m.cnd.Wait() if err = stream.Stop(); err != nil { panic(fmt.Sprintf("stop: %s", err)) } m.on = false m.cnd.Broadcast() }
func main() { if len(os.Args) < 2 { fmt.Println("missing required argument: input file name") return } fmt.Println("Playing. Press Ctrl-C to stop.") sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, os.Kill) fileName := os.Args[1] f, err := os.Open(fileName) chk(err) defer f.Close() id, data, err := readChunk(f) chk(err) if id.String() != "FORM" { fmt.Println("bad file format") return } _, err = data.Read(id[:]) chk(err) if id.String() != "AIFF" { fmt.Println("bad file format") return } var c commonChunk var audio io.Reader for { id, chunk, err := readChunk(data) if err == io.EOF { break } chk(err) switch id.String() { case "COMM": chk(binary.Read(chunk, binary.BigEndian, &c)) case "SSND": chunk.Seek(8, 1) //ignore offset and block audio = chunk default: fmt.Printf("ignoring unknown chunk '%s'\n", id) } } //assume 44100 sample rate, mono, 32 bit portaudio.Initialize() defer portaudio.Terminate() out := make([]int32, 8192) stream, err := portaudio.OpenDefaultStream(0, 1, 44100, len(out), &out) chk(err) defer stream.Close() chk(stream.Start()) defer stream.Stop() for remaining := int(c.NumSamples); remaining > 0; remaining -= len(out) { if len(out) > remaining { out = out[:remaining] } err := binary.Read(audio, binary.BigEndian, out) if err == io.EOF { break } chk(err) chk(stream.Write()) select { case <-sig: return default: } } }
// NewMachine creates the Machine struct and loads it with all // available samples and sequences. it is expected that there will both a // 'samples' directory and a 'sequences' directory within the package func NewMachine(sequenceDir, sampleDir string) (*Machine, error) { m := Machine{sequenceDir: sequenceDir, sampleDir: sampleDir} m.Samples = make(map[string]Sample) // load samples dirs, err := ioutil.ReadDir(m.sampleDir) if err != nil { return nil, err } for _, d := range dirs { if !d.IsDir() { continue } files, err := ioutil.ReadDir(path.Join(m.sampleDir, d.Name())) if err != nil { return nil, err } m.Samples[d.Name()] = Sample{ Name: d.Name(), sounds: make(map[soundKey]*audioData)} for _, f := range files { if strings.ToLower(path.Ext(f.Name())) != ".wav" { continue } ss := strings.SplitN(f.Name(), " ", 2) id, err := strconv.Atoi(ss[0]) if err != nil || len(ss) < 2 { continue } nm := strings.SplitN(ss[1], ".", 2) if len(nm) < 2 { continue } s, err := decodeAudio(path.Join(m.sampleDir, d.Name(), f.Name())) if err != nil { return nil, err } m.Samples[d.Name()].sounds[soundKey{ID: uint16(id), Name: nm[0]}] = s } } // load sequences files, err := ioutil.ReadDir(m.sequenceDir) if err != nil { return nil, err } for _, f := range files { if strings.ToLower(path.Ext(f.Name())) != ".splice" { continue } fp := path.Join(m.sequenceDir, f.Name()) p, err := DecodeFile(fp) if err != nil { return nil, err } nm := strings.SplitN(f.Name(), ".", 2) s := Sequence{Name: nm[0], Version: p.Version, Tempo: p.Tempo} // create sections specified by pattern for _, t := range p.Tracks { if _, ok := m.Samples[p.Version].sounds[soundKey{ID: t.ID, Name: t.Name}]; !ok { return nil, fmt.Errorf("failed to load sample sound: %v %v", t.ID, t.Name) } sec := Section{ID: t.ID, Name: t.Name, Beats: t.Beats, Enabled: true} s.Sections = append(s.Sections, &sec) } // create sections for remaining audio samples for sk := range m.Samples[p.Version].sounds { add := true for _, t := range p.Tracks { if sk.ID == t.ID && sk.Name == t.Name { add = false break } } if add { sec := Section{ID: sk.ID, Name: sk.Name} s.Sections = append(s.Sections, &sec) } } m.Sequences = append(m.Sequences, &s) } if err := portaudio.Initialize(); err != nil { return nil, err } stream, err := portaudio.OpenDefaultStream(0, 1, defaultSampleRate, 0, m.audioCallback) if err != nil { return nil, err } m.stream = stream if err := stream.Start(); err != nil { return nil, err } return &m, nil }
func main() { if len(os.Args) != 2 { fmt.Println("usage: tdrum file.splice") os.Exit(1) } pattern, err := drum.DecodeFile(os.Args[1]) if err != nil { fmt.Printf("error: %s\n", err) os.Exit(1) } sequencer = NewSequencer() if err := sequencer.Add(pattern); err != nil { fmt.Printf("error: %s\n", err) os.Exit(1) } portaudio.Initialize() defer portaudio.Terminate() stream, err := portaudio.OpenDefaultStream(0, 2, 44100, 0, func(o []int32) { sequencer.Read(o) }) if err != nil { log.Fatal(err) } defer stream.Close() stream.Start() defer stream.Stop() if err := termbox.Init(); err != nil { panic(err) } defer termbox.Close() termbox.SetOutputMode(termbox.Output256) eq := make(chan termbox.Event) go func() { for { eq <- termbox.PollEvent() } }() draw(pattern) loop: for { select { case ev := <-eq: if ev.Type == termbox.EventKey && ev.Key == termbox.KeyEsc { break loop } if ev.Type == termbox.EventKey && ev.Key == termbox.KeySpace { if sequencer.Running { sequencer.Reset() } else { sequencer.Start() } } default: draw(pattern) time.Sleep(time.Millisecond * 2) } } }
func main() { if len(os.Args) < 2 { fmt.Println("missing required argument: output file name") return } fmt.Println("Recording. Press Ctrl-C to stop.") sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, os.Kill) fileName := os.Args[1] if !strings.HasSuffix(fileName, ".aiff") { fileName += ".aiff" } f, err := os.Create(fileName) chk(err) // form chunk _, err = f.WriteString("FORM") chk(err) chk(binary.Write(f, binary.BigEndian, int32(0))) //total bytes _, err = f.WriteString("AIFF") chk(err) // common chunk _, err = f.WriteString("COMM") chk(err) chk(binary.Write(f, binary.BigEndian, int32(18))) //size chk(binary.Write(f, binary.BigEndian, int16(1))) //channels chk(binary.Write(f, binary.BigEndian, int32(0))) //number of samples chk(binary.Write(f, binary.BigEndian, int16(32))) //bits per sample _, err = f.Write([]byte{0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0}) //80-bit sample rate 44100 chk(err) // sound chunk _, err = f.WriteString("SSND") chk(err) chk(binary.Write(f, binary.BigEndian, int32(0))) //size chk(binary.Write(f, binary.BigEndian, int32(0))) //offset chk(binary.Write(f, binary.BigEndian, int32(0))) //block nSamples := 0 defer func() { // fill in missing sizes totalBytes := 4 + 8 + 18 + 8 + 8 + 4*nSamples _, err = f.Seek(4, 0) chk(err) chk(binary.Write(f, binary.BigEndian, int32(totalBytes))) _, err = f.Seek(22, 0) chk(err) chk(binary.Write(f, binary.BigEndian, int32(nSamples))) _, err = f.Seek(42, 0) chk(err) chk(binary.Write(f, binary.BigEndian, int32(4*nSamples+8))) chk(f.Close()) }() portaudio.Initialize() defer portaudio.Terminate() in := make([]int32, 64) stream, err := portaudio.OpenDefaultStream(1, 0, 44100, len(in), in) chk(err) defer stream.Close() chk(stream.Start()) for { chk(stream.Read()) chk(binary.Write(f, binary.BigEndian, in)) nSamples += len(in) select { case <-sig: return default: } } chk(stream.Stop()) }
// Play attempts to play the specified pattern for 'n' seconds on the // default audio output device on the current system. An error is // returned if the audio could not be played correctly. func Play(pt *Pattern, n int) error { // Validate that all tracks present in our pattern have sound files loaded. for _, tr := range pt.Tracks { _, ok := tracks[strings.ToLower(tr.Name)+".wav"] if !ok { return fmt.Errorf("track %s could not be found, required to play pattern", tr.Name) } } // Let's do some math first. At a tempo of 120 (bpm), we play 2 beats per second. // That means each beat takes 0.5 seconds. A beat is comprised of a quarter note, // which in turn is comprised of 4 steps. This means, each step takes 0.125 seconds. // The time taken by a step is what we are interested in, as that is what we will // use to schedule the playing of sounds. beatTime := 1.0 / (pt.Tempo / 60.0) stepTime := beatTime / 4.0 // Initialize a list that represents our play queue. This is a list of // sounds that are queued up for playback. var queue = list.New() // We need to keep track of how many steps have been played so far. // This is always a value between 0 and 15 (since our tracks have a fixed // 16 steps). We also track the last time a step was played, so we know // when we can schedule the next. lastStep := 0 lastStepPlayed := time.Duration(0) // Initialize portaudio. err := portaudio.Initialize() if err != nil { return err } defer portaudio.Terminate() // Let's open a stream up and configure our callback. stream, err := portaudio.OpenDefaultStream(0, numChannels, sampleRate, framesPerBuffer, func(output buffer, info portaudio.StreamCallbackTimeInfo) { // If sufficient time has passed since we last played a step, queue up the next. if info.OutputBufferDacTime-lastStepPlayed >= time.Duration(stepTime*float32(time.Second)) { for _, tr := range pt.Tracks { // Is this track scheduled to be played at this step? if tr.Steps[lastStep] == 0x1 { queue.PushBack(&queueItem{tracks[strings.ToLower(tr.Name)+".wav"], 0}) } } lastStepPlayed = info.OutputBufferDacTime lastStep++ if lastStep > 15 { lastStep = 0 } } // Multiplex all queued sounds and play them. multiplexSounds(output, queue) }) if err != nil { return err } // Let's play some drums! err = stream.Start() if err != nil { return err } // For 'n' seconds.... time.Sleep(time.Duration(n) * time.Second) // Stop and cleanup. err = stream.Stop() if err != nil { return err } err = stream.Close() if err != nil { return err } return nil }
// Play an AIFF file using PortAudio // Base on example code from: https://code.google.com/p/portaudio-go/source/browse/portaudio/examples/play.go func PlayAIFF(p string, sig chan int) error { f, err := os.Open(p) if err != nil { return err } defer f.Close() id, data, err := readChunk(f) if err != nil { return err } if id.String() != "FORM" { return ErrBadFileFormat } _, err = data.Read(id[:]) if err != nil { return err } if id.String() != "AIFF" { return ErrBadFileFormat } var c commonChunk var audio io.Reader for { id, chunk, err := readChunk(data) if err == io.EOF { break } if err != nil { return err } switch id.String() { case "COMM": err = binary.Read(chunk, binary.BigEndian, &c) if err != nil { return err } case "SSND": chunk.Seek(8, 1) //ignore offset and block audio = chunk default: fmt.Printf("ignoring unknown chunk '%s'\n", id) } } //assume 44100 sample rate, mono, 32 bit portaudio.Initialize() defer portaudio.Terminate() out := make([]int32, 8192) stream, err := portaudio.OpenDefaultStream(0, 1, 44100, len(out), &out) if err != nil { return err } defer stream.Close() err = stream.Start() if err != nil { return err } defer stream.Stop() for remaining := int(c.NumSamples); remaining > 0; remaining -= len(out) { if len(out) > remaining { out = out[:remaining] } err := binary.Read(audio, binary.BigEndian, out) if err == io.EOF { break } if err != nil { return err } err = stream.Write() if err != nil { return err } select { case <-sig: return nil default: } } return nil }
// Record audio into an AIFF file using PortAudio // Base on example code from: https://code.google.com/p/portaudio-go/source/browse/portaudio/examples/record.go // func RecordAIFF(p string, dur time.Duration, sig chan int) error { func RecordAIFF(options RecordOptions) error { if options.SampleRate <= 0 { options.SampleRate = DefaultSampleRate } if options.InputChannels <= 0 { options.InputChannels = 1 } if options.CallbackInterval <= 0 { options.CallbackInterval = int(options.SampleRate / 10) } f, err := os.Create(options.FilePath) if err != nil { return err } // form chunk if _, err = f.WriteString("FORM"); err != nil { return err } err = binary.Write(f, binary.BigEndian, int32(0)) //total bytes if err != nil { return err } if _, err = f.WriteString("AIFF"); err != nil { return err } // common chunk if _, err = f.WriteString("COMM"); err != nil { return err } binary.Write(f, binary.BigEndian, int32(18)) //size binary.Write(f, binary.BigEndian, int16(1)) //channels binary.Write(f, binary.BigEndian, int32(0)) //number of samples binary.Write(f, binary.BigEndian, int16(32)) //bits per sample _, err = f.Write([]byte{0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0}) //80-bit sample rate 44100 if err != nil { return err } // sound chunk if _, err = f.WriteString("SSND"); err != nil { return err } binary.Write(f, binary.BigEndian, int32(0)) //size binary.Write(f, binary.BigEndian, int32(0)) //offset binary.Write(f, binary.BigEndian, int32(0)) //block nSamples := 0 defer func() { // fill in missing sizes totalBytes := 4 + 8 + 18 + 8 + 8 + 4*nSamples if _, err = f.Seek(4, 0); err != nil { panic(err) } if err := binary.Write(f, binary.BigEndian, int32(totalBytes)); err != nil { panic(err) } if _, err = f.Seek(22, 0); err != nil { panic(err) } if err := binary.Write(f, binary.BigEndian, int32(nSamples)); err != nil { panic(err) } if _, err = f.Seek(42, 0); err != nil { panic(err) } if err := binary.Write(f, binary.BigEndian, int32(4*nSamples+8)); err != nil { panic(err) } f.Close() }() in := make([]int32, 64) stream, err := portaudio.OpenDefaultStream(options.InputChannels, 0, options.SampleRate, len(in), in) if err != nil { return err } defer stream.Close() if err := stream.Start(); err != nil { return err } cbSamples := 0 // sample count of last callback for { if err := stream.Read(); err != nil { return err } err = binary.Write(f, binary.BigEndian, in) if err != nil { return err } nSamples += len(in) if options.StopSignal != nil { select { case <-options.StopSignal: return nil default: } } if options.Callback != nil { if cbSamples == 0 || nSamples-cbSamples > options.CallbackInterval { options.Callback(nSamples) cbSamples = nSamples } } if options.MaxDuration > 0 { if float64(nSamples) > float64(options.MaxDuration/time.Second)*float64(options.SampleRate) { break } } } if err = stream.Stop(); err != nil { return err } return nil }