Skip to content

Commit

Permalink
mulaw to wav headers linaer 16 working
Browse files Browse the repository at this point in the history
  • Loading branch information
pablodz committed Dec 25, 2023
1 parent b311430 commit 2b52c93
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.1.5
v0.2.1
5 changes: 3 additions & 2 deletions audio/audio.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ type AudioInfo struct {
Channels int
BitDepth int
FloatFormat bool
Verbose bool
}

type AudioFormat interface {
Decode(data []byte, info AudioInfo) []float64
Encode(audioData []float64, info AudioInfo) []byte
Decode(data []byte, info AudioInfo) []byte
Encode(audioData []byte, info AudioInfo) []byte
}
5 changes: 2 additions & 3 deletions audio/formats/pcm/pcm.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import (

type PCMFormat struct{}

func (f *PCMFormat) Decode(data []byte, info audio.AudioInfo) []float64 {
func (f *PCMFormat) Decode(data []byte, info audio.AudioInfo) []byte {
log.Printf("Not implemented")
return nil
}

func (f *PCMFormat) Encode(audioData []float64, info audio.AudioInfo) []byte {

func (f *PCMFormat) Encode(audioData []byte, info audio.AudioInfo) []byte {
// convert float64 to byte
data := make([]byte, len(audioData))
for i := 0; i < len(audioData); i++ {
Expand Down
22 changes: 14 additions & 8 deletions audio/formats/ulaw/mulaw.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,26 @@ import (

type MuLawFormat struct{}

func (f *MuLawFormat) Decode(data []byte, info audio.AudioInfo) []float64 {

pcmData := make([]float64, len(data))
func (f *MuLawFormat) Decode(data []byte, info audio.AudioInfo) []byte {
pcmData := make([]byte, len(data)*2)
// remember that each byte need to be converted to two bytes
for i := 0; i < len(data); i++ {
pcmData[i] = float64(utils.DecodeFromULaw(data[i]))
}

log.Println("[Bytes][Org]", data[0:100])
log.Println("[Bytes][Pcm]", pcmData[0:100])
newFrame := utils.DecodeULawToPCM(data[i : i+1])
pcmData[i*2] = newFrame[0]
pcmData[i*2+1] = newFrame[1]

}
if info.Verbose {
log.Println("[MuLaw][Decode] Decoded to PCM")
log.Println("[MuLaw][Decode] PCM Data [0 :100]", pcmData[0:100])
log.Println("[MuLaw][Decode] PCM Data [100:200]", pcmData[100:200])
}

return pcmData
}

func (f *MuLawFormat) Encode(audioData []float64, info audio.AudioInfo) []byte {
func (f *MuLawFormat) Encode(audioData []byte, info audio.AudioInfo) []byte {
log.Printf("Not implemented")
return nil
}
19 changes: 19 additions & 0 deletions audio/utils/endianness.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package utils

import "encoding/binary"

type ENDIANNESS int

const (
LITTLE_ENDIAN ENDIANNESS = iota
BIG_ENDIAN
)

// GetEndianess returns the endianness of the CPU safety
func GetEndianess() ENDIANNESS {
if binary.LittleEndian.Uint16([]byte{0x01, 0x00}) == 1 {
return LITTLE_ENDIAN
} else {
return BIG_ENDIAN
}
}
10 changes: 10 additions & 0 deletions audio/utils/endianness_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package utils

import "testing"

func TestGetEndianess(t *testing.T) {
e := GetEndianess()
if e != LITTLE_ENDIAN && e != BIG_ENDIAN {
t.Errorf("unexpected endianess: %d", e)
}
}
4 changes: 2 additions & 2 deletions audio/utils/tables.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package utils

// ulawDecode is a lookup table for u-law to LPCM
var ulawDecode = [256]int16{
// mulawToPcmTable is a lookup table for converting u-law to 16bit LPCM
var mulawToPcmTable = [256]int16{
-32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
-23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
-15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
Expand Down
15 changes: 12 additions & 3 deletions audio/utils/ulaw_utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
package utils

func DecodeFromULaw(uLaw byte) int16 {
pcm := ulawDecode[uLaw]
return int16(pcm)
// DecodeULawToPCM decodes raw ULaw-encoded audio data to linear PCM.
// Each ULaw frame is 8-bit logarithmic PCM, which is converted to 16-bit linear PCM.
func DecodeULawToPCM(ulaw []byte) []byte {
if len(ulaw) == 0 {
return nil
}
pcm := make([]byte, len(ulaw)*2)
for i, ulawFrame := range ulaw {
pcmFrame := mulawToPcmTable[ulawFrame]
copy(pcm[i*2:], []byte{byte(pcmFrame), byte(pcmFrame >> 8)})
}
return pcm
}
78 changes: 56 additions & 22 deletions audio/utils/wav_headers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package utils
import (
"encoding/json"
"fmt"
"log"
"strings"
"sync"
)

Expand Down Expand Up @@ -36,28 +38,28 @@ type WaveFormat uint16

// WAV FORMAT CODES
const (
WAVE_FORMAT_UNKNOWN = 0x0000 // Microsoft Unknown Wave Format
WAVE_FORMAT_PCM = 0x0001 // Microsoft PCM Format
WAVE_FORMAT_ADPCM = 0x0002 // Microsoft ADPCM Format
WAVE_FORMAT_IEEE_FLOAT = 0x0003 // IEEE float
WAVE_FORMAT_VSELP = 0x0004 // Compaq Computer's VSELP
WAVE_FORMAT_IBM_CVSD = 0x0005 // IBM CVSD
WAVE_FORMAT_ALAW = 0x0006 // ALAW
WAVE_FORMAT_MULAW = 0x0007 // MULAW
WAVE_FORMAT_DTS = 0x0008 // Digital Theater Systems DTS
WAVE_FORMAT_DRM = 0x0009 // Microsoft Corporation
WAVE_FORMAT_WMAVOICE9 = 0x000A // Microsoft Corporation
WAVE_FORMAT_WMAVOICE10 = 0x000B // Microsoft Corporation
WAVE_FORMAT_OKI_ADPCM = 0x0010 // OKI ADPCM
WAVE_FORMAT_DVI_ADPCM = 0x0011 // Intel's DVI ADPCM
WAVE_FORMAT_IMA_ADPCM = WAVE_FORMAT_DVI_ADPCM
WAVE_FORMAT_MEDIASPACE_ADPCM = 0x0012 // Videologic's MediaSpace ADPCM
WAVE_FORMAT_SIERRA_ADPCM = 0x0013 // Sierra ADPCM
WAVE_FORMAT_G723_ADPCM = 0x0014 // G.723 ADPCM
WAVE_FORMAT_DIGISTD = 0x0015 // DSP Solution's DIGISTD
WAVE_FORMAT_DIGIFIX = 0x0016 // DSP Solution's DIGIFIX
WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017 // Dialogic Corporation
WAVE_FORMAT_MEDIAVISION_ADPCM = 0x0018 // Media Vision ADPCM
WAVE_FORMAT_UNKNOWN = 0x0000 // Microsoft Unknown Wave Format
WAVE_FORMAT_PCM = 0x0001 // Microsoft PCM Format
WAVE_FORMAT_ADPCM = 0x0002 // Microsoft ADPCM Format
WAVE_FORMAT_IEEE_FLOAT = 0x0003 // IEEE float
WAVE_FORMAT_VSELP = 0x0004 // Compaq Computer's VSELP
WAVE_FORMAT_IBM_CVSD = 0x0005 // IBM CVSD
WAVE_FORMAT_ALAW = 0x0006 // ALAW
WAVE_FORMAT_MULAW = 0x0007 // MULAW
WAVE_FORMAT_DTS = 0x0008 // Digital Theater Systems DTS
WAVE_FORMAT_DRM = 0x0009 // Microsoft Corporation
WAVE_FORMAT_WMAVOICE9 = 0x000A // Microsoft Corporation
WAVE_FORMAT_WMAVOICE10 = 0x000B // Microsoft Corporation
WAVE_FORMAT_OKI_ADPCM = 0x0010 // OKI ADPCM
WAVE_FORMAT_DVI_ADPCM = 0x0011 // Intel's DVI ADPCM
WAVE_FORMAT_IMA_ADPCM = WAVE_FORMAT_DVI_ADPCM // Intel's DVI ADPCM
WAVE_FORMAT_MEDIASPACE_ADPCM = 0x0012 // Videologic's MediaSpace ADPCM
WAVE_FORMAT_SIERRA_ADPCM = 0x0013 // Sierra ADPCM
WAVE_FORMAT_G723_ADPCM = 0x0014 // G.723 ADPCM
WAVE_FORMAT_DIGISTD = 0x0015 // DSP Solution's DIGISTD
WAVE_FORMAT_DIGIFIX = 0x0016 // DSP Solution's DIGIFIX
WAVE_FORMAT_DIALOGIC_OKI_ADPCM = 0x0017 // Dialogic Corporation
WAVE_FORMAT_MEDIAVISION_ADPCM = 0x0018 // Media Vision ADPCM
)

// GenerateWavHeadersWithConfig generates the headers for a WAV file without the size fields
Expand All @@ -69,6 +71,7 @@ func GenerateWavHeadersWithConfig(head *WavHeader) []byte {

totalLength := head.Length - 8
if totalLength < 0 {
log.Printf("WARNING: Length is less than 8, defaulting to 0\n")
totalLength = 0
}

Expand Down Expand Up @@ -134,3 +137,34 @@ func GenerateWavHeadersWithConfig(head *WavHeader) []byte {
IntToBytes(subChunk2Size),
)
}

// PrintWavHeaders prints the headers of a WAV file
// first 44 bytes of a WAV file
func PrintWavHeaders(headersWav []byte) {
if len(headersWav) != 44 {
log.Println("[ERROR] Headers are not 44 bytes long")
return
}
fmt.Println("Headers (WAV):")
comments := []string{
"(4) Chunk ID [RIFF]",
"(4) Chunk size",
"(4) Format [WAVE]",
"(4) Sub-chunk 1 ID [fmt ]",
"(4) Sub-chunk 1 size",
"(2) Audio format (PCM) & (2) Number of channels",
"(4) Sample rate",
"(4) Byte rate",
"(2) Block align & (2) Bits per sample",
"(4) Sub-chunk 2 ID [data]",
"(4) Sub-chunk 2 size",
}
for i := 0; i < 44; i += 4 {
fmt.Println(
fmt.Sprintf("[%2d,%2d]", i, i+4),
fmt.Sprintf("% 2x", headersWav[i:i+4]),
"\t<"+strings.ToUpper(string(headersWav[i:i+4]))+">\t",
comments[i/4],
)
}
}
15 changes: 2 additions & 13 deletions examples/transcoder/mulaw_2_wav.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package main

import (
"io"
"log"
"os"

"github.com/sopro-dev/sopro-core/audio"
Expand Down Expand Up @@ -30,6 +29,7 @@ func main() {
Channels: 1,
BitDepth: 8,
FloatFormat: false,
Verbose: false,
}

transcoder := audio.NewTranscoder(&mulaw.MuLawFormat{}, &pcm.PCMFormat{})
Expand All @@ -52,21 +52,10 @@ func main() {
Channels: 1,
SampleRate: 8000,
BitDepth: 16,
Verbose: true,
Verbose: audioInfo.Verbose,
})

f.Write(headers)
f.Seek(44, 0)
f.Write(outputData)

f.Seek(0, 0)
// print first 100 bytes of the output file
out, err := io.ReadAll(f)
if err != nil {
panic(err)
}

// get data from :100
log.Println("[Bytes][out]", out[0:100])

}

1 comment on commit 2b52c93

@pablodz
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

Please sign in to comment.