Add proper svn:mime-type
[maemo-rb.git] / apps / plugins / splitedit.c
blobc3562f09535b381f9a6f401f905686e0a8f2bd5e
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"
26 /* variable button definitions */
27 #if CONFIG_KEYPAD == RECORDER_PAD
28 #define SPLITEDIT_QUIT BUTTON_OFF
29 #define SPLITEDIT_PLAY BUTTON_PLAY
30 #define SPLITEDIT_SAVE BUTTON_F1
31 #define SPLITEDIT_LOOP_MODE BUTTON_F2
32 #define SPLITEDIT_SCALE BUTTON_F3
33 #define SPLITEDIT_SPEED50 (BUTTON_ON | BUTTON_LEFT)
34 #define SPLITEDIT_SPEED100 (BUTTON_ON | BUTTON_PLAY)
35 #define SPLITEDIT_SPEED150 (BUTTON_ON | BUTTON_RIGHT)
36 #define SPLITEDIT_MENU_RUN BUTTON_PLAY
38 #elif CONFIG_KEYPAD == ONDIO_PAD
39 #define SPLITEDIT_QUIT BUTTON_OFF
40 #define SPLITEDIT_PLAY_PRE BUTTON_MENU
41 #define SPLITEDIT_PLAY (BUTTON_MENU | BUTTON_REL)
42 #define SPLITEDIT_SAVE (BUTTON_MENU | BUTTON_LEFT)
43 #define SPLITEDIT_LOOP_MODE (BUTTON_MENU | BUTTON_UP)
44 #define SPLITEDIT_SCALE (BUTTON_MENU | BUTTON_RIGHT)
45 #define SPLITEDIT_MENU_RUN BUTTON_RIGHT
47 #elif CONFIG_KEYPAD == IRIVER_H100_PAD
48 #define SPLITEDIT_QUIT BUTTON_OFF
49 #define SPLITEDIT_PLAY BUTTON_ON
50 #define SPLITEDIT_SAVE BUTTON_SELECT
51 #define SPLITEDIT_LOOP_MODE BUTTON_MODE
52 #define SPLITEDIT_SCALE (BUTTON_REC | BUTTON_UP)
53 #define SPLITEDIT_SPEED50 (BUTTON_REC | BUTTON_LEFT)
54 #define SPLITEDIT_SPEED100 (BUTTON_REC | BUTTON_DOWN)
55 #define SPLITEDIT_SPEED150 (BUTTON_REC | BUTTON_RIGHT)
56 #define SPLITEDIT_MENU_RUN BUTTON_RIGHT
58 #elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
59 #define SPLITEDIT_QUIT (BUTTON_REC | BUTTON_REW)
60 #define SPLITEDIT_PLAY (BUTTON_REC | BUTTON_FFWD)
61 #define SPLITEDIT_SAVE BUTTON_FFWD
62 #define SPLITEDIT_LOOP_MODE BUTTON_REW
63 #define SPLITEDIT_SCALE BUTTON_UP
64 #define SPLITEDIT_SPEED50 BUTTON_LEFT
65 #define SPLITEDIT_SPEED100 BUTTON_DOWN
66 #define SPLITEDIT_SPEED150 BUTTON_RIGHT
67 #define SPLITEDIT_MENU_RUN BUTTON_PLAY
69 #define SPLITEDIT_RC_QUIT BUTTON_RC_STOP
70 #endif
72 #define BMPHEIGHT 7
73 #define BMPWIDTH 13
74 unsigned char LOOP_BMP[][13] =
76 {0xfc,0x00,0x10,0x11,0x93,0x7f,0x13,0x11,0x7c,0x38,0x10,0x00,0x7c}, /*ALL */
77 {0x81,0x03,0x7f,0x03,0x91,0x10,0x10,0x10,0x7c,0x38,0x10,0x00,0x7c}, /*FROM*/
78 {0xfc,0x00,0x10,0x10,0x90,0x10,0x7c,0x38,0x11,0x03,0x7f,0x03,0x01}, /*TO */
79 {0x80,0x10,0x10,0x11,0x93,0x7f,0x13,0x11,0x10,0x7c,0x38,0x10,0x00}, /*FREE*/
82 unsigned char CUT_BMP[] =
84 0xc1,0x63,0x63,0x36,0xb6,0x1c,0x1c,0x36,0x77,0x55,0x55,0x55,0x32,
87 unsigned char SCALE_BMP[][13] =
89 {0x80,0x06,0x49,0x66,0xb0,0x18,0x0c,0x06,0x33,0x49,0x30,0x00,0x00}, /*lin*/
90 {0x80,0x30,0x78,0x48,0xff,0x7f,0x00,0x7f,0x7f,0x48,0x78,0x30,0x00}, /*db*/
93 #define TIMEBAR_Y 9
94 #define TIMEBAR_HEIGHT 4
96 #define OSCI_X 0
97 #define OSCI_Y (TIMEBAR_Y + TIMEBAR_HEIGHT + 1)
98 #define OSCI_WIDTH LCD_WIDTH
99 #define OSCI_HEIGHT (LCD_HEIGHT - BMPHEIGHT - OSCI_Y - 1)
101 /* Indices of the menu items in the save editor, see save_editor */
102 #define SE_PART1_SAVE 0
103 #define SE_PART1_NAME 1
104 #define SE_PART2_SAVE 2
105 #define SE_PART2_NAME 3
106 #define SE_SAVE 4
107 #define SE_COUNT 5
109 /* contains the file name of the song that is to be split */
110 static char path_mp3[MAX_PATH];
112 /* Exit code of this plugin */
113 static enum plugin_status splitedit_exit_code = PLUGIN_OK;
115 /* The range in time that the displayed aerea comprises */
116 static unsigned int range_start = 0;
117 static unsigned int range_end = 0;
119 /* The range in time that is being looped */
120 static unsigned int play_start = 0;
121 static unsigned int play_end = 0;
123 /* Point in time (pixel) at which the split mark is set */
124 static int split_x = OSCI_X + (OSCI_WIDTH / 2);
126 /* Contains the peak values */
127 static unsigned char osci_buffer[OSCI_WIDTH];
129 /* if true peak values from a previous loop are only overwritten
130 if the new value is greater than the old value */
131 static bool osci_valid = false;
134 * point in time from which on the osci_buffer is invalid
135 * if set to ~(unsigned int)0 the entire osci_buffer is invalid
137 static unsigned int validation_start = ~(unsigned int)0;
139 /* all the visible aerea is looped */
140 #define LOOP_MODE_ALL 0
142 /* loop starts at split point, ends at right visible border */
143 #define LOOP_MODE_FROM 1
145 /* loop start at left visible border, ends at split point */
146 #define LOOP_MODE_TO 2
148 /* let the song play without looping */
149 #define LOOP_MODE_FREE 3
151 /* see LOOP_MODE_XXX constants vor valid values */
152 static int loop_mode = LOOP_MODE_FREE;
154 /* minimal allowed timespan (ms) of the visible (and looped) aerea*/
155 #define MIN_RANGE_SIZE 1000
157 /* Format time into buf.
159 * buf - buffer to format to.
160 * buf_size - size of buffer.
161 * time - time to format, in milliseconds.
163 static void format_time_ms(char* buf, int buf_size, int time)
165 rb->snprintf(buf, buf_size, "%d:%02d:%03d", time / 60000,
166 time % 60000 / 1000, (time % 60000) % 1000);
170 * converts screen coordinate (pixel) to time (ms)
172 static int xpos_to_time(int xpos)
174 int retval = 0;
175 int range = range_end - range_start;
176 retval = range_start + (((xpos - OSCI_X) * range) / OSCI_WIDTH);
177 return retval;
181 * Converts time (ms) to screen coordinates (pixel).
183 static int time_to_xpos(unsigned int time)
185 int retval = OSCI_X;
187 /* clip the range */
188 if (time < range_start)
190 retval = OSCI_X;
192 else
193 if (time >= range_end)
195 retval = OSCI_X + OSCI_WIDTH;
198 /* do the calculation */
199 else
201 int range = range_end - range_start;
202 retval = OSCI_X + ((time - range_start) * OSCI_WIDTH) / range ;
204 return retval;
208 * Updates the display of the textual data only.
210 static void update_data(void)
212 char buf[20];
213 char timebuf[10];
214 int w, h;
216 /* split point */
217 format_time_ms(timebuf, sizeof timebuf, xpos_to_time(split_x));
218 rb->snprintf(buf, sizeof buf, "Split at: %s", timebuf);
220 rb->lcd_getstringsize(buf, &w, &h);
222 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
223 rb->lcd_fillrect(0, 0, LCD_WIDTH, h);
224 rb->lcd_set_drawmode(DRMODE_SOLID);
225 rb->lcd_puts(0, 0, buf);
226 rb->lcd_update_rect(0, 0, LCD_WIDTH, h);
230 * Displays which part of the song is visible
231 * in the osci.
233 static void update_timebar(struct mp3entry *mp3)
235 rb->gui_scrollbar_draw
237 rb->screens[SCREEN_MAIN],0, TIMEBAR_Y, LCD_WIDTH, TIMEBAR_HEIGHT,
238 mp3->length, range_start, range_end,
239 HORIZONTAL
241 rb->lcd_update_rect(0, TIMEBAR_Y, LCD_WIDTH, TIMEBAR_HEIGHT);
245 * Marks the entire area of the osci buffer invalid.
246 * It will be drawn with new values in the next loop.
248 void splitedit_invalidate_osci(void)
250 osci_valid = false;
251 validation_start = ~(unsigned int)0;
255 * Returns the loop mode. See the LOOP_MODE_XXX constants above.
257 int splitedit_get_loop_mode(void)
259 return loop_mode;
263 * Updates the icons that display the Fn key hints.
265 static void update_icons(void)
267 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
268 rb->lcd_fillrect(0, LCD_HEIGHT - BMPHEIGHT, LCD_WIDTH, BMPHEIGHT);
269 rb->lcd_set_drawmode(DRMODE_SOLID);
271 /* The CUT icon */
272 rb->lcd_mono_bitmap(CUT_BMP,
273 LCD_WIDTH / 3 / 2 - BMPWIDTH / 2, LCD_HEIGHT - BMPHEIGHT,
274 BMPWIDTH, BMPHEIGHT);
276 /* The loop mode icon */
277 rb->lcd_mono_bitmap(LOOP_BMP[splitedit_get_loop_mode()],
278 LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
279 BMPWIDTH, BMPHEIGHT);
281 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
282 /* The scale icon */
283 rb->lcd_mono_bitmap(SCALE_BMP[rb->peak_meter_get_use_dbfs() ? 1 : 0],
284 2 *LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
285 BMPWIDTH, BMPHEIGHT);
286 #else
288 static int idx;
289 if (idx < 0 || idx > 1) idx = 0;
290 idx = 1 - idx;
291 rb->lcd_mono_bitmap(SCALE_BMP[idx],
292 2 *LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
293 BMPWIDTH, BMPHEIGHT);
295 #endif
297 rb->lcd_update_rect(0, LCD_HEIGHT - BMPHEIGHT, LCD_WIDTH, BMPHEIGHT);
301 * Sets the loop mode. See the LOOP_MODE_XXX constants above.
303 void splitedit_set_loop_mode(int mode)
305 int old_loop_mode = loop_mode;
306 /* range restriction */
307 loop_mode = mode % (LOOP_MODE_FREE + 1);
308 switch (loop_mode)
310 case LOOP_MODE_ALL:
311 play_start = range_start;
312 play_end = range_end;
313 break;
315 case LOOP_MODE_FROM:
316 play_start = xpos_to_time(split_x);
317 play_end = range_end;
318 break;
320 case LOOP_MODE_TO:
321 play_start = range_start;
322 play_end = xpos_to_time(split_x);
323 break;
325 case LOOP_MODE_FREE:
326 /* play_start is used when the song plays beyond its end */
327 play_start = range_start;
328 play_end = range_end;
329 break;
332 if (loop_mode != old_loop_mode)
334 update_icons();
339 * Readraws the osci without clear.
341 static void redraw_osci(void)
343 int x;
344 for (x = 0; x < OSCI_WIDTH; x++)
346 if (osci_buffer[x] > 0)
348 rb->lcd_vline
350 OSCI_X + x, OSCI_Y + OSCI_HEIGHT - 1,
351 OSCI_Y + OSCI_HEIGHT - osci_buffer[x] - 1
358 * Sets the range of time in which the user can finetune the split
359 * point. The split point is the center of the time range.
361 static void set_range_by_time(
362 struct mp3entry *mp3,
363 unsigned int split_time,
364 unsigned int range)
366 if (mp3 != NULL)
368 if (range < MIN_RANGE_SIZE)
370 range = MIN_RANGE_SIZE;
372 range_start = (split_time > range / 2) ? (split_time - range / 2) : 0;
373 range_end = MIN(range_start + range, mp3->length);
374 split_x = time_to_xpos(split_time);
376 splitedit_invalidate_osci();
378 /* this sets the play_start / play_end */
379 splitedit_set_loop_mode(splitedit_get_loop_mode());
381 update_data();
382 update_timebar(mp3);
387 * Set the split point in screen coordinates
389 void splitedit_set_split_x(int newx)
391 int minx = split_x - 2 > 0 ? split_x - 2: 0;
393 /* remove old split point from screen, only if moved */
394 if (split_x != newx)
396 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
397 rb->lcd_fillrect(minx, OSCI_Y, 5, 1);
398 rb->lcd_fillrect(split_x-1 > 0 ? split_x - 1: 0, OSCI_Y + 1, 3, 1);
399 rb->lcd_fillrect(split_x, OSCI_Y + 2, 1, OSCI_HEIGHT - 2);
400 rb->lcd_set_drawmode(DRMODE_SOLID);
401 rb->lcd_update_rect(minx, OSCI_Y, 5, OSCI_HEIGHT);
404 if (newx >= OSCI_X && newx < OSCI_X + OSCI_WIDTH)
406 split_x = newx;
407 /* in LOOP_FROM / LOOP_TO modes play_start /play_end must be updated */
408 splitedit_set_loop_mode(splitedit_get_loop_mode());
410 /* display new split time */
411 update_data();
414 /* display new split point */
415 minx = split_x - 2 > 0 ? split_x - 2: 0;
416 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
417 rb->lcd_fillrect(minx, OSCI_Y, 5, 1);
418 rb->lcd_fillrect(split_x - 1 > 0 ? split_x - 1: 0, OSCI_Y + 1, 3, 1);
419 rb->lcd_fillrect(split_x, OSCI_Y + 2, 1, OSCI_HEIGHT - 2);
420 rb->lcd_set_drawmode(DRMODE_SOLID);
421 rb->lcd_update_rect(minx, OSCI_Y, 5, OSCI_HEIGHT);
425 * returns the split point in screen coordinates
427 int splitedit_get_split_x(void)
429 return split_x;
433 * Clears the osci area and redraws it
435 static void update_osci(void)
437 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
438 rb->lcd_fillrect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
439 rb->lcd_set_drawmode(DRMODE_SOLID);
440 redraw_osci();
441 splitedit_set_split_x(splitedit_get_split_x());
442 rb->lcd_update_rect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
446 * Zooms the visable and loopable range by the factor
447 * (counter / denominator). The split point is used as
448 * center point of the new selected range.
450 static void zoom(struct mp3entry *mp3, int counter, int denominator)
452 unsigned char oldbuf[OSCI_WIDTH];
453 int oldrange = range_end - range_start;
454 int range = oldrange * counter / denominator;
455 int i;
456 int oldindex;
457 int oldsplitx;
458 int splitx;
459 int split;
461 /* for stretching / shrinking a second buffer is needed */
462 rb->memcpy(&oldbuf, &osci_buffer, sizeof osci_buffer);
464 /* recalculate the new range and split point */
465 oldsplitx = split_x;
466 split = xpos_to_time(split_x);
468 set_range_by_time(mp3, split, range);
469 range = range_end - range_start;
471 splitx = time_to_xpos(split);
473 /* strech / shrink the existing osci buffer */
474 for (i = 0; i < OSCI_WIDTH; i++)
476 /* oldindex = (i + OSCI_X - splitx) * range / oldrange + oldsplitx ;*/
477 oldindex = (i*range / oldrange) + oldsplitx - (splitx*range /oldrange);
478 if (oldindex >= 0 && oldindex < OSCI_WIDTH)
480 osci_buffer[i] = oldbuf[oldindex];
482 else
484 osci_buffer[i] = 0;
488 splitx = time_to_xpos(split);
489 splitedit_set_split_x(splitx);
490 splitedit_invalidate_osci();
494 static void scroll(struct mp3entry *mp3)
496 zoom(mp3, 1, 1);
497 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
498 update_osci();
499 update_data();
503 * Zooms in by 3/4
505 void splitedit_zoom_in(struct mp3entry *mp3)
507 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
508 rb->lcd_fillrect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
509 rb->lcd_set_drawmode(DRMODE_SOLID);
510 zoom(mp3, 3, 4);
511 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
512 update_osci();
513 update_data();
517 * Zooms out by 4/3
519 void splitedit_zoom_out(struct mp3entry *mp3)
521 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
522 rb->lcd_fillrect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
523 rb->lcd_set_drawmode(DRMODE_SOLID);
524 zoom(mp3, 4, 3);
525 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
526 update_osci();
527 update_data();
531 * Append part_no to the file name.
533 static void generateFileName(char* file_name, int part_no)
535 if (rb->strlen(file_name) <MAX_PATH)
537 int len = rb->strlen(file_name);
538 int ext_len = rb->strlen(".mp3");
539 if (rb->strcasecmp(
540 &file_name[len - ext_len],
541 ".mp3") == 0)
543 int i = 0;
544 /* shift the extension one position to the right*/
545 for (i = len; i > len - ext_len; i--)
547 file_name[i] = file_name[i - 1];
549 file_name[len - ext_len] = '0' + part_no;
551 else
553 rb->splash(0, "wrong extension");
554 rb->button_get(true);
555 rb->button_get(true);
558 else
560 rb->splash(0, "name too long");
561 rb->button_get(true);
562 rb->button_get(true);
569 * Copy bytes from src to dest while displaying a progressbar.
570 * The files must be already open.
572 static int copy_file(
573 int dest,
574 int src,
575 unsigned int bytes,
576 int prg_y,
577 int prg_h)
579 long button;
580 unsigned char *buffer;
581 unsigned int i = 0;
582 ssize_t bytes_read = 1; /* ensure the for loop is executed */
583 size_t buffer_size;
584 buffer = rb->plugin_get_buffer(&buffer_size);
586 for (i = 0; i < bytes && bytes_read > 0; i += bytes_read)
588 ssize_t bytes_written;
589 unsigned int bytes_to_read =
590 bytes - i > buffer_size ? buffer_size : bytes - i;
591 bytes_read = rb->read(src, buffer, bytes_to_read);
592 bytes_written = rb->write(dest, buffer, bytes_read);
594 if (bytes_written < 0) {
595 rb->splash(0, "Write failed in copy.");
596 rb->button_get(true);
597 rb->button_get(true);
598 return -1;
601 button = rb->button_get(false);
603 if (button == SPLITEDIT_QUIT
604 #ifdef SPLITEDIT_RC_QUIT
605 || button == SPLITEDIT_RC_QUIT:
606 #endif
608 rb->splash(0, "Aborting copy.");
609 rb->button_get(true);
610 rb->button_get(true);
611 return -1;
614 rb->gui_scrollbar_draw(rb->screens[SCREEN_MAIN],0, prg_y, LCD_WIDTH,
615 prg_h, bytes, 0, i, HORIZONTAL);
616 rb->lcd_update_rect(0, prg_y, LCD_WIDTH, prg_h);
619 return 0;
623 * Save the files, if the file_name is not NULL
625 static int save(
626 struct mp3entry *mp3,
627 char *file_name1,
628 char *file_name2,
629 int splittime)
631 int file1, file2, src_file;
632 unsigned int end = 0;
633 int retval = 0;
635 /* Verify that file 1 doesn't exit yet */
636 if (file_name1 != NULL)
638 file1 = rb->open(file_name1, O_RDONLY);
639 if (file1 >= 0)
641 rb->close(file1);
642 rb->splash(0, "File 1 exists. Please rename.");
643 rb->button_get(true);
644 rb->button_get(true);
645 return -1;
649 /* Verify that file 2 doesn't exit yet */
650 if (file_name2 != NULL)
652 file2 = rb->open(file_name2, O_RDONLY);
653 if (file2 >= 0)
655 rb->close(file2);
656 rb->splash(0, "File 2 exists. Please rename.");
657 rb->button_get(true);
658 rb->button_get(true);
659 return -2;
663 /* find the file position of the split point */
664 rb->audio_pause();
665 rb->audio_ff_rewind(splittime);
666 rb->yield();
667 rb->yield();
668 end = rb->audio_get_file_pos();
670 /* open the source file */
671 src_file = rb->open(mp3->path, O_RDONLY);
672 if (src_file >= 0)
674 int close_stat = 0;
675 int x, y;
676 long offset;
677 unsigned long last_header = rb->mpeg_get_last_header();
679 rb->lcd_getstringsize("M", &x, &y);
681 /* Find the next frame boundary */
682 rb->lseek(src_file, end, SEEK_SET);
683 rb->find_next_frame(src_file, &offset, 8000, last_header);
684 rb->lseek(src_file, 0, SEEK_SET);
685 end += offset;
687 /* write the file 1 */
688 if (file_name1 != NULL)
690 file1 = rb->open (file_name1, O_WRONLY | O_CREAT, 0666);
691 if (file1 >= 0)
693 int rc = copy_file(file1, src_file, end, y*2 + 1, y -1);
694 close_stat = rb->close(file1);
696 if (close_stat != 0)
698 rb->splashf(0, "failed closing file1: error %d", close_stat);
699 rb->button_get(true);
700 rb->button_get(true);
701 } else {
702 /* If there was an error, cleanup */
703 if (rc) {
704 rb->remove(file_name1);
708 else
710 rb->splashf(0, "Can't write File1: error %d", file1);
711 rb->button_get(true);
712 rb->button_get(true);
713 retval = -1;
716 /* if file1 hasn't been written we're not at the split point yet */
717 else
719 if (rb->lseek(src_file, end, SEEK_SET) < (off_t)end)
721 rb->splashf(0, "Src file to short: error %d", src_file);
722 rb->button_get(true);
723 rb->button_get(true);
727 if (file_name2 != NULL)
729 /* write file 2 */
730 file2 = rb->open (file_name2, O_WRONLY | O_CREAT, 0666);
731 if (file2 >= 0)
733 end = mp3->filesize - end;
734 int rc = copy_file(file2, src_file, end, y * 5 + 1, y -1);
735 close_stat = rb->close(file2);
737 if (close_stat != 0)
739 rb->splashf(0, "failed: closing file2: error %d",
740 close_stat);
741 rb->button_get(true);
742 rb->button_get(true);
743 } else {
744 /* If there was an error, cleanup */
745 if (rc) {
746 rb->remove(file_name2);
750 else
752 rb->splashf(0, "Can't write File2: error %d", file2);
753 rb->button_get(true);
754 rb->button_get(true);
755 retval = -2;
759 close_stat = rb->close(src_file);
760 if (close_stat != 0)
762 rb->splashf(0, "failed: closing src: error %d", close_stat);
763 rb->button_get(true);
764 rb->button_get(true);
767 else
769 rb->splash(0, "Source file not found");
770 rb->button_get(true);
771 rb->button_get(true);
772 retval = -3;
775 rb->audio_resume();
777 return retval;
781 * Let the user choose which file to save with which name
783 static void save_editor(struct mp3entry *mp3, int splittime)
785 bool exit_request = false;
786 int choice = 0;
787 int button = BUTTON_NONE;
788 char part1_name [MAX_PATH];
789 char part2_name [MAX_PATH];
790 bool part1_save = true;
791 bool part2_save = true;
793 /* file name for left part */
794 rb->strlcpy(part1_name, mp3->path, MAX_PATH);
795 generateFileName(part1_name, 1);
797 /* file name for right part */
798 rb->strlcpy(part2_name, mp3->path, MAX_PATH);
799 generateFileName(part2_name, 2);
801 while (!exit_request)
803 int pos;
804 rb->lcd_clear_display();
806 /* Save file1? */
807 rb->lcd_puts_style(0, 0, "Save part 1?", choice == SE_PART1_SAVE);
808 rb->lcd_puts(13, 0, part1_save?"yes":"no");
810 /* trim to display the filename without path */
811 for (pos = rb->strlen(part1_name); pos > 0; pos--)
813 if (part1_name[pos] == '/')
814 break;
816 pos++;
818 /* File name 1 */
819 rb->lcd_puts_scroll_style(0, 1,
820 &part1_name[pos], choice == SE_PART1_NAME);
822 /* Save file2? */
823 rb->lcd_puts_style(0, 3, "Save part 2?", choice == SE_PART2_SAVE);
824 rb->lcd_puts(13, 3, part2_save?"yes":"no");
826 /* trim to display the filename without path */
827 for (pos = rb->strlen(part2_name); pos > 0; pos --)
829 if (part2_name[pos] == '/')
830 break;
832 pos++;
834 /* File name 2 */
835 rb->lcd_puts_scroll_style(0, 4,
836 &part2_name[pos], choice == SE_PART2_NAME);
838 /* Save */
839 rb->lcd_puts_style(0, 6, "Save", choice == SE_SAVE);
841 rb->lcd_update();
844 button = rb->button_get(true);
845 switch (button)
847 case BUTTON_UP:
848 choice = (choice + SE_COUNT - 1) % SE_COUNT;
849 break;
851 case BUTTON_DOWN:
852 choice = (choice + 1) % SE_COUNT;
853 break;
855 case SPLITEDIT_MENU_RUN:
856 switch (choice)
858 int saved;
860 case SE_PART1_SAVE:
861 part1_save = !part1_save;
862 break;
864 case SE_PART1_NAME:
865 rb->kbd_input(part1_name, MAX_PATH);
866 break;
868 case SE_PART2_SAVE:
869 part2_save = !part2_save;
870 break;
872 case SE_PART2_NAME:
873 rb->kbd_input(part2_name, MAX_PATH);
874 break;
876 case SE_SAVE:
877 rb->lcd_stop_scroll();
878 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
879 rb->lcd_fillrect(0, 6*8, LCD_WIDTH, LCD_HEIGHT);
880 rb->lcd_set_drawmode(DRMODE_SOLID);
881 saved = save
883 mp3,
884 part1_save?part1_name:NULL,
885 part2_save?part2_name:NULL,
886 splittime
889 /* if something failed the user may go on choosing */
890 if (saved >= 0)
892 exit_request = true;
894 break;
896 break;
897 #ifdef SPLITEDIT_RC_QUIT
898 case SPLITEDIT_RC_QUIT:
899 #endif
900 case SPLITEDIT_QUIT:
901 exit_request = true;
902 break;
904 default:
905 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
907 splitedit_exit_code = PLUGIN_USB_CONNECTED;
908 exit_request = true;
910 break;
916 * The main loop of the editor
918 unsigned long splitedit_editor(struct mp3entry * mp3_to_split,
919 unsigned int split_time,
920 unsigned int range)
922 int button = BUTTON_NONE;
923 int lastbutton = BUTTON_NONE;
924 struct mp3entry *mp3 = mp3_to_split;
925 unsigned int last_elapsed = 0;
926 int lastx = OSCI_X + (OSCI_WIDTH / 2);
927 int retval = -1;
929 if (mp3 != NULL)
931 /*unsigned short scheme = SCHEME_SPLIT_EDITOR;*/
932 bool exit_request = false;
933 set_range_by_time(mp3, split_time, range);
934 splitedit_set_loop_mode(LOOP_MODE_ALL);
935 update_icons();
937 /*while (scheme != SCHEME_RETURN) {*/
938 while (!exit_request)
940 unsigned int elapsed ;
941 int x ;
943 /* get position */
944 elapsed = mp3->elapsed;
945 x = time_to_xpos(elapsed);
947 /* are we still in the zoomed range? */
948 if (elapsed > play_start && elapsed < play_end)
950 /* read volume info */
951 unsigned short volume;
952 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
953 volume = rb->mas_codec_readreg(0x0c);
954 volume += rb->mas_codec_readreg(0x0d);
955 volume = volume / 2;
956 volume = rb->peak_meter_scale_value(volume, OSCI_HEIGHT);
957 #else
958 volume = OSCI_HEIGHT / 2;
959 #endif
961 /* update osci_buffer */
962 if (osci_valid || lastx == x)
964 int index = x - OSCI_X;
965 osci_buffer[index] = MAX(osci_buffer[index], volume);
967 else
969 int i;
970 osci_buffer[x - OSCI_X] = volume;
971 for (i = lastx + 1; i < x; i++)
973 osci_buffer[i - OSCI_X] = 0;
977 /* make room */
978 rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
979 rb->lcd_fillrect(lastx + 1, OSCI_Y, x - lastx, OSCI_HEIGHT);
980 rb->lcd_set_drawmode(DRMODE_SOLID);
981 /* draw a value */
982 if (osci_buffer[x - OSCI_X] > 0)
984 int i;
985 for (i = lastx +1; i <= x; i++)
987 rb->lcd_vline
989 i, OSCI_Y + OSCI_HEIGHT - 1,
990 OSCI_Y + OSCI_HEIGHT - osci_buffer[i - OSCI_X]-1
995 /* mark the current position */
996 if (lastx != x)
998 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
999 rb->lcd_fillrect(lastx, OSCI_Y, 1, OSCI_HEIGHT);
1000 rb->lcd_fillrect(x, OSCI_Y, 1, OSCI_HEIGHT);
1001 rb->lcd_set_drawmode(DRMODE_SOLID);
1004 /* mark the split point */
1005 if ((x > split_x - 2) && (lastx < split_x + 3))
1007 if ((lastx < split_x) && (x >= split_x))
1009 rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
1010 rb->lcd_fillrect
1012 split_x, OSCI_Y + 2,
1013 1, OSCI_HEIGHT - 2
1015 rb->lcd_set_drawmode(DRMODE_SOLID);
1017 rb->lcd_hline(split_x -2, split_x + 2, OSCI_Y);
1018 rb->lcd_hline(split_x-1, split_x +1,OSCI_Y+1);
1021 /* make visible */
1022 if (lastx <= x)
1024 rb->lcd_update_rect(lastx, OSCI_Y, x-lastx+1, OSCI_HEIGHT);
1026 else
1028 rb->lcd_update_rect
1030 lastx, OSCI_Y,
1031 OSCI_X + OSCI_WIDTH - lastx, OSCI_HEIGHT
1033 rb->lcd_update_rect(0, OSCI_Y, x + 1, OSCI_HEIGHT);
1036 lastx = x;
1039 /* we're not in the zoom range -> rewind */
1040 else
1042 if (elapsed >= play_end)
1044 switch (splitedit_get_loop_mode())
1046 unsigned int range_width;
1048 case LOOP_MODE_ALL:
1049 case LOOP_MODE_TO:
1050 rb->audio_pause();
1051 rb->audio_ff_rewind(range_start);
1052 #if (CONFIG_STORAGE & STORAGE_MMC)
1053 /* MMC is slow - wait some time to allow track reload to finish */
1054 rb->sleep(HZ/20);
1055 if (mp3->elapsed > play_end) /* reload in progress */
1056 rb->splash(10*HZ, "Wait - reloading");
1057 #endif
1058 rb->audio_resume();
1059 break;
1061 case LOOP_MODE_FROM:
1062 rb->audio_pause();
1063 rb->audio_ff_rewind(xpos_to_time(split_x));
1064 #if (CONFIG_STORAGE & STORAGE_MMC)
1065 /* MMC is slow - wait some time to allow track reload to finish */
1066 rb->sleep(HZ/20);
1067 if (mp3->elapsed > play_end) /* reload in progress */
1068 rb->splash(10*HZ, "Wait - reloading");
1069 #endif
1070 rb->audio_resume();
1071 break;
1073 case LOOP_MODE_FREE:
1074 range_width = range_end - range_start;
1075 set_range_by_time(mp3,
1076 range_end + range_width / 2, range_width);
1078 /* play_end und play_start anpassen */
1079 splitedit_set_loop_mode(LOOP_MODE_FREE);
1080 rb->memset(osci_buffer, 0, sizeof osci_buffer);
1081 update_osci();
1082 rb->lcd_update();
1083 break;
1088 button = rb->button_get(false);
1089 rb->yield();
1091 /* here the evaluation of the key scheme starts.
1092 All functions the user triggers are called from
1093 within execute_scheme */
1094 /* key_scheme_execute(button, &scheme); */
1095 switch (button)
1097 case SPLITEDIT_PLAY:
1098 #ifdef SPLITEDIT_PLAY_PRE
1099 if (lastbutton != SPLITEDIT_PLAY_PRE)
1100 break;
1101 #endif
1102 rb->audio_pause();
1103 rb->audio_ff_rewind(xpos_to_time(split_x));
1104 rb->audio_resume();
1105 break;
1107 case BUTTON_UP:
1108 splitedit_zoom_in(mp3);
1109 lastx = time_to_xpos(mp3->elapsed);
1110 break;
1112 case BUTTON_DOWN:
1113 splitedit_zoom_out(mp3);
1114 lastx = time_to_xpos(mp3->elapsed);
1115 break;
1117 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1118 #ifdef SPLITEDIT_SPEED100
1119 case SPLITEDIT_SPEED150:
1120 rb->sound_set_pitch(150L*PITCH_SPEED_PRECISION);
1121 splitedit_invalidate_osci();
1122 break;
1124 case SPLITEDIT_SPEED100:
1125 rb->sound_set_pitch(PITCH_SPEED_100);
1126 splitedit_invalidate_osci();
1127 break;
1129 case SPLITEDIT_SPEED50:
1130 rb->sound_set_pitch(50L*PITCH_SPEED_PRECISION);
1131 splitedit_invalidate_osci();
1132 break;
1133 #endif
1134 #endif
1136 case BUTTON_LEFT:
1137 case BUTTON_LEFT | BUTTON_REPEAT:
1138 if (splitedit_get_split_x() > OSCI_X + 2)
1140 splitedit_set_split_x(splitedit_get_split_x() - 1);
1142 else
1144 scroll(mp3);
1145 lastx = time_to_xpos(mp3->elapsed);
1147 break;
1149 case BUTTON_RIGHT:
1150 case BUTTON_RIGHT | BUTTON_REPEAT:
1151 if (splitedit_get_split_x() < OSCI_X + OSCI_WIDTH-3)
1153 splitedit_set_split_x(splitedit_get_split_x() + 1);
1155 else
1157 scroll(mp3);
1158 lastx = time_to_xpos(mp3->elapsed);
1160 break;
1162 case SPLITEDIT_SAVE:
1163 save_editor(mp3, xpos_to_time(split_x));
1164 rb->lcd_clear_display();
1165 update_osci();
1166 update_timebar(mp3);
1167 update_icons();
1168 break;
1170 case SPLITEDIT_LOOP_MODE:
1171 splitedit_set_loop_mode(splitedit_get_loop_mode() + 1);
1172 update_icons();
1173 break;
1175 case SPLITEDIT_SCALE:
1176 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1177 rb->peak_meter_set_use_dbfs(!rb->peak_meter_get_use_dbfs());
1178 #endif
1179 splitedit_invalidate_osci();
1180 update_icons();
1181 break;
1183 #ifdef SPLITEDIT_RC_QUIT
1184 case SPLITEDIT_RC_QUIT:
1185 #endif
1186 case SPLITEDIT_QUIT:
1187 exit_request = true;
1188 break;
1190 default:
1191 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
1193 splitedit_exit_code = PLUGIN_USB_CONNECTED;
1194 exit_request = true;
1196 break;
1199 if (button != BUTTON_NONE)
1200 lastbutton = button;
1202 if (validation_start == ~(unsigned int)0)
1204 if (elapsed < range_end && elapsed > range_start)
1206 validation_start = elapsed;
1208 else
1210 int endx = time_to_xpos(range_end);
1211 validation_start = xpos_to_time(endx - 2);
1213 last_elapsed = elapsed + 1;
1215 else
1217 if ((last_elapsed <= validation_start) &&
1218 (elapsed > validation_start))
1220 osci_valid = true;
1223 last_elapsed = elapsed;
1225 update_data();
1227 if (mp3 != rb->audio_current_track())
1229 struct mp3entry *new_mp3 = rb->audio_current_track();
1230 if (rb->strncasecmp(path_mp3, new_mp3->path,
1231 sizeof (path_mp3)))
1233 rb->splash(0, "Abort due to file change");
1234 rb->button_get(true);
1235 rb->button_get(true);
1236 exit_request = true;
1238 else
1240 mp3 = new_mp3;
1241 rb->audio_pause();
1242 rb->audio_flush_and_reload_tracks();
1243 rb->audio_ff_rewind(range_start);
1244 rb->audio_resume();
1248 #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
1249 #ifdef SPLITEDIT_SPEED100
1250 rb->sound_set_pitch(1000); /* make sure to reset pitch */
1251 #endif
1252 #endif
1255 return retval;
1258 enum plugin_status plugin_start(const void* parameter)
1260 struct mp3entry* mp3;
1262 (void)parameter;
1263 rb->lcd_clear_display();
1264 rb->lcd_update();
1265 mp3 = rb->audio_current_track();
1266 if (mp3 != NULL)
1268 if (rb->audio_status() & AUDIO_STATUS_PAUSE)
1270 rb->audio_resume();
1272 splitedit_editor(mp3, mp3->elapsed, MIN_RANGE_SIZE * 8);
1274 else
1276 rb->splash(0, "Play or pause a mp3 file first.");
1277 rb->button_get(true);
1278 rb->button_get(true);
1280 return splitedit_exit_code;