blob: 11d266b92f3907c40dbadf46e1ab06a6015f8475 [file] [log] [blame]
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 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 ****************************************************************************/
Brandon Lowc95044a2006-04-13 14:17:12 +000019
20/* TODO: Check for a possibly broken codepath on a rapid skip, stop event */
Brandon Low83ce5682006-04-22 21:31:07 +000021/* TODO: same in reverse ^^ */
Brandon Lowd461a3e2006-04-23 23:27:11 +000022/* TODO: Also play, stop ^^ */
Brandon Low1d41f772006-04-13 21:47:00 +000023/* TODO: Can use the track changed callback to detect end of track and seek
24 * in the previous track until this happens */
25/* Design: we have prev_ti already, have a conditional for what type of seek
26 * to do on a seek request, if it is a previous track seek, skip previous,
27 * and in the request_next_track callback set the offset up the same way that
28 * starting from an offset works. */
Brandon Low363dbc42006-04-14 22:15:38 +000029/* This is also necesary to prevent the problem with buffer overwriting on
30 * automatic track changes */
Brandon Lowc95044a2006-04-13 14:17:12 +000031
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000032#include <stdio.h>
33#include <string.h>
34#include <stdlib.h>
35#include <ctype.h>
36
37#include "system.h"
38#include "thread.h"
39#include "file.h"
40#include "lcd.h"
41#include "font.h"
42#include "backlight.h"
43#include "button.h"
44#include "kernel.h"
45#include "tree.h"
46#include "debug.h"
47#include "sprintf.h"
48#include "settings.h"
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000049#include "codecs.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000050#include "audio.h"
51#include "logf.h"
52#include "mp3_playback.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000053#include "usb.h"
54#include "status.h"
55#include "main_menu.h"
56#include "ata.h"
57#include "screens.h"
58#include "playlist.h"
59#include "playback.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000060#include "pcmbuf.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000061#include "pcm_playback.h"
Linus Nielsen Feltzing735f8272005-12-20 21:49:47 +000062#include "pcm_record.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000063#include "buffer.h"
Miika Pekkarinend8cb7032005-06-26 19:41:29 +000064#include "dsp.h"
Brandon Low8d5a6602006-01-21 23:43:57 +000065#include "abrepeat.h"
Miika Pekkarinenb7251262006-03-26 16:37:18 +000066#include "tagcache.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000067#ifdef HAVE_LCD_BITMAP
68#include "icons.h"
69#include "peakmeter.h"
70#include "action.h"
71#endif
72#include "lang.h"
73#include "bookmark.h"
74#include "misc.h"
75#include "sound.h"
Dave Chapman3ad485b2005-06-14 22:27:57 +000076#include "metadata.h"
Miika Pekkarinen159c52d2005-08-20 11:13:19 +000077#include "talk.h"
Anton Oleynikov3dbb3a22005-11-13 10:57:35 +000078#ifdef CONFIG_TUNER
79#include "radio.h"
Anton Oleynikov3dbb3a22005-11-13 10:57:35 +000080#endif
Kevin Ferraree991bee2005-11-16 15:12:15 +000081#include "splash.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000082
Miika Pekkarinen159c52d2005-08-20 11:13:19 +000083static volatile bool audio_codec_loaded;
84static volatile bool voice_codec_loaded;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000085static volatile bool playing;
Brandon Low857db452006-04-06 04:07:06 +000086static volatile bool paused;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000087
Miika Pekkarinen159c52d2005-08-20 11:13:19 +000088#define CODEC_VORBIS "/.rockbox/codecs/vorbis.codec"
89#define CODEC_MPA_L3 "/.rockbox/codecs/mpa.codec"
90#define CODEC_FLAC "/.rockbox/codecs/flac.codec"
91#define CODEC_WAV "/.rockbox/codecs/wav.codec"
92#define CODEC_A52 "/.rockbox/codecs/a52.codec"
93#define CODEC_MPC "/.rockbox/codecs/mpc.codec"
94#define CODEC_WAVPACK "/.rockbox/codecs/wavpack.codec"
Dave Chapman139c1cb2005-09-22 21:55:37 +000095#define CODEC_ALAC "/.rockbox/codecs/alac.codec"
Dave Chapmancea6d0c2005-10-31 20:56:29 +000096#define CODEC_AAC "/.rockbox/codecs/aac.codec"
Dave Chapman2bf9be12005-11-11 19:45:36 +000097#define CODEC_SHN "/.rockbox/codecs/shorten.codec"
Dave Chapmanfbd8e5d2006-02-01 16:42:02 +000098#define CODEC_AIFF "/.rockbox/codecs/aiff.codec"
Dave Chapman752faa42006-07-18 18:33:12 +000099#define CODEC_SID "/.rockbox/codecs/sid.codec"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000100
Brandon Low62ccbbb2006-04-11 03:55:58 +0000101/* default point to start buffer refill */
Miika Pekkarinende3b04e2005-06-29 14:46:27 +0000102#define AUDIO_DEFAULT_WATERMARK (1024*512)
Brandon Low62ccbbb2006-04-11 03:55:58 +0000103/* amount of data to read in one read() call */
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +0000104#define AUDIO_DEFAULT_FILECHUNK (1024*32)
Brandon Low62ccbbb2006-04-11 03:55:58 +0000105/* point at which the file buffer will fight for CPU time */
Brandon Low930785c2006-04-06 16:21:31 +0000106#define AUDIO_FILEBUF_CRITICAL (1024*128)
Brandon Low62ccbbb2006-04-11 03:55:58 +0000107/* amount of guess-space to allow for codecs that must hunt and peck
108 * for their correct seeek target, 32k seems a good size */
109#define AUDIO_REBUFFER_GUESS_SIZE (1024*32)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000110
Miika Pekkarinend3191162006-01-27 11:39:46 +0000111enum {
112 Q_AUDIO_PLAY = 1,
113 Q_AUDIO_STOP,
114 Q_AUDIO_PAUSE,
Brandon Low27363632006-04-06 21:06:37 +0000115 Q_AUDIO_SKIP,
Brandon Lowab570252006-04-07 18:18:36 +0000116 Q_AUDIO_PRE_FF_REWIND,
Miika Pekkarinend3191162006-01-27 11:39:46 +0000117 Q_AUDIO_FF_REWIND,
Brandon Low72232bd2006-04-09 02:15:35 +0000118 Q_AUDIO_REBUFFER_SEEK,
Brandon Low62ccbbb2006-04-11 03:55:58 +0000119 Q_AUDIO_CHECK_NEW_TRACK,
Miika Pekkarinend3191162006-01-27 11:39:46 +0000120 Q_AUDIO_FLUSH,
121 Q_AUDIO_TRACK_CHANGED,
Brandon Low27363632006-04-06 21:06:37 +0000122 Q_AUDIO_DIR_SKIP,
Brandon Lowda1cddf2006-04-26 04:01:35 +0000123 Q_AUDIO_NEW_PLAYLIST,
Miika Pekkarinend3191162006-01-27 11:39:46 +0000124 Q_AUDIO_POSTINIT,
Brandon Low930785c2006-04-06 16:21:31 +0000125 Q_AUDIO_FILL_BUFFER,
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000126
Brandon Low4b360962006-04-19 00:23:08 +0000127 Q_CODEC_REQUEST_PENDING,
Brandon Lowfd084242006-04-14 13:05:08 +0000128 Q_CODEC_REQUEST_COMPLETE,
Brandon Low0291a6e2006-04-14 14:03:43 +0000129 Q_CODEC_REQUEST_FAILED,
Brandon Lowfd084242006-04-14 13:05:08 +0000130
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000131 Q_VOICE_PLAY,
132 Q_VOICE_STOP,
133
Miika Pekkarinend3191162006-01-27 11:39:46 +0000134 Q_CODEC_LOAD,
135 Q_CODEC_LOAD_DISK,
136};
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000137
138/* As defined in plugins/lib/xxx2wav.h */
139#define MALLOC_BUFSIZE (512*1024)
Miika Pekkarinena4c190f2005-10-30 09:30:14 +0000140#define GUARD_BUFSIZE (32*1024)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000141
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000142/* As defined in plugin.lds */
Thom Johansena7b5a2c2006-03-22 15:19:59 +0000143#if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002
144#define CODEC_IRAM_ORIGIN 0x4000c000
145#else
Jens Arnolda317d742005-09-01 20:57:33 +0000146#define CODEC_IRAM_ORIGIN 0x1000c000
Thom Johansena7b5a2c2006-03-22 15:19:59 +0000147#endif
Jens Arnolda317d742005-09-01 20:57:33 +0000148#define CODEC_IRAM_SIZE 0xc000
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000149
Hardeep Sidhub8d1a552006-05-01 18:18:54 +0000150#ifndef SIMULATOR
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000151extern bool audio_is_initialized;
Hardeep Sidhub8d1a552006-05-01 18:18:54 +0000152#else
153static bool audio_is_initialized = false;
154#endif
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000155
156/* Buffer control thread. */
157static struct event_queue audio_queue;
158static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
159static const char audio_thread_name[] = "audio";
160
161/* Codec thread. */
162static struct event_queue codec_queue;
Brandon Low87484fc2006-04-11 20:41:04 +0000163static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
164IBSS_ATTR;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000165static const char codec_thread_name[] = "codec";
166
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000167/* Voice codec thread. */
168static struct event_queue voice_codec_queue;
Brandon Low87484fc2006-04-11 20:41:04 +0000169static long voice_codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
170IBSS_ATTR;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000171static const char voice_codec_thread_name[] = "voice codec";
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000172struct voice_info {
173 void (*callback)(unsigned char **start, int *size);
174 int size;
175 char *buf;
176};
177
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000178
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000179static struct mutex mutex_codecthread;
Brandon Lowfd084242006-04-14 13:05:08 +0000180static struct event_queue codec_callback_queue;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000181
182static struct mp3entry id3_voice;
183
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000184static char *voicebuf;
Brandon Low86f1e2e2006-03-24 13:43:15 +0000185static size_t voice_remaining;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000186static bool voice_is_playing;
187static void (*voice_getmore)(unsigned char** start, int* size);
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000188static int voice_thread_num = -1;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000189
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000190/* Is file buffer currently being refilled? */
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000191static volatile bool filling IDATA_ATTR;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000192
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000193volatile int current_codec IDATA_ATTR;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000194extern unsigned char codecbuf[];
195
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000196/* Ring buffer where tracks and codecs are loaded. */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000197static char *filebuf;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000198
199/* Total size of the ring buffer. */
Brandon Low86f1e2e2006-03-24 13:43:15 +0000200size_t filebuflen;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000201
202/* Bytes available in the buffer. */
Brandon Low86f1e2e2006-03-24 13:43:15 +0000203size_t filebufused;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000204
205/* Ring buffer read and write indexes. */
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000206static volatile size_t buf_ridx IDATA_ATTR;
207static volatile size_t buf_widx IDATA_ATTR;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000208
Brandon Lowa553d5f2006-03-23 16:18:17 +0000209#ifndef SIMULATOR
210static unsigned char *iram_buf[2];
211#endif
212static unsigned char *dram_buf[2];
213
Miika Pekkarinen34a25a62005-07-15 16:42:01 +0000214/* Step count to the next unbuffered track. */
Miika Pekkarinen431e8132005-06-19 18:41:53 +0000215static int last_peek_offset;
216
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000217/* Track information (count in file buffer, read/write indexes for
218 track ring structure. */
Brandon Lowc95044a2006-04-13 14:17:12 +0000219static int track_ridx;
220static int track_widx;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000221static bool track_changed;
222
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000223/* Partially loaded song's file handle to continue buffering later. */
224static int current_fd;
225
226/* Information about how many bytes left on the buffer re-fill run. */
Brandon Low86f1e2e2006-03-24 13:43:15 +0000227static size_t fill_bytesleft;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000228
229/* Track info structure about songs in the file buffer. */
230static struct track_info tracks[MAX_TRACK];
231
232/* Pointer to track info structure about current song playing. */
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +0000233static struct track_info *cur_ti;
Brandon Lowa3868d32006-01-21 22:42:44 +0000234static struct track_info *prev_ti;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000235
Miika Pekkarinen999f89c2005-12-03 12:14:26 +0000236/* Have we reached end of the current playlist. */
237static bool playlist_end = false;
238
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000239/* Codec API including function callbacks. */
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000240extern struct codec_api ci;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000241extern struct codec_api ci_voice;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000242
Brandon Lowebadcc62006-04-15 02:03:11 +0000243/* Was the skip being executed manual or automatic? */
Brandon Low5cdee942006-04-23 22:30:52 +0000244static bool automatic_skip;
Brandon Low2b187272006-04-18 18:33:09 +0000245static bool dir_skip = false;
Brandon Lowda1cddf2006-04-26 04:01:35 +0000246static bool new_playlist = false;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000247
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +0000248/* Callback function to call when current track has really changed. */
Jens Arnolda88d0762005-08-18 06:05:15 +0000249void (*track_changed_callback)(struct mp3entry *id3);
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000250void (*track_buffer_callback)(struct mp3entry *id3, bool last_track);
Miika Pekkarinen9bde0382005-07-03 18:36:24 +0000251void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track);
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +0000252
Miika Pekkarinend3191162006-01-27 11:39:46 +0000253static void playback_init(void);
254
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +0000255/* Configuration */
Brandon Low86f1e2e2006-03-24 13:43:15 +0000256static size_t conf_watermark;
257static size_t conf_filechunk;
258static size_t buffer_margin;
Miika Pekkarinen2326bea2005-06-10 13:43:12 +0000259
Linus Nielsen Feltzing4aaa3212005-06-06 00:35:21 +0000260static bool v1first = false;
261
Miika Pekkarinen5899ed52005-06-08 10:33:01 +0000262static void mp3_set_elapsed(struct mp3entry* id3);
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000263static int mp3_get_file_pos(void);
Miika Pekkarinen5899ed52005-06-08 10:33:01 +0000264
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000265static void audio_clear_track_entries(
Miika Pekkarinenbb5330c2006-08-03 07:45:53 +0000266 bool clear_buffered, bool clear_unbuffered, bool may_yield);
Miika Pekkarinen8e0b02a2006-08-04 10:29:04 +0000267static bool initialize_buffer_fill(bool clear_tracks);
Brandon Lowda1cddf2006-04-26 04:01:35 +0000268static void audio_fill_file_buffer(
269 bool start_play, bool rebuffer, size_t offset);
Brandon Low4f3bb2d2006-04-09 15:30:28 +0000270
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000271static void swap_codec(void)
272{
Brandon Lowa553d5f2006-03-23 16:18:17 +0000273 int my_codec = current_codec;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000274
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000275 logf("swapping out codec:%d", my_codec);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000276
Brandon Lowa553d5f2006-03-23 16:18:17 +0000277 /* Save our current IRAM and DRAM */
278#ifndef SIMULATOR
279 memcpy(iram_buf[my_codec], (unsigned char *)CODEC_IRAM_ORIGIN,
280 CODEC_IRAM_SIZE);
281#endif
282 memcpy(dram_buf[my_codec], codecbuf, CODEC_SIZE);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000283
Brandon Lowa553d5f2006-03-23 16:18:17 +0000284 do {
285 /* Release my semaphore and force a task switch. */
286 mutex_unlock(&mutex_codecthread);
287 yield();
288 mutex_lock(&mutex_codecthread);
289 /* Loop until the other codec has locked and run */
290 } while (my_codec == current_codec);
291 current_codec = my_codec;
292
293 /* Reload our IRAM and DRAM */
294#ifndef SIMULATOR
295 memcpy((unsigned char *)CODEC_IRAM_ORIGIN, iram_buf[my_codec],
296 CODEC_IRAM_SIZE);
297#endif
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000298 invalidate_icache();
Brandon Lowa553d5f2006-03-23 16:18:17 +0000299 memcpy(codecbuf, dram_buf[my_codec], CODEC_SIZE);
300
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000301 logf("resuming codec:%d", my_codec);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000302}
303
Miika Pekkarinen65d43a22005-08-28 19:55:30 +0000304#ifdef HAVE_ADJUSTABLE_CPU_FREQ
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000305static void voice_boost_cpu(bool state)
306{
Miika Pekkarinen65d43a22005-08-28 19:55:30 +0000307 static bool voice_cpu_boosted = false;
Miika Pekkarinen731b22e2005-11-19 14:04:07 +0000308
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000309 if (state != voice_cpu_boosted)
310 {
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000311 cpu_boost(state);
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000312 voice_cpu_boosted = state;
313 }
314}
Miika Pekkarinen65d43a22005-08-28 19:55:30 +0000315#else
316#define voice_boost_cpu(state) do { } while(0)
317#endif
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000318
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000319static bool voice_pcmbuf_insert_split_callback(
320 const void *ch1, const void *ch2, size_t length)
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000321{
Brandon Low413da2a2006-02-07 20:38:55 +0000322 const char* src[2];
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000323 char *dest;
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000324 long input_size;
Brandon Low413da2a2006-02-07 20:38:55 +0000325 size_t output_size;
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000326
327 src[0] = ch1;
328 src[1] = ch2;
329
330 if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000331 length *= 2; /* Length is per channel */
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000332
Brandon Low522ec272006-04-25 18:21:05 +0000333 while (length)
334 {
Brandon Low413da2a2006-02-07 20:38:55 +0000335 long est_output_size = dsp_output_size(length);
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000336
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000337 while ((dest = pcmbuf_request_voice_buffer(est_output_size,
338 &output_size, playing)) == NULL)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000339 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000340 if (playing)
341 swap_codec();
342 else
343 yield();
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000344 }
345
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000346 /* Get the real input_size for output_size bytes, guarding
347 * against resampling buffer overflows. */
348 input_size = dsp_input_size(output_size);
349
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000350 if (input_size <= 0)
351 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000352 DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld<=0\n",
353 output_size, length, input_size);
354 /* If this happens, there are samples of codec data that don't
355 * become a number of pcm samples, and something is broken */
356 return false;
357 }
358
359 /* Input size has grown, no error, just don't write more than length */
360 if ((size_t)input_size > length)
361 input_size = length;
362
363 output_size = dsp_process(dest, src, input_size);
364
365 if (playing)
366 {
367 pcmbuf_mix_voice(output_size);
368 if (pcmbuf_usage() < 10 || pcmbuf_mix_free() < 30)
369 swap_codec();
Brandon Low413da2a2006-02-07 20:38:55 +0000370 }
371 else
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000372 pcmbuf_write_complete(output_size);
Brandon Low413da2a2006-02-07 20:38:55 +0000373
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000374 length -= input_size;
Brandon Low522ec272006-04-25 18:21:05 +0000375 }
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000376
377 return true;
378}
379
380static bool codec_pcmbuf_insert_split_callback(
381 const void *ch1, const void *ch2, size_t length)
382{
383 const char* src[2];
384 char *dest;
385 long input_size;
386 size_t output_size;
387
388 src[0] = ch1;
389 src[1] = ch2;
390
391 if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
392 length *= 2; /* Length is per channel */
393
Brandon Low522ec272006-04-25 18:21:05 +0000394 while (length)
395 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000396 long est_output_size = dsp_output_size(length);
397 /* Prevent audio from a previous track from playing */
398 if (ci.new_track || ci.stop_codec)
399 return true;
400
401 while ((dest = pcmbuf_request_buffer(est_output_size,
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000402 &output_size)) == NULL)
403 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000404 sleep(1);
405 if (ci.seek_time || ci.new_track || ci.stop_codec)
406 return true;
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000407 }
408
Linus Nielsen Feltzing591d2892005-08-10 23:17:55 +0000409 /* Get the real input_size for output_size bytes, guarding
410 * against resampling buffer overflows. */
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000411 input_size = dsp_input_size(output_size);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000412
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000413 if (input_size <= 0)
414 {
Brandon Low87484fc2006-04-11 20:41:04 +0000415 DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld<=0\n",
416 output_size, length, input_size);
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000417 /* If this happens, there are samples of codec data that don't
418 * become a number of pcm samples, and something is broken */
419 return false;
Brandon Low413da2a2006-02-07 20:38:55 +0000420 }
421
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000422 /* Input size has grown, no error, just don't write more than length */
423 if ((size_t)input_size > length)
Linus Nielsen Feltzing591d2892005-08-10 23:17:55 +0000424 input_size = length;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000425
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000426 output_size = dsp_process(dest, src, input_size);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000427
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000428 pcmbuf_write_complete(output_size);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000429
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000430 if (voice_is_playing && pcm_is_playing() &&
431 pcmbuf_usage() > 30 && pcmbuf_mix_free() > 80)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000432 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000433 swap_codec();
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000434 }
435
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000436 length -= input_size;
Brandon Low522ec272006-04-25 18:21:05 +0000437 }
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000438
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000439 return true;
440}
441
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000442static bool voice_pcmbuf_insert_callback(const char *buf, size_t length)
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000443{
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000444 /* TODO: The audiobuffer API should probably be updated, and be based on
Brandon Low87484fc2006-04-11 20:41:04 +0000445 * pcmbuf_insert_split(). */
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000446 long real_length = length;
447
448 if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000449 length /= 2; /* Length is per channel */
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000450
451 /* Second channel is only used for non-interleaved stereo. */
452 return voice_pcmbuf_insert_split_callback(buf, buf + (real_length / 2),
453 length);
454}
455
456static bool codec_pcmbuf_insert_callback(const char *buf, size_t length)
457{
458 /* TODO: The audiobuffer API should probably be updated, and be based on
459 * pcmbuf_insert_split(). */
460 long real_length = length;
461
462 if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
463 length /= 2; /* Length is per channel */
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000464
465 /* Second channel is only used for non-interleaved stereo. */
466 return codec_pcmbuf_insert_split_callback(buf, buf + (real_length / 2),
467 length);
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000468}
469
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000470static void* get_voice_memory_callback(size_t *size)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000471{
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000472 *size = 0;
473 return NULL;
474}
Brandon Low98097d22006-04-17 17:05:05 +0000475
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000476static void* get_codec_memory_callback(size_t *size)
477{
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000478 *size = MALLOC_BUFSIZE;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000479 if (voice_codec_loaded)
480 return &audiobuf[talk_get_bufsize()];
Brandon Low98097d22006-04-17 17:05:05 +0000481 else
482 return audiobuf;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000483}
484
Brandon Low413da2a2006-02-07 20:38:55 +0000485static void pcmbuf_position_callback(size_t size) ICODE_ATTR;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000486static void pcmbuf_position_callback(size_t size)
487{
Brandon Low413da2a2006-02-07 20:38:55 +0000488 unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
489 prev_ti->id3.elapsed;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000490
491 if (time >= prev_ti->id3.length)
492 {
Brandon Lowa3868d32006-01-21 22:42:44 +0000493 pcmbuf_set_position_callback(NULL);
Brandon Low998610c2006-01-22 04:35:28 +0000494 prev_ti->id3.elapsed = prev_ti->id3.length;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000495 }
496 else
Brandon Lowa3868d32006-01-21 22:42:44 +0000497 prev_ti->id3.elapsed = time;
Brandon Lowa3868d32006-01-21 22:42:44 +0000498}
499
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000500static void voice_set_elapsed_callback(unsigned int value)
501{
502 (void)value;
503}
504
505static void codec_set_elapsed_callback(unsigned int value)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000506{
507 unsigned int latency;
Brandon Lowd461a3e2006-04-23 23:27:11 +0000508 if (ci.seek_time)
509 return;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000510
Brandon Low8d5a6602006-01-21 23:43:57 +0000511#ifdef AB_REPEAT_ENABLE
512 ab_position_report(value);
513#endif
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000514
Brandon Lowd461a3e2006-04-23 23:27:11 +0000515 latency = pcmbuf_get_latency();
Brandon Low87484fc2006-04-11 20:41:04 +0000516 if (value < latency)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000517 cur_ti->id3.elapsed = 0;
Brandon Low87484fc2006-04-11 20:41:04 +0000518 else if (value - latency > cur_ti->id3.elapsed ||
519 value - latency < cur_ti->id3.elapsed - 2)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000520 {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000521 cur_ti->id3.elapsed = value - latency;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000522 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000523}
524
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000525static void voice_set_offset_callback(size_t value)
Ryan Jacksond1917562005-07-12 16:45:38 +0000526{
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000527 (void)value;
528}
Ryan Jacksond1917562005-07-12 16:45:38 +0000529
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000530static void codec_set_offset_callback(size_t value)
531{
Brandon Lowd461a3e2006-04-23 23:27:11 +0000532 unsigned int latency;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000533
Brandon Lowd461a3e2006-04-23 23:27:11 +0000534 if (ci.seek_time)
535 return;
536
537 latency = pcmbuf_get_latency() * cur_ti->id3.bitrate / 8;
Brandon Low87484fc2006-04-11 20:41:04 +0000538 if (value < latency)
Ryan Jacksond1917562005-07-12 16:45:38 +0000539 cur_ti->id3.offset = 0;
Brandon Low87484fc2006-04-11 20:41:04 +0000540 else
Ryan Jacksond1917562005-07-12 16:45:38 +0000541 cur_ti->id3.offset = value - latency;
Ryan Jacksond1917562005-07-12 16:45:38 +0000542}
543
Brandon Low930785c2006-04-06 16:21:31 +0000544static bool filebuf_is_lowdata(void)
545{
546 return filebufused < AUDIO_FILEBUF_CRITICAL;
547}
548
Brandon Low72232bd2006-04-09 02:15:35 +0000549static bool have_tracks(void)
550{
Brandon Low54af3042006-04-12 02:01:26 +0000551 return track_ridx != track_widx || cur_ti->filesize;
Brandon Low72232bd2006-04-09 02:15:35 +0000552}
553
554static bool have_free_tracks(void)
555{
556 if (track_widx < track_ridx)
557 return track_widx + 1 < track_ridx;
558 else if (track_ridx == 0)
559 return track_widx < MAX_TRACK - 1;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000560
561 return true;
Brandon Low72232bd2006-04-09 02:15:35 +0000562}
563
564int audio_track_count(void)
565{
Brandon Low72232bd2006-04-09 02:15:35 +0000566 if (have_tracks())
567 {
Brandon Lowc95044a2006-04-13 14:17:12 +0000568 int relative_track_widx = track_widx;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000569
Brandon Lowc95044a2006-04-13 14:17:12 +0000570 if (track_ridx > track_widx)
571 relative_track_widx += MAX_TRACK;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000572
Brandon Lowc95044a2006-04-13 14:17:12 +0000573 return relative_track_widx - track_ridx + 1;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000574 }
575
576 return 0;
Brandon Low72232bd2006-04-09 02:15:35 +0000577}
578
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000579static void advance_buffer_counters(size_t amount)
580{
Brandon Low86f1e2e2006-03-24 13:43:15 +0000581 buf_ridx += amount;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000582
Brandon Low86f1e2e2006-03-24 13:43:15 +0000583 if (buf_ridx >= filebuflen)
584 buf_ridx -= filebuflen;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000585
Brandon Low86f1e2e2006-03-24 13:43:15 +0000586 ci.curpos += amount;
587 cur_ti->available -= amount;
588 filebufused -= amount;
Brandon Low857db452006-04-06 04:07:06 +0000589
Brandon Low930785c2006-04-06 16:21:31 +0000590 /* Start buffer filling as necessary. */
591 if (!pcmbuf_is_lowdata() && !filling)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000592 {
Brandon Low930785c2006-04-06 16:21:31 +0000593 if (conf_watermark && filebufused <= conf_watermark && playing)
594 queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000595 }
Brandon Low86f1e2e2006-03-24 13:43:15 +0000596}
597
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000598static size_t voice_filebuf_callback(void *ptr, size_t size)
599{
600 (void)ptr;
601 (void)size;
602
603 return 0;
604}
605
Brandon Low86f1e2e2006-03-24 13:43:15 +0000606/* copy up-to size bytes into ptr and return the actual size copied */
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000607static size_t codec_filebuf_callback(void *ptr, size_t size)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000608{
609 char *buf = (char *)ptr;
Brandon Low86f1e2e2006-03-24 13:43:15 +0000610 size_t copy_n;
611 size_t part_n;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000612
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000613 if (ci.stop_codec || !playing)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000614 return 0;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000615
Brandon Low86f1e2e2006-03-24 13:43:15 +0000616 /* The ammount to copy is the lesser of the requested amount and the
617 * amount left of the current track (both on disk and already loaded) */
618 copy_n = MIN(size, cur_ti->available + cur_ti->filerem);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000619
Brandon Low86f1e2e2006-03-24 13:43:15 +0000620 /* Nothing requested OR nothing left */
621 if (copy_n == 0)
622 return 0;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000623
Brandon Low86f1e2e2006-03-24 13:43:15 +0000624 /* Let the disk buffer catch fill until enough data is available */
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000625 while (copy_n > cur_ti->available)
626 {
Brandon Lowbf397b52006-04-15 09:19:49 +0000627 if (!filling)
628 queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000629
Brandon Lowbf397b52006-04-15 09:19:49 +0000630 sleep(1);
Brandon Lowebadcc62006-04-15 02:03:11 +0000631 if (ci.stop_codec || ci.new_track)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000632 return 0;
633 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000634
Brandon Low86f1e2e2006-03-24 13:43:15 +0000635 /* Copy as much as possible without wrapping */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000636 part_n = MIN(copy_n, filebuflen - buf_ridx);
637 memcpy(buf, &filebuf[buf_ridx], part_n);
Brandon Low86f1e2e2006-03-24 13:43:15 +0000638 /* Copy the rest in the case of a wrap */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000639 if (part_n < copy_n) {
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000640 memcpy(&buf[part_n], &filebuf[0], copy_n - part_n);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000641 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000642
Brandon Low86f1e2e2006-03-24 13:43:15 +0000643 /* Update read and other position pointers */
644 advance_buffer_counters(copy_n);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000645
Brandon Low86f1e2e2006-03-24 13:43:15 +0000646 /* Return the actual amount of data copied to the buffer */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000647 return copy_n;
648}
649
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000650static void* voice_request_buffer_callback(size_t *realsize, size_t reqsize)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000651{
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000652 struct event ev;
653
654 if (ci_voice.new_track)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000655 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000656 *realsize = 0;
657 return NULL;
658 }
659
660 while (1)
661 {
662 if (voice_is_playing)
663 queue_wait_w_tmo(&voice_codec_queue, &ev, 0);
664 else if (playing)
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000665 {
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000666 queue_wait_w_tmo(&voice_codec_queue, &ev, 0);
667 if (ev.id == SYS_TIMEOUT)
668 ev.id = Q_AUDIO_PLAY;
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000669 }
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000670 else
671 queue_wait(&voice_codec_queue, &ev);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000672
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000673 switch (ev.id) {
674 case Q_AUDIO_PLAY:
Hardeep Sidhua02fd1a2006-06-17 11:21:22 +0000675 if (playing)
676 swap_codec();
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000677 break;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000678
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000679 case Q_VOICE_STOP:
680 if (voice_is_playing)
681 {
682 /* Clear the current buffer */
683 voice_is_playing = false;
684 voice_getmore = NULL;
685 voice_remaining = 0;
686 voicebuf = NULL;
687 voice_boost_cpu(false);
688 ci_voice.new_track = 1;
689 /* Force the codec to think it's changing tracks */
690 *realsize = 0;
691 return NULL;
692 }
693 else
694 break;
695
696 case SYS_USB_CONNECTED:
Miika Pekkarinenb591bb32006-08-05 07:29:53 +0000697 logf("USB: Voice codec");
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000698 usb_acknowledge(SYS_USB_CONNECTED_ACK);
699 if (audio_codec_loaded)
700 swap_codec();
701 usb_wait_for_disconnect(&voice_codec_queue);
702 break;
703
704 case Q_VOICE_PLAY:
705 {
706 struct voice_info *voice_data;
707 voice_is_playing = true;
708 voice_boost_cpu(true);
709 voice_data = ev.data;
710 voice_remaining = voice_data->size;
711 voicebuf = voice_data->buf;
712 voice_getmore = voice_data->callback;
713 }
714 case SYS_TIMEOUT:
715 goto voice_play_clip;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000716 }
717 }
718
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000719voice_play_clip:
720
721 if (voice_remaining == 0 || voicebuf == NULL)
722 {
723 if (voice_getmore)
724 voice_getmore((unsigned char **)&voicebuf, (int *)&voice_remaining);
725
726 /* If this clip is done */
727 if (!voice_remaining)
728 {
729 queue_post(&voice_codec_queue, Q_VOICE_STOP, 0);
730 /* Force pcm playback. */
731 if (!pcm_is_playing())
732 pcmbuf_play_start();
733 }
734 }
735
Brandon Low86f1e2e2006-03-24 13:43:15 +0000736 *realsize = MIN(voice_remaining, reqsize);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000737
738 if (*realsize == 0)
739 return NULL;
740
741 return voicebuf;
742}
743
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000744static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000745{
Brandon Low86f1e2e2006-03-24 13:43:15 +0000746 size_t short_n, copy_n, buf_rem;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000747
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000748 if (!playing)
749 {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000750 *realsize = 0;
751 return NULL;
752 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000753
Brandon Low86f1e2e2006-03-24 13:43:15 +0000754 copy_n = MIN(reqsize, cur_ti->available + cur_ti->filerem);
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000755 if (copy_n == 0)
756 {
Brandon Low86f1e2e2006-03-24 13:43:15 +0000757 *realsize = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000758 return NULL;
759 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000760
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000761 while (copy_n > cur_ti->available)
762 {
Brandon Lowbf397b52006-04-15 09:19:49 +0000763 if (!filling)
764 queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000765
Brandon Lowbf397b52006-04-15 09:19:49 +0000766 sleep(1);
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000767 if (ci.stop_codec || ci.new_track)
768 {
Miika Pekkarinen9ff373c2005-06-10 20:29:35 +0000769 *realsize = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000770 return NULL;
771 }
772 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000773
Brandon Low86f1e2e2006-03-24 13:43:15 +0000774 /* How much is left at the end of the file buffer before wrap? */
775 buf_rem = filebuflen - buf_ridx;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000776
Brandon Low86f1e2e2006-03-24 13:43:15 +0000777 /* If we can't satisfy the request without wrapping */
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000778 if (buf_rem < copy_n)
779 {
Brandon Low86f1e2e2006-03-24 13:43:15 +0000780 /* How short are we? */
781 short_n = copy_n - buf_rem;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000782
Brandon Low86f1e2e2006-03-24 13:43:15 +0000783 /* If we can fudge it with the guardbuf */
784 if (short_n < GUARD_BUFSIZE)
785 memcpy(&filebuf[filebuflen], &filebuf[0], short_n);
786 else
787 copy_n = buf_rem;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000788 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000789
Brandon Low86f1e2e2006-03-24 13:43:15 +0000790 *realsize = copy_n;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000791
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000792 return (char *)&filebuf[buf_ridx];
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000793}
794
Brandon Low62ccbbb2006-04-11 03:55:58 +0000795static int get_codec_base_type(int type)
796{
797 switch (type) {
798 case AFMT_MPA_L1:
799 case AFMT_MPA_L2:
800 case AFMT_MPA_L3:
801 return AFMT_MPA_L3;
802 }
803
804 return type;
805}
806
Brandon Lowc95044a2006-04-13 14:17:12 +0000807/* Count the data BETWEEN the selected tracks */
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000808static size_t buffer_count_tracks(int from_track, int to_track)
809{
Brandon Lowc95044a2006-04-13 14:17:12 +0000810 size_t amount = 0;
811 bool need_wrap = to_track < from_track;
812
Brandon Lowfbe74d12006-04-14 12:53:29 +0000813 while (1)
Brandon Lowc95044a2006-04-13 14:17:12 +0000814 {
Brandon Lowc95044a2006-04-13 14:17:12 +0000815 if (++from_track >= MAX_TRACK)
816 {
817 from_track -= MAX_TRACK;
818 need_wrap = false;
819 }
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000820
Brandon Low530cad32006-04-14 17:48:01 +0000821 if (from_track >= to_track && !need_wrap)
Brandon Lowfbe74d12006-04-14 12:53:29 +0000822 break;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000823
Brandon Lowfbe74d12006-04-14 12:53:29 +0000824 amount += tracks[from_track].codecsize + tracks[from_track].filesize;
Brandon Lowc95044a2006-04-13 14:17:12 +0000825 }
826 return amount;
827}
828
Brandon Low98097d22006-04-17 17:05:05 +0000829static bool buffer_wind_forward(int new_track_ridx, int old_track_ridx)
Brandon Lowc95044a2006-04-13 14:17:12 +0000830{
831 size_t amount;
832
Brandon Lowc95044a2006-04-13 14:17:12 +0000833 /* Start with the remainder of the previously playing track */
834 amount = tracks[old_track_ridx].filesize - ci.curpos;
835 /* Then collect all data from tracks in between them */
836 amount += buffer_count_tracks(old_track_ridx, new_track_ridx);
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000837
Brandon Low7c986a92006-04-14 17:31:19 +0000838 if (amount > filebufused)
839 return false;
840
Brandon Low98097d22006-04-17 17:05:05 +0000841 logf("bwf:%ldB",amount);
842
Brandon Lowc95044a2006-04-13 14:17:12 +0000843 /* Wind the buffer to the beginning of the target track or its codec */
844 buf_ridx += amount;
845 filebufused -= amount;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000846
Brandon Lowc95044a2006-04-13 14:17:12 +0000847 /* Check and handle buffer wrapping */
848 if (buf_ridx >= filebuflen)
849 buf_ridx -= filebuflen;
850
851 return true;
852}
853
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000854static bool buffer_wind_backward(int new_track_ridx, int old_track_ridx)
855{
Brandon Lowc95044a2006-04-13 14:17:12 +0000856 /* Available buffer data */
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000857 size_t buf_back;
858 /* Start with the previously playing track's data and our data */
859 size_t amount;
860
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000861 buf_back = buf_ridx;
862 amount = ci.curpos;
Brandon Lowc95044a2006-04-13 14:17:12 +0000863 if (buf_ridx < buf_widx)
864 buf_back += filebuflen;
865 buf_back -= buf_widx;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000866
Brandon Low0744e762006-04-13 17:30:54 +0000867 /* If we're not just resetting the current track */
Brandon Lowc95044a2006-04-13 14:17:12 +0000868 if (new_track_ridx != old_track_ridx)
869 {
870 /* Need to wind to before the old track's codec and our filesize */
871 amount += tracks[old_track_ridx].codecsize;
872 amount += tracks[new_track_ridx].filesize;
873
874 /* Rewind the old track to its beginning */
875 tracks[old_track_ridx].available =
876 tracks[old_track_ridx].filesize - tracks[old_track_ridx].filerem;
877 }
878
Brandon Low98097d22006-04-17 17:05:05 +0000879 /* If the codec was ever buffered */
880 if (tracks[new_track_ridx].codecsize)
Brandon Lowc95044a2006-04-13 14:17:12 +0000881 {
Brandon Lowc95044a2006-04-13 14:17:12 +0000882 /* Add the codec to the needed size */
883 amount += tracks[new_track_ridx].codecsize;
884 tracks[new_track_ridx].has_codec = true;
885 }
886
887 /* Then collect all data from tracks between new and old */
888 amount += buffer_count_tracks(new_track_ridx, old_track_ridx);
889
890 /* Do we have space to make this skip? */
891 if (amount > buf_back)
892 return false;
893
Brandon Low0744e762006-04-13 17:30:54 +0000894 logf("bwb:%ldB",amount);
895
Brandon Lowc95044a2006-04-13 14:17:12 +0000896 /* Check and handle buffer wrapping */
897 if (amount > buf_ridx)
898 buf_ridx += filebuflen;
899 /* Rewind the buffer to the beginning of the target track or its codec */
900 buf_ridx -= amount;
901 filebufused += amount;
902
903 /* Reset to the beginning of the new track */
904 tracks[new_track_ridx].available = tracks[new_track_ridx].filesize;
905
906 return true;
907}
908
Brandon Low1d41f772006-04-13 21:47:00 +0000909static void audio_update_trackinfo(void)
Brandon Lowc95044a2006-04-13 14:17:12 +0000910{
Brandon Low1d41f772006-04-13 21:47:00 +0000911 ci.filesize = cur_ti->filesize;
912 cur_ti->id3.elapsed = 0;
913 cur_ti->id3.offset = 0;
914 ci.id3 = &cur_ti->id3;
Brandon Lowc95044a2006-04-13 14:17:12 +0000915 ci.curpos = 0;
Brandon Low1d41f772006-04-13 21:47:00 +0000916 ci.taginfo_ready = &cur_ti->taginfo_ready;
Brandon Lowc95044a2006-04-13 14:17:12 +0000917}
918
Brandon Low62ccbbb2006-04-11 03:55:58 +0000919static void audio_rebuffer(void)
920{
921 logf("Forcing rebuffer");
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000922
Brandon Low4b360962006-04-19 00:23:08 +0000923 /* Notify the codec that this will take a while */
Miika Pekkarinen513cafe2006-07-31 06:12:53 +0000924 /* Currently this can cause some problems (logf in reverse order):
925 * Codec load error:-1
926 * Codec load disk
927 * Codec: Unsupported
928 * Codec finished
929 * New codec:0/3
930 * Clearing tracks:7/7, 1
931 * Forcing rebuffer
932 * Check new track buffer
933 * Request new track
934 * Clearing tracks:5/5, 0
935 * Starting buffer fill
936 * Clearing tracks:5/5, 1
937 * Re-buffering song w/seek
938 */
939 //if (!filling)
940 // queue_post(&codec_callback_queue, Q_CODEC_REQUEST_PENDING, 0);
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000941
Brandon Low62ccbbb2006-04-11 03:55:58 +0000942 /* Stop in progress fill, and clear open file descriptor */
Miika Pekkarinen4ccacd42006-07-29 19:34:12 +0000943 if (current_fd >= 0)
944 {
945 close(current_fd);
946 current_fd = -1;
947 }
Brandon Low62ccbbb2006-04-11 03:55:58 +0000948 filling = false;
949
950 /* Reset buffer and track pointers */
951 buf_ridx = buf_widx = 0;
Miika Pekkarinen4ccacd42006-07-29 19:34:12 +0000952 track_widx = track_ridx;
Miika Pekkarinen513cafe2006-07-31 06:12:53 +0000953 cur_ti = &tracks[track_ridx];
Miika Pekkarinenbb5330c2006-08-03 07:45:53 +0000954 audio_clear_track_entries(true, true, false);
Brandon Low62ccbbb2006-04-11 03:55:58 +0000955 filebufused = 0;
956
Brandon Low62ccbbb2006-04-11 03:55:58 +0000957 /* Fill the buffer */
958 last_peek_offset = -1;
Brandon Low54af3042006-04-12 02:01:26 +0000959 cur_ti->filesize = 0;
Brandon Lowc95044a2006-04-13 14:17:12 +0000960 cur_ti->start_pos = 0;
Miika Pekkarinen4ccacd42006-07-29 19:34:12 +0000961 ci.curpos = 0;
Hardeep Sidhuc9a11cd2006-05-15 01:45:35 +0000962
963 if (!cur_ti->taginfo_ready)
964 memset(&cur_ti->id3, 0, sizeof(struct mp3entry));
965
Brandon Lowda1cddf2006-04-26 04:01:35 +0000966 audio_fill_file_buffer(false, true, 0);
Brandon Low62ccbbb2006-04-11 03:55:58 +0000967}
968
Brandon Low98097d22006-04-17 17:05:05 +0000969static void audio_check_new_track(void)
Brandon Low62ccbbb2006-04-11 03:55:58 +0000970{
Brandon Lowc95044a2006-04-13 14:17:12 +0000971 int track_count = audio_track_count();
972 int old_track_ridx = track_ridx;
Brandon Low2f11d602006-04-14 03:47:32 +0000973 bool forward;
Brandon Lowc95044a2006-04-13 14:17:12 +0000974
Brandon Low2b187272006-04-18 18:33:09 +0000975 if (dir_skip)
976 {
977 dir_skip = false;
978 if (playlist_next_dir(ci.new_track))
979 {
980 ci.new_track = 0;
981 cur_ti->taginfo_ready = false;
982 audio_rebuffer();
983 goto skip_done;
984 }
985 else
986 {
987 queue_post(&codec_callback_queue, Q_CODEC_REQUEST_FAILED, 0);
988 return;
989 }
990 }
991
Brandon Lowda1cddf2006-04-26 04:01:35 +0000992 if (new_playlist)
993 ci.new_track = 0;
994
Brandon Lowc95044a2006-04-13 14:17:12 +0000995 /* If the playlist isn't that big */
Brandon Lowebadcc62006-04-15 02:03:11 +0000996 if (!playlist_check(ci.new_track))
Brandon Lowc95044a2006-04-13 14:17:12 +0000997 {
Brandon Lowebadcc62006-04-15 02:03:11 +0000998 if (ci.new_track >= 0)
Brandon Lowfb966b32006-04-14 14:19:56 +0000999 {
Brandon Low0291a6e2006-04-14 14:03:43 +00001000 queue_post(&codec_callback_queue, Q_CODEC_REQUEST_FAILED, 0);
Brandon Lowfb966b32006-04-14 14:19:56 +00001001 return;
1002 }
Brandon Lowc95044a2006-04-13 14:17:12 +00001003 /* Find the beginning backward if the user over-skips it */
Brandon Lowebadcc62006-04-15 02:03:11 +00001004 while (!playlist_check(++ci.new_track))
1005 if (ci.new_track >= 0)
Brandon Lowfb966b32006-04-14 14:19:56 +00001006 {
Brandon Low0291a6e2006-04-14 14:03:43 +00001007 queue_post(&codec_callback_queue, Q_CODEC_REQUEST_FAILED, 0);
Brandon Lowfb966b32006-04-14 14:19:56 +00001008 return;
1009 }
Brandon Lowc95044a2006-04-13 14:17:12 +00001010 }
1011 /* Update the playlist */
Brandon Lowebadcc62006-04-15 02:03:11 +00001012 last_peek_offset -= ci.new_track;
Hardeep Sidhu1e0b1d52006-06-07 20:43:34 +00001013
1014 if (playlist_next(ci.new_track) < 0)
1015 {
1016 queue_post(&codec_callback_queue, Q_CODEC_REQUEST_FAILED, 0);
1017 return;
1018 }
Brandon Lowc95044a2006-04-13 14:17:12 +00001019
Brandon Lowda1cddf2006-04-26 04:01:35 +00001020 if (new_playlist)
Brandon Low69cf4f52006-04-26 04:31:06 +00001021 {
Brandon Lowda1cddf2006-04-26 04:01:35 +00001022 ci.new_track = 1;
Brandon Low69cf4f52006-04-26 04:31:06 +00001023 new_playlist = false;
1024 }
Brandon Lowda1cddf2006-04-26 04:01:35 +00001025
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001026 track_ridx += ci.new_track;
1027 track_ridx &= MAX_TRACK_MASK;
Brandon Lowc95044a2006-04-13 14:17:12 +00001028
Brandon Low0744e762006-04-13 17:30:54 +00001029 /* Save the old track */
Brandon Low62ccbbb2006-04-11 03:55:58 +00001030 prev_ti = cur_ti;
Brandon Low0744e762006-04-13 17:30:54 +00001031 /* Move to the new track */
Brandon Low62ccbbb2006-04-11 03:55:58 +00001032 cur_ti = &tracks[track_ridx];
1033
Hardeep Sidhu5483da62006-06-03 17:23:20 +00001034 if (automatic_skip)
1035 playlist_end = false;
1036
Brandon Low5cdee942006-04-23 22:30:52 +00001037 track_changed = !automatic_skip;
Brandon Low62ccbbb2006-04-11 03:55:58 +00001038
Brandon Lowc95044a2006-04-13 14:17:12 +00001039 /* If it is not safe to even skip this many track entries */
Brandon Lowebadcc62006-04-15 02:03:11 +00001040 if (ci.new_track >= track_count || ci.new_track <= track_count - MAX_TRACK)
Brandon Lowfbe74d12006-04-14 12:53:29 +00001041 {
Brandon Low4564f5c2006-04-26 12:19:16 +00001042 ci.new_track = 0;
Brandon Lowfbe74d12006-04-14 12:53:29 +00001043 cur_ti->taginfo_ready = false;
Brandon Low62ccbbb2006-04-11 03:55:58 +00001044 audio_rebuffer();
Brandon Low2b187272006-04-18 18:33:09 +00001045 goto skip_done;
Brandon Lowfbe74d12006-04-14 12:53:29 +00001046 }
Brandon Low2b187272006-04-18 18:33:09 +00001047
Brandon Lowda1cddf2006-04-26 04:01:35 +00001048 forward = ci.new_track > 0;
1049 ci.new_track = 0;
1050
Brandon Lowc95044a2006-04-13 14:17:12 +00001051 /* If the target track is clearly not in memory */
Brandon Low2b187272006-04-18 18:33:09 +00001052 if (cur_ti->filesize == 0 || !cur_ti->taginfo_ready)
1053 {
Brandon Lowc95044a2006-04-13 14:17:12 +00001054 audio_rebuffer();
Brandon Low2b187272006-04-18 18:33:09 +00001055 goto skip_done;
1056 }
1057
Brandon Lowc95044a2006-04-13 14:17:12 +00001058 /* The track may be in memory, see if it really is */
Brandon Low2b187272006-04-18 18:33:09 +00001059 if (forward)
Brandon Lowc95044a2006-04-13 14:17:12 +00001060 {
Brandon Low98097d22006-04-17 17:05:05 +00001061 if (!buffer_wind_forward(track_ridx, old_track_ridx))
Brandon Lowc95044a2006-04-13 14:17:12 +00001062 audio_rebuffer();
1063 }
Brandon Low62ccbbb2006-04-11 03:55:58 +00001064 else
1065 {
Brandon Lowc95044a2006-04-13 14:17:12 +00001066 int cur_idx = track_ridx;
1067 bool taginfo_ready = true;
1068 bool wrap = track_ridx > old_track_ridx;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001069
1070 while (1)
1071 {
1072 cur_idx++;
1073 cur_idx &= MAX_TRACK_MASK;
Brandon Lowc95044a2006-04-13 14:17:12 +00001074 if (!(wrap || cur_idx < old_track_ridx))
1075 break;
Brandon Low62ccbbb2006-04-11 03:55:58 +00001076
Brandon Lowc95044a2006-04-13 14:17:12 +00001077 /* If we hit a track in between without valid tag info, bail */
1078 if (!tracks[cur_idx].taginfo_ready)
Brandon Low62ccbbb2006-04-11 03:55:58 +00001079 {
Brandon Lowc95044a2006-04-13 14:17:12 +00001080 taginfo_ready = false;
1081 break;
Brandon Low62ccbbb2006-04-11 03:55:58 +00001082 }
Brandon Lowc95044a2006-04-13 14:17:12 +00001083
1084 tracks[cur_idx].available = tracks[cur_idx].filesize;
1085 if (tracks[cur_idx].codecsize)
1086 tracks[cur_idx].has_codec = true;
Brandon Low62ccbbb2006-04-11 03:55:58 +00001087 }
Brandon Lowc95044a2006-04-13 14:17:12 +00001088 if (taginfo_ready)
1089 {
Brandon Low98097d22006-04-17 17:05:05 +00001090 if (!buffer_wind_backward(track_ridx, old_track_ridx))
Brandon Lowc95044a2006-04-13 14:17:12 +00001091 audio_rebuffer();
1092 }
1093 else
1094 {
Brandon Low0744e762006-04-13 17:30:54 +00001095 cur_ti->taginfo_ready = false;
Brandon Lowc95044a2006-04-13 14:17:12 +00001096 audio_rebuffer();
1097 }
Brandon Low62ccbbb2006-04-11 03:55:58 +00001098 }
Brandon Lowc95044a2006-04-13 14:17:12 +00001099
Brandon Low2b187272006-04-18 18:33:09 +00001100skip_done:
Brandon Low1d41f772006-04-13 21:47:00 +00001101 audio_update_trackinfo();
Brandon Lowfd084242006-04-14 13:05:08 +00001102 queue_post(&codec_callback_queue, Q_CODEC_REQUEST_COMPLETE, 0);
Brandon Low62ccbbb2006-04-11 03:55:58 +00001103}
1104
Brandon Low72232bd2006-04-09 02:15:35 +00001105static void rebuffer_and_seek(size_t newpos)
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001106{
1107 int fd;
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001108 char *trackname;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001109
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001110 trackname = playlist_peek(0);
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001111 /* (Re-)open current track's file handle. */
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001112
1113 fd = open(trackname, O_RDONLY);
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001114 if (fd < 0)
1115 {
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001116 logf("Open failed!");
Brandon Low0291a6e2006-04-14 14:03:43 +00001117 queue_post(&codec_callback_queue, Q_CODEC_REQUEST_FAILED, 0);
Brandon Low72232bd2006-04-09 02:15:35 +00001118 return;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001119 }
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001120
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001121 if (current_fd >= 0)
1122 close(current_fd);
1123 current_fd = fd;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001124
Miika Pekkarinen999f89c2005-12-03 12:14:26 +00001125 playlist_end = false;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001126
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001127 ci.curpos = newpos;
1128
1129 /* Clear codec buffer. */
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001130 track_widx = track_ridx;
Miika Pekkarinen513cafe2006-07-31 06:12:53 +00001131 filebufused = 0;
1132 buf_widx = buf_ridx = 0;
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001133
Brandon Low62ccbbb2006-04-11 03:55:58 +00001134 last_peek_offset = 0;
Miika Pekkarinen513cafe2006-07-31 06:12:53 +00001135 filling = false;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001136 initialize_buffer_fill(true);
Brandon Low01219fa2006-04-11 17:08:11 +00001137
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001138 if (newpos > AUDIO_REBUFFER_GUESS_SIZE)
1139 {
Dave Chapmanb1a272e2006-07-03 21:23:14 +00001140 buf_ridx += AUDIO_REBUFFER_GUESS_SIZE;
Brandon Low01219fa2006-04-11 17:08:11 +00001141 cur_ti->start_pos = newpos - AUDIO_REBUFFER_GUESS_SIZE;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001142 }
1143 else
1144 {
Mark Arigo50f7e4e2006-07-18 21:10:13 +00001145 buf_ridx += newpos;
Brandon Low01219fa2006-04-11 17:08:11 +00001146 cur_ti->start_pos = 0;
Dave Chapmanb1a272e2006-07-03 21:23:14 +00001147 }
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001148
1149 cur_ti->filerem = cur_ti->filesize - cur_ti->start_pos;
1150 cur_ti->available = 0;
1151
1152 lseek(current_fd, cur_ti->start_pos, SEEK_SET);
1153
Brandon Lowfd084242006-04-14 13:05:08 +00001154 queue_post(&codec_callback_queue, Q_CODEC_REQUEST_COMPLETE, 0);
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001155}
1156
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001157static void voice_advance_buffer_callback(size_t amount)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001158{
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001159 amount = MIN(amount, voice_remaining);
1160 voicebuf += amount;
1161 voice_remaining -= amount;
1162}
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001163
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001164static void codec_advance_buffer_callback(size_t amount)
1165{
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001166 if (amount > cur_ti->available + cur_ti->filerem)
Miika Pekkarinen5899ed52005-06-08 10:33:01 +00001167 amount = cur_ti->available + cur_ti->filerem;
Miika Pekkarinen59e0ccb2005-12-01 19:38:08 +00001168
1169 while (amount > cur_ti->available && filling)
1170 sleep(1);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001171
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001172 if (amount > cur_ti->available)
1173 {
Brandon Lowfd084242006-04-14 13:05:08 +00001174 struct event ev;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001175
Brandon Low72232bd2006-04-09 02:15:35 +00001176 queue_post(&audio_queue,
1177 Q_AUDIO_REBUFFER_SEEK, (void *)(ci.curpos + amount));
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001178
Brandon Lowfd084242006-04-14 13:05:08 +00001179 queue_wait(&codec_callback_queue, &ev);
Brandon Low0291a6e2006-04-14 14:03:43 +00001180 switch (ev.id)
1181 {
1182 case Q_CODEC_REQUEST_FAILED:
1183 ci.stop_codec = true;
1184 case Q_CODEC_REQUEST_COMPLETE:
1185 return;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001186
Brandon Low0291a6e2006-04-14 14:03:43 +00001187 default:
1188 logf("Bad event on ccq");
1189 ci.stop_codec = true;
1190 return;
1191 }
Miika Pekkarinen5899ed52005-06-08 10:33:01 +00001192 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001193
Brandon Low86f1e2e2006-03-24 13:43:15 +00001194 advance_buffer_counters(amount);
1195
Ryan Jacksond1917562005-07-12 16:45:38 +00001196 codec_set_offset_callback(ci.curpos);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001197}
1198
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001199static void voice_advance_buffer_loc_callback(void *ptr)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001200{
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001201 size_t amount = (size_t)ptr - (size_t)voicebuf;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001202
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001203 voice_advance_buffer_callback(amount);
1204}
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001205
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001206static void codec_advance_buffer_loc_callback(void *ptr)
1207{
1208 size_t amount = (size_t)ptr - (size_t)&filebuf[buf_ridx];
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001209
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001210 codec_advance_buffer_callback(amount);
1211}
1212
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001213static off_t voice_mp3_get_filepos_callback(int newtime)
1214{
1215 (void)newtime;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001216
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001217 return 0;
1218}
1219
1220static off_t codec_mp3_get_filepos_callback(int newtime)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001221{
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001222 off_t newpos;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001223
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001224 cur_ti->id3.elapsed = newtime;
Miika Pekkarinen82c29272005-06-07 06:34:54 +00001225 newpos = mp3_get_file_pos();
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001226
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001227 return newpos;
1228}
1229
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001230static void voice_do_nothing(void)
1231{
1232 return;
1233}
1234
1235static void codec_seek_complete_callback(void)
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +00001236{
Brandon Low502fbd72006-04-08 12:58:39 +00001237 logf("seek_complete");
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001238 if (pcm_is_paused())
1239 {
Brandon Low502fbd72006-04-08 12:58:39 +00001240 /* If this is not a seamless seek, clear the buffer */
1241 pcmbuf_play_stop();
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001242
Brandon Low502fbd72006-04-08 12:58:39 +00001243 /* If playback was not 'deliberately' paused, unpause now */
1244 if (!paused)
1245 pcmbuf_pause(false);
1246 }
Brandon Low1344ccd2006-04-18 19:51:37 +00001247 ci.seek_time = 0;
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +00001248}
1249
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001250static bool voice_seek_buffer_callback(size_t newpos)
1251{
1252 (void)newpos;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001253
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001254 return false;
1255}
1256
1257static bool codec_seek_buffer_callback(size_t newpos)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001258{
1259 int difference;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001260
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001261 if (newpos >= cur_ti->filesize)
1262 newpos = cur_ti->filesize - 1;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001263
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001264 difference = newpos - ci.curpos;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001265 if (difference >= 0)
1266 {
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001267 /* Seeking forward */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001268 logf("seek: +%d", difference);
1269 codec_advance_buffer_callback(difference);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001270 return true;
1271 }
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001272
1273 /* Seeking backward */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001274 difference = -difference;
1275 if (ci.curpos - difference < 0)
1276 difference = ci.curpos;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001277
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001278 /* We need to reload the song. */
1279 if (newpos < cur_ti->start_pos)
Brandon Low72232bd2006-04-09 02:15:35 +00001280 {
Brandon Lowfd084242006-04-14 13:05:08 +00001281 struct event ev;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001282
Brandon Low72232bd2006-04-09 02:15:35 +00001283 queue_post(&audio_queue, Q_AUDIO_REBUFFER_SEEK, (void *)newpos);
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001284
Brandon Lowfd084242006-04-14 13:05:08 +00001285 queue_wait(&codec_callback_queue, &ev);
Brandon Low0291a6e2006-04-14 14:03:43 +00001286 switch (ev.id)
1287 {
1288 case Q_CODEC_REQUEST_COMPLETE:
1289 return true;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001290
Brandon Low0291a6e2006-04-14 14:03:43 +00001291 case Q_CODEC_REQUEST_FAILED:
1292 ci.stop_codec = true;
1293 return false;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001294
Brandon Low0291a6e2006-04-14 14:03:43 +00001295 default:
1296 logf("Bad event on ccq");
1297 return false;
1298 }
Brandon Low72232bd2006-04-09 02:15:35 +00001299 }
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001300
1301 /* Seeking inside buffer space. */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001302 logf("seek: -%d", difference);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001303 filebufused += difference;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001304 cur_ti->available += difference;
Brandon Low86f1e2e2006-03-24 13:43:15 +00001305 if (buf_ridx < (unsigned)difference)
1306 buf_ridx += filebuflen;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001307 buf_ridx -= difference;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001308 ci.curpos -= difference;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001309
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001310 return true;
1311}
1312
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00001313static void set_filebuf_watermark(int seconds)
1314{
Brandon Low86f1e2e2006-03-24 13:43:15 +00001315 size_t bytes;
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00001316
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001317 if (current_codec == CODEC_IDX_VOICE)
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001318 return;
Magnus Holmgren62634a32005-10-16 08:01:02 +00001319
1320 if (!filebuf)
1321 return; /* Audio buffers not yet set up */
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001322
Brandon Low86f1e2e2006-03-24 13:43:15 +00001323 bytes = MAX(cur_ti->id3.bitrate * seconds * (1000/8), conf_watermark);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001324 bytes = MIN(bytes, filebuflen / 2);
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00001325 conf_watermark = bytes;
1326}
1327
Miika Pekkarinen6a4bfb52005-12-01 18:44:11 +00001328static void codec_configure_callback(int setting, void *value)
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001329{
1330 switch (setting) {
1331 case CODEC_SET_FILEBUF_WATERMARK:
Thom Johansene2824c92006-03-22 16:04:51 +00001332 conf_watermark = (unsigned long)value;
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00001333 set_filebuf_watermark(buffer_margin);
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001334 break;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001335
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001336 case CODEC_SET_FILEBUF_CHUNKSIZE:
Thom Johansene2824c92006-03-22 16:04:51 +00001337 conf_filechunk = (unsigned long)value;
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001338 break;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001339
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001340 default:
Brandon Low87484fc2006-04-11 20:41:04 +00001341 if (!dsp_configure(setting, value)) { logf("Illegal key:%d", setting); }
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001342 }
1343}
1344
Miika Pekkarinend54811f2005-07-02 16:52:30 +00001345void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3,
1346 bool last_track))
1347{
1348 track_buffer_callback = handler;
1349}
1350
1351void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3,
Miika Pekkarinen9bde0382005-07-03 18:36:24 +00001352 bool last_track))
Miika Pekkarinend54811f2005-07-02 16:52:30 +00001353{
1354 track_unbuffer_callback = handler;
1355}
1356
Jens Arnolda88d0762005-08-18 06:05:15 +00001357void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3))
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +00001358{
1359 track_changed_callback = handler;
1360}
1361
Miika Pekkarinen6a4bfb52005-12-01 18:44:11 +00001362static void codec_track_changed(void)
Miika Pekkarinenc3fed622005-06-15 18:59:04 +00001363{
Brandon Low5cdee942006-04-23 22:30:52 +00001364 automatic_skip = false;
Miika Pekkarinenc3fed622005-06-15 18:59:04 +00001365 track_changed = true;
Miika Pekkarinend3191162006-01-27 11:39:46 +00001366 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
Miika Pekkarinenc3fed622005-06-15 18:59:04 +00001367}
1368
Brandon Low413da2a2006-02-07 20:38:55 +00001369static void pcmbuf_track_changed_callback(void)
1370{
Brandon Low413da2a2006-02-07 20:38:55 +00001371 pcmbuf_set_position_callback(NULL);
Brandon Low5cdee942006-04-23 22:30:52 +00001372 codec_track_changed();
Brandon Low413da2a2006-02-07 20:38:55 +00001373}
1374
Brandon Low930785c2006-04-06 16:21:31 +00001375/* Yield to codecs for as long as possible if they are in need of data
1376 * return true if the caller should break to let the audio thread process
1377 * new events */
1378static bool yield_codecs(void)
Miika Pekkarinen82c29272005-06-07 06:34:54 +00001379{
Miika Pekkarinen61716dd2005-06-07 18:03:33 +00001380 yield();
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001381
Brandon Low29f7dd32006-04-07 21:32:30 +00001382 if (!queue_empty(&audio_queue)) return true;
Brandon Low27363632006-04-06 21:06:37 +00001383
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001384 while ((pcmbuf_is_crossfade_active() || pcmbuf_is_lowdata())
Brandon Low930785c2006-04-06 16:21:31 +00001385 && !ci.stop_codec && playing && !filebuf_is_lowdata())
1386 {
Miika Pekkarinenb142a582005-10-30 08:51:47 +00001387 sleep(1);
Brandon Low29f7dd32006-04-07 21:32:30 +00001388 if (!queue_empty(&audio_queue)) return true;
Brandon Low930785c2006-04-06 16:21:31 +00001389 }
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001390
Brandon Low930785c2006-04-06 16:21:31 +00001391 return false;
Miika Pekkarinen82c29272005-06-07 06:34:54 +00001392}
1393
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +00001394/* FIXME: This code should be made more generic and move to metadata.c */
Brandon Lowf3bc1ef2006-04-22 14:40:13 +00001395static void strip_id3v1_tag(void)
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +00001396{
1397 int i;
1398 static const unsigned char tag[] = "TAG";
Brandon Low2f4edab2006-04-14 04:42:11 +00001399 size_t tag_idx;
1400 size_t cur_idx;
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +00001401
Brandon Low2f4edab2006-04-14 04:42:11 +00001402 tag_idx = buf_widx;
1403 if (tag_idx < 128)
1404 tag_idx += filebuflen;
1405 tag_idx -= 128;
1406
1407 if (filebufused > 128 && tag_idx > buf_ridx)
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +00001408 {
Brandon Low2f4edab2006-04-14 04:42:11 +00001409 cur_idx = tag_idx;
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +00001410 for(i = 0;i < 3;i++)
1411 {
Brandon Low2f4edab2006-04-14 04:42:11 +00001412 if(filebuf[cur_idx] != tag[i])
1413 return;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001414
Brandon Low2f4edab2006-04-14 04:42:11 +00001415 if(++cur_idx >= filebuflen)
1416 cur_idx -= filebuflen;
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +00001417 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001418
Brandon Low2f4edab2006-04-14 04:42:11 +00001419 /* Skip id3v1 tag */
1420 logf("Skipping ID3v1 tag");
1421 buf_widx = tag_idx;
1422 tracks[track_widx].available -= 128;
1423 tracks[track_widx].filesize -= 128;
1424 filebufused -= 128;
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +00001425 }
1426}
1427
Brandon Lowda1cddf2006-04-26 04:01:35 +00001428static void audio_read_file(bool quick)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001429{
Brandon Low86f1e2e2006-03-24 13:43:15 +00001430 size_t copy_n;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001431 int rc;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001432
Brandon Lowde602312006-04-07 16:31:20 +00001433 /* If we're called and no file is open, this is an error */
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001434 if (current_fd < 0)
1435 {
Brandon Low98097d22006-04-17 17:05:05 +00001436 logf("Bad fd in arf");
Brandon Lowde602312006-04-07 16:31:20 +00001437 /* Stop this buffer cycle immediately */
1438 fill_bytesleft = 0;
1439 /* Give some hope of miraculous recovery by forcing a track reload */
1440 tracks[track_widx].filesize = 0;
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001441 return ;
Brandon Lowde602312006-04-07 16:31:20 +00001442 }
1443
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001444 while (tracks[track_widx].filerem > 0)
1445 {
Brandon Lowb6607452006-04-11 14:13:41 +00001446 if (fill_bytesleft == 0)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001447 break ;
Brandon Low930785c2006-04-06 16:21:31 +00001448
1449 /* copy_n is the largest chunk that is safe to read */
Brandon Low86f1e2e2006-03-24 13:43:15 +00001450 copy_n = MIN(conf_filechunk, filebuflen - buf_widx);
1451 copy_n = MIN(copy_n, fill_bytesleft);
Brandon Low7c986a92006-04-14 17:31:19 +00001452
Brandon Low930785c2006-04-06 16:21:31 +00001453 /* rc is the actual amount read */
Brandon Low86f1e2e2006-03-24 13:43:15 +00001454 rc = read(current_fd, &filebuf[buf_widx], copy_n);
Brandon Low930785c2006-04-06 16:21:31 +00001455
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001456 if (rc <= 0)
1457 {
Brandon Low930785c2006-04-06 16:21:31 +00001458 /* Reached the end of the file */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001459 tracks[track_widx].filerem = 0;
1460 break ;
1461 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001462
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001463 buf_widx += rc;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001464 if (buf_widx >= filebuflen)
1465 buf_widx -= filebuflen;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001466 tracks[track_widx].available += rc;
Miika Pekkarinen3eb962d2005-07-07 07:15:05 +00001467 tracks[track_widx].filerem -= rc;
Brandon Lowb6607452006-04-11 14:13:41 +00001468
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001469 filebufused += rc;
Brandon Lowb6607452006-04-11 14:13:41 +00001470 if (fill_bytesleft > (unsigned)rc)
1471 fill_bytesleft -= rc;
1472 else
1473 fill_bytesleft = 0;
Brandon Low29f7dd32006-04-07 21:32:30 +00001474
1475 /* Let the codec process until it is out of the danger zone, or there
1476 * is an event to handle. In the latter case, break this fill cycle
1477 * immediately */
Brandon Lowda1cddf2006-04-26 04:01:35 +00001478 if (quick || yield_codecs())
Brandon Low29f7dd32006-04-07 21:32:30 +00001479 break;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001480 }
Linus Nielsen Feltzing27312b22006-01-18 14:20:34 +00001481
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001482 if (tracks[track_widx].filerem == 0)
1483 {
Brandon Lowaf09d222006-04-11 23:08:21 +00001484 logf("Finished buf:%dB", tracks[track_widx].filesize);
Brandon Low930785c2006-04-06 16:21:31 +00001485 close(current_fd);
1486 current_fd = -1;
Brandon Low017914a2006-04-13 20:42:11 +00001487 strip_id3v1_tag();
1488
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001489 track_widx++;
1490 track_widx &= MAX_TRACK_MASK;
Brandon Low87484fc2006-04-11 20:41:04 +00001491
Brandon Lowde602312006-04-07 16:31:20 +00001492 tracks[track_widx].filesize = 0;
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001493 }
1494 else
1495 {
Brandon Lowaf09d222006-04-11 23:08:21 +00001496 logf("Partially buf:%dB",
1497 tracks[track_widx].filesize - tracks[track_widx].filerem);
1498 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001499}
1500
Brandon Low62ccbbb2006-04-11 03:55:58 +00001501static void codec_discard_codec_callback(void)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001502{
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001503 if (cur_ti->has_codec)
1504 {
Brandon Low54af3042006-04-12 02:01:26 +00001505 cur_ti->has_codec = false;
1506 filebufused -= cur_ti->codecsize;
1507 buf_ridx += cur_ti->codecsize;
Brandon Low3b765442006-04-11 12:29:46 +00001508 if (buf_ridx >= filebuflen)
1509 buf_ridx -= filebuflen;
1510 }
Miika Pekkarinen513cafe2006-07-31 06:12:53 +00001511
1512#if 0
1513 /* Check if a buffer desync has happened, log it and stop playback. */
Brandon Low0744e762006-04-13 17:30:54 +00001514 if (buf_ridx != cur_ti->buf_idx)
1515 {
Brandon Low1d41f772006-04-13 21:47:00 +00001516 int offset = cur_ti->buf_idx - buf_ridx;
1517 size_t new_used = filebufused - offset;
Miika Pekkarinen513cafe2006-07-31 06:12:53 +00001518
Brandon Low1d41f772006-04-13 21:47:00 +00001519 logf("Buf off :%d=%d-%d", offset, cur_ti->buf_idx, buf_ridx);
Miika Pekkarinen513cafe2006-07-31 06:12:53 +00001520 logf("Used off:%d",filebufused - new_used);
1521
1522 /* This is a fatal internal error and it's not safe to
1523 * continue playback. */
1524 ci.stop_codec = true;
1525 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
Brandon Low0744e762006-04-13 17:30:54 +00001526 }
Miika Pekkarinen513cafe2006-07-31 06:12:53 +00001527#endif
Miika Pekkarinen6a4bfb52005-12-01 18:44:11 +00001528}
1529
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001530static const char *get_codec_path(int codectype)
1531{
Brandon Low98097d22006-04-17 17:05:05 +00001532 switch (codectype) {
1533 case AFMT_OGG_VORBIS:
1534 logf("Codec: Vorbis");
1535 return CODEC_VORBIS;
1536 case AFMT_MPA_L1:
1537 case AFMT_MPA_L2:
1538 case AFMT_MPA_L3:
1539 logf("Codec: MPA L1/L2/L3");
1540 return CODEC_MPA_L3;
1541 case AFMT_PCM_WAV:
1542 logf("Codec: PCM WAV");
1543 return CODEC_WAV;
1544 case AFMT_FLAC:
1545 logf("Codec: FLAC");
1546 return CODEC_FLAC;
1547 case AFMT_A52:
1548 logf("Codec: A52");
1549 return CODEC_A52;
1550 case AFMT_MPC:
1551 logf("Codec: Musepack");
1552 return CODEC_MPC;
1553 case AFMT_WAVPACK:
1554 logf("Codec: WAVPACK");
1555 return CODEC_WAVPACK;
1556 case AFMT_ALAC:
1557 logf("Codec: ALAC");
1558 return CODEC_ALAC;
1559 case AFMT_AAC:
1560 logf("Codec: AAC");
1561 return CODEC_AAC;
1562 case AFMT_SHN:
1563 logf("Codec: SHN");
1564 return CODEC_SHN;
1565 case AFMT_AIFF:
1566 logf("Codec: PCM AIFF");
1567 return CODEC_AIFF;
Dave Chapman752faa42006-07-18 18:33:12 +00001568 case AFMT_SID:
1569 logf("Codec: SID");
1570 return CODEC_SID;
Brandon Low98097d22006-04-17 17:05:05 +00001571 default:
1572 logf("Codec: Unsupported");
1573 return NULL;
1574 }
1575}
1576
Miika Pekkarinen6a4bfb52005-12-01 18:44:11 +00001577static bool loadcodec(bool start_play)
1578{
Brandon Low86f1e2e2006-03-24 13:43:15 +00001579 size_t size;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001580 int fd;
Brandon Low86f1e2e2006-03-24 13:43:15 +00001581 int rc;
Brandon Low86f1e2e2006-03-24 13:43:15 +00001582 size_t copy_n;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001583 int prev_track;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001584
Brandon Low98097d22006-04-17 17:05:05 +00001585 const char *codec_path = get_codec_path(tracks[track_widx].id3.codectype);
1586 if (codec_path == NULL)
Miika Pekkarinend1704f62005-11-21 11:14:51 +00001587 return false;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001588
Brandon Low62ccbbb2006-04-11 03:55:58 +00001589 tracks[track_widx].has_codec = false;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001590 tracks[track_widx].codecsize = 0;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001591
Brandon Low930785c2006-04-06 16:21:31 +00001592 if (start_play)
1593 {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001594 /* Load the codec directly from disk and save some memory. */
Brandon Low0744e762006-04-13 17:30:54 +00001595 track_ridx = track_widx;
1596 cur_ti = &tracks[track_ridx];
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001597 ci.filesize = cur_ti->filesize;
Brandon Low0744e762006-04-13 17:30:54 +00001598 ci.id3 = &cur_ti->id3;
1599 ci.taginfo_ready = &cur_ti->taginfo_ready;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001600 ci.curpos = 0;
1601 playing = true;
Miika Pekkarinend3191162006-01-27 11:39:46 +00001602 queue_post(&codec_queue, Q_CODEC_LOAD_DISK, (void *)codec_path);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001603 return true;
1604 }
Brandon Low930785c2006-04-06 16:21:31 +00001605 else
1606 {
Brandon Low62ccbbb2006-04-11 03:55:58 +00001607 /* If we already have another track than this one buffered */
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001608 if (track_widx != track_ridx)
1609 {
1610 prev_track = (track_widx - 1) & MAX_TRACK_MASK;
1611
Brandon Low62ccbbb2006-04-11 03:55:58 +00001612 /* If the previous codec is the same as this one, there is no need
1613 * to put another copy of it on the file buffer */
1614 if (get_codec_base_type(tracks[track_widx].id3.codectype) ==
Miika Pekkarinen513cafe2006-07-31 06:12:53 +00001615 get_codec_base_type(tracks[prev_track].id3.codectype)
1616 && audio_codec_loaded)
Brandon Low62ccbbb2006-04-11 03:55:58 +00001617 {
1618 logf("Reusing prev. codec");
1619 return true;
1620 }
Brandon Low930785c2006-04-06 16:21:31 +00001621 }
1622 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001623
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001624 fd = open(codec_path, O_RDONLY);
Brandon Low930785c2006-04-06 16:21:31 +00001625 if (fd < 0)
1626 {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001627 logf("Codec doesn't exist!");
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001628 return false;
1629 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001630
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001631 size = filesize(fd);
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001632
Brandon Low930785c2006-04-06 16:21:31 +00001633 /* Never load a partial codec */
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001634 if (fill_bytesleft < size)
1635 {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001636 logf("Not enough space");
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001637 fill_bytesleft = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001638 close(fd);
1639 return false;
1640 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001641
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001642 while (tracks[track_widx].codecsize < size)
1643 {
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001644 copy_n = MIN(conf_filechunk, filebuflen - buf_widx);
1645 rc = read(fd, &filebuf[buf_widx], copy_n);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001646 if (rc < 0)
1647 return false;
Brandon Low62ccbbb2006-04-11 03:55:58 +00001648
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001649 filebufused += rc;
Brandon Low62ccbbb2006-04-11 03:55:58 +00001650 if (fill_bytesleft > (unsigned)rc)
1651 fill_bytesleft -= rc;
1652 else
1653 fill_bytesleft = 0;
1654
1655 buf_widx += rc;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001656 if (buf_widx >= filebuflen)
1657 buf_widx -= filebuflen;
Brandon Low29f7dd32006-04-07 21:32:30 +00001658
Brandon Low62ccbbb2006-04-11 03:55:58 +00001659 tracks[track_widx].codecsize += rc;
1660
Brandon Low29f7dd32006-04-07 21:32:30 +00001661 yield_codecs();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001662 }
Brandon Low930785c2006-04-06 16:21:31 +00001663
Brandon Low62ccbbb2006-04-11 03:55:58 +00001664 tracks[track_widx].has_codec = true;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001665
Brandon Low62ccbbb2006-04-11 03:55:58 +00001666 close(fd);
1667 logf("Done: %dB", size);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001668
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001669 return true;
1670}
1671
Miika Pekkarinen6a4bfb52005-12-01 18:44:11 +00001672static bool read_next_metadata(void)
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001673{
1674 int fd;
1675 char *trackname;
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001676 int next_idx;
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001677 int status;
1678
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001679 next_idx = track_widx;
1680 if (tracks[next_idx].taginfo_ready)
1681 {
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001682 next_idx++;
1683 next_idx &= MAX_TRACK_MASK;
1684
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001685 if (tracks[next_idx].taginfo_ready)
1686 return true;
1687 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001688
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001689 trackname = playlist_peek(last_peek_offset + 1);
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001690 if (!trackname)
1691 return false;
1692
1693 fd = open(trackname, O_RDONLY);
1694 if (fd < 0)
1695 return false;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001696
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001697 status = get_metadata(&tracks[next_idx],fd,trackname,v1first);
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001698 /* Preload the glyphs in the tags */
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001699 if (status)
1700 {
Brandon Low4f3bb2d2006-04-09 15:30:28 +00001701 if (tracks[next_idx].id3.title)
1702 lcd_getstringsize(tracks[next_idx].id3.title, NULL, NULL);
1703 if (tracks[next_idx].id3.artist)
1704 lcd_getstringsize(tracks[next_idx].id3.artist, NULL, NULL);
1705 if (tracks[next_idx].id3.album)
1706 lcd_getstringsize(tracks[next_idx].id3.album, NULL, NULL);
Marcoen Hirschbergb0fee172005-12-06 13:27:15 +00001707 }
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001708 close(fd);
1709
1710 return status;
1711}
1712
Brandon Lowda1cddf2006-04-26 04:01:35 +00001713static bool audio_load_track(int offset, bool start_play, bool rebuffer)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001714{
1715 char *trackname;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001716 off_t size;
Miika Pekkarinen6a4bfb52005-12-01 18:44:11 +00001717 char msgbuf[80];
Miika Pekkarinende3b04e2005-06-29 14:46:27 +00001718
Miika Pekkarinenb288dda2005-07-10 08:38:16 +00001719 /* Stop buffer filling if there is no free track entries.
1720 Don't fill up the last track entry (we wan't to store next track
1721 metadata there). */
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001722 if (!have_free_tracks())
1723 {
Brandon Low62ccbbb2006-04-11 03:55:58 +00001724 logf("No free tracks");
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001725 return false;
Brandon Low62ccbbb2006-04-11 03:55:58 +00001726 }
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001727
Brandon Lowde602312006-04-07 16:31:20 +00001728 if (current_fd >= 0)
1729 {
Brandon Low348d9ec2006-04-14 17:00:22 +00001730 logf("Nonzero fd in alt");
Brandon Lowde602312006-04-07 16:31:20 +00001731 close(current_fd);
1732 current_fd = -1;
1733 }
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001734
Brandon Low62ccbbb2006-04-11 03:55:58 +00001735 last_peek_offset++;
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001736 peek_again:
Miika Pekkarinende3b04e2005-06-29 14:46:27 +00001737 logf("Buffering track:%d/%d", track_widx, track_ridx);
Brandon Low930785c2006-04-06 16:21:31 +00001738 /* Get track name from current playlist read position. */
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001739 while ((trackname = playlist_peek(last_peek_offset)) != NULL)
1740 {
Brandon Low930785c2006-04-06 16:21:31 +00001741 /* Handle broken playlists. */
Brandon Lowde602312006-04-07 16:31:20 +00001742 current_fd = open(trackname, O_RDONLY);
Miika Pekkarinen513cafe2006-07-31 06:12:53 +00001743 if (current_fd < 0)
1744 {
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001745 logf("Open failed");
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001746 /* Skip invalid entry from playlist. */
Brandon Low62ccbbb2006-04-11 03:55:58 +00001747 playlist_skip_entry(NULL, last_peek_offset);
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001748 }
1749 else
Brandon Low62ccbbb2006-04-11 03:55:58 +00001750 break;
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001751 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001752
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001753 if (!trackname)
1754 {
Miika Pekkarinenc3fed622005-06-15 18:59:04 +00001755 logf("End-of-playlist");
Miika Pekkarinen999f89c2005-12-03 12:14:26 +00001756 playlist_end = true;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001757 return false;
1758 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001759
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001760 /* Initialize track entry. */
Brandon Lowde602312006-04-07 16:31:20 +00001761 size = filesize(current_fd);
Linus Nielsen Feltzing