faiface / beep

A little package that brings sound to any Go application. Suitable for playback and audio-processing.
MIT License
2.03k stars 150 forks source link

Streamer from bytes reader has a length of 0 #123

Open abhobe opened 3 years ago

abhobe commented 3 years ago

I'm trying to read an audio file through a reader from a []byte array, but when I do this, the length of the output stream is 0. However, using the traditional method of os.Open(), the length is 9589248. Both methods can play the audio, but I can't use .Seek() when using the byte array. I'm not sure what's wrong, any help would be appreciated.

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "os"

    "github.com/faiface/beep/mp3"
)

func main() {
    file, err := ioutil.ReadFile("audio.mp3")
    if err != nil {
        panic(err)
    }

    file2, err := os.Open("audio.mp3")
    if err != nil {
        panic(err)
    }

    fileString, _, _ := mp3.Decode(ioutil.NopCloser(bytes.NewReader(file))) // Outputs 0
    fileString2, _, _ := mp3.Decode(file2) // Outputs 9589248

    fmt.Println(fileString.Len())
    fmt.Println(fileString2.Len())
}
Deng-Xian-Sheng commented 2 years ago

I think something is accidentally lost in the process of converting []byte to io.ReadCloser, or os.File is the most appropriate for mp3.Decode, so I recommend converting []byte to os.File; And I succeeded, solved the problem, and got the music length. I try to use os.NewFile(0, "").Write() but the console output is garbled, and the sound cannot be played. So I wrote this myself:

// Package byteToFile
// []byte to fs.file, reference embed
package byteToFile

import (
    "io"
    "io/fs"
    "reflect"
    "time"
)

// A file is a single file in the FS.
// It implements fs.FileInfo and fs.DirEntry.
type file struct {
    name string
    data string
    hash [16]byte // truncated SHA256 hash
}

func (f *file) Name() string               { return f.name }
func (f *file) Size() int64                { return int64(len(f.data)) }
func (f *file) ModTime() time.Time         { return time.Time{} }
func (f *file) IsDir() bool                { return false }
func (f *file) Sys() any                   { return nil }
func (f *file) Type() fs.FileMode          { return f.Mode().Type() }
func (f *file) Info() (fs.FileInfo, error) { return f, nil }

func (f *file) Mode() fs.FileMode {
    if f.IsDir() {
        return fs.ModeDir | 0555
    }
    return 0444
}

// An openFile is a regular file open for reading.
type openFile struct {
    f      *file // the file itself
    offset int64 // current read offset
}

func (f *openFile) Close() error               { return nil }
func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }

// Read reads up to len(b) bytes from the File and stores them in b.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *openFile) Read(b []byte) (int, error) {
    if f.offset >= int64(len(f.f.data)) {
        return 0, io.EOF
    }
    if f.offset < 0 {
        return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
    }
    n := copy(b, f.f.data[f.offset:])
    f.offset += int64(n)
    return n, nil
}

var (
    _ io.Seeker   = (*openFile)(nil)
    _ fs.FileInfo = (*file)(nil)
)

func (f *openFile) Seek(offset int64, whence int) (int64, error) {
    switch whence {
    case 0:
        // offset += 0
    case 1:
        offset += f.offset
    case 2:
        offset += int64(len(f.f.data))
    }
    if offset < 0 || offset > int64(len(f.f.data)) {
        return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid}
    }
    f.offset = offset
    return offset, nil
}

func (f *openFile) Write(b []byte) (int, error) {
    if reflect.ValueOf(f).IsNil() {
        return 0, &fs.PathError{Op: "write", Path: f.f.name, Err: fs.ErrInvalid}
    }
    f.f.data += string(b)
    return len(b), nil
}

func New() *openFile {
    return &openFile{
        f:      &file{},
        offset: 0,
    }
}

It works like this:

package main

import (
    "fmt"
    "demo/byteToFile"
)

func main(){
    data := []byte("I like 邓文怡")
    object := byteToFile.New()
    _,err := object.Write(data)
    if err != nil{
        panic(err)
    }
    read := make([]byte,len(data))
    _,err = object.Read(read)
    if err != nil{
        panic(err)
    }
    fmt.Println(string(read))
}

Hope it can help you! I like beep it's good!

ivcz commented 1 year ago

In order for Len and Seek methods to work you need to implement io.Seeker on the struct you pass into mp3.Decode, which NopCloser does not

ivcz commented 1 year ago

Tested it, and implementing io.Seeker wont actually help, because beep uses go-mp3 package, and it sets the length when you call mp3.Decode, which means it will only be the size of the buffer at that time.

Deng-Xian-Sheng commented 1 year ago

我认为在将[]字节转换为io.ReadCloser或os.File最适合mp3的过程中,意外丢失了一些东西。解码,所以我建议将[]字节转换为os.File;我成功了,解决了问题,并得到了音乐长度。我尝试使用os.NewFile(0, "").Write(),但控制台输出混乱,无法播放声音。所以我自己写了这个:

// Package byteToFile
// []byte to fs.file, reference embed
package byteToFile

import (
  "io"
  "io/fs"
  "reflect"
  "time"
)

// A file is a single file in the FS.
// It implements fs.FileInfo and fs.DirEntry.
type file struct {
  name string
  data string
  hash [16]byte // truncated SHA256 hash
}

func (f *file) Name() string               { return f.name }
func (f *file) Size() int64                { return int64(len(f.data)) }
func (f *file) ModTime() time.Time         { return time.Time{} }
func (f *file) IsDir() bool                { return false }
func (f *file) Sys() any                   { return nil }
func (f *file) Type() fs.FileMode          { return f.Mode().Type() }
func (f *file) Info() (fs.FileInfo, error) { return f, nil }

func (f *file) Mode() fs.FileMode {
  if f.IsDir() {
      return fs.ModeDir | 0555
  }
  return 0444
}

// An openFile is a regular file open for reading.
type openFile struct {
  f      *file // the file itself
  offset int64 // current read offset
}

func (f *openFile) Close() error               { return nil }
func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }

// Read reads up to len(b) bytes from the File and stores them in b.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *openFile) Read(b []byte) (int, error) {
  if f.offset >= int64(len(f.f.data)) {
      return 0, io.EOF
  }
  if f.offset < 0 {
      return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
  }
  n := copy(b, f.f.data[f.offset:])
  f.offset += int64(n)
  return n, nil
}

var (
  _ io.Seeker   = (*openFile)(nil)
  _ fs.FileInfo = (*file)(nil)
)

func (f *openFile) Seek(offset int64, whence int) (int64, error) {
  switch whence {
  case 0:
      // offset += 0
  case 1:
      offset += f.offset
  case 2:
      offset += int64(len(f.f.data))
  }
  if offset < 0 || offset > int64(len(f.f.data)) {
      return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid}
  }
  f.offset = offset
  return offset, nil
}

func (f *openFile) Write(b []byte) (int, error) {
  if reflect.ValueOf(f).IsNil() {
      return 0, &fs.PathError{Op: "write", Path: f.f.name, Err: fs.ErrInvalid}
  }
  f.f.data += string(b)
  return len(b), nil
}

func New() *openFile {
  return &openFile{
      f:      &file{},
      offset: 0,
  }
}

它的工作原理是这样的:

package main

import (
  "fmt"
  "demo/byteToFile"
)

func main(){
  data := []byte("I like 邓文怡")
  object := byteToFile.New()
  _,err := object.Write(data)
  if err != nil{
      panic(err)
  }
  read := make([]byte,len(data))
  _,err = object.Read(read)
  if err != nil{
      panic(err)
  }
  fmt.Println(string(read))
}

希望它能帮到你!我喜欢哔哔声,很好!

I think something is accidentally lost in the process of converting []byte to io.ReadCloser, or os.File is the most appropriate for mp3.Decode, so I recommend converting []byte to os.File; And I succeeded, solved the problem, and got the music length. I try to use os.NewFile(0, "").Write() but the console output is garbled, and the sound cannot be played. So I wrote this myself:

// Package byteToFile
// []byte to fs.file, reference embed
package byteToFile

import (
  "io"
  "io/fs"
  "reflect"
  "time"
)

// A file is a single file in the FS.
// It implements fs.FileInfo and fs.DirEntry.
type file struct {
  name string
  data string
  hash [16]byte // truncated SHA256 hash
}

func (f *file) Name() string               { return f.name }
func (f *file) Size() int64                { return int64(len(f.data)) }
func (f *file) ModTime() time.Time         { return time.Time{} }
func (f *file) IsDir() bool                { return false }
func (f *file) Sys() any                   { return nil }
func (f *file) Type() fs.FileMode          { return f.Mode().Type() }
func (f *file) Info() (fs.FileInfo, error) { return f, nil }

func (f *file) Mode() fs.FileMode {
  if f.IsDir() {
      return fs.ModeDir | 0555
  }
  return 0444
}

// An openFile is a regular file open for reading.
type openFile struct {
  f      *file // the file itself
  offset int64 // current read offset
}

func (f *openFile) Close() error               { return nil }
func (f *openFile) Stat() (fs.FileInfo, error) { return f.f, nil }

// Read reads up to len(b) bytes from the File and stores them in b.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *openFile) Read(b []byte) (int, error) {
  if f.offset >= int64(len(f.f.data)) {
      return 0, io.EOF
  }
  if f.offset < 0 {
      return 0, &fs.PathError{Op: "read", Path: f.f.name, Err: fs.ErrInvalid}
  }
  n := copy(b, f.f.data[f.offset:])
  f.offset += int64(n)
  return n, nil
}

var (
  _ io.Seeker   = (*openFile)(nil)
  _ fs.FileInfo = (*file)(nil)
)

func (f *openFile) Seek(offset int64, whence int) (int64, error) {
  switch whence {
  case 0:
      // offset += 0
  case 1:
      offset += f.offset
  case 2:
      offset += int64(len(f.f.data))
  }
  if offset < 0 || offset > int64(len(f.f.data)) {
      return 0, &fs.PathError{Op: "seek", Path: f.f.name, Err: fs.ErrInvalid}
  }
  f.offset = offset
  return offset, nil
}

func (f *openFile) Write(b []byte) (int, error) {
  if reflect.ValueOf(f).IsNil() {
      return 0, &fs.PathError{Op: "write", Path: f.f.name, Err: fs.ErrInvalid}
  }
  f.f.data += string(b)
  return len(b), nil
}

func New() *openFile {
  return &openFile{
      f:      &file{},
      offset: 0,
  }
}

It works like this:

package main

import (
  "fmt"
  "demo/byteToFile"
)

func main(){
  data := []byte("I like 邓文怡")
  object := byteToFile.New()
  _,err := object.Write(data)
  if err != nil{
      panic(err)
  }
  read := make([]byte,len(data))
  _,err = object.Read(read)
  if err != nil{
      panic(err)
  }
  fmt.Println(string(read))
}

Hope it can help you! I like beep it's good!

I think the problem has been solved. This is my blog article, which contains more information: https://blog.csdn.net/Deng_Xian_Sheng/article/details/125128054?ops_request_misc=&request_id=e233f01259634c8a88aba26c32e9cd7c&biz_id=&utm_medium=distribute.pc_search_result.none -Task blog-2~blog~koosearch~default-1-125128054 null null. 268 ^ v1 ^ control&utm_ Term=beep&spm=1018.2226.3001.4450