blob: d85bdcbd19bba9d13337cbb3ff9d98f742233e28 [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 *
Nicolas Pennequinef5fa8e2008-05-05 09:40:22 +000010 * Copyright (C) 2005-2007 Miika Pekkarinen
11 * Copyright (C) 2007-2008 Nicolas Pennequin
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000012 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000013 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000017 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ****************************************************************************/
Brandon Lowc95044a2006-04-13 14:17:12 +000022
Brandon Low1f3360f2006-10-13 20:48:23 +000023/* TODO: Pause should be handled in here, rather than PCMBUF so that voice can
24 * play whilst audio is paused */
Brandon Lowc95044a2006-04-13 14:17:12 +000025
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000026#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <ctype.h>
30
31#include "system.h"
32#include "thread.h"
33#include "file.h"
Michael Sevakise1dd10d2007-03-19 22:04:17 +000034#include "panic.h"
35#include "memory.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000036#include "lcd.h"
37#include "font.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000038#include "button.h"
39#include "kernel.h"
40#include "tree.h"
41#include "debug.h"
42#include "sprintf.h"
43#include "settings.h"
Daniel Stenberg1dd672f2005-06-22 19:41:30 +000044#include "codecs.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000045#include "audio.h"
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +000046#include "buffering.h"
Nicolas Pennequin33f522d2008-04-03 17:51:53 +000047#include "events.h"
Michael Sevakis99617d72007-11-18 17:12:19 +000048#include "voice_thread.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000049#include "mp3_playback.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000050#include "usb.h"
51#include "status.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000052#include "ata.h"
53#include "screens.h"
54#include "playlist.h"
55#include "playback.h"
Miika Pekkarinen20b38972005-07-13 12:48:22 +000056#include "pcmbuf.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000057#include "buffer.h"
Miika Pekkarinend8cb7032005-06-26 19:41:29 +000058#include "dsp.h"
Brandon Low8d5a6602006-01-21 23:43:57 +000059#include "abrepeat.h"
Nicolas Pennequin9f4bd872007-02-14 14:40:24 +000060#include "cuesheet.h"
Jonathan Gordon710ccb72006-10-25 10:17:57 +000061#ifdef HAVE_TAGCACHE
Miika Pekkarinenb7251262006-03-26 16:37:18 +000062#include "tagcache.h"
Jonathan Gordon710ccb72006-10-25 10:17:57 +000063#endif
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000064#ifdef HAVE_LCD_BITMAP
65#include "icons.h"
66#include "peakmeter.h"
67#include "action.h"
Nicolas Pennequin9d4bed72007-11-11 12:29:37 +000068#include "albumart.h"
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000069#endif
70#include "lang.h"
71#include "bookmark.h"
72#include "misc.h"
73#include "sound.h"
Dave Chapman3ad485b2005-06-14 22:27:57 +000074#include "metadata.h"
Kevin Ferraree991bee2005-11-16 15:12:15 +000075#include "splash.h"
Michael Sevakis4fc717a2006-08-28 22:38:41 +000076#include "talk.h"
Jonathan Gordon0b227952006-11-06 14:24:18 +000077#include "ata_idle_notify.h"
Michael Sevakis4fc717a2006-08-28 22:38:41 +000078
79#ifdef HAVE_RECORDING
80#include "recording.h"
Michael Sevakis0f5cb942006-11-06 18:07:30 +000081#include "talk.h"
Michael Sevakis4fc717a2006-08-28 22:38:41 +000082#endif
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000083
Miika Pekkarinen815684a2006-09-17 08:34:42 +000084#define PLAYBACK_VOICE
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000085
Brandon Low62ccbbb2006-04-11 03:55:58 +000086/* default point to start buffer refill */
Miika Pekkarinende3b04e2005-06-29 14:46:27 +000087#define AUDIO_DEFAULT_WATERMARK (1024*512)
Brandon Low62ccbbb2006-04-11 03:55:58 +000088/* amount of guess-space to allow for codecs that must hunt and peck
89 * for their correct seeek target, 32k seems a good size */
90#define AUDIO_REBUFFER_GUESS_SIZE (1024*32)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +000091
Nicolas Pennequinfb709522007-10-24 22:06:36 +000092/* Define LOGF_ENABLE to enable logf output in this file */
93/*#define LOGF_ENABLE*/
94#include "logf.h"
95
Michael Sevakis0f5cb942006-11-06 18:07:30 +000096/* macros to enable logf for queues
97 logging on SYS_TIMEOUT can be disabled */
Michael Sevakis77771b02007-05-09 03:48:52 +000098#ifdef SIMULATOR
Michael Sevakis0f5cb942006-11-06 18:07:30 +000099/* Define this for logf output of all queuing except SYS_TIMEOUT */
100#define PLAYBACK_LOGQUEUES
101/* Define this to logf SYS_TIMEOUT messages */
Nicolas Pennequinfb709522007-10-24 22:06:36 +0000102/*#define PLAYBACK_LOGQUEUES_SYS_TIMEOUT*/
Steve Bavina3087e42006-08-31 10:01:07 +0000103#endif
104
Miika Pekkarinen815684a2006-09-17 08:34:42 +0000105#ifdef PLAYBACK_LOGQUEUES
Michael Sevakisa21871c2007-05-08 23:13:46 +0000106#define LOGFQUEUE logf
Steve Bavina3087e42006-08-31 10:01:07 +0000107#else
Michael Sevakisa21871c2007-05-08 23:13:46 +0000108#define LOGFQUEUE(...)
Steve Bavina3087e42006-08-31 10:01:07 +0000109#endif
110
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000111#ifdef PLAYBACK_LOGQUEUES_SYS_TIMEOUT
Michael Sevakisa21871c2007-05-08 23:13:46 +0000112#define LOGFQUEUE_SYS_TIMEOUT logf
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000113#else
Michael Sevakisa21871c2007-05-08 23:13:46 +0000114#define LOGFQUEUE_SYS_TIMEOUT(...)
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000115#endif
116
117
118/* Define one constant that includes recording related functionality */
119#if defined(HAVE_RECORDING) && !defined(SIMULATOR)
120#define AUDIO_HAVE_RECORDING
121#endif
122
Miika Pekkarinend3191162006-01-27 11:39:46 +0000123enum {
Michael Sevakisd6f2a542007-11-19 11:59:52 +0000124 Q_NULL = 0,
Miika Pekkarinend3191162006-01-27 11:39:46 +0000125 Q_AUDIO_PLAY = 1,
126 Q_AUDIO_STOP,
127 Q_AUDIO_PAUSE,
Brandon Low27363632006-04-06 21:06:37 +0000128 Q_AUDIO_SKIP,
Brandon Lowab570252006-04-07 18:18:36 +0000129 Q_AUDIO_PRE_FF_REWIND,
Miika Pekkarinend3191162006-01-27 11:39:46 +0000130 Q_AUDIO_FF_REWIND,
Brandon Low62ccbbb2006-04-11 03:55:58 +0000131 Q_AUDIO_CHECK_NEW_TRACK,
Miika Pekkarinend3191162006-01-27 11:39:46 +0000132 Q_AUDIO_FLUSH,
133 Q_AUDIO_TRACK_CHANGED,
Brandon Low27363632006-04-06 21:06:37 +0000134 Q_AUDIO_DIR_SKIP,
Miika Pekkarinend3191162006-01-27 11:39:46 +0000135 Q_AUDIO_POSTINIT,
Brandon Low930785c2006-04-06 16:21:31 +0000136 Q_AUDIO_FILL_BUFFER,
Nicolas Pennequin4e2de442008-04-14 16:17:47 +0000137 Q_AUDIO_FINISH_LOAD,
Brandon Lowfd084242006-04-14 13:05:08 +0000138 Q_CODEC_REQUEST_COMPLETE,
Brandon Low0291a6e2006-04-14 14:03:43 +0000139 Q_CODEC_REQUEST_FAILED,
Brandon Lowfd084242006-04-14 13:05:08 +0000140
Miika Pekkarinend3191162006-01-27 11:39:46 +0000141 Q_CODEC_LOAD,
142 Q_CODEC_LOAD_DISK,
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000143
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000144#ifdef AUDIO_HAVE_RECORDING
Michael Sevakis4fc717a2006-08-28 22:38:41 +0000145 Q_ENCODER_LOAD_DISK,
146 Q_ENCODER_RECORD,
147#endif
Miika Pekkarinend3191162006-01-27 11:39:46 +0000148};
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000149
Nicolas Pennequinf68147e2008-03-28 20:18:53 +0000150enum filling_state {
151 STATE_IDLE, /* audio is stopped: nothing to do */
152 STATE_FILLING, /* adding tracks to the buffer */
153 STATE_FULL, /* can't add any more tracks */
154 STATE_FINISHED, /* all remaining tracks have been added */
155};
156
Bertrik Sikkenc97e5032008-04-28 14:13:13 +0000157#if MEM > 1
158#define MAX_TRACK 128
159#else
160#define MAX_TRACK 32
161#endif
162
163#define MAX_TRACK_MASK (MAX_TRACK-1)
164
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000165/* As defined in plugins/lib/xxx2wav.h */
Dave Chapman0a7ded32006-08-13 09:19:24 +0000166#if MEM > 1
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000167#define MALLOC_BUFSIZE (512*1024)
Miika Pekkarinena4c190f2005-10-30 09:30:14 +0000168#define GUARD_BUFSIZE (32*1024)
Tomasz Malesinski28910e72006-08-12 23:01:52 +0000169#else
170#define MALLOC_BUFSIZE (100*1024)
171#define GUARD_BUFSIZE (8*1024)
172#endif
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000173
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000174/* As defined in plugin.lds */
Daniel Ankers242cbd52006-11-22 00:41:30 +0000175#if defined(CPU_PP)
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000176#define CODEC_IRAM_ORIGIN ((unsigned char *)0x4000c000)
177#define CODEC_IRAM_SIZE ((size_t)0xc000)
Jens Arnold88760182007-03-05 00:04:00 +0000178#elif defined(IAUDIO_X5) || defined(IAUDIO_M5)
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000179#define CODEC_IRAM_ORIGIN ((unsigned char *)0x10010000)
180#define CODEC_IRAM_SIZE ((size_t)0x10000)
Thom Johansena7b5a2c2006-03-22 15:19:59 +0000181#else
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000182#define CODEC_IRAM_ORIGIN ((unsigned char *)0x1000c000)
183#define CODEC_IRAM_SIZE ((size_t)0xc000)
Jens Arnold03adbd22006-11-10 07:45:30 +0000184#endif
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000185
Steve Bavin76691032008-03-25 19:46:35 +0000186bool audio_is_initialized = false;
Michael Sevakis05099142008-04-06 04:34:57 +0000187static bool audio_thread_ready SHAREDBSS_ATTR = false;
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000188
Steve Bavin145ec812006-10-30 08:54:48 +0000189/* Variables are commented with the threads that use them: *
190 * A=audio, C=codec, V=voice. A suffix of - indicates that *
191 * the variable is read but not updated on that thread. */
Steve Bavin6a270062006-11-13 09:07:18 +0000192/* TBD: Split out "audio" and "playback" (ie. calling) threads */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000193
Steve Bavin145ec812006-10-30 08:54:48 +0000194/* Main state control */
Michael Sevakis05099142008-04-06 04:34:57 +0000195static volatile bool audio_codec_loaded SHAREDBSS_ATTR = false; /* Codec loaded? (C/A-) */
196static volatile bool playing SHAREDBSS_ATTR = false; /* Is audio playing? (A) */
197static volatile bool paused SHAREDBSS_ATTR = false; /* Is audio paused? (A/C-) */
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000198
Brandon Lowbe95ff42007-01-26 18:35:49 +0000199/* Ring buffer where compressed audio and codecs are loaded */
Michael Sevakise4efe2f2007-03-09 13:41:33 +0000200static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */
Michael Sevakis1f8326b2007-03-11 00:37:58 +0000201static unsigned char *malloc_buf = NULL; /* Start of malloc buffer (A/C-) */
Brandon Lowbe95ff42007-01-26 18:35:49 +0000202/* FIXME: make filebuflen static */
Michael Sevakise4efe2f2007-03-09 13:41:33 +0000203size_t filebuflen = 0; /* Size of buffer (A/C-) */
Brandon Lowbe95ff42007-01-26 18:35:49 +0000204/* FIXME: make buf_ridx (C/A-) */
Miika Pekkarinenbbd42ad2005-07-01 18:22:04 +0000205
Brandon Lowbe95ff42007-01-26 18:35:49 +0000206/* Possible arrangements of the buffer */
207#define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000208#define BUFFER_STATE_INITIALIZED 0 /* voice+audio OR audio-only */
Brandon Lowbe95ff42007-01-26 18:35:49 +0000209#define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000210static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */
211
Nicolas Pennequin941600f2007-11-08 17:27:43 +0000212/* Used to keep the WPS up-to-date during track transtition */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000213static struct mp3entry prevtrack_id3;
Nicolas Pennequin941600f2007-11-08 17:27:43 +0000214
215/* Used to provide the codec with a pointer */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000216static struct mp3entry curtrack_id3;
Nicolas Pennequin941600f2007-11-08 17:27:43 +0000217
218/* Used to make next track info available while playing last track on buffer */
Nicolas Pennequin12d479f2007-11-08 16:52:29 +0000219static struct mp3entry lasttrack_id3;
Steve Bavin73e2f7b2006-10-12 16:51:22 +0000220
Brandon Lowbe95ff42007-01-26 18:35:49 +0000221/* Track info structure about songs in the file buffer (A/C-) */
Steve Bavin4d344572007-10-02 07:47:43 +0000222struct track_info {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000223 int audio_hid; /* The ID for the track's buffer handle */
224 int id3_hid; /* The ID for the track's metadata handle */
225 int codec_hid; /* The ID for the track's codec handle */
Nicolas Pennequin9d4bed72007-11-11 12:29:37 +0000226#ifdef HAVE_ALBUMART
227 int aa_hid; /* The ID for the track's album art handle */
228#endif
Steve Bavin4d344572007-10-02 07:47:43 +0000229
Steve Bavin4d344572007-10-02 07:47:43 +0000230 size_t filesize; /* File total length */
Steve Bavin4d344572007-10-02 07:47:43 +0000231
232 bool taginfo_ready; /* Is metadata read */
Steve Bavin4d344572007-10-02 07:47:43 +0000233};
234
Brandon Lowbe95ff42007-01-26 18:35:49 +0000235static struct track_info tracks[MAX_TRACK];
Michael Sevakise4efe2f2007-03-09 13:41:33 +0000236static volatile int track_ridx = 0; /* Track being decoded (A/C-) */
237static int track_widx = 0; /* Track being buffered (A) */
Steve Bavin73e2f7b2006-10-12 16:51:22 +0000238
Brandon Lowbe95ff42007-01-26 18:35:49 +0000239#define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */
Nicolas Pennequin49639252007-11-12 00:17:55 +0000240static struct track_info *prev_ti = NULL; /* Pointer to the previously played
241 track */
Brandon Lowa553d5f2006-03-23 16:18:17 +0000242
Brandon Lowbe95ff42007-01-26 18:35:49 +0000243/* Set by the audio thread when the current track information has updated
244 * and the WPS may need to update its cached information */
Michael Sevakise4efe2f2007-03-09 13:41:33 +0000245static bool track_changed = false;
Brandon Lowbe95ff42007-01-26 18:35:49 +0000246
247/* Information used only for filling the buffer */
248/* Playlist steps from playing track to next track to be buffered (A) */
Michael Sevakise4efe2f2007-03-09 13:41:33 +0000249static int last_peek_offset = 0;
Miika Pekkarinen431e8132005-06-19 18:41:53 +0000250
Steve Bavin145ec812006-10-30 08:54:48 +0000251/* Scrobbler support */
Michael Sevakise4efe2f2007-03-09 13:41:33 +0000252static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000253
Nicolas Pennequinf68147e2008-03-28 20:18:53 +0000254static enum filling_state filling;
255
Steve Bavin145ec812006-10-30 08:54:48 +0000256/* Track change controls */
Brandon Lowbe95ff42007-01-26 18:35:49 +0000257static bool automatic_skip = false; /* Who initiated in-progress skip? (C/A-) */
Brandon Lowbe95ff42007-01-26 18:35:49 +0000258static bool dir_skip = false; /* Is a directory skip pending? (A) */
259static bool new_playlist = false; /* Are we starting a new playlist? (A) */
Steve Bavin93b3fb32007-07-31 07:57:59 +0000260static int wps_offset = 0; /* Pending track change offset, to keep WPS responsive (A) */
261static bool skipped_during_pause = false; /* Do we need to clear the PCM buffer when playback resumes (A) */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000262
Nicolas Pennequin4e2de442008-04-14 16:17:47 +0000263static bool start_play_g = false; /* Used by audio_load_track to notify
264 audio_finish_load_track about start_play */
265
Magnus Holmgrenc8d2a242008-03-04 18:56:37 +0000266/* Set to true if the codec thread should send an audio stop request
267 * (typically because the end of the playlist has been reached).
268 */
269static bool codec_requested_stop = false;
270
Michael Sevakise4efe2f2007-03-09 13:41:33 +0000271static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */
Linus Nielsen Feltzing4aaa3212005-06-06 00:35:21 +0000272
Steve Bavin3cc46e72006-09-01 07:59:31 +0000273/* Multiple threads */
Brandon Lowbe95ff42007-01-26 18:35:49 +0000274/* Set the watermark to trigger buffer fill (A/C) FIXME */
Steve Bavin135cc752008-03-28 12:51:33 +0000275static void set_filebuf_watermark(int seconds, size_t max);
Miika Pekkarinen5899ed52005-06-08 10:33:01 +0000276
Steve Bavin3cc46e72006-09-01 07:59:31 +0000277/* Audio thread */
Michael Sevakis05099142008-04-06 04:34:57 +0000278static struct event_queue audio_queue SHAREDBSS_ATTR;
279static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000280static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
281static const char audio_thread_name[] = "audio";
282
283static void audio_thread(void);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000284static void audio_initiate_track_change(long direction);
285static bool audio_have_tracks(void);
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000286static void audio_reset_buffer(void);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000287
288/* Codec thread */
Miika Pekkarinen815684a2006-09-17 08:34:42 +0000289extern struct codec_api ci;
Michael Sevakis05099142008-04-06 04:34:57 +0000290static struct event_queue codec_queue SHAREDBSS_ATTR;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000291static struct queue_sender_list codec_queue_sender_list;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000292static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]
293IBSS_ATTR;
294static const char codec_thread_name[] = "codec";
Brandon Lowbe95ff42007-01-26 18:35:49 +0000295struct thread_entry *codec_thread_p; /* For modifying thread priority later. */
Steve Bavin145ec812006-10-30 08:54:48 +0000296
Michael Sevakisf38274f2008-02-28 22:37:46 +0000297/* PCM buffer messaging */
Michael Sevakis05099142008-04-06 04:34:57 +0000298static struct event_queue pcmbuf_queue SHAREDBSS_ATTR;
Michael Sevakisf38274f2008-02-28 22:37:46 +0000299
300/* Function to be called by pcm buffer callbacks.
301 * Permissible Context(s): Audio interrupt
302 */
Steve Bavin135cc752008-03-28 12:51:33 +0000303static void pcmbuf_callback_queue_post(long id, intptr_t data)
Michael Sevakisf38274f2008-02-28 22:37:46 +0000304{
305 /* No lock since we're already in audio interrupt context */
306 queue_post(&pcmbuf_queue, id, data);
307}
308
309/* Scan the pcmbuf queue and return true if a message pulled.
310 * Permissible Context(s): Thread
311 */
312static bool pcmbuf_queue_scan(struct queue_event *ev)
313{
314 if (!queue_empty(&pcmbuf_queue))
315 {
316 /* Transfer message to audio queue */
317 pcm_play_lock();
318 /* Pull message - never, ever any blocking call! */
319 queue_wait_w_tmo(&pcmbuf_queue, ev, 0);
320 pcm_play_unlock();
321 return true;
322 }
323
324 return false;
325}
326
327/* Clear the pcmbuf queue of messages
328 * Permissible Context(s): Thread
329 */
330static void pcmbuf_queue_clear(void)
331{
332 pcm_play_lock();
333 queue_clear(&pcmbuf_queue);
334 pcm_play_unlock();
335}
336
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000337/* --- Helper functions --- */
338
Steve Bavin135cc752008-03-28 12:51:33 +0000339static struct mp3entry *bufgetid3(int handle_id)
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000340{
341 if (handle_id < 0)
342 return NULL;
343
344 struct mp3entry *id3;
345 ssize_t ret = bufgetdata(handle_id, 0, (void *)&id3);
346
347 if (ret < 0 || ret != sizeof(struct mp3entry))
348 return NULL;
349
350 return id3;
351}
352
Steve Bavin530abe82007-10-26 09:39:05 +0000353static bool clear_track_info(struct track_info *track)
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000354{
Brandon Low31c11642007-11-04 19:01:02 +0000355 /* bufclose returns true if the handle is not found, or if it is closed
356 * successfully, so these checks are safe on non-existant handles */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000357 if (!track)
358 return false;
359
Brandon Low31c11642007-11-04 19:01:02 +0000360 if (track->codec_hid >= 0) {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000361 if (bufclose(track->codec_hid))
Brandon Low31c11642007-11-04 19:01:02 +0000362 track->codec_hid = -1;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000363 else
364 return false;
365 }
366
Brandon Low31c11642007-11-04 19:01:02 +0000367 if (track->id3_hid >= 0) {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000368 if (bufclose(track->id3_hid))
Brandon Low31c11642007-11-04 19:01:02 +0000369 track->id3_hid = -1;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000370 else
371 return false;
372 }
373
Brandon Low31c11642007-11-04 19:01:02 +0000374 if (track->audio_hid >= 0) {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000375 if (bufclose(track->audio_hid))
Brandon Low31c11642007-11-04 19:01:02 +0000376 track->audio_hid = -1;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000377 else
378 return false;
379 }
380
Nicolas Pennequin9d4bed72007-11-11 12:29:37 +0000381#ifdef HAVE_ALBUMART
382 if (track->aa_hid >= 0) {
383 if (bufclose(track->aa_hid))
384 track->aa_hid = -1;
385 else
386 return false;
Nicolas Pennequin9d4bed72007-11-11 12:29:37 +0000387 }
Nicolas Pennequincf37f4c2007-11-11 12:52:07 +0000388#endif
Nicolas Pennequin9d4bed72007-11-11 12:29:37 +0000389
Brandon Low31c11642007-11-04 19:01:02 +0000390 track->filesize = 0;
391 track->taginfo_ready = false;
Brandon Low31c11642007-11-04 19:01:02 +0000392
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000393 return true;
394}
395
Steve Bavin3cc46e72006-09-01 07:59:31 +0000396/* --- External interfaces --- */
397
Michael Sevakis9cd63942007-04-12 05:58:09 +0000398/* This sends a stop message and the audio thread will dump all it's
399 subsequenct messages */
Michael Sevakis99617d72007-11-18 17:12:19 +0000400void audio_hard_stop(void)
Michael Sevakis9cd63942007-04-12 05:58:09 +0000401{
402 /* Stop playback */
403 LOGFQUEUE("audio >| audio Q_AUDIO_STOP: 1");
404 queue_send(&audio_queue, Q_AUDIO_STOP, 1);
Michael Sevakis99617d72007-11-18 17:12:19 +0000405#ifdef PLAYBACK_VOICE
406 voice_stop();
407#endif
408}
409
Steve Bavin135cc752008-03-28 12:51:33 +0000410bool audio_restore_playback(int type)
Michael Sevakis99617d72007-11-18 17:12:19 +0000411{
412 switch (type)
413 {
414 case AUDIO_WANT_PLAYBACK:
415 if (buffer_state != BUFFER_STATE_INITIALIZED)
416 audio_reset_buffer();
417 return true;
418 case AUDIO_WANT_VOICE:
419 if (buffer_state == BUFFER_STATE_TRASHED)
420 audio_reset_buffer();
421 return true;
422 default:
423 return false;
424 }
Michael Sevakis9cd63942007-04-12 05:58:09 +0000425}
426
Steve Bavin135cc752008-03-28 12:51:33 +0000427unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000428{
Michael Sevakisacc29d92006-11-18 02:18:29 +0000429 unsigned char *buf, *end;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000430
Michael Sevakisa1af87f2006-11-27 13:54:50 +0000431 if (audio_is_initialized)
Michael Sevakis97d1ca52006-11-28 15:00:56 +0000432 {
Michael Sevakis9cd63942007-04-12 05:58:09 +0000433 audio_hard_stop();
Michael Sevakis97d1ca52006-11-28 15:00:56 +0000434 }
435 /* else buffer_state will be BUFFER_STATE_TRASHED at this point */
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000436
Michael Sevakisacc29d92006-11-18 02:18:29 +0000437 if (buffer_size == NULL)
438 {
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000439 /* Special case for talk_init to use since it already knows it's
440 trashed */
Michael Sevakisacc29d92006-11-18 02:18:29 +0000441 buffer_state = BUFFER_STATE_TRASHED;
442 return NULL;
443 }
444
Michael Sevakisa1af87f2006-11-27 13:54:50 +0000445 if (talk_buf || buffer_state == BUFFER_STATE_TRASHED
446 || !talk_voice_required())
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000447 {
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000448 logf("get buffer: talk, audio");
449 /* Ok to use everything from audiobuf to audiobufend - voice is loaded,
450 the talk buffer is not needed because voice isn't being used, or
451 could be BUFFER_STATE_TRASHED already. If state is
452 BUFFER_STATE_VOICED_ONLY, no problem as long as memory isn't written
453 without the caller knowing what's going on. Changing certain settings
454 may move it to a worse condition but the memory in use by something
455 else will remain undisturbed.
456 */
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000457 if (buffer_state != BUFFER_STATE_TRASHED)
Michael Sevakisfbac4f82006-11-22 09:13:14 +0000458 {
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000459 talk_buffer_steal();
Michael Sevakisfbac4f82006-11-22 09:13:14 +0000460 buffer_state = BUFFER_STATE_TRASHED;
461 }
Michael Sevakis97d1ca52006-11-28 15:00:56 +0000462
463 buf = audiobuf;
464 end = audiobufend;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000465 }
466 else
467 {
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000468 /* Safe to just return this if already BUFFER_STATE_VOICED_ONLY or
469 still BUFFER_STATE_INITIALIZED */
470 /* Skip talk buffer and move pcm buffer to end to maximize available
471 contiguous memory - no audio running means voice will not need the
472 swap space */
473 logf("get buffer: audio");
Michael Sevakis97d1ca52006-11-28 15:00:56 +0000474 buf = audiobuf + talk_get_bufsize();
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000475 end = audiobufend - pcmbuf_init(audiobufend);
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000476 buffer_state = BUFFER_STATE_VOICED_ONLY;
477 }
478
479 *buffer_size = end - buf;
480
481 return buf;
482}
483
484#ifdef HAVE_RECORDING
485unsigned char *audio_get_recording_buffer(size_t *buffer_size)
486{
Michael Sevakis99617d72007-11-18 17:12:19 +0000487 /* Stop audio, voice and obtain all available buffer space */
Michael Sevakis9cd63942007-04-12 05:58:09 +0000488 audio_hard_stop();
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000489 talk_buffer_steal();
490
Michael Sevakis99617d72007-11-18 17:12:19 +0000491 unsigned char *end = audiobufend;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000492 buffer_state = BUFFER_STATE_TRASHED;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000493 *buffer_size = end - audiobuf;
494
495 return (unsigned char *)audiobuf;
496}
497
498bool audio_load_encoder(int afmt)
499{
500#ifndef SIMULATOR
501 const char *enc_fn = get_codec_filename(afmt | CODEC_TYPE_ENCODER);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000502 if (!enc_fn)
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000503 return false;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000504
505 audio_remove_encoder();
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000506 ci.enc_codec_loaded = 0; /* clear any previous error condition */
Steve Bavin3cc46e72006-09-01 07:59:31 +0000507
Michael Sevakis598629c2007-03-04 04:16:53 +0000508 LOGFQUEUE("codec > Q_ENCODER_LOAD_DISK");
509 queue_post(&codec_queue, Q_ENCODER_LOAD_DISK, (intptr_t)enc_fn);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000510
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000511 while (ci.enc_codec_loaded == 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000512 yield();
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000513
514 logf("codec loaded: %d", ci.enc_codec_loaded);
515
516 return ci.enc_codec_loaded > 0;
517#else
518 (void)afmt;
519 return true;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000520#endif
Steve Bavin3cc46e72006-09-01 07:59:31 +0000521} /* audio_load_encoder */
522
523void audio_remove_encoder(void)
524{
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000525#ifndef SIMULATOR
526 /* force encoder codec unload (if currently loaded) */
527 if (ci.enc_codec_loaded <= 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000528 return;
529
Michael Sevakis598629c2007-03-04 04:16:53 +0000530 ci.stop_encoder = true;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000531 while (ci.enc_codec_loaded > 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000532 yield();
533#endif
534} /* audio_remove_encoder */
535
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000536#endif /* HAVE_RECORDING */
537
Nicolas Pennequin9d4bed72007-11-11 12:29:37 +0000538#ifdef HAVE_ALBUMART
539int audio_current_aa_hid(void)
540{
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000541 int cur_idx;
542 int offset = ci.new_track + wps_offset;
543
544 cur_idx = track_ridx + offset;
545 cur_idx &= MAX_TRACK_MASK;
546
547 return tracks[cur_idx].aa_hid;
Nicolas Pennequin9d4bed72007-11-11 12:29:37 +0000548}
549#endif
550
Steve Bavin3cc46e72006-09-01 07:59:31 +0000551struct mp3entry* audio_current_track(void)
552{
553 const char *filename;
554 const char *p;
555 static struct mp3entry temp_id3;
Nicolas Pennequinbabd3be2008-04-15 20:59:47 +0000556 struct playlist_track_info trackinfo;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000557 int cur_idx;
Miika Pekkarinen815684a2006-09-17 08:34:42 +0000558 int offset = ci.new_track + wps_offset;
Steve Bavin7707f7f2007-10-29 13:55:34 +0000559
Nicolas Pennequincfa64952007-11-19 15:52:25 +0000560 cur_idx = (track_ridx + offset) & MAX_TRACK_MASK;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000561
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000562 if (cur_idx == track_ridx && *curtrack_id3.path)
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000563 {
564 /* The usual case */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000565 return &curtrack_id3;
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000566 }
Nicolas Pennequinb36d3c02008-04-15 19:20:57 +0000567 else if (automatic_skip && offset == -1 && *prevtrack_id3.path)
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000568 {
569 /* We're in a track transition. The codec has moved on to the nex track,
570 but the audio being played is still the same (now previous) track.
571 prevtrack_id3.elapsed is being updated in an ISR by
572 codec_pcmbuf_position_callback */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000573 return &prevtrack_id3;
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000574 }
Brandon Low31c11642007-11-04 19:01:02 +0000575 else if (tracks[cur_idx].id3_hid >= 0)
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000576 {
577 /* Get the ID3 metadata from the main buffer */
Nicolas Pennequinb36d3c02008-04-15 19:20:57 +0000578 struct mp3entry *ret = bufgetid3(tracks[cur_idx].id3_hid);
579 if (ret) return ret;
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000580 }
581
582 /* We didn't find the ID3 metadata, so we fill temp_id3 with the little info
583 we have and return that. */
Steve Bavin3cc46e72006-09-01 07:59:31 +0000584
585 memset(&temp_id3, 0, sizeof(struct mp3entry));
Steve Bavin7707f7f2007-10-29 13:55:34 +0000586
Nicolas Pennequinbabd3be2008-04-15 20:59:47 +0000587 playlist_get_track_info(NULL, playlist_next(0)+wps_offset, &trackinfo);
588 filename = trackinfo.filename;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000589 if (!filename)
590 filename = "No file!";
591
Miika Pekkarinen6ab1c902008-03-09 20:33:19 +0000592#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000593 if (tagcache_fill_tags(&temp_id3, filename))
594 return &temp_id3;
595#endif
596
597 p = strrchr(filename, '/');
598 if (!p)
599 p = filename;
600 else
601 p++;
602
603 strncpy(temp_id3.path, p, sizeof(temp_id3.path)-1);
604 temp_id3.title = &temp_id3.path[0];
605
606 return &temp_id3;
607}
608
609struct mp3entry* audio_next_track(void)
610{
Nicolas Pennequinea9e02b2008-02-26 17:15:30 +0000611 int next_idx;
612 int offset = ci.new_track + wps_offset;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000613
614 if (!audio_have_tracks())
615 return NULL;
616
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000617 if (wps_offset == -1 && *prevtrack_id3.path)
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000618 {
619 /* We're in a track transition. The next track for the WPS is the one
620 currently being decoded. */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000621 return &curtrack_id3;
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000622 }
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000623
Nicolas Pennequinea9e02b2008-02-26 17:15:30 +0000624 next_idx = (track_ridx + offset + 1) & MAX_TRACK_MASK;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000625
Nicolas Pennequin4e2de442008-04-14 16:17:47 +0000626 if (tracks[next_idx].id3_hid >= 0)
627 return bufgetid3(tracks[next_idx].id3_hid);
628
Nicolas Pennequin12d479f2007-11-08 16:52:29 +0000629 if (next_idx == track_widx)
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000630 {
631 /* The next track hasn't been buffered yet, so we return the static
632 version of its metadata. */
Nicolas Pennequin12d479f2007-11-08 16:52:29 +0000633 return &lasttrack_id3;
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000634 }
Nicolas Pennequin12d479f2007-11-08 16:52:29 +0000635
Nicolas Pennequin4e2de442008-04-14 16:17:47 +0000636 return NULL;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000637}
638
639bool audio_has_changed_track(void)
640{
Steve Bavin7707f7f2007-10-29 13:55:34 +0000641 if (track_changed)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000642 {
643 track_changed = false;
644 return true;
645 }
646
647 return false;
648}
649
Steve Bavin135cc752008-03-28 12:51:33 +0000650void audio_play(long offset)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000651{
652 logf("audio_play");
Steve Bavin71dd94a2006-10-25 08:54:25 +0000653
654#ifdef PLAYBACK_VOICE
655 /* Truncate any existing voice output so we don't have spelling
656 * etc. over the first part of the played track */
Steve Bavin32a95752007-10-19 15:31:42 +0000657 talk_force_shutup();
Steve Bavin71dd94a2006-10-25 08:54:25 +0000658#endif
659
660 /* Start playback */
Michael Sevakise486ed02007-05-09 03:44:44 +0000661 LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %ld", offset);
Michael Sevakisd9839bc2007-05-08 22:59:25 +0000662 /* Don't return until playback has actually started */
Michael Sevakis1c59b102007-05-08 22:56:12 +0000663 queue_send(&audio_queue, Q_AUDIO_PLAY, offset);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000664}
665
666void audio_stop(void)
667{
Steve Bavin71dd94a2006-10-25 08:54:25 +0000668 /* Stop playback */
Michael Sevakisd7e8e382007-05-03 21:49:59 +0000669 LOGFQUEUE("audio >| audio Q_AUDIO_STOP");
Steve Bavin71dd94a2006-10-25 08:54:25 +0000670 /* Don't return until playback has actually stopped */
Michael Sevakisd7e8e382007-05-03 21:49:59 +0000671 queue_send(&audio_queue, Q_AUDIO_STOP, 0);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000672}
673
674void audio_pause(void)
675{
Michael Sevakis1c59b102007-05-08 22:56:12 +0000676 LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE");
677 /* Don't return until playback has actually paused */
678 queue_send(&audio_queue, Q_AUDIO_PAUSE, true);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000679}
680
681void audio_resume(void)
682{
Michael Sevakis1c59b102007-05-08 22:56:12 +0000683 LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE resume");
684 /* Don't return until playback has actually resumed */
685 queue_send(&audio_queue, Q_AUDIO_PAUSE, false);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000686}
687
Nicolas Pennequinf7301332008-02-26 17:15:33 +0000688static void audio_skip(int direction)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000689{
Nicolas Pennequinf7301332008-02-26 17:15:33 +0000690 if (playlist_check(ci.new_track + wps_offset + direction))
Miika Pekkarinen815684a2006-09-17 08:34:42 +0000691 {
Steve Bavin081281f2006-09-26 07:19:43 +0000692 if (global_settings.beep)
693 pcmbuf_beep(5000, 100, 2500*global_settings.beep);
694
Nicolas Pennequinf7301332008-02-26 17:15:33 +0000695 LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction);
696 queue_post(&audio_queue, Q_AUDIO_SKIP, direction);
Brandon Lowbe95ff42007-01-26 18:35:49 +0000697 /* Update wps while our message travels inside deep playback queues. */
Nicolas Pennequinf7301332008-02-26 17:15:33 +0000698 wps_offset += direction;
Miika Pekkarinen815684a2006-09-17 08:34:42 +0000699 track_changed = true;
700 }
701 else
702 {
703 /* No more tracks. */
704 if (global_settings.beep)
Steve Bavin081281f2006-09-26 07:19:43 +0000705 pcmbuf_beep(1000, 100, 1000*global_settings.beep);
Miika Pekkarinen815684a2006-09-17 08:34:42 +0000706 }
Steve Bavin3cc46e72006-09-01 07:59:31 +0000707}
708
Nicolas Pennequinf7301332008-02-26 17:15:33 +0000709void audio_next(void)
710{
711 audio_skip(1);
712}
713
Steve Bavin3cc46e72006-09-01 07:59:31 +0000714void audio_prev(void)
715{
Nicolas Pennequinf7301332008-02-26 17:15:33 +0000716 audio_skip(-1);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000717}
718
719void audio_next_dir(void)
720{
721 LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP 1");
Michael Sevakis4b902672006-12-19 16:50:07 +0000722 queue_post(&audio_queue, Q_AUDIO_DIR_SKIP, 1);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000723}
724
725void audio_prev_dir(void)
726{
727 LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP -1");
Michael Sevakis4b902672006-12-19 16:50:07 +0000728 queue_post(&audio_queue, Q_AUDIO_DIR_SKIP, -1);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000729}
730
731void audio_pre_ff_rewind(void)
732{
733 LOGFQUEUE("audio > audio Q_AUDIO_PRE_FF_REWIND");
734 queue_post(&audio_queue, Q_AUDIO_PRE_FF_REWIND, 0);
735}
736
Steve Bavin135cc752008-03-28 12:51:33 +0000737void audio_ff_rewind(long newpos)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000738{
739 LOGFQUEUE("audio > audio Q_AUDIO_FF_REWIND");
Michael Sevakis4b902672006-12-19 16:50:07 +0000740 queue_post(&audio_queue, Q_AUDIO_FF_REWIND, newpos);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000741}
742
743void audio_flush_and_reload_tracks(void)
744{
745 LOGFQUEUE("audio > audio Q_AUDIO_FLUSH");
746 queue_post(&audio_queue, Q_AUDIO_FLUSH, 0);
747}
748
749void audio_error_clear(void)
750{
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000751#ifdef AUDIO_HAVE_RECORDING
752 pcm_rec_error_clear();
753#endif
Steve Bavin3cc46e72006-09-01 07:59:31 +0000754}
755
756int audio_status(void)
757{
758 int ret = 0;
759
760 if (playing)
761 ret |= AUDIO_STATUS_PLAY;
762
763 if (paused)
764 ret |= AUDIO_STATUS_PAUSE;
765
766#ifdef HAVE_RECORDING
767 /* Do this here for constitency with mpeg.c version */
768 ret |= pcm_rec_status();
769#endif
770
771 return ret;
772}
773
Steve Bavin3cc46e72006-09-01 07:59:31 +0000774int audio_get_file_pos(void)
775{
776 return 0;
777}
778
Nils Wallménius0bfa3e72007-08-01 08:50:44 +0000779#ifndef HAVE_FLASH_STORAGE
Steve Bavin135cc752008-03-28 12:51:33 +0000780void audio_set_buffer_margin(int setting)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000781{
782 static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600};
783 buffer_margin = lookup[setting];
Nicolas Pennequin6631e362007-11-05 18:19:38 +0000784 logf("buffer margin: %ld", (long)buffer_margin);
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000785 set_filebuf_watermark(buffer_margin, 0);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000786}
Nils Wallménius0bfa3e72007-08-01 08:50:44 +0000787#endif
Steve Bavin3cc46e72006-09-01 07:59:31 +0000788
Steve Bavinc9df8fd2008-03-28 11:24:24 +0000789/* Take necessary steps to enable or disable the crossfade setting */
Steve Bavin135cc752008-03-28 12:51:33 +0000790void audio_set_crossfade(int enable)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000791{
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000792 size_t offset;
793 bool was_playing;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000794 size_t size;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000795
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000796 /* Tell it the next setting to use */
797 pcmbuf_crossfade_enable(enable);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000798
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000799 /* Return if size hasn't changed or this is too early to determine
800 which in the second case there's no way we could be playing
801 anything at all */
802 if (pcmbuf_is_same_size())
803 {
804 /* This function is a copout and just syncs some variables -
805 to be removed at a later date */
806 pcmbuf_crossfade_enable_finished();
807 return;
808 }
Steve Bavin3cc46e72006-09-01 07:59:31 +0000809
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000810 offset = 0;
811 was_playing = playing;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000812
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000813 /* Playback has to be stopped before changing the buffer size */
Steve Bavin3cc46e72006-09-01 07:59:31 +0000814 if (was_playing)
815 {
816 /* Store the track resume position */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000817 offset = curtrack_id3.offset;
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000818 gui_syncsplash(0, str(LANG_RESTARTING_PLAYBACK));
Steve Bavin3cc46e72006-09-01 07:59:31 +0000819 }
820
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000821 /* Blast it - audio buffer will have to be setup again next time
822 something plays */
823 audio_get_buffer(true, &size);
Steve Bavinc9a78622006-09-28 15:17:59 +0000824
Michael Sevakise1dd10d2007-03-19 22:04:17 +0000825 /* Restart playback if audio was running previously */
826 if (was_playing)
Steve Bavinfaace1e2006-11-01 19:19:36 +0000827 audio_play(offset);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000828}
829
Steve Bavin3cc46e72006-09-01 07:59:31 +0000830/* --- Routines called from multiple threads --- */
Nils Wallménius5b769362007-08-06 13:08:36 +0000831
Steve Bavin135cc752008-03-28 12:51:33 +0000832static void set_filebuf_watermark(int seconds, size_t max)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000833{
834 size_t bytes;
835
Steve Bavin3cc46e72006-09-01 07:59:31 +0000836 if (!filebuf)
837 return; /* Audio buffers not yet set up */
838
Brandon Lowf9064982007-11-04 05:36:35 +0000839 bytes = seconds?MAX(curtrack_id3.bitrate * seconds * (1000/8), max):max;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000840 bytes = MIN(bytes, filebuflen / 2);
Brandon Low33794402007-11-05 17:48:21 +0000841 buf_set_watermark(bytes);
Steve Bavin3cc46e72006-09-01 07:59:31 +0000842}
843
Steve Bavin135cc752008-03-28 12:51:33 +0000844const char *get_codec_filename(int cod_spec)
Steve Bavin3cc46e72006-09-01 07:59:31 +0000845{
846 const char *fname;
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000847
848#ifdef HAVE_RECORDING
849 /* Can choose decoder or encoder if one available */
850 int type = cod_spec & CODEC_TYPE_MASK;
851 int afmt = cod_spec & CODEC_AFMT_MASK;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000852
853 if ((unsigned)afmt >= AFMT_NUM_CODECS)
854 type = AFMT_UNKNOWN | (type & CODEC_TYPE_MASK);
855
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000856 fname = (type == CODEC_TYPE_ENCODER) ?
857 audio_formats[afmt].codec_enc_root_fn :
858 audio_formats[afmt].codec_root_fn;
Steve Bavin3cc46e72006-09-01 07:59:31 +0000859
860 logf("%s: %d - %s",
861 (type == CODEC_TYPE_ENCODER) ? "Encoder" : "Decoder",
862 afmt, fname ? fname : "<unknown>");
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000863#else /* !HAVE_RECORDING */
864 /* Always decoder */
Steve Bavin135cc752008-03-28 12:51:33 +0000865 if ((unsigned)cod_spec >= AFMT_NUM_CODECS)
866 cod_spec = AFMT_UNKNOWN;
867 fname = audio_formats[cod_spec].codec_root_fn;
868 logf("Codec: %d - %s", cod_spec, fname ? fname : "<unknown>");
Michael Sevakis0f5cb942006-11-06 18:07:30 +0000869#endif /* HAVE_RECORDING */
Steve Bavin3cc46e72006-09-01 07:59:31 +0000870
871 return fname;
872} /* get_codec_filename */
873
Steve Bavin3cc46e72006-09-01 07:59:31 +0000874/* --- Codec thread --- */
Michael Sevakisaba6ca02007-02-07 00:51:50 +0000875static bool codec_pcmbuf_insert_callback(
Steve Bavin135cc752008-03-28 12:51:33 +0000876 const void *ch1, const void *ch2, int count)
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000877{
Michael Sevakisaba6ca02007-02-07 00:51:50 +0000878 const char *src[2] = { ch1, ch2 };
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000879
Steve Bavin135cc752008-03-28 12:51:33 +0000880 while (count > 0)
Brandon Low522ec272006-04-25 18:21:05 +0000881 {
Steve Bavin135cc752008-03-28 12:51:33 +0000882 int out_count = dsp_output_count(ci.dsp, count);
Michael Sevakisaba6ca02007-02-07 00:51:50 +0000883 int inp_count;
884 char *dest;
885
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000886 /* Prevent audio from a previous track from playing */
887 if (ci.new_track || ci.stop_codec)
888 return true;
889
Steve Bavin7707f7f2007-10-29 13:55:34 +0000890 while ((dest = pcmbuf_request_buffer(&out_count)) == NULL)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000891 {
Michael Sevakisfadbf0a2007-11-20 03:44:25 +0000892 cancel_cpu_boost();
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000893 sleep(1);
894 if (ci.seek_time || ci.new_track || ci.stop_codec)
895 return true;
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000896 }
897
Linus Nielsen Feltzing591d2892005-08-10 23:17:55 +0000898 /* Get the real input_size for output_size bytes, guarding
899 * against resampling buffer overflows. */
Michael Sevakis99617d72007-11-18 17:12:19 +0000900 inp_count = dsp_input_count(ci.dsp, out_count);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000901
Michael Sevakis09186e32007-02-16 12:01:35 +0000902 if (inp_count <= 0)
903 return true;
Brandon Low413da2a2006-02-07 20:38:55 +0000904
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000905 /* Input size has grown, no error, just don't write more than length */
Steve Bavin135cc752008-03-28 12:51:33 +0000906 if (inp_count > count)
907 inp_count = count;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000908
Michael Sevakis99617d72007-11-18 17:12:19 +0000909 out_count = dsp_process(ci.dsp, dest, src, inp_count);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000910
Michael Sevakis09186e32007-02-16 12:01:35 +0000911 if (out_count <= 0)
912 return true;
913
Michael Sevakisaba6ca02007-02-07 00:51:50 +0000914 pcmbuf_write_complete(out_count);
Miika Pekkarinen159c52d2005-08-20 11:13:19 +0000915
Steve Bavin135cc752008-03-28 12:51:33 +0000916 count -= inp_count;
Brandon Low522ec272006-04-25 18:21:05 +0000917 }
Magnus Holmgren08761aa2005-07-16 12:25:28 +0000918
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000919 return true;
Michael Sevakisaba6ca02007-02-07 00:51:50 +0000920} /* codec_pcmbuf_insert_callback */
Miika Pekkarinend8cb7032005-06-26 19:41:29 +0000921
Steve Bavin3cc46e72006-09-01 07:59:31 +0000922static void* codec_get_memory_callback(size_t *size)
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000923{
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000924 *size = MALLOC_BUFSIZE;
Michael Sevakis1f8326b2007-03-11 00:37:58 +0000925 return malloc_buf;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000926}
927
Nicolas Pennequin496027d2007-11-11 15:50:52 +0000928/* Between the codec and PCM track change, we need to keep updating the
929 "elapsed" value of the previous (to the codec, but current to the
930 user/PCM/WPS) track, so that the progressbar reaches the end.
931 During that transition, the WPS will display prevtrack_id3. */
Steve Bavin135cc752008-03-28 12:51:33 +0000932static void codec_pcmbuf_position_callback(size_t size) ICODE_ATTR;
933static void codec_pcmbuf_position_callback(size_t size)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000934{
Steve Bavin34193e52006-10-26 12:13:28 +0000935 /* This is called from an ISR, so be quick */
Brandon Low413da2a2006-02-07 20:38:55 +0000936 unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY +
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000937 prevtrack_id3.elapsed;
938
Steve Bavin7707f7f2007-10-29 13:55:34 +0000939 if (time >= prevtrack_id3.length)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000940 {
Brandon Lowa3868d32006-01-21 22:42:44 +0000941 pcmbuf_set_position_callback(NULL);
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000942 prevtrack_id3.elapsed = prevtrack_id3.length;
943 }
944 else
945 prevtrack_id3.elapsed = time;
Brandon Lowa3868d32006-01-21 22:42:44 +0000946}
947
Steve Bavin135cc752008-03-28 12:51:33 +0000948static void codec_set_elapsed_callback(unsigned int value)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000949{
950 unsigned int latency;
Brandon Lowd461a3e2006-04-23 23:27:11 +0000951 if (ci.seek_time)
952 return;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000953
Brandon Low8d5a6602006-01-21 23:43:57 +0000954#ifdef AB_REPEAT_ENABLE
955 ab_position_report(value);
956#endif
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000957
Brandon Lowd461a3e2006-04-23 23:27:11 +0000958 latency = pcmbuf_get_latency();
Brandon Low87484fc2006-04-11 20:41:04 +0000959 if (value < latency)
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000960 curtrack_id3.elapsed = 0;
961 else if (value - latency > curtrack_id3.elapsed ||
962 value - latency < curtrack_id3.elapsed - 2)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000963 {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000964 curtrack_id3.elapsed = value - latency;
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000965 }
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000966}
967
Steve Bavin135cc752008-03-28 12:51:33 +0000968static void codec_set_offset_callback(size_t value)
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000969{
Brandon Lowd461a3e2006-04-23 23:27:11 +0000970 unsigned int latency;
Steve Bavin7707f7f2007-10-29 13:55:34 +0000971
Brandon Lowd461a3e2006-04-23 23:27:11 +0000972 if (ci.seek_time)
973 return;
974
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000975 latency = pcmbuf_get_latency() * curtrack_id3.bitrate / 8;
Brandon Low87484fc2006-04-11 20:41:04 +0000976 if (value < latency)
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000977 curtrack_id3.offset = 0;
Brandon Low87484fc2006-04-11 20:41:04 +0000978 else
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000979 curtrack_id3.offset = value - latency;
Ryan Jacksond1917562005-07-12 16:45:38 +0000980}
981
Steve Bavin135cc752008-03-28 12:51:33 +0000982static void codec_advance_buffer_counters(size_t amount)
Miika Pekkarinend43bff92006-07-29 17:25:31 +0000983{
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000984 bufadvance(CUR_TI->audio_hid, amount);
Brandon Low86f1e2e2006-03-24 13:43:15 +0000985 ci.curpos += amount;
Brandon Low86f1e2e2006-03-24 13:43:15 +0000986}
987
988/* copy up-to size bytes into ptr and return the actual size copied */
Steve Bavin135cc752008-03-28 12:51:33 +0000989static size_t codec_filebuf_callback(void *ptr, size_t size)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000990{
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000991 ssize_t copy_n;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000992
Brandon Lowf3bc1ef2006-04-22 14:40:13 +0000993 if (ci.stop_codec || !playing)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +0000994 return 0;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000995
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +0000996 copy_n = bufread(CUR_TI->audio_hid, size, ptr);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +0000997
Brandon Low86f1e2e2006-03-24 13:43:15 +0000998 /* Nothing requested OR nothing left */
999 if (copy_n == 0)
1000 return 0;
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001001
Brandon Low86f1e2e2006-03-24 13:43:15 +00001002 /* Update read and other position pointers */
Steve Bavina3087e42006-08-31 10:01:07 +00001003 codec_advance_buffer_counters(copy_n);
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001004
Brandon Low86f1e2e2006-03-24 13:43:15 +00001005 /* Return the actual amount of data copied to the buffer */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001006 return copy_n;
Michael Sevakis4fc717a2006-08-28 22:38:41 +00001007} /* codec_filebuf_callback */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001008
Steve Bavin135cc752008-03-28 12:51:33 +00001009static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001010{
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001011 size_t copy_n = reqsize;
1012 ssize_t ret;
1013 void *ptr;
Miika Pekkarinen159c52d2005-08-20 11:13:19 +00001014
Steve Bavin7707f7f2007-10-29 13:55:34 +00001015 if (!playing)
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001016 {
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001017 *realsize = 0;
1018 return NULL;
1019 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001020
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001021 ret = bufgetdata(CUR_TI->audio_hid, reqsize, &ptr);
1022 if (ret >= 0)
1023 copy_n = MIN((size_t)ret, reqsize);
1024
1025 if (copy_n == 0)
Miika Pekkarinend43bff92006-07-29 17:25:31 +00001026 {
Brandon Low86f1e2e2006-03-24 13:43:15 +00001027 *realsize = 0;
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001028 return NULL;
1029 }
Zakk Roberts8bdd92b2006-03-30 05:56:19 +00001030
Brandon Low86f1e2e2006-03-24 13:43:15 +00001031 *realsize = copy_n;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001032
1033 return ptr;
Michael Sevakis4fc717a2006-08-28 22:38:41 +00001034} /* codec_request_buffer_callback */
Linus Nielsen Feltzing1c497e62005-06-05 23:05:10 +00001035
Brandon Low62ccbbb2006-04-11 03:55:58 +00001036static int get_codec_base_type(int type)
1037{
1038 switch (type) {
1039 case AFMT_MPA_L1:
1040 case AFMT_MPA_L2:
1041 case AFMT_MPA_L3:
1042 return AFMT_MPA_L3;
1043 }
1044
1045 return type;
1046}
1047
Steve Bavin135cc752008-03-28 12:51:33 +00001048static void codec_advance_buffer_callback(size_t amount)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001049{
Steve Bavin3cc46e72006-09-01 07:59:31 +00001050 codec_advance_buffer_counters(amount);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001051 codec_set_offset_callback(ci.curpos);
1052}
1053
1054static void codec_advance_buffer_loc_callback(void *ptr)
1055{
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001056 size_t amount = buf_get_offset(CUR_TI->audio_hid, ptr);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001057 codec_advance_buffer_callback(amount);
1058}
1059
Steve Bavin3cc46e72006-09-01 07:59:31 +00001060static void codec_seek_complete_callback(void)
1061{
1062 logf("seek_complete");
Steve Bavin7707f7f2007-10-29 13:55:34 +00001063 if (pcm_is_paused())
Steve Bavin3cc46e72006-09-01 07:59:31 +00001064 {
1065 /* If this is not a seamless seek, clear the buffer */
1066 pcmbuf_play_stop();
Michael Sevakis99617d72007-11-18 17:12:19 +00001067 dsp_configure(ci.dsp, DSP_FLUSH, 0);
Michael Sevakisbbef13e2006-11-26 12:02:47 +00001068
Steve Bavin3cc46e72006-09-01 07:59:31 +00001069 /* If playback was not 'deliberately' paused, unpause now */
1070 if (!paused)
1071 pcmbuf_pause(false);
1072 }
1073 ci.seek_time = 0;
1074}
1075
Steve Bavin135cc752008-03-28 12:51:33 +00001076static bool codec_seek_buffer_callback(size_t newpos)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001077{
Steve Bavin3cc46e72006-09-01 07:59:31 +00001078 logf("codec_seek_buffer_callback");
1079
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001080 int ret = bufseek(CUR_TI->audio_hid, newpos);
1081 if (ret == 0) {
1082 ci.curpos = newpos;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001083 return true;
1084 }
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001085 else {
1086 return false;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001087 }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001088}
1089
Steve Bavin135cc752008-03-28 12:51:33 +00001090static void codec_configure_callback(int setting, intptr_t value)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001091{
1092 switch (setting) {
1093 case CODEC_SET_FILEBUF_WATERMARK:
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001094 set_filebuf_watermark(buffer_margin, value);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001095 break;
1096
Steve Bavin3cc46e72006-09-01 07:59:31 +00001097 default:
Michael Sevakis99617d72007-11-18 17:12:19 +00001098 if (!dsp_configure(ci.dsp, setting, value))
1099 { logf("Illegal key:%d", setting); }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001100 }
1101}
1102
1103static void codec_track_changed(void)
1104{
Steve Bavin3cc46e72006-09-01 07:59:31 +00001105 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
1106 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
1107}
1108
1109static void codec_pcmbuf_track_changed_callback(void)
1110{
1111 pcmbuf_set_position_callback(NULL);
Michael Sevakisf38274f2008-02-28 22:37:46 +00001112 pcmbuf_callback_queue_post(Q_AUDIO_TRACK_CHANGED, 0);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001113}
1114
1115static void codec_discard_codec_callback(void)
1116{
Brandon Low31c11642007-11-04 19:01:02 +00001117 if (CUR_TI->codec_hid >= 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001118 {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001119 bufclose(CUR_TI->codec_hid);
Brandon Low31c11642007-11-04 19:01:02 +00001120 CUR_TI->codec_hid = -1;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001121 }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001122}
1123
Steve Bavin7707f7f2007-10-29 13:55:34 +00001124static inline void codec_gapless_track_change(void)
1125{
Brandon Lowbe95ff42007-01-26 18:35:49 +00001126 /* callback keeps the progress bar moving while the pcmbuf empties */
1127 pcmbuf_set_position_callback(codec_pcmbuf_position_callback);
1128 /* set the pcmbuf callback for when the track really changes */
1129 pcmbuf_set_event_handler(codec_pcmbuf_track_changed_callback);
1130}
1131
Steve Bavin7707f7f2007-10-29 13:55:34 +00001132static inline void codec_crossfade_track_change(void)
1133{
Brandon Lowbe95ff42007-01-26 18:35:49 +00001134 /* Initiate automatic crossfade mode */
1135 pcmbuf_crossfade_init(false);
1136 /* Notify the wps that the track change starts now */
1137 codec_track_changed();
1138}
1139
Steve Bavin135cc752008-03-28 12:51:33 +00001140static void codec_track_skip_done(bool was_manual)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001141{
1142 /* Manual track change (always crossfade or flush audio). */
1143 if (was_manual)
1144 {
1145 pcmbuf_crossfade_init(true);
1146 LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED");
1147 queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0);
1148 }
1149 /* Automatic track change w/crossfade, if not in "Track Skip Only" mode. */
1150 else if (pcmbuf_is_crossfade_enabled() && !pcmbuf_is_crossfade_active()
Steve Bavin7707f7f2007-10-29 13:55:34 +00001151 && global_settings.crossfade != CROSSFADE_ENABLE_TRACKSKIP)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001152 {
Steve Bavin7707f7f2007-10-29 13:55:34 +00001153 if (global_settings.crossfade == CROSSFADE_ENABLE_SHUFFLE_AND_TRACKSKIP)
Jonathan Gordon3bceded2006-12-19 01:26:37 +00001154 {
Brandon Lowbe95ff42007-01-26 18:35:49 +00001155 if (global_settings.playlist_shuffle)
1156 /* shuffle mode is on, so crossfade: */
1157 codec_crossfade_track_change();
1158 else
1159 /* shuffle mode is off, so do a gapless track change */
1160 codec_gapless_track_change();
Jonathan Gordon3bceded2006-12-19 01:26:37 +00001161 }
Brandon Lowbe95ff42007-01-26 18:35:49 +00001162 else
1163 /* normal crossfade: */
1164 codec_crossfade_track_change();
Steve Bavin3cc46e72006-09-01 07:59:31 +00001165 }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001166 else
Brandon Lowbe95ff42007-01-26 18:35:49 +00001167 /* normal gapless playback. */
1168 codec_gapless_track_change();
Steve Bavin3cc46e72006-09-01 07:59:31 +00001169}
1170
Steve Bavin7707f7f2007-10-29 13:55:34 +00001171static bool codec_load_next_track(void)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001172{
Michael Sevakise486ed02007-05-09 03:44:44 +00001173 intptr_t result = Q_CODEC_REQUEST_FAILED;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001174
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001175 prev_track_elapsed = curtrack_id3.elapsed;
Linus Nielsen Feltzingda153da2006-10-19 09:42:58 +00001176
Steve Bavin3cc46e72006-09-01 07:59:31 +00001177#ifdef AB_REPEAT_ENABLE
1178 ab_end_of_track_report();
1179#endif
1180
1181 logf("Request new track");
1182
1183 if (ci.new_track == 0)
1184 {
1185 ci.new_track++;
1186 automatic_skip = true;
1187 }
Michael Sevakis42b02082006-12-16 20:19:58 +00001188
Michael Sevakise486ed02007-05-09 03:44:44 +00001189 if (!ci.stop_codec)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001190 {
Michael Sevakise486ed02007-05-09 03:44:44 +00001191 trigger_cpu_boost();
1192 LOGFQUEUE("codec >| audio Q_AUDIO_CHECK_NEW_TRACK");
1193 result = queue_send(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001194 }
Miika Pekkarinen70587522006-10-15 11:57:52 +00001195
Michael Sevakis42b02082006-12-16 20:19:58 +00001196 switch (result)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001197 {
1198 case Q_CODEC_REQUEST_COMPLETE:
Michael Sevakis42b02082006-12-16 20:19:58 +00001199 LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE");
Steve Bavin3cc46e72006-09-01 07:59:31 +00001200 codec_track_skip_done(!automatic_skip);
1201 return true;
1202
1203 case Q_CODEC_REQUEST_FAILED:
Michael Sevakis42b02082006-12-16 20:19:58 +00001204 LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED");
Steve Bavin3cc46e72006-09-01 07:59:31 +00001205 ci.new_track = 0;
1206 ci.stop_codec = true;
Magnus Holmgrenc8d2a242008-03-04 18:56:37 +00001207 codec_requested_stop = true;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001208 return false;
1209
1210 default:
Michael Sevakis42b02082006-12-16 20:19:58 +00001211 LOGFQUEUE("codec |< default");
Steve Bavin3cc46e72006-09-01 07:59:31 +00001212 ci.stop_codec = true;
Magnus Holmgrenc8d2a242008-03-04 18:56:37 +00001213 codec_requested_stop = true;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001214 return false;
1215 }
1216}
1217
1218static bool codec_request_next_track_callback(void)
1219{
1220 int prev_codectype;
1221
1222 if (ci.stop_codec || !playing)
1223 return false;
1224
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001225 prev_codectype = get_codec_base_type(curtrack_id3.codectype);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001226
1227 if (!codec_load_next_track())
1228 return false;
1229
Nicolas Pennequin3c962592008-04-14 10:52:16 +00001230 /* Seek to the beginning of the new track because if the struct
1231 mp3entry was buffered, "elapsed" might not be zero (if the track has
1232 been played already but not unbuffered) */
1233 codec_seek_buffer_callback(curtrack_id3.first_frame_offset);
1234
Steve Bavin3cc46e72006-09-01 07:59:31 +00001235 /* Check if the next codec is the same file. */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001236 if (prev_codectype == get_codec_base_type(curtrack_id3.codectype))
Steve Bavin3cc46e72006-09-01 07:59:31 +00001237 {
1238 logf("New track loaded");
1239 codec_discard_codec_callback();
1240 return true;
1241 }
1242 else
1243 {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001244 logf("New codec:%d/%d", curtrack_id3.codectype, prev_codectype);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001245 return false;
1246 }
1247}
1248
1249static void codec_thread(void)
1250{
Michael Sevakisa9b2fb52007-10-16 01:25:17 +00001251 struct queue_event ev;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001252 int status;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001253
1254 while (1) {
1255 status = 0;
Andree Buschmann63acf3e2008-03-30 10:08:08 +00001256
1257 if (!pcmbuf_is_crossfade_active()) {
1258 cancel_cpu_boost();
1259 }
1260
Steve Bavin3cc46e72006-09-01 07:59:31 +00001261 queue_wait(&codec_queue, &ev);
Magnus Holmgrenc8d2a242008-03-04 18:56:37 +00001262 codec_requested_stop = false;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001263
1264 switch (ev.id) {
1265 case Q_CODEC_LOAD_DISK:
1266 LOGFQUEUE("codec < Q_CODEC_LOAD_DISK");
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001267 queue_reply(&codec_queue, 1);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001268 audio_codec_loaded = true;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001269 ci.stop_codec = false;
1270 status = codec_load_file((const char *)ev.data, &ci);
Steve Bavin2129d0f2007-08-02 13:19:35 +00001271 break;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001272
1273 case Q_CODEC_LOAD:
1274 LOGFQUEUE("codec < Q_CODEC_LOAD");
Brandon Low31c11642007-11-04 19:01:02 +00001275 if (CUR_TI->codec_hid < 0) {
Steve Bavin3cc46e72006-09-01 07:59:31 +00001276 logf("Codec slot is empty!");
1277 /* Wait for the pcm buffer to go empty */
1278 while (pcm_is_playing())
1279 yield();
1280 /* This must be set to prevent an infinite loop */
1281 ci.stop_codec = true;
1282 LOGFQUEUE("codec > codec Q_AUDIO_PLAY");
1283 queue_post(&codec_queue, Q_AUDIO_PLAY, 0);
Steve Bavin2129d0f2007-08-02 13:19:35 +00001284 break;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001285 }
1286
1287 audio_codec_loaded = true;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001288 ci.stop_codec = false;
Brandon Low86919f42007-11-03 17:55:29 +00001289 status = codec_load_buf(CUR_TI->codec_hid, &ci);
Steve Bavin2129d0f2007-08-02 13:19:35 +00001290 break;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001291
Michael Sevakis0f5cb942006-11-06 18:07:30 +00001292#ifdef AUDIO_HAVE_RECORDING
Steve Bavin3cc46e72006-09-01 07:59:31 +00001293 case Q_ENCODER_LOAD_DISK:
1294 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
Michael Sevakis9d56f2d2006-09-17 18:52:31 +00001295 audio_codec_loaded = false; /* Not audio codec! */
Michael Sevakis0f5cb942006-11-06 18:07:30 +00001296 logf("loading encoder");
Michael Sevakis598629c2007-03-04 04:16:53 +00001297 ci.stop_encoder = false;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001298 status = codec_load_file((const char *)ev.data, &ci);
Michael Sevakis0f5cb942006-11-06 18:07:30 +00001299 logf("encoder stopped");
Steve Bavin3cc46e72006-09-01 07:59:31 +00001300 break;
Michael Sevakis0f5cb942006-11-06 18:07:30 +00001301#endif /* AUDIO_HAVE_RECORDING */
Steve Bavin3cc46e72006-09-01 07:59:31 +00001302
Steve Bavin3cc46e72006-09-01 07:59:31 +00001303 default:
1304 LOGFQUEUE("codec < default");
1305 }
1306
1307 if (audio_codec_loaded)
1308 {
1309 if (ci.stop_codec)
1310 {
1311 status = CODEC_OK;
1312 if (!playing)
1313 pcmbuf_play_stop();
Steve Bavinfaace1e2006-11-01 19:19:36 +00001314
Steve Bavin3cc46e72006-09-01 07:59:31 +00001315 }
1316 audio_codec_loaded = false;
1317 }
1318
1319 switch (ev.id) {
1320 case Q_CODEC_LOAD_DISK:
1321 case Q_CODEC_LOAD:
1322 LOGFQUEUE("codec < Q_CODEC_LOAD");
Steve Bavin7707f7f2007-10-29 13:55:34 +00001323 if (playing)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001324 {
Steve Bavin7707f7f2007-10-29 13:55:34 +00001325 if (ci.new_track || status != CODEC_OK)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001326 {
Steve Bavin7707f7f2007-10-29 13:55:34 +00001327 if (!ci.new_track)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001328 {
1329 logf("Codec failure");
Jens Arnold4d6374c2007-03-16 21:56:08 +00001330 gui_syncsplash(HZ*2, "Codec failure");
Steve Bavin3cc46e72006-09-01 07:59:31 +00001331 }
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001332
Steve Bavin3cc46e72006-09-01 07:59:31 +00001333 if (!codec_load_next_track())
1334 {
Magnus Holmgrenc8d2a242008-03-04 18:56:37 +00001335 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
1336 /* End of playlist */
1337 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001338 break;
1339 }
Steve Bavin7707f7f2007-10-29 13:55:34 +00001340 }
1341 else
Steve Bavin3cc46e72006-09-01 07:59:31 +00001342 {
1343 logf("Codec finished");
1344 if (ci.stop_codec)
1345 {
1346 /* Wait for the audio to stop playing before
1347 * triggering the WPS exit */
1348 while(pcm_is_playing())
Steve Bavin004ee262006-10-19 07:43:07 +00001349 {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001350 curtrack_id3.elapsed =
1351 curtrack_id3.length - pcmbuf_get_latency();
Steve Bavin2baaac12006-10-19 08:55:24 +00001352 sleep(1);
Steve Bavin004ee262006-10-19 07:43:07 +00001353 }
Magnus Holmgrenc8d2a242008-03-04 18:56:37 +00001354
1355 if (codec_requested_stop)
1356 {
1357 LOGFQUEUE("codec > audio Q_AUDIO_STOP");
1358 queue_post(&audio_queue, Q_AUDIO_STOP, 0);
1359 }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001360 break;
1361 }
1362 }
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001363
Brandon Low31c11642007-11-04 19:01:02 +00001364 if (CUR_TI->codec_hid >= 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001365 {
1366 LOGFQUEUE("codec > codec Q_CODEC_LOAD");
1367 queue_post(&codec_queue, Q_CODEC_LOAD, 0);
1368 }
Steve Bavinc9859032007-10-29 13:57:29 +00001369 else
Steve Bavin3cc46e72006-09-01 07:59:31 +00001370 {
Brandon Lowbe95ff42007-01-26 18:35:49 +00001371 const char *codec_fn =
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001372 get_codec_filename(curtrack_id3.codectype);
Nicolas Pennequinca243ce2008-01-09 20:37:36 +00001373 if (codec_fn)
1374 {
1375 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
1376 queue_post(&codec_queue, Q_CODEC_LOAD_DISK,
1377 (intptr_t)codec_fn);
1378 }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001379 }
1380 }
1381 break;
1382
Michael Sevakis0f5cb942006-11-06 18:07:30 +00001383#ifdef AUDIO_HAVE_RECORDING
1384 case Q_ENCODER_LOAD_DISK:
1385 LOGFQUEUE("codec < Q_ENCODER_LOAD_DISK");
1386
1387 if (status == CODEC_OK)
1388 break;
1389
1390 logf("Encoder failure");
Jens Arnold4d6374c2007-03-16 21:56:08 +00001391 gui_syncsplash(HZ*2, "Encoder failure");
Michael Sevakis0f5cb942006-11-06 18:07:30 +00001392
1393 if (ci.enc_codec_loaded < 0)
1394 break;
1395
1396 logf("Encoder failed to load");
1397 ci.enc_codec_loaded = -1;
1398 break;
1399#endif /* AUDIO_HAVE_RECORDING */
1400
Steve Bavin3cc46e72006-09-01 07:59:31 +00001401 default:
1402 LOGFQUEUE("codec < default");
1403
1404 } /* end switch */
1405 }
1406}
1407
1408
Nicolas Pennequin33f522d2008-04-03 17:51:53 +00001409/* --- Buffering callbacks --- */
1410
1411static void buffering_low_buffer_callback(void *data)
1412{
1413 (void)data;
1414 logf("low buffer callback");
1415
1416 if (filling == STATE_FULL) {
1417 /* force a refill */
1418 LOGFQUEUE("buffering > audio Q_AUDIO_FILL_BUFFER");
1419 queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0);
1420 }
1421}
1422
1423static void buffering_handle_rebuffer_callback(void *data)
1424{
1425 (void)data;
1426 LOGFQUEUE("audio >| audio Q_AUDIO_FLUSH");
1427 queue_post(&audio_queue, Q_AUDIO_FLUSH, 0);
1428}
1429
1430static void buffering_handle_finished_callback(int *data)
1431{
1432 logf("handle %d finished buffering", *data);
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001433
1434 if (*data == tracks[track_widx].id3_hid)
1435 {
1436 /* The metadata handle for the last loaded track has been buffered.
1437 We can ask the audio thread to load the rest of the track's data. */
1438 LOGFQUEUE("audio >| audio Q_AUDIO_FINISH_LOAD");
1439 queue_post(&audio_queue, Q_AUDIO_FINISH_LOAD, 0);
1440 }
1441 else
1442 {
1443 /* This is most likely an audio handle, so we strip the useless
1444 trailing tags that are left. */
1445 strip_tags(*data);
1446 }
Nicolas Pennequin33f522d2008-04-03 17:51:53 +00001447}
1448
1449
Steve Bavin3cc46e72006-09-01 07:59:31 +00001450/* --- Audio thread --- */
1451
Steve Bavin3cc46e72006-09-01 07:59:31 +00001452static bool audio_have_tracks(void)
1453{
Nicolas Pennequind3b82452007-11-19 16:37:52 +00001454 return (audio_track_count() != 0);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001455}
1456
Nicolas Pennequind3b82452007-11-19 16:37:52 +00001457static int audio_free_track_count(void)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001458{
Nicolas Pennequind3b82452007-11-19 16:37:52 +00001459 /* Used tracks + free tracks adds up to MAX_TRACK - 1 */
1460 return MAX_TRACK - 1 - audio_track_count();
Steve Bavin3cc46e72006-09-01 07:59:31 +00001461}
Steve Bavin7707f7f2007-10-29 13:55:34 +00001462
Steve Bavin3cc46e72006-09-01 07:59:31 +00001463int audio_track_count(void)
1464{
Nicolas Pennequind3b82452007-11-19 16:37:52 +00001465 /* Calculate difference from track_ridx to track_widx
1466 * taking into account a possible wrap-around. */
1467 return (MAX_TRACK + track_widx - track_ridx) & MAX_TRACK_MASK;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001468}
1469
Steve Bavin73e2f7b2006-10-12 16:51:22 +00001470long audio_filebufused(void)
Jens Arnold8c6920e2008-03-08 23:50:55 +00001471{
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001472 return (long) buf_used();
Brandon Lowc95044a2006-04-13 14:17:12 +00001473}
1474
Nicolas Pennequin2ff51322007-11-19 17:30:46 +00001475/* Update track info after successful a codec track change */
Brandon Low1d41f772006-04-13 21:47:00 +00001476static void audio_update_trackinfo(void)
Brandon Lowc95044a2006-04-13 14:17:12 +00001477{
Nicolas Pennequin2ff51322007-11-19 17:30:46 +00001478 /* Load the curent track's metadata into curtrack_id3 */
Brandon Low31c11642007-11-04 19:01:02 +00001479 if (CUR_TI->id3_hid >= 0)
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001480 copy_mp3entry(&curtrack_id3, bufgetid3(CUR_TI->id3_hid));
1481
Nicolas Pennequin2ff51322007-11-19 17:30:46 +00001482 /* Reset current position */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001483 curtrack_id3.elapsed = 0;
1484 curtrack_id3.offset = 0;
Nicolas Pennequin2ff51322007-11-19 17:30:46 +00001485
1486 /* Update the codec API */
1487 ci.filesize = CUR_TI->filesize;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001488 ci.id3 = &curtrack_id3;
Brandon Lowc95044a2006-04-13 14:17:12 +00001489 ci.curpos = 0;
Steve Bavin5f70ad42006-10-12 15:54:36 +00001490 ci.taginfo_ready = &CUR_TI->taginfo_ready;
Brandon Lowc95044a2006-04-13 14:17:12 +00001491}
1492
Nicolas Pennequinc44d2bd2007-11-22 00:20:37 +00001493/* Clear tracks between write and read, non inclusive */
Miika Pekkarinend8204fa2008-03-08 08:05:29 +00001494static void audio_clear_track_entries(void)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001495{
1496 int cur_idx = track_widx;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001497
Miika Pekkarinend8204fa2008-03-08 08:05:29 +00001498 logf("Clearing tracks:%d/%d", track_ridx, track_widx);
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001499
Steve Bavin3cc46e72006-09-01 07:59:31 +00001500 /* Loop over all tracks from write-to-read */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001501 while (1)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001502 {
Nicolas Pennequincfa64952007-11-19 15:52:25 +00001503 cur_idx = (cur_idx + 1) & MAX_TRACK_MASK;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001504
1505 if (cur_idx == track_ridx)
1506 break;
1507
Miika Pekkarinend8204fa2008-03-08 08:05:29 +00001508 clear_track_info(&tracks[cur_idx]);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001509 }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001510}
1511
Nicolas Pennequinc44d2bd2007-11-22 00:20:37 +00001512/* Clear all tracks */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001513static bool audio_release_tracks(void)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001514{
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001515 int i, cur_idx;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001516
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001517 logf("releasing all tracks");
Steve Bavin3cc46e72006-09-01 07:59:31 +00001518
Nicolas Pennequinaef008b2007-11-05 18:46:25 +00001519 for(i = 0; i < MAX_TRACK; i++)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001520 {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001521 cur_idx = (track_ridx + i) & MAX_TRACK_MASK;
1522 if (!clear_track_info(&tracks[cur_idx]))
1523 return false;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001524 }
Mark Arigo9b0ef152006-11-14 15:48:20 +00001525
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001526 return true;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001527}
1528
Steve Bavin135cc752008-03-28 12:51:33 +00001529static bool audio_loadcodec(bool start_play)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001530{
Steve Bavin3cc46e72006-09-01 07:59:31 +00001531 int prev_track;
1532 char codec_path[MAX_PATH]; /* Full path to codec */
Nicolas Pennequin2cf63452008-04-16 16:41:35 +00001533 const struct mp3entry *id3, *prev_id3;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001534
Brandon Low31c11642007-11-04 19:01:02 +00001535 if (tracks[track_widx].id3_hid < 0) {
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001536 return false;
1537 }
1538
Nicolas Pennequin2cf63452008-04-16 16:41:35 +00001539 id3 = bufgetid3(tracks[track_widx].id3_hid);
1540 if (!id3)
1541 return false;
1542
1543 const char *codec_fn = get_codec_filename(id3->codectype);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001544 if (codec_fn == NULL)
1545 return false;
1546
Brandon Low31c11642007-11-04 19:01:02 +00001547 tracks[track_widx].codec_hid = -1;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001548
1549 if (start_play)
1550 {
1551 /* Load the codec directly from disk and save some memory. */
1552 track_ridx = track_widx;
Steve Bavin5f70ad42006-10-12 15:54:36 +00001553 ci.filesize = CUR_TI->filesize;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001554 ci.id3 = &curtrack_id3;
Steve Bavin5f70ad42006-10-12 15:54:36 +00001555 ci.taginfo_ready = &CUR_TI->taginfo_ready;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001556 ci.curpos = 0;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001557 LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK");
Michael Sevakis4b902672006-12-19 16:50:07 +00001558 queue_post(&codec_queue, Q_CODEC_LOAD_DISK, (intptr_t)codec_fn);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001559 return true;
1560 }
1561 else
1562 {
1563 /* If we already have another track than this one buffered */
Steve Bavin7707f7f2007-10-29 13:55:34 +00001564 if (track_widx != track_ridx)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001565 {
1566 prev_track = (track_widx - 1) & MAX_TRACK_MASK;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001567
Nicolas Pennequin2cf63452008-04-16 16:41:35 +00001568 id3 = bufgetid3(tracks[track_widx].id3_hid);
1569 prev_id3 = bufgetid3(tracks[prev_track].id3_hid);
1570
Steve Bavin3cc46e72006-09-01 07:59:31 +00001571 /* If the previous codec is the same as this one, there is no need
1572 * to put another copy of it on the file buffer */
Nicolas Pennequin2cf63452008-04-16 16:41:35 +00001573 if (id3 && prev_id3 &&
1574 get_codec_base_type(id3->codectype) ==
1575 get_codec_base_type(prev_id3->codectype)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001576 && audio_codec_loaded)
1577 {
1578 logf("Reusing prev. codec");
1579 return true;
1580 }
1581 }
1582 }
1583
1584 codec_get_full_path(codec_path, codec_fn);
1585
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001586 tracks[track_widx].codec_hid = bufopen(codec_path, 0, TYPE_CODEC);
1587 if (tracks[track_widx].codec_hid < 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001588 return false;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001589
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001590 logf("Loaded codec");
Brandon Lowc2e1cc12007-11-03 21:48:08 +00001591
Steve Bavin3cc46e72006-09-01 07:59:31 +00001592 return true;
1593}
1594
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001595/* Load metadata for the next track (with bufopen). The rest of the track
1596 loading will be handled by audio_finish_load_track once the metadata has been
1597 actually loaded by the buffering thread. */
1598static bool audio_load_track(size_t offset, bool start_play)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001599{
Steve Bavin38ed9012008-03-25 19:26:00 +00001600 const char *trackname;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001601 int fd = -1;
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001602
1603 start_play_g = start_play; /* will be read by audio_finish_load_track */
Steve Bavin3cc46e72006-09-01 07:59:31 +00001604
1605 /* Stop buffer filling if there is no free track entries.
1606 Don't fill up the last track entry (we wan't to store next track
1607 metadata there). */
Nicolas Pennequind3b82452007-11-19 16:37:52 +00001608 if (!audio_free_track_count())
Steve Bavin3cc46e72006-09-01 07:59:31 +00001609 {
1610 logf("No free tracks");
1611 return false;
1612 }
1613
Steve Bavin3cc46e72006-09-01 07:59:31 +00001614 last_peek_offset++;
Magnus Holmgren68f9e6b2008-02-23 17:46:33 +00001615 tracks[track_widx].taginfo_ready = false;
1616
Steve Bavin3cc46e72006-09-01 07:59:31 +00001617 logf("Buffering track:%d/%d", track_widx, track_ridx);
1618 /* Get track name from current playlist read position. */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001619 while ((trackname = playlist_peek(last_peek_offset)) != NULL)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001620 {
1621 /* Handle broken playlists. */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001622 fd = open(trackname, O_RDONLY);
1623 if (fd < 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001624 {
1625 logf("Open failed");
1626 /* Skip invalid entry from playlist. */
1627 playlist_skip_entry(NULL, last_peek_offset);
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001628 }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001629 else
1630 break;
1631 }
1632
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001633 if (!trackname)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001634 {
1635 logf("End-of-playlist");
Nicolas Pennequin94462262007-11-18 13:41:04 +00001636 memset(&lasttrack_id3, 0, sizeof(struct mp3entry));
Nicolas Pennequinf68147e2008-03-28 20:18:53 +00001637 filling = STATE_FINISHED;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001638 return false;
1639 }
1640
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001641 tracks[track_widx].filesize = filesize(fd);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001642
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001643 if (offset > tracks[track_widx].filesize)
Steve Bavin135cc752008-03-28 12:51:33 +00001644 offset = 0;
Nicolas Pennequin659fe5a2008-01-08 23:48:51 +00001645
Steve Bavin3cc46e72006-09-01 07:59:31 +00001646 /* Set default values */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001647 if (start_play)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001648 {
Brandon Low33794402007-11-05 17:48:21 +00001649 buf_set_watermark(AUDIO_DEFAULT_WATERMARK);
Michael Sevakis99617d72007-11-18 17:12:19 +00001650 dsp_configure(ci.dsp, DSP_RESET, 0);
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001651 track_changed = true;
1652 playlist_update_resume_info(audio_current_track());
Steve Bavin3cc46e72006-09-01 07:59:31 +00001653 }
1654
1655 /* Get track metadata if we don't already have it. */
Brandon Low31c11642007-11-04 19:01:02 +00001656 if (tracks[track_widx].id3_hid < 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001657 {
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001658 tracks[track_widx].id3_hid = bufopen(trackname, 0, TYPE_ID3);
1659
1660 if (tracks[track_widx].id3_hid < 0)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001661 {
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001662 /* Buffer is full. */
1663 get_metadata(&lasttrack_id3, fd, trackname);
1664 last_peek_offset--;
Nicolas Pennequin9d05e502007-10-30 18:01:49 +00001665 close(fd);
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001666 logf("buffer is full for now");
1667 filling = STATE_FULL;
1668 return false;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001669 }
1670
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001671 if (track_widx == track_ridx)
1672 {
1673 buf_request_buffer_handle(tracks[track_widx].id3_hid);
1674 copy_mp3entry(&curtrack_id3, bufgetid3(tracks[track_widx].id3_hid));
1675 curtrack_id3.offset = offset;
1676 }
1677
1678 if (start_play)
1679 {
1680 track_changed = true;
1681 playlist_update_resume_info(audio_current_track());
1682 }
Steve Bavin3cc46e72006-09-01 07:59:31 +00001683 }
1684
Nicolas Pennequinb36d3c02008-04-15 19:20:57 +00001685 close(fd);
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001686 return true;
1687}
1688
1689/* Second part of the track loading: We now have the metadata available, so we
1690 can load the codec, the album art and finally the audio data.
1691 This is called on the audio thread after the buffering thread calls the
1692 buffering_handle_finished_callback callback. */
1693static void audio_finish_load_track(void)
1694{
1695 char msgbuf[80];
1696 size_t file_offset = 0;
1697 size_t offset = 0;
1698 bool start_play = start_play_g;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001699
1700#if 0
Nicolas Pennequin9f4bd872007-02-14 14:40:24 +00001701 if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1)
1702 {
1703 char cuepath[MAX_PATH];
Nicolas Pennequin9f4bd872007-02-14 14:40:24 +00001704
1705 struct cuesheet *cue = start_play ? curr_cue : temp_cue;
1706
Nicolas Pennequin65798182007-05-28 23:18:31 +00001707 if (look_for_cuesheet_file(trackname, cuepath) &&
1708 parse_cuesheet(cuepath, cue))
Nicolas Pennequin9f4bd872007-02-14 14:40:24 +00001709 {
1710 strcpy((cue)->audio_filename, trackname);
1711 if (start_play)
1712 cue_spoof_id3(curr_cue, &tracks[track_widx].id3);
1713 }
1714 }
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001715#endif
Nicolas Pennequin9f4bd872007-02-14 14:40:24 +00001716
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001717 if (tracks[track_widx].id3_hid < 0) {
1718 logf("no metatdata");
1719 return;
1720 }
1721
Nicolas Pennequin95db8f82007-11-11 20:09:13 +00001722 struct mp3entry *track_id3;
1723
1724 if (track_widx == track_ridx)
1725 track_id3 = &curtrack_id3;
1726 else
1727 track_id3 = bufgetid3(tracks[track_widx].id3_hid);
1728
Nicolas Pennequinde026dc2008-04-16 16:18:05 +00001729 if (track_id3->length == 0 && track_id3->filesize == 0)
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001730 {
1731 logf("audio_finish_load_track: invalid metadata");
1732
1733 /* Invalid metadata */
1734 bufclose(tracks[track_widx].id3_hid);
1735 tracks[track_widx].id3_hid = -1;
1736
1737 /* Skip invalid entry from playlist. */
1738 playlist_skip_entry(NULL, last_peek_offset--);
1739
1740 /* load next track */
1741 LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER %d", (int)start_play);
1742 queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, start_play);
1743
1744 return;
1745 }
1746
Nicolas Pennequin95db8f82007-11-11 20:09:13 +00001747#ifdef HAVE_ALBUMART
Nicolas Pennequine71bc672007-11-11 21:27:18 +00001748 if (tracks[track_widx].aa_hid < 0 && gui_sync_wps_uses_albumart())
Nicolas Pennequin95db8f82007-11-11 20:09:13 +00001749 {
1750 char aa_path[MAX_PATH];
1751 if (find_albumart(track_id3, aa_path, sizeof(aa_path)))
1752 tracks[track_widx].aa_hid = bufopen(aa_path, 0, TYPE_BITMAP);
1753 }
1754#endif
1755
Steve Bavin3cc46e72006-09-01 07:59:31 +00001756 /* Load the codec. */
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001757 if (!audio_loadcodec(start_play))
Steve Bavin3cc46e72006-09-01 07:59:31 +00001758 {
Brandon Low0c974932007-11-03 21:55:24 +00001759 if (tracks[track_widx].codec_hid == ERR_BUFFER_FULL)
Steve Bavin3cc46e72006-09-01 07:59:31 +00001760 {
Brandon Low08a4cea2006-10-17 12:56:22 +00001761 /* No space for codec on buffer, not an error */
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001762 return;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001763 }
Brandon Low08a4cea2006-10-17 12:56:22 +00001764
1765 /* This is an error condition, either no codec was found, or reading
1766 * the codec file failed part way through, either way, skip the track */
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001767 snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", track_id3->path);
Brandon Low08a4cea2006-10-17 12:56:22 +00001768 /* We should not use gui_syncplash from audio thread! */
Jens Arnold4d6374c2007-03-16 21:56:08 +00001769 gui_syncsplash(HZ*2, msgbuf);
Brandon Low08a4cea2006-10-17 12:56:22 +00001770 /* Skip invalid entry from playlist. */
1771 playlist_skip_entry(NULL, last_peek_offset);
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001772 return;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001773 }
1774
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001775 track_id3->elapsed = 0;
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001776 offset = track_id3->offset;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001777
Brandon Low11a36612007-11-03 06:21:32 +00001778 enum data_type type = TYPE_PACKET_AUDIO;
1779
1780 switch (track_id3->codectype) {
1781 case AFMT_MPA_L1:
1782 case AFMT_MPA_L2:
1783 case AFMT_MPA_L3:
Steve Bavin135cc752008-03-28 12:51:33 +00001784 if (offset > 0) {
1785 file_offset = offset;
1786 track_id3->offset = offset;
Brandon Low11a36612007-11-03 06:21:32 +00001787 }
1788 break;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001789
Brandon Low11a36612007-11-03 06:21:32 +00001790 case AFMT_WAVPACK:
1791 if (offset > 0) {
Steve Bavin135cc752008-03-28 12:51:33 +00001792 file_offset = offset;
1793 track_id3->offset = offset;
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001794 track_id3->elapsed = track_id3->length / 2;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001795 }
Brandon Low11a36612007-11-03 06:21:32 +00001796 break;
1797
1798 case AFMT_OGG_VORBIS:
1799 case AFMT_SPEEX:
1800 case AFMT_FLAC:
1801 case AFMT_PCM_WAV:
1802 case AFMT_A52:
1803 case AFMT_AAC:
1804 case AFMT_MPC:
1805 case AFMT_APE:
Michael Giacomelli9a946f62008-04-12 19:52:31 +00001806 case AFMT_WMA:
Steve Bavin135cc752008-03-28 12:51:33 +00001807 if (offset > 0)
1808 track_id3->offset = offset;
Brandon Low11a36612007-11-03 06:21:32 +00001809 break;
1810
1811 case AFMT_NSF:
1812 case AFMT_SPC:
1813 case AFMT_SID:
1814 logf("Loading atomic %d",track_id3->codectype);
1815 type = TYPE_ATOMIC_AUDIO;
1816 break;
Steve Bavin3cc46e72006-09-01 07:59:31 +00001817 }
Nicolas Pennequin3e3c43c2007-10-25 21:27:45 +00001818
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001819 logf("alt:%s", track_id3->path);
Steve Bavin3cc46e72006-09-01 07:59:31 +00001820
Brandon Low1df1e7b2007-11-05 16:02:12 +00001821 if (file_offset > AUDIO_REBUFFER_GUESS_SIZE)
1822 file_offset -= AUDIO_REBUFFER_GUESS_SIZE;
1823 else if (track_id3->first_frame_offset)
Brandon Lowd02b5c72007-11-05 01:56:12 +00001824 file_offset = track_id3->first_frame_offset;
Brandon Low1df1e7b2007-11-05 16:02:12 +00001825 else
1826 file_offset = 0;
Brandon Lowd02b5c72007-11-05 01:56:12 +00001827
Nicolas Pennequin4e2de442008-04-14 16:17:47 +00001828