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
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
)
36 swfdec_audio_event_iterate (SwfdecAudio
*audio
, gsize remove
)
38 SwfdecAudioEvent
*event
= SWFDEC_AUDIO_EVENT (audio
);
40 if (event
->n_samples
== 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
;
54 swfdec_audio_event_get_envelop_volume (SwfdecAudioEvent
*event
, guint pos
,
55 guint offset
, guint channel
)
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)
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
);
82 swfdec_audio_event_render (SwfdecAudio
*audio
, gint16
* dest
, gsize start
,
85 SwfdecAudioEvent
*event
= SWFDEC_AUDIO_EVENT (audio
);
86 gsize offset
= event
->offset
+ start
;
87 gsize loop
, samples
, global_offset
, pos
, i
, rendered
;
90 if (event
->n_samples
== 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
);
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
);
108 dest_end
+= samples
* 2;
112 if (event
->n_envelopes
== 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))
120 dest
[i
] *= swfdec_audio_event_get_envelop_volume (event
, pos
,
121 global_offset
+ (i
/ 2), i
% 2) / 32768.0;
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
);
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
;
155 swfdec_audio_event_init (SwfdecAudioEvent
*audio_event
)
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;
168 swfdec_buffer_ref (event
->decoded
);
171 if (event
->start_sample
) {
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);
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
) {
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
,
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
);
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
230 * Returns: a new #SwfdecAudio
233 swfdec_audio_event_new (SwfdecPlayer
*player
, SwfdecSound
*sound
, guint offset
,
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
);
248 swfdec_audio_event_get (SwfdecPlayer
*player
, SwfdecSound
*sound
)
255 for (walk
= player
->priv
->audio
; walk
; walk
= walk
->next
) {
256 SwfdecAudio
*audio
= walk
->data
;
257 if (!SWFDEC_IS_AUDIO_EVENT (audio
))
259 if (SWFDEC_AUDIO_EVENT (audio
)->sound
== sound
) {
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.
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
);
285 SwfdecAudio
*audio
= swfdec_audio_event_get (player
, chunk
->sound
);
287 SWFDEC_LOG ("stopping sound %d", SWFDEC_CHARACTER (chunk
->sound
)->id
);
288 swfdec_audio_remove (audio
);
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
);