summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2005-06-05 23:05:10 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2005-06-05 23:05:10 +0000
commit1c497e60457a2e125a3e4fc839d6453bfe585834 (patch)
tree4688a25afda658a88e900a0c5a6d4f6f755c3268
parentb1e1e44041f7c078a8a1e4f31ab0cde03efd1b2a (diff)
downloadrockbox-1c497e60457a2e125a3e4fc839d6453bfe585834.tar.gz
rockbox-1c497e60457a2e125a3e4fc839d6453bfe585834.zip
First audio codec playback attempt by Miikka Pekkarinen
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6574 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/SOURCES4
-rw-r--r--apps/playback.c1176
-rw-r--r--apps/playback.h67
-rw-r--r--apps/plugin.c59
-rw-r--r--apps/plugin.h5
-rw-r--r--apps/plugins/SOURCES4
-rw-r--r--apps/plugins/codecmpa.c404
-rw-r--r--apps/plugins/codecvorbis.c160
-rw-r--r--apps/plugins/lib/SOURCES3
-rw-r--r--apps/plugins/lib/codeclib.c36
-rw-r--r--apps/plugins/lib/codeclib.h46
-rw-r--r--apps/plugins/lib/xxx2wav.c9
-rw-r--r--apps/plugins/lib/xxx2wav.h1
-rw-r--r--apps/wps.c4
-rw-r--r--docs/CREDITS1
-rw-r--r--firmware/export/pcm_playback.h6
-rw-r--r--firmware/mpeg.c5
-rw-r--r--firmware/pcm_playback.c293
-rw-r--r--firmware/sound.c2
-rwxr-xr-xtools/buildzip.pl4
20 files changed, 2201 insertions, 88 deletions
diff --git a/apps/SOURCES b/apps/SOURCES
index 4d2eebaee9..5e58816d4e 100644
--- a/apps/SOURCES
+++ b/apps/SOURCES
@@ -48,4 +48,6 @@ recorder/radio.c
#ifdef HAVE_RECORDING
recorder/recording.c
#endif
-
+#ifdef IRIVER_H100
+playback.c
+#endif
diff --git a/apps/playback.c b/apps/playback.c
new file mode 100644
index 0000000000..15258a8e13
--- /dev/null
+++ b/apps/playback.c
@@ -0,0 +1,1176 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Miika Pekkarinen
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "system.h"
+#include "thread.h"
+#include "file.h"
+#include "lcd.h"
+#include "font.h"
+#include "backlight.h"
+#include "button.h"
+#include "kernel.h"
+#include "tree.h"
+#include "debug.h"
+#include "sprintf.h"
+#include "settings.h"
+#include "plugin.h"
+#include "wps.h"
+#include "wps-display.h"
+#include "audio.h"
+#include "logf.h"
+#include "mp3_playback.h"
+#include "mp3data.h"
+#include "usb.h"
+#include "status.h"
+#include "main_menu.h"
+#include "ata.h"
+#include "screens.h"
+#include "playlist.h"
+#include "playback.h"
+#include "pcm_playback.h"
+#include "buffer.h"
+#ifdef HAVE_LCD_BITMAP
+#include "icons.h"
+#include "peakmeter.h"
+#include "action.h"
+#endif
+#include "lang.h"
+#include "bookmark.h"
+#include "misc.h"
+#include "sound.h"
+
+static volatile bool playing;
+static volatile bool paused;
+
+#define CODEC_VORBIS "/.rockbox/codecs/codecvorbis.rock";
+#define CODEC_MPA_L3 "/.rockbox/codecs/codecmpa.rock";
+#define CODEC_FLAC "/.rockbox/codecs/codecflac.rock";
+#define CODEC_WAV "/.rockbox/codecs/codecwav.rock";
+
+#define AUDIO_WATERMARK 0x70000
+#define AUDIO_FILE_CHUNK (1024*256)
+
+#define AUDIO_PLAY 1
+#define AUDIO_STOP 2
+#define AUDIO_PAUSE 3
+#define AUDIO_RESUME 4
+#define AUDIO_NEXT 5
+#define AUDIO_PREV 6
+#define AUDIO_FF_REWIND 7
+#define AUDIO_FLUSH_RELOAD 8
+#define AUDIO_CODEC_DONE 9
+
+#define CODEC_LOAD 1
+#define CODEC_LOAD_DISK 2
+
+/* As defined in plugins/lib/xxx2wav.h */
+#define MALLOC_BUFSIZE (512*1024)
+#define GUARD_BUFSIZE (8*1024)
+
+/* TODO:
+ Handle playlist_peek in mpeg.c
+ Track changing
+*/
+
+extern bool audio_is_initialized;
+
+/* Buffer control thread. */
+static struct event_queue audio_queue;
+static long audio_stack[(DEFAULT_STACK_SIZE + 0x1000)/sizeof(long)];
+static const char audio_thread_name[] = "audio";
+
+/* Codec thread. */
+static struct event_queue codec_queue;
+static long codec_stack[(DEFAULT_STACK_SIZE + 0x2500)/sizeof(long)] __attribute__ ((section(".idata")));
+static const char codec_thread_name[] = "codec";
+
+/* Is file buffer currently being refilled? */
+static volatile bool filling;
+
+/* Ring buffer where tracks and codecs are loaded. */
+char *codecbuf;
+
+/* Total size of the ring buffer. */
+int codecbuflen;
+
+/* Bytes available in the buffer. */
+volatile int codecbufused;
+
+/* Ring buffer read and write indexes. */
+static int buf_ridx;
+static int buf_widx;
+
+#define MAX_TRACK 10
+struct track_info {
+ struct mp3entry id3;
+ struct mp3info mp3data;
+ char *codecbuf;
+ size_t codecsize;
+ int codectype;
+
+ volatile char *filebuf;
+ off_t filerem;
+ off_t filesize;
+ off_t filepos;
+ volatile int available;
+ bool taginfo_ready;
+ int playlist_offset;
+};
+
+/* Track information (count in file buffer, read/write indexes for
+ track ring structure. */
+static int track_count;
+static int track_ridx;
+static int track_widx;
+static bool track_changed;
+
+/* Playlist position to tell the next track. */
+static int last_offset;
+
+/* Partially loaded song's file handle to continue buffering later. */
+static int current_fd;
+
+/* Information about how many bytes left on the buffer re-fill run. */
+static size_t fill_bytesleft;
+
+/* Track info structure about songs in the file buffer. */
+static struct track_info tracks[MAX_TRACK];
+
+/* Pointer to track info structure about current song playing. */
+static volatile struct track_info *cur_ti;
+
+/* Codec API including function callbacks. */
+static struct codec_api ci;
+
+/* When we change a song and buffer is not in filling state, this
+ variable keeps information about whether to go a next/previous track. */
+static int new_track;
+
+#ifdef SIMULATOR
+bool audiobuffer_insert_sim(char *buf, size_t length)
+{
+ (void)buf;
+ (void)length;
+
+ return true;
+}
+#endif
+
+void* get_codec_memory_callback(size_t *size)
+{
+ *size = MALLOC_BUFSIZE;
+ return &audiobuf[0];
+}
+
+void codec_set_elapsed_callback(unsigned int value)
+{
+ unsigned int latency;
+
+#ifndef SIMULATOR
+ latency = audiobuffer_get_latency();
+#else
+ latency = 0;
+#endif
+ if (value < latency) {
+ cur_ti->id3.elapsed = 0;
+ } else if (value - latency > cur_ti->id3.elapsed
+ || value - latency < cur_ti->id3.elapsed - 2) {
+ cur_ti->id3.elapsed = value - latency;
+ }
+}
+
+size_t codec_filebuf_callback(void *ptr, size_t size)
+{
+ char *buf = (char *)ptr;
+ int copy_n;
+ int part_n;
+
+ if (ci.stop_codec || !playing)
+ return 0;
+
+ copy_n = MIN((off_t)size, (off_t)cur_ti->available + cur_ti->filerem);
+
+ while (copy_n > cur_ti->available) {
+ yield();
+ if (ci.stop_codec)
+ return 0;
+ }
+
+ if (copy_n == 0)
+ return 0;
+
+ part_n = MIN(copy_n, codecbuflen - buf_ridx);
+ memcpy(buf, &codecbuf[buf_ridx], part_n);
+ if (part_n < copy_n) {
+ part_n = copy_n - part_n;
+ memcpy(&buf[part_n], &codecbuf[0], copy_n - part_n);
+ }
+
+ buf_ridx += copy_n;
+ if (buf_ridx >= codecbuflen)
+ buf_ridx -= codecbuflen;
+ ci.curpos += copy_n;
+ cur_ti->available -= copy_n;
+ codecbufused -= copy_n;
+
+ return copy_n;
+}
+
+void* codec_request_buffer_callback(size_t *realsize, size_t reqsize)
+{
+ size_t part_n;
+
+ if (ci.stop_codec || !playing) {
+ *realsize = 0;
+ return NULL;
+ }
+
+ *realsize = MIN((off_t)reqsize, (off_t)cur_ti->available + cur_ti->filerem);
+ if (*realsize == 0) {
+ return NULL;
+ }
+
+ while ((int)*realsize > cur_ti->available) {
+ // logf("Buffer wait: %d", cur_ti->available);
+ yield();
+ if (ci.stop_codec) {
+ return NULL;
+ }
+ }
+
+ part_n = MIN((int)*realsize, codecbuflen - buf_ridx);
+ if (part_n < *realsize) {
+ part_n += GUARD_BUFSIZE;
+ if (part_n < *realsize)
+ *realsize = part_n;
+ memcpy(&codecbuf[codecbuflen], &codecbuf[0], *realsize -
+ (codecbuflen - buf_ridx));
+ }
+
+ return (char *)&codecbuf[buf_ridx];
+}
+
+void codec_advance_buffer_callback(size_t amount)
+{
+ if ((int)amount > cur_ti->available)
+ amount = cur_ti->available;
+
+ cur_ti->available -= amount;
+ codecbufused -= amount;
+ buf_ridx += amount;
+ if (buf_ridx >= codecbuflen)
+ buf_ridx -= codecbuflen;
+ ci.curpos += amount;
+}
+
+void codec_advance_buffer_loc_callback(void *ptr)
+{
+ size_t amount;
+
+ amount = (int)ptr - (int)&codecbuf[buf_ridx];
+ codec_advance_buffer_callback(amount);
+}
+
+off_t codec_mp3_get_filepos_callback(int newtime)
+{
+ int oldtime;
+ off_t newpos;
+
+ oldtime = cur_ti->id3.elapsed;
+ cur_ti->id3.elapsed = newtime;
+ newpos = audio_get_file_pos();
+ cur_ti->id3.elapsed = oldtime;
+
+ return newpos;
+}
+
+bool codec_seek_buffer_callback(off_t newpos)
+{
+ int difference;
+
+ if (newpos < 0)
+ newpos = 0;
+
+ if (newpos >= cur_ti->filesize)
+ newpos = cur_ti->filesize - 1;
+
+ difference = newpos - ci.curpos;
+ if (difference >= 0) {
+ logf("seek: +%d", difference);
+ codec_advance_buffer_callback(difference);
+#ifndef SIMULATOR
+ pcm_play_stop();
+#endif
+ return true;
+ }
+
+ difference = -difference;
+ if (ci.curpos - difference < 0)
+ difference = ci.curpos;
+
+ if (codecbufused + difference > codecbuflen) {
+ /* We need to reload the song. FIX THIS! */
+ return false;
+ }
+
+ logf("seek: -%d", difference);
+ codecbufused += difference;
+ cur_ti->available += difference;
+ buf_ridx -= difference;
+ if (buf_ridx < 0)
+ buf_ridx = codecbuflen + buf_ridx;
+ ci.curpos -= difference;
+#ifndef SIMULATOR
+ pcm_play_stop();
+#endif
+
+ return true;
+}
+
+/* Simple file type probing by looking filename extension. */
+int probe_file_format(const char *filename)
+{
+ char suffix[4];
+ char *p;
+ int len, i;
+
+ if (filename == NULL)
+ return AFMT_UNKNOWN;
+
+ len = strlen(filename);
+ if (len < 4)
+ return AFMT_UNKNOWN;
+
+ p = (char *)&filename[len-3];
+ memset(suffix, 0, sizeof(suffix));
+ for (i = 0; i < 3; i++) {
+ suffix[i] = tolower(p[i]);
+ }
+
+ if (!strcmp("mp3", suffix))
+ return AFMT_MPA_L3;
+ else if (!strcmp("ogg", suffix))
+ return AFMT_OGG_VORBIS;
+ else if (!strcmp("wav", suffix))
+ return AFMT_PCM_WAV;
+ else if (!strcmp("flac", suffix)) // FIX THIS
+ return AFMT_FLAC;
+ else if (!strcmp("mpc", suffix))
+ return AFMT_MPC;
+ else if (!strcmp("aac", suffix))
+ return AFMT_AAC;
+ else if (!strcmp("ape", suffix))
+ return AFMT_APE;
+ else if (!strcmp("wma", suffix))
+ return AFMT_OGG_VORBIS;
+ else if (!strcmp("a52", suffix))
+ return AFMT_OGG_VORBIS;
+ else if (!strcmp(".rm", suffix))
+ return AFMT_OGG_VORBIS;
+
+ return AFMT_UNKNOWN;
+
+}
+
+void audio_fill_file_buffer(void)
+{
+ size_t i;
+ int rc;
+
+ logf("Filling buffer...");
+ i = 0;
+ while ((off_t)i < tracks[track_widx].filerem) {
+ sleep(5); /* Give codecs some processing time. */
+ if (!playing) {
+ logf("Filling interrupted");
+ close(current_fd);
+ current_fd = -1;
+ return ;
+ }
+
+ if (fill_bytesleft < MIN(AUDIO_FILE_CHUNK,
+ tracks[track_widx].filerem - i))
+ break ;
+
+ rc = MIN(AUDIO_FILE_CHUNK, codecbuflen - buf_widx);
+ rc = read(current_fd, &codecbuf[buf_widx], rc);
+ if (rc <= 0) {
+ tracks[track_widx].filerem = 0;
+ break ;
+ }
+ buf_widx += rc;
+ if (buf_widx == codecbuflen)
+ buf_widx = 0;
+ i += rc;
+ tracks[track_widx].available += rc;
+ fill_bytesleft -= rc;
+ }
+
+ tracks[track_widx].filerem -= i;
+ codecbufused += i;
+ tracks[track_widx].filepos += i;
+ logf("Done:%d", tracks[track_widx].available);
+ if (tracks[track_widx].filerem == 0) {
+ if (++track_widx == MAX_TRACK)
+ track_widx = 0;
+ tracks[track_widx].filerem = 0;
+ close(current_fd);
+ current_fd = -1;
+ }
+}
+
+bool loadcodec(const char *trackname, bool start_play)
+{
+ char msgbuf[80];
+ off_t size;
+ int filetype;
+ int fd;
+ int i, rc;
+ const char *codec_path;
+ int copy_n;
+ int prev_track;
+
+ filetype = probe_file_format(trackname);
+ switch (filetype) {
+ case AFMT_OGG_VORBIS:
+ logf("Codec: Vorbis");
+ codec_path = CODEC_VORBIS;
+ break;
+ case AFMT_MPA_L3:
+ logf("Codec: MPA L3");
+ codec_path = CODEC_MPA_L3;
+ break;
+ case AFMT_PCM_WAV:
+ logf("Codec: PCM WAV");
+ codec_path = CODEC_WAV;
+ break;
+ case AFMT_FLAC:
+ logf("Codec: FLAC");
+ codec_path = CODEC_FLAC;
+ break;
+ default:
+ logf("Codec: Unsupported");
+ snprintf(msgbuf, sizeof(msgbuf)-1, "No codec for: %s", trackname);
+ splash(HZ*2, true, msgbuf);
+ codec_path = NULL;
+ }
+
+ tracks[track_widx].codectype = filetype;
+ tracks[track_widx].codecsize = 0;
+ if (codec_path == NULL)
+ return false;
+
+ if (!start_play) {
+ prev_track = track_widx - 1;
+ if (prev_track < 0)
+ prev_track = MAX_TRACK-1;
+ if (track_count > 0 && filetype == tracks[prev_track].codectype) {
+ logf("Reusing prev. codec");
+ return true;
+ }
+ } else {
+ /* Load the codec directly from disk and save some memory. */
+ cur_ti = &tracks[track_widx];
+ ci.filesize = cur_ti->filesize;
+ ci.id3 = (struct mp3entry *)&cur_ti->id3;
+ ci.mp3data = (struct mp3info *)&cur_ti->mp3data;
+ ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready;
+ ci.curpos = 0;
+ playing = true;
+ logf("Starting codec");
+ queue_post(&codec_queue, CODEC_LOAD_DISK, (void *)codec_path);
+ return true;
+ }
+
+ fd = open(codec_path, O_RDONLY);
+ if (fd < 0) {
+ logf("Codec doesn't exist!");
+ snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", codec_path);
+ splash(HZ*2, true, msgbuf);
+ return false;
+ }
+
+ size = filesize(fd);
+ if ((off_t)fill_bytesleft < size + AUDIO_WATERMARK) {
+ logf("Not enough space");
+ close(fd);
+ return false;
+ }
+
+ i = 0;
+ while (i < size) {
+ yield();
+ if (!playing) {
+ logf("Buffering interrupted");
+ close(fd);
+ return false;
+ }
+
+ copy_n = MIN(AUDIO_FILE_CHUNK, codecbuflen - buf_widx);
+ rc = read(fd, &codecbuf[buf_widx], copy_n);
+ if (rc < 0)
+ return false;
+ buf_widx += rc;
+ if (buf_widx >= codecbuflen)
+ buf_widx = 0;
+ i += rc;
+ }
+ close(fd);
+ logf("Done: %dB", i);
+
+ codecbufused += size;
+ fill_bytesleft -= size;
+ tracks[track_widx].codecsize = size;
+
+ return true;
+}
+
+bool audio_load_track(int offset, bool start_play)
+{
+ char *trackname;
+ int fd;
+ off_t size;
+ int rc, i;
+ int copy_n;
+
+ if (track_count >= MAX_TRACK)
+ return false;
+
+ trackname = playlist_peek(offset);
+ if (!trackname) {
+ return false;
+ }
+
+ fd = open(trackname, O_RDONLY);
+ if (fd < 0)
+ return false;
+
+ size = filesize(fd);
+ tracks[track_widx].filerem = size;
+ tracks[track_widx].filesize = size;
+ tracks[track_widx].filepos = 0;
+ tracks[track_widx].available = 0;
+ tracks[track_widx].taginfo_ready = false;
+ tracks[track_widx].playlist_offset = offset;
+
+ /* Load the codec */
+ if (buf_widx >= codecbuflen)
+ buf_widx = 0;
+
+ tracks[track_widx].codecbuf = &codecbuf[buf_widx];
+ if (!loadcodec(trackname, start_play)) {
+ close(fd);
+ return false;
+ }
+ tracks[track_widx].filebuf = &codecbuf[buf_widx];
+
+ logf("%s", trackname);
+ logf("Buffering track:%d/%d", track_widx, track_ridx);
+
+ if (!playing) {
+ close(fd);
+ return false;
+ }
+
+ /* Load codec specific track tag information. */
+ switch (tracks[track_widx].codectype) {
+ case AFMT_MPA_L3:
+ /* Should check the return value. */
+ mp3info(&tracks[track_widx].id3, trackname, true);
+ lseek(fd, 0, SEEK_SET);
+ get_mp3file_info(fd, &tracks[track_widx].mp3data);
+ logf("T:%s", tracks[track_widx].id3.title);
+ logf("L:%d", tracks[track_widx].id3.length);
+ logf("O:%d", tracks[track_widx].id3.first_frame_offset);
+ logf("F:%d", tracks[track_widx].id3.frequency);
+ tracks[track_widx].taginfo_ready = true;
+ break ;
+ }
+
+ playlist_next(0);
+ last_offset++;
+ track_count++;
+ lseek(fd, 0, SEEK_SET);
+ i = 0;
+ while (i < size) {
+ /* Give codecs some processing time to prevent glitches. */
+ sleep(5);
+ if (!playing) {
+ logf("Buffering interrupted");
+ close(fd);
+ return false;
+ }
+
+ if (fill_bytesleft == 0)
+ break ;
+
+ copy_n = MIN(AUDIO_FILE_CHUNK, codecbuflen - buf_widx);
+ copy_n = MIN(size - i, copy_n);
+ copy_n = MIN((int)fill_bytesleft, copy_n);
+ rc = read(fd, &codecbuf[buf_widx], copy_n);
+ if (rc < 0) {
+ close(fd);
+ return false;
+ }
+ buf_widx += rc;
+ if (buf_widx == codecbuflen)
+ buf_widx = 0;
+ i += rc;
+ tracks[track_widx].available += rc;
+ tracks[track_widx].filerem -= rc;
+ fill_bytesleft -= rc;
+ }
+
+ tracks[track_widx].filepos = i;
+ codecbufused += i;
+
+ /* Leave the file handle open for faster buffer refill. */
+ if (tracks[track_widx].filerem != 0) {
+ current_fd = fd;
+ logf("Partially buf:%d", tracks[track_widx].available);
+ return false;
+ } else {
+ logf("Completely buf.");
+ close(fd);
+ current_fd = -1;
+ if (++track_widx >= MAX_TRACK) {
+ track_widx = 0;
+ }
+ tracks[track_widx].filerem = 0;
+ }
+
+ return true;
+}
+
+void audio_insert_tracks(bool start_playing)
+{
+ fill_bytesleft = codecbuflen - codecbufused;
+ filling = true;
+ while (audio_load_track(last_offset, start_playing)) {
+ start_playing = false;
+ }
+ filling = false;
+}
+
+void audio_play_start(int offset)
+{
+ memset(&tracks, 0, sizeof(struct track_info));
+ sound_set(SOUND_VOLUME, global_settings.volume);
+ track_count = 0;
+ track_changed = true;
+ track_widx = 0;
+ track_ridx = 0;
+ buf_ridx = 0;
+ buf_widx = 0;
+ codecbufused = 0;
+#ifndef SIMULATOR
+ pcm_set_boost_mode(true);
+#endif
+ last_offset = offset;
+ audio_insert_tracks(true);
+#ifndef SIMULATOR
+ pcm_set_boost_mode(false);
+ ata_sleep();
+#endif
+}
+
+void audio_check_buffer(void)
+{
+ int i;
+ int cur_idx;
+
+ /* Start buffer filling as necessary. */
+ if (codecbufused > AUDIO_WATERMARK || !playing)
+ return ;
+
+ filling = true;
+#ifndef SIMULATOR
+ pcm_set_boost_mode(true);
+#endif
+
+ fill_bytesleft = codecbuflen - codecbufused;
+
+ /* Calculate real track count after throwing away old tracks. */
+ cur_idx = track_ridx;
+ for (i = 0; i < track_count; i++) {
+ if (cur_idx == track_widx)
+ break ;
+
+ if (++cur_idx >= MAX_TRACK)
+ cur_idx = 0;
+ }
+
+ track_count = i;
+ if (tracks[cur_idx].filerem != 0)
+ track_count++;
+
+ /* Mark all other entries null. */
+ cur_idx = track_widx;
+ for (i = 0; i < MAX_TRACK - track_count; i++) {
+ if (++cur_idx >= MAX_TRACK)
+ cur_idx = 0;
+ tracks[cur_idx].available = 0;
+ }
+
+ /* Try to load remainings of the file. */
+ if (tracks[track_widx].filerem > 0)
+ audio_fill_file_buffer();
+
+ /* Load new files to fill the entire buffer. */
+ if (tracks[track_widx].filerem == 0)
+ audio_insert_tracks(false);
+
+#ifndef SIMULATOR
+ pcm_set_boost_mode(false);
+ if (playing)
+ ata_sleep();
+#endif
+ filling = false;
+}
+
+void audio_update_trackinfo(void)
+{
+ if (new_track >= 0) {
+ buf_ridx += cur_ti->available;
+ codecbufused -= cur_ti->available;
+
+ cur_ti = &tracks[track_ridx];
+ buf_ridx += cur_ti->codecsize;
+ if (buf_ridx >= codecbuflen)
+ buf_ridx -= codecbuflen;
+ } else {
+ buf_ridx -= ci.curpos;
+ codecbufused += ci.curpos;
+
+ cur_ti = &tracks[track_ridx];
+ buf_ridx -= cur_ti->filesize;
+ buf_ridx -= cur_ti->codecsize;
+ cur_ti->available = cur_ti->filesize;
+ if (buf_ridx < 0)
+ buf_ridx = codecbuflen + buf_ridx;
+ }
+
+ ci.filesize = cur_ti->filesize;
+ ci.id3 = (struct mp3entry *)&cur_ti->id3;
+ ci.mp3data = (struct mp3info *)&cur_ti->mp3data;
+ ci.curpos = 0;
+ ci.taginfo_ready = (bool *)&cur_ti->taginfo_ready;
+ track_changed = true;
+}
+
+void audio_change_track(void)
+{
+ if (track_ridx == track_widx) {
+ logf("No more tracks");
+ playing = false;
+ return ;
+ }
+
+ if (++track_ridx >= MAX_TRACK)
+ track_ridx = 0;
+
+ audio_update_trackinfo();
+ queue_post(&codec_queue, CODEC_LOAD, 0);
+}
+
+bool codec_request_next_track_callback(void)
+{
+ if (ci.stop_codec || !playing)
+ return false;
+
+ logf("Request new track");
+
+ /* Advance to next track. */
+ if (ci.reload_codec && new_track > 0) {
+ if (++track_ridx == MAX_TRACK)
+ track_ridx = 0;
+ if (track_ridx == track_widx && tracks[track_ridx].filerem == 0) {
+ logf("Loading from disk...");
+ new_track = 0;
+ queue_post(&audio_queue, AUDIO_PLAY, (void *)(last_offset));
+ return false;
+ }
+ }
+
+ /* Advance to previous track. */
+ else if (ci.reload_codec && new_track < 0) {
+ if (--track_ridx < 0)
+ track_ridx = MAX_TRACK-1;
+ if (tracks[track_ridx].filesize == 0 ||
+ codecbufused+ci.curpos+tracks[track_ridx].filesize
+ + (off_t)tracks[track_ridx].codecsize > codecbuflen) {
+ logf("Loading from disk...");
+ last_offset -= track_count;
+ if (last_offset < 0)
+ last_offset = 0;
+ new_track = 0;
+ queue_post(&audio_queue, AUDIO_PLAY, (void *)(last_offset));
+ return false;
+ }
+ }
+
+ /* Codec requested track change (next track). */
+ else {
+ if (++track_ridx >= MAX_TRACK)
+ track_ridx = 0;
+
+ if (track_ridx == track_widx && tracks[track_ridx].filerem == 0) {
+ if (ci.reload_codec) {
+ } else {
+ logf("No more tracks");
+ }
+ new_track = 0;
+ return false;
+ }
+ }
+
+ ci.reload_codec = false;
+
+ if (cur_ti->codectype != tracks[track_ridx].codectype) {
+ if (--track_ridx < 0)
+ track_ridx = MAX_TRACK-1;
+ logf("New codec");
+ new_track = 0;
+ return false;
+ }
+
+ logf("On-the-fly change");
+ audio_update_trackinfo();
+ new_track = 0;
+
+ return true;
+}
+
+void audio_thread(void)
+{
+ struct event ev;
+
+ while (1) {
+ yield();
+ audio_check_buffer();
+
+ queue_wait_w_tmo(&audio_queue, &ev, 0);
+ switch (ev.id) {
+ case AUDIO_PLAY:
+ ci.stop_codec = true;
+ ci.reload_codec = false;
+ ci.seek_time = 0;
+#ifndef SIMULATOR
+ pcm_play_stop();
+ pcm_play_pause(true);
+#endif
+ playing = true;
+ paused = false;
+ audio_play_start((int)ev.data);
+ break ;
+
+ case AUDIO_STOP:
+#ifndef SIMULATOR
+ pcm_play_stop();
+#endif
+ paused = false;
+ break ;
+
+ case AUDIO_PAUSE:
+ break ;
+
+ case AUDIO_RESUME:
+ break ;
+
+ case AUDIO_NEXT:
+ break ;
+
+ case AUDIO_CODEC_DONE:
+ //if (playing)
+ // audio_change_track();
+ break ;
+
+#ifndef SIMULATOR
+ case SYS_USB_CONNECTED:
+ playing = false;
+ ci.stop_codec = true;
+ logf("USB Connection");
+ pcm_play_stop();
+ usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ usb_wait_for_disconnect(&audio_queue);
+ break ;
+#endif
+ }
+ }
+}
+
+void codec_thread(void)
+{
+ struct event ev;
+ size_t codecsize;
+ int status;
+ int wrap;
+
+ while (1) {
+ status = 0;
+ queue_wait(&codec_queue, &ev);
+ switch (ev.id) {
+ case CODEC_LOAD_DISK:
+ ci.stop_codec = false;
+ status = codec_load_file((char *)ev.data, &ci);
+ break ;
+
+ case CODEC_LOAD:
+ logf("Codec start");
+ codecsize = cur_ti->codecsize;
+ if (codecsize == 0) {
+ logf("Codec slot is empty!");
+ playing = false;
+ break ;
+ }
+ codecbufused -=codecsize;
+ cur_ti->codecsize = 0;
+
+ ci.stop_codec = false;
+ wrap = (int)&codecbuf[codecbuflen] - (int)cur_ti->codecbuf;
+ status = codec_load_ram(cur_ti->codecbuf, codecsize,
+ &ci, &codecbuf[0], codecbuflen);
+ break ;
+
+#ifndef SIMULATOR
+ case SYS_USB_CONNECTED:
+ usb_acknowledge(SYS_USB_CONNECTED_ACK);
+ usb_wait_for_disconnect(&codec_queue);
+ break ;
+#endif
+ }
+
+ switch (ev.id) {
+ case CODEC_LOAD_DISK:
+ case CODEC_LOAD:
+ if (status != PLUGIN_OK) {
+ logf("Codec failure");
+ playing = false;
+ } else {
+ logf("Codec finished");
+ }
+
+ queue_post(&audio_queue, AUDIO_CODEC_DONE, (void *)status);
+ if (playing && !ci.stop_codec && !ci.reload_codec) {
+ audio_change_track();
+ } else {
+ playing = false;
+ }
+ }
+ }
+}
+
+struct mp3entry* audio_current_track(void)
+{
+ logf("audio_current_track");
+
+ if (track_count > 0 && cur_ti->taginfo_ready)
+ return (struct mp3entry *)&cur_ti->id3;
+ else
+ return NULL;
+}
+
+struct mp3entry* audio_next_track(void)
+{
+ int next_idx = track_ridx + 1;
+
+ if (track_count == 0)
+ return NULL;
+
+ if (next_idx >= MAX_TRACK)
+ next_idx = 0;
+
+ if (!tracks[next_idx].taginfo_ready)
+ return NULL;
+
+ //logf("audio_next_track");
+
+ return &tracks[next_idx].id3;
+}
+
+bool audio_has_changed_track(void)
+{
+ if (track_changed && track_count > 0) {
+ if (!cur_ti->taginfo_ready)
+ return false;
+ track_changed = false;
+ return true;
+ }
+
+ return false;
+}
+
+void audio_play(int offset)
+{
+ logf("audio_play");
+ playing = false;
+ ci.stop_codec = true;
+ queue_post(&audio_queue, AUDIO_PLAY, (void *)offset);
+}
+
+void audio_stop(void)
+{
+ logf("audio_stop");
+ if (!playing)
+ return ;
+
+ playing = false;
+ ci.stop_codec = true;
+ if (current_fd) {
+ close(current_fd);
+ current_fd = -1;
+ }
+ queue_post(&audio_queue, AUDIO_STOP, 0);
+}
+
+void audio_pause(void)
+{
+ logf("audio_pause");
+#ifndef SIMULATOR
+ pcm_play_pause(false);
+#endif
+ paused = true;
+ //queue_post(&audio_queue, AUDIO_PAUSE, 0);
+}
+
+void audio_resume(void)
+{
+ logf("audio_resume");
+#ifndef SIMULATOR
+ pcm_play_pause(true);
+#endif
+ paused = false;
+ //queue_post(&audio_queue, AUDIO_RESUME, 0);
+}
+
+void audio_next(void)
+{
+ logf("audio_next");
+ new_track = 1;
+ ci.reload_codec = true;
+#ifndef SIMULATOR
+ pcm_play_stop();
+#endif
+
+ /* Detect if disk is spinning.. */
+ if (filling) {
+ playing = false;
+ ci.stop_codec = true;
+ queue_post(&audio_queue, AUDIO_PLAY, (void *)(last_offset));
+ }
+}
+
+void audio_prev(void)
+{
+ logf("audio_prev");
+ new_track = -1;
+ ci.reload_codec = true;
+#ifndef SIMULATOR
+ pcm_play_stop();
+#endif
+
+ if (filling) {
+ playing = false;
+ ci.stop_codec = true;
+ if (--last_offset < 0)
+ last_offset = 0;
+ queue_post(&audio_queue, AUDIO_PLAY, (void *)(last_offset));
+ }
+ //queue_post(&audio_queue, AUDIO_PREV, 0);
+}
+
+void audio_ff_rewind(int newpos)
+{
+ logf("rewind: %d", newpos);
+ /* Does not work yet. */
+ if (playing)
+ ci.seek_time = newpos+1;
+}
+
+void audio_flush_and_reload_tracks(void)
+{
+ logf("flush & reload");
+}
+
+void audio_error_clear(void)
+{
+}
+
+int audio_status(void)
+{
+ int ret = 0;
+
+ if (playing)
+ ret |= AUDIO_STATUS_PLAY;
+
+ if (paused)
+ ret |= AUDIO_STATUS_PAUSE;
+
+ return ret;
+}
+
+void audio_init(void)
+{
+ logf("audio api init");
+ codecbuflen = audiobufend - audiobuf - AUDIOBUF_SIZE
+ - MALLOC_BUFSIZE - GUARD_BUFSIZE;
+ //codecbuflen = 2*512*1024;
+ codecbufused = 0;
+ filling = 0;
+ codecbuf = &audiobuf[MALLOC_BUFSIZE];
+ playing = false;
+ paused = false;
+ track_changed = false;
+
+ logf("abuf:%0x", AUDIOBUF_SIZE);
+ logf("fbuf:%0x", codecbuflen);
+ logf("mbuf:%0x", MALLOC_BUFSIZE);
+
+ /* Initialize codec api. */
+ ci.read_filebuf = codec_filebuf_callback;
+#ifndef SIMULATOR
+ ci.audiobuffer_insert = audiobuffer_insert;
+#else
+ ci.audiobuffer_insert = audiobuffer_insert_sim;
+#endif
+ ci.get_codec_memory = get_codec_memory_callback;
+ ci.request_buffer = codec_request_buffer_callback;
+ ci.advance_buffer = codec_advance_buffer_callback;
+ ci.advance_buffer_loc = codec_advance_buffer_loc_callback;
+ ci.request_next_track = codec_request_next_track_callback;
+ ci.mp3_get_filepos = codec_mp3_get_filepos_callback;
+ ci.seek_buffer = codec_seek_buffer_callback;
+ ci.set_elapsed = codec_set_elapsed_callback;
+
+ queue_init(&codec_queue);
+
+ create_thread(codec_thread, codec_stack, sizeof(codec_stack),
+ codec_thread_name);
+ create_thread(audio_thread, audio_stack, sizeof(audio_stack),
+ audio_thread_name);
+#ifndef SIMULATOR
+ audio_is_initialized = true;
+#endif
+}
+
+
diff --git a/apps/playback.h b/apps/playback.h
new file mode 100644
index 0000000000..6f3c4214a2
--- /dev/null
+++ b/apps/playback.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Miika Pekkarinen
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _AUDIO_H
+#define _AUDIO_H
+
+#define AFMT_MPA_L1 0x0001 // MPEG Audio layer 1
+#define AFMT_MPA_L2 0x0002 // MPEG Audio layer 2
+#define AFMT_MPA_L3 0x0004 // MPEG Audio layer 3
+ /* (MPEG-1, 2, 2.5 layers 1, 2 and 3 */
+#define AFMT_PCM_WAV 0x0008 // Uncompressed PCM in a WAV file
+#define AFMT_OGG_VORBIS 0x0010 // Ogg Vorbis
+#define AFMT_FLAC 0x0020 // FLAC
+#define AFMT_MPC 0x0040 // Musepack
+#define AFMT_AAC 0x0080 // AAC
+#define AFMT_APE 0x0100 // Monkey's Audio
+#define AFMT_WMA 0x0200 // Windows Media Audio
+#define AFMT_A52 0x0400 // A/52 (aka AC3) audio
+#define AFMT_REAL 0x0800 // Realaudio
+#define AFMT_UNKNOWN 0x1000 // Unknown file format
+
+struct codec_api {
+ off_t filesize;
+ off_t curpos;
+ size_t bitspersampe;
+
+ /* For gapless mp3 */
+ struct mp3entry *id3;
+ struct mp3info *mp3data;
+ bool *taginfo_ready;
+
+ bool stop_codec;
+ bool reload_codec;
+ int seek_time;
+
+ void* (*get_codec_memory)(size_t *size);
+ bool (*audiobuffer_insert)(char *data, size_t length);
+ void (*set_elapsed)(unsigned int value);
+
+ size_t (*read_filebuf)(void *ptr, size_t size);
+ void* (*request_buffer)(size_t *realsize, size_t reqsize);
+ void (*advance_buffer)(size_t amount);
+ void (*advance_buffer_loc)(void *ptr);
+ bool (*seek_buffer)(off_t newpos);
+ off_t (*mp3_get_filepos)(int newtime);
+ bool (*request_next_track)(void);
+};
+
+#endif
+
+
diff --git a/apps/plugin.c b/apps/plugin.c
index 384ab22ede..81830a8823 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -327,6 +327,65 @@ static const struct plugin_api rockbox_api = {
strcasestr,
};
+#ifdef IRIVER_H100
+int codec_load_ram(char* pluginptr, size_t size, void *parameter, void* ptr2, size_t bufwrap)
+{
+ enum plugin_status (*plugin_start)(struct plugin_api* api, void* param);
+ int copy_n;
+
+ if ((int)&pluginbuf != (int)pluginptr) {
+ /* zero out plugin buffer to ensure a properly zeroed bss area */
+ memset(pluginbuf, 0, PLUGIN_BUFFER_SIZE);
+
+ size = MIN(size, PLUGIN_BUFFER_SIZE);
+ copy_n = MIN(size, bufwrap);
+ memcpy(pluginbuf, pluginptr, copy_n);
+
+ size -= copy_n;
+ if (size > 0) {
+ memcpy(ptr2, &pluginptr[copy_n], size);
+ }
+ }
+
+ plugin_start = (void*)&pluginbuf;
+
+ if (plugin_size <= 0) {
+ return -1;
+ }
+
+ invalidate_icache();
+
+ return plugin_start((struct plugin_api*) &rockbox_api, parameter);
+}
+
+int codec_load_file(const char *plugin, void *parameter)
+{
+ char msgbuf[80];
+ int fd;
+ int rc;
+
+ fd = open(plugin, O_RDONLY);
+ if (fd < 0) {
+ snprintf(msgbuf, sizeof(msgbuf)-1, "Couldn't load codec: %s", plugin);
+ splash(HZ*2, true, msgbuf);
+ return fd;
+ }
+
+ plugin_size = 0;
+
+ do {
+ rc = read(fd, &pluginbuf[0], PLUGIN_BUFFER_SIZE);
+ if (rc < 0)
+ return PLUGIN_ERROR;
+ plugin_size += rc;
+ } while (rc > 0) ;
+ close(fd);
+
+ return codec_load_ram(pluginbuf, plugin_size, parameter, NULL, 0);
+}
+
+#endif
+
int plugin_load(const char* plugin, void* parameter)
{
enum plugin_status (*plugin_start)(struct plugin_api* api, void* param);
diff --git a/apps/plugin.h b/apps/plugin.h
index 2604cae4f9..ada71bf059 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -383,6 +383,11 @@ struct plugin_api {
};
/* defined by the plugin loader (plugin.c) */
+#ifdef IRIVER_H100
+int codec_load_ram(char* pluginptr, size_t size, void *parameter, void* ptr2, size_t bufwrap);
+int codec_load_file(const char* plugin, void* parameter);
+#endif
+
int plugin_load(const char* plugin, void* parameter);
void* plugin_get_buffer(int *buffer_size);
void* plugin_get_audio_buffer(int *buffer_size);
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 64f85b1ab1..6ed0e7c098 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -72,6 +72,10 @@ mpa2wav.c
a52towav.c
flac2wav.c
vorbis2wav.c
+#ifdef IRIVER_H100
+codecvorbis.c
+codecmpa.c
+#endif
wv2wav.c
mpc2wav.c
midi2wav.c
diff --git a/apps/plugins/codecmpa.c b/apps/plugins/codecmpa.c
new file mode 100644
index 0000000000..1125b4bf18
--- /dev/null
+++ b/apps/plugins/codecmpa.c
@@ -0,0 +1,404 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Dave Chapman
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+
+#include <codecs/libmad/mad.h>
+
+#include "playback.h"
+#include "mp3data.h"
+#include "lib/codeclib.h"
+
+static struct plugin_api* rb;
+
+struct mad_stream Stream IDATA_ATTR;
+struct mad_frame Frame IDATA_ATTR;
+struct mad_synth Synth IDATA_ATTR;
+mad_timer_t Timer;
+struct dither d0, d1;
+
+/* The following function is used inside libmad - let's hope it's never
+ called.
+*/
+
+void abort(void) {
+}
+
+/* The "dither" code to convert the 24-bit samples produced by libmad was
+ taken from the coolplayer project - coolplayer.sourceforge.net */
+
+struct dither {
+ mad_fixed_t error[3];
+ mad_fixed_t random;
+};
+
+# define SAMPLE_DEPTH 16
+# define scale(x, y) dither((x), (y))
+
+/*
+ * NAME: prng()
+ * DESCRIPTION: 32-bit pseudo-random number generator
+ */
+static __inline
+unsigned long prng(unsigned long state)
+{
+ return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
+}
+
+/*
+ * NAME: dither()
+ * DESCRIPTION: dither and scale sample
+ */
+static __inline
+signed int dither(mad_fixed_t sample, struct dither *dither)
+{
+ unsigned int scalebits;
+ mad_fixed_t output, mask, random;
+
+ enum {
+ MIN = -MAD_F_ONE,
+ MAX = MAD_F_ONE - 1
+ };
+
+ /* noise shape */
+ sample += dither->error[0] - dither->error[1] + dither->error[2];
+
+ dither->error[2] = dither->error[1];
+ dither->error[1] = dither->error[0] / 2;
+
+ /* bias */
+ output = sample + (1L << (MAD_F_FRACBITS + 1 - SAMPLE_DEPTH - 1));
+
+ scalebits = MAD_F_FRACBITS + 1 - SAMPLE_DEPTH;
+ mask = (1L << scalebits) - 1;
+
+ /* dither */
+ random = prng(dither->random);
+ output += (random & mask) - (dither->random & mask);
+
+ //dither->random = random;
+
+ /* clip */
+ if (output > MAX) {
+ output = MAX;
+
+ if (sample > MAX)
+ sample = MAX;
+ }
+ else if (output < MIN) {
+ output = MIN;
+
+ if (sample < MIN)
+ sample = MIN;
+ }
+
+ /* quantize */
+ output &= ~mask;
+
+ /* error feedback */
+ dither->error[0] = sample - output;
+
+ /* scale */
+ return output >> scalebits;
+}
+
+static __inline
+signed int detect_silence(mad_fixed_t sample)
+{
+ unsigned int scalebits;
+ mad_fixed_t output, mask;
+
+ enum {
+ MIN = -MAD_F_ONE,
+ MAX = MAD_F_ONE - 1
+ };
+
+ /* bias */
+ output = sample + (1L << (MAD_F_FRACBITS + 1 - SAMPLE_DEPTH - 1));
+
+ scalebits = MAD_F_FRACBITS + 1 - SAMPLE_DEPTH;
+ mask = (1L << scalebits) - 1;
+
+ /* clip */
+ if (output > MAX) {
+ output = MAX;
+
+ if (sample > MAX)
+ sample = MAX;
+ }
+ else if (output < MIN) {
+ output = MIN;
+
+ if (sample < MIN)
+ sample = MIN;
+ }
+
+ /* quantize */
+ output &= ~mask;
+
+ /* scale */
+ output >>= scalebits + 4;
+
+ if (output == 0x00 || output == 0xff)
+ return 1;
+
+ return 0;
+}
+#define SHRT_MAX 32767
+
+#define INPUT_CHUNK_SIZE 8192
+#define OUTPUT_BUFFER_SIZE 65536 /* Must be an integer multiple of 4. */
+
+unsigned char OutputBuffer[OUTPUT_BUFFER_SIZE];
+unsigned char *OutputPtr;
+unsigned char *GuardPtr=NULL;
+const unsigned char *OutputBufferEnd=OutputBuffer+OUTPUT_BUFFER_SIZE;
+
+mad_fixed_t mad_frame_overlap[2][32][18] IDATA_ATTR;
+unsigned char mad_main_data[MAD_BUFFER_MDLEN] IDATA_ATTR;
+
+#ifdef USE_IRAM
+extern char iramcopy[];
+extern char iramstart[];
+extern char iramend[];
+#endif
+
+#undef DEBUG_GAPLESS
+
+/* this is the plugin entry point */
+enum plugin_status plugin_start(struct plugin_api* api, void* parm)
+{
+ struct codec_api *ci = (struct codec_api *)parm;
+ int Status=0;
+ size_t size;
+ int file_end;
+ unsigned short Sample;
+ char *InputBuffer;
+ unsigned int samplecount;
+ unsigned int samplesdone;
+ bool first_frame;
+#ifdef DEBUG_GAPLESS
+ bool first = true;
+ int fd;
+#endif
+ int i;
+
+ /* Generic plugin inititialisation */
+
+ TEST_PLUGIN_API(api);
+ rb = api;
+
+#ifdef USE_IRAM
+ rb->memcpy(iramstart, iramcopy, iramend-iramstart);
+#endif
+
+ /* This function sets up the buffers and reads the file into RAM */
+
+ if (codec_init(api, ci)) {
+ return PLUGIN_ERROR;
+ }
+
+ /* Create a decoder instance */
+
+ next_track:
+
+ memset(&Stream, 0, sizeof(struct mad_stream));
+ memset(&Frame, 0, sizeof(struct mad_frame));
+ memset(&Synth, 0, sizeof(struct mad_synth));
+ memset(&Timer, 0, sizeof(mad_timer_t));
+
+ mad_stream_init(&Stream);
+ mad_frame_init(&Frame);
+ mad_synth_init(&Synth);
+ mad_timer_reset(&Timer);
+
+ /* We do this so libmad doesn't try to call codec_calloc() */
+ memset(mad_frame_overlap, 0, sizeof(mad_frame_overlap));
+ Frame.overlap = &mad_frame_overlap;
+ Stream.main_data = &mad_main_data;
+
+#ifdef DEBUG_GAPLESS
+ if (first)
+ fd = rb->open("/first.pcm", O_WRONLY | O_CREAT);
+ else
+ fd = rb->open("/second.pcm", O_WRONLY | O_CREAT);
+ first = false;
+#endif
+
+ samplesdone = 0;
+ first_frame = false;
+ file_end = 0;
+ OutputPtr = OutputBuffer;
+
+ while (!*ci->taginfo_ready)
+ rb->yield();
+
+
+ ci->request_buffer(&size, ci->id3->first_frame_offset);
+ ci->advance_buffer(size);
+
+ samplecount = ci->id3->length * (ci->id3->frequency / 100) / 10;
+ /* rb->snprintf(buf2, sizeof(buf2), "sc: %d", samplecount);
+ rb->splash(0, true, buf2);
+ rb->snprintf(buf2, sizeof(buf2), "length: %d", ci->id3->length);
+ rb->splash(HZ*5, true, buf2);
+ rb->snprintf(buf2, sizeof(buf2), "frequency: %d", ci->id3->frequency);
+ rb->splash(HZ*5, true, buf2); */
+
+ /* This is the decoding loop. */
+ while (1) {
+ rb->yield();
+ if (ci->stop_codec || ci->reload_codec) {
+ break ;
+ }
+
+ if (ci->seek_time) {
+ unsigned int sample_loc;
+ int newpos;
+
+ sample_loc = ci->seek_time/1000 * ci->id3->frequency;
+ newpos = ci->mp3_get_filepos(ci->seek_time-1);
+ if (ci->seek_buffer(newpos)) {
+ ci->seek_time = 0;
+ if (sample_loc >= samplecount + samplesdone)
+ break ;
+ samplecount += samplesdone - sample_loc;
+ samplesdone = sample_loc;
+ }
+ }
+
+ /* Lock buffers */
+ if (Stream.error == 0) {
+ InputBuffer = ci->request_buffer(&size, INPUT_CHUNK_SIZE);
+ if (size == 0 || InputBuffer == NULL)
+ break ;
+ mad_stream_buffer(&Stream, InputBuffer, size);
+ }
+
+ //if ((int)ci->curpos >= ci->id3->first_frame_offset)
+ //first_frame = true;
+
+ if(mad_frame_decode(&Frame,&Stream))
+ {
+ if (Stream.error == MAD_FLAG_INCOMPLETE || Stream.error == MAD_ERROR_BUFLEN) {
+ // rb->splash(HZ*1, true, "Incomplete");
+ /* This makes the codec to support partially corrupted files too. */
+ if (file_end == 30)
+ break ;
+
+ /* Fill the buffer */
+ Stream.error = 0;
+ file_end++;
+ continue ;
+ }
+ else if(MAD_RECOVERABLE(Stream.error))
+ {
+ if(Stream.error!=MAD_ERROR_LOSTSYNC || Stream.this_frame!=GuardPtr)
+ {
+ // rb->splash(HZ*1, true, "Recoverable...!");
+ }
+ continue;
+ }
+ else if(Stream.error==MAD_ERROR_BUFLEN) {
+ //rb->splash(HZ*1, true, "Buflen error");
+ break ;
+ } else {
+ //rb->splash(HZ*1, true, "Unrecoverable error");
+ Status=1;
+ break;
+ }
+ }
+ if (Stream.next_frame)
+ ci->advance_buffer_loc((void *)Stream.next_frame);
+ file_end = false;
+ /* ?? Do we need the timer module? */
+ // mad_timer_add(&Timer,Frame.header.duration);
+
+/* DAVE: This can be used to attenuate the audio */
+// if(DoFilter)
+// ApplyFilter(&Frame);
+
+ mad_synth_frame(&Synth,&Frame);
+
+ //if (!first_frame) {
+ //samplecount -= Synth.pcm.length;
+ //continue ;
+ //}
+
+ /* Convert MAD's numbers to an array of 16-bit LE signed integers */
+ for(i=0;i<Synth.pcm.length;i++)
+ {
+ samplesdone++;
+ //if (ci->mp3data->padding > 0) {
+ // ci->mp3data->padding--;
+ // continue ;
+ //}
+ if (!first_frame) {
+ if (detect_silence(Synth.pcm.samples[0][i]))
+ continue ;
+ first_frame = true;
+ }
+
+ /* Left channel */
+ Sample=scale(Synth.pcm.samples[0][i],&d0);
+ *(OutputPtr++)=Sample>>8;
+ *(OutputPtr++)=Sample&0xff;
+
+ /* Right channel. If the decoded stream is monophonic then
+ * the right output channel is the same as the left one.
+ */
+ if(MAD_NCHANNELS(&Frame.header)==2)
+ Sample=scale(Synth.pcm.samples[1][i],&d1);
+ *(OutputPtr++)=Sample>>8;
+ *(OutputPtr++)=Sample&0xff;
+
+ samplecount--;
+ if (samplecount == 0) {
+#ifdef DEBUG_GAPLESS
+ rb->write(fd, OutputBuffer, (int)OutputPtr-(int)OutputBuffer);
+#endif
+ while (!ci->audiobuffer_insert(OutputBuffer, (int)OutputPtr-(int)OutputBuffer))
+ rb->yield();
+ goto song_end;
+ }
+
+ /* Flush the buffer if it is full. */
+ if(OutputPtr==OutputBufferEnd)
+ {
+#ifdef DEBUG_GAPLESS
+ rb->write(fd, OutputBuffer, OUTPUT_BUFFER_SIZE);
+#endif
+ while (!ci->audiobuffer_insert(OutputBuffer, OUTPUT_BUFFER_SIZE))
+ rb->yield();
+ OutputPtr=OutputBuffer;
+ }
+ }
+ ci->set_elapsed(samplesdone / (ci->id3->frequency/1000));
+ }
+
+ song_end:
+#ifdef DEBUG_GAPLESS
+ rb->close(fd);
+#endif
+ Stream.error = 0;
+
+ if (ci->request_next_track())
+ goto next_track;
+ return PLUGIN_OK;
+}
diff --git a/apps/plugins/codecvorbis.c b/apps/plugins/codecvorbis.c
new file mode 100644
index 0000000000..41db223494
--- /dev/null
+++ b/apps/plugins/codecvorbis.c
@@ -0,0 +1,160 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2002 Björn Stenberg
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include "kernel.h"
+#include "plugin.h"
+
+#include <codecs/Tremor/ivorbisfile.h>
+
+#include "playback.h"
+#include "lib/codeclib.h"
+
+static struct plugin_api* rb;
+
+/* Some standard functions and variables needed by Tremor */
+
+
+int errno;
+
+size_t strlen(const char *s) {
+ return(rb->strlen(s));
+}
+
+char *strcpy(char *dest, const char *src) {
+ return(rb->strcpy(dest,src));
+}
+
+char *strcat(char *dest, const char *src) {
+ return(rb->strcat(dest,src));
+}
+
+size_t read_handler(void *ptr, size_t size, size_t nmemb, void *datasource) {
+ struct codec_api *p = (struct codec_api *) datasource;
+
+ return p->read_filebuf(ptr, nmemb*size);
+}
+
+int seek_handler(void *datasource, ogg_int64_t offset, int whence) {
+ /* We are not seekable at the moment */
+ (void)datasource;
+ (void)offset;
+ (void)whence;
+ return -1;
+}
+
+int close_handler(void *datasource) {
+ (void)datasource;
+ return 0;
+}
+
+long tell_handler(void *datasource) {
+ struct codec_api *p = (struct codec_api *) datasource;
+ return p->curpos;
+}
+
+#ifdef USE_IRAM
+extern char iramcopy[];
+extern char iramstart[];
+extern char iramend[];
+#endif
+
+
+/* reserve the PCM buffer in the IRAM area */
+static char pcmbuf[4096] IDATA_ATTR;
+
+/* this is the plugin entry point */
+enum plugin_status plugin_start(struct plugin_api* api, void* parm)
+{
+ struct codec_api *ci = (struct codec_api *)parm;
+ ov_callbacks callbacks;
+ OggVorbis_File vf;
+ vorbis_info* vi;
+
+ int error;
+ long n;
+ int current_section;
+ int eof;
+#if BYTE_ORDER == BIG_ENDIAN
+ int i;
+ char x;
+#endif
+
+ TEST_PLUGIN_API(api);
+
+ /* if you are using a global api pointer, don't forget to copy it!
+ otherwise you will get lovely "I04: IllInstr" errors... :-) */
+ rb = api;
+
+ #ifdef USE_IRAM
+ rb->memcpy(iramstart, iramcopy, iramend-iramstart);
+ #endif
+
+ /* This function sets up the buffers and reads the file into RAM */
+
+ if (codec_init(api, ci)) {
+ return PLUGIN_ERROR;
+ }
+
+
+ /* Create a decoder instance */
+
+ callbacks.read_func=read_handler;
+ callbacks.seek_func=seek_handler;
+ callbacks.tell_func=tell_handler;
+ callbacks.close_func=close_handler;
+
+ next_track:
+ error=ov_open_callbacks(ci,&vf,NULL,0,callbacks);
+
+ vi=ov_info(&vf,-1);
+
+ if (vi==NULL) {
+ // rb->splash(HZ*2, true, "Vorbis Error");
+ return PLUGIN_ERROR;
+ }
+
+ eof=0;
+ while (!eof) {
+ /* Read host-endian signed 16 bit PCM samples */
+ n=ov_read(&vf,pcmbuf,sizeof(pcmbuf),&current_section);
+
+ if (n==0) {
+ eof=1;
+ } else if (n < 0) {
+ DEBUGF("Error decoding frame\n");
+ } else {
+ rb->yield();
+ if (ci->stop_codec || ci->reload_codec)
+ break ;
+ while (!ci->audiobuffer_insert(pcmbuf, n))
+ rb->yield();
+
+#if BYTE_ORDER == BIG_ENDIAN
+ for (i=0;i<n;i+=2) {
+ x=pcmbuf[i]; pcmbuf[i]=pcmbuf[i+1]; pcmbuf[i+1]=x;
+ }
+#endif
+ }
+ }
+
+ if (ci->request_next_track())
+ goto next_track;
+
+ return PLUGIN_OK;
+}
+
diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES
index 0e8e14cbdf..58356af1ec 100644
--- a/apps/plugins/lib/SOURCES
+++ b/apps/plugins/lib/SOURCES
@@ -35,4 +35,7 @@ playergfx.c
#endif
#if CONFIG_HWCODEC == MASNONE /* software codec platforms */
xxx2wav.c
+#ifdef IRIVER_H100
+codeclib.c
+#endif
#endif
diff --git a/apps/plugins/lib/codeclib.c b/apps/plugins/lib/codeclib.c
new file mode 100644
index 0000000000..d9866ef98d
--- /dev/null
+++ b/apps/plugins/lib/codeclib.c
@@ -0,0 +1,36 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Dave Chapman
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* Various "helper functions" common to all the xxx2wav decoder plugins */
+
+#include "plugin.h"
+#include "playback.h"
+#include "codeclib.h"
+
+struct plugin_api* local_rb;
+
+int codec_init(struct plugin_api* rb, struct codec_api* ci) {
+ local_rb = rb;
+
+ xxx2wav_set_api(rb);
+ mem_ptr = 0;
+ mallocbuf = (unsigned char *)ci->get_codec_memory((size_t *)&bufsize);
+
+ return 0;
+}
diff --git a/apps/plugins/lib/codeclib.h b/apps/plugins/lib/codeclib.h
new file mode 100644
index 0000000000..876e69b57e
--- /dev/null
+++ b/apps/plugins/lib/codeclib.h
@@ -0,0 +1,46 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2005 Dave Chapman
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+/* Various "helper functions" common to all the xxx2wav decoder plugins */
+
+#if CONFIG_CPU == MCF5249 && !defined(SIMULATOR)
+#define ICODE_ATTR __attribute__ ((section(".icode")))
+#define IDATA_ATTR __attribute__ ((section(".idata")))
+#define USE_IRAM 1
+#else
+#define ICODE_ATTR
+#define IDATA_ATTR
+#endif
+
+extern int mem_ptr;
+extern int bufsize;
+extern unsigned char* mallocbuf; // 512K from the start of MP3 buffer
+
+void* codec_malloc(size_t size);
+void* codec_calloc(size_t nmemb, size_t size);
+void* codec_alloca(size_t size);
+void* codec_realloc(void* ptr, size_t size);
+void codec_free(void* ptr);
+void *memcpy(void *dest, const void *src, size_t n);
+void *memset(void *s, int c, size_t n);
+int memcmp(const void *s1, const void *s2, size_t n);
+void* memmove(const void *s1, const void *s2, size_t n);
+
+int codec_init(struct plugin_api* rb, struct codec_api* ci);
+
diff --git a/apps/plugins/lib/xxx2wav.c b/apps/plugins/lib/xxx2wav.c
index d864527fc2..ed67184a05 100644
--- a/apps/plugins/lib/xxx2wav.c
+++ b/apps/plugins/lib/xxx2wav.c
@@ -40,14 +40,14 @@ void* codec_malloc(size_t size) {
x=&mallocbuf[mem_ptr];
mem_ptr+=(size+3)&~3; // Keep memory 32-bit aligned (if it was already?)
-
+/*
if(TIME_AFTER(*(local_rb->current_tick), last_tick + HZ)) {
local_rb->snprintf(s,30,"Memory used: %d",mem_ptr);
local_rb->lcd_putsxy(0,80,s);
last_tick = *(local_rb->current_tick);
local_rb->lcd_update();
- }
+ }*/
return(x);
}
@@ -162,6 +162,11 @@ static unsigned char wav_header[44]={'R','I','F','F', // 0 - ChunkID
};
+void xxx2wav_set_api(struct plugin_api* rb)
+{
+ local_rb = rb;
+}
+
int local_init(char* infilename, char* outfilename, file_info_struct* file_info, struct plugin_api* rb) {
char s[32];
int i,n,bytesleft;
diff --git a/apps/plugins/lib/xxx2wav.h b/apps/plugins/lib/xxx2wav.h
index 7e3afceaba..7e1b942820 100644
--- a/apps/plugins/lib/xxx2wav.h
+++ b/apps/plugins/lib/xxx2wav.h
@@ -64,3 +64,4 @@ void* memmove(const void *s1, const void *s2, size_t n);
void display_status(file_info_struct* file_info);
int local_init(char* infilename, char* outfilename, file_info_struct* file_info, struct plugin_api* rb);
void close_wav(file_info_struct* file_info);
+void xxx2wav_set_api(struct plugin_api* rb);
diff --git a/apps/wps.c b/apps/wps.c
index b44ca53070..0ab2c7ce97 100644
--- a/apps/wps.c
+++ b/apps/wps.c
@@ -75,8 +75,8 @@ static char current_track_path[MAX_PATH+1];
#define WPS_PAUSE BUTTON_ON
#define WPS_MENU (BUTTON_MODE | BUTTON_REL)
#define WPS_MENU_PRE BUTTON_MODE
-#define WPS_BROWSE (BUTTON_ON | BUTTON_REL)
-#define WPS_BROWSE_PRE BUTTON_ON
+#define WPS_BROWSE (BUTTON_SELECT | BUTTON_REL)
+#define WPS_BROWSE_PRE BUTTON_SELECT
#define WPS_EXIT BUTTON_OFF
#define WPS_KEYLOCK (BUTTON_MODE | BUTTON_DOWN)
#define WPS_ID3 (BUTTON_MODE | BUTTON_ON)
diff --git a/docs/CREDITS b/docs/CREDITS
index c32fed3f0c..105c506171 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -114,3 +114,4 @@ Luca Burelli
Alessio Lenzi
David Bryant
Martin Arver
+Miikka Pekkarinen
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h
index 23ec1feee9..f83443b3e9 100644
--- a/firmware/export/pcm_playback.h
+++ b/firmware/export/pcm_playback.h
@@ -19,6 +19,8 @@
#ifndef PCM_PLAYBACK_H
#define PCM_PLAYBACK_H
+#define PCMBUF_SIZE (1*1024*1024)
+
void pcm_init(void);
void pcm_set_frequency(unsigned int frequency);
@@ -38,4 +40,8 @@ bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void));
int pcm_play_num_used_buffers(void);
void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left));
+void pcm_set_boost_mode(bool state);
+unsigned int audiobuffer_get_latency(void);
+bool audiobuffer_insert(char *buf, size_t length);
+
#endif
diff --git a/firmware/mpeg.c b/firmware/mpeg.c
index 211f79201d..e4ea8d67e7 100644
--- a/firmware/mpeg.c
+++ b/firmware/mpeg.c
@@ -1928,6 +1928,7 @@ void mpeg_id3_options(bool _v1first)
v1first = _v1first;
}
+#ifndef IRIVER_H100
struct mp3entry* audio_current_track()
{
#ifdef SIMULATOR
@@ -1961,6 +1962,7 @@ bool audio_has_changed_track(void)
}
return false;
}
+#endif
#if CONFIG_HWCODEC == MAS3587F
void audio_init_playback(void)
@@ -2487,6 +2489,7 @@ void mpeg_set_recording_options(int frequency, int quality,
}
#endif
+#ifndef IRIVER_H100
void audio_play(int offset)
{
#ifdef SIMULATOR
@@ -2713,3 +2716,5 @@ void audio_init(void)
dbg_cnt2us(0);
#endif /* DEBUG */
}
+
+#endif /* IRIVER_H100 */
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c
index f5fc5e7391..318cfd4aa0 100644
--- a/firmware/pcm_playback.c
+++ b/firmware/pcm_playback.c
@@ -21,7 +21,6 @@
#include "debug.h"
#include "panic.h"
#include <kernel.h>
-#include "pcm_playback.h"
#ifndef SIMULATOR
#include "cpu.h"
#include "i2c.h"
@@ -32,19 +31,47 @@
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
+#include "pcm_playback.h"
#include "lcd.h"
#include "button.h"
#include "file.h"
#include "buffer.h"
-
#include "sprintf.h"
#include "button.h"
#include <string.h>
+#define CHUNK_SIZE 32768
+/* Must be a power of 2 */
+#define NUM_PCM_BUFFERS (PCMBUF_SIZE / CHUNK_SIZE)
+#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
+#define PCM_WATERMARK 0x10000
+
static bool pcm_playing;
static bool pcm_paused;
static int pcm_freq = 0x6; /* 44.1 is default */
+static char *audiobuffer;
+static size_t audiobuffer_pos;
+static volatile size_t audiobuffer_free;
+static size_t audiobuffer_fillpos;
+static bool boost_mode;
+
+static unsigned char *next_start;
+static long next_size;
+
+struct pcmbufdesc
+{
+ void *addr;
+ int size;
+ void (*callback)(void); /* Call this when the buffer has been played */
+} pcmbuffers[NUM_PCM_BUFFERS];
+
+volatile int pcmbuf_read_index;
+volatile int pcmbuf_write_index;
+int pcmbuf_unplayed_bytes;
+int pcmbuf_watermark;
+void (*pcmbuf_watermark_callback)(int bytes_left);
+
/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
static void dma_start(const void *addr, long size)
{
@@ -75,6 +102,15 @@ static void dma_stop(void)
IIS2CONFIG = 0x800;
}
+void pcm_boost(bool state)
+{
+ static bool boost_state = false;
+
+ if (state != boost_state) {
+ cpu_boost(state);
+ boost_state = state;
+ }
+}
/* set volume of the main channel */
void pcm_set_volume(int volume)
@@ -113,22 +149,89 @@ void pcm_set_frequency(unsigned int frequency)
/* the registered callback function to ask for more mp3 data */
static void (*callback_for_more)(unsigned char**, long*) = NULL;
+int pcm_play_num_used_buffers(void)
+{
+ return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
+}
+
+static int last_chunksize = 0;
+
+static void pcm_play_callback(unsigned char** start, long* size)
+{
+ struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
+ int sz;
+
+ pcmbuf_unplayed_bytes -= last_chunksize;
+ audiobuffer_free += last_chunksize;
+
+
+ if(desc->size == 0)
+ {
+ /* The buffer is finished, call the callback function */
+ if(desc->callback)
+ desc->callback();
+
+ /* Advance to the next buffer */
+ pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK;
+ desc = &pcmbuffers[pcmbuf_read_index];
+ }
+
+ if(pcm_play_num_used_buffers())
+ {
+ /* Play max 64K at a time */
+ sz = MIN(desc->size, 32768);
+ *start = desc->addr;
+ *size = sz;
+
+ /* Update the buffer descriptor */
+ desc->size -= sz;
+ desc->addr += sz;
+
+ last_chunksize = sz;
+ }
+ else
+ {
+ /* No more buffers */
+ *size = 0;
+ }
+#if 1
+ if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
+ {
+ if(pcmbuf_watermark_callback)
+ {
+ pcmbuf_watermark_callback(pcmbuf_unplayed_bytes);
+ }
+ }
+#endif
+}
+
void pcm_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, long* size))
{
callback_for_more = get_more;
-
dma_start(start, size);
+
+ if (get_more == pcm_play_callback)
+ get_more(&next_start, &next_size);
}
void pcm_play_stop(void)
{
dma_stop();
+ audiobuffer_pos = 0;
+ audiobuffer_fillpos = 0;
+ audiobuffer_free = PCMBUF_SIZE;
+ pcmbuf_read_index = 0;
+ pcmbuf_write_index = 0;
+ pcmbuf_unplayed_bytes = 0;
+ next_start = NULL;
+ next_size = 0;
+ pcm_boost(false);
}
void pcm_play_pause(bool play)
{
- if(pcm_paused && play)
+ if(pcm_paused && play && pcmbuf_unplayed_bytes)
{
/* Enable the FIFO and force one write to it */
IIS2CONFIG = (pcm_freq << 12) | 0x300;
@@ -152,10 +255,8 @@ bool pcm_is_playing(void)
void DMA0(void) __attribute__ ((interrupt_handler, section(".icode")));
void DMA0(void)
{
- unsigned char* start;
- long size = 0;
-
int res = DSR0;
+ bool rockboy = callback_for_more != pcm_play_callback;
DSR0 = 1; /* Clear interrupt */
@@ -166,15 +267,14 @@ void DMA0(void)
}
else
{
- if (callback_for_more)
+ if (callback_for_more && rockboy)
+ callback_for_more(&next_start, &next_size);
+ if(next_size)
{
- callback_for_more(&start, &size);
- }
-
- if(size)
- {
- SAR0 = (unsigned long)start; /* Source address */
- BCR0 = size; /* Bytes to transfer */
+ SAR0 = (unsigned long)next_start; /* Source address */
+ BCR0 = next_size; /* Bytes to transfer */
+ if (callback_for_more && !rockboy)
+ callback_for_more(&next_start, &next_size);
}
else
{
@@ -204,32 +304,11 @@ void pcm_init(void)
/* Enable interrupt at level 7, priority 0 */
ICR4 = (ICR4 & 0xffff00ff) | 0x00001c00;
IMR &= ~(1<<14); /* bit 14 is DMA0 */
-
+
+ pcm_play_init();
pcm_set_frequency(44100);
}
-
-#define NUM_PCM_BUFFERS 16 /* Must be a power of 2 */
-#define NUM_PCM_BUFFERS_MASK (NUM_PCM_BUFFERS - 1)
-
-struct pcmbufdesc
-{
- void *addr;
- int size;
- void (*callback)(void); /* Call this when the buffer has been played */
-} pcmbuffers[NUM_PCM_BUFFERS];
-
-int pcmbuf_read_index;
-int pcmbuf_write_index;
-int pcmbuf_unplayed_bytes;
-int pcmbuf_watermark;
-void (*pcmbuf_watermark_callback)(int bytes_left);
-
-int pcm_play_num_used_buffers(void)
-{
- return (pcmbuf_write_index - pcmbuf_read_index) & NUM_PCM_BUFFERS_MASK;
-}
-
void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left))
{
pcmbuf_watermark = numbytes;
@@ -240,7 +319,7 @@ bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void))
{
/* We don't use the last buffer, since we can't see the difference
between the full and empty condition */
- if(pcm_play_num_used_buffers() < (NUM_PCM_BUFFERS - 1))
+ if(pcm_play_num_used_buffers() < (NUM_PCM_BUFFERS - 2))
{
pcmbuffers[pcmbuf_write_index].addr = addr;
pcmbuffers[pcmbuf_write_index].size = size;
@@ -253,61 +332,111 @@ bool pcm_play_add_chunk(void *addr, int size, void (*callback)(void))
return false;
}
-void pcm_play_init(void)
+void pcm_watermark_callback(int bytes_left)
{
- pcmbuf_read_index = 0;
- pcmbuf_write_index = 0;
- pcmbuf_unplayed_bytes = 0;
- pcm_play_set_watermark(0x10000, NULL);
+ (void)bytes_left;
+
+ /* Fill audio buffer by boosting cpu */
+ pcm_boost(true);
}
-static int last_chunksize = 0;
+void pcm_set_boost_mode(bool state)
+{
+ boost_mode = state;
+ if (boost_mode)
+ pcm_boost(true);
+}
-static void pcm_play_callback(unsigned char** start, long* size)
+void audiobuffer_add_event(void (*event_handler)(void))
{
- struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index];
- int sz;
+ while (!pcm_play_add_chunk(NULL, 0, event_handler))
+ yield();
+}
- pcmbuf_unplayed_bytes -= last_chunksize;
+unsigned int audiobuffer_get_latency(void)
+{
+ int latency;
- if(desc->size == 0)
- {
- /* The buffer is finished, call the callback function */
- if(desc->callback)
- desc->callback();
-
- /* Advance to the next buffer */
- pcmbuf_read_index = (pcmbuf_read_index + 1) & NUM_PCM_BUFFERS_MASK;
- desc = &pcmbuffers[pcmbuf_read_index];
- }
+ /* This has to be done better. */
+ latency = (PCMBUF_SIZE - audiobuffer_free - audiobuffer_fillpos
+ - CHUNK_SIZE)/4 / (44100/1000);
+ if (latency < 0)
+ latency = 0;
- if(pcm_play_num_used_buffers())
- {
- /* Play max 64K at a time */
- sz = MIN(desc->size, 32768);
- *start = desc->addr;
- *size = sz;
-
- /* Update the buffer descriptor */
- desc->size -= sz;
- desc->addr += sz;
+ return latency;
+}
- last_chunksize = sz;
+bool audiobuffer_insert(char *buf, size_t length)
+{
+ size_t copy_n = 0;
+
+ if (audiobuffer_free < length + CHUNK_SIZE) {
+ if (!boost_mode)
+ pcm_boost(false);
+ return false;
}
- else
- {
- /* No more buffers */
- *size = 0;
+
+ if (!pcm_is_playing()) {
+ pcm_boost(true);
+ if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*2)
+ pcm_play_start();
}
-#if 0
- if(pcmbuf_unplayed_bytes <= pcmbuf_watermark)
- {
- if(pcmbuf_watermark_callback)
- {
- pcmbuf_watermark_callback(pcmbuf_unplayed_bytes);
+
+ while (length > 0) {
+ copy_n = MIN(length, PCMBUF_SIZE - audiobuffer_pos -
+ audiobuffer_fillpos);
+ copy_n = MIN(CHUNK_SIZE, copy_n);
+ memcpy(&audiobuffer[audiobuffer_pos+audiobuffer_fillpos],
+ buf, copy_n);
+ buf += copy_n;
+ audiobuffer_free -= copy_n;
+ length -= copy_n;
+
+ /* Pre-buffer to meet CHUNK_SIZE requirement */
+ if (copy_n + audiobuffer_fillpos < CHUNK_SIZE && length == 0) {
+ audiobuffer_fillpos += copy_n;
+ return true;
+ }
+
+ copy_n += audiobuffer_fillpos;
+
+ while (!pcm_play_add_chunk(&audiobuffer[audiobuffer_pos],
+ copy_n, NULL)) {
+ if (!boost_mode)
+ pcm_boost(false);
+ yield();
+ }
+
+ audiobuffer_pos += copy_n;
+ audiobuffer_fillpos = 0;
+
+ if (audiobuffer_pos >= PCMBUF_SIZE) {
+ audiobuffer_pos = 0;
}
}
-#endif
+
+ return true;
+}
+
+void pcm_play_init(void)
+{
+ audiobuffer = &audiobuf[(audiobufend - audiobuf) -
+ PCMBUF_SIZE];
+ audiobuffer_free = PCMBUF_SIZE;
+ audiobuffer_pos = 0;
+ audiobuffer_fillpos = 0;
+ boost_mode = 0;
+ pcmbuf_read_index = 0;
+ pcmbuf_write_index = 0;
+ pcmbuf_unplayed_bytes = 0;
+ pcm_play_set_watermark(PCM_WATERMARK, pcm_watermark_callback);
+
+ /* Play a small chunk of zeroes to initialize the playback system. */
+ audiobuffer_pos = 32000;
+ audiobuffer_free -= audiobuffer_pos;
+ memset(&audiobuffer[0], 0, audiobuffer_pos);
+ pcm_play_add_chunk(&audiobuffer[0], audiobuffer_pos, NULL);
+ pcm_play_start();
}
void pcm_play_start(void)
@@ -320,9 +449,9 @@ void pcm_play_start(void)
{
size = MIN(desc->size, 32768);
start = desc->addr;
- pcm_play_data(start, size, pcm_play_callback);
last_chunksize = size;
desc->size -= size;
desc->addr += size;
+ pcm_play_data(start, size, pcm_play_callback);
}
}
diff --git a/firmware/sound.c b/firmware/sound.c
index 0fb54861c9..25b5ba468c 100644
--- a/firmware/sound.c
+++ b/firmware/sound.c
@@ -458,6 +458,8 @@ void sound_set(int setting, int value)
#elif CONFIG_HWCODEC == MAS3507D
current_volume = -780 + (value * 960 / 100); /* tenth of dB */
set_prescaled_volume();
+#elif CONFIG_HWCODEC == MASNONE
+ pcm_set_volume((value*167117) >> 16);
#endif
break;
diff --git a/tools/buildzip.pl b/tools/buildzip.pl
index e3c013631b..b01a63daf4 100755
--- a/tools/buildzip.pl
+++ b/tools/buildzip.pl
@@ -47,7 +47,9 @@ sub buildzip {
mkdir ".rockbox", 0777;
mkdir ".rockbox/langs", 0777;
mkdir ".rockbox/rocks", 0777;
- `find . -name "*.rock" -o -name "*.ovl" ! -empty | xargs --replace=foo cp foo .rockbox/rocks/`;
+ mkdir ".rockbox/codecs", 0777;
+ `find . -name "codec*.rock" ! -empty | xargs --replace=foo cp foo .rockbox/codecs/`;
+ `find . -name "*.rock" -o -name "*.ovl" ! -empty ! -name "codec*.rock" | xargs --replace=foo cp foo .rockbox/rocks/`;
open VIEWERS, "$ROOT/apps/plugins/viewers.config" or
die "can't open viewers.config";