summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2004-07-29 14:00:22 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2004-07-29 14:00:22 +0000
commit022299ee6c1271f12b14539cf7e000d38048a63a (patch)
treeb955444d27ab81b7d3c0659f50ae158bb09337a5
parentbe6c8ab463d45f1526fe7a2a4e317e77cfba01f7 (diff)
downloadrockbox-022299ee6c1271f12b14539cf7e000d38048a63a.tar.gz
rockbox-022299ee6c1271f12b14539cf7e000d38048a63a.zip
The ninja-cool MP3 split editor plugin by Philipp Pertermann
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@4975 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/plugins/splitedit.c1147
1 files changed, 1147 insertions, 0 deletions
diff --git a/apps/plugins/splitedit.c b/apps/plugins/splitedit.c
new file mode 100644
index 0000000000..db99c5bb77
--- /dev/null
+++ b/apps/plugins/splitedit.c
@@ -0,0 +1,1147 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 Philipp Pertermann
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include "plugin.h"
21
22#ifndef SIMULATOR
23#ifdef HAVE_LCD_BITMAP
24
25#define BMPHEIGHT 7
26#define BMPWIDTH 13
27unsigned char LOOP_BMP[][13] =
28{
29 {0xfc,0x00,0x10,0x11,0x93,0x7f,0x13,0x11,0x7c,0x38,0x10,0x00,0x7c}, /*ALL */
30 {0x81,0x03,0x7f,0x03,0x91,0x10,0x10,0x10,0x7c,0x38,0x10,0x00,0x7c}, /*FROM*/
31 {0xfc,0x00,0x10,0x10,0x90,0x10,0x7c,0x38,0x11,0x03,0x7f,0x03,0x01}, /*TO */
32 {0x80,0x10,0x10,0x11,0x93,0x7f,0x13,0x11,0x10,0x7c,0x38,0x10,0x00}, /*FREE*/
33};
34
35unsigned char CUT_BMP[] =
36{
37 0xc1,0x63,0x63,0x36,0xb6,0x1c,0x1c,0x36,0x77,0x55,0x55,0x55,0x32,
38};
39
40unsigned char SCALE_BMP[][13] =
41{
42 {0x80,0x06,0x49,0x66,0xb0,0x18,0x0c,0x06,0x33,0x49,0x30,0x00,0x00}, /*lin*/
43 {0x80,0x30,0x78,0x48,0xff,0x7f,0x00,0x7f,0x7f,0x48,0x78,0x30,0x00}, /*db*/
44};
45
46#define TIMEBAR_Y 9
47#define TIMEBAR_HEIGHT 4
48
49#define OSCI_X 0
50#define OSCI_Y (TIMEBAR_Y + TIMEBAR_HEIGHT + 1)
51#define OSCI_WIDTH LCD_WIDTH
52#define OSCI_HEIGHT (LCD_HEIGHT - BMPHEIGHT - OSCI_Y - 1)
53
54/* Indices of the menu items in the save editor, see save_editor */
55#define SE_PART1_SAVE 0
56#define SE_PART1_NAME 1
57#define SE_PART2_SAVE 2
58#define SE_PART2_NAME 3
59#define SE_SAVE 4
60#define SE_COUNT 5
61
62/* the global api pointer */
63static struct plugin_api* rb;
64
65/* contains the file name of the song that is to be split */
66static char path_mp3[MAX_PATH];
67
68/* Exit code of this plugin */
69static enum plugin_status splitedit_exit_code = PLUGIN_OK;
70
71/* The range in time that the displayed aerea comprises */
72static unsigned int range_start = 0;
73static unsigned int range_end = 0;
74
75/* The range in time that is being looped */
76static unsigned int play_start = 0;
77static unsigned int play_end = 0;
78
79/* Point in time (pixel) at which the split mark is set */
80static int split_x = OSCI_X + (OSCI_WIDTH / 2);
81
82/* Contains the peak values */
83static unsigned char osci_buffer[OSCI_WIDTH];
84
85/* if true peak values from a previous loop are only overwritten
86 if the new value is greater than the old value */
87static bool osci_valid = false;
88
89/**
90 * point in time from which on the osci_buffer is invalid
91 * if set to ~(unsigned int)0 the entire osci_buffer is invalid
92 */
93static unsigned int validation_start = ~(unsigned int)0;
94
95/* all the visible aerea is looped */
96#define LOOP_MODE_ALL 0
97
98/* loop starts at split point, ends at right visible border */
99#define LOOP_MODE_FROM 1
100
101/* loop start at left visible border, ends at split point */
102#define LOOP_MODE_TO 2
103
104/* let the song play without looping */
105#define LOOP_MODE_FREE 3
106
107/* see LOOP_MODE_XXX constants vor valid values */
108static int loop_mode = LOOP_MODE_FREE;
109
110/* minimal allowed timespan (ms) of the visible (and looped) aerea*/
111#define MIN_RANGE_SIZE 1000
112
113/* Format time into buf.
114 *
115 * buf - buffer to format to.
116 * buf_size - size of buffer.
117 * time - time to format, in milliseconds.
118 */
119static void format_time_ms(char* buf, int buf_size, int time)
120{
121 rb->snprintf(buf, buf_size, "%d:%02d:%03d", time / 60000,
122 time % 60000 / 1000, (time % 60000) % 1000);
123}
124
125/**
126 * converts screen coordinate (pixel) to time (ms)
127 */
128static int xpos_to_time(int xpos)
129{
130 int retval = 0;
131 int range = range_end - range_start;
132 retval = range_start + (((xpos - OSCI_X) * range) / OSCI_WIDTH);
133 return retval;
134}
135
136/**
137 * Converts time (ms) to screen coordinates (pixel).
138 */
139static int time_to_xpos(unsigned int time)
140{
141 int retval = OSCI_X;
142
143 /* clip the range */
144 if (time < range_start)
145 {
146 retval = OSCI_X;
147 }
148 else
149 if (time >= range_end)
150 {
151 retval = OSCI_X + OSCI_WIDTH;
152 }
153
154 /* do the calculation */
155 else
156 {
157 int range = range_end - range_start;
158 retval = OSCI_X + ((time - range_start) * OSCI_WIDTH) / range ;
159 }
160 return retval;
161}
162
163/**
164 * Updates the display of the textual data only.
165 */
166static void update_data(void)
167{
168 char buf[20];
169 char timebuf[10];
170 int w, h;
171
172 /* split point */
173 format_time_ms(timebuf, sizeof buf, xpos_to_time(split_x));
174 rb->snprintf(buf, sizeof buf, "Split at: %s", timebuf);
175
176 rb->lcd_getstringsize(buf, &w, &h);
177
178 rb->lcd_clearrect(0, 0, LCD_WIDTH, h);
179 rb->lcd_puts(0, 0, buf);
180 rb->lcd_update_rect(0, 0, LCD_WIDTH, h);
181}
182
183/**
184 * Displays which part of the song is visible
185 * in the osci.
186 */
187static void update_timebar(struct mp3entry *mp3)
188{
189 rb->scrollbar
190 (
191 0, TIMEBAR_Y, LCD_WIDTH, TIMEBAR_HEIGHT,
192 mp3->length, range_start, range_end,
193 HORIZONTAL
194 );
195 rb->lcd_update_rect(0, TIMEBAR_Y, LCD_WIDTH, TIMEBAR_HEIGHT);
196}
197
198/**
199 * Marks the entire area of the osci buffer invalid.
200 * It will be drawn with new values in the next loop.
201 */
202void splitedit_invalidate_osci(void)
203{
204 osci_valid = false;
205 validation_start = ~(unsigned int)0;
206}
207
208/**
209 * Returns the loop mode. See the LOOP_MODE_XXX constants above.
210 */
211int splitedit_get_loop_mode(void)
212{
213 return loop_mode;
214}
215
216/**
217 * Updates the icons that display the Fn key hints.
218 */
219static void update_icons(void)
220{
221 rb->lcd_clearrect(0, LCD_HEIGHT - BMPHEIGHT, LCD_WIDTH, BMPHEIGHT);
222
223 /* The CUT icon */
224 rb->lcd_bitmap(CUT_BMP,
225 LCD_WIDTH / 3 / 2 - BMPWIDTH / 2, LCD_HEIGHT - BMPHEIGHT,
226 BMPWIDTH, BMPHEIGHT, true);
227
228 /* The loop mode icon */
229 rb->lcd_bitmap(LOOP_BMP[splitedit_get_loop_mode()],
230 LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
231 BMPWIDTH, BMPHEIGHT, true);
232
233#ifdef HAVE_MAS3587F
234 /* The scale icon */
235 rb->lcd_bitmap(SCALE_BMP[rb->peak_meter_get_use_dbfs()],
236 2 *LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
237 BMPWIDTH, BMPHEIGHT, true);
238#else
239 {
240 static int idx;
241 if (idx < 0 || idx > 1) idx = 0;
242 idx = 1 - idx;
243 rb->lcd_bitmap(SCALE_BMP[idx],
244 2 *LCD_WIDTH/3 + LCD_WIDTH/3 / 2 - BMPWIDTH/2, LCD_HEIGHT - BMPHEIGHT,
245 BMPWIDTH, BMPHEIGHT, true);
246 }
247#endif
248
249 rb->lcd_update_rect(0, LCD_HEIGHT - BMPHEIGHT, LCD_WIDTH, BMPHEIGHT);
250}
251
252/**
253 * Sets the loop mode. See the LOOP_MODE_XXX constants above.
254 */
255void splitedit_set_loop_mode(int mode)
256{
257 int old_loop_mode = loop_mode;
258 /* range restriction */
259 loop_mode = mode % (LOOP_MODE_FREE + 1);
260 switch (loop_mode)
261 {
262 case LOOP_MODE_ALL:
263 play_start = range_start;
264 play_end = range_end;
265 break;
266
267 case LOOP_MODE_FROM:
268 play_start = xpos_to_time(split_x);
269 play_end = range_end;
270 break;
271
272 case LOOP_MODE_TO:
273 play_start = range_start;
274 play_end = xpos_to_time(split_x);
275 break;
276
277 case LOOP_MODE_FREE:
278 /* play_start is used when the song plays beyond its end */
279 play_start = range_start;
280 play_end = range_end;
281 break;
282 }
283
284 if (loop_mode != old_loop_mode)
285 {
286 update_icons();
287 }
288}
289
290/**
291 * Readraws the osci without clear.
292 */
293static void redraw_osci(void)
294{
295 int x;
296 for (x = 0; x < OSCI_WIDTH; x++)
297 {
298 if (osci_buffer[x] > 0)
299 {
300 rb->lcd_drawline
301 (
302 OSCI_X + x, OSCI_Y + OSCI_HEIGHT - 1,
303 OSCI_X + x, OSCI_Y + OSCI_HEIGHT - osci_buffer[x] - 1
304 );
305 }
306 }
307}
308
309/**
310 * Sets the range of time in which the user can finetune the split
311 * point. The split point is the center of the time range.
312 */
313static void set_range_by_time(
314 struct mp3entry *mp3,
315 unsigned int split_time,
316 unsigned int range)
317{
318 if (mp3 != NULL)
319 {
320 if (range < MIN_RANGE_SIZE)
321 {
322 range = MIN_RANGE_SIZE;
323 }
324 range_start = (split_time > range / 2) ? (split_time - range / 2) : 0;
325 range_end = MIN(range_start + range, mp3->length);
326 split_x = time_to_xpos(split_time);
327
328 splitedit_invalidate_osci();
329
330 /* this sets the play_start / play_end */
331 splitedit_set_loop_mode(splitedit_get_loop_mode());
332
333 update_data();
334 update_timebar(mp3);
335 }
336}
337
338/**
339 * Set the split point in screen coordinates
340 */
341void splitedit_set_split_x(int newx)
342{
343 int minx = split_x - 2 > 0 ? split_x - 2: 0;
344
345 /* remove old split point from screen, only if moved */
346 if (split_x != newx)
347 {
348 rb->lcd_invertrect (minx, OSCI_Y, 5, 1);
349 rb->lcd_invertrect (split_x-1 > 0 ? split_x - 1: 0, OSCI_Y + 1, 3, 1);
350 rb->lcd_invertrect (split_x, OSCI_Y + 2, 1, OSCI_HEIGHT - 2);
351 rb->lcd_update_rect(minx, OSCI_Y, 5, OSCI_HEIGHT);
352 }
353
354 if (newx >= OSCI_X && newx < OSCI_X + OSCI_WIDTH)
355 {
356 split_x = newx;
357 /* in LOOP_FROM / LOOP_TO modes play_start /play_end must be updated */
358 splitedit_set_loop_mode(splitedit_get_loop_mode());
359
360 /* display new split time */
361 update_data();
362 }
363
364 /* display new split point */
365 minx = split_x - 2 > 0 ? split_x - 2: 0;
366 rb->lcd_invertrect (minx, OSCI_Y, 5, 1);
367 rb->lcd_invertrect (split_x - 1 > 0 ? split_x - 1: 0, OSCI_Y + 1, 3, 1);
368 rb->lcd_invertrect (split_x, OSCI_Y + 2, 1, OSCI_HEIGHT - 2);
369 rb->lcd_update_rect(minx, OSCI_Y, 5, OSCI_HEIGHT);
370}
371
372/**
373 * returns the split point in screen coordinates
374 */
375int splitedit_get_split_x(void)
376{
377 return split_x;
378}
379
380/**
381 * Clears the osci area and redraws it
382 */
383static void update_osci(void)
384{
385 rb->lcd_clearrect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
386 redraw_osci();
387 splitedit_set_split_x(splitedit_get_split_x());
388 rb->lcd_update_rect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
389}
390
391/**
392 * Zooms the visable and loopable range by the factor
393 * (counter / denominator). The split point is used as
394 * center point of the new selected range.
395 */
396static void zoom(struct mp3entry *mp3, int counter, int denominator)
397{
398 unsigned char oldbuf[OSCI_WIDTH];
399 int oldrange = range_end - range_start;
400 int range = oldrange * counter / denominator;
401 int i;
402 int oldindex;
403 int oldsplitx;
404 int splitx;
405 int split;
406
407 /* for stretching / shrinking a second buffer is needed */
408 rb->memcpy(&oldbuf, &osci_buffer, sizeof osci_buffer);
409
410 /* recalculate the new range and split point */
411 oldsplitx = split_x;
412 split = xpos_to_time(split_x);
413
414 set_range_by_time(mp3, split, range);
415 range = range_end - range_start;
416
417 splitx = time_to_xpos(split);
418
419 /* strech / shrink the existing osci buffer */
420 for (i = 0; i < OSCI_WIDTH; i++)
421 {
422 /* oldindex = (i + OSCI_X - splitx) * range / oldrange + oldsplitx ;*/
423 oldindex = (i*range / oldrange) + oldsplitx - (splitx*range /oldrange);
424 if (oldindex >= 0 && oldindex < OSCI_WIDTH)
425 {
426 osci_buffer[i] = oldbuf[oldindex];
427 }
428 else
429 {
430 osci_buffer[i] = 0;
431 }
432 }
433
434 splitx = time_to_xpos(split);
435 splitedit_set_split_x(splitx);
436 splitedit_invalidate_osci();
437
438}
439
440static void scroll(struct mp3entry *mp3)
441{
442 zoom(mp3, 1, 1);
443 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
444 update_osci();
445 update_data();
446}
447
448/**
449 * Zooms in by 3/4
450 */
451void splitedit_zoom_in(struct mp3entry *mp3)
452{
453 rb->lcd_clearrect(OSCI_X, OSCI_Y, OSCI_WIDTH, OSCI_HEIGHT);
454 zoom(mp3, 3, 4);
455 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
456 update_osci();
457 update_data();
458}
459
460/**
461 * Zooms out by 4/3
462 */
463void splitedit_zoom_out(struct mp3entry *mp3)
464{
465 rb->lcd_clearrect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
466 zoom(mp3, 4, 3);
467 rb->lcd_update_rect(OSCI_X, OSCI_Y, LCD_WIDTH, OSCI_HEIGHT);
468 update_osci();
469 update_data();
470}
471
472/**
473 * Append part_no to the file name.
474 */
475static void generateFileName(char* file_name, int part_no)
476{
477 if (rb->strlen(file_name) <MAX_PATH)
478 {
479 int len = rb->strlen(file_name);
480 int ext_len = rb->strlen(".mp3");
481 if (rb->strcasecmp(
482 &file_name[len - ext_len],
483 ".mp3") == 0)
484 {
485 int i = 0;
486 /* shift the extension one position to the right*/
487 for (i = len; i > len - ext_len; i--)
488 {
489 file_name[i] = file_name[i - 1];
490 }
491 file_name[len - ext_len] = '0' + part_no;
492 }
493 else
494 {
495 rb->splash(0, true, "wrong extension");
496 rb->button_get(true);
497 rb->button_get(true);
498 }
499 }
500 else
501 {
502 rb->splash(0, true, "name to long");
503 rb->button_get(true);
504 rb->button_get(true);
505
506 }
507
508}
509
510/**
511 * Copy bytes from src to dest while displaying a progressbar.
512 * The files must be already open.
513 */
514static void copy_file(
515 int dest,
516 int src,
517 unsigned int bytes,
518 int prg_y,
519 int prg_h)
520{
521 unsigned char *buffer;
522 unsigned int i = 0;
523 ssize_t bytes_read = 1; /* ensure the for loop is executed */
524 unsigned int buffer_size;
525 buffer = rb->plugin_get_buffer(&buffer_size);
526
527 for (i = 0; i < bytes && bytes_read > 0; i += bytes_read)
528 {
529 ssize_t bytes_written;
530 unsigned int bytes_to_read =
531 bytes - i > buffer_size ? buffer_size : bytes - i;
532 bytes_read = rb->read(src, buffer, bytes_to_read);
533 bytes_written = rb->write(dest, buffer, bytes_read);
534
535 rb->scrollbar(0, prg_y, LCD_WIDTH, prg_h, bytes, 0, i, HORIZONTAL);
536 rb->lcd_update_rect(0, prg_y, LCD_WIDTH, prg_h);
537 }
538}
539
540/**
541 * Save the files, if the file_name is not NULL
542 */
543static int save(
544 struct mp3entry *mp3,
545 char *file_name1,
546 char *file_name2,
547 int splittime)
548{
549 int file1, file2, src_file;
550 unsigned int end = 0;
551 int retval = 0;
552
553 /* Verify that file 1 doesn't exit yet */
554 if (file_name1 != NULL)
555 {
556 file1 = rb->open(file_name1, O_RDONLY);
557 if (file1 >= 0)
558 {
559 rb->close(file1);
560 rb->splash(0, true, "File 1 exists. Please rename.");
561 rb->button_get(true);
562 rb->button_get(true);
563 return -1;
564 }
565 }
566
567 /* Verify that file 2 doesn't exit yet */
568 if (file_name2 != NULL)
569 {
570 file2 = rb->open(file_name2, O_RDONLY);
571 if (file2 >= 0)
572 {
573 rb->close(file2);
574 rb->splash(0, true, "File 2 exists. Please rename.");
575 rb->button_get(true);
576 rb->button_get(true);
577 return -2;
578 }
579 }
580
581 /* find the file position of the split point */
582 rb->mpeg_pause();
583 rb->mpeg_ff_rewind(splittime);
584 rb->yield();
585 rb->yield();
586 end = rb->mpeg_get_file_pos();
587
588 /* open the source file */
589 src_file = rb->open(mp3->path, O_RDONLY);
590 if (src_file >= 0)
591 {
592 int close_stat = 0;
593 int x, y;
594 int offset;
595 unsigned long last_header = rb->mpeg_get_last_header();
596
597 rb->lcd_getstringsize("M", &x, &y);
598
599 /* Find the next frame boundary */
600 rb->lseek(src_file, end, SEEK_SET);
601 rb->find_next_frame(src_file, &offset, 8000, last_header);
602 rb->lseek(src_file, 0, SEEK_SET);
603 end += offset;
604
605 /* write the file 1 */
606 if (file_name1 != NULL)
607 {
608 file1 = rb->open (file_name1, O_WRONLY | O_CREAT);
609 if (file1 >= 0)
610 {
611 copy_file(file1, src_file, end, y*2 + 1, y -1);
612 close_stat = rb->close(file1);
613
614 if (close_stat != 0)
615 {
616 rb->splash(0, true,
617 "failed closing file1: error %d", close_stat);
618 rb->button_get(true);
619 rb->button_get(true);
620 }
621 }
622 else
623 {
624 rb->splash(0, true,
625 "Can't write File1: error %d", file1);
626 rb->button_get(true);
627 rb->button_get(true);
628 retval = -1;
629 }
630 }
631 /* if file1 hasn't been written we're not at the split point yet */
632 else
633 {
634 if (rb->lseek(src_file, end, SEEK_SET) < (off_t)end)
635 {
636 rb->splash(0, true,
637 "Src file to short: error %d", src_file);
638 rb->button_get(true);
639 rb->button_get(true);
640 }
641 }
642
643 if (file_name2 != NULL)
644 {
645 /* write file 2 */
646 file2 = rb->open (file_name2, O_WRONLY | O_CREAT);
647 if (file2 >= 0)
648 {
649 end = mp3->filesize - end;
650 copy_file(file2, src_file, end, y * 5 + 1, y -1);
651 close_stat = rb->close(file2);
652
653 if (close_stat != 0)
654 {
655 rb->splash(0, true,
656 "failed: closing file2: error %d", close_stat);
657 rb->button_get(true);
658 rb->button_get(true);
659 }
660 }
661 else
662 {
663 rb->splash(0, true,
664 "Can't write File2: error %d", file2);
665 rb->button_get(true);
666 rb->button_get(true);
667 retval = -2;
668 }
669 }
670
671 close_stat = rb->close(src_file);
672 if (close_stat != 0)
673 {
674 rb->splash(0, true,
675 "failed: closing src: error %d", close_stat);
676 rb->button_get(true);
677 rb->button_get(true);
678 }
679 }
680 else
681 {
682 rb->splash(0, true, "Source file not found");
683 rb->button_get(true);
684 rb->button_get(true);
685 retval = -3;
686 }
687
688 rb->mpeg_resume();
689
690 return retval;
691}
692
693/**
694 * Let the user choose which file to save with which name
695 */
696static void save_editor(struct mp3entry *mp3, int splittime)
697{
698 bool exit_request = false;
699 int choice = 0;
700 int button = BUTTON_NONE;
701 char part1_name [MAX_PATH];
702 char part2_name [MAX_PATH];
703 bool part1_save = true;
704 bool part2_save = true;
705
706 /* file name for left part */
707 rb->strncpy(part1_name, mp3->path, MAX_PATH);
708 generateFileName(part1_name, 1);
709
710 /* file name for right part */
711 rb->strncpy(part2_name, mp3->path, MAX_PATH);
712 generateFileName(part2_name, 2);
713
714 while (!exit_request)
715 {
716 int pos;
717 rb->lcd_clear_display();
718
719 /* Save file1? */
720 rb->lcd_puts_style(0, 0, "Save part 1?", choice == SE_PART1_SAVE);
721 rb->lcd_puts(13, 0, part1_save?"yes":"no");
722
723 /* trim to display the filename without path */
724 for (pos = rb->strlen(part1_name); pos > 0; pos--)
725 {
726 if (part1_name[pos] == '/')
727 break;
728 }
729 pos++;
730
731 /* File name 1 */
732 rb->lcd_puts_scroll_style(0, 1,
733 &part1_name[pos], choice == SE_PART1_NAME);
734
735 /* Save file2? */
736 rb->lcd_puts_style(0, 3, "Save part 2?", choice == SE_PART2_SAVE);
737 rb->lcd_puts(13, 3, part2_save?"yes":"no");
738
739 /* trim to display the filename without path */
740 for (pos = rb->strlen(part2_name); pos > 0; pos --)
741 {
742 if (part2_name[pos] == '/')
743 break;
744 }
745 pos++;
746
747 /* File name 2 */
748 rb->lcd_puts_scroll_style(0, 4,
749 &part2_name[pos], choice == SE_PART2_NAME);
750
751 /* Save */
752 rb->lcd_puts_style(0, 6, "Save", choice == SE_SAVE);
753
754 rb->lcd_update();
755
756
757 button = rb->button_get(true);
758 switch (button)
759 {
760 case BUTTON_UP:
761 choice = (choice + SE_COUNT - 1) % SE_COUNT;
762 break;
763
764 case BUTTON_DOWN:
765 choice = (choice + 1) % SE_COUNT;
766 break;
767
768 case BUTTON_PLAY:
769 switch (choice)
770 {
771 int saved;
772
773 case SE_PART1_SAVE:
774 part1_save = !part1_save;
775 break;
776
777 case SE_PART1_NAME:
778 rb->kbd_input(part1_name, MAX_PATH);
779 break;
780
781 case SE_PART2_SAVE:
782 part2_save = !part2_save;
783 break;
784
785 case SE_PART2_NAME:
786 rb->kbd_input(part2_name, MAX_PATH);
787 break;
788
789 case SE_SAVE:
790 rb->lcd_stop_scroll();
791 rb->lcd_clearrect(0, 6*8, LCD_WIDTH, LCD_HEIGHT);
792 saved = save
793 (
794 mp3,
795 part1_save?part1_name:NULL,
796 part2_save?part2_name:NULL,
797 splittime
798 );
799
800 /* if something failed the user may go on choosing */
801 if (saved >= 0)
802 {
803 exit_request = true;
804 }
805 break;
806 }
807 break;
808
809 case BUTTON_OFF:
810 exit_request = true;
811 break;
812 }
813 }
814}
815
816/**
817 * The main loop of the editor
818 */
819unsigned long splitedit_editor(struct mp3entry * mp3_to_split,
820 unsigned int split_time,
821 unsigned int range)
822{
823 int button = BUTTON_NONE;
824 struct mp3entry *mp3 = mp3_to_split;
825 unsigned int last_elapsed = 0;
826 int lastx = OSCI_X + (OSCI_WIDTH / 2);
827 int retval = -1;
828
829 if (mp3 != NULL)
830 {
831 /*unsigned short scheme = SCHEME_SPLIT_EDITOR;*/
832 bool exit_request = false;
833 set_range_by_time(mp3, split_time, range);
834 splitedit_set_loop_mode(LOOP_MODE_ALL);
835 update_icons();
836
837 /*while (scheme != SCHEME_RETURN) {*/
838 while (!exit_request)
839 {
840 unsigned int elapsed ;
841 int x ;
842
843 /* get position */
844 elapsed = mp3->elapsed;
845 x = time_to_xpos(elapsed);
846
847 /* are we still in the zoomed range? */
848 if (elapsed > play_start && elapsed < play_end)
849 {
850 /* read volume info */
851 unsigned short volume;
852#ifdef HAVE_MAS3587F
853 volume = rb->mas_codec_readreg(0x0c);
854 volume += rb->mas_codec_readreg(0x0d);
855 volume = volume / 2;
856 volume = rb->peak_meter_scale_value(volume, OSCI_HEIGHT);
857#else
858 volume = OSCI_HEIGHT / 2;
859#endif
860
861 /* update osci_buffer */
862 if (osci_valid || lastx == x)
863 {
864 int index = x - OSCI_X;
865 osci_buffer[index] = MAX(osci_buffer[index], volume);
866 }
867 else
868 {
869 int i;
870 osci_buffer[x - OSCI_X] = volume;
871 for (i = lastx + 1; i < x; i++)
872 {
873 osci_buffer[i - OSCI_X] = 0;
874 }
875 }
876
877 /* make room */
878 rb->lcd_clearrect(lastx + 1, OSCI_Y, x - lastx, OSCI_HEIGHT);
879 /* draw a value */
880 if (osci_buffer[x - OSCI_X] > 0)
881 {
882 int i;
883 for (i = lastx +1; i <= x; i++)
884 {
885 rb->lcd_drawline
886 (
887 i, OSCI_Y + OSCI_HEIGHT - 1,
888 i, OSCI_Y + OSCI_HEIGHT - osci_buffer[i - OSCI_X]-1
889 );
890 }
891 }
892
893 /* mark the current position */
894 if (lastx != x)
895 {
896 rb->lcd_invertrect(lastx, OSCI_Y, 1, OSCI_HEIGHT);
897 rb->lcd_invertrect(x, OSCI_Y, 1, OSCI_HEIGHT);
898 }
899
900 /* mark the split point */
901 if ((x > split_x - 2) && (lastx < split_x + 3))
902 {
903 if ((lastx < split_x) && (x >= split_x))
904 {
905 rb->lcd_invertrect
906 (
907 split_x, OSCI_Y + 2,
908 1, OSCI_HEIGHT - 2
909 );
910 }
911 rb->lcd_drawline(split_x -2, OSCI_Y, split_x + 2, OSCI_Y);
912 rb->lcd_drawline(split_x-1, OSCI_Y+1, split_x +1,OSCI_Y+1);
913 }
914
915 /* make visible */
916 if (lastx <= x)
917 {
918 rb->lcd_update_rect(lastx, OSCI_Y, x-lastx+1, OSCI_HEIGHT);
919 }
920 else
921 {
922 rb->lcd_update_rect
923 (
924 lastx, OSCI_Y,
925 OSCI_X + OSCI_WIDTH - lastx, OSCI_HEIGHT
926 );
927 rb->lcd_update_rect(0, OSCI_Y, x + 1, OSCI_HEIGHT);
928 }
929
930 lastx = x;
931 }
932
933 /* we're not in the zoom range -> rewind */
934 else
935 {
936 if (elapsed >= play_end)
937 {
938 switch (splitedit_get_loop_mode())
939 {
940 unsigned int range_width;
941
942 case LOOP_MODE_ALL:
943 case LOOP_MODE_TO:
944 rb->mpeg_pause();
945 rb->mpeg_ff_rewind(range_start);
946 rb->mpeg_resume();
947 break;
948
949 case LOOP_MODE_FROM:
950 rb->mpeg_pause();
951 rb->mpeg_ff_rewind(xpos_to_time(split_x));
952 rb->mpeg_resume();
953 break;
954
955 case LOOP_MODE_FREE:
956 range_width = range_end - range_start;
957 set_range_by_time(mp3,
958 range_end + range_width / 2, range_width);
959
960 /* play_end und play_start anpassen */
961 splitedit_set_loop_mode(LOOP_MODE_FREE);
962 rb->memset(osci_buffer, 0, sizeof osci_buffer);
963 update_osci();
964 rb->lcd_update();
965 break;
966 }
967
968 }
969 }
970
971 button = rb->button_get(false);
972 rb->yield();
973
974 /* here the evaluation of the key scheme starts.
975 All functions the user triggers are called from
976 within execute_scheme */
977 /* key_scheme_execute(button, &scheme); */
978 switch (button)
979 {
980 case SYS_USB_CONNECTED:
981 rb->usb_screen();
982 splitedit_exit_code = PLUGIN_USB_CONNECTED;
983 exit_request = true;
984 break;
985
986 case BUTTON_OFF:
987 exit_request = true;
988 break;
989
990 case BUTTON_PLAY:
991 rb->mpeg_pause();
992 rb->mpeg_ff_rewind(xpos_to_time(split_x));
993 rb->mpeg_resume();
994 break;
995
996 case BUTTON_UP:
997 splitedit_zoom_in(mp3);
998 lastx = time_to_xpos(mp3->elapsed);
999 break;
1000
1001 case BUTTON_DOWN:
1002 splitedit_zoom_out(mp3);
1003 lastx = time_to_xpos(mp3->elapsed);
1004 break;
1005
1006#ifdef HAVE_MAS3587F
1007 case BUTTON_ON | BUTTON_RIGHT:
1008 rb->mpeg_set_pitch(1500);
1009 splitedit_invalidate_osci();
1010 break;
1011
1012 case BUTTON_ON | BUTTON_PLAY:
1013 rb->mpeg_set_pitch(1000);
1014 splitedit_invalidate_osci();
1015 break;
1016
1017 case BUTTON_ON | BUTTON_LEFT:
1018 rb->mpeg_set_pitch(500);
1019 splitedit_invalidate_osci();
1020 break;
1021#endif
1022
1023 case BUTTON_LEFT:
1024 case BUTTON_LEFT | BUTTON_REPEAT:
1025 if (splitedit_get_split_x() > OSCI_X + 2)
1026 {
1027 splitedit_set_split_x(splitedit_get_split_x() - 1);
1028 }
1029 else
1030 {
1031 scroll(mp3);
1032 lastx = time_to_xpos(mp3->elapsed);
1033 }
1034 break;
1035
1036 case BUTTON_RIGHT:
1037 case BUTTON_RIGHT | BUTTON_REPEAT:
1038 if (splitedit_get_split_x() < OSCI_X + OSCI_WIDTH-3)
1039 {
1040 splitedit_set_split_x(splitedit_get_split_x() + 1);
1041 }
1042 else
1043 {
1044 scroll(mp3);
1045 lastx = time_to_xpos(mp3->elapsed);
1046 }
1047 break;
1048
1049 case BUTTON_F1 | BUTTON_REL:
1050 save_editor(mp3, xpos_to_time(split_x));
1051 rb->lcd_clear_display();
1052 update_osci();
1053 update_timebar(mp3);
1054 update_icons();
1055 break;
1056
1057 case BUTTON_F2:
1058 splitedit_set_loop_mode(splitedit_get_loop_mode() + 1);
1059 update_icons();
1060 break;
1061
1062 case BUTTON_F3:
1063#ifdef HAVE_MAS3587F
1064 rb->peak_meter_set_use_dbfs(rb->peak_meter_get_use_dbfs() +1);
1065#endif
1066 splitedit_invalidate_osci();
1067 update_icons();
1068 break;
1069 }
1070
1071 if (validation_start == ~(unsigned int)0)
1072 {
1073 if (elapsed < range_end && elapsed > range_start)
1074 {
1075 validation_start = elapsed;
1076 }
1077 else
1078 {
1079 int endx = time_to_xpos(range_end);
1080 validation_start = xpos_to_time(endx - 2);
1081 }
1082 last_elapsed = elapsed + 1;
1083 }
1084 else
1085 {
1086 if ((last_elapsed <= validation_start) &&
1087 (elapsed > validation_start))
1088 {
1089 osci_valid = true;
1090 }
1091
1092 last_elapsed = elapsed;
1093 }
1094 update_data();
1095
1096 if (mp3 != rb->mpeg_current_track())
1097 {
1098 struct mp3entry *new_mp3 = rb->mpeg_current_track();
1099 if (rb->strncasecmp(path_mp3, new_mp3->path,
1100 sizeof (path_mp3)))
1101 {
1102 rb->splash(0, true,"Abort due to file change");
1103 rb->button_get(true);
1104 rb->button_get(true);
1105 exit_request = true;
1106 }
1107 else
1108 {
1109 mp3 = new_mp3;
1110 rb->mpeg_pause();
1111 rb->mpeg_flush_and_reload_tracks();
1112 rb->mpeg_ff_rewind(range_start);
1113 rb->mpeg_resume();
1114 }
1115 }
1116 }
1117 }
1118 return retval;
1119}
1120
1121enum plugin_status plugin_start(struct plugin_api* api, void* parameter)
1122{
1123 struct mp3entry* mp3;
1124
1125 (void)parameter;
1126 rb = api;
1127 rb->lcd_clear_display();
1128 rb->lcd_update();
1129 mp3 = rb->mpeg_current_track();
1130 if (mp3 != NULL)
1131 {
1132 if (rb->mpeg_status() & MPEG_STATUS_PAUSE)
1133 {
1134 rb->mpeg_resume();
1135 }
1136 splitedit_editor(mp3, mp3->elapsed, MIN_RANGE_SIZE * 8);
1137 }
1138 else
1139 {
1140 rb->splash(0, true, "Play or pause a mp3 file first.");
1141 rb->button_get(true);
1142 rb->button_get(true);
1143 }
1144 return splitedit_exit_code;
1145}
1146#endif
1147#endif