1 /*
2     Ming, an SWF output library
3     Copyright (C) 2002  Opaque Industries - http://www.opaque.net/
4 
5     This library is free software; you can redistribute it and/or
6     modify it under the terms of the GNU Lesser General Public
7     License as published by the Free Software Foundation; either
8     version 2.1 of the License, or (at your option) any later version.
9 
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13     Lesser General Public License for more details.
14 
15     You should have received a copy of the GNU Lesser General Public
16     License along with this library; if not, write to the Free Software
17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 */
19 
20 /* $Id$ */
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 
25 #include "outputblock.h"
26 #include "sound.h"
27 #include "soundstream.h"
28 #include "character.h"
29 #include "method.h"
30 #include "input.h"
31 #include "libming.h"
32 #include "mp3.h"
33 
34 struct SWFSound_s
35 {
36 	struct SWFCharacter_s character;
37 
38 	byte flags;
39 	int seekSamples;
40 	byte freeInput;
41 
42 	SWFInput input;
43 	struct SWFSoundStream_s *soundStream;
44 };
45 
46 
47 void
48 writeSWFSoundWithSoundStreamToMethod(SWFSoundStream stream,
49 	SWFByteOutputMethod method, void *data);
50 
51 static int
soundDataSize(SWFSound sound)52 soundDataSize(SWFSound sound)
53 {
54 	if ((sound->flags&SWF_SOUND_COMPRESSION) == SWF_SOUND_NOT_COMPRESSED ||
55 		 (sound->flags&SWF_SOUND_COMPRESSION) == SWF_SOUND_NOT_COMPRESSED_LE)
56 	{
57 		int sampleCount = SWFInput_length(sound->input);
58 
59 		if ((sound->flags & SWF_SOUND_BITS) == SWF_SOUND_16BITS)
60 			sampleCount /= 2;
61 
62 		if ((sound->flags & SWF_SOUND_CHANNELS) == SWF_SOUND_STEREO)
63 			sampleCount /= 2;
64 
65 		return sampleCount;
66 	}
67 	else if ((sound->flags&SWF_SOUND_COMPRESSION) == SWF_SOUND_ADPCM_COMPRESSED)
68 	{
69 		int filesize, channels, nbits;
70 		int bitsize, blocksize, n, res, m;
71 
72 		SWF_assert((sound->flags & SWF_SOUND_BITS) == SWF_SOUND_16BITS);
73 
74 		filesize = SWFInput_length(sound->input);
75 
76 		if ((sound->flags&SWF_SOUND_CHANNELS) == SWF_SOUND_MONO)
77 			channels = 1;
78 		else if ((sound->flags & SWF_SOUND_CHANNELS) == SWF_SOUND_STEREO)
79 			channels = 2;
80 		else
81 			channels = 1;	 /* ? */
82 
83 		nbits = 4;	/* XXX - testing.. */
84 
85 		/*
86 		 * Estimation of the sample count in ADPCM data from file size of the data.
87 		 * This is an approximate calculation.
88 		 */
89 		bitsize = 8 * filesize - (2 + (8 - 1));
90 		/* 2: header, (8 - 1): possible padding */
91 		blocksize = ((16 + 6) + nbits * 4095) * channels;
92 		n = bitsize / blocksize;
93 		res = bitsize % blocksize;
94 		m = (res - (16 + 6) * channels) / (nbits * channels);
95 		return 4096 * n + m;
96 	}
97 	else if ((sound->flags&SWF_SOUND_COMPRESSION) == SWF_SOUND_MP3_COMPRESSED)
98 	{
99 		int pos = SWFInput_tell(sound->input);
100 		int samples = -1;
101 		getMP3Samples(sound->input, sound->flags, &samples);
102 		SWFInput_seek(sound->input, pos, SEEK_SET);
103 		return samples;
104 	}
105 	else
106 	{
107 		SWF_warn("SWFSound: can't determine sampleCount\n");
108 		return 0;
109 	}
110 }
111 
112 
113 void
writeSWFSoundToStream(SWFBlock block,SWFByteOutputMethod method,void * data)114 writeSWFSoundToStream(SWFBlock block, SWFByteOutputMethod method, void *data)
115 {
116 	int l, i;
117 	SWFSound sound = (SWFSound)block;
118 
119 	methodWriteUInt16(CHARACTERID(sound), method, data);
120 	method(sound->flags, data);
121 
122 	l = SWFInput_length(sound->input);
123 
124 	methodWriteUInt32(soundDataSize(sound), method, data);
125 
126 	if ( (sound->flags & SWF_SOUND_COMPRESSION) == SWF_SOUND_MP3_COMPRESSED )
127 		methodWriteUInt16(sound->seekSamples, method, data);
128 
129 	/* write samples */
130 	for ( i=0; i<l; ++i )
131 		method((unsigned char)SWFInput_getChar(sound->input), data);
132 }
133 
134 
135 int
completeDefineSWFSoundBlock(SWFBlock block)136 completeDefineSWFSoundBlock(SWFBlock block)
137 {
138 	SWFSound sound = (SWFSound)block;
139 
140 	if ((sound->flags&SWF_SOUND_COMPRESSION) == SWF_SOUND_MP3_COMPRESSED)
141 		return 7 + 2 + SWFInput_length(sound->input);
142 	else
143 		return 7 + SWFInput_length(sound->input);
144 }
145 
146 
147 void
writeSWFSoundWithSoundStreamToStream(SWFBlock block,SWFByteOutputMethod method,void * data)148 writeSWFSoundWithSoundStreamToStream(SWFBlock block, SWFByteOutputMethod method, void *data)
149 {
150 	SWFSound sound = (SWFSound)block;
151 
152 	methodWriteUInt16(CHARACTERID(sound), method, data);
153 	method(sound->flags, data);
154 
155 	writeSWFSoundWithSoundStreamToMethod(sound->soundStream, method, data);
156 }
157 
158 
159 int
completeDefineSWFSoundWithSoundStreamBlock(SWFBlock block)160 completeDefineSWFSoundWithSoundStreamBlock(SWFBlock block)
161 {
162 	SWFSound sound = (SWFSound)block;
163 	int len = SWFSoundStream_getLength(sound->soundStream, 0) + 9;
164 	SWFSoundStream_rewind(sound->soundStream);
165 	return len;
166 }
167 
168 /*
169  * Set number of samples to seek forward or delay (works with MP3 only)
170  *
171  * If this value is positive, the player seeks this
172  * number of samples into the sound block before the
173  * sound is played.
174  * If this value is negative the player plays this
175  * number of silent samples before playing the sound block
176  */
177 void
SWFSound_setInitialMp3Delay(SWFSound sound,int delaySeek)178 SWFSound_setInitialMp3Delay(SWFSound sound, int delaySeek)
179 {
180 	sound->seekSamples = delaySeek;
181 }
182 
183 
184 void
destroySWFSound(SWFSound sound)185 destroySWFSound(SWFSound sound)
186 {
187 	if (sound->freeInput)
188 		destroySWFInput(sound->input);
189 	destroySWFCharacter((SWFCharacter) sound);
190 }
191 
192 /*
193  * Creates a new EventSound object.
194  *
195  * The sound to be played is contained in a file and specified with flags.
196  *
197  * Flags must contain a sound format, sampling rate, size (in bits) and channels.
198  * If the file contains mp3 data it is not necessary to specify sampling rate,
199  * sound size and channels.
200  *
201  * Possible sound formats are:
202  * - SWF_SOUND_NOT_COMPRESSED
203  * - SWF_SOUND_ADPCM_COMPRESSED
204  * - SWF_SOUND_MP3_COMPRESSED
205  * - SWF_SOUND_NOT_COMPRESSED_LE
206  * - SWF_SOUND_NELLY_COMPRESSED
207  *
208  * Sampling rate must be one of the following values:
209  * - SWF_SOUND_5KHZ
210  * - SWF_SOUND_11KHZ
211  * - SWF_SOUND_22KHZ
212  * - SWF_SOUND_44KHZ
213  *
214  * Sound size is either SWF_SOUND_8BITS or SWF_SOUND_16BITS
215  * Channels are either SWF_SOUND_MONO or SWF_SOUND_STEREO
216  */
217 SWFSound
newSWFSound(FILE * f,byte flags)218 newSWFSound(FILE *f, byte flags)
219 {
220 	SWFSound s = newSWFSound_fromInput(newSWFInput_file(f), flags);
221 	s->freeInput = TRUE;
222 	return s;
223 }
224 
225 /* added by David McNab <david@rebirthing.co.nz> */
226 /* required so that python can pass in file descriptors instead of FILE* streams */
227 SWFSound
newSWFSoundFromFileno(int fd,byte flags)228 newSWFSoundFromFileno(int fd, byte flags)
229 {
230 	FILE *fp = fdopen(fd, "r");
231 	return newSWFSound(fp, flags);
232 }
233 
234 
235 SWFSound
newSWFSound_fromInput(SWFInput input,byte flags)236 newSWFSound_fromInput(SWFInput input, byte flags)
237 {
238 	SWFSound sound = (SWFSound)malloc(sizeof(struct SWFSound_s));
239 	SWFBlock block = (SWFBlock)sound;
240 
241 	SWFCharacterInit((SWFCharacter)sound);
242 
243 	CHARACTERID(sound) = ++SWF_gNumCharacters;
244 
245 	block->type = SWF_DEFINESOUND;
246 
247 	block->writeBlock = writeSWFSoundToStream;
248 	block->complete = completeDefineSWFSoundBlock;
249 	block->dtor = (destroySWFBlockMethod) destroySWFSound;
250 
251 	sound->input = input;
252 	sound->flags = flags;
253 	if((sound->flags&SWF_SOUND_COMPRESSION) == SWF_SOUND_MP3_COMPRESSED)
254 	{
255 		if(getMP3Flags(input, &sound->flags) < 0)
256 		{
257 			free(sound);
258 			return NULL;
259 		}
260 	}
261 	sound->soundStream = 0;
262 	sound->seekSamples = SWFSOUND_INITIAL_DELAY;
263 	sound->freeInput = FALSE;
264 	return sound;
265 }
266 
267 /*
268  * Creates an Event Sound object from a given SoundStream object
269  *
270  * see also newSWFSoundStream()
271  */
272 SWFSound
newSWFSound_fromSoundStream(SWFSoundStream stream)273 newSWFSound_fromSoundStream(SWFSoundStream stream)
274 {
275 	SWFSound sound = (SWFSound)malloc(sizeof(struct SWFSound_s));
276 	SWFBlock block = (SWFBlock)sound;
277 
278 	SWFCharacterInit((SWFCharacter)sound);
279 
280 	CHARACTERID(sound) = ++SWF_gNumCharacters;
281 
282 	block->type = SWF_DEFINESOUND;
283 
284 	block->writeBlock = writeSWFSoundWithSoundStreamToStream;
285 	block->complete = completeDefineSWFSoundWithSoundStreamBlock;
286 	block->dtor = (destroySWFBlockMethod) destroySWFSound;
287 
288 	sound->freeInput = FALSE;
289 	sound->input = 0;
290 	sound->flags = SWFSoundStream_getFlags(stream);
291 	sound->soundStream = stream;
292 
293 	return sound;
294 }
295 
296 /*
297  * Local variables:
298  * tab-width: 2
299  * c-basic-offset: 2
300  * End:
301  */
302