diff options
-rw-r--r-- | apps/SOURCES | 1 | ||||
-rw-r--r-- | apps/buffering.c | 1238 | ||||
-rw-r--r-- | apps/buffering.h | 120 | ||||
-rw-r--r-- | apps/debug_menu.c | 51 | ||||
-rw-r--r-- | apps/playback.c | 1291 | ||||
-rw-r--r-- | apps/playback.h | 4 | ||||
-rw-r--r-- | firmware/export/thread.h | 2 |
7 files changed, 1787 insertions, 920 deletions
diff --git a/apps/SOURCES b/apps/SOURCES index 3686fc29f7..dc5b987b61 100644 --- a/apps/SOURCES +++ b/apps/SOURCES | |||
@@ -14,6 +14,7 @@ menus/display_menu.c | |||
14 | menus/theme_menu.c | 14 | menus/theme_menu.c |
15 | #if CONFIG_CODEC == SWCODEC | 15 | #if CONFIG_CODEC == SWCODEC |
16 | menus/eq_menu.c | 16 | menus/eq_menu.c |
17 | buffering.c | ||
17 | #endif | 18 | #endif |
18 | menus/main_menu.c | 19 | menus/main_menu.c |
19 | menus/playback_menu.c | 20 | menus/playback_menu.c |
diff --git a/apps/buffering.c b/apps/buffering.c new file mode 100644 index 0000000000..7ebcbae39d --- /dev/null +++ b/apps/buffering.c | |||
@@ -0,0 +1,1238 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Nicolas Pennequin | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #include "config.h" | ||
21 | #include <stdio.h> | ||
22 | #include <string.h> | ||
23 | #include <stdlib.h> | ||
24 | #include <ctype.h> | ||
25 | #include "buffering.h" | ||
26 | |||
27 | #include "ata.h" | ||
28 | #include "system.h" | ||
29 | #include "thread.h" | ||
30 | #include "file.h" | ||
31 | #include "panic.h" | ||
32 | #include "memory.h" | ||
33 | #include "lcd.h" | ||
34 | #include "font.h" | ||
35 | #include "button.h" | ||
36 | #include "kernel.h" | ||
37 | #include "tree.h" | ||
38 | #include "debug.h" | ||
39 | #include "sprintf.h" | ||
40 | #include "settings.h" | ||
41 | #include "codecs.h" | ||
42 | #include "audio.h" | ||
43 | #include "mp3_playback.h" | ||
44 | #include "usb.h" | ||
45 | #include "status.h" | ||
46 | #include "screens.h" | ||
47 | #include "playlist.h" | ||
48 | #include "playback.h" | ||
49 | #include "pcmbuf.h" | ||
50 | #include "buffer.h" | ||
51 | |||
52 | #ifdef SIMULATOR | ||
53 | #define ata_disk_is_active() 1 | ||
54 | #endif | ||
55 | |||
56 | #if MEM > 1 | ||
57 | #define GUARD_BUFSIZE (32*1024) | ||
58 | #else | ||
59 | #define GUARD_BUFSIZE (8*1024) | ||
60 | #endif | ||
61 | |||
62 | /* Define LOGF_ENABLE to enable logf output in this file */ | ||
63 | /*#define LOGF_ENABLE*/ | ||
64 | #include "logf.h" | ||
65 | |||
66 | /* macros to enable logf for queues | ||
67 | logging on SYS_TIMEOUT can be disabled */ | ||
68 | #ifdef SIMULATOR | ||
69 | /* Define this for logf output of all queuing except SYS_TIMEOUT */ | ||
70 | #define BUFFERING_LOGQUEUES | ||
71 | /* Define this to logf SYS_TIMEOUT messages */ | ||
72 | /* #define BUFFERING_LOGQUEUES_SYS_TIMEOUT */ | ||
73 | #endif | ||
74 | |||
75 | #ifdef BUFFERING_LOGQUEUES | ||
76 | #define LOGFQUEUE logf | ||
77 | #else | ||
78 | #define LOGFQUEUE(...) | ||
79 | #endif | ||
80 | |||
81 | #ifdef BUFFERING_LOGQUEUES_SYS_TIMEOUT | ||
82 | #define LOGFQUEUE_SYS_TIMEOUT logf | ||
83 | #else | ||
84 | #define LOGFQUEUE_SYS_TIMEOUT(...) | ||
85 | #endif | ||
86 | |||
87 | /* default point to start buffer refill */ | ||
88 | #define BUFFERING_DEFAULT_WATERMARK (1024*512) | ||
89 | /* amount of data to read in one read() call */ | ||
90 | #define BUFFERING_DEFAULT_FILECHUNK (1024*32) | ||
91 | /* point at which the file buffer will fight for CPU time */ | ||
92 | #define BUFFERING_CRITICAL_LEVEL (1024*128) | ||
93 | |||
94 | |||
95 | /* Ring buffer helper macros */ | ||
96 | /* Buffer pointer (p) plus value (v), wrapped if necessary */ | ||
97 | #define RINGBUF_ADD(p,v) ((p+v)<buffer_len ? p+v : p+v-buffer_len) | ||
98 | /* Buffer pointer (p) minus value (v), wrapped if necessary */ | ||
99 | #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+buffer_len-v) | ||
100 | /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */ | ||
101 | #define RINGBUF_ADD_CROSS(p1,v,p2) \ | ||
102 | ((p1<p2) ? (int)(p1+v)-(int)p2 : (int)(p1+v-p2)-(int)buffer_len) | ||
103 | /* Bytes available in the buffer */ | ||
104 | #define BUF_USED RINGBUF_SUB(buf_widx, buf_ridx) | ||
105 | |||
106 | struct memory_handle { | ||
107 | int id; /* A unique ID for the handle */ | ||
108 | enum data_type type; | ||
109 | char path[MAX_PATH]; | ||
110 | int fd; | ||
111 | size_t data; /* Start index of the handle's data buffer */ | ||
112 | volatile size_t ridx; /* Current read pointer, relative to the main buffer */ | ||
113 | size_t widx; /* Current write pointer */ | ||
114 | size_t filesize; /* File total length */ | ||
115 | size_t filerem; /* Remaining bytes of file NOT in buffer */ | ||
116 | volatile size_t available; /* Available bytes to read from buffer */ | ||
117 | size_t offset; /* Offset at which we started reading the file */ | ||
118 | struct memory_handle *next; | ||
119 | }; | ||
120 | /* at all times, we have: filesize == offset + available + filerem */ | ||
121 | |||
122 | |||
123 | static char *buffer; | ||
124 | static char *guard_buffer; | ||
125 | |||
126 | static size_t buffer_len; | ||
127 | |||
128 | static volatile size_t buf_widx; /* current writing position */ | ||
129 | static volatile size_t buf_ridx; /* current reading position */ | ||
130 | /* buf_*idx are values relative to the buffer, not real pointers. */ | ||
131 | |||
132 | /* Configuration */ | ||
133 | static size_t conf_watermark = 0; /* Level to trigger filebuf fill */ | ||
134 | static size_t conf_filechunk = 0; /* Largest chunk the codec accepts */ | ||
135 | static size_t conf_preseek = 0; /* Codec pre-seek margin */ | ||
136 | #if MEM > 8 | ||
137 | static size_t high_watermark = 0; /* High watermark for rebuffer */ | ||
138 | #endif | ||
139 | |||
140 | /* current memory handle in the linked list. NULL when the list is empty. */ | ||
141 | static struct memory_handle *cur_handle; | ||
142 | /* first memory handle in the linked list. NULL when the list is empty. */ | ||
143 | static struct memory_handle *first_handle; | ||
144 | |||
145 | static int num_handles; /* number of handles in the list */ | ||
146 | |||
147 | static int base_handle_id; | ||
148 | |||
149 | static struct mutex llist_mutex; | ||
150 | |||
151 | /* Handle cache (makes find_handle faster). | ||
152 | This needs be to be global so that move_handle can invalidate it. */ | ||
153 | static struct memory_handle *cached_handle = NULL; | ||
154 | |||
155 | static buffer_low_callback buffer_low_callback_funcs[MAX_BUF_CALLBACKS]; | ||
156 | static int buffer_callback_count = 0; | ||
157 | |||
158 | static struct { | ||
159 | size_t remaining; /* Amount of data needing to be buffered */ | ||
160 | size_t wasted; /* Amount of space available for freeing */ | ||
161 | size_t buffered; /* Amount of data currently in the buffer */ | ||
162 | size_t useful; /* Amount of data still useful to the user */ | ||
163 | } data_counters; | ||
164 | |||
165 | |||
166 | /* Messages available to communicate with the buffering thread */ | ||
167 | enum { | ||
168 | Q_BUFFER_HANDLE = 1, /* Request buffering of a handle */ | ||
169 | Q_RESET_HANDLE, /* (internal) Request resetting of a handle to its | ||
170 | offset (the offset has to be set beforehand) */ | ||
171 | Q_CLOSE_HANDLE, /* Request closing a handle */ | ||
172 | Q_BASE_HANDLE, /* Set the reference handle for buf_useful_data */ | ||
173 | |||
174 | /* Configuration: */ | ||
175 | Q_SET_WATERMARK, | ||
176 | Q_SET_CHUNKSIZE, | ||
177 | Q_SET_PRESEEK, | ||
178 | }; | ||
179 | |||
180 | /* Buffering thread */ | ||
181 | void buffering_thread(void); | ||
182 | static long buffering_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]; | ||
183 | static const char buffering_thread_name[] = "buffering"; | ||
184 | static struct thread_entry *buffering_thread_p; | ||
185 | static struct event_queue buffering_queue; | ||
186 | static struct queue_sender_list buffering_queue_sender_list; | ||
187 | |||
188 | |||
189 | /* | ||
190 | LINKED LIST MANAGEMENT | ||
191 | ====================== | ||
192 | |||
193 | add_handle : Add a handle to the list | ||
194 | rm_handle : Remove a handle from the list | ||
195 | find_handle : Get a handle pointer from an ID | ||
196 | move_handle : Move a handle in the buffer (with or without its data) | ||
197 | |||
198 | These functions only handle the linked list structure. They don't touch the | ||
199 | contents of the struct memory_handle headers. They also change the buf_*idx | ||
200 | pointers when necessary and manage the handle IDs. | ||
201 | |||
202 | The first and current (== last) handle are kept track of. | ||
203 | A new handle is added at buf_widx and becomes the current one. | ||
204 | buf_widx always points to the current writing position for the current handle | ||
205 | buf_ridx always points to the location of the first handle. | ||
206 | buf_ridx == buf_widx means the buffer is empty. | ||
207 | */ | ||
208 | |||
209 | |||
210 | /* Add a new handle to the linked list and return it. It will have become the | ||
211 | new current handle. "data_size" must contain the size of what will be in the | ||
212 | handle. On return, it's the size available for the handle. */ | ||
213 | static struct memory_handle *add_handle(size_t *data_size) | ||
214 | { | ||
215 | mutex_lock(&llist_mutex); | ||
216 | |||
217 | /* this will give each handle a unique id */ | ||
218 | static int cur_handle_id = 1; | ||
219 | |||
220 | /* make sure buf_widx is 32-bit aligned so that the handle struct is, | ||
221 | but before that we check we can actually align. */ | ||
222 | if (RINGBUF_ADD_CROSS(buf_widx, 3, buf_ridx) >= 0) { | ||
223 | mutex_unlock(&llist_mutex); | ||
224 | return NULL; | ||
225 | } | ||
226 | buf_widx = (RINGBUF_ADD(buf_widx, 3)) & ~3; | ||
227 | |||
228 | size_t len = (data_size ? *data_size : 0) | ||
229 | + sizeof(struct memory_handle); | ||
230 | |||
231 | /* check that we actually can add the handle and its data */ | ||
232 | int overlap = RINGBUF_ADD_CROSS(buf_widx, len, buf_ridx); | ||
233 | if (overlap >= 0) { | ||
234 | *data_size -= overlap; | ||
235 | len -= overlap; | ||
236 | } | ||
237 | if (len < sizeof(struct memory_handle)) { | ||
238 | /* There isn't even enough space to write the struct */ | ||
239 | mutex_unlock(&llist_mutex); | ||
240 | return NULL; | ||
241 | } | ||
242 | |||
243 | struct memory_handle *new_handle = | ||
244 | (struct memory_handle *)(&buffer[buf_widx]); | ||
245 | |||
246 | /* only advance the buffer write index of the size of the struct */ | ||
247 | buf_widx = RINGBUF_ADD(buf_widx, sizeof(struct memory_handle)); | ||
248 | |||
249 | if (!first_handle) { | ||
250 | /* the new handle is the first one */ | ||
251 | first_handle = new_handle; | ||
252 | } | ||
253 | |||
254 | if (cur_handle) { | ||
255 | cur_handle->next = new_handle; | ||
256 | } | ||
257 | |||
258 | cur_handle = new_handle; | ||
259 | cur_handle->id = cur_handle_id++; | ||
260 | cur_handle->next = NULL; | ||
261 | num_handles++; | ||
262 | |||
263 | mutex_unlock(&llist_mutex); | ||
264 | return cur_handle; | ||
265 | } | ||
266 | |||
267 | /* Delete a given memory handle from the linked list | ||
268 | and return true for success. Nothing is actually erased from memory. */ | ||
269 | static bool rm_handle(struct memory_handle *h) | ||
270 | { | ||
271 | mutex_lock(&llist_mutex); | ||
272 | |||
273 | if (h == first_handle) { | ||
274 | first_handle = h->next; | ||
275 | if (h == cur_handle) { | ||
276 | /* h was the first and last handle: the buffer is now empty */ | ||
277 | cur_handle = NULL; | ||
278 | buf_ridx = buf_widx; | ||
279 | } else { | ||
280 | /* update buf_ridx to point to the new first handle */ | ||
281 | buf_ridx = (void *)first_handle - (void *)buffer; | ||
282 | } | ||
283 | } else { | ||
284 | struct memory_handle *m = first_handle; | ||
285 | while (m && m->next != h) { | ||
286 | m = m->next; | ||
287 | } | ||
288 | if (h && m && m->next == h) { | ||
289 | m->next = h->next; | ||
290 | if (h == cur_handle) { | ||
291 | cur_handle = m; | ||
292 | buf_widx = cur_handle->widx; | ||
293 | } | ||
294 | } else { | ||
295 | mutex_unlock(&llist_mutex); | ||
296 | return false; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | /* Invalidate the cache to prevent it from keeping the old location of h */ | ||
301 | if (h == cached_handle) | ||
302 | cached_handle = NULL; | ||
303 | |||
304 | num_handles--; | ||
305 | |||
306 | mutex_unlock(&llist_mutex); | ||
307 | return true; | ||
308 | } | ||
309 | |||
310 | /* Return a pointer to the memory handle of given ID. | ||
311 | NULL if the handle wasn't found */ | ||
312 | static struct memory_handle *find_handle(int handle_id) | ||
313 | { | ||
314 | if (handle_id <= 0) | ||
315 | return NULL; | ||
316 | |||
317 | mutex_lock(&llist_mutex); | ||
318 | |||
319 | /* simple caching because most of the time the requested handle | ||
320 | will either be the same as the last, or the one after the last */ | ||
321 | if (cached_handle) | ||
322 | { | ||
323 | if (cached_handle->id == handle_id) { | ||
324 | mutex_unlock(&llist_mutex); | ||
325 | return cached_handle; | ||
326 | } else if (cached_handle->next && | ||
327 | (cached_handle->next->id == handle_id)) { | ||
328 | cached_handle = cached_handle->next; | ||
329 | mutex_unlock(&llist_mutex); | ||
330 | return cached_handle; | ||
331 | } | ||
332 | } | ||
333 | |||
334 | struct memory_handle *m = first_handle; | ||
335 | while (m && m->id != handle_id) { | ||
336 | m = m->next; | ||
337 | } | ||
338 | /* This condition can only be reached with !m or m->id == handle_id */ | ||
339 | if (m) { | ||
340 | cached_handle = m; | ||
341 | } | ||
342 | |||
343 | mutex_unlock(&llist_mutex); | ||
344 | return m; | ||
345 | } | ||
346 | |||
347 | /* Move a memory handle and data_size of its data of delta. | ||
348 | Return a pointer to the new location of the handle. | ||
349 | delta is the value of which to move the struct data. | ||
350 | data_size is the amount of data to move along with the struct. */ | ||
351 | static struct memory_handle *move_handle(struct memory_handle *h, | ||
352 | size_t *delta, size_t data_size) | ||
353 | { | ||
354 | mutex_lock(&llist_mutex); | ||
355 | |||
356 | if (*delta < 4) { | ||
357 | /* aligning backwards would yield a negative result, | ||
358 | and moving the handle of such a small amount is a waste | ||
359 | of time anyway. */ | ||
360 | mutex_unlock(&llist_mutex); | ||
361 | return NULL; | ||
362 | } | ||
363 | /* make sure delta is 32-bit aligned so that the handle struct is. */ | ||
364 | *delta = (*delta - 3) & ~3; | ||
365 | |||
366 | size_t newpos = RINGBUF_ADD((void *)h - (void *)buffer, *delta); | ||
367 | |||
368 | struct memory_handle *dest = (struct memory_handle *)(&buffer[newpos]); | ||
369 | |||
370 | /* Invalidate the cache to prevent it from keeping the old location of h */ | ||
371 | if (h == cached_handle) | ||
372 | cached_handle = NULL; | ||
373 | |||
374 | /* the cur_handle pointer might need updating */ | ||
375 | if (h == cur_handle) { | ||
376 | cur_handle = dest; | ||
377 | } | ||
378 | |||
379 | if (h == first_handle) { | ||
380 | first_handle = dest; | ||
381 | buf_ridx = newpos; | ||
382 | } else { | ||
383 | struct memory_handle *m = first_handle; | ||
384 | while (m && m->next != h) { | ||
385 | m = m->next; | ||
386 | } | ||
387 | if (h && m && m->next == h) { | ||
388 | m->next = dest; | ||
389 | } else { | ||
390 | mutex_unlock(&llist_mutex); | ||
391 | return NULL; | ||
392 | } | ||
393 | } | ||
394 | |||
395 | memmove(dest, h, sizeof(struct memory_handle) + data_size); | ||
396 | |||
397 | mutex_unlock(&llist_mutex); | ||
398 | return dest; | ||
399 | } | ||
400 | |||
401 | |||
402 | /* | ||
403 | BUFFER SPACE MANAGEMENT | ||
404 | ======================= | ||
405 | |||
406 | yield_codec : Used by buffer_handle to know if it should interrupt buffering | ||
407 | buffer_handle : Buffer data for a handle | ||
408 | reset_handle : Reset writing position and data buffer of a handle to its | ||
409 | current offset | ||
410 | rebuffer_handle : Seek to a nonbuffered part of a handle by rebuffering the data | ||
411 | shrink_handle : Free buffer space by moving a handle | ||
412 | fill_buffer : Call buffer_handle for all handles that have data to buffer | ||
413 | can_add_handle : Indicate whether it's safe to add a handle | ||
414 | |||
415 | These functions are used by the buffering thread to manage buffer space. | ||
416 | */ | ||
417 | |||
418 | |||
419 | static inline bool filebuf_is_lowdata(void) | ||
420 | { | ||
421 | return BUF_USED < BUFFERING_CRITICAL_LEVEL; | ||
422 | } | ||
423 | |||
424 | /* Yield to the codec thread for as long as possible if it is in need of data. | ||
425 | Return true if the caller should break to let the buffering thread process | ||
426 | new queue events */ | ||
427 | static bool yield_codec(void) | ||
428 | { | ||
429 | yield(); | ||
430 | |||
431 | if (!queue_empty(&buffering_queue)) | ||
432 | return true; | ||
433 | |||
434 | while (pcmbuf_is_lowdata() && !filebuf_is_lowdata()) | ||
435 | { | ||
436 | sleep(2); | ||
437 | |||
438 | if (!queue_empty(&buffering_queue)) | ||
439 | return true; | ||
440 | } | ||
441 | |||
442 | return false; | ||
443 | } | ||
444 | |||
445 | /* Buffer data for the given handle. Return the amount of data buffered | ||
446 | or -1 if the handle wasn't found */ | ||
447 | static ssize_t buffer_handle(int handle_id) | ||
448 | { | ||
449 | logf("buffer_handle(%d)", handle_id); | ||
450 | struct memory_handle *h = find_handle(handle_id); | ||
451 | if (!h) | ||
452 | return -1; | ||
453 | |||
454 | if (h->filerem == 0) { | ||
455 | /* nothing left to buffer */ | ||
456 | return 0; | ||
457 | } | ||
458 | |||
459 | if (h->fd < 0) /* file closed, reopen */ | ||
460 | { | ||
461 | if (*h->path) | ||
462 | h->fd = open(h->path, O_RDONLY); | ||
463 | else | ||
464 | return -1; | ||
465 | |||
466 | if (h->fd < 0) | ||
467 | return -1; | ||
468 | |||
469 | if (h->offset) | ||
470 | lseek(h->fd, h->offset, SEEK_SET); | ||
471 | } | ||
472 | |||
473 | trigger_cpu_boost(); | ||
474 | |||
475 | ssize_t ret = 0; | ||
476 | while (h->filerem > 0) | ||
477 | { | ||
478 | /* max amount to copy */ | ||
479 | size_t copy_n = MIN( MIN(h->filerem, conf_filechunk), | ||
480 | buffer_len - h->widx); | ||
481 | |||
482 | /* stop copying if it would overwrite the reading position | ||
483 | or the next handle */ | ||
484 | if (RINGBUF_ADD_CROSS(h->widx, copy_n, buf_ridx) >= 0 || (h->next && | ||
485 | RINGBUF_ADD_CROSS(h->widx, copy_n, (unsigned) | ||
486 | ((void *)h->next - (void *)buffer)) > 0)) | ||
487 | break; | ||
488 | |||
489 | /* rc is the actual amount read */ | ||
490 | int rc = read(h->fd, &buffer[h->widx], copy_n); | ||
491 | |||
492 | if (rc < 0) | ||
493 | { | ||
494 | if (h->type == TYPE_CODEC) { | ||
495 | logf("Partial codec"); | ||
496 | break; | ||
497 | } | ||
498 | |||
499 | DEBUGF("File ended %ld bytes early\n", (long)h->filerem); | ||
500 | h->filesize -= h->filerem; | ||
501 | h->filerem = 0; | ||
502 | break; | ||
503 | } | ||
504 | |||
505 | /* Advance buffer */ | ||
506 | h->widx = RINGBUF_ADD(h->widx, rc); | ||
507 | if (h == cur_handle) | ||
508 | buf_widx = h->widx; | ||
509 | h->available += rc; | ||
510 | ret += rc; | ||
511 | h->filerem -= rc; | ||
512 | |||
513 | /* Stop buffering if new queue events have arrived */ | ||
514 | if (yield_codec()) | ||
515 | break; | ||
516 | } | ||
517 | |||
518 | if (h->filerem == 0) { | ||
519 | /* finished buffering the file */ | ||
520 | close(h->fd); | ||
521 | h->fd = -1; | ||
522 | } | ||
523 | |||
524 | return ret; | ||
525 | } | ||
526 | |||
527 | /* Reset writing position and data buffer of a handle to its current offset. | ||
528 | Use this after having set the new offset to use. */ | ||
529 | static void reset_handle(int handle_id) | ||
530 | { | ||
531 | logf("reset_handle(%d)", handle_id); | ||
532 | |||
533 | struct memory_handle *h = find_handle(handle_id); | ||
534 | if (!h) | ||
535 | return; | ||
536 | |||
537 | h->widx = h->data; | ||
538 | if (h == cur_handle) | ||
539 | buf_widx = h->widx; | ||
540 | h->available = 0; | ||
541 | h->filerem = h->filesize - h->offset; | ||
542 | |||
543 | if (h->fd >= 0) { | ||
544 | lseek(h->fd, h->offset, SEEK_SET); | ||
545 | } | ||
546 | } | ||
547 | |||
548 | /* Seek to a nonbuffered part of a handle by rebuffering the data. */ | ||
549 | static void rebuffer_handle(int handle_id, size_t newpos) | ||
550 | { | ||
551 | struct memory_handle *h = find_handle(handle_id); | ||
552 | if (!h) | ||
553 | return; | ||
554 | |||
555 | h->offset = newpos; | ||
556 | |||
557 | LOGFQUEUE("? >| buffering Q_RESET_HANDLE"); | ||
558 | queue_send(&buffering_queue, Q_RESET_HANDLE, handle_id); | ||
559 | |||
560 | LOGFQUEUE("? >| buffering Q_BUFFER_HANDLE"); | ||
561 | queue_send(&buffering_queue, Q_BUFFER_HANDLE, handle_id); | ||
562 | |||
563 | h->ridx = h->data; | ||
564 | } | ||
565 | |||
566 | static bool close_handle(int handle_id) | ||
567 | { | ||
568 | struct memory_handle *h = find_handle(handle_id); | ||
569 | if (!h) | ||
570 | return false; | ||
571 | |||
572 | if (h->fd >= 0) { | ||
573 | close(h->fd); | ||
574 | h->fd = -1; | ||
575 | } | ||
576 | |||
577 | rm_handle(h); | ||
578 | return true; | ||
579 | } | ||
580 | |||
581 | /* Free buffer space by moving the handle struct right before the useful | ||
582 | part of its data buffer or by moving all the data. */ | ||
583 | static void shrink_handle(int handle_id) | ||
584 | { | ||
585 | struct memory_handle *h = find_handle(handle_id); | ||
586 | if (!h) | ||
587 | return; | ||
588 | |||
589 | size_t delta; | ||
590 | /* The value of delta might change for alignment reasons */ | ||
591 | |||
592 | if (h->next && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET || | ||
593 | h->type == TYPE_IMAGE) && h->filerem == 0 ) | ||
594 | { | ||
595 | /* metadata handle: we can move all of it */ | ||
596 | delta = RINGBUF_SUB( (unsigned)((void *)h->next - (void *)buffer), | ||
597 | h->data) - h->available; | ||
598 | h = move_handle(h, &delta, h->available); | ||
599 | if (!h) return; | ||
600 | |||
601 | size_t olddata = h->data; | ||
602 | h->data = RINGBUF_ADD(h->data, delta); | ||
603 | h->ridx = RINGBUF_ADD(h->ridx, delta); | ||
604 | h->widx = RINGBUF_ADD(h->widx, delta); | ||
605 | |||
606 | /* when moving a struct mp3entry we need to readjust its pointers. */ | ||
607 | if (h->type == TYPE_ID3 && h->filesize == sizeof(struct mp3entry)) { | ||
608 | adjust_mp3entry((struct mp3entry *)&buffer[h->data], | ||
609 | (void *)&buffer[h->data], | ||
610 | (void *)&buffer[olddata]); | ||
611 | } | ||
612 | } | ||
613 | else | ||
614 | { | ||
615 | /* only move the handle struct */ | ||
616 | delta = RINGBUF_SUB(h->ridx, h->data); | ||
617 | h = move_handle(h, &delta, 0); | ||
618 | if (!h) return; | ||
619 | h->data = RINGBUF_ADD(h->data, delta); | ||
620 | h->available -= delta; | ||
621 | h->offset += delta; | ||
622 | } | ||
623 | } | ||
624 | |||
625 | /* Fill the buffer by buffering as much data as possible for handles that still | ||
626 | have data left to buffer */ | ||
627 | static void fill_buffer(void) | ||
628 | { | ||
629 | logf("fill_buffer()"); | ||
630 | struct memory_handle *m = first_handle; | ||
631 | while (queue_empty(&buffering_queue) && m) { | ||
632 | if (m->filerem > 0) { | ||
633 | buffer_handle(m->id); | ||
634 | } | ||
635 | m = m->next; | ||
636 | } | ||
637 | |||
638 | #ifndef SIMULATOR | ||
639 | if (queue_empty(&buffering_queue)) { | ||
640 | /* only spin the disk down if the filling wasn't interrupted by an | ||
641 | event arriving in the queue. */ | ||
642 | ata_sleep(); | ||
643 | } | ||
644 | #endif | ||
645 | } | ||
646 | |||
647 | /* Check whether it's safe to add a new handle and reserve space to let the | ||
648 | current one finish buffering its data. Used by bufopen and bufalloc as | ||
649 | a preliminary check before even trying to physically add the handle. | ||
650 | Returns true if it's ok to add a new handle, false if not. | ||
651 | */ | ||
652 | static bool can_add_handle(void) | ||
653 | { | ||
654 | if (cur_handle && cur_handle->filerem > 0) { | ||
655 | /* the current handle hasn't finished buffering. We can only add | ||
656 | a new one if there is already enough free space to finish | ||
657 | the buffering. */ | ||
658 | if (cur_handle->filerem < (buffer_len - BUF_USED)) { | ||
659 | /* Before adding the new handle we reserve some space for the | ||
660 | current one to finish buffering its data. */ | ||
661 | buf_widx = RINGBUF_ADD(buf_widx, cur_handle->filerem); | ||
662 | } else { | ||
663 | return false; | ||
664 | } | ||
665 | } | ||
666 | |||
667 | return true; | ||
668 | } | ||
669 | |||
670 | void update_data_counters(void) | ||
671 | { | ||
672 | struct memory_handle *m = find_handle(base_handle_id); | ||
673 | if (!m) | ||
674 | base_handle_id = 0; | ||
675 | |||
676 | memset(&data_counters, 0, sizeof(data_counters)); | ||
677 | |||
678 | m = first_handle; | ||
679 | while (m) { | ||
680 | data_counters.buffered += m->available; | ||
681 | data_counters.wasted += RINGBUF_SUB(m->ridx, m->data); | ||
682 | data_counters.remaining += m->filerem; | ||
683 | |||
684 | if (m->id >= base_handle_id) | ||
685 | data_counters.useful += RINGBUF_SUB(m->widx, m->ridx); | ||
686 | |||
687 | m = m->next; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | |||
692 | /* | ||
693 | MAIN BUFFERING API CALLS | ||
694 | ======================== | ||
695 | |||
696 | bufopen : Request the opening of a new handle for a file | ||
697 | bufalloc : Open a new handle for data other than a file. | ||
698 | bufclose : Close an open handle | ||
699 | bufseek : Set the read pointer in a handle | ||
700 | bufadvance : Move the read pointer in a handle | ||
701 | bufread : Copy data from a handle into a given buffer | ||
702 | bufgetdata : Give a pointer to the handle's data | ||
703 | |||
704 | These functions are exported, to allow interaction with the buffer. | ||
705 | They take care of the content of the structs, and rely on the linked list | ||
706 | management functions for all the actual handle management work. | ||
707 | */ | ||
708 | |||
709 | |||
710 | /* Reserve space in the buffer for a file. | ||
711 | filename: name of the file to open | ||
712 | offset: offset at which to start buffering the file, useful when the first | ||
713 | (offset-1) bytes of the file aren't needed. | ||
714 | return value: <0 if the file cannot be opened, or one file already | ||
715 | queued to be opened, otherwise the handle for the file in the buffer | ||
716 | */ | ||
717 | int bufopen(const char *file, size_t offset, enum data_type type) | ||
718 | { | ||
719 | if (!can_add_handle()) | ||
720 | return -2; | ||
721 | |||
722 | int fd = open(file, O_RDONLY); | ||
723 | if (fd < 0) | ||
724 | return -1; | ||
725 | |||
726 | size_t size = filesize(fd) - offset; | ||
727 | |||
728 | if (type != TYPE_AUDIO && | ||
729 | size + sizeof(struct memory_handle) > buffer_len - buf_widx) | ||
730 | { | ||
731 | /* for types other than audio, the data can't wrap, so we force it */ | ||
732 | buf_widx = 0; | ||
733 | } | ||
734 | |||
735 | struct memory_handle *h = add_handle(&size); | ||
736 | if (!h) | ||
737 | { | ||
738 | DEBUGF("bufopen: failed to add handle\n"); | ||
739 | close(fd); | ||
740 | return -2; | ||
741 | } | ||
742 | |||
743 | strncpy(h->path, file, MAX_PATH); | ||
744 | h->fd = -1; | ||
745 | h->filesize = filesize(fd); | ||
746 | h->filerem = h->filesize - offset; | ||
747 | h->offset = offset; | ||
748 | h->ridx = buf_widx; | ||
749 | h->widx = buf_widx; | ||
750 | h->data = buf_widx; | ||
751 | h->available = 0; | ||
752 | h->type = type; | ||
753 | |||
754 | close(fd); | ||
755 | |||
756 | if (type == TYPE_CODEC || type == TYPE_CUESHEET || type == TYPE_IMAGE) { | ||
757 | /* Immediately buffer those */ | ||
758 | LOGFQUEUE("? >| buffering Q_BUFFER_HANDLE"); | ||
759 | queue_send(&buffering_queue, Q_BUFFER_HANDLE, h->id); | ||
760 | } | ||
761 | |||
762 | logf("bufopen: new hdl %d", h->id); | ||
763 | return h->id; | ||
764 | } | ||
765 | |||
766 | /* Open a new handle from data that needs to be copied from memory. | ||
767 | src is the source buffer from which to copy data. It can be NULL to simply | ||
768 | reserve buffer space. | ||
769 | size is the requested size. The call will only be successful if the | ||
770 | requested amount of data can entirely fit in the buffer without wrapping. | ||
771 | Return value is the handle id for success or <0 for failure. | ||
772 | */ | ||
773 | int bufalloc(const void *src, size_t size, enum data_type type) | ||
774 | { | ||
775 | if (!can_add_handle()) | ||
776 | return -2; | ||
777 | |||
778 | if (buf_widx + size + sizeof(struct memory_handle) > buffer_len) { | ||
779 | /* The data would need to wrap. */ | ||
780 | DEBUGF("bufalloc: data wrap\n"); | ||
781 | return -2; | ||
782 | } | ||
783 | |||
784 | size_t allocsize = size; | ||
785 | struct memory_handle *h = add_handle(&allocsize); | ||
786 | |||
787 | if (!h || allocsize != size) | ||
788 | return -2; | ||
789 | |||
790 | if (src) { | ||
791 | if (type == TYPE_ID3 && size == sizeof(struct mp3entry)) { | ||
792 | /* specially take care of struct mp3entry */ | ||
793 | copy_mp3entry((struct mp3entry *)&buffer[buf_widx], | ||
794 | (struct mp3entry *)src); | ||
795 | } else { | ||
796 | memcpy(&buffer[buf_widx], src, size); | ||
797 | } | ||
798 | } | ||
799 | |||
800 | h->fd = -1; | ||
801 | *h->path = 0; | ||
802 | h->filesize = size; | ||
803 | h->filerem = 0; | ||
804 | h->offset = 0; | ||
805 | h->ridx = buf_widx; | ||
806 | h->widx = buf_widx + size; /* this is safe because the data doesn't wrap */ | ||
807 | h->data = buf_widx; | ||
808 | h->available = size; | ||
809 | h->type = type; | ||
810 | |||
811 | buf_widx += size; /* safe too */ | ||
812 | |||
813 | logf("bufalloc: new hdl %d", h->id); | ||
814 | return h->id; | ||
815 | } | ||
816 | |||
817 | /* Close the handle. Return true for success and false for failure */ | ||
818 | bool bufclose(int handle_id) | ||
819 | { | ||
820 | logf("bufclose(%d)", handle_id); | ||
821 | |||
822 | LOGFQUEUE("buffering >| Q_CLOSE_HANDLE"); | ||
823 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); | ||
824 | } | ||
825 | |||
826 | /* Set reading index in handle (relatively to the start of the file). | ||
827 | Access before the available data will trigger a rebuffer. | ||
828 | Return 0 for success and < 0 for failure: | ||
829 | -1 if the handle wasn't found | ||
830 | -2 if the new requested position was beyond the end of the file | ||
831 | */ | ||
832 | int bufseek(int handle_id, size_t newpos) | ||
833 | { | ||
834 | struct memory_handle *h = find_handle(handle_id); | ||
835 | if (!h) | ||
836 | return -1; | ||
837 | |||
838 | if (newpos > h->filesize) { | ||
839 | /* access beyond the end of the file */ | ||
840 | return -3; | ||
841 | } | ||
842 | else if (newpos < h->offset || h->offset + h->available < newpos) { | ||
843 | /* access before or after buffered data. A rebuffer is needed. */ | ||
844 | rebuffer_handle(handle_id, newpos); | ||
845 | } | ||
846 | else { | ||
847 | h->ridx = RINGBUF_ADD(h->data, newpos - h->offset); | ||
848 | } | ||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | /* Advance the reading index in a handle (relatively to its current position). | ||
853 | Return 0 for success and < 0 for failure */ | ||
854 | int bufadvance(int handle_id, off_t offset) | ||
855 | { | ||
856 | struct memory_handle *h = find_handle(handle_id); | ||
857 | if (!h) | ||
858 | return -1; | ||
859 | |||
860 | size_t newpos = h->offset + RINGBUF_SUB(h->ridx, h->data) + offset; | ||
861 | return bufseek(handle_id, newpos); | ||
862 | } | ||
863 | |||
864 | /* Copy data from the given handle to the dest buffer. | ||
865 | Return the number of bytes copied or < 0 for failure. */ | ||
866 | ssize_t bufread(int handle_id, size_t size, void *dest) | ||
867 | { | ||
868 | struct memory_handle *h = find_handle(handle_id); | ||
869 | if (!h) | ||
870 | return -1; | ||
871 | |||
872 | size_t ret; | ||
873 | size_t copy_n = RINGBUF_SUB(h->widx, h->ridx); | ||
874 | |||
875 | if (size == 0 && h->filerem > 0 && copy_n == 0) | ||
876 | /* Data isn't ready */ | ||
877 | return -2; | ||
878 | |||
879 | if (copy_n < size && h->filerem > 0) | ||
880 | /* Data isn't ready */ | ||
881 | return -2; | ||
882 | |||
883 | if (copy_n == 0 && h->filerem == 0) | ||
884 | /* File is finished reading */ | ||
885 | return 0; | ||
886 | |||
887 | ret = MIN(size, copy_n); | ||
888 | |||
889 | if (h->ridx + ret > buffer_len) | ||
890 | { | ||
891 | /* the data wraps around the end of the buffer */ | ||
892 | size_t read = buffer_len - h->ridx; | ||
893 | memcpy(dest, &buffer[h->ridx], read); | ||
894 | memcpy(dest+read, buffer, ret - read); | ||
895 | } | ||
896 | else | ||
897 | { | ||
898 | memcpy(dest, &buffer[h->ridx], ret); | ||
899 | } | ||
900 | |||
901 | return ret; | ||
902 | } | ||
903 | |||
904 | /* Update the "data" pointer to make the handle's data available to the caller. | ||
905 | Return the length of the available linear data or < 0 for failure. | ||
906 | size is the amount of linear data requested. it can be 0 to get as | ||
907 | much as possible. | ||
908 | The guard buffer may be used to provide the requested size */ | ||
909 | ssize_t bufgetdata(int handle_id, size_t size, void **data) | ||
910 | { | ||
911 | struct memory_handle *h = find_handle(handle_id); | ||
912 | if (!h) | ||
913 | return -1; | ||
914 | |||
915 | ssize_t ret; | ||
916 | size_t copy_n = RINGBUF_SUB(h->widx, h->ridx); | ||
917 | |||
918 | if (size == 0 && h->filerem > 0 && copy_n == 0) | ||
919 | /* Data isn't ready */ | ||
920 | return -2; | ||
921 | |||
922 | if (copy_n < size && h->filerem > 0) | ||
923 | /* Data isn't ready */ | ||
924 | return -2; | ||
925 | |||
926 | if (copy_n == 0 && h->filerem == 0) | ||
927 | /* File is finished reading */ | ||
928 | return 0; | ||
929 | |||
930 | if (h->ridx + size > buffer_len && copy_n >= size) | ||
931 | { | ||
932 | /* the data wraps around the end of the buffer : | ||
933 | use the guard buffer to provide the requested amount of data. */ | ||
934 | size_t copy_n = MIN(h->ridx + size - buffer_len, GUARD_BUFSIZE); | ||
935 | memcpy(guard_buffer, (unsigned char *)buffer, copy_n); | ||
936 | ret = buffer_len - h->ridx + copy_n; | ||
937 | } | ||
938 | else | ||
939 | { | ||
940 | ret = MIN(copy_n, buffer_len - h->ridx); | ||
941 | } | ||
942 | |||
943 | *data = &buffer[h->ridx]; | ||
944 | return ret; | ||
945 | } | ||
946 | |||
947 | /* | ||
948 | SECONDARY EXPORTED FUNCTIONS | ||
949 | ============================ | ||
950 | |||
951 | buf_get_offset | ||
952 | buf_handle_offset | ||
953 | buf_request_buffer_handle | ||
954 | buf_set_base_handle | ||
955 | buf_used | ||
956 | register_buffer_low_callback | ||
957 | unregister_buffer_low_callback | ||
958 | |||
959 | These functions are exported, to allow interaction with the buffer. | ||
960 | They take care of the content of the structs, and rely on the linked list | ||
961 | management functions for all the actual handle management work. | ||
962 | */ | ||
963 | |||
964 | /* Get a handle offset from a pointer */ | ||
965 | ssize_t buf_get_offset(int handle_id, void *ptr) | ||
966 | { | ||
967 | struct memory_handle *h = find_handle(handle_id); | ||
968 | if (!h) | ||
969 | return -1; | ||
970 | |||
971 | return (size_t)ptr - (size_t)&buffer[h->ridx]; | ||
972 | } | ||
973 | |||
974 | ssize_t buf_handle_offset(int handle_id) | ||
975 | { | ||
976 | struct memory_handle *h = find_handle(handle_id); | ||
977 | if (!h) | ||
978 | return -1; | ||
979 | return h->offset; | ||
980 | } | ||
981 | |||
982 | void buf_request_buffer_handle(int handle_id) | ||
983 | { | ||
984 | LOGFQUEUE("buffering >| buffering Q_BUFFER_HANDLE"); | ||
985 | queue_send(&buffering_queue, Q_BUFFER_HANDLE, handle_id); | ||
986 | } | ||
987 | |||
988 | void buf_set_base_handle(int handle_id) | ||
989 | { | ||
990 | LOGFQUEUE("buffering >| buffering Q_BUFFER_HANDLE"); | ||
991 | queue_post(&buffering_queue, Q_BASE_HANDLE, handle_id); | ||
992 | } | ||
993 | |||
994 | /* Return the amount of buffer space used */ | ||
995 | size_t buf_used(void) | ||
996 | { | ||
997 | return BUF_USED; | ||
998 | } | ||
999 | |||
1000 | void buf_set_conf(int setting, size_t value) | ||
1001 | { | ||
1002 | int msg; | ||
1003 | switch (setting) | ||
1004 | { | ||
1005 | case BUFFERING_SET_WATERMARK: | ||
1006 | msg = Q_SET_WATERMARK; | ||
1007 | break; | ||
1008 | |||
1009 | case BUFFERING_SET_CHUNKSIZE: | ||
1010 | msg = Q_SET_CHUNKSIZE; | ||
1011 | break; | ||
1012 | |||
1013 | case BUFFERING_SET_PRESEEK: | ||
1014 | msg = Q_SET_PRESEEK; | ||
1015 | break; | ||
1016 | |||
1017 | default: | ||
1018 | return; | ||
1019 | } | ||
1020 | queue_post(&buffering_queue, msg, value); | ||
1021 | } | ||
1022 | |||
1023 | bool register_buffer_low_callback(buffer_low_callback func) | ||
1024 | { | ||
1025 | int i; | ||
1026 | if (buffer_callback_count >= MAX_BUF_CALLBACKS) | ||
1027 | return false; | ||
1028 | for (i = 0; i < MAX_BUF_CALLBACKS; i++) | ||
1029 | { | ||
1030 | if (buffer_low_callback_funcs[i] == NULL) | ||
1031 | { | ||
1032 | buffer_low_callback_funcs[i] = func; | ||
1033 | buffer_callback_count++; | ||
1034 | return true; | ||
1035 | } | ||
1036 | else if (buffer_low_callback_funcs[i] == func) | ||
1037 | return true; | ||
1038 | } | ||
1039 | return false; | ||
1040 | } | ||
1041 | |||
1042 | void unregister_buffer_low_callback(buffer_low_callback func) | ||
1043 | { | ||
1044 | int i; | ||
1045 | for (i = 0; i < MAX_BUF_CALLBACKS; i++) | ||
1046 | { | ||
1047 | if (buffer_low_callback_funcs[i] == func) | ||
1048 | { | ||
1049 | buffer_low_callback_funcs[i] = NULL; | ||
1050 | buffer_callback_count--; | ||
1051 | } | ||
1052 | } | ||
1053 | return; | ||
1054 | } | ||
1055 | |||
1056 | static void call_buffer_low_callbacks(void) | ||
1057 | { | ||
1058 | int i; | ||
1059 | for (i = 0; i < MAX_BUF_CALLBACKS; i++) | ||
1060 | { | ||
1061 | if (buffer_low_callback_funcs[i]) | ||
1062 | { | ||
1063 | buffer_low_callback_funcs[i](); | ||
1064 | buffer_low_callback_funcs[i] = NULL; | ||
1065 | buffer_callback_count--; | ||
1066 | } | ||
1067 | } | ||
1068 | } | ||
1069 | |||
1070 | void buffering_thread(void) | ||
1071 | { | ||
1072 | struct queue_event ev; | ||
1073 | |||
1074 | while (true) | ||
1075 | { | ||
1076 | queue_wait_w_tmo(&buffering_queue, &ev, HZ/2); | ||
1077 | |||
1078 | switch (ev.id) | ||
1079 | { | ||
1080 | case Q_BUFFER_HANDLE: | ||
1081 | LOGFQUEUE("buffering < Q_BUFFER_HANDLE"); | ||
1082 | queue_reply(&buffering_queue, 1); | ||
1083 | buffer_handle((int)ev.data); | ||
1084 | break; | ||
1085 | |||
1086 | case Q_RESET_HANDLE: | ||
1087 | LOGFQUEUE("buffering < Q_RESET_HANDLE"); | ||
1088 | queue_reply(&buffering_queue, 1); | ||
1089 | reset_handle((int)ev.data); | ||
1090 | break; | ||
1091 | |||
1092 | case Q_CLOSE_HANDLE: | ||
1093 | LOGFQUEUE("buffering < Q_CLOSE_HANDLE"); | ||
1094 | queue_reply(&buffering_queue, close_handle((int)ev.data)); | ||
1095 | break; | ||
1096 | |||
1097 | case Q_BASE_HANDLE: | ||
1098 | LOGFQUEUE("buffering < Q_BASE_HANDLE"); | ||
1099 | base_handle_id = (int)ev.data; | ||
1100 | break; | ||
1101 | |||
1102 | case Q_SET_WATERMARK: | ||
1103 | LOGFQUEUE("buffering < Q_SET_WATERMARK"); | ||
1104 | conf_watermark = (size_t)ev.data; | ||
1105 | break; | ||
1106 | |||
1107 | case Q_SET_CHUNKSIZE: | ||
1108 | LOGFQUEUE("buffering < Q_SET_CHUNKSIZE"); | ||
1109 | conf_filechunk = (size_t)ev.data; | ||
1110 | break; | ||
1111 | |||
1112 | case Q_SET_PRESEEK: | ||
1113 | LOGFQUEUE("buffering < Q_SET_PRESEEK"); | ||
1114 | conf_preseek = (size_t)ev.data; | ||
1115 | break; | ||
1116 | |||
1117 | #ifndef SIMULATOR | ||
1118 | case SYS_USB_CONNECTED: | ||
1119 | LOGFQUEUE("buffering < SYS_USB_CONNECTED"); | ||
1120 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
1121 | usb_wait_for_disconnect(&buffering_queue); | ||
1122 | break; | ||
1123 | #endif | ||
1124 | |||
1125 | case SYS_TIMEOUT: | ||
1126 | LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT"); | ||
1127 | break; | ||
1128 | } | ||
1129 | |||
1130 | update_data_counters(); | ||
1131 | |||
1132 | /* If the buffer is low, call the callbacks to get new data */ | ||
1133 | if (num_handles > 0 && data_counters.useful < conf_watermark) | ||
1134 | { | ||
1135 | call_buffer_low_callbacks(); | ||
1136 | } | ||
1137 | |||
1138 | #if MEM > 8 | ||
1139 | /* If the disk is spinning, take advantage by filling the buffer */ | ||
1140 | if (ata_disk_is_active() && queue_empty(&buffering_queue) && | ||
1141 | data_counters.remaining > 0 && | ||
1142 | data_counters.buffered < high_watermark) | ||
1143 | { | ||
1144 | fill_buffer(); | ||
1145 | } | ||
1146 | |||
1147 | if (ata_disk_is_active() && queue_empty(&buffering_queue) && | ||
1148 | num_handles > 0 && data_counters.useful < high_watermark) | ||
1149 | { | ||
1150 | call_buffer_low_callbacks(); | ||
1151 | } | ||
1152 | #endif | ||
1153 | |||
1154 | if (ev.id == SYS_TIMEOUT && queue_empty(&buffering_queue)) | ||
1155 | { | ||
1156 | if (data_counters.remaining > 0 && | ||
1157 | data_counters.wasted > data_counters.buffered/2) | ||
1158 | { | ||
1159 | /* free buffer from outdated audio data */ | ||
1160 | struct memory_handle *m = first_handle; | ||
1161 | while (m) { | ||
1162 | if (m->type == TYPE_AUDIO) | ||
1163 | shrink_handle(m->id); | ||
1164 | m = m->next; | ||
1165 | } | ||
1166 | |||
1167 | /* free buffer by moving metadata */ | ||
1168 | m = first_handle; | ||
1169 | while (m) { | ||
1170 | if (m->type != TYPE_AUDIO) | ||
1171 | shrink_handle(m->id); | ||
1172 | m = m->next; | ||
1173 | } | ||
1174 | |||
1175 | update_data_counters(); | ||
1176 | } | ||
1177 | |||
1178 | if (data_counters.remaining > 0 && | ||
1179 | data_counters.buffered < conf_watermark) | ||
1180 | { | ||
1181 | fill_buffer(); | ||
1182 | } | ||
1183 | } | ||
1184 | } | ||
1185 | } | ||
1186 | |||
1187 | /* Initialise the buffering subsystem */ | ||
1188 | bool buffering_init(char *buf, size_t buflen) | ||
1189 | { | ||
1190 | if (!buf || !buflen) | ||
1191 | return false; | ||
1192 | |||
1193 | buffer = buf; | ||
1194 | buffer_len = buflen; | ||
1195 | guard_buffer = buf + buflen; | ||
1196 | |||
1197 | buf_widx = 0; | ||
1198 | buf_ridx = 0; | ||
1199 | |||
1200 | first_handle = NULL; | ||
1201 | num_handles = 0; | ||
1202 | |||
1203 | buffer_callback_count = 0; | ||
1204 | memset(buffer_low_callback_funcs, 0, sizeof(buffer_low_callback_funcs)); | ||
1205 | |||
1206 | mutex_init(&llist_mutex); | ||
1207 | |||
1208 | conf_filechunk = BUFFERING_DEFAULT_FILECHUNK; | ||
1209 | conf_watermark = BUFFERING_DEFAULT_WATERMARK; | ||
1210 | |||
1211 | /* Set the high watermark as 75% full...or 25% empty :) */ | ||
1212 | #if MEM > 8 | ||
1213 | high_watermark = 3*buflen / 4; | ||
1214 | #endif | ||
1215 | |||
1216 | if (buffering_thread_p == NULL) | ||
1217 | { | ||
1218 | buffering_thread_p = create_thread( buffering_thread, buffering_stack, | ||
1219 | sizeof(buffering_stack), 0, | ||
1220 | buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING) | ||
1221 | IF_COP(, CPU)); | ||
1222 | |||
1223 | queue_init(&buffering_queue, true); | ||
1224 | queue_enable_queue_send(&buffering_queue, &buffering_queue_sender_list); | ||
1225 | } | ||
1226 | |||
1227 | return true; | ||
1228 | } | ||
1229 | |||
1230 | void buffering_get_debugdata(struct buffering_debug *dbgdata) | ||
1231 | { | ||
1232 | update_data_counters(); | ||
1233 | dbgdata->num_handles = num_handles; | ||
1234 | dbgdata->data_rem = data_counters.remaining; | ||
1235 | dbgdata->wasted_space = data_counters.wasted; | ||
1236 | dbgdata->buffered_data = data_counters.buffered; | ||
1237 | dbgdata->useful_data = data_counters.useful; | ||
1238 | } | ||
diff --git a/apps/buffering.h b/apps/buffering.h new file mode 100644 index 0000000000..a5ad1e283b --- /dev/null +++ b/apps/buffering.h | |||
@@ -0,0 +1,120 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2007 Nicolas Pennequin | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | |||
20 | #ifndef _BUFFERING_H_ | ||
21 | #define _BUFFERING_H_ | ||
22 | |||
23 | #include <sys/types.h> | ||
24 | #include <stdbool.h> | ||
25 | |||
26 | |||
27 | enum data_type { | ||
28 | TYPE_CODEC, | ||
29 | TYPE_AUDIO, | ||
30 | TYPE_STREAM, | ||
31 | TYPE_ID3, | ||
32 | TYPE_CUESHEET, | ||
33 | TYPE_IMAGE, | ||
34 | TYPE_BUFFER, | ||
35 | TYPE_UNKNOWN, | ||
36 | }; | ||
37 | |||
38 | |||
39 | /* Initialise the buffering subsystem */ | ||
40 | bool buffering_init(char *buf, size_t buflen); | ||
41 | |||
42 | |||
43 | /*************************************************************************** | ||
44 | * MAIN BUFFERING API CALLS | ||
45 | * ======================== | ||
46 | * | ||
47 | * bufopen : Reserve space in the buffer for a given file | ||
48 | * bufalloc : Open a new handle from data that needs to be copied from memory | ||
49 | * bufclose : Close an open handle | ||
50 | * bufseek : Set handle reading index, relatively to the start of the file | ||
51 | * bufadvance: Move handle reading index, relatively to current position | ||
52 | * bufread : Copy data from a handle to a buffer | ||
53 | * bufgetdata: Obtain a pointer for linear access to a "size" amount of data | ||
54 | ****************************************************************************/ | ||
55 | |||
56 | int bufopen(const char *file, size_t offset, enum data_type type); | ||
57 | int bufalloc(const void *src, size_t size, enum data_type type); | ||
58 | bool bufclose(int handle_id); | ||
59 | int bufseek(int handle_id, size_t newpos); | ||
60 | int bufadvance(int handle_id, off_t offset); | ||
61 | ssize_t bufread(int handle_id, size_t size, void *dest); | ||
62 | ssize_t bufgetdata(int handle_id, size_t size, void **data); | ||
63 | |||
64 | |||
65 | /*************************************************************************** | ||
66 | * SECONDARY FUNCTIONS | ||
67 | * =================== | ||
68 | * | ||
69 | * buf_get_offset: Get a handle offset from a pointer | ||
70 | * buf_handle_offset: Get the offset of the first buffered byte from the file | ||
71 | * buf_request_buffer_handle: Request buffering of a handle | ||
72 | * buf_set_base_handle: Tell the buffering thread which handle is currently read | ||
73 | * buf_used: Total amount of buffer space used (including allocated space) | ||
74 | ****************************************************************************/ | ||
75 | |||
76 | ssize_t buf_get_offset(int handle_id, void *ptr); | ||
77 | ssize_t buf_handle_offset(int handle_id); | ||
78 | void buf_request_buffer_handle(int handle_id); | ||
79 | void buf_set_base_handle(int handle_id); | ||
80 | size_t buf_used(void); | ||
81 | |||
82 | |||
83 | /*************************************************************************** | ||
84 | * CALLBACK UTILITIES | ||
85 | * ================== | ||
86 | * | ||
87 | * register_buffer_low_callback, unregister_buffer_low_callback: | ||
88 | * | ||
89 | * Register/Unregister callback functions that will get executed when the buffer | ||
90 | * goes below the low watermark. They are executed once, then forgotten. | ||
91 | * | ||
92 | * NOTE: The callbacks are called from the buffering thread, so don't make them | ||
93 | * do too much. Ideally they should just post an event to a queue and return. | ||
94 | ****************************************************************************/ | ||
95 | |||
96 | #define MAX_BUF_CALLBACKS 4 | ||
97 | typedef void (*buffer_low_callback)(void); | ||
98 | bool register_buffer_low_callback(buffer_low_callback func); | ||
99 | void unregister_buffer_low_callback(buffer_low_callback func); | ||
100 | |||
101 | /* Settings */ | ||
102 | enum { | ||
103 | BUFFERING_SET_WATERMARK = 1, | ||
104 | BUFFERING_SET_CHUNKSIZE, | ||
105 | BUFFERING_SET_PRESEEK, | ||
106 | }; | ||
107 | void buf_set_conf(int setting, size_t value); | ||
108 | |||
109 | |||
110 | /* Debugging */ | ||
111 | struct buffering_debug { | ||
112 | int num_handles; | ||
113 | size_t buffered_data; | ||
114 | size_t wasted_space; | ||
115 | size_t data_rem; | ||
116 | size_t useful_data; | ||
117 | }; | ||
118 | void buffering_get_debugdata(struct buffering_debug *dbgdata); | ||
119 | |||
120 | #endif | ||
diff --git a/apps/debug_menu.c b/apps/debug_menu.c index de40226758..c9d962ece4 100644 --- a/apps/debug_menu.c +++ b/apps/debug_menu.c | |||
@@ -75,6 +75,7 @@ | |||
75 | #include "logfdisp.h" | 75 | #include "logfdisp.h" |
76 | #if CONFIG_CODEC == SWCODEC | 76 | #if CONFIG_CODEC == SWCODEC |
77 | #include "pcmbuf.h" | 77 | #include "pcmbuf.h" |
78 | #include "buffering.h" | ||
78 | #if defined(HAVE_SPDIF_OUT) || defined(HAVE_SPDIF_IN) | 79 | #if defined(HAVE_SPDIF_OUT) || defined(HAVE_SPDIF_IN) |
79 | #include "spdif.h" | 80 | #include "spdif.h" |
80 | #endif | 81 | #endif |
@@ -261,7 +262,7 @@ static void dbg_audio_task(void) | |||
261 | ticks++; | 262 | ticks++; |
262 | } | 263 | } |
263 | 264 | ||
264 | static bool dbg_audio_thread(void) | 265 | static bool dbg_buffering_thread(void) |
265 | { | 266 | { |
266 | char buf[32]; | 267 | char buf[32]; |
267 | int button; | 268 | int button; |
@@ -270,6 +271,7 @@ static bool dbg_audio_thread(void) | |||
270 | size_t bufused; | 271 | size_t bufused; |
271 | size_t bufsize = pcmbuf_get_bufsize(); | 272 | size_t bufsize = pcmbuf_get_bufsize(); |
272 | int pcmbufdescs = pcmbuf_descs(); | 273 | int pcmbufdescs = pcmbuf_descs(); |
274 | struct buffering_debug d; | ||
273 | 275 | ||
274 | ticks = boost_ticks = 0; | 276 | ticks = boost_ticks = 0; |
275 | 277 | ||
@@ -292,6 +294,9 @@ static bool dbg_audio_thread(void) | |||
292 | done = true; | 294 | done = true; |
293 | break; | 295 | break; |
294 | } | 296 | } |
297 | |||
298 | buffering_get_debugdata(&d); | ||
299 | |||
295 | line = 0; | 300 | line = 0; |
296 | lcd_clear_display(); | 301 | lcd_clear_display(); |
297 | 302 | ||
@@ -300,19 +305,45 @@ static bool dbg_audio_thread(void) | |||
300 | snprintf(buf, sizeof(buf), "pcm: %7ld/%7ld", (long) bufused, (long) bufsize); | 305 | snprintf(buf, sizeof(buf), "pcm: %7ld/%7ld", (long) bufused, (long) bufsize); |
301 | lcd_puts(0, line++, buf); | 306 | lcd_puts(0, line++, buf); |
302 | 307 | ||
303 | /* Playable space left */ | 308 | gui_scrollbar_draw(&screens[SCREEN_MAIN],0, line*8, LCD_WIDTH, 6, |
304 | gui_scrollbar_draw(&screens[SCREEN_MAIN],0, line*8, LCD_WIDTH, 6, bufsize, 0, bufused, HORIZONTAL); | 309 | bufsize, 0, bufused, HORIZONTAL); |
305 | line++; | 310 | line++; |
306 | 311 | ||
307 | snprintf(buf, sizeof(buf), "codec: %8ld/%8ld", audio_filebufused(), (long) filebuflen); | 312 | snprintf(buf, sizeof(buf), "alloc: %8ld/%8ld", audio_filebufused(), |
313 | (long) filebuflen); | ||
308 | lcd_puts(0, line++, buf); | 314 | lcd_puts(0, line++, buf); |
309 | 315 | ||
310 | /* Playable space left */ | 316 | #if LCD_HEIGHT > 80 |
311 | gui_scrollbar_draw(&screens[SCREEN_MAIN],0, line*8, LCD_WIDTH, 6, filebuflen, 0, | 317 | gui_scrollbar_draw(&screens[SCREEN_MAIN],0, line*8, LCD_WIDTH, 6, |
312 | audio_filebufused(), HORIZONTAL); | 318 | filebuflen, 0, audio_filebufused(), HORIZONTAL); |
319 | line++; | ||
320 | |||
321 | snprintf(buf, sizeof(buf), "real: %8ld/%8ld", (long)d.buffered_data, | ||
322 | (long)filebuflen); | ||
323 | lcd_puts(0, line++, buf); | ||
324 | |||
325 | gui_scrollbar_draw(&screens[SCREEN_MAIN],0, line*8, LCD_WIDTH, 6, | ||
326 | filebuflen, 0, (long)d.buffered_data, HORIZONTAL); | ||
327 | line++; | ||
328 | #endif | ||
329 | |||
330 | snprintf(buf, sizeof(buf), "usefl: %8ld/%8ld", (long)(d.useful_data), | ||
331 | (long)filebuflen); | ||
332 | lcd_puts(0, line++, buf); | ||
333 | |||
334 | #if LCD_HEIGHT > 80 | ||
335 | gui_scrollbar_draw(&screens[SCREEN_MAIN],0, line*8, LCD_WIDTH, 6, | ||
336 | filebuflen, 0, d.useful_data, HORIZONTAL); | ||
313 | line++; | 337 | line++; |
338 | #endif | ||
314 | 339 | ||
315 | snprintf(buf, sizeof(buf), "track count: %2d", audio_track_count()); | 340 | snprintf(buf, sizeof(buf), "data_rem: %ld", (long)d.data_rem); |
341 | lcd_puts(0, line++, buf); | ||
342 | |||
343 | snprintf(buf, sizeof(buf), "track count: %2d", audio_track_count()-1); | ||
344 | lcd_puts(0, line++, buf); | ||
345 | |||
346 | snprintf(buf, sizeof(buf), "handle count: %d", (int)d.num_handles); | ||
316 | lcd_puts(0, line++, buf); | 347 | lcd_puts(0, line++, buf); |
317 | 348 | ||
318 | #ifndef SIMULATOR | 349 | #ifndef SIMULATOR |
@@ -2241,7 +2272,9 @@ static const struct the_menu_item menuitems[] = { | |||
2241 | { "View database info", dbg_tagcache_info }, | 2272 | { "View database info", dbg_tagcache_info }, |
2242 | #endif | 2273 | #endif |
2243 | #ifdef HAVE_LCD_BITMAP | 2274 | #ifdef HAVE_LCD_BITMAP |
2244 | #if CONFIG_CODEC == SWCODEC || !defined(SIMULATOR) | 2275 | #if CONFIG_CODEC == SWCODEC |
2276 | { "View buffering thread", dbg_buffering_thread }, | ||
2277 | #elif !defined(SIMULATOR) | ||
2245 | { "View audio thread", dbg_audio_thread }, | 2278 | { "View audio thread", dbg_audio_thread }, |
2246 | #endif | 2279 | #endif |
2247 | #ifdef PM_DEBUG | 2280 | #ifdef PM_DEBUG |
diff --git a/apps/playback.c b/apps/playback.c index 03bbb9ddd2..0cda680c0b 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include "settings.h" | 46 | #include "settings.h" |
47 | #include "codecs.h" | 47 | #include "codecs.h" |
48 | #include "audio.h" | 48 | #include "audio.h" |
49 | #include "buffering.h" | ||
49 | #include "mp3_playback.h" | 50 | #include "mp3_playback.h" |
50 | #include "usb.h" | 51 | #include "usb.h" |
51 | #include "status.h" | 52 | #include "status.h" |
@@ -91,8 +92,6 @@ | |||
91 | #define AUDIO_DEFAULT_WATERMARK (1024*512) | 92 | #define AUDIO_DEFAULT_WATERMARK (1024*512) |
92 | /* amount of data to read in one read() call */ | 93 | /* amount of data to read in one read() call */ |
93 | #define AUDIO_DEFAULT_FILECHUNK (1024*32) | 94 | #define AUDIO_DEFAULT_FILECHUNK (1024*32) |
94 | /* point at which the file buffer will fight for CPU time */ | ||
95 | #define AUDIO_FILEBUF_CRITICAL (1024*128) | ||
96 | /* amount of guess-space to allow for codecs that must hunt and peck | 95 | /* amount of guess-space to allow for codecs that must hunt and peck |
97 | * for their correct seeek target, 32k seems a good size */ | 96 | * for their correct seeek target, 32k seems a good size */ |
98 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) | 97 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) |
@@ -135,16 +134,12 @@ enum { | |||
135 | Q_AUDIO_SKIP, | 134 | Q_AUDIO_SKIP, |
136 | Q_AUDIO_PRE_FF_REWIND, | 135 | Q_AUDIO_PRE_FF_REWIND, |
137 | Q_AUDIO_FF_REWIND, | 136 | Q_AUDIO_FF_REWIND, |
138 | Q_AUDIO_REBUFFER_SEEK, | ||
139 | Q_AUDIO_CHECK_NEW_TRACK, | 137 | Q_AUDIO_CHECK_NEW_TRACK, |
140 | Q_AUDIO_FLUSH, | 138 | Q_AUDIO_FLUSH, |
141 | Q_AUDIO_TRACK_CHANGED, | 139 | Q_AUDIO_TRACK_CHANGED, |
142 | Q_AUDIO_DIR_SKIP, | 140 | Q_AUDIO_DIR_SKIP, |
143 | Q_AUDIO_POSTINIT, | 141 | Q_AUDIO_POSTINIT, |
144 | Q_AUDIO_FILL_BUFFER, | 142 | Q_AUDIO_FILL_BUFFER, |
145 | #if MEM > 8 | ||
146 | Q_AUDIO_FILL_BUFFER_IF_ACTIVE_ATA, | ||
147 | #endif | ||
148 | Q_CODEC_REQUEST_COMPLETE, | 143 | Q_CODEC_REQUEST_COMPLETE, |
149 | Q_CODEC_REQUEST_FAILED, | 144 | Q_CODEC_REQUEST_FAILED, |
150 | 145 | ||
@@ -196,7 +191,6 @@ bool audio_is_initialized = false; | |||
196 | static volatile bool audio_codec_loaded NOCACHEBSS_ATTR = false; /* Codec loaded? (C/A-) */ | 191 | static volatile bool audio_codec_loaded NOCACHEBSS_ATTR = false; /* Codec loaded? (C/A-) */ |
197 | static volatile bool playing NOCACHEBSS_ATTR = false; /* Is audio playing? (A) */ | 192 | static volatile bool playing NOCACHEBSS_ATTR = false; /* Is audio playing? (A) */ |
198 | static volatile bool paused NOCACHEBSS_ATTR = false; /* Is audio paused? (A/C-) */ | 193 | static volatile bool paused NOCACHEBSS_ATTR = false; /* Is audio paused? (A/C-) */ |
199 | static volatile bool filling IDATA_ATTR = false; /* Is file buffer refilling? (A/C-) */ | ||
200 | 194 | ||
201 | /* Ring buffer where compressed audio and codecs are loaded */ | 195 | /* Ring buffer where compressed audio and codecs are loaded */ |
202 | static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */ | 196 | static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */ |
@@ -204,8 +198,6 @@ static unsigned char *malloc_buf = NULL; /* Start of malloc buffer (A/C-) */ | |||
204 | /* FIXME: make filebuflen static */ | 198 | /* FIXME: make filebuflen static */ |
205 | size_t filebuflen = 0; /* Size of buffer (A/C-) */ | 199 | size_t filebuflen = 0; /* Size of buffer (A/C-) */ |
206 | /* FIXME: make buf_ridx (C/A-) */ | 200 | /* FIXME: make buf_ridx (C/A-) */ |
207 | static volatile size_t buf_ridx IDATA_ATTR = 0; /* Buffer read position (A/C)*/ | ||
208 | static volatile size_t buf_widx IDATA_ATTR = 0; /* Buffer write position (A/C-) */ | ||
209 | 201 | ||
210 | /* Possible arrangements of the buffer */ | 202 | /* Possible arrangements of the buffer */ |
211 | #define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */ | 203 | #define BUFFER_STATE_TRASHED -1 /* trashed; must be reset */ |
@@ -213,29 +205,18 @@ static volatile size_t buf_widx IDATA_ATTR = 0; /* Buffer write position (A/C-) | |||
213 | #define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */ | 205 | #define BUFFER_STATE_VOICED_ONLY 1 /* voice-only */ |
214 | static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */ | 206 | static int buffer_state = BUFFER_STATE_TRASHED; /* Buffer state */ |
215 | 207 | ||
216 | /* Compressed ring buffer helper macros */ | 208 | static struct mp3entry prevtrack_id3; |
217 | /* Buffer pointer (p) plus value (v), wrapped if necessary */ | 209 | static struct mp3entry curtrack_id3; |
218 | #define RINGBUF_ADD(p,v) ((p+v)<filebuflen ? p+v : p+v-filebuflen) | 210 | static struct mp3entry nexttrack_id3; |
219 | /* Buffer pointer (p) minus value (v), wrapped if necessary */ | ||
220 | #define RINGBUF_SUB(p,v) ((p>=v) ? p-v : p+filebuflen-v) | ||
221 | /* How far value (v) plus buffer pointer (p1) will cross buffer pointer (p2) */ | ||
222 | #define RINGBUF_ADD_CROSS(p1,v,p2) \ | ||
223 | ((p1<p2)?(int)(p1+v)-(int)p2:(int)(p1+v-p2)-(int)filebuflen) | ||
224 | /* Bytes available in the buffer */ | ||
225 | #define FILEBUFUSED RINGBUF_SUB(buf_widx, buf_ridx) | ||
226 | 211 | ||
227 | /* Track info structure about songs in the file buffer (A/C-) */ | 212 | /* Track info structure about songs in the file buffer (A/C-) */ |
228 | struct track_info { | 213 | struct track_info { |
229 | struct mp3entry id3; /* TAG metadata */ | 214 | int audio_hid; /* The ID for the track's buffer handle */ |
230 | char *codecbuf; /* Pointer to codec buffer */ | 215 | int id3_hid; /* The ID for the track's metadata handle */ |
231 | size_t codecsize; /* Codec length in bytes */ | 216 | int codec_hid; /* The ID for the track's codec handle */ |
232 | bool has_codec; /* Does this track have a codec on the buffer */ | ||
233 | 217 | ||
234 | size_t buf_idx; /* Pointer to the track's buffer */ | 218 | size_t codecsize; /* Codec length in bytes */ |
235 | size_t filerem; /* Remaining bytes of file NOT in buffer */ | ||
236 | size_t filesize; /* File total length */ | 219 | size_t filesize; /* File total length */ |
237 | size_t start_pos; /* Position to first bytes of file in buffer */ | ||
238 | volatile size_t available; /* Available bytes to read from buffer */ | ||
239 | 220 | ||
240 | bool taginfo_ready; /* Is metadata read */ | 221 | bool taginfo_ready; /* Is metadata read */ |
241 | 222 | ||
@@ -246,7 +227,6 @@ static struct track_info tracks[MAX_TRACK]; | |||
246 | static volatile int track_ridx = 0; /* Track being decoded (A/C-) */ | 227 | static volatile int track_ridx = 0; /* Track being decoded (A/C-) */ |
247 | static int track_widx = 0; /* Track being buffered (A) */ | 228 | static int track_widx = 0; /* Track being buffered (A) */ |
248 | 229 | ||
249 | static struct track_info *prev_ti = NULL; /* Previous track info pointer (A/C-) */ | ||
250 | #define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */ | 230 | #define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */ |
251 | 231 | ||
252 | /* Set by the audio thread when the current track information has updated | 232 | /* Set by the audio thread when the current track information has updated |
@@ -256,8 +236,6 @@ static bool track_changed = false; | |||
256 | /* Information used only for filling the buffer */ | 236 | /* Information used only for filling the buffer */ |
257 | /* Playlist steps from playing track to next track to be buffered (A) */ | 237 | /* Playlist steps from playing track to next track to be buffered (A) */ |
258 | static int last_peek_offset = 0; | 238 | static int last_peek_offset = 0; |
259 | /* Partially loaded track file handle to continue buffering (A) */ | ||
260 | static int current_fd = -1; | ||
261 | 239 | ||
262 | /* Scrobbler support */ | 240 | /* Scrobbler support */ |
263 | static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/ | 241 | static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/ |
@@ -278,19 +256,12 @@ void (*track_buffer_callback)(struct mp3entry *id3, bool last_track) = NULL; | |||
278 | /* When a track's buffer has been overwritten or cleared */ | 256 | /* When a track's buffer has been overwritten or cleared */ |
279 | void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track) = NULL; | 257 | void (*track_unbuffer_callback)(struct mp3entry *id3, bool last_track) = NULL; |
280 | 258 | ||
281 | /* Configuration */ | ||
282 | static size_t conf_watermark = 0; /* Level to trigger filebuf fill (A/C) FIXME */ | ||
283 | static size_t conf_filechunk = 0; /* Largest chunk the codec accepts (A/C) FIXME */ | ||
284 | static size_t conf_preseek = 0; /* Codec pre-seek margin (A/C) FIXME */ | ||
285 | static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */ | 259 | static size_t buffer_margin = 0; /* Buffer margin aka anti-skip buffer (A/C-) */ |
286 | #if MEM > 8 | ||
287 | static size_t high_watermark = 0; /* High watermark for rebuffer (A/V/other) */ | ||
288 | #endif | ||
289 | 260 | ||
290 | /* Multiple threads */ | 261 | /* Multiple threads */ |
291 | static void set_current_codec(int codec_idx); | 262 | static void set_current_codec(int codec_idx); |
292 | /* Set the watermark to trigger buffer fill (A/C) FIXME */ | 263 | /* Set the watermark to trigger buffer fill (A/C) FIXME */ |
293 | static void set_filebuf_watermark(int seconds); | 264 | static void set_filebuf_watermark(int seconds, size_t max); |
294 | 265 | ||
295 | /* Audio thread */ | 266 | /* Audio thread */ |
296 | static struct event_queue audio_queue NOCACHEBSS_ATTR; | 267 | static struct event_queue audio_queue NOCACHEBSS_ATTR; |
@@ -306,6 +277,7 @@ static void audio_reset_buffer(void); | |||
306 | /* Codec thread */ | 277 | /* Codec thread */ |
307 | extern struct codec_api ci; | 278 | extern struct codec_api ci; |
308 | static struct event_queue codec_queue NOCACHEBSS_ATTR; | 279 | static struct event_queue codec_queue NOCACHEBSS_ATTR; |
280 | static struct queue_sender_list codec_queue_sender_list; | ||
309 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] | 281 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] |
310 | IBSS_ATTR; | 282 | IBSS_ATTR; |
311 | static const char codec_thread_name[] = "codec"; | 283 | static const char codec_thread_name[] = "codec"; |
@@ -372,6 +344,73 @@ static void voice_stop(void); | |||
372 | 344 | ||
373 | #endif /* PLAYBACK_VOICE */ | 345 | #endif /* PLAYBACK_VOICE */ |
374 | 346 | ||
347 | |||
348 | /* --- Helper functions --- */ | ||
349 | |||
350 | struct mp3entry *bufgetid3(int handle_id) | ||
351 | { | ||
352 | if (handle_id < 0) | ||
353 | return NULL; | ||
354 | |||
355 | struct mp3entry *id3; | ||
356 | ssize_t ret = bufgetdata(handle_id, 0, (void *)&id3); | ||
357 | |||
358 | if (ret < 0 || ret != sizeof(struct mp3entry)) | ||
359 | return NULL; | ||
360 | |||
361 | return id3; | ||
362 | } | ||
363 | |||
364 | void *bufgetcodec(struct track_info *track) | ||
365 | { | ||
366 | void *ptr; | ||
367 | ssize_t ret = bufgetdata(track->codec_hid, track->codecsize, &ptr); | ||
368 | |||
369 | if (ret == -2) { | ||
370 | buf_request_buffer_handle(CUR_TI->audio_hid); | ||
371 | } | ||
372 | |||
373 | while (ret == -2) { | ||
374 | sleep(1); | ||
375 | ret = bufgetdata(track->codec_hid, track->codecsize, &ptr); | ||
376 | } | ||
377 | |||
378 | if (ret < 0) | ||
379 | return NULL; | ||
380 | else | ||
381 | return ptr; | ||
382 | } | ||
383 | |||
384 | bool clear_track_info(struct track_info *track) | ||
385 | { | ||
386 | if (!track) | ||
387 | return false; | ||
388 | |||
389 | if (track->codec_hid > 0) { | ||
390 | if (bufclose(track->codec_hid)) | ||
391 | track->codec_hid = 0; | ||
392 | else | ||
393 | return false; | ||
394 | } | ||
395 | |||
396 | if (track->id3_hid > 0) { | ||
397 | if (bufclose(track->id3_hid)) | ||
398 | track->id3_hid = 0; | ||
399 | else | ||
400 | return false; | ||
401 | } | ||
402 | |||
403 | if (track->audio_hid > 0) { | ||
404 | if (bufclose(track->audio_hid)) | ||
405 | track->audio_hid = 0; | ||
406 | else | ||
407 | return false; | ||
408 | } | ||
409 | |||
410 | memset(track, 0, sizeof(struct track_info)); | ||
411 | return true; | ||
412 | } | ||
413 | |||
375 | /* --- External interfaces --- */ | 414 | /* --- External interfaces --- */ |
376 | 415 | ||
377 | void mp3_play_data(const unsigned char* start, int size, | 416 | void mp3_play_data(const unsigned char* start, int size, |
@@ -620,8 +659,12 @@ struct mp3entry* audio_current_track(void) | |||
620 | cur_idx = track_ridx + offset; | 659 | cur_idx = track_ridx + offset; |
621 | cur_idx &= MAX_TRACK_MASK; | 660 | cur_idx &= MAX_TRACK_MASK; |
622 | 661 | ||
623 | if (tracks[cur_idx].taginfo_ready) | 662 | if (cur_idx == track_ridx && *curtrack_id3.path) |
624 | return &tracks[cur_idx].id3; | 663 | return &curtrack_id3; |
664 | else if (offset == -1 && *prevtrack_id3.path) | ||
665 | return &prevtrack_id3; | ||
666 | else if (tracks[cur_idx].id3_hid > 0) | ||
667 | return bufgetid3(tracks[cur_idx].id3_hid); | ||
625 | 668 | ||
626 | memset(&temp_id3, 0, sizeof(struct mp3entry)); | 669 | memset(&temp_id3, 0, sizeof(struct mp3entry)); |
627 | 670 | ||
@@ -653,13 +696,16 @@ struct mp3entry* audio_next_track(void) | |||
653 | if (!audio_have_tracks()) | 696 | if (!audio_have_tracks()) |
654 | return NULL; | 697 | return NULL; |
655 | 698 | ||
699 | if (wps_offset == -1 && *prevtrack_id3.path) | ||
700 | return &curtrack_id3; | ||
701 | |||
656 | next_idx++; | 702 | next_idx++; |
657 | next_idx &= MAX_TRACK_MASK; | 703 | next_idx &= MAX_TRACK_MASK; |
658 | 704 | ||
659 | if (!tracks[next_idx].taginfo_ready) | 705 | if (tracks[next_idx].id3_hid <= 0) |
660 | return NULL; | 706 | return NULL; |
661 | 707 | ||
662 | return &tracks[next_idx].id3; | 708 | return &nexttrack_id3; |
663 | } | 709 | } |
664 | 710 | ||
665 | bool audio_has_changed_track(void) | 711 | bool audio_has_changed_track(void) |
@@ -818,8 +864,8 @@ void audio_set_buffer_margin(int setting) | |||
818 | { | 864 | { |
819 | static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; | 865 | static const int lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; |
820 | buffer_margin = lookup[setting]; | 866 | buffer_margin = lookup[setting]; |
821 | logf("buffer margin: %ld", buffer_margin); | 867 | logf("buffer margin: %d", buffer_margin); |
822 | set_filebuf_watermark(buffer_margin); | 868 | set_filebuf_watermark(buffer_margin, 0); |
823 | } | 869 | } |
824 | #endif | 870 | #endif |
825 | 871 | ||
@@ -851,7 +897,7 @@ void audio_set_crossfade(int enable) | |||
851 | if (was_playing) | 897 | if (was_playing) |
852 | { | 898 | { |
853 | /* Store the track resume position */ | 899 | /* Store the track resume position */ |
854 | offset = CUR_TI->id3.offset; | 900 | offset = curtrack_id3.offset; |
855 | gui_syncsplash(0, str(LANG_RESTARTING_PLAYBACK)); | 901 | gui_syncsplash(0, str(LANG_RESTARTING_PLAYBACK)); |
856 | } | 902 | } |
857 | 903 | ||
@@ -997,16 +1043,16 @@ void voice_wait(void) | |||
997 | #endif | 1043 | #endif |
998 | } | 1044 | } |
999 | 1045 | ||
1000 | static void set_filebuf_watermark(int seconds) | 1046 | static void set_filebuf_watermark(int seconds, size_t max) |
1001 | { | 1047 | { |
1002 | size_t bytes; | 1048 | size_t bytes; |
1003 | 1049 | ||
1004 | if (!filebuf) | 1050 | if (!filebuf) |
1005 | return; /* Audio buffers not yet set up */ | 1051 | return; /* Audio buffers not yet set up */ |
1006 | 1052 | ||
1007 | bytes = MAX(CUR_TI->id3.bitrate * seconds * (1000/8), conf_watermark); | 1053 | bytes = MAX(curtrack_id3.bitrate * seconds * (1000/8), max); |
1008 | bytes = MIN(bytes, filebuflen / 2); | 1054 | bytes = MIN(bytes, filebuflen / 2); |
1009 | conf_watermark = bytes; | 1055 | buf_set_conf(BUFFERING_SET_WATERMARK, bytes); |
1010 | } | 1056 | } |
1011 | 1057 | ||
1012 | const char * get_codec_filename(int cod_spec) | 1058 | const char * get_codec_filename(int cod_spec) |
@@ -1422,15 +1468,15 @@ static void codec_pcmbuf_position_callback(size_t size) | |||
1422 | { | 1468 | { |
1423 | /* This is called from an ISR, so be quick */ | 1469 | /* This is called from an ISR, so be quick */ |
1424 | unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY + | 1470 | unsigned int time = size * 1000 / 4 / NATIVE_FREQUENCY + |
1425 | prev_ti->id3.elapsed; | 1471 | prevtrack_id3.elapsed; |
1426 | 1472 | ||
1427 | if (time >= prev_ti->id3.length) | 1473 | if (time >= prevtrack_id3.length) |
1428 | { | 1474 | { |
1429 | pcmbuf_set_position_callback(NULL); | 1475 | pcmbuf_set_position_callback(NULL); |
1430 | prev_ti->id3.elapsed = prev_ti->id3.length; | 1476 | prevtrack_id3.elapsed = prevtrack_id3.length; |
1431 | } | 1477 | } |
1432 | else | 1478 | else |
1433 | prev_ti->id3.elapsed = time; | 1479 | prevtrack_id3.elapsed = time; |
1434 | } | 1480 | } |
1435 | 1481 | ||
1436 | static void codec_set_elapsed_callback(unsigned int value) | 1482 | static void codec_set_elapsed_callback(unsigned int value) |
@@ -1445,11 +1491,11 @@ static void codec_set_elapsed_callback(unsigned int value) | |||
1445 | 1491 | ||
1446 | latency = pcmbuf_get_latency(); | 1492 | latency = pcmbuf_get_latency(); |
1447 | if (value < latency) | 1493 | if (value < latency) |
1448 | CUR_TI->id3.elapsed = 0; | 1494 | curtrack_id3.elapsed = 0; |
1449 | else if (value - latency > CUR_TI->id3.elapsed || | 1495 | else if (value - latency > curtrack_id3.elapsed || |
1450 | value - latency < CUR_TI->id3.elapsed - 2) | 1496 | value - latency < curtrack_id3.elapsed - 2) |
1451 | { | 1497 | { |
1452 | CUR_TI->id3.elapsed = value - latency; | 1498 | curtrack_id3.elapsed = value - latency; |
1453 | } | 1499 | } |
1454 | } | 1500 | } |
1455 | 1501 | ||
@@ -1460,68 +1506,48 @@ static void codec_set_offset_callback(size_t value) | |||
1460 | if (ci.seek_time) | 1506 | if (ci.seek_time) |
1461 | return; | 1507 | return; |
1462 | 1508 | ||
1463 | latency = pcmbuf_get_latency() * CUR_TI->id3.bitrate / 8; | 1509 | latency = pcmbuf_get_latency() * curtrack_id3.bitrate / 8; |
1464 | if (value < latency) | 1510 | if (value < latency) |
1465 | CUR_TI->id3.offset = 0; | 1511 | curtrack_id3.offset = 0; |
1466 | else | 1512 | else |
1467 | CUR_TI->id3.offset = value - latency; | 1513 | curtrack_id3.offset = value - latency; |
1468 | } | 1514 | } |
1469 | 1515 | ||
1470 | static void codec_advance_buffer_counters(size_t amount) | 1516 | static void codec_advance_buffer_counters(size_t amount) |
1471 | { | 1517 | { |
1472 | buf_ridx = RINGBUF_ADD(buf_ridx, amount); | 1518 | bufadvance(CUR_TI->audio_hid, amount); |
1473 | ci.curpos += amount; | 1519 | ci.curpos += amount; |
1474 | CUR_TI->available -= amount; | ||
1475 | |||
1476 | /* Start buffer filling as necessary. */ | ||
1477 | if (!pcmbuf_is_lowdata() && !filling) | ||
1478 | { | ||
1479 | if (FILEBUFUSED < conf_watermark && playing && !playlist_end) | ||
1480 | { | ||
1481 | LOGFQUEUE("codec > audio Q_AUDIO_FILL_BUFFER"); | ||
1482 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | ||
1483 | } | ||
1484 | } | ||
1485 | } | 1520 | } |
1486 | 1521 | ||
1487 | /* copy up-to size bytes into ptr and return the actual size copied */ | 1522 | /* copy up-to size bytes into ptr and return the actual size copied */ |
1488 | static size_t codec_filebuf_callback(void *ptr, size_t size) | 1523 | static size_t codec_filebuf_callback(void *ptr, size_t size) |
1489 | { | 1524 | { |
1490 | char *buf = (char *)ptr; | 1525 | ssize_t copy_n; |
1491 | size_t copy_n; | ||
1492 | size_t part_n; | ||
1493 | 1526 | ||
1494 | if (ci.stop_codec || !playing) | 1527 | if (ci.stop_codec || !playing) |
1495 | return 0; | 1528 | return 0; |
1496 | 1529 | ||
1497 | /* The ammount to copy is the lesser of the requested amount and the | 1530 | copy_n = bufread(CUR_TI->audio_hid, size, ptr); |
1498 | * amount left of the current track (both on disk and already loaded) */ | ||
1499 | copy_n = MIN(size, CUR_TI->available + CUR_TI->filerem); | ||
1500 | 1531 | ||
1501 | /* Nothing requested OR nothing left */ | 1532 | /* Nothing requested OR nothing left */ |
1502 | if (copy_n == 0) | 1533 | if (copy_n == 0) |
1503 | return 0; | 1534 | return 0; |
1504 | 1535 | ||
1536 | |||
1537 | if (copy_n == -2) | ||
1538 | { | ||
1539 | buf_request_buffer_handle(CUR_TI->audio_hid); | ||
1540 | } | ||
1541 | |||
1505 | /* Let the disk buffer catch fill until enough data is available */ | 1542 | /* Let the disk buffer catch fill until enough data is available */ |
1506 | while (copy_n > CUR_TI->available) | 1543 | while (copy_n == -2) |
1507 | { | 1544 | { |
1508 | if (!filling) | ||
1509 | { | ||
1510 | LOGFQUEUE("codec > audio Q_AUDIO_FILL_BUFFER"); | ||
1511 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | ||
1512 | } | ||
1513 | |||
1514 | sleep(1); | 1545 | sleep(1); |
1546 | |||
1515 | if (ci.stop_codec || ci.new_track) | 1547 | if (ci.stop_codec || ci.new_track) |
1516 | return 0; | 1548 | return 0; |
1517 | } | ||
1518 | 1549 | ||
1519 | /* Copy as much as possible without wrapping */ | 1550 | copy_n = bufread(CUR_TI->audio_hid, size, ptr); |
1520 | part_n = MIN(copy_n, filebuflen - buf_ridx); | ||
1521 | memcpy(buf, &filebuf[buf_ridx], part_n); | ||
1522 | /* Copy the rest in the case of a wrap */ | ||
1523 | if (part_n < copy_n) { | ||
1524 | memcpy(&buf[part_n], &filebuf[0], copy_n - part_n); | ||
1525 | } | 1551 | } |
1526 | 1552 | ||
1527 | /* Update read and other position pointers */ | 1553 | /* Update read and other position pointers */ |
@@ -1533,7 +1559,9 @@ static size_t codec_filebuf_callback(void *ptr, size_t size) | |||
1533 | 1559 | ||
1534 | static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) | 1560 | static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) |
1535 | { | 1561 | { |
1536 | size_t short_n, copy_n, buf_rem; | 1562 | size_t copy_n = reqsize; |
1563 | ssize_t ret; | ||
1564 | void *ptr; | ||
1537 | 1565 | ||
1538 | if (!playing) | 1566 | if (!playing) |
1539 | { | 1567 | { |
@@ -1541,48 +1569,38 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) | |||
1541 | return NULL; | 1569 | return NULL; |
1542 | } | 1570 | } |
1543 | 1571 | ||
1544 | copy_n = MIN(reqsize, CUR_TI->available + CUR_TI->filerem); | 1572 | ret = bufgetdata(CUR_TI->audio_hid, reqsize, &ptr); |
1545 | if (copy_n == 0) | 1573 | if (ret >= 0) |
1574 | copy_n = MIN((size_t)ret, reqsize); | ||
1575 | |||
1576 | if (copy_n == 0) | ||
1546 | { | 1577 | { |
1547 | *realsize = 0; | 1578 | *realsize = 0; |
1548 | return NULL; | 1579 | return NULL; |
1549 | } | 1580 | } |
1550 | 1581 | ||
1551 | while (copy_n > CUR_TI->available) | 1582 | if (ret == -2) |
1583 | { | ||
1584 | buf_request_buffer_handle(CUR_TI->audio_hid); | ||
1585 | } | ||
1586 | |||
1587 | /* Let the disk buffer catch fill until enough data is available */ | ||
1588 | while (ret == -2) | ||
1552 | { | 1589 | { |
1553 | if (!filling) | ||
1554 | { | ||
1555 | LOGFQUEUE("codec > audio Q_AUDIO_FILL_BUFFER"); | ||
1556 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | ||
1557 | } | ||
1558 | |||
1559 | sleep(1); | 1590 | sleep(1); |
1591 | |||
1560 | if (ci.stop_codec || ci.new_track) | 1592 | if (ci.stop_codec || ci.new_track) |
1561 | { | 1593 | { |
1562 | *realsize = 0; | 1594 | *realsize = 0; |
1563 | return NULL; | 1595 | return NULL; |
1564 | } | 1596 | } |
1597 | ret = bufgetdata(CUR_TI->audio_hid, reqsize, &ptr); | ||
1565 | } | 1598 | } |
1566 | 1599 | copy_n = MIN((size_t)ret, reqsize); | |
1567 | /* How much is left at the end of the file buffer before wrap? */ | ||
1568 | buf_rem = filebuflen - buf_ridx; | ||
1569 | |||
1570 | /* If we can't satisfy the request without wrapping */ | ||
1571 | if (buf_rem < copy_n) | ||
1572 | { | ||
1573 | /* How short are we? */ | ||
1574 | short_n = copy_n - buf_rem; | ||
1575 | |||
1576 | /* If we can fudge it with the guardbuf */ | ||
1577 | if (short_n < GUARD_BUFSIZE) | ||
1578 | memcpy(&filebuf[filebuflen], &filebuf[0], short_n); | ||
1579 | else | ||
1580 | copy_n = buf_rem; | ||
1581 | } | ||
1582 | 1600 | ||
1583 | *realsize = copy_n; | 1601 | *realsize = copy_n; |
1584 | 1602 | ||
1585 | return (char *)&filebuf[buf_ridx]; | 1603 | return ptr; |
1586 | } /* codec_request_buffer_callback */ | 1604 | } /* codec_request_buffer_callback */ |
1587 | 1605 | ||
1588 | static int get_codec_base_type(int type) | 1606 | static int get_codec_base_type(int type) |
@@ -1599,50 +1617,13 @@ static int get_codec_base_type(int type) | |||
1599 | 1617 | ||
1600 | static void codec_advance_buffer_callback(size_t amount) | 1618 | static void codec_advance_buffer_callback(size_t amount) |
1601 | { | 1619 | { |
1602 | if (amount > CUR_TI->available + CUR_TI->filerem) | ||
1603 | amount = CUR_TI->available + CUR_TI->filerem; | ||
1604 | |||
1605 | while (amount > CUR_TI->available && filling) | ||
1606 | sleep(1); | ||
1607 | |||
1608 | if (amount > CUR_TI->available) | ||
1609 | { | ||
1610 | intptr_t result = Q_CODEC_REQUEST_FAILED; | ||
1611 | |||
1612 | if (!ci.stop_codec) | ||
1613 | { | ||
1614 | LOGFQUEUE("codec >| audio Q_AUDIO_REBUFFER_SEEK"); | ||
1615 | result = queue_send(&audio_queue, Q_AUDIO_REBUFFER_SEEK, | ||
1616 | ci.curpos + amount); | ||
1617 | } | ||
1618 | |||
1619 | switch (result) | ||
1620 | { | ||
1621 | case Q_CODEC_REQUEST_FAILED: | ||
1622 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED"); | ||
1623 | ci.stop_codec = true; | ||
1624 | return; | ||
1625 | |||
1626 | case Q_CODEC_REQUEST_COMPLETE: | ||
1627 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE"); | ||
1628 | return; | ||
1629 | |||
1630 | default: | ||
1631 | LOGFQUEUE("codec |< default"); | ||
1632 | ci.stop_codec = true; | ||
1633 | return; | ||
1634 | } | ||
1635 | } | ||
1636 | |||
1637 | codec_advance_buffer_counters(amount); | 1620 | codec_advance_buffer_counters(amount); |
1638 | |||
1639 | codec_set_offset_callback(ci.curpos); | 1621 | codec_set_offset_callback(ci.curpos); |
1640 | } | 1622 | } |
1641 | 1623 | ||
1642 | static void codec_advance_buffer_loc_callback(void *ptr) | 1624 | static void codec_advance_buffer_loc_callback(void *ptr) |
1643 | { | 1625 | { |
1644 | size_t amount = (size_t)ptr - (size_t)&filebuf[buf_ridx]; | 1626 | size_t amount = buf_get_offset(CUR_TI->audio_hid, ptr); |
1645 | |||
1646 | codec_advance_buffer_callback(amount); | 1627 | codec_advance_buffer_callback(amount); |
1647 | } | 1628 | } |
1648 | 1629 | ||
@@ -1705,7 +1686,7 @@ static off_t codec_mp3_get_filepos_callback(int newtime) | |||
1705 | { | 1686 | { |
1706 | off_t newpos; | 1687 | off_t newpos; |
1707 | 1688 | ||
1708 | CUR_TI->id3.elapsed = newtime; | 1689 | curtrack_id3.elapsed = newtime; |
1709 | newpos = codec_get_file_pos(); | 1690 | newpos = codec_get_file_pos(); |
1710 | 1691 | ||
1711 | return newpos; | 1692 | return newpos; |
@@ -1729,79 +1710,31 @@ static void codec_seek_complete_callback(void) | |||
1729 | 1710 | ||
1730 | static bool codec_seek_buffer_callback(size_t newpos) | 1711 | static bool codec_seek_buffer_callback(size_t newpos) |
1731 | { | 1712 | { |
1732 | int difference; | ||
1733 | |||
1734 | logf("codec_seek_buffer_callback"); | 1713 | logf("codec_seek_buffer_callback"); |
1735 | 1714 | ||
1736 | if (newpos >= CUR_TI->filesize) | 1715 | int ret = bufseek(CUR_TI->audio_hid, newpos); |
1737 | newpos = CUR_TI->filesize - 1; | 1716 | if (ret == 0) { |
1738 | 1717 | ci.curpos = newpos; | |
1739 | difference = newpos - ci.curpos; | ||
1740 | if (difference >= 0) | ||
1741 | { | ||
1742 | /* Seeking forward */ | ||
1743 | logf("seek: +%d", difference); | ||
1744 | codec_advance_buffer_callback(difference); | ||
1745 | return true; | 1718 | return true; |
1746 | } | 1719 | } |
1747 | 1720 | else { | |
1748 | /* Seeking backward */ | 1721 | return false; |
1749 | difference = -difference; | ||
1750 | if (ci.curpos - difference < 0) | ||
1751 | difference = ci.curpos; | ||
1752 | |||
1753 | /* We need to reload the song. */ | ||
1754 | if (newpos < CUR_TI->start_pos) | ||
1755 | { | ||
1756 | intptr_t result = Q_CODEC_REQUEST_FAILED; | ||
1757 | |||
1758 | if (!ci.stop_codec) | ||
1759 | { | ||
1760 | LOGFQUEUE("codec >| audio Q_AUDIO_REBUFFER_SEEK"); | ||
1761 | result = queue_send(&audio_queue, Q_AUDIO_REBUFFER_SEEK, | ||
1762 | newpos); | ||
1763 | } | ||
1764 | |||
1765 | switch (result) | ||
1766 | { | ||
1767 | case Q_CODEC_REQUEST_COMPLETE: | ||
1768 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_COMPLETE"); | ||
1769 | return true; | ||
1770 | |||
1771 | case Q_CODEC_REQUEST_FAILED: | ||
1772 | LOGFQUEUE("codec |< Q_CODEC_REQUEST_FAILED"); | ||
1773 | ci.stop_codec = true; | ||
1774 | return false; | ||
1775 | |||
1776 | default: | ||
1777 | LOGFQUEUE("codec |< default"); | ||
1778 | return false; | ||
1779 | } | ||
1780 | } | 1722 | } |
1781 | |||
1782 | /* Seeking inside buffer space. */ | ||
1783 | logf("seek: -%d", difference); | ||
1784 | CUR_TI->available += difference; | ||
1785 | buf_ridx = RINGBUF_SUB(buf_ridx, (unsigned)difference); | ||
1786 | ci.curpos -= difference; | ||
1787 | |||
1788 | return true; | ||
1789 | } | 1723 | } |
1790 | 1724 | ||
1791 | static void codec_configure_callback(int setting, intptr_t value) | 1725 | static void codec_configure_callback(int setting, intptr_t value) |
1792 | { | 1726 | { |
1793 | switch (setting) { | 1727 | switch (setting) { |
1794 | case CODEC_SET_FILEBUF_WATERMARK: | 1728 | case CODEC_SET_FILEBUF_WATERMARK: |
1795 | conf_watermark = value; | 1729 | set_filebuf_watermark(buffer_margin, value); |
1796 | set_filebuf_watermark(buffer_margin); | ||
1797 | break; | 1730 | break; |
1798 | 1731 | ||
1799 | case CODEC_SET_FILEBUF_CHUNKSIZE: | 1732 | case CODEC_SET_FILEBUF_CHUNKSIZE: |
1800 | conf_filechunk = value; | 1733 | buf_set_conf(BUFFERING_SET_CHUNKSIZE, value); |
1801 | break; | 1734 | break; |
1802 | 1735 | ||
1803 | case CODEC_SET_FILEBUF_PRESEEK: | 1736 | case CODEC_SET_FILEBUF_PRESEEK: |
1804 | conf_preseek = value; | 1737 | buf_set_conf(BUFFERING_SET_PRESEEK, value); |
1805 | break; | 1738 | break; |
1806 | 1739 | ||
1807 | default: | 1740 | default: |
@@ -1811,7 +1744,6 @@ static void codec_configure_callback(int setting, intptr_t value) | |||
1811 | 1744 | ||
1812 | static void codec_track_changed(void) | 1745 | static void codec_track_changed(void) |
1813 | { | 1746 | { |
1814 | automatic_skip = false; | ||
1815 | LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED"); | 1747 | LOGFQUEUE("codec > audio Q_AUDIO_TRACK_CHANGED"); |
1816 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); | 1748 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); |
1817 | } | 1749 | } |
@@ -1824,28 +1756,12 @@ static void codec_pcmbuf_track_changed_callback(void) | |||
1824 | 1756 | ||
1825 | static void codec_discard_codec_callback(void) | 1757 | static void codec_discard_codec_callback(void) |
1826 | { | 1758 | { |
1827 | if (CUR_TI->has_codec) | 1759 | if (CUR_TI->codec_hid > 0) |
1828 | { | 1760 | { |
1829 | CUR_TI->has_codec = false; | 1761 | bufclose(CUR_TI->codec_hid); |
1830 | buf_ridx = RINGBUF_ADD(buf_ridx, CUR_TI->codecsize); | 1762 | CUR_TI->codec_hid = 0; |
1763 | CUR_TI->codecsize = 0; | ||
1831 | } | 1764 | } |
1832 | |||
1833 | #if 0 | ||
1834 | /* Check if a buffer desync has happened, log it and stop playback. */ | ||
1835 | if (buf_ridx != CUR_TI->buf_idx) | ||
1836 | { | ||
1837 | int offset = CUR_TI->buf_idx - buf_ridx; | ||
1838 | size_t new_used = FILEBUFUSED - offset; | ||
1839 | |||
1840 | logf("Buf off :%d=%d-%d", offset, CUR_TI->buf_idx, buf_ridx); | ||
1841 | logf("Used off:%d",FILEBUFUSED - new_used); | ||
1842 | |||
1843 | /* This is a fatal internal error and it's not safe to | ||
1844 | * continue playback. */ | ||
1845 | ci.stop_codec = true; | ||
1846 | queue_post(&audio_queue, Q_AUDIO_STOP, 0); | ||
1847 | } | ||
1848 | #endif | ||
1849 | } | 1765 | } |
1850 | 1766 | ||
1851 | static inline void codec_gapless_track_change(void) { | 1767 | static inline void codec_gapless_track_change(void) { |
@@ -1899,7 +1815,7 @@ static bool codec_load_next_track(void) | |||
1899 | { | 1815 | { |
1900 | intptr_t result = Q_CODEC_REQUEST_FAILED; | 1816 | intptr_t result = Q_CODEC_REQUEST_FAILED; |
1901 | 1817 | ||
1902 | prev_track_elapsed = CUR_TI->id3.elapsed; | 1818 | prev_track_elapsed = curtrack_id3.elapsed; |
1903 | 1819 | ||
1904 | if (ci.seek_time) | 1820 | if (ci.seek_time) |
1905 | codec_seek_complete_callback(); | 1821 | codec_seek_complete_callback(); |
@@ -1950,13 +1866,18 @@ static bool codec_request_next_track_callback(void) | |||
1950 | if (ci.stop_codec || !playing) | 1866 | if (ci.stop_codec || !playing) |
1951 | return false; | 1867 | return false; |
1952 | 1868 | ||
1953 | prev_codectype = get_codec_base_type(CUR_TI->id3.codectype); | 1869 | prev_codectype = get_codec_base_type(curtrack_id3.codectype); |
1954 | 1870 | ||
1955 | if (!codec_load_next_track()) | 1871 | if (!codec_load_next_track()) |
1956 | return false; | 1872 | return false; |
1957 | 1873 | ||
1874 | /* Seek to the beginning of the new track because if the struct mp3entry was | ||
1875 | buffered, "elapsed" might not be zero (if the track has been played | ||
1876 | already but not unbuffered) */ | ||
1877 | codec_seek_buffer_callback(0); | ||
1878 | |||
1958 | /* Check if the next codec is the same file. */ | 1879 | /* Check if the next codec is the same file. */ |
1959 | if (prev_codectype == get_codec_base_type(CUR_TI->id3.codectype)) | 1880 | if (prev_codectype == get_codec_base_type(curtrack_id3.codectype)) |
1960 | { | 1881 | { |
1961 | logf("New track loaded"); | 1882 | logf("New track loaded"); |
1962 | codec_discard_codec_callback(); | 1883 | codec_discard_codec_callback(); |
@@ -1964,7 +1885,7 @@ static bool codec_request_next_track_callback(void) | |||
1964 | } | 1885 | } |
1965 | else | 1886 | else |
1966 | { | 1887 | { |
1967 | logf("New codec:%d/%d", CUR_TI->id3.codectype, prev_codectype); | 1888 | logf("New codec:%d/%d", curtrack_id3.codectype, prev_codectype); |
1968 | return false; | 1889 | return false; |
1969 | } | 1890 | } |
1970 | } | 1891 | } |
@@ -1982,6 +1903,7 @@ static void codec_thread(void) | |||
1982 | switch (ev.id) { | 1903 | switch (ev.id) { |
1983 | case Q_CODEC_LOAD_DISK: | 1904 | case Q_CODEC_LOAD_DISK: |
1984 | LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); | 1905 | LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); |
1906 | queue_reply(&codec_queue, 1); | ||
1985 | audio_codec_loaded = true; | 1907 | audio_codec_loaded = true; |
1986 | #ifdef PLAYBACK_VOICE | 1908 | #ifdef PLAYBACK_VOICE |
1987 | /* Don't sent messages to voice codec if it's already swapped | 1909 | /* Don't sent messages to voice codec if it's already swapped |
@@ -2004,7 +1926,7 @@ static void codec_thread(void) | |||
2004 | 1926 | ||
2005 | case Q_CODEC_LOAD: | 1927 | case Q_CODEC_LOAD: |
2006 | LOGFQUEUE("codec < Q_CODEC_LOAD"); | 1928 | LOGFQUEUE("codec < Q_CODEC_LOAD"); |
2007 | if (!CUR_TI->has_codec) { | 1929 | if (CUR_TI->codec_hid <= 0) { |
2008 | logf("Codec slot is empty!"); | 1930 | logf("Codec slot is empty!"); |
2009 | /* Wait for the pcm buffer to go empty */ | 1931 | /* Wait for the pcm buffer to go empty */ |
2010 | while (pcm_is_playing()) | 1932 | while (pcm_is_playing()) |
@@ -2028,8 +1950,8 @@ static void codec_thread(void) | |||
2028 | #endif | 1950 | #endif |
2029 | set_current_codec(CODEC_IDX_AUDIO); | 1951 | set_current_codec(CODEC_IDX_AUDIO); |
2030 | ci.stop_codec = false; | 1952 | ci.stop_codec = false; |
2031 | wrap = (size_t)&filebuf[filebuflen] - (size_t)CUR_TI->codecbuf; | 1953 | wrap = (size_t)&filebuf[filebuflen] - (size_t)bufgetcodec(CUR_TI); |
2032 | status = codec_load_ram(CUR_TI->codecbuf, CUR_TI->codecsize, | 1954 | status = codec_load_ram(bufgetcodec(CUR_TI), CUR_TI->codecsize, |
2033 | &filebuf[0], wrap, &ci); | 1955 | &filebuf[0], wrap, &ci); |
2034 | #ifdef PLAYBACK_VOICE | 1956 | #ifdef PLAYBACK_VOICE |
2035 | semaphore_release(&sem_codecthread); | 1957 | semaphore_release(&sem_codecthread); |
@@ -2098,7 +2020,7 @@ static void codec_thread(void) | |||
2098 | logf("Codec failure"); | 2020 | logf("Codec failure"); |
2099 | gui_syncsplash(HZ*2, "Codec failure"); | 2021 | gui_syncsplash(HZ*2, "Codec failure"); |
2100 | } | 2022 | } |
2101 | 2023 | ||
2102 | if (!codec_load_next_track()) | 2024 | if (!codec_load_next_track()) |
2103 | { | 2025 | { |
2104 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); | 2026 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); |
@@ -2116,8 +2038,8 @@ static void codec_thread(void) | |||
2116 | * triggering the WPS exit */ | 2038 | * triggering the WPS exit */ |
2117 | while(pcm_is_playing()) | 2039 | while(pcm_is_playing()) |
2118 | { | 2040 | { |
2119 | CUR_TI->id3.elapsed = | 2041 | curtrack_id3.elapsed = |
2120 | CUR_TI->id3.length - pcmbuf_get_latency(); | 2042 | curtrack_id3.length - pcmbuf_get_latency(); |
2121 | sleep(1); | 2043 | sleep(1); |
2122 | } | 2044 | } |
2123 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); | 2045 | LOGFQUEUE("codec > audio Q_AUDIO_STOP"); |
@@ -2126,8 +2048,8 @@ static void codec_thread(void) | |||
2126 | break; | 2048 | break; |
2127 | } | 2049 | } |
2128 | } | 2050 | } |
2129 | 2051 | ||
2130 | if (CUR_TI->has_codec) | 2052 | if (CUR_TI->codec_hid > 0) |
2131 | { | 2053 | { |
2132 | LOGFQUEUE("codec > codec Q_CODEC_LOAD"); | 2054 | LOGFQUEUE("codec > codec Q_CODEC_LOAD"); |
2133 | queue_post(&codec_queue, Q_CODEC_LOAD, 0); | 2055 | queue_post(&codec_queue, Q_CODEC_LOAD, 0); |
@@ -2135,7 +2057,7 @@ static void codec_thread(void) | |||
2135 | else | 2057 | else |
2136 | { | 2058 | { |
2137 | const char *codec_fn = | 2059 | const char *codec_fn = |
2138 | get_codec_filename(CUR_TI->id3.codectype); | 2060 | get_codec_filename(curtrack_id3.codectype); |
2139 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); | 2061 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); |
2140 | queue_post(&codec_queue, Q_CODEC_LOAD_DISK, | 2062 | queue_post(&codec_queue, Q_CODEC_LOAD_DISK, |
2141 | (intptr_t)codec_fn); | 2063 | (intptr_t)codec_fn); |
@@ -2171,11 +2093,6 @@ static void codec_thread(void) | |||
2171 | 2093 | ||
2172 | /* --- Audio thread --- */ | 2094 | /* --- Audio thread --- */ |
2173 | 2095 | ||
2174 | static bool audio_filebuf_is_lowdata(void) | ||
2175 | { | ||
2176 | return FILEBUFUSED < AUDIO_FILEBUF_CRITICAL; | ||
2177 | } | ||
2178 | |||
2179 | static bool audio_have_tracks(void) | 2096 | static bool audio_have_tracks(void) |
2180 | { | 2097 | { |
2181 | return track_ridx != track_widx || CUR_TI->filesize; | 2098 | return track_ridx != track_widx || CUR_TI->filesize; |
@@ -2208,142 +2125,41 @@ int audio_track_count(void) | |||
2208 | 2125 | ||
2209 | long audio_filebufused(void) | 2126 | long audio_filebufused(void) |
2210 | { | 2127 | { |
2211 | return (long) FILEBUFUSED; | 2128 | return (long) buf_used(); |
2212 | } | ||
2213 | |||
2214 | /* Count the data BETWEEN the selected tracks */ | ||
2215 | static size_t audio_buffer_count_tracks(int from_track, int to_track) | ||
2216 | { | ||
2217 | size_t amount = 0; | ||
2218 | bool need_wrap = to_track < from_track; | ||
2219 | |||
2220 | while (1) | ||
2221 | { | ||
2222 | if (++from_track >= MAX_TRACK) | ||
2223 | { | ||
2224 | from_track -= MAX_TRACK; | ||
2225 | need_wrap = false; | ||
2226 | } | ||
2227 | |||
2228 | if (from_track >= to_track && !need_wrap) | ||
2229 | break; | ||
2230 | |||
2231 | amount += tracks[from_track].codecsize + tracks[from_track].filesize; | ||
2232 | } | ||
2233 | return amount; | ||
2234 | } | ||
2235 | |||
2236 | static bool audio_buffer_wind_forward(int new_track_ridx, int old_track_ridx) | ||
2237 | { | ||
2238 | size_t amount; | ||
2239 | |||
2240 | /* Start with the remainder of the previously playing track */ | ||
2241 | amount = tracks[old_track_ridx].filesize - ci.curpos; | ||
2242 | /* Then collect all data from tracks in between them */ | ||
2243 | amount += audio_buffer_count_tracks(old_track_ridx, new_track_ridx); | ||
2244 | logf("bwf:%ldB", (long) amount); | ||
2245 | |||
2246 | if (amount > FILEBUFUSED) | ||
2247 | return false; | ||
2248 | |||
2249 | /* Wind the buffer to the beginning of the target track or its codec */ | ||
2250 | buf_ridx = RINGBUF_ADD(buf_ridx, amount); | ||
2251 | |||
2252 | return true; | ||
2253 | } | 2129 | } |
2254 | 2130 | ||
2255 | static bool audio_buffer_wind_backward(int new_track_ridx, int old_track_ridx) | 2131 | static void audio_update_trackinfo(void) |
2256 | { | 2132 | { |
2257 | /* Available buffer data */ | 2133 | if (CUR_TI->id3_hid > 0) |
2258 | size_t buf_back; | 2134 | copy_mp3entry(&curtrack_id3, bufgetid3(CUR_TI->id3_hid)); |
2259 | /* Start with the previously playing track's data and our data */ | ||
2260 | size_t amount; | ||
2261 | |||
2262 | amount = ci.curpos; | ||
2263 | buf_back = RINGBUF_SUB(buf_ridx, buf_widx); | ||
2264 | |||
2265 | /* If we're not just resetting the current track */ | ||
2266 | if (new_track_ridx != old_track_ridx) | ||
2267 | { | ||
2268 | /* Need to wind to before the old track's codec and our filesize */ | ||
2269 | amount += tracks[old_track_ridx].codecsize; | ||
2270 | amount += tracks[new_track_ridx].filesize; | ||
2271 | 2135 | ||
2272 | /* Rewind the old track to its beginning */ | 2136 | CUR_TI->taginfo_ready = (CUR_TI->id3_hid > 0); |
2273 | tracks[old_track_ridx].available = | ||
2274 | tracks[old_track_ridx].filesize - tracks[old_track_ridx].filerem; | ||
2275 | } | ||
2276 | |||
2277 | /* If the codec was ever buffered */ | ||
2278 | if (tracks[new_track_ridx].codecsize) | ||
2279 | { | ||
2280 | /* Add the codec to the needed size */ | ||
2281 | amount += tracks[new_track_ridx].codecsize; | ||
2282 | tracks[new_track_ridx].has_codec = true; | ||
2283 | } | ||
2284 | |||
2285 | /* Then collect all data from tracks between new and old */ | ||
2286 | amount += audio_buffer_count_tracks(new_track_ridx, old_track_ridx); | ||
2287 | |||
2288 | /* Do we have space to make this skip? */ | ||
2289 | if (amount > buf_back) | ||
2290 | return false; | ||
2291 | 2137 | ||
2292 | logf("bwb:%ldB",amount); | 2138 | int next_idx = track_ridx + 1; |
2293 | 2139 | next_idx &= MAX_TRACK_MASK; | |
2294 | /* Rewind the buffer to the beginning of the target track or its codec */ | ||
2295 | buf_ridx = RINGBUF_SUB(buf_ridx, amount); | ||
2296 | 2140 | ||
2297 | /* Reset to the beginning of the new track */ | 2141 | if (tracks[next_idx].id3_hid > 0) |
2298 | tracks[new_track_ridx].available = tracks[new_track_ridx].filesize; | 2142 | copy_mp3entry(&nexttrack_id3, bufgetid3(tracks[next_idx].id3_hid)); |
2299 | 2143 | ||
2300 | return true; | 2144 | tracks[next_idx].taginfo_ready = (tracks[next_idx].id3_hid > 0); |
2301 | } | ||
2302 | 2145 | ||
2303 | static void audio_update_trackinfo(void) | ||
2304 | { | ||
2305 | ci.filesize = CUR_TI->filesize; | 2146 | ci.filesize = CUR_TI->filesize; |
2306 | CUR_TI->id3.elapsed = 0; | 2147 | curtrack_id3.elapsed = 0; |
2307 | CUR_TI->id3.offset = 0; | 2148 | curtrack_id3.offset = 0; |
2308 | ci.id3 = &CUR_TI->id3; | 2149 | ci.id3 = &curtrack_id3; |
2309 | ci.curpos = 0; | 2150 | ci.curpos = 0; |
2310 | ci.taginfo_ready = &CUR_TI->taginfo_ready; | 2151 | ci.taginfo_ready = &CUR_TI->taginfo_ready; |
2311 | } | 2152 | } |
2312 | 2153 | ||
2313 | /* Yield to codecs for as long as possible if they are in need of data | ||
2314 | * return true if the caller should break to let the audio thread process | ||
2315 | * new events */ | ||
2316 | static bool audio_yield_codecs(void) | ||
2317 | { | ||
2318 | yield(); | ||
2319 | |||
2320 | if (!queue_empty(&audio_queue)) | ||
2321 | return true; | ||
2322 | |||
2323 | while ((pcmbuf_is_crossfade_active() || pcmbuf_is_lowdata()) | ||
2324 | && !ci.stop_codec && playing && !audio_filebuf_is_lowdata()) | ||
2325 | { | ||
2326 | if (filling) | ||
2327 | yield(); | ||
2328 | else | ||
2329 | sleep(2); | ||
2330 | |||
2331 | if (!queue_empty(&audio_queue)) | ||
2332 | return true; | ||
2333 | } | ||
2334 | |||
2335 | return false; | ||
2336 | } | ||
2337 | |||
2338 | static void audio_clear_track_entries(bool clear_unbuffered) | 2154 | static void audio_clear_track_entries(bool clear_unbuffered) |
2339 | { | 2155 | { |
2340 | int cur_idx = track_widx; | 2156 | int cur_idx = track_widx; |
2341 | int last_idx = -1; | 2157 | int last_idx = -1; |
2342 | 2158 | ||
2343 | logf("Clearing tracks:%d/%d, %d", track_ridx, track_widx, clear_unbuffered); | 2159 | logf("Clearing tracks:%d/%d, %d", track_ridx, track_widx, clear_unbuffered); |
2344 | 2160 | ||
2345 | /* Loop over all tracks from write-to-read */ | 2161 | /* Loop over all tracks from write-to-read */ |
2346 | while (1) | 2162 | while (1) |
2347 | { | 2163 | { |
2348 | cur_idx++; | 2164 | cur_idx++; |
2349 | cur_idx &= MAX_TRACK_MASK; | 2165 | cur_idx &= MAX_TRACK_MASK; |
@@ -2353,21 +2169,21 @@ static void audio_clear_track_entries(bool clear_unbuffered) | |||
2353 | 2169 | ||
2354 | /* If the track is buffered, conditionally clear/notify, | 2170 | /* If the track is buffered, conditionally clear/notify, |
2355 | * otherwise clear the track if that option is selected */ | 2171 | * otherwise clear the track if that option is selected */ |
2356 | if (tracks[cur_idx].event_sent) | 2172 | if (tracks[cur_idx].event_sent) |
2357 | { | 2173 | { |
2358 | if (last_idx >= 0) | 2174 | if (last_idx >= 0) |
2359 | { | 2175 | { |
2360 | /* If there is an unbuffer callback, call it, otherwise, | 2176 | /* If there is an unbuffer callback, call it, otherwise, |
2361 | * just clear the track */ | 2177 | * just clear the track */ |
2362 | if (track_unbuffer_callback) | 2178 | if (track_unbuffer_callback && tracks[last_idx].id3_hid > 0) |
2363 | track_unbuffer_callback(&tracks[last_idx].id3, false); | 2179 | track_unbuffer_callback(bufgetid3(tracks[last_idx].id3_hid), false); |
2364 | 2180 | ||
2365 | memset(&tracks[last_idx], 0, sizeof(struct track_info)); | 2181 | clear_track_info(&tracks[last_idx]); |
2366 | } | 2182 | } |
2367 | last_idx = cur_idx; | 2183 | last_idx = cur_idx; |
2368 | } | 2184 | } |
2369 | else if (clear_unbuffered) | 2185 | else if (clear_unbuffered) |
2370 | memset(&tracks[cur_idx], 0, sizeof(struct track_info)); | 2186 | clear_track_info(&tracks[cur_idx]); |
2371 | } | 2187 | } |
2372 | 2188 | ||
2373 | /* We clear the previous instance of a buffered track throughout | 2189 | /* We clear the previous instance of a buffered track throughout |
@@ -2375,203 +2191,51 @@ static void audio_clear_track_entries(bool clear_unbuffered) | |||
2375 | * the last track here */ | 2191 | * the last track here */ |
2376 | if (last_idx >= 0) | 2192 | if (last_idx >= 0) |
2377 | { | 2193 | { |
2378 | if (track_unbuffer_callback) | 2194 | if (track_unbuffer_callback && tracks[last_idx].id3_hid > 0) |
2379 | track_unbuffer_callback(&tracks[last_idx].id3, true); | 2195 | track_unbuffer_callback(bufgetid3(tracks[last_idx].id3_hid), true); |
2380 | memset(&tracks[last_idx], 0, sizeof(struct track_info)); | 2196 | clear_track_info(&tracks[last_idx]); |
2381 | } | 2197 | } |
2382 | } | 2198 | } |
2383 | 2199 | ||
2384 | /* FIXME: This code should be made more generic and move to metadata.c */ | 2200 | static bool audio_release_tracks(void) |
2385 | static void audio_strip_tags(void) | ||
2386 | { | 2201 | { |
2387 | int i; | 2202 | int i, cur_idx; |
2388 | static const unsigned char tag[] = "TAG"; | ||
2389 | static const unsigned char apetag[] = "APETAGEX"; | ||
2390 | size_t tag_idx; | ||
2391 | size_t cur_idx; | ||
2392 | size_t len, version; | ||
2393 | 2203 | ||
2394 | tag_idx = RINGBUF_SUB(buf_widx, 128); | 2204 | logf("releasing all tracks"); |
2395 | 2205 | ||
2396 | if (FILEBUFUSED > 128 && tag_idx > buf_ridx) | 2206 | for(i = 0; i < MAX_TRACKS; i++) |
2397 | { | 2207 | { |
2398 | cur_idx = tag_idx; | 2208 | cur_idx = (track_ridx + i) & MAX_TRACK_MASK; |
2399 | for(i = 0;i < 3;i++) | 2209 | if (!clear_track_info(&tracks[cur_idx])) |
2400 | { | 2210 | return false; |
2401 | if(filebuf[cur_idx] != tag[i]) | ||
2402 | goto strip_ape_tag; | ||
2403 | |||
2404 | cur_idx = RINGBUF_ADD(cur_idx, 1); | ||
2405 | } | ||
2406 | |||
2407 | /* Skip id3v1 tag */ | ||
2408 | logf("Skipping ID3v1 tag"); | ||
2409 | buf_widx = tag_idx; | ||
2410 | tracks[track_widx].available -= 128; | ||
2411 | tracks[track_widx].filesize -= 128; | ||
2412 | } | ||
2413 | |||
2414 | strip_ape_tag: | ||
2415 | /* Check for APE tag (look for the APE tag footer) */ | ||
2416 | tag_idx = RINGBUF_SUB(buf_widx, 32); | ||
2417 | |||
2418 | if (FILEBUFUSED > 32 && tag_idx > buf_ridx) | ||
2419 | { | ||
2420 | cur_idx = tag_idx; | ||
2421 | for(i = 0;i < 8;i++) | ||
2422 | { | ||
2423 | if(filebuf[cur_idx] != apetag[i]) | ||
2424 | return; | ||
2425 | |||
2426 | cur_idx = RINGBUF_ADD(cur_idx, 1); | ||
2427 | } | ||
2428 | |||
2429 | /* Read the version and length from the footer */ | ||
2430 | version = filebuf[tag_idx+8] | (filebuf[tag_idx+9] << 8) | | ||
2431 | (filebuf[tag_idx+10] << 16) | (filebuf[tag_idx+11] << 24); | ||
2432 | len = filebuf[tag_idx+12] | (filebuf[tag_idx+13] << 8) | | ||
2433 | (filebuf[tag_idx+14] << 16) | (filebuf[tag_idx+15] << 24); | ||
2434 | if (version == 2000) | ||
2435 | len += 32; /* APEv2 has a 32 byte header */ | ||
2436 | |||
2437 | /* Skip APE tag */ | ||
2438 | if (FILEBUFUSED > len) | ||
2439 | { | ||
2440 | logf("Skipping APE tag (%ldB)", len); | ||
2441 | buf_widx = RINGBUF_SUB(buf_widx, len); | ||
2442 | tracks[track_widx].available -= len; | ||
2443 | tracks[track_widx].filesize -= len; | ||
2444 | } | ||
2445 | } | ||
2446 | } | ||
2447 | |||
2448 | /* Returns true if a whole file is read, false otherwise */ | ||
2449 | static bool audio_read_file(size_t minimum) | ||
2450 | { | ||
2451 | bool ret_val = false; | ||
2452 | |||
2453 | /* If we're called and no file is open, this is an error */ | ||
2454 | if (current_fd < 0) | ||
2455 | { | ||
2456 | logf("Bad fd in arf"); | ||
2457 | /* Give some hope of miraculous recovery by forcing a track reload */ | ||
2458 | tracks[track_widx].filesize = 0; | ||
2459 | /* Stop this buffering run */ | ||
2460 | return ret_val; | ||
2461 | } | ||
2462 | |||
2463 | trigger_cpu_boost(); | ||
2464 | while (tracks[track_widx].filerem > 0) | ||
2465 | { | ||
2466 | size_t copy_n; | ||
2467 | int overlap; | ||
2468 | int rc; | ||
2469 | |||
2470 | /* copy_n is the largest chunk that is safe to read */ | ||
2471 | copy_n = MIN(conf_filechunk, filebuflen - buf_widx); | ||
2472 | |||
2473 | /* buf_widx == buf_ridx is defined as buffer empty, not buffer full */ | ||
2474 | if (RINGBUF_ADD_CROSS(buf_widx,copy_n,buf_ridx) >= 0) | ||
2475 | break; | ||
2476 | |||
2477 | /* rc is the actual amount read */ | ||
2478 | rc = read(current_fd, &filebuf[buf_widx], copy_n); | ||
2479 | |||
2480 | if (rc < 0) | ||
2481 | { | ||
2482 | logf("File ended %ldB early", tracks[track_widx].filerem); | ||
2483 | tracks[track_widx].filesize -= tracks[track_widx].filerem; | ||
2484 | tracks[track_widx].filerem = 0; | ||
2485 | break; | ||
2486 | } | ||
2487 | |||
2488 | /* How much of the playing track did we overwrite */ | ||
2489 | if (buf_widx == CUR_TI->buf_idx) | ||
2490 | { | ||
2491 | /* Special handling; zero or full overlap? */ | ||
2492 | if (track_widx == track_ridx && CUR_TI->available == 0) | ||
2493 | overlap = 0; | ||
2494 | else | ||
2495 | overlap = rc; | ||
2496 | } | ||
2497 | else | ||
2498 | overlap = RINGBUF_ADD_CROSS(buf_widx,rc,CUR_TI->buf_idx); | ||
2499 | |||
2500 | if ((unsigned)rc > tracks[track_widx].filerem) | ||
2501 | { | ||
2502 | logf("Bad: rc-filerem=%ld, fixing", rc-tracks[track_widx].filerem); | ||
2503 | tracks[track_widx].filesize += rc - tracks[track_widx].filerem; | ||
2504 | tracks[track_widx].filerem = rc; | ||
2505 | } | ||
2506 | |||
2507 | /* Advance buffer */ | ||
2508 | buf_widx = RINGBUF_ADD(buf_widx, rc); | ||
2509 | tracks[track_widx].available += rc; | ||
2510 | tracks[track_widx].filerem -= rc; | ||
2511 | |||
2512 | /* If we write into the playing track, adjust it's buffer info */ | ||
2513 | if (overlap > 0) | ||
2514 | { | ||
2515 | CUR_TI->buf_idx += overlap; | ||
2516 | CUR_TI->start_pos += overlap; | ||
2517 | } | ||
2518 | |||
2519 | /* For a rebuffer, fill at least this minimum */ | ||
2520 | if (minimum > (unsigned)rc) | ||
2521 | minimum -= rc; | ||
2522 | /* Let the codec process up to the watermark */ | ||
2523 | /* Break immediately if this is a quick buffer, or there is an event */ | ||
2524 | else if (minimum || audio_yield_codecs()) | ||
2525 | { | ||
2526 | /* Exit quickly, but don't stop the overall buffering process */ | ||
2527 | ret_val = true; | ||
2528 | break; | ||
2529 | } | ||
2530 | } | 2211 | } |
2531 | 2212 | ||
2532 | if (tracks[track_widx].filerem == 0) | 2213 | return true; |
2533 | { | ||
2534 | logf("Finished buf:%ldB", tracks[track_widx].filesize); | ||
2535 | close(current_fd); | ||
2536 | current_fd = -1; | ||
2537 | audio_strip_tags(); | ||
2538 | |||
2539 | track_widx++; | ||
2540 | track_widx &= MAX_TRACK_MASK; | ||
2541 | |||
2542 | tracks[track_widx].filesize = 0; | ||
2543 | return true; | ||
2544 | } | ||
2545 | else | ||
2546 | { | ||
2547 | logf("%s buf:%ldB", ret_val?"Quick":"Partially", | ||
2548 | tracks[track_widx].filesize - tracks[track_widx].filerem); | ||
2549 | return ret_val; | ||
2550 | } | ||
2551 | } | 2214 | } |
2552 | 2215 | ||
2553 | static bool audio_loadcodec(bool start_play) | 2216 | static bool audio_loadcodec(bool start_play) |
2554 | { | 2217 | { |
2555 | size_t size = 0; | ||
2556 | int fd; | 2218 | int fd; |
2557 | int rc; | ||
2558 | size_t copy_n; | ||
2559 | int prev_track; | 2219 | int prev_track; |
2560 | char codec_path[MAX_PATH]; /* Full path to codec */ | 2220 | char codec_path[MAX_PATH]; /* Full path to codec */ |
2561 | 2221 | ||
2222 | if (tracks[track_widx].id3_hid <= 0) { | ||
2223 | return false; | ||
2224 | } | ||
2225 | |||
2562 | const char * codec_fn = | 2226 | const char * codec_fn = |
2563 | get_codec_filename(tracks[track_widx].id3.codectype); | 2227 | get_codec_filename(bufgetid3(tracks[track_widx].id3_hid)->codectype); |
2564 | if (codec_fn == NULL) | 2228 | if (codec_fn == NULL) |
2565 | return false; | 2229 | return false; |
2566 | 2230 | ||
2567 | tracks[track_widx].has_codec = false; | 2231 | tracks[track_widx].codec_hid = 0; |
2568 | 2232 | ||
2569 | if (start_play) | 2233 | if (start_play) |
2570 | { | 2234 | { |
2571 | /* Load the codec directly from disk and save some memory. */ | 2235 | /* Load the codec directly from disk and save some memory. */ |
2572 | track_ridx = track_widx; | 2236 | track_ridx = track_widx; |
2573 | ci.filesize = CUR_TI->filesize; | 2237 | ci.filesize = CUR_TI->filesize; |
2574 | ci.id3 = &CUR_TI->id3; | 2238 | ci.id3 = &curtrack_id3; |
2575 | ci.taginfo_ready = &CUR_TI->taginfo_ready; | 2239 | ci.taginfo_ready = &CUR_TI->taginfo_ready; |
2576 | ci.curpos = 0; | 2240 | ci.curpos = 0; |
2577 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); | 2241 | LOGFQUEUE("codec > codec Q_CODEC_LOAD_DISK"); |
@@ -2584,11 +2248,13 @@ static bool audio_loadcodec(bool start_play) | |||
2584 | if (track_widx != track_ridx) | 2248 | if (track_widx != track_ridx) |
2585 | { | 2249 | { |
2586 | prev_track = (track_widx - 1) & MAX_TRACK_MASK; | 2250 | prev_track = (track_widx - 1) & MAX_TRACK_MASK; |
2587 | 2251 | ||
2588 | /* If the previous codec is the same as this one, there is no need | 2252 | /* If the previous codec is the same as this one, there is no need |
2589 | * to put another copy of it on the file buffer */ | 2253 | * to put another copy of it on the file buffer */ |
2590 | if (get_codec_base_type(tracks[track_widx].id3.codectype) == | 2254 | if (get_codec_base_type( |
2591 | get_codec_base_type(tracks[prev_track].id3.codectype) | 2255 | bufgetid3(tracks[track_widx].id3_hid)->codectype) == |
2256 | get_codec_base_type( | ||
2257 | bufgetid3(tracks[prev_track].id3_hid)->codectype) | ||
2592 | && audio_codec_loaded) | 2258 | && audio_codec_loaded) |
2593 | { | 2259 | { |
2594 | logf("Reusing prev. codec"); | 2260 | logf("Reusing prev. codec"); |
@@ -2607,39 +2273,17 @@ static bool audio_loadcodec(bool start_play) | |||
2607 | } | 2273 | } |
2608 | 2274 | ||
2609 | tracks[track_widx].codecsize = filesize(fd); | 2275 | tracks[track_widx].codecsize = filesize(fd); |
2610 | 2276 | ||
2611 | /* Never load a partial codec */ | 2277 | tracks[track_widx].codec_hid = bufopen(codec_path, 0, TYPE_CODEC); |
2612 | if (RINGBUF_ADD_CROSS(buf_widx,tracks[track_widx].codecsize,buf_ridx) >= 0) | 2278 | if (tracks[track_widx].codec_hid < 0) |
2613 | { | 2279 | { |
2614 | logf("Not enough space"); | 2280 | logf("Not enough space"); |
2615 | close(fd); | 2281 | close(fd); |
2616 | return false; | 2282 | return false; |
2617 | } | 2283 | } |
2618 | 2284 | ||
2619 | while (size < tracks[track_widx].codecsize) | ||
2620 | { | ||
2621 | copy_n = MIN(conf_filechunk, filebuflen - buf_widx); | ||
2622 | rc = read(fd, &filebuf[buf_widx], copy_n); | ||
2623 | if (rc < 0) | ||
2624 | { | ||
2625 | close(fd); | ||
2626 | /* This is an error condition, likely the codec file is corrupt */ | ||
2627 | logf("Partial codec loaded"); | ||
2628 | /* Must undo the buffer write of the partial codec */ | ||
2629 | buf_widx = RINGBUF_SUB(buf_widx, size); | ||
2630 | tracks[track_widx].codecsize = 0; | ||
2631 | return false; | ||
2632 | } | ||
2633 | |||
2634 | buf_widx = RINGBUF_ADD(buf_widx, rc); | ||
2635 | |||
2636 | size += rc; | ||
2637 | } | ||
2638 | |||
2639 | tracks[track_widx].has_codec = true; | ||
2640 | |||
2641 | close(fd); | 2285 | close(fd); |
2642 | logf("Done: %ldB", size); | 2286 | logf("Loaded codec"); |
2643 | 2287 | ||
2644 | return true; | 2288 | return true; |
2645 | } | 2289 | } |
@@ -2700,94 +2344,99 @@ static void audio_set_elapsed(struct mp3entry* id3) | |||
2700 | } | 2344 | } |
2701 | } | 2345 | } |
2702 | 2346 | ||
2703 | static bool audio_load_track(int offset, bool start_play, bool rebuffer) | 2347 | /* Load one track by making the appropriate bufopen calls. Return true if |
2348 | everything required was loaded correctly, false if not. */ | ||
2349 | static bool audio_load_track(int offset, bool start_play) | ||
2704 | { | 2350 | { |
2705 | char *trackname; | 2351 | char *trackname; |
2706 | off_t size; | ||
2707 | char msgbuf[80]; | 2352 | char msgbuf[80]; |
2353 | int fd = -1; | ||
2354 | int file_offset = 0; | ||
2355 | struct mp3entry id3; | ||
2708 | 2356 | ||
2709 | /* Stop buffer filling if there is no free track entries. | 2357 | /* Stop buffer filling if there is no free track entries. |
2710 | Don't fill up the last track entry (we wan't to store next track | 2358 | Don't fill up the last track entry (we wan't to store next track |
2711 | metadata there). */ | 2359 | metadata there). */ |
2712 | if (!audio_have_free_tracks()) | 2360 | if (!audio_have_free_tracks()) |
2713 | { | 2361 | { |
2714 | logf("No free tracks"); | 2362 | logf("No free tracks"); |
2715 | return false; | 2363 | return false; |
2716 | } | 2364 | } |
2717 | 2365 | ||
2718 | if (current_fd >= 0) | ||
2719 | { | ||
2720 | logf("Nonzero fd in alt"); | ||
2721 | close(current_fd); | ||
2722 | current_fd = -1; | ||
2723 | } | ||
2724 | |||
2725 | last_peek_offset++; | 2366 | last_peek_offset++; |
2726 | peek_again: | 2367 | peek_again: |
2727 | logf("Buffering track:%d/%d", track_widx, track_ridx); | 2368 | logf("Buffering track:%d/%d", track_widx, track_ridx); |
2728 | /* Get track name from current playlist read position. */ | 2369 | /* Get track name from current playlist read position. */ |
2729 | while ((trackname = playlist_peek(last_peek_offset)) != NULL) | 2370 | while ((trackname = playlist_peek(last_peek_offset)) != NULL) |
2730 | { | 2371 | { |
2731 | /* Handle broken playlists. */ | 2372 | /* Handle broken playlists. */ |
2732 | current_fd = open(trackname, O_RDONLY); | 2373 | fd = open(trackname, O_RDONLY); |
2733 | if (current_fd < 0) | 2374 | if (fd < 0) |
2734 | { | 2375 | { |
2735 | logf("Open failed"); | 2376 | logf("Open failed"); |
2736 | /* Skip invalid entry from playlist. */ | 2377 | /* Skip invalid entry from playlist. */ |
2737 | playlist_skip_entry(NULL, last_peek_offset); | 2378 | playlist_skip_entry(NULL, last_peek_offset); |
2738 | } | 2379 | } |
2739 | else | 2380 | else |
2740 | break; | 2381 | break; |
2741 | } | 2382 | } |
2742 | 2383 | ||
2743 | if (!trackname) | 2384 | if (!trackname) |
2744 | { | 2385 | { |
2745 | logf("End-of-playlist"); | 2386 | logf("End-of-playlist"); |
2746 | playlist_end = true; | 2387 | playlist_end = true; |
2747 | return false; | 2388 | return false; |
2748 | } | 2389 | } |
2749 | 2390 | ||
2750 | /* Initialize track entry. */ | 2391 | tracks[track_widx].filesize = filesize(fd); |
2751 | size = filesize(current_fd); | ||
2752 | tracks[track_widx].filerem = size; | ||
2753 | tracks[track_widx].filesize = size; | ||
2754 | tracks[track_widx].available = 0; | ||
2755 | 2392 | ||
2756 | /* Set default values */ | 2393 | /* Set default values */ |
2757 | if (start_play) | 2394 | if (start_play) |
2758 | { | 2395 | { |
2759 | int last_codec = current_codec; | 2396 | int last_codec = current_codec; |
2760 | 2397 | ||
2761 | set_current_codec(CODEC_IDX_AUDIO); | 2398 | set_current_codec(CODEC_IDX_AUDIO); |
2762 | conf_watermark = AUDIO_DEFAULT_WATERMARK; | 2399 | buf_set_conf(BUFFERING_SET_WATERMARK, AUDIO_DEFAULT_WATERMARK); |
2763 | conf_filechunk = AUDIO_DEFAULT_FILECHUNK; | 2400 | buf_set_conf(BUFFERING_SET_CHUNKSIZE, AUDIO_DEFAULT_FILECHUNK); |
2764 | conf_preseek = AUDIO_REBUFFER_GUESS_SIZE; | 2401 | buf_set_conf(BUFFERING_SET_PRESEEK, AUDIO_REBUFFER_GUESS_SIZE); |
2765 | dsp_configure(DSP_RESET, 0); | 2402 | dsp_configure(DSP_RESET, 0); |
2766 | set_current_codec(last_codec); | 2403 | set_current_codec(last_codec); |
2404 | |||
2405 | track_changed = true; | ||
2406 | playlist_update_resume_info(audio_current_track()); | ||
2767 | } | 2407 | } |
2768 | 2408 | ||
2769 | /* Get track metadata if we don't already have it. */ | 2409 | /* Get track metadata if we don't already have it. */ |
2770 | if (!tracks[track_widx].taginfo_ready) | 2410 | if (tracks[track_widx].id3_hid <= 0) |
2771 | { | 2411 | { |
2772 | if (get_metadata(&(tracks[track_widx].id3),current_fd,trackname)) | 2412 | if (get_metadata(&id3, fd, trackname)) |
2773 | { | 2413 | { |
2774 | tracks[track_widx].taginfo_ready = true; | 2414 | tracks[track_widx].id3_hid = bufalloc(&id3, sizeof(struct mp3entry), |
2775 | if (start_play) | 2415 | TYPE_ID3); |
2416 | tracks[track_widx].taginfo_ready = (tracks[track_widx].id3_hid > 0); | ||
2417 | |||
2418 | if (tracks[track_widx].id3_hid <= 0) | ||
2419 | { | ||
2420 | last_peek_offset--; | ||
2421 | close(fd); | ||
2422 | return false; | ||
2423 | } | ||
2424 | |||
2425 | if (track_widx == track_ridx) | ||
2426 | copy_mp3entry(&curtrack_id3, &id3); | ||
2427 | else if (track_widx == ((track_ridx + 1) & MAX_TRACK_MASK)) | ||
2428 | copy_mp3entry(&nexttrack_id3, &id3); | ||
2429 | |||
2430 | if (start_play) | ||
2776 | { | 2431 | { |
2777 | track_changed = true; | 2432 | track_changed = true; |
2778 | playlist_update_resume_info(audio_current_track()); | 2433 | playlist_update_resume_info(audio_current_track()); |
2779 | } | 2434 | } |
2780 | } | 2435 | } |
2781 | else | 2436 | else |
2782 | { | 2437 | { |
2783 | logf("mde:%s!",trackname); | 2438 | logf("mde:%s!",trackname); |
2784 | 2439 | ||
2785 | /* Set filesize to zero to indicate no file was loaded. */ | ||
2786 | tracks[track_widx].filesize = 0; | ||
2787 | tracks[track_widx].filerem = 0; | ||
2788 | close(current_fd); | ||
2789 | current_fd = -1; | ||
2790 | |||
2791 | /* Skip invalid entry from playlist. */ | 2440 | /* Skip invalid entry from playlist. */ |
2792 | playlist_skip_entry(NULL, last_peek_offset); | 2441 | playlist_skip_entry(NULL, last_peek_offset); |
2793 | tracks[track_widx].taginfo_ready = false; | 2442 | tracks[track_widx].taginfo_ready = false; |
@@ -2796,6 +2445,9 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2796 | 2445 | ||
2797 | } | 2446 | } |
2798 | 2447 | ||
2448 | close(fd); | ||
2449 | |||
2450 | #if 0 | ||
2799 | if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1) | 2451 | if (cuesheet_is_enabled() && tracks[track_widx].id3.cuesheet_type == 1) |
2800 | { | 2452 | { |
2801 | char cuepath[MAX_PATH]; | 2453 | char cuepath[MAX_PATH]; |
@@ -2810,17 +2462,11 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2810 | cue_spoof_id3(curr_cue, &tracks[track_widx].id3); | 2462 | cue_spoof_id3(curr_cue, &tracks[track_widx].id3); |
2811 | } | 2463 | } |
2812 | } | 2464 | } |
2465 | #endif | ||
2813 | 2466 | ||
2814 | /* Load the codec. */ | 2467 | /* Load the codec. */ |
2815 | tracks[track_widx].codecbuf = &filebuf[buf_widx]; | 2468 | if (!audio_loadcodec(start_play)) |
2816 | if (!audio_loadcodec(start_play)) | ||
2817 | { | 2469 | { |
2818 | /* Set filesize to zero to indicate no file was loaded. */ | ||
2819 | tracks[track_widx].filesize = 0; | ||
2820 | tracks[track_widx].filerem = 0; | ||
2821 | close(current_fd); | ||
2822 | current_fd = -1; | ||
2823 | |||
2824 | if (tracks[track_widx].codecsize) | 2470 | if (tracks[track_widx].codecsize) |
2825 | { | 2471 | { |
2826 | /* No space for codec on buffer, not an error */ | 2472 | /* No space for codec on buffer, not an error */ |
@@ -2839,32 +2485,35 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2839 | goto peek_again; | 2485 | goto peek_again; |
2840 | } | 2486 | } |
2841 | 2487 | ||
2842 | tracks[track_widx].start_pos = 0; | 2488 | struct mp3entry *track_id3; |
2843 | set_filebuf_watermark(buffer_margin); | ||
2844 | tracks[track_widx].id3.elapsed = 0; | ||
2845 | 2489 | ||
2846 | if (offset > 0) | 2490 | if (track_widx == track_ridx) |
2491 | track_id3 = &curtrack_id3; | ||
2492 | else if (track_widx == ((track_ridx + 1) & MAX_TRACK_MASK)) | ||
2493 | track_id3 = &nexttrack_id3; | ||
2494 | else | ||
2495 | track_id3 = bufgetid3(tracks[track_widx].id3_hid); | ||
2496 | |||
2497 | set_filebuf_watermark(buffer_margin, 0); | ||
2498 | track_id3->elapsed = 0; | ||
2499 | |||
2500 | if (offset > 0) | ||
2847 | { | 2501 | { |
2848 | switch (tracks[track_widx].id3.codectype) { | 2502 | switch (track_id3->codectype) { |
2849 | case AFMT_MPA_L1: | 2503 | case AFMT_MPA_L1: |
2850 | case AFMT_MPA_L2: | 2504 | case AFMT_MPA_L2: |
2851 | case AFMT_MPA_L3: | 2505 | case AFMT_MPA_L3: |
2852 | lseek(current_fd, offset, SEEK_SET); | 2506 | file_offset = offset; |
2853 | tracks[track_widx].id3.offset = offset; | 2507 | track_id3->offset = offset; |
2854 | audio_set_elapsed(&tracks[track_widx].id3); | 2508 | audio_set_elapsed(track_id3); |
2855 | tracks[track_widx].filerem = size - offset; | ||
2856 | ci.curpos = offset; | 2509 | ci.curpos = offset; |
2857 | tracks[track_widx].start_pos = offset; | ||
2858 | break; | 2510 | break; |
2859 | 2511 | ||
2860 | case AFMT_WAVPACK: | 2512 | case AFMT_WAVPACK: |
2861 | lseek(current_fd, offset, SEEK_SET); | 2513 | file_offset = offset; |
2862 | tracks[track_widx].id3.offset = offset; | 2514 | track_id3->offset = offset; |
2863 | tracks[track_widx].id3.elapsed = | 2515 | track_id3->elapsed = track_id3->length / 2; |
2864 | tracks[track_widx].id3.length / 2; | ||
2865 | tracks[track_widx].filerem = size - offset; | ||
2866 | ci.curpos = offset; | 2516 | ci.curpos = offset; |
2867 | tracks[track_widx].start_pos = offset; | ||
2868 | break; | 2517 | break; |
2869 | 2518 | ||
2870 | case AFMT_OGG_VORBIS: | 2519 | case AFMT_OGG_VORBIS: |
@@ -2875,57 +2524,27 @@ static bool audio_load_track(int offset, bool start_play, bool rebuffer) | |||
2875 | case AFMT_AAC: | 2524 | case AFMT_AAC: |
2876 | case AFMT_MPC: | 2525 | case AFMT_MPC: |
2877 | case AFMT_APE: | 2526 | case AFMT_APE: |
2878 | tracks[track_widx].id3.offset = offset; | 2527 | track_id3->offset = offset; |
2879 | break; | 2528 | break; |
2880 | } | 2529 | } |
2881 | } | 2530 | } |
2882 | |||
2883 | logf("alt:%s", trackname); | ||
2884 | tracks[track_widx].buf_idx = buf_widx; | ||
2885 | |||
2886 | return audio_read_file(rebuffer); | ||
2887 | } | ||
2888 | 2531 | ||
2889 | static bool audio_read_next_metadata(void) | 2532 | logf("alt:%s", trackname); |
2890 | { | ||
2891 | int fd; | ||
2892 | char *trackname; | ||
2893 | int next_idx; | ||
2894 | int status; | ||
2895 | |||
2896 | next_idx = track_widx; | ||
2897 | if (tracks[next_idx].taginfo_ready) | ||
2898 | { | ||
2899 | next_idx++; | ||
2900 | next_idx &= MAX_TRACK_MASK; | ||
2901 | |||
2902 | if (tracks[next_idx].taginfo_ready) | ||
2903 | return true; | ||
2904 | } | ||
2905 | 2533 | ||
2906 | trackname = playlist_peek(last_peek_offset + 1); | 2534 | tracks[track_widx].audio_hid = bufopen(trackname, file_offset, TYPE_AUDIO); |
2907 | if (!trackname) | ||
2908 | return false; | ||
2909 | 2535 | ||
2910 | fd = open(trackname, O_RDONLY); | 2536 | if (tracks[track_widx].audio_hid <= 0) |
2911 | if (fd < 0) | ||
2912 | return false; | 2537 | return false; |
2913 | 2538 | ||
2914 | status = get_metadata(&(tracks[next_idx].id3),fd,trackname); | 2539 | if (start_play) |
2915 | /* Preload the glyphs in the tags */ | ||
2916 | if (status) | ||
2917 | { | 2540 | { |
2918 | tracks[next_idx].taginfo_ready = true; | 2541 | buf_request_buffer_handle(tracks[track_widx].audio_hid); |
2919 | if (tracks[next_idx].id3.title) | ||
2920 | lcd_getstringsize(tracks[next_idx].id3.title, NULL, NULL); | ||
2921 | if (tracks[next_idx].id3.artist) | ||
2922 | lcd_getstringsize(tracks[next_idx].id3.artist, NULL, NULL); | ||
2923 | if (tracks[next_idx].id3.album) | ||
2924 | lcd_getstringsize(tracks[next_idx].id3.album, NULL, NULL); | ||
2925 | } | 2542 | } |
2926 | close(fd); | ||
2927 | 2543 | ||
2928 | return status; | 2544 | track_widx++; |
2545 | track_widx &= MAX_TRACK_MASK; | ||
2546 | |||
2547 | return true; | ||
2929 | } | 2548 | } |
2930 | 2549 | ||
2931 | /* Send callback events to notify about new tracks. */ | 2550 | /* Send callback events to notify about new tracks. */ |
@@ -2939,7 +2558,7 @@ static void audio_generate_postbuffer_events(void) | |||
2939 | if (audio_have_tracks()) | 2558 | if (audio_have_tracks()) |
2940 | { | 2559 | { |
2941 | cur_idx = track_ridx; | 2560 | cur_idx = track_ridx; |
2942 | 2561 | ||
2943 | while (1) { | 2562 | while (1) { |
2944 | if (!tracks[cur_idx].event_sent) | 2563 | if (!tracks[cur_idx].event_sent) |
2945 | { | 2564 | { |
@@ -2947,8 +2566,8 @@ static void audio_generate_postbuffer_events(void) | |||
2947 | { | 2566 | { |
2948 | /* Mark the event 'sent' even if we don't really send one */ | 2567 | /* Mark the event 'sent' even if we don't really send one */ |
2949 | tracks[last_idx].event_sent = true; | 2568 | tracks[last_idx].event_sent = true; |
2950 | if (track_buffer_callback) | 2569 | if (track_buffer_callback && tracks[last_idx].id3_hid > 0) |
2951 | track_buffer_callback(&tracks[last_idx].id3, false); | 2570 | track_buffer_callback(bufgetid3(tracks[last_idx].id3_hid), false); |
2952 | } | 2571 | } |
2953 | last_idx = cur_idx; | 2572 | last_idx = cur_idx; |
2954 | } | 2573 | } |
@@ -2961,35 +2580,19 @@ static void audio_generate_postbuffer_events(void) | |||
2961 | if (last_idx >= 0 && !tracks[last_idx].event_sent) | 2580 | if (last_idx >= 0 && !tracks[last_idx].event_sent) |
2962 | { | 2581 | { |
2963 | tracks[last_idx].event_sent = true; | 2582 | tracks[last_idx].event_sent = true; |
2964 | if (track_buffer_callback) | 2583 | if (track_buffer_callback && tracks[last_idx].id3_hid > 0) |
2965 | track_buffer_callback(&tracks[last_idx].id3, true); | 2584 | track_buffer_callback(bufgetid3(tracks[last_idx].id3_hid), true); |
2966 | } | 2585 | } |
2967 | } | 2586 | } |
2968 | } | 2587 | } |
2969 | 2588 | ||
2970 | static bool audio_initialize_buffer_fill(bool clear_tracks) | 2589 | static void low_buffer_callback(void) |
2971 | { | 2590 | { |
2972 | /* Don't initialize if we're already initialized */ | 2591 | LOGFQUEUE("buffering > audio Q_AUDIO_FILL_BUFFER"); |
2973 | if (filling) | 2592 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); |
2974 | return true; | ||
2975 | |||
2976 | logf("Starting buffer fill"); | ||
2977 | |||
2978 | /* Set the filling flag true before calling audio_clear_tracks as that | ||
2979 | * function can yield and we start looping. */ | ||
2980 | filling = true; | ||
2981 | |||
2982 | if (clear_tracks) | ||
2983 | audio_clear_track_entries(false); | ||
2984 | |||
2985 | /* Save the current resume position once. */ | ||
2986 | playlist_update_resume_info(audio_current_track()); | ||
2987 | |||
2988 | return true; | ||
2989 | } | 2593 | } |
2990 | 2594 | ||
2991 | static void audio_fill_file_buffer( | 2595 | static void audio_fill_file_buffer(bool start_play, size_t offset) |
2992 | bool start_play, bool rebuffer, size_t offset) | ||
2993 | { | 2596 | { |
2994 | bool had_next_track = audio_next_track() != NULL; | 2597 | bool had_next_track = audio_next_track() != NULL; |
2995 | bool continue_buffering; | 2598 | bool continue_buffering; |
@@ -3000,61 +2603,49 @@ static void audio_fill_file_buffer( | |||
3000 | if (buffer_state != BUFFER_STATE_INITIALIZED) | 2603 | if (buffer_state != BUFFER_STATE_INITIALIZED) |
3001 | audio_reset_buffer(); | 2604 | audio_reset_buffer(); |
3002 | 2605 | ||
3003 | if (!audio_initialize_buffer_fill(!start_play)) | 2606 | logf("Starting buffer fill"); |
3004 | return ; | ||
3005 | 2607 | ||
3006 | /* If we have a partially buffered track, continue loading, | 2608 | if (!start_play) |
3007 | * otherwise load a new track */ | 2609 | audio_clear_track_entries(false); |
3008 | if (tracks[track_widx].filesize > 0) | ||
3009 | continue_buffering = audio_read_file(rebuffer); | ||
3010 | else | ||
3011 | continue_buffering = audio_load_track(offset, start_play, rebuffer); | ||
3012 | 2610 | ||
3013 | if (!had_next_track && audio_next_track()) | 2611 | /* Save the current resume position once. */ |
3014 | track_changed = true; | 2612 | playlist_update_resume_info(audio_current_track()); |
3015 | 2613 | ||
3016 | /* If we're done buffering */ | 2614 | do { |
3017 | if (!continue_buffering) | 2615 | continue_buffering = audio_load_track(offset, start_play); |
3018 | { | 2616 | start_play = false; |
3019 | audio_read_next_metadata(); | 2617 | offset = 0; |
2618 | sleep(1); | ||
2619 | } while (continue_buffering); | ||
3020 | 2620 | ||
3021 | audio_generate_postbuffer_events(); | 2621 | if (!had_next_track && audio_next_track()) |
3022 | filling = false; | 2622 | track_changed = true; |
3023 | } | ||
3024 | #ifndef SIMULATOR | ||
3025 | ata_sleep(); | ||
3026 | #endif | ||
3027 | 2623 | ||
2624 | audio_generate_postbuffer_events(); | ||
2625 | register_buffer_low_callback(low_buffer_callback); | ||
3028 | } | 2626 | } |
3029 | 2627 | ||
3030 | static void audio_rebuffer(void) | 2628 | static void audio_rebuffer(void) |
3031 | { | 2629 | { |
3032 | logf("Forcing rebuffer"); | 2630 | logf("Forcing rebuffer"); |
3033 | 2631 | ||
3034 | /* Stop in progress fill, and clear open file descriptor */ | 2632 | clear_track_info(CUR_TI); |
3035 | if (current_fd >= 0) | ||
3036 | { | ||
3037 | close(current_fd); | ||
3038 | current_fd = -1; | ||
3039 | } | ||
3040 | filling = false; | ||
3041 | 2633 | ||
3042 | /* Reset buffer and track pointers */ | 2634 | /* Reset track pointers */ |
3043 | CUR_TI->buf_idx = buf_ridx = buf_widx = 0; | ||
3044 | track_widx = track_ridx; | 2635 | track_widx = track_ridx; |
3045 | audio_clear_track_entries(true); | 2636 | audio_clear_track_entries(true); |
3046 | CUR_TI->available = 0; | 2637 | |
2638 | /* Just to make sure none were forgotten */ | ||
2639 | audio_release_tracks(); | ||
3047 | 2640 | ||
3048 | /* Fill the buffer */ | 2641 | /* Fill the buffer */ |
3049 | last_peek_offset = -1; | 2642 | last_peek_offset = -1; |
3050 | CUR_TI->filesize = 0; | ||
3051 | CUR_TI->start_pos = 0; | ||
3052 | ci.curpos = 0; | 2643 | ci.curpos = 0; |
3053 | 2644 | ||
3054 | if (!CUR_TI->taginfo_ready) | 2645 | if (!CUR_TI->taginfo_ready) |
3055 | memset(&CUR_TI->id3, 0, sizeof(struct mp3entry)); | 2646 | memset(&curtrack_id3, 0, sizeof(struct mp3entry)); |
3056 | 2647 | ||
3057 | audio_fill_file_buffer(false, true, 0); | 2648 | audio_fill_file_buffer(false, 0); |
3058 | } | 2649 | } |
3059 | 2650 | ||
3060 | static int audio_check_new_track(void) | 2651 | static int audio_check_new_track(void) |
@@ -3069,7 +2660,6 @@ static int audio_check_new_track(void) | |||
3069 | if (playlist_next_dir(ci.new_track)) | 2660 | if (playlist_next_dir(ci.new_track)) |
3070 | { | 2661 | { |
3071 | ci.new_track = 0; | 2662 | ci.new_track = 0; |
3072 | CUR_TI->taginfo_ready = false; | ||
3073 | audio_rebuffer(); | 2663 | audio_rebuffer(); |
3074 | goto skip_done; | 2664 | goto skip_done; |
3075 | } | 2665 | } |
@@ -3115,22 +2705,34 @@ static int audio_check_new_track(void) | |||
3115 | } | 2705 | } |
3116 | 2706 | ||
3117 | /* Save the old track */ | 2707 | /* Save the old track */ |
3118 | prev_ti = CUR_TI; | 2708 | copy_mp3entry(&prevtrack_id3, &curtrack_id3); |
2709 | |||
2710 | int i, idx; | ||
2711 | for (i = 0; i < ci.new_track; i++) | ||
2712 | { | ||
2713 | idx = (track_ridx + i) & MAX_TRACK_MASK; | ||
2714 | if (buf_handle_offset(tracks[idx].audio_hid) > 0) | ||
2715 | clear_track_info(&tracks[idx]); | ||
2716 | } | ||
3119 | 2717 | ||
3120 | /* Move to the new track */ | 2718 | /* Move to the new track */ |
3121 | track_ridx += ci.new_track; | 2719 | track_ridx += ci.new_track; |
3122 | track_ridx &= MAX_TRACK_MASK; | 2720 | track_ridx &= MAX_TRACK_MASK; |
3123 | 2721 | ||
2722 | buf_set_base_handle(CUR_TI->audio_hid); | ||
2723 | |||
3124 | if (automatic_skip) | 2724 | if (automatic_skip) |
2725 | { | ||
3125 | playlist_end = false; | 2726 | playlist_end = false; |
2727 | wps_offset = -ci.new_track; | ||
2728 | } | ||
3126 | 2729 | ||
3127 | track_changed = !automatic_skip; | 2730 | track_changed = true; |
3128 | 2731 | ||
3129 | /* If it is not safe to even skip this many track entries */ | 2732 | /* If it is not safe to even skip this many track entries */ |
3130 | if (ci.new_track >= track_count || ci.new_track <= track_count - MAX_TRACK) | 2733 | if (ci.new_track >= track_count || ci.new_track <= track_count - MAX_TRACK) |
3131 | { | 2734 | { |
3132 | ci.new_track = 0; | 2735 | ci.new_track = 0; |
3133 | CUR_TI->taginfo_ready = false; | ||
3134 | audio_rebuffer(); | 2736 | audio_rebuffer(); |
3135 | goto skip_done; | 2737 | goto skip_done; |
3136 | } | 2738 | } |
@@ -3146,18 +2748,13 @@ static int audio_check_new_track(void) | |||
3146 | } | 2748 | } |
3147 | 2749 | ||
3148 | /* The track may be in memory, see if it really is */ | 2750 | /* The track may be in memory, see if it really is */ |
3149 | if (forward) | 2751 | if (!forward) |
3150 | { | ||
3151 | if (!audio_buffer_wind_forward(track_ridx, old_track_ridx)) | ||
3152 | audio_rebuffer(); | ||
3153 | } | ||
3154 | else | ||
3155 | { | 2752 | { |
3156 | int cur_idx = track_ridx; | 2753 | int cur_idx = track_ridx; |
3157 | bool taginfo_ready = true; | 2754 | bool taginfo_ready = true; |
3158 | bool wrap = track_ridx > old_track_ridx; | 2755 | bool wrap = track_ridx > old_track_ridx; |
3159 | 2756 | ||
3160 | while (1) | 2757 | while (1) |
3161 | { | 2758 | { |
3162 | cur_idx++; | 2759 | cur_idx++; |
3163 | cur_idx &= MAX_TRACK_MASK; | 2760 | cur_idx &= MAX_TRACK_MASK; |
@@ -3170,19 +2767,9 @@ static int audio_check_new_track(void) | |||
3170 | taginfo_ready = false; | 2767 | taginfo_ready = false; |
3171 | break; | 2768 | break; |
3172 | } | 2769 | } |
3173 | |||
3174 | tracks[cur_idx].available = tracks[cur_idx].filesize; | ||
3175 | if (tracks[cur_idx].codecsize) | ||
3176 | tracks[cur_idx].has_codec = true; | ||
3177 | } | ||
3178 | if (taginfo_ready) | ||
3179 | { | ||
3180 | if (!audio_buffer_wind_backward(track_ridx, old_track_ridx)) | ||
3181 | audio_rebuffer(); | ||
3182 | } | 2770 | } |
3183 | else | 2771 | if (!taginfo_ready) |
3184 | { | 2772 | { |
3185 | CUR_TI->taginfo_ready = false; | ||
3186 | audio_rebuffer(); | 2773 | audio_rebuffer(); |
3187 | } | 2774 | } |
3188 | } | 2775 | } |
@@ -3193,66 +2780,6 @@ skip_done: | |||
3193 | return Q_CODEC_REQUEST_COMPLETE; | 2780 | return Q_CODEC_REQUEST_COMPLETE; |
3194 | } | 2781 | } |
3195 | 2782 | ||
3196 | static int audio_rebuffer_and_seek(size_t newpos) | ||
3197 | { | ||
3198 | size_t real_preseek; | ||
3199 | int fd; | ||
3200 | char *trackname; | ||
3201 | |||
3202 | /* (Re-)open current track's file handle. */ | ||
3203 | trackname = playlist_peek(0); | ||
3204 | fd = open(trackname, O_RDONLY); | ||
3205 | if (fd < 0) | ||
3206 | { | ||
3207 | LOGFQUEUE("audio >|= codec Q_CODEC_REQUEST_FAILED"); | ||
3208 | return Q_CODEC_REQUEST_FAILED; | ||
3209 | } | ||
3210 | |||
3211 | if (current_fd >= 0) | ||
3212 | close(current_fd); | ||
3213 | current_fd = fd; | ||
3214 | |||
3215 | playlist_end = false; | ||
3216 | |||
3217 | ci.curpos = newpos; | ||
3218 | |||
3219 | /* Clear codec buffer. */ | ||
3220 | track_widx = track_ridx; | ||
3221 | tracks[track_widx].buf_idx = buf_widx = buf_ridx = 0; | ||
3222 | |||
3223 | last_peek_offset = 0; | ||
3224 | filling = false; | ||
3225 | audio_initialize_buffer_fill(true); | ||
3226 | |||
3227 | /* This may have been tweaked by the id3v1 code */ | ||
3228 | CUR_TI->filesize=filesize(fd); | ||
3229 | if (newpos > conf_preseek) | ||
3230 | { | ||
3231 | CUR_TI->start_pos = newpos - conf_preseek; | ||
3232 | lseek(current_fd, CUR_TI->start_pos, SEEK_SET); | ||
3233 | CUR_TI->filerem = CUR_TI->filesize - CUR_TI->start_pos; | ||
3234 | real_preseek = conf_preseek; | ||
3235 | } | ||
3236 | else | ||
3237 | { | ||
3238 | CUR_TI->start_pos = 0; | ||
3239 | CUR_TI->filerem = CUR_TI->filesize; | ||
3240 | real_preseek = newpos; | ||
3241 | } | ||
3242 | |||
3243 | CUR_TI->available = 0; | ||
3244 | |||
3245 | audio_read_file(real_preseek); | ||
3246 | |||
3247 | /* Account for the data we just read that is 'behind' us now */ | ||
3248 | CUR_TI->available -= real_preseek; | ||
3249 | |||
3250 | buf_ridx = RINGBUF_ADD(buf_ridx, real_preseek); | ||
3251 | |||
3252 | LOGFQUEUE("audio >|= codec Q_CODEC_REQUEST_COMPLETE"); | ||
3253 | return Q_CODEC_REQUEST_COMPLETE; | ||
3254 | } | ||
3255 | |||
3256 | void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3, | 2783 | void audio_set_track_buffer_event(void (*handler)(struct mp3entry *id3, |
3257 | bool last_track)) | 2784 | bool last_track)) |
3258 | { | 2785 | { |
@@ -3309,7 +2836,7 @@ static void audio_stop_playback(void) | |||
3309 | /* Save the current playing spot, or NULL if the playlist has ended */ | 2836 | /* Save the current playing spot, or NULL if the playlist has ended */ |
3310 | playlist_update_resume_info(id3); | 2837 | playlist_update_resume_info(id3); |
3311 | 2838 | ||
3312 | prev_track_elapsed = CUR_TI->id3.elapsed; | 2839 | prev_track_elapsed = curtrack_id3.elapsed; |
3313 | 2840 | ||
3314 | /* Increment index so runtime info is saved in audio_clear_track_entries(). | 2841 | /* Increment index so runtime info is saved in audio_clear_track_entries(). |
3315 | * Done here, as audio_stop_playback() may be called more than once. | 2842 | * Done here, as audio_stop_playback() may be called more than once. |
@@ -3324,19 +2851,18 @@ static void audio_stop_playback(void) | |||
3324 | } | 2851 | } |
3325 | } | 2852 | } |
3326 | 2853 | ||
3327 | filling = false; | ||
3328 | paused = false; | 2854 | paused = false; |
3329 | audio_stop_codec_flush(); | 2855 | audio_stop_codec_flush(); |
3330 | playing = false; | 2856 | playing = false; |
3331 | 2857 | ||
3332 | if (current_fd >= 0) | 2858 | /* Close all tracks */ |
3333 | { | 2859 | audio_release_tracks(); |
3334 | close(current_fd); | ||
3335 | current_fd = -1; | ||
3336 | } | ||
3337 | 2860 | ||
3338 | /* Mark all entries null. */ | 2861 | /* Mark all entries null. */ |
3339 | audio_clear_track_entries(false); | 2862 | audio_clear_track_entries(false); |
2863 | |||
2864 | memset(&curtrack_id3, 0, sizeof(struct mp3entry)); | ||
2865 | memset(&nexttrack_id3, 0, sizeof(struct mp3entry)); | ||
3340 | } | 2866 | } |
3341 | 2867 | ||
3342 | static void audio_play_start(size_t offset) | 2868 | static void audio_play_start(size_t offset) |
@@ -3358,26 +2884,19 @@ static void audio_play_start(size_t offset) | |||
3358 | ci.new_track = 0; | 2884 | ci.new_track = 0; |
3359 | ci.seek_time = 0; | 2885 | ci.seek_time = 0; |
3360 | wps_offset = 0; | 2886 | wps_offset = 0; |
3361 | |||
3362 | if (current_fd >= 0) | ||
3363 | { | ||
3364 | close(current_fd); | ||
3365 | current_fd = -1; | ||
3366 | } | ||
3367 | 2887 | ||
3368 | sound_set_volume(global_settings.volume); | 2888 | sound_set_volume(global_settings.volume); |
3369 | track_widx = track_ridx = 0; | 2889 | track_widx = track_ridx = 0; |
3370 | buf_ridx = buf_widx = 0; | ||
3371 | 2890 | ||
3372 | /* Mark all entries null. */ | 2891 | /* Mark all entries null. */ |
3373 | memset(tracks, 0, sizeof(struct track_info) * MAX_TRACK); | 2892 | memset(tracks, 0, sizeof(struct track_info) * MAX_TRACK); |
3374 | 2893 | ||
3375 | last_peek_offset = -1; | 2894 | last_peek_offset = -1; |
3376 | 2895 | ||
3377 | /* Officially playing */ | 2896 | /* Officially playing */ |
3378 | queue_reply(&audio_queue, 1); | 2897 | queue_reply(&audio_queue, 1); |
3379 | 2898 | ||
3380 | audio_fill_file_buffer(true, false, offset); | 2899 | audio_fill_file_buffer(true, offset); |
3381 | 2900 | ||
3382 | LOGFQUEUE("audio > audio Q_AUDIO_TRACK_CHANGED"); | 2901 | LOGFQUEUE("audio > audio Q_AUDIO_TRACK_CHANGED"); |
3383 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); | 2902 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); |
@@ -3387,7 +2906,7 @@ static void audio_play_start(size_t offset) | |||
3387 | /* Invalidates all but currently playing track. */ | 2906 | /* Invalidates all but currently playing track. */ |
3388 | static void audio_invalidate_tracks(void) | 2907 | static void audio_invalidate_tracks(void) |
3389 | { | 2908 | { |
3390 | if (audio_have_tracks()) | 2909 | if (audio_have_tracks()) |
3391 | { | 2910 | { |
3392 | last_peek_offset = 0; | 2911 | last_peek_offset = 0; |
3393 | playlist_end = false; | 2912 | playlist_end = false; |
@@ -3396,13 +2915,9 @@ static void audio_invalidate_tracks(void) | |||
3396 | /* Mark all other entries null (also buffered wrong metadata). */ | 2915 | /* Mark all other entries null (also buffered wrong metadata). */ |
3397 | audio_clear_track_entries(true); | 2916 | audio_clear_track_entries(true); |
3398 | 2917 | ||
3399 | /* If the current track is fully buffered, advance the write pointer */ | 2918 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; |
3400 | if (tracks[track_widx].filerem == 0) | ||
3401 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; | ||
3402 | 2919 | ||
3403 | buf_widx = RINGBUF_ADD(buf_ridx, CUR_TI->available); | 2920 | audio_fill_file_buffer(false, 0); |
3404 | |||
3405 | audio_read_next_metadata(); | ||
3406 | } | 2921 | } |
3407 | } | 2922 | } |
3408 | 2923 | ||
@@ -3421,18 +2936,10 @@ static void audio_new_playlist(void) | |||
3421 | track_widx++; | 2936 | track_widx++; |
3422 | track_widx &= MAX_TRACK_MASK; | 2937 | track_widx &= MAX_TRACK_MASK; |
3423 | 2938 | ||
3424 | /* Stop reading the current track */ | ||
3425 | CUR_TI->filerem = 0; | ||
3426 | close(current_fd); | ||
3427 | current_fd = -1; | ||
3428 | |||
3429 | /* Mark the current track as invalid to prevent skipping back to it */ | 2939 | /* Mark the current track as invalid to prevent skipping back to it */ |
3430 | CUR_TI->taginfo_ready = false; | 2940 | CUR_TI->taginfo_ready = false; |
3431 | |||
3432 | /* Invalidate the buffer other than the playing track */ | ||
3433 | buf_widx = RINGBUF_ADD(buf_ridx, CUR_TI->available); | ||
3434 | } | 2941 | } |
3435 | 2942 | ||
3436 | /* Signal the codec to initiate a track change forward */ | 2943 | /* Signal the codec to initiate a track change forward */ |
3437 | new_playlist = true; | 2944 | new_playlist = true; |
3438 | ci.new_track = 1; | 2945 | ci.new_track = 1; |
@@ -3440,7 +2947,7 @@ static void audio_new_playlist(void) | |||
3440 | /* Officially playing */ | 2947 | /* Officially playing */ |
3441 | queue_reply(&audio_queue, 1); | 2948 | queue_reply(&audio_queue, 1); |
3442 | 2949 | ||
3443 | audio_fill_file_buffer(false, true, 0); | 2950 | audio_fill_file_buffer(false, 0); |
3444 | } | 2951 | } |
3445 | 2952 | ||
3446 | static void audio_initiate_track_change(long direction) | 2953 | static void audio_initiate_track_change(long direction) |
@@ -3551,10 +3058,7 @@ static void audio_reset_buffer(void) | |||
3551 | will already be line aligned */ | 3058 | will already be line aligned */ |
3552 | filebuflen &= ~3; | 3059 | filebuflen &= ~3; |
3553 | 3060 | ||
3554 | /* Set the high watermark as 75% full...or 25% empty :) */ | 3061 | buffering_init(filebuf, filebuflen); |
3555 | #if MEM > 8 | ||
3556 | high_watermark = 3*filebuflen / 4; | ||
3557 | #endif | ||
3558 | 3062 | ||
3559 | /* Clear any references to the file buffer */ | 3063 | /* Clear any references to the file buffer */ |
3560 | buffer_state = BUFFER_STATE_INITIALIZED; | 3064 | buffer_state = BUFFER_STATE_INITIALIZED; |
@@ -3588,16 +3092,6 @@ static void audio_reset_buffer(void) | |||
3588 | #endif | 3092 | #endif |
3589 | } | 3093 | } |
3590 | 3094 | ||
3591 | #if MEM > 8 | ||
3592 | /* we dont want this rebuffering on targets with little ram | ||
3593 | because the disk may never spin down */ | ||
3594 | static bool ata_fillbuffer_callback(void) | ||
3595 | { | ||
3596 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER_IF_ACTIVE_ATA, 0); | ||
3597 | return true; | ||
3598 | } | ||
3599 | #endif | ||
3600 | |||
3601 | static void audio_thread(void) | 3095 | static void audio_thread(void) |
3602 | { | 3096 | { |
3603 | struct queue_event ev; | 3097 | struct queue_event ev; |
@@ -3618,41 +3112,17 @@ static void audio_thread(void) | |||
3618 | invalid when an audio codec is loaded */ | 3112 | invalid when an audio codec is loaded */ |
3619 | wait_for_voice_swap_in(); | 3113 | wait_for_voice_swap_in(); |
3620 | #endif | 3114 | #endif |
3621 | 3115 | ||
3622 | while (1) | 3116 | while (1) |
3623 | { | 3117 | { |
3624 | if (filling) | 3118 | queue_wait_w_tmo(&audio_queue, &ev, HZ/2); |
3625 | { | ||
3626 | queue_wait_w_tmo(&audio_queue, &ev, 0); | ||
3627 | if (ev.id == SYS_TIMEOUT) | ||
3628 | ev.id = Q_AUDIO_FILL_BUFFER; | ||
3629 | } | ||
3630 | else | ||
3631 | { | ||
3632 | queue_wait_w_tmo(&audio_queue, &ev, HZ/2); | ||
3633 | #if MEM > 8 | ||
3634 | if (playing && (ev.id == SYS_TIMEOUT) && | ||
3635 | (FILEBUFUSED < high_watermark)) | ||
3636 | register_ata_idle_func(ata_fillbuffer_callback); | ||
3637 | #endif | ||
3638 | } | ||
3639 | 3119 | ||
3640 | switch (ev.id) { | 3120 | switch (ev.id) { |
3641 | #if MEM > 8 | ||
3642 | case Q_AUDIO_FILL_BUFFER_IF_ACTIVE_ATA: | ||
3643 | /* only fill if the disk is still spining */ | ||
3644 | #ifndef SIMULATOR | ||
3645 | if (!ata_disk_is_active()) | ||
3646 | break; | ||
3647 | #endif | ||
3648 | #endif /* MEM > 8 */ | ||
3649 | /* else fall through to Q_AUDIO_FILL_BUFFER */ | ||
3650 | case Q_AUDIO_FILL_BUFFER: | 3121 | case Q_AUDIO_FILL_BUFFER: |
3651 | LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER"); | 3122 | LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER"); |
3652 | if (!filling) | 3123 | if (!playing || playlist_end || ci.stop_codec) |
3653 | if (!playing || playlist_end || ci.stop_codec) | 3124 | break; |
3654 | break; | 3125 | audio_fill_file_buffer(false, 0); |
3655 | audio_fill_file_buffer(false, false, 0); | ||
3656 | break; | 3126 | break; |
3657 | 3127 | ||
3658 | case Q_AUDIO_PLAY: | 3128 | case Q_AUDIO_PLAY: |
@@ -3704,11 +3174,6 @@ static void audio_thread(void) | |||
3704 | ci.seek_time = (long)ev.data+1; | 3174 | ci.seek_time = (long)ev.data+1; |
3705 | break; | 3175 | break; |
3706 | 3176 | ||
3707 | case Q_AUDIO_REBUFFER_SEEK: | ||
3708 | LOGFQUEUE("audio < Q_AUDIO_REBUFFER_SEEK"); | ||
3709 | queue_reply(&audio_queue, audio_rebuffer_and_seek(ev.data)); | ||
3710 | break; | ||
3711 | |||
3712 | case Q_AUDIO_CHECK_NEW_TRACK: | 3177 | case Q_AUDIO_CHECK_NEW_TRACK: |
3713 | LOGFQUEUE("audio < Q_AUDIO_CHECK_NEW_TRACK"); | 3178 | LOGFQUEUE("audio < Q_AUDIO_CHECK_NEW_TRACK"); |
3714 | queue_reply(&audio_queue, audio_check_new_track()); | 3179 | queue_reply(&audio_queue, audio_check_new_track()); |
@@ -3727,8 +3192,14 @@ static void audio_thread(void) | |||
3727 | 3192 | ||
3728 | case Q_AUDIO_TRACK_CHANGED: | 3193 | case Q_AUDIO_TRACK_CHANGED: |
3729 | LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED"); | 3194 | LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED"); |
3195 | if (automatic_skip) | ||
3196 | { | ||
3197 | wps_offset = 0; | ||
3198 | automatic_skip = false; | ||
3199 | prevtrack_id3.path[0] = 0; | ||
3200 | } | ||
3730 | if (track_changed_callback) | 3201 | if (track_changed_callback) |
3731 | track_changed_callback(&CUR_TI->id3); | 3202 | track_changed_callback(&curtrack_id3); |
3732 | track_changed = true; | 3203 | track_changed = true; |
3733 | playlist_update_resume_info(audio_current_track()); | 3204 | playlist_update_resume_info(audio_current_track()); |
3734 | break; | 3205 | break; |
@@ -3740,6 +3211,8 @@ static void audio_thread(void) | |||
3740 | audio_stop_playback(); | 3211 | audio_stop_playback(); |
3741 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | 3212 | usb_acknowledge(SYS_USB_CONNECTED_ACK); |
3742 | usb_wait_for_disconnect(&audio_queue); | 3213 | usb_wait_for_disconnect(&audio_queue); |
3214 | /* release tracks to make sure all handles are closed */ | ||
3215 | audio_release_tracks(); | ||
3743 | break; | 3216 | break; |
3744 | #endif | 3217 | #endif |
3745 | 3218 | ||
@@ -3749,6 +3222,7 @@ static void audio_thread(void) | |||
3749 | 3222 | ||
3750 | default: | 3223 | default: |
3751 | LOGFQUEUE("audio < default"); | 3224 | LOGFQUEUE("audio < default"); |
3225 | break; | ||
3752 | } /* end switch */ | 3226 | } /* end switch */ |
3753 | } /* end while */ | 3227 | } /* end while */ |
3754 | } | 3228 | } |
@@ -3796,6 +3270,7 @@ void audio_init(void) | |||
3796 | queue_init(&audio_queue, true); | 3270 | queue_init(&audio_queue, true); |
3797 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list); | 3271 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list); |
3798 | queue_init(&codec_queue, true); | 3272 | queue_init(&codec_queue, true); |
3273 | queue_enable_queue_send(&codec_queue, &codec_queue_sender_list); | ||
3799 | 3274 | ||
3800 | pcm_init(); | 3275 | pcm_init(); |
3801 | 3276 | ||
@@ -3858,7 +3333,7 @@ void audio_init(void) | |||
3858 | 3333 | ||
3859 | audio_thread_p = create_thread(audio_thread, audio_stack, | 3334 | audio_thread_p = create_thread(audio_thread, audio_stack, |
3860 | sizeof(audio_stack), CREATE_THREAD_FROZEN, | 3335 | sizeof(audio_stack), CREATE_THREAD_FROZEN, |
3861 | audio_thread_name IF_PRIO(, PRIORITY_BUFFERING) | 3336 | audio_thread_name IF_PRIO(, PRIORITY_BACKGROUND) |
3862 | IF_COP(, CPU)); | 3337 | IF_COP(, CPU)); |
3863 | 3338 | ||
3864 | #ifdef PLAYBACK_VOICE | 3339 | #ifdef PLAYBACK_VOICE |
diff --git a/apps/playback.h b/apps/playback.h index 43cdd5972f..9088af9b6e 100644 --- a/apps/playback.h +++ b/apps/playback.h | |||
@@ -32,9 +32,9 @@ | |||
32 | #define CODEC_SET_AUDIOBUF_WATERMARK 4 | 32 | #define CODEC_SET_AUDIOBUF_WATERMARK 4 |
33 | 33 | ||
34 | #if MEM > 1 | 34 | #if MEM > 1 |
35 | #define MAX_TRACK 32 | 35 | #define MAX_TRACK 128 |
36 | #else | 36 | #else |
37 | #define MAX_TRACK 8 | 37 | #define MAX_TRACK 32 |
38 | #endif | 38 | #endif |
39 | 39 | ||
40 | #define MAX_TRACK_MASK (MAX_TRACK-1) | 40 | #define MAX_TRACK_MASK (MAX_TRACK-1) |
diff --git a/firmware/export/thread.h b/firmware/export/thread.h index 867c587794..94c228b9ef 100644 --- a/firmware/export/thread.h +++ b/firmware/export/thread.h | |||
@@ -43,7 +43,7 @@ | |||
43 | #define PRIORITY_BACKGROUND 8 /* Normal application threads */ | 43 | #define PRIORITY_BACKGROUND 8 /* Normal application threads */ |
44 | 44 | ||
45 | #if CONFIG_CODEC == SWCODEC | 45 | #if CONFIG_CODEC == SWCODEC |
46 | #define MAXTHREADS 16 | 46 | #define MAXTHREADS 17 |
47 | #else | 47 | #else |
48 | #define MAXTHREADS 11 | 48 | #define MAXTHREADS 11 |
49 | #endif | 49 | #endif |