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"
|
||||
"time"
|
||||
|
||||
"dynatron.me/x/stillbox/internal/minimp3"
|
||||
"dynatron.me/x/go-minimp3"
|
||||
"github.com/hajimehoshi/oto"
|
||||
)
|
||||
|
||||
|
|
3
go.mod
3
go.mod
|
@ -1,8 +1,9 @@
|
|||
module dynatron.me/x/stillbox
|
||||
|
||||
go 1.21.12
|
||||
go 1.22.5
|
||||
|
||||
require (
|
||||
dynatron.me/x/go-minimp3 v0.0.0-20240805171536-7ea857e216d6
|
||||
github.com/go-chi/chi v1.5.5
|
||||
github.com/go-chi/chi/v5 v5.1.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/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
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