Changeset 198035 in webkit


Ignore:
Timestamp:
Mar 11, 2016 11:35:15 AM (8 years ago)
Author:
jer.noble@apple.com
Message:

Web Audio becomes distorted after sample rate changes
https://bugs.webkit.org/show_bug.cgi?id=154538
<rdar://problem/24771292>

Reviewed by Darin Adler.

When the underlying audio hardware sample rate changes, the AudioUnit render callback will begin asking
for fewer or more frames. For example, when the sample rate goes from 44.1kHz to 48kHz, it will ask for
118 samples instead of 128. (And vice-versa, 140 samples instead of 128.) But the Web Audio engine can only
really handle requests in multiples of 128 samples. In the case where there are requests for < 128 samples,
actually render 128, but save off the unrequested samples in a separate bus. Then fill that bus during the
next request.

  • platform/audio/AudioBus.cpp:

(WebCore::AudioBus::copyFromRange): Added utility method.

  • platform/audio/AudioBus.h:
  • platform/audio/ios/AudioDestinationIOS.cpp:

(WebCore::AudioDestinationIOS::AudioDestinationIOS): Create a "spare" bus.
(WebCore::assignAudioBuffersToBus): Moved from inside render.
(WebCore::AudioDestinationIOS::render): Save off extra samples to the "spare" bus.

  • platform/audio/ios/AudioDestinationIOS.h:
Location:
trunk/Source/WebCore
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r198028 r198035  
     12016-03-10  Jer Noble  <jer.noble@apple.com>
     2
     3        Web Audio becomes distorted after sample rate changes
     4        https://bugs.webkit.org/show_bug.cgi?id=154538
     5        <rdar://problem/24771292>
     6
     7        Reviewed by Darin Adler.
     8
     9        When the underlying audio hardware sample rate changes, the AudioUnit render callback will begin asking
     10        for fewer or more frames. For example, when the sample rate goes from 44.1kHz to 48kHz, it will ask for
     11        118 samples instead of 128. (And vice-versa, 140 samples instead of 128.) But the Web Audio engine can only
     12        really handle requests in multiples of 128 samples. In the case where there are requests for < 128 samples,
     13        actually render 128, but save off the unrequested samples in a separate bus. Then fill that bus during the
     14        next request.
     15
     16        * platform/audio/AudioBus.cpp:
     17        (WebCore::AudioBus::copyFromRange): Added utility method.
     18        * platform/audio/AudioBus.h:
     19        * platform/audio/ios/AudioDestinationIOS.cpp:
     20        (WebCore::AudioDestinationIOS::AudioDestinationIOS): Create a "spare" bus.
     21        (WebCore::assignAudioBuffersToBus): Moved from inside render.
     22        (WebCore::AudioDestinationIOS::render): Save off extra samples to the "spare" bus.
     23        * platform/audio/ios/AudioDestinationIOS.h:
     24
    1252016-03-11  Yusuke Suzuki  <utatane.tea@gmail.com>
    226
  • trunk/Source/WebCore/platform/audio/AudioBus.cpp

    r194496 r198035  
    214214}
    215215
     216void AudioBus::copyFromRange(const AudioBus& sourceBus, unsigned startFrame, unsigned endFrame)
     217{
     218    if (!topologyMatches(sourceBus)) {
     219        ASSERT_NOT_REACHED();
     220        zero();
     221        return;
     222    }
     223
     224    size_t numberOfSourceFrames = sourceBus.length();
     225    bool isRangeSafe = startFrame < endFrame && endFrame <= numberOfSourceFrames;
     226    ASSERT(isRangeSafe);
     227    if (!isRangeSafe) {
     228        zero();
     229        return;
     230    }
     231
     232    unsigned numberOfChannels = this->numberOfChannels();
     233    ASSERT(numberOfChannels <= MaxBusChannels);
     234    if (numberOfChannels > MaxBusChannels) {
     235        zero();
     236        return;
     237    }
     238
     239    for (unsigned i = 0; i < numberOfChannels; ++i)
     240        channel(i)->copyFromRange(sourceBus.channel(i), startFrame, endFrame);
     241}
     242
    216243void AudioBus::copyFrom(const AudioBus& sourceBus, ChannelInterpretation channelInterpretation)
    217244{
  • trunk/Source/WebCore/platform/audio/AudioBus.h

    r177733 r198035  
    123123
    124124    // Copies the samples from the source bus to this one.
     125    void copyFromRange(const AudioBus& sourceBus, unsigned startFrame, unsigned endFrame);
     126
     127    // Copies the samples from the source bus to this one.
    125128    // This is just a simple per-channel copy if the number of channels match, otherwise an up-mix or down-mix is done.
    126129    void copyFrom(const AudioBus& sourceBus, ChannelInterpretation = Speakers);
  • trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp

    r197628 r198035  
    102102    , m_callback(callback)
    103103    , m_renderBus(AudioBus::create(2, kRenderBufferSize, false))
     104    , m_spareBus(AudioBus::create(2, kRenderBufferSize, true))
    104105    , m_sampleRate(sampleRate)
    105106    , m_isPlaying(false)
     
    200201}
    201202
     203static void assignAudioBuffersToBus(AudioBuffer* buffers, AudioBus& bus, UInt32 numberOfBuffers, UInt32 numberOfFrames, UInt32 frameOffset, UInt32 framesThisTime)
     204{
     205    for (UInt32 i = 0; i < numberOfBuffers; ++i) {
     206        UInt32 bytesPerFrame = buffers[i].mDataByteSize / numberOfFrames;
     207        UInt32 byteOffset = frameOffset * bytesPerFrame;
     208        float* memory = reinterpret_cast<float*>(reinterpret_cast<char*>(buffers[i].mData) + byteOffset);
     209        bus.setChannelMemory(i, memory, framesThisTime);
     210    }
     211}
     212
    202213// Pulls on our provider to get rendered audio stream.
    203214OSStatus AudioDestinationIOS::render(UInt32 numberOfFrames, AudioBufferList* ioData)
    204215{
    205216    AudioBuffer* buffers = ioData->mBuffers;
    206     for (UInt32 frameOffset = 0; frameOffset + kRenderBufferSize <= numberOfFrames; frameOffset += kRenderBufferSize) {
    207         UInt32 remainingFrames = std::min<UInt32>(kRenderBufferSize, numberOfFrames - frameOffset);
    208         for (UInt32 i = 0; i < ioData->mNumberBuffers; ++i) {
    209             UInt32 bytesPerFrame = buffers[i].mDataByteSize / numberOfFrames;
    210             UInt32 byteOffset = frameOffset * bytesPerFrame;
    211             float* memory = (float*)((char*)buffers[i].mData + byteOffset);
    212             m_renderBus->setChannelMemory(i, memory, remainingFrames);
     217    UInt32 numberOfBuffers = ioData->mNumberBuffers;
     218    UInt32 framesRemaining = numberOfFrames;
     219    UInt32 frameOffset = 0;
     220    while (framesRemaining > 0) {
     221        if (m_firstSpareFrame && m_lastSpareFrame) {
     222            ASSERT(m_firstSpareFrame < m_lastSpareFrame);
     223            UInt32 framesThisTime = m_lastSpareFrame - m_firstSpareFrame;
     224            assignAudioBuffersToBus(buffers, *m_renderBus, numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
     225            m_renderBus->copyFromRange(*m_spareBus, m_firstSpareFrame, m_lastSpareFrame);
     226            frameOffset += framesThisTime;
     227            framesRemaining -= framesThisTime;
     228            m_firstSpareFrame = 0;
     229            m_lastSpareFrame = 0;
    213230        }
    214         m_callback.render(0, m_renderBus.get(), remainingFrames);
     231
     232        UInt32 framesThisTime = std::min<UInt32>(kRenderBufferSize, framesRemaining);
     233        assignAudioBuffersToBus(buffers, *m_renderBus, numberOfBuffers, numberOfFrames, frameOffset, framesThisTime);
     234
     235        if (framesThisTime < kRenderBufferSize) {
     236            m_callback.render(0, m_spareBus.get(), kRenderBufferSize);
     237            m_renderBus->copyFromRange(*m_spareBus, 0, framesThisTime);
     238            m_firstSpareFrame = framesThisTime;
     239            m_lastSpareFrame = kRenderBufferSize - 1;
     240        } else
     241            m_callback.render(0, m_renderBus.get(), framesThisTime);
     242        frameOffset += framesThisTime;
     243        framesRemaining -= framesThisTime;
    215244    }
    216245
  • trunk/Source/WebCore/platform/audio/ios/AudioDestinationIOS.h

    r197563 r198035  
    6666    AudioIOCallback& m_callback;
    6767    RefPtr<AudioBus> m_renderBus;
     68    RefPtr<AudioBus> m_spareBus;
     69    unsigned m_firstSpareFrame { 0 };
     70    unsigned m_lastSpareFrame { 0 };
    6871
    6972    double m_sampleRate;
Note: See TracChangeset for help on using the changeset viewer.