content/media/AudioSegment.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "AudioSegment.h"
michael@0 7
michael@0 8 #include "AudioStream.h"
michael@0 9 #include "AudioMixer.h"
michael@0 10 #include "AudioChannelFormat.h"
michael@0 11 #include "Latency.h"
michael@0 12 #include "speex/speex_resampler.h"
michael@0 13
michael@0 14 namespace mozilla {
michael@0 15
michael@0 16 template <class SrcT, class DestT>
michael@0 17 static void
michael@0 18 InterleaveAndConvertBuffer(const SrcT** aSourceChannels,
michael@0 19 int32_t aLength, float aVolume,
michael@0 20 int32_t aChannels,
michael@0 21 DestT* aOutput)
michael@0 22 {
michael@0 23 DestT* output = aOutput;
michael@0 24 for (int32_t i = 0; i < aLength; ++i) {
michael@0 25 for (int32_t channel = 0; channel < aChannels; ++channel) {
michael@0 26 float v = AudioSampleToFloat(aSourceChannels[channel][i])*aVolume;
michael@0 27 *output = FloatToAudioSample<DestT>(v);
michael@0 28 ++output;
michael@0 29 }
michael@0 30 }
michael@0 31 }
michael@0 32
michael@0 33 void
michael@0 34 InterleaveAndConvertBuffer(const void** aSourceChannels,
michael@0 35 AudioSampleFormat aSourceFormat,
michael@0 36 int32_t aLength, float aVolume,
michael@0 37 int32_t aChannels,
michael@0 38 AudioDataValue* aOutput)
michael@0 39 {
michael@0 40 switch (aSourceFormat) {
michael@0 41 case AUDIO_FORMAT_FLOAT32:
michael@0 42 InterleaveAndConvertBuffer(reinterpret_cast<const float**>(aSourceChannels),
michael@0 43 aLength,
michael@0 44 aVolume,
michael@0 45 aChannels,
michael@0 46 aOutput);
michael@0 47 break;
michael@0 48 case AUDIO_FORMAT_S16:
michael@0 49 InterleaveAndConvertBuffer(reinterpret_cast<const int16_t**>(aSourceChannels),
michael@0 50 aLength,
michael@0 51 aVolume,
michael@0 52 aChannels,
michael@0 53 aOutput);
michael@0 54 break;
michael@0 55 case AUDIO_FORMAT_SILENCE:
michael@0 56 // nothing to do here.
michael@0 57 break;
michael@0 58 }
michael@0 59 }
michael@0 60
michael@0 61 void
michael@0 62 AudioSegment::ApplyVolume(float aVolume)
michael@0 63 {
michael@0 64 for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
michael@0 65 ci->mVolume *= aVolume;
michael@0 66 }
michael@0 67 }
michael@0 68
michael@0 69 static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */
michael@0 70 static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES] = {0};
michael@0 71
michael@0 72 void
michael@0 73 DownmixAndInterleave(const nsTArray<const void*>& aChannelData,
michael@0 74 AudioSampleFormat aSourceFormat, int32_t aDuration,
michael@0 75 float aVolume, uint32_t aOutputChannels,
michael@0 76 AudioDataValue* aOutput)
michael@0 77 {
michael@0 78 nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channelData;
michael@0 79 nsAutoTArray<float,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> downmixConversionBuffer;
michael@0 80 nsAutoTArray<float,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> downmixOutputBuffer;
michael@0 81
michael@0 82 channelData.SetLength(aChannelData.Length());
michael@0 83 if (aSourceFormat != AUDIO_FORMAT_FLOAT32) {
michael@0 84 NS_ASSERTION(aSourceFormat == AUDIO_FORMAT_S16, "unknown format");
michael@0 85 downmixConversionBuffer.SetLength(aDuration*aChannelData.Length());
michael@0 86 for (uint32_t i = 0; i < aChannelData.Length(); ++i) {
michael@0 87 float* conversionBuf = downmixConversionBuffer.Elements() + (i*aDuration);
michael@0 88 const int16_t* sourceBuf = static_cast<const int16_t*>(aChannelData[i]);
michael@0 89 for (uint32_t j = 0; j < (uint32_t)aDuration; ++j) {
michael@0 90 conversionBuf[j] = AudioSampleToFloat(sourceBuf[j]);
michael@0 91 }
michael@0 92 channelData[i] = conversionBuf;
michael@0 93 }
michael@0 94 } else {
michael@0 95 for (uint32_t i = 0; i < aChannelData.Length(); ++i) {
michael@0 96 channelData[i] = aChannelData[i];
michael@0 97 }
michael@0 98 }
michael@0 99
michael@0 100 downmixOutputBuffer.SetLength(aDuration*aOutputChannels);
michael@0 101 nsAutoTArray<float*,GUESS_AUDIO_CHANNELS> outputChannelBuffers;
michael@0 102 nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> outputChannelData;
michael@0 103 outputChannelBuffers.SetLength(aOutputChannels);
michael@0 104 outputChannelData.SetLength(aOutputChannels);
michael@0 105 for (uint32_t i = 0; i < (uint32_t)aOutputChannels; ++i) {
michael@0 106 outputChannelData[i] = outputChannelBuffers[i] =
michael@0 107 downmixOutputBuffer.Elements() + aDuration*i;
michael@0 108 }
michael@0 109 if (channelData.Length() > aOutputChannels) {
michael@0 110 AudioChannelsDownMix(channelData, outputChannelBuffers.Elements(),
michael@0 111 aOutputChannels, aDuration);
michael@0 112 }
michael@0 113 InterleaveAndConvertBuffer(outputChannelData.Elements(), AUDIO_FORMAT_FLOAT32,
michael@0 114 aDuration, aVolume, aOutputChannels, aOutput);
michael@0 115 }
michael@0 116
michael@0 117 void AudioSegment::ResampleChunks(SpeexResamplerState* aResampler)
michael@0 118 {
michael@0 119 uint32_t inRate, outRate;
michael@0 120
michael@0 121 if (mChunks.IsEmpty()) {
michael@0 122 return;
michael@0 123 }
michael@0 124
michael@0 125 speex_resampler_get_rate(aResampler, &inRate, &outRate);
michael@0 126
michael@0 127 AudioSampleFormat format = AUDIO_FORMAT_SILENCE;
michael@0 128 for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
michael@0 129 if (ci->mBufferFormat != AUDIO_FORMAT_SILENCE) {
michael@0 130 format = ci->mBufferFormat;
michael@0 131 }
michael@0 132 }
michael@0 133
michael@0 134 switch (format) {
michael@0 135 // If the format is silence at this point, all the chunks are silent. The
michael@0 136 // actual function we use does not matter, it's just a matter of changing
michael@0 137 // the chunks duration.
michael@0 138 case AUDIO_FORMAT_SILENCE:
michael@0 139 case AUDIO_FORMAT_FLOAT32:
michael@0 140 Resample<float>(aResampler, inRate, outRate);
michael@0 141 break;
michael@0 142 case AUDIO_FORMAT_S16:
michael@0 143 Resample<int16_t>(aResampler, inRate, outRate);
michael@0 144 break;
michael@0 145 default:
michael@0 146 MOZ_ASSERT(false);
michael@0 147 break;
michael@0 148 }
michael@0 149 }
michael@0 150
michael@0 151 void
michael@0 152 AudioSegment::WriteTo(uint64_t aID, AudioStream* aOutput, AudioMixer* aMixer)
michael@0 153 {
michael@0 154 uint32_t outputChannels = aOutput->GetChannels();
michael@0 155 nsAutoTArray<AudioDataValue,AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
michael@0 156 nsAutoTArray<const void*,GUESS_AUDIO_CHANNELS> channelData;
michael@0 157 // Offset in the buffer that will end up sent to the AudioStream, in samples.
michael@0 158 uint32_t offset = 0;
michael@0 159
michael@0 160 if (!GetDuration()) {
michael@0 161 return;
michael@0 162 }
michael@0 163
michael@0 164 uint32_t outBufferLength = GetDuration() * outputChannels;
michael@0 165 buf.SetLength(outBufferLength);
michael@0 166
michael@0 167
michael@0 168 for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
michael@0 169 AudioChunk& c = *ci;
michael@0 170 uint32_t frames = c.mDuration;
michael@0 171
michael@0 172 // If we have written data in the past, or we have real (non-silent) data
michael@0 173 // to write, we can proceed. Otherwise, it means we just started the
michael@0 174 // AudioStream, and we don't have real data to write to it (just silence).
michael@0 175 // To avoid overbuffering in the AudioStream, we simply drop the silence,
michael@0 176 // here. The stream will underrun and output silence anyways.
michael@0 177 if (c.mBuffer || aOutput->GetWritten()) {
michael@0 178 if (c.mBuffer && c.mBufferFormat != AUDIO_FORMAT_SILENCE) {
michael@0 179 channelData.SetLength(c.mChannelData.Length());
michael@0 180 for (uint32_t i = 0; i < channelData.Length(); ++i) {
michael@0 181 channelData[i] = c.mChannelData[i];
michael@0 182 }
michael@0 183
michael@0 184 if (channelData.Length() < outputChannels) {
michael@0 185 // Up-mix. Note that this might actually make channelData have more
michael@0 186 // than outputChannels temporarily.
michael@0 187 AudioChannelsUpMix(&channelData, outputChannels, gZeroChannel);
michael@0 188 }
michael@0 189
michael@0 190 if (channelData.Length() > outputChannels) {
michael@0 191 // Down-mix.
michael@0 192 DownmixAndInterleave(channelData, c.mBufferFormat, frames,
michael@0 193 c.mVolume, outputChannels, buf.Elements() + offset);
michael@0 194 } else {
michael@0 195 InterleaveAndConvertBuffer(channelData.Elements(), c.mBufferFormat,
michael@0 196 frames, c.mVolume,
michael@0 197 outputChannels,
michael@0 198 buf.Elements() + offset);
michael@0 199 }
michael@0 200 } else {
michael@0 201 // Assumes that a bit pattern of zeroes == 0.0f
michael@0 202 memset(buf.Elements() + offset, 0, outputChannels * frames * sizeof(AudioDataValue));
michael@0 203 }
michael@0 204 offset += frames * outputChannels;
michael@0 205 }
michael@0 206
michael@0 207 if (!c.mTimeStamp.IsNull()) {
michael@0 208 TimeStamp now = TimeStamp::Now();
michael@0 209 // would be more efficient to c.mTimeStamp to ms on create time then pass here
michael@0 210 LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
michael@0 211 (now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp);
michael@0 212 }
michael@0 213 }
michael@0 214
michael@0 215 aOutput->Write(buf.Elements(), offset / outputChannels, &(mChunks[mChunks.Length() - 1].mTimeStamp));
michael@0 216
michael@0 217 if (aMixer) {
michael@0 218 aMixer->Mix(buf.Elements(), outputChannels, GetDuration(), aOutput->GetRate());
michael@0 219 }
michael@0 220 aOutput->Start();
michael@0 221 }
michael@0 222
michael@0 223 }

mercurial