Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 : * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "AudioSegment.h"
7 :
8 : #include "AudioMixer.h"
9 : #include "AudioChannelFormat.h"
10 : #include "Latency.h"
11 : #include <speex/speex_resampler.h>
12 :
13 : namespace mozilla {
14 :
15 : const uint8_t SilentChannel::gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*SilentChannel::AUDIO_PROCESSING_FRAMES] = {0};
16 :
17 : template<>
18 0 : const float* SilentChannel::ZeroChannel<float>()
19 : {
20 0 : return reinterpret_cast<const float*>(SilentChannel::gZeroChannel);
21 : }
22 :
23 : template<>
24 0 : const int16_t* SilentChannel::ZeroChannel<int16_t>()
25 : {
26 0 : return reinterpret_cast<const int16_t*>(SilentChannel::gZeroChannel);
27 : }
28 :
29 : void
30 0 : AudioSegment::ApplyVolume(float aVolume)
31 : {
32 0 : for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
33 0 : ci->mVolume *= aVolume;
34 : }
35 0 : }
36 :
37 0 : void AudioSegment::ResampleChunks(SpeexResamplerState* aResampler, uint32_t aInRate, uint32_t aOutRate)
38 : {
39 0 : if (mChunks.IsEmpty()) {
40 0 : return;
41 : }
42 :
43 0 : MOZ_ASSERT(aResampler || IsNull(), "We can only be here without a resampler if this segment is null.");
44 :
45 0 : AudioSampleFormat format = AUDIO_FORMAT_SILENCE;
46 0 : for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
47 0 : if (ci->mBufferFormat != AUDIO_FORMAT_SILENCE) {
48 0 : format = ci->mBufferFormat;
49 : }
50 : }
51 :
52 0 : switch (format) {
53 : // If the format is silence at this point, all the chunks are silent. The
54 : // actual function we use does not matter, it's just a matter of changing
55 : // the chunks duration.
56 : case AUDIO_FORMAT_SILENCE:
57 : case AUDIO_FORMAT_FLOAT32:
58 0 : Resample<float>(aResampler, aInRate, aOutRate);
59 0 : break;
60 : case AUDIO_FORMAT_S16:
61 0 : Resample<int16_t>(aResampler, aInRate, aOutRate);
62 0 : break;
63 : default:
64 0 : MOZ_ASSERT(false);
65 : break;
66 : }
67 : }
68 :
69 : // This helps to to safely get a pointer to the position we want to start
70 : // writing a planar audio buffer, depending on the channel and the offset in the
71 : // buffer.
72 : static AudioDataValue*
73 0 : PointerForOffsetInChannel(AudioDataValue* aData, size_t aLengthSamples,
74 : uint32_t aChannelCount, uint32_t aChannel,
75 : uint32_t aOffsetSamples)
76 : {
77 0 : size_t samplesPerChannel = aLengthSamples / aChannelCount;
78 0 : size_t beginningOfChannel = samplesPerChannel * aChannel;
79 0 : MOZ_ASSERT(aChannel * samplesPerChannel + aOffsetSamples < aLengthSamples,
80 : "Offset request out of bounds.");
81 0 : return aData + beginningOfChannel + aOffsetSamples;
82 : }
83 :
84 : void
85 0 : AudioSegment::Mix(AudioMixer& aMixer, uint32_t aOutputChannels,
86 : uint32_t aSampleRate)
87 : {
88 : AutoTArray<AudioDataValue, SilentChannel::AUDIO_PROCESSING_FRAMES* GUESS_AUDIO_CHANNELS>
89 0 : buf;
90 0 : AutoTArray<const AudioDataValue*, GUESS_AUDIO_CHANNELS> channelData;
91 0 : uint32_t offsetSamples = 0;
92 0 : uint32_t duration = GetDuration();
93 :
94 0 : if (duration <= 0) {
95 0 : MOZ_ASSERT(duration == 0);
96 0 : return;
97 : }
98 :
99 0 : uint32_t outBufferLength = duration * aOutputChannels;
100 0 : buf.SetLength(outBufferLength);
101 :
102 0 : for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
103 0 : AudioChunk& c = *ci;
104 0 : uint32_t frames = c.mDuration;
105 :
106 : // If the chunk is silent, simply write the right number of silence in the
107 : // buffers.
108 0 : if (c.mBufferFormat == AUDIO_FORMAT_SILENCE) {
109 0 : for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
110 : AudioDataValue* ptr =
111 0 : PointerForOffsetInChannel(buf.Elements(), outBufferLength,
112 0 : aOutputChannels, channel, offsetSamples);
113 0 : PodZero(ptr, frames);
114 : }
115 : } else {
116 : // Othewise, we need to upmix or downmix appropriately, depending on the
117 : // desired input and output channels.
118 0 : channelData.SetLength(c.mChannelData.Length());
119 0 : for (uint32_t i = 0; i < channelData.Length(); ++i) {
120 0 : channelData[i] = static_cast<const AudioDataValue*>(c.mChannelData[i]);
121 : }
122 0 : if (channelData.Length() < aOutputChannels) {
123 : // Up-mix.
124 0 : AudioChannelsUpMix(&channelData, aOutputChannels, SilentChannel::ZeroChannel<AudioDataValue>());
125 0 : for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
126 : AudioDataValue* ptr =
127 0 : PointerForOffsetInChannel(buf.Elements(), outBufferLength,
128 0 : aOutputChannels, channel, offsetSamples);
129 0 : PodCopy(ptr, reinterpret_cast<const AudioDataValue*>(channelData[channel]),
130 0 : frames);
131 : }
132 0 : MOZ_ASSERT(channelData.Length() == aOutputChannels);
133 0 : } else if (channelData.Length() > aOutputChannels) {
134 : // Down mix.
135 0 : AutoTArray<AudioDataValue*, GUESS_AUDIO_CHANNELS> outChannelPtrs;
136 0 : outChannelPtrs.SetLength(aOutputChannels);
137 0 : uint32_t offsetSamples = 0;
138 0 : for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
139 0 : outChannelPtrs[channel] =
140 0 : PointerForOffsetInChannel(buf.Elements(), outBufferLength,
141 : aOutputChannels, channel, offsetSamples);
142 : }
143 0 : AudioChannelsDownMix(channelData, outChannelPtrs.Elements(),
144 0 : aOutputChannels, frames);
145 : } else {
146 : // The channel count is already what we want, just copy it over.
147 0 : for (uint32_t channel = 0; channel < aOutputChannels; channel++) {
148 : AudioDataValue* ptr =
149 0 : PointerForOffsetInChannel(buf.Elements(), outBufferLength,
150 0 : aOutputChannels, channel, offsetSamples);
151 0 : PodCopy(ptr, reinterpret_cast<const AudioDataValue*>(channelData[channel]),
152 0 : frames);
153 : }
154 : }
155 : }
156 0 : offsetSamples += frames;
157 : }
158 :
159 0 : if (offsetSamples) {
160 0 : MOZ_ASSERT(offsetSamples == outBufferLength / aOutputChannels,
161 : "We forgot to write some samples?");
162 0 : aMixer.Mix(buf.Elements(), aOutputChannels, offsetSamples, aSampleRate);
163 : }
164 : }
165 :
166 : void
167 0 : AudioSegment::WriteTo(uint64_t aID, AudioMixer& aMixer, uint32_t aOutputChannels, uint32_t aSampleRate)
168 : {
169 0 : AutoTArray<AudioDataValue,SilentChannel::AUDIO_PROCESSING_FRAMES*GUESS_AUDIO_CHANNELS> buf;
170 : // Offset in the buffer that will be written to the mixer, in samples.
171 0 : uint32_t offset = 0;
172 :
173 0 : if (GetDuration() <= 0) {
174 0 : MOZ_ASSERT(GetDuration() == 0);
175 0 : return;
176 : }
177 :
178 0 : uint32_t outBufferLength = GetDuration() * aOutputChannels;
179 0 : buf.SetLength(outBufferLength);
180 :
181 :
182 0 : for (ChunkIterator ci(*this); !ci.IsEnded(); ci.Next()) {
183 0 : AudioChunk& c = *ci;
184 :
185 0 : switch (c.mBufferFormat) {
186 : case AUDIO_FORMAT_S16:
187 0 : WriteChunk<int16_t>(c, aOutputChannels, buf.Elements() + offset);
188 0 : break;
189 : case AUDIO_FORMAT_FLOAT32:
190 0 : WriteChunk<float>(c, aOutputChannels, buf.Elements() + offset);
191 0 : break;
192 : case AUDIO_FORMAT_SILENCE:
193 : // The mixer is expecting interleaved data, so this is ok.
194 0 : PodZero(buf.Elements() + offset, c.mDuration * aOutputChannels);
195 0 : break;
196 : default:
197 0 : MOZ_ASSERT(false, "Not handled");
198 : }
199 :
200 0 : offset += c.mDuration * aOutputChannels;
201 :
202 0 : if (!c.mTimeStamp.IsNull()) {
203 0 : TimeStamp now = TimeStamp::Now();
204 : // would be more efficient to c.mTimeStamp to ms on create time then pass here
205 0 : LogTime(AsyncLatencyLogger::AudioMediaStreamTrack, aID,
206 0 : (now - c.mTimeStamp).ToMilliseconds(), c.mTimeStamp);
207 : }
208 : }
209 :
210 0 : if (offset) {
211 0 : aMixer.Mix(buf.Elements(), aOutputChannels, offset / aOutputChannels, aSampleRate);
212 : }
213 : }
214 :
215 : } // namespace mozilla
|