blob: 978db3cc87f634e93151c420243dc1b8c3ea2ef3 [file] [log] [blame]
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Miika Pekkarinen
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <stdbool.h>
21#include <stdio.h>
22#include "config.h"
23#include "debug.h"
24#include "panic.h"
25#include <kernel.h>
26#include "pcmbuf.h"
27#include "pcm_playback.h"
28#include "logf.h"
29#ifndef SIMULATOR
30#include "cpu.h"
31#endif
32#include "system.h"
33#include <string.h>
34#include "buffer.h"
Miika Pekkarinenf090dc32005-07-21 11:44:00 +000035#include "settings.h"
36#include "audio.h"
Miika Pekkarinen159c52d2005-08-20 11:13:19 +000037#include "dsp.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000038
Miika Pekkarinen4408b6b2006-02-07 19:17:51 +000039#define PCMBUF_WATERMARK (NATIVE_FREQUENCY * 4 * 1)
Miika Pekkarinen20b38972005-07-13 12:48:22 +000040
Miika Pekkarinen20b38972005-07-13 12:48:22 +000041/* Structure we can use to queue pcm chunks in memory to be played
42 * by the driver code. */
43struct pcmbufdesc
44{
45 void *addr;
Brandon Low413da2a2006-02-07 20:38:55 +000046 size_t size;
47 struct pcmbufdesc* link;
Miika Pekkarinen20b38972005-07-13 12:48:22 +000048 /* Call this when the buffer has been played */
49 void (*callback)(void);
Brandon Low413da2a2006-02-07 20:38:55 +000050};
Miika Pekkarinen20b38972005-07-13 12:48:22 +000051
Brandon Low6c0908b2006-04-23 22:54:34 +000052/* Size of the PCM buffer. */
53static size_t pcmbuf_size IDATA_ATTR = 0;
54
55static char *audiobuffer IDATA_ATTR;
56/* Current audio buffer write index. */
57static size_t audiobuffer_pos IDATA_ATTR;
58/* Amount audiobuffer_pos will be increased.*/
59static size_t audiobuffer_fillpos IDATA_ATTR;
60static char *fadebuf IDATA_ATTR;
61static char *voicebuf IDATA_ATTR;
62
63static void (*pcmbuf_event_handler)(void) IDATA_ATTR;
64static void (*position_callback)(size_t size) IDATA_ATTR;
65
66/* Crossfade related state */
67static bool crossfade_enabled;
68static bool crossfade_mix;
69static bool crossfade_active IDATA_ATTR;
70static bool crossfade_init IDATA_ATTR;
71
72/* Track the current location for processing crossfade */
73static struct pcmbufdesc *crossfade_chunk IDATA_ATTR;
74static size_t crossfade_sample IDATA_ATTR;
75
76/* Counters for fading in new data */
77static size_t crossfade_fade_in_total IDATA_ATTR;
78static size_t crossfade_fade_in_rem IDATA_ATTR;
79
Brandon Low413da2a2006-02-07 20:38:55 +000080static size_t pcmbuf_descsize;
81static struct pcmbufdesc *pcmbuf_read IDATA_ATTR;
82static struct pcmbufdesc *pcmbuf_read_end IDATA_ATTR;
83static struct pcmbufdesc *pcmbuf_write IDATA_ATTR;
84static struct pcmbufdesc *pcmbuf_write_end IDATA_ATTR;
85static size_t last_chunksize IDATA_ATTR;
Brandon Low2da61ff2006-04-24 01:32:28 +000086
Brandon Low413da2a2006-02-07 20:38:55 +000087static size_t pcmbuf_unplayed_bytes IDATA_ATTR;
Brandon Low413da2a2006-02-07 20:38:55 +000088static size_t pcmbuf_watermark IDATA_ATTR;
Brandon Low2da61ff2006-04-24 01:32:28 +000089
Brandon Lowf3bc1ef2006-04-22 14:40:13 +000090static struct pcmbufdesc *pcmbuf_mix_chunk IDATA_ATTR;
91static size_t pcmbuf_mix_sample IDATA_ATTR;
Brandon Low2da61ff2006-04-24 01:32:28 +000092
Miika Pekkarinen4408b6b2006-02-07 19:17:51 +000093static bool low_latency_mode = false;
Brandon Low6c0908b2006-04-23 22:54:34 +000094static bool pcmbuf_flush;
Miika Pekkarinen20b38972005-07-13 12:48:22 +000095
Brandon Low413da2a2006-02-07 20:38:55 +000096/* Helpful macros for use in conditionals this assumes some of the above
97 * static variable names */
98#define NEED_FLUSH(position) \
99 (audiobuffer_fillpos > PCMBUF_TARGET_CHUNK || position >= pcmbuf_size)
100#define LOW_DATA(quarter_secs) \
101 (pcmbuf_unplayed_bytes < NATIVE_FREQUENCY * quarter_secs)
102
Brandon Low413da2a2006-02-07 20:38:55 +0000103static void pcmbuf_under_watermark(void);
Brandon Low37faaab2006-04-20 02:30:59 +0000104static bool pcmbuf_flush_fillpos(void);
Brandon Low413da2a2006-02-07 20:38:55 +0000105
106#if defined(HAVE_ADJUSTABLE_CPU_FREQ) && !defined(SIMULATOR)
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000107void pcmbuf_boost(bool state)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000108{
109 static bool boost_state = false;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000110
Brandon Low0744e762006-04-13 17:30:54 +0000111 if (crossfade_init || crossfade_active)
Brandon Low413da2a2006-02-07 20:38:55 +0000112 return;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000113
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000114 if (state != boost_state) {
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000115 cpu_boost(state);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000116 boost_state = state;
117 }
118}
Miika Pekkarinen65d43a22005-08-28 19:55:30 +0000119#endif
120
Brandon Low413da2a2006-02-07 20:38:55 +0000121#define CALL_IF_EXISTS(function, args...) if (function) function(args)
122/* This function has 2 major logical parts (separated by brackets both for
123 * readability and variable scoping). The first part performs the
124 * operastions related to finishing off the last buffer we fed to the DMA.
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000125 * The second part performs the operations involved in sending a new buffer
Brandon Low413da2a2006-02-07 20:38:55 +0000126 * to the DMA. Finally the function checks the status of the buffer and
127 * boosts if necessary */
128static void pcmbuf_callback(unsigned char** start, size_t* size) ICODE_ATTR;
129static void pcmbuf_callback(unsigned char** start, size_t* size)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000130{
Brandon Low413da2a2006-02-07 20:38:55 +0000131 {
132 struct pcmbufdesc *pcmbuf_current = pcmbuf_read;
133 /* Take the finished buffer out of circulation */
134 pcmbuf_read = pcmbuf_current->link;
135
Brandon Low6c0908b2006-04-23 22:54:34 +0000136 /* The buffer is finished, call the callback functions */
137 CALL_IF_EXISTS(position_callback, last_chunksize);
Brandon Low413da2a2006-02-07 20:38:55 +0000138 CALL_IF_EXISTS(pcmbuf_current->callback);
139
140 /* Put the finished buffer back into circulation */
141 pcmbuf_write_end->link = pcmbuf_current;
142 pcmbuf_write_end = pcmbuf_current;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000143
144 /* If we've read through the mix chunk while it's still mixing there */
145 if (pcmbuf_current == pcmbuf_mix_chunk)
146 pcmbuf_mix_chunk = NULL;
Brandon Low413da2a2006-02-07 20:38:55 +0000147 }
148
Brandon Low37faaab2006-04-20 02:30:59 +0000149process_new_buffer:
Brandon Low413da2a2006-02-07 20:38:55 +0000150 {
151 /* Send the new buffer to the pcm */
152 struct pcmbufdesc *pcmbuf_new = pcmbuf_read;
153 size_t *realsize = size;
154 unsigned char** realstart = start;
155 if(pcmbuf_new)
156 {
157 size_t current_size = pcmbuf_new->size;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000158
Brandon Low413da2a2006-02-07 20:38:55 +0000159 pcmbuf_unplayed_bytes -= current_size;
Brandon Low413da2a2006-02-07 20:38:55 +0000160 last_chunksize = current_size;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000161 *realsize = current_size;
Brandon Low413da2a2006-02-07 20:38:55 +0000162 *realstart = pcmbuf_new->addr;
163 }
164 else
165 {
Brandon Low37faaab2006-04-20 02:30:59 +0000166 /* There may be more data waiting to flush, try to use it */
167 if (pcmbuf_flush_fillpos())
168 goto process_new_buffer;
169
Brandon Low413da2a2006-02-07 20:38:55 +0000170 /* No more buffers */
171 last_chunksize = 0;
172 *realsize = 0;
173 *realstart = NULL;
174 CALL_IF_EXISTS(pcmbuf_event_handler);
175 }
176 }
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000177}
178
Brandon Low413da2a2006-02-07 20:38:55 +0000179void pcmbuf_set_position_callback(void (*callback)(size_t size))
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000180{
Brandon Lowa3868d32006-01-21 22:42:44 +0000181 position_callback = callback;
182}
183
Brandon Low413da2a2006-02-07 20:38:55 +0000184static void pcmbuf_set_watermark_bytes(size_t numbytes)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000185{
186 pcmbuf_watermark = numbytes;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000187}
188
Brandon Low413da2a2006-02-07 20:38:55 +0000189/* This is really just part of pcmbuf_flush_fillpos, but is easier to keep
190 * in a separate function for the moment */
191static inline void pcmbuf_add_chunk(void)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000192{
Brandon Low413da2a2006-02-07 20:38:55 +0000193 register size_t size = audiobuffer_fillpos;
194 /* Grab the next description to write, and change the write pointer */
195 register struct pcmbufdesc *pcmbuf_current = pcmbuf_write;
196 pcmbuf_write = pcmbuf_current->link;
197 /* Fill in the values in the new buffer chunk */
198 pcmbuf_current->addr = &audiobuffer[audiobuffer_pos];
199 pcmbuf_current->size = size;
200 pcmbuf_current->callback = pcmbuf_event_handler;
201 pcmbuf_current->link = NULL;
202 /* This is single use only */
203 pcmbuf_event_handler = NULL;
204 if (pcmbuf_read) {
Brandon Low6c0908b2006-04-23 22:54:34 +0000205 if (pcmbuf_flush)
206 {
207 pcmbuf_write_end->link = pcmbuf_read->link;
208 pcmbuf_read->link = pcmbuf_current;
209 while (pcmbuf_write_end->link)
210 {
211 pcmbuf_write_end = pcmbuf_write_end->link;
212 pcmbuf_unplayed_bytes -= pcmbuf_write_end->size;
213 }
214 pcmbuf_flush = false;
215 }
Brandon Low413da2a2006-02-07 20:38:55 +0000216 /* If there is already a read buffer setup, add to it */
Brandon Low6c0908b2006-04-23 22:54:34 +0000217 else
218 pcmbuf_read_end->link = pcmbuf_current;
Brandon Low413da2a2006-02-07 20:38:55 +0000219 } else {
220 /* Otherwise create the buffer */
221 pcmbuf_read = pcmbuf_current;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000222 }
Brandon Low413da2a2006-02-07 20:38:55 +0000223 /* This is now the last buffer to read */
224 pcmbuf_read_end = pcmbuf_current;
225
226 /* Update bytes counters */
227 pcmbuf_unplayed_bytes += size;
Brandon Low413da2a2006-02-07 20:38:55 +0000228
229 audiobuffer_pos += size;
230 if (audiobuffer_pos >= pcmbuf_size)
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000231 audiobuffer_pos -= pcmbuf_size;
Brandon Low413da2a2006-02-07 20:38:55 +0000232
233 audiobuffer_fillpos = 0;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000234}
235
Brandon Low413da2a2006-02-07 20:38:55 +0000236static void pcmbuf_under_watermark(void)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000237{
238 /* Fill audio buffer by boosting cpu */
239 pcmbuf_boost(true);
Brandon Low413da2a2006-02-07 20:38:55 +0000240 /* Disable crossfade if < .5s of audio */
Brandon Low6c0908b2006-04-23 22:54:34 +0000241 if (LOW_DATA(2))
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000242 crossfade_active = false;
243}
244
Brandon Low413da2a2006-02-07 20:38:55 +0000245void pcmbuf_set_event_handler(void (*event_handler)(void))
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000246{
247 pcmbuf_event_handler = event_handler;
248}
249
250unsigned int pcmbuf_get_latency(void)
251{
Brandon Low413da2a2006-02-07 20:38:55 +0000252 /* Be careful how this calculation is rearranted, it's easy to overflow */
253 size_t bytes = pcmbuf_unplayed_bytes + pcm_get_bytes_waiting();
254 return bytes / 4 / (NATIVE_FREQUENCY/1000);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000255}
256
Miika Pekkarinen4408b6b2006-02-07 19:17:51 +0000257void pcmbuf_set_low_latency(bool state)
258{
259 low_latency_mode = state;
260}
261
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000262bool pcmbuf_is_lowdata(void)
263{
Brandon Low413da2a2006-02-07 20:38:55 +0000264 if (!pcm_is_playing() || pcm_is_paused() ||
265 crossfade_init || crossfade_active)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000266 return false;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000267
Brandon Low413da2a2006-02-07 20:38:55 +0000268 /* 0.5 seconds of buffer is low data */
269 return LOW_DATA(2);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000270}
271
Brandon Low6c0908b2006-04-23 22:54:34 +0000272/* Amount of bytes left in the buffer. */
273inline size_t pcmbuf_free(void)
274{
275 if (pcmbuf_read)
276 {
277 size_t read = (size_t)pcmbuf_read->addr;
278 size_t write =
279 (size_t)&audiobuffer[audiobuffer_pos + audiobuffer_fillpos];
280 if (read < write)
281 read += pcmbuf_size;
282 return read - write;
283 }
284 return pcmbuf_size;
285}
286
Miika Pekkarinena4f8d1c2006-01-27 16:25:44 +0000287bool pcmbuf_crossfade_init(bool manual_skip)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000288{
Brandon Low6c0908b2006-04-23 22:54:34 +0000289 /* Can't do two crossfades at once and, no fade if pcm is off now */
290 if (crossfade_init || crossfade_active || !pcm_is_playing())
291 {
292 pcmbuf_play_stop();
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000293 return false;
294 }
Brandon Low6c0908b2006-04-23 22:54:34 +0000295
296 /* Not enough data, or crossfade disabled, flush the old data instead */
297 if (LOW_DATA(6) || !pcmbuf_is_crossfade_enabled() || low_latency_mode)
298 {
299 pcmbuf_boost(true);
300 pcmbuf_flush = true;
301 return false;
302 }
303
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000304 logf("pcmbuf_crossfade_init");
305 pcmbuf_boost(true);
Miika Pekkarinen90161c92005-07-22 16:46:27 +0000306
Miika Pekkarinena4f8d1c2006-01-27 16:25:44 +0000307 /* Don't enable mix mode when skipping tracks manually. */
Brandon Low6c0908b2006-04-23 22:54:34 +0000308 crossfade_mix = manual_skip && global_settings.crossfade_fade_out_mixmode;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000309 crossfade_init = true;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000310
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000311 return true;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000312
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000313}
314
315void pcmbuf_play_stop(void)
316{
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000317 /** Prevent a very tiny pop from happening by muting audio
318 * until dma has been initialized. */
319 pcm_mute(true);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000320 pcm_play_stop();
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000321 pcm_mute(false);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000322
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000323 pcmbuf_unplayed_bytes = 0;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000324 pcmbuf_mix_chunk = NULL;
Brandon Low413da2a2006-02-07 20:38:55 +0000325 if (pcmbuf_read) {
326 pcmbuf_write_end->link = pcmbuf_read;
327 pcmbuf_write_end = pcmbuf_read_end;
328 pcmbuf_read = pcmbuf_read_end = NULL;
329 }
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000330 audiobuffer_pos = 0;
331 audiobuffer_fillpos = 0;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000332 crossfade_init = false;
333 crossfade_active = false;
Brandon Low6c0908b2006-04-23 22:54:34 +0000334 pcmbuf_flush = false;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000335
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000336 pcmbuf_boost(false);
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000337
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000338}
339
Brandon Low413da2a2006-02-07 20:38:55 +0000340int pcmbuf_used_descs(void) {
341 struct pcmbufdesc *pcmbuf_temp = pcmbuf_read;
342 unsigned int i = 0;
343 while (pcmbuf_temp) {
344 pcmbuf_temp = pcmbuf_temp->link;
345 i++;
346 }
347 return i;
348}
349
350int pcmbuf_descs(void) {
Brandon Low6c0908b2006-04-23 22:54:34 +0000351 return pcmbuf_size / PCMBUF_TARGET_CHUNK;
Brandon Low413da2a2006-02-07 20:38:55 +0000352}
353
354size_t get_pcmbuf_descsize(void) {
355 return pcmbuf_descsize;
356}
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000357
Brandon Low413da2a2006-02-07 20:38:55 +0000358static void pcmbuf_init_pcmbuffers(void) {
359 struct pcmbufdesc *next = pcmbuf_write;
360 next++;
361 pcmbuf_write_end = pcmbuf_write;
362 while ((void *)next < (void *)audiobufend) {
363 pcmbuf_write_end->link=next;
364 pcmbuf_write_end=next;
365 next++;
366 }
367}
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000368
Brandon Low413da2a2006-02-07 20:38:55 +0000369/* Initialize the pcmbuffer the structure looks like this:
370 * ...CODECBUFFER|---------PCMBUF---------|GUARDBUF|DESCS| */
371void pcmbuf_init(size_t bufsize)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000372{
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000373 pcmbuf_size = bufsize;
Brandon Low413da2a2006-02-07 20:38:55 +0000374 pcmbuf_descsize = pcmbuf_descs()*sizeof(struct pcmbufdesc);
375 audiobuffer = (char *)&audiobuf[(audiobufend - audiobuf) -
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000376 (pcmbuf_size + PCMBUF_MIX_CHUNK * 2 + pcmbuf_descsize)];
377 fadebuf = &audiobuffer[pcmbuf_size];
378 voicebuf = &fadebuf[PCMBUF_MIX_CHUNK];
379 pcmbuf_write = (struct pcmbufdesc *)(&voicebuf[PCMBUF_MIX_CHUNK]);
Brandon Low413da2a2006-02-07 20:38:55 +0000380 pcmbuf_init_pcmbuffers();
Brandon Lowee6a95a2006-01-22 00:03:20 +0000381 position_callback = NULL;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000382 pcmbuf_event_handler = NULL;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000383 pcmbuf_play_stop();
384}
385
Brandon Low413da2a2006-02-07 20:38:55 +0000386size_t pcmbuf_get_bufsize(void)
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000387{
388 return pcmbuf_size;
389}
390
Brandon Low413da2a2006-02-07 20:38:55 +0000391void pcmbuf_pause(bool pause) {
Brandon Low8307d0b2006-03-24 02:38:57 +0000392 if (pause)
393 pcm_mute(true);
Brandon Low413da2a2006-02-07 20:38:55 +0000394 pcm_play_pause(!pause);
Brandon Low8307d0b2006-03-24 02:38:57 +0000395 if (!pause)
396 pcm_mute(false);
Brandon Low86c7e1a2006-04-14 16:42:14 +0000397 pcmbuf_boost(!pause && pcm_is_playing());
Brandon Low413da2a2006-02-07 20:38:55 +0000398}
399
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000400/* Force playback. */
401void pcmbuf_play_start(void)
402{
403 if (!pcm_is_playing() && pcmbuf_unplayed_bytes)
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000404 {
405 /** Prevent a very tiny pop from happening by muting audio
406 * until dma has been initialized. */
407 pcm_mute(true);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000408
Brandon Low413da2a2006-02-07 20:38:55 +0000409 last_chunksize = pcmbuf_read->size;
410 pcmbuf_unplayed_bytes -= last_chunksize;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000411 pcm_play_data(pcmbuf_callback,
Brandon Low413da2a2006-02-07 20:38:55 +0000412 (unsigned char *)pcmbuf_read->addr, last_chunksize);
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000413
414 /* Now unmute the audio. */
415 pcm_mute(false);
416 }
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000417}
418
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000419/**
420 * Commit samples waiting to the pcm buffer.
421 */
Brandon Low37faaab2006-04-20 02:30:59 +0000422static bool pcmbuf_flush_fillpos(void)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000423{
Brandon Low413da2a2006-02-07 20:38:55 +0000424 if (audiobuffer_fillpos) {
425 /* Never use the last buffer descriptor */
426 while (pcmbuf_write == pcmbuf_write_end) {
427 logf("pcmbuf_flush_fillpos no descriptors");
428 /* Deboost to let the playback catchup */
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000429 pcmbuf_boost(false);
Brandon Low86f1e2e2006-03-24 13:43:15 +0000430 /* If this happens, something is being stupid */
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000431 if (!pcm_is_playing()) {
Brandon Low413da2a2006-02-07 20:38:55 +0000432 logf("pcmbuf_flush_fillpos error");
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000433 pcmbuf_play_start();
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000434 }
Brandon Low86f1e2e2006-03-24 13:43:15 +0000435 /* Let approximately one chunk of data playback */
436 sleep(PCMBUF_TARGET_CHUNK/(NATIVE_FREQUENCY * 4) / 5);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000437 }
Brandon Low413da2a2006-02-07 20:38:55 +0000438 pcmbuf_add_chunk();
Brandon Low37faaab2006-04-20 02:30:59 +0000439 return true;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000440 }
Brandon Low37faaab2006-04-20 02:30:59 +0000441 return false;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000442}
443
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000444/**
445 * Completely process the crossfade fade out effect with current pcm buffer.
446 */
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000447static void crossfade_process_buffer(size_t fade_in_delay,
448 size_t fade_out_delay, size_t fade_out_rem)
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000449{
Brandon Low6c0908b2006-04-23 22:54:34 +0000450 if (!crossfade_mix)
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000451 {
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000452 /* Fade out the specified amount of the already processed audio */
453 size_t total_fade_out = fade_out_rem;
Brandon Low6c0908b2006-04-23 22:54:34 +0000454 size_t fade_out_sample;
455 struct pcmbufdesc *fade_out_chunk = crossfade_chunk;
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000456
Brandon Low6c0908b2006-04-23 22:54:34 +0000457 /* Find the right chunk to start fading out */
Brandon Low9ca16a62006-04-24 03:43:43 +0000458 fade_out_delay += crossfade_sample * 2;
Brandon Low6c0908b2006-04-23 22:54:34 +0000459 while (fade_out_delay >= fade_out_chunk->size)
460 {
461 fade_out_delay -= fade_out_chunk->size;
462 fade_out_chunk = fade_out_chunk->link;
463 }
464 /* The start sample within the chunk */
465 fade_out_sample = fade_out_delay / 2;
466
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000467 while (fade_out_rem > 0)
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000468 {
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000469 /* Each 1/10 second of audio will have the same fade applied */
470 size_t block_rem = MIN(NATIVE_FREQUENCY * 2 / 10, fade_out_rem);
Brandon Low6c0908b2006-04-23 22:54:34 +0000471 int factor = (fade_out_rem << 8) / total_fade_out;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000472
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000473 fade_out_rem -= block_rem;
474
475 /* Fade this block */
Brandon Low6c0908b2006-04-23 22:54:34 +0000476 while (block_rem > 0)
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000477 {
478 /* Fade one sample */
Brandon Low6c0908b2006-04-23 22:54:34 +0000479 short *buf = (short *)(fade_out_chunk->addr);
480 int sample = buf[fade_out_sample];
481 buf[fade_out_sample++] = (sample * factor) >> 8;
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000482
Brandon Low6c0908b2006-04-23 22:54:34 +0000483 block_rem--;
484 /* Move to the next chunk as needed */
485 if (fade_out_sample * 2 >= fade_out_chunk->size)
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000486 {
Brandon Low6c0908b2006-04-23 22:54:34 +0000487 fade_out_chunk = fade_out_chunk->link;
488 fade_out_sample = 0;
Brandon Lowdbcc9c22006-03-30 00:47:05 +0000489 }
490 }
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000491 }
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000492 }
493
Brandon Low6c0908b2006-04-23 22:54:34 +0000494 /* Find the right chunk and sample to start fading in */
Brandon Low9ca16a62006-04-24 03:43:43 +0000495 fade_in_delay += crossfade_sample * 2;
Brandon Low6c0908b2006-04-23 22:54:34 +0000496 while (fade_in_delay >= crossfade_chunk->size)
497 {
498 fade_in_delay -= crossfade_chunk->size;
499 crossfade_chunk = crossfade_chunk->link;
500 }
501 crossfade_sample = fade_in_delay / 2;
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000502 logf("process done!");
503}
504
Brandon Low6c0908b2006-04-23 22:54:34 +0000505/* Initializes crossfader, calculates all necessary parameters and
506 * performs fade-out with the pcm buffer. */
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000507static void crossfade_start(void)
508{
Brandon Low6c0908b2006-04-23 22:54:34 +0000509 size_t crossfade_rem;
510 size_t fade_out_rem;
511 size_t fade_out_delay;
512 size_t fade_in_delay;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000513
Brandon Low37faaab2006-04-20 02:30:59 +0000514 crossfade_init = false;
Brandon Low413da2a2006-02-07 20:38:55 +0000515 /* Reject crossfade if less than .5s of data */
516 if (LOW_DATA(2)) {
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000517 logf("crossfade rejected");
518 pcmbuf_play_stop();
519 return ;
520 }
521
522 logf("crossfade_start");
Brandon Low413da2a2006-02-07 20:38:55 +0000523 pcmbuf_flush_fillpos();
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000524 crossfade_active = true;
Brandon Low6c0908b2006-04-23 22:54:34 +0000525
Brandon Low413da2a2006-02-07 20:38:55 +0000526 /* Initialize the crossfade buffer size to all of the buffered data that
527 * has not yet been sent to the DMA */
Brandon Low6c0908b2006-04-23 22:54:34 +0000528 crossfade_rem = pcmbuf_unplayed_bytes;
529 crossfade_chunk = pcmbuf_read->link;
Brandon Low8ef18272006-04-24 12:41:30 +0000530 crossfade_sample = 0;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000531
Brandon Low6c0908b2006-04-23 22:54:34 +0000532 /* Get fade out delay from settings. */
533 fade_out_delay =
534 NATIVE_FREQUENCY * global_settings.crossfade_fade_out_delay * 4;
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000535
Brandon Low6c0908b2006-04-23 22:54:34 +0000536 /* Get fade out duration from settings. */
537 fade_out_rem =
538 NATIVE_FREQUENCY * global_settings.crossfade_fade_out_duration * 4;
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000539
Brandon Low6c0908b2006-04-23 22:54:34 +0000540 /* We want only to modify the last part of the buffer. */
541 if (crossfade_rem > fade_out_rem + fade_out_delay)
542 {
543 size_t crossfade_extra = crossfade_rem - fade_out_rem + fade_out_delay;
544 while (crossfade_extra > crossfade_chunk->size)
545 {
546 crossfade_extra -= crossfade_chunk->size;
547 crossfade_chunk = crossfade_chunk->link;
548 }
549 crossfade_sample = crossfade_extra / 2;
550 }
551 /* Truncate fade out duration if necessary. */
552 else if (crossfade_rem < fade_out_rem + fade_out_delay)
553 {
554 size_t crossfade_short = fade_out_rem + fade_out_delay - crossfade_rem;
555 if (fade_out_rem > crossfade_short)
556 fade_out_rem -= crossfade_short;
557 else
558 {
559 fade_out_delay -= crossfade_short - fade_out_rem;
560 fade_out_rem = 0;
561 }
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000562 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000563
Brandon Low6c0908b2006-04-23 22:54:34 +0000564 /* Get also fade in duration and delays from settings. */
565 crossfade_fade_in_total =
566 NATIVE_FREQUENCY * global_settings.crossfade_fade_in_duration * 4;
567 crossfade_fade_in_rem = crossfade_fade_in_total;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000568
Brandon Low6c0908b2006-04-23 22:54:34 +0000569 /* We should avoid to divide by zero. */
570 if (crossfade_fade_in_total == 0)
571 crossfade_fade_in_total = 1;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000572
Brandon Low6c0908b2006-04-23 22:54:34 +0000573 fade_in_delay =
574 NATIVE_FREQUENCY * global_settings.crossfade_fade_in_delay * 4;
575
576 crossfade_process_buffer(fade_in_delay, fade_out_delay, fade_out_rem);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000577}
578
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000579/**
580 * Fades in samples passed to the function and inserts them
581 * to the pcm buffer.
582 */
Brandon Low6c0908b2006-04-23 22:54:34 +0000583static void fade_insert(const char *buf, size_t length)
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000584{
Brandon Low413da2a2006-02-07 20:38:55 +0000585 size_t copy_n;
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000586 int factor;
Brandon Low6c0908b2006-04-23 22:54:34 +0000587 unsigned int i;
588 short *output_buf;
589 const short *input_buf = (const short *)buf;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000590
Brandon Low6c0908b2006-04-23 22:54:34 +0000591 factor = ((crossfade_fade_in_total-crossfade_fade_in_rem)<<8)
592 /crossfade_fade_in_total;
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000593
Brandon Low6c0908b2006-04-23 22:54:34 +0000594 while (pcmbuf_free() < length)
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000595 {
596 pcmbuf_boost(false);
597 sleep(1);
598 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000599
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000600 while (length > 0) {
Brandon Low413da2a2006-02-07 20:38:55 +0000601 unsigned int audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
602 /* Flush as needed */
603 if (NEED_FLUSH(audiobuffer_index))
604 {
605 pcmbuf_flush_fillpos();
606 audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
607 }
Brandon Low413da2a2006-02-07 20:38:55 +0000608 copy_n = MIN(length, pcmbuf_size - audiobuffer_index);
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000609 audiobuffer_fillpos += copy_n;
610 length -= copy_n;
Brandon Low6c0908b2006-04-23 22:54:34 +0000611 output_buf = (short *)&audiobuffer[audiobuffer_index];
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000612
Brandon Low6c0908b2006-04-23 22:54:34 +0000613 for (copy_n /=2, i = 0; i < copy_n; i++)
Miika Pekkarinen07fd5d92005-12-15 18:44:59 +0000614 {
Brandon Low6c0908b2006-04-23 22:54:34 +0000615 int sample = input_buf[i];
616 output_buf[i] = (sample * factor) >> 8;
Miika Pekkarinen07fd5d92005-12-15 18:44:59 +0000617 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000618
Brandon Low6c0908b2006-04-23 22:54:34 +0000619 input_buf += copy_n;
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000620 }
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000621}
622
Brandon Low413da2a2006-02-07 20:38:55 +0000623static void pcmbuf_flush_buffer(const char *buf, size_t length)
624{
625 size_t copy_n;
Brandon Low413da2a2006-02-07 20:38:55 +0000626 while (length > 0) {
627 size_t audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
628 if (NEED_FLUSH(audiobuffer_index))
629 {
630 pcmbuf_flush_fillpos();
631 audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
632 }
633 copy_n = MIN(length, pcmbuf_size - audiobuffer_index);
634 memcpy(&audiobuffer[audiobuffer_index], buf, copy_n);
635 buf += copy_n;
636 audiobuffer_fillpos += copy_n;
637 length -= copy_n;
638 }
639}
640
Brandon Low6c0908b2006-04-23 22:54:34 +0000641static void flush_crossfade(const char *buf, size_t length)
642{
643 const short *input_buf = (const short *)buf;
644 int factor = ((crossfade_fade_in_total-crossfade_fade_in_rem)<<8) /
645 crossfade_fade_in_total;
Brandon Low413da2a2006-02-07 20:38:55 +0000646
Brandon Low6c0908b2006-04-23 22:54:34 +0000647 while (length && crossfade_fade_in_rem && crossfade_chunk)
648 {
649 short *output_buf = (short *)(crossfade_chunk->addr);
650 int sample = *input_buf++;
651 sample = ((sample * factor) >> 8) + output_buf[crossfade_sample];
652 output_buf[crossfade_sample++] = MIN(32767, MAX(-32768, sample));
653
654 length -= 2;
655 crossfade_fade_in_rem -= 2;
656 if (crossfade_sample * 2 >= crossfade_chunk->size)
657 {
658 crossfade_chunk = crossfade_chunk->link;
659 crossfade_sample = 0;
660 }
Brandon Low413da2a2006-02-07 20:38:55 +0000661 }
662
Brandon Low6c0908b2006-04-23 22:54:34 +0000663 buf = (const char *)input_buf;
664
665 if (!crossfade_chunk)
666 {
667 if (crossfade_fade_in_rem > 0 && crossfade_fade_in_total > 0)
668 {
669 size_t size_insert = MIN(crossfade_fade_in_rem, length);
670 fade_insert(buf, size_insert);
671 crossfade_fade_in_rem -= size_insert;
672 length -= size_insert;
673 buf += size_insert;
674 }
675 }
676
677 if (crossfade_fade_in_rem == 0)
678 crossfade_active = false;
679
680 if (length > 0)
681 pcmbuf_flush_buffer(buf, length);
Brandon Low413da2a2006-02-07 20:38:55 +0000682}
683
684static bool prepare_insert(size_t length)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000685{
Miika Pekkarinen4408b6b2006-02-07 19:17:51 +0000686 if (low_latency_mode)
687 {
688 /* 1/4s latency. */
689 if (pcmbuf_unplayed_bytes > NATIVE_FREQUENCY * 4 / 4
690 && pcm_is_playing())
691 return false;
692 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000693
Brandon Low3bbd93b2006-02-13 17:08:53 +0000694 /* Need to save PCMBUF_MIN_CHUNK to prevent wrapping overwriting */
Brandon Low6c0908b2006-04-23 22:54:34 +0000695 if (pcmbuf_free() < length + PCMBUF_MIN_CHUNK && !crossfade_active)
Brandon Low413da2a2006-02-07 20:38:55 +0000696 {
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000697 pcmbuf_boost(false);
698 return false;
699 }
Miika Pekkarinend83b6592005-07-19 19:57:23 +0000700
Brandon Low413da2a2006-02-07 20:38:55 +0000701 if (!pcm_is_playing())
702 {
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000703 pcmbuf_boost(true);
704 crossfade_active = false;
Miika Pekkarinen4408b6b2006-02-07 19:17:51 +0000705 /* Pre-buffer 1s. */
Brandon Low413da2a2006-02-07 20:38:55 +0000706 if (!LOW_DATA(4))
707 {
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000708 logf("pcm starting");
Miika Pekkarinen6d6ca6b2006-01-22 10:25:07 +0000709 pcmbuf_play_start();
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000710 }
Brandon Low3a37fae2006-02-13 16:55:25 +0000711 } else if (pcmbuf_unplayed_bytes <= pcmbuf_watermark)
712 pcmbuf_under_watermark();
713
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000714 return true;
715}
716
Brandon Low413da2a2006-02-07 20:38:55 +0000717void* pcmbuf_request_buffer(size_t length, size_t *realsize)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000718{
Brandon Low37faaab2006-04-20 02:30:59 +0000719 if (crossfade_init)
720 crossfade_start();
721
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000722 if (crossfade_active) {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000723 *realsize = MIN(length, PCMBUF_MIX_CHUNK);
724 return fadebuf;
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000725 }
Brandon Low413da2a2006-02-07 20:38:55 +0000726 else
727 {
728 if(prepare_insert(length))
729 {
730 size_t audiobuffer_index = audiobuffer_pos + audiobuffer_fillpos;
Brandon Low3bbd93b2006-02-13 17:08:53 +0000731 *realsize = length;
732 if (pcmbuf_size - audiobuffer_index >= PCMBUF_MIN_CHUNK)
733 {
734 /* Usual case, there's space here */
735 return &audiobuffer[audiobuffer_index];
Brandon Low413da2a2006-02-07 20:38:55 +0000736 }
737 else
738 {
Brandon Low3bbd93b2006-02-13 17:08:53 +0000739 /* Flush and wrap the buffer */
740 pcmbuf_flush_fillpos();
741 audiobuffer_pos = 0;
742 return &audiobuffer[0];
Brandon Low413da2a2006-02-07 20:38:55 +0000743 }
744 }
745 else
746 {
747 *realsize = 0;
748 return NULL;
749 }
750 }
751}
752
753void* pcmbuf_request_voice_buffer(size_t length, size_t *realsize, bool mix)
754{
755 if (mix)
756 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000757 if (pcmbuf_mix_chunk || pcmbuf_read->link)
758 {
759 *realsize = MIN(length, PCMBUF_MIX_CHUNK);
760 return voicebuf;
761 }
762 else
763 {
764 *realsize = 0;
765 return NULL;
766 }
Brandon Low413da2a2006-02-07 20:38:55 +0000767 }
768 else
769 return pcmbuf_request_buffer(length, realsize);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000770}
771
772bool pcmbuf_is_crossfade_active(void)
773{
774 return crossfade_active || crossfade_init;
775}
776
Brandon Low413da2a2006-02-07 20:38:55 +0000777void pcmbuf_write_complete(size_t length)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000778{
Brandon Low37faaab2006-04-20 02:30:59 +0000779 if (crossfade_active)
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000780 flush_crossfade(fadebuf, length);
Brandon Low413da2a2006-02-07 20:38:55 +0000781 else
782 {
Brandon Low413da2a2006-02-07 20:38:55 +0000783 audiobuffer_fillpos += length;
784
785 if (NEED_FLUSH(audiobuffer_pos + audiobuffer_fillpos))
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000786 pcmbuf_flush_fillpos();
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000787 }
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000788}
789
Brandon Low413da2a2006-02-07 20:38:55 +0000790bool pcmbuf_insert_buffer(const char *buf, size_t length)
791{
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000792 if (!prepare_insert(length))
793 return false;
794
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000795 if (crossfade_active) {
Brandon Low37faaab2006-04-20 02:30:59 +0000796 flush_crossfade(buf, length);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000797 }
Brandon Low413da2a2006-02-07 20:38:55 +0000798 else
799 {
800 pcmbuf_flush_buffer(buf, length);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000801 }
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000802 return true;
803}
804
Brandon Low9535a9a2006-02-22 01:56:44 +0000805/* Get a pointer to where to mix immediate audio */
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000806static inline short* get_mix_insert_buf(void) {
807 if (pcmbuf_read->link)
808 {
809 /* Get the next chunk */
810 char *pcmbuf_mix_buf = pcmbuf_read->link->addr;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000811
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000812 /* Give at least 1/8s clearance. TODO: Check size here? */
813 return (short *)&pcmbuf_mix_buf[NATIVE_FREQUENCY * 4 / 8];
814 }
815 return NULL;
Brandon Low9535a9a2006-02-22 01:56:44 +0000816}
817
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000818/* Generates a constant square wave sound with a given frequency
819 in Hertz for a duration in milliseconds. */
Brandon Low9535a9a2006-02-22 01:56:44 +0000820void pcmbuf_beep(unsigned int frequency, size_t duration, int amplitude)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000821{
Brandon Low9535a9a2006-02-22 01:56:44 +0000822 unsigned int count = 0, i = 0;
Brandon Low413da2a2006-02-07 20:38:55 +0000823 unsigned int interval = NATIVE_FREQUENCY / frequency;
Brandon Low0fcd4112006-04-05 13:00:31 +0000824 long sample;
Brandon Low9535a9a2006-02-22 01:56:44 +0000825 short *buf;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000826 short *pcmbuf_end = (short *)fadebuf;
Brandon Low9535a9a2006-02-22 01:56:44 +0000827 size_t samples = NATIVE_FREQUENCY / 1000 * duration;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000828
Brandon Low0fcd4112006-04-05 13:00:31 +0000829 if (pcm_is_playing())
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000830 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000831 buf = get_mix_insert_buf();
Brandon Low0fcd4112006-04-05 13:00:31 +0000832 while (i++ < samples)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000833 {
Brandon Low0fcd4112006-04-05 13:00:31 +0000834 sample = *buf;
835 *buf++ = MIN(MAX(sample + amplitude, -32768), 32767);
836 if (buf > pcmbuf_end)
837 buf = (short *)audiobuffer;
838 sample = *buf;
839 *buf++ = MIN(MAX(sample + amplitude, -32768), 32767);
840
841 /* Toggle square wav side */
842 if (++count >= interval)
843 {
844 count = 0;
845 amplitude = -amplitude;
846 }
847 if (buf > pcmbuf_end)
848 buf = (short *)audiobuffer;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000849 }
Brandon Low9535a9a2006-02-22 01:56:44 +0000850 }
Brandon Low0fcd4112006-04-05 13:00:31 +0000851 else
852 {
853 buf = (short *)audiobuffer;
854 while (i++ < samples)
855 {
856 *buf++ = amplitude;
857 if (buf > pcmbuf_end)
858 buf = (short *)audiobuffer;
859 *buf++ = amplitude;
860
Brandon Lowa1315802006-04-07 20:03:26 +0000861 /* Toggle square wav side */
Brandon Low0fcd4112006-04-05 13:00:31 +0000862 if (++count >= interval)
863 {
864 count = 0;
865 amplitude = -amplitude;
866 }
867 if (buf > pcmbuf_end)
868 buf = (short *)audiobuffer;
869 }
Brandon Low9535a9a2006-02-22 01:56:44 +0000870 pcm_play_data(NULL, (unsigned char *)audiobuffer, samples * 4);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000871 }
872}
873
874/* Returns pcm buffer usage in percents (0 to 100). */
875int pcmbuf_usage(void)
876{
877 return pcmbuf_unplayed_bytes * 100 / pcmbuf_size;
878}
879
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000880int pcmbuf_mix_free(void)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000881{
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000882 if (pcmbuf_mix_chunk)
883 {
884 size_t my_mix_end =
885 (size_t)&((short *)pcmbuf_mix_chunk->addr)[pcmbuf_mix_sample];
886 size_t my_write_pos = (size_t)&audiobuffer[audiobuffer_pos];
887 if (my_write_pos < my_mix_end)
888 my_write_pos += pcmbuf_size;
889 return (my_write_pos - my_mix_end) * 100 / pcmbuf_unplayed_bytes;
890 }
891 return 100;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000892}
893
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000894void pcmbuf_mix_voice(size_t length)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000895{
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000896 short *ibuf = (short *)voicebuf;
897 short *obuf;
898 size_t chunk_samples;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000899
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000900 if (!pcmbuf_mix_chunk && pcmbuf_read)
901 {
902 pcmbuf_mix_chunk = pcmbuf_read->link;
903 /* Start 1/8s into the next chunk */
904 pcmbuf_mix_sample = NATIVE_FREQUENCY * 4 / 16;
905 }
906 if (!pcmbuf_mix_chunk)
907 return;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000908
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000909 obuf = (short *)pcmbuf_mix_chunk->addr;
910 chunk_samples = pcmbuf_mix_chunk->size / 2;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000911
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000912 length /= 2;
913
914 while (length-- > 0) {
Brandon Low6c0908b2006-04-23 22:54:34 +0000915 int sample = *ibuf++;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000916 if (pcmbuf_mix_sample >= chunk_samples)
917 {
918 pcmbuf_mix_chunk = pcmbuf_mix_chunk->link;
919 if (!pcmbuf_mix_chunk)
920 return;
921 pcmbuf_mix_sample = 0;
922 obuf = pcmbuf_mix_chunk->addr;
923 chunk_samples = pcmbuf_mix_chunk->size / 2;
924 }
Brandon Low920516c2006-04-23 05:30:52 +0000925 sample += obuf[pcmbuf_mix_sample] >> 2;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000926 obuf[pcmbuf_mix_sample++] = MIN(MAX(sample, -32768), 32767);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000927 }
928}
929
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000930void pcmbuf_crossfade_enable(bool on_off)
931{
932 crossfade_enabled = on_off;
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000933
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000934 if (crossfade_enabled) {
Brandon Low413da2a2006-02-07 20:38:55 +0000935 /* If crossfading, try to keep the buffer full other than 2 second */
936 pcmbuf_set_watermark_bytes(pcmbuf_size - PCMBUF_WATERMARK * 2);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000937 } else {
Brandon Low413da2a2006-02-07 20:38:55 +0000938 /* Otherwise, just keep it above 1 second */
Brandon Lowa3868d32006-01-21 22:42:44 +0000939 pcmbuf_set_watermark_bytes(PCMBUF_WATERMARK);
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000940 }
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000941}
942
943bool pcmbuf_is_crossfade_enabled(void)
944{
Miika Pekkarinene7461b32005-11-06 16:40:20 +0000945 if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE)
946 return global_settings.playlist_shuffle;
947
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000948 return crossfade_enabled;
949}
950