1 // EmbedSoundInst.cpp - instance of an embedded sound, for gnash
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "EmbedSoundInst.h"
25 #include "SoundInfo.h" // for use
26 #include "MediaHandler.h" // for use
27 #include "AudioDecoder.h" // for use
28 #include "SoundEnvelope.h" // for use
30 #include "SoundUtils.h"
32 // Debug sound decoding
33 //#define GNASH_DEBUG_SOUNDS_DECODING
36 //#define GNASH_DEBUG_MIXING
41 EmbedSoundInst::EmbedSoundInst(EmbedSound
& soundData
,
42 media::MediaHandler
& mediaHandler
,
43 unsigned int inPoint
, unsigned int outPoint
,
44 const SoundEnvelopes
* env
, int loopCount
)
46 LiveSound(mediaHandler
, soundData
.soundinfo
, inPoint
),
49 // parameters are in stereo samples (44100 per second)
50 // we double to take 2 channels into account
51 // and double again to use bytes
52 _outPoint( outPoint
== std::numeric_limits
<unsigned int>::max() ?
53 std::numeric_limits
<unsigned long>::max()
62 EmbedSoundInst::reachedCustomEnd() const
64 if (_outPoint
== std::numeric_limits
<unsigned long>::max()) return false;
65 if (playbackPosition() >= _outPoint
) return true;
70 EmbedSoundInst::moreData()
72 if (decodingCompleted() || reachedCustomEnd()) {
75 // negative count is documented to mean loop forever.
76 if (loopCount
> 0) --loopCount
;
80 // Nothing more to do.
84 // It's not clear if this happens for Embedded sounds, but it
85 // would permit incremental decoding.
91 EmbedSoundInst::decodeNextBlock()
93 assert(!decodingCompleted());
95 // this value is arbitrary, things would also work
96 // with a smaller value, but 2^16 seems fast enough
97 // to decode not to bother further streamlining it
98 // See https://savannah.gnu.org/bugs/?25456 for a testcase
99 // showing the benefit of chunked decoding.
101 // NOTE: it is reccommended that chunkSize is a multiple
102 // of 4-byte (16-bit stereo), see
103 // https://savannah.gnu.org/patch/?8736
104 const std::uint32_t chunkSize
= 65536;
106 std::uint32_t inputSize
= _soundDef
.size() - decodingPosition
;
107 if ( inputSize
> chunkSize
) inputSize
= chunkSize
;
109 #ifdef GNASH_DEBUG_SOUNDS_DECODING
110 log_debug(" decoding %d bytes", inputSize
);
114 const std::uint8_t* input
= _soundDef
.data(decodingPosition
);
116 std::uint32_t consumed
= 0;
117 std::uint32_t decodedDataSize
= 0;
118 std::uint8_t* decodedData
= decoder().decode(input
, inputSize
,
119 decodedDataSize
, consumed
);
121 decodingPosition
+= consumed
;
123 assert(!(decodedDataSize
%2));
125 // @todo I hope there are no alignment issues in this cast from int8_t* to int16_t* !
126 std::int16_t* samples
= reinterpret_cast<std::int16_t*>(decodedData
);
127 unsigned int nSamples
= decodedDataSize
/2;
129 #ifdef GNASH_DEBUG_MIXING
130 log_debug(" applying volume/envelope to %d bytes (%d samples)"
131 " of decoded data", decodedDataSize
, nSamples
);
135 if (_soundDef
.volume
!= 100) {
136 adjustVolume(samples
, samples
+ nSamples
, _soundDef
.volume
/100.0);
139 /// @todo is use of envelopes really mutually exclusive with
140 /// setting the volume ??
141 else if (envelopes
) {
142 unsigned int firstSample
= playbackPosition() / 2;
143 applyEnvelopes(samples
, nSamples
, firstSample
, *envelopes
);
146 #ifdef GNASH_DEBUG_MIXING
147 log_debug(" appending %d bytes to decoded buffer", decodedDataSize
);
151 // decodedData ownership transferred here
152 appendDecodedData(decodedData
, decodedDataSize
);
156 EmbedSoundInst::applyEnvelopes(std::int16_t* samples
, unsigned int nSamples
,
157 unsigned int firstSampleOffset
, const SoundEnvelopes
& env
)
160 // Number of envelopes defined
161 size_t numEnvs
= env
.size();
163 // Nothing to do if we applied all envelopes already
164 if (numEnvs
<= current_env
) {
168 // Not yet time to use the current envelope
169 if (env
[current_env
].m_mark44
>= firstSampleOffset
+nSamples
) {
173 assert(env
[current_env
].m_mark44
< firstSampleOffset
+nSamples
);
175 // Get next envelope position (absolute samples offset)
176 std::uint32_t next_env_pos
= 0;
177 if (current_env
== (env
.size() - 1)) {
178 // If there is no "next envelope" then set the next envelope
179 // start point to be unreachable
180 next_env_pos
= env
[current_env
].m_mark44
+ nSamples
+ 1;
183 next_env_pos
= env
[current_env
+ 1].m_mark44
;
186 // Scan all samples in the block, applying the envelope
187 // which is in effect in each subportion
188 for (unsigned int i
= 0; i
< nSamples
/ 2; i
+= 2) {
190 // @todo cache these left/right floats (in the SoundEnvelope class?)
191 float left
= env
[current_env
].m_level0
/ 32768.0;
192 float right
= env
[current_env
].m_level1
/ 32768.0;
194 samples
[i
] = samples
[i
] * left
; // Left
195 samples
[i
+ 1] = samples
[i
+ 1] * right
; // Right
197 // TODO: continue from here (what is the code below doing ??
199 // if we encounter the offset of next envelope,
201 if ((firstSampleOffset
+nSamples
-i
) >= next_env_pos
)
203 if (numEnvs
<= ++current_env
) {
204 // no more envelopes to apply
208 // Get next envelope position (absolute samples offset)
209 if (current_env
== (env
.size() - 1)) {
210 // If there is no "next envelope" then set the next
211 // envelope start point to be unreachable
212 next_env_pos
= env
[current_env
].m_mark44
+ nSamples
+ 1;
215 next_env_pos
= env
[current_env
+ 1].m_mark44
;
222 EmbedSoundInst::eof() const
224 // it isn't threaded, but just in case, we call decodingCompleted first
225 // and we also check loopCount... (over paranoid?)
226 return ((decodingCompleted() || reachedCustomEnd())
227 && !loopCount
&& !decodedSamplesAhead());
230 EmbedSoundInst::~EmbedSoundInst()
232 _soundDef
.eraseActiveSound(this);
235 } // gnash.sound namespace