Fixes FS#8651 (noise and/or crash while crossfading). Latest ARM-asm submit for dsp...
[maemo-rb.git] / apps / plugins / splitedit.c
blob7dd6be0408c1cb45d83228e44d48257ba5a596ac
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Philipp Pertermann
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
20 #include "plugin.h"
22 #ifndef SIMULATOR
23 #ifdef HAVE_LCD_BITMAP
25 PLUGIN_HEADER
27 /* variable button definitions */
28 #if CONFIG_KEYPAD == RECORDER_PAD
29 #define SPLITEDIT_QUIT BUTTON_OFF
30 #define SPLITEDIT_PLAY BUTTON_PLAY
31 #define SPLITEDIT_SAVE BUTTON_F1
32 #define SPLITEDIT_LOOP_MODE BUTTON_F2
33 #define SPLITEDIT_SCALE BUTTON_F3
34 #define SPLITEDIT_SPEED50 (BUTTON_ON | BUTTON_LEFT)
35 #define SPLITEDIT_SPEED100 (BUTTON_ON | BUTTON_PLAY)
36 #define SPLITEDIT_SPEED150 (BUTTON_ON | BUTTON_RIGHT)
37 #define SPLITEDIT_MENU_RUN BUTTON_PLAY
39 #elif CONFIG_KEYPAD == ONDIO_PAD
40 #define SPLITEDIT_QUIT BUTTON_OFF
41 #define SPLITEDIT_PLAY_PRE BUTTON_MENU
42 #define SPLITEDIT_PLAY (BUTTON_MENU | BUTTON_REL)
43 #define SPLITEDIT_SAVE (BUTTON_MENU | BUTTON_LEFT)
44 #define SPLITEDIT_LOOP_MODE (BUTTON_MENU | BUTTON_UP)
45 #define SPLITEDIT_SCALE (BUTTON_MENU | BUTTON_RIGHT)
46 #define SPLITEDIT_MENU_RUN BUTTON_RIGHT
48 #elif CONFIG_KEYPAD == IRIVER_H100_PAD
49 #define SPLITEDIT_QUIT BUTTON_OFF
50 #define SPLITEDIT_PLAY BUTTON_ON
51 #define SPLITEDIT_SAVE BUTTON_SELECT
52 #define SPLITEDIT_LOOP_MODE BUTTON_MODE
53 #define SPLITEDIT_SCALE (BUTTON_REC | BUTTON_UP)
54 #define SPLITEDIT_SPEED50 (BUTTON_REC | BUTTON_LEFT)
55 #define SPLITEDIT_SPEED100 (BUTTON_REC | BUTTON_DOWN)
56 #define SPLITEDIT_SPEED150 (BUTTON_REC | BUTTON_RIGHT)
57 #define SPLITEDIT_MENU_RUN BUTTON_RIGHT
59 #define SPLITEDIT_RC_QUIT BUTTON_RC_STOP
60 #endif
62 #define BMPHEIGHT 7
63 #define BMPWIDTH 13
64 unsigned char LOOP_BMP[][13] =
66 {0xfc,0x00,0x10,0x11,0x93,0x7f,0x13,0x11,0x7c,0x38,0x10,0x00,0x7c}, /*ALL */
67 {0x81,0x03,0x7f,0x03,0x91,0x10,0x10,0x10,0x7c,0x38,0x10,0x00,0x7c}, /*FROM*/
68 {0xfc,0x00,0x10,0x10,0x90,0x10,0x7c,0x38,0x11,0x03,0x7f,0x03,0x01}, /*TO */
69 {0x80,0x10,0x10,0x11,0x93,0x7f,0x13,0x11,0x10,0x7c,0x38,0x10,0x00}, /*FREE*/
72 unsigned char CUT_BMP[] =
74 0xc1,0x63,0x63,0x36,0xb6,0x1c,0x1c,0x36,0x77,0x55,0x55,0x55,0x32,
77 unsigned char SCALE_BMP[][13] =
79 {0x80,0x06,0x49,0x66,0xb0,0x18,0x0c,0x06,0x33,0x49,0x30,0x00,0x00}, /*lin*/
80 {0x80,0x30,0x78,0x48,0xff,0x7f,0x00,0x7f,0x7f,0x48,0x78,0x30,0x00}, /*db*/
83 #define TIMEBAR_Y 9
84 #define TIMEBAR_HEIGHT 4
86 #define OSCI_X 0
87 #define OSCI_Y (TIMEBAR_Y + TIMEBAR_HEIGHT + 1)
88 #define OSCI_WIDTH LCD_WIDTH
89 #define OSCI_HEIGHT (LCD_HEIGHT - BMPHEIGHT - OSCI_Y - 1)
91 /* Indices of the menu items in the save editor, see save_editor */
92 #define SE_PART1_SAVE 0
93 #define SE_PART1_NAME 1
94 #define SE_PART2_SAVE 2
95 #define SE_PART2_NAME 3
96 #define SE_SAVE 4
97 #define SE_COUNT 5
99 /* the global api pointer */
100 static struct plugin_api* rb;
102 /* contains the file name of the song that is to be split */
103 static char path_mp3[MAX_PATH];
105 /* Exit code of this plugin */
106 static enum plugin_status splitedit_exit_code = PLUGIN_OK;
108 /* The range in time that the displayed aerea comprises */
109 static unsigned int range_start = 0;
110 static unsigned int range_end = 0;
112 /* The range in time that is being looped */
113 static unsigned int play_start = 0;
114 static unsigned int play_end = 0;
116 /* Point in time (pixel) at which the split mark is set */
117 static int split_x = OSCI_X + (OSCI_WIDTH / 2);
119 /* Contains the peak values */
120 static unsigned char osci_buffer[OSCI_WIDTH];
122 /* if true peak values from a previous loop are only overwritten
123 if the new value is greater than the old value */
124 static bool osci_valid = false;
127 * point in time from which on the osci_buffer is invalid
128 * if set to ~(unsigned int)0 the entire osci_buffer is invalid
130 static unsigned int validation_start = ~(unsigned int)0;
132 /* all the visible aerea is looped */
133 #define LOOP_MODE_ALL 0
135 /* loop starts at split point, ends at right visible border */
136 #define LOOP_MODE_FROM 1
138 /* loop start at left visible border, ends at split point */
139 #define LOOP_MODE_TO 2
141 /* let the song play without looping */
142 #define LOOP_MODE_FREE 3
144 /* see LOOP_MODE_XXX constants vor valid values */
145 static int loop_mode = LOOP_MODE_FREE;
147 /* minimal allowed timespan (ms) of the visible (and looped) aerea*/
148 #define MIN_RANGE_SIZE 1000
150 /* Format time into buf.
152 * buf - buffer to format to.
153 * buf_size - size of buffer.
154 * time - time to format, in milliseconds.
156 static void format_time_ms(char* buf, int buf_size, int time)
158 rb->snprintf(buf, buf_size, "%d:%02d:%03d", time / 60000,
159 time % 60000 / 1000, (time % 60000) % 1000);
163 * converts screen coordinate (pixel) to time (ms)
165 static int xpos_to_time(int xpos)
167 int retval = 0;
168 int range = range_end - range_start;
169 retval = range_start + (((xpos - OSCI_X) * range) / OSCI_WIDTH);
170 return retval;
174 * Converts time (ms) to screen coordinates (pixel).
176 static int time_to_xpos(unsigned int time)
178 int retval = OSCI_X;
180 /* clip the range */
181 if (time < range_start)
183 retval = OSCI_X;
185 else
186 if (time >= range_end)
188 retval = OSCI_X + OSCI_WIDTH;
191 /* do the calculation */
192 else
194 int range = range_end - range_start;
195 retval = OSCI_X + ((time - range_start) * OSCI_WIDTH) / range ;
197 return retval;
201 * Updates the display of the textual data only.
203 static void update_data(void)
205 char buf[20];
206 char timebuf[10];
207 int w, h;
209 /* split point */
210 format_time_ms(timebuf, sizeof buf, xpos_to_time(split_x));
211 rb->snprintf(buf, sizeof buf, "Split at: %s", timebuf);
213 rb->lcd_getstringsize(buf, &w, &h);
215 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
216 rb->lcd_fillrect(0, 0, LCD_WIDTH, h);
217 rb->lcd_set_drawmode(DRMODE_SOLID);
218 rb->lcd_puts(0, 0, buf);
219 rb->lcd_update_rect(0, 0, LCD_WIDTH, h);
223 * Displays which part of the song is visible
224 * in the osci.
226 static void update_timebar(struct mp3entry *mp3)
228 rb->gui_scrollbar_draw
230 rb->screens[SCREEN_MAIN],0, TIMEBAR_Y, LCD_WIDTH, TIMEBAR_HEIGHT,
231 mp3->length, range_start, range_end,
232 HORIZONTAL
234 rb->lcd_update_rect(0, TIMEBAR_Y, LCD_WIDTH, TIMEBAR_HEIGHT);
238 * Marks the entire area of the osci buffer invalid.
239 * It will be drawn with new values in the next loop.
241 void splitedit_invalidate_osci(void)
243 osci_valid = false;
244 validation_start = ~(unsigned int)0;
248 * Returns the loop mode. See the LOOP_MODE_XXX constants above.
250 int splitedit_get_loop_mode(void)
252 return loop_mode;
256 * Updates the icons that display the Fn key hints.
258 static void update_icons(void)
260 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
261 rb->lcd_fillrect(0, LCD_HEIGHT - BMPHEIGHT, LCD_WIDTH, BMPHEIGHT);
262 rb->lcd_set_drawmode(DRMODE_SOLID);
264 /* The CUT icon */
265 rb->lcd_mono_bitmap(CUT_BMP,
266 LCD_WIDTH / 3 / 2 - BMPWIDTH / 2, LCD_HEIGHT - BMPHEIGHT,
267 BMPWIDTH, BMPHEIGHT);
269 /* The loop mode icon */
270 rb->lcd_mono_bitmap(LOOP_BMP[splitedit_get_loop_mode()],
271 LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
272 BMPWIDTH, BMPHEIGHT);
274 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
275 /* The scale icon */
276 rb->lcd_mono_bitmap(SCALE_BMP[rb->peak_meter_get_use_dbfs() ? 1 : 0],
277 2 *LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
278 BMPWIDTH, BMPHEIGHT);
279 #else
281 static int idx;
282 if (idx < 0 || idx > 1) idx = 0;
283 idx = 1 - idx;
284 rb->lcd_mono_bitmap(SCALE_BMP[idx],
285 2 *LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
286 BMPWIDTH, BMPHEIGHT);
288 #endif
290 rb->lcd_update_rect(0, LCD_HEIGHT - BMPHEIGHT, LCD_WIDTH, BMPHEIGHT);
294 * Sets the loop mode. See the LOOP_MODE_XXX constants above.
296 void splitedit_set_loop_mode(int mode)
298 int old_loop_mode = loop_mode;
299 /* range restriction */
300 loop_mode = mode % (LOOP_MODE_FREE + 1);
301 switch (loop_mode)
303 case LOOP_MODE_ALL:
304 play_start = range_start;
305 play_end = range_end;
306 break;
308 case LOOP_MODE_FROM:
309 play_start = xpos_to_time(split_x);
310 play_end = range_end;
311 break;
313 case LOOP_MODE_TO:
314 play_start = range_start;
315 play_end = xpos_to_time(split_x);
316 break;
318 case LOOP_MODE_FREE:
319 /* play_start is used when the song plays beyond its end */
320 play_start = range_start;
321 play_end = range_end;
322 break;
325 if (loop_mode != old_loop_mode)
327 update_icons();
332 * Readraws the osci without clear.
334 static void redraw_osci(void)
336 int x;
337 for (x = 0; x < OSCI_WIDTH; x++)
339 if (osci_buffer[x] > 0)
341 rb->lcd_drawline
343 OSCI_X + x, OSCI_Y + OSCI_HEIGHT - 1,
344 OSCI_X + x, OSCI_Y + OSCI_HEIGHT - osci_buffer[x] - 1
351 * Sets the range of time in which the user can finetune the split
352 * point. The split point is the center of the time range.
354 static void set_range_by_time(
355 struct mp3entry *mp3,
356 unsigned int split_time,
357 unsigned int range)
359 if (mp3 != NULL)
361 if (range < MIN_RANGE_SIZE)
363 range = MIN_RANGE_SIZE;
365 range_start = (split_time > range / 2) ? (split_time - range / 2) : 0;
366 range_end = MIN(range_start + range, mp3->length);
367 split_x = time_to_xpos(split_time);
369 splitedit_invalidate_osci();
371 /* this sets the play_start / play_end */
372 splitedit_set_loop_mode(splitedit_get_loop_mode());
374 update_data();
375 update_timebar(mp3);
380 * Set the split point in screen coordinates
382 void splitedit_set_split_x(int newx)
384 int minx = split_x - 2 > 0 ? split_x - 2: 0;
386 /* remove old split point from screen, only if moved */
387 if (split_x != newx)
389 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
390 rb->lcd_fillrect(minx, OSCI_Y, 5, 1);
391 rb->lcd_fillrect(split_x-1 > 0 ? split_x - 1: 0, OSCI_Y + 1, 3, 1);
392 rb->lcd_fillrect(split_x, OSCI_Y + 2, 1, OSCI_HEIGHT - 2);
393 rb->lcd_set_drawmode(DRMODE_SOLID);
394 rb->lcd_update_rect(minx, OSCI_Y, 5, OSCI_HEIGHT);
397 if (newx >= OSCI_X && newx < OSCI_X + OSCI_WIDTH)
399 split_x = newx;
400 /* in LOOP_FROM / LOOP_TO modes play_start /play_end must be updated */
401 splitedit_set_loop_mode(splitedit_get_loop_mode());
403 /* display new split time */
404 update_data();
407 /* display new split point */
408 minx = split_x - 2 > 0 ? split_x - 2: 0;
409 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
410 rb->lcd_fillrect(minx, OSCI_Y, 5, 1);
411 rb->lcd_fillrect(split_x - 1 > 0 ? split_x - 1: 0, OSCI_Y + 1, 3, 1);
412 rb->lcd_fillrect(split_x, OSCI_Y + 2, 1, OSCI_HEIGHT - 2);
413 rb->lcd_set_drawmode(DRMODE_SOLID);
414 rb->lcd_update_rect(minx, OSCI_Y, 5, OSCI_HEIGHT);
418 * returns the split point in screen coordinates
420 int splitedit_get_split_x(void)
422 return split_x;
426 * Clears the osci area and redraws it
428 static void update_osci(void)
430 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
431 rb->lcd_fillrect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
432 rb->lcd_set_drawmode(DRMODE_SOLID);
433 redraw_osci();
434 splitedit_set_split_x(splitedit_get_split_x());
435 rb->lcd_update_rect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
439 * Zooms the visable and loopable range by the factor
440 * (counter / denominator). The split point is used as
441 * center point of the new selected range.
443 static void zoom(struct mp3entry *mp3, int counter, int denominator)
445 unsigned char oldbuf[OSCI_WIDTH];
446 int oldrange = range_end - range_start;
447 int range = oldrange * counter / denominator;
448 int i;
449 int oldindex;
450 int oldsplitx;
451 int splitx;
452 int split;
454 /* for stretching / shrinking a second buffer is needed */
455 rb->memcpy(&oldbuf, &osci_buffer, sizeof osci_buffer);
457 /* recalculate the new range and split point */
458 oldsplitx = split_x;
459 split = xpos_to_time(split_x);
461 set_range_by_time(mp3, split, range);
462 range = range_end - range_start;
464 splitx = time_to_xpos(split);
466 /* strech / shrink the existing osci buffer */
467 for (i = 0; i < OSCI_WIDTH; i++)
469 /* oldindex = (i + OSCI_X - splitx) * range / oldrange + oldsplitx ;*/
470 oldindex = (i*range / oldrange) + oldsplitx - (splitx*range /oldrange);
471 if (oldindex >= 0 && oldindex < OSCI_WIDTH)
473 osci_buffer[i] = oldbuf[oldindex];
475 else
477 osci_buffer[i] = 0;
481 splitx = time_to_xpos(split);
482 splitedit_set_split_x(splitx);
483 splitedit_invalidate_osci();
487 static void scroll(struct mp3entry *mp3)
489 zoom(mp3, 1, 1);
490 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
491 update_osci();
492 update_data();
496 * Zooms in by 3/4
498 void splitedit_zoom_in(struct mp3entry *mp3)
500 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
501 rb->lcd_fillrect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
502 rb->lcd_set_drawmode(DRMODE_SOLID);
503 zoom(mp3, 3, 4);
504 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
505 update_osci();
506 update_data();
510 * Zooms out by 4/3
512 void splitedit_zoom_out(struct mp3entry *mp3)
514 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
515 rb->lcd_fillrect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
516 rb->lcd_set_drawmode(DRMODE_SOLID);
517 zoom(mp3, 4, 3);
518 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
519 update_osci();
520 update_data();
524 * Append part_no to the file name.
526 static void generateFileName(char* file_name, int part_no)
528 if (rb->strlen(file_name) <MAX_PATH)
530 int len = rb->strlen(file_name);
531 int ext_len = rb->strlen(".mp3");
532 if (rb->strcasecmp(
533 &file_name[len - ext_len],
534 ".mp3") == 0)
536 int i = 0;
537 /* shift the extension one position to the right*/
538 for (i = len; i > len - ext_len; i--)
540 file_name[i] = file_name[i - 1];
542 file_name[len - ext_len] = '0' + part_no;
544 else
546 rb->splash(0, "wrong extension");
547 rb->button_get(true);
548 rb->button_get(true);
551 else
553 rb->splash(0, "name too long");
554 rb->button_get(true);
555 rb->button_get(true);
562 * Copy bytes from src to dest while displaying a progressbar.
563 * The files must be already open.
565 static int copy_file(
566 int dest,
567 int src,
568 unsigned int bytes,
569 int prg_y,
570 int prg_h)
572 long button;
573 unsigned char *buffer;
574 unsigned int i = 0;
575 ssize_t bytes_read = 1; /* ensure the for loop is executed */
576 size_t buffer_size;
577 buffer = rb->plugin_get_buffer(&buffer_size);
579 for (i = 0; i < bytes && bytes_read > 0; i += bytes_read)
581 ssize_t bytes_written;
582 unsigned int bytes_to_read =
583 bytes - i > buffer_size ? buffer_size : bytes - i;
584 bytes_read = rb->read(src, buffer, bytes_to_read);
585 bytes_written = rb->write(dest, buffer, bytes_read);
587 if (bytes_written < 0) {
588 rb->splash(0, "Write failed in copy.");
589 rb->button_get(true);
590 rb->button_get(true);
591 return -1;
594 button = rb->button_get(false);
596 if (button == SPLITEDIT_QUIT
597 #ifdef SPLITEDIT_RC_QUIT
598 || button == SPLITEDIT_RC_QUIT:
599 #endif
601 rb->splash(0, "Aborting copy.");
602 rb->button_get(true);
603 rb->button_get(true);
604 return -1;
607 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, prg_y, LCD_WIDTH,
608 prg_h, bytes, 0, i, HORIZONTAL);
609 rb->lcd_update_rect(0, prg_y, LCD_WIDTH, prg_h);
612 return 0;
616 * Save the files, if the file_name is not NULL
618 static int save(
619 struct mp3entry *mp3,
620 char *file_name1,
621 char *file_name2,
622 int splittime)
624 int file1, file2, src_file;
625 unsigned int end = 0;
626 int retval = 0;
628 /* Verify that file 1 doesn't exit yet */
629 if (file_name1 != NULL)
631 file1 = rb->open(file_name1, O_RDONLY);
632 if (file1 >= 0)
634 rb->close(file1);
635 rb->splash(0, "File 1 exists. Please rename.");
636 rb->button_get(true);
637 rb->button_get(true);
638 return -1;
642 /* Verify that file 2 doesn't exit yet */
643 if (file_name2 != NULL)
645 file2 = rb->open(file_name2, O_RDONLY);
646 if (file2 >= 0)
648 rb->close(file2);
649 rb->splash(0, "File 2 exists. Please rename.");
650 rb->button_get(true);
651 rb->button_get(true);
652 return -2;
656 /* find the file position of the split point */
657 rb->audio_pause();
658 rb->audio_ff_rewind(splittime);
659 rb->yield();
660 rb->yield();
661 end = rb->audio_get_file_pos();
663 /* open the source file */
664 src_file = rb->open(mp3->path, O_RDONLY);
665 if (src_file >= 0)
667 int close_stat = 0;
668 int x, y;
669 long offset;
670 unsigned long last_header = rb->mpeg_get_last_header();
672 rb->lcd_getstringsize("M", &x, &y);
674 /* Find the next frame boundary */
675 rb->lseek(src_file, end, SEEK_SET);
676 rb->find_next_frame(src_file, &offset, 8000, last_header);
677 rb->lseek(src_file, 0, SEEK_SET);
678 end += offset;
680 /* write the file 1 */
681 if (file_name1 != NULL)
683 file1 = rb->open (file_name1, O_WRONLY | O_CREAT);
684 if (file1 >= 0)
686 int rc = copy_file(file1, src_file, end, y*2 + 1, y -1);
687 close_stat = rb->close(file1);
689 if (close_stat != 0)
691 rb->splash(0, "failed closing file1: error %d", close_stat);
692 rb->button_get(true);
693 rb->button_get(true);
694 } else {
695 /* If there was an error, cleanup */
696 if (rc) {
697 rb->remove(file_name1);
701 else
703 rb->splash(0, "Can't write File1: error %d", file1);
704 rb->button_get(true);
705 rb->button_get(true);
706 retval = -1;
709 /* if file1 hasn't been written we're not at the split point yet */
710 else
712 if (rb->lseek(src_file, end, SEEK_SET) < (off_t)end)
714 rb->splash(0, "Src file to short: error %d", src_file);
715 rb->button_get(true);
716 rb->button_get(true);
720 if (file_name2 != NULL)
722 /* write file 2 */
723 file2 = rb->open (file_name2, O_WRONLY | O_CREAT);
724 if (file2 >= 0)
726 end = mp3->filesize - end;
727 int rc = copy_file(file2, src_file, end, y * 5 + 1, y -1);
728 close_stat = rb->close(file2);
730 if (close_stat != 0)
732 rb->splash(0, "failed: closing file2: error %d",
733 close_stat);
734 rb->button_get(true);
735 rb->button_get(true);
736 } else {
737 /* If there was an error, cleanup */
738 if (rc) {
739 rb->remove(file_name2);
743 else
745 rb->splash(0, "Can't write File2: error %d", file2);
746 rb->button_get(true);
747 rb->button_get(true);
748 retval = -2;
752 close_stat = rb->close(src_file);
753 if (close_stat != 0)
755 rb->splash(0, "failed: closing src: error %d", close_stat);
756 rb->button_get(true);
757 rb->button_get(true);
760 else
762 rb->splash(0, "Source file not found");
763 rb->button_get(true);
764 rb->button_get(true);
765 retval = -3;
768 rb->audio_resume();
770 return retval;
774 * Let the user choose which file to save with which name
776 static void save_editor(struct mp3entry *mp3, int splittime)
778 bool exit_request = false;
779 int choice = 0;
780 int button = BUTTON_NONE;
781 char part1_name [MAX_PATH];
782 char part2_name [MAX_PATH];
783 bool part1_save = true;
784 bool part2_save = true;
786 /* file name for left part */
787 rb->strncpy(part1_name, mp3->path, MAX_PATH);
788 generateFileName(part1_name, 1);
790 /* file name for right part */
791 rb->strncpy(part2_name, mp3->path, MAX_PATH);
792 generateFileName(part2_name, 2);
794 while (!exit_request)
796 int pos;
797 rb->lcd_clear_display();
799 /* Save file1? */
800 rb->lcd_puts_style(0, 0, "Save part 1?", choice == SE_PART1_SAVE);
801 rb->lcd_puts(13, 0, part1_save?"yes":"no");
803 /* trim to display the filename without path */
804 for (pos = rb->strlen(part1_name); pos > 0; pos--)
806 if (part1_name[pos] == '/')
807 break;
809 pos++;
811 /* File name 1 */
812 rb->lcd_puts_scroll_style(0, 1,
813 &part1_name[pos], choice == SE_PART1_NAME);
815 /* Save file2? */
816 rb->lcd_puts_style(0, 3, "Save part 2?", choice == SE_PART2_SAVE);
817 rb->lcd_puts(13, 3, part2_save?"yes":"no");
819 /* trim to display the filename without path */
820 for (pos = rb->strlen(part2_name); pos > 0; pos --)
822 if (part2_name[pos] == '/')
823 break;
825 pos++;
827 /* File name 2 */
828 rb->lcd_puts_scroll_style(0, 4,
829 &part2_name[pos], choice == SE_PART2_NAME);
831 /* Save */
832 rb->lcd_puts_style(0, 6, "Save", choice == SE_SAVE);
834 rb->lcd_update();
837 button = rb->button_get(true);
838 switch (button)
840 case BUTTON_UP:
841 choice = (choice + SE_COUNT - 1) % SE_COUNT;
842 break;
844 case BUTTON_DOWN:
845 choice = (choice + 1) % SE_COUNT;
846 break;
848 case SPLITEDIT_MENU_RUN:
849 switch (choice)
851 int saved;
853 case SE_PART1_SAVE:
854 part1_save = !part1_save;
855 break;
857 case SE_PART1_NAME:
858 rb->kbd_input(part1_name, MAX_PATH);
859 break;
861 case SE_PART2_SAVE:
862 part2_save = !part2_save;
863 break;
865 case SE_PART2_NAME:
866 rb->kbd_input(part2_name, MAX_PATH);
867 break;
869 case SE_SAVE:
870 rb->lcd_stop_scroll();
871 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
872 rb->lcd_fillrect(0, 6*8, LCD_WIDTH, LCD_HEIGHT);
873 rb->lcd_set_drawmode(DRMODE_SOLID);
874 saved = save
876 mp3,
877 part1_save?part1_name:NULL,
878 part2_save?part2_name:NULL,
879 splittime
882 /* if something failed the user may go on choosing */
883 if (saved >= 0)
885 exit_request = true;
887 break;
889 break;
890 #ifdef SPLITEDIT_RC_QUIT
891 case SPLITEDIT_RC_QUIT:
892 #endif
893 case SPLITEDIT_QUIT:
894 exit_request = true;
895 break;
897 default:
898 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
900 splitedit_exit_code = PLUGIN_USB_CONNECTED;
901 exit_request = true;
903 break;
909 * The main loop of the editor
911 unsigned long splitedit_editor(struct mp3entry * mp3_to_split,
912 unsigned int split_time,
913 unsigned int range)
915 int button = BUTTON_NONE;
916 int lastbutton = BUTTON_NONE;
917 struct mp3entry *mp3 = mp3_to_split;
918 unsigned int last_elapsed = 0;
919 int lastx = OSCI_X + (OSCI_WIDTH / 2);
920 int retval = -1;
922 if (mp3 != NULL)
924 /*unsigned short scheme = SCHEME_SPLIT_EDITOR;*/
925 bool exit_request = false;
926 set_range_by_time(mp3, split_time, range);
927 splitedit_set_loop_mode(LOOP_MODE_ALL);
928 update_icons();
930 /*while (scheme != SCHEME_RETURN) {*/
931 while (!exit_request)
933 unsigned int elapsed ;
934 int x ;
936 /* get position */
937 elapsed = mp3->elapsed;
938 x = time_to_xpos(elapsed);
940 /* are we still in the zoomed range? */
941 if (elapsed > play_start && elapsed < play_end)
943 /* read volume info */
944 unsigned short volume;
945 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
946 volume = rb->mas_codec_readreg(0x0c);
947 volume += rb->mas_codec_readreg(0x0d);
948 volume = volume / 2;
949 volume = rb->peak_meter_scale_value(volume, OSCI_HEIGHT);
950 #else
951 volume = OSCI_HEIGHT / 2;
952 #endif
954 /* update osci_buffer */
955 if (osci_valid || lastx == x)
957 int index = x - OSCI_X;
958 osci_buffer[index] = MAX(osci_buffer[index], volume);
960 else
962 int i;
963 osci_buffer[x - OSCI_X] = volume;
964 for (i = lastx + 1; i < x; i++)
966 osci_buffer[i - OSCI_X] = 0;
970 /* make room */
971 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
972 rb->lcd_fillrect(lastx + 1, OSCI_Y, x - lastx, OSCI_HEIGHT);
973 rb->lcd_set_drawmode(DRMODE_SOLID);
974 /* draw a value */
975 if (osci_buffer[x - OSCI_X] > 0)
977 int i;
978 for (i = lastx +1; i <= x; i++)
980 rb->lcd_drawline
982 i, OSCI_Y + OSCI_HEIGHT - 1,
983 i, OSCI_Y + OSCI_HEIGHT - osci_buffer[i - OSCI_X]-1
988 /* mark the current position */
989 if (lastx != x)
991 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
992 rb->lcd_fillrect(lastx, OSCI_Y, 1, OSCI_HEIGHT);
993 rb->lcd_fillrect(x, OSCI_Y, 1, OSCI_HEIGHT);
994 rb->lcd_set_drawmode(DRMODE_SOLID);
997 /* mark the split point */
998 if ((x > split_x - 2) && (lastx < split_x + 3))
1000 if ((lastx < split_x) && (x >= split_x))
1002 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1003 rb->lcd_fillrect
1005 split_x, OSCI_Y + 2,
1006 1, OSCI_HEIGHT - 2
1008 rb->lcd_set_drawmode(DRMODE_SOLID);
1010 rb->lcd_drawline(split_x -2, OSCI_Y, split_x + 2, OSCI_Y);
1011 rb->lcd_drawline(split_x-1, OSCI_Y+1, split_x +1,OSCI_Y+1);
1014 /* make visible */
1015 if (lastx <= x)
1017 rb->lcd_update_rect(lastx, OSCI_Y, x-lastx+1, OSCI_HEIGHT);
1019 else
1021 rb->lcd_update_rect
1023 lastx, OSCI_Y,
1024 OSCI_X + OSCI_WIDTH - lastx, OSCI_HEIGHT
1026 rb->lcd_update_rect(0, OSCI_Y, x + 1, OSCI_HEIGHT);
1029 lastx = x;
1032 /* we're not in the zoom range -> rewind */
1033 else
1035 if (elapsed >= play_end)
1037 switch (splitedit_get_loop_mode())
1039 unsigned int range_width;
1041 case LOOP_MODE_ALL:
1042 case LOOP_MODE_TO:
1043 rb->audio_pause();
1044 rb->audio_ff_rewind(range_start);
1045 #ifdef HAVE_MMC
1046 /* MMC is slow - wait some time to allow track reload to finish */
1047 rb->sleep(HZ/20);
1048 if (mp3->elapsed > play_end) /* reload in progress */
1049 rb->splash(10*HZ, "Wait - reloading");
1050 #endif
1051 rb->audio_resume();
1052 break;
1054 case LOOP_MODE_FROM:
1055 rb->audio_pause();
1056 rb->audio_ff_rewind(xpos_to_time(split_x));
1057 #ifdef HAVE_MMC
1058 /* MMC is slow - wait some time to allow track reload to finish */
1059 rb->sleep(HZ/20);
1060 if (mp3->elapsed > play_end) /* reload in progress */
1061 rb->splash(10*HZ, "Wait - reloading");
1062 #endif
1063 rb->audio_resume();
1064 break;
1066 case LOOP_MODE_FREE:
1067 range_width = range_end - range_start;
1068 set_range_by_time(mp3,
1069 range_end + range_width / 2, range_width);
1071 /* play_end und play_start anpassen */
1072 splitedit_set_loop_mode(LOOP_MODE_FREE);
1073 rb->memset(osci_buffer, 0, sizeof osci_buffer);
1074 update_osci();
1075 rb->lcd_update();
1076 break;
1081 button = rb->button_get(false);
1082 rb->yield();
1084 /* here the evaluation of the key scheme starts.
1085 All functions the user triggers are called from
1086 within execute_scheme */
1087 /* key_scheme_execute(button, &scheme); */
1088 switch (button)
1090 case SPLITEDIT_PLAY:
1091 #ifdef SPLITEDIT_PLAY_PRE
1092 if (lastbutton != SPLITEDIT_PLAY_PRE)
1093 break;
1094 #endif
1095 rb->audio_pause();
1096 rb->audio_ff_rewind(xpos_to_time(split_x));
1097 rb->audio_resume();
1098 break;
1100 case BUTTON_UP:
1101 splitedit_zoom_in(mp3);
1102 lastx = time_to_xpos(mp3->elapsed);
1103 break;
1105 case BUTTON_DOWN:
1106 splitedit_zoom_out(mp3);
1107 lastx = time_to_xpos(mp3->elapsed);
1108 break;
1110 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1111 #ifdef SPLITEDIT_SPEED100
1112 case SPLITEDIT_SPEED150:
1113 rb->sound_set_pitch(1500);
1114 splitedit_invalidate_osci();
1115 break;
1117 case SPLITEDIT_SPEED100:
1118 rb->sound_set_pitch(1000);
1119 splitedit_invalidate_osci();
1120 break;
1122 case SPLITEDIT_SPEED50:
1123 rb->sound_set_pitch(500);
1124 splitedit_invalidate_osci();
1125 break;
1126 #endif
1127 #endif
1129 case BUTTON_LEFT:
1130 case BUTTON_LEFT | BUTTON_REPEAT:
1131 if (splitedit_get_split_x() > OSCI_X + 2)
1133 splitedit_set_split_x(splitedit_get_split_x() - 1);
1135 else
1137 scroll(mp3);
1138 lastx = time_to_xpos(mp3->elapsed);
1140 break;
1142 case BUTTON_RIGHT:
1143 case BUTTON_RIGHT | BUTTON_REPEAT:
1144 if (splitedit_get_split_x() < OSCI_X + OSCI_WIDTH-3)
1146 splitedit_set_split_x(splitedit_get_split_x() + 1);
1148 else
1150 scroll(mp3);
1151 lastx = time_to_xpos(mp3->elapsed);
1153 break;
1155 case SPLITEDIT_SAVE:
1156 save_editor(mp3, xpos_to_time(split_x));
1157 rb->lcd_clear_display();
1158 update_osci();
1159 update_timebar(mp3);
1160 update_icons();
1161 break;
1163 case SPLITEDIT_LOOP_MODE:
1164 splitedit_set_loop_mode(splitedit_get_loop_mode() + 1);
1165 update_icons();
1166 break;
1168 case SPLITEDIT_SCALE:
1169 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1170 rb->peak_meter_set_use_dbfs(!rb->peak_meter_get_use_dbfs());
1171 #endif
1172 splitedit_invalidate_osci();
1173 update_icons();
1174 break;
1176 #ifdef SPLITEDIT_RC_QUIT
1177 case SPLITEDIT_RC_QUIT:
1178 #endif
1179 case SPLITEDIT_QUIT:
1180 exit_request = true;
1181 break;
1183 default:
1184 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1186 splitedit_exit_code = PLUGIN_USB_CONNECTED;
1187 exit_request = true;
1189 break;
1192 if (button != BUTTON_NONE)
1193 lastbutton = button;
1195 if (validation_start == ~(unsigned int)0)
1197 if (elapsed < range_end && elapsed > range_start)
1199 validation_start = elapsed;
1201 else
1203 int endx = time_to_xpos(range_end);
1204 validation_start = xpos_to_time(endx - 2);
1206 last_elapsed = elapsed + 1;
1208 else
1210 if ((last_elapsed <= validation_start) &&
1211 (elapsed > validation_start))
1213 osci_valid = true;
1216 last_elapsed = elapsed;
1218 update_data();
1220 if (mp3 != rb->audio_current_track())
1222 struct mp3entry *new_mp3 = rb->audio_current_track();
1223 if (rb->strncasecmp(path_mp3, new_mp3->path,
1224 sizeof (path_mp3)))
1226 rb->splash(0, "Abort due to file change");
1227 rb->button_get(true);
1228 rb->button_get(true);
1229 exit_request = true;
1231 else
1233 mp3 = new_mp3;
1234 rb->audio_pause();
1235 rb->audio_flush_and_reload_tracks();
1236 rb->audio_ff_rewind(range_start);
1237 rb->audio_resume();
1241 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1242 #ifdef SPLITEDIT_SPEED100
1243 rb->sound_set_pitch(1000); /* make sure to reset pitch */
1244 #endif
1245 #endif
1248 return retval;
1251 enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1253 struct mp3entry* mp3;
1255 (void)parameter;
1256 rb = api;
1257 rb->lcd_clear_display();
1258 rb->lcd_update();
1259 mp3 = rb->audio_current_track();
1260 if (mp3 != NULL)
1262 if (rb->audio_status() & AUDIO_STATUS_PAUSE)
1264 rb->audio_resume();
1266 splitedit_editor(mp3, mp3->elapsed, MIN_RANGE_SIZE * 8);
1268 else
1270 rb->splash(0, "Play or pause a mp3 file first.");
1271 rb->button_get(true);
1272 rb->button_get(true);
1274 return splitedit_exit_code;
1276 #endif
1277 #endif