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:
parent
658c8451ea
commit
0ad617cbf0
22 changed files with 566 additions and 23 deletions
|
@ -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
251
apps/abrepeat.c
Normal 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
48
apps/abrepeat.h
Normal 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_ */
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 = ¤t_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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -58,6 +58,7 @@ enum icons_7x8 {
|
|||
Icon_Shuffle,
|
||||
Icon_DownArrow,
|
||||
Icon_UpArrow,
|
||||
Icon_RepeatAB,
|
||||
Icon_Last
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
74
apps/wps.c
74
apps/wps.c
|
@ -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 */
|
||||
|
|
12
apps/wps.h
12
apps/wps.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue