1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Peter D'Hoye
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 ****************************************************************************/
25 /* temp byte buffer */
26 uint8_t samples
[10 * 1024]; /* read 10KB at the time */
28 static struct wav_header
33 int8_t formatchunkid
[4];
34 int32_t formatchunksize
;
40 uint16_t bitspersample
;
41 int8_t datachunkid
[4];
42 int32_t datachunksize
;
45 #define WAV_HEADER_FORMAT "4L44LSSLLSS4L"
55 /* TO DO: limit used LCD height, so the waveform isn't streched vertically? */
57 #define LEFTZERO (LCD_HEIGHT / 4)
58 #define RIGHTZERO (3 * (LCD_HEIGHT / 4))
59 #define YSCALE ((0x8000 / (LCD_HEIGHT / 4)) + 1)
62 static char *audiobuf
;
63 static size_t audiobuflen
;
64 static uint32_t mempeakcount
= 0;
65 static uint32_t filepeakcount
= 0;
66 static uint32_t fppmp
= 0; /* file peaks per mem peaks */
67 static uint32_t zoomlevel
= 1;
68 static uint32_t leftmargin
= 0;
69 static uint32_t center
= 0;
70 static uint32_t ppp
= 1;
72 /* helper function copied from libwavpack bits.c */
73 void little_endian_to_native (void *data
, char *format
)
75 unsigned char *cp
= (unsigned char *) data
;
80 *(long *)cp
= letoh32(*(long *)cp
);
85 *(short *)cp
= letoh16(*(short *)cp
);
90 if (*format
>= '0' && *format
<= '9')
101 display some info about it
102 store peak info in aufiobuf for display routine */
103 static int readwavpeaks(const char *filename
)
105 register uint32_t bytes_read
;
106 register uint32_t fppmp_count
;
107 register int16_t sampleval
;
108 register uint16_t* sampleshort
= NULL
;
111 uint32_t total_bytes_read
= 0;
115 uint32_t peakcount
= 0;
116 struct peakstruct
* peak
= NULL
;
118 if(rb
->strcasecmp (filename
+ rb
->strlen (filename
) - 3, "wav"))
120 rb
->splash(HZ
*2, "Only for wav files!");
124 file
= rb
->open(filename
, O_RDONLY
);
128 rb
->splash(HZ
*2, "Could not open file!");
132 if(rb
->read(file
, &header
, sizeof (header
)) != sizeof (header
))
134 rb
->splash(HZ
*2, "Could not read file!");
139 total_bytes_read
+= sizeof (header
);
140 little_endian_to_native(&header
, WAV_HEADER_FORMAT
);
142 if (rb
->strncmp(header
.chunkid
, "RIFF", 4) ||
143 rb
->strncmp(header
.formatchunkid
, "fmt ", 4) ||
144 rb
->strncmp(header
.datachunkid
, "data", 4) ||
145 (header
.bitspersample
!= 16) ||
146 header
.audioformat
!= 1)
148 rb
->splash(HZ
*2, "Incompatible wav file!");
153 rb
->lcd_clear_display();
154 rb
->lcd_puts(0, 0, "Viewing file:");
155 rb
->lcd_puts_scroll(0, 1, (unsigned char *)filename
);
158 rb
->lcd_putsf(0, 2, "Channels: %s",
159 header
.numchannels
== 1 ? "mono" : "stereo");
160 rb
->lcd_putsf(0, 3, "Bits/sample: %d", header
.bitspersample
);
161 rb
->lcd_putsf(0, 4, "Samplerate: %"PRIu32
" Hz", header
.samplerate
);
163 seconds
= header
.datachunksize
/ header
.byterate
;
164 hours
= seconds
/ 3600;
165 seconds
-= hours
* 3600;
166 minutes
= seconds
/ 60;
167 seconds
-= minutes
* 60;
169 rb
->lcd_putsf(0, 5, "Length (hh:mm:ss): %02d:%02d:%02d", hours
,
172 rb
->lcd_puts(0, 6, "Searching for peaks... ");
175 /* calculate room for peaks */
176 filepeakcount
= header
.datachunksize
/
177 (header
.numchannels
* (header
.bitspersample
/ 8));
178 mempeakcount
= audiobuflen
/ sizeof(struct peakstruct
);
179 fppmp
= (filepeakcount
/ mempeakcount
) + 1;
180 peak
= (struct peakstruct
*)audiobuf
;
184 while(total_bytes_read
< (header
.datachunksize
+
185 sizeof(struct wav_header
)))
187 bytes_read
= rb
->read(file
, &samples
, sizeof(samples
));
188 total_bytes_read
+= bytes_read
;
192 rb
->splash(HZ
*2, "File read error!");
196 if(((bytes_read
/4)*4) != bytes_read
)
198 rb
->splashf(HZ
*2, "bytes_read/*4 err: %ld",(long int)bytes_read
);
203 sampleshort
= (int16_t*)samples
;
204 sampleval
= letoh16(*sampleshort
);
205 peak
->lmin
= sampleval
;
206 peak
->lmax
= sampleval
;
207 sampleval
= letoh16(*(sampleshort
+1));
208 peak
->rmin
= sampleval
;
209 peak
->rmax
= sampleval
;
213 sampleval
= letoh16(*sampleshort
++);
214 if(sampleval
< peak
->lmin
)
215 peak
->lmin
= sampleval
;
216 else if (sampleval
> peak
->lmax
)
217 peak
->lmax
= sampleval
;
219 sampleval
= letoh16(*sampleshort
++);
220 if(sampleval
< peak
->rmin
)
221 peak
->rmin
= sampleval
;
222 else if (sampleval
> peak
->rmax
)
223 peak
->rmax
= sampleval
;
233 sampleval
= letoh16(*sampleshort
);
234 peak
->lmin
= sampleval
;
235 peak
->lmax
= sampleval
;
236 sampleval
= letoh16(*(sampleshort
+1));
237 peak
->rmin
= sampleval
;
238 peak
->rmax
= sampleval
;
242 /* update progress */
243 rb
->lcd_putsf(0, 6, "Searching for peaks... %"PRIu32
"%%",
244 (total_bytes_read
/ ((header
.datachunksize
+
245 sizeof(struct wav_header
)) / 100)));
248 /* allow user to abort */
249 if(ACTION_KBD_ABORT
== rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_NOBLOCK
))
251 rb
->splash(HZ
*2, "ABORTED");
257 rb
->lcd_puts(0, 6, "Searching for peaks... done");
265 int displaypeaks(void)
268 register int lymin
= INT_MAX
;
269 register int lymax
= INT_MIN
;
270 register int rymin
= INT_MAX
;
271 register int rymax
= INT_MIN
;
272 register unsigned int peakcount
= 0;
273 struct peakstruct
* peak
= (struct peakstruct
*)audiobuf
+ leftmargin
;
276 unsigned org_forecolor
= rb
->lcd_get_foreground();
277 rb
->lcd_set_foreground(LCD_LIGHTGRAY
);
280 if(!zoomlevel
) zoomlevel
= 1;
281 ppp
= (mempeakcount
/ LCD_WIDTH
) / zoomlevel
; /* peaks per pixel */
283 rb
->lcd_clear_display();
285 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
- (0x8000 / YSCALE
));
286 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
);
287 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
+ (0x8000 / YSCALE
));
288 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
- (0x8000 / YSCALE
));
289 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
);
290 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
+ (0x8000 / YSCALE
));
293 rb
->lcd_set_foreground(LCD_BLACK
);
297 rb
->lcd_hline(leftmargin
/ (mempeakcount
/ LCD_WIDTH
),
298 (leftmargin
/ (mempeakcount
/ LCD_WIDTH
)) +
299 (LCD_WIDTH
/ zoomlevel
),
302 while((x
< LCD_WIDTH
) && (peakcount
< mempeakcount
))
304 if(peak
->lmin
< lymin
)
306 if(peak
->lmax
> lymax
)
308 if(peak
->rmin
< rymin
)
310 if(peak
->rmax
> rymax
)
313 if(0 == (peakcount
% ppp
))
316 rb
->lcd_vline(x
, LEFTZERO
- (lymax
/ YSCALE
),
317 LEFTZERO
- (lymin
/ YSCALE
));
318 rb
->lcd_vline(x
, RIGHTZERO
- (rymax
/ YSCALE
),
319 RIGHTZERO
- (rymin
/ YSCALE
));
331 rb
->lcd_set_foreground(org_forecolor
);
339 rb
->lcd_clear_display();
340 rb
->lcd_puts(0, 0, "WAVVIEW USAGE:");
341 rb
->lcd_puts(0, 2, "up/down: zoom out/in");
342 rb
->lcd_puts(0, 3, "left/right: pan left/right");
343 rb
->lcd_puts(0, 4, "select: refresh/continue");
344 rb
->lcd_puts(0, 5, "stop/off: quit");
348 enum plugin_status
plugin_start(const void *parameter
)
350 unsigned int quit
= 0;
351 unsigned int action
= 0;
352 unsigned int dodisplay
= 1;
358 audiobuf
= rb
->plugin_get_audio_buffer(&audiobuflen
);
362 rb
->splash(HZ
*2, "unable to get audio buffer!");
366 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
370 retval
= readwavpeaks(parameter
); /* read WAV file and create peaks array */
372 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
373 rb
->cpu_boost(false);
379 /* press any key to continue */
382 retval
= rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_BLOCK
);
383 if(ACTION_KBD_ABORT
== retval
)
385 else if(ACTION_KBD_SELECT
== retval
)
389 /* start with the overview */
396 center
= mempeakcount
/ 2;
404 if(center
< (mempeakcount
/ (zoomlevel
* 2)))
405 center
= mempeakcount
/ (zoomlevel
* 2);
406 if(center
> (((zoomlevel
* 2) - 1) * (mempeakcount
/
408 center
= ((zoomlevel
* 2) - 1) * (mempeakcount
/
410 leftmargin
= center
- (mempeakcount
/ (zoomlevel
* 2));
413 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
420 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
421 rb
->cpu_boost(false);
424 action
= rb
->get_action(CONTEXT_KEYBOARD
, TIMEOUT_BLOCK
);
431 rb
->splashf(HZ
/2, "ZOOM: %dx",(int)zoomlevel
);
433 case ACTION_KBD_DOWN
:
434 if(zoomlevel
< (mempeakcount
/ LCD_WIDTH
/ 2))
436 rb
->splashf(HZ
/2, "ZOOM: %dx",(int)zoomlevel
);
438 case ACTION_KBD_LEFT
:
439 center
-= 10 * (mempeakcount
/ LCD_WIDTH
) / zoomlevel
;
441 case ACTION_KBD_RIGHT
:
442 center
+= 10 * (mempeakcount
/ LCD_WIDTH
) / zoomlevel
;
444 case ACTION_KBD_ABORT
:
447 case ACTION_KBD_SELECT
:
450 case ACTION_KBD_PAGE_FLIP
:
451 /* menu key shows help */
455 retval
= rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_BLOCK
);
456 if((ACTION_KBD_SELECT
== retval
) ||
457 (ACTION_KBD_ABORT
== retval
))