2004-06-10 13:29:52 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
|
|
* \/ \/ \/ \/ \/
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004 Linus Nielsen Feltzing
|
|
|
|
*
|
2008-06-28 18:10:04 +00:00
|
|
|
* 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.
|
2004-06-10 13:29:52 +00:00
|
|
|
*
|
|
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
|
|
* KIND, either express or implied.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
#include "plugin.h"
|
|
|
|
|
2010-08-24 14:30:46 +00:00
|
|
|
|
2006-01-15 18:20:18 +00:00
|
|
|
|
2007-04-21 18:38:25 +00:00
|
|
|
static char *audiobuf;
|
2010-05-07 19:27:42 +00:00
|
|
|
static size_t audiobuflen;
|
2008-01-09 12:39:06 +00:00
|
|
|
unsigned char xingbuf[1500];
|
|
|
|
char tmpname[MAX_PATH];
|
2019-08-08 20:49:16 +00:00
|
|
|
static long last_talk = 0;
|
2004-06-10 13:29:52 +00:00
|
|
|
|
|
|
|
static void xingupdate(int percent)
|
|
|
|
{
|
2010-08-28 21:46:45 +00:00
|
|
|
rb->lcd_putsf(0, 1, "%d%%", percent);
|
2004-06-10 13:29:52 +00:00
|
|
|
rb->lcd_update();
|
2019-08-08 20:49:16 +00:00
|
|
|
if (rb->global_settings->talk_menu)
|
|
|
|
{
|
|
|
|
long now = *(rb->current_tick) / HZ;
|
|
|
|
if (now - last_talk >= 5)
|
|
|
|
{
|
|
|
|
rb->talk_value(percent, UNIT_PERCENT, false);
|
|
|
|
last_talk = now;
|
|
|
|
}
|
|
|
|
}
|
2004-06-10 13:29:52 +00:00
|
|
|
}
|
|
|
|
|
2008-05-13 09:57:56 +00:00
|
|
|
static int insert_data_in_file(const char *fname, int fpos, char *buf, int num_bytes)
|
2004-06-10 13:29:52 +00:00
|
|
|
{
|
|
|
|
int readlen;
|
|
|
|
int rc;
|
|
|
|
int orig_fd, fd;
|
|
|
|
|
|
|
|
rb->snprintf(tmpname, MAX_PATH, "%s.tmp", fname);
|
|
|
|
|
|
|
|
orig_fd = rb->open(fname, O_RDONLY);
|
|
|
|
if(orig_fd < 0) {
|
|
|
|
return 10*orig_fd - 1;
|
|
|
|
}
|
|
|
|
|
2010-05-06 17:35:04 +00:00
|
|
|
fd = rb->creat(tmpname, 0666);
|
2004-06-10 13:29:52 +00:00
|
|
|
if(fd < 0) {
|
|
|
|
rb->close(orig_fd);
|
|
|
|
return 10*fd - 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* First, copy the initial portion (the ID3 tag) */
|
|
|
|
if(fpos) {
|
2005-04-05 11:33:58 +00:00
|
|
|
readlen = rb->read(orig_fd, audiobuf, fpos);
|
2004-06-10 13:29:52 +00:00
|
|
|
if(readlen < 0) {
|
|
|
|
rb->close(fd);
|
|
|
|
rb->close(orig_fd);
|
|
|
|
return 10*readlen - 3;
|
|
|
|
}
|
|
|
|
|
2005-04-05 11:33:58 +00:00
|
|
|
rc = rb->write(fd, audiobuf, readlen);
|
2004-06-10 13:29:52 +00:00
|
|
|
if(rc < 0) {
|
|
|
|
rb->close(fd);
|
|
|
|
rb->close(orig_fd);
|
|
|
|
return 10*rc - 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now insert the data into the file */
|
|
|
|
rc = rb->write(fd, buf, num_bytes);
|
|
|
|
if(rc < 0) {
|
|
|
|
rb->close(orig_fd);
|
|
|
|
rb->close(fd);
|
|
|
|
return 10*rc - 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the file */
|
|
|
|
do {
|
2005-04-05 11:33:58 +00:00
|
|
|
readlen = rb->read(orig_fd, audiobuf, audiobuflen);
|
2004-06-10 13:29:52 +00:00
|
|
|
if(readlen < 0) {
|
|
|
|
rb->close(fd);
|
|
|
|
rb->close(orig_fd);
|
|
|
|
return 10*readlen - 7;
|
|
|
|
}
|
|
|
|
|
2005-04-05 11:33:58 +00:00
|
|
|
rc = rb->write(fd, audiobuf, readlen);
|
2004-06-10 13:29:52 +00:00
|
|
|
if(rc < 0) {
|
|
|
|
rb->close(fd);
|
|
|
|
rb->close(orig_fd);
|
|
|
|
return 10*rc - 8;
|
|
|
|
}
|
|
|
|
} while(readlen > 0);
|
|
|
|
|
|
|
|
rb->close(fd);
|
|
|
|
rb->close(orig_fd);
|
|
|
|
|
|
|
|
/* Remove the old file */
|
|
|
|
rc = rb->remove(fname);
|
|
|
|
if(rc < 0) {
|
|
|
|
return 10*rc - 9;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Replace the old file with the new */
|
|
|
|
rc = rb->rename(tmpname, fname);
|
|
|
|
if(rc < 0) {
|
|
|
|
return 10*rc - 9;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void fileerror(int rc)
|
|
|
|
{
|
2019-08-08 20:49:16 +00:00
|
|
|
rb->splashf(HZ*2, ID2P(LANG_FILE_ERROR), rc);
|
2004-06-10 13:29:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static const unsigned char empty_id3_header[] =
|
|
|
|
{
|
|
|
|
'I', 'D', '3', 0x04, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x1f, 0x76 /* Size is 4096 minus 10 bytes for the header */
|
|
|
|
};
|
|
|
|
|
2008-05-13 09:57:56 +00:00
|
|
|
static bool vbr_fix(const char *selected_file)
|
2004-06-10 13:29:52 +00:00
|
|
|
{
|
|
|
|
struct mp3entry entry;
|
|
|
|
int fd;
|
|
|
|
int rc;
|
|
|
|
int num_frames;
|
|
|
|
int numbytes;
|
|
|
|
int framelen;
|
|
|
|
int unused_space;
|
|
|
|
|
|
|
|
rb->lcd_clear_display();
|
|
|
|
rb->lcd_puts_scroll(0, 0, selected_file);
|
|
|
|
rb->lcd_update();
|
|
|
|
|
|
|
|
xingupdate(0);
|
|
|
|
|
2007-09-19 10:40:55 +00:00
|
|
|
rc = rb->mp3info(&entry, selected_file);
|
2004-06-10 13:29:52 +00:00
|
|
|
if(rc < 0) {
|
|
|
|
fileerror(rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = rb->open(selected_file, O_RDWR);
|
|
|
|
if(fd < 0) {
|
|
|
|
fileerror(fd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
xingupdate(0);
|
|
|
|
|
|
|
|
num_frames = rb->count_mp3_frames(fd, entry.first_frame_offset,
|
2019-08-08 20:49:16 +00:00
|
|
|
entry.filesize, xingupdate, audiobuf, audiobuflen);
|
2004-06-10 13:29:52 +00:00
|
|
|
|
|
|
|
if(num_frames) {
|
|
|
|
/* Note: We don't need to pass a template header because it will be
|
|
|
|
taken from the mpeg stream */
|
|
|
|
framelen = rb->create_xing_header(fd, entry.first_frame_offset,
|
2019-08-08 20:49:16 +00:00
|
|
|
entry.filesize, xingbuf, num_frames, 0,
|
2011-08-14 15:13:00 +00:00
|
|
|
0, xingupdate, true,
|
|
|
|
audiobuf, audiobuflen);
|
2004-06-10 13:29:52 +00:00
|
|
|
|
|
|
|
/* Try to fit the Xing header first in the stream. Replace the existing
|
|
|
|
VBR header if there is one, else see if there is room between the
|
|
|
|
ID3 tag and the first MP3 frame. */
|
|
|
|
if(entry.first_frame_offset - entry.id3v2len >=
|
|
|
|
(unsigned int)framelen) {
|
|
|
|
DEBUGF("Using existing space between ID3 and first frame\n");
|
|
|
|
|
|
|
|
/* Seek to the beginning of the unused space */
|
|
|
|
rc = rb->lseek(fd, entry.id3v2len, SEEK_SET);
|
|
|
|
if(rc < 0) {
|
|
|
|
rb->close(fd);
|
|
|
|
fileerror(rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
unused_space =
|
|
|
|
entry.first_frame_offset - entry.id3v2len - framelen;
|
|
|
|
|
|
|
|
/* Fill the unused space with 0's (using the MP3 buffer)
|
|
|
|
and write it to the file */
|
|
|
|
if(unused_space)
|
|
|
|
{
|
2005-04-05 11:33:58 +00:00
|
|
|
rb->memset(audiobuf, 0, unused_space);
|
|
|
|
rc = rb->write(fd, audiobuf, unused_space);
|
2004-06-10 13:29:52 +00:00
|
|
|
if(rc < 0) {
|
|
|
|
rb->close(fd);
|
|
|
|
fileerror(rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Then write the Xing header */
|
|
|
|
rc = rb->write(fd, xingbuf, framelen);
|
|
|
|
if(rc < 0) {
|
|
|
|
rb->close(fd);
|
|
|
|
fileerror(rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb->close(fd);
|
|
|
|
} else {
|
|
|
|
/* If not, insert some space. If there is an ID3 tag in the
|
|
|
|
file we only insert just enough to squeeze the Xing header
|
|
|
|
in. If not, we insert an additional empty ID3 tag of 4K. */
|
|
|
|
|
|
|
|
rb->close(fd);
|
|
|
|
|
|
|
|
/* Nasty trick alert! The insert_data_in_file() function
|
|
|
|
uses the MP3 buffer when copying the data. We assume
|
|
|
|
that the ID3 tag isn't longer than 1MB so the xing
|
|
|
|
buffer won't be overwritten. */
|
|
|
|
|
|
|
|
if(entry.first_frame_offset) {
|
|
|
|
DEBUGF("Inserting %d bytes\n", framelen);
|
|
|
|
numbytes = framelen;
|
|
|
|
} else {
|
|
|
|
DEBUGF("Inserting 4096+%d bytes\n", framelen);
|
|
|
|
numbytes = 4096 + framelen;
|
|
|
|
|
2005-04-05 11:33:58 +00:00
|
|
|
rb->memset(audiobuf + 0x100000, 0, numbytes);
|
2004-06-10 13:29:52 +00:00
|
|
|
|
|
|
|
/* Insert the ID3 header */
|
2005-04-05 11:33:58 +00:00
|
|
|
rb->memcpy(audiobuf + 0x100000, empty_id3_header,
|
2004-06-10 13:29:52 +00:00
|
|
|
sizeof(empty_id3_header));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy the Xing header */
|
2005-04-05 11:33:58 +00:00
|
|
|
rb->memcpy(audiobuf + 0x100000 + numbytes - framelen,
|
2004-06-10 13:29:52 +00:00
|
|
|
xingbuf, framelen);
|
|
|
|
|
|
|
|
rc = insert_data_in_file(selected_file,
|
|
|
|
entry.first_frame_offset,
|
2005-04-05 11:33:58 +00:00
|
|
|
audiobuf + 0x100000, numbytes);
|
2004-06-10 13:29:52 +00:00
|
|
|
|
|
|
|
if(rc < 0) {
|
|
|
|
fileerror(rc);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
xingupdate(100);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Not a VBR file */
|
|
|
|
DEBUGF("Not a VBR file\n");
|
2019-08-08 20:49:16 +00:00
|
|
|
rb->splash(HZ*2, ID2P(LANG_NOT_A_VBR_FILE));
|
2004-06-10 13:29:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-01-16 10:34:40 +00:00
|
|
|
enum plugin_status plugin_start(const void *parameter)
|
2004-06-10 13:29:52 +00:00
|
|
|
{
|
2019-08-08 20:49:16 +00:00
|
|
|
last_talk = *(rb->current_tick) / HZ;
|
2004-06-10 13:29:52 +00:00
|
|
|
|
|
|
|
if (!parameter)
|
|
|
|
return PLUGIN_ERROR;
|
|
|
|
|
2010-05-07 19:27:42 +00:00
|
|
|
audiobuf = rb->plugin_get_audio_buffer(&audiobuflen);
|
2004-06-10 13:29:52 +00:00
|
|
|
|
2005-06-30 10:45:53 +00:00
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
|
|
rb->cpu_boost(true);
|
|
|
|
#endif
|
|
|
|
|
2004-06-10 13:29:52 +00:00
|
|
|
vbr_fix(parameter);
|
|
|
|
|
2005-06-30 10:45:53 +00:00
|
|
|
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
|
|
|
|
rb->cpu_boost(false);
|
|
|
|
#endif
|
2004-06-10 13:29:52 +00:00
|
|
|
return PLUGIN_OK;
|
|
|
|
}
|