c61e89c0ed
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25843 a1c6a512-1295-4272-9138-f99709370657
303 lines
9 KiB
C
303 lines
9 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2005 David Bryant
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
#include "plugin.h"
|
|
#ifdef RB_PROFILE
|
|
#include "lib/profile_plugin.h"
|
|
#endif
|
|
|
|
#include <codecs/libwavpack/wavpack.h>
|
|
|
|
PLUGIN_HEADER
|
|
|
|
#define SAMPLES_PER_BLOCK 22050
|
|
|
|
static char *audiobuf;
|
|
static ssize_t audiobuflen;
|
|
|
|
static struct wav_header {
|
|
char ckID [4]; /* RIFF chuck header */
|
|
int32_t ckSize;
|
|
char formType [4];
|
|
|
|
char fmt_ckID [4]; /* format chunk header */
|
|
int32_t fmt_ckSize;
|
|
ushort FormatTag, NumChannels;
|
|
uint32_t SampleRate, BytesPerSecond;
|
|
ushort BlockAlign, BitsPerSample;
|
|
|
|
char data_ckID [4]; /* data chunk header */
|
|
int32_t data_ckSize;
|
|
} raw_header, native_header;
|
|
|
|
#define WAV_HEADER_FORMAT "4L44LSSLLSS4L"
|
|
|
|
static void wvupdate (int32_t start_tick,
|
|
int32_t sample_rate,
|
|
uint32_t total_samples,
|
|
uint32_t samples_converted,
|
|
uint32_t bytes_read,
|
|
uint32_t bytes_written)
|
|
{
|
|
int32_t elapsed_ticks = *rb->current_tick - start_tick;
|
|
int compression = 0, progress = 0, realtime = 0;
|
|
char buf[32];
|
|
|
|
if (total_samples)
|
|
progress = (int)(((int64_t) samples_converted * 100 +
|
|
(total_samples/2)) / total_samples);
|
|
|
|
if (elapsed_ticks)
|
|
realtime = (int)(((int64_t) samples_converted * 100 * HZ /
|
|
sample_rate + (elapsed_ticks/2)) / elapsed_ticks);
|
|
|
|
if (bytes_read)
|
|
compression = (int)(((int64_t)(bytes_read - bytes_written) * 100 +
|
|
(bytes_read/2)) / bytes_read);
|
|
|
|
rb->snprintf(buf, 32, "elapsed time: %ld secs",
|
|
(long)(elapsed_ticks + (HZ/2)) / HZ);
|
|
rb->lcd_puts(0, 2, (unsigned char *)buf);
|
|
|
|
rb->snprintf(buf, 32, "progress: %d%%", progress);
|
|
rb->lcd_puts(0, 4, (unsigned char *)buf);
|
|
|
|
rb->snprintf(buf, 32, "realtime: %d%% ", realtime);
|
|
rb->lcd_puts(0, 6, (unsigned char *)buf);
|
|
|
|
rb->snprintf(buf, 32, "compression: %d%% ", compression);
|
|
rb->lcd_puts(0, 8, (unsigned char *)buf);
|
|
|
|
#ifdef HAVE_LCD_BITMAP
|
|
rb->lcd_update();
|
|
#endif
|
|
}
|
|
|
|
#define TEMP_SAMPLES 4096
|
|
|
|
static int32_t temp_buffer [TEMP_SAMPLES] IDATA_ATTR;
|
|
|
|
static int wav2wv(const char *infile)
|
|
{
|
|
int in_fd, out_fd, num_chans, error = false, last_buttons;
|
|
unsigned int32_t total_bytes_read = 0, total_bytes_written = 0;
|
|
unsigned int32_t total_samples, samples_remaining;
|
|
int32_t *input_buffer = (int32_t *) audiobuf;
|
|
unsigned char *output_buffer = (unsigned char *)(audiobuf + 0x100000);
|
|
char outfile[MAX_PATH];
|
|
const char *inextension;
|
|
char *outextension;
|
|
WavpackConfig config;
|
|
WavpackContext *wpc;
|
|
int32_t start_tick;
|
|
|
|
rb->lcd_clear_display();
|
|
rb->lcd_puts_scroll(0, 0, (unsigned char *)infile);
|
|
#ifdef HAVE_LCD_BITMAP
|
|
rb->lcd_update();
|
|
#endif
|
|
|
|
last_buttons = rb->button_status ();
|
|
start_tick = *rb->current_tick;
|
|
inextension = infile + rb->strlen(infile) - 3;
|
|
if (rb->strcasecmp (inextension, "wav")) {
|
|
rb->splash(HZ*2, "only for wav files!");
|
|
return 1;
|
|
}
|
|
|
|
in_fd = rb->open(infile, O_RDONLY);
|
|
|
|
if (in_fd < 0) {
|
|
rb->splash(HZ*2, "could not open file!");
|
|
return true;
|
|
}
|
|
|
|
if (rb->read (in_fd, &raw_header, sizeof (raw_header)) != sizeof (raw_header)) {
|
|
rb->splash(HZ*2, "could not read file!");
|
|
return true;
|
|
}
|
|
|
|
total_bytes_read += sizeof (raw_header);
|
|
rb->memcpy (&native_header, &raw_header, sizeof (raw_header));
|
|
little_endian_to_native (&native_header, WAV_HEADER_FORMAT);
|
|
|
|
if (rb->strncmp (native_header.ckID, "RIFF", 4) ||
|
|
rb->strncmp (native_header.fmt_ckID, "fmt ", 4) ||
|
|
rb->strncmp (native_header.data_ckID, "data", 4) ||
|
|
native_header.FormatTag != 1 || native_header.BitsPerSample != 16) {
|
|
rb->splash(HZ*2, "incompatible wav file!");
|
|
return true;
|
|
}
|
|
|
|
wpc = WavpackOpenFileOutput ();
|
|
|
|
rb->memset (&config, 0, sizeof (config));
|
|
config.bits_per_sample = 16;
|
|
config.bytes_per_sample = 2;
|
|
config.sample_rate = native_header.SampleRate;
|
|
num_chans = config.num_channels = native_header.NumChannels;
|
|
total_samples = native_header.data_ckSize / native_header.BlockAlign;
|
|
|
|
/* config.flags |= CONFIG_HIGH_FLAG; */
|
|
|
|
if (!WavpackSetConfiguration (wpc, &config, total_samples)) {
|
|
rb->splash(HZ*2, "internal error!");
|
|
rb->close (in_fd);
|
|
return true;
|
|
}
|
|
|
|
WavpackAddWrapper (wpc, &raw_header, sizeof (raw_header));
|
|
|
|
rb->strcpy(outfile, infile);
|
|
outextension = outfile + rb->strlen(outfile) - 3;
|
|
outextension[1] = outextension[2];
|
|
outextension[2] = 0;
|
|
out_fd = rb->creat(outfile, 0666);
|
|
|
|
if (out_fd < 0) {
|
|
rb->splash(HZ*2, "could not create file!");
|
|
rb->close (in_fd);
|
|
return true;
|
|
}
|
|
|
|
wvupdate (start_tick, native_header.SampleRate, total_samples, 0, 0, 0);
|
|
|
|
for (samples_remaining = total_samples; samples_remaining;) {
|
|
unsigned int32_t samples_count, samples_to_pack, bytes_count;
|
|
int cnt, buttons;
|
|
int32_t value, *lp;
|
|
signed char *cp;
|
|
|
|
samples_count = SAMPLES_PER_BLOCK;
|
|
|
|
if (samples_count > samples_remaining)
|
|
samples_count = samples_remaining;
|
|
|
|
bytes_count = samples_count * num_chans * 2;
|
|
|
|
if (rb->read (in_fd, input_buffer, bytes_count) != (int32_t) bytes_count) {
|
|
rb->splash(HZ*2, "could not read file!");
|
|
error = true;
|
|
break;
|
|
}
|
|
|
|
total_bytes_read += bytes_count;
|
|
WavpackStartBlock (wpc, output_buffer, output_buffer + 0x100000);
|
|
samples_to_pack = samples_count;
|
|
cp = (signed char *) input_buffer;
|
|
|
|
while (samples_to_pack) {
|
|
unsigned int32_t samples_this_pass = TEMP_SAMPLES / num_chans;
|
|
|
|
if (samples_this_pass > samples_to_pack)
|
|
samples_this_pass = samples_to_pack;
|
|
|
|
lp = temp_buffer;
|
|
cnt = samples_this_pass;
|
|
|
|
if (num_chans == 2)
|
|
while (cnt--) {
|
|
value = *cp++ & 0xff;
|
|
value += *cp++ << 8;
|
|
*lp++ = value;
|
|
value = *cp++ & 0xff;
|
|
value += *cp++ << 8;
|
|
*lp++ = value;
|
|
}
|
|
else
|
|
while (cnt--) {
|
|
value = *cp++ & 0xff;
|
|
value += *cp++ << 8;
|
|
*lp++ = value;
|
|
}
|
|
|
|
if (!WavpackPackSamples (wpc, temp_buffer, samples_this_pass)) {
|
|
rb->splash(HZ*2, "internal error!");
|
|
error = true;
|
|
break;
|
|
}
|
|
|
|
samples_to_pack -= samples_this_pass;
|
|
}
|
|
|
|
if (error)
|
|
break;
|
|
|
|
bytes_count = WavpackFinishBlock (wpc);
|
|
|
|
if (rb->write (out_fd, output_buffer, bytes_count) != (int32_t) bytes_count) {
|
|
rb->splash(HZ*2, "could not write file!");
|
|
error = true;
|
|
break;
|
|
}
|
|
|
|
total_bytes_written += bytes_count;
|
|
samples_remaining -= samples_count;
|
|
|
|
wvupdate (start_tick, native_header.SampleRate, total_samples,
|
|
total_samples - samples_remaining, total_bytes_read, total_bytes_written);
|
|
|
|
buttons = rb->button_status ();
|
|
|
|
if (last_buttons == BUTTON_NONE && buttons != BUTTON_NONE) {
|
|
rb->splash(HZ*2, "operation aborted!");
|
|
error = true;
|
|
break;
|
|
}
|
|
else
|
|
last_buttons = buttons;
|
|
}
|
|
|
|
rb->close (out_fd);
|
|
rb->close (in_fd);
|
|
|
|
if (error) {
|
|
rb->remove (outfile);
|
|
}
|
|
else
|
|
rb->splash(HZ*3, "operation successful");
|
|
|
|
return error;
|
|
}
|
|
|
|
enum plugin_status plugin_start(const void *parameter)
|
|
{
|
|
if (!parameter)
|
|
return PLUGIN_ERROR;
|
|
|
|
audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuflen);
|
|
|
|
if (audiobuflen < 0x200000) {
|
|
rb->splash(HZ*2, "not enough memory!");
|
|
return PLUGIN_ERROR;
|
|
}
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
rb->cpu_boost(true);
|
|
#endif
|
|
|
|
wav2wv (parameter);
|
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
rb->cpu_boost(false);
|
|
#endif
|
|
/* Return PLUGIN_USB_CONNECTED to force a file-tree refresh */
|
|
return PLUGIN_USB_CONNECTED;
|
|
}
|