chromium/chromeos/ash/services/recording/audio_capture_util.cc

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif

#include "chromeos/ash/services/recording/audio_capture_util.h"

#include "base/memory/aligned_memory.h"
#include "chromeos/ash/services/recording/recording_service_constants.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_parameters.h"
#include "media/base/audio_timestamp_helper.h"
#include "media/base/vector_math.h"

namespace recording::audio_capture_util {

namespace {

static_assert(kAudioSampleRate % 100 == 0,
              "Audio sample rate is not divisible by 100");

// Using `media::vector_math::FMAC()` works only if the addresses of `src` and
// `dest` are `kRequiredAlignment` bit aligned.
// This returns true if that's the case.
bool CanUseVectorMath(const float* src, const float* dest) {
  return base::IsAligned(src, media::vector_math::kRequiredAlignment) &&
         base::IsAligned(dest, media::vector_math::kRequiredAlignment);
}

// If `media::vector_math::FMAC()` cannot be used due to lack of required
// alignment, this version can be used to accumulate the `length` number of
// items from `src` on top of the values existing in `dest`.
void Accumulate(const float* src, float* dest, int length) {
  for (int i = 0; i < length; ++i) {
    dest[i] += src[i];
  }
}

}  // namespace

media::AudioParameters GetAudioCaptureParameters() {
  return media::AudioParameters(media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
                                media::ChannelLayoutConfig::Stereo(),
                                kAudioSampleRate, kAudioSampleRate / 100);
}

int64_t NumberOfAudioFramesInDuration(base::TimeDelta duration) {
  return media::AudioTimestampHelper::TimeToFrames(duration, kAudioSampleRate);
}

std::unique_ptr<media::AudioBus> CreateStereoZeroInitializedAudioBusForFrames(
    int64_t frames) {
  auto bus = media::AudioBus::Create(
      media::ChannelLayoutConfig::Stereo().channels(), frames);
  bus->Zero();
  return bus;
}

std::unique_ptr<media::AudioBus> CreateStereoZeroInitializedAudioBusForDuration(
    base::TimeDelta duration) {
  return CreateStereoZeroInitializedAudioBusForFrames(
      NumberOfAudioFramesInDuration(duration));
}

void AccumulateBusTo(const media::AudioBus& source,
                     media::AudioBus* destination,
                     int source_start_frame,
                     int destination_start_frame,
                     int length) {
  CHECK_EQ(source.channels(), source.channels());
  CHECK_LE(source_start_frame + length, source.frames());
  CHECK_LE(destination_start_frame + length, destination->frames());

  for (int i = 0; i < source.channels(); ++i) {
    const float* src = &source.channel(i)[source_start_frame];
    float* dest = &destination->channel(i)[destination_start_frame];
    if (CanUseVectorMath(src, dest)) {
      media::vector_math::FMAC(src, /*scale=*/1, length, dest);
    } else {
      Accumulate(src, dest, length);
    }
  }
}

}  // namespace recording::audio_capture_util