Пример #1
0
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
}
Пример #2
0
// 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
}
Пример #3
0
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
}
Пример #4
0
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)
	}
}
Пример #5
0
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:
		}
	}
}
Пример #6
0
// 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()
}
Пример #7
0
// 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
}
Пример #8
0
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
}
Пример #9
0
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())
	}
}
Пример #10
0
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())
}
Пример #11
0
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())
}
Пример #12
0
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
}
Пример #13
0
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())
}
Пример #14
0
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()
}
Пример #15
0
// 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
}
Пример #16
0
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)
	}
}
Пример #17
0
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
}
Пример #18
0
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()
		}
	}
}
Пример #19
0
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
}
Пример #20
0
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
}
Пример #21
0
// 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()
}
Пример #22
0
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:
		}
	}
}
Пример #23
0
// 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
}
Пример #24
0
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)
		}
	}
}
Пример #25
0
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())
}
Пример #26
0
// 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
}
Пример #27
0
// 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
}
Пример #28
0
// 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
}