Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang: Read buffered input as signed 16bit ints

Tags:

go

buffer

wav

I am trying to read a buffered stream of signed 16 bit integers (wav format), but the bufio.Read method only accepts an array of bytes. My question is a 2-parter:

  1. Can I preformat the byte stream into a buffered int16 array?
  2. If I can't, whats the best way of post-processing the byte array into int16 array? My initial thought is to use tmp arrays and keep pushing/processing them, but I was curious if there was a more idiomatic way of doing this?

    package main
    
    import (
         "bufio"
         "io"
         "log"
         "os/exec"
    )
    
    func main() {
    
        app := "someapp"
    
        cmd := exec.Command(app)
        stdout, err := cmd.StdoutPipe()
        r := bufio.NewReader(stdout)
        if err != nil {
            log.Fatal(err)
        }
        if err := cmd.Start(); err != nil {
            log.Fatal(err)
        }
    
        //"someapp" outputs signed 16bit integers (little endian))
        buf := make([]byte, 0, 4*1024)
    
        for {
            n, err := r.Read(buf[:cap(buf)])    //r.Read only accepts type []byte
            buf = buf[:n]
            if n == 0 {
                if err == nil {
                    continue
                }
                if err == io.EOF {
                    break
                }
                log.Fatal(err)
            }
    
            log.Printf("%x\n", buf)
            //process buf here
    
            if err != nil && err != io.EOF {
                log.Fatal(err)
            }
        }
    }
    
like image 377
cannadayr Avatar asked Mar 09 '15 18:03

cannadayr


2 Answers

When working with IO, you always work with []bytes, there's no way to substitute that with []int16, or pre-format that as int16s, it's always a stream of bytes.

You can look at the encoding/binary package to decode this stream.

// to get the first uint16 as i
i := binary.LittleEndian.Uint16(buf[:2])

You can then iterate through the buf as needed.

You can also use binary.Read to read directly from the io.Reader.

var i uint16
for {
    err := binary.Read(r, binary.LittleEndian, &i)
    if err != nil {
        log.Println(err)
        break
    }
    fmt.Println(i)
}

It may worth noting the simplicity of what needs to be done. Each uint16 is created via:

func (littleEndian) Uint16(b []byte) uint16 {
    return uint16(b[0]) | uint16(b[1])<<8
}
like image 90
JimB Avatar answered Sep 22 '22 20:09

JimB


You can use encoding/binary.Read to fill an []int16 directly from your reader, although technically the answer to your first question is still no (check the source of binary.Read, it reads the data to a []byte first).

like image 38
margnus1 Avatar answered Sep 23 '22 20:09

margnus1