Don't objcopy simulator plugins.
[kugel-rb.git] / apps / plugins / wavview.c
blob7a26a3911fbd114cfcbd40384bb435150cc149ec
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
21 #include "plugin.h"
23 PLUGIN_HEADER
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
32 int8_t chunkid[4];
33 int32_t chunksize;
34 int8_t format[4];
35 int8_t formatchunkid[4];
36 int32_t formatchunksize;
37 int16_t audioformat;
38 int16_t numchannels;
39 uint32_t samplerate;
40 uint32_t byterate;
41 uint16_t blockalign;
42 uint16_t bitspersample;
43 int8_t datachunkid[4];
44 int32_t datachunksize;
45 } header;
47 #define WAV_HEADER_FORMAT "4L44LSSLLSS4L"
49 struct peakstruct
51 int lmin;
52 int lmax;
53 int rmin;
54 int rmax;
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)
63 /* global vars */
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;
79 while (*format) {
80 switch (*format) {
81 case 'L':
82 *(long *)cp = letoh32(*(long *)cp);
83 cp += 4;
84 break;
86 case 'S':
87 *(short *)cp = letoh16(*(short *)cp);
88 cp += 2;
89 break;
91 default:
92 if (*format >= '0' && *format <= '9')
93 cp += *format - '0';
95 break;
97 format++;
100 /* --- */
102 /* read WAV file
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;
112 int file;
113 uint32_t total_bytes_read = 0;
114 char tstr[128];
115 int hours;
116 int minutes;
117 int seconds;
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!");
124 return -1;
127 file = rb->open(filename, O_RDONLY);
129 if(file < 0)
131 rb->splash(HZ*2, "Could not open file!");
132 return -1;
135 if(rb->read(file, &header, sizeof (header)) != sizeof (header))
137 rb->splash(HZ*2, "Could not read file!");
138 rb->close (file);
139 return -1;
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!");
152 rb->close (file);
153 return -1;
156 rb->lcd_clear_display();
157 rb->lcd_puts(0, 0, "Viewing file:");
158 rb->lcd_puts_scroll(0, 1, (unsigned char *)filename);
159 rb->lcd_update();
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,
177 minutes,
178 seconds);
179 rb->lcd_puts(0, 5, tstr);
180 rb->lcd_puts(0, 6, "Searching for peaks... ");
181 rb->lcd_update();
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;
190 fppmp_count = fppmp;
191 mempeakcount = 0;
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;
198 if(0 == bytes_read)
200 rb->splash(HZ*2, "File read error!");
201 rb->close (file);
202 return -1;
204 if(((bytes_read/4)*4) != bytes_read)
206 rb->splashf(HZ*2, "bytes_read/*4 err: %ld",(long int)bytes_read);
207 rb->close (file);
208 return -1;
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;
219 while(bytes_read)
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;
233 bytes_read -= 4;
234 peakcount++;
235 fppmp_count--;
236 if(!fppmp_count)
238 peak++;
239 mempeakcount++;
240 fppmp_count = fppmp;
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);
255 rb->lcd_update();
257 /* allow user to abort */
258 if(ACTION_KBD_ABORT == rb->get_action(CONTEXT_KEYBOARD,TIMEOUT_NOBLOCK))
260 rb->splash(HZ*2, "ABORTED");
261 rb->close (file);
262 return -1;
266 rb->lcd_puts(0, 6, "Searching for peaks... done");
267 rb->lcd_update();
269 rb->close (file);
271 return 0;
274 int displaypeaks(void)
276 register int x = 0;
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;
284 #if LCD_DEPTH > 1
285 unsigned org_forecolor = rb->lcd_get_foreground();
286 rb->lcd_set_foreground(LCD_LIGHTGRAY);
287 #endif
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));
301 #if LCD_DEPTH > 1
302 rb->lcd_set_foreground(LCD_BLACK);
303 #endif
305 /* draw zoombar */
306 rb->lcd_hline(leftmargin / (mempeakcount / LCD_WIDTH),
307 (leftmargin / (mempeakcount / LCD_WIDTH)) +
308 (LCD_WIDTH / zoomlevel),
309 LCD_HEIGHT / 2);
311 while((x < LCD_WIDTH) && (peakcount < mempeakcount))
313 if(peak->lmin < lymin)
314 lymin = peak->lmin;
315 if(peak->lmax > lymax)
316 lymax = peak->lmax;
317 if(peak->rmin < rymin)
318 rymin = peak->rmin;
319 if(peak->rmax > rymax)
320 rymax = peak->rmax;
321 peak++;
322 if(0 == (peakcount % ppp))
324 /* drawing time */
325 rb->lcd_vline(x, LEFTZERO - (lymax / YSCALE),
326 LEFTZERO - (lymin / YSCALE));
327 rb->lcd_vline(x, RIGHTZERO - (rymax / YSCALE),
328 RIGHTZERO - (rymin / YSCALE));
329 lymin = INT_MAX;
330 lymax = INT_MIN;
331 rymin = INT_MAX;
332 rymax = INT_MIN;
333 x++;
334 rb->lcd_update();
336 peakcount++;
339 #if LCD_DEPTH > 1
340 rb->lcd_set_foreground(org_forecolor);
341 #endif
343 return 0;
346 void show_help(void)
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");
354 rb->lcd_update();
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;
362 rb = api;
363 int retval;
365 if (!parameter)
366 return PLUGIN_ERROR;
368 audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuflen);
370 if (!audiobuf)
372 rb->splash(HZ*2, "unable to get audio buffer!");
373 return PLUGIN_ERROR;
376 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
377 rb->cpu_boost(true);
378 #endif
380 retval = readwavpeaks(parameter); /* read WAV file and create peaks array */
382 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
383 rb->cpu_boost(false);
384 #endif
386 if(retval)
387 return 0;
389 /* press any key to continue */
390 while(1)
392 retval = rb->get_action(CONTEXT_KEYBOARD,TIMEOUT_BLOCK);
393 if(ACTION_KBD_ABORT == retval)
394 return 0;
395 else if(ACTION_KBD_SELECT == retval)
396 break;
399 /* start with the overview */
400 zoomlevel = 1;
401 leftmargin = 0;
403 while(!quit)
405 if(!center)
406 center = mempeakcount / 2;
407 if(zoomlevel <= 1)
409 zoomlevel = 1;
410 leftmargin = 0;
412 else
414 if(center < (mempeakcount / (zoomlevel * 2)))
415 center = mempeakcount / (zoomlevel * 2);
416 if(center > (((zoomlevel * 2) - 1) * (mempeakcount /
417 (zoomlevel * 2))))
418 center = ((zoomlevel * 2) - 1) * (mempeakcount /
419 (zoomlevel * 2));
420 leftmargin = center - (mempeakcount / (zoomlevel * 2));
423 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
424 rb->cpu_boost(true);
425 #endif
426 if(dodisplay)
427 displaypeaks();
428 dodisplay = 1;
430 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
431 rb->cpu_boost(false);
432 #endif
434 action = rb->get_action(CONTEXT_KEYBOARD, TIMEOUT_BLOCK);
435 switch(action)
437 case ACTION_KBD_UP:
438 /* zoom out */
439 if(zoomlevel > 1)
440 zoomlevel /= 2;
441 rb->splashf(HZ/2, "ZOOM: %dx",(int)zoomlevel);
442 break;
443 case ACTION_KBD_DOWN:
444 if(zoomlevel < (mempeakcount / LCD_WIDTH / 2))
445 zoomlevel *= 2;
446 rb->splashf(HZ/2, "ZOOM: %dx",(int)zoomlevel);
447 break;
448 case ACTION_KBD_LEFT:
449 center -= 10 * (mempeakcount / LCD_WIDTH) / zoomlevel;
450 break;
451 case ACTION_KBD_RIGHT:
452 center += 10 * (mempeakcount / LCD_WIDTH) / zoomlevel;
453 break;
454 case ACTION_KBD_ABORT:
455 quit = 1;
456 break;
457 case ACTION_KBD_SELECT:
458 /* refresh */
459 break;
460 case ACTION_KBD_PAGE_FLIP:
461 /* menu key shows help */
462 show_help();
463 while(1)
465 retval = rb->get_action(CONTEXT_KEYBOARD,TIMEOUT_BLOCK);
466 if((ACTION_KBD_SELECT == retval) ||
467 (ACTION_KBD_ABORT == retval))
468 break;
470 break;
471 default:
472 /* eat it */
473 dodisplay = 0;
474 break;
478 return 0;