Add separate int16 and float32 interfaces
This commit is contained in:
parent
2dbf75f65b
commit
aeeb264e36
127
minimp3.go
127
minimp3.go
|
@ -1,130 +1,3 @@
|
|||
package minimp3
|
||||
|
||||
/*
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#include "minimp3.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const maxSamplesPerFrame = 1152 * 2
|
||||
|
||||
// Decoder decode the mp3 stream by minimp3
|
||||
type Decoder struct {
|
||||
source io.Reader
|
||||
mp3, pcm []byte
|
||||
mp3Length, pcmLength int
|
||||
lastError error
|
||||
decode C.mp3dec_t
|
||||
info C.mp3dec_frame_info_t
|
||||
}
|
||||
|
||||
// NewDecoder creates and returns a new mp3 decoder with the default internal buffer size.
|
||||
func NewDecoder(source io.Reader) *Decoder {
|
||||
d := &Decoder{
|
||||
source: source,
|
||||
mp3: make([]byte, 1024*16),
|
||||
pcm: make([]byte, maxSamplesPerFrame*C.sizeof_short),
|
||||
}
|
||||
|
||||
C.mp3dec_init(&d.decode)
|
||||
return d
|
||||
}
|
||||
|
||||
// Read copies the decoded audio data from the internal buffer to p and returns the number of bytes copied.
|
||||
// If the internal buffer is empty, then Read first tries to read mp3 data from the source and decode it.
|
||||
// If len(p) == 0, then Read will return zero bytes, but if the internal buffer is empty, then before that it will still try to decode one frame and fill the internal buffer.
|
||||
func (d *Decoder) Read(p []byte) (int, error) {
|
||||
for d.pcmLength == 0 {
|
||||
// If possible, fill the mp3 buffer completely
|
||||
for d.mp3Length < len(d.mp3) && d.lastError == nil {
|
||||
n, err := d.source.Read(d.mp3[d.mp3Length:])
|
||||
d.mp3Length += n
|
||||
d.lastError = err
|
||||
}
|
||||
|
||||
samples := C.mp3dec_decode_frame(&d.decode,
|
||||
(*C.uint8_t)(unsafe.Pointer(&d.mp3[0])), C.int(d.mp3Length),
|
||||
(*C.mp3d_sample_t)(unsafe.Pointer(&d.pcm[0])), &d.info,
|
||||
)
|
||||
|
||||
if d.info.frame_bytes == 0 {
|
||||
return 0, d.lastError
|
||||
}
|
||||
|
||||
d.mp3Length = copy(d.mp3, d.mp3[d.info.frame_bytes:d.mp3Length])
|
||||
d.pcmLength = int(samples * d.info.channels * C.sizeof_short)
|
||||
}
|
||||
|
||||
n := copy(p, d.pcm[:d.pcmLength])
|
||||
// If there is any data left in the pcm buffer, then move it to the beginning of the buffer
|
||||
copy(d.pcm, d.pcm[n:d.pcmLength])
|
||||
d.pcmLength -= n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// Seek sets a new position for reading audio data.
|
||||
// At least one decoded frame is required to set a new position.
|
||||
// If there are no decoded frames, then Seek will return an error.
|
||||
// The mp3 data source must support the io.Seeker interface.
|
||||
// If the source doesn't support io.Seeker, then Seek will panic.
|
||||
func (d *Decoder) Seek(offset int64, whence int) (int64, error) {
|
||||
seeker, ok := d.source.(io.Seeker)
|
||||
if !ok {
|
||||
panic("minimp3: d.source is not seeker")
|
||||
}
|
||||
|
||||
mp3BytesPerMsec := int64(d.info.bitrate_kbps) / 8
|
||||
pcmBytesPerMsec := int64(d.info.channels*C.sizeof_short*d.info.hz) / 1000
|
||||
|
||||
if mp3BytesPerMsec == 0 || pcmBytesPerMsec == 0 {
|
||||
// There is no information about audio data. Probably not a single frame has been decoded yet
|
||||
return 0, errors.New("no frame available")
|
||||
}
|
||||
|
||||
var mp3Offset int64
|
||||
if whence == io.SeekCurrent {
|
||||
// If seeking is performed from the current position, then be aware of buffered audio data
|
||||
offset -= int64(d.pcmLength)
|
||||
mp3Offset = offset / pcmBytesPerMsec * mp3BytesPerMsec
|
||||
mp3Offset -= int64(d.mp3Length)
|
||||
} else {
|
||||
// If seeking is performed from the beginning or the end, then the buffered data does not matter
|
||||
mp3Offset = offset / pcmBytesPerMsec * mp3BytesPerMsec
|
||||
}
|
||||
|
||||
// Internal buffers must always be cleared, regardless of the result of calling the Seek method
|
||||
d.mp3Length = 0
|
||||
d.pcmLength = 0
|
||||
|
||||
mp3Pos, err := seeker.Seek(mp3Offset, whence)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pcmPos := mp3Pos / mp3BytesPerMsec * pcmBytesPerMsec
|
||||
return pcmPos, nil
|
||||
}
|
||||
|
||||
// SampleRate returns the sample rate of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *Decoder) SampleRate() int {
|
||||
return int(d.info.hz)
|
||||
}
|
||||
|
||||
// Channels returns the number of channels of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *Decoder) Channels() int {
|
||||
return int(d.info.channels)
|
||||
}
|
||||
|
||||
// Bitrate returns the mp3 bitrate of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *Decoder) Bitrate() int {
|
||||
return int(d.info.bitrate_kbps)
|
||||
}
|
||||
|
|
130
minimp3_float32.go
Executable file
130
minimp3_float32.go
Executable file
|
@ -0,0 +1,130 @@
|
|||
package minimp3
|
||||
|
||||
/*
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#define MINIMP3_FLOAT_OUTPUT
|
||||
#include "minimp3.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DecoderFloat32 decode the mp3 stream by minimp3
|
||||
type DecoderFloat32 struct {
|
||||
source io.Reader
|
||||
mp3 []byte
|
||||
pcm []float32
|
||||
mp3Length, pcmLength int
|
||||
lastError error
|
||||
decode C.mp3dec_t
|
||||
info C.mp3dec_frame_info_t
|
||||
}
|
||||
|
||||
// NewDecoderFloat32 creates and returns a new mp3 decoder with the default internal buffer size.
|
||||
func NewDecoderFloat32(source io.Reader) *DecoderFloat32 {
|
||||
d := &DecoderFloat32{
|
||||
source: source,
|
||||
mp3: make([]byte, 1024*16),
|
||||
pcm: make([]float32, maxSamplesPerFrame),
|
||||
}
|
||||
|
||||
C.mp3dec_init(&d.decode)
|
||||
return d
|
||||
}
|
||||
|
||||
// Read copies the decoded audio data from the internal buffer to p and returns the number of bytes copied.
|
||||
// If the internal buffer is empty, then Read first tries to read mp3 data from the source and decode it.
|
||||
// If len(p) == 0, then Read will return zero bytes, but if the internal buffer is empty, then before that it will still try to decode one frame and fill the internal buffer.
|
||||
func (d *DecoderFloat32) Read(p []float32) (int, error) {
|
||||
for d.pcmLength == 0 {
|
||||
// If possible, fill the mp3 buffer completely
|
||||
for d.mp3Length < len(d.mp3) && d.lastError == nil {
|
||||
n, err := d.source.Read(d.mp3[d.mp3Length:])
|
||||
d.mp3Length += n
|
||||
d.lastError = err
|
||||
}
|
||||
|
||||
samples := C.mp3dec_decode_frame(&d.decode,
|
||||
(*C.uint8_t)(unsafe.Pointer(&d.mp3[0])), C.int(d.mp3Length),
|
||||
(*C.mp3d_sample_t)(unsafe.Pointer(&d.pcm[0])), &d.info,
|
||||
)
|
||||
|
||||
if d.info.frame_bytes == 0 {
|
||||
return 0, d.lastError
|
||||
}
|
||||
|
||||
d.mp3Length = copy(d.mp3, d.mp3[d.info.frame_bytes:d.mp3Length])
|
||||
d.pcmLength = int(samples * d.info.channels * C.sizeof_float)
|
||||
}
|
||||
|
||||
n := copy(p, d.pcm[:d.pcmLength/C.sizeof_float])
|
||||
// If there is any data left in the pcm buffer, then move it to the beginning of the buffer
|
||||
copy(d.pcm, d.pcm[n:d.pcmLength/C.sizeof_float])
|
||||
d.pcmLength -= n * C.sizeof_float
|
||||
return n / C.sizeof_float, nil
|
||||
}
|
||||
|
||||
// Seek sets a new position for reading audio data.
|
||||
// At least one decoded frame is required to set a new position.
|
||||
// If there are no decoded frames, then Seek will return an error.
|
||||
// The mp3 data source must support the io.Seeker interface.
|
||||
// If the source doesn't support io.Seeker, then Seek will panic.
|
||||
func (d *DecoderFloat32) Seek(offset int64, whence int) (int64, error) {
|
||||
seeker, ok := d.source.(io.Seeker)
|
||||
if !ok {
|
||||
panic("minimp3: d.source is not seeker")
|
||||
}
|
||||
|
||||
mp3BytesPerMsec := int64(d.info.bitrate_kbps) / 8
|
||||
pcmBytesPerMsec := int64(d.info.channels*C.sizeof_float*d.info.hz) / 1000
|
||||
|
||||
if mp3BytesPerMsec == 0 || pcmBytesPerMsec == 0 {
|
||||
// There is no information about audio data. Probably not a single frame has been decoded yet
|
||||
return 0, errors.New("no frame available")
|
||||
}
|
||||
|
||||
var mp3Offset int64
|
||||
if whence == io.SeekCurrent {
|
||||
// If seeking is performed from the current position, then be aware of buffered audio data
|
||||
offset -= int64(d.pcmLength)
|
||||
mp3Offset = offset / pcmBytesPerMsec * mp3BytesPerMsec
|
||||
mp3Offset -= int64(d.mp3Length)
|
||||
} else {
|
||||
// If seeking is performed from the beginning or the end, then the buffered data does not matter
|
||||
mp3Offset = offset / pcmBytesPerMsec * mp3BytesPerMsec
|
||||
}
|
||||
|
||||
// Internal buffers must always be cleared, regardless of the result of calling the Seek method
|
||||
d.mp3Length = 0
|
||||
d.pcmLength = 0
|
||||
|
||||
mp3Pos, err := seeker.Seek(mp3Offset, whence)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pcmPos := mp3Pos / mp3BytesPerMsec * pcmBytesPerMsec
|
||||
return pcmPos, nil
|
||||
}
|
||||
|
||||
// SampleRate returns the sample rate of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *DecoderFloat32) SampleRate() int {
|
||||
return int(d.info.hz)
|
||||
}
|
||||
|
||||
// Channels returns the number of channels of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *DecoderFloat32) Channels() int {
|
||||
return int(d.info.channels)
|
||||
}
|
||||
|
||||
// Bitrate returns the mp3 bitrate of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *DecoderFloat32) Bitrate() int {
|
||||
return int(d.info.bitrate_kbps)
|
||||
}
|
129
minimp3_int16.go
Executable file
129
minimp3_int16.go
Executable file
|
@ -0,0 +1,129 @@
|
|||
package minimp3
|
||||
|
||||
/*
|
||||
#define MINIMP3_IMPLEMENTATION
|
||||
#include "minimp3.h"
|
||||
*/
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DecoderInt16 decode the mp3 stream by minimp3
|
||||
type DecoderInt16 struct {
|
||||
source io.Reader
|
||||
mp3 []byte
|
||||
pcm []int16
|
||||
mp3Length, pcmLength int
|
||||
lastError error
|
||||
decode C.mp3dec_t
|
||||
info C.mp3dec_frame_info_t
|
||||
}
|
||||
|
||||
// NewDecoderInt16 creates and returns a new mp3 decoder with the default internal buffer size.
|
||||
func NewDecoderInt16(source io.Reader) *DecoderInt16 {
|
||||
d := &DecoderInt16{
|
||||
source: source,
|
||||
mp3: make([]byte, 1024*16),
|
||||
pcm: make([]int16, maxSamplesPerFrame),
|
||||
}
|
||||
|
||||
C.mp3dec_init(&d.decode)
|
||||
return d
|
||||
}
|
||||
|
||||
// Read copies the decoded audio data from the internal buffer to p and returns the number of items copied.
|
||||
// If the internal buffer is empty, then Read first tries to read mp3 data from the source and decode it.
|
||||
// If len(p) == 0, then Read will return zero bytes, but if the internal buffer is empty, then before that it will still try to decode one frame and fill the internal buffer.
|
||||
func (d *DecoderInt16) Read(p []int16) (int, error) {
|
||||
for d.pcmLength == 0 {
|
||||
// If possible, fill the mp3 buffer completely
|
||||
for d.mp3Length < len(d.mp3) && d.lastError == nil {
|
||||
n, err := d.source.Read(d.mp3[d.mp3Length:])
|
||||
d.mp3Length += n
|
||||
d.lastError = err
|
||||
}
|
||||
|
||||
samples := C.mp3dec_decode_frame(&d.decode,
|
||||
(*C.uint8_t)(unsafe.Pointer(&d.mp3[0])), C.int(d.mp3Length),
|
||||
(*C.mp3d_sample_t)(unsafe.Pointer(&d.pcm[0])), &d.info,
|
||||
)
|
||||
|
||||
if d.info.frame_bytes == 0 {
|
||||
return 0, d.lastError
|
||||
}
|
||||
|
||||
d.mp3Length = copy(d.mp3, d.mp3[d.info.frame_bytes:d.mp3Length])
|
||||
d.pcmLength = int(samples * d.info.channels * C.sizeof_short)
|
||||
}
|
||||
|
||||
n := copy(p, d.pcm[:d.pcmLength/C.sizeof_short])
|
||||
// If there is any data left in the pcm buffer, then move it to the beginning of the buffer
|
||||
copy(d.pcm, d.pcm[n:d.pcmLength/C.sizeof_short])
|
||||
d.pcmLength -= n * C.sizeof_short
|
||||
return n / C.sizeof_short, nil
|
||||
}
|
||||
|
||||
// Seek sets a new position for reading audio data.
|
||||
// At least one decoded frame is required to set a new position.
|
||||
// If there are no decoded frames, then Seek will return an error.
|
||||
// The mp3 data source must support the io.Seeker interface.
|
||||
// If the source doesn't support io.Seeker, then Seek will panic.
|
||||
func (d *DecoderInt16) Seek(offset int64, whence int) (int64, error) {
|
||||
seeker, ok := d.source.(io.Seeker)
|
||||
if !ok {
|
||||
panic("minimp3: d.source is not seeker")
|
||||
}
|
||||
|
||||
mp3BytesPerMsec := int64(d.info.bitrate_kbps) / 8
|
||||
pcmBytesPerMsec := int64(d.info.channels*C.sizeof_short*d.info.hz) / 1000
|
||||
|
||||
if mp3BytesPerMsec == 0 || pcmBytesPerMsec == 0 {
|
||||
// There is no information about audio data. Probably not a single frame has been decoded yet
|
||||
return 0, errors.New("no frame available")
|
||||
}
|
||||
|
||||
var mp3Offset int64
|
||||
if whence == io.SeekCurrent {
|
||||
// If seeking is performed from the current position, then be aware of buffered audio data
|
||||
offset -= int64(d.pcmLength)
|
||||
mp3Offset = offset / pcmBytesPerMsec * mp3BytesPerMsec
|
||||
mp3Offset -= int64(d.mp3Length)
|
||||
} else {
|
||||
// If seeking is performed from the beginning or the end, then the buffered data does not matter
|
||||
mp3Offset = offset / pcmBytesPerMsec * mp3BytesPerMsec
|
||||
}
|
||||
|
||||
// Internal buffers must always be cleared, regardless of the result of calling the Seek method
|
||||
d.mp3Length = 0
|
||||
d.pcmLength = 0
|
||||
|
||||
mp3Pos, err := seeker.Seek(mp3Offset, whence)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
pcmPos := mp3Pos / mp3BytesPerMsec * pcmBytesPerMsec
|
||||
return pcmPos, nil
|
||||
}
|
||||
|
||||
// SampleRate returns the sample rate of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *DecoderInt16) SampleRate() int {
|
||||
return int(d.info.hz)
|
||||
}
|
||||
|
||||
// Channels returns the number of channels of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *DecoderInt16) Channels() int {
|
||||
return int(d.info.channels)
|
||||
}
|
||||
|
||||
// Bitrate returns the mp3 bitrate of the last decoded frame.
|
||||
// If no frames have been decoded yet, then it will return 0.
|
||||
func (d *DecoderInt16) Bitrate() int {
|
||||
return int(d.info.bitrate_kbps)
|
||||
}
|
Loading…
Reference in a new issue