1 /*
2  * SDL_sound -- An abstract sound format decoding API.
3  * Copyright (C) 2001  Ryan C. Gordon.
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 /*
21  * Speex decoder for SDL_sound.
22  *
23  * This driver handles Speex audio data. Speex is a codec for speech that is
24  *  meant to be transmitted over narrowband network connections. Epic Games
25  *  estimates that their VoIP solution, built on top of Speex, uses around
26  *  500 bytes per second or less to transmit relatively good sounding speech.
27  *
28  * This decoder processes the .spx files that the speexenc program produces.
29  *
30  * Speex isn't meant for general audio compression. Something like Ogg Vorbis
31  *  will give better results in that case.
32  *
33  * Further Speex information can be found at http://www.speex.org/
34  *
35  * This code is based on speexdec.c (see the Speex website).
36  *
37  * Please see the file COPYING in the source's root directory.
38  *
39  *  This file written by Ryan C. Gordon. (icculus@icculus.org)
40  */
41 
42 #if HAVE_CONFIG_H
43 #  include <config.h>
44 #endif
45 
46 #ifdef SOUND_SUPPORTS_SPEEX
47 
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51 #include <assert.h>
52 
53 #include <ogg/ogg.h>
54 #include <speex/speex.h>
55 #include <speex/speex_header.h>
56 
57 #include "SDL_sound.h"
58 
59 #define __SDL_SOUND_INTERNAL__
60 #include "SDL_sound_internal.h"
61 
62 static int SPEEX_init(void);
63 static void SPEEX_quit(void);
64 static int SPEEX_open(Sound_Sample *sample, const char *ext);
65 static void SPEEX_close(Sound_Sample *sample);
66 static Uint32 SPEEX_read(Sound_Sample *sample);
67 static int SPEEX_rewind(Sound_Sample *sample);
68 static int SPEEX_seek(Sound_Sample *sample, Uint32 ms);
69 
70 static const char *extensions_speex[] = { "spx", NULL };
71 const Sound_DecoderFunctions __Sound_DecoderFunctions_SPEEX =
72 {
73     {
74         extensions_speex,
75         "SPEEX speech compression format",
76         "Ryan C. Gordon <icculus@icculus.org>",
77         "http://www.icculus.org/SDL_sound/"
78     },
79 
80     SPEEX_init,       /*   init() method */
81     SPEEX_quit,       /*   quit() method */
82     SPEEX_open,       /*   open() method */
83     SPEEX_close,      /*  close() method */
84     SPEEX_read,       /*   read() method */
85     SPEEX_rewind,     /* rewind() method */
86     SPEEX_seek        /*   seek() method */
87 };
88 
89 #define SPEEX_USE_PERCEPTUAL_ENHANCER 1
90 #define SPEEX_MAGIC  0x5367674F  /* "OggS" in ASCII (littleendian) */
91 #define SPEEX_OGG_BUFSIZE 200
92 
93 /* this is what we store in our internal->decoder_private field... */
94 typedef struct
95 {
96     ogg_sync_state oy;
97     ogg_page og;
98     ogg_packet op;
99     ogg_stream_state os;
100     void *state;
101     SpeexBits bits;
102     int header_count;
103     int frame_size;
104     int nframes;
105     int frames_avail;
106     float *decode_buf;
107     int decode_total;
108     int decode_pos;
109     int have_ogg_packet;
110 } speex_t;
111 
112 
113 static int SPEEX_init(void)
114 {
115     return(1);   /* no-op. */
116 } /* SPEEX_init */
117 
118 
119 static void SPEEX_quit(void)
120 {
121     /* no-op. */
122 } /* SPEEX_quit */
123 
124 
125 static int process_header(speex_t *speex, Sound_Sample *sample)
126 {
127     SpeexMode *mode;
128     SpeexHeader *hptr;
129     SpeexHeader header;
130     int enh_enabled = SPEEX_USE_PERCEPTUAL_ENHANCER;
131     int tmp;
132 
133     hptr = speex_packet_to_header((char*) speex->op.packet, speex->op.bytes);
134     BAIL_IF_MACRO(!hptr, "SPEEX: Cannot read header", 0);
135     memcpy(&header, hptr, sizeof (SpeexHeader)); /* move to stack. */
136     free(hptr);  /* lame that this forces you to malloc... */
137 
138     BAIL_IF_MACRO(header.mode >= SPEEX_NB_MODES, "SPEEX: Unknown mode", 0);
139     BAIL_IF_MACRO(header.mode < 0, "SPEEX: Unknown mode", 0);
140     mode = speex_mode_list[header.mode];
141     BAIL_IF_MACRO(header.speex_version_id > 1, "SPEEX: Unknown version", 0);
142     BAIL_IF_MACRO(mode->bitstream_version < header.mode_bitstream_version,
143                   "SPEEX: Unsupported bitstream version", 0);
144     BAIL_IF_MACRO(mode->bitstream_version > header.mode_bitstream_version,
145                   "SPEEX: Unsupported bitstream version", 0);
146 
147     speex->state = speex_decoder_init(mode);
148     BAIL_IF_MACRO(!speex->state, "SPEEX: Decoder initialization error", 0);
149 
150     speex_decoder_ctl(speex->state, SPEEX_SET_ENH, &enh_enabled);
151     speex_decoder_ctl(speex->state, SPEEX_GET_FRAME_SIZE, &speex->frame_size);
152 
153     speex->decode_buf = (float *) malloc(speex->frame_size * sizeof (float));
154     BAIL_IF_MACRO(!speex->decode_buf, ERR_OUT_OF_MEMORY, 0);
155 
156     speex->nframes = header.frames_per_packet;
157     if (!speex->nframes)
158         speex->nframes = 1;
159 
160     /* !!! FIXME: Write converters to match desired format.
161        !!! FIXME:  We have to convert from Float32 anyhow. */
162     /* !!! FIXME: Is it a performance hit to alter sampling rate?
163        !!! FIXME:  If not, try to match desired rate. */
164     /* !!! FIXME: We force mono output, but speexdec.c has code for stereo.
165        !!! FIXME:  Use that if sample->desired.channels == 2? */
166     tmp = header.rate;
167     speex_decoder_ctl(speex->state, SPEEX_SET_SAMPLING_RATE, &tmp);
168     speex_decoder_ctl(speex->state, SPEEX_GET_SAMPLING_RATE, &tmp);
169     sample->actual.rate = tmp;
170     sample->actual.channels = 1;
171     sample->actual.format = AUDIO_S16SYS;
172 
173     SNDDBG(("SPEEX: %dHz, mono, %svbr, %s mode.\n",
174             (int) sample->actual.rate,
175             header.vbr ? "" : "not ",
176             mode->modeName));
177 
178     /* plus 2: one for this header, one for the comment header. */
179     speex->header_count = header.extra_headers + 2;
180     return(1);
181 } /* process_header */
182 
183 
184 /* !!! FIXME: this code sucks. */
185 static int SPEEX_open(Sound_Sample *sample, const char *ext)
186 {
187     int set_error_str = 1;
188     int bitstream_initialized = 0;
189     Uint8 *buffer = NULL;
190     int packet_count = 0;
191     speex_t *speex = NULL;
192     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
193     SDL_RWops *rw = internal->rw;
194     Uint32 magic;
195 
196     /* Quick rejection. */
197     /*
198      * !!! FIXME: If (ext) is .spx, ignore bad magic number and assume
199      * !!! FIXME:  this is a corrupted file...try to sync up further in
200      * !!! FIXME:  stream. But for general purposes we can't read the
201      * !!! FIXME:  whole RWops here in case it's not a Speex file at all.
202      */
203     magic = SDL_ReadLE32(rw);  /* make sure this is an ogg stream. */
204     BAIL_IF_MACRO(magic != SPEEX_MAGIC, "SPEEX: Not a complete ogg stream", 0);
205     BAIL_IF_MACRO(SDL_RWseek(rw, -4, SEEK_CUR) < 0, ERR_IO_ERROR, 0);
206 
207     speex = (speex_t *) malloc(sizeof (speex_t));
208     BAIL_IF_MACRO(speex == NULL, ERR_OUT_OF_MEMORY, 0);
209     memset(speex, '\0', sizeof (speex_t));
210 
211     speex_bits_init(&speex->bits);
212     if (ogg_sync_init(&speex->oy) != 0) goto speex_open_failed;
213 
214     while (1)
215     {
216         int rc;
217         Uint8 *buffer = (Uint8*)ogg_sync_buffer(&speex->oy, SPEEX_OGG_BUFSIZE);
218         if (buffer == NULL) goto speex_open_failed;
219         rc = SDL_RWread(rw, buffer, 1, SPEEX_OGG_BUFSIZE);
220         if (rc <= 0) goto speex_open_failed;
221         if (ogg_sync_wrote(&speex->oy, rc) != 0) goto speex_open_failed;
222         while (ogg_sync_pageout(&speex->oy, &speex->og) == 1)
223         {
224             if (!bitstream_initialized)
225             {
226                 if (ogg_stream_init(&speex->os, ogg_page_serialno(&speex->og)))
227                     goto speex_open_failed;
228                 bitstream_initialized = 1;
229             } /* if */
230 
231             if (ogg_stream_pagein(&speex->os, &speex->og) != 0)
232                 goto speex_open_failed;
233 
234             while (ogg_stream_packetout(&speex->os, &speex->op) == 1)
235             {
236                 if (speex->op.e_o_s)
237                     goto speex_open_failed;  /* end of stream already?! */
238 
239                 packet_count++;
240                 if (packet_count == 1)  /* need speex header. */
241                 {
242                     if (!process_header(speex, sample))
243                     {
244                         set_error_str = 0; /* process_header will set error string. */
245                         goto speex_open_failed;
246                     } /* if */
247                 } /* if */
248 
249                 if (packet_count > speex->header_count)
250                 {
251                     /* if you made it here, you're ready to get a waveform. */
252                     SNDDBG(("SPEEX: Accepting data stream.\n"));
253 
254                     /* sample->actual is configured in process_header()... */
255                     speex->have_ogg_packet = 1;
256                     sample->flags = SOUND_SAMPLEFLAG_NONE;
257                     internal->decoder_private = speex;
258                     return(1); /* we'll handle this data. */
259                 } /* if */
260             } /* while */
261 
262         } /* while */
263 
264     } /* while */
265 
266     assert(0);  /* shouldn't hit this point. */
267 
268 speex_open_failed:
269     if (speex != NULL)
270     {
271         if (speex->state != NULL)
272             speex_decoder_destroy(speex->state);
273         if (bitstream_initialized)
274             ogg_stream_clear(&speex->os);
275         speex_bits_destroy(&speex->bits);
276         ogg_sync_clear(&speex->oy);
277         free(speex->decode_buf);
278         free(speex);
279     } /* if */
280 
281     if (set_error_str)
282         BAIL_MACRO("SPEEX: decoding error", 0);
283 
284     return(0);
285 } /* SPEEX_open */
286 
287 
288 static void SPEEX_close(Sound_Sample *sample)
289 {
290     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
291     speex_t *speex = (speex_t *) internal->decoder_private;
292     speex_decoder_destroy(speex->state);
293     ogg_stream_clear(&speex->os);
294     speex_bits_destroy(&speex->bits);
295     ogg_sync_clear(&speex->oy);
296     free(speex->decode_buf);
297     free(speex);
298 } /* SPEEX_close */
299 
300 
301 static Uint32 copy_from_decoded(speex_t *speex,
302                                 Sound_SampleInternal *internal,
303                                 Uint32 _cpypos)
304 {
305     /*
306      * !!! FIXME: Obviously, this all needs to change if we allow for
307      * !!! FIXME:  more than mono, S16SYS data.
308      */
309     Uint32 cpypos = _cpypos >> 1;
310     Sint16 *dst = ((Sint16 *) internal->buffer) + cpypos;
311     Sint16 *max;
312     Uint32 maxoutput = (internal->buffer_size >> 1) - cpypos;
313     Uint32 maxavail = speex->decode_total - speex->decode_pos;
314     float *src = speex->decode_buf + speex->decode_pos;
315 
316     if (maxavail < maxoutput)
317         maxoutput = maxavail;
318 
319     speex->decode_pos += maxoutput;
320     cpypos += maxoutput;
321 
322     for (max = dst + maxoutput; dst < max; dst++, src++)
323     {
324         /* !!! FIXME: This screams for vectorization. */
325         register float f = *src;
326         if (f > 32000.0f)  /* eh, speexdec.c clamps like this, too. */
327             f = 32000.0f;
328         else if (f < -32000.0f)
329             f = -32000.0f;
330         *dst = (Sint16) (0.5f + f);
331     } /* for */
332 
333     return(cpypos << 1);
334 } /* copy_from_decoded */
335 
336 
337 /* !!! FIXME: this code sucks. */
338 static Uint32 SPEEX_read(Sound_Sample *sample)
339 {
340     Uint32 retval = 0;
341     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
342     speex_t *speex = (speex_t *) internal->decoder_private;
343     SDL_RWops *rw = internal->rw;
344     Uint8 *buffer;
345     int rc;
346 
347     while (1)
348     {
349         /* see if there's some already-decoded leftovers... */
350         if (speex->decode_total != speex->decode_pos)
351         {
352             retval = copy_from_decoded(speex, internal, retval);
353             if (retval >= internal->buffer_size)
354                 return(retval);  /* whee. */
355         } /* if */
356 
357         /* okay, decoded buffer is spent. What else do we have? */
358         speex->decode_total = speex->decode_pos = 0;
359 
360         if (speex->frames_avail) /* have more frames to decode? */
361         {
362             rc = speex_decode(speex->state, &speex->bits, speex->decode_buf);
363             if (rc < 0) goto speex_read_failed;
364             if (speex_bits_remaining(&speex->bits) < 0) goto speex_read_failed;
365             speex->frames_avail--;
366             speex->decode_total = speex->frame_size;
367             continue;  /* go fill the output buffer... */
368         } /* if */
369 
370         /* need to get more speex frames from available ogg packets... */
371         if (speex->have_ogg_packet)
372         {
373             speex_bits_read_from(&speex->bits,
374                                  (char *) speex->op.packet,
375                                  speex->op.bytes);
376 
377             speex->frames_avail += speex->nframes;
378             if (ogg_stream_packetout(&speex->os, &speex->op) <= 0)
379                 speex->have_ogg_packet = 0;
380             continue;  /* go decode these frames. */
381         } /* if */
382 
383         /* need to get more ogg packets from bitstream... */
384 
385         if (speex->op.e_o_s)   /* okay, we're really spent. */
386         {
387             sample->flags |= SOUND_SAMPLEFLAG_EOF;
388             return(retval);
389         } /* if */
390 
391         while ((!speex->op.e_o_s) && (!speex->have_ogg_packet))
392         {
393             buffer = (Uint8 *) ogg_sync_buffer(&speex->oy, SPEEX_OGG_BUFSIZE);
394             if (buffer == NULL) goto speex_read_failed;
395             rc = SDL_RWread(rw, buffer, 1, SPEEX_OGG_BUFSIZE);
396             if (rc <= 0) goto speex_read_failed;
397             if (ogg_sync_wrote(&speex->oy, rc) != 0) goto speex_read_failed;
398 
399             /* got complete ogg page? */
400             if (ogg_sync_pageout(&speex->oy, &speex->og) == 1)
401             {
402                 if (ogg_stream_pagein(&speex->os, &speex->og) != 0)
403                     goto speex_read_failed;
404 
405                 /* got complete ogg packet? */
406                 if (ogg_stream_packetout(&speex->os, &speex->op) == 1)
407                     speex->have_ogg_packet = 1;
408             } /* if */
409         } /* while */
410     } /* while */
411 
412     assert(0);  /* never hit this. Either return or goto speex_read_failed */
413 
414 speex_read_failed:
415     sample->flags |= SOUND_SAMPLEFLAG_ERROR;
416     /* !!! FIXME: "i/o error" is better in some situations. */
417     BAIL_MACRO("SPEEX: Decoding error", retval);
418 } /* SPEEX_read */
419 
420 
421 static int SPEEX_rewind(Sound_Sample *sample)
422 {
423     /* !!! FIXME */ return(0);
424 } /* SPEEX_rewind */
425 
426 
427 static int SPEEX_seek(Sound_Sample *sample, Uint32 ms)
428 {
429     /* !!! FIXME */ return(0);
430 } /* SPEEX_seek */
431 
432 
433 #endif /* SOUND_SUPPORTS_SPEEX */
434 
435 /* end of speex.c ... */
436 
437