Update with current status
[gnash.git] / libsound / EmbedSoundInst.cpp
blob2661794214dcd1bf37c944d0500fab9c1d60480a
1 // EmbedSoundInst.cpp - instance of an embedded sound, for gnash
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
5 //
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.
15 //
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"
22 #include <cmath>
23 #include <vector>
25 #include "SoundInfo.h" // for use
26 #include "MediaHandler.h" // for use
27 #include "AudioDecoder.h" // for use
28 #include "SoundEnvelope.h" // for use
29 #include "log.h"
30 #include "SoundUtils.h"
32 // Debug sound decoding
33 //#define GNASH_DEBUG_SOUNDS_DECODING
35 // Debug sound mixing
36 //#define GNASH_DEBUG_MIXING
38 namespace gnash {
39 namespace sound {
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),
47 decodingPosition(0),
48 loopCount(loopCount),
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()
54 : outPoint * 4),
55 envelopes(env),
56 current_env(0),
57 _soundDef(soundData)
61 bool
62 EmbedSoundInst::reachedCustomEnd() const
64 if (_outPoint == std::numeric_limits<unsigned long>::max()) return false;
65 if (playbackPosition() >= _outPoint) return true;
66 return false;
69 bool
70 EmbedSoundInst::moreData()
72 if (decodingCompleted() || reachedCustomEnd()) {
74 if (loopCount) {
75 // negative count is documented to mean loop forever.
76 if (loopCount > 0) --loopCount;
77 restart();
78 return true;
80 // Nothing more to do.
81 return false;
84 // It's not clear if this happens for Embedded sounds, but it
85 // would permit incremental decoding.
86 decodeNextBlock();
87 return true;
90 void
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);
111 #endif
113 assert(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);
132 #endif
134 // Adjust volume
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);
148 #endif
151 // decodedData ownership transferred here
152 appendDecodedData(decodedData, decodedDataSize);
155 void
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) {
165 return;
168 // Not yet time to use the current envelope
169 if (env[current_env].m_mark44 >= firstSampleOffset+nSamples) {
170 return;
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;
182 else {
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,
200 // switch to it !
201 if ((firstSampleOffset+nSamples-i) >= next_env_pos)
203 if (numEnvs <= ++current_env) {
204 // no more envelopes to apply
205 return;
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;
214 else {
215 next_env_pos = env[current_env + 1].m_mark44;
221 bool
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
236 } // namespace gnash