diff options
| author | Nathan Perry <avaglir@gmail.com> | 2017-07-28 19:40:56 -0400 |
|---|---|---|
| committer | Nathan Perry <avaglir@gmail.com> | 2017-07-28 19:40:56 -0400 |
| commit | 89e5b420e23eb76393c275be43c082e5410e7f4c (patch) | |
| tree | be020488c4df1847ca6b87f44fc83ddb71169edd | |
| parent | 381ee40964b21f8a8c4cb9c3f84bd2ddc3290e5c (diff) | |
basic playback working
| -rw-r--r-- | downloader/downloader.go | 45 | ||||
| -rw-r--r-- | thulani.go | 3 | ||||
| -rw-r--r-- | wav/wav.go | 92 |
3 files changed, 96 insertions, 44 deletions
diff --git a/downloader/downloader.go b/downloader/downloader.go index 2b3b2f1..76e9f01 100644 --- a/downloader/downloader.go +++ b/downloader/downloader.go @@ -9,8 +9,6 @@ import ( "io/ioutil" "os" - "fmt" - "github.com/mammothbane/thulani-go/wav" "github.com/op/go-logging" ) @@ -20,13 +18,40 @@ var log = logging.MustGetLogger("downloader") func getUrl(inUrl string) (string, error) { dl := exec.Command("youtube-dl", "-f", "bestaudio", "-x", "--get-url", inUrl) - b, err := dl.CombinedOutput() + outpipe, err := dl.StdoutPipe() + if err != nil { + return "", err + } + + errpipe, err := dl.StderrPipe() if err != nil { - log.Errorf("youtube-dl failed: %v", string(b)) return "", err } - tgt, err := url.Parse(string(b)) + err = dl.Start() + if err != nil { + log.Errorf("starting youtube-dl failed") + return "", err + } + + o, ierr := ioutil.ReadAll(outpipe) + if ierr != nil { + log.Errorf("unable to read from output pipe") + return "", err + } + + e, ierr := ioutil.ReadAll(errpipe) + if ierr != nil { + log.Errorf("unable to read from error pipe") + return "", err + } + + if err := dl.Wait(); err != nil { + log.Errorf("error:\n%v", string(e)) + return "", err + } + + tgt, err := url.Parse(string(o)) out := tgt.Scheme + "://" + tgt.Host + tgt.Path + "?" + tgt.Query().Encode() return out, nil @@ -61,15 +86,16 @@ func Download(inUrl string, startTime time.Duration, duration time.Duration) (<- } clearTemp := func() { + if err := file.Close(); err != nil { + log.Errorf("error closing temp file: %q", err) + } + if err := os.Remove(file.Name()); err != nil { log.Errorf("unable to remove temp file: %q", err) } } args = append(args, file.Name()) - //args = append(args, "out.wav") - - fmt.Println(args) dl := exec.Command(`ffmpeg`, args...) b, err := dl.CombinedOutput() @@ -79,7 +105,8 @@ func Download(inUrl string, startTime time.Duration, duration time.Duration) (<- return nil, err } - ch, done, err := wav.Load(file.Name()) + ch := make(chan []byte, 1024*32) + done, err := wav.Load(file.Name(), ch) if err != nil { clearTemp() return nil, err @@ -12,6 +12,7 @@ import ( "github.com/bwmarrin/discordgo" "github.com/mammothbane/thulani-go/downloader" + "math/rand" ) var config *Config @@ -110,7 +111,7 @@ func onMessage(s *discordgo.Session, m *discordgo.MessageCreate) { break } - ch, err := downloader.Download("https://www.youtube.com/watch?v=_K13GJkGvDw", 10*time.Second, 10*time.Second) + ch, err := downloader.Download("https://www.youtube.com/watch?v=_K13GJkGvDw", time.Duration(rand.Intn(10*60))*time.Second, 5*time.Second) if err != nil { log.Errorf("unable to download video") break @@ -10,73 +10,97 @@ import ( "layeh.com/gopus" ) -// number of individual samples per batch (counting all channels) -const samplesPerBatch = 1920 +// number of individual samples per channel per batch +const samplesPerChannelPerBatch = 1920 var log = logging.MustGetLogger("wav") -func Load(filename string) (<-chan []byte, <-chan struct{}, error) { +func Load(filename string, ch chan<- []byte) (<-chan struct{}, error) { cfname := C.CString(filename) wav := C.drwav_open_file(cfname) if wav == nil { - return nil, nil, fmt.Errorf("Unable to initialize drwav.") + close(ch) + return nil, fmt.Errorf("Unable to initialize drwav.") } if int(wav.channels) != 2 { C.drwav_close(wav) - return nil, nil, fmt.Errorf("Wrong number of channels!") + close(ch) + return nil, fmt.Errorf("Wrong number of channels!") } if int(wav.sampleRate) != 48000 { C.drwav_close(wav) - return nil, nil, fmt.Errorf("Wrong sample rate.") + close(ch) + return nil, fmt.Errorf("Wrong sample rate.") } - ch := make(chan []byte, 1024*32) enc, err := gopus.NewEncoder(int(wav.sampleRate), int(wav.channels), gopus.Audio) if err != nil { - return nil, nil, err + close(ch) + return nil, err } - doneCh := make(chan struct{}) - encoderCh := make(chan []int16, 2*48000*2) - go func() { - buf := C.malloc(C.size_t(samplesPerBatch * wav.bytesPerSample)) + done := make(chan struct{}, 1) + go func(wav *C.drwav) { + defer close(done) + defer close(ch) + + samplesPerBatch := samplesPerChannelPerBatch * int(wav.channels) + batchSize := samplesPerBatch * int(wav.bytesPerSample) + + buf := C.malloc(C.size_t(batchSize)) defer C.free(buf) defer C.drwav_close(wav) - for i := 0; i < (int(wav.totalSampleCount)/samplesPerBatch)+1; i++ { - readSamples := C.drwav_read_s16(wav, samplesPerBatch, (*C.dr_int16)(buf)) + elems := make([]int16, samplesPerBatch) + idx := 0 + + for i := 0; i*samplesPerBatch <= int(wav.totalSampleCount); i += 1 { + readSamples := C.drwav_read_s16(wav, C.dr_uint64(samplesPerBatch), (*C.dr_int16)(buf)) slc := (*[1 << 30]int16)(buf)[:readSamples:readSamples] - encoderCh <- slc + readIdx := 0 - if readSamples < samplesPerBatch { - break - } - } - close(encoderCh) - }() + for { + batchSamplesToFill := samplesPerBatch - idx + readSamplesRemaining := int(readSamples) - readIdx - go func(channels int) { - elems := []int16{} - for v := range encoderCh { - elems = append(elems, v...) + // break if we don't have enough samples to fill the rest of the buffer + if readSamplesRemaining < batchSamplesToFill { + break + } + + copy(elems[idx:], slc[readIdx:readIdx+batchSamplesToFill]) + idx = 0 + readIdx += batchSamplesToFill - if len(elems) > samplesPerBatch*channels { - opus, err := enc.Encode(elems[:samplesPerBatch*channels], samplesPerBatch, samplesPerBatch*channels*2) - elems = elems[samplesPerBatch*channels:] + b, err := processPCM(wav, enc, elems[:]) if err != nil { - log.Errorf("Error encoding opus audio: %q", err) + log.Errorf("error encoding pcm: %q", err) continue } - ch <- opus + ch <- b + } + + batchSamplesToFill := samplesPerBatch - idx + readSamplesRemaining := int(readSamples) - readIdx + if readSamplesRemaining >= batchSamplesToFill { + log.Fatalf("Had enough samples to fill batch after for loop.") + } + + copy(elems[idx:], slc) + idx += len(slc) + + if int(readSamples) < samplesPerBatch { + break } } + }(wav) - close(ch) - close(doneCh) - }(int(wav.channels)) + return done, nil +} - return ch, doneCh, nil +func processPCM(wav *C.drwav, enc *gopus.Encoder, data []int16) ([]byte, error) { + return enc.Encode(data, len(data)/int(wav.channels), len(data)*2) } |
