Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to write G.711u (ulaw / mulaw) to wav? #29

Closed
grokify opened this issue Jan 15, 2021 · 3 comments
Closed

How to write G.711u (ulaw / mulaw) to wav? #29

grokify opened this issue Jan 15, 2021 · 3 comments

Comments

@grokify
Copy link

grokify commented Jan 15, 2021

Issue

I have a 8Khz 8-bit PCM ulaw byte slice that I would like to convert to a WAV file. When I try to convert it using this library, I get audio that is loud and choppy, sounding a bit like the following issue.

go-audio/audio#16

I'm using an Encoder as follows:

wav := NewEncoder(f, 8000, 8, 1, 1)

I'm not quite sure how to convert the raw byte slice into an audio.IntBuffer so I'm doing the following, which results in the poor quality audio.

func BytesToInts(bytes []byte) []int {
	ints := []int{}
	for _, b := range bytes {
		ints = append(ints, int(b))
	}
	return ints
}

func UlawByteSliceToIntBuffer(bytes []byte) *audio.IntBuffer {
	return &audio.IntBuffer{
		Format: &audio.Format{
			NumChannels: 1,
			SampleRate:  8000},
		SourceBitDepth: 8,
		Data:           BytesToInts(bytes)}
}

Any tips on how to get this library to work for this?

Alternative 1 - Works

As an alternative, the following raw approach results in good sounding audio where I can write out the bytes without transformation.

var wavHeaderThin = []byte{0x52, 0x49, 0x46, 0x46, 0x62, 0xb8, 0x00, 0x00, 0x57, 0x41, 0x56, 0x45, 0x66, 0x6d, 0x74, 0x20,
	0x12, 0x00, 0x00, 0x00, 0x07, 0x00, 0x01, 0x00, 0x40, 0x1f, 0x00, 0x00, 0x80, 0x3e, 0x00, 0x00,
	0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x66, 0x61, 0x63, 0x74, 0x04, 0x00, 0x00, 0x00, 0xc5, 0x5b,
	0x00, 0x00, 0x64, 0x61, 0x74, 0x61}

func WriteFileWavFromUlaw(filename string, ulawBytes []byte) error {
	f, err := os.Create(filename)
	if err != nil {
		return err
	}
        defer f.Close()
	_, err = WriteWavFromUlaw(f, ulawBytes)
	return err
}

func WriteWavFromUlaw(w io.Writer, ulawBytes []byte) (n int, err error) {
	n1, err := w.Write(wavHeaderThin)
	if err != nil {
		return n, err
	}
	n += n1
	count := len(ulawBytes)
	n2, err := w.Write([]byte{
		byte(count % 256),
		byte((count >> 8) % 256),
		byte((count >> 16) % 256),
		byte((count >> 24) % 256)})
	if err != nil {
		return n, err
	}
	n += n2
	n3, err := w.Write(ulawBytes)
	if err != nil {
		return n, err
	}
	return n + n3, nil
}

I've added this here for easy reuse:

https://github.com/grokify/simplego/blob/master/audio/ulaw/ulaw.go

Alternative 2 - Works

$ ffmpeg -f mulaw -ar 8000 -i input.ulaw output.wav
@vlad-tokarev
Copy link

vlad-tokarev commented Aug 13, 2023

wav := NewEncoder(f, 8000, 8, 1, 1)

The problem lies with the last argument.

TL;DR:
Change it to:
wav := NewEncoder(f, 8000, 8, 1, 7)

Details:

WAV format allows storage of audio data in various encodings, such as raw data, compressed formats, etc. Therefore, you should explicitly set the µ-law compression format in the header. This ensures that media players recognize and play back the audio correctly.

In your example, passing "1" means "uncompressed pulse code modulated samples." This is incorrect since your data is actually encoded in a different, compressed format.

Although the original author of the issue may no longer need assistance due to the passage of time, I thought it would be helpful to present the solution for future readers.

I also suggest closing this issue. @mattetti

@petercsiba
Copy link

FWIW for my use-case encoding the Twilio Stream websocket audio/x-mulaw on my local M2 Mac,
I had to have different encoder params, than the input ones (otherwise ended up 2x slower)

	inputBuffer := &audio.IntBuffer{
		Data: intData,
		Format: &audio.Format{
			SampleRate:  8000,
			NumChannels: 1,
		},
		SourceBitDepth: 8,
	}

       // When using (f, 8000, 8, 1, 7), it ended up 2x slower idk why
        wav := NewEncoder(f, 16000, 16, 1, 7)

@linnv
Copy link

linnv commented Dec 7, 2023

wav := NewEncoder(f, 8000, 8, 1, 1)

The problem lies with the last argument.

TL;DR: Change it to: wav := NewEncoder(f, 8000, 8, 1, 7)

Details:

WAV format allows storage of audio data in various encodings, such as raw data, compressed formats, etc. Therefore, you should explicitly set the µ-law compression format in the header. This ensures that media players recognize and play back the audio correctly.

In your example, passing "1" means "uncompressed pulse code modulated samples." This is incorrect since your data is actually encoded in a different, compressed format.

Although the original author of the issue may no longer need assistance due to the passage of time, I thought it would be helpful to present the solution for future readers.

I also suggest closing this issue. @mattetti

it works
a WavAudioFormat value of 6 or 7 would indicate that the audio data is stored in the A-law or μ-law format, which are forms of compressed PCM commonly used in telephony.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants