Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified _examples/voice/nico.dca
Binary file not shown.
43 changes: 35 additions & 8 deletions _examples/voice/voice.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
"bytes"
"context"
"encoding/binary"
"io"
Expand Down Expand Up @@ -78,6 +79,23 @@ func play(client *bot.Client, closeChan chan os.Signal) {
closeChan <- syscall.SIGTERM
}

// Test if the header is 4 bytes in size for some obscure and unknown reason.
func has4BitHeaders(file *os.File) (bool, error) {
var pad [2]byte
_, err := file.ReadAt(pad[:], 2)
if err != nil {
return false, err
}
if bytes.Equal(pad[:], []byte{0, 0}) {
return true, nil
}
return false, nil
}

// writeOpus reads from the included `nico.dca` file in the [DCA0] file format
// and writes its Opus frames to the io.Writer.
//
// [DCA0]: https://github.com/bwmarrin/dca/wiki/DCA0-specification
func writeOpus(w io.Writer) {
file, err := os.Open("nico.dca")
if err != nil {
Expand All @@ -86,9 +104,21 @@ func writeOpus(w io.Writer) {
ticker := time.NewTicker(time.Millisecond * 20)
defer ticker.Stop()

var lenBuf [4]byte
for range ticker.C {
_, err = io.ReadFull(file, lenBuf[:])
// support flag for skipping 2 bytes after each header
h4, err := has4BitHeaders(file)
if err != nil {
panic("error reading file: " + err.Error())
}

var frameLen int16
// Don't wait for the first tick, run immediately.
for ; true; <-ticker.C {
err = binary.Read(file, binary.LittleEndian, &frameLen)

if err == nil && h4 {
_, err = file.Seek(2, io.SeekCurrent)
}

if err != nil {
if err == io.EOF {
_ = file.Close()
Expand All @@ -98,14 +128,11 @@ func writeOpus(w io.Writer) {
return
}

// Read the integer
frameLen := int64(binary.LittleEndian.Uint32(lenBuf[:]))

// Copy the frame.
_, err = io.CopyN(w, file, frameLen)
_, err = io.CopyN(w, file, int64(frameLen))
if err != nil && err != io.EOF {
_ = file.Close()
return
}
}
}
}