/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Audio stream functions. * * By Shawn Hargreaves (original version by Andrew Ellem). * * See readme.txt for copyright information. */ #include "allegro.h" /* play_audio_stream: * Creates a new audio stream and starts it playing. The length is the * size of each transfer buffer. */ AUDIOSTREAM *play_audio_stream(int len, int bits, int stereo, int freq, int vol, int pan) { AUDIOSTREAM *stream; int i, bufcount; ASSERT(len > 0); ASSERT(bits > 0); ASSERT(freq > 0); /* decide how many buffer fragments we will need */ if ((digi_driver) && (digi_driver->buffer_size)) i = digi_driver->buffer_size(); else i = 2048; if (len >= i) bufcount = 1; else bufcount = (i + len-1) / len; /* create the stream structure */ stream = malloc(sizeof(AUDIOSTREAM)); if (!stream) return NULL; stream->len = len; stream->bufcount = bufcount; stream->bufnum = 0; stream->active = 1; stream->locked = NULL; /* create the underlying sample */ stream->samp = create_sample(bits, stereo, freq, len*bufcount*2); if (!stream->samp) { free(stream); return NULL; } /* fill with silence */ if (bits == 16) { unsigned short *p = stream->samp->data; for (i=0; i < len*bufcount*2 * ((stereo) ? 2 : 1); i++) p[i] = 0x8000; } else { unsigned char *p = stream->samp->data; for (i=0; i < len*bufcount*2 * ((stereo) ? 2 : 1); i++) p[i] = 0x80; } LOCK_DATA(stream, sizeof(AUDIOSTREAM)); /* play the sample in looped mode */ stream->voice = allocate_voice(stream->samp); if (stream->voice < 0) { destroy_sample(stream->samp); UNLOCK_DATA(stream, sizeof(AUDIOSTREAM)); free(stream); return NULL; } voice_set_playmode(stream->voice, PLAYMODE_LOOP); voice_set_volume(stream->voice, vol); voice_set_pan(stream->voice, pan); return stream; } /* stop_audio_stream: * Destroys an audio stream when it is no longer required. */ void stop_audio_stream(AUDIOSTREAM *stream) { ASSERT(stream); if ((stream->locked) && (digi_driver->unlock_voice)) digi_driver->unlock_voice(stream->voice); voice_stop(stream->voice); deallocate_voice(stream->voice); destroy_sample(stream->samp); UNLOCK_DATA(stream, sizeof(AUDIOSTREAM)); free(stream); } /* get_audio_stream_buffer: * Returns a pointer to the next audio buffer, or NULL if the previous * data is still playing. This must be called at regular intervals while * the stream is playing, and you must fill the return address with the * appropriate number (the same length that you specified when you create * the stream) of samples. Call free_audio_stream_buffer() after loading * the new samples, to indicate that the data is now valid. */ void *get_audio_stream_buffer(AUDIOSTREAM *stream) { int pos; char *data = NULL; ASSERT(stream); if (stream->bufnum == stream->active * stream->bufcount) { /* waiting for the sample to switch halves */ pos = voice_get_position(stream->voice); if (stream->active == 0) { if (pos < stream->len*stream->bufcount) return NULL; } else { if (pos >= stream->len*stream->bufcount) return NULL; } stream->active = 1-stream->active; } /* make sure we got access to the right bit of sample data */ if (!stream->locked) { pos = (1-stream->active) * stream->bufcount * stream->len; if (digi_driver->lock_voice) data = digi_driver->lock_voice(stream->voice, pos, pos+stream->bufcount*stream->len); if (data) stream->locked = data; else stream->locked = (char *)stream->samp->data + (pos * ((stream->samp->bits==8) ? 1 : sizeof(short)) * ((stream->samp->stereo) ? 2 : 1)); } return (char *)stream->locked + ((stream->bufnum % stream->bufcount) * stream->len * ((stream->samp->bits==8) ? 1 : sizeof(short)) * ((stream->samp->stereo) ? 2 : 1)); } /* free_audio_stream_buffer: * Indicates that a sample buffer previously returned by a call to * get_audio_stream_buffer() has now been filled with valid data. */ void free_audio_stream_buffer(AUDIOSTREAM *stream) { ASSERT(stream); /* flip buffers */ stream->bufnum++; if (stream->bufnum >= stream->bufcount*2) stream->bufnum = 0; /* unlock old waveform region */ if ((stream->locked) && ((stream->bufnum == 0) || (stream->bufnum == stream->bufcount))) { if (digi_driver->unlock_voice) digi_driver->unlock_voice(stream->voice); stream->locked = NULL; } /* start playing if it wasn't already */ if (voice_get_position(stream->voice) == -1) voice_start(stream->voice); }