Patch #1105616 by Ray Lambert - A-B Repeat for Archos studio/recorder, still not 100% complete, but I wanted to commit it before the 2.5 feature freeze

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@7380 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Linus Nielsen Feltzing 2005-08-21 23:01:12 +00:00
parent 658c8451ea
commit 0ad617cbf0
22 changed files with 566 additions and 23 deletions

View File

@ -2,6 +2,7 @@
logfdisp.c
#endif
alarm_menu.c
abrepeat.c
bookmark.c
credits.c
debug_menu.c

251
apps/abrepeat.c Normal file
View File

@ -0,0 +1,251 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $$
*
* Copyright (C) 2005 Ray Lambert
*
* 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 "abrepeat.h"
#include "settings.h"
#include "audio.h"
#include "kernel.h"
#ifdef HAVE_LCD_BITMAP
#include "lcd.h"
#endif
#ifdef AB_REPEAT_ENABLE
static int ab_audio_event_handler(unsigned short event, unsigned long data)
{
int rc = AUDIO_EVENT_RC_IGNORED;
if ( ab_repeat_mode_enabled() )
{
switch(event)
{
case AUDIO_EVENT_POS_REPORT:
{
if ( ! (audio_status() & AUDIO_STATUS_PAUSE) && ab_reached_B_marker(data) )
{
ab_jump_to_A_marker();
rc = AUDIO_EVENT_RC_HANDLED;
}
break;
}
case AUDIO_EVENT_END_OF_TRACK:
{
if ( ab_A_marker_set() && ! ab_B_marker_set() )
{
ab_jump_to_A_marker();
rc = AUDIO_EVENT_RC_HANDLED;
}
break;
}
}
}
return rc;
}
void ab_repeat_init(void)
{
static bool ab_initialized = false;
if ( ! ab_initialized )
{
ab_initialized = true;
audio_register_event_handler(ab_audio_event_handler,
AUDIO_EVENT_POS_REPORT | AUDIO_EVENT_END_OF_TRACK );
}
}
static unsigned int ab_A_marker = AB_MARKER_NONE;
static unsigned int ab_B_marker = AB_MARKER_NONE;
bool ab_repeat_mode_enabled(void)
{
extern struct user_settings global_settings;
return global_settings.repeat_mode == REPEAT_AB;
}
bool ab_A_marker_set(void)
{
return ab_A_marker != AB_MARKER_NONE;
}
bool ab_B_marker_set(void)
{
return ab_B_marker != AB_MARKER_NONE;
}
unsigned int ab_get_A_marker(void)
{
return ab_A_marker;
}
unsigned int ab_get_B_marker(void)
{
return ab_B_marker;
}
bool ab_reached_B_marker(unsigned int song_position)
{
/* following is the size of the window in which we'll detect that the B marker
was hit; it must be larger than the frequency (in milliseconds) at which this
function is called otherwise detection of the B marker will be unreliable;
we assume that this function will be called on each system tick and derive
the window size from this with a generous margin of error (note: the number
of ticks per second is given by HZ) */
#define B_MARKER_DETECT_WINDOW ((1000/HZ)*10)
if (ab_B_marker != AB_MARKER_NONE)
{
if ( (song_position >= ab_B_marker)
&& (song_position <= (ab_B_marker+B_MARKER_DETECT_WINDOW)) )
return true;
}
return false;
}
/* determines if the given song position is earlier than the A mark;
intended for use in handling the jump NEXT and PREV commands */
bool ab_before_A_marker(unsigned int song_position)
{
return (ab_A_marker != AB_MARKER_NONE)
&& (song_position < ab_A_marker);
}
/* determines if the given song position is later than the A mark;
intended for use in handling the jump PREV command */
bool ab_after_A_marker(unsigned int song_position)
{
/* following is the size of the virtual A marker; we pretend that the A marker is
larger than a single instant in order to give the user time to hit PREV again to
jump back to the start of the song; it should be large enough to allow a
reasonable amount of time for the typical user to react */
#define A_MARKER_VIRTUAL_SIZE 1000
return (ab_A_marker != AB_MARKER_NONE)
&& (song_position > (ab_A_marker+A_MARKER_VIRTUAL_SIZE));
}
void ab_jump_to_A_marker(void)
{
bool paused = (audio_status() & AUDIO_STATUS_PAUSE) != 0;
if ( ! paused )
audio_pause();
audio_ff_rewind(ab_A_marker);
if ( ! paused )
audio_resume();
}
void ab_reset_markers(void)
{
ab_A_marker = AB_MARKER_NONE;
ab_B_marker = AB_MARKER_NONE;
}
/* following is a fudge factor to help overcome the latency between
the time the user hears the passage they want to mark and the time
they actually press the button; the actual song position is adjusted
by this fudge factor when setting a mark */
#define EAR_TO_HAND_LATENCY_FUDGE 200
void ab_set_A_marker(unsigned int song_position)
{
ab_A_marker = song_position;
ab_A_marker = (ab_A_marker >= EAR_TO_HAND_LATENCY_FUDGE)
? (ab_A_marker - EAR_TO_HAND_LATENCY_FUDGE) : 0;
/* check if markers are out of order */
if ( (ab_B_marker != AB_MARKER_NONE) && (ab_A_marker > ab_B_marker) )
ab_B_marker = AB_MARKER_NONE;
}
void ab_set_B_marker(unsigned int song_position)
{
ab_B_marker = song_position;
ab_B_marker = (ab_B_marker >= EAR_TO_HAND_LATENCY_FUDGE)
? (ab_B_marker - EAR_TO_HAND_LATENCY_FUDGE) : 0;
/* check if markers are out of order */
if ( (ab_A_marker != AB_MARKER_NONE) && (ab_B_marker < ab_A_marker) )
ab_A_marker = AB_MARKER_NONE;
}
#ifdef HAVE_LCD_BITMAP
static int ab_calc_mark_x_pos(int mark, int capacity, int offset, int size)
{
int w = size - offset;
return offset + ( (w * mark) / capacity );
}
static void ab_draw_veritcal_line_mark(int x, int y, int h)
{
lcd_set_drawmode(DRMODE_COMPLEMENT);
lcd_vline(x, y, y+h-1);
}
#define DIRECTION_RIGHT 1
#define DIRECTION_LEFT -1
static void ab_draw_arrow_mark(int x, int y, int h, int direction)
{
/* draw lines in decreasing size until a height of zero is reached */
lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
while( h > 0 )
{
lcd_vline(x, y, y+h-1);
h -= 2;
y++;
x += direction;
lcd_set_drawmode(DRMODE_COMPLEMENT);
}
}
void ab_draw_markers(int capacity, int x, int y, int w, int h)
{
/* if both markers are set, determine if they're far enough apart
to draw arrows */
if ( ab_A_marker_set() && ab_B_marker_set() )
{
int xa = ab_calc_mark_x_pos(ab_A_marker, capacity, x, w);
int xb = ab_calc_mark_x_pos(ab_B_marker, capacity, x, w);
int arrow_width = (h+1) / 2;
if ( (xb-xa) < (arrow_width*2) )
{
ab_draw_veritcal_line_mark(xa, y, h);
ab_draw_veritcal_line_mark(xb, y, h);
}
else
{
ab_draw_arrow_mark(xa, y, h, DIRECTION_RIGHT);
ab_draw_arrow_mark(xb, y, h, DIRECTION_LEFT);
}
}
else
{
if (ab_A_marker_set())
{
int xa = ab_calc_mark_x_pos(ab_A_marker, capacity, x, w);
ab_draw_arrow_mark(xa, y, h, DIRECTION_RIGHT);
}
if (ab_B_marker_set())
{
int xb = ab_calc_mark_x_pos(ab_B_marker, capacity, x, w);
ab_draw_arrow_mark(xb, y, h, DIRECTION_LEFT);
}
}
}
#endif /* HAVE_LCD_BITMAP */
#endif /* AB_REPEAT_ENABLE */

48
apps/abrepeat.h Normal file
View File

@ -0,0 +1,48 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $$
*
* Copyright (C) 2005 Ray Lambert
*
* 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 _ABREPEAT_H_
#define _ABREPEAT_H_
#include "system.h"
#include <stdbool.h>
#ifdef AB_REPEAT_ENABLE
#define AB_MARKER_NONE 0
void ab_repeat_init(void);
bool ab_repeat_mode_enabled(void); // test if a/b repeat is enabled
bool ab_A_marker_set(void);
bool ab_B_marker_set(void);
unsigned int ab_get_A_marker(void);
unsigned int ab_get_B_marker(void);
bool ab_reached_B_marker(unsigned int song_position);
bool ab_before_A_marker(unsigned int song_position);
bool ab_after_A_marker(unsigned int song_position);
void ab_jump_to_A_marker(void);
void ab_reset_markers(void);
void ab_set_A_marker(unsigned int song_position);
void ab_set_B_marker(unsigned int song_position);
#ifdef HAVE_LCD_BITMAP
void ab_draw_markers(int capacity, int x, int y, int w, int h);
#endif
#endif
#endif /* _ABREPEAT_H_ */

View File

@ -47,6 +47,7 @@
#include "sprintf.h"
#include "talk.h"
#include "misc.h"
#include "abrepeat.h"
#define MAX_BOOKMARKS 10
#define MAX_BOOKMARK_SIZE 350
@ -778,6 +779,12 @@ static void display_bookmark(const char* bookmark,
/* bookmark shuffle and repeat states*/
switch (repeat_mode)
{
#ifdef AB_REPEAT_ENABLE
case REPEAT_AB:
statusbar_icon_play_mode(Icon_RepeatAB);
break;
#endif
case REPEAT_ONE:
statusbar_icon_play_mode(Icon_RepeatOne);
break;

View File

@ -3286,3 +3286,9 @@ desc: in beep volume in playback settings
eng: "Strong"
voice "Strong"
new:
id: LANG_REPEAT_AB
desc: repeat one song
eng: "A-B"
voice: "A-B"
new:

View File

@ -85,6 +85,7 @@
#include "misc.h"
#include "button.h"
#include "filetree.h"
#include "abrepeat.h"
#ifdef HAVE_LCD_BITMAP
#include "icons.h"
#include "widgets.h"
@ -867,6 +868,9 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
}
case REPEAT_ONE:
#ifdef AB_REPEAT_ENABLE
case REPEAT_AB:
#endif
next_index = current_index;
break;
@ -1916,7 +1920,11 @@ int playlist_next(int steps)
struct playlist_info* playlist = &current_playlist;
int index;
if (steps > 0 && global_settings.repeat_mode != REPEAT_ONE)
if ( (steps > 0)
#ifdef AB_REPEAT_ENABLE
&& (global_settings.repeat_mode != REPEAT_AB)
#endif
&& (global_settings.repeat_mode != REPEAT_ONE) )
{
int i, j;

View File

@ -73,6 +73,7 @@ const unsigned char bitmap_icons_7x8[][7] =
{0x3e,0x41,0x51,0x41,0x45,0x41,0x3e}, /* Shuffle playmode (dice) */
{0x04,0x0c,0x1c,0x3c,0x1c,0x0c,0x04}, /* Down-arrow */
{0x20,0x30,0x38,0x3c,0x38,0x30,0x20}, /* Up-arrow */
{0x7f,0x04,0x4e,0x5f,0x44,0x38,0x7f}, /* Repeat-AB playmode */
};
#if CONFIG_LED == LED_VIRTUAL

View File

@ -58,6 +58,7 @@ enum icons_7x8 {
Icon_Shuffle,
Icon_DownArrow,
Icon_UpArrow,
Icon_RepeatAB,
Icon_Last
};

View File

@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 Björn Stenberg
* Copyright (C) 2002 Bj<EFBFBD>n 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.
@ -45,6 +45,7 @@
#include "debug.h"
#include "led.h"
#include "sound.h"
#include "abrepeat.h"
#include "wps-display.h"
#if defined(HAVE_LCD_BITMAP)
#include "widgets.h"
@ -593,6 +594,12 @@ bool quick_screen(int context, int button)
case REPEAT_SHUFFLE:
ptr = str(LANG_SHUFFLE);
break;
#ifdef AB_REPEAT_ENABLE
case REPEAT_AB:
ptr = str(LANG_REPEAT_AB);
break;
#endif
}
lcd_getstringsize(str(LANG_REPEAT),&w,&h);

View File

@ -78,7 +78,7 @@ const char rec_base_directory[] = REC_BASE_DIR;
#include "pcm_playback.h"
#endif
#define CONFIG_BLOCK_VERSION 24
#define CONFIG_BLOCK_VERSION 25
#define CONFIG_BLOCK_SIZE 512
#define RTC_BLOCK_SIZE 44
@ -205,7 +205,7 @@ static const struct bit_entry rtc_bits[] =
{16 | SIGNED, S_O(resume_first_index), 0, NULL, NULL },
{32 | SIGNED, S_O(resume_offset), -1, NULL, NULL },
{32 | SIGNED, S_O(resume_seed), -1, NULL, NULL },
{2, S_O(repeat_mode), REPEAT_ALL, "repeat", "off,all,one,shuffle" },
{3, S_O(repeat_mode), REPEAT_ALL, "repeat", "off,all,one,shuffle,ab" },
/* LCD */
{6, S_O(contrast), 40, "contrast", NULL },
#ifdef CONFIG_BACKLIGHT

View File

@ -24,6 +24,7 @@
#include "config.h"
#include "file.h"
#include "timefuncs.h"
#include "abrepeat.h"
#define ROCKBOX_DIR "/.rockbox"
#define FONT_DIR "/fonts"
@ -229,7 +230,7 @@ struct user_settings
/* misc options */
int repeat_mode; /* 0=off 1=repeat all 2=repeat one 3=shuffle */
int repeat_mode; /* 0=off 1=repeat all 2=repeat one 3=shuffle 4=ab */
int dirfilter; /* 0=display all, 1=only supported, 2=only music,
3=dirs+playlists, 4=ID3 database */
bool sort_case; /* dir sort order: 0=case insensitive, 1=sensitive */
@ -403,8 +404,17 @@ extern const char rec_base_directory[];
#define SETTINGS_ALL 3 /* both */
/* repeat mode options */
enum { REPEAT_OFF, REPEAT_ALL, REPEAT_ONE, REPEAT_SHUFFLE,
NUM_REPEAT_MODES };
enum
{
REPEAT_OFF,
REPEAT_ALL,
REPEAT_ONE,
REPEAT_SHUFFLE,
#ifdef AB_REPEAT_ENABLE
REPEAT_AB,
#endif
NUM_REPEAT_MODES
};
/* dir filter options */
/* Note: Any new filter modes need to be added before NUM_FILTER_MODES.

View File

@ -46,6 +46,7 @@
#include "talk.h"
#include "timefuncs.h"
#include "misc.h"
#include "abrepeat.h"
#include "power.h"
#include "database.h"
@ -604,11 +605,14 @@ static bool repeat_mode(void)
{ STR(LANG_REPEAT_ALL) },
{ STR(LANG_REPEAT_ONE) },
{ STR(LANG_SHUFFLE) },
#ifdef AB_REPEAT_ENABLE
{ STR(LANG_REPEAT_AB) }
#endif
};
int old_repeat = global_settings.repeat_mode;
result = set_option( str(LANG_REPEAT), &global_settings.repeat_mode,
INT, names, 4, NULL );
INT, names, NUM_REPEAT_MODES, NULL );
if (old_repeat != global_settings.repeat_mode &&
(audio_status() & AUDIO_STATUS_PLAY))

View File

@ -27,6 +27,7 @@
#include "mp3_playback.h"
#include "audio.h"
#include "wps.h"
#include "abrepeat.h"
#ifdef HAVE_RTC
#include "timefuncs.h"
#endif
@ -262,6 +263,12 @@ void status_draw(bool force_redraw)
info.redraw_volume = statusbar_icon_volume(info.volume);
statusbar_icon_play_state(current_playmode() + Icon_Play);
switch (info.repeat) {
#ifdef AB_REPEAT_ENABLE
case REPEAT_AB:
statusbar_icon_play_mode(Icon_RepeatAB);
break;
#endif
case REPEAT_ONE:
statusbar_icon_play_mode(Icon_RepeatOne);
break;

View File

@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 Björn Stenberg
* Copyright (C) 2002 Bj<EFBFBD>n 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.
@ -44,6 +44,7 @@
#include "sprintf.h"
#include "backlight.h"
#include "button.h"
#include "abrepeat.h"
#ifdef HAVE_LCD_BITMAP
#include "icons.h"
@ -1214,11 +1215,18 @@ bool wps_refresh(struct mp3entry* id3,
#ifdef HAVE_LCD_BITMAP
/* progress */
if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
scrollbar(0, i*h + offset + (h > 7 ? (h - 6) / 2 : 1), LCD_WIDTH, 6,
id3->length?id3->length:1, 0,
id3->length?id3->elapsed + ff_rewind_count:0,
HORIZONTAL);
if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
{
#define PROGRESS_BAR_HEIGHT 6 /* this should probably be defined elsewhere; config-*.h perhaps? */
int sby = i*h + offset + (h > 7 ? (h - 6) / 2 : 1);
scrollbar(0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT,
id3->length?id3->length:1, 0,
id3->length?id3->elapsed + ff_rewind_count:0,
HORIZONTAL);
#ifdef AB_REPEAT_ENABLE
if ( ab_repeat_mode_enabled() )
ab_draw_markers(id3->length, 0, sby, LCD_WIDTH, PROGRESS_BAR_HEIGHT);
#endif
update_line = true;
}
if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {

View File

@ -51,6 +51,7 @@
#include "misc.h"
#include "sound.h"
#include "onplay.h"
#include "abrepeat.h"
#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
/* 3% of 30min file == 54s step size */
@ -347,6 +348,11 @@ long wps_show(void)
lcd_setmargins(0, 0);
#endif
#ifdef AB_REPEAT_ENABLE
ab_repeat_init();
ab_reset_markers();
#endif
ff_rewind = false;
if(audio_status() & AUDIO_STATUS_PLAY)
@ -585,6 +591,19 @@ long wps_show(void)
break;
#endif
#endif
#ifdef AB_REPEAT_ENABLE
/* if we're in A/B repeat mode and the current position
is past the A marker, jump back to the A marker... */
if ( ab_repeat_mode_enabled() && ab_after_A_marker(id3->elapsed) )
{
ab_jump_to_A_marker();
update_track = true;
break;
}
/* ...otherwise, do it normally */
#endif
if (!id3 || (id3->elapsed < 3*1000)) {
audio_prev();
}
@ -612,6 +631,19 @@ long wps_show(void)
break;
#endif
#endif
#ifdef AB_REPEAT_ENABLE
/* if we're in A/B repeat mode and the current position is
before the A marker, jump to the A marker... */
if ( ab_repeat_mode_enabled() && ab_before_A_marker(id3->elapsed) )
{
ab_jump_to_A_marker();
update_track = true;
break;
}
/* ...otherwise, do it normally */
#endif
audio_next();
break;
@ -674,7 +706,8 @@ long wps_show(void)
/* pitch screen */
#if CONFIG_KEYPAD == RECORDER_PAD
case BUTTON_ON | BUTTON_REPEAT:
case BUTTON_ON | BUTTON_UP:
case BUTTON_ON | BUTTON_DOWN:
if (2 == pitch_screen())
return SYS_USB_CONNECTED;
restore = true;
@ -682,6 +715,41 @@ long wps_show(void)
#endif
#endif
#ifdef AB_REPEAT_ENABLE
#ifdef WPS_AB_SET_A_MARKER
/* set A marker for A-B repeat */
case WPS_AB_SET_A_MARKER:
if (ab_repeat_mode_enabled())
ab_set_A_marker(id3->elapsed);
break;
#endif
#ifdef WPS_AB_SET_B_MARKER
/* set B marker for A-B repeat and jump to A */
case WPS_AB_SET_B_MARKER:
if (ab_repeat_mode_enabled())
{
ab_set_B_marker(id3->elapsed);
ab_jump_to_A_marker();
update_track = true;
}
break;
#endif
#ifdef WPS_AB_RESET_AB_MARKERS
/* reset A&B markers */
case WPS_AB_RESET_AB_MARKERS:
if (ab_repeat_mode_enabled())
{
ab_reset_markers();
update_track = true;
}
break;
#endif
#endif /* AB_REPEAT_ENABLE */
/* stop and exit wps */
#ifdef WPS_EXIT
case WPS_EXIT:
@ -706,6 +774,7 @@ long wps_show(void)
default:
if(default_event_handler(button) == SYS_USB_CONNECTED)
return SYS_USB_CONNECTED;
update_track = true;
break;
}
@ -734,6 +803,9 @@ long wps_show(void)
lcd_stop_scroll();
bookmark_autobookmark();
audio_stop();
#ifdef AB_REPEAT_ENABLE
ab_reset_markers();
#endif
/* Keys can be locked when exiting, so either unlock here
or implement key locking in tree.c too */

View File

@ -79,6 +79,12 @@
#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT)
#define WPS_QUICK BUTTON_F2
#ifdef AB_REPEAT_ENABLE
#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT)
#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT)
#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_OFF)
#endif
#define WPS_RC_NEXT BUTTON_RC_RIGHT
#define WPS_RC_PREV BUTTON_RC_LEFT
#define WPS_RC_PAUSE BUTTON_RC_PLAY
@ -106,6 +112,12 @@
#define WPS_ID3 (BUTTON_MENU | BUTTON_ON)
#define WPS_CONTEXT (BUTTON_PLAY | BUTTON_REPEAT)
#ifdef AB_REPEAT_ENABLE
#define WPS_AB_SET_A_MARKER (BUTTON_ON | BUTTON_LEFT)
#define WPS_AB_SET_B_MARKER (BUTTON_ON | BUTTON_RIGHT)
#define WPS_AB_RESET_AB_MARKERS (BUTTON_ON | BUTTON_STOP)
#endif
#define WPS_RC_NEXT BUTTON_RC_RIGHT
#define WPS_RC_PREV BUTTON_RC_LEFT
#define WPS_RC_PAUSE BUTTON_RC_PLAY

View File

@ -79,4 +79,44 @@ int audio_get_file_pos(void);
void audio_beep(int duration);
void audio_init_playback(void);
/***********************************************************************/
/* audio event handling */
/* subscribe to one or more audio event(s) by OR'ing together the desired */
/* event IDs (defined below); a handler is called with a solitary event ID */
/* (so switch() is okay) and possibly some useful data (depending on the */
/* event); a handler must return one of the return codes defined below */
typedef int (*AUDIO_EVENT_HANDLER)(unsigned short event, unsigned long data);
void audio_register_event_handler(AUDIO_EVENT_HANDLER handler, unsigned short mask);
/***********************************************************************/
/* handler return codes */
#define AUDIO_EVENT_RC_IGNORED 200
/* indicates that no action was taken or the event was not recognized */
#define AUDIO_EVENT_RC_HANDLED 201
/* indicates that the event was handled and some action was taken which renders
the original event invalid; USE WITH CARE!; this return code aborts all further
processing of the given event */
/***********************************************************************/
/* audio event IDs */
#define AUDIO_EVENT_POS_REPORT (1<<0)
/* sends a periodic song position report to handlers; a report is sent on
each kernal tick; the number of ticks per second is defined by HZ; on each
report the current song position is passed in 'data'; if a handler takes an
action that changes the song or the song position it must return
AUDIO_EVENT_RC_HANDLED which suppresses the event for any remaining handlers */
#define AUDIO_EVENT_END_OF_TRACK (1<<1)
/* generated when the end of the currently playing track is reached; no
data is passed; if the handler implements some alternate end-of-track
processing it should return AUDIO_EVENT_RC_HANDLED which suppresses the
event for any remaining handlers as well as the normal end-of-track
processing */
#endif

View File

@ -24,6 +24,8 @@
/* Define this if you have an FM Radio */
#define CONFIG_TUNER S1A0903X01
#define AB_REPEAT_ENABLE 1
#ifndef SIMULATOR
/* Define this if you have a MAS3587F */

View File

@ -12,6 +12,8 @@
/* The number of bytes reserved for loadable plugins */
#define PLUGIN_BUFFER_SIZE 0x8000
#define AB_REPEAT_ENABLE 1
#ifndef SIMULATOR
/* Define this if you have a SH7034 */

View File

@ -18,6 +18,8 @@
/* The number of bytes reserved for loadable plugins */
#define PLUGIN_BUFFER_SIZE 0x8000
#define AB_REPEAT_ENABLE 1
#ifndef SIMULATOR
/* Define this if you have a MAS3587F */

View File

@ -21,6 +21,8 @@
/* The number of bytes reserved for loadable plugins */
#define PLUGIN_BUFFER_SIZE 0x8000
#define AB_REPEAT_ENABLE 1
#ifndef SIMULATOR
/* Define this if you have a SH7034 */

View File

@ -280,6 +280,52 @@ static struct trackdata *get_trackdata(int offset)
}
#endif /* !SIMULATOR */
/***********************************************************************/
/* audio event handling */
#define MAX_EVENT_HANDLERS 10
struct event_handlers_table
{
AUDIO_EVENT_HANDLER handler;
unsigned short mask;
};
static struct event_handlers_table event_handlers[MAX_EVENT_HANDLERS];
static int event_handlers_count = 0;
void audio_register_event_handler(AUDIO_EVENT_HANDLER handler, unsigned short mask)
{
if (event_handlers_count < MAX_EVENT_HANDLERS)
{
event_handlers[event_handlers_count].handler = handler;
event_handlers[event_handlers_count].mask = mask;
event_handlers_count++;
}
}
/* dispatch calls each handler in the order registered and returns after some
handler actually handles the event (the event is assumed to no longer be valid
after this, due to the handler changing some condition); returns true if someone
handled the event, which is expected to cause the caller to skip its own handling
of the event */
#ifndef SIMULATOR
static bool audio_dispatch_event(unsigned short event, unsigned long data)
{
int i = 0;
for(i=0; i < event_handlers_count; i++)
{
if ( event_handlers[i].mask & event )
{
int rc = event_handlers[i].handler(event, data);
if ( rc == AUDIO_EVENT_RC_HANDLED )
return true;
}
}
return false;
}
#endif
/***********************************************************************/
static void set_elapsed(struct mp3entry* id3)
{
if ( id3->vbr ) {
@ -730,9 +776,10 @@ void rec_tick(void)
void playback_tick(void)
{
get_trackdata(0)->id3.elapsed +=
(current_tick - last_dma_tick) * 1000 / HZ;
struct trackdata *ptd = get_trackdata(0);
ptd->id3.elapsed += (current_tick - last_dma_tick) * 1000 / HZ;
last_dma_tick = current_tick;
audio_dispatch_event(AUDIO_EVENT_POS_REPORT, (unsigned long)ptd->id3.elapsed);
}
static void reset_mp3_buffer(void)
@ -762,8 +809,11 @@ static void transfer_end(unsigned char** ppbuf, int* psize)
{
if (audiobuf_read == get_trackdata(track_offset)->mempos)
{
queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0);
track_offset++;
if ( ! audio_dispatch_event(AUDIO_EVENT_END_OF_TRACK, 0) )
{
queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0);
track_offset++;
}
}
}
@ -823,10 +873,12 @@ static void transfer_end(unsigned char** ppbuf, int* psize)
}
else
{
DEBUGF("No more MP3 data. Stopping.\n");
queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0);
playing = false;
if ( ! audio_dispatch_event(AUDIO_EVENT_END_OF_TRACK, 0) )
{
DEBUGF("No more MP3 data. Stopping.\n");
queue_post(&mpeg_queue, MPEG_TRACK_CHANGE, 0);
playing = false;
}
}
*psize = 0; /* no more transfer */
}