blob: 37443df64f5e85aedb250d3a6305c0b368dc0bb6 [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 ****************************************************************************/
19#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
22#include <ctype.h>
23
24#include "system.h"
25#include "thread.h"
26#include "file.h"
27#include "lcd.h"
28#include "font.h"
29#include "backlight.h"
30#include "button.h"
31#include "kernel.h"
32#include "tree.h"
33#include "debug.h"
34#include "sprintf.h"
35#include "settings.h"
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000036#include "codecs.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000037#include "wps.h"
38#include "wps-display.h"
39#include "audio.h"
40#include "logf.h"
41#include "mp3_playback.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000042#include "usb.h"
43#include "status.h"
44#include "main_menu.h"
45#include "ata.h"
46#include "screens.h"
47#include "playlist.h"
48#include "playback.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000049#include "pcmbuf.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000050#include "pcm_playback.h"
51#include "buffer.h"
Miika Pekkarinend8cb7032005-06-26 19:41:29 +000052#include "dsp.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000053#ifdef HAVE_LCD_BITMAP
54#include "icons.h"
55#include "peakmeter.h"
56#include "action.h"
57#endif
58#include "lang.h"
59#include "bookmark.h"
60#include "misc.h"
61#include "sound.h"
Dave Chapman3ad485b2005-06-14 22:27:57 +000062#include "metadata.h"
Miika Pekkarinen159c52d2005-08-20 11:13:19 +000063#include "talk.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000064
Miika Pekkarinen159c52d2005-08-20 11:13:19 +000065static volatile bool audio_codec_loaded;
66static volatile bool voice_codec_loaded;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000067static volatile bool playing;
68static volatile bool paused;
69
Miika Pekkarinen159c52d2005-08-20 11:13:19 +000070#define CODEC_VORBIS "/.rockbox/codecs/vorbis.codec"
71#define CODEC_MPA_L3 "/.rockbox/codecs/mpa.codec"
72#define CODEC_FLAC "/.rockbox/codecs/flac.codec"
73#define CODEC_WAV "/.rockbox/codecs/wav.codec"
74#define CODEC_A52 "/.rockbox/codecs/a52.codec"
75#define CODEC_MPC "/.rockbox/codecs/mpc.codec"
76#define CODEC_WAVPACK "/.rockbox/codecs/wavpack.codec"
Dave Chapman139c1cb2005-09-22 21:55:37 +000077#define CODEC_ALAC "/.rockbox/codecs/alac.codec"
Dave Chapmancea6d0c2005-10-31 20:56:29 +000078#define CODEC_AAC "/.rockbox/codecs/aac.codec"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000079
Miika Pekkarinen431e8132005-06-19 18:41:53 +000080#define AUDIO_FILL_CYCLE (1024*256)
Miika Pekkarinende3b04e2005-06-29 14:46:27 +000081#define AUDIO_DEFAULT_WATERMARK (1024*512)
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +000082#define AUDIO_DEFAULT_FILECHUNK (1024*32)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000083
84#define AUDIO_PLAY 1
85#define AUDIO_STOP 2
86#define AUDIO_PAUSE 3
87#define AUDIO_RESUME 4
88#define AUDIO_NEXT 5
89#define AUDIO_PREV 6
90#define AUDIO_FF_REWIND 7
91#define AUDIO_FLUSH_RELOAD 8
92#define AUDIO_CODEC_DONE 9
Miika Pekkarinen58af47c2005-06-13 22:09:12 +000093#define AUDIO_FLUSH 10
Miika Pekkarinen431e8132005-06-19 18:41:53 +000094#define AUDIO_TRACK_CHANGED 11
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000095
96#define CODEC_LOAD 1
97#define CODEC_LOAD_DISK 2
98
99/* As defined in plugins/lib/xxx2wav.h */
100#define MALLOC_BUFSIZE (512*1024)
Miika Pekkarinena4c190f2005-10-30 09:30:14 +0000101#define GUARD_BUFSIZE (32*1024)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000102
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000103/* As defined in plugin.lds */
Jens Arnolda317d742005-09-01 20:57:33 +0000104#define CODEC_IRAM_ORIGIN 0x1000c000
105#define CODEC_IRAM_SIZE 0xc000
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000106
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000107extern bool audio_is_initialized;
108
109/* Buffer control thread. */
110static struct event_queue audio_queue;
111static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
112static const char audio_thread_name[] = "audio";
113
114/* Codec thread. */
115static struct event_queue codec_queue;
Jens Arnoldabd9f832005-10-19 19:35:24 +0000116static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_ATTR;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000117static const char codec_thread_name[] = "codec";
118
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000119/* Voice codec thread. */
120static struct event_queue voice_codec_queue;
121/* Not enough IRAM for this. */
Jens Arnoldabd9f832005-10-19 19:35:24 +0000122static long voice_codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_ATTR;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000123static const char voice_codec_thread_name[] = "voice codec";
124
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000125static struct mutex mutex_bufferfill;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000126static struct mutex mutex_codecthread;
127
128static struct mp3entry id3_voice;
129
130#define CODEC_IDX_AUDIO 0
131#define CODEC_IDX_VOICE 1
132
133static char *voicebuf;
134static int voice_remaining;
135static bool voice_is_playing;
136static void (*voice_getmore)(unsigned char** start, int* size);
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000137
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000138/* Is file buffer currently being refilled? */
139static volatile bool filling;
140
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000141volatile int current_codec;
142extern unsigned char codecbuf[];
143
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000144/* Ring buffer where tracks and codecs are loaded. */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000145static char *filebuf;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000146
147/* Total size of the ring buffer. */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000148int filebuflen;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000149
150/* Bytes available in the buffer. */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000151int filebufused;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000152
153/* Ring buffer read and write indexes. */
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000154static volatile int buf_ridx;
155static volatile int buf_widx;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000156
Miika Pekkarinen34a25a62005-07-15 16:42:01 +0000157/* Step count to the next unbuffered track. */
Miika Pekkarinen431e8132005-06-19 18:41:53 +0000158static int last_peek_offset;
159
Miika Pekkarinen34a25a62005-07-15 16:42:01 +0000160/* Index of the last buffered track. */
161static int last_index;
162
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000163/* Track information (count in file buffer, read/write indexes for
164 track ring structure. */
Linus Nielsen Feltzing17098e12005-06-22 20:37:31 +0000165int track_count;
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000166static volatile int track_ridx;
167static volatile int track_widx;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000168static bool track_changed;
169
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000170/* Partially loaded song's file handle to continue buffering later. */
171static int current_fd;
172
173/* Information about how many bytes left on the buffer re-fill run. */
Miika Pekkarinen85f49732005-06-27 19:29:49 +0000174static long fill_bytesleft;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000175
176/* Track info structure about songs in the file buffer. */
177static struct track_info tracks[MAX_TRACK];
178
179/* Pointer to track info structure about current song playing. */
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +0000180static struct track_info *cur_ti;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000181
182/* Codec API including function callbacks. */
Daniel Stenberg1dd672f2005-06-22 19:41:30 +0000183extern struct codec_api ci;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000184extern struct codec_api ci_voice;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000185
186/* When we change a song and buffer is not in filling state, this
187 variable keeps information about whether to go a next/previous track. */
188static int new_track;
189
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +0000190/* Callback function to call when current track has really changed. */
Jens Arnolda88d0762005-08-18 06:05:15 +0000191void (*track_changed_callback)(struct mp3entry *id3);
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000192void (*track_buffer_callback)(struct mp3entry *id3, bool last_track);
Miika Pekkarinen9bde0382005-07-03 18:36:24 +0000193void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track);
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +0000194
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +0000195/* Configuration */
196static int conf_bufferlimit;
197static int conf_watermark;
198static int conf_filechunk;
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000199static int buffer_margin;
Miika Pekkarinen2326bea2005-06-10 13:43:12 +0000200
Linus Nielsen Feltzing4aaa3212005-06-06 00:35:21 +0000201static bool v1first = false;
202
Miika Pekkarinen5899ed52005-06-08 10:33:01 +0000203static void mp3_set_elapsed(struct mp3entry* id3);
204int mp3_get_file_pos(void);
205
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000206static void do_swap(int idx_old, int idx_new)
207{
208#ifndef SIMULATOR
209 unsigned char *iram_p = (unsigned char *)(CODEC_IRAM_ORIGIN);
210 unsigned char *iram_buf[2];
211#endif
212 unsigned char *dram_buf[2];
213
214
215#ifndef SIMULATOR
216 iram_buf[0] = &filebuf[filebuflen];
217 iram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE];
218 memcpy(iram_buf[idx_old], iram_p, CODEC_IRAM_SIZE);
219 memcpy(iram_p, iram_buf[idx_new], CODEC_IRAM_SIZE);
220#endif
221
222 dram_buf[0] = &filebuf[filebuflen+CODEC_IRAM_SIZE*2];
223 dram_buf[1] = &filebuf[filebuflen+CODEC_IRAM_SIZE*2+CODEC_SIZE];
224 memcpy(dram_buf[idx_old], codecbuf, CODEC_SIZE);
225 memcpy(codecbuf, dram_buf[idx_new], CODEC_SIZE);
226}
227
228static void swap_codec(void)
229{
230 int last_codec;
231
232 logf("swapping codec:%d", current_codec);
233
234 /* We should swap codecs' IRAM contents and code space. */
235 do_swap(current_codec, !current_codec);
236
237 last_codec = current_codec;
238 current_codec = !current_codec;
239
240 /* Release the semaphore and force a task switch. */
241 mutex_unlock(&mutex_codecthread);
242 sleep(1);
243
244 /* Waiting until we are ready to run again. */
245 mutex_lock(&mutex_codecthread);
246
247 /* Check if codec swap did not happen. */
248 if (current_codec != last_codec)
249 {
250 logf("no codec switch happened!");
251 do_swap(current_codec, !current_codec);
252 current_codec = !current_codec;
253 }
254
255 invalidate_icache();
256 logf("codec resuming:%d", current_codec);
257}
258
Miika Pekkarinen65d43a22005-08-28 19:55:30 +0000259#ifdef HAVE_ADJUSTABLE_CPU_FREQ
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000260static void voice_boost_cpu(bool state)
261{
Miika Pekkarinen65d43a22005-08-28 19:55:30 +0000262 static bool voice_cpu_boosted = false;
263
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000264 if (state != voice_cpu_boosted)
265 {
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000266 cpu_boost(state);
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000267 voice_cpu_boosted = state;
268 }
269}
Miika Pekkarinen65d43a22005-08-28 19:55:30 +0000270#else
271#define voice_boost_cpu(state) do { } while(0)
272#endif
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000273
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000274bool codec_pcmbuf_insert_split_callback(void *ch1, void *ch2,
275 long length)
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000276{
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000277 char* src[2];
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000278 char *dest;
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000279 long input_size;
280 long output_size;
281
282 src[0] = ch1;
283 src[1] = ch2;
284
Miika Pekkarinenacdcdb02005-09-10 21:02:42 +0000285 while (paused)
286 {
287 if (pcm_is_playing())
288 pcm_play_pause(false);
289 sleep(1);
290 if (ci.stop_codec || ci.reload_codec || ci.seek_time)
291 return true;
292 }
293
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000294 if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
295 {
296 length *= 2; /* Length is per channel */
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000297 }
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000298
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000299 while (length > 0) {
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +0000300 /* This will prevent old audio from playing when skipping tracks. */
Miika Pekkarinenbdf558c2005-10-22 21:07:35 +0000301 if ((ci.reload_codec || ci.stop_codec) && current_codec != CODEC_IDX_VOICE)
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +0000302 return true;
303
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000304 while ((dest = pcmbuf_request_buffer(dsp_output_size(length),
305 &output_size)) == NULL) {
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +0000306 sleep(1);
Miika Pekkarinenbdf558c2005-10-22 21:07:35 +0000307 if ((ci.reload_codec || ci.stop_codec) && current_codec != CODEC_IDX_VOICE)
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +0000308 return true;
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000309 }
310
Linus Nielsen Feltzing591d2892005-08-10 23:17:55 +0000311 /* Get the real input_size for output_size bytes, guarding
312 * against resampling buffer overflows. */
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000313 input_size = dsp_input_size(output_size);
Linus Nielsen Feltzing591d2892005-08-10 23:17:55 +0000314 if (input_size > length) {
315 DEBUGF("Error: dsp_input_size(%ld=dsp_output_size(%ld))=%ld > %ld\n",
316 output_size, length, input_size, length);
317 input_size = length;
318 }
319
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000320 if (input_size <= 0) {
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000321 pcmbuf_flush_buffer(0);
Linus Nielsen Feltzing591d2892005-08-10 23:17:55 +0000322 DEBUGF("Warning: dsp_input_size(%ld=dsp_output_size(%ld))=%ld <= 0\n",
323 output_size, length, input_size);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000324 /* should we really continue, or should we break?
325 * We should probably continue because calling pcmbuf_flush_buffer(0)
326 * will wrap the buffer if it was fully filled and so next call to
327 * pcmbuf_request_buffer should give the requested output_size. */
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000328 continue;
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000329 }
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000330
331 output_size = dsp_process(dest, src, input_size);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000332
333 /* Hotswap between audio and voice codecs as necessary. */
334 switch (current_codec)
335 {
336 case CODEC_IDX_AUDIO:
337 pcmbuf_flush_buffer(output_size);
338 if (voice_is_playing && pcmbuf_usage() > 30
339 && pcmbuf_mix_usage() < 20)
340 {
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000341 voice_boost_cpu(true);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000342 swap_codec();
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000343 voice_boost_cpu(false);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000344 }
345 break ;
346
347 case CODEC_IDX_VOICE:
348 if (audio_codec_loaded) {
349 pcmbuf_mix(dest, output_size);
350 if ((pcmbuf_usage() < 10)
351 || pcmbuf_mix_usage() > 70)
352 swap_codec();
353 } else {
354 pcmbuf_flush_buffer(output_size);
355 }
356 break ;
357 }
358
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000359 length -= input_size;
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000360 }
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000361
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000362 return true;
363}
364
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000365bool codec_pcmbuf_insert_callback(char *buf, long length)
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000366{
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000367 /* TODO: The audiobuffer API should probably be updated, and be based on
368 * pcmbuf_insert_split().
369 */
370 long real_length = length;
371
372 if (dsp_stereo_mode() == STEREO_NONINTERLEAVED)
373 {
374 length /= 2; /* Length is per channel */
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000375 }
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000376
377 /* Second channel is only used for non-interleaved stereo. */
378 return codec_pcmbuf_insert_split_callback(buf, buf + (real_length / 2),
379 length);
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000380}
381
Miika Pekkarinen85f49732005-06-27 19:29:49 +0000382void* get_codec_memory_callback(long *size)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000383{
384 *size = MALLOC_BUFSIZE;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000385 if (voice_codec_loaded)
386 return &audiobuf[talk_get_bufsize()];
387
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000388 return &audiobuf[0];
389}
390
391void codec_set_elapsed_callback(unsigned int value)
392{
393 unsigned int latency;
394
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000395 if (ci.stop_codec || current_codec == CODEC_IDX_VOICE)
Miika Pekkarinencf18f962005-06-20 06:49:21 +0000396 return ;
397
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000398 latency = pcmbuf_get_latency();
Miika Pekkarinen92ea04a2005-06-15 12:53:50 +0000399
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000400 if (value < latency) {
401 cur_ti->id3.elapsed = 0;
402 } else if (value - latency > cur_ti->id3.elapsed
403 || value - latency < cur_ti->id3.elapsed - 2) {
404 cur_ti->id3.elapsed = value - latency;
405 }
406}
407
Ryan Jacksond1917562005-07-12 16:45:38 +0000408void codec_set_offset_callback(unsigned int value)
409{
410 unsigned int latency;
411
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000412 if (ci.stop_codec || current_codec == CODEC_IDX_VOICE)
Ryan Jacksond1917562005-07-12 16:45:38 +0000413 return ;
414
Miika Pekkarineneab434c2005-07-22 06:32:55 +0000415 latency = pcmbuf_get_latency() * cur_ti->id3.bitrate / 8;
Ryan Jacksond1917562005-07-12 16:45:38 +0000416
417 if (value < latency) {
418 cur_ti->id3.offset = 0;
Linus Nielsen Feltzing40a19882005-08-10 22:54:59 +0000419 } else {
Ryan Jacksond1917562005-07-12 16:45:38 +0000420 cur_ti->id3.offset = value - latency;
421 }
422}
423
Miika Pekkarinen85f49732005-06-27 19:29:49 +0000424long codec_filebuf_callback(void *ptr, long size)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000425{
426 char *buf = (char *)ptr;
427 int copy_n;
428 int part_n;
429
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000430 if (ci.stop_codec || !playing || current_codec == CODEC_IDX_VOICE)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000431 return 0;
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000432
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000433 copy_n = MIN((off_t)size, (off_t)cur_ti->available + cur_ti->filerem);
434
435 while (copy_n > cur_ti->available) {
436 yield();
Miika Pekkarinen63788782005-07-30 20:46:38 +0000437 if (ci.stop_codec || ci.reload_codec)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000438 return 0;
439 }
440
441 if (copy_n == 0)
442 return 0;
443
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000444 part_n = MIN(copy_n, filebuflen - buf_ridx);
445 memcpy(buf, &filebuf[buf_ridx], part_n);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000446 if (part_n < copy_n) {
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000447 memcpy(&buf[part_n], &filebuf[0], copy_n - part_n);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000448 }
449
450 buf_ridx += copy_n;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000451 if (buf_ridx >= filebuflen)
452 buf_ridx -= filebuflen;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000453 ci.curpos += copy_n;
454 cur_ti->available -= copy_n;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000455 filebufused -= copy_n;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000456
457 return copy_n;
458}
459
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000460void* voice_request_data(long *realsize, long reqsize)
461{
462 while (queue_empty(&voice_codec_queue) && (voice_remaining == 0
463 || voicebuf == NULL) && !ci_voice.stop_codec)
464 {
465 yield();
466 if (audio_codec_loaded && (pcmbuf_usage() < 30
467 || !voice_is_playing || voicebuf == NULL))
468 {
469 swap_codec();
470 }
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000471 else if (!voice_is_playing)
472 {
473 voice_boost_cpu(false);
474 if (!pcm_is_playing())
475 pcmbuf_boost(false);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000476 sleep(HZ/16);
Miika Pekkarinen29aad552005-08-28 14:16:03 +0000477 }
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000478
479 if (voice_remaining)
480 {
481 voice_is_playing = true;
482 break ;
483 }
484
485 if (voice_getmore != NULL)
486 {
487 voice_getmore((unsigned char **)&voicebuf, (int *)&voice_remaining);
488
489 if (!voice_remaining)
490 {
491 voice_is_playing = false;
492 /* Force pcm playback. */
493 pcmbuf_play_start();
494 }
495 }
496 }
497
498 if (reqsize < 0)
499 reqsize = 0;
500
501 voice_is_playing = true;
502 *realsize = voice_remaining;
503 if (*realsize > reqsize)
504 *realsize = reqsize;
505
506 if (*realsize == 0)
507 return NULL;
508
509 return voicebuf;
510}
511
Miika Pekkarinen85f49732005-06-27 19:29:49 +0000512void* codec_request_buffer_callback(long *realsize, long reqsize)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000513{
Miika Pekkarinen85f49732005-06-27 19:29:49 +0000514 long part_n;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000515
516 /* Voice codec. */
517 if (current_codec == CODEC_IDX_VOICE) {
518 return voice_request_data(realsize, reqsize);
519 }
520
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000521 if (ci.stop_codec || !playing) {
522 *realsize = 0;
523 return NULL;
524 }
525
526 *realsize = MIN((off_t)reqsize, (off_t)cur_ti->available + cur_ti->filerem);
527 if (*realsize == 0) {
528 return NULL;
529 }
530
531 while ((int)*realsize > cur_ti->available) {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000532 yield();
Miika Pekkarinen63788782005-07-30 20:46:38 +0000533 if (ci.stop_codec || ci.reload_codec) {
Miika Pekkarinen9ff373c2005-06-10 20:29:35 +0000534 *realsize = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000535 return NULL;
536 }
537 }
538
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000539 part_n = MIN((int)*realsize, filebuflen - buf_ridx);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000540 if (part_n < *realsize) {
541 part_n += GUARD_BUFSIZE;
542 if (part_n < *realsize)
543 *realsize = part_n;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000544 memcpy(&filebuf[filebuflen], &filebuf[0], *realsize -
545 (filebuflen - buf_ridx));
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000546 }
547
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000548 return (char *)&filebuf[buf_ridx];
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000549}
550
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000551static bool rebuffer_and_seek(int newpos)
552{
553 int fd;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000554
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000555 logf("Re-buffering song");
556 mutex_lock(&mutex_bufferfill);
557
558 /* (Re-)open current track's file handle. */
559 fd = open(playlist_peek(0), O_RDONLY);
560 if (fd < 0) {
561 logf("Open failed!");
562 mutex_unlock(&mutex_bufferfill);
563 return false;
564 }
565 if (current_fd >= 0)
566 close(current_fd);
567 current_fd = fd;
568
569 /* Clear codec buffer. */
570 audio_invalidate_tracks();
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000571 filebufused = 0;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000572 buf_ridx = buf_widx = 0;
573 cur_ti->filerem = cur_ti->filesize - newpos;
574 cur_ti->filepos = newpos;
575 cur_ti->start_pos = newpos;
576 ci.curpos = newpos;
577 cur_ti->available = 0;
578 lseek(current_fd, newpos, SEEK_SET);
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000579
580 mutex_unlock(&mutex_bufferfill);
581
Miika Pekkarinen2724d0b2005-07-01 21:00:02 +0000582 while (cur_ti->available == 0 && cur_ti->filerem > 0) {
583 yield();
Miika Pekkarinen63788782005-07-30 20:46:38 +0000584 if (ci.stop_codec || ci.reload_codec)
Miika Pekkarinen2724d0b2005-07-01 21:00:02 +0000585 return false;
586 }
Linus Nielsen Feltzingc4b7c672005-07-11 06:47:35 +0000587
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000588 return true;
589}
590
Miika Pekkarinen85f49732005-06-27 19:29:49 +0000591void codec_advance_buffer_callback(long amount)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000592{
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000593 if (current_codec == CODEC_IDX_VOICE) {
594 //logf("voice ad.buf:%d", amount);
595 amount = MAX(0, MIN(amount, voice_remaining));
596 voicebuf += amount;
597 voice_remaining -= amount;
598
599 return ;
600 }
601
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000602 if (amount > cur_ti->available + cur_ti->filerem)
Miika Pekkarinen5899ed52005-06-08 10:33:01 +0000603 amount = cur_ti->available + cur_ti->filerem;
Miika Pekkarinen58af47c2005-06-13 22:09:12 +0000604
Miika Pekkarinen5755ffe2005-07-20 18:50:07 +0000605 if (amount > cur_ti->available) {
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000606 if (!rebuffer_and_seek(ci.curpos + amount))
607 ci.stop_codec = true;
608 return ;
Miika Pekkarinen5899ed52005-06-08 10:33:01 +0000609 }
Miika Pekkarinen58af47c2005-06-13 22:09:12 +0000610
611 buf_ridx += amount;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000612 if (buf_ridx >= filebuflen)
613 buf_ridx -= filebuflen;
Miika Pekkarinen58af47c2005-06-13 22:09:12 +0000614 cur_ti->available -= amount;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000615 filebufused -= amount;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000616 ci.curpos += amount;
Ryan Jacksond1917562005-07-12 16:45:38 +0000617 codec_set_offset_callback(ci.curpos);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000618}
619
620void codec_advance_buffer_loc_callback(void *ptr)
621{
Miika Pekkarinen85f49732005-06-27 19:29:49 +0000622 long amount;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000623
624 if (current_codec == CODEC_IDX_VOICE)
625 amount = (int)ptr - (int)voicebuf;
626 else
627 amount = (int)ptr - (int)&filebuf[buf_ridx];
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000628 codec_advance_buffer_callback(amount);
629}
630
631off_t codec_mp3_get_filepos_callback(int newtime)
632{
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000633 off_t newpos;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000634
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000635 cur_ti->id3.elapsed = newtime;
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000636 newpos = mp3_get_file_pos();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000637
638 return newpos;
639}
640
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +0000641void codec_seek_complete_callback(void)
642{
Thom Johansen849c2bb2005-10-10 18:20:40 +0000643 /* assume we're called from non-voice codec, as they shouldn't seek */
644 ci.seek_time = 0;
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +0000645 pcmbuf_flush_audio();
646}
647
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000648bool codec_seek_buffer_callback(off_t newpos)
649{
650 int difference;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000651
652 if (current_codec == CODEC_IDX_VOICE)
653 return false;
654
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000655 if (newpos < 0)
656 newpos = 0;
657
658 if (newpos >= cur_ti->filesize)
659 newpos = cur_ti->filesize - 1;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000660
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000661 difference = newpos - ci.curpos;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000662 /* Seeking forward */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000663 if (difference >= 0) {
664 logf("seek: +%d", difference);
665 codec_advance_buffer_callback(difference);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000666 return true;
667 }
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000668
669 /* Seeking backward */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000670 difference = -difference;
671 if (ci.curpos - difference < 0)
672 difference = ci.curpos;
673
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000674 /* We need to reload the song. */
675 if (newpos < cur_ti->start_pos)
676 return rebuffer_and_seek(newpos);
677
678 /* Seeking inside buffer space. */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000679 logf("seek: -%d", difference);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000680 filebufused += difference;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000681 cur_ti->available += difference;
682 buf_ridx -= difference;
683 if (buf_ridx < 0)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000684 buf_ridx = filebuflen + buf_ridx;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000685 ci.curpos -= difference;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000686
687 return true;
688}
689
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000690static void set_filebuf_watermark(int seconds)
691{
692 long bytes;
693
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000694 if (current_codec == CODEC_IDX_VOICE)
695 return ;
Magnus Holmgren62634a32005-10-16 08:01:02 +0000696
697 if (!filebuf)
698 return; /* Audio buffers not yet set up */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000699
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000700 bytes = MAX((int)cur_ti->id3.bitrate * seconds * (1000/8), conf_watermark);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000701 bytes = MIN(bytes, filebuflen / 2);
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000702 conf_watermark = bytes;
703}
704
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +0000705void codec_configure_callback(int setting, void *value)
706{
707 switch (setting) {
708 case CODEC_SET_FILEBUF_WATERMARK:
709 conf_watermark = (unsigned int)value;
Miika Pekkarinenf090dc32005-07-21 11:44:00 +0000710 set_filebuf_watermark(buffer_margin);
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +0000711 break;
712
713 case CODEC_SET_FILEBUF_CHUNKSIZE:
714 conf_filechunk = (unsigned int)value;
715 break;
716
717 case CODEC_SET_FILEBUF_LIMIT:
718 conf_bufferlimit = (unsigned int)value;
719 break;
720
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000721 case CODEC_DSP_ENABLE:
722 if ((bool)value)
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000723 ci.pcmbuf_insert = codec_pcmbuf_insert_callback;
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000724 else
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000725 ci.pcmbuf_insert = pcmbuf_insert_buffer;
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000726 break ;
727
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +0000728 default:
Miika Pekkarinen65b840d2005-06-26 20:01:33 +0000729 if (!dsp_configure(setting, value)) {
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000730 logf("Illegal key: %d", setting);
Miika Pekkarinen65b840d2005-06-26 20:01:33 +0000731 }
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +0000732 }
733}
734
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000735void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3,
736 bool last_track))
737{
738 track_buffer_callback = handler;
739}
740
741void audio_set_track_unbuffer_event(void (*handler)(struct mp3entry *id3,
Miika Pekkarinen9bde0382005-07-03 18:36:24 +0000742 bool last_track))
Miika Pekkarinend54811f2005-07-02 16:52:30 +0000743{
744 track_unbuffer_callback = handler;
745}
746
Jens Arnolda88d0762005-08-18 06:05:15 +0000747void audio_set_track_changed_event(void (*handler)(struct mp3entry *id3))
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +0000748{
749 track_changed_callback = handler;
750}
751
Miika Pekkarinenc3fed622005-06-15 18:59:04 +0000752void codec_track_changed(void)
753{
754 track_changed = true;
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +0000755 queue_post(&audio_queue, AUDIO_TRACK_CHANGED, 0);
Miika Pekkarinenc3fed622005-06-15 18:59:04 +0000756}
757
Miika Pekkarinen3e33a0f2005-07-05 15:51:59 +0000758/* Give codecs or file buffering the right amount of processing time
759 to prevent pcm audio buffer from going empty. */
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000760void yield_codecs(void)
761{
Miika Pekkarinen61716dd2005-06-07 18:03:33 +0000762 yield();
Miika Pekkarinenacdcdb02005-09-10 21:02:42 +0000763 if (!pcm_is_playing() && !paused)
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000764 sleep(5);
Miika Pekkarinen20b38972005-07-13 12:48:22 +0000765 while ((pcmbuf_is_crossfade_active() || pcmbuf_is_lowdata())
Miika Pekkarinen3e33a0f2005-07-05 15:51:59 +0000766 && !ci.stop_codec && playing && queue_empty(&audio_queue)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000767 && filebufused > (128*1024))
Miika Pekkarinenb142a582005-10-30 08:51:47 +0000768 sleep(1);
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000769}
770
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +0000771/* FIXME: This code should be made more generic and move to metadata.c */
772void strip_id3v1_tag(void)
773{
774 int i;
775 static const unsigned char tag[] = "TAG";
776 int tagptr;
777 bool found = true;
778
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000779 if (filebufused >= 128)
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +0000780 {
781 tagptr = buf_widx - 128;
782 if (tagptr < 0)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000783 tagptr += filebuflen;
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +0000784
785 for(i = 0;i < 3;i++)
786 {
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000787 if(tagptr >= filebuflen)
788 tagptr -= filebuflen;
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +0000789
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000790 if(filebuf[tagptr] != tag[i])
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +0000791 {
792 found = false;
793 break;
794 }
795
796 tagptr++;
797 }
798
799 if(found)
800 {
801 /* Skip id3v1 tag */
802 logf("Skipping ID3v1 tag\n");
803 buf_widx -= 128;
804 tracks[track_widx].available -= 128;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000805 filebufused -= 128;
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +0000806 }
807 }
808}
809
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000810void audio_fill_file_buffer(void)
811{
Miika Pekkarinen85f49732005-06-27 19:29:49 +0000812 long i, size;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000813 int rc;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000814
Miika Pekkarinen431e8132005-06-19 18:41:53 +0000815 if (current_fd < 0)
816 return ;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000817
818 /* Throw away buffered codec. */
819 if (tracks[track_widx].start_pos != 0)
820 tracks[track_widx].codecsize = 0;
821
Miika Pekkarinenb142a582005-10-30 08:51:47 +0000822 mutex_lock(&mutex_bufferfill);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000823 i = 0;
Miika Pekkarinen431e8132005-06-19 18:41:53 +0000824 size = MIN(tracks[track_widx].filerem, AUDIO_FILL_CYCLE);
825 while (i < size) {
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000826 /* Give codecs some processing time. */
827 yield_codecs();
828
Miika Pekkarinen431e8132005-06-19 18:41:53 +0000829 if (fill_bytesleft == 0)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000830 break ;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000831 rc = MIN(conf_filechunk, filebuflen - buf_widx);
Miika Pekkarinen3e33f852005-07-10 06:58:02 +0000832 rc = MIN(rc, fill_bytesleft);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000833 rc = read(current_fd, &filebuf[buf_widx], rc);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000834 if (rc <= 0) {
835 tracks[track_widx].filerem = 0;
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +0000836 strip_id3v1_tag();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000837 break ;
838 }
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000839
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000840 buf_widx += rc;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000841 if (buf_widx >= filebuflen)
842 buf_widx -= filebuflen;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000843 i += rc;
844 tracks[track_widx].available += rc;
Miika Pekkarinen3eb962d2005-07-07 07:15:05 +0000845 tracks[track_widx].filerem -= rc;
846 tracks[track_widx].filepos += rc;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000847 filebufused += rc;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000848 fill_bytesleft -= rc;
849 }
Miika Pekkarinenb142a582005-10-30 08:51:47 +0000850 mutex_unlock(&mutex_bufferfill);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000851
Miika Pekkarinen431e8132005-06-19 18:41:53 +0000852 /*logf("Filled:%d/%d", tracks[track_widx].available,
853 tracks[track_widx].filerem);*/
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000854}
855
856bool loadcodec(const char *trackname, bool start_play)
857{
858 char msgbuf[80];
859 off_t size;
Jens Arnold579210d2005-09-01 17:03:09 +0000860 unsigned int filetype;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000861 int fd;
862 int i, rc;
863 const char *codec_path;
864 int copy_n;
865 int prev_track;
866
867 filetype = probe_file_format(trackname);
868 switch (filetype) {
869 case AFMT_OGG_VORBIS:
870 logf("Codec: Vorbis");
871 codec_path = CODEC_VORBIS;
872 break;
Jens Arnolde2cd5812005-06-18 09:51:10 +0000873 case AFMT_MPA_L1:
Miika Pekkarinen5899ed52005-06-08 10:33:01 +0000874 case AFMT_MPA_L2:
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000875 case AFMT_MPA_L3:
Jens Arnolde2cd5812005-06-18 09:51:10 +0000876 logf("Codec: MPA L1/L2/L3");
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000877 codec_path = CODEC_MPA_L3;
878 break;
879 case AFMT_PCM_WAV:
880 logf("Codec: PCM WAV");
881 codec_path = CODEC_WAV;
882 break;
883 case AFMT_FLAC:
884 logf("Codec: FLAC");
885 codec_path = CODEC_FLAC;
886 break;
Dave Chapman55ed7d72005-06-11 10:08:17 +0000887 case AFMT_A52:
888 logf("Codec: A52");
889 codec_path = CODEC_A52;
890 break;
Dave Chapman2f2d7d42005-06-11 12:40:27 +0000891 case AFMT_MPC:
892 logf("Codec: Musepack");
893 codec_path = CODEC_MPC;
894 break;
Dave Bryant57c6f6e2005-06-13 06:00:35 +0000895 case AFMT_WAVPACK:
896 logf("Codec: WAVPACK");
897 codec_path = CODEC_WAVPACK;
898 break;
Dave Chapman139c1cb2005-09-22 21:55:37 +0000899 case AFMT_ALAC:
900 logf("Codec: ALAC");
901 codec_path = CODEC_ALAC;
902 break;
Dave Chapmancea6d0c2005-10-31 20:56:29 +0000903 case AFMT_AAC:
904 logf("Codec: AAC");
905 codec_path = CODEC_AAC;
906 break;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000907 default:
908 logf("Codec: Unsupported");
909 snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", trackname);
910 splash(HZ*2, true, msgbuf);
911 codec_path = NULL;
912 }
913
Dave Chapman961c9a32005-06-18 16:24:27 +0000914 tracks[track_widx].id3.codectype = filetype;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000915 tracks[track_widx].codecsize = 0;
916 if (codec_path == NULL)
917 return false;
918
919 if (!start_play) {
920 prev_track = track_widx - 1;
921 if (prev_track < 0)
922 prev_track = MAX_TRACK-1;
Dave Chapman961c9a32005-06-18 16:24:27 +0000923 if (track_count > 0 && filetype == tracks[prev_track].id3.codectype) {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000924 logf("Reusing prev. codec");
925 return true;
926 }
927 } else {
928 /* Load the codec directly from disk and save some memory. */
929 cur_ti = &tracks[track_widx];
930 ci.filesize = cur_ti->filesize;
931 ci.id3 = (struct mp3entry *)&cur_ti->id3;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000932 ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready;
933 ci.curpos = 0;
934 playing = true;
935 logf("Starting codec");
936 queue_post(&codec_queue, CODEC_LOAD_DISK, (void *)codec_path);
937 return true;
938 }
939
940 fd = open(codec_path, O_RDONLY);
941 if (fd < 0) {
942 logf("Codec doesn't exist!");
943 snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", codec_path);
944 splash(HZ*2, true, msgbuf);
945 return false;
946 }
947
948 size = filesize(fd);
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +0000949 if ((off_t)fill_bytesleft < size + conf_watermark) {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000950 logf("Not enough space");
Miika Pekkarinene3617492005-07-17 21:44:58 +0000951 /* Set codectype back to zero to indicate no codec was loaded. */
952 tracks[track_widx].id3.codectype = 0;
Miika Pekkarinen431e8132005-06-19 18:41:53 +0000953 fill_bytesleft = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000954 close(fd);
955 return false;
956 }
957
958 i = 0;
959 while (i < size) {
Miika Pekkarinen82c29272005-06-07 06:34:54 +0000960 yield_codecs();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000961
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000962 copy_n = MIN(conf_filechunk, filebuflen - buf_widx);
963 rc = read(fd, &filebuf[buf_widx], copy_n);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000964 if (rc < 0)
965 return false;
966 buf_widx += rc;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000967 filebufused += rc;
Miika Pekkarinen3e33f852005-07-10 06:58:02 +0000968 fill_bytesleft -= rc;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000969 if (buf_widx >= filebuflen)
970 buf_widx -= filebuflen;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000971 i += rc;
972 }
973 close(fd);
974 logf("Done: %dB", i);
975
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000976 tracks[track_widx].codecsize = size;
977
978 return true;
979}
980
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +0000981bool read_next_metadata(void)
982{
983 int fd;
984 char *trackname;
985 int next_track;
986 int status;
987
Miika Pekkarinen527ce402005-07-10 08:46:12 +0000988 next_track = track_widx;
989 if (tracks[track_widx].taginfo_ready)
990 next_track++;
991
992 if (next_track >= MAX_TRACK)
993 next_track -= MAX_TRACK;
994
995 if (tracks[next_track].taginfo_ready)
996 return true;
997
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +0000998 trackname = playlist_peek(last_peek_offset + 1);
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +0000999 if (!trackname)
1000 return false;
1001
1002 fd = open(trackname, O_RDONLY);
1003 if (fd < 0)
1004 return false;
Miika Pekkarinen527ce402005-07-10 08:46:12 +00001005
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001006 /* Start buffer refilling also because we need to spin-up the disk. */
1007 filling = true;
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001008 tracks[next_track].id3.codectype = probe_file_format(trackname);
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001009 status = get_metadata(&tracks[next_track],fd,trackname,v1first);
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001010 tracks[next_track].id3.codectype = 0;
Miika Pekkarinen8ad60cc2005-07-04 06:06:30 +00001011 track_changed = true;
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001012 close(fd);
1013
1014 return status;
1015}
1016
Miika Pekkarinena9ac3d12005-06-08 10:45:40 +00001017bool audio_load_track(int offset, bool start_play, int peek_offset)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001018{
1019 char *trackname;
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001020 int fd = -1;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001021 off_t size;
1022 int rc, i;
1023 int copy_n;
Miika Pekkarinende3b04e2005-06-29 14:46:27 +00001024
Miika Pekkarinenb288dda2005-07-10 08:38:16 +00001025 /* Stop buffer filling if there is no free track entries.
1026 Don't fill up the last track entry (we wan't to store next track
1027 metadata there). */
1028 if (track_count >= MAX_TRACK - 1) {
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001029 fill_bytesleft = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001030 return false;
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001031 }
1032
1033 /* Don't start loading track if the current write position already
1034 contains a BUFFERED track. The entry may contain the metadata
1035 which is ok. */
1036 if (tracks[track_widx].filesize != 0)
1037 return false;
1038
Miika Pekkarinen7e58bd72005-07-30 17:53:56 +00001039 last_index = playlist_get_display_index();
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001040
1041 peek_again:
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001042 /* Get track name from current playlist read position. */
Miika Pekkarinende3b04e2005-06-29 14:46:27 +00001043 logf("Buffering track:%d/%d", track_widx, track_ridx);
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001044 /* Handle broken playlists. */
1045 while ( (trackname = playlist_peek(peek_offset)) != NULL) {
1046 fd = open(trackname, O_RDONLY);
1047 if (fd < 0) {
1048 logf("Open failed");
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001049 /* Skip invalid entry from playlist. */
1050 playlist_skip_entry(NULL, peek_offset);
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001051 continue ;
1052 }
1053 break ;
1054 }
1055
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001056 if (!trackname) {
Miika Pekkarinenc3fed622005-06-15 18:59:04 +00001057 logf("End-of-playlist");
1058 conf_watermark = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001059 return false;
1060 }
1061
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001062 /* Initialize track entry. */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001063 size = filesize(fd);
1064 tracks[track_widx].filerem = size;
1065 tracks[track_widx].filesize = size;
1066 tracks[track_widx].filepos = 0;
1067 tracks[track_widx].available = 0;
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001068 //tracks[track_widx].taginfo_ready = false;
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001069 tracks[track_widx].playlist_offset = peek_offset;
1070 last_peek_offset = peek_offset;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001071
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001072 if (buf_widx >= filebuflen)
1073 buf_widx -= filebuflen;
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001074
1075 /* Set default values */
1076 if (start_play) {
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001077 int last_codec = current_codec;
1078 current_codec = CODEC_IDX_AUDIO;
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001079 conf_bufferlimit = 0;
1080 conf_watermark = AUDIO_DEFAULT_WATERMARK;
1081 conf_filechunk = AUDIO_DEFAULT_FILECHUNK;
Miika Pekkarinend8cb7032005-06-26 19:41:29 +00001082 dsp_configure(DSP_RESET, 0);
1083 ci.configure(CODEC_DSP_ENABLE, false);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001084 current_codec = last_codec;
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001085 }
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001086
1087 /* Load the codec. */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001088 tracks[track_widx].codecbuf = &filebuf[buf_widx];
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001089 if (!loadcodec(trackname, start_play)) {
1090 close(fd);
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001091 /* Stop buffer filling if codec load failed. */
1092 fill_bytesleft = 0;
Miika Pekkarinene3617492005-07-17 21:44:58 +00001093 /* Set filesize to zero to indicate no file was loaded. */
1094 tracks[track_widx].filesize = 0;
1095 tracks[track_widx].filerem = 0;
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001096
1097 /* Try skipping to next track. */
1098 if (fill_bytesleft > 0) {
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001099 /* Skip invalid entry from playlist. */
1100 playlist_skip_entry(NULL, peek_offset);
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001101 goto peek_again;
1102 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001103 return false;
1104 }
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001105 // tracks[track_widx].filebuf = &filebuf[buf_widx];
Miika Pekkarinend94cba62005-06-13 15:26:53 +00001106 tracks[track_widx].start_pos = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001107
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001108 /* Get track metadata if we don't already have it. */
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001109 if (!tracks[track_widx].taginfo_ready) {
1110 if (!get_metadata(&tracks[track_widx],fd,trackname,v1first)) {
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00001111 logf("Metadata error!");
1112 tracks[track_widx].filesize = 0;
Linus Nielsen Feltzing064c7af2005-08-10 23:04:34 +00001113 tracks[track_widx].filerem = 0;
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001114 close(fd);
Miika Pekkarinenc52f7f12005-10-21 06:40:45 +00001115 /* Skip invalid entry from playlist. */
1116 playlist_skip_entry(NULL, peek_offset);
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001117 goto peek_again;
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001118 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001119 }
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00001120 set_filebuf_watermark(buffer_margin);
Miika Pekkarinenaa30f042005-07-05 07:25:55 +00001121 tracks[track_widx].id3.elapsed = 0;
Dave Chapman3ad485b2005-06-14 22:27:57 +00001122
1123 /* Starting playback from an offset is only support in MPA at the moment */
1124 if (offset > 0) {
Miika Pekkarinen3eb962d2005-07-07 07:15:05 +00001125 switch (tracks[track_widx].id3.codectype) {
1126 case AFMT_MPA_L2:
1127 case AFMT_MPA_L3:
1128 lseek(fd, offset, SEEK_SET);
1129 tracks[track_widx].id3.offset = offset;
1130 mp3_set_elapsed(&tracks[track_widx].id3);
1131 tracks[track_widx].filepos = offset;
1132 tracks[track_widx].filerem = tracks[track_widx].filesize - offset;
1133 ci.curpos = offset;
1134 tracks[track_widx].start_pos = offset;
1135 break;
1136
1137 case AFMT_WAVPACK:
1138 lseek(fd, offset, SEEK_SET);
1139 tracks[track_widx].id3.offset = offset;
1140 tracks[track_widx].id3.elapsed = tracks[track_widx].id3.length / 2;
1141 tracks[track_widx].filepos = offset;
1142 tracks[track_widx].filerem = tracks[track_widx].filesize - offset;
1143 ci.curpos = offset;
1144 tracks[track_widx].start_pos = offset;
1145 break;
Linus Nielsen Feltzingc4b7c672005-07-11 06:47:35 +00001146 case AFMT_OGG_VORBIS:
Ryan Jacksonb301b432005-07-28 18:43:33 +00001147 case AFMT_FLAC:
Linus Nielsen Feltzingc4b7c672005-07-11 06:47:35 +00001148 tracks[track_widx].id3.offset = offset;
1149 break;
Miika Pekkarinen3eb962d2005-07-07 07:15:05 +00001150 }
1151 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001152
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001153 if (start_play) {
1154 track_count++;
Miika Pekkarinen3e33f852005-07-10 06:58:02 +00001155 codec_track_changed();
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001156 }
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001157
1158 /* Do some initial file buffering. */
Miika Pekkarinen74528fc2005-10-30 08:27:34 +00001159 mutex_lock(&mutex_bufferfill);
Miika Pekkarinend94cba62005-06-13 15:26:53 +00001160 i = tracks[track_widx].start_pos;
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001161 size = MIN(size, AUDIO_FILL_CYCLE);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001162 while (i < size) {
1163 /* Give codecs some processing time to prevent glitches. */
Miika Pekkarinen82c29272005-06-07 06:34:54 +00001164 yield_codecs();
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00001165
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001166 if (fill_bytesleft == 0)
1167 break ;
1168
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001169 copy_n = MIN(conf_filechunk, filebuflen - buf_widx);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001170 copy_n = MIN(size - i, copy_n);
1171 copy_n = MIN((int)fill_bytesleft, copy_n);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001172 rc = read(fd, &filebuf[buf_widx], copy_n);
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00001173 if (rc < copy_n) {
Miika Pekkarinenfe468b12005-06-09 19:31:35 +00001174 logf("File error!");
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00001175 tracks[track_widx].filesize = 0;
1176 tracks[track_widx].filerem = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001177 close(fd);
Miika Pekkarinen74528fc2005-10-30 08:27:34 +00001178 mutex_unlock(&mutex_bufferfill);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001179 return false;
1180 }
1181 buf_widx += rc;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001182 if (buf_widx >= filebuflen)
1183 buf_widx -= filebuflen;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001184 i += rc;
1185 tracks[track_widx].available += rc;
1186 tracks[track_widx].filerem -= rc;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001187 filebufused += rc;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001188 fill_bytesleft -= rc;
1189 }
Miika Pekkarinen74528fc2005-10-30 08:27:34 +00001190 mutex_unlock(&mutex_bufferfill);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001191
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001192 if (!start_play)
1193 track_count++;
1194
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001195 tracks[track_widx].filepos = i;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001196
Miika Pekkarinenef72f992005-06-14 14:36:46 +00001197 if (current_fd >= 0) {
1198 close(current_fd);
1199 current_fd = -1;
1200 }
1201
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001202 /* Leave the file handle open for faster buffer refill. */
1203 if (tracks[track_widx].filerem != 0) {
1204 current_fd = fd;
1205 logf("Partially buf:%d", tracks[track_widx].available);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001206 } else {
1207 logf("Completely buf.");
1208 close(fd);
Linus Nielsen Feltzingdf807982005-07-07 09:53:02 +00001209
1210 strip_id3v1_tag();
1211
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001212 if (++track_widx >= MAX_TRACK) {
1213 track_widx = 0;
1214 }
1215 tracks[track_widx].filerem = 0;
1216 }
1217
1218 return true;
1219}
1220
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001221void audio_play_start(int offset)
1222{
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001223 if (current_fd >= 0) {
1224 close(current_fd);
1225 current_fd = -1;
1226 }
Miika Pekkarinen0d63cbb2005-07-10 20:37:36 +00001227
Miika Pekkarinenfe468b12005-06-09 19:31:35 +00001228 memset(&tracks, 0, sizeof(struct track_info) * MAX_TRACK);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001229 sound_set(SOUND_VOLUME, global_settings.volume);
1230 track_count = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001231 track_widx = 0;
1232 track_ridx = 0;
1233 buf_ridx = 0;
1234 buf_widx = 0;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001235 filebufused = 0;
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001236 pcmbuf_set_boost_mode(true);
Miika Pekkarinencf18f962005-06-20 06:49:21 +00001237
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001238 fill_bytesleft = filebuflen;
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001239 filling = true;
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001240 last_peek_offset = -1;
Miika Pekkarinen74528fc2005-10-30 08:27:34 +00001241
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001242 if (audio_load_track(offset, true, 0)) {
Miika Pekkarinend54811f2005-07-02 16:52:30 +00001243 if (track_buffer_callback) {
1244 cur_ti->event_sent = true;
1245 track_buffer_callback(&cur_ti->id3, true);
1246 }
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001247 } else {
1248 logf("Failure");
1249 }
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00001250
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001251 pcmbuf_set_boost_mode(false);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001252}
1253
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001254void audio_clear_track_entries(bool buffered_only)
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001255{
Miika Pekkarinen9bde0382005-07-03 18:36:24 +00001256 int cur_idx, event_count;
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001257 int i;
1258
1259 cur_idx = track_widx;
Miika Pekkarinen9bde0382005-07-03 18:36:24 +00001260 event_count = 0;
1261 for (i = 0; i < MAX_TRACK - track_count; i++) {
1262 if (++cur_idx >= MAX_TRACK)
1263 cur_idx = 0;
1264
1265 if (tracks[cur_idx].event_sent)
1266 event_count++;
1267
1268 if (!track_unbuffer_callback)
1269 memset(&tracks[cur_idx], 0, sizeof(struct track_info));
1270 }
1271
1272 if (!track_unbuffer_callback)
1273 return ;
1274
1275 cur_idx = track_widx;
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001276 for (i = 0; i < MAX_TRACK - track_count; i++) {
1277 if (++cur_idx >= MAX_TRACK)
1278 cur_idx = 0;
Miika Pekkarinend54811f2005-07-02 16:52:30 +00001279
Miika Pekkarinen9bde0382005-07-03 18:36:24 +00001280 /* Send an event to notify that track has finished. */
1281 if (tracks[cur_idx].event_sent) {
Miika Pekkarinen9bde0382005-07-03 18:36:24 +00001282 event_count--;
1283 track_unbuffer_callback(&tracks[cur_idx].id3, event_count == 0);
1284 }
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001285
1286 if (tracks[cur_idx].event_sent || !buffered_only)
1287 memset(&tracks[cur_idx], 0, sizeof(struct track_info));
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001288 }
1289}
1290
Miika Pekkarinend54811f2005-07-02 16:52:30 +00001291/* Send callback events to notify about new tracks. */
1292static void generate_postbuffer_events(void)
1293{
1294 int i;
1295 int cur_ridx, event_count;
1296
Miika Pekkarinend54811f2005-07-02 16:52:30 +00001297 /* At first determine how many unsent events we have. */
1298 cur_ridx = track_ridx;
1299 event_count = 0;
1300 for (i = 0; i < track_count; i++) {
1301 if (!tracks[cur_ridx].event_sent)
1302 event_count++;
1303 if (++cur_ridx >= MAX_TRACK)
1304 cur_ridx -= MAX_TRACK;
1305 }
1306
1307 /* Now sent these events. */
1308 cur_ridx = track_ridx;
1309 for (i = 0; i < track_count; i++) {
1310 if (!tracks[cur_ridx].event_sent) {
1311 tracks[cur_ridx].event_sent = true;
1312 event_count--;
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001313 /* We still want to set event_sent flags even if not using
1314 event callbacks. */
1315 if (track_buffer_callback)
1316 track_buffer_callback(&tracks[cur_ridx].id3, event_count == 0);
Miika Pekkarinend54811f2005-07-02 16:52:30 +00001317 }
1318 if (++cur_ridx >= MAX_TRACK)
1319 cur_ridx -= MAX_TRACK;
1320 }
1321}
1322
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001323void initialize_buffer_fill(void)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001324{
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001325 int cur_idx, i;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001326
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001327
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001328 fill_bytesleft = filebuflen - filebufused;
Miika Pekkarinenc520d692005-07-01 18:38:10 +00001329 cur_ti->start_pos = ci.curpos;
Miika Pekkarinende3b04e2005-06-29 14:46:27 +00001330
1331 if (filling)
1332 return ;
1333
Miika Pekkarinen29aad552005-08-28 14:16:03 +00001334 pcmbuf_set_boost_mode(true);
1335
Miika Pekkarinende3b04e2005-06-29 14:46:27 +00001336 filling = true;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001337
1338 /* Calculate real track count after throwing away old tracks. */
1339 cur_idx = track_ridx;
1340 for (i = 0; i < track_count; i++) {
1341 if (cur_idx == track_widx)
1342 break ;
1343
1344 if (++cur_idx >= MAX_TRACK)
1345 cur_idx = 0;
1346 }
1347
Miika Pekkarinenf5df9cd2005-07-18 06:10:53 +00001348 track_count = i;
Miika Pekkarinene3617492005-07-17 21:44:58 +00001349 if (tracks[track_widx].filesize == 0) {
1350 if (--track_widx < 0)
1351 track_widx = MAX_TRACK - 1;
Miika Pekkarinenf5df9cd2005-07-18 06:10:53 +00001352 } else {
1353 track_count++;
Miika Pekkarinene3617492005-07-17 21:44:58 +00001354 }
1355
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001356 /* Mark all buffered entries null (not metadata for next track). */
1357 audio_clear_track_entries(true);
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001358}
1359
1360void audio_check_buffer(void)
1361{
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001362 /* Start buffer filling as necessary. */
Ryan Jackson7d513252005-10-12 08:49:37 +00001363 if ((!conf_watermark || filebufused > conf_watermark
1364 || !queue_empty(&audio_queue) || !playing || ci.stop_codec
1365 || ci.reload_codec) && !filling)
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001366 return ;
1367
Miika Pekkarinende3b04e2005-06-29 14:46:27 +00001368 initialize_buffer_fill();
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001369
1370 /* Limit buffering size at first run. */
Miika Pekkarinen3e33f852005-07-10 06:58:02 +00001371 if (conf_bufferlimit && fill_bytesleft > conf_bufferlimit
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001372 - filebufused) {
1373 fill_bytesleft = MAX(0, conf_bufferlimit - filebufused);
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001374 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001375
1376 /* Try to load remainings of the file. */
1377 if (tracks[track_widx].filerem > 0)
1378 audio_fill_file_buffer();
1379
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001380 /* Increase track write index as necessary. */
1381 if (tracks[track_widx].filerem == 0 && tracks[track_widx].filesize != 0) {
1382 if (++track_widx == MAX_TRACK)
1383 track_widx = 0;
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001384 }
1385
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001386 /* Load new files to fill the entire buffer. */
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001387 if (audio_load_track(0, false, last_peek_offset + 1)) {
1388
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001389 } else if (tracks[track_widx].filerem == 0 || fill_bytesleft == 0) {
Miika Pekkarinen527ce402005-07-10 08:46:12 +00001390 /* Read next unbuffered track's metadata as necessary. */
1391 read_next_metadata();
Miika Pekkarinen2e884cc2005-07-15 21:37:03 +00001392
Miika Pekkarinend54811f2005-07-02 16:52:30 +00001393 generate_postbuffer_events();
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001394 filling = false;
Miika Pekkarinen84d6f9e2005-06-29 20:50:58 +00001395 conf_bufferlimit = 0;
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001396 pcmbuf_set_boost_mode(false);
1397
1398#ifndef SIMULATOR
Miika Pekkarinenb288dda2005-07-10 08:38:16 +00001399 if (playing)
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001400 ata_sleep();
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001401#endif
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001402 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001403}
1404
1405void audio_update_trackinfo(void)
1406{
1407 if (new_track >= 0) {
1408 buf_ridx += cur_ti->available;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001409 filebufused -= cur_ti->available;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001410
1411 cur_ti = &tracks[track_ridx];
1412 buf_ridx += cur_ti->codecsize;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001413 filebufused -= cur_ti->codecsize;
1414 if (buf_ridx >= filebuflen)
1415 buf_ridx -= filebuflen;
Miika Pekkarinen92ea04a2005-06-15 12:53:50 +00001416
Miika Pekkarinenb4bc1062005-06-09 07:19:16 +00001417 if (!filling)
Miika Pekkarinen20b38972005-07-13 12:48:22 +00001418 pcmbuf_set_boost_mode(false);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001419 } else {
Miika Pekkarinen1c5b3922005-06-11 13:47:01 +00001420 buf_ridx -= ci.curpos + cur_ti->codecsize;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001421 filebufused += ci.curpos + cur_ti->codecsize;
Ryan Jacksonc6949542005-09-24 15:44:07 +00001422 cur_ti->available = cur_ti->filesize - cur_ti->filerem;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001423
1424 cur_ti = &tracks[track_ridx];
Miika Pekkarinen1c5b3922005-06-11 13:47:01 +00001425 buf_ridx -= cur_ti->filesize;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001426 filebufused += cur_ti->filesize;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001427 cur_ti->available = cur_ti->filesize;
1428 if (buf_ridx < 0)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001429 buf_ridx = filebuflen + buf_ridx;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001430 }
Miika Pekkarinenaa30f042005-07-05 07:25:55 +00001431
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001432 ci.filesize = cur_ti->filesize;
Miika Pekkarinena380d902005-06-11 18:05:16 +00001433 cur_ti->id3.elapsed = 0;
1434 cur_ti->id3.offset = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001435 ci.id3 = (struct mp3entry *)&cur_ti->id3;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001436 ci.curpos = 0;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001437 cur_ti->start_pos = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001438 ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready;
Miika Pekkarinencd8bfeb2005-07-17 15:35:17 +00001439 if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()) {
Miika Pekkarinen90161c92005-07-22 16:46:27 +00001440 pcmbuf_crossfade_init(new_track ? CROSSFADE_MODE_CROSSFADE
1441 : global_settings.crossfade);
Miika Pekkarinenb469e732005-07-02 17:45:12 +00001442 codec_track_changed();
Miika Pekkarinencd8bfeb2005-07-17 15:35:17 +00001443 } else {
1444 pcmbuf_add_event(codec_track_changed);
1445 }
Miika Pekkarinenae5002d2005-07-23 08:48:10 +00001446 last_index = playlist_get_display_index();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001447}
1448
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001449static void audio_stop_playback(void)
1450{
Jonas Häggqvist0dd0d312005-09-26 01:20:02 +00001451 paused = false;
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001452 playing = false;
Miika Pekkarinen21598112005-07-15 07:57:09 +00001453 filling = false;
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001454 ci.stop_codec = true;
1455 if (current_fd >= 0) {
1456 close(current_fd);
1457 current_fd = -1;
1458 }
Miika Pekkarinenb6048c42005-07-15 18:42:01 +00001459 pcmbuf_play_stop();
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001460 while (audio_codec_loaded)
Miika Pekkarinen21598112005-07-15 07:57:09 +00001461 yield();
Miika Pekkarinenacdcdb02005-09-10 21:02:42 +00001462
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001463 track_count = 0;
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001464 /* Mark all entries null. */
1465 audio_clear_track_entries(false);
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001466}
1467
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001468/* Request the next track with new codec. */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001469void audio_change_track(void)
1470{
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001471 logf("change track");
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001472
1473 /* Wait for new track data. */
Miika Pekkarinen22960c32005-07-06 15:44:59 +00001474 while (track_count <= 1 && filling)
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001475 yield();
1476
1477 /* If we are not filling, then it must be end-of-playlist. */
Miika Pekkarinen22960c32005-07-06 15:44:59 +00001478 if (track_count <= 1) {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001479 logf("No more tracks");
Miika Pekkarinenc3fed622005-06-15 18:59:04 +00001480 while (pcm_is_playing())
1481 yield();
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001482 audio_stop_playback();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001483 return ;
1484 }
1485
1486 if (++track_ridx >= MAX_TRACK)
1487 track_ridx = 0;
1488
1489 audio_update_trackinfo();
1490 queue_post(&codec_queue, CODEC_LOAD, 0);
1491}
1492
Miika Pekkarinen645a2e12005-07-10 16:33:03 +00001493static int get_codec_base_type(int type)
1494{
1495 switch (type) {
1496 case AFMT_MPA_L1:
1497 case AFMT_MPA_L2:
1498 case AFMT_MPA_L3:
1499 return AFMT_MPA_L3;
1500 }
1501
1502 return type;
1503}
1504
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001505bool codec_request_next_track_callback(void)
1506{
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001507 if (current_codec == CODEC_IDX_VOICE) {
1508 voice_remaining = 0;
Miika Pekkarinene3dc61e2005-08-20 18:55:15 +00001509 /* Terminate the codec if they are messages waiting on the queue or
1510 core has been requested the codec to be terminated. */
1511 return !ci_voice.stop_codec && queue_empty(&voice_codec_queue);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001512 }
1513
Miika Pekkarinen3e33f852005-07-10 06:58:02 +00001514 if (ci.stop_codec || !playing)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001515 return false;
1516
1517 logf("Request new track");
Ryan Jackson795ce8b2005-07-24 04:17:34 +00001518
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001519 /* Advance to next track. */
1520 if (ci.reload_codec && new_track > 0) {
Ryan Jackson08121642005-08-05 23:51:48 +00001521 if (!playlist_check(new_track)) {
1522 ci.reload_codec = false;
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001523 return false;
Ryan Jackson08121642005-08-05 23:51:48 +00001524 }
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001525 last_peek_offset--;
Miika Pekkarinen3e33f852005-07-10 06:58:02 +00001526 playlist_next(new_track);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001527 if (++track_ridx == MAX_TRACK)
1528 track_ridx = 0;
Ryan Jackson08121642005-08-05 23:51:48 +00001529
Miika Pekkarinend6e79422005-07-05 15:19:22 +00001530 /* Wait for new track data (codectype 0 is invalid). When a correct
1531 codectype is set, we can assume that the filesize is correct. */
1532 while (tracks[track_ridx].id3.codectype == 0 && filling
1533 && !ci.stop_codec)
1534 yield();
Ryan Jackson08121642005-08-05 23:51:48 +00001535
Miika Pekkarinen82c29272005-06-07 06:34:54 +00001536 if (tracks[track_ridx].filesize == 0) {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001537 logf("Loading from disk...");
1538 new_track = 0;
Miika Pekkarinen095854b2005-08-06 05:50:14 +00001539 last_index = -1;
Miika Pekkarinen5899ed52005-06-08 10:33:01 +00001540 queue_post(&audio_queue, AUDIO_PLAY, 0);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001541 return false;
1542 }
1543 }
1544
1545 /* Advance to previous track. */
1546 else if (ci.reload_codec && new_track < 0) {
Ryan Jackson08121642005-08-05 23:51:48 +00001547 if (!playlist_check(new_track)) {
1548 ci.reload_codec = false;
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001549 return false;
Ryan Jackson08121642005-08-05 23:51:48 +00001550 }
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001551 last_peek_offset++;
Miika Pekkarinen3e33f852005-07-10 06:58:02 +00001552 playlist_next(new_track);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001553 if (--track_ridx < 0)
1554 track_ridx = MAX_TRACK-1;
1555 if (tracks[track_ridx].filesize == 0 ||
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001556 filebufused+ci.curpos+tracks[track_ridx].filesize
1557 /*+ (off_t)tracks[track_ridx].codecsize*/ > filebuflen) {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001558 logf("Loading from disk...");
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001559 new_track = 0;
Miika Pekkarinen095854b2005-08-06 05:50:14 +00001560 last_index = -1;
Miika Pekkarinen5899ed52005-06-08 10:33:01 +00001561 queue_post(&audio_queue, AUDIO_PLAY, 0);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001562 return false;
1563 }
1564 }
1565
1566 /* Codec requested track change (next track). */
1567 else {
Miika Pekkarinen05820cb2005-07-30 18:47:12 +00001568 if (!playlist_check(1)) {
1569 ci.reload_codec = false;
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001570 return false;
Miika Pekkarinen05820cb2005-07-30 18:47:12 +00001571 }
Miika Pekkarinen2d79df52005-07-05 13:34:52 +00001572 last_peek_offset--;
Miika Pekkarinen5899ed52005-06-08 10:33:01 +00001573 playlist_next(1);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001574 if (++track_ridx >= MAX_TRACK)
1575 track_ridx = 0;
1576
Miika Pekkarinend6e79422005-07-05 15:19:22 +00001577 /* Wait for new track data (codectype 0 is invalid). When a correct
1578 codectype is set, we can assume that the filesize is correct. */
1579 while (tracks[track_ridx].id3.codectype == 0 && filling
1580 && !ci.stop_codec)
1581 yield();
1582
1583 if (tracks[track_ridx].filesize == 0) {
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00001584 logf("No more tracks [2]");
Miika Pekkarinen05820cb2005-07-30 18:47:12 +00001585 ci.stop_codec = true;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001586 new_track = 0;
Miika Pekkarinen05820cb2005-07-30 18:47:12 +00001587 last_index = -1;
1588 queue_post(&audio_queue, AUDIO_PLAY, 0);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001589 return false;
1590 }
1591 }
1592
1593 ci.reload_codec = false;
1594
Miika Pekkarinen645a2e12005-07-10 16:33:03 +00001595 /* Check if the next codec is the same file. */
1596 if (get_codec_base_type(cur_ti->id3.codectype) !=
1597 get_codec_base_type(tracks[track_ridx].id3.codectype)) {
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00001598 logf("New codec:%d/%d", cur_ti->id3.codectype,
1599 tracks[track_ridx].id3.codectype);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001600 if (--track_ridx < 0)
1601 track_ridx = MAX_TRACK-1;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001602 new_track = 0;
1603 return false;
1604 }
1605
1606 logf("On-the-fly change");
1607 audio_update_trackinfo();
1608 new_track = 0;
1609
1610 return true;
1611}
1612
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001613/* Invalidates all but currently playing track. */
1614void audio_invalidate_tracks(void)
1615{
1616 if (track_count == 0) {
Ryan Jackson7d0b7e92005-10-07 22:55:30 +00001617 /* This call doesn't seem necessary anymore. Uncomment it
1618 if things break */
1619 /* queue_post(&audio_queue, AUDIO_PLAY, 0); */
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001620 return ;
1621 }
1622
1623 track_count = 1;
Miika Pekkarinenfd7952e2005-07-30 17:00:06 +00001624 last_peek_offset = 0;
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001625 track_widx = track_ridx;
Miika Pekkarinencda55bb2005-07-16 06:26:29 +00001626 /* Mark all other entries null (also buffered wrong metadata). */
1627 audio_clear_track_entries(false);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001628 filebufused = cur_ti->available;
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001629 buf_widx = buf_ridx + cur_ti->available;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001630 if (buf_widx >= filebuflen)
1631 buf_widx -= filebuflen;
Miika Pekkarinenf46c9f22005-07-03 18:03:20 +00001632 read_next_metadata();
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001633}
1634
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001635static void initiate_track_change(int peek_index)
1636{
1637 if (!playlist_check(peek_index))
1638 return ;
Ryan Jackson795ce8b2005-07-24 04:17:34 +00001639
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001640 /* Detect if disk is spinning.. */
1641 if (filling) {
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001642 playlist_next(peek_index);
1643 queue_post(&audio_queue, AUDIO_PLAY, 0);
Miika Pekkarinencd8bfeb2005-07-17 15:35:17 +00001644 } else {
Miika Pekkarinend83b6592005-07-19 19:57:23 +00001645 new_track = peek_index;
1646 ci.reload_codec = true;
1647 if (!pcmbuf_is_crossfade_enabled())
1648 pcmbuf_flush_audio();
Miika Pekkarinencd8bfeb2005-07-17 15:35:17 +00001649 }
1650
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00001651 codec_track_changed();
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001652}
1653
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001654void audio_thread(void)
1655{
1656 struct event ev;
1657
1658 while (1) {
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001659 yield_codecs();
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001660
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001661 audio_check_buffer();
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00001662
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001663 queue_wait_w_tmo(&audio_queue, &ev, 0);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001664 switch (ev.id) {
1665 case AUDIO_PLAY:
Miika Pekkarinen095854b2005-08-06 05:50:14 +00001666 /* Refuse to start playback if we are already playing
1667 the requested track. This is needed because when skipping
1668 tracks fast, AUDIO_PLAY commands will get queued with the
1669 the same track and playback will stutter. */
Miika Pekkarinen91464062005-09-04 08:38:00 +00001670 if (last_index == playlist_get_display_index() && playing
1671 && pcm_is_playing()) {
Miika Pekkarinen095854b2005-08-06 05:50:14 +00001672 logf("already playing req. track");
1673 break ;
1674 }
1675
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001676 logf("starting...");
Miika Pekkarinen3b907072005-06-30 16:28:40 +00001677 playing = true;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001678 ci.stop_codec = true;
1679 ci.reload_codec = false;
1680 ci.seek_time = 0;
Miika Pekkarinen90161c92005-07-22 16:46:27 +00001681 pcmbuf_crossfade_init(CROSSFADE_MODE_CROSSFADE);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001682 while (audio_codec_loaded)
Miika Pekkarinend83b6592005-07-19 19:57:23 +00001683 yield();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001684 audio_play_start((int)ev.data);
Ryan Jackson7d0b7e92005-10-07 22:55:30 +00001685
Hardeep Sidhu839dbca2005-07-04 22:50:57 +00001686 playlist_update_resume_info(audio_current_track());
Ryan Jackson7d0b7e92005-10-07 22:55:30 +00001687
Miika Pekkarinenbdf558c2005-10-22 21:07:35 +00001688 /* If there are no tracks in the playlist, then the playlist
Ryan Jackson7d0b7e92005-10-07 22:55:30 +00001689 was empty or none of the filenames were valid. No point
1690 in playing an empty playlist. */
1691 if (playlist_amount() == 0) {
1692 audio_stop_playback();
1693 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001694 break ;
1695
1696 case AUDIO_STOP:
Hardeep Sidhu839dbca2005-07-04 22:50:57 +00001697 if (playing)
1698 playlist_update_resume_info(audio_current_track());
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001699 audio_stop_playback();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001700 break ;
1701
1702 case AUDIO_PAUSE:
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001703 logf("audio_pause");
Miika Pekkarinenacdcdb02005-09-10 21:02:42 +00001704 /* We will pause the pcm playback in audiobuffer insert function
1705 to prevent a loop inside the pcm buffer. */
1706 // pcm_play_pause(false);
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001707 paused = true;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001708 break ;
1709
1710 case AUDIO_RESUME:
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001711 logf("audio_resume");
1712 pcm_play_pause(true);
1713 paused = false;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001714 break ;
1715
1716 case AUDIO_NEXT:
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001717 logf("audio_next");
Miika Pekkarinen9d7fd182005-08-20 21:17:41 +00001718 if (global_settings.beep)
1719 pcmbuf_beep(5000, 100, 2500*global_settings.beep);
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001720 initiate_track_change(1);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001721 break ;
1722
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001723 case AUDIO_PREV:
1724 logf("audio_prev");
Miika Pekkarinen9d7fd182005-08-20 21:17:41 +00001725 if (global_settings.beep)
1726 pcmbuf_beep(5000, 100, 2500*global_settings.beep);
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001727 initiate_track_change(-1);
1728 break;
1729
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00001730 case AUDIO_FLUSH:
1731 audio_invalidate_tracks();
1732 break ;
1733
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001734 case AUDIO_TRACK_CHANGED:
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +00001735 if (track_changed_callback)
Jens Arnolda88d0762005-08-18 06:05:15 +00001736 track_changed_callback(&cur_ti->id3);
Hardeep Sidhu839dbca2005-07-04 22:50:57 +00001737 playlist_update_resume_info(audio_current_track());
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001738 break ;
1739
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001740 case AUDIO_CODEC_DONE:
1741 //if (playing)
1742 // audio_change_track();
1743 break ;
1744
1745#ifndef SIMULATOR
1746 case SYS_USB_CONNECTED:
Miika Pekkarinene3dc61e2005-08-20 18:55:15 +00001747 logf("USB: Audio core");
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001748 audio_stop_playback();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001749 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1750 usb_wait_for_disconnect(&audio_queue);
1751 break ;
1752#endif
Hardeep Sidhu839dbca2005-07-04 22:50:57 +00001753 case SYS_TIMEOUT:
1754 if (playing)
1755 playlist_update_resume_info(audio_current_track());
1756 break;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001757 }
1758 }
1759}
1760
1761void codec_thread(void)
1762{
1763 struct event ev;
Miika Pekkarinen85f49732005-06-27 19:29:49 +00001764 long codecsize;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001765 int status;
1766 int wrap;
1767
1768 while (1) {
1769 status = 0;
1770 queue_wait(&codec_queue, &ev);
1771 switch (ev.id) {
1772 case CODEC_LOAD_DISK:
1773 ci.stop_codec = false;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001774 audio_codec_loaded = true;
1775 mutex_lock(&mutex_codecthread);
1776 current_codec = CODEC_IDX_AUDIO;
1777 status = codec_load_file((char *)ev.data, &ci);
1778 mutex_unlock(&mutex_codecthread);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001779 break ;
1780
1781 case CODEC_LOAD:
1782 logf("Codec start");
1783 codecsize = cur_ti->codecsize;
1784 if (codecsize == 0) {
1785 logf("Codec slot is empty!");
Miika Pekkarinen3e33f852005-07-10 06:58:02 +00001786 /* Wait for the pcm buffer to go empty */
1787 while (pcm_is_playing())
1788 yield();
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001789 audio_stop_playback();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001790 break ;
1791 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001792
1793 ci.stop_codec = false;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001794 wrap = (int)&filebuf[filebuflen] - (int)cur_ti->codecbuf;
1795 audio_codec_loaded = true;
1796 mutex_lock(&mutex_codecthread);
1797 current_codec = CODEC_IDX_AUDIO;
1798 status = codec_load_ram(cur_ti->codecbuf, codecsize,
1799 &filebuf[0], wrap, &ci);
1800 mutex_unlock(&mutex_codecthread);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001801 break ;
1802
1803#ifndef SIMULATOR
1804 case SYS_USB_CONNECTED:
Miika Pekkarinene3dc61e2005-08-20 18:55:15 +00001805 while (voice_codec_loaded) {
1806 if (current_codec != CODEC_IDX_VOICE)
1807 swap_codec();
1808 sleep(1);
1809 }
1810 logf("USB: Audio codec");
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001811 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1812 usb_wait_for_disconnect(&codec_queue);
1813 break ;
1814#endif
1815 }
Miika Pekkarinen84d6f9e2005-06-29 20:50:58 +00001816
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001817 audio_codec_loaded = false;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001818
1819 switch (ev.id) {
1820 case CODEC_LOAD_DISK:
1821 case CODEC_LOAD:
Daniel Stenberg1dd672f2005-06-22 19:41:30 +00001822 if (status != CODEC_OK) {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001823 logf("Codec failure");
Miika Pekkarinen9c70b1b2005-07-02 17:03:19 +00001824 audio_stop_playback();
Miika Pekkarinene3617492005-07-17 21:44:58 +00001825 splash(HZ*2, true, "Codec failure");
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001826 } else {
1827 logf("Codec finished");
1828 }
1829
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001830 if (playing && !ci.stop_codec && !ci.reload_codec) {
1831 audio_change_track();
Miika Pekkarinenfe468b12005-06-09 19:31:35 +00001832 continue ;
1833 } else if (ci.stop_codec) {
1834 //playing = false;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001835 }
Miika Pekkarinenfe468b12005-06-09 19:31:35 +00001836 //queue_post(&audio_queue, AUDIO_CODEC_DONE, (void *)status);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001837 }
1838 }
1839}
1840
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001841static void reset_buffer(void)
1842{
1843 filebuf = &audiobuf[MALLOC_BUFSIZE];
1844 filebuflen = audiobufend - audiobuf - pcmbuf_get_bufsize()
1845 - PCMBUF_GUARD - MALLOC_BUFSIZE - GUARD_BUFSIZE;
1846
1847 if (talk_get_bufsize() && voice_codec_loaded)
1848 {
1849 filebuf = &filebuf[talk_get_bufsize()];
1850 filebuflen -= 2*CODEC_IRAM_SIZE + 2*CODEC_SIZE + talk_get_bufsize();
1851 }
1852}
1853
1854void voice_codec_thread(void)
1855{
1856 struct event ev;
1857 int status;
1858
1859 current_codec = CODEC_IDX_AUDIO;
1860 voice_codec_loaded = false;
1861 while (1) {
1862 status = 0;
Miika Pekkarinenb2e94ef2005-08-21 18:38:27 +00001863 voice_is_playing = false;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001864 queue_wait(&voice_codec_queue, &ev);
1865 switch (ev.id) {
1866 case CODEC_LOAD_DISK:
1867 logf("Loading voice codec");
1868 audio_stop_playback();
1869 mutex_lock(&mutex_codecthread);
1870 current_codec = CODEC_IDX_VOICE;
1871 dsp_configure(DSP_RESET, 0);
1872 ci.configure(CODEC_DSP_ENABLE, (bool *)true);
1873 voice_remaining = 0;
1874 voice_getmore = NULL;
1875 voice_codec_loaded = true;
1876 reset_buffer();
1877 ci_voice.stop_codec = false;
1878
1879 status = codec_load_file((char *)ev.data, &ci_voice);
1880
1881 logf("Voice codec finished");
1882 audio_stop_playback();
1883 mutex_unlock(&mutex_codecthread);
1884 current_codec = CODEC_IDX_AUDIO;
1885 voice_codec_loaded = false;
1886 reset_buffer();
1887 break ;
1888
1889#ifndef SIMULATOR
1890 case SYS_USB_CONNECTED:
Miika Pekkarinene3dc61e2005-08-20 18:55:15 +00001891 logf("USB: Voice codec");
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001892 usb_acknowledge(SYS_USB_CONNECTED_ACK);
1893 usb_wait_for_disconnect(&voice_codec_queue);
1894 break ;
1895#endif
1896 }
1897 }
1898}
1899
1900void voice_init(void)
1901{
Magnus Holmgren0293dba2005-10-16 12:05:58 +00001902 if (!filebuf)
1903 return; /* Audio buffers not yet set up */
1904
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001905 while (voice_codec_loaded)
1906 {
1907 logf("Terminating voice codec");
1908 ci_voice.stop_codec = true;
1909 if (current_codec != CODEC_IDX_VOICE)
1910 swap_codec();
1911 sleep(1);
1912 }
1913
1914 if (!talk_get_bufsize())
1915 return ;
1916
1917 logf("Starting voice codec");
1918 queue_post(&voice_codec_queue, CODEC_LOAD_DISK, (void *)CODEC_MPA_L3);
1919 while (!voice_codec_loaded)
1920 sleep(1);
1921}
1922
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001923struct mp3entry* audio_current_track(void)
1924{
Miika Pekkarinenaa30f042005-07-05 07:25:55 +00001925 // logf("audio_current_track");
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001926
1927 if (track_count > 0 && cur_ti->taginfo_ready)
1928 return (struct mp3entry *)&cur_ti->id3;
1929 else
1930 return NULL;
1931}
1932
1933struct mp3entry* audio_next_track(void)
1934{
1935 int next_idx = track_ridx + 1;
1936
1937 if (track_count == 0)
1938 return NULL;
1939
1940 if (next_idx >= MAX_TRACK)
1941 next_idx = 0;
1942
1943 if (!tracks[next_idx].taginfo_ready)
1944 return NULL;
1945
1946 //logf("audio_next_track");
1947
1948 return &tracks[next_idx].id3;
1949}
1950
1951bool audio_has_changed_track(void)
1952{
Miika Pekkarinend8cb7032005-06-26 19:41:29 +00001953 if (track_changed && track_count > 0 && playing) {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001954 if (!cur_ti->taginfo_ready)
1955 return false;
1956 track_changed = false;
1957 return true;
1958 }
1959
1960 return false;
1961}
1962
1963void audio_play(int offset)
1964{
1965 logf("audio_play");
Miika Pekkarinen34a25a62005-07-15 16:42:01 +00001966 last_index = -1;
Miika Pekkarinenacdcdb02005-09-10 21:02:42 +00001967 paused = false;
1968 pcm_play_pause(true);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001969 queue_post(&audio_queue, AUDIO_PLAY, (void *)offset);
1970}
1971
1972void audio_stop(void)
1973{
1974 logf("audio_stop");
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001975 queue_post(&audio_queue, AUDIO_STOP, 0);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001976 while (playing || audio_codec_loaded)
Miika Pekkarinen84d6f9e2005-06-29 20:50:58 +00001977 yield();
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001978}
1979
Miika Pekkarinenbd1131e2005-07-20 01:58:27 +00001980bool mp3_pause_done(void)
1981{
1982 return paused;
1983}
1984
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001985void audio_pause(void)
1986{
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001987 queue_post(&audio_queue, AUDIO_PAUSE, 0);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001988}
1989
1990void audio_resume(void)
1991{
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001992 queue_post(&audio_queue, AUDIO_RESUME, 0);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001993}
1994
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001995void audio_next(void)
1996{
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00001997 queue_post(&audio_queue, AUDIO_NEXT, 0);
Miika Pekkarinen431e8132005-06-19 18:41:53 +00001998}
1999
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002000void audio_prev(void)
2001{
Linus Nielsen Feltzingffd207f2005-07-06 19:40:17 +00002002 queue_post(&audio_queue, AUDIO_PREV, 0);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002003}
2004
2005void audio_ff_rewind(int newpos)
2006{
2007 logf("rewind: %d", newpos);
Miika Pekkarinen2724d0b2005-07-01 21:00:02 +00002008 if (playing) {
Miika Pekkarinen20b38972005-07-13 12:48:22 +00002009 pcmbuf_play_stop();
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +00002010 ci.seek_time = newpos+1;
Miika Pekkarinen2724d0b2005-07-01 21:00:02 +00002011 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002012}
2013
2014void audio_flush_and_reload_tracks(void)
2015{
2016 logf("flush & reload");
Miika Pekkarinen58af47c2005-06-13 22:09:12 +00002017 queue_post(&audio_queue, AUDIO_FLUSH, 0);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002018}
2019
2020void audio_error_clear(void)
2021{
2022}
2023
2024int audio_status(void)
2025{
2026 int ret = 0;
2027
2028 if (playing)
2029 ret |= AUDIO_STATUS_PLAY;
2030
2031 if (paused)
2032 ret |= AUDIO_STATUS_PAUSE;
2033
2034 return ret;
2035}
2036
Linus Nielsen Feltzing4aaa3212005-06-06 00:35:21 +00002037int audio_get_file_pos(void)
2038{
2039 return 0;
2040}
2041
Miika Pekkarinen82c29272005-06-07 06:34:54 +00002042
2043/* Copied from mpeg.c. Should be moved somewhere else. */
Miika Pekkarinen5899ed52005-06-08 10:33:01 +00002044static void mp3_set_elapsed(struct mp3entry* id3)
2045{
2046 if ( id3->vbr ) {
2047 if ( id3->has_toc ) {
2048 /* calculate elapsed time using TOC */
2049 int i;
2050 unsigned int remainder, plen, relpos, nextpos;
2051
2052 /* find wich percent we're at */
2053 for (i=0; i<100; i++ )
2054 {
Jens Arnoldec9b2022005-09-10 12:28:16 +00002055 if ( id3->offset < id3->toc[i] * (id3->filesize / 256) )
Miika Pekkarinen5899ed52005-06-08 10:33:01 +00002056 {
2057 break;
2058 }
2059 }
2060
2061 i--;
2062 if (i < 0)
2063 i = 0;
2064
2065 relpos = id3->toc[i];
2066
2067 if (i < 99)
2068 {
2069 nextpos = id3->toc[i+1];
2070 }
2071 else
2072 {
2073 nextpos = 256;
2074 }
2075
2076 remainder = id3->offset - (relpos * (id3->filesize / 256));
2077
2078 /* set time for this percent (divide before multiply to prevent
2079 overflow on long files. loss of precision is negligible on
2080 short files) */
2081 id3->elapsed = i * (id3->length / 100);
2082
2083 /* calculate remainder time */
2084 plen = (nextpos - relpos) * (id3->filesize / 256);
2085 id3->elapsed += (((remainder * 100) / plen) *
2086 (id3->length / 10000));
2087 }
2088 else {
2089 /* no TOC exists. set a rough estimate using average bitrate */
2090 int tpk = id3->length / (id3->filesize / 1024);
2091 id3->elapsed = id3->offset / 1024 * tpk;
2092 }
2093 }
2094 else
Jens Arnoldec9b2022005-09-10 12:28:16 +00002095 /* constant bitrate, use exact calculation */
2096 id3->elapsed = id3->offset / (id3->bitrate / 8);
Miika Pekkarinen5899ed52005-06-08 10:33:01 +00002097}
2098
2099/* Copied from mpeg.c. Should be moved somewhere else. */
Miika Pekkarinen82c29272005-06-07 06:34:54 +00002100int mp3_get_file_pos(void)
2101{
2102 int pos = -1;
2103 struct mp3entry *id3 = audio_current_track();
2104
2105 if (id3->vbr)
2106 {
2107 if (id3->has_toc)
2108 {
2109 /* Use the TOC to find the new position */
2110 unsigned int percent, remainder;
2111 int curtoc, nexttoc, plen;
2112
2113 percent = (id3->elapsed*100)/id3->length;
2114 if (percent > 99)
2115 percent = 99;
2116
2117 curtoc = id3->toc[percent];
2118
2119 if (percent < 99)
2120 nexttoc = id3->toc[percent+1];
2121 else
2122 nexttoc = 256;
2123
2124 pos = (id3->filesize/256)*curtoc;
2125
2126 /* Use the remainder to get a more accurate position */
2127 remainder = (id3->elapsed*100)%id3->length;
2128 remainder = (remainder*100)/id3->length;
2129 plen = (nexttoc - curtoc)*(id3->filesize/256);
2130 pos += (plen/100)*remainder;
2131 }
2132 else
2133 {
2134 /* No TOC exists, estimate the new position */
2135 pos = (id3->filesize / (id3->length / 1000)) *
2136 (id3->elapsed / 1000);
2137 }
2138 }
Jens Arnoldec9b2022005-09-10 12:28:16 +00002139 else if (id3->bitrate)
2140 pos = id3->elapsed * (id3->bitrate / 8);
Miika Pekkarinen82c29272005-06-07 06:34:54 +00002141 else
2142 {
2143 return -1;
2144 }
2145
2146 if (pos >= (int)(id3->filesize - id3->id3v1len))
2147 {
2148 /* Don't seek right to the end of the file so that we can
2149 transition properly to the next song */
2150 pos = id3->filesize - id3->id3v1len - 1;
2151 }
2152 else if (pos < (int)id3->first_frame_offset)
2153 {
2154 /* skip past id3v2 tag and other leading garbage */
2155 pos = id3->first_frame_offset;
2156 }
2157 return pos;
2158}
2159
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002160void mp3_play_data(const unsigned char* start, int size,
2161 void (*get_more)(unsigned char** start, int* size))
2162{
2163 voice_getmore = get_more;
2164 voicebuf = (unsigned char *)start;
2165 voice_remaining = size;
2166 voice_is_playing = true;
2167 pcmbuf_reset_mixpos();
2168}
2169
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002170void audio_set_buffer_margin(int setting)
Linus Nielsen Feltzing4aaa3212005-06-06 00:35:21 +00002171{
Magnus Holmgrene830efc2005-08-24 16:21:24 +00002172 static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600};
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002173 buffer_margin = lookup[setting];
2174 logf("buffer margin: %ds", buffer_margin);
2175 set_filebuf_watermark(buffer_margin);
Linus Nielsen Feltzing4aaa3212005-06-06 00:35:21 +00002176}
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002177
Miika Pekkarinen90161c92005-07-22 16:46:27 +00002178/* Set crossfade & PCM buffer length. */
2179void audio_set_crossfade(int type)
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002180{
2181 long size;
Miika Pekkarinenab2163b2005-07-21 15:47:29 +00002182 bool was_playing = playing;
Miika Pekkarinen857ee672005-07-21 16:16:30 +00002183 int offset = 0;
Magnus Holmgrene830efc2005-08-24 16:21:24 +00002184 static const int lookup[] = {1, 2, 4, 6, 8, 10, 12, 14};
Miika Pekkarinen90161c92005-07-22 16:46:27 +00002185 int seconds = lookup[global_settings.crossfade_duration];
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002186
Magnus Holmgren62634a32005-10-16 08:01:02 +00002187 if (!filebuf)
2188 return; /* Audio buffers not yet set up */
2189
Miika Pekkarinenab2163b2005-07-21 15:47:29 +00002190 /* Store the track resume position */
2191 if (playing)
2192 offset = cur_ti->id3.offset;
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002193
Miika Pekkarinen90161c92005-07-22 16:46:27 +00002194 if (type == CROSSFADE_MODE_OFF)
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002195 seconds = 1;
Miika Pekkarinen90161c92005-07-22 16:46:27 +00002196
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002197 /* Buffer has to be at least 2s long. */
2198 seconds += 2;
2199 logf("buf len: %d", seconds);
2200 size = seconds * (NATIVE_FREQUENCY*4);
2201 if (pcmbuf_get_bufsize() == size)
2202 return ;
2203
Miika Pekkarinenab2163b2005-07-21 15:47:29 +00002204 /* Playback has to be stopped before changing the buffer size. */
2205 audio_stop_playback();
2206
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002207 /* Re-initialize audio system. */
Miika Pekkarinene0d17512005-07-22 16:57:08 +00002208 if (was_playing)
2209 splash(0, true, str(LANG_RESTARTING_PLAYBACK));
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002210 pcmbuf_init(size);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002211 pcmbuf_crossfade_enable(type != CROSSFADE_MODE_OFF);
2212 reset_buffer();
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002213 logf("abuf:%dB", pcmbuf_get_bufsize());
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002214 logf("fbuf:%dB", filebuflen);
Miika Pekkarinenab2163b2005-07-21 15:47:29 +00002215
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002216 voice_init();
2217
Miika Pekkarinenab2163b2005-07-21 15:47:29 +00002218 /* Restart playback. */
Miika Pekkarinene0d17512005-07-22 16:57:08 +00002219 if (was_playing) {
Miika Pekkarinenab2163b2005-07-21 15:47:29 +00002220 audio_play(offset);
Miika Pekkarinene0d17512005-07-22 16:57:08 +00002221
2222 /* Wait for the playback to start again (and display the splash
2223 screen during that period. */
2224 playing = true;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002225 while (playing && !audio_codec_loaded)
Miika Pekkarinene0d17512005-07-22 16:57:08 +00002226 yield();
2227 }
Miika Pekkarinenf090dc32005-07-21 11:44:00 +00002228}
Linus Nielsen Feltzing4aaa3212005-06-06 00:35:21 +00002229
2230void mpeg_id3_options(bool _v1first)
2231{
2232 v1first = _v1first;
2233}
2234
Miika Pekkarinend54811f2005-07-02 16:52:30 +00002235void test_buffer_event(struct mp3entry *id3, bool last_track)
2236{
Miika Pekkarinene21cf842005-07-03 19:20:40 +00002237 (void)id3;
2238 (void)last_track;
2239
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00002240 logf("be:%d%s", last_track, id3->path);
Miika Pekkarinend54811f2005-07-02 16:52:30 +00002241}
2242
Miika Pekkarinen9bde0382005-07-03 18:36:24 +00002243void test_unbuffer_event(struct mp3entry *id3, bool last_track)
Miika Pekkarinend54811f2005-07-02 16:52:30 +00002244{
Miika Pekkarinene21cf842005-07-03 19:20:40 +00002245 (void)id3;
2246 (void)last_track;
2247
Miika Pekkarinen5d9e0532005-07-08 20:01:06 +00002248 logf("ube:%d%s", last_track, id3->path);
Miika Pekkarinend54811f2005-07-02 16:52:30 +00002249}
Miika Pekkarinen9bde0382005-07-03 18:36:24 +00002250
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002251void audio_init(void)
2252{
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002253 static bool voicetagtrue = true;
2254
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002255 logf("audio api init");
Miika Pekkarinenab2163b2005-07-21 15:47:29 +00002256 pcm_init();
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002257 filebufused = 0;
Miika Pekkarinenb4bc1062005-06-09 07:19:16 +00002258 filling = false;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002259 current_codec = CODEC_IDX_AUDIO;
2260 filebuf = &audiobuf[MALLOC_BUFSIZE];
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002261 playing = false;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002262 audio_codec_loaded = false;
2263 voice_is_playing = false;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002264 paused = false;
2265 track_changed = false;
Miika Pekkarinen94b917e2005-06-11 18:14:41 +00002266 current_fd = -1;
Miika Pekkarinend54811f2005-07-02 16:52:30 +00002267 track_buffer_callback = NULL;
2268 track_unbuffer_callback = NULL;
Miika Pekkarinen7d6d1222005-06-29 21:36:30 +00002269 track_changed_callback = NULL;
Miika Pekkarinen3eb962d2005-07-07 07:15:05 +00002270 /* Just to prevent cur_ti never be anything random. */
2271 cur_ti = &tracks[0];
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002272
Miika Pekkarinend54811f2005-07-02 16:52:30 +00002273 audio_set_track_buffer_event(test_buffer_event);
2274 audio_set_track_unbuffer_event(test_unbuffer_event);
Miika Pekkarinene21cf842005-07-03 19:20:40 +00002275
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002276 /* Initialize codec api. */
2277 ci.read_filebuf = codec_filebuf_callback;
Miika Pekkarinen20b38972005-07-13 12:48:22 +00002278 ci.pcmbuf_insert = pcmbuf_insert_buffer;
2279 ci.pcmbuf_insert_split = codec_pcmbuf_insert_split_callback;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002280 ci.get_codec_memory = get_codec_memory_callback;
2281 ci.request_buffer = codec_request_buffer_callback;
2282 ci.advance_buffer = codec_advance_buffer_callback;
2283 ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
2284 ci.request_next_track = codec_request_next_track_callback;
2285 ci.mp3_get_filepos = codec_mp3_get_filepos_callback;
2286 ci.seek_buffer = codec_seek_buffer_callback;
Miika Pekkarinen8a7d1042005-08-21 18:12:31 +00002287 ci.seek_complete = codec_seek_complete_callback;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002288 ci.set_elapsed = codec_set_elapsed_callback;
Ryan Jacksond1917562005-07-12 16:45:38 +00002289 ci.set_offset = codec_set_offset_callback;
Miika Pekkarinen68b9acd2005-06-10 15:02:10 +00002290 ci.configure = codec_configure_callback;
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00002291
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002292 memcpy(&ci_voice, &ci, sizeof(struct codec_api));
2293 memset(&id3_voice, 0, sizeof(struct mp3entry));
2294 ci_voice.taginfo_ready = &voicetagtrue;
2295 ci_voice.id3 = &id3_voice;
2296 ci_voice.pcmbuf_insert = codec_pcmbuf_insert_callback;
2297 id3_voice.frequency = 11200;
2298 id3_voice.length = 1000000L;
2299
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +00002300 mutex_init(&mutex_bufferfill);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002301 mutex_init(&mutex_codecthread);
2302
Miika Pekkarinen82c29272005-06-07 06:34:54 +00002303 queue_init(&audio_queue);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002304 queue_init(&codec_queue);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002305 queue_init(&voice_codec_queue);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002306
2307 create_thread(codec_thread, codec_stack, sizeof(codec_stack),
2308 codec_thread_name);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00002309 create_thread(voice_codec_thread, voice_codec_stack,
2310 sizeof(voice_codec_stack), voice_codec_thread_name);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002311 create_thread(audio_thread, audio_stack, sizeof(audio_stack),
2312 audio_thread_name);
Magnus Holmgren0293dba2005-10-16 12:05:58 +00002313
2314 /* Apply relevant settings */
2315 audio_set_buffer_margin(global_settings.buffer_margin);
2316 audio_set_crossfade(global_settings.crossfade);
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00002317}
2318
2319