rockbox/apps/plugins/wav2wv.c

328 lines
9.6 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 David Bryant
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* 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 struct plugin_api* rb;
void *memset(void *s, int c, size_t n) {
return(rb->memset(s,c,n));
}
void *memcpy(void *dest, const void *src, size_t n) {
return(rb->memcpy(dest,src,n));
}
static char *audiobuf;
static int audiobuflen;
static struct wav_header {
char ckID [4]; /* RIFF chuck header */
long ckSize;
char formType [4];
char fmt_ckID [4]; /* format chunk header */
long fmt_ckSize;
ushort FormatTag, NumChannels;
ulong SampleRate, BytesPerSecond;
ushort BlockAlign, BitsPerSample;
char data_ckID [4]; /* data chunk header */
long data_ckSize;
} raw_header, native_header;
#define WAV_HEADER_FORMAT "4L44LSSLLSS4L"
static void wvupdate (long start_tick,
long sample_rate,
ulong total_samples,
ulong samples_converted,
ulong bytes_read,
ulong bytes_written)
{
long elapsed_ticks = *rb->current_tick - start_tick;
int compression = 0, progress = 0, realtime = 0;
char buf[32];
if (total_samples)
progress = (int)(((long long) samples_converted * 100 +
(total_samples/2)) / total_samples);
if (elapsed_ticks)
realtime = (int)(((long long) samples_converted * 100 * HZ /
sample_rate + (elapsed_ticks/2)) / elapsed_ticks);
if (bytes_read)
compression = (int)(((long long)(bytes_read - bytes_written) * 100 +
(bytes_read/2)) / bytes_read);
rb->snprintf(buf, 32, "elapsed time: %d secs", (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 long temp_buffer [TEMP_SAMPLES] IDATA_ATTR;
static int wav2wv (char *filename)
{
int in_fd, out_fd, num_chans, error = false, last_buttons;
unsigned long total_bytes_read = 0, total_bytes_written = 0;
unsigned long total_samples, samples_remaining;
long *input_buffer = (long *) audiobuf;
unsigned char *output_buffer = (unsigned char *)(audiobuf + 0x100000);
char *extension, save_a;
WavpackConfig config;
WavpackContext *wpc;
long start_tick;
rb->lcd_clear_display();
rb->lcd_puts_scroll(0, 0, (unsigned char *)filename);
#ifdef HAVE_LCD_BITMAP
rb->lcd_update();
#endif
last_buttons = rb->button_status ();
start_tick = *rb->current_tick;
extension = filename + rb->strlen (filename) - 3;
if (rb->strcasecmp (extension, "wav")) {
rb->splash(HZ*2, true, "only for wav files!");
return 1;
}
in_fd = rb->open(filename, O_RDONLY);
if (in_fd < 0) {
rb->splash(HZ*2, true, "could not open file!");
return true;
}
if (rb->read (in_fd, &raw_header, sizeof (raw_header)) != sizeof (raw_header)) {
rb->splash(HZ*2, true, "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, true, "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, true, "internal error!");
rb->close (in_fd);
return true;
}
WavpackAddWrapper (wpc, &raw_header, sizeof (raw_header));
save_a = extension [1];
extension [1] = extension [2];
extension [2] = 0;
out_fd = rb->creat (filename, O_WRONLY);
extension [2] = extension [1];
extension [1] = save_a;
if (out_fd < 0) {
rb->splash(HZ*2, true, "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 long samples_count, samples_to_pack, bytes_count;
int cnt, buttons;
long 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) != (long) bytes_count) {
rb->splash(HZ*2, true, "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 long 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, true, "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) != (long) bytes_count) {
rb->splash(HZ*2, true, "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, true, "operation aborted!");
error = true;
break;
}
else
last_buttons = buttons;
}
rb->close (out_fd);
rb->close (in_fd);
if (error) {
save_a = extension [1];
extension [1] = extension [2];
extension [2] = 0;
rb->remove (filename);
extension [2] = extension [1];
extension [1] = save_a;
}
else
rb->splash(HZ*3, true, "operation successful");
return error;
}
enum plugin_status plugin_start(struct plugin_api* api, void *parameter)
{
#ifdef RB_PROFILE
/* This doesn't start profiling or anything, it just gives the
* profiling functions that are compiled in someplace to call,
* this is needed here to let this compile with profiling support
* since it calls code from a codec that is compiled with profiling
* support */
profile_init(api);
#endif
rb = api;
if (!parameter)
return PLUGIN_ERROR;
audiobuf = rb->plugin_get_audio_buffer(&audiobuflen);
if (audiobuflen < 0x200000) {
rb->splash(HZ*2, true, "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;
}