1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * Audio stream functions.
12 *
13 * By Shawn Hargreaves (original version by Andrew Ellem).
14 *
15 * See readme.txt for copyright information.
16 */
17
18
19 #include "allegro.h"
20 #include "allegro/internal/aintern.h"
21
22
23
24 /* play_audio_stream:
25 * Creates a new audio stream and starts it playing. The length is the
26 * size of each transfer buffer.
27 */
play_audio_stream(int len,int bits,int stereo,int freq,int vol,int pan)28 AUDIOSTREAM *play_audio_stream(int len, int bits, int stereo, int freq, int vol, int pan)
29 {
30 AUDIOSTREAM *stream;
31 int i, bufcount;
32 ASSERT(len > 0);
33 ASSERT(bits > 0);
34 ASSERT(freq > 0);
35
36 /* decide how many buffer fragments we will need */
37 if ((digi_driver) && (digi_driver->buffer_size))
38 i = digi_driver->buffer_size();
39 else
40 i = 2048;
41
42 if (len >= i)
43 bufcount = 1;
44 else
45 bufcount = (i + len-1) / len;
46
47 /* create the stream structure */
48 stream = _AL_MALLOC(sizeof(AUDIOSTREAM));
49 if (!stream)
50 return NULL;
51
52 stream->len = len;
53 stream->bufcount = bufcount;
54 stream->bufnum = 0;
55 stream->active = 1;
56 stream->locked = NULL;
57
58 /* create the underlying sample */
59 stream->samp = create_sample(bits, stereo, freq, len*bufcount*2);
60 if (!stream->samp) {
61 _AL_FREE(stream);
62 return NULL;
63 }
64
65 /* fill with silence */
66 if (bits == 16) {
67 unsigned short *p = stream->samp->data;
68 for (i=0; i < len*bufcount*2 * ((stereo) ? 2 : 1); i++)
69 p[i] = 0x8000;
70 }
71 else {
72 unsigned char *p = stream->samp->data;
73 for (i=0; i < len*bufcount*2 * ((stereo) ? 2 : 1); i++)
74 p[i] = 0x80;
75 }
76
77 LOCK_DATA(stream, sizeof(AUDIOSTREAM));
78
79 /* play the sample in looped mode */
80 stream->voice = allocate_voice(stream->samp);
81 if (stream->voice < 0) {
82 destroy_sample(stream->samp);
83 UNLOCK_DATA(stream, sizeof(AUDIOSTREAM));
84 _AL_FREE(stream);
85 return NULL;
86 }
87
88 voice_set_playmode(stream->voice, PLAYMODE_LOOP);
89 voice_set_volume(stream->voice, vol);
90 voice_set_pan(stream->voice, pan);
91
92 return stream;
93 }
94
95
96
97 /* stop_audio_stream:
98 * Destroys an audio stream when it is no longer required.
99 */
stop_audio_stream(AUDIOSTREAM * stream)100 void stop_audio_stream(AUDIOSTREAM *stream)
101 {
102 ASSERT(stream);
103
104 if ((stream->locked) && (digi_driver->unlock_voice))
105 digi_driver->unlock_voice(stream->voice);
106
107 voice_stop(stream->voice);
108 deallocate_voice(stream->voice);
109
110 destroy_sample(stream->samp);
111
112 UNLOCK_DATA(stream, sizeof(AUDIOSTREAM));
113 _AL_FREE(stream);
114 }
115
116
117
118 /* get_audio_stream_buffer:
119 * Returns a pointer to the next audio buffer, or NULL if the previous
120 * data is still playing. This must be called at regular intervals while
121 * the stream is playing, and you must fill the return address with the
122 * appropriate number (the same length that you specified when you create
123 * the stream) of samples. Call free_audio_stream_buffer() after loading
124 * the new samples, to indicate that the data is now valid.
125 */
get_audio_stream_buffer(AUDIOSTREAM * stream)126 void *get_audio_stream_buffer(AUDIOSTREAM *stream)
127 {
128 int pos;
129 char *data = NULL;
130 ASSERT(stream);
131
132 if (stream->bufnum == stream->active * stream->bufcount) {
133 /* waiting for the sample to switch halves */
134 pos = voice_get_position(stream->voice);
135
136 if (stream->active == 0) {
137 if (pos < stream->len*stream->bufcount)
138 return NULL;
139 }
140 else {
141 if (pos >= stream->len*stream->bufcount)
142 return NULL;
143 }
144
145 stream->active = 1-stream->active;
146 }
147
148 /* make sure we got access to the right bit of sample data */
149 if (!stream->locked) {
150 pos = (1-stream->active) * stream->bufcount * stream->len;
151
152 if (digi_driver->lock_voice)
153 data = digi_driver->lock_voice(stream->voice, pos, pos+stream->bufcount*stream->len);
154
155 if (data)
156 stream->locked = data;
157 else
158 stream->locked = (char *)stream->samp->data + (pos * ((stream->samp->bits==8) ? 1 : sizeof(short)) * ((stream->samp->stereo) ? 2 : 1));
159 }
160
161 return (char *)stream->locked + ((stream->bufnum % stream->bufcount) *
162 stream->len *
163 ((stream->samp->bits==8) ? 1 : sizeof(short)) *
164 ((stream->samp->stereo) ? 2 : 1));
165 }
166
167
168
169 /* free_audio_stream_buffer:
170 * Indicates that a sample buffer previously returned by a call to
171 * get_audio_stream_buffer() has now been filled with valid data.
172 */
free_audio_stream_buffer(AUDIOSTREAM * stream)173 void free_audio_stream_buffer(AUDIOSTREAM *stream)
174 {
175 ASSERT(stream);
176
177 /* flip buffers */
178 stream->bufnum++;
179
180 if (stream->bufnum >= stream->bufcount*2)
181 stream->bufnum = 0;
182
183 /* unlock old waveform region */
184 if ((stream->locked) &&
185 ((stream->bufnum == 0) || (stream->bufnum == stream->bufcount))) {
186
187 if (digi_driver->unlock_voice)
188 digi_driver->unlock_voice(stream->voice);
189
190 stream->locked = NULL;
191 }
192
193 /* start playing if it wasn't already */
194 if (voice_get_position(stream->voice) == -1)
195 voice_start(stream->voice);
196 }
197