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 +#include +#include +#include + +#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 + +#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;imp3data->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 + +#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),¤t_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;irequest_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 -#include "pcm_playback.h" #ifndef SIMULATOR #include "cpu.h" #include "i2c.h" @@ -32,19 +31,47 @@ #include #include #include +#include "pcm_playback.h" #include "lcd.h" #include "button.h" #include "file.h" #include "buffer.h" - #include "sprintf.h" #include "button.h" #include +#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,154 +149,11 @@ 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; -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); -} - -void pcm_play_stop(void) -{ - dma_stop(); -} - -void pcm_play_pause(bool play) -{ - if(pcm_paused && play) - { - /* Enable the FIFO and force one write to it */ - IIS2CONFIG = (pcm_freq << 12) | 0x300; - DCR0 |= DMA_START; - - pcm_paused = false; - } - else if(!pcm_paused && !play) - { - IIS2CONFIG = 0x800; - pcm_paused = true; - } -} - -bool pcm_is_playing(void) -{ - return pcm_playing; -} - -/* DMA0 Interrupt is called when the DMA has finished transfering a chunk */ -void DMA0(void) __attribute__ ((interrupt_handler, section(".icode"))); -void DMA0(void) -{ - unsigned char* start; - long size = 0; - - int res = DSR0; - - DSR0 = 1; /* Clear interrupt */ - - /* Stop on error */ - if(res & 0x70) - { - dma_stop(); - } - else - { - if (callback_for_more) - { - callback_for_more(&start, &size); - } - - if(size) - { - SAR0 = (unsigned long)start; /* Source address */ - BCR0 = size; /* Bytes to transfer */ - } - else - { - /* Finished playing */ - dma_stop(); - } - } - - IPR |= (1<<14); /* Clear pending interrupt request */ -} - -void pcm_init(void) -{ - pcm_playing = false; - pcm_paused = false; - - uda1380_init(); - - BUSMASTER_CTRL = 0x81; /* PARK[1,0]=10 + BCR24BIT */ - DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */ - DMAROUTE = (DMAROUTE & 0xffffff00) | DMA0_REQ_AUDIO_1; - DMACONFIG = 1; /* DMA0Req = PDOR3 */ - - /* Reset the audio FIFO */ - IIS2CONFIG = 0x800; - - /* Enable interrupt at level 7, priority 0 */ - ICR4 = (ICR4 & 0xffff00ff) | 0x00001c00; - IMR &= ~(1<<14); /* bit 14 is DMA0 */ - - 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; - pcmbuf_watermark_callback = callback; -} - -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)) - { - pcmbuffers[pcmbuf_write_index].addr = addr; - pcmbuffers[pcmbuf_write_index].size = size; - pcmbuffers[pcmbuf_write_index].callback = callback; - pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK; - pcmbuf_unplayed_bytes += size; - return true; - } - else - return false; -} - -void pcm_play_init(void) -{ - pcmbuf_read_index = 0; - pcmbuf_write_index = 0; - pcmbuf_unplayed_bytes = 0; - pcm_play_set_watermark(0x10000, NULL); -} - static int last_chunksize = 0; static void pcm_play_callback(unsigned char** start, long* size) @@ -269,6 +162,8 @@ static void pcm_play_callback(unsigned char** start, long* size) int sz; pcmbuf_unplayed_bytes -= last_chunksize; + audiobuffer_free += last_chunksize; + if(desc->size == 0) { @@ -299,7 +194,7 @@ static void pcm_play_callback(unsigned char** start, long* size) /* No more buffers */ *size = 0; } -#if 0 +#if 1 if(pcmbuf_unplayed_bytes <= pcmbuf_watermark) { if(pcmbuf_watermark_callback) @@ -310,6 +205,240 @@ static void pcm_play_callback(unsigned char** start, long* size) #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 && pcmbuf_unplayed_bytes) + { + /* Enable the FIFO and force one write to it */ + IIS2CONFIG = (pcm_freq << 12) | 0x300; + DCR0 |= DMA_START; + + pcm_paused = false; + } + else if(!pcm_paused && !play) + { + IIS2CONFIG = 0x800; + pcm_paused = true; + } +} + +bool pcm_is_playing(void) +{ + return pcm_playing; +} + +/* DMA0 Interrupt is called when the DMA has finished transfering a chunk */ +void DMA0(void) __attribute__ ((interrupt_handler, section(".icode"))); +void DMA0(void) +{ + int res = DSR0; + bool rockboy = callback_for_more != pcm_play_callback; + + DSR0 = 1; /* Clear interrupt */ + + /* Stop on error */ + if(res & 0x70) + { + dma_stop(); + } + else + { + if (callback_for_more && rockboy) + callback_for_more(&next_start, &next_size); + if(next_size) + { + 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 + { + /* Finished playing */ + dma_stop(); + } + } + + IPR |= (1<<14); /* Clear pending interrupt request */ +} + +void pcm_init(void) +{ + pcm_playing = false; + pcm_paused = false; + + uda1380_init(); + + BUSMASTER_CTRL = 0x81; /* PARK[1,0]=10 + BCR24BIT */ + DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */ + DMAROUTE = (DMAROUTE & 0xffffff00) | DMA0_REQ_AUDIO_1; + DMACONFIG = 1; /* DMA0Req = PDOR3 */ + + /* Reset the audio FIFO */ + IIS2CONFIG = 0x800; + + /* 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); +} + +void pcm_play_set_watermark(int numbytes, void (*callback)(int bytes_left)) +{ + pcmbuf_watermark = numbytes; + pcmbuf_watermark_callback = callback; +} + +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 - 2)) + { + pcmbuffers[pcmbuf_write_index].addr = addr; + pcmbuffers[pcmbuf_write_index].size = size; + pcmbuffers[pcmbuf_write_index].callback = callback; + pcmbuf_write_index = (pcmbuf_write_index+1) & NUM_PCM_BUFFERS_MASK; + pcmbuf_unplayed_bytes += size; + return true; + } + else + return false; +} + +void pcm_watermark_callback(int bytes_left) +{ + (void)bytes_left; + + /* Fill audio buffer by boosting cpu */ + pcm_boost(true); +} + +void pcm_set_boost_mode(bool state) +{ + boost_mode = state; + if (boost_mode) + pcm_boost(true); +} + +void audiobuffer_add_event(void (*event_handler)(void)) +{ + while (!pcm_play_add_chunk(NULL, 0, event_handler)) + yield(); +} + +unsigned int audiobuffer_get_latency(void) +{ + int latency; + + /* This has to be done better. */ + latency = (PCMBUF_SIZE - audiobuffer_free - audiobuffer_fillpos + - CHUNK_SIZE)/4 / (44100/1000); + if (latency < 0) + latency = 0; + + return latency; +} + +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; + } + + if (!pcm_is_playing()) { + pcm_boost(true); + if (audiobuffer_free < PCMBUF_SIZE - CHUNK_SIZE*2) + pcm_play_start(); + } + + 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; + } + } + + 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) { struct pcmbufdesc *desc = &pcmbuffers[pcmbuf_read_index]; @@ -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";