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 ssize_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;
116 uint32_t peakcount
= 0;
117 struct peakstruct
* peak
= NULL
;
119 if(rb
->strcasecmp (filename
+ rb
->strlen (filename
) - 3, "wav"))
121 rb
->splash(HZ
*2, "Only for wav files!");
125 file
= rb
->open(filename
, O_RDONLY
);
129 rb
->splash(HZ
*2, "Could not open file!");
133 if(rb
->read(file
, &header
, sizeof (header
)) != sizeof (header
))
135 rb
->splash(HZ
*2, "Could not read file!");
140 total_bytes_read
+= sizeof (header
);
141 little_endian_to_native(&header
, WAV_HEADER_FORMAT
);
143 if (rb
->strncmp(header
.chunkid
, "RIFF", 4) ||
144 rb
->strncmp(header
.formatchunkid
, "fmt ", 4) ||
145 rb
->strncmp(header
.datachunkid
, "data", 4) ||
146 (header
.bitspersample
!= 16) ||
147 header
.audioformat
!= 1)
149 rb
->splash(HZ
*2, "Incompatible wav file!");
154 rb
->lcd_clear_display();
155 rb
->lcd_puts(0, 0, "Viewing file:");
156 rb
->lcd_puts_scroll(0, 1, (unsigned char *)filename
);
159 rb
->snprintf(tstr
,127, "Channels: %s",
160 header
.numchannels
== 1 ? "mono" : "stereo");
161 rb
->lcd_puts(0, 2, tstr
);
163 rb
->snprintf(tstr
,127, "Bits/sample: %d", header
.bitspersample
);
164 rb
->lcd_puts(0, 3, tstr
);
166 rb
->snprintf(tstr
,127, "Samplerate: %d Hz", (int)(header
.samplerate
));
167 rb
->lcd_puts(0, 4, tstr
);
169 seconds
= header
.datachunksize
/ header
.byterate
;
170 hours
= seconds
/ 3600;
171 seconds
-= hours
* 3600;
172 minutes
= seconds
/ 60;
173 seconds
-= minutes
* 60;
174 rb
->snprintf(tstr
,127, "Length (hh:mm:ss): %02d:%02d:%02d", hours
,
177 rb
->lcd_puts(0, 5, tstr
);
178 rb
->lcd_puts(0, 6, "Searching for peaks... ");
181 /* calculate room for peaks */
182 filepeakcount
= header
.datachunksize
/
183 (header
.numchannels
* (header
.bitspersample
/ 8));
184 mempeakcount
= audiobuflen
/ sizeof(struct peakstruct
);
185 fppmp
= (filepeakcount
/ mempeakcount
) + 1;
186 peak
= (struct peakstruct
*)audiobuf
;
190 while(total_bytes_read
< (header
.datachunksize
+
191 sizeof(struct wav_header
)))
193 bytes_read
= rb
->read(file
, &samples
, sizeof(samples
));
194 total_bytes_read
+= bytes_read
;
198 rb
->splash(HZ
*2, "File read error!");
202 if(((bytes_read
/4)*4) != bytes_read
)
204 rb
->splashf(HZ
*2, "bytes_read/*4 err: %ld",(long int)bytes_read
);
209 sampleshort
= (int16_t*)samples
;
210 sampleval
= letoh16(*sampleshort
);
211 peak
->lmin
= sampleval
;
212 peak
->lmax
= sampleval
;
213 sampleval
= letoh16(*(sampleshort
+1));
214 peak
->rmin
= sampleval
;
215 peak
->rmax
= sampleval
;
219 sampleval
= letoh16(*sampleshort
++);
220 if(sampleval
< peak
->lmin
)
221 peak
->lmin
= sampleval
;
222 else if (sampleval
> peak
->lmax
)
223 peak
->lmax
= sampleval
;
225 sampleval
= letoh16(*sampleshort
++);
226 if(sampleval
< peak
->rmin
)
227 peak
->rmin
= sampleval
;
228 else if (sampleval
> peak
->rmax
)
229 peak
->rmax
= sampleval
;
239 sampleval
= letoh16(*sampleshort
);
240 peak
->lmin
= sampleval
;
241 peak
->lmax
= sampleval
;
242 sampleval
= letoh16(*(sampleshort
+1));
243 peak
->rmin
= sampleval
;
244 peak
->rmax
= sampleval
;
248 /* update progress */
249 rb
->snprintf(tstr
,127, "Searching for peaks... %d%%",(int)
250 (total_bytes_read
/ ((header
.datachunksize
+
251 sizeof(struct wav_header
)) / 100)));
252 rb
->lcd_puts(0, 6, tstr
);
255 /* allow user to abort */
256 if(ACTION_KBD_ABORT
== rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_NOBLOCK
))
258 rb
->splash(HZ
*2, "ABORTED");
264 rb
->lcd_puts(0, 6, "Searching for peaks... done");
272 int displaypeaks(void)
275 register int lymin
= INT_MAX
;
276 register int lymax
= INT_MIN
;
277 register int rymin
= INT_MAX
;
278 register int rymax
= INT_MIN
;
279 register unsigned int peakcount
= 0;
280 struct peakstruct
* peak
= (struct peakstruct
*)audiobuf
+ leftmargin
;
283 unsigned org_forecolor
= rb
->lcd_get_foreground();
284 rb
->lcd_set_foreground(LCD_LIGHTGRAY
);
287 if(!zoomlevel
) zoomlevel
= 1;
288 ppp
= (mempeakcount
/ LCD_WIDTH
) / zoomlevel
; /* peaks per pixel */
290 rb
->lcd_clear_display();
292 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
- (0x8000 / YSCALE
));
293 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
);
294 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
+ (0x8000 / YSCALE
));
295 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
- (0x8000 / YSCALE
));
296 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
);
297 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
+ (0x8000 / YSCALE
));
300 rb
->lcd_set_foreground(LCD_BLACK
);
304 rb
->lcd_hline(leftmargin
/ (mempeakcount
/ LCD_WIDTH
),
305 (leftmargin
/ (mempeakcount
/ LCD_WIDTH
)) +
306 (LCD_WIDTH
/ zoomlevel
),
309 while((x
< LCD_WIDTH
) && (peakcount
< mempeakcount
))
311 if(peak
->lmin
< lymin
)
313 if(peak
->lmax
> lymax
)
315 if(peak
->rmin
< rymin
)
317 if(peak
->rmax
> rymax
)
320 if(0 == (peakcount
% ppp
))
323 rb
->lcd_vline(x
, LEFTZERO
- (lymax
/ YSCALE
),
324 LEFTZERO
- (lymin
/ YSCALE
));
325 rb
->lcd_vline(x
, RIGHTZERO
- (rymax
/ YSCALE
),
326 RIGHTZERO
- (rymin
/ YSCALE
));
338 rb
->lcd_set_foreground(org_forecolor
);
346 rb
->lcd_clear_display();
347 rb
->lcd_puts(0, 0, "WAVVIEW USAGE:");
348 rb
->lcd_puts(0, 2, "up/down: zoom out/in");
349 rb
->lcd_puts(0, 3, "left/right: pan left/right");
350 rb
->lcd_puts(0, 4, "select: refresh/continue");
351 rb
->lcd_puts(0, 5, "stop/off: quit");
355 enum plugin_status
plugin_start(const void *parameter
)
357 unsigned int quit
= 0;
358 unsigned int action
= 0;
359 unsigned int dodisplay
= 1;
365 audiobuf
= rb
->plugin_get_audio_buffer((size_t *)&audiobuflen
);
369 rb
->splash(HZ
*2, "unable to get audio buffer!");
373 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
377 retval
= readwavpeaks(parameter
); /* read WAV file and create peaks array */
379 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
380 rb
->cpu_boost(false);
386 /* press any key to continue */
389 retval
= rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_BLOCK
);
390 if(ACTION_KBD_ABORT
== retval
)
392 else if(ACTION_KBD_SELECT
== retval
)
396 /* start with the overview */
403 center
= mempeakcount
/ 2;
411 if(center
< (mempeakcount
/ (zoomlevel
* 2)))
412 center
= mempeakcount
/ (zoomlevel
* 2);
413 if(center
> (((zoomlevel
* 2) - 1) * (mempeakcount
/
415 center
= ((zoomlevel
* 2) - 1) * (mempeakcount
/
417 leftmargin
= center
- (mempeakcount
/ (zoomlevel
* 2));
420 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
427 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
428 rb
->cpu_boost(false);
431 action
= rb
->get_action(CONTEXT_KEYBOARD
, TIMEOUT_BLOCK
);
438 rb
->splashf(HZ
/2, "ZOOM: %dx",(int)zoomlevel
);
440 case ACTION_KBD_DOWN
:
441 if(zoomlevel
< (mempeakcount
/ LCD_WIDTH
/ 2))
443 rb
->splashf(HZ
/2, "ZOOM: %dx",(int)zoomlevel
);
445 case ACTION_KBD_LEFT
:
446 center
-= 10 * (mempeakcount
/ LCD_WIDTH
) / zoomlevel
;
448 case ACTION_KBD_RIGHT
:
449 center
+= 10 * (mempeakcount
/ LCD_WIDTH
) / zoomlevel
;
451 case ACTION_KBD_ABORT
:
454 case ACTION_KBD_SELECT
:
457 case ACTION_KBD_PAGE_FLIP
:
458 /* menu key shows help */
462 retval
= rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_BLOCK
);
463 if((ACTION_KBD_SELECT
== retval
) ||
464 (ACTION_KBD_ABORT
== retval
))