Bug 1248861: P5. Add AudioConverter class and relatives. r?cpearce draft
authorJean-Yves Avenard <jyavenard@mozilla.com>
Mon, 04 Apr 2016 18:22:05 +1000
changeset 348780 25c889863c725311f3bed47988123b4313eb3550
parent 348779 85b8d2564e2f0aab2585c0b3060f0986dea5ea22
child 348781 0f6fc6b8a9ff5edcec3e5e3e62721976aa2a8f25
push id14921
push userbmo:jyavenard@mozilla.com
push dateFri, 08 Apr 2016 03:22:06 +0000
reviewerscpearce
bugs1248861
milestone48.0a1
Bug 1248861: P5. Add AudioConverter class and relatives. r?cpearce To be used in combination with AudioDataBuffer class that will be able to perform format conversion. Can currently only perform channel re-ordering. Future use will add downmixing, upmixing and resampling capabilities. MozReview-Commit-ID: 2FBu9aRVtgj
dom/media/AudioConverter.cpp
dom/media/AudioConverter.h
dom/media/moz.build
new file mode 100644
--- /dev/null
+++ b/dom/media/AudioConverter.cpp
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AudioConverter.h"
+#include <string.h>
+
+/*
+ *  Parts derived from MythTV AudioConvert Class
+ *  Created by Jean-Yves Avenard.
+ *
+ *  Copyright (C) Bubblestuff Pty Ltd 2013
+ *  Copyright (C) foobum@gmail.com 2010
+ */
+
+namespace mozilla {
+
+AudioConverter::AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut)
+  : mIn(aIn)
+  , mOut(aOut)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aIn.Channels() == aOut.Channels() &&
+                        aIn.Rate() == aOut.Rate() &&
+                        aIn.Format() == aOut.Format() &&
+                        aIn.Interleaved() == aOut.Interleaved(),
+                        "Only channel reordering is supported at this stage");
+  MOZ_DIAGNOSTIC_ASSERT(aOut.Interleaved(), "planar audio format not supported");
+  InitChannelMap();
+}
+
+bool
+AudioConverter::InitChannelMap()
+{
+  if (!CanReorderAudio()) {
+    return false;
+  }
+  for (uint32_t i = 0; i < mIn.Layout().Count(); i++) {
+    for (uint32_t j = 0; j < mIn.Layout().Count(); j++) {
+      if (mOut.Layout()[j] == mIn.Layout()[i]) {
+        mChannelOrderMap[j] = i;
+        break;
+      }
+    }
+  }
+  return true;
+}
+
+bool
+AudioConverter::CanWorkInPlace() const
+{
+  return mIn.Channels() * mIn.Rate() * AudioConfig::SampleSize(mIn.Format()) <=
+    mOut.Channels() * mOut.Rate() * AudioConfig::SampleSize(mOut.Format());
+}
+
+size_t
+AudioConverter::Process(void* aOut, const void* aIn, size_t aBytes)
+{
+  if (!CanWorkInPlace()) {
+    return 0;
+  }
+  if (mIn.Layout() != mOut.Layout() &&
+      CanReorderAudio()) {
+    ReOrderInterleavedChannels(aOut, aIn, aBytes);
+  }
+  return aBytes;
+}
+
+// Reorder interleaved channels.
+// Can work in place (e.g aOut == aIn).
+template <class AudioDataType>
+void
+_ReOrderInterleavedChannels(AudioDataType* aOut, const AudioDataType* aIn,
+                            uint32_t aFrames, uint32_t aChannels,
+                            const uint32_t* aChannelOrderMap)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aChannels <= MAX_AUDIO_CHANNELS);
+  AudioDataType val[MAX_AUDIO_CHANNELS];
+  for (uint32_t i = 0; i < aFrames; i++) {
+    for (uint32_t j = 0; j < aChannels; j++) {
+      val[j] = aIn[aChannelOrderMap[j]];
+    }
+    for (uint32_t j = 0; j < aChannels; j++) {
+      aOut[j] = val[j];
+    }
+    aOut += aChannels;
+    aIn += aChannels;
+  }
+}
+
+void
+AudioConverter::ReOrderInterleavedChannels(void* aOut, const void* aIn,
+                                           size_t aDataSize) const
+{
+  MOZ_DIAGNOSTIC_ASSERT(mIn.Channels() == mOut.Channels());
+
+  if (mOut.Layout() == mIn.Layout()) {
+    return;
+  }
+  if (mOut.Channels() == 1) {
+    // If channel count is 1, planar and non-planar formats are the same and
+    // there's nothing to reorder.
+    memmove(aOut, aIn, aDataSize);
+    return;
+  }
+
+  uint32_t bits = AudioConfig::FormatToBits(mOut.Format());
+  switch (bits) {
+    case 8:
+      _ReOrderInterleavedChannels((uint8_t*)aOut, (const uint8_t*)aIn,
+                                  aDataSize/sizeof(uint8_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+    case 16:
+      _ReOrderInterleavedChannels((int16_t*)aOut,(const int16_t*)aIn,
+                                  aDataSize/sizeof(int16_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+    default:
+      MOZ_DIAGNOSTIC_ASSERT(AudioConfig::SampleSize(mOut.Format()) == 4);
+      _ReOrderInterleavedChannels((int32_t*)aOut,(const int32_t*)aIn,
+                                  aDataSize/sizeof(int32_t)/mIn.Channels(),
+                                  mIn.Channels(), mChannelOrderMap);
+      break;
+  }
+}
+
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/AudioConverter.h
@@ -0,0 +1,146 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#if !defined(AudioConverter_h)
+#define AudioConverter_h
+
+#include "MediaInfo.h"
+
+namespace mozilla {
+
+template <AudioConfig::SampleFormat T> struct AudioDataBufferTypeChooser;
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_U8>
+{ typedef uint8_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S16>
+{ typedef int16_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24LSB>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S24>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_S32>
+{ typedef int32_t Type; };
+template <> struct AudioDataBufferTypeChooser<AudioConfig::FORMAT_FLT>
+{ typedef float Type; };
+
+// 'Value' is the type used externally to deal with stored value.
+// AudioDataBuffer can perform conversion between different SampleFormat content.
+template <AudioConfig::SampleFormat Format, typename Value = typename AudioDataBufferTypeChooser<Format>::Type>
+class AudioDataBuffer
+{
+public:
+  AudioDataBuffer() {}
+  AudioDataBuffer(Value* aBuffer, size_t aLength)
+    : mBuffer(aBuffer, aLength)
+  {}
+  explicit AudioDataBuffer(const AudioDataBuffer& aOther)
+    : mBuffer(aOther.mBuffer)
+  {}
+  AudioDataBuffer(AudioDataBuffer&& aOther)
+    : mBuffer(Move(aOther.mBuffer))
+  {}
+  template <AudioConfig::SampleFormat OtherFormat, typename OtherValue>
+  explicit AudioDataBuffer(const AudioDataBuffer<OtherFormat, OtherValue>& other)
+  {
+    // TODO: Convert from different type, may use asm routines.
+    MOZ_CRASH("Conversion not implemented yet");
+  }
+
+  // A u8, s16 and float aligned buffer can only be treated as
+  // FORMAT_U8, FORMAT_S16 and FORMAT_FLT respectively.
+  // So allow them as copy and move constructors.
+  explicit AudioDataBuffer(const AlignedByteBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_U8,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedShortBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_S16,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedFloatBuffer& aBuffer)
+    : mBuffer(aBuffer)
+  {
+    static_assert(Format == AudioConfig::FORMAT_FLT,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(AlignedByteBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_U8,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(AlignedShortBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_S16,
+                  "Conversion not implemented yet");
+  }
+  explicit AudioDataBuffer(const AlignedFloatBuffer&& aBuffer)
+    : mBuffer(Move(aBuffer))
+  {
+    static_assert(Format == AudioConfig::FORMAT_FLT,
+                  "Conversion not implemented yet");
+  }
+
+  Value* Data() const { return mBuffer.Data(); }
+  size_t Length() const { return mBuffer.Length(); }
+  size_t Size() const { return mBuffer.Size(); }
+  AlignedBuffer<Value> Forget()
+  {
+    // Correct type -> Just give values as-is.
+    return Move(mBuffer);
+  }
+private:
+  AlignedBuffer<Value> mBuffer;
+};
+
+typedef AudioDataBuffer<AudioConfig::FORMAT_DEFAULT> AudioSampleBuffer;
+
+class AudioConverter {
+public:
+  AudioConverter(const AudioConfig& aIn, const AudioConfig& aOut);
+
+  // Attempt to convert the AudioDataBuffer in place.
+  // Will return 0 if the conversion wasn't possible.
+  // Process may allocate memory internally should intermediary steps be
+  // required.
+  template <AudioConfig::SampleFormat Type, typename Value>
+  size_t Process(AudioDataBuffer<Type, Value>& aBuffer)
+  {
+    MOZ_DIAGNOSTIC_ASSERT(mIn.Format() == mOut.Format() && mIn.Format() == Type);
+    return Process(aBuffer.Data(), aBuffer.Data(), aBuffer.Size());
+  }
+  bool CanWorkInPlace() const;
+  bool CanReorderAudio() const
+  {
+    return mIn.Layout().IsValid() && mOut.Layout().IsValid() &&
+      mIn.Layout().Map() == mOut.Layout().Map();
+  }
+
+private:
+  const AudioConfig mIn;
+  const AudioConfig mOut;
+  uint32_t mChannelOrderMap[MAX_AUDIO_CHANNELS];
+  bool InitChannelMap();
+  /**
+   * Process
+   * Parameters:
+   * aOut  : destination buffer where converted samples will be copied
+   * aIn   : source buffer
+   * aBytes: size in bytes of source buffer
+   *
+   * Return Value: size in bytes of samples converted or 0 if error
+   */
+  size_t Process(void* aOut, const void* aIn, size_t aBytes);
+  void ReOrderInterleavedChannels(void* aOut, const void* aIn, size_t aDataSize) const;
+};
+
+} // namespace mozilla
+
+#endif /* AudioConverter_h */
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -84,16 +84,17 @@ XPIDL_MODULE = 'dom_media'
 
 EXPORTS += [
     'AbstractMediaDecoder.h',
     'ADTSDecoder.h',
     'ADTSDemuxer.h',
     'AudioBufferUtils.h',
     'AudioChannelFormat.h',
     'AudioCompactor.h',
+    'AudioConverter.h',
     'AudioMixer.h',
     'AudioPacketizer.h',
     'AudioSampleFormat.h',
     'AudioSegment.h',
     'AudioStream.h',
     'Benchmark.h',
     'BufferMediaResource.h',
     'CubebUtils.h',
@@ -190,16 +191,17 @@ EXPORTS.mozilla.dom += [
 ]
 
 UNIFIED_SOURCES += [
     'ADTSDecoder.cpp',
     'ADTSDemuxer.cpp',
     'AudioCaptureStream.cpp',
     'AudioChannelFormat.cpp',
     'AudioCompactor.cpp',
+    'AudioConverter.cpp',
     'AudioSegment.cpp',
     'AudioStream.cpp',
     'AudioStreamTrack.cpp',
     'AudioTrack.cpp',
     'AudioTrackList.cpp',
     'Benchmark.cpp',
     'CanvasCaptureMediaStream.cpp',
     'CubebUtils.cpp',