add blend mode tests
[swfdec.git] / swfdec / swfdec_audio_event.c
blob2f835e9749f1216aeba972538e301b2b4c026401
1 /* Swfdec
2 * Copyright (C) 2003-2006 David Schleef <ds@schleef.org>
3 * 2005-2006 Eric Anholt <eric@anholt.net>
4 * 2006-2008 Benjamin Otte <otte@gnome.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library 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 GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301 USA
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
26 #include <string.h>
27 #include "swfdec_audio_event.h"
28 #include "swfdec_debug.h"
29 #include "swfdec_player_internal.h"
32 G_DEFINE_TYPE (SwfdecAudioEvent, swfdec_audio_event, SWFDEC_TYPE_AUDIO)
35 static gsize
36 swfdec_audio_event_iterate (SwfdecAudio *audio, gsize remove)
38 SwfdecAudioEvent *event = SWFDEC_AUDIO_EVENT (audio);
40 if (event->n_samples == 0)
41 return 0;
43 event->offset += remove;
44 event->loop += event->offset / event->n_samples;
45 event->offset %= event->n_samples;
47 if (event->loop < event->n_loops)
48 return event->n_samples * (event->n_loops - event->loop) - event->offset;
49 else
50 return 0;
53 static guint16
54 swfdec_audio_event_get_envelop_volume (SwfdecAudioEvent *event, guint pos,
55 guint offset, guint channel)
57 double distance;
59 g_return_val_if_fail (SWFDEC_IS_AUDIO_EVENT (event), 32768);
60 g_return_val_if_fail (pos <= event->n_envelopes, 32768);
61 g_return_val_if_fail (channel == 0 || channel == 1, 32768);
63 if (event->n_envelopes == 0)
64 return 32768;
66 if (pos == 0)
67 return event->envelope[pos].volume[channel];
69 if (pos == event->n_envelopes)
70 return event->envelope[pos - 1].volume[channel];
72 distance = event->envelope[pos].offset - event->envelope[pos - 1].offset;
73 g_return_val_if_fail (offset >= event->envelope[pos - 1].offset, 1);
74 offset -= event->envelope[pos - 1].offset;
75 g_return_val_if_fail (offset < distance, 1);
77 return event->envelope[pos - 1].volume[channel] * (1 - offset / distance) +
78 event->envelope[pos].volume[channel] * (offset / distance);
81 static gsize
82 swfdec_audio_event_render (SwfdecAudio *audio, gint16* dest, gsize start,
83 gsize n_samples)
85 SwfdecAudioEvent *event = SWFDEC_AUDIO_EVENT (audio);
86 gsize offset = event->offset + start;
87 gsize loop, samples, global_offset, pos, i, rendered;
88 gint16 *dest_end;
90 if (event->n_samples == 0)
91 return 0;
94 guint loop_length = (event->stop_sample != 0 ? event->stop_sample :
95 event->n_samples) - event->start_sample;
97 global_offset = 2 * (event->loop * loop_length +
98 event->offset - event->start_sample);
101 dest_end = dest;
102 loop = event->loop + offset / event->n_samples;
103 offset %= event->n_samples;
104 for (rendered = 0; loop < event->n_loops && rendered < n_samples; loop++) {
105 samples = MIN (n_samples - rendered, event->n_samples - offset);
106 swfdec_sound_buffer_render (dest_end, event->decoded, offset, samples);
107 rendered += samples;
108 dest_end += samples * 2;
109 offset = 0;
112 if (event->n_envelopes == 0)
113 return rendered;
115 pos = 0;
116 for (i = 0; i < (guint) (dest_end - dest); i++) {
117 while (pos < event->n_envelopes &&
118 event->envelope[pos].offset <= global_offset + (i / 2))
119 pos++;
120 dest[i] *= swfdec_audio_event_get_envelop_volume (event, pos,
121 global_offset + (i / 2), i % 2) / 32768.0;
123 return rendered;
126 static void
127 swfdec_audio_event_dispose (GObject *object)
129 SwfdecAudioEvent *audio = SWFDEC_AUDIO_EVENT (object);
131 g_free (audio->envelope);
132 audio->envelope = NULL;
133 audio->n_envelopes = 0;
134 if (audio->decoded) {
135 swfdec_buffer_unref (audio->decoded);
136 audio->decoded = NULL;
139 G_OBJECT_CLASS (swfdec_audio_event_parent_class)->dispose (object);
142 static void
143 swfdec_audio_event_class_init (SwfdecAudioEventClass *klass)
145 GObjectClass *object_class = G_OBJECT_CLASS (klass);
146 SwfdecAudioClass *audio_class = SWFDEC_AUDIO_CLASS (klass);
148 object_class->dispose = swfdec_audio_event_dispose;
150 audio_class->iterate = swfdec_audio_event_iterate;
151 audio_class->render = swfdec_audio_event_render;
154 static void
155 swfdec_audio_event_init (SwfdecAudioEvent *audio_event)
159 static void
160 swfdec_audio_event_decode (SwfdecAudioEvent *event)
162 event->decoded = swfdec_sound_get_decoded (event->sound);
163 if (event->decoded == NULL) {
164 SWFDEC_INFO ("Could not decode audio.");
165 event->n_samples = 0;
166 return;
167 } else {
168 swfdec_buffer_ref (event->decoded);
171 if (event->start_sample) {
172 guint skip;
173 skip = 4 * event->start_sample;
174 if (skip >= event->decoded->length) {
175 SWFDEC_WARNING ("start sample %"G_GSIZE_FORMAT" > total number of samples %"G_GSIZE_FORMAT,
176 event->start_sample, event->decoded->length / 4);
177 swfdec_buffer_unref (event->decoded);
178 event->decoded = swfdec_buffer_new (0);
179 } else {
180 SwfdecBuffer *sub = swfdec_buffer_new_subbuffer (event->decoded,
181 skip, event->decoded->length - skip);
182 swfdec_buffer_unref (event->decoded);
183 event->decoded = sub;
186 if (event->stop_sample) {
187 guint keep;
188 keep = 4 * (event->stop_sample - event->start_sample);
189 if (keep > event->decoded->length) {
190 SWFDEC_WARNING ("stop sample %"G_GSIZE_FORMAT" outside of decoded number of samples %"G_GSIZE_FORMAT,
191 event->stop_sample, event->decoded->length / 4 +
192 event->start_sample);
193 } else if (keep < event->decoded->length) {
194 SwfdecBuffer *sub = swfdec_buffer_new_subbuffer (event->decoded,
195 0, keep);
196 swfdec_buffer_unref (event->decoded);
197 event->decoded = sub;
200 event->n_samples = event->decoded->length / 4;
201 SWFDEC_LOG ("total 44100Hz samples: %"G_GSIZE_FORMAT, event->n_samples);
204 static SwfdecAudioEvent *
205 swfdec_audio_event_create (SwfdecSound *sound, guint offset, guint end_offset, guint n_loops)
207 SwfdecAudioEvent *event;
209 event = g_object_new (SWFDEC_TYPE_AUDIO_EVENT, NULL);
210 event->sound = sound;
211 event->start_sample = offset;
212 event->n_loops = n_loops;
213 event->stop_sample = end_offset;
214 swfdec_audio_event_decode (event);
215 event->offset = 0;
217 return event;
221 * swfdec_audio_event_new:
222 * @player: the #SwfdecPlayer to play the sound in
223 * @sound: the sound to be played
224 * @offset: offset into sound in samples at which to start playing
225 * @n_loops: number of times the sound should be played
227 * Starts playing back a sound from the given offset and loops the sound
228 * @n_loops times.
230 * Returns: a new #SwfdecAudio
232 SwfdecAudio *
233 swfdec_audio_event_new (SwfdecPlayer *player, SwfdecSound *sound, guint offset,
234 guint n_loops)
236 SwfdecAudioEvent *event;
238 g_return_val_if_fail (player == NULL || SWFDEC_IS_PLAYER (player), NULL);
239 g_return_val_if_fail (SWFDEC_IS_SOUND (sound), NULL);
241 event = swfdec_audio_event_create (sound, offset, 0, n_loops);
242 swfdec_audio_add (SWFDEC_AUDIO (event), player);
244 return SWFDEC_AUDIO (event);
247 static SwfdecAudio *
248 swfdec_audio_event_get (SwfdecPlayer *player, SwfdecSound *sound)
250 GList *walk;
252 if (player == NULL)
253 return NULL;
255 for (walk = player->priv->audio; walk; walk = walk->next) {
256 SwfdecAudio *audio = walk->data;
257 if (!SWFDEC_IS_AUDIO_EVENT (audio))
258 continue;
259 if (SWFDEC_AUDIO_EVENT (audio)->sound == sound) {
260 return audio;
263 return NULL;
267 * swfdec_audio_event_new_from_chunk:
268 * @player: a #SwfdecPlayer or NULL
269 * @event: a sound event to start playing back
271 * Starts playback of the given sound event (or, when @player is NULL, creates
272 * an element for playing back the given sound).
274 * Returns: the sound effect or NULL if no new sound was created.
276 SwfdecAudio *
277 swfdec_audio_event_new_from_chunk (SwfdecPlayer *player, SwfdecSoundChunk *chunk)
279 SwfdecAudioEvent *event;
281 g_return_val_if_fail (player == NULL || SWFDEC_IS_PLAYER (player), NULL);
282 g_return_val_if_fail (chunk != NULL, NULL);
284 if (chunk->stop) {
285 SwfdecAudio *audio = swfdec_audio_event_get (player, chunk->sound);
286 if (audio) {
287 SWFDEC_LOG ("stopping sound %d", SWFDEC_CHARACTER (chunk->sound)->id);
288 swfdec_audio_remove (audio);
290 return NULL;
292 SWFDEC_LOG ("adding sound %d to playing sounds", SWFDEC_CHARACTER (chunk->sound)->id);
293 if (chunk->no_restart &&
294 (event = (SwfdecAudioEvent *) swfdec_audio_event_get (player, chunk->sound))) {
295 SWFDEC_DEBUG ("sound %d is already playing, reusing it",
296 SWFDEC_CHARACTER (event->sound)->id);
297 g_object_ref (event);
298 return SWFDEC_AUDIO (event);
300 event = swfdec_audio_event_create (chunk->sound, chunk->start_sample,
301 chunk->stop_sample, chunk->loop_count);
302 event->n_envelopes = chunk->n_envelopes;
303 if (event->n_envelopes)
304 event->envelope = g_memdup (chunk->envelope, sizeof (SwfdecSoundEnvelope) * event->n_envelopes);
305 SWFDEC_DEBUG ("playing sound %d from offset %"G_GSIZE_FORMAT" now",
306 SWFDEC_CHARACTER (event->sound)->id, event->start_sample);
307 swfdec_audio_add (SWFDEC_AUDIO (event), player);
309 return SWFDEC_AUDIO (event);