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/. */
6 #include "AudioSegment.h"
7 #include "AudioMixer.h"
8 #include "AudioChannelFormat.h"
9 #include <speex/speex_resampler.h>
14 SilentChannel::gZeroChannel
[MAX_AUDIO_SAMPLE_SIZE
*
15 SilentChannel::AUDIO_PROCESSING_FRAMES
] = {0};
18 const float* SilentChannel::ZeroChannel
<float>() {
19 return reinterpret_cast<const float*>(SilentChannel::gZeroChannel
);
23 const int16_t* SilentChannel::ZeroChannel
<int16_t>() {
24 return reinterpret_cast<const int16_t*>(SilentChannel::gZeroChannel
);
27 void AudioSegment::ApplyVolume(float aVolume
) {
28 for (ChunkIterator
ci(*this); !ci
.IsEnded(); ci
.Next()) {
29 ci
->mVolume
*= aVolume
;
33 void AudioSegment::ResampleChunks(nsAutoRef
<SpeexResamplerState
>& aResampler
,
34 uint32_t* aResamplerChannelCount
,
35 uint32_t aInRate
, uint32_t aOutRate
) {
36 if (mChunks
.IsEmpty()) {
40 AudioSampleFormat format
= AUDIO_FORMAT_SILENCE
;
41 for (ChunkIterator
ci(*this); !ci
.IsEnded(); ci
.Next()) {
42 if (ci
->mBufferFormat
!= AUDIO_FORMAT_SILENCE
) {
43 format
= ci
->mBufferFormat
;
48 // If the format is silence at this point, all the chunks are silent. The
49 // actual function we use does not matter, it's just a matter of changing
50 // the chunks duration.
51 case AUDIO_FORMAT_SILENCE
:
52 case AUDIO_FORMAT_FLOAT32
:
53 Resample
<float>(aResampler
, aResamplerChannelCount
, aInRate
, aOutRate
);
55 case AUDIO_FORMAT_S16
:
56 Resample
<int16_t>(aResampler
, aResamplerChannelCount
, aInRate
, aOutRate
);
64 // This helps to to safely get a pointer to the position we want to start
65 // writing a planar audio buffer, depending on the channel and the offset in the
67 static AudioDataValue
* PointerForOffsetInChannel(AudioDataValue
* aData
,
68 size_t aLengthSamples
,
69 uint32_t aChannelCount
,
71 uint32_t aOffsetSamples
) {
72 size_t samplesPerChannel
= aLengthSamples
/ aChannelCount
;
73 size_t beginningOfChannel
= samplesPerChannel
* aChannel
;
74 MOZ_ASSERT(aChannel
* samplesPerChannel
+ aOffsetSamples
< aLengthSamples
,
75 "Offset request out of bounds.");
76 return aData
+ beginningOfChannel
+ aOffsetSamples
;
79 void AudioSegment::Mix(AudioMixer
& aMixer
, uint32_t aOutputChannels
,
80 uint32_t aSampleRate
) {
81 AutoTArray
<AudioDataValue
,
82 SilentChannel::AUDIO_PROCESSING_FRAMES
* GUESS_AUDIO_CHANNELS
>
84 AutoTArray
<const AudioDataValue
*, GUESS_AUDIO_CHANNELS
> channelData
;
85 uint32_t offsetSamples
= 0;
86 uint32_t duration
= GetDuration();
89 MOZ_ASSERT(duration
== 0);
93 uint32_t outBufferLength
= duration
* aOutputChannels
;
94 buf
.SetLength(outBufferLength
);
96 for (ChunkIterator
ci(*this); !ci
.IsEnded(); ci
.Next()) {
98 uint32_t frames
= c
.mDuration
;
100 // If the chunk is silent, simply write the right number of silence in the
102 if (c
.mBufferFormat
== AUDIO_FORMAT_SILENCE
) {
103 for (uint32_t channel
= 0; channel
< aOutputChannels
; channel
++) {
104 AudioDataValue
* ptr
=
105 PointerForOffsetInChannel(buf
.Elements(), outBufferLength
,
106 aOutputChannels
, channel
, offsetSamples
);
107 PodZero(ptr
, frames
);
110 // Othewise, we need to upmix or downmix appropriately, depending on the
111 // desired input and output channels.
112 channelData
.SetLength(c
.mChannelData
.Length());
113 for (uint32_t i
= 0; i
< channelData
.Length(); ++i
) {
114 channelData
[i
] = static_cast<const AudioDataValue
*>(c
.mChannelData
[i
]);
116 if (channelData
.Length() < aOutputChannels
) {
118 AudioChannelsUpMix(&channelData
, aOutputChannels
,
119 SilentChannel::ZeroChannel
<AudioDataValue
>());
120 for (uint32_t channel
= 0; channel
< aOutputChannels
; channel
++) {
121 AudioDataValue
* ptr
= PointerForOffsetInChannel(
122 buf
.Elements(), outBufferLength
, aOutputChannels
, channel
,
125 reinterpret_cast<const AudioDataValue
*>(channelData
[channel
]),
128 MOZ_ASSERT(channelData
.Length() == aOutputChannels
);
129 } else if (channelData
.Length() > aOutputChannels
) {
131 AutoTArray
<AudioDataValue
*, GUESS_AUDIO_CHANNELS
> outChannelPtrs
;
132 outChannelPtrs
.SetLength(aOutputChannels
);
133 uint32_t offsetSamples
= 0;
134 for (uint32_t channel
= 0; channel
< aOutputChannels
; channel
++) {
135 outChannelPtrs
[channel
] = PointerForOffsetInChannel(
136 buf
.Elements(), outBufferLength
, aOutputChannels
, channel
,
139 AudioChannelsDownMix(channelData
, outChannelPtrs
.Elements(),
140 aOutputChannels
, frames
);
142 // The channel count is already what we want, just copy it over.
143 for (uint32_t channel
= 0; channel
< aOutputChannels
; channel
++) {
144 AudioDataValue
* ptr
= PointerForOffsetInChannel(
145 buf
.Elements(), outBufferLength
, aOutputChannels
, channel
,
148 reinterpret_cast<const AudioDataValue
*>(channelData
[channel
]),
153 offsetSamples
+= frames
;
157 MOZ_ASSERT(offsetSamples
== outBufferLength
/ aOutputChannels
,
158 "We forgot to write some samples?");
159 aMixer
.Mix(buf
.Elements(), aOutputChannels
, offsetSamples
, aSampleRate
);
163 void AudioSegment::WriteTo(AudioMixer
& aMixer
, uint32_t aOutputChannels
,
164 uint32_t aSampleRate
) {
165 AutoTArray
<AudioDataValue
,
166 SilentChannel::AUDIO_PROCESSING_FRAMES
* GUESS_AUDIO_CHANNELS
>
168 // Offset in the buffer that will be written to the mixer, in samples.
171 if (GetDuration() <= 0) {
172 MOZ_ASSERT(GetDuration() == 0);
176 uint32_t outBufferLength
= GetDuration() * aOutputChannels
;
177 buf
.SetLength(outBufferLength
);
179 for (ChunkIterator
ci(*this); !ci
.IsEnded(); ci
.Next()) {
182 switch (c
.mBufferFormat
) {
183 case AUDIO_FORMAT_S16
:
184 WriteChunk
<int16_t>(c
, aOutputChannels
, buf
.Elements() + offset
);
186 case AUDIO_FORMAT_FLOAT32
:
187 WriteChunk
<float>(c
, aOutputChannels
, buf
.Elements() + offset
);
189 case AUDIO_FORMAT_SILENCE
:
190 // The mixer is expecting interleaved data, so this is ok.
191 PodZero(buf
.Elements() + offset
, c
.mDuration
* aOutputChannels
);
194 MOZ_ASSERT(false, "Not handled");
197 offset
+= c
.mDuration
* aOutputChannels
;
201 aMixer
.Mix(buf
.Elements(), aOutputChannels
, offset
/ aOutputChannels
,
206 } // namespace mozilla