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