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 static const struct plugin_api
* rb
;
27 /* temp byte buffer */
28 uint8_t samples
[10 * 1024]; /* read 10KB at the time */
30 static struct wav_header
35 int8_t formatchunkid
[4];
36 int32_t formatchunksize
;
42 uint16_t bitspersample
;
43 int8_t datachunkid
[4];
44 int32_t datachunksize
;
47 #define WAV_HEADER_FORMAT "4L44LSSLLSS4L"
57 /* TO DO: limit used LCD height, so the waveform isn't streched vertically? */
59 #define LEFTZERO (LCD_HEIGHT / 4)
60 #define RIGHTZERO (3 * (LCD_HEIGHT / 4))
61 #define YSCALE ((0x8000 / (LCD_HEIGHT / 4)) + 1)
64 static char *audiobuf
;
65 static ssize_t audiobuflen
;
66 static uint32_t mempeakcount
= 0;
67 static uint32_t filepeakcount
= 0;
68 static uint32_t fppmp
= 0; /* file peaks per mem peaks */
69 static uint32_t zoomlevel
= 1;
70 static uint32_t leftmargin
= 0;
71 static uint32_t center
= 0;
72 static uint32_t ppp
= 1;
74 /* helper function copied from libwavpack bits.c */
75 void little_endian_to_native (void *data
, char *format
)
77 unsigned char *cp
= (unsigned char *) data
;
82 *(long *)cp
= letoh32(*(long *)cp
);
87 *(short *)cp
= letoh16(*(short *)cp
);
92 if (*format
>= '0' && *format
<= '9')
103 display some info about it
104 store peak info in aufiobuf for display routine */
105 static int readwavpeaks(const char *filename
)
107 register uint32_t bytes_read
;
108 register uint32_t fppmp_count
;
109 register int16_t sampleval
;
110 register uint16_t* sampleshort
= NULL
;
113 uint32_t total_bytes_read
= 0;
118 uint32_t peakcount
= 0;
119 struct peakstruct
* peak
= NULL
;
121 if(rb
->strcasecmp (filename
+ rb
->strlen (filename
) - 3, "wav"))
123 rb
->splash(HZ
*2, "Only for wav files!");
127 file
= rb
->open(filename
, O_RDONLY
);
131 rb
->splash(HZ
*2, "Could not open file!");
135 if(rb
->read(file
, &header
, sizeof (header
)) != sizeof (header
))
137 rb
->splash(HZ
*2, "Could not read file!");
142 total_bytes_read
+= sizeof (header
);
143 little_endian_to_native(&header
, WAV_HEADER_FORMAT
);
145 if (rb
->strncmp(header
.chunkid
, "RIFF", 4) ||
146 rb
->strncmp(header
.formatchunkid
, "fmt ", 4) ||
147 rb
->strncmp(header
.datachunkid
, "data", 4) ||
148 (header
.bitspersample
!= 16) ||
149 header
.audioformat
!= 1)
151 rb
->splash(HZ
*2, "Incompatible wav file!");
156 rb
->lcd_clear_display();
157 rb
->lcd_puts(0, 0, "Viewing file:");
158 rb
->lcd_puts_scroll(0, 1, (unsigned char *)filename
);
161 rb
->snprintf(tstr
,127, "Channels: %s",
162 header
.numchannels
== 1 ? "mono" : "stereo");
163 rb
->lcd_puts(0, 2, tstr
);
165 rb
->snprintf(tstr
,127, "Bits/sample: %d", header
.bitspersample
);
166 rb
->lcd_puts(0, 3, tstr
);
168 rb
->snprintf(tstr
,127, "Samplerate: %d Hz", (int)(header
.samplerate
));
169 rb
->lcd_puts(0, 4, tstr
);
171 seconds
= header
.datachunksize
/ header
.byterate
;
172 hours
= seconds
/ 3600;
173 seconds
-= hours
* 3600;
174 minutes
= seconds
/ 60;
175 seconds
-= minutes
* 60;
176 rb
->snprintf(tstr
,127, "Length (hh:mm:ss): %02d:%02d:%02d", hours
,
179 rb
->lcd_puts(0, 5, tstr
);
180 rb
->lcd_puts(0, 6, "Searching for peaks... ");
183 /* calculate room for peaks */
184 filepeakcount
= header
.datachunksize
/
185 (header
.numchannels
* (header
.bitspersample
/ 8));
186 mempeakcount
= audiobuflen
/ sizeof(struct peakstruct
);
187 fppmp
= (filepeakcount
/ mempeakcount
) + 1;
188 peak
= (struct peakstruct
*)audiobuf
;
192 while(total_bytes_read
< (header
.datachunksize
+
193 sizeof(struct wav_header
)))
195 bytes_read
= rb
->read(file
, &samples
, sizeof(samples
));
196 total_bytes_read
+= bytes_read
;
200 rb
->splash(HZ
*2, "File read error!");
204 if(((bytes_read
/4)*4) != bytes_read
)
206 rb
->splashf(HZ
*2, "bytes_read/*4 err: %ld",(long int)bytes_read
);
211 sampleshort
= (int16_t*)samples
;
212 sampleval
= letoh16(*sampleshort
);
213 peak
->lmin
= sampleval
;
214 peak
->lmax
= sampleval
;
215 sampleval
= letoh16(*(sampleshort
+1));
216 peak
->rmin
= sampleval
;
217 peak
->rmax
= sampleval
;
221 sampleval
= letoh16(*sampleshort
++);
222 if(sampleval
< peak
->lmin
)
223 peak
->lmin
= sampleval
;
224 else if (sampleval
> peak
->lmax
)
225 peak
->lmax
= sampleval
;
227 sampleval
= letoh16(*sampleshort
++);
228 if(sampleval
< peak
->rmin
)
229 peak
->rmin
= sampleval
;
230 else if (sampleval
> peak
->rmax
)
231 peak
->rmax
= sampleval
;
241 sampleval
= letoh16(*sampleshort
);
242 peak
->lmin
= sampleval
;
243 peak
->lmax
= sampleval
;
244 sampleval
= letoh16(*(sampleshort
+1));
245 peak
->rmin
= sampleval
;
246 peak
->rmax
= sampleval
;
250 /* update progress */
251 rb
->snprintf(tstr
,127, "Searching for peaks... %d%%",(int)
252 (total_bytes_read
/ ((header
.datachunksize
+
253 sizeof(struct wav_header
)) / 100)));
254 rb
->lcd_puts(0, 6, tstr
);
257 /* allow user to abort */
258 if(ACTION_KBD_ABORT
== rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_NOBLOCK
))
260 rb
->splash(HZ
*2, "ABORTED");
266 rb
->lcd_puts(0, 6, "Searching for peaks... done");
274 int displaypeaks(void)
277 register int lymin
= INT_MAX
;
278 register int lymax
= INT_MIN
;
279 register int rymin
= INT_MAX
;
280 register int rymax
= INT_MIN
;
281 register unsigned int peakcount
= 0;
282 struct peakstruct
* peak
= (struct peakstruct
*)audiobuf
+ leftmargin
;
285 unsigned org_forecolor
= rb
->lcd_get_foreground();
286 rb
->lcd_set_foreground(LCD_LIGHTGRAY
);
289 if(!zoomlevel
) zoomlevel
= 1;
290 ppp
= (mempeakcount
/ LCD_WIDTH
) / zoomlevel
; /* peaks per pixel */
292 rb
->lcd_clear_display();
294 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
- (0x8000 / YSCALE
));
295 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
);
296 rb
->lcd_hline(0, LCD_WIDTH
-1, LEFTZERO
+ (0x8000 / YSCALE
));
297 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
- (0x8000 / YSCALE
));
298 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
);
299 rb
->lcd_hline(0, LCD_WIDTH
-1, RIGHTZERO
+ (0x8000 / YSCALE
));
302 rb
->lcd_set_foreground(LCD_BLACK
);
306 rb
->lcd_hline(leftmargin
/ (mempeakcount
/ LCD_WIDTH
),
307 (leftmargin
/ (mempeakcount
/ LCD_WIDTH
)) +
308 (LCD_WIDTH
/ zoomlevel
),
311 while((x
< LCD_WIDTH
) && (peakcount
< mempeakcount
))
313 if(peak
->lmin
< lymin
)
315 if(peak
->lmax
> lymax
)
317 if(peak
->rmin
< rymin
)
319 if(peak
->rmax
> rymax
)
322 if(0 == (peakcount
% ppp
))
325 rb
->lcd_vline(x
, LEFTZERO
- (lymax
/ YSCALE
),
326 LEFTZERO
- (lymin
/ YSCALE
));
327 rb
->lcd_vline(x
, RIGHTZERO
- (rymax
/ YSCALE
),
328 RIGHTZERO
- (rymin
/ YSCALE
));
340 rb
->lcd_set_foreground(org_forecolor
);
348 rb
->lcd_clear_display();
349 rb
->lcd_puts(0, 0, "WAVVIEW USAGE:");
350 rb
->lcd_puts(0, 2, "up/down: zoom out/in");
351 rb
->lcd_puts(0, 3, "left/right: pan left/right");
352 rb
->lcd_puts(0, 4, "select: refresh/continue");
353 rb
->lcd_puts(0, 5, "stop/off: quit");
357 enum plugin_status
plugin_start(const struct plugin_api
* api
, const void *parameter
)
359 unsigned int quit
= 0;
360 unsigned int action
= 0;
361 unsigned int dodisplay
= 1;
368 audiobuf
= rb
->plugin_get_audio_buffer((size_t *)&audiobuflen
);
372 rb
->splash(HZ
*2, "unable to get audio buffer!");
376 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
380 retval
= readwavpeaks(parameter
); /* read WAV file and create peaks array */
382 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
383 rb
->cpu_boost(false);
389 /* press any key to continue */
392 retval
= rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_BLOCK
);
393 if(ACTION_KBD_ABORT
== retval
)
395 else if(ACTION_KBD_SELECT
== retval
)
399 /* start with the overview */
406 center
= mempeakcount
/ 2;
414 if(center
< (mempeakcount
/ (zoomlevel
* 2)))
415 center
= mempeakcount
/ (zoomlevel
* 2);
416 if(center
> (((zoomlevel
* 2) - 1) * (mempeakcount
/
418 center
= ((zoomlevel
* 2) - 1) * (mempeakcount
/
420 leftmargin
= center
- (mempeakcount
/ (zoomlevel
* 2));
423 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
430 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
431 rb
->cpu_boost(false);
434 action
= rb
->get_action(CONTEXT_KEYBOARD
, TIMEOUT_BLOCK
);
441 rb
->splashf(HZ
/2, "ZOOM: %dx",(int)zoomlevel
);
443 case ACTION_KBD_DOWN
:
444 if(zoomlevel
< (mempeakcount
/ LCD_WIDTH
/ 2))
446 rb
->splashf(HZ
/2, "ZOOM: %dx",(int)zoomlevel
);
448 case ACTION_KBD_LEFT
:
449 center
-= 10 * (mempeakcount
/ LCD_WIDTH
) / zoomlevel
;
451 case ACTION_KBD_RIGHT
:
452 center
+= 10 * (mempeakcount
/ LCD_WIDTH
) / zoomlevel
;
454 case ACTION_KBD_ABORT
:
457 case ACTION_KBD_SELECT
:
460 case ACTION_KBD_PAGE_FLIP
:
461 /* menu key shows help */
465 retval
= rb
->get_action(CONTEXT_KEYBOARD
,TIMEOUT_BLOCK
);
466 if((ACTION_KBD_SELECT
== retval
) ||
467 (ACTION_KBD_ABORT
== retval
))