From f9f540d0782af307e1442670a37b2c5bb3b395d5 Mon Sep 17 00:00:00 2001 From: Alexander Pinnecke Date: Wed, 15 Mar 2017 08:20:11 +0100 Subject: [PATCH] Added tmp files --- .gitignore | 3 -- tmp/sample/buffer.go | 48 +++++++++++++++++++++++ tmp/sample/input/audio.go | 56 +++++++++++++++++++++++++++ tmp/sample/input/utils.go | 34 ++++++++++++++++ tmp/sample/input/wav_file.go | 75 ++++++++++++++++++++++++++++++++++++ tmp/sample/output/audio.go | 44 +++++++++++++++++++++ tmp/sample/output/beep.go | 36 +++++++++++++++++ tmp/sample/output/fanout.go | 23 +++++++++++ 8 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 tmp/sample/buffer.go create mode 100644 tmp/sample/input/audio.go create mode 100644 tmp/sample/input/utils.go create mode 100644 tmp/sample/input/wav_file.go create mode 100644 tmp/sample/output/audio.go create mode 100644 tmp/sample/output/beep.go create mode 100644 tmp/sample/output/fanout.go diff --git a/.gitignore b/.gitignore index 3332b35..5650dce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,2 @@ vendor -dmx-auto-control -tmp -fixtures diff --git a/tmp/sample/buffer.go b/tmp/sample/buffer.go new file mode 100644 index 0000000..4b8f77a --- /dev/null +++ b/tmp/sample/buffer.go @@ -0,0 +1,48 @@ +package sample + +import ( + "io" + "sync" +) + +// Buffer is a storage for read samples that is thread safe by mutex. This is kinda FIFO Queue. +type Buffer struct { + m sync.Mutex + data []float64 +} + +// NewBuffer returns a new Buffer instance +func NewBuffer() *Buffer { + return &Buffer{} +} + +// Write adds a sample to the buffer +func (b *Buffer) Write(data float64) { + b.m.Lock() + defer b.m.Unlock() + + b.data = append(b.data, data) +} + +// WriteAll writes all given samples to the Buffer +func (b *Buffer) WriteAll(data []float64) { + b.m.Lock() + defer b.m.Unlock() + + b.data = append(b.data, data...) +} + +// Read returns the first sample from the buffer and deletes it. +func (b *Buffer) Read() (data float64, err error) { + b.m.Lock() + defer b.m.Unlock() + + if len(b.data) == 0 { + return 0, io.EOF + } + + data = b.data[0] + b.data = b.data[1:] + + return data, nil +} diff --git a/tmp/sample/input/audio.go b/tmp/sample/input/audio.go new file mode 100644 index 0000000..80e31f2 --- /dev/null +++ b/tmp/sample/input/audio.go @@ -0,0 +1,56 @@ +package input + +import ( + "os" + + "github.com/apinnecke/dmx-auto-control/sample" + "github.com/gordonklaus/portaudio" +) + +type AudioReader struct { + in *sample.Buffer +} + +func NewAudioReader(in *sample.Buffer) *AudioReader { + return &AudioReader{ + in: in, + } +} + +func (r *AudioReader) Read(sig chan os.Signal) (err error) { + portaudio.Initialize() + defer portaudio.Terminate() + + in := make([]int32, 64) + stream, err := portaudio.OpenDefaultStream(1, 0, 44100, 64, in) + if err != nil { + return err + } + defer stream.Close() + + err = stream.Start() + if err != nil { + return err + } + + for { + err = stream.Read() + if err != nil { + return err + } + + r.in.WriteAll(in) + if err != nil { + return err + } + + select { + case <-sig: + return + default: + } + } + + err = stream.Stop() + return +} diff --git a/tmp/sample/input/utils.go b/tmp/sample/input/utils.go new file mode 100644 index 0000000..a2e3192 --- /dev/null +++ b/tmp/sample/input/utils.go @@ -0,0 +1,34 @@ +package input + +import ( + "fmt" + "math" +) + +func toInt(s []byte, bytesPerSample uint32) (n int32) { + switch bytesPerSample { + case 1: + n = int32(s[0]) + case 2: + n = int32(s[0]) + int32(s[1])<<8 + case 3: + n = int32(s[0]) + int32(s[1])<<8 + int32(s[2])<<16 + case 4: + n = int32(s[0]) + int32(s[1])<<8 + int32(s[2])<<16 + int32(s[3])<<24 + default: + n = 0 + panic(fmt.Errorf("Unhandled bytesPerSample! b:%d", bytesPerSample)) + } + + return +} + +func checkNegative(i int32, bytesPerSample uint32) int32 { + maxVal := int32(math.Pow(2, float64(int(bytesPerSample)*8))) + + if i > maxVal/2-1 { + return i - maxVal + } + + return i +} diff --git a/tmp/sample/input/wav_file.go b/tmp/sample/input/wav_file.go new file mode 100644 index 0000000..f10469a --- /dev/null +++ b/tmp/sample/input/wav_file.go @@ -0,0 +1,75 @@ +package input + +import ( + "io" + "os" + + "github.com/apinnecke/dmx-auto-control/sample" + "github.com/cryptix/wav" +) + +// WavFileReader reads the samples from a wav file +type WavFileReader struct { + in *sample.Buffer + wavReader *wav.Reader +} + +// NewWavFileReader returns a new NewWavFileReader and checks weather the given file is even valid +func NewWavFileReader(file string, in *sample.Buffer) (r *WavFileReader, err error) { + testInfo, err := os.Stat(file) + if err != nil { + return + } + + testWav, err := os.Open(file) + if err != nil { + return + } + + wavReader, err := wav.NewReader(testWav, testInfo.Size()) + if err != nil { + return + } + + r = &WavFileReader{ + in: in, + wavReader: wavReader, + } + return +} + +// Read reads the files content to the buffer +func (r *WavFileReader) Read(sig chan os.Signal) (err error) { + meta := r.wavReader.GetFile() + bytesPerSample := uint32(meta.SignificantBits / 8) + + for { + var b []byte + b, err = r.wavReader.ReadRawSample() + if err == io.EOF { + return nil + } else if err != nil { + return + } + + i := toInt(b, bytesPerSample) + s := checkNegative(i, bytesPerSample) + r.in.Write(s) + + select { + case <-sig: + return + default: + } + } + + return +} + +// ReadOrPanic reads the content, panic in case of error +func (r *WavFileReader) ReadOrPanic() { + err := r.Read() + if err != nil { + panic(err) + } +} diff --git a/tmp/sample/output/audio.go b/tmp/sample/output/audio.go new file mode 100644 index 0000000..9e0a75d --- /dev/null +++ b/tmp/sample/output/audio.go @@ -0,0 +1,44 @@ +package output + +import ( + "sync" + + "github.com/gordonklaus/portaudio" +) + +type AudioOutput struct { + sampleRate uint + out []int32 + m sync.Mutex +} + +func NewAudio() { + +} + +func (a *AudioOutput) Start() { + portaudio.Initialize() + defer portaudio.Terminate() + + stream, err := portaudio.OpenDefaultStream(0, 1, float64(a.sampleRate), 0, a.processAudio) + if err != nil { + panic(err) + } + defer stream.Close() + + err = stream.Start() + if err != nil { + panic(err) + } + defer stream.Stop() +} + +func (a *AudioOutput) processAudio(out []int32) { + for i := range out { + if len(a.out) == 0 { + out[i] = 0 + continue + } + + } +} diff --git a/tmp/sample/output/beep.go b/tmp/sample/output/beep.go new file mode 100644 index 0000000..5750e41 --- /dev/null +++ b/tmp/sample/output/beep.go @@ -0,0 +1,36 @@ +package output + +import ( + "fmt" + "github.com/cryptix/wav" +) + +func Beep(meta wav.File, c chan int32, highPass int32) { + lastSample := int32(0) + beeped := false + lastBeepSample := 0 + lastZeroBeepSample := 0 + i := 0 + + for s := range c { + if s > lastSample { + lastSample = s + } else { + if !beeped && s > highPass { + fmt.Printf("BEEP %v %v \n", i, s) + lastBeepSample = i + beeped = true + } + + if s < 100 && s > -100 && (lastBeepSample+4000 < i) { + if lastZeroBeepSample+10 < i { + beeped = false + } + + lastZeroBeepSample = i + } + } + + i++ + } +} diff --git a/tmp/sample/output/fanout.go b/tmp/sample/output/fanout.go new file mode 100644 index 0000000..592c72b --- /dev/null +++ b/tmp/sample/output/fanout.go @@ -0,0 +1,23 @@ +package output + +// from http://stackoverflow.com/a/16931348/2688600 +func FanOut(ch <-chan int32, size, lag int32) []chan int32 { + cs := make([]chan int32, size) + for i, _ := range cs { + // The size of the channels buffer controls how far behind the recievers + // of the fanOut channels can lag the other channels. + cs[i] = make(chan int32, lag) + } + go func() { + for i := range ch { + for _, c := range cs { + c <- i + } + } + for _, c := range cs { + // close all our fanOut channels when the input channel is exhausted. + close(c) + } + }() + return cs +}