diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-04-27 03:08:23 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-04-27 03:08:23 +0000 |
commit | c537d5958e8b421ac4f9bef6c8b9e7425a6cf167 (patch) | |
tree | 7ed36518fb6524da7bbd913ba7619b85b5d15d23 | |
parent | dcf0f8de4a37ff1d2ea510aef75fa67977a8bdcc (diff) | |
download | rockbox-c537d5958e8b421ac4f9bef6c8b9e7425a6cf167.tar.gz rockbox-c537d5958e8b421ac4f9bef6c8b9e7425a6cf167.zip |
Commit FS#12069 - Playback rework - first stages. Gives as thorough as possible a treatment of codec management, track change and metadata logic as possible while maintaining fairly narrow focus and not rewriting everything all at once. Please see the rockbox-dev mail archive on 2011-04-25 (Playback engine rework) for a more thorough manifest of what was addressed. Plugins and codecs become incompatible.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29785 a1c6a512-1295-4272-9138-f99709370657
62 files changed, 5559 insertions, 3733 deletions
diff --git a/apps/appevents.h b/apps/appevents.h index fd578b90a2..a303491ae9 100644 --- a/apps/appevents.h +++ b/apps/appevents.h | |||
@@ -35,6 +35,7 @@ enum { | |||
35 | PLAYBACK_EVENT_TRACK_BUFFER, | 35 | PLAYBACK_EVENT_TRACK_BUFFER, |
36 | PLAYBACK_EVENT_TRACK_FINISH, | 36 | PLAYBACK_EVENT_TRACK_FINISH, |
37 | PLAYBACK_EVENT_TRACK_CHANGE, | 37 | PLAYBACK_EVENT_TRACK_CHANGE, |
38 | PLAYBACK_EVENT_TRACK_SKIP, | ||
38 | PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, | 39 | PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, |
39 | }; | 40 | }; |
40 | 41 | ||
diff --git a/apps/buffering.c b/apps/buffering.c index 578f0f261a..a130a787ff 100644 --- a/apps/buffering.c +++ b/apps/buffering.c | |||
@@ -58,7 +58,7 @@ | |||
58 | #define GUARD_BUFSIZE (32*1024) | 58 | #define GUARD_BUFSIZE (32*1024) |
59 | 59 | ||
60 | /* Define LOGF_ENABLE to enable logf output in this file */ | 60 | /* Define LOGF_ENABLE to enable logf output in this file */ |
61 | /*#define LOGF_ENABLE*/ | 61 | /* #define LOGF_ENABLE */ |
62 | #include "logf.h" | 62 | #include "logf.h" |
63 | 63 | ||
64 | /* macros to enable logf for queues | 64 | /* macros to enable logf for queues |
@@ -82,8 +82,6 @@ | |||
82 | #define LOGFQUEUE_SYS_TIMEOUT(...) | 82 | #define LOGFQUEUE_SYS_TIMEOUT(...) |
83 | #endif | 83 | #endif |
84 | 84 | ||
85 | /* default point to start buffer refill */ | ||
86 | #define BUFFERING_DEFAULT_WATERMARK (1024*128) | ||
87 | /* amount of data to read in one read() call */ | 85 | /* amount of data to read in one read() call */ |
88 | #define BUFFERING_DEFAULT_FILECHUNK (1024*32) | 86 | #define BUFFERING_DEFAULT_FILECHUNK (1024*32) |
89 | 87 | ||
@@ -94,6 +92,8 @@ | |||
94 | struct memory_handle { | 92 | struct memory_handle { |
95 | int id; /* A unique ID for the handle */ | 93 | int id; /* A unique ID for the handle */ |
96 | enum data_type type; /* Type of data buffered with this handle */ | 94 | enum data_type type; /* Type of data buffered with this handle */ |
95 | int8_t pinned; /* Count of references */ | ||
96 | int8_t signaled; /* Stop any attempt at waiting to get the data */ | ||
97 | char path[MAX_PATH]; /* Path if data originated in a file */ | 97 | char path[MAX_PATH]; /* Path if data originated in a file */ |
98 | int fd; /* File descriptor to path (-1 if closed) */ | 98 | int fd; /* File descriptor to path (-1 if closed) */ |
99 | size_t data; /* Start index of the handle's data buffer */ | 99 | size_t data; /* Start index of the handle's data buffer */ |
@@ -125,9 +125,7 @@ static volatile size_t buf_ridx; /* current reading position */ | |||
125 | 125 | ||
126 | /* Configuration */ | 126 | /* Configuration */ |
127 | static size_t conf_watermark = 0; /* Level to trigger filebuf fill */ | 127 | static size_t conf_watermark = 0; /* Level to trigger filebuf fill */ |
128 | #if MEMORYSIZE > 8 | ||
129 | static size_t high_watermark = 0; /* High watermark for rebuffer */ | 128 | static size_t high_watermark = 0; /* High watermark for rebuffer */ |
130 | #endif | ||
131 | 129 | ||
132 | /* current memory handle in the linked list. NULL when the list is empty. */ | 130 | /* current memory handle in the linked list. NULL when the list is empty. */ |
133 | static struct memory_handle *cur_handle; | 131 | static struct memory_handle *cur_handle; |
@@ -162,7 +160,6 @@ enum | |||
162 | Q_REBUFFER_HANDLE, /* Request reset and rebuffering of a handle at a new | 160 | Q_REBUFFER_HANDLE, /* Request reset and rebuffering of a handle at a new |
163 | file starting position. */ | 161 | file starting position. */ |
164 | Q_CLOSE_HANDLE, /* Request closing a handle */ | 162 | Q_CLOSE_HANDLE, /* Request closing a handle */ |
165 | Q_BASE_HANDLE, /* Set the reference handle for buf_useful_data */ | ||
166 | 163 | ||
167 | /* Configuration: */ | 164 | /* Configuration: */ |
168 | Q_START_FILL, /* Request that the buffering thread initiate a buffer | 165 | Q_START_FILL, /* Request that the buffering thread initiate a buffer |
@@ -222,6 +219,9 @@ static inline ssize_t ringbuf_add_cross(uintptr_t p1, size_t v, uintptr_t p2) | |||
222 | /* Bytes available in the buffer */ | 219 | /* Bytes available in the buffer */ |
223 | #define BUF_USED ringbuf_sub(buf_widx, buf_ridx) | 220 | #define BUF_USED ringbuf_sub(buf_widx, buf_ridx) |
224 | 221 | ||
222 | /* Real buffer watermark */ | ||
223 | #define BUF_WATERMARK MIN(conf_watermark, high_watermark) | ||
224 | |||
225 | /* | 225 | /* |
226 | LINKED LIST MANAGEMENT | 226 | LINKED LIST MANAGEMENT |
227 | ====================== | 227 | ====================== |
@@ -313,6 +313,12 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, | |||
313 | /* Prevent buffering thread from looking at it */ | 313 | /* Prevent buffering thread from looking at it */ |
314 | new_handle->filerem = 0; | 314 | new_handle->filerem = 0; |
315 | 315 | ||
316 | /* Handle can be moved by default */ | ||
317 | new_handle->pinned = 0; | ||
318 | |||
319 | /* Handle data can be waited for by default */ | ||
320 | new_handle->signaled = 0; | ||
321 | |||
316 | /* only advance the buffer write index of the size of the struct */ | 322 | /* only advance the buffer write index of the size of the struct */ |
317 | buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle)); | 323 | buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle)); |
318 | 324 | ||
@@ -364,6 +370,9 @@ static bool rm_handle(const struct memory_handle *h) | |||
364 | buf_widx = cur_handle->widx; | 370 | buf_widx = cur_handle->widx; |
365 | } | 371 | } |
366 | } else { | 372 | } else { |
373 | /* If we don't find ourselves, this is a seriously incoherent | ||
374 | state with a corrupted list and severe action is needed! */ | ||
375 | panicf("rm_handle fail: %d", h->id); | ||
367 | return false; | 376 | return false; |
368 | } | 377 | } |
369 | } | 378 | } |
@@ -385,8 +394,7 @@ static struct memory_handle *find_handle(int handle_id) | |||
385 | 394 | ||
386 | /* simple caching because most of the time the requested handle | 395 | /* simple caching because most of the time the requested handle |
387 | will either be the same as the last, or the one after the last */ | 396 | will either be the same as the last, or the one after the last */ |
388 | if (cached_handle) | 397 | if (cached_handle) { |
389 | { | ||
390 | if (cached_handle->id == handle_id) { | 398 | if (cached_handle->id == handle_id) { |
391 | return cached_handle; | 399 | return cached_handle; |
392 | } else if (cached_handle->next && | 400 | } else if (cached_handle->next && |
@@ -618,20 +626,22 @@ static void update_data_counters(struct data_counters *dc) | |||
618 | static inline bool buffer_is_low(void) | 626 | static inline bool buffer_is_low(void) |
619 | { | 627 | { |
620 | update_data_counters(NULL); | 628 | update_data_counters(NULL); |
621 | return data_counters.useful < (conf_watermark / 2); | 629 | return data_counters.useful < BUF_WATERMARK / 2; |
622 | } | 630 | } |
623 | 631 | ||
624 | /* Q_BUFFER_HANDLE event and buffer data for the given handle. | 632 | /* Q_BUFFER_HANDLE event and buffer data for the given handle. |
625 | Return whether or not the buffering should continue explicitly. */ | 633 | Return whether or not the buffering should continue explicitly. */ |
626 | static bool buffer_handle(int handle_id, size_t to_buffer) | 634 | static bool buffer_handle(int handle_id, size_t to_buffer) |
627 | { | 635 | { |
628 | logf("buffer_handle(%d)", handle_id); | 636 | logf("buffer_handle(%d, %lu)", handle_id, (unsigned long)to_buffer); |
629 | struct memory_handle *h = find_handle(handle_id); | 637 | struct memory_handle *h = find_handle(handle_id); |
630 | bool stop = false; | 638 | bool stop = false; |
631 | 639 | ||
632 | if (!h) | 640 | if (!h) |
633 | return true; | 641 | return true; |
634 | 642 | ||
643 | logf(" type: %d", (int)h->type); | ||
644 | |||
635 | if (h->filerem == 0) { | 645 | if (h->filerem == 0) { |
636 | /* nothing left to buffer */ | 646 | /* nothing left to buffer */ |
637 | return true; | 647 | return true; |
@@ -659,13 +669,13 @@ static bool buffer_handle(int handle_id, size_t to_buffer) | |||
659 | if (!get_metadata((struct mp3entry *)(buffer + h->data), | 669 | if (!get_metadata((struct mp3entry *)(buffer + h->data), |
660 | h->fd, h->path)) { | 670 | h->fd, h->path)) { |
661 | /* metadata parsing failed: clear the buffer. */ | 671 | /* metadata parsing failed: clear the buffer. */ |
662 | memset(buffer + h->data, 0, sizeof(struct mp3entry)); | 672 | wipe_mp3entry((struct mp3entry *)(buffer + h->data)); |
663 | } | 673 | } |
664 | close(h->fd); | 674 | close(h->fd); |
665 | h->fd = -1; | 675 | h->fd = -1; |
666 | h->filerem = 0; | 676 | h->filerem = 0; |
667 | h->available = sizeof(struct mp3entry); | 677 | h->available = sizeof(struct mp3entry); |
668 | h->widx += sizeof(struct mp3entry); | 678 | h->widx = ringbuf_add(h->widx, sizeof(struct mp3entry)); |
669 | send_event(BUFFER_EVENT_FINISHED, &handle_id); | 679 | send_event(BUFFER_EVENT_FINISHED, &handle_id); |
670 | return true; | 680 | return true; |
671 | } | 681 | } |
@@ -698,7 +708,7 @@ static bool buffer_handle(int handle_id, size_t to_buffer) | |||
698 | break; | 708 | break; |
699 | } | 709 | } |
700 | 710 | ||
701 | DEBUGF("File ended %ld bytes early\n", (long)h->filerem); | 711 | logf("File ended %ld bytes early\n", (long)h->filerem); |
702 | h->filesize -= h->filerem; | 712 | h->filesize -= h->filerem; |
703 | h->filerem = 0; | 713 | h->filerem = 0; |
704 | break; | 714 | break; |
@@ -770,22 +780,31 @@ static bool close_handle(int handle_id) | |||
770 | part of its data buffer or by moving all the data. */ | 780 | part of its data buffer or by moving all the data. */ |
771 | static void shrink_handle(struct memory_handle *h) | 781 | static void shrink_handle(struct memory_handle *h) |
772 | { | 782 | { |
773 | size_t delta; | ||
774 | |||
775 | if (!h) | 783 | if (!h) |
776 | return; | 784 | return; |
777 | 785 | ||
778 | if (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET || | 786 | if (h->type == TYPE_PACKET_AUDIO) { |
779 | h->type == TYPE_BITMAP || h->type == TYPE_CODEC || | 787 | /* only move the handle struct */ |
780 | h->type == TYPE_ATOMIC_AUDIO) | 788 | /* data is pinned by default - if we start moving packet audio, |
781 | { | 789 | the semantics will determine whether or not data is movable |
790 | but the handle will remain movable in either case */ | ||
791 | size_t delta = ringbuf_sub(h->ridx, h->data); | ||
792 | |||
793 | /* The value of delta might change for alignment reasons */ | ||
794 | if (!move_handle(&h, &delta, 0, true)) | ||
795 | return; | ||
796 | |||
797 | h->data = ringbuf_add(h->data, delta); | ||
798 | h->available -= delta; | ||
799 | h->offset += delta; | ||
800 | } else { | ||
782 | /* metadata handle: we can move all of it */ | 801 | /* metadata handle: we can move all of it */ |
783 | if (!h->next || h->filerem != 0) | 802 | if (h->pinned || !h->next || h->filerem != 0) |
784 | return; /* Last handle or not finished loading */ | 803 | return; /* Pinned, last handle or not finished loading */ |
785 | 804 | ||
786 | uintptr_t handle_distance = | 805 | uintptr_t handle_distance = |
787 | ringbuf_sub(ringbuf_offset(h->next), h->data); | 806 | ringbuf_sub(ringbuf_offset(h->next), h->data); |
788 | delta = handle_distance - h->available; | 807 | size_t delta = handle_distance - h->available; |
789 | 808 | ||
790 | /* The value of delta might change for alignment reasons */ | 809 | /* The value of delta might change for alignment reasons */ |
791 | if (!move_handle(&h, &delta, h->available, h->type==TYPE_CODEC)) | 810 | if (!move_handle(&h, &delta, h->available, h->type==TYPE_CODEC)) |
@@ -806,15 +825,6 @@ static void shrink_handle(struct memory_handle *h) | |||
806 | struct bitmap *bmp = (struct bitmap *)&buffer[h->data]; | 825 | struct bitmap *bmp = (struct bitmap *)&buffer[h->data]; |
807 | bmp->data = &buffer[h->data + sizeof(struct bitmap)]; | 826 | bmp->data = &buffer[h->data + sizeof(struct bitmap)]; |
808 | } | 827 | } |
809 | } else { | ||
810 | /* only move the handle struct */ | ||
811 | delta = ringbuf_sub(h->ridx, h->data); | ||
812 | if (!move_handle(&h, &delta, 0, true)) | ||
813 | return; | ||
814 | |||
815 | h->data = ringbuf_add(h->data, delta); | ||
816 | h->available -= delta; | ||
817 | h->offset += delta; | ||
818 | } | 828 | } |
819 | } | 829 | } |
820 | 830 | ||
@@ -962,6 +972,8 @@ int bufopen(const char *file, size_t offset, enum data_type type, | |||
962 | mutex_unlock(&llist_mutex); | 972 | mutex_unlock(&llist_mutex); |
963 | return handle_id; | 973 | return handle_id; |
964 | } | 974 | } |
975 | else if (type == TYPE_UNKNOWN) | ||
976 | return ERR_UNSUPPORTED_TYPE; | ||
965 | #ifdef APPLICATION | 977 | #ifdef APPLICATION |
966 | /* loading code from memory is not supported in application builds */ | 978 | /* loading code from memory is not supported in application builds */ |
967 | else if (type == TYPE_CODEC) | 979 | else if (type == TYPE_CODEC) |
@@ -1083,7 +1095,12 @@ int bufopen(const char *file, size_t offset, enum data_type type, | |||
1083 | */ | 1095 | */ |
1084 | int bufalloc(const void *src, size_t size, enum data_type type) | 1096 | int bufalloc(const void *src, size_t size, enum data_type type) |
1085 | { | 1097 | { |
1086 | int handle_id = ERR_BUFFER_FULL; | 1098 | int handle_id; |
1099 | |||
1100 | if (type == TYPE_UNKNOWN) | ||
1101 | return ERR_UNSUPPORTED_TYPE; | ||
1102 | |||
1103 | handle_id = ERR_BUFFER_FULL; | ||
1087 | 1104 | ||
1088 | mutex_lock(&llist_mutex); | 1105 | mutex_lock(&llist_mutex); |
1089 | 1106 | ||
@@ -1124,7 +1141,14 @@ int bufalloc(const void *src, size_t size, enum data_type type) | |||
1124 | bool bufclose(int handle_id) | 1141 | bool bufclose(int handle_id) |
1125 | { | 1142 | { |
1126 | logf("bufclose(%d)", handle_id); | 1143 | logf("bufclose(%d)", handle_id); |
1127 | 1144 | #if 0 | |
1145 | /* Don't interrupt the buffering thread if the handle is already | ||
1146 | stale */ | ||
1147 | if (!find_handle(handle_id)) { | ||
1148 | logf(" handle already closed"); | ||
1149 | return true; | ||
1150 | } | ||
1151 | #endif | ||
1128 | LOGFQUEUE("buffering >| Q_CLOSE_HANDLE %d", handle_id); | 1152 | LOGFQUEUE("buffering >| Q_CLOSE_HANDLE %d", handle_id); |
1129 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); | 1153 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); |
1130 | } | 1154 | } |
@@ -1236,9 +1260,10 @@ static int seek_handle(struct memory_handle *h, size_t newpos) | |||
1236 | 1260 | ||
1237 | /* Set reading index in handle (relatively to the start of the file). | 1261 | /* Set reading index in handle (relatively to the start of the file). |
1238 | Access before the available data will trigger a rebuffer. | 1262 | Access before the available data will trigger a rebuffer. |
1239 | Return 0 for success and < 0 for failure: | 1263 | Return 0 for success and for failure: |
1240 | -1 if the handle wasn't found | 1264 | ERR_HANDLE_NOT_FOUND if the handle wasn't found |
1241 | -2 if the new requested position was beyond the end of the file | 1265 | ERR_INVALID_VALUE if the new requested position was beyond the end of |
1266 | the file | ||
1242 | */ | 1267 | */ |
1243 | int bufseek(int handle_id, size_t newpos) | 1268 | int bufseek(int handle_id, size_t newpos) |
1244 | { | 1269 | { |
@@ -1250,7 +1275,11 @@ int bufseek(int handle_id, size_t newpos) | |||
1250 | } | 1275 | } |
1251 | 1276 | ||
1252 | /* Advance the reading index in a handle (relatively to its current position). | 1277 | /* Advance the reading index in a handle (relatively to its current position). |
1253 | Return 0 for success and < 0 for failure */ | 1278 | Return 0 for success and for failure: |
1279 | ERR_HANDLE_NOT_FOUND if the handle wasn't found | ||
1280 | ERR_INVALID_VALUE if the new requested position was beyond the end of | ||
1281 | the file | ||
1282 | */ | ||
1254 | int bufadvance(int handle_id, off_t offset) | 1283 | int bufadvance(int handle_id, off_t offset) |
1255 | { | 1284 | { |
1256 | struct memory_handle *h = find_handle(handle_id); | 1285 | struct memory_handle *h = find_handle(handle_id); |
@@ -1261,6 +1290,18 @@ int bufadvance(int handle_id, off_t offset) | |||
1261 | return seek_handle(h, newpos); | 1290 | return seek_handle(h, newpos); |
1262 | } | 1291 | } |
1263 | 1292 | ||
1293 | /* Get the read position from the start of the file | ||
1294 | Returns the offset from byte 0 of the file and for failure: | ||
1295 | ERR_HANDLE_NOT_FOUND if the handle wasn't found | ||
1296 | */ | ||
1297 | off_t bufftell(int handle_id) | ||
1298 | { | ||
1299 | const struct memory_handle *h = find_handle(handle_id); | ||
1300 | if (!h) | ||
1301 | return ERR_HANDLE_NOT_FOUND; | ||
1302 | return h->offset + ringbuf_sub(h->ridx, h->data); | ||
1303 | } | ||
1304 | |||
1264 | /* Used by bufread and bufgetdata to prepare the buffer and retrieve the | 1305 | /* Used by bufread and bufgetdata to prepare the buffer and retrieve the |
1265 | * actual amount of data available for reading. This function explicitly | 1306 | * actual amount of data available for reading. This function explicitly |
1266 | * does not check the validity of the input handle. It does do range checks | 1307 | * does not check the validity of the input handle. It does do range checks |
@@ -1306,7 +1347,7 @@ static struct memory_handle *prep_bufdata(int handle_id, size_t *size, | |||
1306 | /* it is not safe for a non-buffering thread to sleep while | 1347 | /* it is not safe for a non-buffering thread to sleep while |
1307 | * holding a handle */ | 1348 | * holding a handle */ |
1308 | h = find_handle(handle_id); | 1349 | h = find_handle(handle_id); |
1309 | if (!h) | 1350 | if (!h || h->signaled != 0) |
1310 | return NULL; | 1351 | return NULL; |
1311 | avail = handle_size_available(h); | 1352 | avail = handle_size_available(h); |
1312 | } | 1353 | } |
@@ -1447,9 +1488,14 @@ SECONDARY EXPORTED FUNCTIONS | |||
1447 | buf_handle_offset | 1488 | buf_handle_offset |
1448 | buf_request_buffer_handle | 1489 | buf_request_buffer_handle |
1449 | buf_set_base_handle | 1490 | buf_set_base_handle |
1491 | buf_handle_data_type | ||
1492 | buf_is_handle | ||
1493 | buf_pin_handle | ||
1494 | buf_signal_handle | ||
1495 | buf_length | ||
1450 | buf_used | 1496 | buf_used |
1451 | register_buffering_callback | 1497 | buf_set_watermark |
1452 | unregister_buffering_callback | 1498 | buf_get_watermark |
1453 | 1499 | ||
1454 | These functions are exported, to allow interaction with the buffer. | 1500 | These functions are exported, to allow interaction with the buffer. |
1455 | They take care of the content of the structs, and rely on the linked list | 1501 | They take care of the content of the structs, and rely on the linked list |
@@ -1472,8 +1518,61 @@ void buf_request_buffer_handle(int handle_id) | |||
1472 | 1518 | ||
1473 | void buf_set_base_handle(int handle_id) | 1519 | void buf_set_base_handle(int handle_id) |
1474 | { | 1520 | { |
1475 | LOGFQUEUE("buffering > Q_BASE_HANDLE %d", handle_id); | 1521 | mutex_lock(&llist_mutex); |
1476 | queue_post(&buffering_queue, Q_BASE_HANDLE, handle_id); | 1522 | base_handle_id = handle_id; |
1523 | mutex_unlock(&llist_mutex); | ||
1524 | } | ||
1525 | |||
1526 | enum data_type buf_handle_data_type(int handle_id) | ||
1527 | { | ||
1528 | const struct memory_handle *h = find_handle(handle_id); | ||
1529 | if (!h) | ||
1530 | return TYPE_UNKNOWN; | ||
1531 | return h->type; | ||
1532 | } | ||
1533 | |||
1534 | ssize_t buf_handle_remaining(int handle_id) | ||
1535 | { | ||
1536 | const struct memory_handle *h = find_handle(handle_id); | ||
1537 | if (!h) | ||
1538 | return ERR_HANDLE_NOT_FOUND; | ||
1539 | return h->filerem; | ||
1540 | } | ||
1541 | |||
1542 | bool buf_is_handle(int handle_id) | ||
1543 | { | ||
1544 | return find_handle(handle_id) != NULL; | ||
1545 | } | ||
1546 | |||
1547 | bool buf_pin_handle(int handle_id, bool pin) | ||
1548 | { | ||
1549 | struct memory_handle *h = find_handle(handle_id); | ||
1550 | if (!h) | ||
1551 | return false; | ||
1552 | |||
1553 | if (pin) { | ||
1554 | h->pinned++; | ||
1555 | } else if (h->pinned > 0) { | ||
1556 | h->pinned--; | ||
1557 | } | ||
1558 | |||
1559 | return true; | ||
1560 | } | ||
1561 | |||
1562 | bool buf_signal_handle(int handle_id, bool signal) | ||
1563 | { | ||
1564 | struct memory_handle *h = find_handle(handle_id); | ||
1565 | if (!h) | ||
1566 | return false; | ||
1567 | |||
1568 | h->signaled = signal ? 1 : 0; | ||
1569 | return true; | ||
1570 | } | ||
1571 | |||
1572 | /* Return the size of the ringbuffer */ | ||
1573 | size_t buf_length(void) | ||
1574 | { | ||
1575 | return buffer_len; | ||
1477 | } | 1576 | } |
1478 | 1577 | ||
1479 | /* Return the amount of buffer space used */ | 1578 | /* Return the amount of buffer space used */ |
@@ -1487,6 +1586,21 @@ void buf_set_watermark(size_t bytes) | |||
1487 | conf_watermark = bytes; | 1586 | conf_watermark = bytes; |
1488 | } | 1587 | } |
1489 | 1588 | ||
1589 | size_t buf_get_watermark(void) | ||
1590 | { | ||
1591 | return BUF_WATERMARK; | ||
1592 | } | ||
1593 | |||
1594 | #ifdef HAVE_IO_PRIORITY | ||
1595 | void buf_back_off_storage(bool back_off) | ||
1596 | { | ||
1597 | int priority = back_off ? | ||
1598 | IO_PRIORITY_BACKGROUND : IO_PRIORITY_IMMEDIATE; | ||
1599 | thread_set_io_priority(buffering_thread_id, priority); | ||
1600 | } | ||
1601 | #endif | ||
1602 | |||
1603 | /** -- buffer thread helpers -- **/ | ||
1490 | static void shrink_buffer_inner(struct memory_handle *h) | 1604 | static void shrink_buffer_inner(struct memory_handle *h) |
1491 | { | 1605 | { |
1492 | if (h == NULL) | 1606 | if (h == NULL) |
@@ -1503,7 +1617,7 @@ static void shrink_buffer(void) | |||
1503 | shrink_buffer_inner(first_handle); | 1617 | shrink_buffer_inner(first_handle); |
1504 | } | 1618 | } |
1505 | 1619 | ||
1506 | void buffering_thread(void) | 1620 | static void NORETURN_ATTR buffering_thread(void) |
1507 | { | 1621 | { |
1508 | bool filling = false; | 1622 | bool filling = false; |
1509 | struct queue_event ev; | 1623 | struct queue_event ev; |
@@ -1511,19 +1625,21 @@ void buffering_thread(void) | |||
1511 | 1625 | ||
1512 | while (true) | 1626 | while (true) |
1513 | { | 1627 | { |
1514 | if (!filling) { | 1628 | if (num_handles > 0) { |
1629 | if (!filling) { | ||
1630 | cancel_cpu_boost(); | ||
1631 | } | ||
1632 | queue_wait_w_tmo(&buffering_queue, &ev, filling ? 1 : HZ/2); | ||
1633 | } else { | ||
1634 | filling = false; | ||
1515 | cancel_cpu_boost(); | 1635 | cancel_cpu_boost(); |
1636 | queue_wait(&buffering_queue, &ev); | ||
1516 | } | 1637 | } |
1517 | 1638 | ||
1518 | queue_wait_w_tmo(&buffering_queue, &ev, filling ? 5 : HZ/2); | ||
1519 | |||
1520 | switch (ev.id) | 1639 | switch (ev.id) |
1521 | { | 1640 | { |
1522 | case Q_START_FILL: | 1641 | case Q_START_FILL: |
1523 | LOGFQUEUE("buffering < Q_START_FILL %d", (int)ev.data); | 1642 | LOGFQUEUE("buffering < Q_START_FILL %d", (int)ev.data); |
1524 | /* Call buffer callbacks here because this is one of two ways | ||
1525 | * to begin a full buffer fill */ | ||
1526 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); | ||
1527 | shrink_buffer(); | 1643 | shrink_buffer(); |
1528 | queue_reply(&buffering_queue, 1); | 1644 | queue_reply(&buffering_queue, 1); |
1529 | filling |= buffer_handle((int)ev.data, 0); | 1645 | filling |= buffer_handle((int)ev.data, 0); |
@@ -1553,36 +1669,21 @@ void buffering_thread(void) | |||
1553 | filling = true; | 1669 | filling = true; |
1554 | break; | 1670 | break; |
1555 | 1671 | ||
1556 | case Q_BASE_HANDLE: | ||
1557 | LOGFQUEUE("buffering < Q_BASE_HANDLE %d", (int)ev.data); | ||
1558 | base_handle_id = (int)ev.data; | ||
1559 | break; | ||
1560 | |||
1561 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
1562 | case SYS_USB_CONNECTED: | ||
1563 | LOGFQUEUE("buffering < SYS_USB_CONNECTED"); | ||
1564 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
1565 | usb_wait_for_disconnect(&buffering_queue); | ||
1566 | break; | ||
1567 | #endif | ||
1568 | |||
1569 | case SYS_TIMEOUT: | 1672 | case SYS_TIMEOUT: |
1570 | LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT"); | 1673 | LOGFQUEUE_SYS_TIMEOUT("buffering < SYS_TIMEOUT"); |
1571 | break; | 1674 | break; |
1572 | } | 1675 | } |
1573 | 1676 | ||
1574 | update_data_counters(NULL); | 1677 | if (num_handles == 0 || !queue_empty(&buffering_queue)) |
1575 | 1678 | continue; | |
1576 | /* If the buffer is low, call the callbacks to get new data */ | ||
1577 | if (num_handles > 0 && data_counters.useful <= conf_watermark) | ||
1578 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); | ||
1579 | 1679 | ||
1680 | update_data_counters(NULL); | ||
1580 | #if 0 | 1681 | #if 0 |
1581 | /* TODO: This needs to be fixed to use the idle callback, disable it | 1682 | /* TODO: This needs to be fixed to use the idle callback, disable it |
1582 | * for simplicity until its done right */ | 1683 | * for simplicity until its done right */ |
1583 | #if MEMORYSIZE > 8 | 1684 | #if MEMORYSIZE > 8 |
1584 | /* If the disk is spinning, take advantage by filling the buffer */ | 1685 | /* If the disk is spinning, take advantage by filling the buffer */ |
1585 | else if (storage_disk_is_active() && queue_empty(&buffering_queue)) { | 1686 | else if (storage_disk_is_active()) { |
1586 | if (num_handles > 0 && data_counters.useful <= high_watermark) | 1687 | if (num_handles > 0 && data_counters.useful <= high_watermark) |
1587 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); | 1688 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); |
1588 | 1689 | ||
@@ -1597,15 +1698,23 @@ void buffering_thread(void) | |||
1597 | #endif | 1698 | #endif |
1598 | #endif | 1699 | #endif |
1599 | 1700 | ||
1600 | if (queue_empty(&buffering_queue)) { | 1701 | if (filling) { |
1601 | if (filling) { | 1702 | if (data_counters.remaining > 0 && BUF_USED < buffer_len) { |
1602 | if (data_counters.remaining > 0 && BUF_USED < buffer_len) | 1703 | filling = fill_buffer(); |
1603 | filling = fill_buffer(); | 1704 | } |
1604 | else if (data_counters.remaining == 0) | 1705 | else if (data_counters.remaining == 0) { |
1605 | filling = false; | 1706 | filling = false; |
1606 | } else if (ev.id == SYS_TIMEOUT) { | 1707 | } |
1607 | if (data_counters.remaining > 0 && | 1708 | } else if (ev.id == SYS_TIMEOUT) { |
1608 | data_counters.useful <= conf_watermark) { | 1709 | if (data_counters.useful < BUF_WATERMARK) { |
1710 | /* The buffer is low and we're idle, just watching the levels | ||
1711 | - call the callbacks to get new data */ | ||
1712 | send_event(BUFFER_EVENT_BUFFER_LOW, NULL); | ||
1713 | |||
1714 | /* Continue anything else we haven't finished - it might | ||
1715 | get booted off or stop early because the receiver hasn't | ||
1716 | had a chance to clear anything yet */ | ||
1717 | if (data_counters.remaining > 0) { | ||
1609 | shrink_buffer(); | 1718 | shrink_buffer(); |
1610 | filling = fill_buffer(); | 1719 | filling = fill_buffer(); |
1611 | } | 1720 | } |
@@ -1618,9 +1727,14 @@ void buffering_init(void) | |||
1618 | { | 1727 | { |
1619 | mutex_init(&llist_mutex); | 1728 | mutex_init(&llist_mutex); |
1620 | 1729 | ||
1621 | conf_watermark = BUFFERING_DEFAULT_WATERMARK; | 1730 | /* Thread should absolutely not respond to USB because if it waits first, |
1622 | 1731 | then it cannot properly service the handles and leaks will happen - | |
1623 | queue_init(&buffering_queue, true); | 1732 | this is a worker thread and shouldn't need to care about any system |
1733 | notifications. | ||
1734 | *** | ||
1735 | Whoever is using buffering should be responsible enough to clear all | ||
1736 | the handles at the right time. */ | ||
1737 | queue_init(&buffering_queue, false); | ||
1624 | buffering_thread_id = create_thread( buffering_thread, buffering_stack, | 1738 | buffering_thread_id = create_thread( buffering_thread, buffering_stack, |
1625 | sizeof(buffering_stack), CREATE_THREAD_FROZEN, | 1739 | sizeof(buffering_stack), CREATE_THREAD_FROZEN, |
1626 | buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING) | 1740 | buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING) |
@@ -1636,6 +1750,9 @@ bool buffering_reset(char *buf, size_t buflen) | |||
1636 | /* Wraps of storage-aligned data must also be storage aligned, | 1750 | /* Wraps of storage-aligned data must also be storage aligned, |
1637 | thus buf and buflen must be a aligned to an integer multiple of | 1751 | thus buf and buflen must be a aligned to an integer multiple of |
1638 | the storage alignment */ | 1752 | the storage alignment */ |
1753 | |||
1754 | buflen -= GUARD_BUFSIZE; | ||
1755 | |||
1639 | STORAGE_ALIGN_BUFFER(buf, buflen); | 1756 | STORAGE_ALIGN_BUFFER(buf, buflen); |
1640 | 1757 | ||
1641 | if (!buf || !buflen) | 1758 | if (!buf || !buflen) |
@@ -1654,10 +1771,13 @@ bool buffering_reset(char *buf, size_t buflen) | |||
1654 | num_handles = 0; | 1771 | num_handles = 0; |
1655 | base_handle_id = -1; | 1772 | base_handle_id = -1; |
1656 | 1773 | ||
1657 | /* Set the high watermark as 75% full...or 25% empty :) */ | 1774 | /* Set the high watermark as 75% full...or 25% empty :) |
1658 | #if MEMORYSIZE > 8 | 1775 | This is the greatest fullness that will trigger low-buffer events |
1776 | no matter what the setting because high-bitrate files can have | ||
1777 | ludicrous margins that even exceed the buffer size - most common | ||
1778 | with a huge anti-skip buffer but even without that setting, | ||
1779 | staying constantly active in buffering is pointless */ | ||
1659 | high_watermark = 3*buflen / 4; | 1780 | high_watermark = 3*buflen / 4; |
1660 | #endif | ||
1661 | 1781 | ||
1662 | thread_thaw(buffering_thread_id); | 1782 | thread_thaw(buffering_thread_id); |
1663 | 1783 | ||
@@ -1673,5 +1793,5 @@ void buffering_get_debugdata(struct buffering_debug *dbgdata) | |||
1673 | dbgdata->wasted_space = dc.wasted; | 1793 | dbgdata->wasted_space = dc.wasted; |
1674 | dbgdata->buffered_data = dc.buffered; | 1794 | dbgdata->buffered_data = dc.buffered; |
1675 | dbgdata->useful_data = dc.useful; | 1795 | dbgdata->useful_data = dc.useful; |
1676 | dbgdata->watermark = conf_watermark; | 1796 | dbgdata->watermark = BUF_WATERMARK; |
1677 | } | 1797 | } |
diff --git a/apps/buffering.h b/apps/buffering.h index 34d6d641c7..2e4cfd3968 100644 --- a/apps/buffering.h +++ b/apps/buffering.h | |||
@@ -28,14 +28,13 @@ | |||
28 | 28 | ||
29 | 29 | ||
30 | enum data_type { | 30 | enum data_type { |
31 | TYPE_UNKNOWN = 0, /* invalid type indicator */ | ||
32 | TYPE_ID3, | ||
31 | TYPE_CODEC, | 33 | TYPE_CODEC, |
32 | TYPE_PACKET_AUDIO, | 34 | TYPE_PACKET_AUDIO, |
33 | TYPE_ATOMIC_AUDIO, | 35 | TYPE_ATOMIC_AUDIO, |
34 | TYPE_ID3, | ||
35 | TYPE_CUESHEET, | 36 | TYPE_CUESHEET, |
36 | TYPE_BITMAP, | 37 | TYPE_BITMAP, |
37 | TYPE_BUFFER, | ||
38 | TYPE_UNKNOWN, | ||
39 | }; | 38 | }; |
40 | 39 | ||
41 | /* Error return values */ | 40 | /* Error return values */ |
@@ -63,6 +62,7 @@ bool buffering_reset(char *buf, size_t buflen); | |||
63 | * bufclose : Close an open handle | 62 | * bufclose : Close an open handle |
64 | * bufseek : Set handle reading index, relatively to the start of the file | 63 | * bufseek : Set handle reading index, relatively to the start of the file |
65 | * bufadvance: Move handle reading index, relatively to current position | 64 | * bufadvance: Move handle reading index, relatively to current position |
65 | * bufftell : Return the handle's file read position | ||
66 | * bufread : Copy data from a handle to a buffer | 66 | * bufread : Copy data from a handle to a buffer |
67 | * bufgetdata: Obtain a pointer for linear access to a "size" amount of data | 67 | * bufgetdata: Obtain a pointer for linear access to a "size" amount of data |
68 | * bufgettail: Out-of-band get the last size bytes of a handle. | 68 | * bufgettail: Out-of-band get the last size bytes of a handle. |
@@ -81,28 +81,40 @@ int bufalloc(const void *src, size_t size, enum data_type type); | |||
81 | bool bufclose(int handle_id); | 81 | bool bufclose(int handle_id); |
82 | int bufseek(int handle_id, size_t newpos); | 82 | int bufseek(int handle_id, size_t newpos); |
83 | int bufadvance(int handle_id, off_t offset); | 83 | int bufadvance(int handle_id, off_t offset); |
84 | off_t bufftell(int handle_id); | ||
84 | ssize_t bufread(int handle_id, size_t size, void *dest); | 85 | ssize_t bufread(int handle_id, size_t size, void *dest); |
85 | ssize_t bufgetdata(int handle_id, size_t size, void **data); | 86 | ssize_t bufgetdata(int handle_id, size_t size, void **data); |
86 | ssize_t bufgettail(int handle_id, size_t size, void **data); | 87 | ssize_t bufgettail(int handle_id, size_t size, void **data); |
87 | ssize_t bufcuttail(int handle_id, size_t size); | 88 | ssize_t bufcuttail(int handle_id, size_t size); |
88 | 89 | ||
89 | |||
90 | /*************************************************************************** | 90 | /*************************************************************************** |
91 | * SECONDARY FUNCTIONS | 91 | * SECONDARY FUNCTIONS |
92 | * =================== | 92 | * =================== |
93 | * | 93 | * |
94 | * buf_handle_data_type: return the handle's data type | ||
95 | * buf_is_handle: is the handle valid? | ||
96 | * buf_pin_handle: Disallow/allow handle movement. Handle may still be removed. | ||
94 | * buf_handle_offset: Get the offset of the first buffered byte from the file | 97 | * buf_handle_offset: Get the offset of the first buffered byte from the file |
95 | * buf_request_buffer_handle: Request buffering of a handle | 98 | * buf_request_buffer_handle: Request buffering of a handle |
96 | * buf_set_base_handle: Tell the buffering thread which handle is currently read | 99 | * buf_set_base_handle: Tell the buffering thread which handle is currently read |
100 | * buf_length: Total size of ringbuffer | ||
97 | * buf_used: Total amount of buffer space used (including allocated space) | 101 | * buf_used: Total amount of buffer space used (including allocated space) |
102 | * buf_back_off_storage: tell buffering thread to take it easy | ||
98 | ****************************************************************************/ | 103 | ****************************************************************************/ |
99 | 104 | ||
105 | enum data_type buf_handle_data_type(int handle_id); | ||
106 | ssize_t buf_handle_remaining(int handle_id); | ||
107 | bool buf_is_handle(int handle_id); | ||
100 | ssize_t buf_handle_offset(int handle_id); | 108 | ssize_t buf_handle_offset(int handle_id); |
101 | void buf_request_buffer_handle(int handle_id); | 109 | void buf_request_buffer_handle(int handle_id); |
102 | void buf_set_base_handle(int handle_id); | 110 | void buf_set_base_handle(int handle_id); |
111 | size_t buf_length(void); | ||
103 | size_t buf_used(void); | 112 | size_t buf_used(void); |
104 | 113 | bool buf_pin_handle(int handle_id, bool pin); | |
105 | 114 | bool buf_signal_handle(int handle_id, bool signal); | |
115 | #ifdef HAVE_IO_PRIORITY | ||
116 | void buf_back_off_storage(bool back_off); | ||
117 | #endif | ||
106 | 118 | ||
107 | /* Settings */ | 119 | /* Settings */ |
108 | enum { | 120 | enum { |
@@ -110,6 +122,7 @@ enum { | |||
110 | BUFFERING_SET_CHUNKSIZE, | 122 | BUFFERING_SET_CHUNKSIZE, |
111 | }; | 123 | }; |
112 | void buf_set_watermark(size_t bytes); | 124 | void buf_set_watermark(size_t bytes); |
125 | size_t buf_get_watermark(void); | ||
113 | 126 | ||
114 | /* Debugging */ | 127 | /* Debugging */ |
115 | struct buffering_debug { | 128 | struct buffering_debug { |
diff --git a/apps/codec_thread.c b/apps/codec_thread.c index 65a7ebc7d5..7cf45c3490 100644 --- a/apps/codec_thread.c +++ b/apps/codec_thread.c | |||
@@ -9,6 +9,7 @@ | |||
9 | * | 9 | * |
10 | * Copyright (C) 2005-2007 Miika Pekkarinen | 10 | * Copyright (C) 2005-2007 Miika Pekkarinen |
11 | * Copyright (C) 2007-2008 Nicolas Pennequin | 11 | * Copyright (C) 2007-2008 Nicolas Pennequin |
12 | * Copyright (C) 2011 Michael Sevakis | ||
12 | * | 13 | * |
13 | * This program is free software; you can redistribute it and/or | 14 | * This program is free software; you can redistribute it and/or |
14 | * modify it under the terms of the GNU General Public License | 15 | * modify it under the terms of the GNU General Public License |
@@ -21,16 +22,14 @@ | |||
21 | ****************************************************************************/ | 22 | ****************************************************************************/ |
22 | #include "config.h" | 23 | #include "config.h" |
23 | #include "system.h" | 24 | #include "system.h" |
24 | #include "playback.h" | ||
25 | #include "codec_thread.h" | ||
26 | #include "kernel.h" | 25 | #include "kernel.h" |
27 | #include "codecs.h" | 26 | #include "codecs.h" |
28 | #include "buffering.h" | 27 | #include "codec_thread.h" |
29 | #include "pcmbuf.h" | 28 | #include "pcmbuf.h" |
29 | #include "playback.h" | ||
30 | #include "buffering.h" | ||
30 | #include "dsp.h" | 31 | #include "dsp.h" |
31 | #include "abrepeat.h" | ||
32 | #include "metadata.h" | 32 | #include "metadata.h" |
33 | #include "splash.h" | ||
34 | 33 | ||
35 | /* Define LOGF_ENABLE to enable logf output in this file */ | 34 | /* Define LOGF_ENABLE to enable logf output in this file */ |
36 | /*#define LOGF_ENABLE*/ | 35 | /*#define LOGF_ENABLE*/ |
@@ -57,38 +56,45 @@ | |||
57 | #define LOGFQUEUE_SYS_TIMEOUT(...) | 56 | #define LOGFQUEUE_SYS_TIMEOUT(...) |
58 | #endif | 57 | #endif |
59 | 58 | ||
60 | |||
61 | /* Variables are commented with the threads that use them: | 59 | /* Variables are commented with the threads that use them: |
62 | * A=audio, C=codec, V=voice. A suffix of - indicates that | 60 | * A=audio, C=codec |
63 | * the variable is read but not updated on that thread. | 61 | * - = reads only |
64 | 62 | * | |
65 | * Unless otherwise noted, the extern variables are located | 63 | * Unless otherwise noted, the extern variables are located |
66 | * in playback.c. | 64 | * in playback.c. |
67 | */ | 65 | */ |
68 | 66 | ||
69 | /* Main state control */ | 67 | /* Q_LOAD_CODEC parameter data */ |
70 | 68 | struct codec_load_info | |
71 | /* Type of codec loaded? (C/A) */ | 69 | { |
72 | static int current_codectype SHAREDBSS_ATTR = AFMT_UNKNOWN; | 70 | int hid; /* audio handle id (specify < 0 to use afmt) */ |
71 | int afmt; /* codec specification (AFMT_*) */ | ||
72 | }; | ||
73 | 73 | ||
74 | extern struct mp3entry *thistrack_id3, /* the currently playing track */ | ||
75 | *othertrack_id3; /* prev track during track-change-transition, or end of playlist, | ||
76 | * next track otherwise */ | ||
77 | 74 | ||
78 | /* Track change controls */ | 75 | /** --- Main state control --- **/ |
79 | extern struct event_queue audio_queue SHAREDBSS_ATTR; | ||
80 | 76 | ||
77 | static int codec_type = AFMT_UNKNOWN; /* Codec type (C,A-) */ | ||
81 | 78 | ||
79 | /* Private interfaces to main playback control */ | ||
80 | extern void audio_codec_update_elapsed(unsigned long value); | ||
81 | extern void audio_codec_update_offset(size_t value); | ||
82 | extern void audio_queue_post(long id, intptr_t data); | ||
82 | extern struct codec_api ci; /* from codecs.c */ | 83 | extern struct codec_api ci; /* from codecs.c */ |
83 | 84 | ||
84 | /* Codec thread */ | 85 | /* Codec thread */ |
85 | static unsigned int codec_thread_id; /* For modifying thread priority later */ | 86 | static unsigned int codec_thread_id; /* For modifying thread priority later */ |
86 | static struct event_queue codec_queue SHAREDBSS_ATTR; | 87 | static struct event_queue codec_queue SHAREDBSS_ATTR; |
87 | static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR; | 88 | static struct queue_sender_list codec_queue_sender_list SHAREDBSS_ATTR; |
88 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] | 89 | static long codec_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)] IBSS_ATTR; |
89 | IBSS_ATTR; | ||
90 | static const char codec_thread_name[] = "codec"; | 90 | static const char codec_thread_name[] = "codec"; |
91 | 91 | ||
92 | static void unload_codec(void); | ||
93 | |||
94 | /* Messages are only ever sent one at a time to the codec from the audio | ||
95 | thread. This is important for correct operation unless playback is | ||
96 | stopped. */ | ||
97 | |||
92 | /* static routines */ | 98 | /* static routines */ |
93 | static void codec_queue_ack(intptr_t ackme) | 99 | static void codec_queue_ack(intptr_t ackme) |
94 | { | 100 | { |
@@ -100,52 +106,63 @@ static intptr_t codec_queue_send(long id, intptr_t data) | |||
100 | return queue_send(&codec_queue, id, data); | 106 | return queue_send(&codec_queue, id, data); |
101 | } | 107 | } |
102 | 108 | ||
103 | /**************************************/ | 109 | /* Poll the state of the codec queue. Returns < 0 if the message is urgent |
110 | and any state should exit, > 0 if it's a run message (and it was | ||
111 | scrubbed), 0 if message was ignored. */ | ||
112 | static int codec_check_queue__have_msg(void) | ||
113 | { | ||
114 | struct queue_event ev; | ||
104 | 115 | ||
105 | /** misc external functions */ | 116 | queue_peek(&codec_queue, &ev); |
106 | 117 | ||
107 | /* Used to check whether a new codec must be loaded. See array audio_formats[] | 118 | /* Seek, pause or stop? Just peek and return if so. Codec |
108 | * in metadata.c */ | 119 | must handle the command after returing. Inserts will not |
109 | int get_codec_base_type(int type) | 120 | be allowed until it complies. */ |
110 | { | 121 | switch (ev.id) |
111 | int base_type = type; | 122 | { |
112 | switch (type) { | 123 | case Q_CODEC_SEEK: |
113 | case AFMT_MPA_L1: | 124 | LOGFQUEUE("codec - Q_CODEC_SEEK", ev.id); |
114 | case AFMT_MPA_L2: | 125 | return -1; |
115 | case AFMT_MPA_L3: | 126 | case Q_CODEC_PAUSE: |
116 | base_type = AFMT_MPA_L3; | 127 | LOGFQUEUE("codec - Q_CODEC_PAUSE", ev.id); |
117 | break; | 128 | return -1; |
118 | case AFMT_MPC_SV7: | 129 | case Q_CODEC_STOP: |
119 | case AFMT_MPC_SV8: | 130 | LOGFQUEUE("codec - Q_CODEC_STOP", ev.id); |
120 | base_type = AFMT_MPC_SV7; | 131 | return -1; |
121 | break; | ||
122 | case AFMT_MP4_AAC: | ||
123 | case AFMT_MP4_AAC_HE: | ||
124 | base_type = AFMT_MP4_AAC; | ||
125 | break; | ||
126 | case AFMT_SAP: | ||
127 | case AFMT_CMC: | ||
128 | case AFMT_CM3: | ||
129 | case AFMT_CMR: | ||
130 | case AFMT_CMS: | ||
131 | case AFMT_DMC: | ||
132 | case AFMT_DLT: | ||
133 | case AFMT_MPT: | ||
134 | case AFMT_MPD: | ||
135 | case AFMT_RMT: | ||
136 | case AFMT_TMC: | ||
137 | case AFMT_TM8: | ||
138 | case AFMT_TM2: | ||
139 | base_type = AFMT_SAP; | ||
140 | break; | ||
141 | default: | ||
142 | break; | ||
143 | } | 132 | } |
144 | 133 | ||
145 | return base_type; | 134 | /* This is in error in this context unless it's "go, go, go!" */ |
135 | queue_wait(&codec_queue, &ev); | ||
136 | |||
137 | if (ev.id == Q_CODEC_RUN) | ||
138 | { | ||
139 | logf("codec < Q_CODEC_RUN: already running!"); | ||
140 | codec_queue_ack(Q_CODEC_RUN); | ||
141 | return 1; | ||
142 | } | ||
143 | |||
144 | /* Ignore it */ | ||
145 | logf("codec < bad req %ld (%s)", ev.id, __func__); | ||
146 | codec_queue_ack(Q_NULL); | ||
147 | return 0; | ||
148 | } | ||
149 | |||
150 | /* Does the audio format type equal CODEC_TYPE_ENCODER? */ | ||
151 | static inline bool type_is_encoder(int afmt) | ||
152 | { | ||
153 | #ifdef AUDIO_HAVE_RECORDING | ||
154 | return (afmt & CODEC_TYPE_MASK) == CODEC_TYPE_ENCODER; | ||
155 | #else | ||
156 | return false; | ||
157 | (void)afmt; | ||
158 | #endif | ||
146 | } | 159 | } |
147 | 160 | ||
148 | const char *get_codec_filename(int cod_spec) | 161 | /**************************************/ |
162 | |||
163 | |||
164 | /** --- Miscellaneous external functions --- **/ | ||
165 | const char * get_codec_filename(int cod_spec) | ||
149 | { | 166 | { |
150 | const char *fname; | 167 | const char *fname; |
151 | 168 | ||
@@ -173,7 +190,7 @@ const char *get_codec_filename(int cod_spec) | |||
173 | #endif /* HAVE_RECORDING */ | 190 | #endif /* HAVE_RECORDING */ |
174 | 191 | ||
175 | return fname; | 192 | return fname; |
176 | } /* get_codec_filename */ | 193 | } |
177 | 194 | ||
178 | /* Borrow the codec thread and return the ID */ | 195 | /* Borrow the codec thread and return the ID */ |
179 | void codec_thread_do_callback(void (*fn)(void), unsigned int *id) | 196 | void codec_thread_do_callback(void (*fn)(void), unsigned int *id) |
@@ -189,9 +206,9 @@ void codec_thread_do_callback(void (*fn)(void), unsigned int *id) | |||
189 | } | 206 | } |
190 | 207 | ||
191 | 208 | ||
192 | /** codec API callbacks */ | 209 | /** --- codec API callbacks --- **/ |
193 | 210 | ||
194 | static void* codec_get_buffer(size_t *size) | 211 | static void * codec_get_buffer(size_t *size) |
195 | { | 212 | { |
196 | ssize_t s = CODEC_SIZE - codec_size; | 213 | ssize_t s = CODEC_SIZE - codec_size; |
197 | void *buf = &codecbuf[codec_size]; | 214 | void *buf = &codecbuf[codec_size]; |
@@ -215,15 +232,19 @@ static void codec_pcmbuf_insert_callback( | |||
215 | int inp_count; | 232 | int inp_count; |
216 | char *dest; | 233 | char *dest; |
217 | 234 | ||
218 | /* Prevent audio from a previous track from playing */ | 235 | while (1) |
219 | if (ci.new_track || ci.stop_codec) | ||
220 | return; | ||
221 | |||
222 | while ((dest = pcmbuf_request_buffer(&out_count)) == NULL) | ||
223 | { | 236 | { |
237 | if ((dest = pcmbuf_request_buffer(&out_count)) != NULL) | ||
238 | break; | ||
239 | |||
224 | cancel_cpu_boost(); | 240 | cancel_cpu_boost(); |
225 | sleep(1); | 241 | |
226 | if (ci.seek_time || ci.new_track || ci.stop_codec) | 242 | /* It will be awhile before space is available but we want |
243 | "instant" response to any message */ | ||
244 | queue_wait_w_tmo(&codec_queue, NULL, HZ/20); | ||
245 | |||
246 | if (!queue_empty(&codec_queue) && | ||
247 | codec_check_queue__have_msg() < 0) | ||
227 | return; | 248 | return; |
228 | } | 249 | } |
229 | 250 | ||
@@ -247,62 +268,28 @@ static void codec_pcmbuf_insert_callback( | |||
247 | 268 | ||
248 | count -= inp_count; | 269 | count -= inp_count; |
249 | } | 270 | } |
250 | } /* codec_pcmbuf_insert_callback */ | 271 | } |
251 | 272 | ||
252 | static void codec_set_elapsed_callback(unsigned long value) | 273 | /* helper function, not a callback */ |
274 | static bool codec_advance_buffer_counters(size_t amount) | ||
253 | { | 275 | { |
254 | if (ci.seek_time) | 276 | if (bufadvance(ci.audio_hid, amount) < 0) |
255 | return; | ||
256 | |||
257 | #ifdef AB_REPEAT_ENABLE | ||
258 | ab_position_report(value); | ||
259 | #endif | ||
260 | |||
261 | unsigned long latency = pcmbuf_get_latency(); | ||
262 | if (value < latency) | ||
263 | thistrack_id3->elapsed = 0; | ||
264 | else | ||
265 | { | 277 | { |
266 | unsigned long elapsed = value - latency; | 278 | ci.curpos = ci.filesize; |
267 | if (elapsed > thistrack_id3->elapsed || | 279 | return false; |
268 | elapsed < thistrack_id3->elapsed - 2) | ||
269 | { | ||
270 | thistrack_id3->elapsed = elapsed; | ||
271 | } | ||
272 | } | 280 | } |
273 | } | ||
274 | |||
275 | static void codec_set_offset_callback(size_t value) | ||
276 | { | ||
277 | if (ci.seek_time) | ||
278 | return; | ||
279 | 281 | ||
280 | unsigned long latency = pcmbuf_get_latency() * thistrack_id3->bitrate / 8; | ||
281 | if (value < latency) | ||
282 | thistrack_id3->offset = 0; | ||
283 | else | ||
284 | thistrack_id3->offset = value - latency; | ||
285 | } | ||
286 | |||
287 | /* helper function, not a callback */ | ||
288 | static void codec_advance_buffer_counters(size_t amount) | ||
289 | { | ||
290 | bufadvance(get_audio_hid(), amount); | ||
291 | ci.curpos += amount; | 282 | ci.curpos += amount; |
283 | return true; | ||
292 | } | 284 | } |
293 | 285 | ||
294 | /* copy up-to size bytes into ptr and return the actual size copied */ | 286 | /* copy up-to size bytes into ptr and return the actual size copied */ |
295 | static size_t codec_filebuf_callback(void *ptr, size_t size) | 287 | static size_t codec_filebuf_callback(void *ptr, size_t size) |
296 | { | 288 | { |
297 | ssize_t copy_n; | 289 | ssize_t copy_n = bufread(ci.audio_hid, size, ptr); |
298 | |||
299 | if (ci.stop_codec) | ||
300 | return 0; | ||
301 | |||
302 | copy_n = bufread(get_audio_hid(), size, ptr); | ||
303 | 290 | ||
304 | /* Nothing requested OR nothing left */ | 291 | /* Nothing requested OR nothing left */ |
305 | if (copy_n == 0) | 292 | if (copy_n <= 0) |
306 | return 0; | 293 | return 0; |
307 | 294 | ||
308 | /* Update read and other position pointers */ | 295 | /* Update read and other position pointers */ |
@@ -310,15 +297,15 @@ static size_t codec_filebuf_callback(void *ptr, size_t size) | |||
310 | 297 | ||
311 | /* Return the actual amount of data copied to the buffer */ | 298 | /* Return the actual amount of data copied to the buffer */ |
312 | return copy_n; | 299 | return copy_n; |
313 | } /* codec_filebuf_callback */ | 300 | } |
314 | 301 | ||
315 | static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) | 302 | static void * codec_request_buffer_callback(size_t *realsize, size_t reqsize) |
316 | { | 303 | { |
317 | size_t copy_n = reqsize; | 304 | size_t copy_n = reqsize; |
318 | ssize_t ret; | 305 | ssize_t ret; |
319 | void *ptr; | 306 | void *ptr; |
320 | 307 | ||
321 | ret = bufgetdata(get_audio_hid(), reqsize, &ptr); | 308 | ret = bufgetdata(ci.audio_hid, reqsize, &ptr); |
322 | if (ret >= 0) | 309 | if (ret >= 0) |
323 | copy_n = MIN((size_t)ret, reqsize); | 310 | copy_n = MIN((size_t)ret, reqsize); |
324 | else | 311 | else |
@@ -329,101 +316,103 @@ static void* codec_request_buffer_callback(size_t *realsize, size_t reqsize) | |||
329 | 316 | ||
330 | *realsize = copy_n; | 317 | *realsize = copy_n; |
331 | return ptr; | 318 | return ptr; |
332 | } /* codec_request_buffer_callback */ | 319 | } |
333 | 320 | ||
334 | static void codec_advance_buffer_callback(size_t amount) | 321 | static void codec_advance_buffer_callback(size_t amount) |
335 | { | 322 | { |
336 | codec_advance_buffer_counters(amount); | 323 | if (!codec_advance_buffer_counters(amount)) |
337 | codec_set_offset_callback(ci.curpos); | 324 | return; |
325 | |||
326 | audio_codec_update_offset(ci.curpos); | ||
338 | } | 327 | } |
339 | 328 | ||
340 | static bool codec_seek_buffer_callback(size_t newpos) | 329 | static bool codec_seek_buffer_callback(size_t newpos) |
341 | { | 330 | { |
342 | logf("codec_seek_buffer_callback"); | 331 | logf("codec_seek_buffer_callback"); |
343 | 332 | ||
344 | int ret = bufseek(get_audio_hid(), newpos); | 333 | int ret = bufseek(ci.audio_hid, newpos); |
345 | if (ret == 0) { | 334 | if (ret == 0) |
335 | { | ||
346 | ci.curpos = newpos; | 336 | ci.curpos = newpos; |
347 | return true; | 337 | return true; |
348 | } | 338 | } |
349 | else { | 339 | |
350 | return false; | 340 | return false; |
351 | } | ||
352 | } | 341 | } |
353 | 342 | ||
354 | static void codec_seek_complete_callback(void) | 343 | static void codec_seek_complete_callback(void) |
355 | { | 344 | { |
356 | struct queue_event ev; | ||
357 | |||
358 | logf("seek_complete"); | 345 | logf("seek_complete"); |
359 | 346 | ||
360 | /* Clear DSP */ | 347 | /* Clear DSP */ |
361 | dsp_configure(ci.dsp, DSP_FLUSH, 0); | 348 | dsp_configure(ci.dsp, DSP_FLUSH, 0); |
362 | 349 | ||
363 | /* Post notification to audio thread */ | 350 | /* Post notification to audio thread */ |
364 | LOGFQUEUE("audio > Q_AUDIO_SEEK_COMPLETE"); | 351 | LOGFQUEUE("audio > Q_AUDIO_CODEC_SEEK_COMPLETE"); |
365 | queue_post(&audio_queue, Q_AUDIO_SEEK_COMPLETE, 0); | 352 | audio_queue_post(Q_AUDIO_CODEC_SEEK_COMPLETE, 0); |
366 | |||
367 | /* Wait for ACK */ | ||
368 | queue_wait(&codec_queue, &ev); | ||
369 | 353 | ||
370 | /* ACK back in context */ | 354 | /* Wait for urgent or go message */ |
371 | codec_queue_ack(Q_AUDIO_SEEK_COMPLETE); | 355 | do |
356 | { | ||
357 | queue_wait(&codec_queue, NULL); | ||
358 | } | ||
359 | while (codec_check_queue__have_msg() == 0); | ||
372 | } | 360 | } |
373 | 361 | ||
374 | static bool codec_request_next_track_callback(void) | 362 | static void codec_configure_callback(int setting, intptr_t value) |
375 | { | 363 | { |
376 | struct queue_event ev; | 364 | if (!dsp_configure(ci.dsp, setting, value)) |
377 | 365 | { | |
378 | logf("Request new track"); | 366 | logf("Illegal key: %d", setting); |
367 | } | ||
368 | } | ||
379 | 369 | ||
380 | audio_set_prev_elapsed(thistrack_id3->elapsed); | 370 | static enum codec_command_action |
371 | codec_get_command_callback(intptr_t *param) | ||
372 | { | ||
373 | yield(); | ||
381 | 374 | ||
382 | #ifdef AB_REPEAT_ENABLE | 375 | if (LIKELY(queue_empty(&codec_queue))) |
383 | ab_end_of_track_report(); | 376 | return CODEC_ACTION_NULL; /* As you were */ |
384 | #endif | ||
385 | 377 | ||
386 | if (ci.stop_codec) | 378 | /* Process the message - return requested action and data (if any should |
379 | be expected) */ | ||
380 | while (1) | ||
387 | { | 381 | { |
388 | /* Handle ACK in outer loop */ | 382 | enum codec_command_action action = CODEC_ACTION_NULL; |
389 | LOGFQUEUE("codec: already stopping"); | 383 | struct queue_event ev; |
390 | return false; | 384 | queue_wait(&codec_queue, &ev); |
391 | } | ||
392 | 385 | ||
393 | trigger_cpu_boost(); | 386 | switch (ev.id) |
387 | { | ||
388 | case Q_CODEC_RUN: /* Already running */ | ||
389 | LOGFQUEUE("codec < Q_CODEC_RUN"); | ||
390 | break; | ||
394 | 391 | ||
395 | /* Post request to audio thread */ | 392 | case Q_CODEC_PAUSE: /* Stay here and wait */ |
396 | LOGFQUEUE("codec > audio Q_AUDIO_CHECK_NEW_TRACK"); | 393 | LOGFQUEUE("codec < Q_CODEC_PAUSE"); |
397 | queue_post(&audio_queue, Q_AUDIO_CHECK_NEW_TRACK, 0); | 394 | codec_queue_ack(Q_CODEC_PAUSE); |
395 | continue; | ||
398 | 396 | ||
399 | /* Wait for ACK */ | 397 | case Q_CODEC_SEEK: /* Audio wants codec to seek */ |
400 | queue_wait(&codec_queue, &ev); | 398 | LOGFQUEUE("codec < Q_CODEC_SEEK %ld", ev.data); |
399 | *param = ev.data; | ||
400 | action = CODEC_ACTION_SEEK_TIME; | ||
401 | break; | ||
401 | 402 | ||
402 | if (ev.data == Q_CODEC_REQUEST_COMPLETE) | 403 | case Q_CODEC_STOP: /* Must only return 0 in main loop */ |
403 | { | 404 | LOGFQUEUE("codec < Q_CODEC_STOP"); |
404 | /* Seek to the beginning of the new track because if the struct | 405 | action = CODEC_ACTION_HALT; |
405 | mp3entry was buffered, "elapsed" might not be zero (if the track has | 406 | break; |
406 | been played already but not unbuffered) */ | ||
407 | codec_seek_buffer_callback(thistrack_id3->first_frame_offset); | ||
408 | } | ||
409 | 407 | ||
410 | /* ACK back in context */ | 408 | default: /* This is in error in this context. */ |
411 | codec_queue_ack(Q_AUDIO_CHECK_NEW_TRACK); | 409 | ev.id = Q_NULL; |
410 | logf("codec bad req %ld (%s)", ev.id, __func__); | ||
411 | } | ||
412 | 412 | ||
413 | if (ev.data != Q_CODEC_REQUEST_COMPLETE || ci.stop_codec) | 413 | codec_queue_ack(ev.id); |
414 | { | 414 | return action; |
415 | LOGFQUEUE("codec <= request failed (%d)", ev.data); | ||
416 | return false; | ||
417 | } | 415 | } |
418 | |||
419 | LOGFQUEUE("codec <= Q_CODEC_REQEST_COMPLETE"); | ||
420 | return true; | ||
421 | } | ||
422 | |||
423 | static void codec_configure_callback(int setting, intptr_t value) | ||
424 | { | ||
425 | if (!dsp_configure(ci.dsp, setting, value)) | ||
426 | { logf("Illegal key:%d", setting); } | ||
427 | } | 416 | } |
428 | 417 | ||
429 | /* Initialize codec API */ | 418 | /* Initialize codec API */ |
@@ -433,119 +422,215 @@ void codec_init_codec_api(void) | |||
433 | CODEC_IDX_AUDIO); | 422 | CODEC_IDX_AUDIO); |
434 | ci.codec_get_buffer = codec_get_buffer; | 423 | ci.codec_get_buffer = codec_get_buffer; |
435 | ci.pcmbuf_insert = codec_pcmbuf_insert_callback; | 424 | ci.pcmbuf_insert = codec_pcmbuf_insert_callback; |
436 | ci.set_elapsed = codec_set_elapsed_callback; | 425 | ci.set_elapsed = audio_codec_update_elapsed; |
437 | ci.read_filebuf = codec_filebuf_callback; | 426 | ci.read_filebuf = codec_filebuf_callback; |
438 | ci.request_buffer = codec_request_buffer_callback; | 427 | ci.request_buffer = codec_request_buffer_callback; |
439 | ci.advance_buffer = codec_advance_buffer_callback; | 428 | ci.advance_buffer = codec_advance_buffer_callback; |
440 | ci.seek_buffer = codec_seek_buffer_callback; | 429 | ci.seek_buffer = codec_seek_buffer_callback; |
441 | ci.seek_complete = codec_seek_complete_callback; | 430 | ci.seek_complete = codec_seek_complete_callback; |
442 | ci.request_next_track = codec_request_next_track_callback; | 431 | ci.set_offset = audio_codec_update_offset; |
443 | ci.set_offset = codec_set_offset_callback; | ||
444 | ci.configure = codec_configure_callback; | 432 | ci.configure = codec_configure_callback; |
433 | ci.get_command = codec_get_command_callback; | ||
445 | } | 434 | } |
446 | 435 | ||
447 | 436 | ||
448 | /* track change */ | 437 | /** --- CODEC THREAD --- **/ |
449 | 438 | ||
450 | /** CODEC THREAD */ | 439 | /* Handle Q_CODEC_LOAD */ |
451 | static void codec_thread(void) | 440 | static void load_codec(const struct codec_load_info *ev_data) |
452 | { | 441 | { |
453 | struct queue_event ev; | 442 | int status = CODEC_ERROR; |
443 | /* Save a local copy so we can let the audio thread go ASAP */ | ||
444 | struct codec_load_info data = *ev_data; | ||
445 | bool const encoder = type_is_encoder(data.afmt); | ||
454 | 446 | ||
447 | if (codec_type != AFMT_UNKNOWN) | ||
448 | { | ||
449 | /* Must have unloaded it first */ | ||
450 | logf("a codec is already loaded"); | ||
451 | if (data.hid >= 0) | ||
452 | bufclose(data.hid); | ||
453 | return; | ||
454 | } | ||
455 | 455 | ||
456 | while (1) | 456 | trigger_cpu_boost(); |
457 | |||
458 | if (!encoder) | ||
457 | { | 459 | { |
458 | int status = CODEC_OK; | 460 | /* Do this now because codec may set some things up at load time */ |
459 | void *handle = NULL; | 461 | dsp_configure(ci.dsp, DSP_RESET, 0); |
460 | int hid; | 462 | } |
461 | const char *codec_fn; | 463 | |
462 | 464 | if (data.hid >= 0) | |
463 | #ifdef HAVE_CROSSFADE | 465 | { |
464 | if (!pcmbuf_is_crossfade_active()) | 466 | /* First try buffer load */ |
465 | #endif | 467 | status = codec_load_buf(data.hid, &ci); |
468 | bufclose(data.hid); | ||
469 | } | ||
470 | |||
471 | if (status < 0) | ||
472 | { | ||
473 | /* Either not a valid handle or the buffer method failed */ | ||
474 | const char *codec_fn = get_codec_filename(data.afmt); | ||
475 | if (codec_fn) | ||
466 | { | 476 | { |
467 | cancel_cpu_boost(); | 477 | #ifdef HAVE_IO_PRIORITY |
478 | buf_back_off_storage(true); | ||
479 | #endif | ||
480 | status = codec_load_file(codec_fn, &ci); | ||
481 | #ifdef HAVE_IO_PRIORITY | ||
482 | buf_back_off_storage(false); | ||
483 | #endif | ||
468 | } | 484 | } |
485 | } | ||
486 | |||
487 | if (status >= 0) | ||
488 | { | ||
489 | codec_type = data.afmt; | ||
490 | codec_queue_ack(Q_CODEC_LOAD); | ||
491 | return; | ||
492 | } | ||
493 | |||
494 | /* Failed - get rid of it */ | ||
495 | unload_codec(); | ||
496 | } | ||
497 | |||
498 | /* Handle Q_CODEC_RUN */ | ||
499 | static void run_codec(void) | ||
500 | { | ||
501 | bool const encoder = type_is_encoder(codec_type); | ||
502 | int status; | ||
503 | |||
504 | if (codec_type == AFMT_UNKNOWN) | ||
505 | { | ||
506 | logf("no codec to run"); | ||
507 | return; | ||
508 | } | ||
509 | |||
510 | codec_queue_ack(Q_CODEC_RUN); | ||
511 | |||
512 | trigger_cpu_boost(); | ||
513 | |||
514 | if (!encoder) | ||
515 | { | ||
516 | /* This will be either the initial buffered offset or where it left off | ||
517 | if it remained buffered and we're skipping back to it and it is best | ||
518 | to have ci.curpos in sync with the handle's read position - it's the | ||
519 | codec's responsibility to ensure it has the correct positions - | ||
520 | playback is sorta dumb and only has a vague idea about what to | ||
521 | buffer based upon what metadata has to say */ | ||
522 | ci.curpos = bufftell(ci.audio_hid); | ||
523 | |||
524 | /* Pin the codec's audio data in place */ | ||
525 | buf_pin_handle(ci.audio_hid, true); | ||
526 | } | ||
527 | |||
528 | status = codec_run_proc(); | ||
529 | |||
530 | if (!encoder) | ||
531 | { | ||
532 | /* Codec is done with it - let it move */ | ||
533 | buf_pin_handle(ci.audio_hid, false); | ||
534 | |||
535 | /* Notify audio that we're done for better or worse - advise of the | ||
536 | status */ | ||
537 | LOGFQUEUE("codec > audio Q_AUDIO_CODEC_COMPLETE: %d", status); | ||
538 | audio_queue_post(Q_AUDIO_CODEC_COMPLETE, status); | ||
539 | } | ||
540 | } | ||
541 | |||
542 | /* Handle Q_CODEC_SEEK */ | ||
543 | static void seek_codec(unsigned long time) | ||
544 | { | ||
545 | if (codec_type == AFMT_UNKNOWN) | ||
546 | { | ||
547 | logf("no codec to seek"); | ||
548 | codec_queue_ack(Q_CODEC_SEEK); | ||
549 | codec_seek_complete_callback(); | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | /* Post it up one level */ | ||
554 | queue_post(&codec_queue, Q_CODEC_SEEK, time); | ||
555 | codec_queue_ack(Q_CODEC_SEEK); | ||
556 | |||
557 | /* Have to run it again */ | ||
558 | run_codec(); | ||
559 | } | ||
560 | |||
561 | /* Handle Q_CODEC_UNLOAD */ | ||
562 | static void unload_codec(void) | ||
563 | { | ||
564 | /* Tell codec to clean up */ | ||
565 | codec_type = AFMT_UNKNOWN; | ||
566 | codec_close(); | ||
567 | } | ||
568 | |||
569 | /* Handle Q_CODEC_DO_CALLBACK */ | ||
570 | static void do_callback(void (* callback)(void)) | ||
571 | { | ||
572 | codec_queue_ack(Q_CODEC_DO_CALLBACK); | ||
573 | |||
574 | if (callback) | ||
575 | { | ||
576 | cpucache_commit_discard(); | ||
577 | callback(); | ||
578 | cpucache_commit(); | ||
579 | } | ||
580 | } | ||
581 | |||
582 | /* Codec thread function */ | ||
583 | static void NORETURN_ATTR codec_thread(void) | ||
584 | { | ||
585 | struct queue_event ev; | ||
586 | |||
587 | while (1) | ||
588 | { | ||
589 | cancel_cpu_boost(); | ||
469 | 590 | ||
470 | queue_wait(&codec_queue, &ev); | 591 | queue_wait(&codec_queue, &ev); |
471 | 592 | ||
472 | switch (ev.id) | 593 | switch (ev.id) |
473 | { | 594 | { |
474 | case Q_CODEC_LOAD_DISK: | 595 | case Q_CODEC_LOAD: |
475 | LOGFQUEUE("codec < Q_CODEC_LOAD_DISK"); | 596 | LOGFQUEUE("codec < Q_CODEC_LOAD"); |
476 | codec_fn = get_codec_filename(ev.data); | 597 | load_codec((const struct codec_load_info *)ev.data); |
477 | if (!codec_fn) | 598 | break; |
478 | break; | ||
479 | #ifdef AUDIO_HAVE_RECORDING | ||
480 | if (ev.data & CODEC_TYPE_ENCODER) | ||
481 | { | ||
482 | ev.id = Q_ENCODER_LOAD_DISK; | ||
483 | handle = codec_load_file(codec_fn, &ci); | ||
484 | if (handle) | ||
485 | codec_queue_ack(Q_ENCODER_LOAD_DISK); | ||
486 | } | ||
487 | else | ||
488 | #endif | ||
489 | { | ||
490 | codec_queue_ack(Q_CODEC_LOAD_DISK); | ||
491 | handle = codec_load_file(codec_fn, &ci); | ||
492 | } | ||
493 | break; | ||
494 | 599 | ||
495 | case Q_CODEC_LOAD: | 600 | case Q_CODEC_RUN: |
496 | LOGFQUEUE("codec < Q_CODEC_LOAD"); | 601 | LOGFQUEUE("codec < Q_CODEC_RUN"); |
497 | codec_queue_ack(Q_CODEC_LOAD); | 602 | run_codec(); |
498 | hid = (int)ev.data; | 603 | break; |
499 | handle = codec_load_buf(hid, &ci); | ||
500 | bufclose(hid); | ||
501 | break; | ||
502 | 604 | ||
503 | case Q_CODEC_DO_CALLBACK: | 605 | case Q_CODEC_PAUSE: |
504 | LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK"); | 606 | LOGFQUEUE("codec < Q_CODEC_PAUSE"); |
505 | codec_queue_ack(Q_CODEC_DO_CALLBACK); | 607 | break; |
506 | if ((void*)ev.data != NULL) | ||
507 | { | ||
508 | cpucache_commit_discard(); | ||
509 | ((void (*)(void))ev.data)(); | ||
510 | cpucache_commit(); | ||
511 | } | ||
512 | break; | ||
513 | 608 | ||
514 | default: | 609 | case Q_CODEC_SEEK: |
515 | LOGFQUEUE("codec < default : %ld", ev.id); | 610 | LOGFQUEUE("codec < Q_CODEC_SEEK: %lu", (unsigned long)ev.data); |
516 | } | 611 | seek_codec(ev.data); |
612 | break; | ||
517 | 613 | ||
518 | if (handle) | 614 | case Q_CODEC_UNLOAD: |
519 | { | 615 | LOGFQUEUE("codec < Q_CODEC_UNLOAD"); |
520 | /* Codec loaded - call the entrypoint */ | 616 | unload_codec(); |
521 | yield(); | 617 | break; |
522 | logf("codec running"); | ||
523 | status = codec_begin(handle); | ||
524 | logf("codec stopped"); | ||
525 | codec_close(handle); | ||
526 | current_codectype = AFMT_UNKNOWN; | ||
527 | |||
528 | if (ci.stop_codec) | ||
529 | status = CODEC_OK; | ||
530 | } | ||
531 | 618 | ||
532 | switch (ev.id) | 619 | case Q_CODEC_DO_CALLBACK: |
533 | { | 620 | LOGFQUEUE("codec < Q_CODEC_DO_CALLBACK"); |
534 | #ifdef AUDIO_HAVE_RECORDING | 621 | do_callback((void (*)(void))ev.data); |
535 | case Q_ENCODER_LOAD_DISK: | 622 | break; |
536 | #endif | 623 | |
537 | case Q_CODEC_LOAD_DISK: | 624 | default: |
538 | case Q_CODEC_LOAD: | 625 | LOGFQUEUE("codec < default : %ld", ev.id); |
539 | /* Notify about the status */ | ||
540 | if (!handle) | ||
541 | status = CODEC_ERROR; | ||
542 | LOGFQUEUE("codec > audio notify status: %d", status); | ||
543 | queue_post(&audio_queue, ev.id, status); | ||
544 | break; | ||
545 | } | 626 | } |
546 | } | 627 | } |
547 | } | 628 | } |
548 | 629 | ||
630 | |||
631 | /** --- Miscellaneous external interfaces -- **/ | ||
632 | |||
633 | /* Create the codec thread and init kernel objects */ | ||
549 | void make_codec_thread(void) | 634 | void make_codec_thread(void) |
550 | { | 635 | { |
551 | queue_init(&codec_queue, false); | 636 | queue_init(&codec_queue, false); |
@@ -558,78 +643,86 @@ void make_codec_thread(void) | |||
558 | codec_thread_id); | 643 | codec_thread_id); |
559 | } | 644 | } |
560 | 645 | ||
646 | /* Unfreeze the codec thread */ | ||
561 | void codec_thread_resume(void) | 647 | void codec_thread_resume(void) |
562 | { | 648 | { |
563 | thread_thaw(codec_thread_id); | 649 | thread_thaw(codec_thread_id); |
564 | } | 650 | } |
565 | 651 | ||
652 | /* Is the current thread the codec thread? */ | ||
566 | bool is_codec_thread(void) | 653 | bool is_codec_thread(void) |
567 | { | 654 | { |
568 | return thread_self() == codec_thread_id; | 655 | return thread_self() == codec_thread_id; |
569 | } | 656 | } |
570 | 657 | ||
571 | #ifdef HAVE_PRIORITY_SCHEDULING | 658 | #ifdef HAVE_PRIORITY_SCHEDULING |
659 | /* Obtain codec thread's current priority */ | ||
572 | int codec_thread_get_priority(void) | 660 | int codec_thread_get_priority(void) |
573 | { | 661 | { |
574 | return thread_get_priority(codec_thread_id); | 662 | return thread_get_priority(codec_thread_id); |
575 | } | 663 | } |
576 | 664 | ||
665 | /* Set the codec thread's priority and return the old value */ | ||
577 | int codec_thread_set_priority(int priority) | 666 | int codec_thread_set_priority(int priority) |
578 | { | 667 | { |
579 | return thread_set_priority(codec_thread_id, priority); | 668 | return thread_set_priority(codec_thread_id, priority); |
580 | } | 669 | } |
581 | #endif /* HAVE_PRIORITY_SCHEDULING */ | 670 | #endif /* HAVE_PRIORITY_SCHEDULING */ |
582 | 671 | ||
583 | /* functions for audio thread use */ | ||
584 | intptr_t codec_ack_msg(intptr_t data, bool stop_codec) | ||
585 | { | ||
586 | intptr_t resp; | ||
587 | LOGFQUEUE("codec >| Q_CODEC_ACK: %d", data); | ||
588 | if (stop_codec) | ||
589 | ci.stop_codec = true; | ||
590 | resp = codec_queue_send(Q_CODEC_ACK, data); | ||
591 | if (stop_codec) | ||
592 | codec_stop(); | ||
593 | LOGFQUEUE(" ack: %ld", resp); | ||
594 | return resp; | ||
595 | } | ||
596 | 672 | ||
673 | /** --- Functions for audio thread use --- **/ | ||
674 | |||
675 | /* Load a decoder or encoder and set the format type */ | ||
597 | bool codec_load(int hid, int cod_spec) | 676 | bool codec_load(int hid, int cod_spec) |
598 | { | 677 | { |
599 | bool retval = false; | 678 | struct codec_load_info parm = { hid, cod_spec }; |
600 | 679 | ||
601 | ci.stop_codec = false; | 680 | LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d, %d", hid, cod_spec); |
602 | current_codectype = cod_spec; | 681 | return codec_queue_send(Q_CODEC_LOAD, (intptr_t)&parm) != 0; |
682 | } | ||
603 | 683 | ||
604 | if (hid >= 0) | 684 | /* Begin decoding the current file */ |
605 | { | 685 | void codec_go(void) |
606 | LOGFQUEUE("audio >| codec Q_CODEC_LOAD: %d", hid); | 686 | { |
607 | retval = codec_queue_send(Q_CODEC_LOAD, hid) != Q_NULL; | 687 | LOGFQUEUE("audio >| codec Q_CODEC_RUN"); |
608 | } | 688 | codec_queue_send(Q_CODEC_RUN, 0); |
609 | else | 689 | } |
610 | { | ||
611 | LOGFQUEUE("audio >| codec Q_CODEC_LOAD_DISK: %d", cod_spec); | ||
612 | retval = codec_queue_send(Q_CODEC_LOAD_DISK, cod_spec) != Q_NULL; | ||
613 | } | ||
614 | 690 | ||
615 | if (!retval) | 691 | /* Instruct the codec to seek to the specified time (should be properly |
616 | { | 692 | paused or stopped first to avoid possible buffering deadlock) */ |
617 | ci.stop_codec = true; | 693 | void codec_seek(long time) |
618 | current_codectype = AFMT_UNKNOWN; | 694 | { |
619 | } | 695 | LOGFQUEUE("audio > codec Q_CODEC_SEEK: %ld", time); |
696 | codec_queue_send(Q_CODEC_SEEK, time); | ||
697 | } | ||
620 | 698 | ||
621 | return retval; | 699 | /* Pause the codec and make it wait for further instructions inside the |
700 | command callback */ | ||
701 | bool codec_pause(void) | ||
702 | { | ||
703 | LOGFQUEUE("audio >| codec Q_CODEC_PAUSE"); | ||
704 | return codec_queue_send(Q_CODEC_PAUSE, 0) != Q_NULL; | ||
622 | } | 705 | } |
623 | 706 | ||
707 | /* Stop codec if running - codec stays resident if loaded */ | ||
624 | void codec_stop(void) | 708 | void codec_stop(void) |
625 | { | 709 | { |
626 | ci.stop_codec = true; | ||
627 | /* Wait until it's in the main loop */ | 710 | /* Wait until it's in the main loop */ |
628 | while (codec_ack_msg(0, false) != Q_NULL); | 711 | LOGFQUEUE("audio >| codec Q_CODEC_STOP"); |
629 | current_codectype = AFMT_UNKNOWN; | 712 | while (codec_queue_send(Q_CODEC_STOP, 0) != Q_NULL); |
713 | } | ||
714 | |||
715 | /* Call the codec's exit routine and close all references */ | ||
716 | void codec_unload(void) | ||
717 | { | ||
718 | codec_stop(); | ||
719 | LOGFQUEUE("audio >| codec Q_CODEC_UNLOAD"); | ||
720 | codec_queue_send(Q_CODEC_UNLOAD, 0); | ||
630 | } | 721 | } |
631 | 722 | ||
723 | /* Return the afmt type of the loaded codec - sticks until calling | ||
724 | codec_unload unless initial load failed */ | ||
632 | int codec_loaded(void) | 725 | int codec_loaded(void) |
633 | { | 726 | { |
634 | return current_codectype; | 727 | return codec_type; |
635 | } | 728 | } |
diff --git a/apps/codec_thread.h b/apps/codec_thread.h index 7056e2cdf5..acd7e556e2 100644 --- a/apps/codec_thread.h +++ b/apps/codec_thread.h | |||
@@ -25,7 +25,6 @@ | |||
25 | #include <stdbool.h> | 25 | #include <stdbool.h> |
26 | 26 | ||
27 | /* codec identity */ | 27 | /* codec identity */ |
28 | int get_codec_base_type(int type); | ||
29 | const char *get_codec_filename(int cod_spec); | 28 | const char *get_codec_filename(int cod_spec); |
30 | 29 | ||
31 | /* codec thread */ | 30 | /* codec thread */ |
@@ -44,10 +43,14 @@ int codec_thread_set_priority(int priority); | |||
44 | #endif | 43 | #endif |
45 | 44 | ||
46 | /* codec commands - on audio thread only! */ | 45 | /* codec commands - on audio thread only! */ |
47 | intptr_t codec_ack_msg(intptr_t data, bool stop_codec); | ||
48 | bool codec_load(int hid, int cod_spec); | 46 | bool codec_load(int hid, int cod_spec); |
47 | void codec_go(void); | ||
48 | bool codec_pause(void); | ||
49 | void codec_seek(long time); | ||
49 | void codec_stop(void); | 50 | void codec_stop(void); |
51 | void codec_unload(void); | ||
50 | int codec_loaded(void); | 52 | int codec_loaded(void); |
53 | |||
51 | /* */ | 54 | /* */ |
52 | 55 | ||
53 | #endif /* _CODEC_THREAD_H */ | 56 | #endif /* _CODEC_THREAD_H */ |
diff --git a/apps/codecs.c b/apps/codecs.c index 28e8c2a1c0..25ace4969b 100644 --- a/apps/codecs.c +++ b/apps/codecs.c | |||
@@ -79,13 +79,10 @@ static int open(const char* pathname, int flags, ...) | |||
79 | #endif | 79 | #endif |
80 | struct codec_api ci = { | 80 | struct codec_api ci = { |
81 | 81 | ||
82 | 0, /* filesize */ | 82 | 0, /* filesize */ |
83 | 0, /* curpos */ | 83 | 0, /* curpos */ |
84 | NULL, /* id3 */ | 84 | NULL, /* id3 */ |
85 | NULL, /* taginfo_ready */ | 85 | ERR_HANDLE_NOT_FOUND, /* audio_hid */ |
86 | false, /* stop_codec */ | ||
87 | 0, /* new_track */ | ||
88 | 0, /* seek_time */ | ||
89 | NULL, /* struct dsp_config *dsp */ | 86 | NULL, /* struct dsp_config *dsp */ |
90 | NULL, /* codec_get_buffer */ | 87 | NULL, /* codec_get_buffer */ |
91 | NULL, /* pcmbuf_insert */ | 88 | NULL, /* pcmbuf_insert */ |
@@ -95,9 +92,9 @@ struct codec_api ci = { | |||
95 | NULL, /* advance_buffer */ | 92 | NULL, /* advance_buffer */ |
96 | NULL, /* seek_buffer */ | 93 | NULL, /* seek_buffer */ |
97 | NULL, /* seek_complete */ | 94 | NULL, /* seek_complete */ |
98 | NULL, /* request_next_track */ | ||
99 | NULL, /* set_offset */ | 95 | NULL, /* set_offset */ |
100 | NULL, /* configure */ | 96 | NULL, /* configure */ |
97 | NULL, /* get_command */ | ||
101 | 98 | ||
102 | /* kernel/ system */ | 99 | /* kernel/ system */ |
103 | #if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE | 100 | #if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE |
@@ -174,10 +171,16 @@ void codec_get_full_path(char *path, const char *codec_root_fn) | |||
174 | CODECS_DIR, codec_root_fn); | 171 | CODECS_DIR, codec_root_fn); |
175 | } | 172 | } |
176 | 173 | ||
177 | static void * codec_load_ram(void *handle, struct codec_api *api) | 174 | /** codec loading and call interface **/ |
175 | static void *curr_handle = NULL; | ||
176 | static struct codec_header *c_hdr = NULL; | ||
177 | |||
178 | static int codec_load_ram(struct codec_api *api) | ||
178 | { | 179 | { |
179 | struct codec_header *c_hdr = lc_get_header(handle); | 180 | struct lc_header *hdr; |
180 | struct lc_header *hdr = c_hdr ? &c_hdr->lc_hdr : NULL; | 181 | |
182 | c_hdr = lc_get_header(curr_handle); | ||
183 | hdr = c_hdr ? &c_hdr->lc_hdr : NULL; | ||
181 | 184 | ||
182 | if (hdr == NULL | 185 | if (hdr == NULL |
183 | || (hdr->magic != CODEC_MAGIC | 186 | || (hdr->magic != CODEC_MAGIC |
@@ -193,15 +196,17 @@ static void * codec_load_ram(void *handle, struct codec_api *api) | |||
193 | ) | 196 | ) |
194 | { | 197 | { |
195 | logf("codec header error"); | 198 | logf("codec header error"); |
196 | lc_close(handle); | 199 | lc_close(curr_handle); |
197 | return NULL; | 200 | curr_handle = NULL; |
201 | return CODEC_ERROR; | ||
198 | } | 202 | } |
199 | 203 | ||
200 | if (hdr->api_version > CODEC_API_VERSION | 204 | if (hdr->api_version > CODEC_API_VERSION |
201 | || hdr->api_version < CODEC_MIN_API_VERSION) { | 205 | || hdr->api_version < CODEC_MIN_API_VERSION) { |
202 | logf("codec api version error"); | 206 | logf("codec api version error"); |
203 | lc_close(handle); | 207 | lc_close(curr_handle); |
204 | return NULL; | 208 | curr_handle = NULL; |
209 | return CODEC_ERROR; | ||
205 | } | 210 | } |
206 | 211 | ||
207 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 212 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
@@ -212,63 +217,66 @@ static void * codec_load_ram(void *handle, struct codec_api *api) | |||
212 | 217 | ||
213 | *(c_hdr->api) = api; | 218 | *(c_hdr->api) = api; |
214 | 219 | ||
215 | return handle; | 220 | logf("Codec: calling entrypoint"); |
221 | return c_hdr->entry_point(CODEC_LOAD); | ||
216 | } | 222 | } |
217 | 223 | ||
218 | void * codec_load_buf(int hid, struct codec_api *api) | 224 | int codec_load_buf(int hid, struct codec_api *api) |
219 | { | 225 | { |
220 | int rc; | 226 | int rc = bufread(hid, CODEC_SIZE, codecbuf); |
221 | void *handle; | 227 | |
222 | rc = bufread(hid, CODEC_SIZE, codecbuf); | ||
223 | if (rc < 0) { | 228 | if (rc < 0) { |
224 | logf("Codec: cannot read buf handle"); | 229 | logf("Codec: cannot read buf handle"); |
225 | return NULL; | 230 | return CODEC_ERROR; |
226 | } | 231 | } |
227 | 232 | ||
228 | handle = lc_open_from_mem(codecbuf, rc); | 233 | curr_handle = lc_open_from_mem(codecbuf, rc); |
229 | 234 | ||
230 | if (handle == NULL) { | 235 | if (curr_handle == NULL) { |
231 | logf("error loading codec"); | 236 | logf("Codec: load error"); |
232 | return NULL; | 237 | return CODEC_ERROR; |
233 | } | 238 | } |
234 | 239 | ||
235 | return codec_load_ram(handle, api); | 240 | return codec_load_ram(api); |
236 | } | 241 | } |
237 | 242 | ||
238 | void * codec_load_file(const char *plugin, struct codec_api *api) | 243 | int codec_load_file(const char *plugin, struct codec_api *api) |
239 | { | 244 | { |
240 | char path[MAX_PATH]; | 245 | char path[MAX_PATH]; |
241 | void *handle; | ||
242 | 246 | ||
243 | codec_get_full_path(path, plugin); | 247 | codec_get_full_path(path, plugin); |
244 | 248 | ||
245 | handle = lc_open(path, codecbuf, CODEC_SIZE); | 249 | curr_handle = lc_open(path, codecbuf, CODEC_SIZE); |
246 | 250 | ||
247 | if (handle == NULL) { | 251 | if (curr_handle == NULL) { |
248 | logf("Codec: cannot read file"); | 252 | logf("Codec: cannot read file"); |
249 | return NULL; | 253 | return CODEC_ERROR; |
250 | } | 254 | } |
251 | 255 | ||
252 | return codec_load_ram(handle, api); | 256 | return codec_load_ram(api); |
253 | } | 257 | } |
254 | 258 | ||
255 | int codec_begin(void *handle) | 259 | int codec_run_proc(void) |
256 | { | 260 | { |
257 | int status = CODEC_ERROR; | 261 | if (curr_handle == NULL) { |
258 | struct codec_header *c_hdr; | 262 | logf("Codec: no codec to run"); |
259 | 263 | return CODEC_ERROR; | |
260 | c_hdr = lc_get_header(handle); | ||
261 | |||
262 | if (c_hdr != NULL) { | ||
263 | logf("Codec: calling entry_point"); | ||
264 | status = c_hdr->entry_point(); | ||
265 | } | 264 | } |
266 | 265 | ||
267 | return status; | 266 | logf("Codec: entering run state"); |
267 | return c_hdr->run_proc(); | ||
268 | } | 268 | } |
269 | 269 | ||
270 | void codec_close(void *handle) | 270 | int codec_close(void) |
271 | { | 271 | { |
272 | if (handle) | 272 | int status = CODEC_OK; |
273 | lc_close(handle); | 273 | |
274 | if (curr_handle != NULL) { | ||
275 | logf("Codec: cleaning up"); | ||
276 | status = c_hdr->entry_point(CODEC_UNLOAD); | ||
277 | lc_close(curr_handle); | ||
278 | curr_handle = NULL; | ||
279 | } | ||
280 | |||
281 | return status; | ||
274 | } | 282 | } |
diff --git a/apps/codecs.h b/apps/codecs.h index d96b2a7c9a..5c50116038 100644 --- a/apps/codecs.h +++ b/apps/codecs.h | |||
@@ -75,12 +75,18 @@ | |||
75 | #define CODEC_ENC_MAGIC 0x52454E43 /* RENC */ | 75 | #define CODEC_ENC_MAGIC 0x52454E43 /* RENC */ |
76 | 76 | ||
77 | /* increase this every time the api struct changes */ | 77 | /* increase this every time the api struct changes */ |
78 | #define CODEC_API_VERSION 41 | 78 | #define CODEC_API_VERSION 42 |
79 | 79 | ||
80 | /* update this to latest version if a change to the api struct breaks | 80 | /* update this to latest version if a change to the api struct breaks |
81 | backwards compatibility (and please take the opportunity to sort in any | 81 | backwards compatibility (and please take the opportunity to sort in any |
82 | new function which are "waiting" at the end of the function table) */ | 82 | new function which are "waiting" at the end of the function table) */ |
83 | #define CODEC_MIN_API_VERSION 41 | 83 | #define CODEC_MIN_API_VERSION 42 |
84 | |||
85 | /* reasons for calling codec main entrypoint */ | ||
86 | enum codec_entry_call_reason { | ||
87 | CODEC_LOAD = 0, | ||
88 | CODEC_UNLOAD | ||
89 | }; | ||
84 | 90 | ||
85 | /* codec return codes */ | 91 | /* codec return codes */ |
86 | enum codec_status { | 92 | enum codec_status { |
@@ -88,6 +94,13 @@ enum codec_status { | |||
88 | CODEC_ERROR = -1, | 94 | CODEC_ERROR = -1, |
89 | }; | 95 | }; |
90 | 96 | ||
97 | /* codec command action codes */ | ||
98 | enum codec_command_action { | ||
99 | CODEC_ACTION_HALT = -1, | ||
100 | CODEC_ACTION_NULL = 0, | ||
101 | CODEC_ACTION_SEEK_TIME = 1, | ||
102 | }; | ||
103 | |||
91 | /* NOTE: To support backwards compatibility, only add new functions at | 104 | /* NOTE: To support backwards compatibility, only add new functions at |
92 | the end of the structure. Every time you add a new function, | 105 | the end of the structure. Every time you add a new function, |
93 | remember to increase CODEC_API_VERSION. If you make changes to the | 106 | remember to increase CODEC_API_VERSION. If you make changes to the |
@@ -95,24 +108,12 @@ enum codec_status { | |||
95 | version | 108 | version |
96 | */ | 109 | */ |
97 | struct codec_api { | 110 | struct codec_api { |
98 | |||
99 | off_t filesize; /* Total file length */ | 111 | off_t filesize; /* Total file length */ |
100 | off_t curpos; /* Current buffer position */ | 112 | off_t curpos; /* Current buffer position */ |
101 | 113 | ||
102 | /* For gapless mp3 */ | ||
103 | struct mp3entry *id3; /* TAG metadata pointer */ | 114 | struct mp3entry *id3; /* TAG metadata pointer */ |
104 | bool *taginfo_ready; /* Is metadata read */ | 115 | int audio_hid; /* Current audio handle */ |
105 | 116 | ||
106 | /* Codec should periodically check if stop_codec is set to true. | ||
107 | In case it is, codec must return immediately */ | ||
108 | volatile bool stop_codec; | ||
109 | /* Codec should periodically check if new_track is non zero. | ||
110 | When it is, the codec should request a new track. */ | ||
111 | volatile int new_track; | ||
112 | /* If seek_time != 0, codec should seek to that song position (in ms) | ||
113 | if codec supports seeking. */ | ||
114 | volatile long seek_time; | ||
115 | |||
116 | /* The dsp instance to be used for audio output */ | 117 | /* The dsp instance to be used for audio output */ |
117 | struct dsp_config *dsp; | 118 | struct dsp_config *dsp; |
118 | 119 | ||
@@ -138,14 +139,12 @@ struct codec_api { | |||
138 | bool (*seek_buffer)(size_t newpos); | 139 | bool (*seek_buffer)(size_t newpos); |
139 | /* Codec should call this function when it has done the seeking. */ | 140 | /* Codec should call this function when it has done the seeking. */ |
140 | void (*seek_complete)(void); | 141 | void (*seek_complete)(void); |
141 | /* Request file change from file buffer. Returns true is next | 142 | /* Update the current position */ |
142 | track is available and changed. If return value is false, | ||
143 | codec should exit immediately with PLUGIN_OK status. */ | ||
144 | bool (*request_next_track)(void); | ||
145 | |||
146 | void (*set_offset)(size_t value); | 143 | void (*set_offset)(size_t value); |
147 | /* Configure different codec buffer parameters. */ | 144 | /* Configure different codec buffer parameters. */ |
148 | void (*configure)(int setting, intptr_t value); | 145 | void (*configure)(int setting, intptr_t value); |
146 | /* Obtain command action on what to do next */ | ||
147 | enum codec_command_action (*get_command)(intptr_t *param); | ||
149 | 148 | ||
150 | /* kernel/ system */ | 149 | /* kernel/ system */ |
151 | #if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE | 150 | #if defined(CPU_ARM) && CONFIG_PLATFORM & PLATFORM_NATIVE |
@@ -231,7 +230,8 @@ struct codec_api { | |||
231 | /* codec header */ | 230 | /* codec header */ |
232 | struct codec_header { | 231 | struct codec_header { |
233 | struct lc_header lc_hdr; /* must be first */ | 232 | struct lc_header lc_hdr; /* must be first */ |
234 | enum codec_status(*entry_point)(void); | 233 | enum codec_status(*entry_point)(enum codec_entry_call_reason reason); |
234 | enum codec_status(*run_proc)(void); | ||
235 | struct codec_api **api; | 235 | struct codec_api **api; |
236 | }; | 236 | }; |
237 | 237 | ||
@@ -248,13 +248,15 @@ extern unsigned char plugin_end_addr[]; | |||
248 | const struct codec_header __header \ | 248 | const struct codec_header __header \ |
249 | __attribute__ ((section (".header")))= { \ | 249 | __attribute__ ((section (".header")))= { \ |
250 | { CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ | 250 | { CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ |
251 | plugin_start_addr, plugin_end_addr }, codec_start, &ci }; | 251 | plugin_start_addr, plugin_end_addr }, codec_start, \ |
252 | codec_run, &ci }; | ||
252 | /* encoders */ | 253 | /* encoders */ |
253 | #define CODEC_ENC_HEADER \ | 254 | #define CODEC_ENC_HEADER \ |
254 | const struct codec_header __header \ | 255 | const struct codec_header __header \ |
255 | __attribute__ ((section (".header")))= { \ | 256 | __attribute__ ((section (".header")))= { \ |
256 | { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ | 257 | { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, \ |
257 | plugin_start_addr, plugin_end_addr }, codec_start, &ci }; | 258 | plugin_start_addr, plugin_end_addr }, codec_start, \ |
259 | codec_run, &ci }; | ||
258 | 260 | ||
259 | #else /* def SIMULATOR */ | 261 | #else /* def SIMULATOR */ |
260 | /* decoders */ | 262 | /* decoders */ |
@@ -262,12 +264,12 @@ extern unsigned char plugin_end_addr[]; | |||
262 | const struct codec_header __header \ | 264 | const struct codec_header __header \ |
263 | __attribute__((visibility("default"))) = { \ | 265 | __attribute__((visibility("default"))) = { \ |
264 | { CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \ | 266 | { CODEC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \ |
265 | codec_start, &ci }; | 267 | codec_start, codec_run, &ci }; |
266 | /* encoders */ | 268 | /* encoders */ |
267 | #define CODEC_ENC_HEADER \ | 269 | #define CODEC_ENC_HEADER \ |
268 | const struct codec_header __header = { \ | 270 | const struct codec_header __header = { \ |
269 | { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \ | 271 | { CODEC_ENC_MAGIC, TARGET_ID, CODEC_API_VERSION, NULL, NULL }, \ |
270 | codec_start, &ci }; | 272 | codec_start, codec_run, &ci }; |
271 | #endif /* SIMULATOR */ | 273 | #endif /* SIMULATOR */ |
272 | #endif /* CODEC */ | 274 | #endif /* CODEC */ |
273 | 275 | ||
@@ -276,13 +278,14 @@ extern unsigned char plugin_end_addr[]; | |||
276 | void codec_get_full_path(char *path, const char *codec_root_fn); | 278 | void codec_get_full_path(char *path, const char *codec_root_fn); |
277 | 279 | ||
278 | /* defined by the codec loader (codec.c) */ | 280 | /* defined by the codec loader (codec.c) */ |
279 | void * codec_load_buf(int hid, struct codec_api *api); | 281 | int codec_load_buf(int hid, struct codec_api *api); |
280 | void * codec_load_file(const char* codec, struct codec_api *api); | 282 | int codec_load_file(const char* codec, struct codec_api *api); |
281 | int codec_begin(void *handle); | 283 | int codec_run_proc(void); |
282 | void codec_close(void *handle); | 284 | int codec_halt(void); |
285 | int codec_close(void); | ||
283 | 286 | ||
284 | /* defined by the codec */ | 287 | /* defined by the codec */ |
285 | enum codec_status codec_start(void); | 288 | enum codec_status codec_start(enum codec_entry_call_reason reason); |
286 | enum codec_status codec_main(void); | 289 | enum codec_status codec_run(void); |
287 | 290 | ||
288 | #endif | 291 | #endif /* _CODECS_H_ */ |
diff --git a/apps/codecs/a52.c b/apps/codecs/a52.c index 00fdeea309..4cd293e37f 100644 --- a/apps/codecs/a52.c +++ b/apps/codecs/a52.c | |||
@@ -116,27 +116,31 @@ static void a52_decode_data(uint8_t *start, uint8_t *end) | |||
116 | } | 116 | } |
117 | 117 | ||
118 | /* this is the codec entry point */ | 118 | /* this is the codec entry point */ |
119 | enum codec_status codec_main(void) | 119 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
120 | { | ||
121 | if (reason == CODEC_LOAD) { | ||
122 | /* Generic codec initialisation */ | ||
123 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
124 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
125 | } | ||
126 | else if (reason == CODEC_UNLOAD) { | ||
127 | if (state) | ||
128 | a52_free(state); | ||
129 | } | ||
130 | |||
131 | return CODEC_OK; | ||
132 | } | ||
133 | |||
134 | /* this is called for each file to process */ | ||
135 | enum codec_status codec_run(void) | ||
120 | { | 136 | { |
121 | size_t n; | 137 | size_t n; |
122 | unsigned char *filebuf; | 138 | unsigned char *filebuf; |
123 | int sample_loc; | 139 | int sample_loc; |
124 | int retval; | 140 | intptr_t param; |
125 | 141 | ||
126 | /* Generic codec initialisation */ | 142 | if (codec_init()) |
127 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | 143 | return CODEC_ERROR; |
128 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
129 | |||
130 | next_track: | ||
131 | retval = CODEC_OK; | ||
132 | |||
133 | if (codec_init()) { | ||
134 | retval = CODEC_ERROR; | ||
135 | goto exit; | ||
136 | } | ||
137 | |||
138 | if (codec_wait_taginfo() != 0) | ||
139 | goto request_next_track; | ||
140 | 144 | ||
141 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 145 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
142 | codec_set_replaygain(ci->id3); | 146 | codec_set_replaygain(ci->id3); |
@@ -153,15 +157,18 @@ next_track: | |||
153 | } | 157 | } |
154 | } | 158 | } |
155 | else { | 159 | else { |
160 | ci->seek_buffer(ci->id3->first_frame_offset); | ||
156 | samplesdone = 0; | 161 | samplesdone = 0; |
157 | } | 162 | } |
158 | 163 | ||
159 | while (1) { | 164 | while (1) { |
160 | if (ci->stop_codec || ci->new_track) | 165 | enum codec_command_action action = ci->get_command(¶m); |
166 | |||
167 | if (action == CODEC_ACTION_HALT) | ||
161 | break; | 168 | break; |
162 | 169 | ||
163 | if (ci->seek_time) { | 170 | if (action == CODEC_ACTION_SEEK_TIME) { |
164 | sample_loc = (ci->seek_time - 1)/1000 * ci->id3->frequency; | 171 | sample_loc = param/1000 * ci->id3->frequency; |
165 | 172 | ||
166 | if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*ci->id3->bytesperframe)) { | 173 | if (ci->seek_buffer((sample_loc/A52_SAMPLESPERFRAME)*ci->id3->bytesperframe)) { |
167 | samplesdone = sample_loc; | 174 | samplesdone = sample_loc; |
@@ -179,11 +186,5 @@ next_track: | |||
179 | ci->advance_buffer(n); | 186 | ci->advance_buffer(n); |
180 | } | 187 | } |
181 | 188 | ||
182 | request_next_track: | 189 | return CODEC_OK; |
183 | if (ci->request_next_track()) | ||
184 | goto next_track; | ||
185 | |||
186 | exit: | ||
187 | a52_free(state); | ||
188 | return retval; | ||
189 | } | 190 | } |
diff --git a/apps/codecs/a52_rm.c b/apps/codecs/a52_rm.c index f5e4923292..c1930aa7b4 100644 --- a/apps/codecs/a52_rm.c +++ b/apps/codecs/a52_rm.c | |||
@@ -124,36 +124,44 @@ static void a52_decode_data(uint8_t *start, uint8_t *end) | |||
124 | } | 124 | } |
125 | } | 125 | } |
126 | 126 | ||
127 | |||
128 | /* this is the codec entry point */ | 127 | /* this is the codec entry point */ |
129 | enum codec_status codec_main(void) | 128 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
129 | { | ||
130 | if (reason == CODEC_LOAD) { | ||
131 | /* Generic codec initialisation */ | ||
132 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
133 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
134 | } | ||
135 | else if (reason == CODEC_UNLOAD) { | ||
136 | if (state) | ||
137 | a52_free(state); | ||
138 | } | ||
139 | |||
140 | return CODEC_OK; | ||
141 | } | ||
142 | |||
143 | /* this is called for each file to process */ | ||
144 | enum codec_status codec_run(void) | ||
130 | { | 145 | { |
131 | size_t n; | 146 | size_t n; |
132 | uint8_t *filebuf; | 147 | uint8_t *filebuf; |
133 | int retval, consumed, packet_offset; | 148 | int consumed, packet_offset; |
134 | int playback_on = -1; | 149 | int playback_on = -1; |
135 | size_t resume_offset; | 150 | size_t resume_offset; |
136 | 151 | intptr_t param; | |
137 | /* Generic codec initialisation */ | 152 | enum codec_command_action action = CODEC_ACTION_NULL; |
138 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
139 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
140 | |||
141 | next_track: | ||
142 | retval = CODEC_OK; | ||
143 | 153 | ||
144 | if (codec_init()) { | 154 | if (codec_init()) { |
145 | retval = CODEC_ERROR; | 155 | return CODEC_ERROR; |
146 | goto exit; | ||
147 | } | 156 | } |
148 | 157 | ||
149 | if (codec_wait_taginfo() != 0) | ||
150 | goto request_next_track; | ||
151 | |||
152 | resume_offset = ci->id3->offset; | 158 | resume_offset = ci->id3->offset; |
153 | 159 | ||
154 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 160 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
155 | codec_set_replaygain(ci->id3); | 161 | codec_set_replaygain(ci->id3); |
156 | 162 | ||
163 | ci->seek_buffer(ci->id3->first_frame_offset); | ||
164 | |||
157 | /* Intializations */ | 165 | /* Intializations */ |
158 | state = a52_init(0); | 166 | state = a52_init(0); |
159 | ci->memset(&rmctx,0,sizeof(RMContext)); | 167 | ci->memset(&rmctx,0,sizeof(RMContext)); |
@@ -165,26 +173,34 @@ next_track: | |||
165 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; | 173 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; |
166 | /* put number of subpackets to skip in resume_offset */ | 174 | /* put number of subpackets to skip in resume_offset */ |
167 | resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); | 175 | resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); |
168 | ci->seek_time = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); | 176 | param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); |
177 | action = CODEC_ACTION_SEEK_TIME; | ||
178 | } | ||
179 | else { | ||
180 | /* Seek to the first packet */ | ||
181 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE ); | ||
169 | } | 182 | } |
170 | |||
171 | /* Seek to the first packet */ | ||
172 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE ); | ||
173 | 183 | ||
174 | /* The main decoding loop */ | 184 | /* The main decoding loop */ |
175 | while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) { | 185 | while((unsigned)rmctx.audio_pkt_cnt < rmctx.nb_packets) { |
176 | ci->yield(); | 186 | if (action == CODEC_ACTION_NULL) |
177 | if (ci->stop_codec || ci->new_track) | 187 | action = ci->get_command(¶m); |
188 | |||
189 | if (action == CODEC_ACTION_HALT) | ||
178 | break; | 190 | break; |
179 | 191 | ||
180 | if (ci->seek_time) { | 192 | if (action == CODEC_ACTION_SEEK_TIME) { |
181 | packet_offset = ci->seek_time / ((rmctx.block_align*8*1000)/rmctx.bit_rate); | 193 | packet_offset = param / ((rmctx.block_align*8*1000)/rmctx.bit_rate); |
182 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE)); | 194 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + |
195 | packet_offset*(rmctx.block_align + PACKET_HEADER_SIZE)); | ||
183 | rmctx.audio_pkt_cnt = packet_offset; | 196 | rmctx.audio_pkt_cnt = packet_offset; |
184 | samplesdone = (rmctx.sample_rate/1000 * ci->seek_time); | 197 | samplesdone = (rmctx.sample_rate/1000 * param); |
198 | ci->set_elapsed(samplesdone/(frequency/1000)); | ||
185 | ci->seek_complete(); | 199 | ci->seek_complete(); |
186 | } | 200 | } |
187 | 201 | ||
202 | action = CODEC_ACTION_NULL; | ||
203 | |||
188 | filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE); | 204 | filebuf = ci->request_buffer(&n, rmctx.block_align + PACKET_HEADER_SIZE); |
189 | consumed = rm_get_packet(&filebuf, &rmctx, &pkt); | 205 | consumed = rm_get_packet(&filebuf, &rmctx, &pkt); |
190 | 206 | ||
@@ -195,8 +211,7 @@ next_track: | |||
195 | return CODEC_ERROR; | 211 | return CODEC_ERROR; |
196 | } | 212 | } |
197 | else { | 213 | else { |
198 | retval = CODEC_OK; | 214 | break; |
199 | goto exit; | ||
200 | } | 215 | } |
201 | } | 216 | } |
202 | 217 | ||
@@ -205,11 +220,5 @@ next_track: | |||
205 | ci->advance_buffer(pkt.length); | 220 | ci->advance_buffer(pkt.length); |
206 | } | 221 | } |
207 | 222 | ||
208 | request_next_track: | 223 | return CODEC_OK; |
209 | if (ci->request_next_track()) | ||
210 | goto next_track; | ||
211 | |||
212 | exit: | ||
213 | a52_free(state); | ||
214 | return retval; | ||
215 | } | 224 | } |
diff --git a/apps/codecs/aac.c b/apps/codecs/aac.c index 5638dc49a9..90cf0438ce 100644 --- a/apps/codecs/aac.c +++ b/apps/codecs/aac.c | |||
@@ -33,7 +33,19 @@ CODEC_HEADER | |||
33 | #define FAAD_BYTE_BUFFER_SIZE (2048-12) | 33 | #define FAAD_BYTE_BUFFER_SIZE (2048-12) |
34 | 34 | ||
35 | /* this is the codec entry point */ | 35 | /* this is the codec entry point */ |
36 | enum codec_status codec_main(void) | 36 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
37 | { | ||
38 | if (reason == CODEC_LOAD) { | ||
39 | /* Generic codec initialisation */ | ||
40 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
41 | ci->configure(DSP_SET_SAMPLE_DEPTH, 29); | ||
42 | } | ||
43 | |||
44 | return CODEC_OK; | ||
45 | } | ||
46 | |||
47 | /* this is called for each file to process */ | ||
48 | enum codec_status codec_run(void) | ||
37 | { | 49 | { |
38 | /* Note that when dealing with QuickTime/MPEG4 files, terminology is | 50 | /* Note that when dealing with QuickTime/MPEG4 files, terminology is |
39 | * a bit confusing. Files with sound are split up in chunks, where | 51 | * a bit confusing. Files with sound are split up in chunks, where |
@@ -59,25 +71,15 @@ enum codec_status codec_main(void) | |||
59 | uint32_t sbr_fac = 1; | 71 | uint32_t sbr_fac = 1; |
60 | unsigned char c = 0; | 72 | unsigned char c = 0; |
61 | void *ret; | 73 | void *ret; |
62 | 74 | intptr_t param; | |
63 | /* Generic codec initialisation */ | ||
64 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
65 | ci->configure(DSP_SET_SAMPLE_DEPTH, 29); | ||
66 | |||
67 | next_track: | ||
68 | err = CODEC_OK; | ||
69 | 75 | ||
70 | /* Clean and initialize decoder structures */ | 76 | /* Clean and initialize decoder structures */ |
71 | memset(&demux_res , 0, sizeof(demux_res)); | 77 | memset(&demux_res , 0, sizeof(demux_res)); |
72 | if (codec_init()) { | 78 | if (codec_init()) { |
73 | LOGF("FAAD: Codec init error\n"); | 79 | LOGF("FAAD: Codec init error\n"); |
74 | err = CODEC_ERROR; | 80 | return CODEC_ERROR; |
75 | goto exit; | ||
76 | } | 81 | } |
77 | 82 | ||
78 | if (codec_wait_taginfo() != 0) | ||
79 | goto done; | ||
80 | |||
81 | file_offset = ci->id3->offset; | 83 | file_offset = ci->id3->offset; |
82 | 84 | ||
83 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 85 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
@@ -85,12 +87,13 @@ next_track: | |||
85 | 87 | ||
86 | stream_create(&input_stream,ci); | 88 | stream_create(&input_stream,ci); |
87 | 89 | ||
90 | ci->seek_buffer(ci->id3->first_frame_offset); | ||
91 | |||
88 | /* if qtmovie_read returns successfully, the stream is up to | 92 | /* if qtmovie_read returns successfully, the stream is up to |
89 | * the movie data, which can be used directly by the decoder */ | 93 | * the movie data, which can be used directly by the decoder */ |
90 | if (!qtmovie_read(&input_stream, &demux_res)) { | 94 | if (!qtmovie_read(&input_stream, &demux_res)) { |
91 | LOGF("FAAD: File init error\n"); | 95 | LOGF("FAAD: File init error\n"); |
92 | err = CODEC_ERROR; | 96 | return CODEC_ERROR; |
93 | goto done; | ||
94 | } | 97 | } |
95 | 98 | ||
96 | /* initialise the sound converter */ | 99 | /* initialise the sound converter */ |
@@ -98,8 +101,7 @@ next_track: | |||
98 | 101 | ||
99 | if (!decoder) { | 102 | if (!decoder) { |
100 | LOGF("FAAD: Decode open error\n"); | 103 | LOGF("FAAD: Decode open error\n"); |
101 | err = CODEC_ERROR; | 104 | return CODEC_ERROR; |
102 | goto done; | ||
103 | } | 105 | } |
104 | 106 | ||
105 | NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); | 107 | NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); |
@@ -109,8 +111,7 @@ next_track: | |||
109 | err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c); | 111 | err = NeAACDecInit2(decoder, demux_res.codecdata, demux_res.codecdata_len, &s, &c); |
110 | if (err) { | 112 | if (err) { |
111 | LOGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type); | 113 | LOGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type); |
112 | err = CODEC_ERROR; | 114 | return CODEC_ERROR; |
113 | goto done; | ||
114 | } | 115 | } |
115 | 116 | ||
116 | #ifdef SBR_DEC | 117 | #ifdef SBR_DEC |
@@ -150,20 +151,19 @@ next_track: | |||
150 | 151 | ||
151 | /* The main decoding loop */ | 152 | /* The main decoding loop */ |
152 | while (i < demux_res.num_sample_byte_sizes) { | 153 | while (i < demux_res.num_sample_byte_sizes) { |
153 | ci->yield(); | 154 | enum codec_command_action action = ci->get_command(¶m); |
154 | 155 | ||
155 | if (ci->stop_codec || ci->new_track) { | 156 | if (action == CODEC_ACTION_HALT) |
156 | break; | 157 | break; |
157 | } | ||
158 | 158 | ||
159 | /* Deal with any pending seek requests */ | 159 | /* Deal with any pending seek requests */ |
160 | if (ci->seek_time) { | 160 | if (action == CODEC_ACTION_SEEK_TIME) { |
161 | /* Seek to the desired time position. Important: When seeking in SBR | 161 | /* Seek to the desired time position. Important: When seeking in SBR |
162 | * upsampling files the seek_time must be divided by 2 when calling | 162 | * upsampling files the seek_time must be divided by 2 when calling |
163 | * m4a_seek and the resulting sound_samples_done must be expanded | 163 | * m4a_seek and the resulting sound_samples_done must be expanded |
164 | * by a factor 2. This is done via using sbr_fac. */ | 164 | * by a factor 2. This is done via using sbr_fac. */ |
165 | if (m4a_seek(&demux_res, &input_stream, | 165 | if (m4a_seek(&demux_res, &input_stream, |
166 | ((ci->seek_time-1)/10/sbr_fac)*(ci->id3->frequency/100), | 166 | (param/10/sbr_fac)*(ci->id3->frequency/100), |
167 | &sound_samples_done, (int*) &i)) { | 167 | &sound_samples_done, (int*) &i)) { |
168 | sound_samples_done *= sbr_fac; | 168 | sound_samples_done *= sbr_fac; |
169 | elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); | 169 | elapsed_time = (sound_samples_done * 10) / (ci->id3->frequency / 100); |
@@ -194,8 +194,7 @@ next_track: | |||
194 | else if (file_offset == 0) | 194 | else if (file_offset == 0) |
195 | { | 195 | { |
196 | LOGF("AAC: get_sample_offset error\n"); | 196 | LOGF("AAC: get_sample_offset error\n"); |
197 | err = CODEC_ERROR; | 197 | return CODEC_ERROR; |
198 | goto done; | ||
199 | } | 198 | } |
200 | 199 | ||
201 | /* Request the required number of bytes from the input buffer */ | 200 | /* Request the required number of bytes from the input buffer */ |
@@ -207,8 +206,7 @@ next_track: | |||
207 | /* NeAACDecDecode may sometimes return NULL without setting error. */ | 206 | /* NeAACDecDecode may sometimes return NULL without setting error. */ |
208 | if (ret == NULL || frame_info.error > 0) { | 207 | if (ret == NULL || frame_info.error > 0) { |
209 | LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error)); | 208 | LOGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error)); |
210 | err = CODEC_ERROR; | 209 | return CODEC_ERROR; |
211 | goto done; | ||
212 | } | 210 | } |
213 | 211 | ||
214 | /* Advance codec buffer (no need to call set_offset because of this) */ | 212 | /* Advance codec buffer (no need to call set_offset because of this) */ |
@@ -251,12 +249,6 @@ next_track: | |||
251 | i++; | 249 | i++; |
252 | } | 250 | } |
253 | 251 | ||
254 | done: | ||
255 | LOGF("AAC: Decoded %lu samples\n", (unsigned long)sound_samples_done); | 252 | LOGF("AAC: Decoded %lu samples\n", (unsigned long)sound_samples_done); |
256 | 253 | return CODEC_OK; | |
257 | if (ci->request_next_track()) | ||
258 | goto next_track; | ||
259 | |||
260 | exit: | ||
261 | return err; | ||
262 | } | 254 | } |
diff --git a/apps/codecs/adx.c b/apps/codecs/adx.c index 832b94797b..a1b57fce58 100644 --- a/apps/codecs/adx.c +++ b/apps/codecs/adx.c | |||
@@ -45,7 +45,19 @@ static const long cutoff = 500; | |||
45 | static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR; | 45 | static int16_t samples[WAV_CHUNK_SIZE] IBSS_ATTR; |
46 | 46 | ||
47 | /* this is the codec entry point */ | 47 | /* this is the codec entry point */ |
48 | enum codec_status codec_main(void) | 48 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
49 | { | ||
50 | if (reason == CODEC_LOAD) { | ||
51 | /* Generic codec initialisation */ | ||
52 | /* we only render 16 bits */ | ||
53 | ci->configure(DSP_SET_SAMPLE_DEPTH, 16); | ||
54 | } | ||
55 | |||
56 | return CODEC_OK; | ||
57 | } | ||
58 | |||
59 | /* this is called for each file to process */ | ||
60 | enum codec_status codec_run(void) | ||
49 | { | 61 | { |
50 | int channels; | 62 | int channels; |
51 | int sampleswritten, i; | 63 | int sampleswritten, i; |
@@ -62,12 +74,8 @@ enum codec_status codec_main(void) | |||
62 | off_t chanstart, bufoff; | 74 | off_t chanstart, bufoff; |
63 | /*long coef1=0x7298L,coef2=-0x3350L;*/ | 75 | /*long coef1=0x7298L,coef2=-0x3350L;*/ |
64 | long coef1, coef2; | 76 | long coef1, coef2; |
77 | intptr_t param; | ||
65 | 78 | ||
66 | /* Generic codec initialisation */ | ||
67 | /* we only render 16 bits */ | ||
68 | ci->configure(DSP_SET_SAMPLE_DEPTH, 16); | ||
69 | |||
70 | next_track: | ||
71 | DEBUGF("ADX: next_track\n"); | 79 | DEBUGF("ADX: next_track\n"); |
72 | if (codec_init()) { | 80 | if (codec_init()) { |
73 | return CODEC_ERROR; | 81 | return CODEC_ERROR; |
@@ -77,10 +85,6 @@ next_track: | |||
77 | /* init history */ | 85 | /* init history */ |
78 | ch1_1=ch1_2=ch2_1=ch2_2=0; | 86 | ch1_1=ch1_2=ch2_1=ch2_2=0; |
79 | 87 | ||
80 | /* wait for track info to load */ | ||
81 | if (codec_wait_taginfo() != 0) | ||
82 | goto request_next_track; | ||
83 | |||
84 | codec_set_replaygain(ci->id3); | 88 | codec_set_replaygain(ci->id3); |
85 | 89 | ||
86 | /* Get header */ | 90 | /* Get header */ |
@@ -226,10 +230,10 @@ next_track: | |||
226 | /* The main decoder loop */ | 230 | /* The main decoder loop */ |
227 | 231 | ||
228 | while (!endofstream) { | 232 | while (!endofstream) { |
229 | ci->yield(); | 233 | enum codec_command_action action = ci->get_command(¶m); |
230 | if (ci->stop_codec || ci->new_track) { | 234 | |
235 | if (action == CODEC_ACTION_HALT) | ||
231 | break; | 236 | break; |
232 | } | ||
233 | 237 | ||
234 | /* do we need to loop? */ | 238 | /* do we need to loop? */ |
235 | if (bufoff > end_adr-18*channels && looping) { | 239 | if (bufoff > end_adr-18*channels && looping) { |
@@ -254,17 +258,17 @@ next_track: | |||
254 | } | 258 | } |
255 | 259 | ||
256 | /* do we need to seek? */ | 260 | /* do we need to seek? */ |
257 | if (ci->seek_time) { | 261 | if (action == CODEC_ACTION_SEEK_TIME) { |
258 | uint32_t newpos; | 262 | uint32_t newpos; |
259 | 263 | ||
260 | DEBUGF("ADX: seek to %ldms\n",ci->seek_time); | 264 | DEBUGF("ADX: seek to %ldms\n", (long)param); |
261 | 265 | ||
262 | endofstream = 0; | 266 | endofstream = 0; |
263 | loop_count = 0; | 267 | loop_count = 0; |
264 | fade_count = -1; /* disable fade */ | 268 | fade_count = -1; /* disable fade */ |
265 | fade_frames = 1; | 269 | fade_frames = 1; |
266 | 270 | ||
267 | newpos = (((uint64_t)avgbytespersec*(ci->seek_time - 1)) | 271 | newpos = (((uint64_t)avgbytespersec*param) |
268 | / (1000LL*18*channels))*(18*channels); | 272 | / (1000LL*18*channels))*(18*channels); |
269 | bufoff = chanstart + newpos; | 273 | bufoff = chanstart + newpos; |
270 | while (bufoff > end_adr-18*channels) { | 274 | while (bufoff > end_adr-18*channels) { |
@@ -385,9 +389,5 @@ next_track: | |||
385 | 1000LL/avgbytespersec); | 389 | 1000LL/avgbytespersec); |
386 | } | 390 | } |
387 | 391 | ||
388 | request_next_track: | ||
389 | if (ci->request_next_track()) | ||
390 | goto next_track; | ||
391 | |||
392 | return CODEC_OK; | 392 | return CODEC_OK; |
393 | } | 393 | } |
diff --git a/apps/codecs/aiff.c b/apps/codecs/aiff.c index d4cf8660dd..3fc137eaeb 100644 --- a/apps/codecs/aiff.c +++ b/apps/codecs/aiff.c | |||
@@ -61,9 +61,20 @@ static const struct pcm_codec *get_codec(uint32_t formattag) | |||
61 | return NULL; | 61 | return NULL; |
62 | } | 62 | } |
63 | 63 | ||
64 | enum codec_status codec_main(void) | 64 | /* this is the codec entry point */ |
65 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
66 | { | ||
67 | if (reason == CODEC_LOAD) { | ||
68 | /* Generic codec initialisation */ | ||
69 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
70 | } | ||
71 | |||
72 | return CODEC_OK; | ||
73 | } | ||
74 | |||
75 | /* this is called for each file to process */ | ||
76 | enum codec_status codec_run(void) | ||
65 | { | 77 | { |
66 | int status; | ||
67 | struct pcm_format format; | 78 | struct pcm_format format; |
68 | uint32_t bytesdone, decodedsamples; | 79 | uint32_t bytesdone, decodedsamples; |
69 | uint32_t num_sample_frames = 0; | 80 | uint32_t num_sample_frames = 0; |
@@ -77,38 +88,28 @@ enum codec_status codec_main(void) | |||
77 | bool is_aifc = false; | 88 | bool is_aifc = false; |
78 | const struct pcm_codec *codec; | 89 | const struct pcm_codec *codec; |
79 | uint32_t size; | 90 | uint32_t size; |
80 | 91 | intptr_t param; | |
81 | /* Generic codec initialisation */ | ||
82 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
83 | |||
84 | next_track: | ||
85 | status = CODEC_OK; | ||
86 | 92 | ||
87 | if (codec_init()) { | 93 | if (codec_init()) { |
88 | status = CODEC_ERROR; | 94 | return CODEC_ERROR; |
89 | goto exit; | ||
90 | } | 95 | } |
91 | 96 | ||
92 | if (codec_wait_taginfo() != 0) | ||
93 | goto done; | ||
94 | |||
95 | codec_set_replaygain(ci->id3); | 97 | codec_set_replaygain(ci->id3); |
96 | 98 | ||
97 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ | 99 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ |
98 | bytesdone = ci->id3->offset; | 100 | bytesdone = ci->id3->offset; |
99 | 101 | ||
100 | /* assume the AIFF header is less than 1024 bytes */ | 102 | /* assume the AIFF header is less than 1024 bytes */ |
103 | ci->seek_buffer(0); | ||
101 | buf = ci->request_buffer(&n, 1024); | 104 | buf = ci->request_buffer(&n, 1024); |
102 | if (n < 54) { | 105 | if (n < 54) { |
103 | status = CODEC_ERROR; | 106 | return CODEC_ERROR; |
104 | goto done; | ||
105 | } | 107 | } |
106 | 108 | ||
107 | if (memcmp(buf, "FORM", 4) != 0) | 109 | if (memcmp(buf, "FORM", 4) != 0) |
108 | { | 110 | { |
109 | DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[0]); | 111 | DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[0]); |
110 | status = CODEC_ERROR; | 112 | return CODEC_ERROR; |
111 | goto done; | ||
112 | } | 113 | } |
113 | if (memcmp(&buf[8], "AIFF", 4) == 0) | 114 | if (memcmp(&buf[8], "AIFF", 4) == 0) |
114 | is_aifc = false; | 115 | is_aifc = false; |
@@ -117,8 +118,7 @@ next_track: | |||
117 | else | 118 | else |
118 | { | 119 | { |
119 | DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[8]); | 120 | DEBUGF("CODEC_ERROR: does not aiff format %4.4s\n", (char*)&buf[8]); |
120 | status = CODEC_ERROR; | 121 | return CODEC_ERROR; |
121 | goto done; | ||
122 | } | 122 | } |
123 | 123 | ||
124 | buf += 12; | 124 | buf += 12; |
@@ -141,8 +141,7 @@ next_track: | |||
141 | { | 141 | { |
142 | DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu < %d\n", | 142 | DEBUGF("CODEC_ERROR: 'COMM' chunk size=%lu < %d\n", |
143 | (unsigned long)size, (is_aifc)?22:18); | 143 | (unsigned long)size, (is_aifc)?22:18); |
144 | status = CODEC_ERROR; | 144 | return CODEC_ERROR; |
145 | goto done; | ||
146 | } | 145 | } |
147 | /* num_channels */ | 146 | /* num_channels */ |
148 | format.channels = ((buf[8]<<8)|buf[9]); | 147 | format.channels = ((buf[8]<<8)|buf[9]); |
@@ -154,8 +153,7 @@ next_track: | |||
154 | /* sample_rate (don't use last 4 bytes, only integer fs) */ | 153 | /* sample_rate (don't use last 4 bytes, only integer fs) */ |
155 | if (buf[16] != 0x40) { | 154 | if (buf[16] != 0x40) { |
156 | DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n"); | 155 | DEBUGF("CODEC_ERROR: weird sampling rate (no @)\n"); |
157 | status = CODEC_ERROR; | 156 | return CODEC_ERROR; |
158 | goto done; | ||
159 | } | 157 | } |
160 | format.samplespersec = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1; | 158 | format.samplespersec = ((buf[18]<<24)|(buf[19]<<16)|(buf[20]<<8)|buf[21])+1; |
161 | format.samplespersec >>= (16 + 14 - buf[17]); | 159 | format.samplespersec >>= (16 + 14 - buf[17]); |
@@ -181,8 +179,7 @@ next_track: | |||
181 | } else if (memcmp(buf, "SSND", 4)==0) { | 179 | } else if (memcmp(buf, "SSND", 4)==0) { |
182 | if (format.bitspersample == 0) { | 180 | if (format.bitspersample == 0) { |
183 | DEBUGF("CODEC_ERROR: unsupported chunk order\n"); | 181 | DEBUGF("CODEC_ERROR: unsupported chunk order\n"); |
184 | status = CODEC_ERROR; | 182 | return CODEC_ERROR; |
185 | goto done; | ||
186 | } | 183 | } |
187 | /* offset2snd */ | 184 | /* offset2snd */ |
188 | offset2snd = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11]; | 185 | offset2snd = (buf[8]<<24)|(buf[9]<<16)|(buf[10]<<8)|buf[11]; |
@@ -205,21 +202,18 @@ next_track: | |||
205 | buf += size; | 202 | buf += size; |
206 | if (n < size) { | 203 | if (n < size) { |
207 | DEBUGF("CODEC_ERROR: AIFF header size > 1024\n"); | 204 | DEBUGF("CODEC_ERROR: AIFF header size > 1024\n"); |
208 | status = CODEC_ERROR; | 205 | return CODEC_ERROR; |
209 | goto done; | ||
210 | } | 206 | } |
211 | n -= size; | 207 | n -= size; |
212 | } /* while 'SSND' */ | 208 | } /* while 'SSND' */ |
213 | 209 | ||
214 | if (format.channels == 0) { | 210 | if (format.channels == 0) { |
215 | DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n"); | 211 | DEBUGF("CODEC_ERROR: 'COMM' chunk not found or 0-channels file\n"); |
216 | status = CODEC_ERROR; | 212 | return CODEC_ERROR; |
217 | goto done; | ||
218 | } | 213 | } |
219 | if (format.numbytes == 0) { | 214 | if (format.numbytes == 0) { |
220 | DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n"); | 215 | DEBUGF("CODEC_ERROR: 'SSND' chunk not found or has zero length\n"); |
221 | status = CODEC_ERROR; | 216 | return CODEC_ERROR; |
222 | goto done; | ||
223 | } | 217 | } |
224 | 218 | ||
225 | codec = get_codec(format.formattag); | 219 | codec = get_codec(format.formattag); |
@@ -227,14 +221,12 @@ next_track: | |||
227 | { | 221 | { |
228 | DEBUGF("CODEC_ERROR: AIFC does not support compressionType: 0x%x\n", | 222 | DEBUGF("CODEC_ERROR: AIFC does not support compressionType: 0x%x\n", |
229 | (unsigned int)format.formattag); | 223 | (unsigned int)format.formattag); |
230 | status = CODEC_ERROR; | 224 | return CODEC_ERROR; |
231 | goto done; | ||
232 | } | 225 | } |
233 | 226 | ||
234 | if (!codec->set_format(&format)) | 227 | if (!codec->set_format(&format)) |
235 | { | 228 | { |
236 | status = CODEC_ERROR; | 229 | return CODEC_ERROR; |
237 | goto done; | ||
238 | } | 230 | } |
239 | 231 | ||
240 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 232 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
@@ -245,21 +237,18 @@ next_track: | |||
245 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | 237 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); |
246 | } else { | 238 | } else { |
247 | DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n"); | 239 | DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n"); |
248 | status = CODEC_ERROR; | 240 | return CODEC_ERROR; |
249 | goto done; | ||
250 | } | 241 | } |
251 | 242 | ||
252 | if (format.samplesperblock == 0) | 243 | if (format.samplesperblock == 0) |
253 | { | 244 | { |
254 | DEBUGF("CODEC_ERROR: samplesperblock is 0\n"); | 245 | DEBUGF("CODEC_ERROR: samplesperblock is 0\n"); |
255 | status = CODEC_ERROR; | 246 | return CODEC_ERROR; |
256 | goto done; | ||
257 | } | 247 | } |
258 | if (format.blockalign == 0) | 248 | if (format.blockalign == 0) |
259 | { | 249 | { |
260 | DEBUGF("CODEC_ERROR: blockalign is 0\n"); | 250 | DEBUGF("CODEC_ERROR: blockalign is 0\n"); |
261 | status = CODEC_ERROR; | 251 | return CODEC_ERROR; |
262 | goto done; | ||
263 | } | 252 | } |
264 | 253 | ||
265 | /* check chunksize */ | 254 | /* check chunksize */ |
@@ -269,8 +258,7 @@ next_track: | |||
269 | if (format.chunksize == 0) | 258 | if (format.chunksize == 0) |
270 | { | 259 | { |
271 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); | 260 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); |
272 | status = CODEC_ERROR; | 261 | return CODEC_ERROR; |
273 | goto done; | ||
274 | } | 262 | } |
275 | 263 | ||
276 | firstblockposn = 1024 - n; | 264 | firstblockposn = 1024 - n; |
@@ -283,13 +271,13 @@ next_track: | |||
283 | PCM_SEEK_POS, NULL); | 271 | PCM_SEEK_POS, NULL); |
284 | 272 | ||
285 | if (newpos->pos > format.numbytes) | 273 | if (newpos->pos > format.numbytes) |
286 | goto done; | 274 | return CODEC_OK; |
275 | |||
287 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 276 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
288 | { | 277 | { |
289 | bytesdone = newpos->pos; | 278 | bytesdone = newpos->pos; |
290 | decodedsamples = newpos->samples; | 279 | decodedsamples = newpos->samples; |
291 | } | 280 | } |
292 | ci->seek_complete(); | ||
293 | } else { | 281 | } else { |
294 | /* already where we need to be */ | 282 | /* already where we need to be */ |
295 | bytesdone = 0; | 283 | bytesdone = 0; |
@@ -299,21 +287,29 @@ next_track: | |||
299 | endofstream = 0; | 287 | endofstream = 0; |
300 | 288 | ||
301 | while (!endofstream) { | 289 | while (!endofstream) { |
302 | ci->yield(); | 290 | enum codec_command_action action = ci->get_command(¶m); |
303 | if (ci->stop_codec || ci->new_track) | 291 | |
292 | if (action == CODEC_ACTION_HALT) | ||
304 | break; | 293 | break; |
305 | 294 | ||
306 | if (ci->seek_time) { | 295 | if (action == CODEC_ACTION_SEEK_TIME) { |
307 | /* 3rd args(read_buffer) is unnecessary in the format which AIFF supports. */ | 296 | /* 3rd args(read_buffer) is unnecessary in the format which AIFF supports. */ |
308 | struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME, NULL); | 297 | struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, NULL); |
309 | 298 | ||
310 | if (newpos->pos > format.numbytes) | 299 | if (newpos->pos > format.numbytes) |
300 | { | ||
301 | ci->set_elapsed(ci->id3->length); | ||
302 | ci->seek_complete(); | ||
311 | break; | 303 | break; |
304 | } | ||
305 | |||
312 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 306 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
313 | { | 307 | { |
314 | bytesdone = newpos->pos; | 308 | bytesdone = newpos->pos; |
315 | decodedsamples = newpos->samples; | 309 | decodedsamples = newpos->samples; |
316 | } | 310 | } |
311 | |||
312 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
317 | ci->seek_complete(); | 313 | ci->seek_complete(); |
318 | } | 314 | } |
319 | aifbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); | 315 | aifbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); |
@@ -326,11 +322,10 @@ next_track: | |||
326 | endofstream = 1; | 322 | endofstream = 1; |
327 | } | 323 | } |
328 | 324 | ||
329 | status = codec->decode(aifbuf, n, samples, &bufcount); | 325 | if (codec->decode(aifbuf, n, samples, &bufcount) == CODEC_ERROR) |
330 | if (status == CODEC_ERROR) | ||
331 | { | 326 | { |
332 | DEBUGF("codec error\n"); | 327 | DEBUGF("codec error\n"); |
333 | goto done; | 328 | return CODEC_ERROR; |
334 | } | 329 | } |
335 | 330 | ||
336 | ci->pcmbuf_insert(samples, NULL, bufcount); | 331 | ci->pcmbuf_insert(samples, NULL, bufcount); |
@@ -343,13 +338,6 @@ next_track: | |||
343 | 338 | ||
344 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | 339 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); |
345 | } | 340 | } |
346 | status = CODEC_OK; | ||
347 | |||
348 | done: | ||
349 | if (ci->request_next_track()) | ||
350 | goto next_track; | ||
351 | 341 | ||
352 | exit: | 342 | return CODEC_OK; |
353 | return status; | ||
354 | } | 343 | } |
355 | |||
diff --git a/apps/codecs/aiff_enc.c b/apps/codecs/aiff_enc.c index 69496f70ac..fc44196eb0 100644 --- a/apps/codecs/aiff_enc.c +++ b/apps/codecs/aiff_enc.c | |||
@@ -359,40 +359,42 @@ static bool init_encoder(void) | |||
359 | return true; | 359 | return true; |
360 | } /* init_encoder */ | 360 | } /* init_encoder */ |
361 | 361 | ||
362 | /* main codec entry point */ | 362 | /* this is the codec entry point */ |
363 | enum codec_status codec_main(void) | 363 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
364 | { | 364 | { |
365 | if (!init_encoder()) | 365 | if (reason == CODEC_LOAD) { |
366 | return CODEC_ERROR; | 366 | if (!init_encoder()) |
367 | return CODEC_ERROR; | ||
368 | } | ||
369 | else if (reason == CODEC_UNLOAD) { | ||
370 | /* reset parameters to initial state */ | ||
371 | ci->enc_set_parameters(NULL); | ||
372 | } | ||
367 | 373 | ||
374 | return CODEC_OK; | ||
375 | } | ||
376 | |||
377 | /* this is called for each file to process */ | ||
378 | enum codec_status codec_run(void) | ||
379 | { | ||
368 | /* main encoding loop */ | 380 | /* main encoding loop */ |
369 | while(!ci->stop_codec) | 381 | while (ci->get_command(NULL) != CODEC_ACTION_HALT) |
370 | { | 382 | { |
371 | uint32_t *src; | 383 | uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); |
384 | struct enc_chunk_hdr *chunk; | ||
372 | 385 | ||
373 | while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL) | 386 | if (src == NULL) |
374 | { | 387 | continue; |
375 | struct enc_chunk_hdr *chunk; | ||
376 | |||
377 | if (ci->stop_codec) | ||
378 | break; | ||
379 | 388 | ||
380 | chunk = ci->enc_get_chunk(); | 389 | chunk = ci->enc_get_chunk(); |
381 | chunk->enc_size = enc_size; | 390 | chunk->enc_size = enc_size; |
382 | chunk->num_pcm = PCM_SAMP_PER_CHUNK; | 391 | chunk->num_pcm = PCM_SAMP_PER_CHUNK; |
383 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); | 392 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); |
384 | 393 | ||
385 | chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data); | 394 | chunk_to_aiff_format(src, (uint32_t *)chunk->enc_data); |
386 | |||
387 | ci->enc_finish_chunk(); | ||
388 | ci->yield(); | ||
389 | } | ||
390 | 395 | ||
391 | ci->yield(); | 396 | ci->enc_finish_chunk(); |
392 | } | 397 | } |
393 | 398 | ||
394 | /* reset parameters to initial state */ | ||
395 | ci->enc_set_parameters(NULL); | ||
396 | |||
397 | return CODEC_OK; | 399 | return CODEC_OK; |
398 | } /* codec_start */ | 400 | } |
diff --git a/apps/codecs/alac.c b/apps/codecs/alac.c index cd9129a278..82bda264c3 100644 --- a/apps/codecs/alac.c +++ b/apps/codecs/alac.c | |||
@@ -32,116 +32,114 @@ CODEC_HEADER | |||
32 | static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR; | 32 | static int32_t outputbuffer[ALAC_MAX_CHANNELS][ALAC_BLOCKSIZE] IBSS_ATTR; |
33 | 33 | ||
34 | /* this is the codec entry point */ | 34 | /* this is the codec entry point */ |
35 | enum codec_status codec_main(void) | 35 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
36 | { | 36 | { |
37 | size_t n; | 37 | if (reason == CODEC_LOAD) { |
38 | demux_res_t demux_res; | 38 | /* Generic codec initialisation */ |
39 | stream_t input_stream; | 39 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); |
40 | uint32_t samplesdone; | 40 | ci->configure(DSP_SET_SAMPLE_DEPTH, ALAC_OUTPUT_DEPTH-1); |
41 | uint32_t elapsedtime; | 41 | } |
42 | int samplesdecoded; | ||
43 | unsigned int i; | ||
44 | unsigned char* buffer; | ||
45 | alac_file alac; | ||
46 | int retval; | ||
47 | |||
48 | /* Generic codec initialisation */ | ||
49 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
50 | ci->configure(DSP_SET_SAMPLE_DEPTH, ALAC_OUTPUT_DEPTH-1); | ||
51 | |||
52 | next_track: | ||
53 | retval = CODEC_OK; | ||
54 | |||
55 | /* Clean and initialize decoder structures */ | ||
56 | memset(&demux_res , 0, sizeof(demux_res)); | ||
57 | if (codec_init()) { | ||
58 | LOGF("ALAC: Error initialising codec\n"); | ||
59 | retval = CODEC_ERROR; | ||
60 | goto exit; | ||
61 | } | ||
62 | |||
63 | if (codec_wait_taginfo() != 0) | ||
64 | goto done; | ||
65 | |||
66 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | ||
67 | codec_set_replaygain(ci->id3); | ||
68 | 42 | ||
69 | stream_create(&input_stream,ci); | 43 | return CODEC_OK; |
44 | } | ||
70 | 45 | ||
71 | /* Read from ci->id3->offset before calling qtmovie_read. */ | 46 | /* this is called for each file to process */ |
72 | samplesdone = (uint32_t)(((uint64_t)(ci->id3->offset) * ci->id3->frequency) / | 47 | enum codec_status codec_run(void) |
73 | (ci->id3->bitrate*128)); | 48 | { |
74 | 49 | size_t n; | |
75 | /* if qtmovie_read returns successfully, the stream is up to | 50 | demux_res_t demux_res; |
76 | * the movie data, which can be used directly by the decoder */ | 51 | stream_t input_stream; |
77 | if (!qtmovie_read(&input_stream, &demux_res)) { | 52 | uint32_t samplesdone; |
78 | LOGF("ALAC: Error initialising file\n"); | 53 | uint32_t elapsedtime = 0; |
79 | retval = CODEC_ERROR; | 54 | int samplesdecoded; |
80 | goto done; | 55 | unsigned int i; |
81 | } | 56 | unsigned char* buffer; |
82 | 57 | alac_file alac; | |
83 | /* initialise the sound converter */ | 58 | intptr_t param; |
84 | create_alac(demux_res.sound_sample_size, demux_res.num_channels,&alac); | 59 | |
85 | alac_set_info(&alac, demux_res.codecdata); | 60 | /* Clean and initialize decoder structures */ |
86 | 61 | memset(&demux_res , 0, sizeof(demux_res)); | |
87 | /* Set i for first frame, seek to desired sample position for resuming. */ | 62 | if (codec_init()) { |
88 | i=0; | 63 | LOGF("ALAC: Error initialising codec\n"); |
89 | if (samplesdone > 0) { | 64 | return CODEC_ERROR; |
90 | if (m4a_seek(&demux_res, &input_stream, samplesdone, | ||
91 | &samplesdone, (int*) &i)) { | ||
92 | elapsedtime = (samplesdone * 10) / (ci->id3->frequency / 100); | ||
93 | ci->set_elapsed(elapsedtime); | ||
94 | } else { | ||
95 | samplesdone = 0; | ||
96 | } | 65 | } |
97 | } | ||
98 | 66 | ||
99 | /* The main decoding loop */ | 67 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
100 | while (i < demux_res.num_sample_byte_sizes) { | 68 | codec_set_replaygain(ci->id3); |
101 | ci->yield(); | 69 | |
102 | if (ci->stop_codec || ci->new_track) { | 70 | ci->seek_buffer(0); |
103 | break; | 71 | |
72 | stream_create(&input_stream,ci); | ||
73 | |||
74 | /* Read from ci->id3->offset before calling qtmovie_read. */ | ||
75 | samplesdone = (uint32_t)(((uint64_t)(ci->id3->offset) * ci->id3->frequency) / | ||
76 | (ci->id3->bitrate*128)); | ||
77 | |||
78 | /* if qtmovie_read returns successfully, the stream is up to | ||
79 | * the movie data, which can be used directly by the decoder */ | ||
80 | if (!qtmovie_read(&input_stream, &demux_res)) { | ||
81 | LOGF("ALAC: Error initialising file\n"); | ||
82 | return CODEC_ERROR; | ||
104 | } | 83 | } |
105 | 84 | ||
106 | /* Deal with any pending seek requests */ | 85 | /* initialise the sound converter */ |
107 | if (ci->seek_time) { | 86 | create_alac(demux_res.sound_sample_size, demux_res.num_channels,&alac); |
108 | if (m4a_seek(&demux_res, &input_stream, | 87 | alac_set_info(&alac, demux_res.codecdata); |
109 | ((ci->seek_time-1)/10) * (ci->id3->frequency/100), | 88 | |
110 | &samplesdone, (int *)&i)) { | 89 | /* Set i for first frame, seek to desired sample position for resuming. */ |
111 | elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); | 90 | i=0; |
112 | ci->set_elapsed(elapsedtime); | 91 | if (samplesdone > 0) { |
113 | } | 92 | if (m4a_seek(&demux_res, &input_stream, samplesdone, |
114 | ci->seek_complete(); | 93 | &samplesdone, (int*) &i)) { |
94 | elapsedtime = (samplesdone * 10) / (ci->id3->frequency / 100); | ||
95 | ci->set_elapsed(elapsedtime); | ||
96 | } else { | ||
97 | samplesdone = 0; | ||
98 | } | ||
115 | } | 99 | } |
116 | 100 | ||
117 | /* Request the required number of bytes from the input buffer */ | 101 | /* The main decoding loop */ |
118 | buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE); | 102 | while (i < demux_res.num_sample_byte_sizes) { |
103 | enum codec_command_action action = ci->get_command(¶m); | ||
104 | |||
105 | if (action == CODEC_ACTION_HALT) | ||
106 | break; | ||
119 | 107 | ||
120 | /* Decode one block - returned samples will be host-endian */ | 108 | /* Request the required number of bytes from the input buffer */ |
121 | ci->yield(); | 109 | buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE); |
122 | samplesdecoded=alac_decode_frame(&alac, buffer, outputbuffer, ci->yield); | ||
123 | 110 | ||
124 | /* Advance codec buffer by amount of consumed bytes */ | 111 | /* Deal with any pending seek requests */ |
125 | ci->advance_buffer(alac.bytes_consumed); | 112 | if (action == CODEC_ACTION_SEEK_TIME) { |
113 | if (m4a_seek(&demux_res, &input_stream, | ||
114 | (param/10) * (ci->id3->frequency/100), | ||
115 | &samplesdone, (int *)&i)) { | ||
116 | elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); | ||
117 | } | ||
118 | ci->set_elapsed(elapsedtime); | ||
119 | ci->seek_complete(); | ||
120 | } | ||
126 | 121 | ||
127 | /* Output the audio */ | 122 | /* Request the required number of bytes from the input buffer */ |
128 | ci->yield(); | 123 | buffer=ci->request_buffer(&n, ALAC_BYTE_BUFFER_SIZE); |
129 | ci->pcmbuf_insert(outputbuffer[0], outputbuffer[1], samplesdecoded); | ||
130 | 124 | ||
131 | /* Update the elapsed-time indicator */ | 125 | /* Decode one block - returned samples will be host-endian */ |
132 | samplesdone+=samplesdecoded; | 126 | samplesdecoded=alac_decode_frame(&alac, buffer, outputbuffer, ci->yield); |
133 | elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); | 127 | ci->yield(); |
134 | ci->set_elapsed(elapsedtime); | ||
135 | 128 | ||
136 | i++; | 129 | /* Advance codec buffer by amount of consumed bytes */ |
137 | } | 130 | ci->advance_buffer(alac.bytes_consumed); |
138 | 131 | ||
139 | done: | 132 | /* Output the audio */ |
140 | LOGF("ALAC: Decoded %lu samples\n",(unsigned long)samplesdone); | 133 | ci->pcmbuf_insert(outputbuffer[0], outputbuffer[1], samplesdecoded); |
141 | 134 | ||
142 | if (ci->request_next_track()) | 135 | /* Update the elapsed-time indicator */ |
143 | goto next_track; | 136 | samplesdone+=samplesdecoded; |
137 | elapsedtime=(samplesdone*10)/(ci->id3->frequency/100); | ||
138 | ci->set_elapsed(elapsedtime); | ||
139 | |||
140 | i++; | ||
141 | } | ||
144 | 142 | ||
145 | exit: | 143 | LOGF("ALAC: Decoded %lu samples\n",(unsigned long)samplesdone); |
146 | return retval; | 144 | return CODEC_OK; |
147 | } | 145 | } |
diff --git a/apps/codecs/ape.c b/apps/codecs/ape.c index 11d973ab26..8f95a01ec7 100644 --- a/apps/codecs/ape.c +++ b/apps/codecs/ape.c | |||
@@ -127,13 +127,23 @@ static void ape_resume(struct ape_ctx_t* ape_ctx, size_t resume_offset, | |||
127 | } | 127 | } |
128 | 128 | ||
129 | /* this is the codec entry point */ | 129 | /* this is the codec entry point */ |
130 | enum codec_status codec_main(void) | 130 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
131 | { | ||
132 | if (reason == CODEC_LOAD) { | ||
133 | /* Generic codec initialisation */ | ||
134 | ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1); | ||
135 | } | ||
136 | |||
137 | return CODEC_OK; | ||
138 | } | ||
139 | |||
140 | /* this is called for each file to process */ | ||
141 | enum codec_status codec_run(void) | ||
131 | { | 142 | { |
132 | struct ape_ctx_t ape_ctx; | 143 | struct ape_ctx_t ape_ctx; |
133 | uint32_t samplesdone; | 144 | uint32_t samplesdone; |
134 | uint32_t elapsedtime; | 145 | uint32_t elapsedtime; |
135 | size_t bytesleft; | 146 | size_t bytesleft; |
136 | int retval; | ||
137 | 147 | ||
138 | uint32_t currentframe; | 148 | uint32_t currentframe; |
139 | uint32_t newfilepos; | 149 | uint32_t newfilepos; |
@@ -145,33 +155,24 @@ enum codec_status codec_main(void) | |||
145 | int res; | 155 | int res; |
146 | int firstbyte; | 156 | int firstbyte; |
147 | size_t resume_offset; | 157 | size_t resume_offset; |
148 | 158 | intptr_t param; | |
149 | /* Generic codec initialisation */ | ||
150 | ci->configure(DSP_SET_SAMPLE_DEPTH, APE_OUTPUT_DEPTH-1); | ||
151 | |||
152 | next_track: | ||
153 | retval = CODEC_OK; | ||
154 | 159 | ||
155 | if (codec_init()) { | 160 | if (codec_init()) { |
156 | LOGF("APE: Error initialising codec\n"); | 161 | LOGF("APE: Error initialising codec\n"); |
157 | retval = CODEC_ERROR; | 162 | return CODEC_ERROR; |
158 | goto exit; | ||
159 | } | 163 | } |
160 | 164 | ||
161 | if (codec_wait_taginfo() != 0) | ||
162 | goto done; | ||
163 | |||
164 | /* Remember the resume position - when the codec is opened, the | 165 | /* Remember the resume position - when the codec is opened, the |
165 | playback engine will reset it. */ | 166 | playback engine will reset it. */ |
166 | resume_offset = ci->id3->offset; | 167 | resume_offset = ci->id3->offset; |
167 | 168 | ||
169 | ci->seek_buffer(0); | ||
168 | inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); | 170 | inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); |
169 | 171 | ||
170 | /* Read the file headers to populate the ape_ctx struct */ | 172 | /* Read the file headers to populate the ape_ctx struct */ |
171 | if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) { | 173 | if (ape_parseheaderbuf(inbuffer,&ape_ctx) < 0) { |
172 | LOGF("APE: Error reading header\n"); | 174 | LOGF("APE: Error reading header\n"); |
173 | retval = CODEC_ERROR; | 175 | return CODEC_ERROR; |
174 | goto exit; | ||
175 | } | 176 | } |
176 | 177 | ||
177 | /* Initialise the seektable for this file */ | 178 | /* Initialise the seektable for this file */ |
@@ -243,16 +244,16 @@ frame_start: | |||
243 | /* Decode the frame a chunk at a time */ | 244 | /* Decode the frame a chunk at a time */ |
244 | while (nblocks > 0) | 245 | while (nblocks > 0) |
245 | { | 246 | { |
246 | ci->yield(); | 247 | enum codec_command_action action = ci->get_command(¶m); |
247 | if (ci->stop_codec || ci->new_track) { | 248 | |
249 | if (action == CODEC_ACTION_HALT) | ||
248 | goto done; | 250 | goto done; |
249 | } | ||
250 | 251 | ||
251 | /* Deal with any pending seek requests */ | 252 | /* Deal with any pending seek requests */ |
252 | if (ci->seek_time) | 253 | if (action == CODEC_ACTION_SEEK_TIME) |
253 | { | 254 | { |
254 | if (ape_calc_seekpos(&ape_ctx, | 255 | if (ape_calc_seekpos(&ape_ctx, |
255 | ((ci->seek_time-1)/10) * (ci->id3->frequency/100), | 256 | (param/10) * (ci->id3->frequency/100), |
256 | ¤tframe, | 257 | ¤tframe, |
257 | &newfilepos, | 258 | &newfilepos, |
258 | &samplestoskip)) | 259 | &samplestoskip)) |
@@ -266,9 +267,12 @@ frame_start: | |||
266 | ci->seek_buffer(newfilepos); | 267 | ci->seek_buffer(newfilepos); |
267 | inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); | 268 | inbuffer = ci->request_buffer(&bytesleft, INPUT_CHUNKSIZE); |
268 | 269 | ||
270 | elapsedtime = (samplesdone*10)/(ape_ctx.samplerate/100); | ||
271 | ci->set_elapsed(elapsedtime); | ||
269 | ci->seek_complete(); | 272 | ci->seek_complete(); |
270 | goto frame_start; /* Sorry... */ | 273 | goto frame_start; /* Sorry... */ |
271 | } | 274 | } |
275 | |||
272 | ci->seek_complete(); | 276 | ci->seek_complete(); |
273 | } | 277 | } |
274 | 278 | ||
@@ -281,8 +285,7 @@ frame_start: | |||
281 | { | 285 | { |
282 | /* Frame decoding error, abort */ | 286 | /* Frame decoding error, abort */ |
283 | LOGF("APE: Frame %lu, error %d\n",(unsigned long)currentframe,res); | 287 | LOGF("APE: Frame %lu, error %d\n",(unsigned long)currentframe,res); |
284 | retval = CODEC_ERROR; | 288 | return CODEC_ERROR; |
285 | goto done; | ||
286 | } | 289 | } |
287 | 290 | ||
288 | ci->yield(); | 291 | ci->yield(); |
@@ -320,10 +323,5 @@ frame_start: | |||
320 | 323 | ||
321 | done: | 324 | done: |
322 | LOGF("APE: Decoded %lu samples\n",(unsigned long)samplesdone); | 325 | LOGF("APE: Decoded %lu samples\n",(unsigned long)samplesdone); |
323 | 326 | return CODEC_OK; | |
324 | if (ci->request_next_track()) | ||
325 | goto next_track; | ||
326 | |||
327 | exit: | ||
328 | return retval; | ||
329 | } | 327 | } |
diff --git a/apps/codecs/asap.c b/apps/codecs/asap.c index 9447c738d2..f12dc6a0c5 100644 --- a/apps/codecs/asap.c +++ b/apps/codecs/asap.c | |||
@@ -29,24 +29,21 @@ CODEC_HEADER | |||
29 | static byte samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */ | 29 | static byte samples[CHUNK_SIZE] IBSS_ATTR; /* The sample buffer */ |
30 | static ASAP_State asap; /* asap codec state */ | 30 | static ASAP_State asap; /* asap codec state */ |
31 | 31 | ||
32 | /* this is the codec entry point */ | 32 | /* this is called for each file to process */ |
33 | enum codec_status codec_main(void) | 33 | enum codec_status codec_run(void) |
34 | { | 34 | { |
35 | int n_bytes; | 35 | int n_bytes; |
36 | int song; | 36 | int song; |
37 | int duration; | 37 | int duration; |
38 | char* module; | 38 | char* module; |
39 | int bytesPerSample =2; | 39 | int bytesPerSample =2; |
40 | intptr_t param; | ||
40 | 41 | ||
41 | next_track: | ||
42 | if (codec_init()) { | 42 | if (codec_init()) { |
43 | DEBUGF("codec init failed\n"); | 43 | DEBUGF("codec init failed\n"); |
44 | return CODEC_ERROR; | 44 | return CODEC_ERROR; |
45 | } | 45 | } |
46 | 46 | ||
47 | if (codec_wait_taginfo() != 0) | ||
48 | goto request_next_track; | ||
49 | |||
50 | codec_set_replaygain(ci->id3); | 47 | codec_set_replaygain(ci->id3); |
51 | 48 | ||
52 | int bytes_done =0; | 49 | int bytes_done =0; |
@@ -97,19 +94,20 @@ next_track: | |||
97 | 94 | ||
98 | /* The main decoder loop */ | 95 | /* The main decoder loop */ |
99 | while (1) { | 96 | while (1) { |
100 | ci->yield(); | 97 | enum codec_command_action action = ci->get_command(¶m); |
101 | if (ci->stop_codec || ci->new_track) | 98 | |
99 | if (action == CODEC_ACTION_HALT) | ||
102 | break; | 100 | break; |
103 | 101 | ||
104 | if (ci->seek_time) { | 102 | if (action == CODEC_ACTION_SEEK_TIME) { |
105 | /* New time is ready in ci->seek_time */ | 103 | /* New time is ready in param */ |
106 | 104 | ||
107 | /* seek to pos */ | 105 | /* seek to pos */ |
108 | ASAP_Seek(&asap,ci->seek_time); | 106 | ASAP_Seek(&asap,param); |
109 | /* update elapsed */ | ||
110 | ci->set_elapsed(ci->seek_time); | ||
111 | /* update bytes_done */ | 107 | /* update bytes_done */ |
112 | bytes_done = ci->seek_time*44.1*2; | 108 | bytes_done = param*44.1*2; |
109 | /* update elapsed */ | ||
110 | ci->set_elapsed((bytes_done / 2) / 44.1); | ||
113 | /* seek ready */ | 111 | /* seek ready */ |
114 | ci->seek_complete(); | 112 | ci->seek_complete(); |
115 | } | 113 | } |
@@ -129,10 +127,6 @@ next_track: | |||
129 | if(n_bytes != sizeof(samples)) | 127 | if(n_bytes != sizeof(samples)) |
130 | break; | 128 | break; |
131 | } | 129 | } |
132 | |||
133 | request_next_track: | ||
134 | if (ci->request_next_track()) | ||
135 | goto next_track; | ||
136 | 130 | ||
137 | return CODEC_OK; | 131 | return CODEC_OK; |
138 | } | 132 | } |
diff --git a/apps/codecs/atrac3_oma.c b/apps/codecs/atrac3_oma.c index 73f3ad29fd..ab24783368 100644 --- a/apps/codecs/atrac3_oma.c +++ b/apps/codecs/atrac3_oma.c | |||
@@ -33,24 +33,22 @@ CODEC_HEADER | |||
33 | 33 | ||
34 | static ATRAC3Context q IBSS_ATTR; | 34 | static ATRAC3Context q IBSS_ATTR; |
35 | 35 | ||
36 | /* this is the codec entry point */ | 36 | /* this is called for each file to process */ |
37 | enum codec_status codec_main(void) | 37 | enum codec_status codec_run(void) |
38 | { | 38 | { |
39 | static size_t buff_size; | 39 | static size_t buff_size; |
40 | int datasize, res, frame_counter, total_frames, seek_frame_offset; | 40 | int datasize, res, frame_counter, total_frames, seek_frame_offset; |
41 | uint8_t *bit_buffer; | 41 | uint8_t *bit_buffer; |
42 | int elapsed = 0; | 42 | int elapsed = 0; |
43 | size_t resume_offset; | 43 | size_t resume_offset; |
44 | intptr_t param; | ||
45 | enum codec_command_action action = CODEC_ACTION_NULL; | ||
44 | 46 | ||
45 | next_track: | ||
46 | if (codec_init()) { | 47 | if (codec_init()) { |
47 | DEBUGF("codec init failed\n"); | 48 | DEBUGF("codec init failed\n"); |
48 | return CODEC_ERROR; | 49 | return CODEC_ERROR; |
49 | } | 50 | } |
50 | 51 | ||
51 | if (codec_wait_taginfo() != 0) | ||
52 | goto done; | ||
53 | |||
54 | resume_offset = ci->id3->offset; | 52 | resume_offset = ci->id3->offset; |
55 | 53 | ||
56 | codec_set_replaygain(ci->id3); | 54 | codec_set_replaygain(ci->id3); |
@@ -60,84 +58,88 @@ next_track: | |||
60 | ci->configure(DSP_SET_SAMPLE_DEPTH, 17); /* Remark: atrac3 uses s15.0 by default, s15.2 was hacked. */ | 58 | ci->configure(DSP_SET_SAMPLE_DEPTH, 17); /* Remark: atrac3 uses s15.0 by default, s15.2 was hacked. */ |
61 | ci->configure(DSP_SET_STEREO_MODE, ci->id3->channels == 1 ? | 59 | ci->configure(DSP_SET_STEREO_MODE, ci->id3->channels == 1 ? |
62 | STEREO_MONO : STEREO_NONINTERLEAVED); | 60 | STEREO_MONO : STEREO_NONINTERLEAVED); |
63 | 61 | ||
64 | res =atrac3_decode_init(&q, ci->id3); | 62 | ci->seek_buffer(0); |
63 | |||
64 | res = atrac3_decode_init(&q, ci->id3); | ||
65 | if(res < 0) { | 65 | if(res < 0) { |
66 | DEBUGF("failed to initialize OMA atrac decoder\n"); | 66 | DEBUGF("failed to initialize OMA atrac decoder\n"); |
67 | return CODEC_ERROR; | 67 | return CODEC_ERROR; |
68 | } | 68 | } |
69 | 69 | ||
70 | total_frames = (ci->id3->filesize - ci->id3->first_frame_offset) / FRAMESIZE; | ||
71 | frame_counter = 0; | ||
72 | |||
70 | /* check for a mid-track resume and force a seek time accordingly */ | 73 | /* check for a mid-track resume and force a seek time accordingly */ |
71 | if(resume_offset > ci->id3->first_frame_offset) { | 74 | if(resume_offset > ci->id3->first_frame_offset) { |
72 | resume_offset -= ci->id3->first_frame_offset; | 75 | resume_offset -= ci->id3->first_frame_offset; |
73 | /* calculate resume_offset in frames */ | 76 | /* calculate resume_offset in frames */ |
74 | resume_offset = (int)resume_offset / FRAMESIZE; | 77 | resume_offset = (int)resume_offset / FRAMESIZE; |
75 | ci->seek_time = (int)resume_offset * ((FRAMESIZE * 8)/BITRATE); | 78 | param = (int)resume_offset * ((FRAMESIZE * 8)/BITRATE); |
79 | action = CODEC_ACTION_SEEK_TIME; | ||
80 | } | ||
81 | else { | ||
82 | ci->set_elapsed(0); | ||
83 | ci->seek_buffer(ci->id3->first_frame_offset); | ||
76 | } | 84 | } |
77 | total_frames = (ci->id3->filesize - ci->id3->first_frame_offset) / FRAMESIZE; | ||
78 | frame_counter = 0; | ||
79 | |||
80 | ci->set_elapsed(0); | ||
81 | ci->seek_buffer(0); | ||
82 | ci->advance_buffer(ci->id3->first_frame_offset); | ||
83 | 85 | ||
84 | /* The main decoder loop */ | 86 | /* The main decoder loop */ |
85 | seek_start : | ||
86 | while(frame_counter < total_frames) | 87 | while(frame_counter < total_frames) |
87 | { | 88 | { |
89 | if (action == CODEC_ACTION_NULL) | ||
90 | action = ci->get_command(¶m); | ||
91 | |||
92 | if (action == CODEC_ACTION_HALT) | ||
93 | break; | ||
94 | |||
88 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE); | 95 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE); |
89 | 96 | ||
90 | ci->yield(); | 97 | if (action == CODEC_ACTION_SEEK_TIME) { |
91 | if (ci->stop_codec || ci->new_track) | 98 | /* Do not allow seeking beyond the file's length */ |
92 | goto done; | 99 | if ((unsigned) param > ci->id3->length) { |
93 | 100 | ci->set_elapsed(ci->id3->length); | |
94 | if (ci->seek_time) { | 101 | ci->seek_complete(); |
95 | ci->set_elapsed(ci->seek_time); | 102 | break; |
96 | 103 | } | |
97 | /* Do not allow seeking beyond the file's length */ | 104 | |
98 | if ((unsigned) ci->seek_time > ci->id3->length) { | 105 | /* Seek to the start of the track */ |
99 | ci->seek_complete(); | 106 | if (param == 0) { |
100 | goto done; | 107 | elapsed = 0; |
101 | } | 108 | ci->set_elapsed(0); |
102 | 109 | ci->seek_buffer(ci->id3->first_frame_offset); | |
103 | /* Seek to the start of the track */ | 110 | ci->seek_complete(); |
104 | if (ci->seek_time == 1) { | 111 | action = CODEC_ACTION_NULL; |
105 | ci->set_elapsed(0); | 112 | continue; |
106 | ci->seek_complete(); | 113 | } |
107 | ci->seek_buffer(ci->id3->first_frame_offset); | 114 | |
108 | elapsed = 0; | 115 | seek_frame_offset = (param * BITRATE) / (8 * FRAMESIZE); |
109 | goto seek_start; | 116 | frame_counter = seek_frame_offset; |
110 | } | 117 | ci->seek_buffer(ci->id3->first_frame_offset + seek_frame_offset* FRAMESIZE); |
111 | seek_frame_offset = (ci->seek_time * BITRATE) / (8 * FRAMESIZE); | 118 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE); |
112 | frame_counter = seek_frame_offset; | 119 | elapsed = param; |
113 | ci->seek_buffer(ci->id3->first_frame_offset + seek_frame_offset* FRAMESIZE); | ||
114 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, FRAMESIZE); | ||
115 | elapsed = ci->seek_time; | ||
116 | |||
117 | ci->set_elapsed(elapsed); | ||
118 | ci->seek_complete(); | ||
119 | } | ||
120 | |||
121 | res = atrac3_decode_frame(FRAMESIZE, &q, &datasize, bit_buffer, FRAMESIZE); | ||
122 | |||
123 | if(res != (int)FRAMESIZE) { | ||
124 | DEBUGF("codec error\n"); | ||
125 | return CODEC_ERROR; | ||
126 | } | ||
127 | |||
128 | if(datasize) | ||
129 | ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024, q.samples_per_frame / ci->id3->channels); | ||
130 | |||
131 | elapsed += (FRAMESIZE * 8) / BITRATE; | ||
132 | ci->set_elapsed(elapsed); | 120 | ci->set_elapsed(elapsed); |
133 | 121 | ci->seek_complete(); | |
134 | ci->advance_buffer(FRAMESIZE); | 122 | } |
135 | frame_counter++; | 123 | |
136 | } | 124 | action = CODEC_ACTION_NULL; |
137 | 125 | ||
138 | done: | 126 | res = atrac3_decode_frame(FRAMESIZE, &q, &datasize, bit_buffer, FRAMESIZE); |
139 | if (ci->request_next_track()) | 127 | |
140 | goto next_track; | 128 | if(res != (int)FRAMESIZE) { |
129 | DEBUGF("codec error\n"); | ||
130 | return CODEC_ERROR; | ||
131 | } | ||
132 | |||
133 | if(datasize) | ||
134 | ci->pcmbuf_insert(q.outSamples, q.outSamples + 1024, | ||
135 | q.samples_per_frame / ci->id3->channels); | ||
136 | |||
137 | elapsed += (FRAMESIZE * 8) / BITRATE; | ||
138 | ci->set_elapsed(elapsed); | ||
139 | |||
140 | ci->advance_buffer(FRAMESIZE); | ||
141 | frame_counter++; | ||
142 | } | ||
141 | 143 | ||
142 | return CODEC_OK; | 144 | return CODEC_OK; |
143 | } | 145 | } |
diff --git a/apps/codecs/atrac3_rm.c b/apps/codecs/atrac3_rm.c index 6a77d24283..1322e917ed 100644 --- a/apps/codecs/atrac3_rm.c +++ b/apps/codecs/atrac3_rm.c | |||
@@ -41,9 +41,9 @@ static void init_rm(RMContext *rmctx) | |||
41 | memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char)); | 41 | memcpy(ci->id3->id3v2buf, (char*)rmctx->codec_extradata, rmctx->extradata_size*sizeof(char)); |
42 | } | 42 | } |
43 | 43 | ||
44 | /* this is the codec entry point */ | 44 | /* this is called for each file to process */ |
45 | enum codec_status codec_main(void) | 45 | enum codec_status codec_run(void) |
46 | { | 46 | { |
47 | static size_t buff_size; | 47 | static size_t buff_size; |
48 | int datasize, res, consumed, i, time_offset; | 48 | int datasize, res, consumed, i, time_offset; |
49 | uint8_t *bit_buffer; | 49 | uint8_t *bit_buffer; |
@@ -52,16 +52,14 @@ enum codec_status codec_main(void) | |||
52 | int scrambling_unit_size, num_units, elapsed = 0; | 52 | int scrambling_unit_size, num_units, elapsed = 0; |
53 | int playback_on = -1; | 53 | int playback_on = -1; |
54 | size_t resume_offset; | 54 | size_t resume_offset; |
55 | intptr_t param; | ||
56 | enum codec_command_action action = CODEC_ACTION_NULL; | ||
55 | 57 | ||
56 | next_track: | ||
57 | if (codec_init()) { | 58 | if (codec_init()) { |
58 | DEBUGF("codec init failed\n"); | 59 | DEBUGF("codec init failed\n"); |
59 | return CODEC_ERROR; | 60 | return CODEC_ERROR; |
60 | } | 61 | } |
61 | 62 | ||
62 | if (codec_wait_taginfo() != 0) | ||
63 | goto done; | ||
64 | |||
65 | resume_offset = ci->id3->offset; | 63 | resume_offset = ci->id3->offset; |
66 | 64 | ||
67 | codec_set_replaygain(ci->id3); | 65 | codec_set_replaygain(ci->id3); |
@@ -69,6 +67,7 @@ next_track: | |||
69 | ci->memset(&pkt,0,sizeof(RMPacket)); | 67 | ci->memset(&pkt,0,sizeof(RMPacket)); |
70 | ci->memset(&q,0,sizeof(ATRAC3Context)); | 68 | ci->memset(&q,0,sizeof(ATRAC3Context)); |
71 | 69 | ||
70 | ci->seek_buffer(0); | ||
72 | init_rm(&rmctx); | 71 | init_rm(&rmctx); |
73 | 72 | ||
74 | ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); | 73 | ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); |
@@ -84,22 +83,25 @@ next_track: | |||
84 | h = rmctx.sub_packet_h; | 83 | h = rmctx.sub_packet_h; |
85 | scrambling_unit_size = h*fs; | 84 | scrambling_unit_size = h*fs; |
86 | 85 | ||
87 | res =atrac3_decode_init(&q, ci->id3); | 86 | res = atrac3_decode_init(&q, ci->id3); |
88 | if(res < 0) { | 87 | if(res < 0) { |
89 | DEBUGF("failed to initialize RM atrac decoder\n"); | 88 | DEBUGF("failed to initialize RM atrac decoder\n"); |
90 | return CODEC_ERROR; | 89 | return CODEC_ERROR; |
91 | } | 90 | } |
92 | 91 | ||
93 | /* check for a mid-track resume and force a seek time accordingly */ | 92 | /* check for a mid-track resume and force a seek time accordingly */ |
94 | if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { | 93 | if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { |
95 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; | 94 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; |
96 | num_units = (int)resume_offset / scrambling_unit_size; | 95 | num_units = (int)resume_offset / scrambling_unit_size; |
97 | /* put number of subpackets to skip in resume_offset */ | 96 | /* put number of subpackets to skip in resume_offset */ |
98 | resume_offset /= (sps + PACKET_HEADER_SIZE); | 97 | resume_offset /= (sps + PACKET_HEADER_SIZE); |
99 | ci->seek_time = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); | 98 | param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); |
99 | action = CODEC_ACTION_SEEK_TIME; | ||
100 | } | 100 | } |
101 | 101 | else { | |
102 | ci->set_elapsed(0); | 102 | ci->set_elapsed(0); |
103 | } | ||
104 | |||
103 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | 105 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); |
104 | 106 | ||
105 | /* The main decoder loop */ | 107 | /* The main decoder loop */ |
@@ -115,22 +117,23 @@ seek_start : | |||
115 | return CODEC_ERROR; | 117 | return CODEC_ERROR; |
116 | } | 118 | } |
117 | else | 119 | else |
118 | goto done; | 120 | return CODEC_OK; |
119 | } | 121 | } |
120 | 122 | ||
121 | for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) | 123 | for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) |
122 | { | 124 | { |
123 | ci->yield(); | 125 | if (action == CODEC_ACTION_NULL) |
124 | if (ci->stop_codec || ci->new_track) | 126 | action = ci->get_command(¶m); |
125 | goto done; | ||
126 | 127 | ||
127 | if (ci->seek_time) { | 128 | if (action == CODEC_ACTION_HALT) |
128 | ci->set_elapsed(ci->seek_time); | 129 | return CODEC_OK; |
129 | 130 | ||
131 | if (action == CODEC_ACTION_SEEK_TIME) { | ||
130 | /* Do not allow seeking beyond the file's length */ | 132 | /* Do not allow seeking beyond the file's length */ |
131 | if ((unsigned) ci->seek_time > ci->id3->length) { | 133 | if ((unsigned) param > ci->id3->length) { |
134 | ci->set_elapsed(ci->id3->length); | ||
132 | ci->seek_complete(); | 135 | ci->seek_complete(); |
133 | goto done; | 136 | return CODEC_OK; |
134 | } | 137 | } |
135 | 138 | ||
136 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | 139 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); |
@@ -139,12 +142,13 @@ seek_start : | |||
139 | rmctx.frame_number = 0; | 142 | rmctx.frame_number = 0; |
140 | 143 | ||
141 | /* Seek to the start of the track */ | 144 | /* Seek to the start of the track */ |
142 | if (ci->seek_time == 1) { | 145 | if (param == 0) { |
143 | ci->set_elapsed(0); | 146 | ci->set_elapsed(0); |
144 | ci->seek_complete(); | 147 | ci->seek_complete(); |
148 | action = CODEC_ACTION_NULL; | ||
145 | goto seek_start; | 149 | goto seek_start; |
146 | } | 150 | } |
147 | num_units = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); | 151 | num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); |
148 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); | 152 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); |
149 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | 153 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); |
150 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); | 154 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); |
@@ -155,12 +159,12 @@ seek_start : | |||
155 | return CODEC_ERROR; | 159 | return CODEC_ERROR; |
156 | } | 160 | } |
157 | else | 161 | else |
158 | goto done; | 162 | return CODEC_OK; |
159 | } | 163 | } |
160 | 164 | ||
161 | packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; | 165 | packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; |
162 | rmctx.frame_number = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate)); | 166 | rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate)); |
163 | while(rmctx.audiotimestamp > (unsigned) ci->seek_time) { | 167 | while(rmctx.audiotimestamp > (unsigned) param) { |
164 | rmctx.audio_pkt_cnt = 0; | 168 | rmctx.audio_pkt_cnt = 0; |
165 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); | 169 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); |
166 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | 170 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); |
@@ -168,16 +172,19 @@ seek_start : | |||
168 | packet_count += rmctx.audio_pkt_cnt; | 172 | packet_count += rmctx.audio_pkt_cnt; |
169 | num_units--; | 173 | num_units--; |
170 | } | 174 | } |
171 | time_offset = ci->seek_time - rmctx.audiotimestamp; | 175 | time_offset = param - rmctx.audiotimestamp; |
172 | i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); | 176 | i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); |
173 | elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i; | 177 | elapsed = rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i; |
174 | ci->set_elapsed(elapsed); | 178 | ci->set_elapsed(elapsed); |
175 | ci->seek_complete(); | 179 | ci->seek_complete(); |
176 | } | 180 | } |
181 | |||
182 | action = CODEC_ACTION_NULL; | ||
183 | |||
177 | if(pkt.length) | 184 | if(pkt.length) |
178 | res = atrac3_decode_frame(rmctx.block_align, &q, &datasize, pkt.frames[i], rmctx.block_align); | 185 | res = atrac3_decode_frame(rmctx.block_align, &q, &datasize, pkt.frames[i], rmctx.block_align); |
179 | else /* indicates that there are no remaining frames */ | 186 | else /* indicates that there are no remaining frames */ |
180 | goto done; | 187 | return CODEC_OK; |
181 | 188 | ||
182 | if(res != rmctx.block_align) { | 189 | if(res != rmctx.block_align) { |
183 | DEBUGF("codec error\n"); | 190 | DEBUGF("codec error\n"); |
@@ -196,9 +203,5 @@ seek_start : | |||
196 | ci->advance_buffer(consumed); | 203 | ci->advance_buffer(consumed); |
197 | } | 204 | } |
198 | 205 | ||
199 | done : | 206 | return CODEC_OK; |
200 | if (ci->request_next_track()) | ||
201 | goto next_track; | ||
202 | |||
203 | return CODEC_OK; | ||
204 | } | 207 | } |
diff --git a/apps/codecs/au.c b/apps/codecs/au.c index 3f9436c9e7..e06f931cf9 100644 --- a/apps/codecs/au.c +++ b/apps/codecs/au.c | |||
@@ -106,9 +106,19 @@ static int convert_au_format(unsigned int encoding, struct pcm_format *fmt) | |||
106 | } | 106 | } |
107 | 107 | ||
108 | /* this is the codec entry point */ | 108 | /* this is the codec entry point */ |
109 | enum codec_status codec_main(void) | 109 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
110 | { | ||
111 | if (reason == CODEC_LOAD) { | ||
112 | /* Generic codec initialisation */ | ||
113 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
114 | } | ||
115 | |||
116 | return CODEC_OK; | ||
117 | } | ||
118 | |||
119 | /* this is called for each file to process */ | ||
120 | enum codec_status codec_run(void) | ||
110 | { | 121 | { |
111 | int status; | ||
112 | struct pcm_format format; | 122 | struct pcm_format format; |
113 | uint32_t bytesdone, decodedsamples; | 123 | uint32_t bytesdone, decodedsamples; |
114 | size_t n; | 124 | size_t n; |
@@ -119,22 +129,13 @@ enum codec_status codec_main(void) | |||
119 | off_t firstblockposn; /* position of the first block in file */ | 129 | off_t firstblockposn; /* position of the first block in file */ |
120 | const struct pcm_codec *codec; | 130 | const struct pcm_codec *codec; |
121 | int offset = 0; | 131 | int offset = 0; |
122 | 132 | intptr_t param; | |
123 | /* Generic codec initialisation */ | 133 | |
124 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
125 | |||
126 | next_track: | ||
127 | status = CODEC_OK; | ||
128 | |||
129 | if (codec_init()) { | 134 | if (codec_init()) { |
130 | DEBUGF("codec_init() error\n"); | 135 | DEBUGF("codec_init() error\n"); |
131 | status = CODEC_ERROR; | 136 | return CODEC_ERROR; |
132 | goto exit; | ||
133 | } | 137 | } |
134 | 138 | ||
135 | if (codec_wait_taginfo() != 0) | ||
136 | goto done; | ||
137 | |||
138 | codec_set_replaygain(ci->id3); | 139 | codec_set_replaygain(ci->id3); |
139 | 140 | ||
140 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ | 141 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ |
@@ -145,6 +146,7 @@ next_track: | |||
145 | format.is_little_endian = false; | 146 | format.is_little_endian = false; |
146 | 147 | ||
147 | /* set format */ | 148 | /* set format */ |
149 | ci->seek_buffer(0); | ||
148 | buf = ci->request_buffer(&n, 24); | 150 | buf = ci->request_buffer(&n, 24); |
149 | if (n < 24 || (memcmp(buf, ".snd", 4) != 0)) | 151 | if (n < 24 || (memcmp(buf, ".snd", 4) != 0)) |
150 | { | 152 | { |
@@ -170,8 +172,7 @@ next_track: | |||
170 | if (offset < 24) | 172 | if (offset < 24) |
171 | { | 173 | { |
172 | DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset); | 174 | DEBUGF("CODEC_ERROR: sun audio offset size is small: %d\n", offset); |
173 | status = CODEC_ERROR; | 175 | return CODEC_ERROR; |
174 | goto done; | ||
175 | } | 176 | } |
176 | /* data size */ | 177 | /* data size */ |
177 | format.numbytes = get_be32(buf + 8); | 178 | format.numbytes = get_be32(buf + 8); |
@@ -182,8 +183,7 @@ next_track: | |||
182 | if (format.formattag == AU_FORMAT_UNSUPPORT) | 183 | if (format.formattag == AU_FORMAT_UNSUPPORT) |
183 | { | 184 | { |
184 | DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf + 12)); | 185 | DEBUGF("CODEC_ERROR: sun audio unsupport format: %d\n", get_be32(buf + 12)); |
185 | status = CODEC_ERROR; | 186 | return CODEC_ERROR; |
186 | goto done; | ||
187 | } | 187 | } |
188 | /* skip sample rate */ | 188 | /* skip sample rate */ |
189 | format.channels = get_be32(buf + 20); | 189 | format.channels = get_be32(buf + 20); |
@@ -202,20 +202,17 @@ next_track: | |||
202 | if (!codec) | 202 | if (!codec) |
203 | { | 203 | { |
204 | DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format.formattag); | 204 | DEBUGF("CODEC_ERROR: unsupport sun audio format: %x\n", (int)format.formattag); |
205 | status = CODEC_ERROR; | 205 | return CODEC_ERROR; |
206 | goto done; | ||
207 | } | 206 | } |
208 | 207 | ||
209 | if (!codec->set_format(&format)) | 208 | if (!codec->set_format(&format)) |
210 | { | 209 | { |
211 | status = CODEC_ERROR; | 210 | return CODEC_ERROR; |
212 | goto done; | ||
213 | } | 211 | } |
214 | 212 | ||
215 | if (format.numbytes == 0) { | 213 | if (format.numbytes == 0) { |
216 | DEBUGF("CODEC_ERROR: data size is 0\n"); | 214 | DEBUGF("CODEC_ERROR: data size is 0\n"); |
217 | status = CODEC_ERROR; | 215 | return CODEC_ERROR; |
218 | goto done; | ||
219 | } | 216 | } |
220 | 217 | ||
221 | /* check chunksize */ | 218 | /* check chunksize */ |
@@ -225,8 +222,7 @@ next_track: | |||
225 | if (format.chunksize == 0) | 222 | if (format.chunksize == 0) |
226 | { | 223 | { |
227 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); | 224 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); |
228 | status = CODEC_ERROR; | 225 | return CODEC_ERROR; |
229 | goto done; | ||
230 | } | 226 | } |
231 | 227 | ||
232 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 228 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
@@ -236,8 +232,7 @@ next_track: | |||
236 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | 232 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); |
237 | } else { | 233 | } else { |
238 | DEBUGF("CODEC_ERROR: more than 2 channels\n"); | 234 | DEBUGF("CODEC_ERROR: more than 2 channels\n"); |
239 | status = CODEC_ERROR; | 235 | return CODEC_ERROR; |
240 | goto done; | ||
241 | } | 236 | } |
242 | 237 | ||
243 | /* make sure we're at the correct offset */ | 238 | /* make sure we're at the correct offset */ |
@@ -253,7 +248,6 @@ next_track: | |||
253 | bytesdone = newpos->pos; | 248 | bytesdone = newpos->pos; |
254 | decodedsamples = newpos->samples; | 249 | decodedsamples = newpos->samples; |
255 | } | 250 | } |
256 | ci->seek_complete(); | ||
257 | } else { | 251 | } else { |
258 | /* already where we need to be */ | 252 | /* already where we need to be */ |
259 | bytesdone = 0; | 253 | bytesdone = 0; |
@@ -263,22 +257,29 @@ next_track: | |||
263 | endofstream = 0; | 257 | endofstream = 0; |
264 | 258 | ||
265 | while (!endofstream) { | 259 | while (!endofstream) { |
266 | ci->yield(); | 260 | enum codec_command_action action = ci->get_command(¶m); |
267 | if (ci->stop_codec || ci->new_track) { | 261 | |
262 | if (action == CODEC_ACTION_HALT) | ||
268 | break; | 263 | break; |
269 | } | ||
270 | 264 | ||
271 | if (ci->seek_time) { | 265 | if (action == CODEC_ACTION_SEEK_TIME) { |
272 | /* 3rd args(read_buffer) is unnecessary in the format which Sun Audio supports. */ | 266 | /* 3rd args(read_buffer) is unnecessary in the format which Sun Audio supports. */ |
273 | struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME, NULL); | 267 | struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, NULL); |
274 | 268 | ||
275 | if (newpos->pos > format.numbytes) | 269 | if (newpos->pos > format.numbytes) |
270 | { | ||
271 | ci->set_elapsed(ci->id3->length); | ||
272 | ci->seek_complete(); | ||
276 | break; | 273 | break; |
274 | } | ||
275 | |||
277 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 276 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
278 | { | 277 | { |
279 | bytesdone = newpos->pos; | 278 | bytesdone = newpos->pos; |
280 | decodedsamples = newpos->samples; | 279 | decodedsamples = newpos->samples; |
281 | } | 280 | } |
281 | |||
282 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
282 | ci->seek_complete(); | 283 | ci->seek_complete(); |
283 | } | 284 | } |
284 | 285 | ||
@@ -290,11 +291,10 @@ next_track: | |||
290 | endofstream = 1; | 291 | endofstream = 1; |
291 | } | 292 | } |
292 | 293 | ||
293 | status = codec->decode(aubuf, n, samples, &bufcount); | 294 | if (codec->decode(aubuf, n, samples, &bufcount) == CODEC_ERROR) |
294 | if (status == CODEC_ERROR) | ||
295 | { | 295 | { |
296 | DEBUGF("codec error\n"); | 296 | DEBUGF("codec error\n"); |
297 | goto done; | 297 | return CODEC_ERROR; |
298 | } | 298 | } |
299 | 299 | ||
300 | ci->pcmbuf_insert(samples, NULL, bufcount); | 300 | ci->pcmbuf_insert(samples, NULL, bufcount); |
@@ -308,9 +308,5 @@ next_track: | |||
308 | } | 308 | } |
309 | 309 | ||
310 | done: | 310 | done: |
311 | if (ci->request_next_track()) | 311 | return CODEC_OK; |
312 | goto next_track; | ||
313 | |||
314 | exit: | ||
315 | return status; | ||
316 | } | 312 | } |
diff --git a/apps/codecs/codec_crt0.c b/apps/codecs/codec_crt0.c index cf14e460ec..33876272c6 100644 --- a/apps/codecs/codec_crt0.c +++ b/apps/codecs/codec_crt0.c | |||
@@ -27,39 +27,45 @@ struct codec_api *ci DATA_ATTR; | |||
27 | extern unsigned char plugin_bss_start[]; | 27 | extern unsigned char plugin_bss_start[]; |
28 | extern unsigned char plugin_end_addr[]; | 28 | extern unsigned char plugin_end_addr[]; |
29 | 29 | ||
30 | extern enum codec_status codec_main(void); | 30 | extern enum codec_status codec_main(enum codec_entry_call_reason reason); |
31 | 31 | ||
32 | /* stub, the entry point is called via its reference in __header to | 32 | /* stub, the entry point is called via its reference in __header to |
33 | * avoid warning with certain compilers */ | 33 | * avoid warning with certain compilers */ |
34 | int _start(void) {return 0;} | 34 | int _start(void) {return 0;} |
35 | 35 | ||
36 | enum codec_status codec_start(void) | 36 | enum codec_status codec_start(enum codec_entry_call_reason reason) |
37 | { | 37 | { |
38 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 38 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
39 | #ifdef USE_IRAM | 39 | if (reason == CODEC_LOAD) |
40 | extern char iramcopy[], iramstart[], iramend[], iedata[], iend[]; | ||
41 | size_t iram_size = iramend - iramstart; | ||
42 | size_t ibss_size = iend - iedata; | ||
43 | if (iram_size > 0 || ibss_size > 0) | ||
44 | { | 40 | { |
45 | ci->memcpy(iramstart, iramcopy, iram_size); | 41 | #ifdef USE_IRAM |
46 | ci->memset(iedata, 0, ibss_size); | 42 | extern char iramcopy[], iramstart[], iramend[], iedata[], iend[]; |
47 | /* make the icache (if it exists) up to date with the new code */ | 43 | size_t iram_size = iramend - iramstart; |
44 | size_t ibss_size = iend - iedata; | ||
45 | if (iram_size > 0 || ibss_size > 0) | ||
46 | { | ||
47 | ci->memcpy(iramstart, iramcopy, iram_size); | ||
48 | ci->memset(iedata, 0, ibss_size); | ||
49 | /* make the icache (if it exists) up to date with the new code */ | ||
50 | ci->cpucache_invalidate(); | ||
51 | /* barrier to prevent reordering iram copy and BSS clearing, | ||
52 | * because the BSS segment alias the IRAM copy. | ||
53 | */ | ||
54 | asm volatile ("" ::: "memory"); | ||
55 | } | ||
56 | #endif /* PLUGIN_USE_IRAM */ | ||
57 | ci->memset(plugin_bss_start, 0, plugin_end_addr - plugin_bss_start); | ||
58 | /* Some parts of bss may be used via a no-cache alias (at least | ||
59 | * portalplayer has this). If we don't clear the cache, those aliases | ||
60 | * may read garbage */ | ||
48 | ci->cpucache_invalidate(); | 61 | ci->cpucache_invalidate(); |
49 | /* barrier to prevent reordering iram copy and BSS clearing, | ||
50 | * because the BSS segment alias the IRAM copy. | ||
51 | */ | ||
52 | asm volatile ("" ::: "memory"); | ||
53 | } | 62 | } |
54 | #endif /* PLUGIN_USE_IRAM */ | 63 | #endif /* CONFIG_PLATFORM */ |
55 | ci->memset(plugin_bss_start, 0, plugin_end_addr - plugin_bss_start); | ||
56 | /* Some parts of bss may be used via a no-cache alias (at least | ||
57 | * portalplayer has this). If we don't clear the cache, those aliases | ||
58 | * may read garbage */ | ||
59 | ci->cpucache_invalidate(); | ||
60 | #endif | ||
61 | 64 | ||
62 | return codec_main(); | 65 | /* Note: If for any reason codec_main would not be called with CODEC_LOAD |
66 | * because the above code failed then it must not be ever be called with | ||
67 | * any other value and some strategy to avoid doing so must be conceived */ | ||
68 | return codec_main(reason); | ||
63 | } | 69 | } |
64 | 70 | ||
65 | #if defined(CPU_ARM) && (CONFIG_PLATFORM & PLATFORM_NATIVE) | 71 | #if defined(CPU_ARM) && (CONFIG_PLATFORM & PLATFORM_NATIVE) |
diff --git a/apps/codecs/cook.c b/apps/codecs/cook.c index 015618986c..a6b4a1153e 100644 --- a/apps/codecs/cook.c +++ b/apps/codecs/cook.c | |||
@@ -38,9 +38,9 @@ static void init_rm(RMContext *rmctx) | |||
38 | memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); | 38 | memcpy(rmctx, (void*)(( (intptr_t)ci->id3->id3v2buf + 3 ) &~ 3), sizeof(RMContext)); |
39 | } | 39 | } |
40 | 40 | ||
41 | /* this is the codec entry point */ | 41 | /* this is called for each file to process */ |
42 | enum codec_status codec_main(void) | 42 | enum codec_status codec_run(void) |
43 | { | 43 | { |
44 | static size_t buff_size; | 44 | static size_t buff_size; |
45 | int datasize, res, consumed, i, time_offset; | 45 | int datasize, res, consumed, i, time_offset; |
46 | uint8_t *bit_buffer; | 46 | uint8_t *bit_buffer; |
@@ -48,16 +48,14 @@ enum codec_status codec_main(void) | |||
48 | uint32_t packet_count; | 48 | uint32_t packet_count; |
49 | int scrambling_unit_size, num_units; | 49 | int scrambling_unit_size, num_units; |
50 | size_t resume_offset; | 50 | size_t resume_offset; |
51 | intptr_t param = 0; | ||
52 | enum codec_command_action action = CODEC_ACTION_NULL; | ||
51 | 53 | ||
52 | next_track: | ||
53 | if (codec_init()) { | 54 | if (codec_init()) { |
54 | DEBUGF("codec init failed\n"); | 55 | DEBUGF("codec init failed\n"); |
55 | return CODEC_ERROR; | 56 | return CODEC_ERROR; |
56 | } | 57 | } |
57 | 58 | ||
58 | if (codec_wait_taginfo() != 0) | ||
59 | goto done; | ||
60 | |||
61 | resume_offset = ci->id3->offset; | 59 | resume_offset = ci->id3->offset; |
62 | 60 | ||
63 | codec_set_replaygain(ci->id3); | 61 | codec_set_replaygain(ci->id3); |
@@ -65,6 +63,8 @@ next_track: | |||
65 | ci->memset(&pkt,0,sizeof(RMPacket)); | 63 | ci->memset(&pkt,0,sizeof(RMPacket)); |
66 | ci->memset(&q,0,sizeof(COOKContext)); | 64 | ci->memset(&q,0,sizeof(COOKContext)); |
67 | 65 | ||
66 | ci->seek_buffer(0); | ||
67 | |||
68 | init_rm(&rmctx); | 68 | init_rm(&rmctx); |
69 | 69 | ||
70 | ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); | 70 | ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); |
@@ -87,20 +87,21 @@ next_track: | |||
87 | DEBUGF("failed to initialize cook decoder\n"); | 87 | DEBUGF("failed to initialize cook decoder\n"); |
88 | return CODEC_ERROR; | 88 | return CODEC_ERROR; |
89 | } | 89 | } |
90 | 90 | ||
91 | /* check for a mid-track resume and force a seek time accordingly */ | 91 | /* check for a mid-track resume and force a seek time accordingly */ |
92 | if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { | 92 | if(resume_offset > rmctx.data_offset + DATA_HEADER_SIZE) { |
93 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; | 93 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; |
94 | num_units = (int)resume_offset / scrambling_unit_size; | 94 | num_units = (int)resume_offset / scrambling_unit_size; |
95 | /* put number of subpackets to skip in resume_offset */ | 95 | /* put number of subpackets to skip in resume_offset */ |
96 | resume_offset /= (sps + PACKET_HEADER_SIZE); | 96 | resume_offset /= (sps + PACKET_HEADER_SIZE); |
97 | ci->seek_time = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); | 97 | param = (int)resume_offset * ((sps * 8 * 1000)/rmctx.bit_rate); |
98 | action = CODEC_ACTION_SEEK_TIME; | ||
98 | } | 99 | } |
99 | 100 | ||
100 | ci->set_elapsed(0); | 101 | ci->set_elapsed(0); |
101 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | 102 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); |
102 | 103 | ||
103 | /* The main decoder loop */ | 104 | /* The main decoder loop */ |
104 | seek_start : | 105 | seek_start : |
105 | while(packet_count) | 106 | while(packet_count) |
106 | { | 107 | { |
@@ -112,18 +113,19 @@ seek_start : | |||
112 | } | 113 | } |
113 | 114 | ||
114 | for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) | 115 | for(i = 0; i < rmctx.audio_pkt_cnt*(fs/sps) ; i++) |
115 | { | 116 | { |
116 | ci->yield(); | 117 | if (action == CODEC_ACTION_NULL) |
117 | if (ci->stop_codec || ci->new_track) | 118 | action = ci->get_command(¶m); |
118 | goto done; | ||
119 | 119 | ||
120 | if (ci->seek_time) { | 120 | if (action == CODEC_ACTION_HALT) |
121 | ci->set_elapsed(ci->seek_time); | 121 | return CODEC_OK; |
122 | 122 | ||
123 | if (action == CODEC_ACTION_SEEK_TIME) { | ||
123 | /* Do not allow seeking beyond the file's length */ | 124 | /* Do not allow seeking beyond the file's length */ |
124 | if ((unsigned) ci->seek_time > ci->id3->length) { | 125 | if ((unsigned) param > ci->id3->length) { |
126 | ci->set_elapsed(ci->id3->length); | ||
125 | ci->seek_complete(); | 127 | ci->seek_complete(); |
126 | goto done; | 128 | return CODEC_OK; |
127 | } | 129 | } |
128 | 130 | ||
129 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | 131 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); |
@@ -132,22 +134,24 @@ seek_start : | |||
132 | rmctx.frame_number = 0; | 134 | rmctx.frame_number = 0; |
133 | 135 | ||
134 | /* Seek to the start of the track */ | 136 | /* Seek to the start of the track */ |
135 | if (ci->seek_time == 1) { | 137 | if (param == 0) { |
136 | ci->set_elapsed(0); | 138 | ci->set_elapsed(0); |
137 | ci->seek_complete(); | 139 | ci->seek_complete(); |
140 | action = CODEC_ACTION_NULL; | ||
138 | goto seek_start; | 141 | goto seek_start; |
139 | } | 142 | } |
140 | num_units = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); | 143 | num_units = (param/(sps*1000*8/rmctx.bit_rate))/(h*(fs/sps)); |
141 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); | 144 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * num_units); |
142 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | 145 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); |
143 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); | 146 | consumed = rm_get_packet(&bit_buffer, &rmctx, &pkt); |
144 | if(consumed < 0) { | 147 | if(consumed < 0) { |
145 | DEBUGF("rm_get_packet failed\n"); | 148 | DEBUGF("rm_get_packet failed\n"); |
146 | return CODEC_ERROR; | 149 | ci->seek_complete(); |
150 | return CODEC_ERROR; | ||
147 | } | 151 | } |
148 | packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; | 152 | packet_count = rmctx.nb_packets - rmctx.audio_pkt_cnt * num_units; |
149 | rmctx.frame_number = ((ci->seek_time)/(sps*1000*8/rmctx.bit_rate)); | 153 | rmctx.frame_number = (param/(sps*1000*8/rmctx.bit_rate)); |
150 | while(rmctx.audiotimestamp > (unsigned) ci->seek_time) { | 154 | while(rmctx.audiotimestamp > (unsigned) param) { |
151 | rmctx.audio_pkt_cnt = 0; | 155 | rmctx.audio_pkt_cnt = 0; |
152 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); | 156 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE + consumed * (num_units-1)); |
153 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); | 157 | bit_buffer = (uint8_t *) ci->request_buffer(&buff_size, scrambling_unit_size); |
@@ -155,11 +159,14 @@ seek_start : | |||
155 | packet_count += rmctx.audio_pkt_cnt; | 159 | packet_count += rmctx.audio_pkt_cnt; |
156 | num_units--; | 160 | num_units--; |
157 | } | 161 | } |
158 | time_offset = ci->seek_time - rmctx.audiotimestamp; | 162 | time_offset = param - rmctx.audiotimestamp; |
159 | i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); | 163 | i = (time_offset/((sps * 8 * 1000)/rmctx.bit_rate)); |
160 | ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i); | 164 | ci->set_elapsed(rmctx.audiotimestamp+(1000*8*sps/rmctx.bit_rate)*i); |
161 | ci->seek_complete(); | 165 | ci->seek_complete(); |
162 | } | 166 | } |
167 | |||
168 | action = CODEC_ACTION_NULL; | ||
169 | |||
163 | res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align); | 170 | res = cook_decode_frame(&rmctx,&q, rm_outbuf, &datasize, pkt.frames[i], rmctx.block_align); |
164 | rmctx.frame_number++; | 171 | rmctx.frame_number++; |
165 | 172 | ||
@@ -181,9 +188,5 @@ seek_start : | |||
181 | ci->advance_buffer(consumed); | 188 | ci->advance_buffer(consumed); |
182 | } | 189 | } |
183 | 190 | ||
184 | done : | 191 | return CODEC_OK; |
185 | if (ci->request_next_track()) | ||
186 | goto next_track; | ||
187 | |||
188 | return CODEC_OK; | ||
189 | } | 192 | } |
diff --git a/apps/codecs/flac.c b/apps/codecs/flac.c index 89d14b98a7..a5521b584f 100644 --- a/apps/codecs/flac.c +++ b/apps/codecs/flac.c | |||
@@ -418,40 +418,40 @@ static bool flac_seek_offset(FLACContext* fc, uint32_t offset) { | |||
418 | } | 418 | } |
419 | 419 | ||
420 | /* this is the codec entry point */ | 420 | /* this is the codec entry point */ |
421 | enum codec_status codec_main(void) | 421 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
422 | { | ||
423 | if (reason == CODEC_LOAD) { | ||
424 | /* Generic codec initialisation */ | ||
425 | ci->configure(DSP_SET_SAMPLE_DEPTH, FLAC_OUTPUT_DEPTH-1); | ||
426 | } | ||
427 | |||
428 | return CODEC_OK; | ||
429 | } | ||
430 | |||
431 | /* this is called for each file to process */ | ||
432 | enum codec_status codec_run(void) | ||
422 | { | 433 | { |
423 | int8_t *buf; | 434 | int8_t *buf; |
424 | FLACContext fc; | 435 | FLACContext fc; |
425 | uint32_t samplesdone = 0; | 436 | uint32_t samplesdone; |
426 | uint32_t elapsedtime; | 437 | uint32_t elapsedtime; |
427 | size_t bytesleft; | 438 | size_t bytesleft; |
428 | int consumed; | 439 | int consumed; |
429 | int res; | 440 | int res; |
430 | int frame; | 441 | int frame; |
431 | int retval; | 442 | intptr_t param; |
432 | |||
433 | /* Generic codec initialisation */ | ||
434 | ci->configure(DSP_SET_SAMPLE_DEPTH, FLAC_OUTPUT_DEPTH-1); | ||
435 | 443 | ||
436 | next_track: | ||
437 | retval = CODEC_OK; | ||
438 | |||
439 | if (codec_init()) { | 444 | if (codec_init()) { |
440 | LOGF("FLAC: Error initialising codec\n"); | 445 | LOGF("FLAC: Error initialising codec\n"); |
441 | retval = CODEC_ERROR; | 446 | return CODEC_ERROR; |
442 | goto exit; | ||
443 | } | 447 | } |
444 | 448 | ||
445 | if (codec_wait_taginfo() != 0) | ||
446 | goto done; | ||
447 | |||
448 | /* Need to save offset for later use (cleared indirectly by flac_init) */ | 449 | /* Need to save offset for later use (cleared indirectly by flac_init) */ |
449 | samplesdone = ci->id3->offset; | 450 | samplesdone = ci->id3->offset; |
450 | 451 | ||
451 | if (!flac_init(&fc,ci->id3->first_frame_offset)) { | 452 | if (!flac_init(&fc,ci->id3->first_frame_offset)) { |
452 | LOGF("FLAC: Error initialising codec\n"); | 453 | LOGF("FLAC: Error initialising codec\n"); |
453 | retval = CODEC_ERROR; | 454 | return CODEC_ERROR; |
454 | goto done; | ||
455 | } | 455 | } |
456 | 456 | ||
457 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 457 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
@@ -459,35 +459,34 @@ next_track: | |||
459 | STEREO_MONO : STEREO_NONINTERLEAVED); | 459 | STEREO_MONO : STEREO_NONINTERLEAVED); |
460 | codec_set_replaygain(ci->id3); | 460 | codec_set_replaygain(ci->id3); |
461 | 461 | ||
462 | if (samplesdone) { | 462 | flac_seek_offset(&fc, samplesdone); |
463 | flac_seek_offset(&fc, samplesdone); | 463 | samplesdone=0; |
464 | samplesdone=0; | ||
465 | } | ||
466 | 464 | ||
467 | /* The main decoding loop */ | 465 | /* The main decoding loop */ |
468 | frame=0; | 466 | frame=0; |
469 | buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); | 467 | buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); |
470 | while (bytesleft) { | 468 | while (bytesleft) { |
471 | ci->yield(); | 469 | enum codec_command_action action = ci->get_command(¶m); |
472 | if (ci->stop_codec || ci->new_track) { | 470 | |
471 | if (action == CODEC_ACTION_HALT) | ||
473 | break; | 472 | break; |
474 | } | ||
475 | 473 | ||
476 | /* Deal with any pending seek requests */ | 474 | /* Deal with any pending seek requests */ |
477 | if (ci->seek_time) { | 475 | if (action == CODEC_ACTION_SEEK_TIME) { |
478 | if (flac_seek(&fc,(uint32_t)(((uint64_t)(ci->seek_time-1) | 476 | if (flac_seek(&fc,(uint32_t)(((uint64_t)param |
479 | *ci->id3->frequency)/1000))) { | 477 | *ci->id3->frequency)/1000))) { |
480 | /* Refill the input buffer */ | 478 | /* Refill the input buffer */ |
481 | buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); | 479 | buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); |
482 | } | 480 | } |
481 | |||
482 | ci->set_elapsed(param); | ||
483 | ci->seek_complete(); | 483 | ci->seek_complete(); |
484 | } | 484 | } |
485 | 485 | ||
486 | if((res=flac_decode_frame(&fc,decoded0,decoded1,buf, | 486 | if((res=flac_decode_frame(&fc,decoded0,decoded1,buf, |
487 | bytesleft,ci->yield)) < 0) { | 487 | bytesleft,ci->yield)) < 0) { |
488 | LOGF("FLAC: Frame %d, error %d\n",frame,res); | 488 | LOGF("FLAC: Frame %d, error %d\n",frame,res); |
489 | retval = CODEC_ERROR; | 489 | return CODEC_ERROR; |
490 | goto done; | ||
491 | } | 490 | } |
492 | consumed=fc.gb.index/8; | 491 | consumed=fc.gb.index/8; |
493 | frame++; | 492 | frame++; |
@@ -507,14 +506,7 @@ next_track: | |||
507 | 506 | ||
508 | buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); | 507 | buf = ci->request_buffer(&bytesleft, MAX_FRAMESIZE); |
509 | } | 508 | } |
510 | retval = CODEC_OK; | ||
511 | 509 | ||
512 | done: | ||
513 | LOGF("FLAC: Decoded %lu samples\n",(unsigned long)samplesdone); | 510 | LOGF("FLAC: Decoded %lu samples\n",(unsigned long)samplesdone); |
514 | 511 | return CODEC_OK; | |
515 | if (ci->request_next_track()) | ||
516 | goto next_track; | ||
517 | |||
518 | exit: | ||
519 | return retval; | ||
520 | } | 512 | } |
diff --git a/apps/codecs/lib/codeclib.c b/apps/codecs/lib/codeclib.c index af0894c498..443c0bbdcf 100644 --- a/apps/codecs/lib/codeclib.c +++ b/apps/codecs/lib/codeclib.c | |||
@@ -33,6 +33,16 @@ unsigned char* mp3buf; // The actual MP3 buffer from Rockbox | |||
33 | unsigned char* mallocbuf; // 512K from the start of MP3 buffer | 33 | unsigned char* mallocbuf; // 512K from the start of MP3 buffer |
34 | unsigned char* filebuf; // The rest of the MP3 buffer | 34 | unsigned char* filebuf; // The rest of the MP3 buffer |
35 | 35 | ||
36 | /* this is the default codec entry point for when nothing needs to be done | ||
37 | on load or unload */ | ||
38 | enum codec_status __attribute__((weak)) | ||
39 | codec_main(enum codec_entry_call_reason reason) | ||
40 | { | ||
41 | /* Nothing to do */ | ||
42 | return CODEC_OK; | ||
43 | (void)reason; | ||
44 | } | ||
45 | |||
36 | int codec_init(void) | 46 | int codec_init(void) |
37 | { | 47 | { |
38 | mem_ptr = 0; | 48 | mem_ptr = 0; |
@@ -41,7 +51,7 @@ int codec_init(void) | |||
41 | return 0; | 51 | return 0; |
42 | } | 52 | } |
43 | 53 | ||
44 | void codec_set_replaygain(struct mp3entry* id3) | 54 | void codec_set_replaygain(const struct mp3entry *id3) |
45 | { | 55 | { |
46 | ci->configure(DSP_SET_TRACK_GAIN, id3->track_gain); | 56 | ci->configure(DSP_SET_TRACK_GAIN, id3->track_gain); |
47 | ci->configure(DSP_SET_ALBUM_GAIN, id3->album_gain); | 57 | ci->configure(DSP_SET_ALBUM_GAIN, id3->album_gain); |
@@ -49,19 +59,6 @@ void codec_set_replaygain(struct mp3entry* id3) | |||
49 | ci->configure(DSP_SET_ALBUM_PEAK, id3->album_peak); | 59 | ci->configure(DSP_SET_ALBUM_PEAK, id3->album_peak); |
50 | } | 60 | } |
51 | 61 | ||
52 | /* Note: codec really needs its own private metdata copy for the current | ||
53 | track being processed in order to be stable. */ | ||
54 | int codec_wait_taginfo(void) | ||
55 | { | ||
56 | while (!*ci->taginfo_ready && !ci->stop_codec && !ci->new_track) | ||
57 | ci->sleep(0); | ||
58 | if (ci->stop_codec) | ||
59 | return -1; | ||
60 | if (ci->new_track) | ||
61 | return 1; | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | /* Various "helper functions" common to all the xxx2wav decoder plugins */ | 62 | /* Various "helper functions" common to all the xxx2wav decoder plugins */ |
66 | 63 | ||
67 | 64 | ||
diff --git a/apps/codecs/lib/codeclib.h b/apps/codecs/lib/codeclib.h index 41b466ed1f..30091c5333 100644 --- a/apps/codecs/lib/codeclib.h +++ b/apps/codecs/lib/codeclib.h | |||
@@ -156,8 +156,7 @@ static inline unsigned int bs_generic(unsigned int v, int mode) | |||
156 | /* Various codec helper functions */ | 156 | /* Various codec helper functions */ |
157 | 157 | ||
158 | int codec_init(void); | 158 | int codec_init(void); |
159 | void codec_set_replaygain(struct mp3entry* id3); | 159 | void codec_set_replaygain(const struct mp3entry *id3); |
160 | int codec_wait_taginfo(void); /* 0 = success */ | ||
161 | 160 | ||
162 | #ifdef RB_PROFILE | 161 | #ifdef RB_PROFILE |
163 | void __cyg_profile_func_enter(void *this_fn, void *call_site) | 162 | void __cyg_profile_func_enter(void *this_fn, void *call_site) |
diff --git a/apps/codecs/mod.c b/apps/codecs/mod.c index 4ace721a1e..3dfaac663f 100644 --- a/apps/codecs/mod.c +++ b/apps/codecs/mod.c | |||
@@ -1218,47 +1218,37 @@ void synthrender(int32_t *renderbuffer, int samplecount) | |||
1218 | } | 1218 | } |
1219 | } | 1219 | } |
1220 | 1220 | ||
1221 | /* this is the codec entry point */ | ||
1222 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
1223 | { | ||
1224 | if (reason == CODEC_LOAD) { | ||
1225 | /* Make use of 44.1khz */ | ||
1226 | ci->configure(DSP_SET_FREQUENCY, 44100); | ||
1227 | /* Sample depth is 28 bit host endian */ | ||
1228 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
1229 | /* Stereo output */ | ||
1230 | ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); | ||
1231 | } | ||
1232 | |||
1233 | return CODEC_OK; | ||
1234 | } | ||
1221 | 1235 | ||
1222 | enum codec_status codec_main(void) | 1236 | /* this is called for each file to process */ |
1237 | enum codec_status codec_run(void) | ||
1223 | { | 1238 | { |
1224 | size_t n; | 1239 | size_t n; |
1225 | unsigned char *modfile; | 1240 | unsigned char *modfile; |
1226 | int old_patterntableposition; | 1241 | int old_patterntableposition; |
1227 | |||
1228 | int bytesdone; | 1242 | int bytesdone; |
1243 | intptr_t param; | ||
1229 | 1244 | ||
1230 | next_track: | ||
1231 | if (codec_init()) { | 1245 | if (codec_init()) { |
1232 | return CODEC_ERROR; | 1246 | return CODEC_ERROR; |
1233 | } | 1247 | } |
1234 | 1248 | ||
1235 | if (codec_wait_taginfo() != 0) | ||
1236 | goto request_next_track; | ||
1237 | |||
1238 | codec_set_replaygain(ci->id3); | 1249 | codec_set_replaygain(ci->id3); |
1239 | 1250 | ||
1240 | /* Load MOD file */ | 1251 | /* Load MOD file */ |
1241 | /* | ||
1242 | * This is the save way | ||
1243 | |||
1244 | size_t bytesfree; | ||
1245 | unsigned int filesize; | ||
1246 | |||
1247 | p = modfile; | ||
1248 | bytesfree=sizeof(modfile); | ||
1249 | while ((n = ci->read_filebuf(p, bytesfree)) > 0) { | ||
1250 | p += n; | ||
1251 | bytesfree -= n; | ||
1252 | if (bytesfree == 0) | ||
1253 | return CODEC_ERROR; | ||
1254 | } | ||
1255 | filesize = p-modfile; | ||
1256 | |||
1257 | if (filesize == 0) | ||
1258 | return CODEC_ERROR; | ||
1259 | */ | ||
1260 | |||
1261 | /* Directly use mod in buffer */ | ||
1262 | ci->seek_buffer(0); | 1252 | ci->seek_buffer(0); |
1263 | modfile = ci->request_buffer(&n, ci->filesize); | 1253 | modfile = ci->request_buffer(&n, ci->filesize); |
1264 | if (!modfile || n < (size_t)ci->filesize) { | 1254 | if (!modfile || n < (size_t)ci->filesize) { |
@@ -1268,27 +1258,22 @@ next_track: | |||
1268 | initmodplayer(); | 1258 | initmodplayer(); |
1269 | loadmod(modfile); | 1259 | loadmod(modfile); |
1270 | 1260 | ||
1271 | /* Make use of 44.1khz */ | ||
1272 | ci->configure(DSP_SET_FREQUENCY, 44100); | ||
1273 | /* Sample depth is 28 bit host endian */ | ||
1274 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
1275 | /* Stereo output */ | ||
1276 | ci->configure(DSP_SET_STEREO_MODE, STEREO_INTERLEAVED); | ||
1277 | |||
1278 | /* The main decoder loop */ | 1261 | /* The main decoder loop */ |
1279 | ci->set_elapsed(0); | 1262 | ci->set_elapsed(0); |
1280 | bytesdone = 0; | 1263 | bytesdone = 0; |
1281 | old_patterntableposition = 0; | 1264 | old_patterntableposition = 0; |
1282 | 1265 | ||
1283 | while (1) { | 1266 | while (1) { |
1284 | ci->yield(); | 1267 | enum codec_command_action action = ci->get_command(¶m); |
1285 | if (ci->stop_codec || ci->new_track) | 1268 | |
1269 | if (action == CODEC_ACTION_HALT) | ||
1286 | break; | 1270 | break; |
1287 | 1271 | ||
1288 | if (ci->seek_time) { | 1272 | if (action == CODEC_ACTION_SEEK_TIME) { |
1289 | /* New time is ready in ci->seek_time */ | 1273 | /* New time is ready in param */ |
1290 | modplayer.patterntableposition = ci->seek_time/1000; | 1274 | modplayer.patterntableposition = param/1000; |
1291 | modplayer.currentline = 0; | 1275 | modplayer.currentline = 0; |
1276 | ci->set_elapsed(modplayer.patterntableposition*1000+500); | ||
1292 | ci->seek_complete(); | 1277 | ci->seek_complete(); |
1293 | } | 1278 | } |
1294 | 1279 | ||
@@ -1305,9 +1290,5 @@ next_track: | |||
1305 | 1290 | ||
1306 | } | 1291 | } |
1307 | 1292 | ||
1308 | request_next_track: | ||
1309 | if (ci->request_next_track()) | ||
1310 | goto next_track; | ||
1311 | |||
1312 | return CODEC_OK; | 1293 | return CODEC_OK; |
1313 | } | 1294 | } |
diff --git a/apps/codecs/mp3_enc.c b/apps/codecs/mp3_enc.c index e7893fd14a..2f5528f74c 100644 --- a/apps/codecs/mp3_enc.c +++ b/apps/codecs/mp3_enc.c | |||
@@ -2584,45 +2584,46 @@ static bool enc_init(void) | |||
2584 | return true; | 2584 | return true; |
2585 | } /* enc_init */ | 2585 | } /* enc_init */ |
2586 | 2586 | ||
2587 | enum codec_status codec_main(void) | 2587 | /* this is the codec entry point */ |
2588 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
2588 | { | 2589 | { |
2589 | /* Generic codec initialisation */ | 2590 | if (reason == CODEC_LOAD) { |
2590 | if (!enc_init()) | 2591 | if (!enc_init()) |
2591 | return CODEC_ERROR; | 2592 | return CODEC_ERROR; |
2593 | } | ||
2594 | else if (reason == CODEC_UNLOAD) { | ||
2595 | /* reset parameters to initial state */ | ||
2596 | ci->enc_set_parameters(NULL); | ||
2597 | } | ||
2598 | |||
2599 | return CODEC_OK; | ||
2600 | } | ||
2592 | 2601 | ||
2602 | /* this is called for each file to process */ | ||
2603 | enum codec_status codec_run(void) | ||
2604 | { | ||
2593 | /* main encoding loop */ | 2605 | /* main encoding loop */ |
2594 | while (!ci->stop_codec) | 2606 | while(ci->get_command(NULL) != CODEC_ACTION_HALT) |
2595 | { | 2607 | { |
2596 | char *buffer; | 2608 | char *buffer = buffer = ci->enc_get_pcm_data(pcm_chunk_size); |
2597 | 2609 | struct enc_chunk_hdr *chunk; | |
2598 | while ((buffer = ci->enc_get_pcm_data(pcm_chunk_size)) != NULL) | ||
2599 | { | ||
2600 | struct enc_chunk_hdr *chunk; | ||
2601 | |||
2602 | if (ci->stop_codec) | ||
2603 | break; | ||
2604 | 2610 | ||
2605 | chunk = ci->enc_get_chunk(); | 2611 | if(buffer == NULL) |
2606 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); | 2612 | continue; |
2607 | 2613 | ||
2608 | encode_frame(buffer, chunk); | 2614 | chunk = ci->enc_get_chunk(); |
2615 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); | ||
2609 | 2616 | ||
2610 | if (chunk->num_pcm < samp_per_frame) | 2617 | encode_frame(buffer, chunk); |
2611 | { | ||
2612 | ci->enc_unget_pcm_data(pcm_chunk_size - chunk->num_pcm*4); | ||
2613 | chunk->num_pcm = samp_per_frame; | ||
2614 | } | ||
2615 | 2618 | ||
2616 | ci->enc_finish_chunk(); | 2619 | if (chunk->num_pcm < samp_per_frame) |
2617 | 2620 | { | |
2618 | ci->yield(); | 2621 | ci->enc_unget_pcm_data(pcm_chunk_size - chunk->num_pcm*4); |
2622 | chunk->num_pcm = samp_per_frame; | ||
2619 | } | 2623 | } |
2620 | 2624 | ||
2621 | ci->yield(); | 2625 | ci->enc_finish_chunk(); |
2622 | } | 2626 | } |
2623 | 2627 | ||
2624 | /* reset parameters to initial state */ | ||
2625 | ci->enc_set_parameters(NULL); | ||
2626 | |||
2627 | return CODEC_OK; | 2628 | return CODEC_OK; |
2628 | } /* codec_start */ | 2629 | } |
diff --git a/apps/codecs/mpa.c b/apps/codecs/mpa.c index 4b49775029..c9e2131450 100644 --- a/apps/codecs/mpa.c +++ b/apps/codecs/mpa.c | |||
@@ -268,8 +268,8 @@ static bool mad_synth_thread_create(void) | |||
268 | 268 | ||
269 | static void mad_synth_thread_quit(void) | 269 | static void mad_synth_thread_quit(void) |
270 | { | 270 | { |
271 | /*mop up COP thread*/ | 271 | /* mop up COP thread */ |
272 | die=1; | 272 | die = 1; |
273 | ci->semaphore_release(&synth_pending_sem); | 273 | ci->semaphore_release(&synth_pending_sem); |
274 | ci->thread_wait(mad_synth_thread_id); | 274 | ci->thread_wait(mad_synth_thread_id); |
275 | ci->cpucache_invalidate(); | 275 | ci->cpucache_invalidate(); |
@@ -299,9 +299,30 @@ static inline void mad_synth_thread_unwait_pcm(void) | |||
299 | #endif /* MPA_SYNTH_ON_COP */ | 299 | #endif /* MPA_SYNTH_ON_COP */ |
300 | 300 | ||
301 | /* this is the codec entry point */ | 301 | /* this is the codec entry point */ |
302 | enum codec_status codec_main(void) | 302 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
303 | { | ||
304 | if (reason == CODEC_LOAD) { | ||
305 | /* Create a decoder instance */ | ||
306 | if (codec_init()) | ||
307 | return CODEC_ERROR; | ||
308 | |||
309 | ci->configure(DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS); | ||
310 | |||
311 | /* does nothing on 1 processor systems except return true */ | ||
312 | if(!mad_synth_thread_create()) | ||
313 | return CODEC_ERROR; | ||
314 | } | ||
315 | else if (reason == CODEC_UNLOAD) { | ||
316 | /* mop up COP thread - MT only */ | ||
317 | mad_synth_thread_quit(); | ||
318 | } | ||
319 | |||
320 | return CODEC_OK; | ||
321 | } | ||
322 | |||
323 | /* this is called for each file to process */ | ||
324 | enum codec_status codec_run(void) | ||
303 | { | 325 | { |
304 | int status; | ||
305 | size_t size; | 326 | size_t size; |
306 | int file_end; | 327 | int file_end; |
307 | int samples_to_skip; /* samples to skip in total for this file (at start) */ | 328 | int samples_to_skip; /* samples to skip in total for this file (at start) */ |
@@ -312,27 +333,12 @@ enum codec_status codec_main(void) | |||
312 | unsigned long current_frequency = 0; | 333 | unsigned long current_frequency = 0; |
313 | int framelength; | 334 | int framelength; |
314 | int padding = MAD_BUFFER_GUARD; /* to help mad decode the last frame */ | 335 | int padding = MAD_BUFFER_GUARD; /* to help mad decode the last frame */ |
315 | 336 | intptr_t param; | |
316 | if (codec_init()) | ||
317 | return CODEC_ERROR; | ||
318 | |||
319 | /* Create a decoder instance */ | ||
320 | |||
321 | ci->configure(DSP_SET_SAMPLE_DEPTH, MAD_F_FRACBITS); | ||
322 | |||
323 | /*does nothing on 1 processor systems except return true*/ | ||
324 | if(!mad_synth_thread_create()) | ||
325 | return CODEC_ERROR; | ||
326 | |||
327 | next_track: | ||
328 | status = CODEC_OK; | ||
329 | 337 | ||
330 | /* Reinitializing seems to be necessary to avoid playback quircks when seeking. */ | 338 | /* Reinitializing seems to be necessary to avoid playback quircks when seeking. */ |
331 | init_mad(); | 339 | init_mad(); |
332 | 340 | ||
333 | file_end = 0; | 341 | file_end = 0; |
334 | if (codec_wait_taginfo() != 0) | ||
335 | goto request_next_track; | ||
336 | 342 | ||
337 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 343 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
338 | current_frequency = ci->id3->frequency; | 344 | current_frequency = ci->id3->frequency; |
@@ -379,29 +385,35 @@ next_track: | |||
379 | 385 | ||
380 | /* This is the decoding loop. */ | 386 | /* This is the decoding loop. */ |
381 | while (1) { | 387 | while (1) { |
382 | ci->yield(); | 388 | enum codec_command_action action = ci->get_command(¶m); |
383 | if (ci->stop_codec || ci->new_track) | 389 | |
390 | if (action == CODEC_ACTION_HALT) | ||
384 | break; | 391 | break; |
385 | 392 | ||
386 | if (ci->seek_time) { | 393 | if (action == CODEC_ACTION_SEEK_TIME) { |
387 | int newpos; | 394 | int newpos; |
388 | 395 | ||
389 | /*make sure the synth thread is idle before seeking - MT only*/ | 396 | /*make sure the synth thread is idle before seeking - MT only*/ |
390 | mad_synth_thread_wait_pcm(); | 397 | mad_synth_thread_wait_pcm(); |
391 | mad_synth_thread_unwait_pcm(); | 398 | mad_synth_thread_unwait_pcm(); |
392 | 399 | ||
393 | samplesdone = ((int64_t)(ci->seek_time-1))*current_frequency/1000; | 400 | samplesdone = ((int64_t)param)*current_frequency/1000; |
394 | 401 | ||
395 | if (ci->seek_time-1 == 0) { | 402 | if (param == 0) { |
396 | newpos = ci->id3->first_frame_offset; | 403 | newpos = ci->id3->first_frame_offset; |
397 | samples_to_skip = start_skip; | 404 | samples_to_skip = start_skip; |
398 | } else { | 405 | } else { |
399 | newpos = get_file_pos(ci->seek_time-1); | 406 | newpos = get_file_pos(param); |
400 | samples_to_skip = 0; | 407 | samples_to_skip = 0; |
401 | } | 408 | } |
402 | 409 | ||
403 | if (!ci->seek_buffer(newpos)) | 410 | if (!ci->seek_buffer(newpos)) |
411 | { | ||
412 | ci->seek_complete(); | ||
404 | break; | 413 | break; |
414 | } | ||
415 | |||
416 | ci->set_elapsed((samplesdone * 1000) / current_frequency); | ||
405 | ci->seek_complete(); | 417 | ci->seek_complete(); |
406 | init_mad(); | 418 | init_mad(); |
407 | framelength = 0; | 419 | framelength = 0; |
@@ -435,8 +447,7 @@ next_track: | |||
435 | continue; | 447 | continue; |
436 | } else { | 448 | } else { |
437 | /* Some other unrecoverable error */ | 449 | /* Some other unrecoverable error */ |
438 | status = CODEC_ERROR; | 450 | return CODEC_ERROR; |
439 | break; | ||
440 | } | 451 | } |
441 | } | 452 | } |
442 | 453 | ||
@@ -504,12 +515,5 @@ next_track: | |||
504 | framelength - stop_skip); | 515 | framelength - stop_skip); |
505 | } | 516 | } |
506 | 517 | ||
507 | request_next_track: | 518 | return CODEC_OK; |
508 | if (ci->request_next_track()) | ||
509 | goto next_track; | ||
510 | |||
511 | /*mop up COP thread - MT only*/ | ||
512 | mad_synth_thread_quit(); | ||
513 | |||
514 | return status; | ||
515 | } | 519 | } |
diff --git a/apps/codecs/mpc.c b/apps/codecs/mpc.c index 187c37e597..bbe2d9943b 100644 --- a/apps/codecs/mpc.c +++ b/apps/codecs/mpc.c | |||
@@ -52,8 +52,20 @@ static mpc_int32_t get_size_impl(mpc_reader *reader) | |||
52 | return ci->filesize; | 52 | return ci->filesize; |
53 | } | 53 | } |
54 | 54 | ||
55 | /* This is the codec entry point. */ | 55 | /* this is the codec entry point */ |
56 | enum codec_status codec_main(void) | 56 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
57 | { | ||
58 | if (reason == CODEC_LOAD) { | ||
59 | /* musepack's sample representation is 18.14 | ||
60 | * DSP_SET_SAMPLE_DEPTH = 14 (FRACT) + 16 (NATIVE) - 1 (SIGN) = 29 */ | ||
61 | ci->configure(DSP_SET_SAMPLE_DEPTH, 29); | ||
62 | } | ||
63 | |||
64 | return CODEC_OK; | ||
65 | } | ||
66 | |||
67 | /* this is called for each file to process */ | ||
68 | enum codec_status codec_run(void) | ||
57 | { | 69 | { |
58 | mpc_int64_t samplesdone; | 70 | mpc_int64_t samplesdone; |
59 | uint32_t frequency; /* 0.1 kHz accuracy */ | 71 | uint32_t frequency; /* 0.1 kHz accuracy */ |
@@ -64,39 +76,27 @@ enum codec_status codec_main(void) | |||
64 | mpc_streaminfo info; | 76 | mpc_streaminfo info; |
65 | mpc_frame_info frame; | 77 | mpc_frame_info frame; |
66 | mpc_demux *demux = NULL; | 78 | mpc_demux *demux = NULL; |
67 | int retval; | 79 | intptr_t param; |
68 | 80 | ||
69 | frame.buffer = sample_buffer; | 81 | frame.buffer = sample_buffer; |
70 | 82 | ||
71 | /* musepack's sample representation is 18.14 | ||
72 | * DSP_SET_SAMPLE_DEPTH = 14 (FRACT) + 16 (NATIVE) - 1 (SIGN) = 29 */ | ||
73 | ci->configure(DSP_SET_SAMPLE_DEPTH, 29); | ||
74 | |||
75 | /* Create a decoder instance */ | 83 | /* Create a decoder instance */ |
76 | reader.read = read_impl; | 84 | reader.read = read_impl; |
77 | reader.seek = seek_impl; | 85 | reader.seek = seek_impl; |
78 | reader.tell = tell_impl; | 86 | reader.tell = tell_impl; |
79 | reader.get_size = get_size_impl; | 87 | reader.get_size = get_size_impl; |
80 | 88 | ||
81 | next_track: | ||
82 | retval = CODEC_OK; | ||
83 | |||
84 | if (codec_init()) | 89 | if (codec_init()) |
85 | { | 90 | return CODEC_ERROR; |
86 | retval = CODEC_ERROR; | ||
87 | goto exit; | ||
88 | } | ||
89 | 91 | ||
90 | if (codec_wait_taginfo() != 0) | 92 | /* Prep position */ |
91 | goto done; | 93 | ci->seek_buffer(0); |
92 | 94 | ||
93 | /* Initialize demux/decoder. */ | 95 | /* Initialize demux/decoder. */ |
94 | demux = mpc_demux_init(&reader); | 96 | demux = mpc_demux_init(&reader); |
95 | if (NULL == demux) | 97 | if (NULL == demux) |
96 | { | 98 | return CODEC_ERROR; |
97 | retval = CODEC_ERROR; | 99 | |
98 | goto done; | ||
99 | } | ||
100 | /* Read file's streaminfo data. */ | 100 | /* Read file's streaminfo data. */ |
101 | mpc_demux_get_info(demux, &info); | 101 | mpc_demux_get_info(demux, &info); |
102 | 102 | ||
@@ -117,11 +117,8 @@ next_track: | |||
117 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | 117 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); |
118 | else if (info.channels == 1) | 118 | else if (info.channels == 1) |
119 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | 119 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); |
120 | else | 120 | else |
121 | { | 121 | return CODEC_ERROR; |
122 | retval = CODEC_ERROR; | ||
123 | goto done; | ||
124 | } | ||
125 | 122 | ||
126 | codec_set_replaygain(ci->id3); | 123 | codec_set_replaygain(ci->id3); |
127 | 124 | ||
@@ -142,21 +139,24 @@ next_track: | |||
142 | /* This is the decoding loop. */ | 139 | /* This is the decoding loop. */ |
143 | do | 140 | do |
144 | { | 141 | { |
142 | enum codec_command_action action = ci->get_command(¶m); | ||
143 | |||
144 | if (action == CODEC_ACTION_HALT) | ||
145 | return CODEC_OK; | ||
146 | |||
145 | /* Complete seek handler. */ | 147 | /* Complete seek handler. */ |
146 | if (ci->seek_time) | 148 | if (action == CODEC_ACTION_SEEK_TIME) |
147 | { | 149 | { |
148 | mpc_int64_t new_offset = ((ci->seek_time - 1)/10)*frequency; | 150 | mpc_int64_t new_offset = (param/10)*frequency; |
149 | if (mpc_demux_seek_sample(demux, new_offset) == MPC_STATUS_OK) | 151 | if (mpc_demux_seek_sample(demux, new_offset) == MPC_STATUS_OK) |
150 | { | 152 | { |
151 | samplesdone = new_offset; | 153 | samplesdone = new_offset; |
152 | ci->set_elapsed(ci->seek_time); | ||
153 | } | 154 | } |
155 | |||
156 | elapsed_time = (samplesdone*10)/frequency; | ||
157 | ci->set_elapsed(elapsed_time); | ||
154 | ci->seek_complete(); | 158 | ci->seek_complete(); |
155 | } | 159 | } |
156 | |||
157 | /* Stop or skip occured, exit decoding loop. */ | ||
158 | if (ci->stop_codec || ci->new_track) | ||
159 | break; | ||
160 | 160 | ||
161 | /* Decode one frame. */ | 161 | /* Decode one frame. */ |
162 | status = mpc_demux_decode(demux, &frame); | 162 | status = mpc_demux_decode(demux, &frame); |
@@ -164,8 +164,7 @@ next_track: | |||
164 | if (frame.bits == -1) | 164 | if (frame.bits == -1) |
165 | { | 165 | { |
166 | /* Decoding error, exit decoding loop. */ | 166 | /* Decoding error, exit decoding loop. */ |
167 | retval = (status == MPC_STATUS_OK) ? CODEC_OK : CODEC_ERROR; | 167 | return (status == MPC_STATUS_OK) ? CODEC_OK : CODEC_ERROR; |
168 | goto done; | ||
169 | } | 168 | } |
170 | else | 169 | else |
171 | { | 170 | { |
@@ -181,11 +180,4 @@ next_track: | |||
181 | ci->set_offset( (samplesdone * byterate)/(frequency*100) ); | 180 | ci->set_offset( (samplesdone * byterate)/(frequency*100) ); |
182 | } | 181 | } |
183 | } while (true); | 182 | } while (true); |
184 | |||
185 | done: | ||
186 | if (ci->request_next_track()) | ||
187 | goto next_track; | ||
188 | |||
189 | exit: | ||
190 | return retval; | ||
191 | } | 183 | } |
diff --git a/apps/codecs/nsf.c b/apps/codecs/nsf.c index 2f37da81d2..72f6974214 100644 --- a/apps/codecs/nsf.c +++ b/apps/codecs/nsf.c | |||
@@ -4307,46 +4307,44 @@ static void set_codec_track(int t, int d) { | |||
4307 | nSilenceTrackMS=5000; | 4307 | nSilenceTrackMS=5000; |
4308 | SetFadeTime(track,track+fade, fNSFPlaybackSpeed,def); | 4308 | SetFadeTime(track,track+fade, fNSFPlaybackSpeed,def); |
4309 | } | 4309 | } |
4310 | ci->id3->elapsed=d*1000; /* d is track no to display */ | 4310 | ci->set_elapsed(d*1000); /* d is track no to display */ |
4311 | } | 4311 | } |
4312 | 4312 | ||
4313 | /** Operational info **/ | ||
4314 | static int track = 0; | ||
4315 | static char last_path[MAX_PATH]; | ||
4316 | static int dontresettrack = 0; | ||
4317 | |||
4313 | /* this is the codec entry point */ | 4318 | /* this is the codec entry point */ |
4314 | enum codec_status codec_main(void) | 4319 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
4320 | { | ||
4321 | if (reason == CODEC_LOAD) { | ||
4322 | /* we only render 16 bits, 44.1KHz, Stereo */ | ||
4323 | ci->configure(DSP_SET_SAMPLE_DEPTH, 16); | ||
4324 | ci->configure(DSP_SET_FREQUENCY, 44100); | ||
4325 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | ||
4326 | |||
4327 | RebuildOutputTables(); | ||
4328 | } | ||
4329 | |||
4330 | return CODEC_OK; | ||
4331 | } | ||
4332 | |||
4333 | /* this is called for each file to process */ | ||
4334 | enum codec_status codec_run(void) | ||
4315 | { | 4335 | { |
4316 | int written; | 4336 | int written; |
4317 | uint8_t *buf; | 4337 | uint8_t *buf; |
4318 | size_t n; | 4338 | size_t n; |
4319 | int endofstream; /* end of stream flag */ | 4339 | int endofstream; /* end of stream flag */ |
4320 | int track; | 4340 | int usingplaylist = 0; |
4321 | int dontresettrack; | ||
4322 | char last_path[MAX_PATH]; | ||
4323 | int usingplaylist; | ||
4324 | |||
4325 | /* we only render 16 bits */ | ||
4326 | ci->configure(DSP_SET_SAMPLE_DEPTH, 16); | ||
4327 | |||
4328 | ci->configure(DSP_SET_FREQUENCY, 44100); | ||
4329 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | ||
4330 | |||
4331 | RebuildOutputTables(); | ||
4332 | |||
4333 | dontresettrack=0; | ||
4334 | last_path[0]='\0'; | ||
4335 | track=0; | ||
4336 | 4341 | ||
4337 | next_track: | ||
4338 | usingplaylist=0; | ||
4339 | DEBUGF("NSF: next_track\n"); | 4342 | DEBUGF("NSF: next_track\n"); |
4340 | if (codec_init()) { | 4343 | if (codec_init()) { |
4341 | return CODEC_ERROR; | 4344 | return CODEC_ERROR; |
4342 | } | 4345 | } |
4343 | DEBUGF("NSF: after init\n"); | 4346 | DEBUGF("NSF: after init\n"); |
4344 | 4347 | ||
4345 | |||
4346 | /* wait for track info to load */ | ||
4347 | if (codec_wait_taginfo() != 0) | ||
4348 | goto request_next_track; | ||
4349 | |||
4350 | codec_set_replaygain(ci->id3); | 4348 | codec_set_replaygain(ci->id3); |
4351 | 4349 | ||
4352 | /* Read the entire file */ | 4350 | /* Read the entire file */ |
@@ -4408,22 +4406,27 @@ init_nsf: | |||
4408 | reset_profile_timers(); | 4406 | reset_profile_timers(); |
4409 | 4407 | ||
4410 | while (!endofstream) { | 4408 | while (!endofstream) { |
4409 | intptr_t param; | ||
4410 | enum codec_command_action action = ci->get_command(¶m); | ||
4411 | 4411 | ||
4412 | ci->yield(); | 4412 | if (action == CODEC_ACTION_HALT) |
4413 | if (ci->stop_codec || ci->new_track) { | ||
4414 | break; | 4413 | break; |
4415 | } | ||
4416 | 4414 | ||
4417 | if (ci->seek_time >0) { | 4415 | if (action == CODEC_ACTION_SEEK_TIME) { |
4418 | track=ci->seek_time/1000; | 4416 | if (param > 0) { |
4419 | if (usingplaylist) { | 4417 | track=param/1000; |
4420 | if (track>=nPlaylistSize) break; | 4418 | if (usingplaylist) { |
4421 | } else { | 4419 | if (track>=nPlaylistSize) break; |
4422 | if (track>=nTrackCount) break; | 4420 | } else { |
4421 | if (track>=nTrackCount) break; | ||
4422 | } | ||
4423 | dontresettrack=1; | ||
4424 | ci->seek_complete(); | ||
4425 | goto init_nsf; | ||
4426 | } | ||
4427 | else { | ||
4428 | ci->seek_complete(); | ||
4423 | } | 4429 | } |
4424 | ci->seek_complete(); | ||
4425 | dontresettrack=1; | ||
4426 | goto init_nsf; | ||
4427 | } | 4430 | } |
4428 | 4431 | ||
4429 | ENTER_TIMER(total); | 4432 | ENTER_TIMER(total); |
@@ -4449,22 +4452,17 @@ init_nsf: | |||
4449 | 4452 | ||
4450 | print_timers(last_path,track); | 4453 | print_timers(last_path,track); |
4451 | 4454 | ||
4452 | request_next_track: | ||
4453 | if (ci->request_next_track()) { | ||
4454 | if (ci->global_settings->repeat_mode==REPEAT_ONE) { | 4455 | if (ci->global_settings->repeat_mode==REPEAT_ONE) { |
4455 | /* in repeat one mode just advance to the next track */ | 4456 | /* in repeat one mode just advance to the next track */ |
4456 | track++; | 4457 | track++; |
4457 | if (track>=nTrackCount) track=0; | 4458 | if (track>=nTrackCount) track=0; |
4458 | dontresettrack=1; | 4459 | dontresettrack=1; |
4459 | /* at this point we can't tell if another file has been selected */ | 4460 | /* at this point we can't tell if another file has been selected */ |
4460 | goto next_track; | ||
4461 | } else { | 4461 | } else { |
4462 | /* otherwise do a proper load of the next file */ | 4462 | /* otherwise do a proper load of the next file */ |
4463 | dontresettrack=0; | 4463 | dontresettrack=0; |
4464 | last_path[0]='\0'; | 4464 | last_path[0]='\0'; |
4465 | } | 4465 | } |
4466 | goto next_track; /* when we fall through here we'll reload the file */ | ||
4467 | } | ||
4468 | 4466 | ||
4469 | return CODEC_OK; | 4467 | return CODEC_OK; |
4470 | } | 4468 | } |
diff --git a/apps/codecs/raac.c b/apps/codecs/raac.c index b322ae7df3..4b73f41462 100644 --- a/apps/codecs/raac.c +++ b/apps/codecs/raac.c | |||
@@ -35,8 +35,21 @@ static void init_rm(RMContext *rmctx) | |||
35 | 35 | ||
36 | static RMContext rmctx; | 36 | static RMContext rmctx; |
37 | static RMPacket pkt; | 37 | static RMPacket pkt; |
38 | |||
38 | /* this is the codec entry point */ | 39 | /* this is the codec entry point */ |
39 | enum codec_status codec_main(void) | 40 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
41 | { | ||
42 | if (reason == CODEC_LOAD) { | ||
43 | /* Generic codec initialisation */ | ||
44 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
45 | ci->configure(DSP_SET_SAMPLE_DEPTH, 29); | ||
46 | } | ||
47 | |||
48 | return CODEC_OK; | ||
49 | } | ||
50 | |||
51 | /* this is called for each file to process */ | ||
52 | enum codec_status codec_run(void) | ||
40 | { | 53 | { |
41 | static NeAACDecFrameInfo frame_info; | 54 | static NeAACDecFrameInfo frame_info; |
42 | NeAACDecHandle decoder; | 55 | NeAACDecHandle decoder; |
@@ -49,26 +62,21 @@ enum codec_status codec_main(void) | |||
49 | unsigned char c = 0; /* channels */ | 62 | unsigned char c = 0; /* channels */ |
50 | int playback_on = -1; | 63 | int playback_on = -1; |
51 | size_t resume_offset; | 64 | size_t resume_offset; |
52 | 65 | intptr_t param; | |
53 | /* Generic codec initialisation */ | 66 | enum codec_command_action action = CODEC_ACTION_NULL; |
54 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
55 | ci->configure(DSP_SET_SAMPLE_DEPTH, 29); | ||
56 | |||
57 | next_track: | ||
58 | err = CODEC_OK; | ||
59 | 67 | ||
60 | if (codec_init()) { | 68 | if (codec_init()) { |
61 | DEBUGF("FAAD: Codec init error\n"); | 69 | DEBUGF("FAAD: Codec init error\n"); |
62 | return CODEC_ERROR; | 70 | return CODEC_ERROR; |
63 | } | 71 | } |
64 | 72 | ||
65 | if (codec_wait_taginfo() != 0) | ||
66 | goto done; | ||
67 | |||
68 | resume_offset = ci->id3->offset; | 73 | resume_offset = ci->id3->offset; |
69 | 74 | ||
70 | ci->memset(&rmctx,0,sizeof(RMContext)); | 75 | ci->memset(&rmctx,0,sizeof(RMContext)); |
71 | ci->memset(&pkt,0,sizeof(RMPacket)); | 76 | ci->memset(&pkt,0,sizeof(RMPacket)); |
77 | |||
78 | ci->seek_buffer(0); | ||
79 | |||
72 | init_rm(&rmctx); | 80 | init_rm(&rmctx); |
73 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 81 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
74 | codec_set_replaygain(ci->id3); | 82 | codec_set_replaygain(ci->id3); |
@@ -78,9 +86,9 @@ next_track: | |||
78 | 86 | ||
79 | if (!decoder) { | 87 | if (!decoder) { |
80 | DEBUGF("FAAD: Decode open error\n"); | 88 | DEBUGF("FAAD: Decode open error\n"); |
81 | err = CODEC_ERROR; | 89 | return CODEC_ERROR; |
82 | goto done; | 90 | } |
83 | } | 91 | |
84 | NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); | 92 | NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(decoder); |
85 | conf->outputFormat = FAAD_FMT_16BIT; /* irrelevant, we don't convert */ | 93 | conf->outputFormat = FAAD_FMT_16BIT; /* irrelevant, we don't convert */ |
86 | NeAACDecSetConfiguration(decoder, conf); | 94 | NeAACDecSetConfiguration(decoder, conf); |
@@ -91,8 +99,7 @@ next_track: | |||
91 | 99 | ||
92 | if (err) { | 100 | if (err) { |
93 | DEBUGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type); | 101 | DEBUGF("FAAD: DecInit: %d, %d\n", err, decoder->object_type); |
94 | err = CODEC_ERROR; | 102 | return CODEC_ERROR; |
95 | goto done; | ||
96 | } | 103 | } |
97 | 104 | ||
98 | /* check for a mid-track resume and force a seek time accordingly */ | 105 | /* check for a mid-track resume and force a seek time accordingly */ |
@@ -100,36 +107,38 @@ next_track: | |||
100 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; | 107 | resume_offset -= rmctx.data_offset + DATA_HEADER_SIZE; |
101 | /* put number of subpackets to skip in resume_offset */ | 108 | /* put number of subpackets to skip in resume_offset */ |
102 | resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); | 109 | resume_offset /= (rmctx.block_align + PACKET_HEADER_SIZE); |
103 | ci->seek_time = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); | 110 | param = (int)resume_offset * ((rmctx.block_align * 8 * 1000)/rmctx.bit_rate); |
111 | action = CODEC_ACTION_SEEK_TIME; | ||
104 | } | 112 | } |
105 | 113 | ||
106 | ci->id3->frequency = s; | 114 | ci->id3->frequency = s; /* FIXME: Won't get it to the UI */ |
107 | ci->set_elapsed(0); | 115 | ci->set_elapsed(0); |
108 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | 116 | ci->advance_buffer(rmctx.data_offset + DATA_HEADER_SIZE); |
109 | 117 | ||
110 | /* The main decoding loop */ | 118 | /* The main decoding loop */ |
111 | seek_start: | ||
112 | while (1) { | 119 | while (1) { |
113 | ci->yield(); | 120 | if (action == CODEC_ACTION_NULL) |
114 | if (ci->stop_codec || ci->new_track) { | 121 | action = ci->get_command(¶m); |
122 | |||
123 | if (action == CODEC_ACTION_HALT) | ||
115 | break; | 124 | break; |
116 | } | ||
117 | 125 | ||
118 | if (ci->seek_time) { | 126 | if (action == CODEC_ACTION_SEEK_TIME) { |
119 | |||
120 | /* Do not allow seeking beyond the file's length */ | 127 | /* Do not allow seeking beyond the file's length */ |
121 | if ((unsigned) ci->seek_time > ci->id3->length) { | 128 | if ((unsigned) param > ci->id3->length) { |
129 | ci->set_elapsed(ci->id3->length); | ||
122 | ci->seek_complete(); | 130 | ci->seek_complete(); |
123 | goto done; | 131 | break; |
124 | } | 132 | } |
125 | 133 | ||
126 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); | 134 | ci->seek_buffer(rmctx.data_offset + DATA_HEADER_SIZE); |
127 | 135 | ||
128 | /* Seek to the start of the track */ | 136 | /* Seek to the start of the track */ |
129 | if (ci->seek_time == 1) { | 137 | if (param == 0) { |
130 | ci->set_elapsed(0); | 138 | ci->set_elapsed(0); |
131 | ci->seek_complete(); | 139 | ci->seek_complete(); |
132 | goto seek_start; | 140 | action = CODEC_ACTION_NULL; |
141 | continue; | ||
133 | } | 142 | } |
134 | 143 | ||
135 | skipped = 0; | 144 | skipped = 0; |
@@ -141,21 +150,30 @@ seek_start: | |||
141 | if(playback_on == -1) { | 150 | if(playback_on == -1) { |
142 | /* Error only if packet-parsing failed and playback hadn't started */ | 151 | /* Error only if packet-parsing failed and playback hadn't started */ |
143 | DEBUGF("rm_get_packet failed\n"); | 152 | DEBUGF("rm_get_packet failed\n"); |
153 | ci->seek_complete(); | ||
144 | return CODEC_ERROR; | 154 | return CODEC_ERROR; |
145 | } | 155 | } |
146 | else | 156 | else { |
147 | goto done; | 157 | ci->seek_complete(); |
158 | return CODEC_OK; | ||
159 | } | ||
148 | } | 160 | } |
149 | skipped += pkt.length; | 161 | skipped += pkt.length; |
150 | if(pkt.timestamp > (unsigned)ci->seek_time) break; | 162 | |
163 | if(pkt.timestamp > (unsigned)param) | ||
164 | break; | ||
165 | |||
151 | ci->advance_buffer(pkt.length); | 166 | ci->advance_buffer(pkt.length); |
152 | } | 167 | } |
153 | ci->seek_buffer(pkt_offset + rmctx.data_offset + DATA_HEADER_SIZE); | 168 | ci->seek_buffer(pkt_offset + rmctx.data_offset + DATA_HEADER_SIZE); |
154 | buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000); | 169 | buffer = ci->request_buffer(&n,rmctx.audio_framesize + 1000); |
155 | NeAACDecPostSeekReset(decoder, decoder->frame); | 170 | NeAACDecPostSeekReset(decoder, decoder->frame); |
171 | ci->set_elapsed(pkt.timestamp); | ||
156 | ci->seek_complete(); | 172 | ci->seek_complete(); |
157 | } | 173 | } |
158 | 174 | ||
175 | action = CODEC_ACTION_NULL; | ||
176 | |||
159 | /* Request the required number of bytes from the input buffer */ | 177 | /* Request the required number of bytes from the input buffer */ |
160 | buffer=ci->request_buffer(&n,rmctx.audio_framesize + 1000); | 178 | buffer=ci->request_buffer(&n,rmctx.audio_framesize + 1000); |
161 | consumed = rm_get_packet(&buffer, &rmctx, &pkt); | 179 | consumed = rm_get_packet(&buffer, &rmctx, &pkt); |
@@ -167,20 +185,20 @@ seek_start: | |||
167 | return CODEC_ERROR; | 185 | return CODEC_ERROR; |
168 | } | 186 | } |
169 | else | 187 | else |
170 | goto done; | 188 | break; |
171 | } | 189 | } |
172 | 190 | ||
173 | playback_on = 1; | 191 | playback_on = 1; |
174 | if (pkt.timestamp >= ci->id3->length) | 192 | if (pkt.timestamp >= ci->id3->length) |
175 | goto done; | 193 | break; |
194 | |||
176 | /* Decode one block - returned samples will be host-endian */ | 195 | /* Decode one block - returned samples will be host-endian */ |
177 | for(i = 0; i < rmctx.sub_packet_cnt; i++) { | 196 | for(i = 0; i < rmctx.sub_packet_cnt; i++) { |
178 | ret = NeAACDecDecode(decoder, &frame_info, buffer, rmctx.sub_packet_lengths[i]); | 197 | ret = NeAACDecDecode(decoder, &frame_info, buffer, rmctx.sub_packet_lengths[i]); |
179 | buffer += rmctx.sub_packet_lengths[i]; | 198 | buffer += rmctx.sub_packet_lengths[i]; |
180 | if (frame_info.error > 0) { | 199 | if (frame_info.error > 0) { |
181 | DEBUGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error)); | 200 | DEBUGF("FAAD: decode error '%s'\n", NeAACDecGetErrorMessage(frame_info.error)); |
182 | err = CODEC_ERROR; | 201 | return CODEC_ERROR; |
183 | goto exit; | ||
184 | } | 202 | } |
185 | ci->pcmbuf_insert(decoder->time_out[0], | 203 | ci->pcmbuf_insert(decoder->time_out[0], |
186 | decoder->time_out[1], | 204 | decoder->time_out[1], |
@@ -191,11 +209,5 @@ seek_start: | |||
191 | ci->advance_buffer(pkt.length); | 209 | ci->advance_buffer(pkt.length); |
192 | } | 210 | } |
193 | 211 | ||
194 | done: | 212 | return CODEC_OK; |
195 | if (ci->request_next_track()) | ||
196 | goto next_track; | ||
197 | |||
198 | exit: | ||
199 | return err; | ||
200 | } | 213 | } |
201 | |||
diff --git a/apps/codecs/shorten.c b/apps/codecs/shorten.c index 83a9c34da8..db66991679 100644 --- a/apps/codecs/shorten.c +++ b/apps/codecs/shorten.c | |||
@@ -37,7 +37,19 @@ static int32_t offset1[MAX_OFFSET_SIZE] IBSS_ATTR; | |||
37 | static int8_t ibuf[MAX_BUFFER_SIZE] IBSS_ATTR; | 37 | static int8_t ibuf[MAX_BUFFER_SIZE] IBSS_ATTR; |
38 | 38 | ||
39 | /* this is the codec entry point */ | 39 | /* this is the codec entry point */ |
40 | enum codec_status codec_main(void) | 40 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
41 | { | ||
42 | if (reason == CODEC_LOAD) { | ||
43 | /* Generic codec initialisation */ | ||
44 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
45 | ci->configure(DSP_SET_SAMPLE_DEPTH, SHN_OUTPUT_DEPTH-1); | ||
46 | } | ||
47 | |||
48 | return CODEC_OK; | ||
49 | } | ||
50 | |||
51 | /* this is called for each file to process */ | ||
52 | enum codec_status codec_run(void) | ||
41 | { | 53 | { |
42 | ShortenContext sc; | 54 | ShortenContext sc; |
43 | uint32_t samplesdone; | 55 | uint32_t samplesdone; |
@@ -45,21 +57,14 @@ enum codec_status codec_main(void) | |||
45 | int8_t *buf; | 57 | int8_t *buf; |
46 | int consumed, res, nsamples; | 58 | int consumed, res, nsamples; |
47 | size_t bytesleft; | 59 | size_t bytesleft; |
60 | intptr_t param; | ||
48 | 61 | ||
49 | /* Generic codec initialisation */ | ||
50 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | ||
51 | ci->configure(DSP_SET_SAMPLE_DEPTH, SHN_OUTPUT_DEPTH-1); | ||
52 | |||
53 | next_track: | ||
54 | /* Codec initialization */ | 62 | /* Codec initialization */ |
55 | if (codec_init()) { | 63 | if (codec_init()) { |
56 | LOGF("Shorten: codec_init error\n"); | 64 | LOGF("Shorten: codec_init error\n"); |
57 | return CODEC_ERROR; | 65 | return CODEC_ERROR; |
58 | } | 66 | } |
59 | 67 | ||
60 | if (codec_wait_taginfo() != 0) | ||
61 | goto request_next_track; | ||
62 | |||
63 | codec_set_replaygain(ci->id3); | 68 | codec_set_replaygain(ci->id3); |
64 | 69 | ||
65 | /* Shorten decoder initialization */ | 70 | /* Shorten decoder initialization */ |
@@ -103,14 +108,15 @@ seek_start: | |||
103 | samplesdone = 0; | 108 | samplesdone = 0; |
104 | buf = ci->request_buffer(&bytesleft, MAX_BUFFER_SIZE); | 109 | buf = ci->request_buffer(&bytesleft, MAX_BUFFER_SIZE); |
105 | while (bytesleft) { | 110 | while (bytesleft) { |
106 | ci->yield(); | 111 | enum codec_command_action action = ci->get_command(¶m); |
107 | if (ci->stop_codec || ci->new_track) { | 112 | |
113 | if (action == CODEC_ACTION_HALT) | ||
108 | break; | 114 | break; |
109 | } | ||
110 | 115 | ||
111 | /* Seek to start of track */ | 116 | /* Seek to start of track */ |
112 | if (ci->seek_time == 1) { | 117 | if (action == CODEC_ACTION_SEEK_TIME) { |
113 | if (ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) { | 118 | if (param == 0 && |
119 | ci->seek_buffer(sc.header_bits/8 + ci->id3->first_frame_offset)) { | ||
114 | sc.bitindex = sc.header_bits - 8*(sc.header_bits/8); | 120 | sc.bitindex = sc.header_bits - 8*(sc.header_bits/8); |
115 | ci->set_elapsed(0); | 121 | ci->set_elapsed(0); |
116 | ci->seek_complete(); | 122 | ci->seek_complete(); |
@@ -128,7 +134,7 @@ seek_start: | |||
128 | if (res == FN_ERROR) { | 134 | if (res == FN_ERROR) { |
129 | LOGF("Shorten: shorten_decode_frames error (%lu)\n", | 135 | LOGF("Shorten: shorten_decode_frames error (%lu)\n", |
130 | (unsigned long)samplesdone); | 136 | (unsigned long)samplesdone); |
131 | break; | 137 | return CODEC_ERROR; |
132 | } else { | 138 | } else { |
133 | /* Insert decoded samples in pcmbuf */ | 139 | /* Insert decoded samples in pcmbuf */ |
134 | if (nsamples) { | 140 | if (nsamples) { |
@@ -153,9 +159,5 @@ seek_start: | |||
153 | sc.bitindex = sc.gb.index - 8*consumed; | 159 | sc.bitindex = sc.gb.index - 8*consumed; |
154 | } | 160 | } |
155 | 161 | ||
156 | request_next_track: | ||
157 | if (ci->request_next_track()) | ||
158 | goto next_track; | ||
159 | |||
160 | return CODEC_OK; | 162 | return CODEC_OK; |
161 | } | 163 | } |
diff --git a/apps/codecs/sid.c b/apps/codecs/sid.c index 52c1289fff..0edbabe0b6 100644 --- a/apps/codecs/sid.c +++ b/apps/codecs/sid.c | |||
@@ -1203,34 +1203,47 @@ unsigned short LoadSIDFromMemory(void *pSidData, unsigned short *load_addr, | |||
1203 | return *load_addr; | 1203 | return *load_addr; |
1204 | } | 1204 | } |
1205 | 1205 | ||
1206 | static int nSamplesRendered = 0; | ||
1207 | static int nSamplesPerCall = 882; /* This is PAL SID single speed (44100/50Hz) */ | ||
1208 | static int nSamplesToRender = 0; | ||
1206 | 1209 | ||
1207 | enum codec_status codec_main(void) | 1210 | /* this is the codec entry point */ |
1211 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
1208 | { | 1212 | { |
1209 | unsigned int filesize; | 1213 | if (reason == CODEC_LOAD) { |
1214 | /* Make use of 44.1khz */ | ||
1215 | ci->configure(DSP_SWITCH_FREQUENCY, 44100); | ||
1216 | /* Sample depth is 28 bit host endian */ | ||
1217 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
1218 | /* Mono output */ | ||
1219 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | ||
1220 | } | ||
1221 | |||
1222 | return CODEC_OK; | ||
1223 | } | ||
1210 | 1224 | ||
1225 | /* this is called for each file to process */ | ||
1226 | enum codec_status codec_run(void) | ||
1227 | { | ||
1228 | unsigned int filesize; | ||
1211 | unsigned short load_addr, init_addr, play_addr; | 1229 | unsigned short load_addr, init_addr, play_addr; |
1212 | unsigned char subSongsMax, subSong, song_speed; | 1230 | unsigned char subSongsMax, subSong, song_speed; |
1231 | intptr_t param; | ||
1213 | 1232 | ||
1214 | int nSamplesRendered = 0; | ||
1215 | int nSamplesPerCall = 882; /* This is PAL SID single speed (44100/50Hz) */ | ||
1216 | int nSamplesToRender = 0; | ||
1217 | |||
1218 | next_track: | ||
1219 | if (codec_init()) { | 1233 | if (codec_init()) { |
1220 | return CODEC_ERROR; | 1234 | return CODEC_ERROR; |
1221 | } | 1235 | } |
1222 | 1236 | ||
1223 | if (codec_wait_taginfo() != 0) | ||
1224 | goto request_next_track; | ||
1225 | |||
1226 | codec_set_replaygain(ci->id3); | 1237 | codec_set_replaygain(ci->id3); |
1227 | 1238 | ||
1228 | /* Load SID file the read_filebuf callback will return the full requested | 1239 | /* Load SID file the read_filebuf callback will return the full requested |
1229 | * size if at all possible, so there is no need to loop */ | 1240 | * size if at all possible, so there is no need to loop */ |
1241 | ci->seek_buffer(0); | ||
1230 | filesize = ci->read_filebuf(sidfile, sizeof(sidfile)); | 1242 | filesize = ci->read_filebuf(sidfile, sizeof(sidfile)); |
1231 | 1243 | ||
1232 | if (filesize == 0) | 1244 | if (filesize == 0) { |
1233 | return CODEC_ERROR; | 1245 | return CODEC_ERROR; |
1246 | } | ||
1234 | 1247 | ||
1235 | c64Init(44100); | 1248 | c64Init(44100); |
1236 | LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, | 1249 | LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, |
@@ -1239,37 +1252,30 @@ next_track: | |||
1239 | cpuJSR(init_addr, subSong); /* Start the song initialize */ | 1252 | cpuJSR(init_addr, subSong); /* Start the song initialize */ |
1240 | 1253 | ||
1241 | 1254 | ||
1242 | /* Make use of 44.1khz */ | ||
1243 | ci->configure(DSP_SWITCH_FREQUENCY, 44100); | ||
1244 | /* Sample depth is 28 bit host endian */ | ||
1245 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
1246 | /* Mono output */ | ||
1247 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | ||
1248 | |||
1249 | |||
1250 | /* Set the elapsed time to the current subsong (in seconds) */ | 1255 | /* Set the elapsed time to the current subsong (in seconds) */ |
1251 | ci->set_elapsed(subSong*1000); | 1256 | ci->set_elapsed(subSong*1000); |
1252 | 1257 | ||
1253 | /* The main decoder loop */ | 1258 | /* The main decoder loop */ |
1254 | while (1) { | 1259 | while (1) { |
1255 | ci->yield(); | 1260 | enum codec_command_action action = ci->get_command(¶m); |
1256 | if (ci->stop_codec || ci->new_track) | 1261 | |
1262 | if (action == CODEC_ACTION_HALT) | ||
1257 | break; | 1263 | break; |
1258 | 1264 | ||
1259 | if (ci->seek_time) { | 1265 | if (action == CODEC_ACTION_SEEK_TIME) { |
1260 | /* New time is ready in ci->seek_time */ | 1266 | /* New time is ready in param */ |
1261 | 1267 | ||
1262 | /* Start playing from scratch */ | 1268 | /* Start playing from scratch */ |
1263 | c64Init(44100); | 1269 | c64Init(44100); |
1264 | LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, &subSongsMax, &subSong, &song_speed, filesize); | 1270 | LoadSIDFromMemory(sidfile, &load_addr, &init_addr, &play_addr, |
1265 | sidPoke(24, 15); /* Turn on full volume */ | 1271 | &subSongsMax, &subSong, &song_speed, filesize); |
1266 | subSong = ci->seek_time / 1000; /* Now use the current seek time in seconds as subsong */ | 1272 | sidPoke(24, 15); /* Turn on full volume */ |
1267 | cpuJSR(init_addr, subSong); /* Start the song initialize */ | 1273 | subSong = param / 1000; /* Now use the current seek time in seconds as subsong */ |
1268 | nSamplesToRender = 0; /* Start the rendering from scratch */ | 1274 | cpuJSR(init_addr, subSong); /* Start the song initialize */ |
1269 | 1275 | nSamplesToRender = 0; /* Start the rendering from scratch */ | |
1270 | ci->seek_complete(); | ||
1271 | 1276 | ||
1272 | /* Set the elapsed time to the current subsong (in seconds) */ | 1277 | /* Set the elapsed time to the current subsong (in seconds) */ |
1278 | ci->seek_complete(); | ||
1273 | ci->set_elapsed(subSong*1000); | 1279 | ci->set_elapsed(subSong*1000); |
1274 | } | 1280 | } |
1275 | 1281 | ||
@@ -1306,9 +1312,5 @@ next_track: | |||
1306 | ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE); | 1312 | ci->pcmbuf_insert(samples, NULL, CHUNK_SIZE); |
1307 | } | 1313 | } |
1308 | 1314 | ||
1309 | request_next_track: | ||
1310 | if (ci->request_next_track()) | ||
1311 | goto next_track; | ||
1312 | |||
1313 | return CODEC_OK; | 1315 | return CODEC_OK; |
1314 | } | 1316 | } |
diff --git a/apps/codecs/smaf.c b/apps/codecs/smaf.c index 3e8a41387d..9211daa9aa 100644 --- a/apps/codecs/smaf.c +++ b/apps/codecs/smaf.c | |||
@@ -332,9 +332,20 @@ static uint8_t *read_buffer(size_t *realsize) | |||
332 | return buffer; | 332 | return buffer; |
333 | } | 333 | } |
334 | 334 | ||
335 | enum codec_status codec_main(void) | 335 | /* this is the codec entry point */ |
336 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
337 | { | ||
338 | if (reason == CODEC_LOAD) { | ||
339 | /* Generic codec initialisation */ | ||
340 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
341 | } | ||
342 | |||
343 | return CODEC_OK; | ||
344 | } | ||
345 | |||
346 | /* this is called for each file to process */ | ||
347 | enum codec_status codec_run(void) | ||
336 | { | 348 | { |
337 | int status; | ||
338 | uint32_t decodedsamples; | 349 | uint32_t decodedsamples; |
339 | size_t n; | 350 | size_t n; |
340 | int bufcount; | 351 | int bufcount; |
@@ -342,20 +353,10 @@ enum codec_status codec_main(void) | |||
342 | uint8_t *smafbuf; | 353 | uint8_t *smafbuf; |
343 | off_t firstblockposn; /* position of the first block in file */ | 354 | off_t firstblockposn; /* position of the first block in file */ |
344 | const struct pcm_codec *codec; | 355 | const struct pcm_codec *codec; |
345 | 356 | intptr_t param; | |
346 | /* Generic codec initialisation */ | ||
347 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
348 | 357 | ||
349 | next_track: | 358 | if (codec_init()) |
350 | status = CODEC_OK; | 359 | return CODEC_ERROR; |
351 | |||
352 | if (codec_init()) { | ||
353 | status = CODEC_ERROR; | ||
354 | goto exit; | ||
355 | } | ||
356 | |||
357 | if (codec_wait_taginfo() != 0) | ||
358 | goto done; | ||
359 | 360 | ||
360 | codec_set_replaygain(ci->id3); | 361 | codec_set_replaygain(ci->id3); |
361 | 362 | ||
@@ -365,24 +366,22 @@ next_track: | |||
365 | decodedsamples = 0; | 366 | decodedsamples = 0; |
366 | codec = 0; | 367 | codec = 0; |
367 | 368 | ||
369 | ci->seek_buffer(0); | ||
368 | if (!parse_header(&format, &firstblockposn)) | 370 | if (!parse_header(&format, &firstblockposn)) |
369 | { | 371 | { |
370 | status = CODEC_ERROR; | 372 | return CODEC_ERROR; |
371 | goto done; | ||
372 | } | 373 | } |
373 | 374 | ||
374 | codec = get_codec(format.formattag); | 375 | codec = get_codec(format.formattag); |
375 | if (codec == 0) | 376 | if (codec == 0) |
376 | { | 377 | { |
377 | DEBUGF("CODEC_ERROR: unsupport audio format: 0x%x\n", (int)format.formattag); | 378 | DEBUGF("CODEC_ERROR: unsupport audio format: 0x%x\n", (int)format.formattag); |
378 | status = CODEC_ERROR; | 379 | return CODEC_ERROR; |
379 | goto done; | ||
380 | } | 380 | } |
381 | 381 | ||
382 | if (!codec->set_format(&format)) | 382 | if (!codec->set_format(&format)) |
383 | { | 383 | { |
384 | status = CODEC_ERROR; | 384 | return CODEC_ERROR; |
385 | goto done; | ||
386 | } | 385 | } |
387 | 386 | ||
388 | /* check chunksize */ | 387 | /* check chunksize */ |
@@ -392,8 +391,7 @@ next_track: | |||
392 | if (format.chunksize == 0) | 391 | if (format.chunksize == 0) |
393 | { | 392 | { |
394 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); | 393 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); |
395 | status = CODEC_ERROR; | 394 | return CODEC_ERROR; |
396 | goto done; | ||
397 | } | 395 | } |
398 | 396 | ||
399 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 397 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
@@ -404,12 +402,10 @@ next_track: | |||
404 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | 402 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); |
405 | } else { | 403 | } else { |
406 | DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n"); | 404 | DEBUGF("CODEC_ERROR: more than 2 channels unsupported\n"); |
407 | status = CODEC_ERROR; | 405 | return CODEC_ERROR; |
408 | goto done; | ||
409 | } | 406 | } |
410 | 407 | ||
411 | ci->seek_buffer(firstblockposn); | 408 | ci->seek_buffer(firstblockposn); |
412 | ci->seek_complete(); | ||
413 | 409 | ||
414 | /* make sure we're at the correct offset */ | 410 | /* make sure we're at the correct offset */ |
415 | if (bytesdone > (uint32_t) firstblockposn) | 411 | if (bytesdone > (uint32_t) firstblockposn) |
@@ -419,13 +415,13 @@ next_track: | |||
419 | PCM_SEEK_POS, &read_buffer); | 415 | PCM_SEEK_POS, &read_buffer); |
420 | 416 | ||
421 | if (newpos->pos > format.numbytes) | 417 | if (newpos->pos > format.numbytes) |
422 | goto done; | 418 | return CODEC_OK; |
419 | |||
423 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 420 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
424 | { | 421 | { |
425 | bytesdone = newpos->pos; | 422 | bytesdone = newpos->pos; |
426 | decodedsamples = newpos->samples; | 423 | decodedsamples = newpos->samples; |
427 | } | 424 | } |
428 | ci->seek_complete(); | ||
429 | } | 425 | } |
430 | else | 426 | else |
431 | { | 427 | { |
@@ -437,23 +433,32 @@ next_track: | |||
437 | endofstream = 0; | 433 | endofstream = 0; |
438 | 434 | ||
439 | while (!endofstream) { | 435 | while (!endofstream) { |
440 | ci->yield(); | 436 | enum codec_command_action action = ci->get_command(¶m); |
441 | if (ci->stop_codec || ci->new_track) | 437 | |
438 | if (action == CODEC_ACTION_HALT) | ||
442 | break; | 439 | break; |
443 | 440 | ||
444 | if (ci->seek_time) { | 441 | if (action == CODEC_ACTION_SEEK_TIME) { |
445 | struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME, | 442 | struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, |
446 | &read_buffer); | 443 | &read_buffer); |
447 | 444 | ||
448 | if (newpos->pos > format.numbytes) | 445 | if (newpos->pos > format.numbytes) |
446 | { | ||
447 | ci->set_elapsed(ci->id3->length); | ||
448 | ci->seek_complete(); | ||
449 | break; | 449 | break; |
450 | } | ||
451 | |||
450 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 452 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
451 | { | 453 | { |
452 | bytesdone = newpos->pos; | 454 | bytesdone = newpos->pos; |
453 | decodedsamples = newpos->samples; | 455 | decodedsamples = newpos->samples; |
454 | } | 456 | } |
457 | |||
458 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
455 | ci->seek_complete(); | 459 | ci->seek_complete(); |
456 | } | 460 | } |
461 | |||
457 | smafbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); | 462 | smafbuf = (uint8_t *)ci->request_buffer(&n, format.chunksize); |
458 | 463 | ||
459 | if (n == 0) | 464 | if (n == 0) |
@@ -464,11 +469,10 @@ next_track: | |||
464 | endofstream = 1; | 469 | endofstream = 1; |
465 | } | 470 | } |
466 | 471 | ||
467 | status = codec->decode(smafbuf, n, samples, &bufcount); | 472 | if (codec->decode(smafbuf, n, samples, &bufcount) == CODEC_ERROR) |
468 | if (status == CODEC_ERROR) | ||
469 | { | 473 | { |
470 | DEBUGF("codec error\n"); | 474 | DEBUGF("codec error\n"); |
471 | goto done; | 475 | return CODEC_ERROR; |
472 | } | 476 | } |
473 | 477 | ||
474 | ci->pcmbuf_insert(samples, NULL, bufcount); | 478 | ci->pcmbuf_insert(samples, NULL, bufcount); |
@@ -482,11 +486,5 @@ next_track: | |||
482 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | 486 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); |
483 | } | 487 | } |
484 | 488 | ||
485 | done: | 489 | return CODEC_OK; |
486 | if (ci->request_next_track()) | ||
487 | goto next_track; | ||
488 | |||
489 | exit: | ||
490 | return status; | ||
491 | } | 490 | } |
492 | |||
diff --git a/apps/codecs/spc.c b/apps/codecs/spc.c index 4db2878964..1b49761810 100644 --- a/apps/codecs/spc.c +++ b/apps/codecs/spc.c | |||
@@ -260,14 +260,6 @@ static inline void samples_release_rdbuf(void) | |||
260 | static inline int32_t * samples_get_rdbuf(void) | 260 | static inline int32_t * samples_get_rdbuf(void) |
261 | { | 261 | { |
262 | ci->semaphore_wait(&sample_queue.emu_sem_head, TIMEOUT_BLOCK); | 262 | ci->semaphore_wait(&sample_queue.emu_sem_head, TIMEOUT_BLOCK); |
263 | |||
264 | if (ci->stop_codec || ci->new_track) | ||
265 | { | ||
266 | /* Told to stop. Buffer must be released. */ | ||
267 | samples_release_rdbuf(); | ||
268 | return NULL; | ||
269 | } | ||
270 | |||
271 | return sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK].audio; | 263 | return sample_queue.wav_chunk[sample_queue.head & WAV_CHUNK_MASK].audio; |
272 | } | 264 | } |
273 | 265 | ||
@@ -390,11 +382,10 @@ static inline void spc_emu_quit(void) | |||
390 | } | 382 | } |
391 | } | 383 | } |
392 | 384 | ||
393 | static inline bool spc_play_get_samples(int32_t **samples) | 385 | static inline int32_t * spc_play_get_samples(void) |
394 | { | 386 | { |
395 | /* obtain filled samples buffer */ | 387 | /* obtain filled samples buffer */ |
396 | *samples = samples_get_rdbuf(); | 388 | return samples_get_rdbuf(); |
397 | return *samples != NULL; | ||
398 | } | 389 | } |
399 | 390 | ||
400 | static inline void spc_play_send_samples(int32_t *samples) | 391 | static inline void spc_play_send_samples(int32_t *samples) |
@@ -433,15 +424,14 @@ static inline void spc_play_send_samples(int32_t *samples) | |||
433 | #define spc_emu_quit() | 424 | #define spc_emu_quit() |
434 | #define samples_release_rdbuf() | 425 | #define samples_release_rdbuf() |
435 | 426 | ||
436 | static inline bool spc_play_get_samples(int32_t **samples) | 427 | static inline int32_t * spc_play_get_samples(void) |
437 | { | 428 | { |
438 | ENTER_TIMER(render); | 429 | ENTER_TIMER(render); |
439 | /* fill samples buffer */ | 430 | /* fill samples buffer */ |
440 | if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,wav_chunk) ) | 431 | if ( SPC_play(&spc_emu,WAV_CHUNK_SIZE*2,wav_chunk) ) |
441 | assert( false ); | 432 | assert( false ); |
442 | EXIT_TIMER(render); | 433 | EXIT_TIMER(render); |
443 | *samples = wav_chunk; | 434 | return wav_chunk; |
444 | return true; | ||
445 | } | 435 | } |
446 | #endif /* SPC_DUAL_CORE */ | 436 | #endif /* SPC_DUAL_CORE */ |
447 | 437 | ||
@@ -454,6 +444,7 @@ static int play_track( void ) | |||
454 | unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000; | 444 | unsigned long fadeendsample = (ID666.length+ID666.fade)*(long long) SAMPLE_RATE/1000; |
455 | int fadedec = 0; | 445 | int fadedec = 0; |
456 | int fadevol = 0x7fffffffl; | 446 | int fadevol = 0x7fffffffl; |
447 | intptr_t param; | ||
457 | 448 | ||
458 | if (fadeendsample>fadestartsample) | 449 | if (fadeendsample>fadestartsample) |
459 | fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1; | 450 | fadedec=0x7fffffffl/(fadeendsample-fadestartsample)+1; |
@@ -462,25 +453,26 @@ static int play_track( void ) | |||
462 | 453 | ||
463 | while ( 1 ) | 454 | while ( 1 ) |
464 | { | 455 | { |
465 | ci->yield(); | 456 | enum codec_command_action action = ci->get_command(¶m); |
466 | if (ci->stop_codec || ci->new_track) { | 457 | |
458 | if (action == CODEC_ACTION_HALT) | ||
467 | break; | 459 | break; |
468 | } | ||
469 | 460 | ||
470 | if (ci->seek_time) { | 461 | if (action == CODEC_ACTION_SEEK_TIME) { |
471 | int curtime = sampleswritten*1000LL/SAMPLE_RATE; | 462 | int curtime = sampleswritten*1000LL/SAMPLE_RATE; |
472 | DEBUGF("seek to %ld\ncurrently at %d\n",ci->seek_time,curtime); | 463 | DEBUGF("seek to %ld\ncurrently at %d\n", (long)param, curtime); |
473 | if (ci->seek_time < curtime) { | 464 | if (param < curtime) { |
474 | DEBUGF("seek backwards = reset\n"); | 465 | DEBUGF("seek backwards = reset\n"); |
466 | ci->set_elapsed(0); | ||
475 | ci->seek_complete(); | 467 | ci->seek_complete(); |
476 | return 1; | 468 | return 1; |
477 | } | 469 | } |
470 | |||
471 | ci->set_elapsed(curtime); | ||
478 | ci->seek_complete(); | 472 | ci->seek_complete(); |
479 | } | 473 | } |
480 | 474 | ||
481 | int32_t *samples; | 475 | int32_t *samples = spc_play_get_samples(); |
482 | if (!spc_play_get_samples(&samples)) | ||
483 | break; | ||
484 | 476 | ||
485 | sampleswritten += WAV_CHUNK_SIZE; | 477 | sampleswritten += WAV_CHUNK_SIZE; |
486 | 478 | ||
@@ -532,67 +524,61 @@ static int play_track( void ) | |||
532 | } | 524 | } |
533 | 525 | ||
534 | /* this is the codec entry point */ | 526 | /* this is the codec entry point */ |
535 | enum codec_status codec_main(void) | 527 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
536 | { | 528 | { |
537 | enum codec_status stat = CODEC_ERROR; | 529 | if (reason == CODEC_LOAD) { |
538 | 530 | if (!spc_emu_start()) | |
539 | if (!spc_emu_start()) | 531 | return CODEC_ERROR; |
540 | goto codec_quit; | ||
541 | |||
542 | do | ||
543 | { | ||
544 | DEBUGF("SPC: next_track\n"); | ||
545 | if (codec_init()) { | ||
546 | goto codec_quit; | ||
547 | } | ||
548 | DEBUGF("SPC: after init\n"); | ||
549 | 532 | ||
550 | ci->configure(DSP_SET_SAMPLE_DEPTH, 24); | 533 | ci->configure(DSP_SET_SAMPLE_DEPTH, 24); |
551 | ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE); | 534 | ci->configure(DSP_SET_FREQUENCY, SAMPLE_RATE); |
552 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); | 535 | ci->configure(DSP_SET_STEREO_MODE, STEREO_NONINTERLEAVED); |
536 | } | ||
537 | else if (reason == CODEC_UNLOAD) { | ||
538 | spc_emu_quit(); | ||
539 | } | ||
553 | 540 | ||
554 | /* wait for track info to load */ | 541 | return CODEC_OK; |
555 | if (codec_wait_taginfo() != 0) | 542 | } |
556 | continue; | ||
557 | |||
558 | codec_set_replaygain(ci->id3); | ||
559 | 543 | ||
560 | /* Read the entire file */ | 544 | /* this is called for each file to process */ |
561 | DEBUGF("SPC: request initial buffer\n"); | 545 | enum codec_status codec_run(void) |
562 | ci->seek_buffer(0); | 546 | { |
563 | size_t buffersize; | 547 | DEBUGF("SPC: next_track\n"); |
564 | uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize); | 548 | if (codec_init()) |
565 | if (!buffer) { | 549 | return CODEC_ERROR; |
566 | goto codec_quit; | 550 | DEBUGF("SPC: after init\n"); |
551 | |||
552 | codec_set_replaygain(ci->id3); | ||
553 | |||
554 | /* Read the entire file */ | ||
555 | DEBUGF("SPC: request initial buffer\n"); | ||
556 | ci->seek_buffer(0); | ||
557 | size_t buffersize; | ||
558 | uint8_t* buffer = ci->request_buffer(&buffersize, ci->filesize); | ||
559 | if (!buffer) | ||
560 | return CODEC_ERROR; | ||
561 | |||
562 | DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize); | ||
563 | do | ||
564 | { | ||
565 | if (load_spc_buffer(buffer, buffersize)) { | ||
566 | DEBUGF("SPC load failure\n"); | ||
567 | return CODEC_ERROR; | ||
567 | } | 568 | } |
568 | 569 | ||
569 | DEBUGF("SPC: read size = 0x%lx\n",(unsigned long)buffersize); | 570 | LoadID666(buffer+0x2e); |
570 | do | ||
571 | { | ||
572 | if (load_spc_buffer(buffer, buffersize)) { | ||
573 | DEBUGF("SPC load failure\n"); | ||
574 | goto codec_quit; | ||
575 | } | ||
576 | |||
577 | LoadID666(buffer+0x2e); | ||
578 | 571 | ||
579 | if (ci->global_settings->repeat_mode!=REPEAT_ONE && ID666.length==0) { | 572 | if (ci->global_settings->repeat_mode!=REPEAT_ONE && ID666.length==0) { |
580 | ID666.length=3*60*1000; /* 3 minutes */ | 573 | ID666.length=3*60*1000; /* 3 minutes */ |
581 | ID666.fade=5*1000; /* 5 seconds */ | 574 | ID666.fade=5*1000; /* 5 seconds */ |
582 | } | ||
583 | |||
584 | reset_profile_timers(); | ||
585 | } | 575 | } |
586 | while ( play_track() ); | ||
587 | 576 | ||
588 | print_timers(ci->id3->path); | 577 | reset_profile_timers(); |
589 | } | 578 | } |
590 | while ( ci->request_next_track() ); | 579 | while ( play_track() ); |
591 | 580 | ||
592 | stat = CODEC_OK; | 581 | print_timers(ci->id3->path); |
593 | 582 | ||
594 | codec_quit: | 583 | return CODEC_OK; |
595 | spc_emu_quit(); | ||
596 | |||
597 | return stat; | ||
598 | } | 584 | } |
diff --git a/apps/codecs/speex.c b/apps/codecs/speex.c index 7a1efa9753..e394efc3d5 100644 --- a/apps/codecs/speex.c +++ b/apps/codecs/speex.c | |||
@@ -367,11 +367,12 @@ static void *process_header(spx_ogg_packet *op, | |||
367 | return st; | 367 | return st; |
368 | } | 368 | } |
369 | 369 | ||
370 | /* this is the codec entry point */ | 370 | /* this is called for each file to process */ |
371 | enum codec_status codec_main(void) | 371 | enum codec_status codec_run(void) |
372 | { | 372 | { |
373 | int error = CODEC_ERROR; | ||
374 | |||
373 | SpeexBits bits; | 375 | SpeexBits bits; |
374 | int error; | ||
375 | int eof = 0; | 376 | int eof = 0; |
376 | spx_ogg_sync_state oy; | 377 | spx_ogg_sync_state oy; |
377 | spx_ogg_page og; | 378 | spx_ogg_page og; |
@@ -383,7 +384,7 @@ enum codec_status codec_main(void) | |||
383 | int eos = 0; | 384 | int eos = 0; |
384 | SpeexStereoState *stereo; | 385 | SpeexStereoState *stereo; |
385 | int channels = -1; | 386 | int channels = -1; |
386 | int rate = 0, samplerate = 0; | 387 | int samplerate = 0; |
387 | int extra_headers = 0; | 388 | int extra_headers = 0; |
388 | int stream_init = 0; | 389 | int stream_init = 0; |
389 | int page_nb_packets, frame_size, packet_count = 0; | 390 | int page_nb_packets, frame_size, packet_count = 0; |
@@ -392,26 +393,22 @@ enum codec_status codec_main(void) | |||
392 | unsigned long strtoffset = 0; | 393 | unsigned long strtoffset = 0; |
393 | void *st = NULL; | 394 | void *st = NULL; |
394 | int j = 0; | 395 | int j = 0; |
396 | intptr_t param; | ||
395 | 397 | ||
396 | memset(&bits, 0, sizeof(bits)); | 398 | memset(&bits, 0, sizeof(bits)); |
397 | memset(&oy, 0, sizeof(oy)); | 399 | memset(&oy, 0, sizeof(oy)); |
398 | 400 | ||
399 | /* Ogg handling still uses mallocs, so reset the malloc buffer per track */ | 401 | /* Ogg handling still uses mallocs, so reset the malloc buffer per track */ |
400 | next_track: | ||
401 | error = CODEC_OK; | ||
402 | |||
403 | if (codec_init()) { | 402 | if (codec_init()) { |
404 | error = CODEC_ERROR; | ||
405 | goto exit; | 403 | goto exit; |
406 | } | 404 | } |
407 | 405 | ||
406 | ci->seek_buffer(0); | ||
407 | |||
408 | stereo = speex_stereo_state_init(); | 408 | stereo = speex_stereo_state_init(); |
409 | spx_ogg_sync_init(&oy); | 409 | spx_ogg_sync_init(&oy); |
410 | spx_ogg_alloc_buffer(&oy,2*CHUNKSIZE); | 410 | spx_ogg_alloc_buffer(&oy,2*CHUNKSIZE); |
411 | 411 | ||
412 | if (codec_wait_taginfo() != 0) | ||
413 | goto done; | ||
414 | |||
415 | strtoffset = ci->id3->offset; | 412 | strtoffset = ci->id3->offset; |
416 | 413 | ||
417 | samplerate = ci->id3->frequency; | 414 | samplerate = ci->id3->frequency; |
@@ -419,30 +416,32 @@ next_track: | |||
419 | 416 | ||
420 | eof = 0; | 417 | eof = 0; |
421 | while (!eof) { | 418 | while (!eof) { |
422 | ci->yield(); | 419 | enum codec_command_action action = ci->get_command(¶m); |
423 | if (ci->stop_codec || ci->new_track) | 420 | |
421 | if (action == CODEC_ACTION_HALT) | ||
424 | break; | 422 | break; |
425 | 423 | ||
426 | /*seek (seeks to the page before the position) */ | 424 | /*seek (seeks to the page before the position) */ |
427 | if (ci->seek_time) { | 425 | if (action == CODEC_ACTION_SEEK_TIME) { |
428 | if(samplerate!=0&&packet_count>1){ | 426 | if(samplerate!=0&&packet_count>1){ |
429 | LOGF("Speex seek page:%lld,%lld,%ld,%lld,%d\n", | 427 | LOGF("Speex seek page:%lld,%lld,%ld,%lld,%d\n", |
430 | ((spx_int64_t)ci->seek_time/1000) * | 428 | ((spx_int64_t)param/1000) * |
431 | (spx_int64_t)samplerate, | 429 | (spx_int64_t)samplerate, |
432 | page_granule, ci->seek_time, | 430 | page_granule, param, |
433 | (page_granule/samplerate)*1000, samplerate); | 431 | (page_granule/samplerate)*1000, samplerate); |
434 | 432 | ||
435 | speex_seek_page_granule(((spx_int64_t)ci->seek_time/1000) * | 433 | speex_seek_page_granule(((spx_int64_t)param/1000) * |
436 | (spx_int64_t)samplerate, | 434 | (spx_int64_t)samplerate, |
437 | page_granule, &oy, headerssize); | 435 | page_granule, &oy, headerssize); |
438 | ci->seek_complete(); | ||
439 | } | 436 | } |
437 | |||
438 | ci->set_elapsed(param); | ||
439 | ci->seek_complete(); | ||
440 | } | 440 | } |
441 | 441 | ||
442 | next_page: | 442 | next_page: |
443 | /*Get the ogg buffer for writing*/ | 443 | /*Get the ogg buffer for writing*/ |
444 | if(get_more_data(&oy)<1){/*read error*/ | 444 | if(get_more_data(&oy)<1){/*read error*/ |
445 | error=CODEC_ERROR; | ||
446 | goto done; | 445 | goto done; |
447 | } | 446 | } |
448 | 447 | ||
@@ -477,8 +476,7 @@ next_page: | |||
477 | nframes=1; | 476 | nframes=1; |
478 | 477 | ||
479 | if (!st){ | 478 | if (!st){ |
480 | error=CODEC_ERROR; | 479 | goto done; |
481 | goto exit; | ||
482 | } | 480 | } |
483 | 481 | ||
484 | ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); | 482 | ci->configure(DSP_SET_FREQUENCY, ci->id3->frequency); |
@@ -557,31 +555,18 @@ next_page: | |||
557 | } | 555 | } |
558 | 556 | ||
559 | done: | 557 | done: |
560 | if (ci->request_next_track()) { | 558 | /* Clean things up for the next track */ |
561 | 559 | speex_bits_destroy(&bits); | |
562 | /* Clean things up for the next track */ | ||
563 | 560 | ||
561 | if (st) | ||
564 | if (st) | 562 | if (st) |
565 | speex_decoder_destroy(st); | 563 | speex_decoder_destroy(st); |
566 | 564 | ||
567 | if (stream_init == 1) | ||
568 | spx_ogg_stream_reset(&os); | ||
569 | |||
570 | spx_ogg_sync_reset(&oy); | ||
571 | |||
572 | cur_granule = stream_init = rate = samplerate = headerssize | ||
573 | = packet_count = eos = 0; | ||
574 | |||
575 | goto next_track; | ||
576 | } | ||
577 | |||
578 | exit: | ||
579 | speex_bits_destroy(&bits); | ||
580 | |||
581 | if (stream_init) | 565 | if (stream_init) |
582 | spx_ogg_stream_destroy(&os); | 566 | spx_ogg_stream_destroy(&os); |
583 | 567 | ||
584 | spx_ogg_sync_destroy(&oy); | 568 | spx_ogg_sync_destroy(&oy); |
585 | 569 | ||
570 | exit: | ||
586 | return error; | 571 | return error; |
587 | } | 572 | } |
diff --git a/apps/codecs/tta.c b/apps/codecs/tta.c index 1d0846ea61..c75f2b0a57 100644 --- a/apps/codecs/tta.c +++ b/apps/codecs/tta.c | |||
@@ -34,36 +34,36 @@ CODEC_HEADER | |||
34 | static int32_t samples[PCM_BUFFER_LENGTH * 2] IBSS_ATTR; | 34 | static int32_t samples[PCM_BUFFER_LENGTH * 2] IBSS_ATTR; |
35 | 35 | ||
36 | /* this is the codec entry point */ | 36 | /* this is the codec entry point */ |
37 | enum codec_status codec_main(void) | 37 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
38 | { | ||
39 | if (reason == CODEC_LOAD) { | ||
40 | /* Generic codec initialisation */ | ||
41 | ci->configure(DSP_SET_SAMPLE_DEPTH, TTA_OUTPUT_DEPTH - 1); | ||
42 | } | ||
43 | |||
44 | return CODEC_OK; | ||
45 | } | ||
46 | |||
47 | /* this is called for each file to process */ | ||
48 | enum codec_status codec_run(void) | ||
38 | { | 49 | { |
39 | tta_info info; | 50 | tta_info info; |
40 | int status; | ||
41 | unsigned int decodedsamples; | 51 | unsigned int decodedsamples; |
42 | int endofstream; | 52 | int endofstream; |
43 | int new_pos = 0; | 53 | int new_pos = 0; |
44 | int sample_count; | 54 | int sample_count; |
45 | 55 | intptr_t param; | |
46 | /* Generic codec initialisation */ | ||
47 | ci->configure(DSP_SET_SAMPLE_DEPTH, TTA_OUTPUT_DEPTH - 1); | ||
48 | 56 | ||
49 | next_track: | ||
50 | status = CODEC_OK; | ||
51 | |||
52 | if (codec_init()) | 57 | if (codec_init()) |
53 | { | 58 | { |
54 | DEBUGF("codec_init() error\n"); | 59 | DEBUGF("codec_init() error\n"); |
55 | status = CODEC_ERROR; | 60 | return CODEC_ERROR; |
56 | goto exit; | ||
57 | } | 61 | } |
58 | 62 | ||
59 | if (codec_wait_taginfo() != 0) | 63 | ci->seek_buffer(0); |
60 | goto done; | ||
61 | 64 | ||
62 | if (set_tta_info(&info) < 0 || player_init(&info) < 0) | 65 | if (set_tta_info(&info) < 0 || player_init(&info) < 0) |
63 | { | 66 | return CODEC_ERROR; |
64 | status = CODEC_ERROR; | ||
65 | goto exit; | ||
66 | } | ||
67 | 67 | ||
68 | codec_set_replaygain(ci->id3); | 68 | codec_set_replaygain(ci->id3); |
69 | 69 | ||
@@ -74,8 +74,8 @@ next_track: | |||
74 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | 74 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); |
75 | } else { | 75 | } else { |
76 | DEBUGF("CODEC_ERROR: more than 2 channels\n"); | 76 | DEBUGF("CODEC_ERROR: more than 2 channels\n"); |
77 | status = CODEC_ERROR; | 77 | player_stop(); |
78 | goto done; | 78 | return CODEC_ERROR; |
79 | } | 79 | } |
80 | 80 | ||
81 | /* The main decoder loop */ | 81 | /* The main decoder loop */ |
@@ -88,31 +88,31 @@ next_track: | |||
88 | new_pos = set_position(ci->id3->offset, TTA_SEEK_POS); | 88 | new_pos = set_position(ci->id3->offset, TTA_SEEK_POS); |
89 | if (new_pos >= 0) | 89 | if (new_pos >= 0) |
90 | decodedsamples = new_pos; | 90 | decodedsamples = new_pos; |
91 | ci->seek_complete(); | ||
92 | } | 91 | } |
93 | 92 | ||
94 | while (!endofstream) | 93 | while (!endofstream) |
95 | { | 94 | { |
96 | ci->yield(); | 95 | enum codec_command_action action = ci->get_command(¶m); |
97 | if (ci->stop_codec || ci->new_track) | 96 | |
97 | if (action == CODEC_ACTION_HALT) | ||
98 | break; | 98 | break; |
99 | 99 | ||
100 | if (ci->seek_time) | 100 | if (action == CODEC_ACTION_SEEK_TIME) |
101 | { | 101 | { |
102 | new_pos = set_position(ci->seek_time / SEEK_STEP, TTA_SEEK_TIME); | 102 | new_pos = set_position(param / SEEK_STEP, TTA_SEEK_TIME); |
103 | if (new_pos >= 0) | 103 | if (new_pos >= 0) |
104 | { | 104 | { |
105 | decodedsamples = new_pos; | 105 | decodedsamples = new_pos; |
106 | ci->seek_complete(); | ||
107 | } | 106 | } |
107 | |||
108 | ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH); | ||
109 | ci->seek_complete(); | ||
108 | } | 110 | } |
109 | 111 | ||
110 | sample_count = get_samples(samples); | 112 | sample_count = get_samples(samples); |
111 | if (sample_count < 0) | 113 | if (sample_count < 0) |
112 | { | ||
113 | status = CODEC_ERROR; | ||
114 | break; | 114 | break; |
115 | } | 115 | |
116 | ci->pcmbuf_insert(samples, NULL, sample_count); | 116 | ci->pcmbuf_insert(samples, NULL, sample_count); |
117 | decodedsamples += sample_count; | 117 | decodedsamples += sample_count; |
118 | if (decodedsamples >= info.DATALENGTH) | 118 | if (decodedsamples >= info.DATALENGTH) |
@@ -120,11 +120,6 @@ next_track: | |||
120 | ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH); | 120 | ci->set_elapsed((uint64_t)info.LENGTH * 1000 * decodedsamples / info.DATALENGTH); |
121 | } | 121 | } |
122 | 122 | ||
123 | done: | ||
124 | player_stop(); | 123 | player_stop(); |
125 | if (ci->request_next_track()) | 124 | return CODEC_OK; |
126 | goto next_track; | ||
127 | |||
128 | exit: | ||
129 | return status; | ||
130 | } | 125 | } |
diff --git a/apps/codecs/vorbis.c b/apps/codecs/vorbis.c index 0a36a37c8b..e02d459262 100644 --- a/apps/codecs/vorbis.c +++ b/apps/codecs/vorbis.c | |||
@@ -104,14 +104,25 @@ static bool vorbis_set_codec_parameters(OggVorbis_File *vf) | |||
104 | } | 104 | } |
105 | 105 | ||
106 | /* this is the codec entry point */ | 106 | /* this is the codec entry point */ |
107 | enum codec_status codec_main(void) | 107 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
108 | { | ||
109 | if (reason == CODEC_LOAD) { | ||
110 | if (codec_init()) | ||
111 | return CODEC_ERROR; | ||
112 | ci->configure(DSP_SET_SAMPLE_DEPTH, 24); | ||
113 | } | ||
114 | |||
115 | return CODEC_OK; | ||
116 | } | ||
117 | |||
118 | /* this is called for each file to process */ | ||
119 | enum codec_status codec_run(void) | ||
108 | { | 120 | { |
109 | ov_callbacks callbacks; | 121 | ov_callbacks callbacks; |
110 | OggVorbis_File vf; | 122 | OggVorbis_File vf; |
111 | ogg_int32_t **pcm; | 123 | ogg_int32_t **pcm; |
112 | 124 | ||
113 | bool initialized = false; /* First init done? */ | 125 | int error = CODEC_ERROR; |
114 | int error; | ||
115 | long n; | 126 | long n; |
116 | int current_section; | 127 | int current_section; |
117 | int previous_section; | 128 | int previous_section; |
@@ -120,36 +131,24 @@ enum codec_status codec_main(void) | |||
120 | ogg_int64_t vf_dataoffsets; | 131 | ogg_int64_t vf_dataoffsets; |
121 | ogg_uint32_t vf_serialnos; | 132 | ogg_uint32_t vf_serialnos; |
122 | ogg_int64_t vf_pcmlengths[2]; | 133 | ogg_int64_t vf_pcmlengths[2]; |
123 | 134 | intptr_t param; | |
124 | ci->configure(DSP_SET_SAMPLE_DEPTH, 24); | ||
125 | |||
126 | if (codec_init()) { | ||
127 | error = CODEC_ERROR; | ||
128 | goto exit; | ||
129 | } | ||
130 | 135 | ||
131 | #if defined(CPU_ARM) || defined(CPU_COLDFIRE) || defined(CPU_MIPS) | 136 | #if defined(CPU_ARM) || defined(CPU_COLDFIRE) || defined(CPU_MIPS) |
132 | if (setjmp(rb_jump_buf) != 0) { | 137 | if (setjmp(rb_jump_buf) != 0) { |
133 | /* malloc failed; skip to next track */ | 138 | /* malloc failed; finish with this track */ |
134 | error = CODEC_ERROR; | ||
135 | goto done; | 139 | goto done; |
136 | } | 140 | } |
137 | #endif | 141 | #endif |
138 | |||
139 | next_track: | ||
140 | error = CODEC_OK; | ||
141 | |||
142 | ogg_malloc_init(); | 142 | ogg_malloc_init(); |
143 | 143 | ||
144 | if (codec_wait_taginfo() != 0) | ||
145 | goto done; | ||
146 | |||
147 | /* Create a decoder instance */ | 144 | /* Create a decoder instance */ |
148 | callbacks.read_func = read_handler; | 145 | callbacks.read_func = read_handler; |
149 | callbacks.seek_func = initial_seek_handler; | 146 | callbacks.seek_func = initial_seek_handler; |
150 | callbacks.tell_func = tell_handler; | 147 | callbacks.tell_func = tell_handler; |
151 | callbacks.close_func = close_handler; | 148 | callbacks.close_func = close_handler; |
152 | 149 | ||
150 | ci->seek_buffer(0); | ||
151 | |||
153 | /* Open a non-seekable stream */ | 152 | /* Open a non-seekable stream */ |
154 | error = ov_open_callbacks(ci, &vf, NULL, 0, callbacks); | 153 | error = ov_open_callbacks(ci, &vf, NULL, 0, callbacks); |
155 | 154 | ||
@@ -186,15 +185,13 @@ next_track: | |||
186 | vf.end = ci->id3->filesize; | 185 | vf.end = ci->id3->filesize; |
187 | vf.ready_state = OPENED; | 186 | vf.ready_state = OPENED; |
188 | vf.links = 1; | 187 | vf.links = 1; |
189 | initialized = true; | ||
190 | } else { | 188 | } else { |
191 | DEBUGF("Vorbis: ov_open failed: %d\n", error); | 189 | DEBUGF("Vorbis: ov_open failed: %d\n", error); |
192 | error = CODEC_ERROR; | ||
193 | goto done; | 190 | goto done; |
194 | } | 191 | } |
195 | 192 | ||
196 | if (ci->id3->offset) { | 193 | if (ci->id3->offset) { |
197 | ci->advance_buffer(ci->id3->offset); | 194 | ci->seek_buffer(ci->id3->offset); |
198 | ov_raw_seek(&vf, ci->id3->offset); | 195 | ov_raw_seek(&vf, ci->id3->offset); |
199 | ci->set_elapsed(ov_time_tell(&vf)); | 196 | ci->set_elapsed(ov_time_tell(&vf)); |
200 | ci->set_offset(ov_raw_tell(&vf)); | 197 | ci->set_offset(ov_raw_tell(&vf)); |
@@ -203,14 +200,17 @@ next_track: | |||
203 | previous_section = -1; | 200 | previous_section = -1; |
204 | eof = 0; | 201 | eof = 0; |
205 | while (!eof) { | 202 | while (!eof) { |
206 | ci->yield(); | 203 | enum codec_command_action action = ci->get_command(¶m); |
207 | if (ci->stop_codec || ci->new_track) | 204 | |
205 | if (action == CODEC_ACTION_HALT) | ||
208 | break; | 206 | break; |
209 | 207 | ||
210 | if (ci->seek_time) { | 208 | if (action == CODEC_ACTION_SEEK_TIME) { |
211 | if (ov_time_seek(&vf, ci->seek_time - 1)) { | 209 | if (ov_time_seek(&vf, param)) { |
212 | //ci->logf("ov_time_seek failed"); | 210 | //ci->logf("ov_time_seek failed"); |
213 | } | 211 | } |
212 | |||
213 | ci->set_elapsed(ov_time_tell(&vf)); | ||
214 | ci->seek_complete(); | 214 | ci->seek_complete(); |
215 | } | 215 | } |
216 | 216 | ||
@@ -220,7 +220,6 @@ next_track: | |||
220 | /* Change DSP and buffer settings for this bitstream */ | 220 | /* Change DSP and buffer settings for this bitstream */ |
221 | if (current_section != previous_section) { | 221 | if (current_section != previous_section) { |
222 | if (!vorbis_set_codec_parameters(&vf)) { | 222 | if (!vorbis_set_codec_parameters(&vf)) { |
223 | error = CODEC_ERROR; | ||
224 | goto done; | 223 | goto done; |
225 | } else { | 224 | } else { |
226 | previous_section = current_section; | 225 | previous_section = current_section; |
@@ -238,6 +237,7 @@ next_track: | |||
238 | } | 237 | } |
239 | } | 238 | } |
240 | 239 | ||
240 | error = CODEC_OK; | ||
241 | done: | 241 | done: |
242 | #if 0 /* defined(SIMULATOR) */ | 242 | #if 0 /* defined(SIMULATOR) */ |
243 | { | 243 | { |
@@ -249,18 +249,12 @@ done: | |||
249 | #endif | 249 | #endif |
250 | ogg_malloc_destroy(); | 250 | ogg_malloc_destroy(); |
251 | 251 | ||
252 | if (ci->request_next_track()) { | 252 | /* Clean things up for the next track */ |
253 | if (!initialized) | 253 | vf.dataoffsets = NULL; |
254 | goto next_track; | 254 | vf.offsets = NULL; |
255 | /* Clean things up for the next track */ | 255 | vf.serialnos = NULL; |
256 | vf.dataoffsets = NULL; | 256 | vf.pcmlengths = NULL; |
257 | vf.offsets = NULL; | 257 | ov_clear(&vf); |
258 | vf.serialnos = NULL; | 258 | |
259 | vf.pcmlengths = NULL; | ||
260 | ov_clear(&vf); | ||
261 | goto next_track; | ||
262 | } | ||
263 | |||
264 | exit: | ||
265 | return error; | 259 | return error; |
266 | } | 260 | } |
diff --git a/apps/codecs/vox.c b/apps/codecs/vox.c index c7f39342c3..bf274c6917 100644 --- a/apps/codecs/vox.c +++ b/apps/codecs/vox.c | |||
@@ -44,9 +44,19 @@ static uint8_t *read_buffer(size_t *realsize) | |||
44 | } | 44 | } |
45 | 45 | ||
46 | /* this is the codec entry point */ | 46 | /* this is the codec entry point */ |
47 | enum codec_status codec_main(void) | 47 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
48 | { | ||
49 | if (reason == CODEC_LOAD) { | ||
50 | /* Generic codec initialisation */ | ||
51 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
52 | } | ||
53 | |||
54 | return CODEC_OK; | ||
55 | } | ||
56 | |||
57 | /* this is called for each file to process */ | ||
58 | enum codec_status codec_run(void) | ||
48 | { | 59 | { |
49 | int status; | ||
50 | uint32_t decodedsamples; | 60 | uint32_t decodedsamples; |
51 | size_t n; | 61 | size_t n; |
52 | int bufcount; | 62 | int bufcount; |
@@ -54,26 +64,18 @@ enum codec_status codec_main(void) | |||
54 | uint8_t *voxbuf; | 64 | uint8_t *voxbuf; |
55 | off_t firstblockposn = 0; /* position of the first block in file */ | 65 | off_t firstblockposn = 0; /* position of the first block in file */ |
56 | const struct pcm_codec *codec; | 66 | const struct pcm_codec *codec; |
57 | 67 | intptr_t param; | |
58 | /* Generic codec initialisation */ | ||
59 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
60 | |||
61 | next_track: | ||
62 | status = CODEC_OK; | ||
63 | 68 | ||
64 | if (codec_init()) { | 69 | if (codec_init()) { |
65 | DEBUGF("codec_init() error\n"); | 70 | DEBUGF("codec_init() error\n"); |
66 | status = CODEC_ERROR; | 71 | return CODEC_ERROR; |
67 | goto exit; | ||
68 | } | 72 | } |
69 | 73 | ||
70 | if (codec_wait_taginfo() != 0) | ||
71 | goto done; | ||
72 | |||
73 | codec_set_replaygain(ci->id3); | 74 | codec_set_replaygain(ci->id3); |
74 | 75 | ||
75 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ | 76 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ |
76 | bytesdone = ci->id3->offset; | 77 | bytesdone = ci->id3->offset; |
78 | ci->seek_buffer(0); | ||
77 | 79 | ||
78 | ci->memset(&format, 0, sizeof(struct pcm_format)); | 80 | ci->memset(&format, 0, sizeof(struct pcm_format)); |
79 | 81 | ||
@@ -96,20 +98,16 @@ next_track: | |||
96 | if (!codec) | 98 | if (!codec) |
97 | { | 99 | { |
98 | DEBUGF("CODEC_ERROR: dialogic oki adpcm codec does not load.\n"); | 100 | DEBUGF("CODEC_ERROR: dialogic oki adpcm codec does not load.\n"); |
99 | status = CODEC_ERROR; | 101 | return CODEC_ERROR; |
100 | goto done; | ||
101 | } | 102 | } |
102 | 103 | ||
103 | if (!codec->set_format(&format)) | 104 | if (!codec->set_format(&format)) { |
104 | { | 105 | return CODEC_ERROR; |
105 | status = CODEC_ERROR; | ||
106 | goto done; | ||
107 | } | 106 | } |
108 | 107 | ||
109 | if (format.numbytes == 0) { | 108 | if (format.numbytes == 0) { |
110 | DEBUGF("CODEC_ERROR: data size is 0\n"); | 109 | DEBUGF("CODEC_ERROR: data size is 0\n"); |
111 | status = CODEC_ERROR; | 110 | return CODEC_ERROR; |
112 | goto done; | ||
113 | } | 111 | } |
114 | 112 | ||
115 | /* check chunksize */ | 113 | /* check chunksize */ |
@@ -118,8 +116,7 @@ next_track: | |||
118 | if (format.chunksize == 0) | 116 | if (format.chunksize == 0) |
119 | { | 117 | { |
120 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); | 118 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); |
121 | status = CODEC_ERROR; | 119 | return CODEC_ERROR; |
122 | goto done; | ||
123 | } | 120 | } |
124 | 121 | ||
125 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 122 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
@@ -131,14 +128,14 @@ next_track: | |||
131 | struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, | 128 | struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, |
132 | PCM_SEEK_POS, &read_buffer); | 129 | PCM_SEEK_POS, &read_buffer); |
133 | 130 | ||
134 | if (newpos->pos > format.numbytes) | 131 | if (newpos->pos > format.numbytes) { |
135 | goto done; | 132 | return CODEC_OK; |
133 | } | ||
136 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 134 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
137 | { | 135 | { |
138 | bytesdone = newpos->pos; | 136 | bytesdone = newpos->pos; |
139 | decodedsamples = newpos->samples; | 137 | decodedsamples = newpos->samples; |
140 | } | 138 | } |
141 | ci->seek_complete(); | ||
142 | } else { | 139 | } else { |
143 | /* already where we need to be */ | 140 | /* already where we need to be */ |
144 | bytesdone = 0; | 141 | bytesdone = 0; |
@@ -148,22 +145,29 @@ next_track: | |||
148 | endofstream = 0; | 145 | endofstream = 0; |
149 | 146 | ||
150 | while (!endofstream) { | 147 | while (!endofstream) { |
151 | ci->yield(); | 148 | enum codec_command_action action = ci->get_command(¶m); |
152 | if (ci->stop_codec || ci->new_track) { | 149 | |
150 | if (action == CODEC_ACTION_HALT) | ||
153 | break; | 151 | break; |
154 | } | ||
155 | 152 | ||
156 | if (ci->seek_time) { | 153 | if (action == CODEC_ACTION_SEEK_TIME) { |
157 | struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME, | 154 | struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, |
158 | &read_buffer); | 155 | &read_buffer); |
159 | 156 | ||
160 | if (newpos->pos > format.numbytes) | 157 | if (newpos->pos > format.numbytes) |
158 | { | ||
159 | ci->set_elapsed(ci->id3->length); | ||
160 | ci->seek_complete(); | ||
161 | break; | 161 | break; |
162 | } | ||
163 | |||
162 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 164 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
163 | { | 165 | { |
164 | bytesdone = newpos->pos; | 166 | bytesdone = newpos->pos; |
165 | decodedsamples = newpos->samples; | 167 | decodedsamples = newpos->samples; |
166 | } | 168 | } |
169 | |||
170 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
167 | ci->seek_complete(); | 171 | ci->seek_complete(); |
168 | } | 172 | } |
169 | 173 | ||
@@ -175,11 +179,10 @@ next_track: | |||
175 | endofstream = 1; | 179 | endofstream = 1; |
176 | } | 180 | } |
177 | 181 | ||
178 | status = codec->decode(voxbuf, n, samples, &bufcount); | 182 | if (codec->decode(voxbuf, n, samples, &bufcount) == CODEC_ERROR) |
179 | if (status == CODEC_ERROR) | ||
180 | { | 183 | { |
181 | DEBUGF("codec error\n"); | 184 | DEBUGF("codec error\n"); |
182 | goto done; | 185 | return CODEC_ERROR; |
183 | } | 186 | } |
184 | 187 | ||
185 | ci->pcmbuf_insert(samples, NULL, bufcount); | 188 | ci->pcmbuf_insert(samples, NULL, bufcount); |
@@ -192,10 +195,5 @@ next_track: | |||
192 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | 195 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); |
193 | } | 196 | } |
194 | 197 | ||
195 | done: | 198 | return CODEC_OK; |
196 | if (ci->request_next_track()) | ||
197 | goto next_track; | ||
198 | |||
199 | exit: | ||
200 | return status; | ||
201 | } | 199 | } |
diff --git a/apps/codecs/wav.c b/apps/codecs/wav.c index e179470f27..42bcc7081f 100644 --- a/apps/codecs/wav.c +++ b/apps/codecs/wav.c | |||
@@ -151,9 +151,19 @@ static uint8_t *read_buffer(size_t *realsize) | |||
151 | } | 151 | } |
152 | 152 | ||
153 | /* this is the codec entry point */ | 153 | /* this is the codec entry point */ |
154 | enum codec_status codec_main(void) | 154 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
155 | { | ||
156 | if (reason == CODEC_LOAD) { | ||
157 | /* Generic codec initialisation */ | ||
158 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
159 | } | ||
160 | |||
161 | return CODEC_OK; | ||
162 | } | ||
163 | |||
164 | /* this is called for each file to process */ | ||
165 | enum codec_status codec_run(void) | ||
155 | { | 166 | { |
156 | int status; | ||
157 | uint32_t decodedsamples; | 167 | uint32_t decodedsamples; |
158 | size_t n; | 168 | size_t n; |
159 | int bufcount; | 169 | int bufcount; |
@@ -163,38 +173,28 @@ enum codec_status codec_main(void) | |||
163 | off_t firstblockposn; /* position of the first block in file */ | 173 | off_t firstblockposn; /* position of the first block in file */ |
164 | const struct pcm_codec *codec; | 174 | const struct pcm_codec *codec; |
165 | uint32_t size; | 175 | uint32_t size; |
166 | 176 | intptr_t param; | |
167 | /* Generic codec initialisation */ | ||
168 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
169 | |||
170 | next_track: | ||
171 | status = CODEC_OK; | ||
172 | 177 | ||
173 | if (codec_init()) { | 178 | if (codec_init()) { |
174 | DEBUGF("codec_init() error\n"); | 179 | DEBUGF("codec_init() error\n"); |
175 | status = CODEC_ERROR; | 180 | return CODEC_ERROR; |
176 | goto exit; | ||
177 | } | 181 | } |
178 | 182 | ||
179 | if (codec_wait_taginfo() != 0) | ||
180 | goto done; | ||
181 | |||
182 | codec_set_replaygain(ci->id3); | 183 | codec_set_replaygain(ci->id3); |
183 | 184 | ||
184 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ | 185 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ |
185 | bytesdone = ci->id3->offset; | 186 | bytesdone = ci->id3->offset; |
186 | 187 | ||
187 | /* get RIFF chunk header */ | 188 | /* get RIFF chunk header */ |
189 | ci->seek_buffer(0); | ||
188 | buf = ci->request_buffer(&n, 12); | 190 | buf = ci->request_buffer(&n, 12); |
189 | if (n < 12) { | 191 | if (n < 12) { |
190 | DEBUGF("request_buffer error\n"); | 192 | DEBUGF("request_buffer error\n"); |
191 | status = CODEC_ERROR; | 193 | return CODEC_ERROR; |
192 | goto done; | ||
193 | } | 194 | } |
194 | if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) { | 195 | if ((memcmp(buf, "RIFF", 4) != 0) || (memcmp(&buf[8], "WAVE", 4) != 0)) { |
195 | DEBUGF("CODEC_ERROR: missing riff header\n"); | 196 | DEBUGF("CODEC_ERROR: missing riff header\n"); |
196 | status = CODEC_ERROR; | 197 | return CODEC_ERROR; |
197 | goto done; | ||
198 | } | 198 | } |
199 | 199 | ||
200 | /* advance to first WAVE chunk */ | 200 | /* advance to first WAVE chunk */ |
@@ -215,8 +215,7 @@ next_track: | |||
215 | if (n < 8) { | 215 | if (n < 8) { |
216 | DEBUGF("data chunk request_buffer error\n"); | 216 | DEBUGF("data chunk request_buffer error\n"); |
217 | /* no more chunks, 'data' chunk must not have been found */ | 217 | /* no more chunks, 'data' chunk must not have been found */ |
218 | status = CODEC_ERROR; | 218 | return CODEC_ERROR; |
219 | goto done; | ||
220 | } | 219 | } |
221 | 220 | ||
222 | /* chunkSize */ | 221 | /* chunkSize */ |
@@ -225,8 +224,7 @@ next_track: | |||
225 | if (size < 16) { | 224 | if (size < 16) { |
226 | DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%lu < 16\n", | 225 | DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%lu < 16\n", |
227 | (unsigned long)size); | 226 | (unsigned long)size); |
228 | status = CODEC_ERROR; | 227 | return CODEC_ERROR; |
229 | goto done; | ||
230 | } | 228 | } |
231 | /* wFormatTag */ | 229 | /* wFormatTag */ |
232 | format.formattag=buf[8]|(buf[9]<<8); | 230 | format.formattag=buf[8]|(buf[9]<<8); |
@@ -256,8 +254,7 @@ next_track: | |||
256 | if (format.size < 22) { | 254 | if (format.size < 22) { |
257 | DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is " | 255 | DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is " |
258 | "missing extension\n"); | 256 | "missing extension\n"); |
259 | status = CODEC_ERROR; | 257 | return CODEC_ERROR; |
260 | goto done; | ||
261 | } | 258 | } |
262 | /* wValidBitsPerSample */ | 259 | /* wValidBitsPerSample */ |
263 | format.bitspersample = buf[26]|(buf[27]<<8); | 260 | format.bitspersample = buf[26]|(buf[27]<<8); |
@@ -273,8 +270,7 @@ next_track: | |||
273 | { | 270 | { |
274 | if (!set_msadpcm_coeffs(buf)) | 271 | if (!set_msadpcm_coeffs(buf)) |
275 | { | 272 | { |
276 | status = CODEC_ERROR; | 273 | return CODEC_ERROR; |
277 | goto done; | ||
278 | } | 274 | } |
279 | } | 275 | } |
280 | 276 | ||
@@ -284,8 +280,7 @@ next_track: | |||
284 | { | 280 | { |
285 | DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n", | 281 | DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n", |
286 | (unsigned int) format.formattag); | 282 | (unsigned int) format.formattag); |
287 | status = CODEC_ERROR; | 283 | return CODEC_ERROR; |
288 | goto done; | ||
289 | } | 284 | } |
290 | 285 | ||
291 | /* riff 8bit linear pcm is unsigned */ | 286 | /* riff 8bit linear pcm is unsigned */ |
@@ -295,8 +290,7 @@ next_track: | |||
295 | /* set format, parse codec specific tag, check format, and calculate chunk size */ | 290 | /* set format, parse codec specific tag, check format, and calculate chunk size */ |
296 | if (!codec->set_format(&format)) | 291 | if (!codec->set_format(&format)) |
297 | { | 292 | { |
298 | status = CODEC_ERROR; | 293 | return CODEC_ERROR; |
299 | goto done; | ||
300 | } | 294 | } |
301 | } else if (memcmp(buf, "data", 4) == 0) { | 295 | } else if (memcmp(buf, "data", 4) == 0) { |
302 | format.numbytes = size; | 296 | format.numbytes = size; |
@@ -324,31 +318,26 @@ next_track: | |||
324 | if (!codec) | 318 | if (!codec) |
325 | { | 319 | { |
326 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n"); | 320 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n"); |
327 | status = CODEC_ERROR; | 321 | return CODEC_ERROR; |
328 | goto done; | ||
329 | } | 322 | } |
330 | 323 | ||
331 | /* common format check */ | 324 | /* common format check */ |
332 | if (format.channels == 0) { | 325 | if (format.channels == 0) { |
333 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n"); | 326 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n"); |
334 | status = CODEC_ERROR; | 327 | return CODEC_ERROR; |
335 | goto done; | ||
336 | } | 328 | } |
337 | if (format.samplesperblock == 0) { | 329 | if (format.samplesperblock == 0) { |
338 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n"); | 330 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n"); |
339 | status = CODEC_ERROR; | 331 | return CODEC_ERROR; |
340 | goto done; | ||
341 | } | 332 | } |
342 | if (format.blockalign == 0) | 333 | if (format.blockalign == 0) |
343 | { | 334 | { |
344 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n"); | 335 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n"); |
345 | status = CODEC_ERROR; | 336 | return CODEC_ERROR; |
346 | goto done; | ||
347 | } | 337 | } |
348 | if (format.numbytes == 0) { | 338 | if (format.numbytes == 0) { |
349 | DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n"); | 339 | DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n"); |
350 | status = CODEC_ERROR; | 340 | return CODEC_ERROR; |
351 | goto done; | ||
352 | } | 341 | } |
353 | 342 | ||
354 | /* check chunksize */ | 343 | /* check chunksize */ |
@@ -358,8 +347,7 @@ next_track: | |||
358 | if (format.chunksize == 0) | 347 | if (format.chunksize == 0) |
359 | { | 348 | { |
360 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); | 349 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); |
361 | status = CODEC_ERROR; | 350 | return CODEC_ERROR; |
362 | goto done; | ||
363 | } | 351 | } |
364 | 352 | ||
365 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 353 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
@@ -369,8 +357,7 @@ next_track: | |||
369 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | 357 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); |
370 | } else { | 358 | } else { |
371 | DEBUGF("CODEC_ERROR: more than 2 channels\n"); | 359 | DEBUGF("CODEC_ERROR: more than 2 channels\n"); |
372 | status = CODEC_ERROR; | 360 | return CODEC_ERROR; |
373 | goto done; | ||
374 | } | 361 | } |
375 | 362 | ||
376 | /* make sure we're at the correct offset */ | 363 | /* make sure we're at the correct offset */ |
@@ -380,13 +367,12 @@ next_track: | |||
380 | PCM_SEEK_POS, &read_buffer); | 367 | PCM_SEEK_POS, &read_buffer); |
381 | 368 | ||
382 | if (newpos->pos > format.numbytes) | 369 | if (newpos->pos > format.numbytes) |
383 | goto done; | 370 | return CODEC_OK; |
384 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 371 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
385 | { | 372 | { |
386 | bytesdone = newpos->pos; | 373 | bytesdone = newpos->pos; |
387 | decodedsamples = newpos->samples; | 374 | decodedsamples = newpos->samples; |
388 | } | 375 | } |
389 | ci->seek_complete(); | ||
390 | } else { | 376 | } else { |
391 | /* already where we need to be */ | 377 | /* already where we need to be */ |
392 | bytesdone = 0; | 378 | bytesdone = 0; |
@@ -396,22 +382,28 @@ next_track: | |||
396 | endofstream = 0; | 382 | endofstream = 0; |
397 | 383 | ||
398 | while (!endofstream) { | 384 | while (!endofstream) { |
399 | ci->yield(); | 385 | enum codec_command_action action = ci->get_command(¶m); |
400 | if (ci->stop_codec || ci->new_track) { | 386 | |
387 | if (action == CODEC_ACTION_HALT) | ||
401 | break; | 388 | break; |
402 | } | ||
403 | 389 | ||
404 | if (ci->seek_time) { | 390 | if (action == CODEC_ACTION_SEEK_TIME) { |
405 | struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME, | 391 | struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, |
406 | &read_buffer); | 392 | &read_buffer); |
407 | |||
408 | if (newpos->pos > format.numbytes) | 393 | if (newpos->pos > format.numbytes) |
394 | { | ||
395 | ci->set_elapsed(ci->id3->length); | ||
396 | ci->seek_complete(); | ||
409 | break; | 397 | break; |
398 | } | ||
399 | |||
410 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 400 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
411 | { | 401 | { |
412 | bytesdone = newpos->pos; | 402 | bytesdone = newpos->pos; |
413 | decodedsamples = newpos->samples; | 403 | decodedsamples = newpos->samples; |
414 | } | 404 | } |
405 | |||
406 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
415 | ci->seek_complete(); | 407 | ci->seek_complete(); |
416 | } | 408 | } |
417 | 409 | ||
@@ -423,11 +415,10 @@ next_track: | |||
423 | endofstream = 1; | 415 | endofstream = 1; |
424 | } | 416 | } |
425 | 417 | ||
426 | status = codec->decode(wavbuf, n, samples, &bufcount); | 418 | if (codec->decode(wavbuf, n, samples, &bufcount) == CODEC_ERROR) |
427 | if (status == CODEC_ERROR) | ||
428 | { | 419 | { |
429 | DEBUGF("codec error\n"); | 420 | DEBUGF("codec error\n"); |
430 | goto done; | 421 | return CODEC_ERROR; |
431 | } | 422 | } |
432 | 423 | ||
433 | ci->pcmbuf_insert(samples, NULL, bufcount); | 424 | ci->pcmbuf_insert(samples, NULL, bufcount); |
@@ -440,10 +431,5 @@ next_track: | |||
440 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | 431 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); |
441 | } | 432 | } |
442 | 433 | ||
443 | done: | 434 | return CODEC_OK; |
444 | if (ci->request_next_track()) | ||
445 | goto next_track; | ||
446 | |||
447 | exit: | ||
448 | return status; | ||
449 | } | 435 | } |
diff --git a/apps/codecs/wav64.c b/apps/codecs/wav64.c index 9dbdab8368..c763e6f7f0 100644 --- a/apps/codecs/wav64.c +++ b/apps/codecs/wav64.c | |||
@@ -159,9 +159,19 @@ static uint8_t *read_buffer(size_t *realsize) | |||
159 | } | 159 | } |
160 | 160 | ||
161 | /* this is the codec entry point */ | 161 | /* this is the codec entry point */ |
162 | enum codec_status codec_main(void) | 162 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
163 | { | ||
164 | if (reason == CODEC_LOAD) { | ||
165 | /* Generic codec initialisation */ | ||
166 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
167 | } | ||
168 | |||
169 | return CODEC_OK; | ||
170 | } | ||
171 | |||
172 | /* this is called for each file to process */ | ||
173 | enum codec_status codec_run(void) | ||
163 | { | 174 | { |
164 | int status; | ||
165 | uint32_t decodedsamples; | 175 | uint32_t decodedsamples; |
166 | size_t n; | 176 | size_t n; |
167 | int bufcount; | 177 | int bufcount; |
@@ -171,39 +181,29 @@ enum codec_status codec_main(void) | |||
171 | off_t firstblockposn; /* position of the first block in file */ | 181 | off_t firstblockposn; /* position of the first block in file */ |
172 | const struct pcm_codec *codec; | 182 | const struct pcm_codec *codec; |
173 | uint64_t size; | 183 | uint64_t size; |
174 | 184 | intptr_t param; | |
175 | /* Generic codec initialisation */ | ||
176 | ci->configure(DSP_SET_SAMPLE_DEPTH, PCM_OUTPUT_DEPTH-1); | ||
177 | |||
178 | next_track: | ||
179 | status = CODEC_OK; | ||
180 | 185 | ||
181 | if (codec_init()) { | 186 | if (codec_init()) { |
182 | DEBUGF("codec_init() error\n"); | 187 | DEBUGF("codec_init() error\n"); |
183 | status = CODEC_ERROR; | 188 | return CODEC_ERROR; |
184 | goto exit; | ||
185 | } | 189 | } |
186 | 190 | ||
187 | if (codec_wait_taginfo() != 0) | ||
188 | goto done; | ||
189 | |||
190 | codec_set_replaygain(ci->id3); | 191 | codec_set_replaygain(ci->id3); |
191 | 192 | ||
192 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ | 193 | /* Need to save offset for later use (cleared indirectly by advance_buffer) */ |
193 | bytesdone = ci->id3->offset; | 194 | bytesdone = ci->id3->offset; |
194 | 195 | ||
195 | /* get RIFF chunk header */ | 196 | /* get RIFF chunk header */ |
197 | ci->seek_buffer(0); | ||
196 | buf = ci->request_buffer(&n, 40); | 198 | buf = ci->request_buffer(&n, 40); |
197 | if (n < 40) { | 199 | if (n < 40) { |
198 | DEBUGF("request_buffer error\n"); | 200 | DEBUGF("request_buffer error\n"); |
199 | status = CODEC_ERROR; | 201 | return CODEC_ERROR; |
200 | goto done; | ||
201 | } | 202 | } |
202 | if ((memcmp(buf , WAVE64_GUID_RIFF, 16) != 0) || | 203 | if ((memcmp(buf , WAVE64_GUID_RIFF, 16) != 0) || |
203 | (memcmp(buf+24, WAVE64_GUID_WAVE, 16) != 0)) | 204 | (memcmp(buf+24, WAVE64_GUID_WAVE, 16) != 0)) |
204 | { | 205 | { |
205 | status = CODEC_ERROR; | 206 | return CODEC_ERROR; |
206 | goto done; | ||
207 | } | 207 | } |
208 | 208 | ||
209 | /* advance to first WAVE chunk */ | 209 | /* advance to first WAVE chunk */ |
@@ -224,8 +224,7 @@ next_track: | |||
224 | if (n < 8) { | 224 | if (n < 8) { |
225 | DEBUGF("data chunk request_buffer error\n"); | 225 | DEBUGF("data chunk request_buffer error\n"); |
226 | /* no more chunks, 'data' chunk must not have been found */ | 226 | /* no more chunks, 'data' chunk must not have been found */ |
227 | status = CODEC_ERROR; | 227 | return CODEC_ERROR; |
228 | goto done; | ||
229 | } | 228 | } |
230 | 229 | ||
231 | /* chunkSize */ | 230 | /* chunkSize */ |
@@ -233,8 +232,7 @@ next_track: | |||
233 | if (memcmp(buf, WAVE64_GUID_FMT, 16) == 0) { | 232 | if (memcmp(buf, WAVE64_GUID_FMT, 16) == 0) { |
234 | if (size < 16) { | 233 | if (size < 16) { |
235 | DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%d < 16\n", (int)size); | 234 | DEBUGF("CODEC_ERROR: 'fmt ' chunk size=%d < 16\n", (int)size); |
236 | status = CODEC_ERROR; | 235 | return CODEC_ERROR; |
237 | goto done; | ||
238 | } | 236 | } |
239 | /* wFormatTag */ | 237 | /* wFormatTag */ |
240 | format.formattag=buf[24]|(buf[25]<<8); | 238 | format.formattag=buf[24]|(buf[25]<<8); |
@@ -263,8 +261,7 @@ next_track: | |||
263 | if (format.size < 22) { | 261 | if (format.size < 22) { |
264 | DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is " | 262 | DEBUGF("CODEC_ERROR: WAVE_FORMAT_EXTENSIBLE is " |
265 | "missing extension\n"); | 263 | "missing extension\n"); |
266 | status = CODEC_ERROR; | 264 | return CODEC_ERROR; |
267 | goto done; | ||
268 | } | 265 | } |
269 | /* wValidBitsPerSample */ | 266 | /* wValidBitsPerSample */ |
270 | format.bitspersample = buf[42]|(buf[43]<<8); | 267 | format.bitspersample = buf[42]|(buf[43]<<8); |
@@ -279,10 +276,7 @@ next_track: | |||
279 | if (format.formattag == WAVE_FORMAT_ADPCM) | 276 | if (format.formattag == WAVE_FORMAT_ADPCM) |
280 | { | 277 | { |
281 | if (!set_msadpcm_coeffs(buf)) | 278 | if (!set_msadpcm_coeffs(buf)) |
282 | { | 279 | return CODEC_ERROR; |
283 | status = CODEC_ERROR; | ||
284 | goto done; | ||
285 | } | ||
286 | } | 280 | } |
287 | 281 | ||
288 | /* get codec */ | 282 | /* get codec */ |
@@ -291,8 +285,7 @@ next_track: | |||
291 | { | 285 | { |
292 | DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n", | 286 | DEBUGF("CODEC_ERROR: unsupported wave format 0x%x\n", |
293 | (unsigned int) format.formattag); | 287 | (unsigned int) format.formattag); |
294 | status = CODEC_ERROR; | 288 | return CODEC_ERROR; |
295 | goto done; | ||
296 | } | 289 | } |
297 | 290 | ||
298 | /* riff 8bit linear pcm is unsigned */ | 291 | /* riff 8bit linear pcm is unsigned */ |
@@ -301,10 +294,7 @@ next_track: | |||
301 | 294 | ||
302 | /* check format, and calculate chunk size */ | 295 | /* check format, and calculate chunk size */ |
303 | if (!codec->set_format(&format)) | 296 | if (!codec->set_format(&format)) |
304 | { | 297 | return CODEC_ERROR; |
305 | status = CODEC_ERROR; | ||
306 | goto done; | ||
307 | } | ||
308 | } else if (memcmp(buf, WAVE64_GUID_DATA, 16) == 0) { | 298 | } else if (memcmp(buf, WAVE64_GUID_DATA, 16) == 0) { |
309 | format.numbytes = size; | 299 | format.numbytes = size; |
310 | /* advance to start of data */ | 300 | /* advance to start of data */ |
@@ -330,31 +320,26 @@ next_track: | |||
330 | if (!codec) | 320 | if (!codec) |
331 | { | 321 | { |
332 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n"); | 322 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found\n"); |
333 | status = CODEC_ERROR; | 323 | return CODEC_ERROR; |
334 | goto done; | ||
335 | } | 324 | } |
336 | 325 | ||
337 | /* common format check */ | 326 | /* common format check */ |
338 | if (format.channels == 0) { | 327 | if (format.channels == 0) { |
339 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n"); | 328 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-channels file\n"); |
340 | status = CODEC_ERROR; | 329 | return CODEC_ERROR; |
341 | goto done; | ||
342 | } | 330 | } |
343 | if (format.samplesperblock == 0) { | 331 | if (format.samplesperblock == 0) { |
344 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n"); | 332 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-wSamplesPerBlock file\n"); |
345 | status = CODEC_ERROR; | 333 | return CODEC_ERROR; |
346 | goto done; | ||
347 | } | 334 | } |
348 | if (format.blockalign == 0) | 335 | if (format.blockalign == 0) |
349 | { | 336 | { |
350 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n"); | 337 | DEBUGF("CODEC_ERROR: 'fmt ' chunk not found or 0-blockalign file\n"); |
351 | status = CODEC_ERROR; | 338 | return CODEC_ERROR; |
352 | goto done; | ||
353 | } | 339 | } |
354 | if (format.numbytes == 0) { | 340 | if (format.numbytes == 0) { |
355 | DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n"); | 341 | DEBUGF("CODEC_ERROR: 'data' chunk not found or has zero-length\n"); |
356 | status = CODEC_ERROR; | 342 | return CODEC_ERROR; |
357 | goto done; | ||
358 | } | 343 | } |
359 | 344 | ||
360 | /* check chunksize */ | 345 | /* check chunksize */ |
@@ -364,8 +349,7 @@ next_track: | |||
364 | if (format.chunksize == 0) | 349 | if (format.chunksize == 0) |
365 | { | 350 | { |
366 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); | 351 | DEBUGF("CODEC_ERROR: chunksize is 0\n"); |
367 | status = CODEC_ERROR; | 352 | return CODEC_ERROR; |
368 | goto done; | ||
369 | } | 353 | } |
370 | 354 | ||
371 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); | 355 | ci->configure(DSP_SWITCH_FREQUENCY, ci->id3->frequency); |
@@ -375,8 +359,7 @@ next_track: | |||
375 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); | 359 | ci->configure(DSP_SET_STEREO_MODE, STEREO_MONO); |
376 | } else { | 360 | } else { |
377 | DEBUGF("CODEC_ERROR: more than 2 channels\n"); | 361 | DEBUGF("CODEC_ERROR: more than 2 channels\n"); |
378 | status = CODEC_ERROR; | 362 | return CODEC_ERROR; |
379 | goto done; | ||
380 | } | 363 | } |
381 | 364 | ||
382 | /* make sure we're at the correct offset */ | 365 | /* make sure we're at the correct offset */ |
@@ -385,14 +368,14 @@ next_track: | |||
385 | struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, | 368 | struct pcm_pos *newpos = codec->get_seek_pos(bytesdone - firstblockposn, |
386 | PCM_SEEK_POS, &read_buffer); | 369 | PCM_SEEK_POS, &read_buffer); |
387 | 370 | ||
388 | if (newpos->pos > format.numbytes) | 371 | if (newpos->pos > format.numbytes) { |
389 | goto done; | 372 | return CODEC_OK; |
373 | } | ||
390 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 374 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
391 | { | 375 | { |
392 | bytesdone = newpos->pos; | 376 | bytesdone = newpos->pos; |
393 | decodedsamples = newpos->samples; | 377 | decodedsamples = newpos->samples; |
394 | } | 378 | } |
395 | ci->seek_complete(); | ||
396 | } else { | 379 | } else { |
397 | /* already where we need to be */ | 380 | /* already where we need to be */ |
398 | bytesdone = 0; | 381 | bytesdone = 0; |
@@ -402,22 +385,29 @@ next_track: | |||
402 | endofstream = 0; | 385 | endofstream = 0; |
403 | 386 | ||
404 | while (!endofstream) { | 387 | while (!endofstream) { |
405 | ci->yield(); | 388 | enum codec_command_action action = ci->get_command(¶m); |
406 | if (ci->stop_codec || ci->new_track) { | 389 | |
390 | if (action == CODEC_ACTION_HALT) | ||
407 | break; | 391 | break; |
408 | } | ||
409 | 392 | ||
410 | if (ci->seek_time) { | 393 | if (action == CODEC_ACTION_SEEK_TIME) { |
411 | struct pcm_pos *newpos = codec->get_seek_pos(ci->seek_time, PCM_SEEK_TIME, | 394 | struct pcm_pos *newpos = codec->get_seek_pos(param, PCM_SEEK_TIME, |
412 | &read_buffer); | 395 | &read_buffer); |
413 | 396 | ||
414 | if (newpos->pos > format.numbytes) | 397 | if (newpos->pos > format.numbytes) |
398 | { | ||
399 | ci->set_elapsed(ci->id3->length); | ||
400 | ci->seek_complete(); | ||
415 | break; | 401 | break; |
402 | } | ||
403 | |||
416 | if (ci->seek_buffer(firstblockposn + newpos->pos)) | 404 | if (ci->seek_buffer(firstblockposn + newpos->pos)) |
417 | { | 405 | { |
418 | bytesdone = newpos->pos; | 406 | bytesdone = newpos->pos; |
419 | decodedsamples = newpos->samples; | 407 | decodedsamples = newpos->samples; |
420 | } | 408 | } |
409 | |||
410 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | ||
421 | ci->seek_complete(); | 411 | ci->seek_complete(); |
422 | } | 412 | } |
423 | 413 | ||
@@ -429,11 +419,10 @@ next_track: | |||
429 | endofstream = 1; | 419 | endofstream = 1; |
430 | } | 420 | } |
431 | 421 | ||
432 | status = codec->decode(wavbuf, n, samples, &bufcount); | 422 | if (codec->decode(wavbuf, n, samples, &bufcount) == CODEC_ERROR) |
433 | if (status == CODEC_ERROR) | ||
434 | { | 423 | { |
435 | DEBUGF("codec error\n"); | 424 | DEBUGF("codec error\n"); |
436 | goto done; | 425 | return CODEC_ERROR; |
437 | } | 426 | } |
438 | 427 | ||
439 | ci->pcmbuf_insert(samples, NULL, bufcount); | 428 | ci->pcmbuf_insert(samples, NULL, bufcount); |
@@ -445,12 +434,6 @@ next_track: | |||
445 | endofstream = 1; | 434 | endofstream = 1; |
446 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); | 435 | ci->set_elapsed(decodedsamples*1000LL/ci->id3->frequency); |
447 | } | 436 | } |
448 | status = CODEC_OK; | ||
449 | |||
450 | done: | ||
451 | if (ci->request_next_track()) | ||
452 | goto next_track; | ||
453 | 437 | ||
454 | exit: | 438 | return CODEC_OK; |
455 | return status; | ||
456 | } | 439 | } |
diff --git a/apps/codecs/wav_enc.c b/apps/codecs/wav_enc.c index ef1a88ec23..e4afeaf93c 100644 --- a/apps/codecs/wav_enc.c +++ b/apps/codecs/wav_enc.c | |||
@@ -345,40 +345,42 @@ static bool init_encoder(void) | |||
345 | return true; | 345 | return true; |
346 | } /* init_encoder */ | 346 | } /* init_encoder */ |
347 | 347 | ||
348 | /* main codec entry point */ | 348 | /* this is the codec entry point */ |
349 | enum codec_status codec_main(void) | 349 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
350 | { | 350 | { |
351 | if (!init_encoder()) | 351 | if (reason == CODEC_LOAD) { |
352 | return CODEC_ERROR; | 352 | if (!init_encoder()) |
353 | return CODEC_ERROR; | ||
354 | } | ||
355 | else if (reason == CODEC_UNLOAD) { | ||
356 | /* reset parameters to initial state */ | ||
357 | ci->enc_set_parameters(NULL); | ||
358 | } | ||
353 | 359 | ||
360 | return CODEC_OK; | ||
361 | } | ||
362 | |||
363 | /* this is called for each file to process */ | ||
364 | enum codec_status codec_run(void) | ||
365 | { | ||
354 | /* main encoding loop */ | 366 | /* main encoding loop */ |
355 | while(!ci->stop_codec) | 367 | while(ci->get_command(NULL) != CODEC_ACTION_HALT) |
356 | { | 368 | { |
357 | uint32_t *src; | 369 | uint32_t *src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); |
370 | struct enc_chunk_hdr *chunk; | ||
358 | 371 | ||
359 | while ((src = (uint32_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL) | 372 | if(src == NULL) |
360 | { | 373 | continue; |
361 | struct enc_chunk_hdr *chunk; | ||
362 | |||
363 | if (ci->stop_codec) | ||
364 | break; | ||
365 | 374 | ||
366 | chunk = ci->enc_get_chunk(); | 375 | chunk = ci->enc_get_chunk(); |
367 | chunk->enc_size = enc_size; | 376 | chunk->enc_size = enc_size; |
368 | chunk->num_pcm = PCM_SAMP_PER_CHUNK; | 377 | chunk->num_pcm = PCM_SAMP_PER_CHUNK; |
369 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); | 378 | chunk->enc_data = ENC_CHUNK_SKIP_HDR(chunk->enc_data, chunk); |
370 | 379 | ||
371 | chunk_to_wav_format(src, (uint32_t *)chunk->enc_data); | 380 | chunk_to_wav_format(src, (uint32_t *)chunk->enc_data); |
372 | 381 | ||
373 | ci->enc_finish_chunk(); | 382 | ci->enc_finish_chunk(); |
374 | ci->yield(); | ||
375 | } | ||
376 | |||
377 | ci->yield(); | ||
378 | } | 383 | } |
379 | 384 | ||
380 | /* reset parameters to initial state */ | ||
381 | ci->enc_set_parameters(NULL); | ||
382 | |||
383 | return CODEC_OK; | 385 | return CODEC_OK; |
384 | } /* codec_start */ | 386 | } |
diff --git a/apps/codecs/wavpack.c b/apps/codecs/wavpack.c index d27a9fb621..ccb9f41190 100644 --- a/apps/codecs/wavpack.c +++ b/apps/codecs/wavpack.c | |||
@@ -31,39 +31,39 @@ static int32_t temp_buffer [BUFFER_SIZE] IBSS_ATTR; | |||
31 | static int32_t read_callback (void *buffer, int32_t bytes) | 31 | static int32_t read_callback (void *buffer, int32_t bytes) |
32 | { | 32 | { |
33 | int32_t retval = ci->read_filebuf (buffer, bytes); | 33 | int32_t retval = ci->read_filebuf (buffer, bytes); |
34 | ci->id3->offset = ci->curpos; | 34 | ci->set_offset(ci->curpos); |
35 | return retval; | 35 | return retval; |
36 | } | 36 | } |
37 | 37 | ||
38 | /* this is the codec entry point */ | 38 | /* this is the codec entry point */ |
39 | enum codec_status codec_main(void) | 39 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
40 | { | ||
41 | if (reason == CODEC_LOAD) { | ||
42 | /* Generic codec initialisation */ | ||
43 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | ||
44 | } | ||
45 | |||
46 | return CODEC_OK; | ||
47 | } | ||
48 | |||
49 | /* this is called for each file to process */ | ||
50 | enum codec_status codec_run(void) | ||
40 | { | 51 | { |
41 | WavpackContext *wpc; | 52 | WavpackContext *wpc; |
42 | char error [80]; | 53 | char error [80]; |
43 | int bps, nchans, sr_100; | 54 | int bps, nchans, sr_100; |
44 | int retval; | 55 | intptr_t param; |
45 | 56 | ||
46 | /* Generic codec initialisation */ | 57 | if (codec_init()) |
47 | ci->configure(DSP_SET_SAMPLE_DEPTH, 28); | 58 | return CODEC_ERROR; |
48 | 59 | ||
49 | next_track: | 60 | ci->seek_buffer (ci->id3->offset); |
50 | retval = CODEC_OK; | ||
51 | 61 | ||
52 | if (codec_init()) { | ||
53 | retval = CODEC_ERROR; | ||
54 | goto exit; | ||
55 | } | ||
56 | |||
57 | if (codec_wait_taginfo() != 0) | ||
58 | goto done; | ||
59 | |||
60 | /* Create a decoder instance */ | 62 | /* Create a decoder instance */ |
61 | wpc = WavpackOpenFileInput (read_callback, error); | 63 | wpc = WavpackOpenFileInput (read_callback, error); |
62 | 64 | ||
63 | if (!wpc) { | 65 | if (!wpc) |
64 | retval = CODEC_ERROR; | 66 | return CODEC_ERROR; |
65 | goto done; | ||
66 | } | ||
67 | 67 | ||
68 | ci->configure(DSP_SWITCH_FREQUENCY, WavpackGetSampleRate (wpc)); | 68 | ci->configure(DSP_SWITCH_FREQUENCY, WavpackGetSampleRate (wpc)); |
69 | codec_set_replaygain(ci->id3); | 69 | codec_set_replaygain(ci->id3); |
@@ -77,56 +77,48 @@ next_track: | |||
77 | /* The main decoder loop */ | 77 | /* The main decoder loop */ |
78 | 78 | ||
79 | while (1) { | 79 | while (1) { |
80 | int32_t nsamples; | 80 | int32_t nsamples; |
81 | enum codec_command_action action = ci->get_command(¶m); | ||
82 | |||
83 | if (action == CODEC_ACTION_HALT) | ||
84 | break; | ||
81 | 85 | ||
82 | if (ci->seek_time && ci->taginfo_ready && ci->id3->length) { | 86 | if (action == CODEC_ACTION_SEEK_TIME) { |
83 | ci->seek_time--; | ||
84 | int curpos_ms = WavpackGetSampleIndex (wpc) / sr_100 * 10; | 87 | int curpos_ms = WavpackGetSampleIndex (wpc) / sr_100 * 10; |
85 | int n, d, skip; | 88 | int n, d, skip; |
86 | 89 | ||
87 | if (ci->seek_time > curpos_ms) { | 90 | if (param > curpos_ms) { |
88 | n = ci->seek_time - curpos_ms; | 91 | n = param - curpos_ms; |
89 | d = ci->id3->length - curpos_ms; | 92 | d = ci->id3->length - curpos_ms; |
90 | skip = (int)((int64_t)(ci->filesize - ci->curpos) * n / d); | 93 | skip = (int)((int64_t)(ci->filesize - ci->curpos) * n / d); |
91 | ci->seek_buffer (ci->curpos + skip); | 94 | ci->seek_buffer (ci->curpos + skip); |
92 | } | 95 | } |
93 | else { | 96 | else if (curpos_ms != 0) { |
94 | n = curpos_ms - ci->seek_time; | 97 | n = curpos_ms - param; |
95 | d = curpos_ms; | 98 | d = curpos_ms; |
96 | skip = (int)((int64_t) ci->curpos * n / d); | 99 | skip = (int)((int64_t) ci->curpos * n / d); |
97 | ci->seek_buffer (ci->curpos - skip); | 100 | ci->seek_buffer (ci->curpos - skip); |
98 | } | 101 | } |
99 | 102 | ||
100 | wpc = WavpackOpenFileInput (read_callback, error); | 103 | wpc = WavpackOpenFileInput (read_callback, error); |
101 | ci->seek_complete(); | ||
102 | |||
103 | if (!wpc) | 104 | if (!wpc) |
105 | { | ||
106 | ci->seek_complete(); | ||
104 | break; | 107 | break; |
108 | } | ||
105 | 109 | ||
106 | ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10); | 110 | ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10); |
107 | ci->yield (); | 111 | ci->seek_complete(); |
108 | } | 112 | } |
109 | 113 | ||
110 | nsamples = WavpackUnpackSamples (wpc, temp_buffer, BUFFER_SIZE / nchans); | 114 | nsamples = WavpackUnpackSamples (wpc, temp_buffer, BUFFER_SIZE / nchans); |
111 | 115 | ||
112 | if (!nsamples || ci->stop_codec || ci->new_track) | 116 | if (!nsamples) |
113 | break; | ||
114 | |||
115 | ci->yield (); | ||
116 | |||
117 | if (ci->stop_codec || ci->new_track) | ||
118 | break; | 117 | break; |
119 | 118 | ||
120 | ci->pcmbuf_insert (temp_buffer, NULL, nsamples); | 119 | ci->pcmbuf_insert (temp_buffer, NULL, nsamples); |
121 | |||
122 | ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10); | 120 | ci->set_elapsed (WavpackGetSampleIndex (wpc) / sr_100 * 10); |
123 | ci->yield (); | ||
124 | } | 121 | } |
125 | 122 | ||
126 | done: | 123 | return CODEC_OK; |
127 | if (ci->request_next_track()) | ||
128 | goto next_track; | ||
129 | |||
130 | exit: | ||
131 | return retval; | ||
132 | } | 124 | } |
diff --git a/apps/codecs/wavpack_enc.c b/apps/codecs/wavpack_enc.c index d908e284be..730cf0734b 100644 --- a/apps/codecs/wavpack_enc.c +++ b/apps/codecs/wavpack_enc.c | |||
@@ -389,77 +389,79 @@ static bool init_encoder(void) | |||
389 | return true; | 389 | return true; |
390 | } /* init_encoder */ | 390 | } /* init_encoder */ |
391 | 391 | ||
392 | enum codec_status codec_main(void) | 392 | /* this is the codec entry point */ |
393 | enum codec_status codec_main(enum codec_entry_call_reason reason) | ||
393 | { | 394 | { |
394 | /* initialize params and config */ | 395 | if (reason == CODEC_LOAD) { |
395 | if (!init_encoder()) | 396 | /* initialize params and config */ |
396 | return CODEC_ERROR; | 397 | if (!init_encoder()) |
398 | return CODEC_ERROR; | ||
399 | } | ||
400 | else if (reason == CODEC_UNLOAD) { | ||
401 | /* reset parameters to initial state */ | ||
402 | ci->enc_set_parameters(NULL); | ||
403 | } | ||
397 | 404 | ||
405 | return CODEC_OK; | ||
406 | } | ||
407 | |||
408 | /* this is called for each file to process */ | ||
409 | enum codec_status codec_run(void) | ||
410 | { | ||
398 | /* main encoding loop */ | 411 | /* main encoding loop */ |
399 | while(!ci->stop_codec) | 412 | while(ci->get_command(NULL) != CODEC_ACTION_HALT) |
400 | { | 413 | { |
401 | uint8_t *src; | 414 | uint8_t *src = (uint8_t *)ci->enc_get_pcm_data(PCM_CHUNK_SIZE); |
415 | struct enc_chunk_hdr *chunk; | ||
416 | bool abort_chunk; | ||
417 | uint8_t *dst; | ||
418 | uint8_t *src_end; | ||
402 | 419 | ||
403 | while ((src = ci->enc_get_pcm_data(PCM_CHUNK_SIZE)) != NULL) | 420 | if(src == NULL) |
404 | { | 421 | continue; |
405 | struct enc_chunk_hdr *chunk; | ||
406 | bool abort_chunk; | ||
407 | uint8_t *dst; | ||
408 | uint8_t *src_end; | ||
409 | |||
410 | if(ci->stop_codec) | ||
411 | break; | ||
412 | |||
413 | abort_chunk = true; | ||
414 | 422 | ||
415 | chunk = ci->enc_get_chunk(); | 423 | chunk = ci->enc_get_chunk(); |
416 | 424 | ||
417 | /* reset counts and pointer */ | 425 | /* reset counts and pointer */ |
418 | chunk->enc_size = 0; | 426 | chunk->enc_size = 0; |
419 | chunk->num_pcm = 0; | 427 | chunk->num_pcm = 0; |
420 | chunk->enc_data = NULL; | 428 | chunk->enc_data = NULL; |
421 | 429 | ||
422 | dst = ENC_CHUNK_SKIP_HDR(dst, chunk); | 430 | dst = ENC_CHUNK_SKIP_HDR(dst, chunk); |
423 | 431 | ||
424 | WavpackStartBlock(wpc, dst, dst + data_size); | 432 | WavpackStartBlock(wpc, dst, dst + data_size); |
425 | 433 | ||
426 | chunk_to_int32((uint32_t*)src); | 434 | chunk_to_int32((uint32_t*)src); |
427 | src = input_buffer; | 435 | src = input_buffer; |
428 | src_end = src + input_size; | 436 | src_end = src + input_size; |
429 | 437 | ||
430 | /* encode chunk in four steps yielding between each */ | 438 | /* encode chunk in four steps yielding between each */ |
431 | do | 439 | do |
440 | { | ||
441 | abort_chunk = true; | ||
442 | if (WavpackPackSamples(wpc, (int32_t *)src, | ||
443 | PCM_SAMP_PER_CHUNK/4)) | ||
432 | { | 444 | { |
433 | if (WavpackPackSamples(wpc, (int32_t *)src, | 445 | chunk->num_pcm += PCM_SAMP_PER_CHUNK/4; |
434 | PCM_SAMP_PER_CHUNK/4)) | 446 | ci->yield(); |
435 | { | 447 | /* could've been stopped in some way */ |
436 | chunk->num_pcm += PCM_SAMP_PER_CHUNK/4; | 448 | abort_chunk = chunk->flags & CHUNKF_ABORT; |
437 | ci->yield(); | ||
438 | /* could've been stopped in some way */ | ||
439 | abort_chunk = ci->stop_codec || | ||
440 | (chunk->flags & CHUNKF_ABORT); | ||
441 | } | ||
442 | |||
443 | src += input_step; | ||
444 | } | 449 | } |
445 | while (!abort_chunk && src < src_end); | ||
446 | 450 | ||
447 | if (!abort_chunk) | 451 | src += input_step; |
448 | { | ||
449 | chunk->enc_data = dst; | ||
450 | if (chunk->num_pcm < PCM_SAMP_PER_CHUNK) | ||
451 | ci->enc_unget_pcm_data(PCM_CHUNK_SIZE - chunk->num_pcm*4); | ||
452 | /* finish the chunk and store chunk size info */ | ||
453 | chunk->enc_size = WavpackFinishBlock(wpc); | ||
454 | ci->enc_finish_chunk(); | ||
455 | } | ||
456 | } | 452 | } |
453 | while (!abort_chunk && src < src_end); | ||
457 | 454 | ||
458 | ci->yield(); | 455 | if (!abort_chunk) |
456 | { | ||
457 | chunk->enc_data = dst; | ||
458 | if (chunk->num_pcm < PCM_SAMP_PER_CHUNK) | ||
459 | ci->enc_unget_pcm_data(PCM_CHUNK_SIZE - chunk->num_pcm*4); | ||
460 | /* finish the chunk and store chunk size info */ | ||
461 | chunk->enc_size = WavpackFinishBlock(wpc); | ||
462 | ci->enc_finish_chunk(); | ||
463 | } | ||
459 | } | 464 | } |
460 | 465 | ||
461 | /* reset parameters to initial state */ | ||
462 | ci->enc_set_parameters(NULL); | ||
463 | |||
464 | return CODEC_OK; | 466 | return CODEC_OK; |
465 | } /* codec_start */ | 467 | } |
diff --git a/apps/codecs/wma.c b/apps/codecs/wma.c index 1b46813444..c327fafb5a 100644 --- a/apps/codecs/wma.c +++ b/apps/codecs/wma.c | |||
@@ -29,53 +29,52 @@ CODEC_HEADER | |||
29 | static WMADecodeContext wmadec; | 29 | static WMADecodeContext wmadec; |
30 | 30 | ||
31 | /* this is the codec entry point */ | 31 | /* this is the codec entry point */ |
32 | enum codec_status codec_main(void) | 32 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
33 | { | ||
34 | if (reason == CODEC_LOAD) { | ||
35 | /* Generic codec initialisation */ | ||
36 | ci->configure(DSP_SET_SAMPLE_DEPTH, 29); | ||
37 | } | ||
38 | |||
39 | return CODEC_OK; | ||
40 | } | ||
41 | |||
42 | /* this is called for each file to process */ | ||
43 | enum codec_status codec_run(void) | ||
33 | { | 44 | { |
34 | uint32_t elapsedtime; | 45 | uint32_t elapsedtime; |
35 | int retval; | ||
36 | asf_waveformatex_t wfx; | 46 | asf_waveformatex_t wfx; |
37 | size_t resume_offset; | 47 | size_t resume_offset; |
38 | int i; | 48 | int i; |
39 | int wmares, res; | 49 | int wmares; |
50 | int res = 0; | ||
40 | uint8_t* audiobuf; | 51 | uint8_t* audiobuf; |
41 | int audiobufsize; | 52 | int audiobufsize; |
42 | int packetlength = 0; | 53 | int packetlength = 0; |
43 | int errcount = 0; | 54 | int errcount = 0; |
44 | 55 | intptr_t param; | |
45 | /* Generic codec initialisation */ | ||
46 | ci->configure(DSP_SET_SAMPLE_DEPTH, 29); | ||
47 | |||
48 | next_track: | ||
49 | retval = CODEC_OK; | ||
50 | 56 | ||
51 | /* Proper reset of the decoder context. */ | 57 | /* Proper reset of the decoder context. */ |
52 | memset(&wmadec, 0, sizeof(wmadec)); | 58 | memset(&wmadec, 0, sizeof(wmadec)); |
53 | 59 | ||
54 | /* Wait for the metadata to be read */ | ||
55 | if (codec_wait_taginfo() != 0) | ||
56 | goto done; | ||
57 | |||
58 | /* Remember the resume position - when the codec is opened, the | 60 | /* Remember the resume position - when the codec is opened, the |
59 | playback engine will reset it. */ | 61 | playback engine will reset it. */ |
60 | resume_offset = ci->id3->offset; | 62 | resume_offset = ci->id3->offset; |
61 | 63 | ||
62 | restart_track: | 64 | restart_track: |
63 | retval = CODEC_OK; | ||
64 | |||
65 | if (codec_init()) { | 65 | if (codec_init()) { |
66 | LOGF("WMA: Error initialising codec\n"); | 66 | LOGF("WMA: Error initialising codec\n"); |
67 | retval = CODEC_ERROR; | 67 | return CODEC_ERROR; |
68 | goto exit; | ||
69 | } | 68 | } |
70 | 69 | ||
71 | /* Copy the format metadata we've stored in the id3 TOC field. This | 70 | /* Copy the format metadata we've stored in the id3 TOC field. This |
72 | saves us from parsing it again here. */ | 71 | saves us from parsing it again here. */ |
73 | memcpy(&wfx, ci->id3->toc, sizeof(wfx)); | 72 | memcpy(&wfx, ci->id3->toc, sizeof(wfx)); |
74 | 73 | ||
74 | ci->seek_buffer(ci->id3->first_frame_offset); | ||
75 | if (wma_decode_init(&wmadec,&wfx) < 0) { | 75 | if (wma_decode_init(&wmadec,&wfx) < 0) { |
76 | LOGF("WMA: Unsupported or corrupt file\n"); | 76 | LOGF("WMA: Unsupported or corrupt file\n"); |
77 | retval = CODEC_ERROR; | 77 | return CODEC_ERROR; |
78 | goto exit; | ||
79 | } | 78 | } |
80 | 79 | ||
81 | if (resume_offset > ci->id3->first_frame_offset) | 80 | if (resume_offset > ci->id3->first_frame_offset) |
@@ -101,34 +100,35 @@ restart_track: | |||
101 | codec_set_replaygain(ci->id3); | 100 | codec_set_replaygain(ci->id3); |
102 | 101 | ||
103 | /* The main decoding loop */ | 102 | /* The main decoding loop */ |
104 | |||
105 | res = 1; | ||
106 | while (res >= 0) | 103 | while (res >= 0) |
107 | { | 104 | { |
108 | ci->yield(); | 105 | enum codec_command_action action = ci->get_command(¶m); |
109 | if (ci->stop_codec || ci->new_track) { | 106 | |
110 | goto done; | 107 | if (action == CODEC_ACTION_HALT) |
111 | } | 108 | break; |
112 | 109 | ||
113 | /* Deal with any pending seek requests */ | 110 | /* Deal with any pending seek requests */ |
114 | if (ci->seek_time){ | 111 | if (action == CODEC_ACTION_SEEK_TIME) { |
115 | 112 | ||
116 | if (ci->seek_time == 1) { | 113 | if (param == 0) { |
114 | ci->set_elapsed(0); | ||
117 | ci->seek_complete(); | 115 | ci->seek_complete(); |
118 | goto restart_track; /* Pretend you never saw this... */ | 116 | goto restart_track; /* Pretend you never saw this... */ |
119 | } | 117 | } |
120 | 118 | ||
121 | elapsedtime = asf_seek(ci->seek_time, &wfx); | 119 | elapsedtime = asf_seek(param, &wfx); |
122 | if (elapsedtime < 1){ | 120 | if (elapsedtime < 1){ |
121 | ci->set_elapsed(0); | ||
123 | ci->seek_complete(); | 122 | ci->seek_complete(); |
124 | goto next_track; | 123 | break; |
125 | } | 124 | } |
126 | /*DEBUGF("Seek returned %d\n", (int)elapsedtime);*/ | 125 | /*DEBUGF("Seek returned %d\n", (int)elapsedtime);*/ |
127 | ci->set_elapsed(elapsedtime); | ||
128 | 126 | ||
129 | /*flush the wma decoder state*/ | 127 | /*flush the wma decoder state*/ |
130 | wmadec.last_superframe_len = 0; | 128 | wmadec.last_superframe_len = 0; |
131 | wmadec.last_bitoffset = 0; | 129 | wmadec.last_bitoffset = 0; |
130 | |||
131 | ci->set_elapsed(elapsedtime); | ||
132 | ci->seek_complete(); | 132 | ci->seek_complete(); |
133 | } | 133 | } |
134 | errcount = 0; | 134 | errcount = 0; |
@@ -140,10 +140,15 @@ new_packet: | |||
140 | * times. If we succeed, the error counter will be reset. | 140 | * times. If we succeed, the error counter will be reset. |
141 | */ | 141 | */ |
142 | 142 | ||
143 | if (res == ASF_ERROR_EOF) { | ||
144 | /* File ended - not an error */ | ||
145 | break; | ||
146 | } | ||
147 | |||
143 | errcount++; | 148 | errcount++; |
144 | DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount); | 149 | DEBUGF("read_packet error %d, errcount %d\n",wmares, errcount); |
145 | if (errcount > 5) { | 150 | if (errcount > 5) { |
146 | goto done; | 151 | return CODEC_ERROR; |
147 | } else { | 152 | } else { |
148 | ci->advance_buffer(packetlength); | 153 | ci->advance_buffer(packetlength); |
149 | goto new_packet; | 154 | goto new_packet; |
@@ -163,7 +168,7 @@ new_packet: | |||
163 | errcount++; | 168 | errcount++; |
164 | DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount); | 169 | DEBUGF("WMA decode error %d, errcount %d\n",wmares, errcount); |
165 | if (errcount > 5) { | 170 | if (errcount > 5) { |
166 | goto done; | 171 | return CODEC_ERROR; |
167 | } else { | 172 | } else { |
168 | ci->advance_buffer(packetlength); | 173 | ci->advance_buffer(packetlength); |
169 | goto new_packet; | 174 | goto new_packet; |
@@ -173,18 +178,12 @@ new_packet: | |||
173 | elapsedtime += (wmares*10)/(wfx.rate/100); | 178 | elapsedtime += (wmares*10)/(wfx.rate/100); |
174 | ci->set_elapsed(elapsedtime); | 179 | ci->set_elapsed(elapsedtime); |
175 | } | 180 | } |
176 | ci->yield(); | ||
177 | } | 181 | } |
178 | } | 182 | } |
179 | 183 | ||
180 | ci->advance_buffer(packetlength); | 184 | ci->advance_buffer(packetlength); |
181 | } | 185 | } |
182 | 186 | ||
183 | done: | ||
184 | /*LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);*/ | 187 | /*LOGF("WMA: Decoded %ld samples\n",elapsedtime*wfx.rate/1000);*/ |
185 | 188 | return CODEC_OK; | |
186 | if (ci->request_next_track()) | ||
187 | goto next_track; | ||
188 | exit: | ||
189 | return retval; | ||
190 | } | 189 | } |
diff --git a/apps/codecs/wmapro.c b/apps/codecs/wmapro.c index c02dddeeb3..b6a8e47f25 100644 --- a/apps/codecs/wmapro.c +++ b/apps/codecs/wmapro.c | |||
@@ -27,11 +27,22 @@ CODEC_HEADER | |||
27 | 27 | ||
28 | int32_t *dec[2]; /* pointers to the output buffers in WMAProDecodeCtx in wmaprodec.c */ | 28 | int32_t *dec[2]; /* pointers to the output buffers in WMAProDecodeCtx in wmaprodec.c */ |
29 | 29 | ||
30 | |||
30 | /* this is the codec entry point */ | 31 | /* this is the codec entry point */ |
31 | enum codec_status codec_main(void) | 32 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
33 | { | ||
34 | if (reason == CODEC_LOAD) { | ||
35 | /* Generic codec initialisation */ | ||
36 | ci->configure(DSP_SET_SAMPLE_DEPTH, WMAPRO_DSP_SAMPLE_DEPTH); | ||
37 | } | ||
38 | |||
39 | return CODEC_OK; | ||
40 | } | ||
41 | |||
42 | /* this is called for each file to process */ | ||
43 | enum codec_status codec_run(void) | ||
32 | { | 44 | { |
33 | uint32_t elapsedtime; | 45 | uint32_t elapsedtime; |
34 | int retval; | ||
35 | asf_waveformatex_t wfx; /* Holds the stream properties */ | 46 | asf_waveformatex_t wfx; /* Holds the stream properties */ |
36 | size_t resume_offset; | 47 | size_t resume_offset; |
37 | int res; /* Return values from asf_read_packet() and decode_packet() */ | 48 | int res; /* Return values from asf_read_packet() and decode_packet() */ |
@@ -42,28 +53,15 @@ enum codec_status codec_main(void) | |||
42 | int pktcnt = 0; /* Count of the packets played */ | 53 | int pktcnt = 0; /* Count of the packets played */ |
43 | uint8_t *data; /* Pointer to decoder input buffer */ | 54 | uint8_t *data; /* Pointer to decoder input buffer */ |
44 | int size; /* Size of the input frame to the decoder */ | 55 | int size; /* Size of the input frame to the decoder */ |
45 | 56 | intptr_t param; | |
46 | /* Generic codec initialisation */ | ||
47 | ci->configure(DSP_SET_SAMPLE_DEPTH, WMAPRO_DSP_SAMPLE_DEPTH); | ||
48 | |||
49 | |||
50 | next_track: | ||
51 | retval = CODEC_OK; | ||
52 | |||
53 | /* Wait for the metadata to be read */ | ||
54 | if (codec_wait_taginfo() != 0) | ||
55 | goto done; | ||
56 | 57 | ||
57 | /* Remember the resume position */ | 58 | /* Remember the resume position */ |
58 | resume_offset = ci->id3->offset; | 59 | resume_offset = ci->id3->offset; |
59 | 60 | ||
60 | restart_track: | 61 | restart_track: |
61 | retval = CODEC_OK; | ||
62 | |||
63 | if (codec_init()) { | 62 | if (codec_init()) { |
64 | LOGF("(WMA PRO) Error: Error initialising codec\n"); | 63 | LOGF("(WMA PRO) Error: Error initialising codec\n"); |
65 | retval = CODEC_ERROR; | 64 | return CODEC_ERROR; |
66 | goto done; | ||
67 | } | 65 | } |
68 | 66 | ||
69 | /* Copy the format metadata we've stored in the id3 TOC field. This | 67 | /* Copy the format metadata we've stored in the id3 TOC field. This |
@@ -77,8 +75,7 @@ restart_track: | |||
77 | 75 | ||
78 | if (decode_init(&wfx) < 0) { | 76 | if (decode_init(&wfx) < 0) { |
79 | LOGF("(WMA PRO) Error: Unsupported or corrupt file\n"); | 77 | LOGF("(WMA PRO) Error: Unsupported or corrupt file\n"); |
80 | retval = CODEC_ERROR; | 78 | return CODEC_ERROR; |
81 | goto done; | ||
82 | } | 79 | } |
83 | 80 | ||
84 | /* Now advance the file position to the first frame */ | 81 | /* Now advance the file position to the first frame */ |
@@ -91,23 +88,24 @@ restart_track: | |||
91 | 88 | ||
92 | while (pktcnt < wfx.numpackets) | 89 | while (pktcnt < wfx.numpackets) |
93 | { | 90 | { |
94 | ci->yield(); | 91 | enum codec_command_action action = ci->get_command(¶m); |
95 | if (ci->stop_codec || ci->new_track) { | 92 | |
96 | goto done; | 93 | if (action == CODEC_ACTION_HALT) |
97 | } | 94 | break; |
98 | |||
99 | /* Deal with any pending seek requests */ | ||
100 | if (ci->seek_time){ | ||
101 | 95 | ||
102 | if (ci->seek_time == 1) { | 96 | /* Deal with any pending seek requests */ |
97 | if (action == CODEC_ACTION_SEEK_TIME) { | ||
98 | if (param == 0) { | ||
99 | ci->set_elapsed(0); | ||
103 | ci->seek_complete(); | 100 | ci->seek_complete(); |
104 | goto restart_track; /* Pretend you never saw this... */ | 101 | goto restart_track; /* Pretend you never saw this... */ |
105 | } | 102 | } |
106 | 103 | ||
107 | elapsedtime = asf_seek(ci->seek_time, &wfx); | 104 | elapsedtime = asf_seek(param, &wfx); |
108 | if (elapsedtime < 1){ | 105 | if (elapsedtime < 1){ |
106 | ci->set_elapsed(0); | ||
109 | ci->seek_complete(); | 107 | ci->seek_complete(); |
110 | goto next_track; | 108 | break; |
111 | } | 109 | } |
112 | 110 | ||
113 | ci->set_elapsed(elapsedtime); | 111 | ci->set_elapsed(elapsedtime); |
@@ -117,8 +115,8 @@ restart_track: | |||
117 | res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx); | 115 | res = asf_read_packet(&audiobuf, &audiobufsize, &packetlength, &wfx); |
118 | 116 | ||
119 | if (res < 0) { | 117 | if (res < 0) { |
120 | LOGF("(WMA PRO) Warning: asf_read_packet returned %d", res); | 118 | LOGF("(WMA PRO) Warning: asf_read_packet returned %d", res); |
121 | goto done; | 119 | return CODEC_ERROR; |
122 | } else { | 120 | } else { |
123 | data = audiobuf; | 121 | data = audiobuf; |
124 | size = audiobufsize; | 122 | size = audiobufsize; |
@@ -132,7 +130,7 @@ restart_track: | |||
132 | res = decode_packet(&wfx, dec, &outlen, data, size); | 130 | res = decode_packet(&wfx, dec, &outlen, data, size); |
133 | if(res < 0) { | 131 | if(res < 0) { |
134 | LOGF("(WMA PRO) Error: decode_packet returned %d", res); | 132 | LOGF("(WMA PRO) Error: decode_packet returned %d", res); |
135 | goto done; | 133 | return CODEC_ERROR; |
136 | } | 134 | } |
137 | data += res; | 135 | data += res; |
138 | size -= res; | 136 | size -= res; |
@@ -152,10 +150,6 @@ restart_track: | |||
152 | ci->advance_buffer(packetlength); | 150 | ci->advance_buffer(packetlength); |
153 | } | 151 | } |
154 | 152 | ||
155 | done: | 153 | return CODEC_OK; |
156 | if (ci->request_next_track()) | ||
157 | goto next_track; | ||
158 | |||
159 | return retval; | ||
160 | } | 154 | } |
161 | 155 | ||
diff --git a/apps/codecs/wmavoice.c b/apps/codecs/wmavoice.c index ddf66828f1..64c8cd1692 100644 --- a/apps/codecs/wmavoice.c +++ b/apps/codecs/wmavoice.c | |||
@@ -52,10 +52,20 @@ static void init_codec_ctx(AVCodecContext *avctx, asf_waveformatex_t *wfx) | |||
52 | } | 52 | } |
53 | 53 | ||
54 | /* this is the codec entry point */ | 54 | /* this is the codec entry point */ |
55 | enum codec_status codec_main(void) | 55 | enum codec_status codec_main(enum codec_entry_call_reason reason) |
56 | { | ||
57 | if (reason == CODEC_LOAD) { | ||
58 | /* Generic codec initialisation */ | ||
59 | ci->configure(DSP_SET_SAMPLE_DEPTH, 31); | ||
60 | } | ||
61 | |||
62 | return CODEC_OK; | ||
63 | } | ||
64 | |||
65 | /* this is called for each file to process */ | ||
66 | enum codec_status codec_run(void) | ||
56 | { | 67 | { |
57 | uint32_t elapsedtime; | 68 | uint32_t elapsedtime; |
58 | int retval; | ||
59 | asf_waveformatex_t wfx; /* Holds the stream properties */ | 69 | asf_waveformatex_t wfx; /* Holds the stream properties */ |
60 | size_t resume_offset; | 70 | size_t resume_offset; |
61 | int res; /* Return values from asf_read_packet() and decode_packet() */ | 71 | int res; /* Return values from asf_read_packet() and decode_packet() */ |
@@ -64,27 +74,14 @@ enum codec_status codec_main(void) | |||
64 | int packetlength = 0; /* Logical packet size (minus the header size) */ | 74 | int packetlength = 0; /* Logical packet size (minus the header size) */ |
65 | int outlen = 0; /* Number of bytes written to the output buffer */ | 75 | int outlen = 0; /* Number of bytes written to the output buffer */ |
66 | int pktcnt = 0; /* Count of the packets played */ | 76 | int pktcnt = 0; /* Count of the packets played */ |
67 | 77 | intptr_t param; | |
68 | /* Generic codec initialisation */ | ||
69 | ci->configure(DSP_SET_SAMPLE_DEPTH, 31); | ||
70 | |||
71 | |||
72 | next_track: | ||
73 | retval = CODEC_OK; | ||
74 | |||
75 | /* Wait for the metadata to be read */ | ||
76 | if (codec_wait_taginfo() != 0) | ||
77 | goto done; | ||
78 | 78 | ||
79 | /* Remember the resume position */ | 79 | /* Remember the resume position */ |
80 | resume_offset = ci->id3->offset; | 80 | resume_offset = ci->id3->offset; |
81 | restart_track: | 81 | restart_track: |
82 | retval = CODEC_OK; | ||
83 | |||
84 | if (codec_init()) { | 82 | if (codec_init()) { |
85 | LOGF("(WMA Voice) Error: Error initialising codec\n"); | 83 | LOGF("(WMA Voice) Error: Error initialising codec\n"); |
86 | retval = CODEC_ERROR; | 84 | return CODEC_ERROR; |
87 | goto done; | ||
88 | } | 85 | } |
89 | 86 | ||
90 | /* Copy the format metadata we've stored in the id3 TOC field. This | 87 | /* Copy the format metadata we've stored in the id3 TOC field. This |
@@ -97,14 +94,15 @@ restart_track: | |||
97 | ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ? | 94 | ci->configure(DSP_SET_STEREO_MODE, wfx.channels == 1 ? |
98 | STEREO_MONO : STEREO_INTERLEAVED); | 95 | STEREO_MONO : STEREO_INTERLEAVED); |
99 | codec_set_replaygain(ci->id3); | 96 | codec_set_replaygain(ci->id3); |
97 | |||
98 | ci->seek_buffer(0); | ||
100 | 99 | ||
101 | /* Initialise the AVCodecContext */ | 100 | /* Initialise the AVCodecContext */ |
102 | init_codec_ctx(&avctx, &wfx); | 101 | init_codec_ctx(&avctx, &wfx); |
103 | 102 | ||
104 | if (wmavoice_decode_init(&avctx) < 0) { | 103 | if (wmavoice_decode_init(&avctx) < 0) { |
105 | LOGF("(WMA Voice) Error: Unsupported or corrupt file\n"); | 104 | LOGF("(WMA Voice) Error: Unsupported or corrupt file\n"); |
106 | retval = CODEC_ERROR; | 105 | return CODEC_ERROR; |
107 | goto done; | ||
108 | } | 106 | } |
109 | 107 | ||
110 | /* Now advance the file position to the first frame */ | 108 | /* Now advance the file position to the first frame */ |
@@ -117,21 +115,24 @@ restart_track: | |||
117 | 115 | ||
118 | while (pktcnt < wfx.numpackets) | 116 | while (pktcnt < wfx.numpackets) |
119 | { | 117 | { |
120 | ci->yield(); | 118 | enum codec_command_action action = ci->get_command(¶m); |
121 | if (ci->stop_codec || ci->new_track) { | 119 | |
122 | goto done; | 120 | if (action == CODEC_ACTION_HALT) |
123 | } | 121 | break; |
124 | 122 | ||
125 | /* Deal with any pending seek requests */ | 123 | /* Deal with any pending seek requests */ |
126 | if (ci->seek_time){ | 124 | if (action == CODEC_ACTION_SEEK_TIME) { |
125 | ci->set_elapsed(param); | ||
127 | 126 | ||
128 | if (ci->seek_time == 1) { | 127 | if (param == 0) { |
128 | ci->set_elapsed(0); | ||
129 | ci->seek_complete(); | 129 | ci->seek_complete(); |
130 | goto restart_track; /* Pretend you never saw this... */ | 130 | goto restart_track; /* Pretend you never saw this... */ |
131 | } | 131 | } |
132 | 132 | ||
133 | elapsedtime = asf_seek(ci->seek_time, &wfx); | 133 | elapsedtime = asf_seek(param, &wfx); |
134 | if (elapsedtime < 1){ | 134 | if (elapsedtime < 1){ |
135 | ci->set_elapsed(0); | ||
135 | ci->seek_complete(); | 136 | ci->seek_complete(); |
136 | goto next_track; | 137 | goto next_track; |
137 | } | 138 | } |
@@ -145,7 +146,7 @@ new_packet: | |||
145 | 146 | ||
146 | if (res < 0) { | 147 | if (res < 0) { |
147 | LOGF("(WMA Voice) read_packet error %d\n",res); | 148 | LOGF("(WMA Voice) read_packet error %d\n",res); |
148 | goto done; | 149 | return CODEC_ERROR; |
149 | } else { | 150 | } else { |
150 | avpkt.data = audiobuf; | 151 | avpkt.data = audiobuf; |
151 | avpkt.size = audiobufsize; | 152 | avpkt.size = audiobufsize; |
@@ -165,8 +166,9 @@ new_packet: | |||
165 | ci->advance_buffer(packetlength); | 166 | ci->advance_buffer(packetlength); |
166 | goto new_packet; | 167 | goto new_packet; |
167 | } | 168 | } |
168 | else | 169 | else { |
169 | goto done; | 170 | return CODEC_ERROR; |
171 | } | ||
170 | } | 172 | } |
171 | avpkt.data += res; | 173 | avpkt.data += res; |
172 | avpkt.size -= res; | 174 | avpkt.size -= res; |
@@ -186,10 +188,6 @@ new_packet: | |||
186 | ci->advance_buffer(packetlength); | 188 | ci->advance_buffer(packetlength); |
187 | } | 189 | } |
188 | 190 | ||
189 | done: | 191 | return CODEC_OK; |
190 | if (ci->request_next_track()) | ||
191 | goto next_track; | ||
192 | |||
193 | return retval; | ||
194 | } | 192 | } |
195 | 193 | ||
diff --git a/apps/gui/wps.c b/apps/gui/wps.c index c33268e6bd..069df09c36 100644 --- a/apps/gui/wps.c +++ b/apps/gui/wps.c | |||
@@ -219,10 +219,10 @@ static int skintouch_to_wps(struct wps_data *data) | |||
219 | #endif | 219 | #endif |
220 | case ACTION_TOUCH_SCROLLBAR: | 220 | case ACTION_TOUCH_SCROLLBAR: |
221 | skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/100; | 221 | skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/100; |
222 | if (!skin_get_global_state()->paused) | ||
223 | #if (CONFIG_CODEC == SWCODEC) | 222 | #if (CONFIG_CODEC == SWCODEC) |
224 | audio_pre_ff_rewind(); | 223 | audio_pre_ff_rewind(); |
225 | #else | 224 | #else |
225 | if (!skin_get_global_state()->paused) | ||
226 | audio_pause(); | 226 | audio_pause(); |
227 | #endif | 227 | #endif |
228 | audio_ff_rewind(skin_get_global_state()->id3->elapsed); | 228 | audio_ff_rewind(skin_get_global_state()->id3->elapsed); |
@@ -300,10 +300,10 @@ bool ffwd_rew(int button) | |||
300 | if ( (audio_status() & AUDIO_STATUS_PLAY) && | 300 | if ( (audio_status() & AUDIO_STATUS_PLAY) && |
301 | skin_get_global_state()->id3 && skin_get_global_state()->id3->length ) | 301 | skin_get_global_state()->id3 && skin_get_global_state()->id3->length ) |
302 | { | 302 | { |
303 | if (!skin_get_global_state()->paused) | ||
304 | #if (CONFIG_CODEC == SWCODEC) | 303 | #if (CONFIG_CODEC == SWCODEC) |
305 | audio_pre_ff_rewind(); | 304 | audio_pre_ff_rewind(); |
306 | #else | 305 | #else |
306 | if (!skin_get_global_state()->paused) | ||
307 | audio_pause(); | 307 | audio_pause(); |
308 | #endif | 308 | #endif |
309 | #if CONFIG_KEYPAD == PLAYER_PAD | 309 | #if CONFIG_KEYPAD == PLAYER_PAD |
@@ -472,10 +472,10 @@ static void prev_track(unsigned long skip_thresh) | |||
472 | return; | 472 | return; |
473 | } | 473 | } |
474 | 474 | ||
475 | if (!state->paused) | ||
476 | #if (CONFIG_CODEC == SWCODEC) | 475 | #if (CONFIG_CODEC == SWCODEC) |
477 | audio_pre_ff_rewind(); | 476 | audio_pre_ff_rewind(); |
478 | #else | 477 | #else |
478 | if (!state->paused) | ||
479 | audio_pause(); | 479 | audio_pause(); |
480 | #endif | 480 | #endif |
481 | 481 | ||
@@ -554,16 +554,20 @@ static void play_hop(int direction) | |||
554 | { | 554 | { |
555 | elapsed += step * direction; | 555 | elapsed += step * direction; |
556 | } | 556 | } |
557 | if((audio_status() & AUDIO_STATUS_PLAY) && !state->paused) | 557 | if(audio_status() & AUDIO_STATUS_PLAY) |
558 | { | 558 | { |
559 | #if (CONFIG_CODEC == SWCODEC) | 559 | #if (CONFIG_CODEC == SWCODEC) |
560 | audio_pre_ff_rewind(); | 560 | audio_pre_ff_rewind(); |
561 | #else | 561 | #else |
562 | audio_pause(); | 562 | if (!state->paused) |
563 | audio_pause(); | ||
563 | #endif | 564 | #endif |
564 | } | 565 | } |
566 | |||
567 | #if (CONFIG_CODEC == SWCODEC) | ||
568 | audio_ff_rewind(elapsed); | ||
569 | #else | ||
565 | audio_ff_rewind(state->id3->elapsed = elapsed); | 570 | audio_ff_rewind(state->id3->elapsed = elapsed); |
566 | #if (CONFIG_CODEC != SWCODEC) | ||
567 | if (!state->paused) | 571 | if (!state->paused) |
568 | audio_resume(); | 572 | audio_resume(); |
569 | #endif | 573 | #endif |
@@ -849,10 +853,10 @@ long gui_wps_show(void) | |||
849 | { | 853 | { |
850 | if (state->id3->cuesheet) | 854 | if (state->id3->cuesheet) |
851 | { | 855 | { |
852 | if (!state->paused) | ||
853 | #if (CONFIG_CODEC == SWCODEC) | 856 | #if (CONFIG_CODEC == SWCODEC) |
854 | audio_pre_ff_rewind(); | 857 | audio_pre_ff_rewind(); |
855 | #else | 858 | #else |
859 | if (!state->paused) | ||
856 | audio_pause(); | 860 | audio_pause(); |
857 | #endif | 861 | #endif |
858 | audio_ff_rewind(0); | 862 | audio_ff_rewind(0); |
@@ -1146,6 +1150,17 @@ static void nextid3available_callback(void* param) | |||
1146 | skin_request_full_update(WPS); | 1150 | skin_request_full_update(WPS); |
1147 | } | 1151 | } |
1148 | 1152 | ||
1153 | #ifdef AUDIO_FAST_SKIP_PREVIEW | ||
1154 | /* this is called on the audio_skip caller thread */ | ||
1155 | static void track_skip_callback(void *param) | ||
1156 | { | ||
1157 | struct wps_state *state = skin_get_global_state(); | ||
1158 | state->id3 = audio_current_track(); | ||
1159 | state->nid3 = audio_next_track(); | ||
1160 | skin_request_full_update(WPS); | ||
1161 | (void)param; | ||
1162 | } | ||
1163 | #endif /* AUDIO_FAST_SKIP_PREVIEW */ | ||
1149 | 1164 | ||
1150 | static void wps_state_init(void) | 1165 | static void wps_state_init(void) |
1151 | { | 1166 | { |
@@ -1167,6 +1182,9 @@ static void wps_state_init(void) | |||
1167 | /* add the WPS track event callbacks */ | 1182 | /* add the WPS track event callbacks */ |
1168 | add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback); | 1183 | add_event(PLAYBACK_EVENT_TRACK_CHANGE, false, track_changed_callback); |
1169 | add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_callback); | 1184 | add_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, false, nextid3available_callback); |
1185 | #ifdef AUDIO_FAST_SKIP_PREVIEW | ||
1186 | add_event(PLAYBACK_EVENT_TRACK_SKIP, false, track_skip_callback); | ||
1187 | #endif | ||
1170 | } | 1188 | } |
1171 | 1189 | ||
1172 | 1190 | ||
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c index 9ed0bc5651..4153c1c776 100644 --- a/apps/menus/playback_menu.c +++ b/apps/menus/playback_menu.c | |||
@@ -159,9 +159,13 @@ static int cuesheet_callback(int action,const struct menu_item_ex *this_item) | |||
159 | switch (action) | 159 | switch (action) |
160 | { | 160 | { |
161 | case ACTION_EXIT_MENUITEM: /* on exit */ | 161 | case ACTION_EXIT_MENUITEM: /* on exit */ |
162 | #if CONFIG_CODEC == SWCODEC | ||
163 | audio_set_cuesheet(global_settings.cuesheet); | ||
164 | #else | ||
162 | if (global_settings.cuesheet) | 165 | if (global_settings.cuesheet) |
163 | splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); | 166 | splash(HZ*2, ID2P(LANG_PLEASE_REBOOT)); |
164 | break; | 167 | break; |
168 | #endif | ||
165 | } | 169 | } |
166 | return action; | 170 | return action; |
167 | } | 171 | } |
diff --git a/apps/metadata.c b/apps/metadata.c index 46b9482bc7..e88603721b 100644 --- a/apps/metadata.c +++ b/apps/metadata.c | |||
@@ -33,7 +33,7 @@ | |||
33 | 33 | ||
34 | #if CONFIG_CODEC == SWCODEC | 34 | #if CONFIG_CODEC == SWCODEC |
35 | 35 | ||
36 | /* For trailing tag stripping */ | 36 | /* For trailing tag stripping and base audio data types */ |
37 | #include "buffering.h" | 37 | #include "buffering.h" |
38 | 38 | ||
39 | #include "metadata/metadata_common.h" | 39 | #include "metadata/metadata_common.h" |
@@ -239,6 +239,94 @@ const int afmt_rec_format[AFMT_NUM_CODECS] = | |||
239 | }; | 239 | }; |
240 | #endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */ | 240 | #endif /* CONFIG_CODEC == SWCODEC && defined (HAVE_RECORDING) */ |
241 | 241 | ||
242 | #if CONFIG_CODEC == SWCODEC | ||
243 | /* Get the canonical AFMT type */ | ||
244 | int get_audio_base_codec_type(int type) | ||
245 | { | ||
246 | int base_type = type; | ||
247 | switch (type) { | ||
248 | case AFMT_MPA_L1: | ||
249 | case AFMT_MPA_L2: | ||
250 | case AFMT_MPA_L3: | ||
251 | base_type = AFMT_MPA_L3; | ||
252 | break; | ||
253 | case AFMT_MPC_SV7: | ||
254 | case AFMT_MPC_SV8: | ||
255 | base_type = AFMT_MPC_SV7; | ||
256 | break; | ||
257 | case AFMT_MP4_AAC: | ||
258 | case AFMT_MP4_AAC_HE: | ||
259 | base_type = AFMT_MP4_AAC; | ||
260 | break; | ||
261 | case AFMT_SAP: | ||
262 | case AFMT_CMC: | ||
263 | case AFMT_CM3: | ||
264 | case AFMT_CMR: | ||
265 | case AFMT_CMS: | ||
266 | case AFMT_DMC: | ||
267 | case AFMT_DLT: | ||
268 | case AFMT_MPT: | ||
269 | case AFMT_MPD: | ||
270 | case AFMT_RMT: | ||
271 | case AFMT_TMC: | ||
272 | case AFMT_TM8: | ||
273 | case AFMT_TM2: | ||
274 | base_type = AFMT_SAP; | ||
275 | break; | ||
276 | default: | ||
277 | break; | ||
278 | } | ||
279 | |||
280 | return base_type; | ||
281 | } | ||
282 | |||
283 | /* Get the basic audio type */ | ||
284 | enum data_type get_audio_base_data_type(int afmt) | ||
285 | { | ||
286 | if ((unsigned)afmt >= AFMT_NUM_CODECS) | ||
287 | return TYPE_UNKNOWN; | ||
288 | |||
289 | switch (get_audio_base_codec_type(afmt)) | ||
290 | { | ||
291 | case AFMT_NSF: | ||
292 | case AFMT_SPC: | ||
293 | case AFMT_SID: | ||
294 | case AFMT_MOD: | ||
295 | case AFMT_SAP: | ||
296 | /* Type must be allocated and loaded in its entirety onto | ||
297 | the buffer */ | ||
298 | return TYPE_ATOMIC_AUDIO; | ||
299 | |||
300 | default: | ||
301 | /* Assume type may be loaded and discarded incrementally */ | ||
302 | return TYPE_PACKET_AUDIO; | ||
303 | |||
304 | case AFMT_UNKNOWN: | ||
305 | /* Have no idea at all */ | ||
306 | return TYPE_UNKNOWN; | ||
307 | } | ||
308 | } | ||
309 | |||
310 | /* Is the format allowed to buffer starting at some offset other than 0 | ||
311 | or first frame only for resume purposes? */ | ||
312 | bool format_buffers_with_offset(int afmt) | ||
313 | { | ||
314 | switch (afmt) | ||
315 | { | ||
316 | case AFMT_MPA_L1: | ||
317 | case AFMT_MPA_L2: | ||
318 | case AFMT_MPA_L3: | ||
319 | case AFMT_WAVPACK: | ||
320 | /* Format may be loaded at the first needed frame */ | ||
321 | return true; | ||
322 | default: | ||
323 | /* Format must be loaded from the beginning of the file | ||
324 | (does not imply 'atomic', while 'atomic' implies 'no offset') */ | ||
325 | return false; | ||
326 | } | ||
327 | } | ||
328 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
329 | |||
242 | 330 | ||
243 | /* Simple file type probing by looking at the filename extension. */ | 331 | /* Simple file type probing by looking at the filename extension. */ |
244 | unsigned int probe_file_format(const char *filename) | 332 | unsigned int probe_file_format(const char *filename) |
@@ -313,7 +401,7 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname) | |||
313 | } | 401 | } |
314 | 402 | ||
315 | /* Clear the mp3entry to avoid having bogus pointers appear */ | 403 | /* Clear the mp3entry to avoid having bogus pointers appear */ |
316 | memset(id3, 0, sizeof(struct mp3entry)); | 404 | wipe_mp3entry(id3); |
317 | 405 | ||
318 | /* Take our best guess at the codec type based on file extension */ | 406 | /* Take our best guess at the codec type based on file extension */ |
319 | id3->codectype = probe_file_format(trackname); | 407 | id3->codectype = probe_file_format(trackname); |
@@ -414,6 +502,44 @@ void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig) | |||
414 | adjust_mp3entry(dest, dest, orig); | 502 | adjust_mp3entry(dest, dest, orig); |
415 | } | 503 | } |
416 | 504 | ||
505 | /* A shortcut to simplify the common task of clearing the struct */ | ||
506 | void wipe_mp3entry(struct mp3entry *id3) | ||
507 | { | ||
508 | memset(id3, 0, sizeof (struct mp3entry)); | ||
509 | } | ||
510 | |||
511 | #if CONFIG_CODEC == SWCODEC | ||
512 | /* Glean what is possible from the filename alone - does not parse metadata */ | ||
513 | void fill_metadata_from_path(struct mp3entry *id3, const char *trackname) | ||
514 | { | ||
515 | char *p; | ||
516 | |||
517 | /* Clear the mp3entry to avoid having bogus pointers appear */ | ||
518 | wipe_mp3entry(id3); | ||
519 | |||
520 | /* Find the filename portion of the path */ | ||
521 | p = strrchr(trackname, '/'); | ||
522 | strlcpy(id3->id3v2buf, p ? ++p : id3->path, ID3V2_BUF_SIZE); | ||
523 | |||
524 | /* Get the format from the extension and trim it off */ | ||
525 | p = strrchr(id3->id3v2buf, '.'); | ||
526 | if (p) | ||
527 | { | ||
528 | /* Might be wrong for container formats - should we bother? */ | ||
529 | id3->codectype = probe_file_format(p); | ||
530 | |||
531 | if (id3->codectype != AFMT_UNKNOWN) | ||
532 | *p = '\0'; | ||
533 | } | ||
534 | |||
535 | /* Set the filename as the title */ | ||
536 | id3->title = id3->id3v2buf; | ||
537 | |||
538 | /* Copy the path info */ | ||
539 | strlcpy(id3->path, trackname, sizeof (id3->path)); | ||
540 | } | ||
541 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
542 | |||
417 | #ifndef __PCTOOL__ | 543 | #ifndef __PCTOOL__ |
418 | #ifdef HAVE_TAGCACHE | 544 | #ifdef HAVE_TAGCACHE |
419 | #if CONFIG_CODEC == SWCODEC | 545 | #if CONFIG_CODEC == SWCODEC |
diff --git a/apps/metadata.h b/apps/metadata.h index c22c1b3ecf..b268a3d474 100644 --- a/apps/metadata.h +++ b/apps/metadata.h | |||
@@ -266,9 +266,7 @@ struct mp3entry { | |||
266 | 266 | ||
267 | /* resume related */ | 267 | /* resume related */ |
268 | unsigned long offset; /* bytes played */ | 268 | unsigned long offset; /* bytes played */ |
269 | #if CONFIG_CODEC != SWCODEC | ||
270 | int index; /* playlist index */ | 269 | int index; /* playlist index */ |
271 | #endif | ||
272 | 270 | ||
273 | #ifdef HAVE_TAGCACHE | 271 | #ifdef HAVE_TAGCACHE |
274 | unsigned char autoresumable; /* caches result of autoresumable() */ | 272 | unsigned char autoresumable; /* caches result of autoresumable() */ |
@@ -309,9 +307,14 @@ bool get_metadata(struct mp3entry* id3, int fd, const char* trackname); | |||
309 | bool mp3info(struct mp3entry *entry, const char *filename); | 307 | bool mp3info(struct mp3entry *entry, const char *filename); |
310 | void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig); | 308 | void adjust_mp3entry(struct mp3entry *entry, void *dest, const void *orig); |
311 | void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig); | 309 | void copy_mp3entry(struct mp3entry *dest, const struct mp3entry *orig); |
310 | void wipe_mp3entry(struct mp3entry *id3); | ||
312 | 311 | ||
313 | #if CONFIG_CODEC == SWCODEC | 312 | #if CONFIG_CODEC == SWCODEC |
313 | void fill_metadata_from_path(struct mp3entry *id3, const char *trackname); | ||
314 | int get_audio_base_codec_type(int type); | ||
314 | void strip_tags(int handle_id); | 315 | void strip_tags(int handle_id); |
316 | enum data_type get_audio_base_data_type(int afmt); | ||
317 | bool format_buffers_with_offset(int afmt); | ||
315 | #endif | 318 | #endif |
316 | 319 | ||
317 | #ifdef HAVE_TAGCACHE | 320 | #ifdef HAVE_TAGCACHE |
diff --git a/apps/metadata/nsf.c b/apps/metadata/nsf.c index abb4e6fd80..9207a14048 100644 --- a/apps/metadata/nsf.c +++ b/apps/metadata/nsf.c | |||
@@ -40,6 +40,9 @@ bool get_nsf_metadata(int fd, struct mp3entry* id3) | |||
40 | 40 | ||
41 | p = id3->id3v2buf; | 41 | p = id3->id3v2buf; |
42 | 42 | ||
43 | /* Length */ | ||
44 | id3->length = buf[6]*1000; | ||
45 | |||
43 | /* Title */ | 46 | /* Title */ |
44 | memcpy(p, &buf[14], 32); | 47 | memcpy(p, &buf[14], 32); |
45 | id3->title = p; | 48 | id3->title = p; |
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c index f548b156b3..c7baad08e4 100644 --- a/apps/pcmbuf.c +++ b/apps/pcmbuf.c | |||
@@ -86,6 +86,8 @@ static size_t pcmbuffer_pos IDATA_ATTR; | |||
86 | /* Amount pcmbuffer_pos will be increased.*/ | 86 | /* Amount pcmbuffer_pos will be increased.*/ |
87 | static size_t pcmbuffer_fillpos IDATA_ATTR; | 87 | static size_t pcmbuffer_fillpos IDATA_ATTR; |
88 | 88 | ||
89 | static struct chunkdesc *first_desc; | ||
90 | |||
89 | /* Gapless playback */ | 91 | /* Gapless playback */ |
90 | static bool track_transition IDATA_ATTR; | 92 | static bool track_transition IDATA_ATTR; |
91 | 93 | ||
@@ -144,6 +146,11 @@ static void write_to_crossfade(size_t length); | |||
144 | static void pcmbuf_finish_crossfade_enable(void); | 146 | static void pcmbuf_finish_crossfade_enable(void); |
145 | #endif | 147 | #endif |
146 | 148 | ||
149 | /* Callbacks into playback.c */ | ||
150 | extern void audio_pcmbuf_position_callback(unsigned int time); | ||
151 | extern void audio_pcmbuf_track_change(bool pcmbuf); | ||
152 | extern bool audio_pcmbuf_may_play(void); | ||
153 | |||
147 | 154 | ||
148 | /**************************************/ | 155 | /**************************************/ |
149 | 156 | ||
@@ -153,9 +160,8 @@ static void pcmbuf_finish_crossfade_enable(void); | |||
153 | #ifndef SIMULATOR | 160 | #ifndef SIMULATOR |
154 | #undef DESC_DEBUG | 161 | #undef DESC_DEBUG |
155 | #endif | 162 | #endif |
163 | |||
156 | #ifdef DESC_DEBUG | 164 | #ifdef DESC_DEBUG |
157 | static struct chunkdesc *first_desc; | ||
158 | static bool show_desc_in_use = false; | ||
159 | #define DISPLAY_DESC(caller) while(!show_desc(caller)) | 165 | #define DISPLAY_DESC(caller) while(!show_desc(caller)) |
160 | #define DESC_IDX(desc) (desc ? desc - first_desc : -1) | 166 | #define DESC_IDX(desc) (desc ? desc - first_desc : -1) |
161 | #define SHOW_DESC(desc) if(DESC_IDX(desc)==-1) DEBUGF("--"); \ | 167 | #define SHOW_DESC(desc) if(DESC_IDX(desc)==-1) DEBUGF("--"); \ |
@@ -231,6 +237,7 @@ static void commit_chunk(bool flush_next_time) | |||
231 | /* Flush! Discard all data after the currently playing chunk, | 237 | /* Flush! Discard all data after the currently playing chunk, |
232 | and make the current chunk play next */ | 238 | and make the current chunk play next */ |
233 | logf("commit_chunk: flush"); | 239 | logf("commit_chunk: flush"); |
240 | pcm_play_lock(); | ||
234 | write_end_chunk->link = read_chunk->link; | 241 | write_end_chunk->link = read_chunk->link; |
235 | read_chunk->link = pcmbuf_current; | 242 | read_chunk->link = pcmbuf_current; |
236 | while (write_end_chunk->link) | 243 | while (write_end_chunk->link) |
@@ -238,6 +245,9 @@ static void commit_chunk(bool flush_next_time) | |||
238 | write_end_chunk = write_end_chunk->link; | 245 | write_end_chunk = write_end_chunk->link; |
239 | pcmbuf_unplayed_bytes -= write_end_chunk->size; | 246 | pcmbuf_unplayed_bytes -= write_end_chunk->size; |
240 | } | 247 | } |
248 | |||
249 | read_chunk->end_of_track = track_transition; | ||
250 | pcm_play_unlock(); | ||
241 | } | 251 | } |
242 | /* If there is already a read buffer setup, add to it */ | 252 | /* If there is already a read buffer setup, add to it */ |
243 | else | 253 | else |
@@ -248,7 +258,7 @@ static void commit_chunk(bool flush_next_time) | |||
248 | /* Otherwise create the buffer */ | 258 | /* Otherwise create the buffer */ |
249 | read_chunk = pcmbuf_current; | 259 | read_chunk = pcmbuf_current; |
250 | } | 260 | } |
251 | 261 | ||
252 | /* If flush_next_time is true, then the current chunk will be thrown out | 262 | /* If flush_next_time is true, then the current chunk will be thrown out |
253 | * and the next chunk to be committed will be the next to be played. | 263 | * and the next chunk to be committed will be the next to be played. |
254 | * This is used to empty the PCM buffer for a track change. */ | 264 | * This is used to empty the PCM buffer for a track change. */ |
@@ -354,7 +364,7 @@ static bool prepare_insert(size_t length) | |||
354 | #endif | 364 | #endif |
355 | { | 365 | { |
356 | logf("pcm starting"); | 366 | logf("pcm starting"); |
357 | if (!(audio_status() & AUDIO_STATUS_PAUSE)) | 367 | if (audio_pcmbuf_may_play()) |
358 | pcmbuf_play_start(); | 368 | pcmbuf_play_start(); |
359 | } | 369 | } |
360 | } | 370 | } |
@@ -373,8 +383,12 @@ void *pcmbuf_request_buffer(int *count) | |||
373 | /* crossfade has begun, put the new track samples in fadebuf */ | 383 | /* crossfade has begun, put the new track samples in fadebuf */ |
374 | if (crossfade_active) | 384 | if (crossfade_active) |
375 | { | 385 | { |
376 | *count = MIN(*count, CROSSFADE_BUFSIZE/4); | 386 | int cnt = MIN(*count, CROSSFADE_BUFSIZE/4); |
377 | return fadebuf; | 387 | if (prepare_insert(cnt << 2)) |
388 | { | ||
389 | *count = cnt; | ||
390 | return fadebuf; | ||
391 | } | ||
378 | } | 392 | } |
379 | else | 393 | else |
380 | #endif | 394 | #endif |
@@ -421,9 +435,7 @@ void pcmbuf_write_complete(int count) | |||
421 | 435 | ||
422 | static inline void init_pcmbuffers(void) | 436 | static inline void init_pcmbuffers(void) |
423 | { | 437 | { |
424 | #ifdef DESC_DEBUG | ||
425 | first_desc = write_chunk; | 438 | first_desc = write_chunk; |
426 | #endif | ||
427 | struct chunkdesc *next = write_chunk; | 439 | struct chunkdesc *next = write_chunk; |
428 | next++; | 440 | next++; |
429 | write_end_chunk = write_chunk; | 441 | write_end_chunk = write_chunk; |
@@ -494,19 +506,27 @@ void pcmbuf_monitor_track_change(bool monitor) | |||
494 | currently playing chunk. If not, cancel notification. */ | 506 | currently playing chunk. If not, cancel notification. */ |
495 | track_transition = monitor; | 507 | track_transition = monitor; |
496 | read_end_chunk->end_of_track = monitor; | 508 | read_end_chunk->end_of_track = monitor; |
509 | if (!monitor) | ||
510 | { | ||
511 | /* Clear all notifications */ | ||
512 | struct chunkdesc *desc = first_desc; | ||
513 | struct chunkdesc *end = desc + pcmbuf_descs(); | ||
514 | while (desc < end) | ||
515 | desc++->end_of_track = false; | ||
516 | } | ||
497 | } | 517 | } |
498 | else | 518 | else |
499 | { | 519 | { |
500 | /* Post now if PCM stopped and last buffer was sent. */ | 520 | /* Post now if PCM stopped and last buffer was sent. */ |
501 | track_transition = false; | 521 | track_transition = false; |
502 | if (monitor) | 522 | if (monitor) |
503 | audio_post_track_change(false); | 523 | audio_pcmbuf_track_change(false); |
504 | } | 524 | } |
505 | 525 | ||
506 | pcm_play_unlock(); | 526 | pcm_play_unlock(); |
507 | } | 527 | } |
508 | 528 | ||
509 | void pcmbuf_start_track_change(bool auto_skip) | 529 | bool pcmbuf_start_track_change(bool auto_skip) |
510 | { | 530 | { |
511 | bool crossfade = false; | 531 | bool crossfade = false; |
512 | #ifdef HAVE_CROSSFADE | 532 | #ifdef HAVE_CROSSFADE |
@@ -546,9 +566,6 @@ void pcmbuf_start_track_change(bool auto_skip) | |||
546 | 566 | ||
547 | /* Cancel any pending automatic gapless transition */ | 567 | /* Cancel any pending automatic gapless transition */ |
548 | pcmbuf_monitor_track_change(false); | 568 | pcmbuf_monitor_track_change(false); |
549 | |||
550 | /* Notify the wps that the track change starts now */ | ||
551 | audio_post_track_change(false); | ||
552 | 569 | ||
553 | /* Can't do two crossfades at once and, no fade if pcm is off now */ | 570 | /* Can't do two crossfades at once and, no fade if pcm is off now */ |
554 | if ( | 571 | if ( |
@@ -559,7 +576,8 @@ void pcmbuf_start_track_change(bool auto_skip) | |||
559 | { | 576 | { |
560 | pcmbuf_play_stop(); | 577 | pcmbuf_play_stop(); |
561 | pcm_play_unlock(); | 578 | pcm_play_unlock(); |
562 | return; | 579 | /* Notify playback that the track change starts now */ |
580 | return true; | ||
563 | } | 581 | } |
564 | 582 | ||
565 | /* Not enough data, or not crossfading, flush the old data instead */ | 583 | /* Not enough data, or not crossfading, flush the old data instead */ |
@@ -584,6 +602,9 @@ void pcmbuf_start_track_change(bool auto_skip) | |||
584 | /* Keep trigger outside the play lock or HW FIFO underruns can happen | 602 | /* Keep trigger outside the play lock or HW FIFO underruns can happen |
585 | since frequency scaling is *not* always fast */ | 603 | since frequency scaling is *not* always fast */ |
586 | trigger_cpu_boost(); | 604 | trigger_cpu_boost(); |
605 | |||
606 | /* Notify playback that the track change starts now */ | ||
607 | return true; | ||
587 | } | 608 | } |
588 | else /* automatic and not crossfading, so do gapless track change */ | 609 | else /* automatic and not crossfading, so do gapless track change */ |
589 | { | 610 | { |
@@ -593,6 +614,7 @@ void pcmbuf_start_track_change(bool auto_skip) | |||
593 | * as the last one in the track. */ | 614 | * as the last one in the track. */ |
594 | logf(" gapless track change"); | 615 | logf(" gapless track change"); |
595 | pcmbuf_monitor_track_change(true); | 616 | pcmbuf_monitor_track_change(true); |
617 | return false; | ||
596 | } | 618 | } |
597 | } | 619 | } |
598 | 620 | ||
@@ -623,7 +645,7 @@ static void pcmbuf_pcm_callback(unsigned char** start, size_t* size) | |||
623 | if (pcmbuf_current->end_of_track) | 645 | if (pcmbuf_current->end_of_track) |
624 | { | 646 | { |
625 | track_transition = false; | 647 | track_transition = false; |
626 | audio_post_track_change(true); | 648 | audio_pcmbuf_track_change(true); |
627 | } | 649 | } |
628 | 650 | ||
629 | /* Put the finished chunk back into circulation */ | 651 | /* Put the finished chunk back into circulation */ |
@@ -955,9 +977,6 @@ static void write_to_crossfade(size_t length) | |||
955 | return; | 977 | return; |
956 | } | 978 | } |
957 | 979 | ||
958 | /* Commit samples to the buffer */ | ||
959 | while (!prepare_insert(length)) | ||
960 | sleep(1); | ||
961 | while (length > 0) | 980 | while (length > 0) |
962 | { | 981 | { |
963 | COMMIT_IF_NEEDED; | 982 | COMMIT_IF_NEEDED; |
diff --git a/apps/pcmbuf.h b/apps/pcmbuf.h index 618b1babad..b7bf8c2b16 100644 --- a/apps/pcmbuf.h +++ b/apps/pcmbuf.h | |||
@@ -33,13 +33,21 @@ void pcmbuf_play_start(void); | |||
33 | void pcmbuf_play_stop(void); | 33 | void pcmbuf_play_stop(void); |
34 | void pcmbuf_pause(bool pause); | 34 | void pcmbuf_pause(bool pause); |
35 | void pcmbuf_monitor_track_change(bool monitor); | 35 | void pcmbuf_monitor_track_change(bool monitor); |
36 | void pcmbuf_start_track_change(bool manual_skip); | 36 | bool pcmbuf_start_track_change(bool manual_skip); |
37 | 37 | ||
38 | /* Crossfade */ | 38 | /* Crossfade */ |
39 | #ifdef HAVE_CROSSFADE | 39 | #ifdef HAVE_CROSSFADE |
40 | bool pcmbuf_is_crossfade_active(void); | 40 | bool pcmbuf_is_crossfade_active(void); |
41 | void pcmbuf_request_crossfade_enable(bool on_off); | 41 | void pcmbuf_request_crossfade_enable(bool on_off); |
42 | bool pcmbuf_is_same_size(void); | 42 | bool pcmbuf_is_same_size(void); |
43 | #else | ||
44 | /* Dummy functions with sensible returns */ | ||
45 | static inline bool pcmbuf_is_crossfade_active(void) | ||
46 | { return false; } | ||
47 | static inline void pcmbuf_request_crossfade_enable(bool on_off) | ||
48 | { return; (void)on_off; } | ||
49 | static inline bool pcmbuf_is_same_size(void) | ||
50 | { return true; } | ||
43 | #endif | 51 | #endif |
44 | 52 | ||
45 | /* Voice */ | 53 | /* Voice */ |
diff --git a/apps/playback.c b/apps/playback.c index 632fd05d3d..a369d15715 100644 --- a/apps/playback.c +++ b/apps/playback.c | |||
@@ -9,6 +9,7 @@ | |||
9 | * | 9 | * |
10 | * Copyright (C) 2005-2007 Miika Pekkarinen | 10 | * Copyright (C) 2005-2007 Miika Pekkarinen |
11 | * Copyright (C) 2007-2008 Nicolas Pennequin | 11 | * Copyright (C) 2007-2008 Nicolas Pennequin |
12 | * Copyright (C) 2011 Michael Sevakis | ||
12 | * | 13 | * |
13 | * This program is free software; you can redistribute it and/or | 14 | * This program is free software; you can redistribute it and/or |
14 | * modify it under the terms of the GNU General Public License | 15 | * modify it under the terms of the GNU General Public License |
@@ -19,53 +20,61 @@ | |||
19 | * KIND, either express or implied. | 20 | * KIND, either express or implied. |
20 | * | 21 | * |
21 | ****************************************************************************/ | 22 | ****************************************************************************/ |
22 | |||
23 | /* TODO: Pause should be handled in here, rather than PCMBUF so that voice can | ||
24 | * play whilst audio is paused */ | ||
25 | #include "config.h" | 23 | #include "config.h" |
26 | #include "system.h" | 24 | #include "system.h" |
27 | #include <string.h> | ||
28 | #include "playback.h" | ||
29 | #include "codec_thread.h" | ||
30 | #include "kernel.h" | 25 | #include "kernel.h" |
26 | #include "panic.h" | ||
27 | #include "buffer.h" | ||
28 | #include "sound.h" | ||
29 | #include "ata.h" | ||
30 | #include "usb.h" | ||
31 | #include "codecs.h" | 31 | #include "codecs.h" |
32 | #include "buffering.h" | 32 | #include "codec_thread.h" |
33 | #include "voice_thread.h" | 33 | #include "voice_thread.h" |
34 | #include "usb.h" | 34 | #include "metadata.h" |
35 | #include "ata.h" | 35 | #include "cuesheet.h" |
36 | #include "buffering.h" | ||
37 | #include "talk.h" | ||
36 | #include "playlist.h" | 38 | #include "playlist.h" |
39 | #include "abrepeat.h" | ||
37 | #include "pcmbuf.h" | 40 | #include "pcmbuf.h" |
38 | #include "buffer.h" | 41 | #include "playback.h" |
39 | #include "cuesheet.h" | 42 | |
40 | #ifdef HAVE_TAGCACHE | 43 | #ifdef HAVE_TAGCACHE |
41 | #include "tagcache.h" | 44 | #include "tagcache.h" |
42 | #endif | 45 | #endif |
46 | |||
47 | #ifdef AUDIO_HAVE_RECORDING | ||
48 | #include "pcm_record.h" | ||
49 | #endif | ||
50 | |||
43 | #ifdef HAVE_LCD_BITMAP | 51 | #ifdef HAVE_LCD_BITMAP |
44 | #ifdef HAVE_ALBUMART | 52 | #ifdef HAVE_ALBUMART |
45 | #include "albumart.h" | 53 | #include "albumart.h" |
46 | #endif | 54 | #endif |
47 | #endif | 55 | #endif |
48 | #include "sound.h" | ||
49 | #include "metadata.h" | ||
50 | #include "splash.h" | ||
51 | #include "talk.h" | ||
52 | #include "panic.h" | ||
53 | 56 | ||
54 | #ifdef HAVE_RECORDING | 57 | /* TODO: The audio thread really is doing multitasking of acting like a |
55 | #include "pcm_record.h" | 58 | consumer and producer of tracks. It may be advantageous to better |
56 | #endif | 59 | logically separate the two functions. I won't go that far just yet. */ |
57 | 60 | ||
61 | /* Internal support for voice playback */ | ||
58 | #define PLAYBACK_VOICE | 62 | #define PLAYBACK_VOICE |
59 | 63 | ||
60 | /* amount of guess-space to allow for codecs that must hunt and peck | 64 | #if CONFIG_PLATFORM & PLATFORM_NATIVE |
61 | * for their correct seeek target, 32k seems a good size */ | 65 | /* Application builds don't support direct code loading */ |
66 | #define HAVE_CODEC_BUFFERING | ||
67 | #endif | ||
68 | |||
69 | /* Amount of guess-space to allow for codecs that must hunt and peck | ||
70 | * for their correct seek target, 32k seems a good size */ | ||
62 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) | 71 | #define AUDIO_REBUFFER_GUESS_SIZE (1024*32) |
63 | 72 | ||
64 | /* Define LOGF_ENABLE to enable logf output in this file */ | 73 | /* Define LOGF_ENABLE to enable logf output in this file */ |
65 | /*#define LOGF_ENABLE*/ | 74 | /* #define LOGF_ENABLE */ |
66 | #include "logf.h" | 75 | #include "logf.h" |
67 | 76 | ||
68 | /* macros to enable logf for queues | 77 | /* Macros to enable logf for queues |
69 | logging on SYS_TIMEOUT can be disabled */ | 78 | logging on SYS_TIMEOUT can be disabled */ |
70 | #ifdef SIMULATOR | 79 | #ifdef SIMULATOR |
71 | /* Define this for logf output of all queuing except SYS_TIMEOUT */ | 80 | /* Define this for logf output of all queuing except SYS_TIMEOUT */ |
@@ -86,202 +95,292 @@ | |||
86 | #define LOGFQUEUE_SYS_TIMEOUT(...) | 95 | #define LOGFQUEUE_SYS_TIMEOUT(...) |
87 | #endif | 96 | #endif |
88 | 97 | ||
98 | /* Variables are commented with the threads that use them: | ||
99 | * A=audio, C=codec, O=other. A suffix of "-" indicates that the variable is | ||
100 | * read but not updated on that thread. Audio is the only user unless otherwise | ||
101 | * specified. | ||
102 | */ | ||
89 | 103 | ||
90 | static enum filling_state { | 104 | /** Miscellaneous **/ |
91 | STATE_IDLE, /* audio is stopped: nothing to do */ | 105 | bool audio_is_initialized = false; /* (A,O-) */ |
92 | STATE_FILLING, /* adding tracks to the buffer */ | 106 | extern struct codec_api ci; /* (A,C) */ |
93 | STATE_FULL, /* can't add any more tracks */ | 107 | |
94 | STATE_END_OF_PLAYLIST, /* all remaining tracks have been added */ | 108 | /** Possible arrangements of the main buffer **/ |
95 | STATE_FINISHED, /* all remaining tracks are fully buffered */ | 109 | static enum audio_buffer_state |
96 | STATE_ENDING, /* audio playback is ending */ | 110 | { |
97 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 111 | AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */ |
98 | STATE_USB, /* USB mode, ignore most messages */ | 112 | AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */ |
113 | AUDIOBUF_STATE_VOICED_ONLY = 1, /* voice-only */ | ||
114 | } buffer_state = AUDIOBUF_STATE_TRASHED; /* (A,O) */ | ||
115 | |||
116 | /** Main state control **/ | ||
117 | static bool ff_rw_mode SHAREDBSS_ATTR = false; /* Pre-ff-rewind mode (A,O-) */ | ||
118 | |||
119 | enum play_status | ||
120 | { | ||
121 | PLAY_STOPPED = 0, | ||
122 | PLAY_PLAYING = AUDIO_STATUS_PLAY, | ||
123 | PLAY_PAUSED = AUDIO_STATUS_PLAY | AUDIO_STATUS_PAUSE, | ||
124 | } play_status = PLAY_STOPPED; | ||
125 | |||
126 | /* Sizeable things that only need exist during playback and not when stopped */ | ||
127 | static struct audio_scratch_memory | ||
128 | { | ||
129 | struct mp3entry codec_id3; /* (A,C) */ | ||
130 | struct mp3entry unbuffered_id3; | ||
131 | struct cuesheet *curr_cue; /* Will follow this structure */ | ||
132 | } * audio_scratch_memory = NULL; | ||
133 | |||
134 | /* These are used to store the current, next and optionally the peek-ahead | ||
135 | * mp3entry's - this guarentees that the pointer returned by audio_current/ | ||
136 | * next_track will be valid for the full duration of the currently playing | ||
137 | * track */ | ||
138 | enum audio_id3_types | ||
139 | { | ||
140 | /* These are allocated statically */ | ||
141 | PLAYING_ID3 = 0, | ||
142 | NEXTTRACK_ID3, | ||
143 | #ifdef AUDIO_FAST_SKIP_PREVIEW | ||
144 | /* The real playing metadata must has to be protected since it contains | ||
145 | critical info for other features */ | ||
146 | PLAYING_PEEK_ID3, | ||
99 | #endif | 147 | #endif |
100 | } filling; | 148 | ID3_TYPE_NUM_STATIC, |
101 | 149 | /* These go in the scratch memory */ | |
102 | /* As defined in plugins/lib/xxx2wav.h */ | 150 | UNBUFFERED_ID3 = ID3_TYPE_NUM_STATIC, |
103 | #define GUARD_BUFSIZE (32*1024) | 151 | CODEC_ID3, |
104 | 152 | }; | |
105 | bool audio_is_initialized = false; | 153 | static struct mp3entry static_id3_entries[ID3_TYPE_NUM_STATIC]; /* (A,O) */ |
106 | static bool audio_thread_ready SHAREDBSS_ATTR = false; | ||
107 | 154 | ||
108 | /* Variables are commented with the threads that use them: * | 155 | /* Peeking functions can yield and mess us up */ |
109 | * A=audio, C=codec, V=voice. A suffix of - indicates that * | 156 | static struct mutex id3_mutex SHAREDBSS_ATTR; /* (A,0)*/ |
110 | * the variable is read but not updated on that thread. */ | ||
111 | /* TBD: Split out "audio" and "playback" (ie. calling) threads */ | ||
112 | 157 | ||
113 | /* Main state control */ | ||
114 | static volatile bool playing SHAREDBSS_ATTR = false;/* Is audio playing? (A) */ | ||
115 | static volatile bool paused SHAREDBSS_ATTR = false; /* Is audio paused? (A/C-) */ | ||
116 | 158 | ||
117 | /* Ring buffer where compressed audio and codecs are loaded */ | 159 | /** For Scrobbler support **/ |
118 | static unsigned char *filebuf = NULL; /* Start of buffer (A/C-) */ | ||
119 | static size_t filebuflen = 0; /* Size of buffer (A/C-) */ | ||
120 | /* FIXME: make buf_ridx (C/A-) */ | ||
121 | 160 | ||
122 | /* Possible arrangements of the buffer */ | 161 | /* Previous track elapsed time */ |
123 | enum audio_buffer_state | 162 | static unsigned long prev_track_elapsed = 0; /* (A,O-) */ |
124 | { | ||
125 | AUDIOBUF_STATE_TRASHED = -1, /* trashed; must be reset */ | ||
126 | AUDIOBUF_STATE_INITIALIZED = 0, /* voice+audio OR audio-only */ | ||
127 | AUDIOBUF_STATE_VOICED_ONLY = 1, /* voice-only */ | ||
128 | }; | ||
129 | static int buffer_state = AUDIOBUF_STATE_TRASHED; /* Buffer state */ | ||
130 | |||
131 | /* These are used to store the current and next (or prev if the current is the last) | ||
132 | * mp3entry's in a round-robin system. This guarentees that the pointer returned | ||
133 | * by audio_current/next_track will be valid for the full duration of the | ||
134 | * currently playing track */ | ||
135 | static struct mp3entry mp3entry_buf[2]; | ||
136 | struct mp3entry *thistrack_id3, /* the currently playing track */ | ||
137 | *othertrack_id3; /* prev track during track-change-transition, or end of playlist, | ||
138 | * next track otherwise */ | ||
139 | static struct mp3entry unbuffered_id3; /* the id3 for the first unbuffered track */ | ||
140 | |||
141 | /* for cuesheet support */ | ||
142 | static struct cuesheet *curr_cue = NULL; | ||
143 | 163 | ||
144 | 164 | ||
165 | /** For album art support **/ | ||
145 | #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT | 166 | #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT |
146 | |||
147 | #ifdef HAVE_ALBUMART | 167 | #ifdef HAVE_ALBUMART |
148 | 168 | ||
149 | static struct albumart_slot { | 169 | static struct albumart_slot |
150 | struct dim dim; /* holds width, height of the albumart */ | 170 | { |
151 | int used; /* counter, increments if something uses it */ | 171 | struct dim dim; /* Holds width, height of the albumart */ |
152 | } albumart_slots[MAX_MULTIPLE_AA]; | 172 | int used; /* Counter; increments if something uses it */ |
173 | } albumart_slots[MAX_MULTIPLE_AA]; /* (A,O) */ | ||
153 | 174 | ||
154 | #define FOREACH_ALBUMART(i) for(i = 0;i < MAX_MULTIPLE_AA; i++) | 175 | #define FOREACH_ALBUMART(i) for(i = 0;i < MAX_MULTIPLE_AA; i++) |
155 | #endif | 176 | #endif /* HAVE_ALBUMART */ |
156 | 177 | ||
157 | 178 | ||
158 | #define MAX_TRACK 128 | 179 | /** Information used for tracking buffer fills **/ |
159 | #define MAX_TRACK_MASK (MAX_TRACK-1) | ||
160 | 180 | ||
161 | /* Track info structure about songs in the file buffer (A/C-) */ | 181 | /* Buffer and thread state tracking */ |
162 | static struct track_info { | 182 | static enum filling_state |
163 | int audio_hid; /* The ID for the track's buffer handle */ | 183 | { |
164 | int id3_hid; /* The ID for the track's metadata handle */ | 184 | STATE_BOOT = 0, /* audio thread is not ready yet */ |
165 | int codec_hid; /* The ID for the track's codec handle */ | 185 | STATE_IDLE, /* audio is stopped: nothing to do */ |
166 | #ifdef HAVE_ALBUMART | 186 | STATE_FILLING, /* adding tracks to the buffer */ |
167 | int aa_hid[MAX_MULTIPLE_AA];/* The ID for the track's album art handle */ | 187 | STATE_FULL, /* can't add any more tracks */ |
188 | STATE_END_OF_PLAYLIST, /* all remaining tracks have been added */ | ||
189 | STATE_FINISHED, /* all remaining tracks are fully buffered */ | ||
190 | STATE_ENDING, /* audio playback is ending */ | ||
191 | STATE_ENDED, /* audio playback is done */ | ||
192 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
193 | STATE_USB, /* USB mode, ignore most messages */ | ||
168 | #endif | 194 | #endif |
169 | int cuesheet_hid; /* The ID for the track's parsed cueesheet handle */ | 195 | } filling = STATE_BOOT; |
170 | 196 | ||
171 | size_t filesize; /* File total length */ | 197 | /* Track info - holds information about each track in the buffer */ |
198 | struct track_info | ||
199 | { | ||
200 | /* In per-track allocated order: */ | ||
201 | int id3_hid; /* Metadata handle ID */ | ||
202 | int cuesheet_hid; /* Parsed cueesheet handle ID */ | ||
203 | #ifdef HAVE_ALBUMART | ||
204 | int aa_hid[MAX_MULTIPLE_AA];/* Album art handle IDs */ | ||
205 | #endif | ||
206 | #ifdef HAVE_CODEC_BUFFERING | ||
207 | int codec_hid; /* Buffered codec handle ID */ | ||
208 | #endif | ||
209 | int audio_hid; /* Main audio data handle ID */ | ||
210 | size_t filesize; /* File total length on disk | ||
211 | TODO: This should be stored | ||
212 | in the handle or the | ||
213 | id3 and would use less | ||
214 | ram */ | ||
215 | }; | ||
172 | 216 | ||
173 | bool taginfo_ready; /* Is metadata read */ | 217 | /* Track list - holds info about all buffered tracks */ |
174 | 218 | #if MEMORYSIZE >= 32 | |
175 | } tracks[MAX_TRACK]; | 219 | #define TRACK_LIST_LEN 128 /* Must be 2^int(+n) */ |
220 | #elif MEMORYSIZE >= 16 | ||
221 | #define TRACK_LIST_LEN 64 | ||
222 | #elif MEMORYSIZE >= 8 | ||
223 | #define TRACK_LIST_LEN 32 | ||
224 | #else | ||
225 | #define TRACK_LIST_LEN 16 | ||
226 | #endif | ||
176 | 227 | ||
177 | static volatile int track_ridx = 0; /* Track being decoded (A/C-) */ | 228 | #define TRACK_LIST_MASK (TRACK_LIST_LEN-1) |
178 | static int track_widx = 0; /* Track being buffered (A) */ | ||
179 | #define CUR_TI (&tracks[track_ridx]) /* Playing track info pointer (A/C-) */ | ||
180 | 229 | ||
181 | static struct track_info *prev_ti = NULL; /* Pointer to the previously played | 230 | static struct |
182 | track */ | 231 | { |
232 | /* read, write and current are maintained unwrapped, limited only by the | ||
233 | unsigned int range and wrap-safe comparisons are used */ | ||
183 | 234 | ||
184 | /* Information used only for filling the buffer */ | 235 | /* NOTE: there appears to be a bug in arm-elf-eabi-gcc 4.4.4 for ARMv4 where |
185 | /* Playlist steps from playing track to next track to be buffered (A) */ | 236 | if 'end' follows 'start' in this structure, track_list_count performs |
186 | static int last_peek_offset = 0; | 237 | 'start - end' rather than 'end - start', giving negative count values... |
238 | so leave it this way for now! */ | ||
239 | unsigned int end; /* Next open position */ | ||
240 | unsigned int start; /* First track in list */ | ||
241 | unsigned int current; /* Currently decoding track */ | ||
242 | struct track_info tracks[TRACK_LIST_LEN]; /* Buffered track information */ | ||
243 | } track_list; /* (A, O-) */ | ||
187 | 244 | ||
188 | /* Scrobbler support */ | ||
189 | static unsigned long prev_track_elapsed = 0; /* Previous track elapsed time (C/A-)*/ | ||
190 | 245 | ||
191 | /* Track change controls */ | 246 | /* Playlist steps from playlist position to next track to be buffered */ |
192 | static bool automatic_skip = false; /* Who initiated in-progress skip? (A) */ | 247 | static int playlist_peek_offset = 0; |
193 | extern bool track_transition; /* Are we in a track transition? */ | ||
194 | static bool dir_skip = false; /* Is a directory skip pending? (A) */ | ||
195 | static bool new_playlist = false; /* Are we starting a new playlist? (A) */ | ||
196 | static int wps_offset = 0; /* Pending track change offset, to keep WPS responsive (A) */ | ||
197 | static bool skipped_during_pause = false; /* Do we need to clear the PCM buffer when playback resumes (A) */ | ||
198 | 248 | ||
199 | static bool start_play_g = false; /* Used by audio_load_track to notify | 249 | /* Metadata handle of track load in progress (meaning all handles have not |
200 | audio_finish_load_track about start_play */ | 250 | yet been opened for the track, id3 always exists or the track does not) |
201 | 251 | ||
202 | /* True when a track load is in progress, i.e. audio_load_track() has returned | 252 | Tracks are keyed by their metadata handles if track list pointers are |
203 | * but audio_finish_load_track() hasn't been called yet. Used to avoid allowing | 253 | insufficient to make comparisons */ |
204 | * audio_load_track() to get called twice in a row, which would cause problems. | 254 | static int in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; |
205 | */ | ||
206 | static bool track_load_started = false; | ||
207 | 255 | ||
208 | #ifdef HAVE_DISK_STORAGE | 256 | #ifdef HAVE_DISK_STORAGE |
209 | static size_t buffer_margin = 5; /* Buffer margin aka anti-skip buffer (A/C-) */ | 257 | /* Buffer margin A.K.A. anti-skip buffer (in seconds) */ |
258 | static size_t buffer_margin = 5; | ||
210 | #endif | 259 | #endif |
211 | 260 | ||
212 | /* Event queues */ | 261 | /* Values returned for track loading */ |
213 | struct event_queue audio_queue SHAREDBSS_ATTR; | 262 | enum track_load_status |
214 | static struct event_queue pcmbuf_queue SHAREDBSS_ATTR; | 263 | { |
264 | LOAD_TRACK_ERR_START_CODEC = -6, | ||
265 | LOAD_TRACK_ERR_FINISH_FAILED = -5, | ||
266 | LOAD_TRACK_ERR_FINISH_FULL = -4, | ||
267 | LOAD_TRACK_ERR_BUSY = -3, | ||
268 | LOAD_TRACK_ERR_NO_MORE = -2, | ||
269 | LOAD_TRACK_ERR_FAILED = -1, | ||
270 | LOAD_TRACK_OK = 0, | ||
271 | LOAD_TRACK_READY = 1, | ||
272 | }; | ||
215 | 273 | ||
216 | extern struct codec_api ci; | 274 | /** Track change controls **/ |
217 | extern unsigned int codec_thread_id; | 275 | |
276 | /* What sort of skip is pending globally? */ | ||
277 | enum track_skip_type | ||
278 | { | ||
279 | /* Relative to what user is intended to see: */ | ||
280 | /* Codec: +0, Track List: +0, Playlist: +0 */ | ||
281 | TRACK_SKIP_NONE = 0, /* no track skip */ | ||
282 | /* Codec: +1, Track List: +1, Playlist: +0 */ | ||
283 | TRACK_SKIP_AUTO, /* codec-initiated skip */ | ||
284 | /* Codec: +1, Track List: +1, Playlist: +1 */ | ||
285 | TRACK_SKIP_AUTO_NEW_PLAYLIST, /* codec-initiated skip is new playlist */ | ||
286 | /* Codec: xx, Track List: +0, Playlist: +0 */ | ||
287 | TRACK_SKIP_AUTO_END_PLAYLIST, /* codec-initiated end of playlist */ | ||
288 | /* Manual skip: Never pends */ | ||
289 | TRACK_SKIP_MANUAL, /* manual track skip */ | ||
290 | /* Manual skip: Never pends */ | ||
291 | TRACK_SKIP_DIR_CHANGE, /* manual directory skip */ | ||
292 | } skip_pending = TRACK_SKIP_NONE; | ||
293 | |||
294 | /* Note about TRACK_SKIP_AUTO_NEW_PLAYLIST: | ||
295 | Fixing playlist code to be able to peek into the first song of | ||
296 | the next playlist would fix any issues and this wouldn't need | ||
297 | to be a special case since pre-advancing the playlist would be | ||
298 | unneeded - it could be much more like TRACK_SKIP_AUTO and all | ||
299 | actions that require reversal during an in-progress transition | ||
300 | would work as expected */ | ||
301 | |||
302 | /* Used to indicate status for the events. Must be separate to satisfy all | ||
303 | clients so the correct metadata is read when sending the change events | ||
304 | and also so that it is read correctly outside the events. */ | ||
305 | static bool automatic_skip = false; /* (A, O-) */ | ||
306 | |||
307 | /* Pending manual track skip offset */ | ||
308 | static int skip_offset = 0; /* (A, O) */ | ||
309 | |||
310 | /* Track change notification */ | ||
311 | static struct | ||
312 | { | ||
313 | unsigned int in; /* Number of pcmbuf posts (audio isr) */ | ||
314 | unsigned int out; /* Number of times audio has read the difference */ | ||
315 | } track_change = { 0, 0 }; | ||
316 | |||
317 | /** Codec status **/ | ||
318 | /* Did the codec notify us it finished while we were paused or while still | ||
319 | in an automatic transition? | ||
320 | |||
321 | If paused, it is necessary to defer a codec-initiated skip until resuming | ||
322 | or else the track will move forward while not playing audio! | ||
323 | |||
324 | If in-progress, skips should not build-up ahead of where the WPS is when | ||
325 | really short tracks finish decoding. | ||
326 | |||
327 | If it is forgotten, it will be missed altogether and playback will just sit | ||
328 | there looking stupid and comatose until the user does something */ | ||
329 | static bool codec_skip_pending = false; | ||
330 | static int codec_skip_status; | ||
331 | static bool codec_seeking = false; /* Codec seeking ack expected? */ | ||
218 | 332 | ||
219 | /* Multiple threads */ | 333 | |
220 | /* Set the watermark to trigger buffer fill (A/C) */ | 334 | /* Event queues */ |
221 | static void set_filebuf_watermark(void); | 335 | static struct event_queue audio_queue SHAREDBSS_ATTR; |
222 | 336 | ||
223 | /* Audio thread */ | 337 | /* Audio thread */ |
224 | static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR; | 338 | static struct queue_sender_list audio_queue_sender_list SHAREDBSS_ATTR; |
225 | static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)]; | 339 | static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)]; |
226 | static const char audio_thread_name[] = "audio"; | 340 | static const char audio_thread_name[] = "audio"; |
341 | static unsigned int audio_thread_id = 0; | ||
342 | |||
343 | /* Forward declarations */ | ||
344 | enum audio_start_playback_flags | ||
345 | { | ||
346 | AUDIO_START_RESTART = 0x1, /* "Restart" playback (flush _all_ tracks) */ | ||
347 | AUDIO_START_NEWBUF = 0x2, /* Mark the audiobuffer as invalid */ | ||
348 | }; | ||
227 | 349 | ||
228 | static void audio_thread(void); | 350 | static void audio_start_playback(size_t offset, unsigned int flags); |
229 | static void audio_initiate_track_change(long direction); | ||
230 | static bool audio_have_tracks(void); | ||
231 | static void audio_reset_buffer(void); | ||
232 | static void audio_stop_playback(void); | 351 | static void audio_stop_playback(void); |
352 | static void buffer_event_buffer_low_callback(void *data); | ||
353 | static void buffer_event_rebuffer_callback(void *data); | ||
354 | static void buffer_event_finished_callback(void *data); | ||
355 | |||
233 | 356 | ||
234 | /**************************************/ | 357 | /**************************************/ |
235 | 358 | ||
236 | /** Pcmbuf callbacks */ | 359 | /** --- audio_queue helpers --- **/ |
237 | 360 | ||
238 | /* Between the codec and PCM track change, we need to keep updating the | 361 | /* codec thread needs access */ |
239 | * "elapsed" value of the previous (to the codec, but current to the | 362 | void audio_queue_post(long id, intptr_t data) |
240 | * user/PCM/WPS) track, so that the progressbar reaches the end. | ||
241 | * During that transition, the WPS will display othertrack_id3. */ | ||
242 | void audio_pcmbuf_position_callback(unsigned int time) | ||
243 | { | 363 | { |
244 | time += othertrack_id3->elapsed; | 364 | queue_post(&audio_queue, id, data); |
245 | othertrack_id3->elapsed = (time >= othertrack_id3->length) | ||
246 | ? othertrack_id3->length : time; | ||
247 | } | 365 | } |
248 | 366 | ||
249 | /* Post message from pcmbuf that the end of the previous track | 367 | static intptr_t audio_queue_send(long id, intptr_t data) |
250 | * has just been played. */ | ||
251 | void audio_post_track_change(bool pcmbuf) | ||
252 | { | 368 | { |
253 | if (pcmbuf) | 369 | return queue_send(&audio_queue, id, data); |
254 | { | ||
255 | LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED"); | ||
256 | queue_post(&pcmbuf_queue, Q_AUDIO_TRACK_CHANGED, 0); | ||
257 | } | ||
258 | else | ||
259 | { | ||
260 | LOGFQUEUE("pcmbuf > audio Q_AUDIO_TRACK_CHANGED"); | ||
261 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); | ||
262 | } | ||
263 | } | 370 | } |
264 | 371 | ||
265 | /* Scan the pcmbuf queue and return true if a message pulled */ | ||
266 | static bool pcmbuf_queue_scan(struct queue_event *ev) | ||
267 | { | ||
268 | if (!queue_empty(&pcmbuf_queue)) | ||
269 | { | ||
270 | /* Transfer message to audio queue */ | ||
271 | pcm_play_lock(); | ||
272 | /* Pull message - never, ever any blocking call! */ | ||
273 | queue_wait_w_tmo(&pcmbuf_queue, ev, 0); | ||
274 | pcm_play_unlock(); | ||
275 | return true; | ||
276 | } | ||
277 | |||
278 | return false; | ||
279 | } | ||
280 | 372 | ||
373 | /** --- MP3Entry --- **/ | ||
281 | 374 | ||
282 | /** Helper functions */ | 375 | /* Does the mp3entry have enough info for us to use it? */ |
376 | static struct mp3entry * valid_mp3entry(const struct mp3entry *id3) | ||
377 | { | ||
378 | return id3 && (id3->length != 0 || id3->filesize != 0) && | ||
379 | id3->codectype != AFMT_UNKNOWN ? (struct mp3entry *)id3 : NULL; | ||
380 | } | ||
283 | 381 | ||
284 | static struct mp3entry *bufgetid3(int handle_id) | 382 | /* Return a pointer to an mp3entry on the buffer, as it is */ |
383 | static struct mp3entry * bufgetid3(int handle_id) | ||
285 | { | 384 | { |
286 | if (handle_id < 0) | 385 | if (handle_id < 0) |
287 | return NULL; | 386 | return NULL; |
@@ -295,6 +394,7 @@ static struct mp3entry *bufgetid3(int handle_id) | |||
295 | return id3; | 394 | return id3; |
296 | } | 395 | } |
297 | 396 | ||
397 | /* Read an mp3entry from the buffer, adjusted */ | ||
298 | static bool bufreadid3(int handle_id, struct mp3entry *id3out) | 398 | static bool bufreadid3(int handle_id, struct mp3entry *id3out) |
299 | { | 399 | { |
300 | struct mp3entry *id3 = bufgetid3(handle_id); | 400 | struct mp3entry *id3 = bufgetid3(handle_id); |
@@ -308,1200 +408,1441 @@ static bool bufreadid3(int handle_id, struct mp3entry *id3out) | |||
308 | return false; | 408 | return false; |
309 | } | 409 | } |
310 | 410 | ||
311 | static bool clear_track_info(struct track_info *track) | 411 | /* Lock the id3 mutex */ |
412 | static void id3_mutex_lock(void) | ||
312 | { | 413 | { |
313 | /* bufclose returns true if the handle is not found, or if it is closed | 414 | mutex_lock(&id3_mutex); |
314 | * successfully, so these checks are safe on non-existant handles */ | ||
315 | if (!track) | ||
316 | return false; | ||
317 | |||
318 | if (track->codec_hid >= 0) { | ||
319 | if (bufclose(track->codec_hid)) | ||
320 | track->codec_hid = -1; | ||
321 | else | ||
322 | return false; | ||
323 | } | ||
324 | |||
325 | if (track->id3_hid >= 0) { | ||
326 | if (bufclose(track->id3_hid)) | ||
327 | track->id3_hid = -1; | ||
328 | else | ||
329 | return false; | ||
330 | } | ||
331 | |||
332 | if (track->audio_hid >= 0) { | ||
333 | if (bufclose(track->audio_hid)) | ||
334 | track->audio_hid = -1; | ||
335 | else | ||
336 | return false; | ||
337 | } | ||
338 | |||
339 | #ifdef HAVE_ALBUMART | ||
340 | { | ||
341 | int i; | ||
342 | FOREACH_ALBUMART(i) | ||
343 | { | ||
344 | if (track->aa_hid[i] >= 0) { | ||
345 | if (bufclose(track->aa_hid[i])) | ||
346 | track->aa_hid[i] = -1; | ||
347 | else | ||
348 | return false; | ||
349 | } | ||
350 | } | ||
351 | } | ||
352 | #endif | ||
353 | |||
354 | if (track->cuesheet_hid >= 0) { | ||
355 | if (bufclose(track->cuesheet_hid)) | ||
356 | track->cuesheet_hid = -1; | ||
357 | else | ||
358 | return false; | ||
359 | } | ||
360 | |||
361 | track->filesize = 0; | ||
362 | track->taginfo_ready = false; | ||
363 | |||
364 | return true; | ||
365 | } | 415 | } |
366 | 416 | ||
367 | /* --- External interfaces --- */ | 417 | /* Unlock the id3 mutex */ |
368 | 418 | static void id3_mutex_unlock(void) | |
369 | /* This sends a stop message and the audio thread will dump all it's | ||
370 | subsequenct messages */ | ||
371 | void audio_hard_stop(void) | ||
372 | { | 419 | { |
373 | /* Stop playback */ | 420 | mutex_unlock(&id3_mutex); |
374 | LOGFQUEUE("audio >| audio Q_AUDIO_STOP: 1"); | ||
375 | queue_send(&audio_queue, Q_AUDIO_STOP, 1); | ||
376 | #ifdef PLAYBACK_VOICE | ||
377 | voice_stop(); | ||
378 | #endif | ||
379 | } | 421 | } |
380 | 422 | ||
381 | bool audio_restore_playback(int type) | 423 | /* Return one of the collection of mp3entry pointers - collect them all here */ |
424 | static inline struct mp3entry * id3_get(enum audio_id3_types id3_num) | ||
382 | { | 425 | { |
383 | switch (type) | 426 | switch (id3_num) |
384 | { | 427 | { |
385 | case AUDIO_WANT_PLAYBACK: | 428 | case UNBUFFERED_ID3: |
386 | if (buffer_state != AUDIOBUF_STATE_INITIALIZED) | 429 | return &audio_scratch_memory->unbuffered_id3; |
387 | audio_reset_buffer(); | 430 | case CODEC_ID3: |
388 | return true; | 431 | return &audio_scratch_memory->codec_id3; |
389 | case AUDIO_WANT_VOICE: | ||
390 | if (buffer_state == AUDIOBUF_STATE_TRASHED) | ||
391 | audio_reset_buffer(); | ||
392 | return true; | ||
393 | default: | 432 | default: |
394 | return false; | 433 | return &static_id3_entries[id3_num]; |
395 | } | 434 | } |
396 | } | 435 | } |
397 | 436 | ||
398 | unsigned char *audio_get_buffer(bool talk_buf, size_t *buffer_size) | 437 | /* Copy an mp3entry into one of the mp3 entries */ |
438 | static void id3_write(enum audio_id3_types id3_num, | ||
439 | const struct mp3entry *id3_src) | ||
399 | { | 440 | { |
400 | unsigned char *buf, *end; | 441 | struct mp3entry *dest_id3 = id3_get(id3_num); |
401 | |||
402 | if (audio_is_initialized) | ||
403 | { | ||
404 | audio_hard_stop(); | ||
405 | } | ||
406 | /* else buffer_state will be AUDIOBUF_STATE_TRASHED at this point */ | ||
407 | |||
408 | /* Reset the buffering thread so that it doesn't try to use the data */ | ||
409 | buffering_reset(filebuf, filebuflen); | ||
410 | |||
411 | if (buffer_size == NULL) | ||
412 | { | ||
413 | /* Special case for talk_init to use since it already knows it's | ||
414 | trashed */ | ||
415 | buffer_state = AUDIOBUF_STATE_TRASHED; | ||
416 | return NULL; | ||
417 | } | ||
418 | 442 | ||
419 | if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED | 443 | if (id3_src) |
420 | || !talk_voice_required()) | 444 | copy_mp3entry(dest_id3, id3_src); |
421 | { | ||
422 | logf("get buffer: talk, audio"); | ||
423 | /* Ok to use everything from audiobuf to audiobufend - voice is loaded, | ||
424 | the talk buffer is not needed because voice isn't being used, or | ||
425 | could be AUDIOBUF_STATE_TRASHED already. If state is | ||
426 | AUDIOBUF_STATE_VOICED_ONLY, no problem as long as memory isn't written | ||
427 | without the caller knowing what's going on. Changing certain settings | ||
428 | may move it to a worse condition but the memory in use by something | ||
429 | else will remain undisturbed. | ||
430 | */ | ||
431 | if (buffer_state != AUDIOBUF_STATE_TRASHED) | ||
432 | { | ||
433 | talk_buffer_steal(); | ||
434 | buffer_state = AUDIOBUF_STATE_TRASHED; | ||
435 | } | ||
436 | |||
437 | buf = audiobuf; | ||
438 | end = audiobufend; | ||
439 | } | ||
440 | else | 445 | else |
441 | { | 446 | wipe_mp3entry(dest_id3); |
442 | /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or | ||
443 | still AUDIOBUF_STATE_INITIALIZED */ | ||
444 | /* Skip talk buffer and move pcm buffer to end to maximize available | ||
445 | contiguous memory - no audio running means voice will not need the | ||
446 | swap space */ | ||
447 | logf("get buffer: audio"); | ||
448 | buf = audiobuf + talk_get_bufsize(); | ||
449 | end = audiobufend - pcmbuf_init(audiobufend); | ||
450 | buffer_state = AUDIOBUF_STATE_VOICED_ONLY; | ||
451 | } | ||
452 | |||
453 | *buffer_size = end - buf; | ||
454 | |||
455 | return buf; | ||
456 | } | 447 | } |
457 | 448 | ||
458 | bool audio_buffer_state_trashed(void) | 449 | /* Call id3_write "safely" because peek aheads can yield, even if the fast |
450 | preview isn't enabled */ | ||
451 | static void id3_write_locked(enum audio_id3_types id3_num, | ||
452 | const struct mp3entry *id3_src) | ||
459 | { | 453 | { |
460 | return buffer_state == AUDIOBUF_STATE_TRASHED; | 454 | id3_mutex_lock(); |
455 | id3_write(id3_num, id3_src); | ||
456 | id3_mutex_unlock(); | ||
461 | } | 457 | } |
462 | 458 | ||
463 | #ifdef HAVE_RECORDING | 459 | |
464 | unsigned char *audio_get_recording_buffer(size_t *buffer_size) | 460 | /** --- Track info --- **/ |
461 | |||
462 | /* Close a handle and mark it invalid */ | ||
463 | static void track_info_close_handle(int *hid_p) | ||
465 | { | 464 | { |
466 | /* Stop audio, voice and obtain all available buffer space */ | 465 | int hid = *hid_p; |
467 | audio_hard_stop(); | ||
468 | talk_buffer_steal(); | ||
469 | 466 | ||
470 | unsigned char *end = audiobufend; | 467 | /* bufclose returns true if the handle is not found, or if it is closed |
471 | buffer_state = AUDIOBUF_STATE_TRASHED; | 468 | * successfully, so these checks are safe on non-existant handles */ |
472 | *buffer_size = end - audiobuf; | 469 | if (hid >= 0) |
470 | bufclose(hid); | ||
473 | 471 | ||
474 | return (unsigned char *)audiobuf; | 472 | /* Always reset to "no handle" in case it was something else */ |
473 | *hid_p = ERR_HANDLE_NOT_FOUND; | ||
475 | } | 474 | } |
476 | 475 | ||
477 | bool audio_load_encoder(int afmt) | 476 | /* Close all handles in a struct track_info and clear it */ |
477 | static void track_info_close(struct track_info *info) | ||
478 | { | 478 | { |
479 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 479 | /* Close them in the order they are allocated on the buffer to speed up |
480 | LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: %d", afmt); | 480 | the handle searching */ |
481 | return queue_send(&audio_queue, Q_AUDIO_LOAD_ENCODER, afmt) > 0; | 481 | track_info_close_handle(&info->id3_hid); |
482 | #else | 482 | track_info_close_handle(&info->cuesheet_hid); |
483 | (void)afmt; | 483 | #ifdef HAVE_ALBUMART |
484 | return true; | 484 | int i; |
485 | FOREACH_ALBUMART(i) | ||
486 | track_info_close_handle(&info->aa_hid[i]); | ||
485 | #endif | 487 | #endif |
486 | } /* audio_load_encoder */ | 488 | #ifdef HAVE_CODEC_BUFFERING |
489 | track_info_close_handle(&info->codec_hid); | ||
490 | #endif | ||
491 | track_info_close_handle(&info->audio_hid); | ||
492 | info->filesize = 0; | ||
493 | } | ||
487 | 494 | ||
488 | void audio_remove_encoder(void) | 495 | /* Invalidate all members to initial values - does not close handles */ |
496 | static void track_info_wipe(struct track_info * info) | ||
489 | { | 497 | { |
490 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 498 | info->id3_hid = ERR_HANDLE_NOT_FOUND; |
491 | LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: NULL"); | 499 | info->cuesheet_hid = ERR_HANDLE_NOT_FOUND; |
492 | queue_send(&audio_queue, Q_AUDIO_LOAD_ENCODER, AFMT_UNKNOWN); | 500 | #ifdef HAVE_ALBUMART |
501 | int i; | ||
502 | FOREACH_ALBUMART(i) | ||
503 | info->aa_hid[i] = ERR_HANDLE_NOT_FOUND; | ||
504 | #endif | ||
505 | #ifdef HAVE_CODEC_BUFFERING | ||
506 | info->codec_hid = ERR_HANDLE_NOT_FOUND; | ||
493 | #endif | 507 | #endif |
494 | } /* audio_remove_encoder */ | 508 | info->audio_hid = ERR_HANDLE_NOT_FOUND; |
509 | info->filesize = 0; | ||
510 | } | ||
495 | 511 | ||
496 | #endif /* HAVE_RECORDING */ | ||
497 | 512 | ||
513 | /** --- Track list --- **/ | ||
498 | 514 | ||
499 | struct mp3entry* audio_current_track(void) | 515 | /* Initialize the track list */ |
516 | static void track_list_init(void) | ||
500 | { | 517 | { |
501 | const char *filename; | 518 | int i; |
502 | struct playlist_track_info trackinfo; | 519 | for (i = 0; i < TRACK_LIST_LEN; i++) |
503 | int cur_idx; | 520 | track_info_wipe(&track_list.tracks[i]); |
504 | int offset = ci.new_track + wps_offset; | ||
505 | struct mp3entry *write_id3; | ||
506 | |||
507 | cur_idx = (track_ridx + offset) & MAX_TRACK_MASK; | ||
508 | 521 | ||
509 | if (cur_idx == track_ridx && *thistrack_id3->path) | 522 | track_list.start = track_list.end = track_list.current; |
510 | { | 523 | } |
511 | /* The usual case */ | ||
512 | if (tracks[cur_idx].cuesheet_hid >= 0 && !thistrack_id3->cuesheet) | ||
513 | { | ||
514 | bufread(tracks[cur_idx].cuesheet_hid, sizeof(struct cuesheet), curr_cue); | ||
515 | thistrack_id3->cuesheet = curr_cue; | ||
516 | } | ||
517 | return thistrack_id3; | ||
518 | } | ||
519 | else if (automatic_skip && offset == -1 && *othertrack_id3->path) | ||
520 | { | ||
521 | /* We're in a track transition. The codec has moved on to the next track, | ||
522 | but the audio being played is still the same (now previous) track. | ||
523 | othertrack_id3.elapsed is being updated in an ISR by | ||
524 | codec_pcmbuf_position_callback */ | ||
525 | if (tracks[cur_idx].cuesheet_hid >= 0 && !thistrack_id3->cuesheet) | ||
526 | { | ||
527 | bufread(tracks[cur_idx].cuesheet_hid, sizeof(struct cuesheet), curr_cue); | ||
528 | othertrack_id3->cuesheet = curr_cue; | ||
529 | } | ||
530 | return othertrack_id3; | ||
531 | } | ||
532 | 524 | ||
533 | if (offset != 0) | 525 | /* Return number of items allocated in the list */ |
534 | { | 526 | static unsigned int track_list_count(void) |
535 | /* Codec may be using thistrack_id3, so it must not be overwritten. | 527 | { |
536 | If this is a manual skip, othertrack_id3 will become | 528 | return track_list.end - track_list.start; |
537 | thistrack_id3 in audio_check_new_track(). | 529 | } |
538 | FIXME: If this is an automatic skip, it probably means multiple | ||
539 | short tracks fit in the PCM buffer. Overwriting othertrack_id3 | ||
540 | can lead to an incorrect value later. | ||
541 | Note that othertrack_id3 may also be used for next track. | ||
542 | */ | ||
543 | write_id3 = othertrack_id3; | ||
544 | } | ||
545 | else | ||
546 | { | ||
547 | write_id3 = thistrack_id3; | ||
548 | } | ||
549 | 530 | ||
550 | if (tracks[cur_idx].id3_hid >= 0) | 531 | /* Return true if the list is empty */ |
551 | { | 532 | static inline bool track_list_empty(void) |
552 | /* The current track's info has been buffered but not read yet, so get it */ | 533 | { |
553 | if (bufreadid3(tracks[cur_idx].id3_hid, write_id3)) | 534 | return track_list.end == track_list.start; |
554 | return write_id3; | 535 | } |
555 | } | ||
556 | 536 | ||
557 | /* We didn't find the ID3 metadata, so we fill temp_id3 with the little info | 537 | /* Returns true if the list is holding the maximum number of items */ |
558 | we have and return that. */ | 538 | static bool track_list_full(void) |
539 | { | ||
540 | return track_list.end - track_list.start >= TRACK_LIST_LEN; | ||
541 | } | ||
559 | 542 | ||
560 | memset(write_id3, 0, sizeof(struct mp3entry)); | 543 | /* Test if the index is within the allocated range */ |
544 | static bool track_list_in_range(int pos) | ||
545 | { | ||
546 | return (int)(pos - track_list.start) >= 0 && | ||
547 | (int)(pos - track_list.end) < 0; | ||
548 | } | ||
561 | 549 | ||
562 | playlist_get_track_info(NULL, playlist_next(0)+wps_offset, &trackinfo); | 550 | static struct track_info * track_list_entry(int pos) |
563 | filename = trackinfo.filename; | 551 | { |
564 | if (!filename) | 552 | return &track_list.tracks[pos & TRACK_LIST_MASK]; |
565 | filename = "No file!"; | 553 | } |
566 | 554 | ||
567 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) | 555 | /* Return the info of the last allocation plus an offset, NULL if result is |
568 | if (tagcache_fill_tags(write_id3, filename)) | 556 | out of bounds */ |
569 | return write_id3; | 557 | static struct track_info * track_list_last(int offset) |
570 | #endif | 558 | { |
559 | /* Last is before the end since the end isn't inclusive */ | ||
560 | unsigned int pos = track_list.end + offset - 1; | ||
571 | 561 | ||
572 | strlcpy(write_id3->path, filename, sizeof(write_id3->path)); | 562 | if (!track_list_in_range(pos)) |
573 | write_id3->title = strrchr(write_id3->path, '/'); | 563 | return NULL; |
574 | if (!write_id3->title) | ||
575 | write_id3->title = &write_id3->path[0]; | ||
576 | else | ||
577 | write_id3->title++; | ||
578 | 564 | ||
579 | return write_id3; | 565 | return track_list_entry(pos); |
580 | } | 566 | } |
581 | 567 | ||
582 | struct mp3entry* audio_next_track(void) | 568 | /* Allocate space at the end for another track if not full */ |
569 | static struct track_info * track_list_alloc_track(void) | ||
583 | { | 570 | { |
584 | int next_idx; | 571 | if (track_list_full()) |
585 | int offset = ci.new_track + wps_offset; | ||
586 | |||
587 | if (!audio_have_tracks()) | ||
588 | return NULL; | 572 | return NULL; |
589 | 573 | ||
590 | if (wps_offset == -1 && *thistrack_id3->path) | 574 | return track_list_entry(track_list.end++); |
591 | { | 575 | } |
592 | /* We're in a track transition. The next track for the WPS is the one | ||
593 | currently being decoded. */ | ||
594 | return thistrack_id3; | ||
595 | } | ||
596 | 576 | ||
597 | next_idx = (track_ridx + offset + 1) & MAX_TRACK_MASK; | 577 | /* Remove the last track entry allocated in order to support backing out |
578 | of a track load */ | ||
579 | static void track_list_unalloc_track(void) | ||
580 | { | ||
581 | if (track_list_empty()) | ||
582 | return; | ||
598 | 583 | ||
599 | if (tracks[next_idx].id3_hid >= 0) | 584 | track_list.end--; |
600 | { | ||
601 | if (bufreadid3(tracks[next_idx].id3_hid, othertrack_id3)) | ||
602 | return othertrack_id3; | ||
603 | else | ||
604 | return NULL; | ||
605 | } | ||
606 | 585 | ||
607 | if (next_idx == track_widx) | 586 | if (track_list.current == track_list.end && |
587 | track_list.current != track_list.start) | ||
608 | { | 588 | { |
609 | /* The next track hasn't been buffered yet, so we return the static | 589 | /* Current _must_ remain within bounds */ |
610 | version of its metadata. */ | 590 | track_list.current--; |
611 | return &unbuffered_id3; | ||
612 | } | 591 | } |
613 | |||
614 | return NULL; | ||
615 | } | 592 | } |
616 | 593 | ||
617 | /* gets a copy of the id3 data */ | 594 | /* Return current track plus an offset, NULL if result is out of bounds */ |
618 | bool audio_peek_track(struct mp3entry* id3, int offset) | 595 | static struct track_info * track_list_current(int offset) |
619 | { | 596 | { |
620 | int next_idx; | 597 | unsigned int pos = track_list.current + offset; |
621 | int new_offset = ci.new_track + wps_offset + offset; | ||
622 | |||
623 | if (!audio_have_tracks()) | ||
624 | return false; | ||
625 | next_idx = (track_ridx + new_offset) & MAX_TRACK_MASK; | ||
626 | 598 | ||
627 | if (tracks[next_idx].id3_hid >= 0) | 599 | if (!track_list_in_range(pos)) |
628 | return bufreadid3(tracks[next_idx].id3_hid, id3); | 600 | return NULL; |
629 | |||
630 | return false; | ||
631 | } | ||
632 | |||
633 | #ifdef HAVE_ALBUMART | ||
634 | |||
635 | int playback_current_aa_hid(int slot) | ||
636 | { | ||
637 | if (slot < 0) | ||
638 | return -1; | ||
639 | int cur_idx; | ||
640 | int offset = ci.new_track + wps_offset; | ||
641 | 601 | ||
642 | cur_idx = track_ridx + offset; | 602 | return track_list_entry(pos); |
643 | cur_idx &= MAX_TRACK_MASK; | ||
644 | return tracks[cur_idx].aa_hid[slot]; | ||
645 | } | 603 | } |
646 | 604 | ||
647 | int playback_claim_aa_slot(struct dim *dim) | 605 | /* Return current based upon what's intended that the user sees - not |
606 | necessarily where decoding is taking place */ | ||
607 | static struct track_info * track_list_user_current(int offset) | ||
648 | { | 608 | { |
649 | int i; | 609 | if (skip_pending == TRACK_SKIP_AUTO || |
650 | 610 | skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST) | |
651 | /* first try to find a slot already having the size to reuse it | ||
652 | * since we don't want albumart of the same size buffered multiple times */ | ||
653 | FOREACH_ALBUMART(i) | ||
654 | { | ||
655 | struct albumart_slot *slot = &albumart_slots[i]; | ||
656 | if (slot->dim.width == dim->width | ||
657 | && slot->dim.height == dim->height) | ||
658 | { | ||
659 | slot->used++; | ||
660 | return i; | ||
661 | } | ||
662 | } | ||
663 | /* size is new, find a free slot */ | ||
664 | FOREACH_ALBUMART(i) | ||
665 | { | 611 | { |
666 | if (!albumart_slots[i].used) | 612 | offset--; |
667 | { | ||
668 | albumart_slots[i].used++; | ||
669 | albumart_slots[i].dim = *dim; | ||
670 | return i; | ||
671 | } | ||
672 | } | 613 | } |
673 | /* sorry, no free slot */ | ||
674 | return -1; | ||
675 | } | ||
676 | 614 | ||
677 | void playback_release_aa_slot(int slot) | 615 | return track_list_current(offset); |
678 | { | ||
679 | /* invalidate the albumart_slot */ | ||
680 | struct albumart_slot *aa_slot = &albumart_slots[slot]; | ||
681 | |||
682 | if (aa_slot->used > 0) | ||
683 | aa_slot->used--; | ||
684 | } | 616 | } |
685 | 617 | ||
686 | #endif | 618 | /* Advance current track by an offset, return false if result is out of |
687 | void audio_play(long offset) | 619 | bounds */ |
620 | static struct track_info * track_list_advance_current(int offset) | ||
688 | { | 621 | { |
689 | logf("audio_play"); | 622 | unsigned int pos = track_list.current + offset; |
690 | 623 | ||
691 | #ifdef PLAYBACK_VOICE | 624 | if (!track_list_in_range(pos)) |
692 | /* Truncate any existing voice output so we don't have spelling | 625 | return NULL; |
693 | * etc. over the first part of the played track */ | ||
694 | talk_force_shutup(); | ||
695 | #endif | ||
696 | 626 | ||
697 | /* Start playback */ | 627 | track_list.current = pos; |
698 | LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %ld", offset); | 628 | return track_list_entry(pos); |
699 | /* Don't return until playback has actually started */ | ||
700 | queue_send(&audio_queue, Q_AUDIO_PLAY, offset); | ||
701 | } | 629 | } |
702 | 630 | ||
703 | void audio_stop(void) | 631 | /* Clear tracks in the list, optionally preserving the current track - |
632 | returns 'false' if the operation was changed */ | ||
633 | enum track_clear_action | ||
704 | { | 634 | { |
705 | /* Stop playback */ | 635 | TRACK_LIST_CLEAR_ALL = 0, /* Clear all tracks */ |
706 | LOGFQUEUE("audio >| audio Q_AUDIO_STOP"); | 636 | TRACK_LIST_KEEP_CURRENT, /* Keep current only; clear before + after */ |
707 | /* Don't return until playback has actually stopped */ | 637 | TRACK_LIST_KEEP_NEW /* Keep current and those that follow */ |
708 | queue_send(&audio_queue, Q_AUDIO_STOP, 0); | 638 | }; |
709 | } | ||
710 | 639 | ||
711 | void audio_pause(void) | 640 | static void track_list_clear(enum track_clear_action action) |
712 | { | 641 | { |
713 | LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE"); | 642 | logf("%s(%d)", __func__, (int)action); |
714 | /* Don't return until playback has actually paused */ | ||
715 | queue_send(&audio_queue, Q_AUDIO_PAUSE, true); | ||
716 | } | ||
717 | 643 | ||
718 | void audio_resume(void) | 644 | /* Don't care now since rebuffering is imminent */ |
719 | { | 645 | buf_set_watermark(0); |
720 | LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE resume"); | ||
721 | /* Don't return until playback has actually resumed */ | ||
722 | queue_send(&audio_queue, Q_AUDIO_PAUSE, false); | ||
723 | } | ||
724 | 646 | ||
725 | void audio_skip(int direction) | 647 | if (action != TRACK_LIST_CLEAR_ALL) |
726 | { | ||
727 | if (playlist_check(ci.new_track + wps_offset + direction)) | ||
728 | { | 648 | { |
729 | if (global_settings.beep) | 649 | struct track_info *cur = track_list_current(0); |
730 | pcmbuf_beep(2000, 100, 2500*global_settings.beep); | ||
731 | 650 | ||
732 | LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", direction); | 651 | if (!cur || cur->id3_hid < 0) |
733 | queue_post(&audio_queue, Q_AUDIO_SKIP, direction); | 652 | action = TRACK_LIST_CLEAR_ALL; /* Nothing worthwhile keeping */ |
734 | /* Update wps while our message travels inside deep playback queues. */ | ||
735 | wps_offset += direction; | ||
736 | } | 653 | } |
737 | else | 654 | |
655 | /* Noone should see this progressing */ | ||
656 | int start = track_list.start; | ||
657 | int current = track_list.current; | ||
658 | int end = track_list.end; | ||
659 | |||
660 | track_list.start = current; | ||
661 | |||
662 | switch (action) | ||
738 | { | 663 | { |
739 | /* No more tracks. */ | 664 | case TRACK_LIST_CLEAR_ALL: |
740 | if (global_settings.beep) | 665 | /* Result: .start = .current, .end = .current */ |
741 | pcmbuf_beep(1000, 100, 1500*global_settings.beep); | 666 | track_list.end = current; |
667 | break; | ||
668 | |||
669 | case TRACK_LIST_KEEP_CURRENT: | ||
670 | /* Result: .start = .current, .end = .current + 1 */ | ||
671 | track_list.end = current + 1; | ||
672 | break; | ||
673 | |||
674 | case TRACK_LIST_KEEP_NEW: | ||
675 | /* Result: .start = .current, .end = .end */ | ||
676 | end = current; | ||
677 | break; | ||
742 | } | 678 | } |
743 | } | ||
744 | 679 | ||
745 | void audio_next(void) | 680 | /* Close all open handles in the range except the for the current track |
746 | { | 681 | if preserving that */ |
747 | audio_skip(1); | 682 | while (start != end) |
748 | } | 683 | { |
684 | if (action != TRACK_LIST_KEEP_CURRENT || start != current) | ||
685 | { | ||
686 | struct track_info *info = | ||
687 | &track_list.tracks[start & TRACK_LIST_MASK]; | ||
749 | 688 | ||
750 | void audio_prev(void) | 689 | /* If this is the in-progress load, abort it */ |
751 | { | 690 | if (in_progress_id3_hid >= 0 && |
752 | audio_skip(-1); | 691 | info->id3_hid == in_progress_id3_hid) |
753 | } | 692 | { |
693 | in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; | ||
694 | } | ||
754 | 695 | ||
755 | void audio_next_dir(void) | 696 | track_info_close(info); |
756 | { | 697 | } |
757 | LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP 1"); | ||
758 | queue_post(&audio_queue, Q_AUDIO_DIR_SKIP, 1); | ||
759 | } | ||
760 | 698 | ||
761 | void audio_prev_dir(void) | 699 | start++; |
762 | { | 700 | } |
763 | LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP -1"); | ||
764 | queue_post(&audio_queue, Q_AUDIO_DIR_SKIP, -1); | ||
765 | } | 701 | } |
766 | 702 | ||
767 | void audio_pre_ff_rewind(void) | ||
768 | { | ||
769 | LOGFQUEUE("audio > audio Q_AUDIO_PRE_FF_REWIND"); | ||
770 | queue_post(&audio_queue, Q_AUDIO_PRE_FF_REWIND, 0); | ||
771 | } | ||
772 | 703 | ||
773 | void audio_ff_rewind(long newpos) | 704 | /** --- Audio buffer -- **/ |
774 | { | ||
775 | LOGFQUEUE("audio > audio Q_AUDIO_FF_REWIND"); | ||
776 | queue_post(&audio_queue, Q_AUDIO_FF_REWIND, newpos); | ||
777 | } | ||
778 | 705 | ||
779 | void audio_flush_and_reload_tracks(void) | 706 | /* What size is needed for the scratch buffer? */ |
707 | static size_t scratch_mem_size(void) | ||
780 | { | 708 | { |
781 | LOGFQUEUE("audio > audio Q_AUDIO_FLUSH"); | 709 | size_t size = sizeof (struct audio_scratch_memory); |
782 | queue_post(&audio_queue, Q_AUDIO_FLUSH, 0); | 710 | |
711 | if (global_settings.cuesheet) | ||
712 | size += sizeof (struct cuesheet); | ||
713 | |||
714 | return size; | ||
783 | } | 715 | } |
784 | 716 | ||
785 | void audio_error_clear(void) | 717 | /* Initialize the memory area where data is stored that is only used when |
718 | playing audio and anything depending upon it */ | ||
719 | static void scratch_mem_init(void *mem) | ||
786 | { | 720 | { |
787 | #ifdef AUDIO_HAVE_RECORDING | 721 | audio_scratch_memory = (struct audio_scratch_memory *)mem; |
788 | pcm_rec_error_clear(); | 722 | id3_write_locked(UNBUFFERED_ID3, NULL); |
789 | #endif | 723 | id3_write(CODEC_ID3, NULL); |
724 | ci.id3 = id3_get(CODEC_ID3); | ||
725 | audio_scratch_memory->curr_cue = NULL; | ||
726 | |||
727 | if (global_settings.cuesheet) | ||
728 | { | ||
729 | audio_scratch_memory->curr_cue = | ||
730 | SKIPBYTES((struct cuesheet *)audio_scratch_memory, | ||
731 | sizeof (struct audio_scratch_memory)); | ||
732 | } | ||
790 | } | 733 | } |
791 | 734 | ||
792 | int audio_status(void) | 735 | /* Set up the audio buffer for playback */ |
736 | static void audio_reset_buffer(void) | ||
793 | { | 737 | { |
794 | int ret = 0; | 738 | /* |
739 | * Layout audio buffer as follows: | ||
740 | * [[|TALK]|SCRATCH|BUFFERING|PCM|] | ||
741 | */ | ||
795 | 742 | ||
796 | if (playing) | 743 | /* see audio_get_recording_buffer if this is modified */ |
797 | ret |= AUDIO_STATUS_PLAY; | 744 | logf("%s()", __func__); |
798 | 745 | ||
799 | if (paused) | 746 | /* If the setup of anything allocated before the file buffer is |
800 | ret |= AUDIO_STATUS_PAUSE; | 747 | changed, do check the adjustments after the buffer_alloc call |
748 | as it will likely be affected and need sliding over */ | ||
801 | 749 | ||
802 | #ifdef HAVE_RECORDING | 750 | /* Initially set up file buffer as all space available */ |
803 | /* Do this here for constitency with mpeg.c version */ | 751 | unsigned char *filebuf = audiobuf + talk_get_bufsize(); |
804 | /* FIXME: pcm_rec_status() is deprecated */ | 752 | size_t filebuflen = audiobufend - filebuf; |
805 | ret |= pcm_rec_status(); | 753 | size_t allocsize; |
806 | #endif | ||
807 | 754 | ||
808 | return ret; | 755 | ALIGN_BUFFER(filebuf, filebuflen, sizeof (intptr_t)); |
809 | } | ||
810 | 756 | ||
811 | bool audio_automatic_skip(void) | 757 | /* Subtract whatever the pcm buffer says it used plus the guard buffer */ |
812 | { | 758 | allocsize = pcmbuf_init(filebuf + filebuflen); |
813 | return automatic_skip; | ||
814 | } | ||
815 | 759 | ||
816 | int audio_get_file_pos(void) | 760 | /* Make sure filebuflen is a pointer sized multiple after adjustment */ |
817 | { | 761 | allocsize = ALIGN_UP(allocsize, sizeof (intptr_t)); |
818 | return 0; | 762 | if (allocsize > filebuflen) |
819 | } | 763 | goto bufpanic; |
820 | 764 | ||
821 | #ifdef HAVE_DISK_STORAGE | 765 | filebuflen -= allocsize; |
822 | void audio_set_buffer_margin(int setting) | ||
823 | { | ||
824 | static const unsigned short lookup[] = {5, 15, 30, 60, 120, 180, 300, 600}; | ||
825 | buffer_margin = lookup[setting]; | ||
826 | logf("buffer margin: %ld", (long)buffer_margin); | ||
827 | set_filebuf_watermark(); | ||
828 | } | ||
829 | #endif | ||
830 | 766 | ||
831 | #ifdef HAVE_CROSSFADE | 767 | /* Scratch memory */ |
832 | /* Take necessary steps to enable or disable the crossfade setting */ | 768 | allocsize = scratch_mem_size(); |
833 | void audio_set_crossfade(int enable) | 769 | if (allocsize > filebuflen) |
834 | { | 770 | goto bufpanic; |
835 | size_t offset; | ||
836 | bool was_playing; | ||
837 | size_t size; | ||
838 | 771 | ||
839 | /* Tell it the next setting to use */ | 772 | scratch_mem_init(filebuf); |
840 | pcmbuf_request_crossfade_enable(enable); | 773 | filebuf += allocsize; |
774 | filebuflen -= allocsize; | ||
841 | 775 | ||
842 | /* Return if size hasn't changed or this is too early to determine | 776 | buffering_reset(filebuf, filebuflen); |
843 | which in the second case there's no way we could be playing | ||
844 | anything at all */ | ||
845 | if (pcmbuf_is_same_size()) return; | ||
846 | 777 | ||
847 | offset = 0; | 778 | /* Clear any references to the file buffer */ |
848 | was_playing = playing; | 779 | buffer_state = AUDIOBUF_STATE_INITIALIZED; |
849 | 780 | ||
850 | /* Playback has to be stopped before changing the buffer size */ | 781 | #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) |
851 | if (was_playing) | 782 | /* Make sure everything adds up - yes, some info is a bit redundant but |
783 | aids viewing and the sumation of certain variables should add up to | ||
784 | the location of others. */ | ||
852 | { | 785 | { |
853 | /* Store the track resume position */ | 786 | size_t pcmbufsize; |
854 | offset = thistrack_id3->offset; | 787 | const unsigned char *pcmbuf = pcmbuf_get_meminfo(&pcmbufsize); |
788 | logf("fbuf: %08X", (unsigned)filebuf); | ||
789 | logf("fbufe: %08X", (unsigned)(filebuf + filebuflen)); | ||
790 | logf("sbuf: %08X", (unsigned)audio_scratch_memory); | ||
791 | logf("sbufe: %08X", (unsigned)(audio_scratch_memory + allocsize)); | ||
792 | logf("pcmb: %08X", (unsigned)pcmbuf); | ||
793 | logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize)); | ||
855 | } | 794 | } |
795 | #endif | ||
856 | 796 | ||
857 | /* Blast it - audio buffer will have to be setup again next time | 797 | return; |
858 | something plays */ | ||
859 | audio_get_buffer(true, &size); | ||
860 | 798 | ||
861 | /* Restart playback if audio was running previously */ | 799 | bufpanic: |
862 | if (was_playing) | 800 | panicf("%s(): EOM (%zu > %zu)", __func__, allocsize, filebuflen); |
863 | audio_play(offset); | ||
864 | } | 801 | } |
865 | #endif | ||
866 | |||
867 | /* --- Routines called from multiple threads --- */ | ||
868 | 802 | ||
869 | static void set_filebuf_watermark(void) | 803 | /* Set the buffer margin to begin rebuffering when 'seconds' from empty */ |
804 | static void audio_update_filebuf_watermark(int seconds) | ||
870 | { | 805 | { |
871 | if (!filebuf) | 806 | size_t bytes = 0; |
872 | return; /* Audio buffers not yet set up */ | ||
873 | 807 | ||
874 | #ifdef HAVE_DISK_STORAGE | 808 | #ifdef HAVE_DISK_STORAGE |
875 | int seconds; | ||
876 | int spinup = ata_spinup_time(); | 809 | int spinup = ata_spinup_time(); |
810 | |||
811 | if (seconds == 0) | ||
812 | { | ||
813 | /* By current setting */ | ||
814 | seconds = buffer_margin; | ||
815 | } | ||
816 | else | ||
817 | { | ||
818 | /* New setting */ | ||
819 | buffer_margin = seconds; | ||
820 | |||
821 | if (buf_get_watermark() == 0) | ||
822 | { | ||
823 | /* Write a watermark only if the audio thread already did so for | ||
824 | itself or it will fail to set the event and the watermark - if | ||
825 | it hasn't yet, it will use the new setting when it does */ | ||
826 | return; | ||
827 | } | ||
828 | } | ||
829 | |||
877 | if (spinup) | 830 | if (spinup) |
878 | seconds = (spinup / HZ) + 1; | 831 | seconds += (spinup / HZ) + 1; |
879 | else | 832 | else |
880 | seconds = 5; | 833 | seconds += 5; |
881 | 834 | ||
882 | seconds += buffer_margin; | 835 | seconds += buffer_margin; |
883 | #else | 836 | #else |
884 | /* flash storage */ | 837 | /* flash storage */ |
885 | int seconds = 1; | 838 | seconds = 1; |
886 | #endif | 839 | #endif |
887 | 840 | ||
888 | /* bitrate of last track in buffer dictates watermark */ | 841 | /* Watermark is a function of the bitrate of the last track in the buffer */ |
889 | struct mp3entry* id3 = NULL; | 842 | struct mp3entry *id3 = NULL; |
890 | if (tracks[track_widx].taginfo_ready) | 843 | struct track_info *info = track_list_last(0); |
891 | id3 = bufgetid3(tracks[track_widx].id3_hid); | 844 | |
845 | if (info) | ||
846 | id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | ||
847 | |||
848 | if (id3) | ||
849 | { | ||
850 | if (get_audio_base_data_type(id3->codectype) == TYPE_PACKET_AUDIO) | ||
851 | { | ||
852 | bytes = id3->bitrate * (1000/8) * seconds; | ||
853 | } | ||
854 | else | ||
855 | { | ||
856 | /* Bitrate has no meaning to buffering margin for atomic audio - | ||
857 | rebuffer when it's the only track left unless it's the only | ||
858 | track that fits, in which case we should avoid constant buffer | ||
859 | low events */ | ||
860 | if (track_list_count() > 1) | ||
861 | bytes = info->filesize + 1; | ||
862 | } | ||
863 | } | ||
892 | else | 864 | else |
893 | id3 = bufgetid3(tracks[track_widx-1].id3_hid); | 865 | { |
894 | if (!id3) { | 866 | /* Then set the minimum - this should not occur anyway */ |
895 | logf("fwmark: No id3 for last track (r%d/w%d), aborting!", track_ridx, track_widx); | 867 | logf("fwmark: No id3 for last track (s%u/c%u/e%u)", |
896 | return; | 868 | track_list.start, track_list.current, track_list.end); |
897 | } | 869 | } |
898 | size_t bytes = id3->bitrate * (1000/8) * seconds; | 870 | |
899 | buf_set_watermark(bytes); | 871 | /* Actually setting zero disables the notification and we use that |
900 | logf("fwmark: %d", bytes); | 872 | to detect that it has been reset */ |
873 | buf_set_watermark(MAX(bytes, 1)); | ||
874 | logf("fwmark: %lu", (unsigned long)bytes); | ||
901 | } | 875 | } |
902 | 876 | ||
903 | /* --- Buffering callbacks --- */ | ||
904 | 877 | ||
905 | static void buffering_low_buffer_callback(void *data) | 878 | /** -- Track change notification -- **/ |
879 | |||
880 | /* Check the pcmbuf track changes and return write the message into the event | ||
881 | if there are any */ | ||
882 | static inline bool audio_pcmbuf_track_change_scan(void) | ||
906 | { | 883 | { |
907 | (void)data; | 884 | if (track_change.out != track_change.in) |
908 | logf("low buffer callback"); | 885 | { |
886 | track_change.out++; | ||
887 | return true; | ||
888 | } | ||
889 | |||
890 | return false; | ||
891 | } | ||
892 | |||
893 | /* Clear outstanding track change posts */ | ||
894 | static inline void audio_pcmbuf_track_change_clear(void) | ||
895 | { | ||
896 | track_change.out = track_change.in; | ||
897 | } | ||
898 | |||
899 | /* Post a track change notification - called by audio ISR */ | ||
900 | static inline void audio_pcmbuf_track_change_post(void) | ||
901 | { | ||
902 | track_change.in++; | ||
903 | } | ||
909 | 904 | ||
910 | if (filling == STATE_FULL || filling == STATE_END_OF_PLAYLIST) { | 905 | |
911 | /* force a refill */ | 906 | /** --- Helper functions --- **/ |
912 | LOGFQUEUE("buffering > audio Q_AUDIO_FILL_BUFFER"); | 907 | |
913 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | 908 | /* Removes messages that might end up in the queue before or while processing |
909 | a manual track change. Responding to them would be harmful since they | ||
910 | belong to a previous track's playback period. Anything that would generate | ||
911 | the stale messages must first be put into a state where it will not do so. | ||
912 | */ | ||
913 | static void audio_clear_track_notifications(void) | ||
914 | { | ||
915 | static const long filter_list[][2] = | ||
916 | { | ||
917 | /* codec messages */ | ||
918 | { Q_AUDIO_CODEC_SEEK_COMPLETE, Q_AUDIO_CODEC_COMPLETE }, | ||
919 | /* track change messages */ | ||
920 | { Q_AUDIO_TRACK_CHANGED, Q_AUDIO_TRACK_CHANGED }, | ||
921 | }; | ||
922 | |||
923 | const int filter_count = ARRAYLEN(filter_list) - 1; | ||
924 | |||
925 | /* Remove any pcmbuf notifications */ | ||
926 | pcmbuf_monitor_track_change(false); | ||
927 | audio_pcmbuf_track_change_clear(); | ||
928 | |||
929 | /* Scrub the audio queue of the old mold */ | ||
930 | while (queue_peek_ex(&audio_queue, NULL, | ||
931 | filter_count | QPEEK_REMOVE_EVENTS, | ||
932 | filter_list)) | ||
933 | { | ||
934 | yield(); /* Not strictly needed, per se, ad infinitum, ra, ra */ | ||
914 | } | 935 | } |
915 | } | 936 | } |
916 | 937 | ||
917 | static void buffering_handle_rebuffer_callback(void *data) | 938 | /* Takes actions based upon track load status codes */ |
939 | static void audio_handle_track_load_status(int trackstat) | ||
918 | { | 940 | { |
919 | (void)data; | 941 | switch (trackstat) |
920 | LOGFQUEUE("audio >| audio Q_AUDIO_FLUSH"); | 942 | { |
921 | queue_post(&audio_queue, Q_AUDIO_FLUSH, 0); | 943 | case LOAD_TRACK_ERR_NO_MORE: |
944 | if (track_list_count() > 0) | ||
945 | break; | ||
946 | |||
947 | case LOAD_TRACK_ERR_START_CODEC: | ||
948 | audio_queue_post(Q_AUDIO_CODEC_COMPLETE, CODEC_ERROR); | ||
949 | break; | ||
950 | |||
951 | default: | ||
952 | break; | ||
953 | } | ||
922 | } | 954 | } |
923 | 955 | ||
924 | static void buffering_handle_finished_callback(void *data) | 956 | /* Announce the end of playing the current track */ |
957 | static void audio_playlist_track_finish(void) | ||
925 | { | 958 | { |
926 | logf("handle %d finished buffering", *(int*)data); | 959 | struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); |
927 | int hid = (*(int*)data); | ||
928 | 960 | ||
929 | if (hid == tracks[track_widx].id3_hid) | 961 | playlist_update_resume_info(filling == STATE_ENDED ? NULL : id3); |
962 | |||
963 | if (id3) | ||
930 | { | 964 | { |
931 | int offset = ci.new_track + wps_offset; | 965 | send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); |
932 | int next_idx = (track_ridx + offset + 1) & MAX_TRACK_MASK; | 966 | prev_track_elapsed = id3->elapsed; |
933 | /* The metadata handle for the last loaded track has been buffered. | ||
934 | We can ask the audio thread to load the rest of the track's data. */ | ||
935 | LOGFQUEUE("audio > audio Q_AUDIO_FINISH_LOAD"); | ||
936 | queue_post(&audio_queue, Q_AUDIO_FINISH_LOAD, 0); | ||
937 | if (tracks[next_idx].id3_hid == hid) | ||
938 | send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, NULL); | ||
939 | } | 967 | } |
940 | else | 968 | else |
941 | { | 969 | { |
942 | /* This is most likely an audio handle, so we strip the useless | 970 | prev_track_elapsed = 0; |
943 | trailing tags that are left. */ | ||
944 | strip_tags(hid); | ||
945 | |||
946 | if (hid == tracks[track_widx-1].audio_hid | ||
947 | && filling == STATE_END_OF_PLAYLIST) | ||
948 | { | ||
949 | /* This was the last track in the playlist. | ||
950 | We now have all the data we need. */ | ||
951 | logf("last track finished buffering"); | ||
952 | filling = STATE_FINISHED; | ||
953 | } | ||
954 | } | 971 | } |
955 | } | 972 | } |
956 | 973 | ||
974 | /* Announce the beginning of the new track */ | ||
975 | static void audio_playlist_track_change(void) | ||
976 | { | ||
977 | struct mp3entry *id3 = valid_mp3entry(id3_get(PLAYING_ID3)); | ||
978 | |||
979 | if (id3) | ||
980 | send_event(PLAYBACK_EVENT_TRACK_CHANGE, id3); | ||
957 | 981 | ||
958 | /* --- Audio thread --- */ | 982 | playlist_update_resume_info(id3); |
983 | } | ||
959 | 984 | ||
960 | static bool audio_have_tracks(void) | 985 | /* Change the data for the next track and send the event */ |
986 | static void audio_update_and_announce_next_track(const struct mp3entry *id3_next) | ||
961 | { | 987 | { |
962 | return (audio_track_count() != 0); | 988 | id3_write_locked(NEXTTRACK_ID3, id3_next); |
989 | send_event(PLAYBACK_EVENT_NEXTTRACKID3_AVAILABLE, | ||
990 | id3_get(NEXTTRACK_ID3)); | ||
963 | } | 991 | } |
964 | 992 | ||
965 | static int audio_free_track_count(void) | 993 | /* Bring the user current mp3entry up to date and set a new offset for the |
994 | buffered metadata */ | ||
995 | static void playing_id3_sync(struct track_info *user_info, size_t offset) | ||
966 | { | 996 | { |
967 | /* Used tracks + free tracks adds up to MAX_TRACK - 1 */ | 997 | id3_mutex_lock(); |
968 | return MAX_TRACK - 1 - audio_track_count(); | 998 | |
999 | struct mp3entry *id3 = bufgetid3(user_info->id3_hid); | ||
1000 | |||
1001 | if (offset == (size_t)-1) | ||
1002 | { | ||
1003 | struct mp3entry *ply_id3 = id3_get(PLAYING_ID3); | ||
1004 | size_t play_offset = ply_id3->offset; | ||
1005 | long play_elapsed = ply_id3->elapsed; | ||
1006 | id3_write(PLAYING_ID3, id3); | ||
1007 | ply_id3->offset = play_offset; | ||
1008 | ply_id3->elapsed = play_elapsed; | ||
1009 | offset = 0; | ||
1010 | } | ||
1011 | else | ||
1012 | { | ||
1013 | id3_write(PLAYING_ID3, id3); | ||
1014 | } | ||
1015 | |||
1016 | if (id3) | ||
1017 | id3->offset = offset; | ||
1018 | |||
1019 | id3_mutex_unlock(); | ||
969 | } | 1020 | } |
970 | 1021 | ||
971 | int audio_track_count(void) | 1022 | /* Wipe-out track metadata - current is optional */ |
1023 | static void wipe_track_metadata(bool current) | ||
972 | { | 1024 | { |
973 | /* Calculate difference from track_ridx to track_widx | 1025 | id3_mutex_lock(); |
974 | * taking into account a possible wrap-around. */ | 1026 | |
975 | return (MAX_TRACK + track_widx - track_ridx) & MAX_TRACK_MASK; | 1027 | if (current) |
1028 | id3_write(PLAYING_ID3, NULL); | ||
1029 | |||
1030 | id3_write(NEXTTRACK_ID3, NULL); | ||
1031 | id3_write(UNBUFFERED_ID3, NULL); | ||
1032 | |||
1033 | id3_mutex_unlock(); | ||
976 | } | 1034 | } |
977 | 1035 | ||
978 | long audio_filebufused(void) | 1036 | /* Called when buffering is completed on the last track handle */ |
1037 | static void filling_is_finished(void) | ||
979 | { | 1038 | { |
980 | return (long) buf_used(); | 1039 | logf("last track finished buffering"); |
1040 | |||
1041 | /* There's no more to load or watch for */ | ||
1042 | buf_set_watermark(0); | ||
1043 | filling = STATE_FINISHED; | ||
981 | } | 1044 | } |
982 | 1045 | ||
983 | /* Update track info after successful a codec track change */ | 1046 | /* Stop the codec decoding or waiting for its data to be ready - returns |
984 | static void audio_update_trackinfo(void) | 1047 | 'false' if the codec ended up stopped */ |
1048 | static bool halt_decoding_track(bool stop) | ||
985 | { | 1049 | { |
986 | bool resume = false; | 1050 | /* If it was waiting for us to clear the buffer to make a rebuffer |
1051 | happen, it should cease otherwise codec_stop could deadlock waiting | ||
1052 | for the codec to go to its main loop - codec's request will now | ||
1053 | force-fail */ | ||
1054 | bool retval = false; | ||
987 | 1055 | ||
988 | /* Load the curent track's metadata into curtrack_id3 */ | 1056 | buf_signal_handle(ci.audio_hid, true); |
989 | if (CUR_TI->id3_hid >= 0) | ||
990 | bufreadid3(CUR_TI->id3_hid, thistrack_id3); | ||
991 | 1057 | ||
992 | /* Reset current position */ | 1058 | if (stop) |
993 | thistrack_id3->elapsed = 0; | 1059 | codec_stop(); |
1060 | else | ||
1061 | retval = codec_pause(); | ||
994 | 1062 | ||
995 | #ifdef HAVE_TAGCACHE | 1063 | audio_clear_track_notifications(); |
996 | /* Ignoring resume position for automatic track change if so configured */ | ||
997 | resume = global_settings.autoresume_enable && | ||
998 | (!automatic_skip /* Resume all manually selected tracks */ | ||
999 | || global_settings.autoresume_automatic == AUTORESUME_NEXTTRACK_ALWAYS | ||
1000 | || (global_settings.autoresume_automatic != AUTORESUME_NEXTTRACK_NEVER | ||
1001 | /* Not never resume? */ | ||
1002 | && autoresumable(thistrack_id3))); /* Pass Resume filter? */ | ||
1003 | #endif | ||
1004 | 1064 | ||
1005 | if (!resume) | 1065 | /* We now know it's idle and not waiting for buffered data */ |
1006 | { | 1066 | buf_signal_handle(ci.audio_hid, false); |
1007 | thistrack_id3->offset = 0; | ||
1008 | } | ||
1009 | 1067 | ||
1010 | logf("audio_update_trackinfo: Set offset for %s to %lX\n", | 1068 | codec_skip_pending = false; |
1011 | thistrack_id3->title, thistrack_id3->offset); | 1069 | codec_seeking = false; |
1012 | 1070 | ||
1013 | /* Update the codec API */ | 1071 | return retval; |
1014 | ci.filesize = CUR_TI->filesize; | ||
1015 | ci.id3 = thistrack_id3; | ||
1016 | ci.curpos = 0; | ||
1017 | ci.taginfo_ready = &CUR_TI->taginfo_ready; | ||
1018 | } | 1072 | } |
1019 | 1073 | ||
1020 | /* Clear tracks between write and read, non inclusive */ | 1074 | /* Clear the PCM on a manual skip */ |
1021 | static void audio_clear_track_entries(void) | 1075 | static void audio_clear_paused_pcm(void) |
1022 | { | 1076 | { |
1023 | int cur_idx = track_widx; | 1077 | if (play_status == PLAY_PAUSED && !pcmbuf_is_crossfade_active()) |
1024 | 1078 | pcmbuf_play_stop(); | |
1025 | logf("Clearing tracks: r%d/w%d", track_ridx, track_widx); | 1079 | } |
1026 | 1080 | ||
1027 | /* Loop over all tracks from write-to-read */ | 1081 | /* End the ff/rw mode */ |
1028 | while (1) | 1082 | static void audio_ff_rewind_end(void) |
1083 | { | ||
1084 | /* A seamless seek (not calling audio_pre_ff_rewind) skips this | ||
1085 | section */ | ||
1086 | if (ff_rw_mode) | ||
1029 | { | 1087 | { |
1030 | cur_idx = (cur_idx + 1) & MAX_TRACK_MASK; | 1088 | ff_rw_mode = false; |
1031 | 1089 | ||
1032 | if (cur_idx == track_ridx) | 1090 | if (codec_seeking) |
1033 | break; | 1091 | { |
1092 | /* Clear the buffer */ | ||
1093 | pcmbuf_play_stop(); | ||
1094 | } | ||
1034 | 1095 | ||
1035 | clear_track_info(&tracks[cur_idx]); | 1096 | if (play_status != PLAY_PAUSED) |
1097 | { | ||
1098 | /* Seeking-while-playing, resume PCM playback */ | ||
1099 | pcmbuf_pause(false); | ||
1100 | } | ||
1036 | } | 1101 | } |
1037 | } | 1102 | } |
1038 | 1103 | ||
1039 | /* Clear all tracks */ | 1104 | /* Complete the codec seek */ |
1040 | static bool audio_release_tracks(void) | 1105 | static void audio_complete_codec_seek(void) |
1041 | { | 1106 | { |
1042 | int i, cur_idx; | 1107 | /* If a seek completed while paused, 'paused' is true. |
1043 | 1108 | * If seeking from seek mode, 'ff_rw_mode' is true. */ | |
1044 | logf("releasing all tracks"); | 1109 | if (codec_seeking) |
1045 | |||
1046 | for(i = 0; i < MAX_TRACK; i++) | ||
1047 | { | 1110 | { |
1048 | cur_idx = (track_ridx + i) & MAX_TRACK_MASK; | 1111 | audio_ff_rewind_end(); |
1049 | if (!clear_track_info(&tracks[cur_idx])) | 1112 | codec_seeking = false; /* set _after_ the call! */ |
1050 | return false; | ||
1051 | } | 1113 | } |
1114 | /* else it's waiting and we must repond */ | ||
1115 | } | ||
1052 | 1116 | ||
1053 | return true; | 1117 | /* Get the current cuesheet pointer */ |
1118 | static inline struct cuesheet * get_current_cuesheet(void) | ||
1119 | { | ||
1120 | return audio_scratch_memory->curr_cue; | ||
1054 | } | 1121 | } |
1055 | 1122 | ||
1056 | static bool audio_loadcodec(bool start_play) | 1123 | /* Read the cuesheet from the buffer */ |
1124 | static void buf_read_cuesheet(int handle_id) | ||
1057 | { | 1125 | { |
1058 | int prev_track, hid; | 1126 | struct cuesheet *cue = get_current_cuesheet(); |
1059 | char codec_path[MAX_PATH]; /* Full path to codec */ | ||
1060 | const struct mp3entry *id3, *prev_id3; | ||
1061 | 1127 | ||
1062 | if (tracks[track_widx].id3_hid < 0) { | 1128 | if (!cue || handle_id < 0) |
1063 | return false; | 1129 | return; |
1064 | } | ||
1065 | 1130 | ||
1066 | id3 = bufgetid3(tracks[track_widx].id3_hid); | 1131 | bufread(handle_id, sizeof (struct cuesheet), cue); |
1067 | if (!id3) | 1132 | } |
1068 | return false; | ||
1069 | 1133 | ||
1070 | const char *codec_fn = get_codec_filename(id3->codectype); | 1134 | /* Backend to peek/current/next track metadata interface functions - |
1071 | if (codec_fn == NULL) | 1135 | fill in the mp3entry with as much information as we may obtain about |
1136 | the track at the specified offset from the user current track - | ||
1137 | returns false if no information exists with us */ | ||
1138 | static bool audio_get_track_metadata(int offset, struct mp3entry *id3) | ||
1139 | { | ||
1140 | if (play_status == PLAY_STOPPED) | ||
1072 | return false; | 1141 | return false; |
1073 | 1142 | ||
1074 | tracks[track_widx].codec_hid = -1; | 1143 | if (id3->path[0] != '\0') |
1144 | return true; /* Already filled */ | ||
1145 | |||
1146 | struct track_info *info = track_list_user_current(offset); | ||
1075 | 1147 | ||
1076 | if (start_play) | 1148 | if (!info) |
1077 | { | 1149 | { |
1078 | /* Load the codec directly from disk and save some memory. */ | 1150 | struct mp3entry *ub_id3 = id3_get(UNBUFFERED_ID3); |
1079 | track_ridx = track_widx; | 1151 | |
1080 | ci.filesize = CUR_TI->filesize; | 1152 | if (offset > 0 && track_list_user_current(offset - 1)) |
1081 | ci.id3 = thistrack_id3; | 1153 | { |
1082 | ci.taginfo_ready = &CUR_TI->taginfo_ready; | 1154 | /* Try the unbuffered id3 since we're moving forward */ |
1083 | ci.curpos = 0; | 1155 | if (ub_id3->path[0] != '\0') |
1084 | return codec_load(-1, id3->codectype); | 1156 | { |
1157 | copy_mp3entry(id3, ub_id3); | ||
1158 | return true; | ||
1159 | } | ||
1160 | } | ||
1085 | } | 1161 | } |
1086 | else | 1162 | else if (bufreadid3(info->id3_hid, id3)) |
1087 | { | 1163 | { |
1088 | /* If we already have another track than this one buffered */ | 1164 | return true; |
1089 | if (track_widx != track_ridx) | 1165 | } |
1090 | { | ||
1091 | prev_track = (track_widx - 1) & MAX_TRACK_MASK; | ||
1092 | |||
1093 | id3 = bufgetid3(tracks[track_widx].id3_hid); | ||
1094 | prev_id3 = bufgetid3(tracks[prev_track].id3_hid); | ||
1095 | 1166 | ||
1096 | /* If the previous codec is the same as this one and the current | 1167 | /* We didn't find the ID3 metadata, so we fill it with the little info we |
1097 | * one is the correct one, there is no need to put another copy of | 1168 | have and return that */ |
1098 | * it on the file buffer */ | ||
1099 | if (id3 && prev_id3) | ||
1100 | { | ||
1101 | int codt = get_codec_base_type(id3->codectype); | ||
1102 | int prev_codt = get_codec_base_type(prev_id3->codectype); | ||
1103 | int cod_loaded = get_codec_base_type(codec_loaded()); | ||
1104 | 1169 | ||
1105 | if (codt == prev_codt && codt == cod_loaded) | 1170 | char path[MAX_PATH+1]; |
1106 | { | 1171 | if (playlist_peek(offset, path, sizeof (path))) |
1107 | logf("Reusing prev. codec"); | 1172 | { |
1108 | return true; | 1173 | #if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE) |
1109 | } | 1174 | /* Try to get it from the database */ |
1110 | } | 1175 | if (!tagcache_fill_tags(id3, path)) |
1176 | #endif | ||
1177 | { | ||
1178 | /* By now, filename is the only source of info */ | ||
1179 | fill_metadata_from_path(id3, path); | ||
1111 | } | 1180 | } |
1181 | |||
1182 | return true; | ||
1112 | } | 1183 | } |
1113 | 1184 | ||
1114 | codec_get_full_path(codec_path, codec_fn); | 1185 | wipe_mp3entry(id3); |
1115 | 1186 | ||
1116 | hid = tracks[track_widx].codec_hid = bufopen(codec_path, 0, TYPE_CODEC, NULL); | 1187 | return false; |
1188 | } | ||
1117 | 1189 | ||
1118 | /* not an error if codec load it supported, will load it from disk | 1190 | /* Get a resume rewind adjusted offset from the ID3 */ |
1119 | * application builds don't support it | 1191 | unsigned long resume_rewind_adjusted_offset(const struct mp3entry *id3) |
1120 | */ | 1192 | { |
1121 | if (hid < 0 && hid != ERR_UNSUPPORTED_TYPE) | 1193 | unsigned long offset = id3->offset; |
1122 | return false; | 1194 | size_t resume_rewind = global_settings.resume_rewind * |
1195 | id3->bitrate * (1000/8); | ||
1123 | 1196 | ||
1124 | if (hid >= 0) | 1197 | if (offset < resume_rewind) |
1125 | logf("Loaded codec"); | 1198 | offset = 0; |
1126 | else | 1199 | else |
1127 | logf("Buffering codec unsupported, load later from disk"); | 1200 | offset -= resume_rewind; |
1128 | 1201 | ||
1129 | return true; | 1202 | return offset; |
1130 | } | 1203 | } |
1131 | 1204 | ||
1132 | /* Load metadata for the next track (with bufopen). The rest of the track | 1205 | /* Get the codec into ram and initialize it - keep it if it's ready */ |
1133 | loading will be handled by audio_finish_load_track once the metadata has been | 1206 | static bool audio_init_codec(struct track_info *track_info, |
1134 | actually loaded by the buffering thread. */ | 1207 | struct mp3entry *track_id3) |
1135 | static bool audio_load_track(size_t offset, bool start_play) | ||
1136 | { | 1208 | { |
1137 | char name_buf[MAX_PATH + 1]; | 1209 | int codt_loaded = get_audio_base_codec_type(codec_loaded()); |
1138 | const char *trackname; | 1210 | int hid = ERR_HANDLE_NOT_FOUND; |
1139 | int fd = -1; | ||
1140 | 1211 | ||
1141 | if (track_load_started) { | 1212 | if (codt_loaded != AFMT_UNKNOWN) |
1142 | /* There is already a track load in progress, so track_widx hasn't been | 1213 | { |
1143 | incremented yet. Loading another track would overwrite the one that | 1214 | int codt = get_audio_base_codec_type(track_id3->codectype); |
1144 | hasn't finished loading. */ | 1215 | |
1145 | logf("audio_load_track(): a track load is already in progress"); | 1216 | if (codt == codt_loaded) |
1146 | return false; | 1217 | { |
1218 | /* Codec is the same base type */ | ||
1219 | logf("Reusing prev. codec: %d", track_id3->codectype); | ||
1220 | #ifdef HAVE_CODEC_BUFFERING | ||
1221 | /* Close any buffered codec (we could have skipped directly to a | ||
1222 | format transistion that is the same format as the current track | ||
1223 | and the buffered one is no longer needed) */ | ||
1224 | track_info_close_handle(&track_info->codec_hid); | ||
1225 | #endif | ||
1226 | return true; | ||
1227 | } | ||
1228 | else | ||
1229 | { | ||
1230 | /* New codec - first make sure the old one's gone */ | ||
1231 | logf("Removing prev. codec: %d", codt_loaded); | ||
1232 | codec_unload(); | ||
1233 | } | ||
1147 | } | 1234 | } |
1148 | 1235 | ||
1149 | start_play_g = start_play; /* will be read by audio_finish_load_track */ | 1236 | logf("New codec: %d/%d", track_id3->codectype, codec_loaded()); |
1150 | 1237 | ||
1151 | /* Stop buffer filling if there is no free track entries. | 1238 | #ifdef HAVE_CODEC_BUFFERING |
1152 | Don't fill up the last track entry (we wan't to store next track | 1239 | /* Codec thread will close the handle even if it fails and will load from |
1153 | metadata there). */ | 1240 | storage if hid is not valid or the buffer load fails */ |
1154 | if (!audio_free_track_count()) | 1241 | hid = track_info->codec_hid; |
1155 | { | 1242 | track_info->codec_hid = ERR_HANDLE_NOT_FOUND; |
1156 | logf("No free tracks"); | 1243 | #endif |
1244 | |||
1245 | return codec_load(hid, track_id3->codectype); | ||
1246 | (void)track_info; /* When codec buffering isn't supported */ | ||
1247 | } | ||
1248 | |||
1249 | /* Start the codec for the current track scheduled to be decoded */ | ||
1250 | static bool audio_start_codec(bool auto_skip) | ||
1251 | { | ||
1252 | struct track_info *info = track_list_current(0); | ||
1253 | struct mp3entry *cur_id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | ||
1254 | |||
1255 | if (!cur_id3) | ||
1157 | return false; | 1256 | return false; |
1158 | } | ||
1159 | 1257 | ||
1160 | last_peek_offset++; | 1258 | buf_pin_handle(info->id3_hid, true); |
1161 | tracks[track_widx].taginfo_ready = false; | ||
1162 | 1259 | ||
1163 | logf("Buffering track: r%d/w%d", track_ridx, track_widx); | 1260 | if (!audio_init_codec(info, cur_id3)) |
1164 | /* Get track name from current playlist read position. */ | ||
1165 | while ((trackname = playlist_peek(last_peek_offset, name_buf, | ||
1166 | sizeof(name_buf))) != NULL) | ||
1167 | { | 1261 | { |
1168 | /* Handle broken playlists. */ | 1262 | buf_pin_handle(info->id3_hid, false); |
1169 | fd = open(trackname, O_RDONLY); | 1263 | return false; |
1170 | if (fd < 0) | ||
1171 | { | ||
1172 | logf("Open failed"); | ||
1173 | /* Skip invalid entry from playlist. */ | ||
1174 | playlist_skip_entry(NULL, last_peek_offset); | ||
1175 | } | ||
1176 | else | ||
1177 | break; | ||
1178 | } | 1264 | } |
1179 | 1265 | ||
1180 | if (!trackname) | 1266 | #ifdef HAVE_TAGCACHE |
1267 | bool autoresume_enable = global_settings.autoresume_enable; | ||
1268 | |||
1269 | if (autoresume_enable && !cur_id3->offset) | ||
1181 | { | 1270 | { |
1182 | logf("End-of-playlist"); | 1271 | /* Resume all manually selected tracks */ |
1183 | memset(&unbuffered_id3, 0, sizeof(struct mp3entry)); | 1272 | bool resume = !auto_skip; |
1184 | filling = STATE_END_OF_PLAYLIST; | 1273 | |
1274 | /* Send the "buffer" event to obtain the resume position for the codec */ | ||
1275 | send_event(PLAYBACK_EVENT_TRACK_BUFFER, cur_id3); | ||
1185 | 1276 | ||
1186 | if (thistrack_id3->length == 0 && thistrack_id3->filesize == 0) | 1277 | if (!resume) |
1187 | { | 1278 | { |
1188 | /* Stop playback if no valid track was found. */ | 1279 | /* Automatic skip - do further tests to see if we should just |
1189 | audio_stop_playback(); | 1280 | ignore any autoresume position */ |
1281 | int autoresume_automatic = global_settings.autoresume_automatic; | ||
1282 | |||
1283 | switch (autoresume_automatic) | ||
1284 | { | ||
1285 | case AUTORESUME_NEXTTRACK_ALWAYS: | ||
1286 | /* Just resume unconditionally */ | ||
1287 | resume = true; | ||
1288 | break; | ||
1289 | case AUTORESUME_NEXTTRACK_NEVER: | ||
1290 | /* Force-rewind it */ | ||
1291 | break; | ||
1292 | default: | ||
1293 | /* Not "never resume" - pass resume filter? */ | ||
1294 | resume = autoresumable(cur_id3); | ||
1295 | } | ||
1190 | } | 1296 | } |
1191 | 1297 | ||
1192 | return false; | 1298 | if (!resume) |
1299 | cur_id3->offset = 0; | ||
1300 | |||
1301 | logf("%s: Set offset for %s to %lX\n", __func__, | ||
1302 | cur_id3->title, cur_id3->offset); | ||
1193 | } | 1303 | } |
1304 | #endif /* HAVE_TAGCACHE */ | ||
1194 | 1305 | ||
1195 | tracks[track_widx].filesize = filesize(fd); | 1306 | /* Rewind the required amount - if an autoresume was done, this also rewinds |
1307 | that by the setting's amount | ||
1196 | 1308 | ||
1197 | if (offset > tracks[track_widx].filesize) | 1309 | It would be best to have bookkeeping about whether or not the track |
1198 | offset = 0; | 1310 | sounded or not since skipping to it or else skipping to it while paused |
1311 | and back again will cause accumulation of silent rewinds - that's not | ||
1312 | our job to track directly nor could it be in any reasonable way | ||
1313 | */ | ||
1314 | cur_id3->offset = resume_rewind_adjusted_offset(cur_id3); | ||
1315 | |||
1316 | /* Update the codec API with the metadata and track info */ | ||
1317 | id3_write(CODEC_ID3, cur_id3); | ||
1199 | 1318 | ||
1200 | /* Set default values */ | 1319 | ci.audio_hid = info->audio_hid; |
1201 | if (start_play) | 1320 | ci.filesize = info->filesize; |
1321 | buf_set_base_handle(info->audio_hid); | ||
1322 | |||
1323 | /* All required data is now available for the codec */ | ||
1324 | codec_go(); | ||
1325 | |||
1326 | #ifdef HAVE_TAGCACHE | ||
1327 | if (!autoresume_enable || cur_id3->offset) | ||
1328 | #endif | ||
1202 | { | 1329 | { |
1203 | buf_set_watermark(filebuflen/2); | 1330 | /* Send the "buffer" event now */ |
1204 | dsp_configure(ci.dsp, DSP_RESET, 0); | 1331 | send_event(PLAYBACK_EVENT_TRACK_BUFFER, cur_id3); |
1205 | playlist_update_resume_info(audio_current_track()); | ||
1206 | } | 1332 | } |
1207 | 1333 | ||
1208 | /* Get track metadata if we don't already have it. */ | 1334 | buf_pin_handle(info->id3_hid, false); |
1209 | if (tracks[track_widx].id3_hid < 0) | 1335 | return true; |
1336 | |||
1337 | (void)auto_skip; /* ifndef HAVE_TAGCACHE */ | ||
1338 | } | ||
1339 | |||
1340 | |||
1341 | /** --- Audio thread --- **/ | ||
1342 | |||
1343 | /* Load and parse a cuesheet for the file - returns false if the buffer | ||
1344 | is full */ | ||
1345 | static bool audio_load_cuesheet(struct track_info *info, | ||
1346 | struct mp3entry *track_id3) | ||
1347 | { | ||
1348 | struct cuesheet *cue = get_current_cuesheet(); | ||
1349 | track_id3->cuesheet = NULL; | ||
1350 | |||
1351 | if (cue && info->cuesheet_hid == ERR_HANDLE_NOT_FOUND) | ||
1210 | { | 1352 | { |
1211 | tracks[track_widx].id3_hid = bufopen(trackname, 0, TYPE_ID3, NULL); | 1353 | /* If error other than a full buffer, then mark it "unsupported" to |
1354 | avoid reloading attempt */ | ||
1355 | int hid = ERR_UNSUPPORTED_TYPE; | ||
1356 | char cuepath[MAX_PATH]; | ||
1212 | 1357 | ||
1213 | if (tracks[track_widx].id3_hid < 0) | 1358 | #ifdef HAVE_IO_PRIORITY |
1359 | buf_back_off_storage(true); | ||
1360 | #endif | ||
1361 | if (look_for_cuesheet_file(track_id3->path, cuepath)) | ||
1214 | { | 1362 | { |
1215 | /* Buffer is full. */ | 1363 | hid = bufalloc(NULL, sizeof (struct cuesheet), TYPE_CUESHEET); |
1216 | get_metadata(&unbuffered_id3, fd, trackname); | ||
1217 | last_peek_offset--; | ||
1218 | close(fd); | ||
1219 | logf("buffer is full for now (get metadata)"); | ||
1220 | filling = STATE_FULL; | ||
1221 | return false; | ||
1222 | } | ||
1223 | 1364 | ||
1224 | if (track_widx == track_ridx) | 1365 | if (hid >= 0) |
1225 | { | 1366 | { |
1226 | /* TODO: Superfluos buffering call? */ | 1367 | void *cuesheet = NULL; |
1227 | buf_request_buffer_handle(tracks[track_widx].id3_hid); | 1368 | bufgetdata(hid, sizeof (struct cuesheet), &cuesheet); |
1228 | if (bufreadid3(tracks[track_widx].id3_hid, thistrack_id3)) | 1369 | |
1229 | { | 1370 | if (parse_cuesheet(cuepath, (struct cuesheet *)cuesheet)) |
1230 | thistrack_id3->offset = offset; | 1371 | { |
1231 | logf("audio_load_track: set offset for %s to %lX\n", | 1372 | /* Indicate cuesheet is present (while track remains |
1232 | thistrack_id3->title, | 1373 | buffered) */ |
1233 | offset); | 1374 | track_id3->cuesheet = cue; |
1375 | } | ||
1376 | else | ||
1377 | { | ||
1378 | bufclose(hid); | ||
1379 | hid = ERR_UNSUPPORTED_TYPE; | ||
1380 | } | ||
1234 | } | 1381 | } |
1235 | else | ||
1236 | memset(thistrack_id3, 0, sizeof(struct mp3entry)); | ||
1237 | } | 1382 | } |
1238 | 1383 | ||
1239 | if (start_play) | 1384 | #ifdef HAVE_IO_PRIORITY |
1385 | buf_back_off_storage(false); | ||
1386 | #endif | ||
1387 | if (hid == ERR_BUFFER_FULL) | ||
1240 | { | 1388 | { |
1241 | playlist_update_resume_info(audio_current_track()); | 1389 | logf("buffer is full for now (%s)", __func__); |
1390 | return false; | ||
1391 | } | ||
1392 | else | ||
1393 | { | ||
1394 | if (hid < 0) | ||
1395 | logf("Cuesheet loading failed"); | ||
1396 | |||
1397 | info->cuesheet_hid = hid; | ||
1242 | } | 1398 | } |
1243 | } | 1399 | } |
1244 | 1400 | ||
1245 | close(fd); | ||
1246 | track_load_started = true; /* Remember that we've started loading a track */ | ||
1247 | return true; | 1401 | return true; |
1248 | } | 1402 | } |
1249 | 1403 | ||
1250 | #ifdef HAVE_ALBUMART | 1404 | #ifdef HAVE_ALBUMART |
1251 | /* Load any album art for the file */ | 1405 | /* Load any album art for the file - returns false if the buffer is full */ |
1252 | static void audio_load_albumart(struct mp3entry *track_id3) | 1406 | static bool audio_load_albumart(struct track_info *info, |
1407 | struct mp3entry *track_id3) | ||
1253 | { | 1408 | { |
1254 | int i; | 1409 | int i; |
1255 | |||
1256 | FOREACH_ALBUMART(i) | 1410 | FOREACH_ALBUMART(i) |
1257 | { | 1411 | { |
1258 | struct bufopen_bitmap_data user_data; | 1412 | struct bufopen_bitmap_data user_data; |
1259 | int hid = ERR_HANDLE_NOT_FOUND; | 1413 | int *aa_hid = &info->aa_hid[i]; |
1414 | int hid = ERR_UNSUPPORTED_TYPE; | ||
1260 | 1415 | ||
1261 | /* albumart_slots may change during a yield of bufopen, | 1416 | /* albumart_slots may change during a yield of bufopen, |
1262 | * but that's no problem */ | 1417 | * but that's no problem */ |
1263 | if (tracks[track_widx].aa_hid[i] >= 0 || !albumart_slots[i].used) | 1418 | if (*aa_hid >= 0 || *aa_hid == ERR_UNSUPPORTED_TYPE || |
1419 | !albumart_slots[i].used) | ||
1264 | continue; | 1420 | continue; |
1265 | 1421 | ||
1266 | memset(&user_data, 0, sizeof(user_data)); | 1422 | memset(&user_data, 0, sizeof(user_data)); |
1267 | user_data.dim = &(albumart_slots[i].dim); | 1423 | user_data.dim = &albumart_slots[i].dim; |
1268 | 1424 | ||
1269 | /* we can only decode jpeg for embedded AA */ | 1425 | #ifdef HAVE_IO_PRIORITY |
1426 | buf_back_off_storage(true); | ||
1427 | #endif | ||
1428 | |||
1429 | /* We can only decode jpeg for embedded AA */ | ||
1270 | if (track_id3->embed_albumart && track_id3->albumart.type == AA_TYPE_JPG) | 1430 | if (track_id3->embed_albumart && track_id3->albumart.type == AA_TYPE_JPG) |
1271 | { | 1431 | { |
1272 | user_data.embedded_albumart = &(track_id3->albumart); | 1432 | user_data.embedded_albumart = &track_id3->albumart; |
1273 | hid = bufopen(track_id3->path, 0, TYPE_BITMAP, &user_data); | 1433 | hid = bufopen(track_id3->path, 0, TYPE_BITMAP, &user_data); |
1274 | } | 1434 | } |
1275 | 1435 | ||
1276 | if (hid < 0 && hid != ERR_BUFFER_FULL) | 1436 | if (hid < 0 && hid != ERR_BUFFER_FULL) |
1277 | { | 1437 | { |
1278 | /* no embedded AA or it couldn't be loaded, try other sources */ | 1438 | /* No embedded AA or it couldn't be loaded - try other sources */ |
1279 | char path[MAX_PATH]; | 1439 | char path[MAX_PATH]; |
1280 | 1440 | ||
1281 | if (find_albumart(track_id3, path, sizeof(path), | 1441 | if (find_albumart(track_id3, path, sizeof(path), |
1282 | &(albumart_slots[i].dim))) | 1442 | &albumart_slots[i].dim)) |
1283 | { | 1443 | { |
1284 | user_data.embedded_albumart = NULL; | 1444 | user_data.embedded_albumart = NULL; |
1285 | hid = bufopen(path, 0, TYPE_BITMAP, &user_data); | 1445 | hid = bufopen(path, 0, TYPE_BITMAP, &user_data); |
1286 | } | 1446 | } |
1287 | } | 1447 | } |
1288 | 1448 | ||
1449 | #ifdef HAVE_IO_PRIORITY | ||
1450 | buf_back_off_storage(false); | ||
1451 | #endif | ||
1289 | if (hid == ERR_BUFFER_FULL) | 1452 | if (hid == ERR_BUFFER_FULL) |
1290 | { | 1453 | { |
1291 | filling = STATE_FULL; | 1454 | logf("buffer is full for now (%s)", __func__); |
1292 | logf("buffer is full for now (get album art)"); | 1455 | return false; |
1293 | } | 1456 | } |
1294 | else if (hid < 0) | 1457 | else |
1295 | { | 1458 | { |
1296 | logf("Album art loading failed"); | 1459 | /* If error other than a full buffer, then mark it "unsupported" |
1297 | } | 1460 | to avoid reloading attempt */ |
1461 | if (hid < 0) | ||
1462 | { | ||
1463 | logf("Album art loading failed"); | ||
1464 | hid = ERR_UNSUPPORTED_TYPE; | ||
1465 | } | ||
1298 | 1466 | ||
1299 | tracks[track_widx].aa_hid[i] = hid; | 1467 | *aa_hid = hid; |
1468 | } | ||
1300 | } | 1469 | } |
1470 | |||
1471 | return true; | ||
1301 | } | 1472 | } |
1302 | #endif | 1473 | #endif /* HAVE_ALBUMART */ |
1303 | 1474 | ||
1304 | /* Second part of the track loading: We now have the metadata available, so we | 1475 | #ifdef HAVE_CODEC_BUFFERING |
1305 | can load the codec, the album art and finally the audio data. | 1476 | /* Load a codec for the file onto the buffer - assumes we're working from the |
1306 | This is called on the audio thread after the buffering thread calls the | 1477 | currently loading track - not called for the current track */ |
1307 | buffering_handle_finished_callback callback. */ | 1478 | static bool audio_buffer_codec(struct track_info *track_info, |
1308 | static void audio_finish_load_track(void) | 1479 | struct mp3entry *track_id3) |
1309 | { | 1480 | { |
1310 | size_t file_offset = 0; | 1481 | /* This will not be the current track -> it cannot be the first and the |
1311 | size_t offset = 0; | 1482 | current track cannot be ahead of buffering -> there is a previous |
1312 | bool start_play = start_play_g; | 1483 | track entry which is either current or ahead of the current */ |
1484 | struct track_info *prev_info = track_list_last(-1); | ||
1485 | struct mp3entry *prev_id3 = bufgetid3(prev_info->id3_hid); | ||
1313 | 1486 | ||
1314 | track_load_started = false; | 1487 | /* If the previous codec is the same as this one, there is no need to |
1488 | put another copy of it on the file buffer (in other words, only | ||
1489 | buffer codecs at format transitions) */ | ||
1490 | if (prev_id3) | ||
1491 | { | ||
1492 | int codt = get_audio_base_codec_type(track_id3->codectype); | ||
1493 | int prev_codt = get_audio_base_codec_type(prev_id3->codectype); | ||
1315 | 1494 | ||
1316 | if (tracks[track_widx].id3_hid < 0) { | 1495 | if (codt == prev_codt) |
1317 | logf("No metadata"); | 1496 | { |
1318 | return; | 1497 | logf("Reusing prev. codec: %d", prev_id3->codectype); |
1498 | return true; | ||
1499 | } | ||
1319 | } | 1500 | } |
1501 | /* else just load it (harmless) */ | ||
1320 | 1502 | ||
1321 | struct mp3entry *track_id3; | 1503 | /* Load the codec onto the buffer if possible */ |
1504 | const char *codec_fn = get_codec_filename(track_id3->codectype); | ||
1505 | if (!codec_fn) | ||
1506 | return false; | ||
1322 | 1507 | ||
1323 | if (track_widx == track_ridx) | 1508 | char codec_path[MAX_PATH+1]; /* Full path to codec */ |
1324 | track_id3 = thistrack_id3; | 1509 | codec_get_full_path(codec_path, codec_fn); |
1325 | else | 1510 | |
1326 | track_id3 = bufgetid3(tracks[track_widx].id3_hid); | 1511 | track_info->codec_hid = bufopen(codec_path, 0, TYPE_CODEC, NULL); |
1327 | 1512 | ||
1328 | if (track_id3->length == 0 && track_id3->filesize == 0) | 1513 | if (track_info->codec_hid >= 0) |
1329 | { | 1514 | { |
1330 | logf("audio_finish_load_track: invalid metadata"); | 1515 | logf("Buffered codec: %d", afmt); |
1516 | return true; | ||
1517 | } | ||
1331 | 1518 | ||
1332 | /* Invalid metadata */ | 1519 | return false; |
1333 | bufclose(tracks[track_widx].id3_hid); | 1520 | } |
1334 | tracks[track_widx].id3_hid = -1; | 1521 | #endif /* HAVE_CODEC_BUFFERING */ |
1335 | 1522 | ||
1336 | /* Skip invalid entry from playlist. */ | 1523 | /* Load metadata for the next track (with bufopen). The rest of the track |
1337 | playlist_skip_entry(NULL, last_peek_offset--); | 1524 | loading will be handled by audio_finish_load_track once the metadata has |
1525 | been actually loaded by the buffering thread. | ||
1338 | 1526 | ||
1339 | /* load next track */ | 1527 | Each track is arranged in the buffer as follows: |
1340 | LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER %d", (int)start_play); | 1528 | <id3|[cuesheet|][album art|][codec|]audio> |
1341 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, start_play); | ||
1342 | 1529 | ||
1343 | return; | 1530 | The next will not be loaded until the previous succeeds if the buffer was |
1344 | } | 1531 | full at the time. To put any metadata after audio would make those handles |
1345 | /* Try to load a cuesheet for the track */ | 1532 | unmovable. |
1346 | if (curr_cue) | 1533 | */ |
1534 | static int audio_load_track(void) | ||
1535 | { | ||
1536 | if (in_progress_id3_hid >= 0) | ||
1347 | { | 1537 | { |
1348 | char cuepath[MAX_PATH]; | 1538 | /* There must be an info pointer if the in-progress id3 is even there */ |
1349 | if (look_for_cuesheet_file(track_id3->path, cuepath)) | 1539 | struct track_info *info = track_list_last(0); |
1540 | |||
1541 | if (info->id3_hid == in_progress_id3_hid) | ||
1350 | { | 1542 | { |
1351 | void *temp; | 1543 | if (filling == STATE_FILLING) |
1352 | tracks[track_widx].cuesheet_hid = | ||
1353 | bufalloc(NULL, sizeof(struct cuesheet), TYPE_CUESHEET); | ||
1354 | if (tracks[track_widx].cuesheet_hid >= 0) | ||
1355 | { | 1544 | { |
1356 | bufgetdata(tracks[track_widx].cuesheet_hid, | 1545 | /* Haven't finished the metadata but the notification is |
1357 | sizeof(struct cuesheet), &temp); | 1546 | anticipated to come soon */ |
1358 | struct cuesheet *cuesheet = (struct cuesheet*)temp; | 1547 | logf("%s(): in progress ok: %d". __func__, info->id3_hid); |
1359 | if (!parse_cuesheet(cuepath, cuesheet)) | 1548 | return LOAD_TRACK_OK; |
1360 | { | 1549 | } |
1361 | bufclose(tracks[track_widx].cuesheet_hid); | 1550 | else if (filling == STATE_FULL) |
1362 | track_id3->cuesheet = NULL; | 1551 | { |
1363 | } | 1552 | /* Buffer was full trying to complete the load after the |
1553 | metadata finished, so attempt to continue - older handles | ||
1554 | should have been cleared already */ | ||
1555 | logf("%s(): finishing load: %d". __func__, info->id3_hid); | ||
1556 | filling = STATE_FILLING; | ||
1557 | buffer_event_finished_callback(&info->id3_hid); | ||
1558 | return LOAD_TRACK_OK; | ||
1364 | } | 1559 | } |
1365 | } | 1560 | } |
1561 | |||
1562 | /* Some old, stray buffering message */ | ||
1563 | logf("%s(): already in progress: %d". __func__, info->id3_hid); | ||
1564 | return LOAD_TRACK_ERR_BUSY; | ||
1366 | } | 1565 | } |
1367 | 1566 | ||
1368 | #ifdef HAVE_ALBUMART | 1567 | filling = STATE_FILLING; |
1369 | audio_load_albumart(track_id3); | 1568 | |
1370 | #endif | 1569 | struct track_info *info = track_list_alloc_track(); |
1570 | if (info == NULL) | ||
1571 | { | ||
1572 | /* List is full so stop buffering tracks - however, attempt to obtain | ||
1573 | metadata as the unbuffered id3 */ | ||
1574 | logf("No free tracks"); | ||
1575 | filling = STATE_FULL; | ||
1576 | } | ||
1577 | |||
1578 | playlist_peek_offset++; | ||
1579 | |||
1580 | logf("Buffering track: s%u/c%u/e%u/p%d", | ||
1581 | track_list.start, track_list.current, track_list.end, | ||
1582 | playlist_peek_offset); | ||
1583 | |||
1584 | /* Get track name from current playlist read position */ | ||
1585 | int fd = -1; | ||
1586 | char name_buf[MAX_PATH + 1]; | ||
1587 | const char *trackname; | ||
1371 | 1588 | ||
1372 | /* Load the codec. */ | 1589 | while (1) |
1373 | if (!audio_loadcodec(start_play)) | ||
1374 | { | 1590 | { |
1375 | if (tracks[track_widx].codec_hid == ERR_BUFFER_FULL) | 1591 | |
1592 | trackname = playlist_peek(playlist_peek_offset, name_buf, | ||
1593 | sizeof (name_buf)); | ||
1594 | |||
1595 | if (!trackname) | ||
1596 | break; | ||
1597 | |||
1598 | /* Test for broken playlists by probing for the files */ | ||
1599 | fd = open(trackname, O_RDONLY); | ||
1600 | if (fd >= 0) | ||
1601 | break; | ||
1602 | |||
1603 | logf("Open failed"); | ||
1604 | /* Skip invalid entry from playlist */ | ||
1605 | playlist_skip_entry(NULL, playlist_peek_offset); | ||
1606 | |||
1607 | /* Sync the playlist if it isn't finished */ | ||
1608 | if (playlist_peek(playlist_peek_offset, NULL, 0)) | ||
1609 | playlist_next(0); | ||
1610 | } | ||
1611 | |||
1612 | if (!trackname) | ||
1613 | { | ||
1614 | /* No track - exhausted the playlist entries */ | ||
1615 | logf("End-of-playlist"); | ||
1616 | id3_write_locked(UNBUFFERED_ID3, NULL); | ||
1617 | |||
1618 | if (filling != STATE_FULL) | ||
1619 | track_list_unalloc_track(); /* Free this entry */ | ||
1620 | |||
1621 | playlist_peek_offset--; /* Maintain at last index */ | ||
1622 | |||
1623 | /* We can end up here after the real last track signals its completion | ||
1624 | and miss the transition to STATE_FINISHED esp. if dropping the last | ||
1625 | songs of a playlist late in their load (2nd stage) */ | ||
1626 | info = track_list_last(0); | ||
1627 | |||
1628 | if (info && buf_handle_remaining(info->audio_hid) == 0) | ||
1629 | filling_is_finished(); | ||
1630 | else | ||
1631 | filling = STATE_END_OF_PLAYLIST; | ||
1632 | |||
1633 | return LOAD_TRACK_ERR_NO_MORE; | ||
1634 | } | ||
1635 | |||
1636 | /* Successfully opened the file - get track metadata */ | ||
1637 | if (filling == STATE_FULL || | ||
1638 | (info->id3_hid = bufopen(trackname, 0, TYPE_ID3, NULL)) < 0) | ||
1639 | { | ||
1640 | /* Buffer or track list is full */ | ||
1641 | struct mp3entry *ub_id3; | ||
1642 | |||
1643 | playlist_peek_offset--; | ||
1644 | |||
1645 | /* Load the metadata for the first unbuffered track */ | ||
1646 | ub_id3 = id3_get(UNBUFFERED_ID3); | ||
1647 | id3_mutex_lock(); | ||
1648 | get_metadata(ub_id3, fd, trackname); | ||
1649 | id3_mutex_unlock(); | ||
1650 | |||
1651 | if (filling != STATE_FULL) | ||
1376 | { | 1652 | { |
1377 | /* No space for codec on buffer, not an error */ | 1653 | track_list_unalloc_track(); |
1378 | filling = STATE_FULL; | 1654 | filling = STATE_FULL; |
1379 | return; | ||
1380 | } | 1655 | } |
1381 | 1656 | ||
1382 | /* This is an error condition, either no codec was found, or reading | 1657 | logf("%s: buffer is full for now (%u tracks)", __func__, |
1383 | * the codec file failed part way through, either way, skip the track */ | 1658 | track_list_count()); |
1384 | /* FIXME: We should not use splashf from audio thread! */ | 1659 | } |
1385 | splashf(HZ*2, "No codec for: %s", track_id3->path); | 1660 | else |
1386 | /* Skip invalid entry from playlist. */ | 1661 | { |
1387 | playlist_skip_entry(NULL, last_peek_offset); | 1662 | /* Successful load initiation */ |
1388 | return; | 1663 | info->filesize = filesize(fd); |
1664 | in_progress_id3_hid = info->id3_hid; /* Remember what's in-progress */ | ||
1389 | } | 1665 | } |
1390 | 1666 | ||
1391 | track_id3->elapsed = 0; | 1667 | close(fd); |
1392 | offset = track_id3->offset; | 1668 | return LOAD_TRACK_OK; |
1393 | size_t resume_rewind = (global_settings.resume_rewind * | 1669 | } |
1394 | track_id3->bitrate * 1000) / 8; | ||
1395 | 1670 | ||
1396 | if (offset < resume_rewind) | 1671 | /* Second part of the track loading: We now have the metadata available, so we |
1672 | can load the codec, the album art and finally the audio data. | ||
1673 | This is called on the audio thread after the buffering thread calls the | ||
1674 | buffering_handle_finished_callback callback. */ | ||
1675 | static int audio_finish_load_track(struct track_info *info) | ||
1676 | { | ||
1677 | int trackstat = LOAD_TRACK_OK; | ||
1678 | |||
1679 | if (info->id3_hid != in_progress_id3_hid) | ||
1397 | { | 1680 | { |
1398 | offset = 0; | 1681 | /* We must not be here if not! */ |
1682 | logf("%s: wrong track %d/%d", __func__, info->id3_hid, | ||
1683 | in_progress_id3_hid); | ||
1684 | return LOAD_TRACK_ERR_BUSY; | ||
1399 | } | 1685 | } |
1400 | else | 1686 | |
1687 | /* The current track for decoding (there is always one if the list is | ||
1688 | populated) */ | ||
1689 | struct track_info *cur_info = track_list_current(0); | ||
1690 | struct mp3entry *track_id3 = valid_mp3entry(bufgetid3(info->id3_hid)); | ||
1691 | |||
1692 | if (!track_id3) | ||
1401 | { | 1693 | { |
1402 | offset -= resume_rewind; | 1694 | /* This is an error condition. Track cannot be played without valid |
1695 | metadata; skip the track. */ | ||
1696 | logf("No metadata for: %s", track_id3->path); | ||
1697 | trackstat = LOAD_TRACK_ERR_FINISH_FAILED; | ||
1698 | goto audio_finish_load_track_exit; | ||
1699 | } | ||
1700 | |||
1701 | /* Try to load a cuesheet for the track */ | ||
1702 | if (!audio_load_cuesheet(info, track_id3)) | ||
1703 | { | ||
1704 | /* No space for cuesheet on buffer, not an error */ | ||
1705 | filling = STATE_FULL; | ||
1706 | goto audio_finish_load_track_exit; | ||
1403 | } | 1707 | } |
1404 | 1708 | ||
1405 | enum data_type type = TYPE_PACKET_AUDIO; | 1709 | #ifdef HAVE_ALBUMART |
1710 | /* Try to load album art for the track */ | ||
1711 | if (!audio_load_albumart(info, track_id3)) | ||
1712 | { | ||
1713 | /* No space for album art on buffer, not an error */ | ||
1714 | filling = STATE_FULL; | ||
1715 | goto audio_finish_load_track_exit; | ||
1716 | } | ||
1717 | #endif | ||
1406 | 1718 | ||
1407 | switch (track_id3->codectype) { | 1719 | #ifdef HAVE_CODEC_BUFFERING |
1408 | case AFMT_MPA_L1: | 1720 | /* Try to buffer a codec for the track */ |
1409 | case AFMT_MPA_L2: | 1721 | if (info != cur_info && !audio_buffer_codec(info, track_id3)) |
1410 | case AFMT_MPA_L3: | 1722 | { |
1411 | if (offset > 0) { | 1723 | if (info->codec_hid == ERR_BUFFER_FULL) |
1412 | file_offset = offset; | 1724 | { |
1725 | /* No space for codec on buffer, not an error */ | ||
1726 | filling = STATE_FULL; | ||
1727 | logf("buffer is full for now (%s)", __func__); | ||
1413 | } | 1728 | } |
1414 | break; | 1729 | else |
1415 | 1730 | { | |
1416 | case AFMT_WAVPACK: | 1731 | /* This is an error condition, either no codec was found, or |
1417 | if (offset > 0) { | 1732 | reading the codec file failed part way through, either way, |
1418 | file_offset = offset; | 1733 | skip the track */ |
1419 | track_id3->elapsed = track_id3->length / 2; | 1734 | logf("No codec for: %s", track_id3->path); |
1735 | trackstat = LOAD_TRACK_ERR_FINISH_FAILED; | ||
1420 | } | 1736 | } |
1421 | break; | ||
1422 | 1737 | ||
1423 | case AFMT_NSF: | 1738 | goto audio_finish_load_track_exit; |
1424 | case AFMT_SPC: | ||
1425 | case AFMT_SID: | ||
1426 | logf("Loading atomic %d",track_id3->codectype); | ||
1427 | type = TYPE_ATOMIC_AUDIO; | ||
1428 | break; | ||
1429 | |||
1430 | default: | ||
1431 | /* no special treatment needed */ | ||
1432 | break; | ||
1433 | } | 1739 | } |
1740 | #endif /* HAVE_CODEC_BUFFERING */ | ||
1434 | 1741 | ||
1435 | track_id3->offset = offset; | 1742 | /** Finally, load the audio **/ |
1743 | size_t file_offset = 0; | ||
1744 | track_id3->elapsed = 0; | ||
1745 | |||
1746 | if (track_id3->offset >= info->filesize) | ||
1747 | track_id3->offset = 0; | ||
1748 | |||
1749 | logf("%s: set offset for %s to %lu\n", __func__, | ||
1750 | id3->title, (unsigned long)offset); | ||
1751 | |||
1752 | /* Adjust for resume rewind so we know what to buffer - starting the codec | ||
1753 | calls it again, so we don't save it (and they shouldn't accumulate) */ | ||
1754 | size_t offset = resume_rewind_adjusted_offset(track_id3); | ||
1755 | |||
1756 | enum data_type audiotype = get_audio_base_data_type(track_id3->codectype); | ||
1757 | |||
1758 | if (audiotype == TYPE_ATOMIC_AUDIO) | ||
1759 | logf("Loading atomic %d", track_id3->codectype); | ||
1760 | |||
1761 | if (format_buffers_with_offset(track_id3->codectype)) | ||
1762 | { | ||
1763 | /* This format can begin buffering from any point */ | ||
1764 | file_offset = offset; | ||
1765 | } | ||
1436 | 1766 | ||
1437 | logf("load track: %s", track_id3->path); | 1767 | logf("load track: %s", track_id3->path); |
1438 | 1768 | ||
1439 | if (file_offset > AUDIO_REBUFFER_GUESS_SIZE) | 1769 | if (file_offset > AUDIO_REBUFFER_GUESS_SIZE) |
1770 | { | ||
1771 | /* We can buffer later in the file, adjust the hunt-and-peck margin */ | ||
1440 | file_offset -= AUDIO_REBUFFER_GUESS_SIZE; | 1772 | file_offset -= AUDIO_REBUFFER_GUESS_SIZE; |
1441 | else if (track_id3->first_frame_offset) | 1773 | } |
1442 | file_offset = track_id3->first_frame_offset; | ||
1443 | else | 1774 | else |
1444 | file_offset = 0; | 1775 | { |
1776 | /* No offset given or it is very minimal - begin at the first frame | ||
1777 | according to the metadata */ | ||
1778 | file_offset = track_id3->first_frame_offset; | ||
1779 | } | ||
1445 | 1780 | ||
1446 | tracks[track_widx].audio_hid = bufopen(track_id3->path, file_offset, type, | 1781 | int hid = bufopen(track_id3->path, file_offset, audiotype, NULL); |
1447 | NULL); | ||
1448 | 1782 | ||
1449 | /* No space left, not an error */ | 1783 | if (hid >= 0) |
1450 | if (tracks[track_widx].audio_hid == ERR_BUFFER_FULL) | ||
1451 | { | 1784 | { |
1452 | filling = STATE_FULL; | 1785 | info->audio_hid = hid; |
1453 | logf("buffer is full for now (load audio)"); | 1786 | |
1454 | return; | 1787 | if (info == cur_info) |
1788 | { | ||
1789 | /* This is the current track to decode - should be started now */ | ||
1790 | trackstat = LOAD_TRACK_READY; | ||
1791 | } | ||
1455 | } | 1792 | } |
1456 | else if (tracks[track_widx].audio_hid < 0) | 1793 | else |
1457 | { | 1794 | { |
1458 | /* another error, do not continue either */ | 1795 | /* Buffer could be full but not properly so if this is the only |
1459 | logf("Could not add audio data handle"); | 1796 | track! */ |
1460 | return; | 1797 | if (hid == ERR_BUFFER_FULL && audio_track_count() > 1) |
1798 | { | ||
1799 | filling = STATE_FULL; | ||
1800 | logf("Buffer is full for now (%s)", __func__); | ||
1801 | } | ||
1802 | else | ||
1803 | { | ||
1804 | /* Nothing to play if no audio handle - skip this */ | ||
1805 | logf("Could not add audio data handle"); | ||
1806 | trackstat = LOAD_TRACK_ERR_FINISH_FAILED; | ||
1807 | } | ||
1461 | } | 1808 | } |
1462 | 1809 | ||
1463 | /* All required data is now available for the codec -- unless the | 1810 | audio_finish_load_track_exit: |
1464 | autoresume feature is in effect. In the latter case, the codec | 1811 | if (trackstat < LOAD_TRACK_OK) |
1465 | must wait until after PLAYBACK_EVENT_TRACK_BUFFER, which may | ||
1466 | generate a resume position. */ | ||
1467 | #ifdef HAVE_TAGCACHE | ||
1468 | if (!global_settings.autoresume_enable || offset) | ||
1469 | #endif | ||
1470 | tracks[track_widx].taginfo_ready = true; | ||
1471 | |||
1472 | if (start_play) | ||
1473 | { | 1812 | { |
1474 | ci.curpos=file_offset; | 1813 | playlist_skip_entry(NULL, playlist_peek_offset); |
1475 | buf_request_buffer_handle(tracks[track_widx].audio_hid); | 1814 | track_info_close(info); |
1476 | } | 1815 | track_list_unalloc_track(); |
1477 | 1816 | ||
1478 | send_event(PLAYBACK_EVENT_TRACK_BUFFER, track_id3); | 1817 | if (playlist_peek(playlist_peek_offset, NULL, 0)) |
1818 | playlist_next(0); | ||
1479 | 1819 | ||
1480 | #ifdef HAVE_TAGCACHE | 1820 | playlist_peek_offset--; |
1481 | /* In case the autoresume feature has been enabled, finally all | 1821 | } |
1482 | required data is available for the codec. */ | ||
1483 | if (global_settings.autoresume_enable && !offset) | ||
1484 | tracks[track_widx].taginfo_ready = true; | ||
1485 | #endif | ||
1486 | |||
1487 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; | ||
1488 | 1822 | ||
1489 | /* load next track */ | 1823 | if (filling != STATE_FULL) |
1490 | LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER"); | 1824 | { |
1491 | queue_post(&audio_queue, Q_AUDIO_FILL_BUFFER, 0); | 1825 | /* Load next track - error or not */ |
1826 | in_progress_id3_hid = ERR_HANDLE_NOT_FOUND; | ||
1827 | LOGFQUEUE("audio > audio Q_AUDIO_FILL_BUFFER"); | ||
1828 | audio_queue_post(Q_AUDIO_FILL_BUFFER, 0); | ||
1829 | } | ||
1830 | else | ||
1831 | { | ||
1832 | /* Full */ | ||
1833 | trackstat = LOAD_TRACK_ERR_FINISH_FULL; | ||
1834 | } | ||
1492 | 1835 | ||
1493 | return; | 1836 | return trackstat; |
1494 | } | 1837 | } |
1495 | 1838 | ||
1496 | static void audio_fill_file_buffer(bool start_play, size_t offset) | 1839 | /* Start a new track load */ |
1840 | static int audio_fill_file_buffer(void) | ||
1497 | { | 1841 | { |
1498 | trigger_cpu_boost(); | 1842 | if (play_status == PLAY_STOPPED) |
1843 | return LOAD_TRACK_ERR_FAILED; | ||
1499 | 1844 | ||
1500 | /* No need to rebuffer if there are track skips pending, | 1845 | trigger_cpu_boost(); |
1501 | * however don't cancel buffering on skipping while filling. */ | ||
1502 | if (ci.new_track != 0 && filling != STATE_FILLING) | ||
1503 | return; | ||
1504 | filling = STATE_FILLING; | ||
1505 | 1846 | ||
1506 | /* Must reset the buffer before use if trashed or voice only - voice | 1847 | /* Must reset the buffer before use if trashed or voice only - voice |
1507 | file size shouldn't have changed so we can go straight from | 1848 | file size shouldn't have changed so we can go straight from |
@@ -1511,772 +1852,1848 @@ static void audio_fill_file_buffer(bool start_play, size_t offset) | |||
1511 | 1852 | ||
1512 | logf("Starting buffer fill"); | 1853 | logf("Starting buffer fill"); |
1513 | 1854 | ||
1514 | if (!start_play) | 1855 | int trackstat = audio_load_track(); |
1515 | audio_clear_track_entries(); | ||
1516 | 1856 | ||
1517 | /* Save the current resume position once. */ | 1857 | if (trackstat >= LOAD_TRACK_OK) |
1518 | playlist_update_resume_info(audio_current_track()); | 1858 | { |
1859 | if (track_list_current(0) == track_list_user_current(0)) | ||
1860 | playlist_next(0); | ||
1519 | 1861 | ||
1520 | audio_load_track(offset, start_play); | 1862 | if (filling == STATE_FULL && !track_list_user_current(1)) |
1863 | { | ||
1864 | /* There are no user tracks on the buffer after this therefore | ||
1865 | this is the next track */ | ||
1866 | audio_update_and_announce_next_track(id3_get(UNBUFFERED_ID3)); | ||
1867 | } | ||
1868 | } | ||
1869 | |||
1870 | return trackstat; | ||
1521 | } | 1871 | } |
1522 | 1872 | ||
1523 | static void audio_rebuffer(void) | 1873 | /* Discard unwanted tracks and start refill from after the specified playlist |
1874 | offset */ | ||
1875 | static int audio_reset_and_rebuffer( | ||
1876 | enum track_clear_action action, int peek_offset) | ||
1524 | { | 1877 | { |
1525 | logf("Forcing rebuffer"); | 1878 | logf("Forcing rebuffer: 0x%X, %d", flags, peek_offset); |
1526 | 1879 | ||
1527 | clear_track_info(CUR_TI); | 1880 | id3_write_locked(UNBUFFERED_ID3, NULL); |
1528 | 1881 | ||
1529 | /* Reset track pointers */ | 1882 | /* Remove unwanted tracks - caller must have ensured codec isn't using |
1530 | track_widx = track_ridx; | 1883 | any */ |
1531 | audio_clear_track_entries(); | 1884 | track_list_clear(action); |
1532 | 1885 | ||
1533 | /* Reset a possibly interrupted track load */ | 1886 | /* Refill at specified position (-1 starts at index offset 0) */ |
1534 | track_load_started = false; | 1887 | playlist_peek_offset = peek_offset; |
1535 | 1888 | ||
1536 | /* Fill the buffer */ | 1889 | /* Fill the buffer */ |
1537 | last_peek_offset = -1; | 1890 | return audio_fill_file_buffer(); |
1538 | ci.curpos = 0; | 1891 | } |
1539 | 1892 | ||
1540 | if (!CUR_TI->taginfo_ready) | 1893 | /* Handle buffering events |
1541 | memset(thistrack_id3, 0, sizeof(struct mp3entry)); | 1894 | (Q_AUDIO_BUFFERING) */ |
1895 | static void audio_on_buffering(int event) | ||
1896 | { | ||
1897 | enum track_clear_action action; | ||
1898 | int peek_offset; | ||
1899 | |||
1900 | if (track_list_empty()) | ||
1901 | return; | ||
1902 | |||
1903 | switch (event) | ||
1904 | { | ||
1905 | case BUFFER_EVENT_BUFFER_LOW: | ||
1906 | if (filling != STATE_FULL && filling != STATE_END_OF_PLAYLIST) | ||
1907 | return; /* Should be nothing left to fill */ | ||
1908 | |||
1909 | /* Clear old tracks and continue buffering where it left off */ | ||
1910 | action = TRACK_LIST_KEEP_NEW; | ||
1911 | peek_offset = playlist_peek_offset; | ||
1912 | break; | ||
1542 | 1913 | ||
1543 | audio_fill_file_buffer(false, 0); | 1914 | case BUFFER_EVENT_REBUFFER: |
1915 | /* Remove all but the currently decoding track and redo buffering | ||
1916 | after that */ | ||
1917 | action = TRACK_LIST_KEEP_CURRENT; | ||
1918 | peek_offset = (skip_pending == TRACK_SKIP_AUTO) ? 1 : 0; | ||
1919 | break; | ||
1920 | |||
1921 | default: | ||
1922 | return; | ||
1923 | } | ||
1924 | |||
1925 | switch (skip_pending) | ||
1926 | { | ||
1927 | case TRACK_SKIP_NONE: | ||
1928 | case TRACK_SKIP_AUTO: | ||
1929 | case TRACK_SKIP_AUTO_NEW_PLAYLIST: | ||
1930 | audio_reset_and_rebuffer(action, peek_offset); | ||
1931 | break; | ||
1932 | |||
1933 | case TRACK_SKIP_AUTO_END_PLAYLIST: | ||
1934 | /* Already finished */ | ||
1935 | break; | ||
1936 | |||
1937 | default: | ||
1938 | /* Invalid */ | ||
1939 | logf("Buffering call, inv. state: %d", (int)skip_pending); | ||
1940 | } | ||
1544 | } | 1941 | } |
1545 | 1942 | ||
1546 | /* Called on request from the codec to get a new track. This is the codec part | 1943 | /* Handle starting the next track load |
1547 | of the track transition. */ | 1944 | (Q_AUDIO_FILL_BUFFER) */ |
1548 | static void audio_last_track(bool automatic) | 1945 | static void audio_on_fill_buffer(void) |
1549 | { | 1946 | { |
1550 | if (automatic) | 1947 | audio_handle_track_load_status(audio_fill_file_buffer()); |
1948 | } | ||
1949 | |||
1950 | /* Handle posted load track finish event | ||
1951 | (Q_AUDIO_FINISH_LOAD_TRACK) */ | ||
1952 | static void audio_on_finish_load_track(int id3_hid) | ||
1953 | { | ||
1954 | struct track_info *info = track_list_last(0); | ||
1955 | |||
1956 | if (!info || !buf_is_handle(id3_hid)) | ||
1957 | return; | ||
1958 | |||
1959 | if (info == track_list_user_current(1)) | ||
1551 | { | 1960 | { |
1552 | ci.new_track = 0; | 1961 | /* Just loaded the metadata right after the current position */ |
1553 | automatic_skip = false; | 1962 | audio_update_and_announce_next_track(bufgetid3(info->id3_hid)); |
1963 | } | ||
1554 | 1964 | ||
1555 | if (filling != STATE_ENDING) | 1965 | if (audio_finish_load_track(info) != LOAD_TRACK_READY) |
1966 | return; /* Not current track */ | ||
1967 | |||
1968 | bool is_user_current = info == track_list_user_current(0); | ||
1969 | |||
1970 | if (is_user_current) | ||
1971 | { | ||
1972 | /* Copy cuesheet */ | ||
1973 | buf_read_cuesheet(info->cuesheet_hid); | ||
1974 | } | ||
1975 | |||
1976 | if (audio_start_codec(automatic_skip)) | ||
1977 | { | ||
1978 | if (is_user_current) | ||
1556 | { | 1979 | { |
1557 | /* Monitor remaining PCM before stopping */ | 1980 | /* Be sure all tagtree info is synchronized; it will be needed for the |
1558 | filling = STATE_ENDING; | 1981 | track finish event - the sync will happen when finalizing a track |
1559 | pcmbuf_monitor_track_change(true); | 1982 | change otherwise */ |
1560 | } | 1983 | bool was_valid = valid_mp3entry(id3_get(PLAYING_ID3)); |
1561 | 1984 | ||
1562 | codec_stop(); | 1985 | playing_id3_sync(info, -1); |
1986 | |||
1987 | if (!was_valid) | ||
1988 | { | ||
1989 | /* Playing id3 hadn't been updated yet because no valid track | ||
1990 | was yet available - treat like the first track */ | ||
1991 | audio_playlist_track_change(); | ||
1992 | } | ||
1993 | } | ||
1563 | } | 1994 | } |
1564 | else | 1995 | else |
1565 | { | 1996 | { |
1997 | audio_handle_track_load_status(LOAD_TRACK_ERR_START_CODEC); | ||
1998 | } | ||
1999 | } | ||
2000 | |||
2001 | /* Called when handles other than metadata handles have finished buffering | ||
2002 | (Q_AUDIO_HANDLE_FINISHED) */ | ||
2003 | static void audio_on_handle_finished(int hid) | ||
2004 | { | ||
2005 | /* Right now, only audio handles should end up calling this */ | ||
2006 | if (filling == STATE_END_OF_PLAYLIST) | ||
2007 | { | ||
2008 | struct track_info *info = track_list_last(0); | ||
2009 | |||
2010 | /* Really we don't know which order the handles will actually complete | ||
2011 | to zero bytes remaining since another thread is doing it - be sure | ||
2012 | it's the right one */ | ||
2013 | if (info && info->audio_hid == hid) | ||
2014 | { | ||
2015 | /* This was the last track in the playlist and we now have all the | ||
2016 | data we need */ | ||
2017 | filling_is_finished(); | ||
2018 | } | ||
2019 | } | ||
2020 | } | ||
2021 | |||
2022 | /* Called to make an outstanding track skip the current track and to send the | ||
2023 | transition events */ | ||
2024 | static void audio_finalise_track_change(bool delayed) | ||
2025 | { | ||
2026 | switch (skip_pending) | ||
2027 | { | ||
2028 | case TRACK_SKIP_NONE: /* Manual skip */ | ||
2029 | break; | ||
2030 | |||
2031 | case TRACK_SKIP_AUTO: | ||
2032 | case TRACK_SKIP_AUTO_NEW_PLAYLIST: | ||
2033 | { | ||
2034 | int playlist_delta = skip_pending == TRACK_SKIP_AUTO ? 1 : 0; | ||
2035 | audio_playlist_track_finish(); | ||
2036 | |||
2037 | if (!playlist_peek(playlist_delta, NULL, 0)) | ||
2038 | { | ||
2039 | /* Track ended up rejected - push things ahead like the codec blew | ||
2040 | it (because it was never started and now we're here where it | ||
2041 | should have been decoding the next track by now) - next, a | ||
2042 | directory change or end of playback will most likely happen */ | ||
2043 | skip_pending = TRACK_SKIP_NONE; | ||
2044 | audio_handle_track_load_status(LOAD_TRACK_ERR_START_CODEC); | ||
2045 | return; | ||
2046 | } | ||
2047 | |||
2048 | if (!playlist_delta) | ||
2049 | break; | ||
2050 | |||
2051 | playlist_peek_offset -= playlist_delta; | ||
2052 | if (playlist_next(playlist_delta) >= 0) | ||
2053 | break; | ||
2054 | /* What!? Disappear? Hopeless bleak despair */ | ||
2055 | } | ||
2056 | /* Fallthrough */ | ||
2057 | case TRACK_SKIP_AUTO_END_PLAYLIST: | ||
2058 | default: /* Invalid */ | ||
2059 | filling = STATE_ENDED; | ||
1566 | audio_stop_playback(); | 2060 | audio_stop_playback(); |
2061 | return; | ||
2062 | } | ||
2063 | |||
2064 | struct track_info *info = track_list_current(0); | ||
2065 | struct mp3entry *track_id3 = NULL; | ||
2066 | |||
2067 | id3_mutex_lock(); | ||
2068 | |||
2069 | /* Update the current cuesheet if any and enabled */ | ||
2070 | if (info) | ||
2071 | { | ||
2072 | buf_read_cuesheet(info->cuesheet_hid); | ||
2073 | track_id3 = bufgetid3(info->id3_hid); | ||
2074 | } | ||
2075 | |||
2076 | id3_write(PLAYING_ID3, track_id3); | ||
2077 | |||
2078 | if (delayed) | ||
2079 | { | ||
2080 | /* Delayed skip where codec is ahead of user's current track */ | ||
2081 | struct mp3entry *ci_id3 = id3_get(CODEC_ID3); | ||
2082 | struct mp3entry *ply_id3 = id3_get(PLAYING_ID3); | ||
2083 | ply_id3->elapsed = ci_id3->elapsed; | ||
2084 | ply_id3->offset = ci_id3->offset; | ||
1567 | } | 2085 | } |
2086 | |||
2087 | /* The skip is technically over */ | ||
2088 | skip_pending = TRACK_SKIP_NONE; | ||
2089 | |||
2090 | /* Sync the next track information */ | ||
2091 | info = track_list_current(1); | ||
2092 | |||
2093 | id3_write(NEXTTRACK_ID3, info ? bufgetid3(info->id3_hid) : | ||
2094 | id3_get(UNBUFFERED_ID3)); | ||
2095 | |||
2096 | id3_mutex_unlock(); | ||
2097 | |||
2098 | audio_playlist_track_change(); | ||
1568 | } | 2099 | } |
1569 | 2100 | ||
1570 | static void audio_check_new_track(void) | 2101 | /* Actually begin a transition and take care of the codec change - may complete |
2102 | it now or ask pcmbuf for notification depending on the type and what pcmbuf | ||
2103 | has to say */ | ||
2104 | static void audio_begin_track_change(bool auto_skip, int trackstat) | ||
1571 | { | 2105 | { |
1572 | int track_count; | 2106 | /* Even if the new track is bad, the old track must be finished off */ |
1573 | int old_track_ridx; | 2107 | bool finalised = pcmbuf_start_track_change(auto_skip); |
1574 | int i, idx; | ||
1575 | bool forward; | ||
1576 | struct mp3entry *temp; | ||
1577 | 2108 | ||
1578 | if (ci.new_track == 0) | 2109 | if (finalised) |
1579 | { | 2110 | { |
1580 | ci.new_track++; | 2111 | /* pcmbuf says that the transition happens now - complete it */ |
1581 | automatic_skip = true; | 2112 | audio_finalise_track_change(false); |
2113 | |||
2114 | if (play_status == PLAY_STOPPED) | ||
2115 | return; /* Stopped us */ | ||
2116 | } | ||
2117 | |||
2118 | if (!auto_skip) | ||
2119 | audio_clear_paused_pcm(); | ||
2120 | |||
2121 | if (trackstat >= LOAD_TRACK_OK) | ||
2122 | { | ||
2123 | struct track_info *info = track_list_current(0); | ||
2124 | |||
2125 | if (info->audio_hid < 0) | ||
2126 | return; | ||
2127 | |||
2128 | /* Everything needed for the codec is ready - start it */ | ||
2129 | if (audio_start_codec(auto_skip)) | ||
2130 | { | ||
2131 | if (finalised) | ||
2132 | playing_id3_sync(info, -1); | ||
2133 | return; | ||
2134 | } | ||
2135 | |||
2136 | trackstat = LOAD_TRACK_ERR_START_CODEC; | ||
1582 | } | 2137 | } |
1583 | 2138 | ||
1584 | track_count = audio_track_count(); | 2139 | audio_handle_track_load_status(trackstat); |
1585 | old_track_ridx = track_ridx; | 2140 | } |
2141 | |||
2142 | /* Transition to end-of-playlist state and begin wait for PCM to finish */ | ||
2143 | static void audio_monitor_end_of_playlist(void) | ||
2144 | { | ||
2145 | skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST; | ||
2146 | filling = STATE_ENDING; | ||
2147 | pcmbuf_monitor_track_change(true); | ||
2148 | } | ||
2149 | |||
2150 | /* Codec has completed decoding the track | ||
2151 | (usually Q_AUDIO_CODEC_COMPLETE) */ | ||
2152 | static void audio_on_codec_complete(int status) | ||
2153 | { | ||
2154 | logf("%s(%d)", __func__, status); | ||
1586 | 2155 | ||
1587 | /* Now it's good time to send track finish events. */ | 2156 | if (play_status == PLAY_STOPPED) |
1588 | send_event(PLAYBACK_EVENT_TRACK_FINISH, thistrack_id3); | 2157 | return; |
1589 | /* swap the mp3entry pointers */ | ||
1590 | temp = thistrack_id3; | ||
1591 | thistrack_id3 = othertrack_id3; | ||
1592 | othertrack_id3 = temp; | ||
1593 | ci.id3 = thistrack_id3; | ||
1594 | memset(thistrack_id3, 0, sizeof(struct mp3entry)); | ||
1595 | 2158 | ||
1596 | if (dir_skip) | 2159 | /* If it didn't notify us first, don't expect "seek complete" message |
2160 | since the codec can't post it now - do things like it would have | ||
2161 | done */ | ||
2162 | audio_complete_codec_seek(); | ||
2163 | |||
2164 | if (play_status == PLAY_PAUSED || skip_pending != TRACK_SKIP_NONE) | ||
1597 | { | 2165 | { |
1598 | dir_skip = false; | 2166 | /* Old-hay on the ip-skay - codec has completed decoding |
1599 | /* regardless of the return value we need to rebuffer. | 2167 | |
1600 | if it fails the old playlist will resume, else the | 2168 | Paused: We're not sounding it, so just remember that it happened |
1601 | next dir will start playing */ | 2169 | and the resume will begin the transition |
1602 | playlist_next_dir(ci.new_track); | 2170 | |
1603 | ci.new_track = 0; | 2171 | Skipping: There was already a skip in progress, remember it and |
1604 | audio_rebuffer(); | 2172 | allow no further progress until the PCM from the previous |
1605 | goto skip_done; | 2173 | song has finished |
2174 | */ | ||
2175 | codec_skip_pending = true; | ||
2176 | codec_skip_status = status; | ||
2177 | return; | ||
1606 | } | 2178 | } |
1607 | 2179 | ||
1608 | if (new_playlist) | 2180 | codec_skip_pending = false; |
1609 | ci.new_track = 0; | ||
1610 | 2181 | ||
1611 | /* If the playlist isn't that big */ | 2182 | if (status >= 0) |
1612 | if (automatic_skip) | ||
1613 | { | 2183 | { |
1614 | while (!playlist_check(ci.new_track)) | 2184 | /* Normal automatic skip */ |
2185 | ab_end_of_track_report(); | ||
2186 | } | ||
2187 | |||
2188 | int trackstat = LOAD_TRACK_OK; | ||
2189 | |||
2190 | automatic_skip = true; | ||
2191 | skip_pending = TRACK_SKIP_AUTO; | ||
2192 | |||
2193 | /* Does this track have an entry allocated? */ | ||
2194 | struct track_info *info = track_list_advance_current(1); | ||
2195 | |||
2196 | if (!info || info->audio_hid < 0) | ||
2197 | { | ||
2198 | bool end_of_playlist = false; | ||
2199 | |||
2200 | if (info) | ||
1615 | { | 2201 | { |
1616 | if (ci.new_track >= 0) | 2202 | /* Track load is not complete - it might have stopped on a |
2203 | full buffer without reaching the audio handle or we just | ||
2204 | arrived at it early | ||
2205 | |||
2206 | If this type is atomic and we couldn't get the audio, | ||
2207 | perhaps it would need to wrap to make the allocation and | ||
2208 | handles are in the way - to maximize the liklihood it can | ||
2209 | be allocated, clear all handles to reset the buffer and | ||
2210 | its indexes to 0 - for packet audio, this should not be an | ||
2211 | issue and a pointless full reload of all the track's | ||
2212 | metadata may be avoided */ | ||
2213 | |||
2214 | struct mp3entry *track_id3 = bufgetid3(info->id3_hid); | ||
2215 | |||
2216 | if (track_id3 && | ||
2217 | get_audio_base_data_type(track_id3->codectype) | ||
2218 | == TYPE_PACKET_AUDIO) | ||
1617 | { | 2219 | { |
1618 | audio_last_track(true); | 2220 | /* Continue filling after this track */ |
2221 | audio_reset_and_rebuffer(TRACK_LIST_KEEP_CURRENT, 1); | ||
2222 | audio_begin_track_change(true, trackstat); | ||
1619 | return; | 2223 | return; |
1620 | } | 2224 | } |
1621 | ci.new_track++; | 2225 | /* else rebuffer at this track; status applies to the track we |
2226 | want */ | ||
2227 | } | ||
2228 | else if (!playlist_peek(1, NULL, 0)) | ||
2229 | { | ||
2230 | /* Play sequence is complete - directory change or other playlist | ||
2231 | resequencing - the playlist must now be advanced in order to | ||
2232 | continue since a peek ahead to the next track is not possible */ | ||
2233 | skip_pending = TRACK_SKIP_AUTO_NEW_PLAYLIST; | ||
2234 | end_of_playlist = playlist_next(1) < 0; | ||
2235 | } | ||
2236 | |||
2237 | if (!end_of_playlist) | ||
2238 | { | ||
2239 | trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, | ||
2240 | skip_pending == TRACK_SKIP_AUTO ? 0 : -1); | ||
2241 | |||
2242 | if (trackstat == LOAD_TRACK_ERR_NO_MORE) | ||
2243 | { | ||
2244 | /* Failed to find anything afterall - do playlist switchover | ||
2245 | instead */ | ||
2246 | skip_pending = TRACK_SKIP_AUTO_NEW_PLAYLIST; | ||
2247 | end_of_playlist = playlist_next(1) < 0; | ||
2248 | } | ||
2249 | } | ||
2250 | |||
2251 | if (end_of_playlist) | ||
2252 | { | ||
2253 | audio_monitor_end_of_playlist(); | ||
2254 | return; | ||
2255 | } | ||
2256 | } | ||
2257 | |||
2258 | audio_begin_track_change(true, trackstat); | ||
2259 | } | ||
2260 | |||
2261 | /* Called when codec completes seek operation | ||
2262 | (usually Q_AUDIO_CODEC_SEEK_COMPLETE) */ | ||
2263 | static void audio_on_codec_seek_complete(void) | ||
2264 | { | ||
2265 | logf("%s()", __func__); | ||
2266 | audio_complete_codec_seek(); | ||
2267 | codec_go(); | ||
2268 | } | ||
2269 | |||
2270 | /* Called when PCM track change has completed | ||
2271 | (Q_AUDIO_TRACK_CHANGED) */ | ||
2272 | static void audio_on_track_changed(void) | ||
2273 | { | ||
2274 | /* Finish whatever is pending so that the WPS is in sync */ | ||
2275 | audio_finalise_track_change(true); | ||
2276 | |||
2277 | if (codec_skip_pending) | ||
2278 | { | ||
2279 | /* Codec got ahead completing a short track - complete the | ||
2280 | codec's skip and begin the next */ | ||
2281 | codec_skip_pending = false; | ||
2282 | audio_on_codec_complete(codec_skip_status); | ||
2283 | } | ||
2284 | } | ||
2285 | |||
2286 | /* Begin playback from an idle state, transition to a new playlist or | ||
2287 | invalidate the buffer and resume (if playing). | ||
2288 | (usually Q_AUDIO_PLAY, Q_AUDIO_REMAKE_AUDIO_BUFFER) */ | ||
2289 | static void audio_start_playback(size_t offset, unsigned int flags) | ||
2290 | { | ||
2291 | enum play_status old_status = play_status; | ||
2292 | |||
2293 | if (flags & AUDIO_START_NEWBUF) | ||
2294 | { | ||
2295 | /* Mark the buffer dirty - if not playing, it will be reset next | ||
2296 | time */ | ||
2297 | if (buffer_state == AUDIOBUF_STATE_INITIALIZED) | ||
2298 | buffer_state = AUDIOBUF_STATE_VOICED_ONLY; | ||
2299 | } | ||
2300 | |||
2301 | if (old_status != PLAY_STOPPED) | ||
2302 | { | ||
2303 | logf("%s(%lu): skipping", __func__, (unsigned long)offset); | ||
2304 | |||
2305 | halt_decoding_track(true); | ||
2306 | |||
2307 | automatic_skip = false; | ||
2308 | ff_rw_mode = false; | ||
2309 | |||
2310 | if (flags & AUDIO_START_RESTART) | ||
2311 | { | ||
2312 | /* Clear out some stuff to resume the current track where it | ||
2313 | left off */ | ||
2314 | pcmbuf_play_stop(); | ||
2315 | offset = id3_get(PLAYING_ID3)->offset; | ||
2316 | track_list_clear(TRACK_LIST_CLEAR_ALL); | ||
2317 | } | ||
2318 | else | ||
2319 | { | ||
2320 | /* This is more-or-less treated as manual track transition */ | ||
2321 | /* Save resume information for current track */ | ||
2322 | audio_playlist_track_finish(); | ||
2323 | track_list_clear(TRACK_LIST_CLEAR_ALL); | ||
2324 | |||
2325 | /* Indicate manual track change */ | ||
2326 | pcmbuf_start_track_change(false); | ||
2327 | audio_clear_paused_pcm(); | ||
2328 | wipe_track_metadata(true); | ||
1622 | } | 2329 | } |
2330 | |||
2331 | /* Set after track finish event in case skip was in progress */ | ||
2332 | skip_pending = TRACK_SKIP_NONE; | ||
2333 | } | ||
2334 | else | ||
2335 | { | ||
2336 | if (flags & AUDIO_START_RESTART) | ||
2337 | return; /* Must already be playing */ | ||
2338 | |||
2339 | /* Cold playback start from a stopped state */ | ||
2340 | logf("%s(%lu): starting", __func__, offset); | ||
2341 | |||
2342 | /* Set audio parameters */ | ||
2343 | #if INPUT_SRC_CAPS != 0 | ||
2344 | audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); | ||
2345 | audio_set_output_source(AUDIO_SRC_PLAYBACK); | ||
2346 | #endif | ||
2347 | #ifndef PLATFORM_HAS_VOLUME_CHANGE | ||
2348 | sound_set_volume(global_settings.volume); | ||
2349 | #endif | ||
2350 | /* Update our state */ | ||
2351 | play_status = PLAY_PLAYING; | ||
2352 | } | ||
2353 | |||
2354 | /* Start fill from beginning of playlist */ | ||
2355 | playlist_peek_offset = -1; | ||
2356 | buf_set_base_handle(-1); | ||
2357 | |||
2358 | /* Officially playing */ | ||
2359 | queue_reply(&audio_queue, 1); | ||
2360 | |||
2361 | /* Add these now - finish event for the first id3 will most likely be sent | ||
2362 | immediately */ | ||
2363 | add_event(BUFFER_EVENT_REBUFFER, false, buffer_event_rebuffer_callback); | ||
2364 | add_event(BUFFER_EVENT_FINISHED, false, buffer_event_finished_callback); | ||
2365 | |||
2366 | if (old_status == PLAY_STOPPED) | ||
2367 | { | ||
2368 | /* Send coldstart event */ | ||
2369 | send_event(PLAYBACK_EVENT_START_PLAYBACK, NULL); | ||
1623 | } | 2370 | } |
1624 | 2371 | ||
1625 | /* Update the playlist */ | 2372 | /* Fill the buffer */ |
1626 | last_peek_offset -= ci.new_track; | 2373 | int trackstat = audio_fill_file_buffer(); |
2374 | |||
2375 | if (trackstat >= LOAD_TRACK_OK) | ||
2376 | { | ||
2377 | /* This is the currently playing track - get metadata, stat */ | ||
2378 | playing_id3_sync(track_list_current(0), offset); | ||
1627 | 2379 | ||
1628 | if (playlist_next(ci.new_track) < 0) | 2380 | if (valid_mp3entry(id3_get(PLAYING_ID3))) |
2381 | { | ||
2382 | /* Only if actually changing tracks... */ | ||
2383 | if (!(flags & AUDIO_START_RESTART)) | ||
2384 | audio_playlist_track_change(); | ||
2385 | } | ||
2386 | } | ||
2387 | else | ||
1629 | { | 2388 | { |
1630 | /* End of list */ | 2389 | /* Found nothing playable */ |
1631 | audio_last_track(automatic_skip); | 2390 | audio_handle_track_load_status(trackstat); |
2391 | } | ||
2392 | } | ||
2393 | |||
2394 | /* Stop playback and enter an idle state | ||
2395 | (usually Q_AUDIO_STOP) */ | ||
2396 | static void audio_stop_playback(void) | ||
2397 | { | ||
2398 | logf("%s()", __func__); | ||
2399 | |||
2400 | if (play_status == PLAY_STOPPED) | ||
2401 | return; | ||
2402 | |||
2403 | /* Stop the codec and unload it */ | ||
2404 | halt_decoding_track(true); | ||
2405 | pcmbuf_play_stop(); | ||
2406 | codec_unload(); | ||
2407 | |||
2408 | /* Save resume information - "filling" might have been set to | ||
2409 | "STATE_ENDED" by caller in order to facilitate end of playlist */ | ||
2410 | audio_playlist_track_finish(); | ||
2411 | |||
2412 | skip_pending = TRACK_SKIP_NONE; | ||
2413 | automatic_skip = false; | ||
2414 | |||
2415 | /* Close all tracks and mark them NULL */ | ||
2416 | remove_event(BUFFER_EVENT_REBUFFER, buffer_event_rebuffer_callback); | ||
2417 | remove_event(BUFFER_EVENT_FINISHED, buffer_event_finished_callback); | ||
2418 | remove_event(BUFFER_EVENT_BUFFER_LOW, buffer_event_buffer_low_callback); | ||
2419 | |||
2420 | track_list_clear(TRACK_LIST_CLEAR_ALL); | ||
2421 | |||
2422 | /* Update our state */ | ||
2423 | ff_rw_mode = false; | ||
2424 | play_status = PLAY_STOPPED; | ||
2425 | |||
2426 | wipe_track_metadata(true); | ||
2427 | |||
2428 | /* Go idle */ | ||
2429 | filling = STATE_IDLE; | ||
2430 | cancel_cpu_boost(); | ||
2431 | } | ||
2432 | |||
2433 | /* Pause the playback of the current track | ||
2434 | (Q_AUDIO_PAUSE) */ | ||
2435 | static void audio_on_pause(bool pause) | ||
2436 | { | ||
2437 | logf("%s(%s)", __func__, pause ? "true" : "false"); | ||
2438 | |||
2439 | if (play_status == PLAY_STOPPED || pause == (play_status == PLAY_PAUSED)) | ||
1632 | return; | 2440 | return; |
2441 | |||
2442 | if (!ff_rw_mode) | ||
2443 | { | ||
2444 | /* Not in ff/rw mode - may set the state (otherwise this could make | ||
2445 | old data play because seek hasn't completed and cleared it) */ | ||
2446 | pcmbuf_pause(pause); | ||
1633 | } | 2447 | } |
1634 | 2448 | ||
1635 | if (new_playlist) | 2449 | play_status = pause ? PLAY_PAUSED : PLAY_PLAYING; |
2450 | |||
2451 | if (!pause && codec_skip_pending) | ||
1636 | { | 2452 | { |
1637 | ci.new_track = 1; | 2453 | /* Actually do the skip that is due - resets the status flag */ |
1638 | new_playlist = false; | 2454 | audio_on_codec_complete(codec_skip_status); |
1639 | } | 2455 | } |
2456 | } | ||
2457 | |||
2458 | /* Skip a certain number of tracks forwards or backwards | ||
2459 | (Q_AUDIO_SKIP) */ | ||
2460 | static void audio_on_skip(void) | ||
2461 | { | ||
2462 | id3_mutex_lock(); | ||
2463 | |||
2464 | /* Eat the delta to keep it synced, even if not playing */ | ||
2465 | int toskip = skip_offset; | ||
2466 | skip_offset = 0; | ||
1640 | 2467 | ||
1641 | /* Save a pointer to the old track to allow later clearing */ | 2468 | logf("%s(): %d", __func__, toskip); |
1642 | prev_ti = CUR_TI; | 2469 | |
2470 | id3_mutex_unlock(); | ||
2471 | |||
2472 | if (play_status == PLAY_STOPPED) | ||
2473 | return; | ||
1643 | 2474 | ||
1644 | for (i = 0; i < ci.new_track; i++) | 2475 | /* Force codec to abort this track */ |
2476 | halt_decoding_track(true); | ||
2477 | |||
2478 | /* Kill the ff/rw halt */ | ||
2479 | ff_rw_mode = false; | ||
2480 | |||
2481 | /* Manual skip */ | ||
2482 | automatic_skip = false; | ||
2483 | |||
2484 | /* If there was an auto skip in progress, there will be residual | ||
2485 | advancement of the playlist and/or track list so compensation will be | ||
2486 | required in order to end up in the right spot */ | ||
2487 | int track_list_delta = toskip; | ||
2488 | int playlist_delta = toskip; | ||
2489 | |||
2490 | if (skip_pending != TRACK_SKIP_NONE) | ||
1645 | { | 2491 | { |
1646 | idx = (track_ridx + i) & MAX_TRACK_MASK; | 2492 | if (skip_pending != TRACK_SKIP_AUTO_END_PLAYLIST) |
1647 | struct mp3entry *id3 = bufgetid3(tracks[idx].id3_hid); | 2493 | track_list_delta--; |
1648 | ssize_t offset = buf_handle_offset(tracks[idx].audio_hid); | 2494 | |
1649 | if (!id3 || offset < 0 || (unsigned)offset > id3->first_frame_offset) | 2495 | if (skip_pending == TRACK_SKIP_AUTO_NEW_PLAYLIST) |
2496 | playlist_delta--; | ||
2497 | } | ||
2498 | |||
2499 | audio_playlist_track_finish(); | ||
2500 | skip_pending = TRACK_SKIP_NONE; | ||
2501 | |||
2502 | /* Update the playlist current track now */ | ||
2503 | while (playlist_next(playlist_delta) < 0) | ||
2504 | { | ||
2505 | /* Manual skip out of range (because the playlist wasn't updated | ||
2506 | yet by us and so the check in audio_skip returned 'ok') - bring | ||
2507 | back into range */ | ||
2508 | int d = toskip < 0 ? 1 : -1; | ||
2509 | |||
2510 | while (!playlist_check(playlist_delta)) | ||
1650 | { | 2511 | { |
1651 | /* We don't have all the audio data for that track, so clear it, | 2512 | if (playlist_delta == d) |
1652 | but keep the metadata. */ | ||
1653 | if (tracks[idx].audio_hid >= 0 && bufclose(tracks[idx].audio_hid)) | ||
1654 | { | 2513 | { |
1655 | tracks[idx].audio_hid = -1; | 2514 | /* Had to move the opposite direction to correct, which is |
1656 | tracks[idx].filesize = 0; | 2515 | wrong - this is the end */ |
2516 | filling = STATE_ENDED; | ||
2517 | audio_stop_playback(); | ||
2518 | return; | ||
1657 | } | 2519 | } |
2520 | |||
2521 | playlist_delta += d; | ||
2522 | track_list_delta += d; | ||
1658 | } | 2523 | } |
1659 | } | 2524 | } |
1660 | 2525 | ||
1661 | /* Move to the new track */ | 2526 | /* Adjust things by how much the playlist was manually moved */ |
1662 | track_ridx = (track_ridx + ci.new_track) & MAX_TRACK_MASK; | 2527 | playlist_peek_offset -= playlist_delta; |
1663 | buf_set_base_handle(CUR_TI->audio_hid); | ||
1664 | 2528 | ||
1665 | if (automatic_skip) | 2529 | struct track_info *info = track_list_advance_current(track_list_delta); |
1666 | { | 2530 | int trackstat = LOAD_TRACK_OK; |
1667 | wps_offset = -ci.new_track; | ||
1668 | } | ||
1669 | 2531 | ||
1670 | /* If it is not safe to even skip this many track entries */ | 2532 | if (!info || info->audio_hid < 0) |
1671 | if (ci.new_track >= track_count || ci.new_track <= track_count - MAX_TRACK) | ||
1672 | { | 2533 | { |
1673 | ci.new_track = 0; | 2534 | /* We don't know the next track thus we know we don't have it */ |
1674 | audio_rebuffer(); | 2535 | trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1); |
1675 | goto skip_done; | ||
1676 | } | 2536 | } |
1677 | 2537 | ||
1678 | forward = ci.new_track > 0; | 2538 | audio_begin_track_change(false, trackstat); |
1679 | ci.new_track = 0; | 2539 | } |
2540 | |||
2541 | /* Skip to the next/previous directory | ||
2542 | (Q_AUDIO_DIR_SKIP) */ | ||
2543 | static void audio_on_dir_skip(int direction) | ||
2544 | { | ||
2545 | logf("%s(%d)", __func__, direction); | ||
2546 | |||
2547 | id3_mutex_lock(); | ||
2548 | skip_offset = 0; | ||
2549 | id3_mutex_unlock(); | ||
2550 | |||
2551 | if (play_status == PLAY_STOPPED) | ||
2552 | return; | ||
2553 | |||
2554 | /* Force codec to abort this track */ | ||
2555 | halt_decoding_track(true); | ||
2556 | |||
2557 | /* Kill the ff/rw halt */ | ||
2558 | ff_rw_mode = false; | ||
2559 | |||
2560 | /* Manual skip */ | ||
2561 | automatic_skip = false; | ||
2562 | |||
2563 | audio_playlist_track_finish(); | ||
2564 | |||
2565 | /* Unless automatic and gapless, skips do not pend */ | ||
2566 | skip_pending = TRACK_SKIP_NONE; | ||
2567 | |||
2568 | /* Regardless of the return value we need to rebuffer. If it fails the old | ||
2569 | playlist will resume, else the next dir will start playing. */ | ||
2570 | playlist_next_dir(direction); | ||
2571 | |||
2572 | wipe_track_metadata(false); | ||
1680 | 2573 | ||
1681 | /* If the target track is clearly not in memory */ | 2574 | int trackstat = audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1); |
1682 | if (CUR_TI->filesize == 0 || !CUR_TI->taginfo_ready) | 2575 | |
2576 | if (trackstat == LOAD_TRACK_ERR_NO_MORE) | ||
1683 | { | 2577 | { |
1684 | audio_rebuffer(); | 2578 | /* The day the music died - finish-off whatever is playing and call it |
1685 | goto skip_done; | 2579 | quits */ |
2580 | audio_monitor_end_of_playlist(); | ||
2581 | return; | ||
1686 | } | 2582 | } |
1687 | 2583 | ||
1688 | /* When skipping backwards, it is possible that we've found a track that's | 2584 | audio_begin_track_change(false, trackstat); |
1689 | * buffered, but which is around the track-wrap and therefore not the track | 2585 | } |
1690 | * we are looking for */ | 2586 | |
1691 | if (!forward) | 2587 | /* Enter seek mode in order to start a seek |
2588 | (Q_AUDIO_PRE_FF_REWIND) */ | ||
2589 | static void audio_on_pre_ff_rewind(void) | ||
2590 | { | ||
2591 | logf("%s()", __func__); | ||
2592 | |||
2593 | if (play_status == PLAY_STOPPED || ff_rw_mode) | ||
2594 | return; | ||
2595 | |||
2596 | ff_rw_mode = true; | ||
2597 | |||
2598 | if (play_status == PLAY_PAUSED) | ||
2599 | return; | ||
2600 | |||
2601 | pcmbuf_pause(true); | ||
2602 | } | ||
2603 | |||
2604 | /* Seek the playback of the current track to the specified time | ||
2605 | (Q_AUDIO_FF_REWIND) */ | ||
2606 | static void audio_on_ff_rewind(long time) | ||
2607 | { | ||
2608 | logf("%s(%ld)", __func__, time); | ||
2609 | |||
2610 | if (play_status == PLAY_STOPPED) | ||
2611 | return; | ||
2612 | |||
2613 | enum track_skip_type pending = skip_pending; | ||
2614 | |||
2615 | switch (pending) | ||
2616 | { | ||
2617 | case TRACK_SKIP_NONE: /* The usual case */ | ||
2618 | case TRACK_SKIP_AUTO: /* Have to back it out (fun!) */ | ||
2619 | case TRACK_SKIP_AUTO_END_PLAYLIST: /* Still have the last codec used */ | ||
1692 | { | 2620 | { |
1693 | int cur_idx = track_ridx; | 2621 | struct mp3entry *id3 = id3_get(PLAYING_ID3); |
1694 | bool taginfo_ready = true; | 2622 | struct mp3entry *ci_id3 = id3_get(CODEC_ID3); |
1695 | /* We've wrapped the buffer backwards if new > old */ | 2623 | |
1696 | bool wrap = track_ridx > old_track_ridx; | 2624 | automatic_skip = false; |
2625 | |||
2626 | /* Send event before clobbering the time */ | ||
2627 | /* FIXME: Nasty, but the tagtree expects this so that rewinding and | ||
2628 | then skipping back to this track resumes properly. Something else | ||
2629 | should be sent. We're not _really_ finishing the track are we? */ | ||
2630 | if (time == 0) | ||
2631 | send_event(PLAYBACK_EVENT_TRACK_FINISH, id3); | ||
2632 | |||
2633 | /* Prevent user codec time update - coerce to something that is | ||
2634 | innocuous concerning lookaheads */ | ||
2635 | if (pending == TRACK_SKIP_NONE) | ||
2636 | skip_pending = TRACK_SKIP_AUTO_END_PLAYLIST; | ||
2637 | |||
2638 | id3->elapsed = time; | ||
2639 | queue_reply(&audio_queue, 1); | ||
2640 | |||
2641 | bool haltres = halt_decoding_track(pending == TRACK_SKIP_AUTO); | ||
2642 | |||
2643 | /* Need this set in case ff/rw mode + error but _after_ the codec | ||
2644 | halt that will reset it */ | ||
2645 | codec_seeking = true; | ||
1697 | 2646 | ||
1698 | while (1) | 2647 | if (pending == TRACK_SKIP_AUTO) |
1699 | { | 2648 | { |
1700 | cur_idx = (cur_idx + 1) & MAX_TRACK_MASK; | 2649 | if (!track_list_advance_current(-1)) |
2650 | { | ||
2651 | /* Not in list - must rebuffer at the current playlist index */ | ||
2652 | if (audio_reset_and_rebuffer(TRACK_LIST_CLEAR_ALL, -1) | ||
2653 | < LOAD_TRACK_OK) | ||
2654 | { | ||
2655 | /* Codec is stopped */ | ||
2656 | break; | ||
2657 | } | ||
2658 | } | ||
2659 | } | ||
1701 | 2660 | ||
1702 | /* if we've advanced past the wrap when cur_idx is zeroed */ | 2661 | /* Set after audio_fill_file_buffer to disable playing id3 clobber if |
1703 | if (!cur_idx) | 2662 | rebuffer is needed */ |
1704 | wrap = false; | 2663 | skip_pending = TRACK_SKIP_NONE; |
2664 | struct track_info *cur_info = track_list_current(0); | ||
1705 | 2665 | ||
1706 | /* if we aren't still on the wrap and we've caught the old track */ | 2666 | /* Track must complete the loading _now_ since a codec and audio |
1707 | if (!(wrap || cur_idx < old_track_ridx)) | 2667 | handle are needed in order to do the seek */ |
1708 | break; | 2668 | if (cur_info->audio_hid < 0 && |
2669 | audio_finish_load_track(cur_info) != LOAD_TRACK_READY) | ||
2670 | { | ||
2671 | /* Call above should push any load sequence - no need for | ||
2672 | halt_decoding_track here if no skip was pending here because | ||
2673 | there would not be a codec started if no audio handle was yet | ||
2674 | opened */ | ||
2675 | break; | ||
2676 | } | ||
1709 | 2677 | ||
1710 | /* If we hit a track in between without valid tag info, bail */ | 2678 | if (pending == TRACK_SKIP_AUTO) |
1711 | if (!tracks[cur_idx].taginfo_ready) | 2679 | { |
2680 | if (!bufreadid3(cur_info->id3_hid, ci_id3) || | ||
2681 | !audio_init_codec(cur_info, ci_id3)) | ||
1712 | { | 2682 | { |
1713 | taginfo_ready = false; | 2683 | /* We should have still been able to get it - skip it and move |
2684 | onto the next one - like it or not this track is borken */ | ||
1714 | break; | 2685 | break; |
1715 | } | 2686 | } |
2687 | |||
2688 | /* Set the codec API to the correct metadata and track info */ | ||
2689 | ci.audio_hid = cur_info->audio_hid; | ||
2690 | ci.filesize = cur_info->filesize; | ||
2691 | buf_set_base_handle(cur_info->audio_hid); | ||
1716 | } | 2692 | } |
1717 | if (!taginfo_ready) | 2693 | |
2694 | if (!haltres) | ||
1718 | { | 2695 | { |
1719 | audio_rebuffer(); | 2696 | /* If codec must be (re)started, reset the offset */ |
2697 | ci_id3->offset = 0; | ||
1720 | } | 2698 | } |
1721 | } | ||
1722 | 2699 | ||
1723 | skip_done: | 2700 | codec_seek(time); |
1724 | audio_update_trackinfo(); | 2701 | return; |
1725 | pcmbuf_start_track_change(automatic_skip); | 2702 | } |
1726 | 2703 | ||
1727 | if (get_codec_base_type(codec_loaded()) == | 2704 | case TRACK_SKIP_AUTO_NEW_PLAYLIST: |
1728 | get_codec_base_type(thistrack_id3->codectype)) | ||
1729 | { | 2705 | { |
1730 | /* codec is the same base type */ | 2706 | /* We cannot do this because the playlist must be reversed by one |
1731 | logf("New track loaded"); | 2707 | and it doesn't always return the same song when going backwards |
1732 | codec_ack_msg(Q_CODEC_REQUEST_COMPLETE, false); | 2708 | across boundaries as forwards (either because of randomization |
2709 | or inconsistency in deciding what the previous track should be), | ||
2710 | therefore the whole operation would often end up as nonsense - | ||
2711 | lock out seeking for a couple seconds */ | ||
2712 | |||
2713 | /* Sure as heck cancel seek mode too! */ | ||
2714 | audio_ff_rewind_end(); | ||
2715 | return; | ||
2716 | } | ||
2717 | |||
2718 | default: | ||
2719 | /* Won't see this */ | ||
2720 | return; | ||
1733 | } | 2721 | } |
1734 | else | 2722 | |
2723 | if (play_status == PLAY_STOPPED) | ||
1735 | { | 2724 | { |
1736 | /* a codec change is required */ | 2725 | /* Playback ended because of an error completing a track load */ |
1737 | logf("New codec: %d/%d", thistrack_id3->codectype, codec_loaded()); | 2726 | return; |
1738 | codec_ack_msg(Q_CODEC_REQUEST_COMPLETE, true); | ||
1739 | codec_load(tracks[track_ridx].codec_hid, thistrack_id3->codectype); | ||
1740 | tracks[track_ridx].codec_hid = -1; /* Codec thread will close it */ | ||
1741 | } | 2727 | } |
2728 | |||
2729 | /* Always fake it as a codec start error which will handle mode | ||
2730 | cancellations and skip to the next track */ | ||
2731 | audio_handle_track_load_status(LOAD_TRACK_ERR_START_CODEC); | ||
1742 | } | 2732 | } |
1743 | 2733 | ||
1744 | unsigned long audio_prev_elapsed(void) | 2734 | /* Invalidates all but currently playing track |
2735 | (Q_AUDIO_FLUSH) */ | ||
2736 | static void audio_on_audio_flush(void) | ||
1745 | { | 2737 | { |
1746 | return prev_track_elapsed; | 2738 | logf("%s", __func__); |
2739 | |||
2740 | if (track_list_empty()) | ||
2741 | return; /* Nothing to flush out */ | ||
2742 | |||
2743 | switch (skip_pending) | ||
2744 | { | ||
2745 | case TRACK_SKIP_NONE: | ||
2746 | case TRACK_SKIP_AUTO_END_PLAYLIST: | ||
2747 | /* Remove all but the currently playing track from the list and | ||
2748 | refill after that */ | ||
2749 | track_list_clear(TRACK_LIST_KEEP_CURRENT); | ||
2750 | playlist_peek_offset = 0; | ||
2751 | id3_write_locked(UNBUFFERED_ID3, NULL); | ||
2752 | audio_update_and_announce_next_track(NULL); | ||
2753 | |||
2754 | /* Ignore return since it's about the next track, not this one */ | ||
2755 | audio_fill_file_buffer(); | ||
2756 | |||
2757 | if (skip_pending == TRACK_SKIP_NONE) | ||
2758 | break; | ||
2759 | |||
2760 | /* There's now a track after this one now - convert to auto skip - | ||
2761 | no skip should pend right now because multiple flush messages can | ||
2762 | be fired which would cause a restart in the below cases */ | ||
2763 | skip_pending = TRACK_SKIP_NONE; | ||
2764 | audio_clear_track_notifications(); | ||
2765 | audio_queue_post(Q_AUDIO_CODEC_COMPLETE, CODEC_OK); | ||
2766 | break; | ||
2767 | |||
2768 | case TRACK_SKIP_AUTO: | ||
2769 | case TRACK_SKIP_AUTO_NEW_PLAYLIST: | ||
2770 | /* Precisely removing what it already decoded for the next track is | ||
2771 | not possible so a restart is required in order to continue the | ||
2772 | currently playing track without the now invalid future track | ||
2773 | playing */ | ||
2774 | audio_start_playback(0, AUDIO_START_RESTART); | ||
2775 | break; | ||
2776 | |||
2777 | default: /* Nothing else is a state */ | ||
2778 | break; | ||
2779 | } | ||
1747 | } | 2780 | } |
1748 | 2781 | ||
1749 | void audio_set_prev_elapsed(unsigned long setting) | 2782 | #ifdef AUDIO_HAVE_RECORDING |
2783 | /* Load the requested encoder type | ||
2784 | (Q_AUDIO_LOAD_ENCODER) */ | ||
2785 | static void audio_on_load_encoder(int afmt) | ||
1750 | { | 2786 | { |
1751 | prev_track_elapsed = setting; | 2787 | bool res = true; |
2788 | |||
2789 | if (play_status != PLAY_STOPPED) | ||
2790 | audio_stop_playback(); /* Can't load both types at once */ | ||
2791 | else | ||
2792 | codec_unload(); /* Encoder still loaded, stop and unload it */ | ||
2793 | |||
2794 | if (afmt != AFMT_UNKNOWN) | ||
2795 | { | ||
2796 | res = codec_load(-1, afmt | CODEC_TYPE_ENCODER); | ||
2797 | if (res) | ||
2798 | codec_go(); /* These are run immediately */ | ||
2799 | } | ||
2800 | |||
2801 | queue_reply(&audio_queue, res); | ||
1752 | } | 2802 | } |
2803 | #endif /* AUDIO_HAVE_RECORDING */ | ||
1753 | 2804 | ||
1754 | /* Stop the codec and reset the PCM buffer */ | 2805 | static void audio_thread(void) |
1755 | static void audio_stop_codec_flush(void) | ||
1756 | { | 2806 | { |
1757 | bool pcm_playing; | 2807 | struct queue_event ev; |
1758 | 2808 | ||
1759 | pcmbuf_pause(true); | 2809 | pcm_postinit(); |
1760 | 2810 | ||
1761 | codec_stop(); | 2811 | filling = STATE_IDLE; |
1762 | 2812 | ||
1763 | pcm_play_lock(); | 2813 | while (1) |
2814 | { | ||
2815 | switch (filling) | ||
2816 | { | ||
2817 | /* Active states */ | ||
2818 | case STATE_FULL: | ||
2819 | case STATE_END_OF_PLAYLIST: | ||
2820 | if (buf_get_watermark() == 0) | ||
2821 | { | ||
2822 | /* End of buffering for now, let's calculate the watermark, | ||
2823 | register for a low buffer event and unboost */ | ||
2824 | audio_update_filebuf_watermark(0); | ||
2825 | add_event(BUFFER_EVENT_BUFFER_LOW, true, | ||
2826 | buffer_event_buffer_low_callback); | ||
2827 | } | ||
2828 | /* Fall-through */ | ||
2829 | case STATE_FINISHED: | ||
2830 | /* All data was buffered */ | ||
2831 | cancel_cpu_boost(); | ||
2832 | /* Fall-through */ | ||
2833 | case STATE_FILLING: | ||
2834 | case STATE_ENDING: | ||
2835 | if (audio_pcmbuf_track_change_scan()) | ||
2836 | { | ||
2837 | /* Transfer notification to audio queue event */ | ||
2838 | ev.id = Q_AUDIO_TRACK_CHANGED; | ||
2839 | ev.data = 1; | ||
2840 | } | ||
2841 | else | ||
2842 | { | ||
2843 | /* If doing auto skip, poll pcmbuf track notifications a bit | ||
2844 | faster to promply detect the transition */ | ||
2845 | queue_wait_w_tmo(&audio_queue, &ev, | ||
2846 | skip_pending == TRACK_SKIP_NONE ? | ||
2847 | HZ/2 : HZ/10); | ||
2848 | } | ||
2849 | break; | ||
1764 | 2850 | ||
1765 | pcm_playing = pcm_is_playing(); | 2851 | /* Idle states */ |
2852 | default: | ||
2853 | queue_wait(&audio_queue, &ev); | ||
1766 | 2854 | ||
1767 | pcmbuf_play_stop(); | 2855 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
1768 | queue_clear(&pcmbuf_queue); | 2856 | switch (ev.id) |
2857 | { | ||
2858 | #ifdef AUDIO_HAVE_RECORDING | ||
2859 | /* Must monitor the encoder message for recording so it can remove | ||
2860 | it if we process the insertion before it does. It cannot simply | ||
2861 | be removed from under recording however. */ | ||
2862 | case Q_AUDIO_LOAD_ENCODER: | ||
2863 | break; | ||
2864 | #endif | ||
2865 | case SYS_USB_DISCONNECTED: | ||
2866 | filling = STATE_IDLE; | ||
2867 | break; | ||
2868 | |||
2869 | default: | ||
2870 | if (filling == STATE_USB) | ||
2871 | continue; | ||
2872 | } | ||
2873 | #endif /* CONFIG_PLATFORM */ | ||
2874 | } | ||
2875 | |||
2876 | switch (ev.id) | ||
2877 | { | ||
2878 | /** Codec and track change messages **/ | ||
2879 | case Q_AUDIO_CODEC_COMPLETE: | ||
2880 | /* Codec is done processing track and has gone idle */ | ||
2881 | LOGFQUEUE("audio < Q_AUDIO_CODEC_COMPLETE: %ld", (long)ev.data); | ||
2882 | audio_on_codec_complete(ev.data); | ||
2883 | break; | ||
2884 | |||
2885 | case Q_AUDIO_CODEC_SEEK_COMPLETE: | ||
2886 | /* Codec is done seeking */ | ||
2887 | LOGFQUEUE("audio < Q_AUDIO_SEEK_COMPLETE"); | ||
2888 | audio_on_codec_seek_complete(); | ||
2889 | break; | ||
2890 | |||
2891 | case Q_AUDIO_TRACK_CHANGED: | ||
2892 | /* PCM track change done */ | ||
2893 | LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED"); | ||
2894 | audio_on_track_changed(); | ||
2895 | break; | ||
2896 | |||
2897 | /** Control messages **/ | ||
2898 | case Q_AUDIO_PLAY: | ||
2899 | LOGFQUEUE("audio < Q_AUDIO_PLAY"); | ||
2900 | audio_start_playback(ev.data, 0); | ||
2901 | break; | ||
2902 | |||
2903 | case Q_AUDIO_STOP: | ||
2904 | LOGFQUEUE("audio < Q_AUDIO_STOP"); | ||
2905 | audio_stop_playback(); | ||
2906 | if (ev.data != 0) | ||
2907 | queue_clear(&audio_queue); | ||
2908 | break; | ||
2909 | |||
2910 | case Q_AUDIO_PAUSE: | ||
2911 | LOGFQUEUE("audio < Q_AUDIO_PAUSE"); | ||
2912 | audio_on_pause(ev.data); | ||
2913 | break; | ||
2914 | |||
2915 | case Q_AUDIO_SKIP: | ||
2916 | LOGFQUEUE("audio < Q_AUDIO_SKIP"); | ||
2917 | audio_on_skip(); | ||
2918 | break; | ||
1769 | 2919 | ||
1770 | if (pcm_playing) | 2920 | case Q_AUDIO_DIR_SKIP: |
1771 | pcmbuf_pause(paused); | 2921 | LOGFQUEUE("audio < Q_AUDIO_DIR_SKIP"); |
2922 | audio_on_dir_skip(ev.data); | ||
2923 | break; | ||
2924 | |||
2925 | case Q_AUDIO_PRE_FF_REWIND: | ||
2926 | LOGFQUEUE("audio < Q_AUDIO_PRE_FF_REWIND"); | ||
2927 | audio_on_pre_ff_rewind(); | ||
2928 | break; | ||
2929 | |||
2930 | case Q_AUDIO_FF_REWIND: | ||
2931 | LOGFQUEUE("audio < Q_AUDIO_FF_REWIND"); | ||
2932 | audio_on_ff_rewind(ev.data); | ||
2933 | break; | ||
2934 | |||
2935 | case Q_AUDIO_FLUSH: | ||
2936 | LOGFQUEUE("audio < Q_AUDIO_FLUSH: %d", (int)ev.data); | ||
2937 | audio_on_audio_flush(); | ||
2938 | break; | ||
1772 | 2939 | ||
1773 | pcm_play_unlock(); | 2940 | /** Buffering messages **/ |
2941 | case Q_AUDIO_BUFFERING: | ||
2942 | /* some buffering event */ | ||
2943 | LOGFQUEUE("audio < Q_AUDIO_BUFFERING: %d", (int)ev.data); | ||
2944 | audio_on_buffering(ev.data); | ||
2945 | break; | ||
2946 | |||
2947 | case Q_AUDIO_FILL_BUFFER: | ||
2948 | /* continue buffering next track */ | ||
2949 | LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER"); | ||
2950 | audio_on_fill_buffer(); | ||
2951 | break; | ||
2952 | |||
2953 | case Q_AUDIO_FINISH_LOAD_TRACK: | ||
2954 | /* metadata is buffered */ | ||
2955 | LOGFQUEUE("audio < Q_AUDIO_FINISH_LOAD_TRACK"); | ||
2956 | audio_on_finish_load_track(ev.data); | ||
2957 | break; | ||
2958 | |||
2959 | case Q_AUDIO_HANDLE_FINISHED: | ||
2960 | /* some other type is buffered */ | ||
2961 | LOGFQUEUE("audio < Q_AUDIO_HANDLE_FINISHED"); | ||
2962 | audio_on_handle_finished(ev.data); | ||
2963 | break; | ||
2964 | |||
2965 | /** Miscellaneous messages **/ | ||
2966 | case Q_AUDIO_REMAKE_AUDIO_BUFFER: | ||
2967 | /* buffer needs to be reinitialized */ | ||
2968 | LOGFQUEUE("audio < Q_AUDIO_REMAKE_AUDIO_BUFFER"); | ||
2969 | audio_start_playback(0, AUDIO_START_RESTART | AUDIO_START_NEWBUF); | ||
2970 | break; | ||
2971 | |||
2972 | #ifdef HAVE_DISK_STORAGE | ||
2973 | case Q_AUDIO_UPDATE_WATERMARK: | ||
2974 | /* buffering watermark needs updating */ | ||
2975 | LOGFQUEUE("audio < Q_AUDIO_UPDATE_WATERMARK: %d", (int)ev.data); | ||
2976 | audio_update_filebuf_watermark(ev.data); | ||
2977 | break; | ||
2978 | #endif /* HAVE_DISK_STORAGE */ | ||
2979 | |||
2980 | #ifdef AUDIO_HAVE_RECORDING | ||
2981 | case Q_AUDIO_LOAD_ENCODER: | ||
2982 | /* load an encoder for recording */ | ||
2983 | LOGFQUEUE("audio < Q_AUDIO_LOAD_ENCODER: %d", (int)ev.data); | ||
2984 | audio_on_load_encoder(ev.data); | ||
2985 | break; | ||
2986 | #endif /* AUDIO_HAVE_RECORDING */ | ||
2987 | |||
2988 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | ||
2989 | case SYS_USB_CONNECTED: | ||
2990 | LOGFQUEUE("audio < SYS_USB_CONNECTED"); | ||
2991 | audio_stop_playback(); | ||
2992 | #ifdef PLAYBACK_VOICE | ||
2993 | voice_stop(); | ||
2994 | #endif | ||
2995 | filling = STATE_USB; | ||
2996 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
2997 | break; | ||
2998 | #endif /* (CONFIG_PLATFORM & PLATFORM_NATIVE) */ | ||
2999 | |||
3000 | case SYS_TIMEOUT: | ||
3001 | LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT"); | ||
3002 | break; | ||
3003 | |||
3004 | default: | ||
3005 | /* LOGFQUEUE("audio < default : %08lX", ev.id); */ | ||
3006 | break; | ||
3007 | } /* end switch */ | ||
3008 | } /* end while */ | ||
1774 | } | 3009 | } |
1775 | 3010 | ||
1776 | static void audio_stop_playback(void) | 3011 | |
3012 | /* --- Buffering callbacks --- */ | ||
3013 | |||
3014 | /* Called when fullness is below the watermark level */ | ||
3015 | static void buffer_event_buffer_low_callback(void *data) | ||
1777 | { | 3016 | { |
1778 | if (playing) | 3017 | logf("low buffer callback"); |
1779 | { | 3018 | LOGFQUEUE("buffering > audio Q_AUDIO_BUFFERING: buffer low"); |
1780 | /* If we were playing, save resume information */ | 3019 | audio_queue_post(Q_AUDIO_BUFFERING, BUFFER_EVENT_BUFFER_LOW); |
1781 | struct mp3entry *id3 = NULL; | 3020 | (void)data; |
3021 | } | ||
1782 | 3022 | ||
1783 | if (!ci.stop_codec) | 3023 | /* Called when handles must be discarded in order to buffer new data */ |
1784 | id3 = audio_current_track(); | 3024 | static void buffer_event_rebuffer_callback(void *data) |
3025 | { | ||
3026 | logf("rebuffer callback"); | ||
3027 | LOGFQUEUE("buffering > audio Q_AUDIO_BUFFERING: rebuffer"); | ||
3028 | audio_queue_post(Q_AUDIO_BUFFERING, BUFFER_EVENT_REBUFFER); | ||
3029 | (void)data; | ||
3030 | } | ||
1785 | 3031 | ||
1786 | /* Save the current playing spot, or NULL if the playlist has ended */ | 3032 | /* A handle has completed buffering and all required data is available */ |
1787 | playlist_update_resume_info(id3); | 3033 | static void buffer_event_finished_callback(void *data) |
3034 | { | ||
3035 | int hid = *(const int *)data; | ||
3036 | const enum data_type htype = buf_handle_data_type(hid); | ||
1788 | 3037 | ||
1789 | /* Now it's good time to send track finish events. Do this | 3038 | logf("handle %d finished buffering (type:%u)", hid, (unsigned)htype); |
1790 | only if this hasn't been done already as part of a track | ||
1791 | switch. */ | ||
1792 | if (id3 == thistrack_id3) | ||
1793 | send_event(PLAYBACK_EVENT_TRACK_FINISH, thistrack_id3); | ||
1794 | 3039 | ||
1795 | /* TODO: Create auto bookmark too? */ | 3040 | /* Limit queue traffic */ |
3041 | switch (htype) | ||
3042 | { | ||
3043 | case TYPE_ID3: | ||
3044 | /* The metadata handle for the last loaded track has been buffered. | ||
3045 | We can ask the audio thread to load the rest of the track's data. */ | ||
3046 | LOGFQUEUE("buffering > audio Q_AUDIO_FINISH_LOAD_TRACK: %d", hid); | ||
3047 | audio_queue_post(Q_AUDIO_FINISH_LOAD_TRACK, hid); | ||
3048 | break; | ||
1796 | 3049 | ||
1797 | prev_track_elapsed = othertrack_id3->elapsed; | 3050 | case TYPE_PACKET_AUDIO: |
3051 | /* Strip any useless trailing tags that are left. */ | ||
3052 | strip_tags(hid); | ||
3053 | /* Fall-through */ | ||
3054 | case TYPE_ATOMIC_AUDIO: | ||
3055 | LOGFQUEUE("buffering > audio Q_AUDIO_HANDLE_FINISHED: %d", hid); | ||
3056 | audio_queue_post(Q_AUDIO_HANDLE_FINISHED, hid); | ||
3057 | break; | ||
1798 | 3058 | ||
1799 | remove_event(BUFFER_EVENT_BUFFER_LOW, buffering_low_buffer_callback); | 3059 | default: |
3060 | /* Don't care to know about these */ | ||
3061 | break; | ||
1800 | } | 3062 | } |
3063 | } | ||
1801 | 3064 | ||
1802 | audio_stop_codec_flush(); | ||
1803 | paused = false; | ||
1804 | playing = false; | ||
1805 | track_load_started = false; | ||
1806 | 3065 | ||
1807 | filling = STATE_IDLE; | 3066 | /** -- Codec callbacks -- **/ |
3067 | |||
3068 | /* Update elapsed times with latency-adjusted values */ | ||
3069 | void audio_codec_update_elapsed(unsigned long value) | ||
3070 | { | ||
3071 | #ifdef AB_REPEAT_ENABLE | ||
3072 | ab_position_report(value); | ||
3073 | #endif | ||
3074 | |||
3075 | unsigned long latency = pcmbuf_get_latency(); | ||
3076 | |||
3077 | if (LIKELY(value >= latency)) | ||
3078 | { | ||
3079 | unsigned long elapsed = value - latency; | ||
3080 | |||
3081 | if (elapsed > value || elapsed < value - 2) | ||
3082 | value = elapsed; | ||
3083 | } | ||
3084 | else | ||
3085 | { | ||
3086 | value = 0; | ||
3087 | } | ||
1808 | 3088 | ||
1809 | /* Mark all entries null. */ | 3089 | /* Track codec: used later when updating the playing at the user |
1810 | audio_clear_track_entries(); | 3090 | transition */ |
3091 | id3_get(CODEC_ID3)->elapsed = value; | ||
1811 | 3092 | ||
1812 | /* Close all tracks */ | 3093 | /* If a skip is pending, the PCM buffer is updating the time on the |
1813 | audio_release_tracks(); | 3094 | previous song */ |
3095 | if (LIKELY(skip_pending == TRACK_SKIP_NONE)) | ||
3096 | id3_get(PLAYING_ID3)->elapsed = value; | ||
1814 | } | 3097 | } |
1815 | 3098 | ||
1816 | static void audio_play_start(size_t offset) | 3099 | /* Update offsets with latency-adjusted values */ |
3100 | void audio_codec_update_offset(size_t value) | ||
1817 | { | 3101 | { |
1818 | int i; | 3102 | struct mp3entry *ci_id3 = id3_get(CODEC_ID3); |
3103 | unsigned long latency = pcmbuf_get_latency() * ci_id3->bitrate / 8; | ||
1819 | 3104 | ||
1820 | send_event(PLAYBACK_EVENT_START_PLAYBACK, NULL); | 3105 | if (LIKELY(value >= latency)) |
1821 | #if INPUT_SRC_CAPS != 0 | 3106 | { |
1822 | audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); | 3107 | value -= latency; |
1823 | audio_set_output_source(AUDIO_SRC_PLAYBACK); | 3108 | } |
1824 | #endif | 3109 | else |
3110 | { | ||
3111 | value = 0; | ||
3112 | } | ||
1825 | 3113 | ||
1826 | paused = false; | 3114 | /* Track codec: used later when updating the playing id3 at the user |
1827 | audio_stop_codec_flush(); | 3115 | transition */ |
3116 | ci_id3->offset = value; | ||
1828 | 3117 | ||
1829 | playing = true; | 3118 | /* If a skip is pending, the PCM buffer is updating the time on the |
1830 | track_load_started = false; | 3119 | previous song */ |
3120 | if (LIKELY(skip_pending == TRACK_SKIP_NONE)) | ||
3121 | id3_get(PLAYING_ID3)->offset = value; | ||
3122 | } | ||
1831 | 3123 | ||
1832 | ci.new_track = 0; | ||
1833 | ci.seek_time = 0; | ||
1834 | wps_offset = 0; | ||
1835 | 3124 | ||
1836 | #ifndef PLATFORM_HAS_VOLUME_CHANGE | 3125 | /** --- Pcmbuf callbacks --- **/ |
1837 | sound_set_volume(global_settings.volume); | ||
1838 | #endif | ||
1839 | track_widx = track_ridx = 0; | ||
1840 | buf_set_base_handle(-1); | ||
1841 | 3126 | ||
1842 | /* Clear all track entries. */ | 3127 | /* Between the codec and PCM track change, we need to keep updating the |
1843 | for (i = 0; i < MAX_TRACK; i++) { | 3128 | * "elapsed" value of the previous (to the codec, but current to the |
1844 | clear_track_info(&tracks[i]); | 3129 | * user/PCM/WPS) track, so that the progressbar reaches the end. */ |
3130 | void audio_pcmbuf_position_callback(unsigned int time) | ||
3131 | { | ||
3132 | struct mp3entry *id3 = id3_get(PLAYING_ID3); | ||
3133 | |||
3134 | time += id3->elapsed; | ||
3135 | |||
3136 | id3->elapsed = MIN(time, id3->length); | ||
3137 | } | ||
3138 | |||
3139 | /* Post message from pcmbuf that the end of the previous track has just | ||
3140 | * been played */ | ||
3141 | void audio_pcmbuf_track_change(bool pcmbuf) | ||
3142 | { | ||
3143 | if (pcmbuf) | ||
3144 | { | ||
3145 | /* Notify of the change in special-purpose semaphore object */ | ||
3146 | LOGFQUEUE("pcmbuf > pcmbuf Q_AUDIO_TRACK_CHANGED"); | ||
3147 | audio_pcmbuf_track_change_post(); | ||
1845 | } | 3148 | } |
3149 | else | ||
3150 | { | ||
3151 | /* Safe to post directly to the queue */ | ||
3152 | LOGFQUEUE("pcmbuf > audio Q_AUDIO_TRACK_CHANGED"); | ||
3153 | audio_queue_post(Q_AUDIO_TRACK_CHANGED, 0); | ||
3154 | } | ||
3155 | } | ||
1846 | 3156 | ||
1847 | last_peek_offset = -1; | 3157 | /* May pcmbuf start PCM playback when the buffer is full enough? */ |
3158 | bool audio_pcmbuf_may_play(void) | ||
3159 | { | ||
3160 | return play_status == PLAY_PLAYING && !ff_rw_mode; | ||
3161 | } | ||
1848 | 3162 | ||
1849 | /* Officially playing */ | ||
1850 | queue_reply(&audio_queue, 1); | ||
1851 | 3163 | ||
1852 | audio_fill_file_buffer(true, offset); | 3164 | /** -- External interfaces -- **/ |
1853 | 3165 | ||
1854 | add_event(BUFFER_EVENT_BUFFER_LOW, false, buffering_low_buffer_callback); | 3166 | /* Return the playback and recording status */ |
3167 | int audio_status(void) | ||
3168 | { | ||
3169 | unsigned int ret = play_status; | ||
3170 | |||
3171 | #ifdef AUDIO_HAVE_RECORDING | ||
3172 | /* Do this here for constitency with mpeg.c version */ | ||
3173 | ret |= pcm_rec_status(); | ||
3174 | #endif | ||
1855 | 3175 | ||
1856 | LOGFQUEUE("audio > audio Q_AUDIO_TRACK_CHANGED"); | 3176 | return (int)ret; |
1857 | queue_post(&audio_queue, Q_AUDIO_TRACK_CHANGED, 0); | ||
1858 | } | 3177 | } |
1859 | 3178 | ||
3179 | /* Clear all accumulated audio errors for playback and recording */ | ||
3180 | void audio_error_clear(void) | ||
3181 | { | ||
3182 | #ifdef AUDIO_HAVE_RECORDING | ||
3183 | pcm_rec_error_clear(); | ||
3184 | #endif | ||
3185 | } | ||
1860 | 3186 | ||
1861 | /* Invalidates all but currently playing track. */ | 3187 | /* Get a copy of the id3 data for the for current track + offset + skip delta */ |
1862 | static void audio_invalidate_tracks(void) | 3188 | bool audio_peek_track(struct mp3entry *id3, int offset) |
1863 | { | 3189 | { |
1864 | if (audio_have_tracks()) | 3190 | bool retval = false; |
3191 | |||
3192 | id3_mutex_lock(); | ||
3193 | |||
3194 | if (play_status != PLAY_STOPPED) | ||
1865 | { | 3195 | { |
1866 | last_peek_offset = 0; | 3196 | id3->path[0] = '\0'; /* Null path means it should be filled now */ |
1867 | track_widx = track_ridx; | 3197 | retval = audio_get_track_metadata(offset + skip_offset, id3) && |
3198 | id3->path[0] != '\0'; | ||
3199 | } | ||
1868 | 3200 | ||
1869 | /* Mark all other entries null (also buffered wrong metadata). */ | 3201 | id3_mutex_unlock(); |
1870 | audio_clear_track_entries(); | ||
1871 | 3202 | ||
1872 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; | 3203 | return retval; |
3204 | } | ||
3205 | |||
3206 | /* Return the mp3entry for the currently playing track */ | ||
3207 | struct mp3entry * audio_current_track(void) | ||
3208 | { | ||
3209 | struct mp3entry *id3; | ||
1873 | 3210 | ||
1874 | audio_fill_file_buffer(false, 0); | 3211 | id3_mutex_lock(); |
1875 | send_event(PLAYBACK_EVENT_TRACK_CHANGE, thistrack_id3); | 3212 | |
3213 | #ifdef AUDIO_FAST_SKIP_PREVIEW | ||
3214 | if (skip_offset != 0) | ||
3215 | { | ||
3216 | /* This is a peekahead */ | ||
3217 | id3 = id3_get(PLAYING_PEEK_ID3); | ||
3218 | audio_peek_track(id3, 0); | ||
1876 | } | 3219 | } |
3220 | else | ||
3221 | #endif | ||
3222 | { | ||
3223 | /* Normal case */ | ||
3224 | id3 = id3_get(PLAYING_ID3); | ||
3225 | audio_get_track_metadata(0, id3); | ||
3226 | } | ||
3227 | |||
3228 | id3_mutex_unlock(); | ||
3229 | |||
3230 | return id3; | ||
1877 | } | 3231 | } |
1878 | 3232 | ||
1879 | static void audio_new_playlist(void) | 3233 | /* Obtains the mp3entry for the next track from the current */ |
3234 | struct mp3entry * audio_next_track(void) | ||
1880 | { | 3235 | { |
1881 | /* Prepare to start a new fill from the beginning of the playlist */ | 3236 | struct mp3entry *id3 = id3_get(NEXTTRACK_ID3); |
1882 | last_peek_offset = -1; | ||
1883 | 3237 | ||
1884 | /* Signal the codec to initiate a track change forward */ | 3238 | id3_mutex_lock(); |
1885 | new_playlist = true; | ||
1886 | ci.new_track = 1; | ||
1887 | 3239 | ||
1888 | if (audio_have_tracks()) | 3240 | #ifdef AUDIO_FAST_SKIP_PREVIEW |
3241 | if (skip_offset != 0) | ||
3242 | { | ||
3243 | /* This is a peekahead */ | ||
3244 | if (!audio_peek_track(id3, 1)) | ||
3245 | id3 = NULL; | ||
3246 | } | ||
3247 | else | ||
3248 | #endif | ||
1889 | { | 3249 | { |
1890 | if (paused) | 3250 | /* Normal case */ |
1891 | skipped_during_pause = true; | 3251 | if (!audio_get_track_metadata(1, id3)) |
1892 | track_widx = track_ridx; | 3252 | id3 = NULL; |
1893 | audio_clear_track_entries(); | 3253 | } |
1894 | 3254 | ||
1895 | track_widx = (track_widx + 1) & MAX_TRACK_MASK; | 3255 | id3_mutex_unlock(); |
1896 | 3256 | ||
1897 | /* Mark the current track as invalid to prevent skipping back to it */ | 3257 | return id3; |
1898 | CUR_TI->taginfo_ready = false; | 3258 | } |
1899 | } | ||
1900 | 3259 | ||
1901 | /* Officially playing */ | 3260 | /* Start playback at the specified offset */ |
1902 | queue_reply(&audio_queue, 1); | 3261 | void audio_play(long offset) |
3262 | { | ||
3263 | logf("audio_play"); | ||
1903 | 3264 | ||
1904 | audio_fill_file_buffer(false, 0); | 3265 | #ifdef PLAYBACK_VOICE |
3266 | /* Truncate any existing voice output so we don't have spelling | ||
3267 | * etc. over the first part of the played track */ | ||
3268 | talk_force_shutup(); | ||
3269 | #endif | ||
3270 | |||
3271 | LOGFQUEUE("audio >| audio Q_AUDIO_PLAY: %ld", offset); | ||
3272 | audio_queue_send(Q_AUDIO_PLAY, offset); | ||
1905 | } | 3273 | } |
1906 | 3274 | ||
1907 | /* Called on manual track skip */ | 3275 | /* Stop playback if playing */ |
1908 | static void audio_initiate_track_change(long direction) | 3276 | void audio_stop(void) |
1909 | { | 3277 | { |
1910 | logf("audio_initiate_track_change(%ld)", direction); | 3278 | LOGFQUEUE("audio >| audio Q_AUDIO_STOP"); |
3279 | audio_queue_send(Q_AUDIO_STOP, 0); | ||
3280 | } | ||
1911 | 3281 | ||
1912 | ci.new_track += direction; | 3282 | /* Pause playback if playing */ |
1913 | wps_offset -= direction; | 3283 | void audio_pause(void) |
1914 | if (paused) | 3284 | { |
1915 | skipped_during_pause = true; | 3285 | LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE"); |
3286 | audio_queue_send(Q_AUDIO_PAUSE, true); | ||
3287 | } | ||
3288 | |||
3289 | /* This sends a stop message and the audio thread will dump all its | ||
3290 | subsequent messages */ | ||
3291 | void audio_hard_stop(void) | ||
3292 | { | ||
3293 | /* Stop playback */ | ||
3294 | LOGFQUEUE("audio >| audio Q_AUDIO_STOP: 1"); | ||
3295 | audio_queue_send(Q_AUDIO_STOP, 1); | ||
3296 | #ifdef PLAYBACK_VOICE | ||
3297 | voice_stop(); | ||
3298 | #endif | ||
1916 | } | 3299 | } |
1917 | 3300 | ||
1918 | /* Called on manual dir skip */ | 3301 | /* Resume playback if paused */ |
1919 | static void audio_initiate_dir_change(long direction) | 3302 | void audio_resume(void) |
1920 | { | 3303 | { |
1921 | dir_skip = true; | 3304 | LOGFQUEUE("audio >| audio Q_AUDIO_PAUSE resume"); |
1922 | ci.new_track = direction; | 3305 | audio_queue_send(Q_AUDIO_PAUSE, false); |
1923 | if (paused) | ||
1924 | skipped_during_pause = true; | ||
1925 | } | 3306 | } |
1926 | 3307 | ||
1927 | /* Called when PCM track change is complete */ | 3308 | /* Skip the specified number of tracks forward or backward from the current */ |
1928 | static void audio_finalise_track_change(void) | 3309 | void audio_skip(int offset) |
1929 | { | 3310 | { |
1930 | logf("audio_finalise_track_change"); | 3311 | id3_mutex_lock(); |
3312 | |||
3313 | /* If offset has to be backed-out to stay in range, no skip is done */ | ||
3314 | int accum = skip_offset + offset; | ||
1931 | 3315 | ||
1932 | if (automatic_skip) | 3316 | while (offset != 0 && !playlist_check(accum)) |
1933 | { | 3317 | { |
1934 | wps_offset = 0; | 3318 | offset += offset < 0 ? 1 : -1; |
1935 | automatic_skip = false; | 3319 | accum = skip_offset + offset; |
3320 | } | ||
1936 | 3321 | ||
1937 | /* Invalidate prevtrack_id3 */ | 3322 | if (offset != 0) |
1938 | memset(othertrack_id3, 0, sizeof(struct mp3entry)); | 3323 | { |
3324 | /* Accumulate net manual skip count since the audio thread last | ||
3325 | processed one */ | ||
3326 | skip_offset = accum; | ||
1939 | 3327 | ||
1940 | if (prev_ti && prev_ti->audio_hid < 0) | 3328 | if (global_settings.beep) |
1941 | { | 3329 | pcmbuf_beep(2000, 100, 2500*global_settings.beep); |
1942 | /* No audio left so we clear all the track info. */ | 3330 | |
1943 | clear_track_info(prev_ti); | 3331 | LOGFQUEUE("audio > audio Q_AUDIO_SKIP %d", offset); |
1944 | } | 3332 | |
3333 | #ifdef AUDIO_FAST_SKIP_PREVIEW | ||
3334 | /* Do this before posting so that the audio thread can correct us | ||
3335 | when things settle down - additionally, if audio gets a message | ||
3336 | and the delta is zero, the Q_AUDIO_SKIP handler (audio_on_skip) | ||
3337 | handler a skip event with the correct info but doesn't skip */ | ||
3338 | send_event(PLAYBACK_EVENT_TRACK_SKIP, NULL); | ||
3339 | #endif /* AUDIO_FAST_SKIP_PREVIEW */ | ||
3340 | |||
3341 | /* Playback only needs the final state even if more than one is | ||
3342 | processed because it wasn't removed in time */ | ||
3343 | queue_remove_from_head(&audio_queue, Q_AUDIO_SKIP); | ||
3344 | audio_queue_post(Q_AUDIO_SKIP, 0); | ||
1945 | } | 3345 | } |
1946 | send_event(PLAYBACK_EVENT_TRACK_CHANGE, thistrack_id3); | 3346 | else |
1947 | playlist_update_resume_info(audio_current_track()); | 3347 | { |
3348 | /* No more tracks */ | ||
3349 | if (global_settings.beep) | ||
3350 | pcmbuf_beep(1000, 100, 1500*global_settings.beep); | ||
3351 | } | ||
3352 | |||
3353 | id3_mutex_unlock(); | ||
1948 | } | 3354 | } |
1949 | 3355 | ||
1950 | static void audio_seek_complete(void) | 3356 | /* Skip one track forward from the current */ |
3357 | void audio_next(void) | ||
1951 | { | 3358 | { |
1952 | logf("audio_seek_complete"); | 3359 | audio_skip(1); |
3360 | } | ||
1953 | 3361 | ||
1954 | if (!playing) | 3362 | /* Skip one track backward from the current */ |
1955 | return; | 3363 | void audio_prev(void) |
3364 | { | ||
3365 | audio_skip(-1); | ||
3366 | } | ||
1956 | 3367 | ||
1957 | /* If seeking-while-playing, pcm_is_paused() is true. | 3368 | /* Move one directory forward */ |
1958 | * If seeking-while-paused, audio_status PAUSE is true. | 3369 | void audio_next_dir(void) |
1959 | * A seamless seek skips this section. */ | 3370 | { |
1960 | ci.seek_time = 0; | 3371 | LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP 1"); |
3372 | audio_queue_post(Q_AUDIO_DIR_SKIP, 1); | ||
3373 | } | ||
1961 | 3374 | ||
1962 | pcm_play_lock(); | 3375 | /* Move one directory backward */ |
3376 | void audio_prev_dir(void) | ||
3377 | { | ||
3378 | LOGFQUEUE("audio > audio Q_AUDIO_DIR_SKIP -1"); | ||
3379 | audio_queue_post(Q_AUDIO_DIR_SKIP, -1); | ||
3380 | } | ||
1963 | 3381 | ||
1964 | if (pcm_is_paused() || paused) | 3382 | /* Pause playback in order to start a seek that flushes the old audio */ |
1965 | { | 3383 | void audio_pre_ff_rewind(void) |
1966 | /* Clear the buffer */ | 3384 | { |
1967 | pcmbuf_play_stop(); | 3385 | LOGFQUEUE("audio > audio Q_AUDIO_PRE_FF_REWIND"); |
3386 | audio_queue_post(Q_AUDIO_PRE_FF_REWIND, 0); | ||
3387 | } | ||
1968 | 3388 | ||
1969 | /* If seeking-while-playing, resume PCM playback */ | 3389 | /* Seek to the new time in the current track */ |
1970 | if (!paused) | 3390 | void audio_ff_rewind(long time) |
1971 | pcmbuf_pause(false); | 3391 | { |
1972 | } | 3392 | LOGFQUEUE("audio > audio Q_AUDIO_FF_REWIND"); |
3393 | audio_queue_post(Q_AUDIO_FF_REWIND, time); | ||
3394 | } | ||
1973 | 3395 | ||
1974 | pcm_play_unlock(); | 3396 | /* Clear all but the currently playing track then rebuffer */ |
3397 | void audio_flush_and_reload_tracks(void) | ||
3398 | { | ||
3399 | LOGFQUEUE("audio > audio Q_AUDIO_FLUSH"); | ||
3400 | audio_queue_post(Q_AUDIO_FLUSH, 0); | ||
1975 | } | 3401 | } |
1976 | 3402 | ||
1977 | static void audio_codec_status_message(long reason, int status) | 3403 | /* Return the pointer to the main audio buffer, optionally preserving |
3404 | voicing */ | ||
3405 | unsigned char * audio_get_buffer(bool talk_buf, size_t *buffer_size) | ||
1978 | { | 3406 | { |
1979 | /* TODO: Push the errors up to the normal UI somewhere */ | 3407 | unsigned char *buf, *end; |
1980 | switch (reason) | 3408 | |
3409 | if (audio_is_initialized) | ||
1981 | { | 3410 | { |
1982 | case Q_CODEC_LOAD_DISK: | 3411 | audio_hard_stop(); |
1983 | case Q_CODEC_LOAD: | 3412 | } |
1984 | if (!playing) | 3413 | /* else buffer_state will be AUDIOBUF_STATE_TRASHED at this point */ |
1985 | return; | ||
1986 | 3414 | ||
1987 | if (status < 0) | 3415 | if (buffer_size == NULL) |
3416 | { | ||
3417 | /* Special case for talk_init to use since it already knows it's | ||
3418 | trashed */ | ||
3419 | buffer_state = AUDIOBUF_STATE_TRASHED; | ||
3420 | return NULL; | ||
3421 | } | ||
3422 | |||
3423 | if (talk_buf || buffer_state == AUDIOBUF_STATE_TRASHED | ||
3424 | || !talk_voice_required()) | ||
3425 | { | ||
3426 | logf("get buffer: talk, audio"); | ||
3427 | /* Ok to use everything from audiobuf to audiobufend - voice is loaded, | ||
3428 | the talk buffer is not needed because voice isn't being used, or | ||
3429 | could be AUDIOBUF_STATE_TRASHED already. If state is | ||
3430 | AUDIOBUF_STATE_VOICED_ONLY, no problem as long as memory isn't written | ||
3431 | without the caller knowing what's going on. Changing certain settings | ||
3432 | may move it to a worse condition but the memory in use by something | ||
3433 | else will remain undisturbed. | ||
3434 | */ | ||
3435 | if (buffer_state != AUDIOBUF_STATE_TRASHED) | ||
1988 | { | 3436 | { |
1989 | splash(HZ*2, "Codec failure"); | 3437 | talk_buffer_steal(); |
1990 | audio_check_new_track(); | 3438 | buffer_state = AUDIOBUF_STATE_TRASHED; |
1991 | } | 3439 | } |
1992 | break; | ||
1993 | 3440 | ||
1994 | #ifdef AUDIO_HAVE_RECORDING | 3441 | buf = audiobuf; |
1995 | case Q_ENCODER_LOAD_DISK: | 3442 | end = audiobufend; |
1996 | if (status < 0) | ||
1997 | splash(HZ*2, "Encoder failure"); | ||
1998 | break; | ||
1999 | #endif /* AUDIO_HAVE_RECORDING */ | ||
2000 | } | 3443 | } |
3444 | else | ||
3445 | { | ||
3446 | /* Safe to just return this if already AUDIOBUF_STATE_VOICED_ONLY or | ||
3447 | still AUDIOBUF_STATE_INITIALIZED */ | ||
3448 | /* Skip talk buffer and move pcm buffer to end to maximize available | ||
3449 | contiguous memory - no audio running means voice will not need the | ||
3450 | swap space */ | ||
3451 | logf("get buffer: audio"); | ||
3452 | buf = audiobuf + talk_get_bufsize(); | ||
3453 | end = audiobufend - pcmbuf_init(audiobufend); | ||
3454 | buffer_state = AUDIOBUF_STATE_VOICED_ONLY; | ||
3455 | } | ||
3456 | |||
3457 | *buffer_size = end - buf; | ||
3458 | |||
3459 | return buf; | ||
2001 | } | 3460 | } |
2002 | 3461 | ||
2003 | /* | 3462 | #ifdef HAVE_RECORDING |
2004 | * Layout audio buffer as follows - iram buffer depends on target: | 3463 | /* Stop audio, voice and obtain all available buffer space */ |
2005 | * [|SWAP:iram][|TALK]|FILE|GUARD|PCM|[SWAP:dram[|iram]|] | 3464 | unsigned char * audio_get_recording_buffer(size_t *buffer_size) |
2006 | */ | ||
2007 | static void audio_reset_buffer(void) | ||
2008 | { | 3465 | { |
2009 | /* see audio_get_recording_buffer if this is modified */ | 3466 | audio_hard_stop(); |
2010 | logf("audio_reset_buffer"); | 3467 | talk_buffer_steal(); |
2011 | 3468 | ||
2012 | /* If the setup of anything allocated before the file buffer is | 3469 | unsigned char *end = audiobufend; |
2013 | changed, do check the adjustments after the buffer_alloc call | 3470 | buffer_state = AUDIOBUF_STATE_TRASHED; |
2014 | as it will likely be affected and need sliding over */ | 3471 | *buffer_size = end - audiobuf; |
2015 | 3472 | ||
2016 | /* Initially set up file buffer as all space available */ | 3473 | return (unsigned char *)audiobuf; |
2017 | filebuf = audiobuf + talk_get_bufsize(); | 3474 | } |
2018 | filebuflen = audiobufend - filebuf; | 3475 | #endif /* HAVE_RECORDING */ |
2019 | 3476 | ||
2020 | ALIGN_BUFFER(filebuf, filebuflen, sizeof (intptr_t)); | 3477 | /* Restore audio buffer to a particular state (one more valid than the current |
3478 | state) */ | ||
3479 | bool audio_restore_playback(int type) | ||
3480 | { | ||
3481 | switch (type) | ||
3482 | { | ||
3483 | case AUDIO_WANT_PLAYBACK: | ||
3484 | if (buffer_state != AUDIOBUF_STATE_INITIALIZED) | ||
3485 | audio_reset_buffer(); | ||
3486 | return true; | ||
3487 | case AUDIO_WANT_VOICE: | ||
3488 | if (buffer_state == AUDIOBUF_STATE_TRASHED) | ||
3489 | audio_reset_buffer(); | ||
3490 | return true; | ||
3491 | default: | ||
3492 | return false; | ||
3493 | } | ||
3494 | } | ||
2021 | 3495 | ||
2022 | /* Subtract whatever the pcm buffer says it used plus the guard buffer */ | 3496 | /* Has the playback buffer been completely claimed? */ |
2023 | size_t pcmbuf_size = pcmbuf_init(filebuf + filebuflen) + GUARD_BUFSIZE; | 3497 | bool audio_buffer_state_trashed(void) |
3498 | { | ||
3499 | return buffer_state == AUDIOBUF_STATE_TRASHED; | ||
3500 | } | ||
2024 | 3501 | ||
2025 | /* Make sure filebuflen is a pointer sized multiple after adjustment */ | ||
2026 | pcmbuf_size = ALIGN_UP(pcmbuf_size, sizeof (intptr_t)); | ||
2027 | 3502 | ||
2028 | if(pcmbuf_size > filebuflen) | 3503 | /** --- Miscellaneous public interfaces --- **/ |
2029 | panicf("%s(): EOM (%zu > %zu)", __func__, pcmbuf_size, filebuflen); | ||
2030 | 3504 | ||
2031 | filebuflen -= pcmbuf_size; | 3505 | #ifdef HAVE_ALBUMART |
2032 | buffering_reset(filebuf, filebuflen); | 3506 | /* Return which album art handle is current for the user in the given slot */ |
3507 | int playback_current_aa_hid(int slot) | ||
3508 | { | ||
3509 | if ((unsigned)slot < MAX_MULTIPLE_AA) | ||
3510 | { | ||
3511 | struct track_info *info = track_list_user_current(skip_offset); | ||
2033 | 3512 | ||
2034 | /* Clear any references to the file buffer */ | 3513 | if (!info && abs(skip_offset) <= 1) |
2035 | buffer_state = AUDIOBUF_STATE_INITIALIZED; | 3514 | { |
3515 | /* Give the actual position a go */ | ||
3516 | info = track_list_user_current(0); | ||
3517 | } | ||
2036 | 3518 | ||
2037 | #if defined(ROCKBOX_HAS_LOGF) && defined(LOGF_ENABLE) | 3519 | if (info) |
2038 | /* Make sure everything adds up - yes, some info is a bit redundant but | 3520 | return info->aa_hid[slot]; |
2039 | aids viewing and the sumation of certain variables should add up to | ||
2040 | the location of others. */ | ||
2041 | { | ||
2042 | size_t pcmbufsize; | ||
2043 | const unsigned char *pcmbuf = pcmbuf_get_meminfo(&pcmbufsize); | ||
2044 | logf("fbuf: %08X", (unsigned)filebuf); | ||
2045 | logf("fbufe: %08X", (unsigned)(filebuf + filebuflen)); | ||
2046 | logf("gbuf: %08X", (unsigned)(filebuf + filebuflen)); | ||
2047 | logf("gbufe: %08X", (unsigned)(filebuf + filebuflen + GUARD_BUFSIZE)); | ||
2048 | logf("pcmb: %08X", (unsigned)pcmbuf); | ||
2049 | logf("pcmbe: %08X", (unsigned)(pcmbuf + pcmbufsize)); | ||
2050 | } | 3521 | } |
2051 | #endif | 3522 | |
3523 | return ERR_HANDLE_NOT_FOUND; | ||
2052 | } | 3524 | } |
2053 | 3525 | ||
2054 | static void audio_thread(void) | 3526 | /* Find an album art slot that doesn't match the dimensions of another that |
3527 | is already claimed - increment the use count if it is */ | ||
3528 | int playback_claim_aa_slot(struct dim *dim) | ||
2055 | { | 3529 | { |
2056 | struct queue_event ev; | 3530 | int i; |
2057 | |||
2058 | pcm_postinit(); | ||
2059 | |||
2060 | audio_thread_ready = true; | ||
2061 | 3531 | ||
2062 | while (1) | 3532 | /* First try to find a slot already having the size to reuse it since we |
3533 | don't want albumart of the same size buffered multiple times */ | ||
3534 | FOREACH_ALBUMART(i) | ||
2063 | { | 3535 | { |
2064 | switch (filling) { | 3536 | struct albumart_slot *slot = &albumart_slots[i]; |
2065 | case STATE_IDLE: | ||
2066 | queue_wait(&audio_queue, &ev); | ||
2067 | break; | ||
2068 | 3537 | ||
2069 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 3538 | if (slot->dim.width == dim->width && |
2070 | case STATE_USB: | 3539 | slot->dim.height == dim->height) |
2071 | queue_wait(&audio_queue, &ev); | 3540 | { |
2072 | switch (ev.id) { | 3541 | slot->used++; |
2073 | #ifdef AUDIO_HAVE_RECORDING | 3542 | return i; |
2074 | /* Must monitor the encoder message for recording so it can | 3543 | } |
2075 | remove it if we process the insertion before it does. It | 3544 | } |
2076 | cannot simply be removed from under recording however. */ | ||
2077 | case Q_AUDIO_LOAD_ENCODER: | ||
2078 | break; | ||
2079 | #endif | ||
2080 | case SYS_USB_DISCONNECTED: | ||
2081 | filling = STATE_IDLE; | ||
2082 | default: | ||
2083 | continue; | ||
2084 | } | ||
2085 | break; | ||
2086 | #endif /* CONFIG_PLATFORM */ | ||
2087 | 3545 | ||
2088 | default: | 3546 | /* Size is new, find a free slot */ |
2089 | /* End of buffering, let's calculate the watermark and | 3547 | FOREACH_ALBUMART(i) |
2090 | unboost */ | 3548 | { |
2091 | set_filebuf_watermark(); | 3549 | if (!albumart_slots[i].used) |
2092 | cancel_cpu_boost(); | 3550 | { |
2093 | /* Fall-through */ | 3551 | albumart_slots[i].used++; |
2094 | case STATE_FILLING: | 3552 | albumart_slots[i].dim = *dim; |
2095 | case STATE_ENDING: | 3553 | return i; |
2096 | if (!pcmbuf_queue_scan(&ev)) | ||
2097 | queue_wait_w_tmo(&audio_queue, &ev, HZ/2); | ||
2098 | break; | ||
2099 | } | 3554 | } |
3555 | } | ||
2100 | 3556 | ||
2101 | switch (ev.id) { | 3557 | /* Sorry, no free slot */ |
3558 | return -1; | ||
3559 | } | ||
2102 | 3560 | ||
2103 | case Q_AUDIO_FILL_BUFFER: | 3561 | /* Invalidate the albumart_slot - decrement the use count if > 0 */ |
2104 | LOGFQUEUE("audio < Q_AUDIO_FILL_BUFFER %d", (int)ev.data); | 3562 | void playback_release_aa_slot(int slot) |
2105 | audio_fill_file_buffer((bool)ev.data, 0); | 3563 | { |
2106 | break; | 3564 | if ((unsigned)slot < MAX_MULTIPLE_AA) |
3565 | { | ||
3566 | struct albumart_slot *aa_slot = &albumart_slots[slot]; | ||
2107 | 3567 | ||
2108 | case Q_AUDIO_FINISH_LOAD: | 3568 | if (aa_slot->used > 0) |
2109 | LOGFQUEUE("audio < Q_AUDIO_FINISH_LOAD"); | 3569 | aa_slot->used--; |
2110 | audio_finish_load_track(); | 3570 | } |
2111 | buf_set_base_handle(CUR_TI->audio_hid); | 3571 | } |
2112 | break; | 3572 | #endif /* HAVE_ALBUMART */ |
2113 | 3573 | ||
2114 | case Q_AUDIO_PLAY: | ||
2115 | LOGFQUEUE("audio < Q_AUDIO_PLAY"); | ||
2116 | if (playing && ev.data <= 0) | ||
2117 | audio_new_playlist(); | ||
2118 | else | ||
2119 | { | ||
2120 | audio_stop_playback(); | ||
2121 | audio_play_start((size_t)ev.data); | ||
2122 | } | ||
2123 | break; | ||
2124 | 3574 | ||
2125 | case Q_AUDIO_STOP: | 3575 | #ifdef HAVE_RECORDING |
2126 | LOGFQUEUE("audio < Q_AUDIO_STOP"); | 3576 | /* Load an encoder and run it */ |
2127 | if (playing) | 3577 | bool audio_load_encoder(int afmt) |
2128 | audio_stop_playback(); | 3578 | { |
2129 | if (ev.data != 0) | 3579 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
2130 | queue_clear(&audio_queue); | 3580 | LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: %d", afmt); |
2131 | break; | 3581 | return audio_queue_send(Q_AUDIO_LOAD_ENCODER, afmt) != 0; |
3582 | #else | ||
3583 | (void)afmt; | ||
3584 | return true; | ||
3585 | #endif | ||
3586 | } | ||
2132 | 3587 | ||
2133 | case Q_AUDIO_PAUSE: | 3588 | /* Stop an encoder and unload it */ |
2134 | LOGFQUEUE("audio < Q_AUDIO_PAUSE"); | 3589 | void audio_remove_encoder(void) |
2135 | if (!(bool) ev.data && skipped_during_pause | 3590 | { |
2136 | #ifdef HAVE_CROSSFADE | 3591 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) |
2137 | && !pcmbuf_is_crossfade_active() | 3592 | LOGFQUEUE("audio >| Q_AUDIO_LOAD_ENCODER: NULL"); |
3593 | audio_queue_send(Q_AUDIO_LOAD_ENCODER, AFMT_UNKNOWN); | ||
2138 | #endif | 3594 | #endif |
2139 | ) | 3595 | } |
2140 | pcmbuf_play_stop(); /* Flush old track on resume after skip */ | 3596 | #endif /* HAVE_RECORDING */ |
2141 | skipped_during_pause = false; | ||
2142 | if (!playing) | ||
2143 | break; | ||
2144 | pcmbuf_pause((bool)ev.data); | ||
2145 | paused = (bool)ev.data; | ||
2146 | break; | ||
2147 | 3597 | ||
2148 | case Q_AUDIO_SKIP: | 3598 | /* Is an automatic skip in progress? If called outside transistion callbacks, |
2149 | LOGFQUEUE("audio < Q_AUDIO_SKIP"); | 3599 | indicates the last skip type at the time it was processed and isn't very |
2150 | audio_initiate_track_change((long)ev.data); | 3600 | meaningful. */ |
2151 | break; | 3601 | bool audio_automatic_skip(void) |
3602 | { | ||
3603 | return automatic_skip; | ||
3604 | } | ||
2152 | 3605 | ||
2153 | case Q_AUDIO_PRE_FF_REWIND: | 3606 | /* Would normally calculate byte offset from an elapsed time but is not |
2154 | LOGFQUEUE("audio < Q_AUDIO_PRE_FF_REWIND"); | 3607 | used on SWCODEC */ |
2155 | if (!playing) | 3608 | int audio_get_file_pos(void) |
2156 | break; | 3609 | { |
2157 | pcmbuf_pause(true); | 3610 | return 0; |
2158 | break; | 3611 | } |
2159 | 3612 | ||
2160 | case Q_AUDIO_FF_REWIND: | 3613 | /* Return the elasped time of the track previous to the current */ |
2161 | LOGFQUEUE("audio < Q_AUDIO_FF_REWIND"); | 3614 | unsigned long audio_prev_elapsed(void) |
2162 | if (!playing) | 3615 | { |
2163 | break; | 3616 | return prev_track_elapsed; |
3617 | } | ||
2164 | 3618 | ||
2165 | if (filling == STATE_ENDING) | 3619 | /* Is the audio thread ready to accept commands? */ |
2166 | { | 3620 | bool audio_is_thread_ready(void) |
2167 | /* Temp workaround: There is no codec available */ | 3621 | { |
2168 | if (!paused) | 3622 | return filling != STATE_BOOT; |
2169 | pcmbuf_pause(false); | 3623 | } |
2170 | break; | ||
2171 | } | ||
2172 | 3624 | ||
2173 | if ((long)ev.data == 0) | 3625 | /* Return total file buffer length after accounting for the talk buf */ |
2174 | { | 3626 | size_t audio_get_filebuflen(void) |
2175 | /* About to restart the track - send track finish | 3627 | { |
2176 | events if not already done. */ | 3628 | return buf_length(); |
2177 | if (thistrack_id3 == audio_current_track()) | 3629 | } |
2178 | send_event(PLAYBACK_EVENT_TRACK_FINISH, thistrack_id3); | ||
2179 | } | ||
2180 | 3630 | ||
2181 | if (automatic_skip) | 3631 | /* How many tracks exist on the buffer - full or partial */ |
2182 | { | 3632 | int audio_track_count(void) |
2183 | /* An automatic track skip is in progress. Finalize it, | 3633 | __attribute__((alias("track_list_count"))); |
2184 | then go back to the previous track */ | ||
2185 | audio_finalise_track_change(); | ||
2186 | ci.new_track = -1; | ||
2187 | } | ||
2188 | ci.seek_time = (long)ev.data+1; | ||
2189 | break; | ||
2190 | 3634 | ||
2191 | case Q_AUDIO_CHECK_NEW_TRACK: | 3635 | /* Return total ringbuffer space occupied - ridx to widx */ |
2192 | LOGFQUEUE("audio < Q_AUDIO_CHECK_NEW_TRACK"); | 3636 | long audio_filebufused(void) |
2193 | audio_check_new_track(); | 3637 | { |
2194 | break; | 3638 | return buf_used(); |
3639 | } | ||
2195 | 3640 | ||
2196 | case Q_AUDIO_DIR_SKIP: | ||
2197 | LOGFQUEUE("audio < Q_AUDIO_DIR_SKIP"); | ||
2198 | audio_initiate_dir_change(ev.data); | ||
2199 | break; | ||
2200 | 3641 | ||
2201 | case Q_AUDIO_FLUSH: | 3642 | /** -- Settings -- **/ |
2202 | LOGFQUEUE("audio < Q_AUDIO_FLUSH"); | ||
2203 | audio_invalidate_tracks(); | ||
2204 | break; | ||
2205 | 3643 | ||
2206 | case Q_AUDIO_TRACK_CHANGED: | 3644 | /* Enable or disable cuesheet support and allocate/don't allocate the |
2207 | /* PCM track change done */ | 3645 | extra associated resources */ |
2208 | LOGFQUEUE("audio < Q_AUDIO_TRACK_CHANGED"); | 3646 | void audio_set_cuesheet(int enable) |
2209 | /* Set new playlist position for resuming. */ | 3647 | { |
2210 | playlist_update_resume_index(); | 3648 | if (play_status == PLAY_STOPPED || !enable != !get_current_cuesheet()) |
2211 | if (filling != STATE_ENDING) | 3649 | { |
2212 | audio_finalise_track_change(); | 3650 | LOGFQUEUE("audio >| audio Q_AUDIO_REMAKE_AUDIO_BUFFER"); |
2213 | else if (playing) | 3651 | audio_queue_send(Q_AUDIO_REMAKE_AUDIO_BUFFER, 0); |
2214 | audio_stop_playback(); | 3652 | } |
2215 | break; | 3653 | } |
2216 | 3654 | ||
2217 | case Q_AUDIO_SEEK_COMPLETE: | 3655 | #ifdef HAVE_DISK_STORAGE |
2218 | /* Codec seek done */ | 3656 | /* Set the audio antiskip buffer margin by index */ |
2219 | LOGFQUEUE("audio < Q_AUDIO_SEEK_COMPLETE"); | 3657 | void audio_set_buffer_margin(int setting) |
2220 | audio_seek_complete(); | 3658 | { |
2221 | codec_ack_msg(Q_AUDIO_SEEK_COMPLETE, false); | 3659 | static const unsigned short lookup[] = |
2222 | break; | 3660 | { 5, 15, 30, 60, 120, 180, 300, 600 }; |
2223 | 3661 | ||
2224 | case Q_CODEC_LOAD: | 3662 | if ((unsigned)setting >= ARRAYLEN(lookup)) |
2225 | case Q_CODEC_LOAD_DISK: | 3663 | setting = 0; |
2226 | #ifdef AUDIO_HAVE_RECORDING | ||
2227 | case Q_ENCODER_LOAD_DISK: | ||
2228 | #endif | ||
2229 | /* These are received when a codec has finished normally or | ||
2230 | upon a codec error */ | ||
2231 | audio_codec_status_message(ev.id, ev.data); | ||
2232 | break; | ||
2233 | 3664 | ||
2234 | #if (CONFIG_PLATFORM & PLATFORM_NATIVE) | 3665 | logf("buffer margin: %u", (unsigned)lookup[setting]); |
2235 | case SYS_USB_CONNECTED: | ||
2236 | LOGFQUEUE("audio < SYS_USB_CONNECTED"); | ||
2237 | if (playing) | ||
2238 | audio_stop_playback(); | ||
2239 | #ifdef PLAYBACK_VOICE | ||
2240 | voice_stop(); | ||
2241 | #endif | ||
2242 | filling = STATE_USB; | ||
2243 | usb_acknowledge(SYS_USB_CONNECTED_ACK); | ||
2244 | break; | ||
2245 | #endif | ||
2246 | 3666 | ||
2247 | #ifdef AUDIO_HAVE_RECORDING | 3667 | LOGFQUEUE("audio > audio Q_AUDIO_UPDATE_WATERMARK: %u", |
2248 | case Q_AUDIO_LOAD_ENCODER: | 3668 | (unsigned)lookup[setting]); |
2249 | if (playing) | 3669 | audio_queue_post(Q_AUDIO_UPDATE_WATERMARK, lookup[setting]); |
2250 | audio_stop_playback(); | 3670 | } |
2251 | else | 3671 | #endif /* HAVE_DISK_STORAGE */ |
2252 | codec_stop(); /* If encoder still loaded, stop it */ | ||
2253 | 3672 | ||
2254 | if (ev.data == AFMT_UNKNOWN) | 3673 | #ifdef HAVE_CROSSFADE |
2255 | break; | 3674 | /* Take necessary steps to enable or disable the crossfade setting */ |
3675 | void audio_set_crossfade(int enable) | ||
3676 | { | ||
3677 | /* Tell it the next setting to use */ | ||
3678 | pcmbuf_request_crossfade_enable(enable); | ||
2256 | 3679 | ||
2257 | queue_reply(&audio_queue, | 3680 | /* Return if size hasn't changed or this is too early to determine |
2258 | codec_load(-1, ev.data | CODEC_TYPE_ENCODER)); | 3681 | which in the second case there's no way we could be playing |
2259 | break; | 3682 | anything at all */ |
2260 | #endif /* AUDIO_HAVE_RECORDING */ | 3683 | if (!pcmbuf_is_same_size()) |
3684 | { | ||
3685 | LOGFQUEUE("audio >| audio Q_AUDIO_REMAKE_AUDIO_BUFFER"); | ||
3686 | audio_queue_send(Q_AUDIO_REMAKE_AUDIO_BUFFER, 0); | ||
3687 | } | ||
3688 | } | ||
3689 | #endif /* HAVE_CROSSFADE */ | ||
2261 | 3690 | ||
2262 | case SYS_TIMEOUT: | ||
2263 | LOGFQUEUE_SYS_TIMEOUT("audio < SYS_TIMEOUT"); | ||
2264 | break; | ||
2265 | 3691 | ||
2266 | default: | 3692 | /** -- Startup -- **/ |
2267 | /* LOGFQUEUE("audio < default : %08lX", ev.id); */ | ||
2268 | break; | ||
2269 | } /* end switch */ | ||
2270 | } /* end while */ | ||
2271 | } | ||
2272 | 3693 | ||
2273 | /* Initialize the audio system - called from init() in main.c. | 3694 | /* Initialize the audio system - called from init() in main.c */ |
2274 | * Last function because of all the references to internal symbols | ||
2275 | */ | ||
2276 | void audio_init(void) | 3695 | void audio_init(void) |
2277 | { | 3696 | { |
2278 | unsigned int audio_thread_id; | ||
2279 | |||
2280 | /* Can never do this twice */ | 3697 | /* Can never do this twice */ |
2281 | if (audio_is_initialized) | 3698 | if (audio_is_initialized) |
2282 | { | 3699 | { |
@@ -2290,31 +3707,20 @@ void audio_init(void) | |||
2290 | to send messages. Thread creation will be delayed however so nothing | 3707 | to send messages. Thread creation will be delayed however so nothing |
2291 | starts running until ready if something yields such as talk_init. */ | 3708 | starts running until ready if something yields such as talk_init. */ |
2292 | queue_init(&audio_queue, true); | 3709 | queue_init(&audio_queue, true); |
2293 | queue_init(&pcmbuf_queue, false); | 3710 | |
3711 | mutex_init(&id3_mutex); | ||
2294 | 3712 | ||
2295 | pcm_init(); | 3713 | pcm_init(); |
2296 | 3714 | ||
2297 | codec_init_codec_api(); | 3715 | codec_init_codec_api(); |
2298 | 3716 | ||
2299 | thistrack_id3 = &mp3entry_buf[0]; | ||
2300 | othertrack_id3 = &mp3entry_buf[1]; | ||
2301 | |||
2302 | /* cuesheet support */ | ||
2303 | if (global_settings.cuesheet) | ||
2304 | curr_cue = (struct cuesheet*)buffer_alloc(sizeof(struct cuesheet)); | ||
2305 | |||
2306 | /* initialize the buffer */ | ||
2307 | filebuf = audiobuf; | ||
2308 | |||
2309 | /* audio_reset_buffer must to know the size of voice buffer so init | ||
2310 | talk first */ | ||
2311 | talk_init(); | ||
2312 | |||
2313 | make_codec_thread(); | 3717 | make_codec_thread(); |
2314 | 3718 | ||
3719 | /* This thread does buffer, so match its priority */ | ||
2315 | audio_thread_id = create_thread(audio_thread, audio_stack, | 3720 | audio_thread_id = create_thread(audio_thread, audio_stack, |
2316 | sizeof(audio_stack), CREATE_THREAD_FROZEN, | 3721 | sizeof(audio_stack), CREATE_THREAD_FROZEN, |
2317 | audio_thread_name IF_PRIO(, PRIORITY_USER_INTERFACE) | 3722 | audio_thread_name |
3723 | IF_PRIO(, MIN(PRIORITY_BUFFERING, PRIORITY_USER_INTERFACE)) | ||
2318 | IF_COP(, CPU)); | 3724 | IF_COP(, CPU)); |
2319 | 3725 | ||
2320 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list, | 3726 | queue_enable_queue_send(&audio_queue, &audio_queue_sender_list, |
@@ -2324,39 +3730,21 @@ void audio_init(void) | |||
2324 | voice_thread_init(); | 3730 | voice_thread_init(); |
2325 | #endif | 3731 | #endif |
2326 | 3732 | ||
3733 | /* audio_reset_buffer must to know the size of voice buffer so init | ||
3734 | talk first */ | ||
3735 | talk_init(); | ||
3736 | |||
2327 | #ifdef HAVE_CROSSFADE | 3737 | #ifdef HAVE_CROSSFADE |
2328 | /* Set crossfade setting for next buffer init which should be about... */ | 3738 | /* Set crossfade setting for next buffer init which should be about... */ |
2329 | pcmbuf_request_crossfade_enable(global_settings.crossfade); | 3739 | pcmbuf_request_crossfade_enable(global_settings.crossfade); |
2330 | #endif | 3740 | #endif |
2331 | 3741 | ||
2332 | /* initialize the buffering system */ | 3742 | /* Initialize the buffering system */ |
2333 | 3743 | track_list_init(); | |
2334 | buffering_init(); | 3744 | buffering_init(); |
2335 | /* ...now! Set up the buffers */ | 3745 | /* ...now! Set up the buffers */ |
2336 | audio_reset_buffer(); | 3746 | audio_reset_buffer(); |
2337 | 3747 | ||
2338 | int i; | ||
2339 | for(i = 0; i < MAX_TRACK; i++) | ||
2340 | { | ||
2341 | tracks[i].audio_hid = -1; | ||
2342 | tracks[i].id3_hid = -1; | ||
2343 | tracks[i].codec_hid = -1; | ||
2344 | tracks[i].cuesheet_hid = -1; | ||
2345 | } | ||
2346 | #ifdef HAVE_ALBUMART | ||
2347 | FOREACH_ALBUMART(i) | ||
2348 | { | ||
2349 | int j; | ||
2350 | for (j = 0; j < MAX_TRACK; j++) | ||
2351 | { | ||
2352 | tracks[j].aa_hid[i] = -1; | ||
2353 | } | ||
2354 | } | ||
2355 | #endif | ||
2356 | |||
2357 | add_event(BUFFER_EVENT_REBUFFER, false, buffering_handle_rebuffer_callback); | ||
2358 | add_event(BUFFER_EVENT_FINISHED, false, buffering_handle_finished_callback); | ||
2359 | |||
2360 | /* Probably safe to say */ | 3748 | /* Probably safe to say */ |
2361 | audio_is_initialized = true; | 3749 | audio_is_initialized = true; |
2362 | 3750 | ||
@@ -2365,26 +3753,10 @@ void audio_init(void) | |||
2365 | audio_set_buffer_margin(global_settings.buffer_margin); | 3753 | audio_set_buffer_margin(global_settings.buffer_margin); |
2366 | #endif | 3754 | #endif |
2367 | 3755 | ||
2368 | /* it's safe to let the threads run now */ | 3756 | /* It's safe to let the threads run now */ |
2369 | #ifdef PLAYBACK_VOICE | 3757 | #ifdef PLAYBACK_VOICE |
2370 | voice_thread_resume(); | 3758 | voice_thread_resume(); |
2371 | #endif | 3759 | #endif |
2372 | codec_thread_resume(); | 3760 | codec_thread_resume(); |
2373 | thread_thaw(audio_thread_id); | 3761 | thread_thaw(audio_thread_id); |
2374 | |||
2375 | } /* audio_init */ | ||
2376 | |||
2377 | bool audio_is_thread_ready(void) | ||
2378 | { | ||
2379 | return audio_thread_ready; | ||
2380 | } | ||
2381 | |||
2382 | size_t audio_get_filebuflen(void) | ||
2383 | { | ||
2384 | return filebuflen; | ||
2385 | } | ||
2386 | |||
2387 | int get_audio_hid() | ||
2388 | { | ||
2389 | return CUR_TI->audio_hid; | ||
2390 | } | 3762 | } |
diff --git a/apps/playback.h b/apps/playback.h index 76c394603f..225946cfaf 100644 --- a/apps/playback.h +++ b/apps/playback.h | |||
@@ -26,6 +26,16 @@ | |||
26 | #include <stdlib.h> | 26 | #include <stdlib.h> |
27 | #include "config.h" | 27 | #include "config.h" |
28 | 28 | ||
29 | #if CONFIG_CODEC == SWCODEC | ||
30 | /* Including the code for fast previews is entirely optional since it | ||
31 | does add two more mp3entry's - for certain targets it may be less | ||
32 | beneficial such as flash-only storage */ | ||
33 | #if MEMORYSIZE > 2 | ||
34 | #define AUDIO_FAST_SKIP_PREVIEW | ||
35 | #endif | ||
36 | |||
37 | #endif /* CONFIG_CODEC == SWCODEC */ | ||
38 | |||
29 | #ifdef HAVE_ALBUMART | 39 | #ifdef HAVE_ALBUMART |
30 | 40 | ||
31 | #include "bmp.h" | 41 | #include "bmp.h" |
@@ -67,6 +77,8 @@ long audio_filebufused(void); | |||
67 | void audio_pre_ff_rewind(void); | 77 | void audio_pre_ff_rewind(void); |
68 | void audio_skip(int direction); | 78 | void audio_skip(int direction); |
69 | void audio_hard_stop(void); /* Stops audio from serving playback */ | 79 | void audio_hard_stop(void); /* Stops audio from serving playback */ |
80 | |||
81 | void audio_set_cuesheet(int enable); | ||
70 | #ifdef HAVE_CROSSFADE | 82 | #ifdef HAVE_CROSSFADE |
71 | void audio_set_crossfade(int enable); | 83 | void audio_set_crossfade(int enable); |
72 | #endif | 84 | #endif |
@@ -78,11 +90,10 @@ enum | |||
78 | }; | 90 | }; |
79 | bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */ | 91 | bool audio_restore_playback(int type); /* Restores the audio buffer to handle the requested playback */ |
80 | size_t audio_get_filebuflen(void); | 92 | size_t audio_get_filebuflen(void); |
81 | void audio_pcmbuf_position_callback(unsigned int time) ICODE_ATTR; | ||
82 | void audio_post_track_change(bool pcmbuf); | ||
83 | int get_audio_hid(void); | ||
84 | void audio_set_prev_elapsed(unsigned long setting); | ||
85 | bool audio_buffer_state_trashed(void); | 93 | bool audio_buffer_state_trashed(void); |
94 | |||
95 | /* Automatic transition? Only valid to call during the track change events, | ||
96 | otherwise the result is undefined. */ | ||
86 | bool audio_automatic_skip(void); | 97 | bool audio_automatic_skip(void); |
87 | 98 | ||
88 | /* Define one constant that includes recording related functionality */ | 99 | /* Define one constant that includes recording related functionality */ |
@@ -91,35 +102,62 @@ bool audio_automatic_skip(void); | |||
91 | #endif | 102 | #endif |
92 | 103 | ||
93 | enum { | 104 | enum { |
94 | Q_NULL = 0, | 105 | Q_NULL = 0, /* reserved */ |
106 | |||
107 | /* -> audio */ | ||
95 | Q_AUDIO_PLAY = 1, | 108 | Q_AUDIO_PLAY = 1, |
96 | Q_AUDIO_STOP, | 109 | Q_AUDIO_STOP, |
97 | Q_AUDIO_PAUSE, | 110 | Q_AUDIO_PAUSE, |
98 | Q_AUDIO_SKIP, | 111 | Q_AUDIO_SKIP, |
99 | Q_AUDIO_PRE_FF_REWIND, | 112 | Q_AUDIO_PRE_FF_REWIND, |
100 | Q_AUDIO_FF_REWIND, | 113 | Q_AUDIO_FF_REWIND, |
101 | Q_AUDIO_CHECK_NEW_TRACK, | ||
102 | Q_AUDIO_FLUSH, | 114 | Q_AUDIO_FLUSH, |
103 | Q_AUDIO_TRACK_CHANGED, | ||
104 | Q_AUDIO_SEEK_COMPLETE, | ||
105 | Q_AUDIO_DIR_SKIP, | 115 | Q_AUDIO_DIR_SKIP, |
106 | Q_AUDIO_POSTINIT, | ||
107 | Q_AUDIO_FILL_BUFFER, | ||
108 | Q_AUDIO_FINISH_LOAD, | ||
109 | Q_CODEC_REQUEST_COMPLETE, | ||
110 | Q_CODEC_REQUEST_FAILED, | ||
111 | 116 | ||
117 | /* pcmbuf -> audio */ | ||
118 | Q_AUDIO_TRACK_CHANGED, | ||
119 | |||
120 | /* audio -> audio */ | ||
121 | Q_AUDIO_FILL_BUFFER, /* continue buffering next track */ | ||
122 | |||
123 | /* buffering -> audio */ | ||
124 | Q_AUDIO_BUFFERING, /* some buffer event */ | ||
125 | Q_AUDIO_FINISH_LOAD_TRACK, /* metadata is buffered */ | ||
126 | Q_AUDIO_HANDLE_FINISHED, /* some other type is buffered */ | ||
127 | |||
128 | /* codec -> audio (*) */ | ||
129 | Q_AUDIO_CODEC_SEEK_COMPLETE, | ||
130 | Q_AUDIO_CODEC_COMPLETE, | ||
131 | |||
132 | /* audio -> codec */ | ||
112 | Q_CODEC_LOAD, | 133 | Q_CODEC_LOAD, |
113 | Q_CODEC_LOAD_DISK, | 134 | Q_CODEC_RUN, |
135 | Q_CODEC_PAUSE, | ||
136 | Q_CODEC_SEEK, | ||
137 | Q_CODEC_STOP, | ||
138 | Q_CODEC_UNLOAD, | ||
139 | |||
114 | 140 | ||
141 | /*- miscellanous -*/ | ||
115 | #ifdef AUDIO_HAVE_RECORDING | 142 | #ifdef AUDIO_HAVE_RECORDING |
116 | Q_AUDIO_LOAD_ENCODER, | 143 | /* -> codec */ |
117 | Q_ENCODER_LOAD_DISK, | 144 | Q_AUDIO_LOAD_ENCODER, /* load an encoder for recording */ |
118 | Q_ENCODER_RECORD, | ||
119 | #endif | 145 | #endif |
120 | 146 | /* -> codec */ | |
121 | Q_CODEC_DO_CALLBACK, | 147 | Q_CODEC_DO_CALLBACK, |
122 | Q_CODEC_ACK, | ||
123 | }; | ||
124 | 148 | ||
149 | |||
150 | /*- settings -*/ | ||
151 | |||
152 | #ifdef HAVE_DISK_STORAGE | ||
153 | /* -> audio */ | ||
154 | Q_AUDIO_UPDATE_WATERMARK, /* buffering watermark needs updating */ | ||
125 | #endif | 155 | #endif |
156 | /* -> audio */ | ||
157 | Q_AUDIO_REMAKE_AUDIO_BUFFER, /* buffer needs to be reinitialized */ | ||
158 | }; | ||
159 | |||
160 | /* (*) If you change these, you must check audio_clear_track_notifications | ||
161 | in playback.c for correctness */ | ||
162 | |||
163 | #endif /* _PLAYBACK_H */ | ||
diff --git a/apps/playlist.c b/apps/playlist.c index 14ebb7a198..d17bf230a5 100644 --- a/apps/playlist.c +++ b/apps/playlist.c | |||
@@ -822,9 +822,6 @@ static int add_track_to_playlist(struct playlist_info* playlist, | |||
822 | playlist->amount++; | 822 | playlist->amount++; |
823 | playlist->num_inserted_tracks++; | 823 | playlist->num_inserted_tracks++; |
824 | 824 | ||
825 | /* Update index for resume. */ | ||
826 | playlist_update_resume_index(); | ||
827 | |||
828 | return insert_position; | 825 | return insert_position; |
829 | } | 826 | } |
830 | 827 | ||
@@ -925,9 +922,6 @@ static int remove_track_from_playlist(struct playlist_info* playlist, | |||
925 | sync_control(playlist, false); | 922 | sync_control(playlist, false); |
926 | } | 923 | } |
927 | 924 | ||
928 | /* Update index for resume. */ | ||
929 | playlist_update_resume_index(); | ||
930 | |||
931 | return 0; | 925 | return 0; |
932 | } | 926 | } |
933 | 927 | ||
@@ -987,9 +981,6 @@ static int randomise_playlist(struct playlist_info* playlist, | |||
987 | playlist->first_index, NULL, NULL, NULL); | 981 | playlist->first_index, NULL, NULL, NULL); |
988 | } | 982 | } |
989 | 983 | ||
990 | /* Update index for resume. */ | ||
991 | playlist_update_resume_index(); | ||
992 | |||
993 | return 0; | 984 | return 0; |
994 | } | 985 | } |
995 | 986 | ||
@@ -1030,9 +1021,6 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current, | |||
1030 | playlist->first_index, -1, NULL, NULL, NULL); | 1021 | playlist->first_index, -1, NULL, NULL, NULL); |
1031 | } | 1022 | } |
1032 | 1023 | ||
1033 | /* Update index for resume. */ | ||
1034 | playlist_update_resume_index(); | ||
1035 | |||
1036 | return 0; | 1024 | return 0; |
1037 | } | 1025 | } |
1038 | 1026 | ||
@@ -1205,9 +1193,6 @@ static void find_and_set_playlist_index(struct playlist_info* playlist, | |||
1205 | break; | 1193 | break; |
1206 | } | 1194 | } |
1207 | } | 1195 | } |
1208 | |||
1209 | /* Update index for resume. */ | ||
1210 | playlist_update_resume_index(); | ||
1211 | } | 1196 | } |
1212 | 1197 | ||
1213 | /* | 1198 | /* |
@@ -2486,6 +2471,12 @@ const char* playlist_peek(int steps, char* buf, size_t buf_size) | |||
2486 | if (index < 0) | 2471 | if (index < 0) |
2487 | return NULL; | 2472 | return NULL; |
2488 | 2473 | ||
2474 | #if CONFIG_CODEC == SWCODEC | ||
2475 | /* Just testing - don't care about the file name */ | ||
2476 | if (!buf || !buf_size) | ||
2477 | return ""; | ||
2478 | #endif | ||
2479 | |||
2489 | control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; | 2480 | control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK; |
2490 | seek = playlist->indices[index] & PLAYLIST_SEEK_MASK; | 2481 | seek = playlist->indices[index] & PLAYLIST_SEEK_MASK; |
2491 | 2482 | ||
@@ -2632,30 +2623,17 @@ int playlist_get_resume_info(int *resume_index) | |||
2632 | return 0; | 2623 | return 0; |
2633 | } | 2624 | } |
2634 | 2625 | ||
2635 | /* Get current playlist index. */ | ||
2636 | int playlist_get_index(void) | ||
2637 | { | ||
2638 | return current_playlist.index; | ||
2639 | } | ||
2640 | |||
2641 | /* Update resume index within playlist_info structure. */ | ||
2642 | void playlist_update_resume_index(void) | ||
2643 | { | ||
2644 | struct playlist_info* playlist = ¤t_playlist; | ||
2645 | playlist->resume_index = playlist->index; | ||
2646 | } | ||
2647 | |||
2648 | /* Update resume info for current playing song. Returns -1 on error. */ | 2626 | /* Update resume info for current playing song. Returns -1 on error. */ |
2649 | int playlist_update_resume_info(const struct mp3entry* id3) | 2627 | int playlist_update_resume_info(const struct mp3entry* id3) |
2650 | { | 2628 | { |
2651 | struct playlist_info* playlist = ¤t_playlist; | 2629 | struct playlist_info* playlist = ¤t_playlist; |
2652 | 2630 | ||
2653 | if (id3) | 2631 | if (id3) |
2654 | { | 2632 | { |
2655 | if (global_status.resume_index != playlist->resume_index || | 2633 | if (global_status.resume_index != playlist->index || |
2656 | global_status.resume_offset != id3->offset) | 2634 | global_status.resume_offset != id3->offset) |
2657 | { | 2635 | { |
2658 | global_status.resume_index = playlist->resume_index; | 2636 | global_status.resume_index = playlist->index; |
2659 | global_status.resume_offset = id3->offset; | 2637 | global_status.resume_offset = id3->offset; |
2660 | status_save(); | 2638 | status_save(); |
2661 | } | 2639 | } |
@@ -3203,9 +3181,6 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index) | |||
3203 | queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); | 3181 | queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0); |
3204 | #endif | 3182 | #endif |
3205 | 3183 | ||
3206 | /* Update index for resume. */ | ||
3207 | playlist_update_resume_index(); | ||
3208 | |||
3209 | return result; | 3184 | return result; |
3210 | } | 3185 | } |
3211 | 3186 | ||
diff --git a/apps/playlist.h b/apps/playlist.h index a0e3b579f7..9c45769981 100644 --- a/apps/playlist.h +++ b/apps/playlist.h | |||
@@ -90,7 +90,6 @@ struct playlist_info | |||
90 | int buffer_end_pos; /* last position where buffer was written */ | 90 | int buffer_end_pos; /* last position where buffer was written */ |
91 | int index; /* index of current playing track */ | 91 | int index; /* index of current playing track */ |
92 | int first_index; /* index of first song in playlist */ | 92 | int first_index; /* index of first song in playlist */ |
93 | int resume_index; /* index of playing track to resume */ | ||
94 | int amount; /* number of tracks in the index */ | 93 | int amount; /* number of tracks in the index */ |
95 | int last_insert_pos; /* last position we inserted a track */ | 94 | int last_insert_pos; /* last position we inserted a track */ |
96 | int seed; /* shuffle seed */ | 95 | int seed; /* shuffle seed */ |
@@ -132,7 +131,6 @@ const char *playlist_peek(int steps, char* buf, size_t buf_size); | |||
132 | int playlist_next(int steps); | 131 | int playlist_next(int steps); |
133 | bool playlist_next_dir(int direction); | 132 | bool playlist_next_dir(int direction); |
134 | int playlist_get_resume_info(int *resume_index); | 133 | int playlist_get_resume_info(int *resume_index); |
135 | int playlist_get_index(void); | ||
136 | int playlist_update_resume_info(const struct mp3entry* id3); | 134 | int playlist_update_resume_info(const struct mp3entry* id3); |
137 | int playlist_get_display_index(void); | 135 | int playlist_get_display_index(void); |
138 | int playlist_amount(void); | 136 | int playlist_amount(void); |
@@ -176,6 +174,5 @@ int playlist_directory_tracksearch(const char* dirname, bool recurse, | |||
176 | int (*callback)(char*, void*), | 174 | int (*callback)(char*, void*), |
177 | void* context); | 175 | void* context); |
178 | int playlist_remove_all_tracks(struct playlist_info *playlist); | 176 | int playlist_remove_all_tracks(struct playlist_info *playlist); |
179 | void playlist_update_resume_index(void); | ||
180 | 177 | ||
181 | #endif /* __PLAYLIST_H__ */ | 178 | #endif /* __PLAYLIST_H__ */ |
diff --git a/apps/plugin.c b/apps/plugin.c index ea290c89a7..bb326d937b 100644 --- a/apps/plugin.c +++ b/apps/plugin.c | |||
@@ -692,7 +692,7 @@ static const struct plugin_api rockbox_api = { | |||
692 | #if CONFIG_CODEC == SWCODEC | 692 | #if CONFIG_CODEC == SWCODEC |
693 | codec_thread_do_callback, | 693 | codec_thread_do_callback, |
694 | codec_load_file, | 694 | codec_load_file, |
695 | codec_begin, | 695 | codec_run_proc, |
696 | codec_close, | 696 | codec_close, |
697 | get_codec_filename, | 697 | get_codec_filename, |
698 | find_array_ptr, | 698 | find_array_ptr, |
diff --git a/apps/plugin.h b/apps/plugin.h index 4537c6670b..cdf34e28b1 100644 --- a/apps/plugin.h +++ b/apps/plugin.h | |||
@@ -145,12 +145,12 @@ void* plugin_get_buffer(size_t *buffer_size); | |||
145 | #define PLUGIN_MAGIC 0x526F634B /* RocK */ | 145 | #define PLUGIN_MAGIC 0x526F634B /* RocK */ |
146 | 146 | ||
147 | /* increase this every time the api struct changes */ | 147 | /* increase this every time the api struct changes */ |
148 | #define PLUGIN_API_VERSION 203 | 148 | #define PLUGIN_API_VERSION 204 |
149 | 149 | ||
150 | /* update this to latest version if a change to the api struct breaks | 150 | /* update this to latest version if a change to the api struct breaks |
151 | backwards compatibility (and please take the opportunity to sort in any | 151 | backwards compatibility (and please take the opportunity to sort in any |
152 | new function which are "waiting" at the end of the function table) */ | 152 | new function which are "waiting" at the end of the function table) */ |
153 | #define PLUGIN_MIN_API_VERSION 203 | 153 | #define PLUGIN_MIN_API_VERSION 204 |
154 | 154 | ||
155 | /* plugin return codes */ | 155 | /* plugin return codes */ |
156 | /* internal returns start at 0x100 to make exit(1..255) work */ | 156 | /* internal returns start at 0x100 to make exit(1..255) work */ |
@@ -799,9 +799,9 @@ struct plugin_api { | |||
799 | #if CONFIG_CODEC == SWCODEC | 799 | #if CONFIG_CODEC == SWCODEC |
800 | void (*codec_thread_do_callback)(void (*fn)(void), | 800 | void (*codec_thread_do_callback)(void (*fn)(void), |
801 | unsigned int *audio_thread_id); | 801 | unsigned int *audio_thread_id); |
802 | void * (*codec_load_file)(const char* codec, struct codec_api *api); | 802 | int (*codec_load_file)(const char* codec, struct codec_api *api); |
803 | int (*codec_begin)(void *handle); | 803 | int (*codec_run_proc)(void); |
804 | void (*codec_close)(void *handle); | 804 | int (*codec_close)(void); |
805 | const char *(*get_codec_filename)(int cod_spec); | 805 | const char *(*get_codec_filename)(int cod_spec); |
806 | void ** (*find_array_ptr)(void **arr, void *ptr); | 806 | void ** (*find_array_ptr)(void **arr, void *ptr); |
807 | int (*remove_array_ptr)(void **arr, void *ptr); | 807 | int (*remove_array_ptr)(void **arr, void *ptr); |
diff --git a/apps/plugins/test_codec.c b/apps/plugins/test_codec.c index 855503a0ec..4bde1ba39d 100644 --- a/apps/plugins/test_codec.c +++ b/apps/plugins/test_codec.c | |||
@@ -127,7 +127,6 @@ struct test_track_info { | |||
127 | }; | 127 | }; |
128 | 128 | ||
129 | static struct test_track_info track; | 129 | static struct test_track_info track; |
130 | static bool taginfo_ready = true; | ||
131 | 130 | ||
132 | static bool use_dsp; | 131 | static bool use_dsp; |
133 | 132 | ||
@@ -433,6 +432,7 @@ static void pcmbuf_insert_wav_checksum(const void *ch1, const void *ch2, int cou | |||
433 | static void set_elapsed(unsigned long value) | 432 | static void set_elapsed(unsigned long value) |
434 | { | 433 | { |
435 | elapsed = value; | 434 | elapsed = value; |
435 | ci.id3->elapsed = value; | ||
436 | } | 436 | } |
437 | 437 | ||
438 | 438 | ||
@@ -482,6 +482,7 @@ static void* request_buffer(size_t *realsize, size_t reqsize) | |||
482 | static void advance_buffer(size_t amount) | 482 | static void advance_buffer(size_t amount) |
483 | { | 483 | { |
484 | ci.curpos += amount; | 484 | ci.curpos += amount; |
485 | ci.id3->offset = ci.curpos; | ||
485 | } | 486 | } |
486 | 487 | ||
487 | 488 | ||
@@ -499,20 +500,17 @@ static void seek_complete(void) | |||
499 | /* Do nothing */ | 500 | /* Do nothing */ |
500 | } | 501 | } |
501 | 502 | ||
502 | /* Request file change from file buffer. Returns true is next | 503 | /* Codec calls this to know what it should do next. */ |
503 | track is available and changed. If return value is false, | 504 | static enum codec_command_action get_command(intptr_t *param) |
504 | codec should exit immediately with PLUGIN_OK status. */ | ||
505 | static bool request_next_track(void) | ||
506 | { | 505 | { |
507 | /* We are only decoding a single track */ | 506 | rb->yield(); |
508 | return false; | 507 | return CODEC_ACTION_NULL; /* just continue processing */ |
508 | (void)param; | ||
509 | } | 509 | } |
510 | 510 | ||
511 | |||
512 | static void set_offset(size_t value) | 511 | static void set_offset(size_t value) |
513 | { | 512 | { |
514 | /* ??? */ | 513 | ci.id3->offset = value; |
515 | (void)value; | ||
516 | } | 514 | } |
517 | 515 | ||
518 | 516 | ||
@@ -546,6 +544,9 @@ static void init_ci(void) | |||
546 | { | 544 | { |
547 | /* --- Our "fake" implementations of the codec API functions. --- */ | 545 | /* --- Our "fake" implementations of the codec API functions. --- */ |
548 | 546 | ||
547 | ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP, | ||
548 | CODEC_IDX_AUDIO); | ||
549 | |||
549 | ci.codec_get_buffer = codec_get_buffer; | 550 | ci.codec_get_buffer = codec_get_buffer; |
550 | 551 | ||
551 | if (wavinfo.fd >= 0 || checksum) { | 552 | if (wavinfo.fd >= 0 || checksum) { |
@@ -560,11 +561,9 @@ static void init_ci(void) | |||
560 | ci.advance_buffer = advance_buffer; | 561 | ci.advance_buffer = advance_buffer; |
561 | ci.seek_buffer = seek_buffer; | 562 | ci.seek_buffer = seek_buffer; |
562 | ci.seek_complete = seek_complete; | 563 | ci.seek_complete = seek_complete; |
563 | ci.request_next_track = request_next_track; | ||
564 | ci.set_offset = set_offset; | 564 | ci.set_offset = set_offset; |
565 | ci.configure = configure; | 565 | ci.configure = configure; |
566 | ci.dsp = (struct dsp_config *)rb->dsp_configure(NULL, DSP_MYDSP, | 566 | ci.get_command = get_command; |
567 | CODEC_IDX_AUDIO); | ||
568 | 567 | ||
569 | /* --- "Core" functions --- */ | 568 | /* --- "Core" functions --- */ |
570 | 569 | ||
@@ -620,20 +619,22 @@ static void init_ci(void) | |||
620 | static void codec_thread(void) | 619 | static void codec_thread(void) |
621 | { | 620 | { |
622 | const char* codecname; | 621 | const char* codecname; |
623 | void *handle; | 622 | int res; |
624 | int res = CODEC_ERROR; | ||
625 | 623 | ||
626 | codecname = rb->get_codec_filename(track.id3.codectype); | 624 | codecname = rb->get_codec_filename(track.id3.codectype); |
627 | 625 | ||
628 | /* Load the codec and start decoding. */ | 626 | /* Load the codec */ |
629 | handle = rb->codec_load_file(codecname,&ci); | 627 | res = rb->codec_load_file(codecname, &ci); |
630 | 628 | ||
631 | if (handle != NULL) | 629 | if (res >= 0) |
632 | { | 630 | { |
633 | res = rb->codec_begin(handle); | 631 | /* Decode the file */ |
634 | rb->codec_close(handle); | 632 | res = rb->codec_run_proc(); |
635 | } | 633 | } |
636 | 634 | ||
635 | /* Clean up */ | ||
636 | rb->codec_close(); | ||
637 | |||
637 | /* Signal to the main thread that we are done */ | 638 | /* Signal to the main thread that we are done */ |
638 | endtick = *rb->current_tick - rebuffertick; | 639 | endtick = *rb->current_tick - rebuffertick; |
639 | codec_playing = false; | 640 | codec_playing = false; |
@@ -705,11 +706,7 @@ static enum plugin_status test_track(const char* filename) | |||
705 | /* Prepare the codec struct for playing the whole file */ | 706 | /* Prepare the codec struct for playing the whole file */ |
706 | ci.filesize = track.filesize; | 707 | ci.filesize = track.filesize; |
707 | ci.id3 = &track.id3; | 708 | ci.id3 = &track.id3; |
708 | ci.taginfo_ready = &taginfo_ready; | ||
709 | ci.curpos = 0; | 709 | ci.curpos = 0; |
710 | ci.stop_codec = false; | ||
711 | ci.new_track = 0; | ||
712 | ci.seek_time = 0; | ||
713 | 710 | ||
714 | if (use_dsp) | 711 | if (use_dsp) |
715 | rb->dsp_configure(ci.dsp, DSP_RESET, 0); | 712 | rb->dsp_configure(ci.dsp, DSP_RESET, 0); |
diff --git a/firmware/export/kernel.h b/firmware/export/kernel.h index 66efce33f6..54a53f3607 100644 --- a/firmware/export/kernel.h +++ b/firmware/export/kernel.h | |||
@@ -86,6 +86,7 @@ | |||
86 | #define SYS_VOLUME_CHANGED MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 5) | 86 | #define SYS_VOLUME_CHANGED MAKE_SYS_EVENT(SYS_EVENT_CLS_MISC, 5) |
87 | 87 | ||
88 | #define IS_SYSEVENT(ev) ((ev & SYS_EVENT) == SYS_EVENT) | 88 | #define IS_SYSEVENT(ev) ((ev & SYS_EVENT) == SYS_EVENT) |
89 | #define EVENT_RESERVED (~0) | ||
89 | 90 | ||
90 | #ifndef TIMEOUT_BLOCK | 91 | #ifndef TIMEOUT_BLOCK |
91 | #define TIMEOUT_BLOCK -1 | 92 | #define TIMEOUT_BLOCK -1 |
@@ -249,6 +250,15 @@ extern bool queue_in_queue_send(struct event_queue *q); | |||
249 | #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */ | 250 | #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */ |
250 | extern bool queue_empty(const struct event_queue* q); | 251 | extern bool queue_empty(const struct event_queue* q); |
251 | extern bool queue_peek(struct event_queue *q, struct queue_event *ev); | 252 | extern bool queue_peek(struct event_queue *q, struct queue_event *ev); |
253 | |||
254 | #define QPEEK_FILTER_COUNT_MASK (0xffu) /* 0x00=1 filter, 0xff=256 filters */ | ||
255 | #define QPEEK_FILTER_HEAD_ONLY (1u << 8) /* Ignored if no filters */ | ||
256 | #define QPEEK_REMOVE_EVENTS (1u << 9) /* Remove or discard events */ | ||
257 | extern bool queue_peek_ex(struct event_queue *q, | ||
258 | struct queue_event *ev, | ||
259 | unsigned int flags, | ||
260 | const long (*filters)[2]); | ||
261 | |||
252 | extern void queue_clear(struct event_queue* q); | 262 | extern void queue_clear(struct event_queue* q); |
253 | extern void queue_remove_from_head(struct event_queue *q, long id); | 263 | extern void queue_remove_from_head(struct event_queue *q, long id); |
254 | extern int queue_count(const struct event_queue *q); | 264 | extern int queue_count(const struct event_queue *q); |
diff --git a/firmware/kernel.c b/firmware/kernel.c index 4fcfcb9d30..288ebbbede 100644 --- a/firmware/kernel.c +++ b/firmware/kernel.c | |||
@@ -516,8 +516,10 @@ void queue_wait(struct event_queue *q, struct queue_event *ev) | |||
516 | oldlevel = disable_irq_save(); | 516 | oldlevel = disable_irq_save(); |
517 | corelock_lock(&q->cl); | 517 | corelock_lock(&q->cl); |
518 | 518 | ||
519 | /* auto-reply */ | 519 | #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME |
520 | /* Auto-reply (even if ev is NULL to avoid stalling a waiting thread) */ | ||
520 | queue_do_auto_reply(q->send); | 521 | queue_do_auto_reply(q->send); |
522 | #endif | ||
521 | 523 | ||
522 | while(1) | 524 | while(1) |
523 | { | 525 | { |
@@ -541,12 +543,18 @@ void queue_wait(struct event_queue *q, struct queue_event *ev) | |||
541 | corelock_lock(&q->cl); | 543 | corelock_lock(&q->cl); |
542 | } | 544 | } |
543 | 545 | ||
544 | q->read = rd + 1; | 546 | #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME |
545 | rd &= QUEUE_LENGTH_MASK; | 547 | if(ev) |
546 | *ev = q->events[rd]; | 548 | #endif |
549 | { | ||
550 | q->read = rd + 1; | ||
551 | rd &= QUEUE_LENGTH_MASK; | ||
552 | *ev = q->events[rd]; | ||
547 | 553 | ||
548 | /* Get data for a waiting thread if one */ | 554 | /* Get data for a waiting thread if one */ |
549 | queue_do_fetch_sender(q->send, rd); | 555 | queue_do_fetch_sender(q->send, rd); |
556 | } | ||
557 | /* else just waiting on non-empty */ | ||
550 | 558 | ||
551 | corelock_unlock(&q->cl); | 559 | corelock_unlock(&q->cl); |
552 | restore_irq(oldlevel); | 560 | restore_irq(oldlevel); |
@@ -566,8 +574,10 @@ void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks) | |||
566 | oldlevel = disable_irq_save(); | 574 | oldlevel = disable_irq_save(); |
567 | corelock_lock(&q->cl); | 575 | corelock_lock(&q->cl); |
568 | 576 | ||
569 | /* Auto-reply */ | 577 | #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME |
578 | /* Auto-reply (even if ev is NULL to avoid stalling a waiting thread) */ | ||
570 | queue_do_auto_reply(q->send); | 579 | queue_do_auto_reply(q->send); |
580 | #endif | ||
571 | 581 | ||
572 | rd = q->read; | 582 | rd = q->read; |
573 | wr = q->write; | 583 | wr = q->write; |
@@ -590,20 +600,26 @@ void queue_wait_w_tmo(struct event_queue *q, struct queue_event *ev, int ticks) | |||
590 | wr = q->write; | 600 | wr = q->write; |
591 | } | 601 | } |
592 | 602 | ||
593 | /* no worry about a removed message here - status is checked inside | 603 | #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME |
594 | locks - perhaps verify if timeout or false alarm */ | 604 | if(ev) |
595 | if (rd != wr) | 605 | #endif |
596 | { | ||
597 | q->read = rd + 1; | ||
598 | rd &= QUEUE_LENGTH_MASK; | ||
599 | *ev = q->events[rd]; | ||
600 | /* Get data for a waiting thread if one */ | ||
601 | queue_do_fetch_sender(q->send, rd); | ||
602 | } | ||
603 | else | ||
604 | { | 606 | { |
605 | ev->id = SYS_TIMEOUT; | 607 | /* no worry about a removed message here - status is checked inside |
608 | locks - perhaps verify if timeout or false alarm */ | ||
609 | if (rd != wr) | ||
610 | { | ||
611 | q->read = rd + 1; | ||
612 | rd &= QUEUE_LENGTH_MASK; | ||
613 | *ev = q->events[rd]; | ||
614 | /* Get data for a waiting thread if one */ | ||
615 | queue_do_fetch_sender(q->send, rd); | ||
616 | } | ||
617 | else | ||
618 | { | ||
619 | ev->id = SYS_TIMEOUT; | ||
620 | } | ||
606 | } | 621 | } |
622 | /* else just waiting on non-empty */ | ||
607 | 623 | ||
608 | corelock_unlock(&q->cl); | 624 | corelock_unlock(&q->cl); |
609 | restore_irq(oldlevel); | 625 | restore_irq(oldlevel); |
@@ -740,23 +756,99 @@ void queue_reply(struct event_queue *q, intptr_t retval) | |||
740 | } | 756 | } |
741 | #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */ | 757 | #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */ |
742 | 758 | ||
743 | bool queue_peek(struct event_queue *q, struct queue_event *ev) | 759 | #ifdef HAVE_EXTENDED_MESSAGING_AND_NAME |
760 | /* Scan the even queue from head to tail, returning any event from the | ||
761 | filter list that was found, optionally removing the event. If an | ||
762 | event is returned, synchronous events are handled in the same manner as | ||
763 | with queue_wait(_w_tmo); if discarded, then as queue_clear. | ||
764 | If filters are NULL, any event matches. If filters exist, the default | ||
765 | is to search the full queue depth. | ||
766 | Earlier filters take precedence. | ||
767 | |||
768 | Return true if an event was found, false otherwise. */ | ||
769 | bool queue_peek_ex(struct event_queue *q, struct queue_event *ev, | ||
770 | unsigned int flags, const long (*filters)[2]) | ||
744 | { | 771 | { |
745 | unsigned int rd; | 772 | bool have_msg; |
773 | unsigned int rd, wr; | ||
774 | int oldlevel; | ||
746 | 775 | ||
747 | if(q->read == q->write) | 776 | if(LIKELY(q->read == q->write)) |
748 | return false; | 777 | return false; /* Empty: do nothing further */ |
749 | 778 | ||
750 | bool have_msg = false; | 779 | have_msg = false; |
751 | 780 | ||
752 | int oldlevel = disable_irq_save(); | 781 | oldlevel = disable_irq_save(); |
753 | corelock_lock(&q->cl); | 782 | corelock_lock(&q->cl); |
754 | 783 | ||
755 | rd = q->read; | 784 | /* Starting at the head, find first match */ |
756 | if(rd != q->write) | 785 | for(rd = q->read, wr = q->write; rd != wr; rd++) |
757 | { | 786 | { |
758 | *ev = q->events[rd & QUEUE_LENGTH_MASK]; | 787 | struct queue_event *e = &q->events[rd & QUEUE_LENGTH_MASK]; |
788 | |||
789 | if(filters) | ||
790 | { | ||
791 | /* Have filters - find the first thing that passes */ | ||
792 | const long (* f)[2] = filters; | ||
793 | const long (* const f_last)[2] = | ||
794 | &filters[flags & QPEEK_FILTER_COUNT_MASK]; | ||
795 | long id = e->id; | ||
796 | |||
797 | do | ||
798 | { | ||
799 | if(UNLIKELY(id >= (*f)[0] && id <= (*f)[1])) | ||
800 | goto passed_filter; | ||
801 | } | ||
802 | while(++f <= f_last); | ||
803 | |||
804 | if(LIKELY(!(flags & QPEEK_FILTER_HEAD_ONLY))) | ||
805 | continue; /* No match; test next event */ | ||
806 | else | ||
807 | break; /* Only check the head */ | ||
808 | } | ||
809 | /* else - anything passes */ | ||
810 | |||
811 | passed_filter: | ||
812 | |||
813 | /* Found a matching event */ | ||
759 | have_msg = true; | 814 | have_msg = true; |
815 | |||
816 | if(ev) | ||
817 | *ev = *e; /* Caller wants the event */ | ||
818 | |||
819 | if(flags & QPEEK_REMOVE_EVENTS) | ||
820 | { | ||
821 | /* Do event removal */ | ||
822 | unsigned int r = q->read; | ||
823 | q->read = r + 1; /* Advance head */ | ||
824 | |||
825 | if(ev) | ||
826 | { | ||
827 | /* Auto-reply */ | ||
828 | queue_do_auto_reply(q->send); | ||
829 | /* Get the thread waiting for reply, if any */ | ||
830 | queue_do_fetch_sender(q->send, rd & QUEUE_LENGTH_MASK); | ||
831 | } | ||
832 | else | ||
833 | { | ||
834 | /* Release any thread waiting on this message */ | ||
835 | queue_do_unblock_sender(q->send, rd & QUEUE_LENGTH_MASK); | ||
836 | } | ||
837 | |||
838 | /* Slide messages forward into the gap if not at the head */ | ||
839 | while(rd != r) | ||
840 | { | ||
841 | unsigned int dst = rd & QUEUE_LENGTH_MASK; | ||
842 | unsigned int src = --rd & QUEUE_LENGTH_MASK; | ||
843 | |||
844 | q->events[dst] = q->events[src]; | ||
845 | /* Keep sender wait list in sync */ | ||
846 | if(q->send) | ||
847 | q->send->senders[dst] = q->send->senders[src]; | ||
848 | } | ||
849 | } | ||
850 | |||
851 | break; | ||
760 | } | 852 | } |
761 | 853 | ||
762 | corelock_unlock(&q->cl); | 854 | corelock_unlock(&q->cl); |
@@ -765,30 +857,42 @@ bool queue_peek(struct event_queue *q, struct queue_event *ev) | |||
765 | return have_msg; | 857 | return have_msg; |
766 | } | 858 | } |
767 | 859 | ||
768 | /* Poll queue to see if a message exists - careful in using the result if | 860 | bool queue_peek(struct event_queue *q, struct queue_event *ev) |
769 | * queue_remove_from_head is called when messages are posted - possibly use | ||
770 | * queue_wait_w_tmo(&q, 0) in that case or else a removed message that | ||
771 | * unsignals the queue may cause an unwanted block */ | ||
772 | bool queue_empty(const struct event_queue* q) | ||
773 | { | 861 | { |
774 | return ( q->read == q->write ); | 862 | return queue_peek_ex(q, ev, 0, NULL); |
775 | } | 863 | } |
776 | 864 | ||
777 | void queue_clear(struct event_queue* q) | 865 | void queue_remove_from_head(struct event_queue *q, long id) |
778 | { | 866 | { |
779 | int oldlevel; | 867 | const long f[2] = { id, id }; |
868 | while (queue_peek_ex(q, NULL, | ||
869 | QPEEK_FILTER_HEAD_ONLY | QPEEK_REMOVE_EVENTS, &f)); | ||
870 | } | ||
871 | #else /* !HAVE_EXTENDED_MESSAGING_AND_NAME */ | ||
872 | /* The more powerful routines aren't required */ | ||
873 | bool queue_peek(struct event_queue *q, struct queue_event *ev) | ||
874 | { | ||
875 | unsigned int rd; | ||
780 | 876 | ||
781 | oldlevel = disable_irq_save(); | 877 | if(q->read == q->write) |
782 | corelock_lock(&q->cl); | 878 | return false; |
783 | 879 | ||
784 | /* Release all threads waiting in the queue for a reply - | 880 | bool have_msg = false; |
785 | dequeued sent message will be handled by owning thread */ | ||
786 | queue_release_all_senders(q); | ||
787 | 881 | ||
788 | q->read = q->write; | 882 | int oldlevel = disable_irq_save(); |
883 | corelock_lock(&q->cl); | ||
884 | |||
885 | rd = q->read; | ||
886 | if(rd != q->write) | ||
887 | { | ||
888 | *ev = q->events[rd & QUEUE_LENGTH_MASK]; | ||
889 | have_msg = true; | ||
890 | } | ||
789 | 891 | ||
790 | corelock_unlock(&q->cl); | 892 | corelock_unlock(&q->cl); |
791 | restore_irq(oldlevel); | 893 | restore_irq(oldlevel); |
894 | |||
895 | return have_msg; | ||
792 | } | 896 | } |
793 | 897 | ||
794 | void queue_remove_from_head(struct event_queue *q, long id) | 898 | void queue_remove_from_head(struct event_queue *q, long id) |
@@ -816,6 +920,33 @@ void queue_remove_from_head(struct event_queue *q, long id) | |||
816 | corelock_unlock(&q->cl); | 920 | corelock_unlock(&q->cl); |
817 | restore_irq(oldlevel); | 921 | restore_irq(oldlevel); |
818 | } | 922 | } |
923 | #endif /* HAVE_EXTENDED_MESSAGING_AND_NAME */ | ||
924 | |||
925 | /* Poll queue to see if a message exists - careful in using the result if | ||
926 | * queue_remove_from_head is called when messages are posted - possibly use | ||
927 | * queue_wait_w_tmo(&q, 0) in that case or else a removed message that | ||
928 | * unsignals the queue may cause an unwanted block */ | ||
929 | bool queue_empty(const struct event_queue* q) | ||
930 | { | ||
931 | return ( q->read == q->write ); | ||
932 | } | ||
933 | |||
934 | void queue_clear(struct event_queue* q) | ||
935 | { | ||
936 | int oldlevel; | ||
937 | |||
938 | oldlevel = disable_irq_save(); | ||
939 | corelock_lock(&q->cl); | ||
940 | |||
941 | /* Release all threads waiting in the queue for a reply - | ||
942 | dequeued sent message will be handled by owning thread */ | ||
943 | queue_release_all_senders(q); | ||
944 | |||
945 | q->read = q->write; | ||
946 | |||
947 | corelock_unlock(&q->cl); | ||
948 | restore_irq(oldlevel); | ||
949 | } | ||
819 | 950 | ||
820 | /** | 951 | /** |
821 | * The number of events waiting in the queue. | 952 | * The number of events waiting in the queue. |