Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamer from bytes reader has a length of 0 #123

Open
abhobe opened this issue Jun 1, 2021 · 4 comments
Open

Streamer from bytes reader has a length of 0 #123

abhobe opened this issue Jun 1, 2021 · 4 comments

Comments

@abhobe
Copy link

abhobe commented Jun 1, 2021

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
Copy link

Deng-Xian-Sheng commented Jun 4, 2022

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
Copy link

ivcz commented Jun 6, 2023

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
Copy link

ivcz commented Jun 6, 2023

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
Copy link

我认为在将[]字节转换为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-2blogkoosearch~default-1-125128054 null null. 268 ^ v1 ^ control&utm_ Term=beep&spm=1018.2226.3001.4450

MarkKremer pushed a commit to MarkKremer/beep that referenced this issue Apr 6, 2024
…name-to-readme-links

Apply the master -> main branch rename to links and images in the README
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants