Skip to content

Commit

Permalink
Merge pull request #6 from braheezy/mp3
Browse files Browse the repository at this point in the history
Pure Go MP3 encoding support
  • Loading branch information
braheezy authored Sep 29, 2023
2 parents 78152a6 + 6647f3b commit f721f5d
Show file tree
Hide file tree
Showing 17 changed files with 2,367 additions and 62 deletions.
24 changes: 4 additions & 20 deletions cmd/convert_mp3.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
//go:build !windows

package cmd

import (
Expand All @@ -8,9 +6,9 @@ import (
"log"
"os"

"github.com/braheezy/goqoa/pkg/mp3"
"github.com/braheezy/goqoa/pkg/qoa"
"github.com/tosone/minimp3"
"github.com/viert/go-lame"
)

func decodeMp3(inputData *[]byte) ([]int16, *qoa.QOA) {
Expand Down Expand Up @@ -39,27 +37,13 @@ func decodeMp3(inputData *[]byte) ([]int16, *qoa.QOA) {

func encodeMp3(outputFile string, q *qoa.QOA, decodedData []int16) {
fmt.Println("Output format is MP3")

mp3File, err := os.Create(outputFile)
if err != nil {
log.Fatalf("Error creating MP3 file: %v", err)
}
defer mp3File.Close()
mp3Encoder := mp3.NewEncoder(int(q.SampleRate), int(q.Channels))

mp3Encoder := lame.NewEncoder(mp3File)
defer mp3Encoder.Close()

mp3Encoder.SetNumChannels(int(q.Channels))
mp3Encoder.SetInSamplerate(int(q.SampleRate))

// Convert the PCM data to a []byte
pcmBytes := make([]byte, len(decodedData)*2) // Assuming 16-bit PCM (2 bytes per sample)
for i, val := range decodedData {
binary.LittleEndian.PutUint16(pcmBytes[i*2:], uint16(val))
}

// Encode and write the PCM data to the MP3 file
_, err = mp3Encoder.Write(pcmBytes)
if err != nil {
log.Fatalf("Error encoding audio data to MP3: %v", err)
}
mp3Encoder.Write(mp3File, decodedData)
}
18 changes: 0 additions & 18 deletions cmd/convert_mp3_windows.go

This file was deleted.

32 changes: 11 additions & 21 deletions cmd/goqoa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding/hex"
"fmt"
"os"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -52,25 +51,16 @@ func TestConvertCmd(t *testing.T) {
inputFormat: "flac",
outputFormat: "qoa",
},
}
if runtime.GOOS != "windows" {
mp3TestCases := []struct {
audioFormat string
inputFormat string
outputFormat string
}{
{
audioFormat: "mp3",
inputFormat: "mp3",
outputFormat: "qoa",
},
{
audioFormat: "mp3",
inputFormat: "qoa",
outputFormat: "mp3",
},
}
tt = append(tt, mp3TestCases...)
{
audioFormat: "mp3",
inputFormat: "mp3",
outputFormat: "qoa",
},
{
audioFormat: "mp3",
inputFormat: "qoa",
outputFormat: "mp3",
},
}

for _, tc := range tt {
Expand Down Expand Up @@ -100,7 +90,7 @@ func TestConvertCmd(t *testing.T) {
actualChecksumStr := hex.EncodeToString(actualChecksum[:])

// Compare the checksums
require.Equalf(t, expectedChecksumStr, actualChecksumStr, "Conversion failed for %s -> %s", tc.inputFormat, tc.outputFormat)
require.Equalf(t, expectedChecksumStr, actualChecksumStr, "(%s) Conversion failed for %s -> %s", tc.audioFormat, tc.inputFormat, tc.outputFormat)

os.Remove(outputFilename)
}
Expand Down
Binary file modified cmd/testdata/mp3/test.qoa.mp3
Binary file not shown.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ require (
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
github.com/tosone/minimp3 v1.0.2
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d
)

require (
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tosone/minimp3 v1.0.2 h1:htFE2EbP7Y4CJ8KGW9c55tKWRpzv9kXkEmCYGxFzVjA=
github.com/tosone/minimp3 v1.0.2/go.mod h1:N6vjknGR7PboMTyJVhe/7RHNQiIc0jWZxFKlSWdfzwc=
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d h1:LptdD7GTUZeklomtW5vZ1AHwBvDBUCZ2Ftpaz7uEI7g=
github.com/viert/go-lame v0.0.0-20201108052322-bb552596b11d/go.mod h1:EqTcYM7y4JlSfeTI47pmNu3EZQuCuLQefsQyg1Imlz8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
Expand Down
58 changes: 58 additions & 0 deletions pkg/mp3/bitstream.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// bitstream.go manages writes to the bitstream
package mp3

const (
// Minimum size of the buffer in bytes
MINIMUM = 4
// Maximum length of word written or read from bit stream
MAX_LENGTH = 32
BUFFER_SIZE = 4096
)

type bitstream struct {
data []uint8
dataSize int
dataPosition int
cache uint32
cacheBits int
}

func (bs *bitstream) open(size int) {
bs.data = make([]uint8, size)
bs.dataSize = size
bs.dataPosition = 0
bs.cache = 0
bs.cacheBits = 32
}

func (bs *bitstream) putBits(val uint32, N uint) {
if bs.cacheBits > int(N) {
bs.cacheBits -= int(N)
bs.cache |= val << uint32(bs.cacheBits)
} else {
if bs.dataPosition+4 >= bs.dataSize {
newCapacity := bs.dataSize + (bs.dataSize >> 1)
newSlice := make([]byte, newCapacity)
copy(newSlice, bs.data)
bs.data = newSlice
bs.dataSize = newCapacity
}
N -= uint(bs.cacheBits)
bs.cache |= val >> N
bs.data[bs.dataPosition] = uint8(bs.cache >> 24)
bs.data[bs.dataPosition+1] = uint8(bs.cache >> 16)
bs.data[bs.dataPosition+2] = uint8(bs.cache >> 8)
bs.data[bs.dataPosition+3] = uint8(bs.cache)

bs.dataPosition += 4
bs.cacheBits = int(32 - N)
if N != 0 {
bs.cache = val << uint(bs.cacheBits)
} else {
bs.cache = 0
}
}
}
func (bs *bitstream) getBitsCount() int {
return bs.dataPosition*8 + 32 - bs.cacheBits
}
Loading

0 comments on commit f721f5d

Please sign in to comment.