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

Fix halved sample value for 16/24bit wav audio #161

Merged
merged 3 commits into from
Jul 10, 2024
Merged

Conversation

Eiton
Copy link
Contributor

@Eiton Eiton commented May 15, 2024

The original code is mapping sample value of 16/24bit wav audio to [-0.5xxx,0.5].
This commit changes the range to [-1,1).

@MarkKremer
Copy link
Contributor

Thanks!

I'll review this after I'm back from holliday.

@MarkKremer
Copy link
Contributor

The pipeline fails on gofmt but your changes look correct. Tnx!

Do you happen to have time to figure out the encoding as well? It's a bit more complexly written.

Testcase:

func TestEncodeDecodeRoundTrip(t *testing.T) {
	numChannelsS := []int{1, 2}
	precisions := []int{1, 2, 3}

	for _, numChannels := range numChannelsS {
		for _, precision := range precisions {
			name := fmt.Sprintf("%d_channel(s)_%d_precision", numChannels, precision)
			t.Run(name, func(t *testing.T) {
				var s beep.Streamer
				s, data := testtools.RandomDataStreamer(100)

				if numChannels == 1 {
					s = effects.Mono(s)
					for i := range data {
						mix := (data[i][0] + data[i][1]) / 2
						data[i][0] = mix
						data[i][1] = mix
					}
				}

				var w writerseeker.WriterSeeker

				format := beep.Format{SampleRate: 44100, NumChannels: numChannels, Precision: precision}

				err := Encode(&w, s, format)
				assert.NoError(t, err)

				s, decodedFormat, err := Decode(w.Reader())
				assert.NoError(t, err)
				assert.Equal(t, format, decodedFormat)
				assert.Equal(t, data, testtools.Collect(s))
			})
		}
	}
}

@Eiton
Copy link
Contributor Author

Eiton commented May 23, 2024

I think it may not be an appropriate test case.
The random float64 data generated by RandomDataStreamer() loses precision when it is encoded to 8/16/24bit wav format.

@MarkKremer
Copy link
Contributor

Ok, does this seem fairer?

func TestEncodeDecodeRoundTrip(t *testing.T) {
	numChannelsS := []int{1, 2}
	precisions := []int{1, 2, 3}

	for _, numChannels := range numChannelsS {
		for _, precision := range precisions {
			name := fmt.Sprintf("%d_channel(s)_%d_precision", numChannels, precision)
			t.Run(name, func(t *testing.T) {
				var s beep.Streamer
				s, data := testtools.RandomDataStreamer(1000)

				if numChannels == 1 {
					s = effects.Mono(s)
					for i := range data {
						mix := (data[i][0] + data[i][1]) / 2
						data[i][0] = mix
						data[i][1] = mix
					}
				}

				var w writerseeker.WriterSeeker

				format := beep.Format{SampleRate: 44100, NumChannels: numChannels, Precision: precision}

				err := Encode(&w, s, format)
				assert.NoError(t, err)

				s, decodedFormat, err := Decode(w.Reader())
				assert.NoError(t, err)
				assert.Equal(t, format, decodedFormat)

				actual := testtools.Collect(s)
				assert.Len(t, actual, 1000)

				// Delta is determined as follows:
				// The float values range from -1 to 1, which difference is 2.0.
				// For each byte of precision, there are 8 bits -> 2^(precision*8) different possible values.
				// So, fitting 2^(precision*8) values into a range of 2.0, each "step" must not
				// be bigger than 2.0 / math.Pow(2, float64(precision*8)).
				delta := 2.0 / math.Pow(2, float64(precision*8))
				for i := range actual {
					if actual[i][0] < data[i][0]-delta || actual[i][0] > data[i][0]+delta {
						t.Fatalf("encoded & decoded sample doesn't match orginal. expected: %v, actual: %v", data[i][0], actual[i][0])
					}
					if actual[i][1] < data[i][1]-delta || actual[i][1] > data[i][1]+delta {
						t.Fatalf("encoded & decoded sample doesn't match orginal. expected: %v, actual: %v", data[i][1], actual[i][1])
					}
				}
			})
		}
	}
}

@Eiton
Copy link
Contributor Author

Eiton commented May 26, 2024

I updated the code. Now it should pass the test.

Copy link
Contributor

@MarkKremer MarkKremer left a comment

Choose a reason for hiding this comment

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

Sorry for the delay, life got in the way & I want to make sure we have the encoding/decoding right this time.

buffer.go Show resolved Hide resolved
buffer.go Show resolved Hide resolved
buffer.go Outdated Show resolved Hide resolved
buffer.go Outdated Show resolved Hide resolved
wav/decode.go Show resolved Hide resolved
wav/decode.go Show resolved Hide resolved
wav/decode.go Show resolved Hide resolved
wav/decode.go Show resolved Hide resolved
wav/decode.go Show resolved Hide resolved
wav/decode.go Show resolved Hide resolved
@Eiton
Copy link
Contributor Author

Eiton commented Jul 8, 2024

I updated the code.

Copy link
Contributor

@MarkKremer MarkKremer left a comment

Choose a reason for hiding this comment

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

You're a star!

@MarkKremer MarkKremer merged commit f7a7d8c into gopxl:main Jul 10, 2024
1 check passed
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

Successfully merging this pull request may close these issues.

2 participants