mirror of
https://github.com/LadybirdBrowser/ladybird
synced 2026-04-25 17:25:08 +02:00
LibMedia: Fix multi-channel decode for dumb containers (like WAV)
For web audio, I reckon an occasional misjudged channel layout is better than more frequent exceptions. Signed PCM is normalized with unsigned max divided by 2, not signed max. If you divide by the signed max (32767), you get headroom that can exceed the threshold below -1.0. It's not audible, this mostly matters for tests that assume correct normalization. But it turns out there's no shortage of "golden ears" jackholes out there who swear they can hear the difference.
This commit is contained in:
committed by
Gregory Bertilson
parent
9418981398
commit
0dea87110e
Notes:
github-actions[bot]
2026-02-13 23:58:32 +00:00
Author: https://github.com/jonbgamble Commit: https://github.com/LadybirdBrowser/ladybird/commit/0dea87110e2 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/7805 Reviewed-by: https://github.com/R-Goc Reviewed-by: https://github.com/Zaggy1024
@@ -140,7 +140,8 @@ requires(IsSigned<T>)
|
||||
static float float_sample_from_frame_data(u8** data, size_t plane, size_t index)
|
||||
{
|
||||
auto* pointer = reinterpret_cast<T*>(data[plane]);
|
||||
return pointer[index] / static_cast<float>(NumericLimits<T>::max());
|
||||
constexpr float full_scale = NumericLimits<MakeUnsigned<T>>::max() / 2;
|
||||
return static_cast<float>(pointer[index]) / static_cast<float>(full_scale);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -42,7 +42,16 @@ ErrorOr<Audio::ChannelMap> av_channel_layout_to_channel_map(AVChannelLayout cons
|
||||
return Audio::ChannelMap::mono();
|
||||
if (layout.nb_channels == 2)
|
||||
return Audio::ChannelMap::stereo();
|
||||
return Error::from_string_literal("Unspecified channel order was neither mono nor stereo");
|
||||
if (layout.nb_channels == 4)
|
||||
return Audio::ChannelMap::quadrophonic();
|
||||
if (layout.nb_channels == 6)
|
||||
return Audio::ChannelMap::surround_5_1();
|
||||
if (layout.nb_channels == 8)
|
||||
return Audio::ChannelMap::surround_7_1();
|
||||
|
||||
for (int i = 0; i < layout.nb_channels; ++i)
|
||||
channels[i] = Audio::Channel::Unknown;
|
||||
return Audio::ChannelMap(channels);
|
||||
}
|
||||
|
||||
#define AV_CHANNEL_TO_AUDIO_CHANNEL(audio_channel, av_channel) \
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibMedia/Audio/ChannelMap.h>
|
||||
#include <LibMedia/Containers/Matroska/MatroskaDemuxer.h>
|
||||
#include <LibMedia/FFmpeg/FFmpegDemuxer.h>
|
||||
#include <LibMedia/IncrementallyPopulatedStream.h>
|
||||
@@ -106,3 +107,34 @@ TEST_CASE(video_provider_start_suspend_then_exit)
|
||||
MUST(Core::System::sleep_ms(1));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE(audio_provider_underspecified_5_1_channel_map)
|
||||
{
|
||||
Core::EventLoop loop;
|
||||
|
||||
auto stream = load_test_file("WAV/tone_44100_5_1_underspecified.wav"sv);
|
||||
auto demuxer = create_demuxer(stream);
|
||||
auto track = TRY_OR_FAIL(demuxer->get_preferred_track_for_type(Media::TrackType::Audio));
|
||||
VERIFY(track.has_value());
|
||||
|
||||
auto provider = TRY_OR_FAIL(Media::AudioDataProvider::try_create(Core::EventLoop::current_weak(), demuxer, track.release_value()));
|
||||
|
||||
provider->start();
|
||||
|
||||
auto time_limit = AK::Duration::from_seconds(1);
|
||||
auto start_time = MonotonicTime::now_coarse();
|
||||
|
||||
while (true) {
|
||||
auto block = provider->retrieve_block();
|
||||
if (!block.is_empty()) {
|
||||
EXPECT_EQ(block.channel_count(), 6);
|
||||
EXPECT_EQ(block.sample_specification().channel_map(), Audio::ChannelMap::surround_5_1());
|
||||
return;
|
||||
}
|
||||
if (MonotonicTime::now_coarse() - start_time >= time_limit)
|
||||
break;
|
||||
loop.pump(Core::EventLoop::WaitMode::PollForEvents);
|
||||
}
|
||||
|
||||
FAIL("Decoding timed out.");
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <LibCore/EventLoop.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibMedia/Audio/ChannelMap.h>
|
||||
#include <LibMedia/Containers/Matroska/MatroskaDemuxer.h>
|
||||
#include <LibMedia/Containers/Matroska/Reader.h>
|
||||
#include <LibMedia/Demuxer.h>
|
||||
@@ -65,7 +67,7 @@ static inline void decode_video(StringView path, size_t expected_frame_count, T
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
static inline void decode_audio(StringView path, u32 sample_rate, u8 channel_count, size_t expected_sample_count)
|
||||
static inline void decode_audio(StringView path, u32 sample_rate, u8 channel_count, size_t expected_sample_count, Optional<Audio::ChannelMap> expected_channel_map = {})
|
||||
{
|
||||
Core::EventLoop loop;
|
||||
|
||||
@@ -105,6 +107,8 @@ static inline void decode_audio(StringView path, u32 sample_rate, u8 channel_cou
|
||||
} else {
|
||||
EXPECT_EQ(block.sample_rate(), sample_rate);
|
||||
EXPECT_EQ(block.channel_count(), channel_count);
|
||||
if (expected_channel_map.has_value())
|
||||
EXPECT_EQ(block.sample_specification().channel_map(), expected_channel_map.value());
|
||||
|
||||
VERIFY(sample_count == 0 || last_sample <= block.timestamp_in_samples());
|
||||
last_sample = block.timestamp_in_samples() + static_cast<i64>(block.sample_count());
|
||||
|
||||
@@ -58,3 +58,13 @@ TEST_CASE(stereo_44khz)
|
||||
{
|
||||
decode_audio("WAV/tone_44100_stereo.wav"sv, 44100, 2, 220500);
|
||||
}
|
||||
|
||||
TEST_CASE(stereo_8khz_24bit)
|
||||
{
|
||||
decode_audio("WAV/voices_8000_stereo_int24.wav"sv, 8000, 2, 23493);
|
||||
}
|
||||
|
||||
TEST_CASE(underspecified_5_1_44khz)
|
||||
{
|
||||
decode_audio("WAV/tone_44100_5_1_underspecified.wav"sv, 44100, 6, 44100, Audio::ChannelMap::surround_5_1());
|
||||
}
|
||||
|
||||
BIN
Tests/LibMedia/WAV/tone_44100_5_1_underspecified.wav
Normal file
BIN
Tests/LibMedia/WAV/tone_44100_5_1_underspecified.wav
Normal file
Binary file not shown.
BIN
Tests/LibMedia/WAV/voices_8000_stereo_int24.wav
Normal file
BIN
Tests/LibMedia/WAV/voices_8000_stereo_int24.wav
Normal file
Binary file not shown.
Reference in New Issue
Block a user