1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* 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 "AudioResampler.h"
namespace mozilla {
AudioResampler::AudioResampler(uint32_t aInRate, uint32_t aOutRate,
media::TimeUnit aPreBufferDuration,
const PrincipalHandle& aPrincipalHandle)
: mResampler(aInRate, aOutRate, aPreBufferDuration),
mOutputChunks(aOutRate / 10, STEREO, aPrincipalHandle) {}
void AudioResampler::AppendInput(const AudioSegment& aInSegment) {
MOZ_ASSERT(aInSegment.GetDuration());
for (AudioSegment::ConstChunkIterator iter(aInSegment); !iter.IsEnded();
iter.Next()) {
const AudioChunk& chunk = *iter;
if (!mIsSampleFormatSet) {
// We don't know the format yet and all buffers are empty.
if (chunk.mBufferFormat == AUDIO_FORMAT_SILENCE) {
// Only silence has been received and the format is unkown. Igonre it,
// if Resampler() is called it will return silence too.
continue;
}
// First no silence data, set the format once for lifetime and let it
// continue the rest of the flow. We will not get in here again.
mOutputChunks.SetSampleFormat(chunk.mBufferFormat);
mResampler.SetSampleFormat(chunk.mBufferFormat);
mIsSampleFormatSet = true;
}
MOZ_ASSERT(mIsSampleFormatSet);
if (chunk.IsNull()) {
mResampler.AppendInputSilence(chunk.GetDuration());
continue;
}
// Make sure the channel is up to date. An AudioSegment can contain chunks
// with different channel count.
UpdateChannels(chunk.mChannelData.Length());
if (chunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
mResampler.AppendInput(chunk.ChannelData<float>(), chunk.GetDuration());
} else {
mResampler.AppendInput(chunk.ChannelData<int16_t>(), chunk.GetDuration());
}
}
}
AudioSegment AudioResampler::Resample(uint32_t aOutFrames, bool* aHasUnderrun) {
MOZ_ASSERT(aHasUnderrun);
AudioSegment segment;
// We don't know what to do yet and we only have received silence if any just
// return what they want and leave
if (!mIsSampleFormatSet) {
segment.AppendNullData(aOutFrames);
return segment;
}
media::TimeUnit outDuration(aOutFrames, mResampler.GetOutRate());
mResampler.EnsurePreBuffer(outDuration);
const media::TimeUnit chunkCapacity(mOutputChunks.ChunkCapacity(),
mResampler.GetOutRate());
while (!outDuration.IsZero()) {
MOZ_ASSERT(outDuration.IsPositive());
AudioChunk& chunk = mOutputChunks.GetNext();
const media::TimeUnit chunkDuration = std::min(outDuration, chunkCapacity);
outDuration -= chunkDuration;
const uint32_t outFrames =
chunkDuration.ToTicksAtRate(mResampler.GetOutRate());
for (uint32_t i = 0; i < chunk.ChannelCount(); ++i) {
if (chunk.mBufferFormat == AUDIO_FORMAT_FLOAT32) {
*aHasUnderrun |= mResampler.Resample(
chunk.ChannelDataForWrite<float>(i), outFrames, i);
} else {
*aHasUnderrun |= mResampler.Resample(
chunk.ChannelDataForWrite<int16_t>(i), outFrames, i);
}
}
chunk.mDuration = outFrames;
// Create a copy in order to consume that copy and not the pre-allocated
// chunk
segment.AppendAndConsumeChunk(AudioChunk(chunk));
}
return segment;
}
void AudioResampler::Update(uint32_t aOutRate, uint32_t aChannels) {
mResampler.UpdateResampler(aOutRate, aChannels);
mOutputChunks.Update(aChannels);
}
uint32_t AudioResampler::InputCapacityFrames() const {
return mResampler.InFramesBufferSize();
}
uint32_t AudioResampler::InputReadableFrames() const {
return mResampler.InFramesBuffered(0);
}
} // namespace mozilla
|