Skip to content

Commit

Permalink
Start working on client code to prove library works
Browse files Browse the repository at this point in the history
  • Loading branch information
braheezy committed Sep 12, 2023
1 parent f96bb1a commit f8fbaf6
Show file tree
Hide file tree
Showing 10 changed files with 242 additions and 191 deletions.
54 changes: 35 additions & 19 deletions cmd/convert_mp3.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,10 @@ import (
"encoding/binary"
"fmt"
"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 +38,44 @@ 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)
config := mp3.GlobalConfig{}
config.SetDefaults()
config.MPEG.Bitrate = 16
config.Wave.SampleRate = int(q.SampleRate)
config.Wave.Channels = int(q.Channels)
if q.Channels > 1 {
config.MPEG.Mode = mp3.STEREO
} else {
config.MPEG.Mode = mp3.MONO
}

encoder, err := config.NewEncoder()
if err != nil {
log.Fatalf("Error creating MP3 file: %v", err)
log.Fatalf("Error creating MP3 encoder: %v", err)
}
defer mp3File.Close()

mp3Encoder := lame.NewEncoder(mp3File)
defer mp3Encoder.Close()
encoder.Write(outputFile, decodedData)
// mp3File, err := os.Create(outputFile)
// if err != nil {
// log.Fatalf("Error creating MP3 file: %v", err)
// }
// defer mp3File.Close()

mp3Encoder.SetNumChannels(int(q.Channels))
mp3Encoder.SetInSamplerate(int(q.SampleRate))
// mp3Encoder := lame.NewEncoder(mp3File)
// defer mp3Encoder.Close()

// 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))
}
// mp3Encoder.SetNumChannels(int(q.Channels))
// mp3Encoder.SetInSamplerate(int(q.SampleRate))

// 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)
}
// // 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)
// }
}
40 changes: 40 additions & 0 deletions pkg/mp3/encoder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package mp3

import "os"

func (enc *GlobalConfig) Write(filePath string, pcmData []int16) error {
// Open the output MP3 file for writing.
outputFile, err := os.Create(filePath)
if err != nil {
return err
}
defer outputFile.Close()
var x *int

// Encoding Loop: Encode PCM data and write to the output file.
var encodedData []byte
samplesPerPass := enc.samplesPerPass()
for len(pcmData) > 0 {
// Determine the number of samples to encode in this pass.
if len(pcmData) < samplesPerPass {
samplesPerPass = len(pcmData)
}

encodedData = append(encodedData,
encodeBufferInterleaved(enc, pcmData[:samplesPerPass], x)...)
_, err := outputFile.Write(encodedData)
if err != nil {
return err
}

pcmData = pcmData[samplesPerPass:]
}

encodedData = flush(enc, x)
_, err = outputFile.Write(encodedData)
if err != nil {
return err
}

return nil
}
60 changes: 30 additions & 30 deletions pkg/mp3/l3bitstream.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ package mp3
// from a layer3 encoder's perspective the bit stream is primarily
// a series of main_data() blocks, with header and side information
// inserted at the proper locations to maintain framing. (See Figure A.7 in the IS).
func formatBitstream(config *globalConfig) {
for ch := 0; ch < config.wave.Channels; ch++ {
for gr := 0; gr < config.mpeg.GranulesPerFrame; gr++ {
func formatBitstream(config *GlobalConfig) {
for ch := 0; ch < config.Wave.Channels; ch++ {
for gr := 0; gr < config.MPEG.GranulesPerFrame; gr++ {
pi := &config.l3Encoding[ch][gr]
pr := &config.mdctFrequency[ch][gr]
for i := 0; i < GRANULE_SIZE; i++ {
Expand All @@ -22,10 +22,10 @@ func formatBitstream(config *globalConfig) {
encodeMainData(config)
}

func encodeMainData(config *globalConfig) {
func encodeMainData(config *GlobalConfig) {
sideInfo := config.sideInfo
for gr := 0; gr < config.mpeg.GranulesPerFrame; gr++ {
for ch := 0; ch < config.wave.Channels; ch++ {
for gr := 0; gr < config.MPEG.GranulesPerFrame; gr++ {
for ch := 0; ch < config.Wave.Channels; ch++ {
granInfo := sideInfo.granules[gr].channels[ch]
sLen1 := slen1Table[granInfo.ScaleFactorCompress]
sLen2 := slen2Table[granInfo.ScaleFactorCompress]
Expand Down Expand Up @@ -57,59 +57,59 @@ func encodeMainData(config *globalConfig) {
}
}

func encodeSideInfo(config *globalConfig) {
func encodeSideInfo(config *GlobalConfig) {
sideInfo := config.sideInfo

config.bitstream.putBits(0x7ff, 11)
config.bitstream.putBits(uint32(config.mpeg.Version), 2)
if config.mpeg.Crc {
config.bitstream.putBits(uint32(config.MPEG.Version), 2)
if config.MPEG.Crc {
config.bitstream.putBits(1, 1)
} else {
config.bitstream.putBits(0, 1)
}

config.bitstream.putBits(uint32(config.mpeg.BitrateIndex), 4)
config.bitstream.putBits(uint32(config.mpeg.SampleRateIndex%3), 2)
config.bitstream.putBits(uint32(config.mpeg.Padding), 1)
config.bitstream.putBits(uint32(config.mpeg.Ext), 1)
config.bitstream.putBits(uint32(config.mpeg.Mode), 2)
config.bitstream.putBits(uint32(config.mpeg.ModeExt), 2)
config.bitstream.putBits(uint32(config.mpeg.Copyright), 1)
config.bitstream.putBits(uint32(config.mpeg.Original), 1)
config.bitstream.putBits(uint32(config.mpeg.EmpH), 2)

if config.mpeg.Version == MPEG_I {
config.bitstream.putBits(uint32(config.MPEG.BitrateIndex), 4)
config.bitstream.putBits(uint32(config.MPEG.SampleRateIndex%3), 2)
config.bitstream.putBits(uint32(config.MPEG.Padding), 1)
config.bitstream.putBits(uint32(config.MPEG.Ext), 1)
config.bitstream.putBits(uint32(config.MPEG.Mode), 2)
config.bitstream.putBits(uint32(config.MPEG.ModeExt), 2)
config.bitstream.putBits(uint32(config.MPEG.Copyright), 1)
config.bitstream.putBits(uint32(config.MPEG.Original), 1)
config.bitstream.putBits(uint32(config.MPEG.EmpH), 2)

if config.MPEG.Version == MPEG_I {
config.bitstream.putBits(0, 9)
if config.wave.Channels == 2 {
if config.Wave.Channels == 2 {
config.bitstream.putBits(uint32(sideInfo.privateBits), 3)
} else {
config.bitstream.putBits(uint32(sideInfo.privateBits), 5)
}
} else {
config.bitstream.putBits(0, 8)
if config.wave.Channels == 2 {
if config.Wave.Channels == 2 {
config.bitstream.putBits(uint32(sideInfo.privateBits), 2)
} else {
config.bitstream.putBits(uint32(sideInfo.privateBits), 1)
}
}

if config.mpeg.Version == MPEG_I {
for ch := 0; ch < config.wave.Channels; ch++ {
if config.MPEG.Version == MPEG_I {
for ch := 0; ch < config.Wave.Channels; ch++ {
for scfsiBand := 0; scfsiBand < 4; scfsiBand++ {
config.bitstream.putBits(uint32(sideInfo.scaleFactorSelectInfo[ch][scfsiBand]), 1)
}
}
}

for gr := 0; gr < config.mpeg.GranulesPerFrame; gr++ {
for ch := 0; ch < config.wave.Channels; ch++ {
for gr := 0; gr < config.MPEG.GranulesPerFrame; gr++ {
for ch := 0; ch < config.Wave.Channels; ch++ {
granInfo := &sideInfo.granules[gr].channels[ch]

config.bitstream.putBits(uint32(granInfo.Part2_3Length), 12)
config.bitstream.putBits(uint32(granInfo.BigValues), 9)
config.bitstream.putBits(uint32(granInfo.GlobalGain), 8)
if config.mpeg.Version == MPEG_I {
if config.MPEG.Version == MPEG_I {
config.bitstream.putBits(uint32(granInfo.ScaleFactorCompress), 4)
} else {
config.bitstream.putBits(uint32(granInfo.ScaleFactorCompress), 9)
Expand All @@ -123,7 +123,7 @@ func encodeSideInfo(config *globalConfig) {
config.bitstream.putBits(uint32(granInfo.Region0Count), 4)
config.bitstream.putBits(uint32(granInfo.Region1Count), 3)

if config.mpeg.Version == MPEG_I {
if config.MPEG.Version == MPEG_I {
config.bitstream.putBits(uint32(granInfo.PreFlag), 1)
}
config.bitstream.putBits(uint32(granInfo.ScaleFactorScale), 1)
Expand All @@ -132,8 +132,8 @@ func encodeSideInfo(config *globalConfig) {
}
}

func huffmanCodeBits(config *globalConfig, ix *[576]int, granInfo *GranuleInfo) {
scaleFactors := scaleFactorBandIndex[config.mpeg.SampleRateIndex]
func huffmanCodeBits(config *GlobalConfig, ix *[576]int, granInfo *GranuleInfo) {
scaleFactors := scaleFactorBandIndex[config.MPEG.SampleRateIndex]

bits := config.bitstream.getBitsCount()

Expand Down
36 changes: 18 additions & 18 deletions pkg/mp3/l3loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const (
)

// innerLoop selects the best quantizerStepSize for a particular set of scaleFactors.
func innerLoop(ix [GRANULE_SIZE]int, maxBits int, granuleInfo *GranuleInfo, granuleIndex int, ch int, config *globalConfig) int {
func innerLoop(ix [GRANULE_SIZE]int, maxBits int, granuleInfo *GranuleInfo, granuleIndex int, ch int, config *GlobalConfig) int {
if maxBits < 0 {
granuleInfo.QuantizerStepSize--
}
Expand Down Expand Up @@ -49,7 +49,7 @@ func innerLoop(ix [GRANULE_SIZE]int, maxBits int, granuleInfo *GranuleInfo, gran
// global gain. This module calls the inner iteration loop.
// l3XMin: the allowed distortion of the scaleFactor.
// ix: vector of quantized values ix(0..575)
func outerLoop(maxBits int, l3XMin *PsyXMin, ix [GRANULE_SIZE]int, granuleIndex, ch int, config *globalConfig) int {
func outerLoop(maxBits int, l3XMin *PsyXMin, ix [GRANULE_SIZE]int, granuleIndex, ch int, config *GlobalConfig) int {
sideInfo := &config.sideInfo
codeInfo := &sideInfo.granules[granuleIndex].channels[ch]

Expand All @@ -64,10 +64,10 @@ func outerLoop(maxBits int, l3XMin *PsyXMin, ix [GRANULE_SIZE]int, granuleIndex,
return int(codeInfo.Part2_3Length)
}

func iterationLoop(config *globalConfig) {
func iterationLoop(config *GlobalConfig) {

for ch := config.wave.Channels; ch > 0; ch-- {
for gr := 0; gr < config.mpeg.GranulesPerFrame; gr++ {
for ch := config.Wave.Channels - 1; ch >= 0; ch-- {
for gr := 0; gr < config.MPEG.GranulesPerFrame; gr++ {
// Setup pointers
ix := config.l3Encoding[ch][gr]
config.l3loop.XR = config.mdctFrequency[ch][gr][:]
Expand All @@ -88,7 +88,7 @@ func iterationLoop(config *globalConfig) {
l3XMin := PsyXMin{}
calcXMin(&config.ratio, codeInfo, &l3XMin, gr, ch)

if config.mpeg.Version == MPEG_I {
if config.MPEG.Version == MPEG_I {
calcSCFSI(&l3XMin, ch, gr, config)
}

Expand Down Expand Up @@ -137,12 +137,12 @@ func iterationLoop(config *globalConfig) {
}

// loopInitialize calculates the look up tables used by the iteration loop.
func loopInitialize(config *globalConfig) {
func loopInitialize(config *GlobalConfig) {
// quantize: stepsize conversion, fourth root of 2 table.
// The table is inverted (negative power) from the equation given
// in the spec because it is quicker to do x*y than x/y.
// The 0.5 is for rounding.
for i := 128; i > 0; i-- {
for i := 127 - 1; i >= 0; i-- {
config.l3loop.StepTable[i] = math.Pow(2.0, float64(127-i)/4)
if config.l3loop.StepTable[i]*2 > 0x7fffffff {
config.l3loop.StepTableI[i] = 0x7fffffff
Expand All @@ -156,25 +156,25 @@ func loopInitialize(config *globalConfig) {

// quantize: vector conversion, three quarter power table.
// The 0.5 is for rounding, the .0946 comes from the spec.
for i := 10000; i > 0; i-- {
for i := 10000 - 1; i >= 0; i-- {
config.l3loop.Int2idx[i] = int(math.Sqrt(math.Sqrt(float64(i)*float64(i)) - 0.0946 + 0.5))
}
}

// calcSCFSI calculates the scalefactor select information ( scfsi )
func calcSCFSI(l3XMin *PsyXMin, ch, granuleIndex int, config *globalConfig) {
func calcSCFSI(l3XMin *PsyXMin, ch, granuleIndex int, config *GlobalConfig) {
l3Side := &config.sideInfo
// This is the scfsi_band table from 2.4.2.7 of the IS
scfsiBandLong := [5]int{0, 6, 11, 16, 21}

condition := 0
scaleFactorBandLong := scaleFactorBandIndex[config.mpeg.SampleRateIndex]
scaleFactorBandLong := scaleFactorBandIndex[config.MPEG.SampleRateIndex]

config.l3loop.Xrmaxl[granuleIndex] = config.l3loop.Xrmax

temp := 0
// the total energy of the granule
for i := GRANULE_SIZE; i > 0; i-- {
for i := GRANULE_SIZE - 1; i >= 0; i-- {
// a bit of scaling to avoid overflow, (not very good)
temp += int(config.l3loop.Xrsq[i] >> 10)
}
Expand Down Expand Up @@ -202,7 +202,7 @@ func calcSCFSI(l3XMin *PsyXMin, ch, granuleIndex int, config *globalConfig) {
}

if granuleIndex == 1 {
for gr := 2; gr > 0; gr-- {
for gr := MAX_GRANULES - 1; gr >= 0; gr-- {
// the spectral values are not all zero
if config.l3loop.Xrmaxl[gr] != 0 {
condition++
Expand Down Expand Up @@ -247,7 +247,7 @@ func calcSCFSI(l3XMin *PsyXMin, ch, granuleIndex int, config *globalConfig) {

// calcPart2Length calculates the number of bits needed to encode the scaleFactor in the
// main data block.
func calcPart2Length(granuleIndex, ch int, config *globalConfig) uint {
func calcPart2Length(granuleIndex, ch int, config *GlobalConfig) uint {
granuleInfo := &config.sideInfo.granules[granuleIndex].channels[ch]

bits := uint(0)
Expand Down Expand Up @@ -283,7 +283,7 @@ func calcXMin(ratio *PsyRatio, codeInfo *GranuleInfo, l3XMin *PsyXMin, granuleIn
// step size. The following optional code written by Seymour Shlien will speed up the shine_outer_loop code which is
// called by iteration_loop. When BIN_SEARCH is defined, the shine_outer_loop function precedes the call to the
// function shine_inner_loop with a call to bin_search gain defined below, which returns a good starting quantizerStepSize.
func binSearchStepSize(desiredRate int, ix [GRANULE_SIZE]int, codeInfo *GranuleInfo, config *globalConfig) int {
func binSearchStepSize(desiredRate int, ix [GRANULE_SIZE]int, codeInfo *GranuleInfo, config *GlobalConfig) int {
next := -120
count := 120

Expand Down Expand Up @@ -391,7 +391,7 @@ func bigValuesBitCount(ix [GRANULE_SIZE]int, granuleInfo *GranuleInfo) int {

// quantize perform quantization of the vector xr ( -> ix).
// Returns maximum value of ix
func quantize(ix [GRANULE_SIZE]int, stepSize int, config *globalConfig) int {
func quantize(ix [GRANULE_SIZE]int, stepSize int, config *GlobalConfig) int {
// 2**(-stepSize/4)
scaleI := config.l3loop.StepTableI[stepSize+127]

Expand Down Expand Up @@ -618,7 +618,7 @@ func newChooseTable(ix [GRANULE_SIZE]int, begin, end uint32) uint {
}

// subDivide subdivides the bigValue region which will use separate Huffman tables.
func subDivide(codeInfo *GranuleInfo, config *globalConfig) {
func subDivide(codeInfo *GranuleInfo, config *GlobalConfig) {
type subRegion struct {
region0Count uint32
region1Count uint32
Expand Down Expand Up @@ -655,7 +655,7 @@ func subDivide(codeInfo *GranuleInfo, config *globalConfig) {
codeInfo.Region0Count = 0
codeInfo.Region1Count = 0
} else {
scaleFactorBandLong := scaleFactorBandIndex[config.mpeg.SampleRateIndex][:]
scaleFactorBandLong := scaleFactorBandIndex[config.MPEG.SampleRateIndex][:]
bigValuesRegion := codeInfo.BigValues * 2

scaleFactorBandCountAnalysis := 0
Expand Down
Loading

0 comments on commit f8fbaf6

Please sign in to comment.