New makefile solution: A single invocation of 'make' to build the entire tree. Fully...
[kugel-rb.git] / apps / plugins / splitedit.c
blobdda46a0f29810dc26691f8a70c7a6134f498443d
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002 Philipp Pertermann
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include "plugin.h"
24 #ifndef SIMULATOR
25 #ifdef HAVE_LCD_BITMAP
27 PLUGIN_HEADER
29 /* variable button definitions */
30 #if CONFIG_KEYPAD == RECORDER_PAD
31 #define SPLITEDIT_QUIT BUTTON_OFF
32 #define SPLITEDIT_PLAY BUTTON_PLAY
33 #define SPLITEDIT_SAVE BUTTON_F1
34 #define SPLITEDIT_LOOP_MODE BUTTON_F2
35 #define SPLITEDIT_SCALE BUTTON_F3
36 #define SPLITEDIT_SPEED50 (BUTTON_ON | BUTTON_LEFT)
37 #define SPLITEDIT_SPEED100 (BUTTON_ON | BUTTON_PLAY)
38 #define SPLITEDIT_SPEED150 (BUTTON_ON | BUTTON_RIGHT)
39 #define SPLITEDIT_MENU_RUN BUTTON_PLAY
41 #elif CONFIG_KEYPAD == ONDIO_PAD
42 #define SPLITEDIT_QUIT BUTTON_OFF
43 #define SPLITEDIT_PLAY_PRE BUTTON_MENU
44 #define SPLITEDIT_PLAY (BUTTON_MENU | BUTTON_REL)
45 #define SPLITEDIT_SAVE (BUTTON_MENU | BUTTON_LEFT)
46 #define SPLITEDIT_LOOP_MODE (BUTTON_MENU | BUTTON_UP)
47 #define SPLITEDIT_SCALE (BUTTON_MENU | BUTTON_RIGHT)
48 #define SPLITEDIT_MENU_RUN BUTTON_RIGHT
50 #elif CONFIG_KEYPAD == IRIVER_H100_PAD
51 #define SPLITEDIT_QUIT BUTTON_OFF
52 #define SPLITEDIT_PLAY BUTTON_ON
53 #define SPLITEDIT_SAVE BUTTON_SELECT
54 #define SPLITEDIT_LOOP_MODE BUTTON_MODE
55 #define SPLITEDIT_SCALE (BUTTON_REC | BUTTON_UP)
56 #define SPLITEDIT_SPEED50 (BUTTON_REC | BUTTON_LEFT)
57 #define SPLITEDIT_SPEED100 (BUTTON_REC | BUTTON_DOWN)
58 #define SPLITEDIT_SPEED150 (BUTTON_REC | BUTTON_RIGHT)
59 #define SPLITEDIT_MENU_RUN BUTTON_RIGHT
61 #define SPLITEDIT_RC_QUIT BUTTON_RC_STOP
62 #endif
64 #define BMPHEIGHT 7
65 #define BMPWIDTH 13
66 unsigned char LOOP_BMP[][13] =
68 {0xfc,0x00,0x10,0x11,0x93,0x7f,0x13,0x11,0x7c,0x38,0x10,0x00,0x7c}, /*ALL */
69 {0x81,0x03,0x7f,0x03,0x91,0x10,0x10,0x10,0x7c,0x38,0x10,0x00,0x7c}, /*FROM*/
70 {0xfc,0x00,0x10,0x10,0x90,0x10,0x7c,0x38,0x11,0x03,0x7f,0x03,0x01}, /*TO */
71 {0x80,0x10,0x10,0x11,0x93,0x7f,0x13,0x11,0x10,0x7c,0x38,0x10,0x00}, /*FREE*/
74 unsigned char CUT_BMP[] =
76 0xc1,0x63,0x63,0x36,0xb6,0x1c,0x1c,0x36,0x77,0x55,0x55,0x55,0x32,
79 unsigned char SCALE_BMP[][13] =
81 {0x80,0x06,0x49,0x66,0xb0,0x18,0x0c,0x06,0x33,0x49,0x30,0x00,0x00}, /*lin*/
82 {0x80,0x30,0x78,0x48,0xff,0x7f,0x00,0x7f,0x7f,0x48,0x78,0x30,0x00}, /*db*/
85 #define TIMEBAR_Y 9
86 #define TIMEBAR_HEIGHT 4
88 #define OSCI_X 0
89 #define OSCI_Y (TIMEBAR_Y + TIMEBAR_HEIGHT + 1)
90 #define OSCI_WIDTH LCD_WIDTH
91 #define OSCI_HEIGHT (LCD_HEIGHT - BMPHEIGHT - OSCI_Y - 1)
93 /* Indices of the menu items in the save editor, see save_editor */
94 #define SE_PART1_SAVE 0
95 #define SE_PART1_NAME 1
96 #define SE_PART2_SAVE 2
97 #define SE_PART2_NAME 3
98 #define SE_SAVE 4
99 #define SE_COUNT 5
101 /* the global api pointer */
102 static const struct plugin_api* rb;
104 /* contains the file name of the song that is to be split */
105 static char path_mp3[MAX_PATH];
107 /* Exit code of this plugin */
108 static enum plugin_status splitedit_exit_code = PLUGIN_OK;
110 /* The range in time that the displayed aerea comprises */
111 static unsigned int range_start = 0;
112 static unsigned int range_end = 0;
114 /* The range in time that is being looped */
115 static unsigned int play_start = 0;
116 static unsigned int play_end = 0;
118 /* Point in time (pixel) at which the split mark is set */
119 static int split_x = OSCI_X + (OSCI_WIDTH / 2);
121 /* Contains the peak values */
122 static unsigned char osci_buffer[OSCI_WIDTH];
124 /* if true peak values from a previous loop are only overwritten
125 if the new value is greater than the old value */
126 static bool osci_valid = false;
129 * point in time from which on the osci_buffer is invalid
130 * if set to ~(unsigned int)0 the entire osci_buffer is invalid
132 static unsigned int validation_start = ~(unsigned int)0;
134 /* all the visible aerea is looped */
135 #define LOOP_MODE_ALL 0
137 /* loop starts at split point, ends at right visible border */
138 #define LOOP_MODE_FROM 1
140 /* loop start at left visible border, ends at split point */
141 #define LOOP_MODE_TO 2
143 /* let the song play without looping */
144 #define LOOP_MODE_FREE 3
146 /* see LOOP_MODE_XXX constants vor valid values */
147 static int loop_mode = LOOP_MODE_FREE;
149 /* minimal allowed timespan (ms) of the visible (and looped) aerea*/
150 #define MIN_RANGE_SIZE 1000
152 /* Format time into buf.
154 * buf - buffer to format to.
155 * buf_size - size of buffer.
156 * time - time to format, in milliseconds.
158 static void format_time_ms(char* buf, int buf_size, int time)
160 rb->snprintf(buf, buf_size, "%d:%02d:%03d", time / 60000,
161 time % 60000 / 1000, (time % 60000) % 1000);
165 * converts screen coordinate (pixel) to time (ms)
167 static int xpos_to_time(int xpos)
169 int retval = 0;
170 int range = range_end - range_start;
171 retval = range_start + (((xpos - OSCI_X) * range) / OSCI_WIDTH);
172 return retval;
176 * Converts time (ms) to screen coordinates (pixel).
178 static int time_to_xpos(unsigned int time)
180 int retval = OSCI_X;
182 /* clip the range */
183 if (time < range_start)
185 retval = OSCI_X;
187 else
188 if (time >= range_end)
190 retval = OSCI_X + OSCI_WIDTH;
193 /* do the calculation */
194 else
196 int range = range_end - range_start;
197 retval = OSCI_X + ((time - range_start) * OSCI_WIDTH) / range ;
199 return retval;
203 * Updates the display of the textual data only.
205 static void update_data(void)
207 char buf[20];
208 char timebuf[10];
209 int w, h;
211 /* split point */
212 format_time_ms(timebuf, sizeof buf, xpos_to_time(split_x));
213 rb->snprintf(buf, sizeof buf, "Split at: %s", timebuf);
215 rb->lcd_getstringsize(buf, &w, &h);
217 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
218 rb->lcd_fillrect(0, 0, LCD_WIDTH, h);
219 rb->lcd_set_drawmode(DRMODE_SOLID);
220 rb->lcd_puts(0, 0, buf);
221 rb->lcd_update_rect(0, 0, LCD_WIDTH, h);
225 * Displays which part of the song is visible
226 * in the osci.
228 static void update_timebar(struct mp3entry *mp3)
230 rb->gui_scrollbar_draw
232 rb->screens[SCREEN_MAIN],0, TIMEBAR_Y, LCD_WIDTH, TIMEBAR_HEIGHT,
233 mp3->length, range_start, range_end,
234 HORIZONTAL
236 rb->lcd_update_rect(0, TIMEBAR_Y, LCD_WIDTH, TIMEBAR_HEIGHT);
240 * Marks the entire area of the osci buffer invalid.
241 * It will be drawn with new values in the next loop.
243 void splitedit_invalidate_osci(void)
245 osci_valid = false;
246 validation_start = ~(unsigned int)0;
250 * Returns the loop mode. See the LOOP_MODE_XXX constants above.
252 int splitedit_get_loop_mode(void)
254 return loop_mode;
258 * Updates the icons that display the Fn key hints.
260 static void update_icons(void)
262 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
263 rb->lcd_fillrect(0, LCD_HEIGHT - BMPHEIGHT, LCD_WIDTH, BMPHEIGHT);
264 rb->lcd_set_drawmode(DRMODE_SOLID);
266 /* The CUT icon */
267 rb->lcd_mono_bitmap(CUT_BMP,
268 LCD_WIDTH / 3 / 2 - BMPWIDTH / 2, LCD_HEIGHT - BMPHEIGHT,
269 BMPWIDTH, BMPHEIGHT);
271 /* The loop mode icon */
272 rb->lcd_mono_bitmap(LOOP_BMP[splitedit_get_loop_mode()],
273 LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
274 BMPWIDTH, BMPHEIGHT);
276 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
277 /* The scale icon */
278 rb->lcd_mono_bitmap(SCALE_BMP[rb->peak_meter_get_use_dbfs() ? 1 : 0],
279 2 *LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
280 BMPWIDTH, BMPHEIGHT);
281 #else
283 static int idx;
284 if (idx < 0 || idx > 1) idx = 0;
285 idx = 1 - idx;
286 rb->lcd_mono_bitmap(SCALE_BMP[idx],
287 2 *LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
288 BMPWIDTH, BMPHEIGHT);
290 #endif
292 rb->lcd_update_rect(0, LCD_HEIGHT - BMPHEIGHT, LCD_WIDTH, BMPHEIGHT);
296 * Sets the loop mode. See the LOOP_MODE_XXX constants above.
298 void splitedit_set_loop_mode(int mode)
300 int old_loop_mode = loop_mode;
301 /* range restriction */
302 loop_mode = mode % (LOOP_MODE_FREE + 1);
303 switch (loop_mode)
305 case LOOP_MODE_ALL:
306 play_start = range_start;
307 play_end = range_end;
308 break;
310 case LOOP_MODE_FROM:
311 play_start = xpos_to_time(split_x);
312 play_end = range_end;
313 break;
315 case LOOP_MODE_TO:
316 play_start = range_start;
317 play_end = xpos_to_time(split_x);
318 break;
320 case LOOP_MODE_FREE:
321 /* play_start is used when the song plays beyond its end */
322 play_start = range_start;
323 play_end = range_end;
324 break;
327 if (loop_mode != old_loop_mode)
329 update_icons();
334 * Readraws the osci without clear.
336 static void redraw_osci(void)
338 int x;
339 for (x = 0; x < OSCI_WIDTH; x++)
341 if (osci_buffer[x] > 0)
343 rb->lcd_vline
345 OSCI_X + x, OSCI_Y + OSCI_HEIGHT - 1,
346 OSCI_Y + OSCI_HEIGHT - osci_buffer[x] - 1
353 * Sets the range of time in which the user can finetune the split
354 * point. The split point is the center of the time range.
356 static void set_range_by_time(
357 struct mp3entry *mp3,
358 unsigned int split_time,
359 unsigned int range)
361 if (mp3 != NULL)
363 if (range < MIN_RANGE_SIZE)
365 range = MIN_RANGE_SIZE;
367 range_start = (split_time > range / 2) ? (split_time - range / 2) : 0;
368 range_end = MIN(range_start + range, mp3->length);
369 split_x = time_to_xpos(split_time);
371 splitedit_invalidate_osci();
373 /* this sets the play_start / play_end */
374 splitedit_set_loop_mode(splitedit_get_loop_mode());
376 update_data();
377 update_timebar(mp3);
382 * Set the split point in screen coordinates
384 void splitedit_set_split_x(int newx)
386 int minx = split_x - 2 > 0 ? split_x - 2: 0;
388 /* remove old split point from screen, only if moved */
389 if (split_x != newx)
391 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
392 rb->lcd_fillrect(minx, OSCI_Y, 5, 1);
393 rb->lcd_fillrect(split_x-1 > 0 ? split_x - 1: 0, OSCI_Y + 1, 3, 1);
394 rb->lcd_fillrect(split_x, OSCI_Y + 2, 1, OSCI_HEIGHT - 2);
395 rb->lcd_set_drawmode(DRMODE_SOLID);
396 rb->lcd_update_rect(minx, OSCI_Y, 5, OSCI_HEIGHT);
399 if (newx >= OSCI_X && newx < OSCI_X + OSCI_WIDTH)
401 split_x = newx;
402 /* in LOOP_FROM / LOOP_TO modes play_start /play_end must be updated */
403 splitedit_set_loop_mode(splitedit_get_loop_mode());
405 /* display new split time */
406 update_data();
409 /* display new split point */
410 minx = split_x - 2 > 0 ? split_x - 2: 0;
411 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
412 rb->lcd_fillrect(minx, OSCI_Y, 5, 1);
413 rb->lcd_fillrect(split_x - 1 > 0 ? split_x - 1: 0, OSCI_Y + 1, 3, 1);
414 rb->lcd_fillrect(split_x, OSCI_Y + 2, 1, OSCI_HEIGHT - 2);
415 rb->lcd_set_drawmode(DRMODE_SOLID);
416 rb->lcd_update_rect(minx, OSCI_Y, 5, OSCI_HEIGHT);
420 * returns the split point in screen coordinates
422 int splitedit_get_split_x(void)
424 return split_x;
428 * Clears the osci area and redraws it
430 static void update_osci(void)
432 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
433 rb->lcd_fillrect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
434 rb->lcd_set_drawmode(DRMODE_SOLID);
435 redraw_osci();
436 splitedit_set_split_x(splitedit_get_split_x());
437 rb->lcd_update_rect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
441 * Zooms the visable and loopable range by the factor
442 * (counter / denominator). The split point is used as
443 * center point of the new selected range.
445 static void zoom(struct mp3entry *mp3, int counter, int denominator)
447 unsigned char oldbuf[OSCI_WIDTH];
448 int oldrange = range_end - range_start;
449 int range = oldrange * counter / denominator;
450 int i;
451 int oldindex;
452 int oldsplitx;
453 int splitx;
454 int split;
456 /* for stretching / shrinking a second buffer is needed */
457 rb->memcpy(&oldbuf, &osci_buffer, sizeof osci_buffer);
459 /* recalculate the new range and split point */
460 oldsplitx = split_x;
461 split = xpos_to_time(split_x);
463 set_range_by_time(mp3, split, range);
464 range = range_end - range_start;
466 splitx = time_to_xpos(split);
468 /* strech / shrink the existing osci buffer */
469 for (i = 0; i < OSCI_WIDTH; i++)
471 /* oldindex = (i + OSCI_X - splitx) * range / oldrange + oldsplitx ;*/
472 oldindex = (i*range / oldrange) + oldsplitx - (splitx*range /oldrange);
473 if (oldindex >= 0 && oldindex < OSCI_WIDTH)
475 osci_buffer[i] = oldbuf[oldindex];
477 else
479 osci_buffer[i] = 0;
483 splitx = time_to_xpos(split);
484 splitedit_set_split_x(splitx);
485 splitedit_invalidate_osci();
489 static void scroll(struct mp3entry *mp3)
491 zoom(mp3, 1, 1);
492 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
493 update_osci();
494 update_data();
498 * Zooms in by 3/4
500 void splitedit_zoom_in(struct mp3entry *mp3)
502 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
503 rb->lcd_fillrect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
504 rb->lcd_set_drawmode(DRMODE_SOLID);
505 zoom(mp3, 3, 4);
506 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
507 update_osci();
508 update_data();
512 * Zooms out by 4/3
514 void splitedit_zoom_out(struct mp3entry *mp3)
516 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
517 rb->lcd_fillrect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
518 rb->lcd_set_drawmode(DRMODE_SOLID);
519 zoom(mp3, 4, 3);
520 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
521 update_osci();
522 update_data();
526 * Append part_no to the file name.
528 static void generateFileName(char* file_name, int part_no)
530 if (rb->strlen(file_name) <MAX_PATH)
532 int len = rb->strlen(file_name);
533 int ext_len = rb->strlen(".mp3");
534 if (rb->strcasecmp(
535 &file_name[len - ext_len],
536 ".mp3") == 0)
538 int i = 0;
539 /* shift the extension one position to the right*/
540 for (i = len; i > len - ext_len; i--)
542 file_name[i] = file_name[i - 1];
544 file_name[len - ext_len] = '0' + part_no;
546 else
548 rb->splash(0, "wrong extension");
549 rb->button_get(true);
550 rb->button_get(true);
553 else
555 rb->splash(0, "name too long");
556 rb->button_get(true);
557 rb->button_get(true);
564 * Copy bytes from src to dest while displaying a progressbar.
565 * The files must be already open.
567 static int copy_file(
568 int dest,
569 int src,
570 unsigned int bytes,
571 int prg_y,
572 int prg_h)
574 long button;
575 unsigned char *buffer;
576 unsigned int i = 0;
577 ssize_t bytes_read = 1; /* ensure the for loop is executed */
578 size_t buffer_size;
579 buffer = rb->plugin_get_buffer(&buffer_size);
581 for (i = 0; i < bytes && bytes_read > 0; i += bytes_read)
583 ssize_t bytes_written;
584 unsigned int bytes_to_read =
585 bytes - i > buffer_size ? buffer_size : bytes - i;
586 bytes_read = rb->read(src, buffer, bytes_to_read);
587 bytes_written = rb->write(dest, buffer, bytes_read);
589 if (bytes_written < 0) {
590 rb->splash(0, "Write failed in copy.");
591 rb->button_get(true);
592 rb->button_get(true);
593 return -1;
596 button = rb->button_get(false);
598 if (button == SPLITEDIT_QUIT
599 #ifdef SPLITEDIT_RC_QUIT
600 || button == SPLITEDIT_RC_QUIT:
601 #endif
603 rb->splash(0, "Aborting copy.");
604 rb->button_get(true);
605 rb->button_get(true);
606 return -1;
609 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, prg_y, LCD_WIDTH,
610 prg_h, bytes, 0, i, HORIZONTAL);
611 rb->lcd_update_rect(0, prg_y, LCD_WIDTH, prg_h);
614 return 0;
618 * Save the files, if the file_name is not NULL
620 static int save(
621 struct mp3entry *mp3,
622 char *file_name1,
623 char *file_name2,
624 int splittime)
626 int file1, file2, src_file;
627 unsigned int end = 0;
628 int retval = 0;
630 /* Verify that file 1 doesn't exit yet */
631 if (file_name1 != NULL)
633 file1 = rb->open(file_name1, O_RDONLY);
634 if (file1 >= 0)
636 rb->close(file1);
637 rb->splash(0, "File 1 exists. Please rename.");
638 rb->button_get(true);
639 rb->button_get(true);
640 return -1;
644 /* Verify that file 2 doesn't exit yet */
645 if (file_name2 != NULL)
647 file2 = rb->open(file_name2, O_RDONLY);
648 if (file2 >= 0)
650 rb->close(file2);
651 rb->splash(0, "File 2 exists. Please rename.");
652 rb->button_get(true);
653 rb->button_get(true);
654 return -2;
658 /* find the file position of the split point */
659 rb->audio_pause();
660 rb->audio_ff_rewind(splittime);
661 rb->yield();
662 rb->yield();
663 end = rb->audio_get_file_pos();
665 /* open the source file */
666 src_file = rb->open(mp3->path, O_RDONLY);
667 if (src_file >= 0)
669 int close_stat = 0;
670 int x, y;
671 long offset;
672 unsigned long last_header = rb->mpeg_get_last_header();
674 rb->lcd_getstringsize("M", &x, &y);
676 /* Find the next frame boundary */
677 rb->lseek(src_file, end, SEEK_SET);
678 rb->find_next_frame(src_file, &offset, 8000, last_header);
679 rb->lseek(src_file, 0, SEEK_SET);
680 end += offset;
682 /* write the file 1 */
683 if (file_name1 != NULL)
685 file1 = rb->open (file_name1, O_WRONLY | O_CREAT);
686 if (file1 >= 0)
688 int rc = copy_file(file1, src_file, end, y*2 + 1, y -1);
689 close_stat = rb->close(file1);
691 if (close_stat != 0)
693 rb->splashf(0, "failed closing file1: error %d", close_stat);
694 rb->button_get(true);
695 rb->button_get(true);
696 } else {
697 /* If there was an error, cleanup */
698 if (rc) {
699 rb->remove(file_name1);
703 else
705 rb->splashf(0, "Can't write File1: error %d", file1);
706 rb->button_get(true);
707 rb->button_get(true);
708 retval = -1;
711 /* if file1 hasn't been written we're not at the split point yet */
712 else
714 if (rb->lseek(src_file, end, SEEK_SET) < (off_t)end)
716 rb->splashf(0, "Src file to short: error %d", src_file);
717 rb->button_get(true);
718 rb->button_get(true);
722 if (file_name2 != NULL)
724 /* write file 2 */
725 file2 = rb->open (file_name2, O_WRONLY | O_CREAT);
726 if (file2 >= 0)
728 end = mp3->filesize - end;
729 int rc = copy_file(file2, src_file, end, y * 5 + 1, y -1);
730 close_stat = rb->close(file2);
732 if (close_stat != 0)
734 rb->splashf(0, "failed: closing file2: error %d",
735 close_stat);
736 rb->button_get(true);
737 rb->button_get(true);
738 } else {
739 /* If there was an error, cleanup */
740 if (rc) {
741 rb->remove(file_name2);
745 else
747 rb->splashf(0, "Can't write File2: error %d", file2);
748 rb->button_get(true);
749 rb->button_get(true);
750 retval = -2;
754 close_stat = rb->close(src_file);
755 if (close_stat != 0)
757 rb->splashf(0, "failed: closing src: error %d", close_stat);
758 rb->button_get(true);
759 rb->button_get(true);
762 else
764 rb->splash(0, "Source file not found");
765 rb->button_get(true);
766 rb->button_get(true);
767 retval = -3;
770 rb->audio_resume();
772 return retval;
776 * Let the user choose which file to save with which name
778 static void save_editor(struct mp3entry *mp3, int splittime)
780 bool exit_request = false;
781 int choice = 0;
782 int button = BUTTON_NONE;
783 char part1_name [MAX_PATH];
784 char part2_name [MAX_PATH];
785 bool part1_save = true;
786 bool part2_save = true;
788 /* file name for left part */
789 rb->strncpy(part1_name, mp3->path, MAX_PATH);
790 generateFileName(part1_name, 1);
792 /* file name for right part */
793 rb->strncpy(part2_name, mp3->path, MAX_PATH);
794 generateFileName(part2_name, 2);
796 while (!exit_request)
798 int pos;
799 rb->lcd_clear_display();
801 /* Save file1? */
802 rb->lcd_puts_style(0, 0, "Save part 1?", choice == SE_PART1_SAVE);
803 rb->lcd_puts(13, 0, part1_save?"yes":"no");
805 /* trim to display the filename without path */
806 for (pos = rb->strlen(part1_name); pos > 0; pos--)
808 if (part1_name[pos] == '/')
809 break;
811 pos++;
813 /* File name 1 */
814 rb->lcd_puts_scroll_style(0, 1,
815 &part1_name[pos], choice == SE_PART1_NAME);
817 /* Save file2? */
818 rb->lcd_puts_style(0, 3, "Save part 2?", choice == SE_PART2_SAVE);
819 rb->lcd_puts(13, 3, part2_save?"yes":"no");
821 /* trim to display the filename without path */
822 for (pos = rb->strlen(part2_name); pos > 0; pos --)
824 if (part2_name[pos] == '/')
825 break;
827 pos++;
829 /* File name 2 */
830 rb->lcd_puts_scroll_style(0, 4,
831 &part2_name[pos], choice == SE_PART2_NAME);
833 /* Save */
834 rb->lcd_puts_style(0, 6, "Save", choice == SE_SAVE);
836 rb->lcd_update();
839 button = rb->button_get(true);
840 switch (button)
842 case BUTTON_UP:
843 choice = (choice + SE_COUNT - 1) % SE_COUNT;
844 break;
846 case BUTTON_DOWN:
847 choice = (choice + 1) % SE_COUNT;
848 break;
850 case SPLITEDIT_MENU_RUN:
851 switch (choice)
853 int saved;
855 case SE_PART1_SAVE:
856 part1_save = !part1_save;
857 break;
859 case SE_PART1_NAME:
860 rb->kbd_input(part1_name, MAX_PATH);
861 break;
863 case SE_PART2_SAVE:
864 part2_save = !part2_save;
865 break;
867 case SE_PART2_NAME:
868 rb->kbd_input(part2_name, MAX_PATH);
869 break;
871 case SE_SAVE:
872 rb->lcd_stop_scroll();
873 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
874 rb->lcd_fillrect(0, 6*8, LCD_WIDTH, LCD_HEIGHT);
875 rb->lcd_set_drawmode(DRMODE_SOLID);
876 saved = save
878 mp3,
879 part1_save?part1_name:NULL,
880 part2_save?part2_name:NULL,
881 splittime
884 /* if something failed the user may go on choosing */
885 if (saved >= 0)
887 exit_request = true;
889 break;
891 break;
892 #ifdef SPLITEDIT_RC_QUIT
893 case SPLITEDIT_RC_QUIT:
894 #endif
895 case SPLITEDIT_QUIT:
896 exit_request = true;
897 break;
899 default:
900 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
902 splitedit_exit_code = PLUGIN_USB_CONNECTED;
903 exit_request = true;
905 break;
911 * The main loop of the editor
913 unsigned long splitedit_editor(struct mp3entry * mp3_to_split,
914 unsigned int split_time,
915 unsigned int range)
917 int button = BUTTON_NONE;
918 int lastbutton = BUTTON_NONE;
919 struct mp3entry *mp3 = mp3_to_split;
920 unsigned int last_elapsed = 0;
921 int lastx = OSCI_X + (OSCI_WIDTH / 2);
922 int retval = -1;
924 if (mp3 != NULL)
926 /*unsigned short scheme = SCHEME_SPLIT_EDITOR;*/
927 bool exit_request = false;
928 set_range_by_time(mp3, split_time, range);
929 splitedit_set_loop_mode(LOOP_MODE_ALL);
930 update_icons();
932 /*while (scheme != SCHEME_RETURN) {*/
933 while (!exit_request)
935 unsigned int elapsed ;
936 int x ;
938 /* get position */
939 elapsed = mp3->elapsed;
940 x = time_to_xpos(elapsed);
942 /* are we still in the zoomed range? */
943 if (elapsed > play_start && elapsed < play_end)
945 /* read volume info */
946 unsigned short volume;
947 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
948 volume = rb->mas_codec_readreg(0x0c);
949 volume += rb->mas_codec_readreg(0x0d);
950 volume = volume / 2;
951 volume = rb->peak_meter_scale_value(volume, OSCI_HEIGHT);
952 #else
953 volume = OSCI_HEIGHT / 2;
954 #endif
956 /* update osci_buffer */
957 if (osci_valid || lastx == x)
959 int index = x - OSCI_X;
960 osci_buffer[index] = MAX(osci_buffer[index], volume);
962 else
964 int i;
965 osci_buffer[x - OSCI_X] = volume;
966 for (i = lastx + 1; i < x; i++)
968 osci_buffer[i - OSCI_X] = 0;
972 /* make room */
973 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
974 rb->lcd_fillrect(lastx + 1, OSCI_Y, x - lastx, OSCI_HEIGHT);
975 rb->lcd_set_drawmode(DRMODE_SOLID);
976 /* draw a value */
977 if (osci_buffer[x - OSCI_X] > 0)
979 int i;
980 for (i = lastx +1; i <= x; i++)
982 rb->lcd_vline
984 i, OSCI_Y + OSCI_HEIGHT - 1,
985 OSCI_Y + OSCI_HEIGHT - osci_buffer[i - OSCI_X]-1
990 /* mark the current position */
991 if (lastx != x)
993 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
994 rb->lcd_fillrect(lastx, OSCI_Y, 1, OSCI_HEIGHT);
995 rb->lcd_fillrect(x, OSCI_Y, 1, OSCI_HEIGHT);
996 rb->lcd_set_drawmode(DRMODE_SOLID);
999 /* mark the split point */
1000 if ((x > split_x - 2) && (lastx < split_x + 3))
1002 if ((lastx < split_x) && (x >= split_x))
1004 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1005 rb->lcd_fillrect
1007 split_x, OSCI_Y + 2,
1008 1, OSCI_HEIGHT - 2
1010 rb->lcd_set_drawmode(DRMODE_SOLID);
1012 rb->lcd_hline(split_x -2, split_x + 2, OSCI_Y);
1013 rb->lcd_hline(split_x-1, split_x +1,OSCI_Y+1);
1016 /* make visible */
1017 if (lastx <= x)
1019 rb->lcd_update_rect(lastx, OSCI_Y, x-lastx+1, OSCI_HEIGHT);
1021 else
1023 rb->lcd_update_rect
1025 lastx, OSCI_Y,
1026 OSCI_X + OSCI_WIDTH - lastx, OSCI_HEIGHT
1028 rb->lcd_update_rect(0, OSCI_Y, x + 1, OSCI_HEIGHT);
1031 lastx = x;
1034 /* we're not in the zoom range -> rewind */
1035 else
1037 if (elapsed >= play_end)
1039 switch (splitedit_get_loop_mode())
1041 unsigned int range_width;
1043 case LOOP_MODE_ALL:
1044 case LOOP_MODE_TO:
1045 rb->audio_pause();
1046 rb->audio_ff_rewind(range_start);
1047 #if (CONFIG_STORAGE & STORAGE_MMC)
1048 /* MMC is slow - wait some time to allow track reload to finish */
1049 rb->sleep(HZ/20);
1050 if (mp3->elapsed > play_end) /* reload in progress */
1051 rb->splash(10*HZ, "Wait - reloading");
1052 #endif
1053 rb->audio_resume();
1054 break;
1056 case LOOP_MODE_FROM:
1057 rb->audio_pause();
1058 rb->audio_ff_rewind(xpos_to_time(split_x));
1059 #if (CONFIG_STORAGE & STORAGE_MMC)
1060 /* MMC is slow - wait some time to allow track reload to finish */
1061 rb->sleep(HZ/20);
1062 if (mp3->elapsed > play_end) /* reload in progress */
1063 rb->splash(10*HZ, "Wait - reloading");
1064 #endif
1065 rb->audio_resume();
1066 break;
1068 case LOOP_MODE_FREE:
1069 range_width = range_end - range_start;
1070 set_range_by_time(mp3,
1071 range_end + range_width / 2, range_width);
1073 /* play_end und play_start anpassen */
1074 splitedit_set_loop_mode(LOOP_MODE_FREE);
1075 rb->memset(osci_buffer, 0, sizeof osci_buffer);
1076 update_osci();
1077 rb->lcd_update();
1078 break;
1083 button = rb->button_get(false);
1084 rb->yield();
1086 /* here the evaluation of the key scheme starts.
1087 All functions the user triggers are called from
1088 within execute_scheme */
1089 /* key_scheme_execute(button, &scheme); */
1090 switch (button)
1092 case SPLITEDIT_PLAY:
1093 #ifdef SPLITEDIT_PLAY_PRE
1094 if (lastbutton != SPLITEDIT_PLAY_PRE)
1095 break;
1096 #endif
1097 rb->audio_pause();
1098 rb->audio_ff_rewind(xpos_to_time(split_x));
1099 rb->audio_resume();
1100 break;
1102 case BUTTON_UP:
1103 splitedit_zoom_in(mp3);
1104 lastx = time_to_xpos(mp3->elapsed);
1105 break;
1107 case BUTTON_DOWN:
1108 splitedit_zoom_out(mp3);
1109 lastx = time_to_xpos(mp3->elapsed);
1110 break;
1112 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1113 #ifdef SPLITEDIT_SPEED100
1114 case SPLITEDIT_SPEED150:
1115 rb->sound_set_pitch(1500);
1116 splitedit_invalidate_osci();
1117 break;
1119 case SPLITEDIT_SPEED100:
1120 rb->sound_set_pitch(1000);
1121 splitedit_invalidate_osci();
1122 break;
1124 case SPLITEDIT_SPEED50:
1125 rb->sound_set_pitch(500);
1126 splitedit_invalidate_osci();
1127 break;
1128 #endif
1129 #endif
1131 case BUTTON_LEFT:
1132 case BUTTON_LEFT | BUTTON_REPEAT:
1133 if (splitedit_get_split_x() > OSCI_X + 2)
1135 splitedit_set_split_x(splitedit_get_split_x() - 1);
1137 else
1139 scroll(mp3);
1140 lastx = time_to_xpos(mp3->elapsed);
1142 break;
1144 case BUTTON_RIGHT:
1145 case BUTTON_RIGHT | BUTTON_REPEAT:
1146 if (splitedit_get_split_x() < OSCI_X + OSCI_WIDTH-3)
1148 splitedit_set_split_x(splitedit_get_split_x() + 1);
1150 else
1152 scroll(mp3);
1153 lastx = time_to_xpos(mp3->elapsed);
1155 break;
1157 case SPLITEDIT_SAVE:
1158 save_editor(mp3, xpos_to_time(split_x));
1159 rb->lcd_clear_display();
1160 update_osci();
1161 update_timebar(mp3);
1162 update_icons();
1163 break;
1165 case SPLITEDIT_LOOP_MODE:
1166 splitedit_set_loop_mode(splitedit_get_loop_mode() + 1);
1167 update_icons();
1168 break;
1170 case SPLITEDIT_SCALE:
1171 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1172 rb->peak_meter_set_use_dbfs(!rb->peak_meter_get_use_dbfs());
1173 #endif
1174 splitedit_invalidate_osci();
1175 update_icons();
1176 break;
1178 #ifdef SPLITEDIT_RC_QUIT
1179 case SPLITEDIT_RC_QUIT:
1180 #endif
1181 case SPLITEDIT_QUIT:
1182 exit_request = true;
1183 break;
1185 default:
1186 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1188 splitedit_exit_code = PLUGIN_USB_CONNECTED;
1189 exit_request = true;
1191 break;
1194 if (button != BUTTON_NONE)
1195 lastbutton = button;
1197 if (validation_start == ~(unsigned int)0)
1199 if (elapsed < range_end && elapsed > range_start)
1201 validation_start = elapsed;
1203 else
1205 int endx = time_to_xpos(range_end);
1206 validation_start = xpos_to_time(endx - 2);
1208 last_elapsed = elapsed + 1;
1210 else
1212 if ((last_elapsed <= validation_start) &&
1213 (elapsed > validation_start))
1215 osci_valid = true;
1218 last_elapsed = elapsed;
1220 update_data();
1222 if (mp3 != rb->audio_current_track())
1224 struct mp3entry *new_mp3 = rb->audio_current_track();
1225 if (rb->strncasecmp(path_mp3, new_mp3->path,
1226 sizeof (path_mp3)))
1228 rb->splash(0, "Abort due to file change");
1229 rb->button_get(true);
1230 rb->button_get(true);
1231 exit_request = true;
1233 else
1235 mp3 = new_mp3;
1236 rb->audio_pause();
1237 rb->audio_flush_and_reload_tracks();
1238 rb->audio_ff_rewind(range_start);
1239 rb->audio_resume();
1243 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1244 #ifdef SPLITEDIT_SPEED100
1245 rb->sound_set_pitch(1000); /* make sure to reset pitch */
1246 #endif
1247 #endif
1250 return retval;
1253 enum plugin_status plugin_start(const struct plugin_api* api, const void* parameter)
1255 struct mp3entry* mp3;
1257 (void)parameter;
1258 rb = api;
1259 rb->lcd_clear_display();
1260 rb->lcd_update();
1261 mp3 = rb->audio_current_track();
1262 if (mp3 != NULL)
1264 if (rb->audio_status() & AUDIO_STATUS_PAUSE)
1266 rb->audio_resume();
1268 splitedit_editor(mp3, mp3->elapsed, MIN_RANGE_SIZE * 8);
1270 else
1272 rb->splash(0, "Play or pause a mp3 file first.");
1273 rb->button_get(true);
1274 rb->button_get(true);
1276 return splitedit_exit_code;
1278 #endif
1279 #endif