Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "media/base/audio_bus.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/safe_numerics.h"
      9 #include "media/audio/audio_parameters.h"
     10 #include "media/base/limits.h"
     11 #include "media/base/vector_math.h"
     12 
     13 namespace media {
     14 
     15 static const uint8 kUint8Bias = 128;
     16 
     17 static bool IsAligned(void* ptr) {
     18   return (reinterpret_cast<uintptr_t>(ptr) &
     19           (AudioBus::kChannelAlignment - 1)) == 0U;
     20 }
     21 
     22 // Calculates the required size for an AudioBus with the given params, sets
     23 // |aligned_frames| to the actual frame length of each channel array.
     24 static int CalculateMemorySizeInternal(int channels, int frames,
     25                                        int* out_aligned_frames) {
     26   // Choose a size such that each channel will be aligned by
     27   // kChannelAlignment when stored in a contiguous block.
     28   int aligned_frames =
     29       ((frames * sizeof(float) + AudioBus::kChannelAlignment - 1) &
     30        ~(AudioBus::kChannelAlignment - 1)) / sizeof(float);
     31 
     32   if (out_aligned_frames)
     33     *out_aligned_frames = aligned_frames;
     34 
     35   return sizeof(float) * channels * aligned_frames;
     36 }
     37 
     38 // |Format| is the destination type.  If a bias is present, |Fixed| must be a
     39 // type larger than |Format| such that operations can be made without
     40 // overflowing.  Without a bias |Fixed| must be the same as |Format|.
     41 template<class Format, class Fixed, Format Bias>
     42 static void FromInterleavedInternal(const void* src, int start_frame,
     43                                     int frames, AudioBus* dest,
     44                                     float min, float max) {
     45   COMPILE_ASSERT((Bias == 0 && sizeof(Fixed) == sizeof(Format)) ||
     46                  sizeof(Fixed) > sizeof(Format), invalid_deinterleave_types);
     47   const Format* source = static_cast<const Format*>(src);
     48   const int channels = dest->channels();
     49   for (int ch = 0; ch < channels; ++ch) {
     50     float* channel_data = dest->channel(ch);
     51     for (int i = start_frame, offset = ch; i < start_frame + frames;
     52          ++i, offset += channels) {
     53       const Fixed v = static_cast<Fixed>(source[offset]) - Bias;
     54       channel_data[i] = v * (v < 0 ? -min : max);
     55     }
     56   }
     57 }
     58 
     59 // |Format| is the destination type.  If a bias is present, |Fixed| must be a
     60 // type larger than |Format| such that operations can be made without
     61 // overflowing.  Without a bias |Fixed| must be the same as |Format|.
     62 template<class Format, class Fixed, Format Bias>
     63 static void ToInterleavedInternal(const AudioBus* source, int start_frame,
     64                                   int frames, void* dst, Fixed min, Fixed max) {
     65   COMPILE_ASSERT((Bias == 0 && sizeof(Fixed) == sizeof(Format)) ||
     66                  sizeof(Fixed) > sizeof(Format), invalid_interleave_types);
     67   Format* dest = static_cast<Format*>(dst);
     68   const int channels = source->channels();
     69   for (int ch = 0; ch < channels; ++ch) {
     70     const float* channel_data = source->channel(ch);
     71     for (int i = start_frame, offset = ch; i < start_frame + frames;
     72          ++i, offset += channels) {
     73       const float v = channel_data[i];
     74 
     75       Fixed sample;
     76       if (v < 0)
     77         sample = v <= -1 ? min : static_cast<Fixed>(-v * min);
     78       else
     79         sample = v >= 1 ? max : static_cast<Fixed>(v * max);
     80 
     81       dest[offset] = static_cast<Format>(sample) + Bias;
     82     }
     83   }
     84 }
     85 
     86 static void ValidateConfig(int channels, int frames) {
     87   CHECK_GT(frames, 0);
     88   CHECK_GT(channels, 0);
     89   CHECK_LE(channels, static_cast<int>(limits::kMaxChannels));
     90 }
     91 
     92 static void CheckOverflow(int start_frame, int frames, int total_frames) {
     93   CHECK_GE(start_frame, 0);
     94   CHECK_GE(frames, 0);
     95   CHECK_GT(total_frames, 0);
     96   int sum = start_frame + frames;
     97   CHECK_LE(sum, total_frames);
     98   CHECK_GE(sum, 0);
     99 }
    100 
    101 AudioBus::AudioBus(int channels, int frames)
    102     : frames_(frames),
    103       can_set_channel_data_(false) {
    104   ValidateConfig(channels, frames_);
    105 
    106   int aligned_frames = 0;
    107   int size = CalculateMemorySizeInternal(channels, frames, &aligned_frames);
    108 
    109   data_.reset(static_cast<float*>(base::AlignedAlloc(
    110       size, AudioBus::kChannelAlignment)));
    111 
    112   BuildChannelData(channels, aligned_frames, data_.get());
    113 }
    114 
    115 AudioBus::AudioBus(int channels, int frames, float* data)
    116     : frames_(frames),
    117       can_set_channel_data_(false) {
    118   // Since |data| may have come from an external source, ensure it's valid.
    119   CHECK(data);
    120   ValidateConfig(channels, frames_);
    121 
    122   int aligned_frames = 0;
    123   CalculateMemorySizeInternal(channels, frames, &aligned_frames);
    124 
    125   BuildChannelData(channels, aligned_frames, data);
    126 }
    127 
    128 AudioBus::AudioBus(int frames, const std::vector<float*>& channel_data)
    129     : channel_data_(channel_data),
    130       frames_(frames),
    131       can_set_channel_data_(false) {
    132   ValidateConfig(
    133       base::checked_numeric_cast<int>(channel_data_.size()), frames_);
    134 
    135   // Sanity check wrapped vector for alignment and channel count.
    136   for (size_t i = 0; i < channel_data_.size(); ++i)
    137     DCHECK(IsAligned(channel_data_[i]));
    138 }
    139 
    140 AudioBus::AudioBus(int channels)
    141     : channel_data_(channels),
    142       frames_(0),
    143       can_set_channel_data_(true) {
    144   CHECK_GT(channels, 0);
    145   for (size_t i = 0; i < channel_data_.size(); ++i)
    146     channel_data_[i] = NULL;
    147 }
    148 
    149 AudioBus::~AudioBus() {}
    150 
    151 scoped_ptr<AudioBus> AudioBus::Create(int channels, int frames) {
    152   return scoped_ptr<AudioBus>(new AudioBus(channels, frames));
    153 }
    154 
    155 scoped_ptr<AudioBus> AudioBus::Create(const AudioParameters& params) {
    156   return scoped_ptr<AudioBus>(new AudioBus(
    157       params.channels(), params.frames_per_buffer()));
    158 }
    159 
    160 scoped_ptr<AudioBus> AudioBus::CreateWrapper(int channels) {
    161   return scoped_ptr<AudioBus>(new AudioBus(channels));
    162 }
    163 
    164 scoped_ptr<AudioBus> AudioBus::WrapVector(
    165     int frames, const std::vector<float*>& channel_data) {
    166   return scoped_ptr<AudioBus>(new AudioBus(frames, channel_data));
    167 }
    168 
    169 scoped_ptr<AudioBus> AudioBus::WrapMemory(int channels, int frames,
    170                                           void* data) {
    171   // |data| must be aligned by AudioBus::kChannelAlignment.
    172   CHECK(IsAligned(data));
    173   return scoped_ptr<AudioBus>(new AudioBus(
    174       channels, frames, static_cast<float*>(data)));
    175 }
    176 
    177 scoped_ptr<AudioBus> AudioBus::WrapMemory(const AudioParameters& params,
    178                                           void* data) {
    179   // |data| must be aligned by AudioBus::kChannelAlignment.
    180   CHECK(IsAligned(data));
    181   return scoped_ptr<AudioBus>(new AudioBus(
    182       params.channels(), params.frames_per_buffer(),
    183       static_cast<float*>(data)));
    184 }
    185 
    186 void AudioBus::SetChannelData(int channel, float* data) {
    187   CHECK(can_set_channel_data_);
    188   CHECK(data);
    189   CHECK_GE(channel, 0);
    190   CHECK_LT(static_cast<size_t>(channel), channel_data_.size());
    191   DCHECK(IsAligned(data));
    192   channel_data_[channel] = data;
    193 }
    194 
    195 void AudioBus::set_frames(int frames) {
    196   CHECK(can_set_channel_data_);
    197   ValidateConfig(static_cast<int>(channel_data_.size()), frames);
    198   frames_ = frames;
    199 }
    200 
    201 void AudioBus::ZeroFramesPartial(int start_frame, int frames) {
    202   CheckOverflow(start_frame, frames, frames_);
    203 
    204   if (frames <= 0)
    205     return;
    206 
    207   for (size_t i = 0; i < channel_data_.size(); ++i) {
    208     memset(channel_data_[i] + start_frame, 0,
    209            frames * sizeof(*channel_data_[i]));
    210   }
    211 }
    212 
    213 void AudioBus::ZeroFrames(int frames) {
    214   ZeroFramesPartial(0, frames);
    215 }
    216 
    217 void AudioBus::Zero() {
    218   ZeroFrames(frames_);
    219 }
    220 
    221 int AudioBus::CalculateMemorySize(const AudioParameters& params) {
    222   return CalculateMemorySizeInternal(
    223       params.channels(), params.frames_per_buffer(), NULL);
    224 }
    225 
    226 int AudioBus::CalculateMemorySize(int channels, int frames) {
    227   return CalculateMemorySizeInternal(channels, frames, NULL);
    228 }
    229 
    230 void AudioBus::BuildChannelData(int channels, int aligned_frames, float* data) {
    231   DCHECK(IsAligned(data));
    232   DCHECK_EQ(channel_data_.size(), 0U);
    233   // Separate audio data out into channels for easy lookup later.  Figure out
    234   channel_data_.reserve(channels);
    235   for (int i = 0; i < channels; ++i)
    236     channel_data_.push_back(data + i * aligned_frames);
    237 }
    238 
    239 // TODO(dalecurtis): See if intrinsic optimizations help any here.
    240 void AudioBus::FromInterleavedPartial(const void* source, int start_frame,
    241                                       int frames, int bytes_per_sample) {
    242   CheckOverflow(start_frame, frames, frames_);
    243   switch (bytes_per_sample) {
    244     case 1:
    245       FromInterleavedInternal<uint8, int16, kUint8Bias>(
    246           source, start_frame, frames, this,
    247           1.0f / kint8min, 1.0f / kint8max);
    248       break;
    249     case 2:
    250       FromInterleavedInternal<int16, int16, 0>(
    251           source, start_frame, frames, this,
    252           1.0f / kint16min, 1.0f / kint16max);
    253       break;
    254     case 4:
    255       FromInterleavedInternal<int32, int32, 0>(
    256           source, start_frame, frames, this,
    257           1.0f / kint32min, 1.0f / kint32max);
    258       break;
    259     default:
    260       NOTREACHED() << "Unsupported bytes per sample encountered.";
    261       ZeroFramesPartial(start_frame, frames);
    262       return;
    263   }
    264 
    265   // Don't clear remaining frames if this is a partial deinterleave.
    266   if (!start_frame) {
    267     // Zero any remaining frames.
    268     ZeroFramesPartial(frames, frames_ - frames);
    269   }
    270 }
    271 
    272 void AudioBus::FromInterleaved(const void* source, int frames,
    273                                int bytes_per_sample) {
    274   FromInterleavedPartial(source, 0, frames, bytes_per_sample);
    275 }
    276 
    277 void AudioBus::ToInterleaved(int frames, int bytes_per_sample,
    278                              void* dest) const {
    279   ToInterleavedPartial(0, frames, bytes_per_sample, dest);
    280 }
    281 
    282 // TODO(dalecurtis): See if intrinsic optimizations help any here.
    283 void AudioBus::ToInterleavedPartial(int start_frame, int frames,
    284                                     int bytes_per_sample, void* dest) const {
    285   CheckOverflow(start_frame, frames, frames_);
    286   switch (bytes_per_sample) {
    287     case 1:
    288       ToInterleavedInternal<uint8, int16, kUint8Bias>(
    289           this, start_frame, frames, dest, kint8min, kint8max);
    290       break;
    291     case 2:
    292       ToInterleavedInternal<int16, int16, 0>(
    293           this, start_frame, frames, dest, kint16min, kint16max);
    294       break;
    295     case 4:
    296       ToInterleavedInternal<int32, int32, 0>(
    297           this, start_frame, frames, dest, kint32min, kint32max);
    298       break;
    299     default:
    300       NOTREACHED() << "Unsupported bytes per sample encountered.";
    301       memset(dest, 0, frames * bytes_per_sample);
    302       return;
    303   }
    304 }
    305 
    306 void AudioBus::CopyTo(AudioBus* dest) const {
    307   CopyPartialFramesTo(0, frames(), 0, dest);
    308 }
    309 
    310 void AudioBus::CopyPartialFramesTo(int source_start_frame,
    311                                    int frame_count,
    312                                    int dest_start_frame,
    313                                    AudioBus* dest) const {
    314   CHECK_EQ(channels(), dest->channels());
    315   CHECK_LE(source_start_frame + frame_count, frames());
    316   CHECK_LE(dest_start_frame + frame_count, dest->frames());
    317 
    318   // Since we don't know if the other AudioBus is wrapped or not (and we don't
    319   // want to care), just copy using the public channel() accessors.
    320   for (int i = 0; i < channels(); ++i) {
    321     memcpy(dest->channel(i) + dest_start_frame,
    322            channel(i) + source_start_frame,
    323            sizeof(*channel(i)) * frame_count);
    324   }
    325 }
    326 
    327 void AudioBus::Scale(float volume) {
    328   if (volume > 0 && volume != 1) {
    329     for (int i = 0; i < channels(); ++i)
    330       vector_math::FMUL(channel(i), volume, frames(), channel(i));
    331   } else if (volume == 0) {
    332     Zero();
    333   }
    334 }
    335 
    336 }  // namespace media
    337