Move minimp3
This commit is contained in:
parent
7f2709ea0b
commit
0f3684223a
6 changed files with 5 additions and 2075 deletions
|
@ -4,7 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"dynatron.me/x/stillbox/internal/minimp3"
|
"dynatron.me/x/go-minimp3"
|
||||||
"github.com/hajimehoshi/oto"
|
"github.com/hajimehoshi/oto"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -1,8 +1,9 @@
|
||||||
module dynatron.me/x/stillbox
|
module dynatron.me/x/stillbox
|
||||||
|
|
||||||
go 1.21.12
|
go 1.22.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
dynatron.me/x/go-minimp3 v0.0.0-20240805171536-7ea857e216d6
|
||||||
github.com/go-chi/chi v1.5.5
|
github.com/go-chi/chi v1.5.5
|
||||||
github.com/go-chi/chi/v5 v5.1.0
|
github.com/go-chi/chi/v5 v5.1.0
|
||||||
github.com/go-chi/httprate v0.9.0
|
github.com/go-chi/httprate v0.9.0
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1,3 +1,5 @@
|
||||||
|
dynatron.me/x/go-minimp3 v0.0.0-20240805171536-7ea857e216d6 h1:04vlkvfe/mkY4CFpMj0TnrwS2uUiYnyAxe/WxhR0Xgs=
|
||||||
|
dynatron.me/x/go-minimp3 v0.0.0-20240805171536-7ea857e216d6/go.mod h1:zgYpOsy9VkbzyavLQARfhavkKybknoJyQz0pSBjnWwU=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
This is vendored from https://github.com/tosone/minimp3.
|
|
||||||
|
|
||||||
tosone/minimp3 is MIT licensed, minimp3 itself is PD.
|
|
|
@ -1,215 +0,0 @@
|
||||||
package minimp3
|
|
||||||
|
|
||||||
/*
|
|
||||||
#define MINIMP3_IMPLEMENTATION
|
|
||||||
|
|
||||||
#include "minimp3.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
int decode(mp3dec_t *dec, mp3dec_frame_info_t *info, unsigned char *data, int *length, unsigned char *decoded, int *decoded_length) {
|
|
||||||
int samples;
|
|
||||||
short pcm[MINIMP3_MAX_SAMPLES_PER_FRAME];
|
|
||||||
samples = mp3dec_decode_frame(dec, data, *length, pcm, info);
|
|
||||||
*decoded_length = samples * info->channels * 2;
|
|
||||||
*length -= info->frame_bytes;
|
|
||||||
unsigned char buffer[samples * info->channels * 2];
|
|
||||||
memcpy(buffer, (unsigned char*)&(pcm), sizeof(short) * samples * info->channels);
|
|
||||||
memcpy(decoded, buffer, sizeof(short) * samples * info->channels);
|
|
||||||
return info->frame_bytes;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"io"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
const maxSamplesPerFrame = 1152 * 2
|
|
||||||
|
|
||||||
// Decoder decode the mp3 stream by minimp3
|
|
||||||
type Decoder struct {
|
|
||||||
readerLocker *sync.Mutex
|
|
||||||
data []byte
|
|
||||||
decoderLocker *sync.Mutex
|
|
||||||
decodedData []byte
|
|
||||||
decode C.mp3dec_t
|
|
||||||
info C.mp3dec_frame_info_t
|
|
||||||
context context.Context
|
|
||||||
contextCancel context.CancelFunc
|
|
||||||
SampleRate int
|
|
||||||
Channels int
|
|
||||||
Kbps int
|
|
||||||
Layer int
|
|
||||||
|
|
||||||
originalEof bool // if the original reader is EOF, set this to true
|
|
||||||
}
|
|
||||||
|
|
||||||
// BufferSize Decoded data buffer size.
|
|
||||||
var BufferSize = 1024 * 10
|
|
||||||
|
|
||||||
// WaitForDataDuration wait for the data time duration.
|
|
||||||
var WaitForDataDuration = time.Millisecond * 10
|
|
||||||
|
|
||||||
// NewDecoder decode mp3 stream and get a Decoder for read the raw data to play.
|
|
||||||
func NewDecoder(reader io.Reader) (dec *Decoder, err error) {
|
|
||||||
dec = new(Decoder)
|
|
||||||
dec.readerLocker = new(sync.Mutex)
|
|
||||||
dec.decoderLocker = new(sync.Mutex)
|
|
||||||
dec.context, dec.contextCancel = context.WithCancel(context.Background())
|
|
||||||
dec.decode = C.mp3dec_t{}
|
|
||||||
C.mp3dec_init(&dec.decode)
|
|
||||||
dec.info = C.mp3dec_frame_info_t{}
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-dec.context.Done():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if len(dec.data) > BufferSize {
|
|
||||||
<-time.After(WaitForDataDuration)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var data = make([]byte, 512)
|
|
||||||
var n int
|
|
||||||
n, err = reader.Read(data)
|
|
||||||
|
|
||||||
dec.readerLocker.Lock()
|
|
||||||
dec.data = append(dec.data, data[:n]...)
|
|
||||||
dec.readerLocker.Unlock()
|
|
||||||
if err == io.EOF {
|
|
||||||
dec.originalEof = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
dec.originalEof = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-dec.context.Done():
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if len(dec.decodedData) > BufferSize {
|
|
||||||
<-time.After(WaitForDataDuration)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var decoded = [maxSamplesPerFrame * 2]byte{}
|
|
||||||
var decodedLength = C.int(0)
|
|
||||||
var length = C.int(len(dec.data))
|
|
||||||
if len(dec.data) == 0 {
|
|
||||||
<-time.After(WaitForDataDuration)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
frameSize := C.decode(&dec.decode, &dec.info,
|
|
||||||
(*C.uchar)(unsafe.Pointer(&dec.data[0])),
|
|
||||||
&length, (*C.uchar)(unsafe.Pointer(&decoded[0])),
|
|
||||||
&decodedLength)
|
|
||||||
if int(frameSize) == 0 {
|
|
||||||
<-time.After(WaitForDataDuration)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dec.SampleRate = int(dec.info.hz)
|
|
||||||
dec.Channels = int(dec.info.channels)
|
|
||||||
dec.Kbps = int(dec.info.bitrate_kbps)
|
|
||||||
dec.Layer = int(dec.info.layer)
|
|
||||||
dec.readerLocker.Lock()
|
|
||||||
dec.decoderLocker.Lock()
|
|
||||||
dec.decodedData = append(dec.decodedData, decoded[:decodedLength]...)
|
|
||||||
if int(frameSize) <= len(dec.data) {
|
|
||||||
dec.data = dec.data[int(frameSize):]
|
|
||||||
}
|
|
||||||
dec.decoderLocker.Unlock()
|
|
||||||
dec.readerLocker.Unlock()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Started check the record mp3 stream started ot not.
|
|
||||||
func (dec *Decoder) Started() (channel chan bool) {
|
|
||||||
channel = make(chan bool)
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-dec.context.Done():
|
|
||||||
channel <- false
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if len(dec.decodedData) != 0 {
|
|
||||||
channel <- true
|
|
||||||
} else {
|
|
||||||
<-time.After(time.Millisecond * 100)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read read the raw stream
|
|
||||||
func (dec *Decoder) Read(data []byte) (n int, err error) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-dec.context.Done(): // if the decoder is stopped, then here should return EOF
|
|
||||||
err = io.EOF
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
if len(dec.data) == 0 && len(dec.decodedData) == 0 && dec.originalEof {
|
|
||||||
err = io.EOF
|
|
||||||
return
|
|
||||||
} else if len(dec.decodedData) > 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
<-time.After(WaitForDataDuration)
|
|
||||||
}
|
|
||||||
dec.decoderLocker.Lock()
|
|
||||||
defer dec.decoderLocker.Unlock()
|
|
||||||
n = copy(data, dec.decodedData[:])
|
|
||||||
dec.decodedData = dec.decodedData[n:]
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close stop the decode mp3 stream cycle.
|
|
||||||
func (dec *Decoder) Close() {
|
|
||||||
if dec.contextCancel != nil {
|
|
||||||
dec.contextCancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeFull put all of the mp3 data to decode.
|
|
||||||
func DecodeFull(mp3 []byte) (dec *Decoder, decodedData []byte, err error) {
|
|
||||||
dec = new(Decoder)
|
|
||||||
dec.decode = C.mp3dec_t{}
|
|
||||||
C.mp3dec_init(&dec.decode)
|
|
||||||
info := C.mp3dec_frame_info_t{}
|
|
||||||
var length = C.int(len(mp3))
|
|
||||||
for {
|
|
||||||
var decoded = [maxSamplesPerFrame * 2]byte{}
|
|
||||||
var decodedLength = C.int(0)
|
|
||||||
frameSize := C.decode(&dec.decode,
|
|
||||||
&info, (*C.uchar)(unsafe.Pointer(&mp3[0])),
|
|
||||||
&length, (*C.uchar)(unsafe.Pointer(&decoded[0])),
|
|
||||||
&decodedLength)
|
|
||||||
if int(frameSize) == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
decodedData = append(decodedData, decoded[:decodedLength]...)
|
|
||||||
if int(frameSize) < len(mp3) {
|
|
||||||
mp3 = mp3[int(frameSize):]
|
|
||||||
}
|
|
||||||
dec.SampleRate = int(info.hz)
|
|
||||||
dec.Channels = int(info.channels)
|
|
||||||
dec.Kbps = int(info.bitrate_kbps)
|
|
||||||
dec.Layer = int(info.layer)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue