Prepare new maemo release
[maemo-rb.git] / apps / plugins / vbrfix.c
blobaf7b81700292e46be69e7e492e24131ab5d19426
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2004 Linus Nielsen Feltzing
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"
25 static char *audiobuf;
26 static size_t audiobuflen;
27 unsigned char xingbuf[1500];
28 char tmpname[MAX_PATH];
30 static void xingupdate(int percent)
32 rb->lcd_putsf(0, 1, "%d%%", percent);
33 rb->lcd_update();
36 static int insert_data_in_file(const char *fname, int fpos, char *buf, int num_bytes)
38 int readlen;
39 int rc;
40 int orig_fd, fd;
42 rb->snprintf(tmpname, MAX_PATH, "%s.tmp", fname);
44 orig_fd = rb->open(fname, O_RDONLY);
45 if(orig_fd < 0) {
46 return 10*orig_fd - 1;
49 fd = rb->creat(tmpname, 0666);
50 if(fd < 0) {
51 rb->close(orig_fd);
52 return 10*fd - 2;
55 /* First, copy the initial portion (the ID3 tag) */
56 if(fpos) {
57 readlen = rb->read(orig_fd, audiobuf, fpos);
58 if(readlen < 0) {
59 rb->close(fd);
60 rb->close(orig_fd);
61 return 10*readlen - 3;
64 rc = rb->write(fd, audiobuf, readlen);
65 if(rc < 0) {
66 rb->close(fd);
67 rb->close(orig_fd);
68 return 10*rc - 4;
72 /* Now insert the data into the file */
73 rc = rb->write(fd, buf, num_bytes);
74 if(rc < 0) {
75 rb->close(orig_fd);
76 rb->close(fd);
77 return 10*rc - 5;
80 /* Copy the file */
81 do {
82 readlen = rb->read(orig_fd, audiobuf, audiobuflen);
83 if(readlen < 0) {
84 rb->close(fd);
85 rb->close(orig_fd);
86 return 10*readlen - 7;
89 rc = rb->write(fd, audiobuf, readlen);
90 if(rc < 0) {
91 rb->close(fd);
92 rb->close(orig_fd);
93 return 10*rc - 8;
95 } while(readlen > 0);
97 rb->close(fd);
98 rb->close(orig_fd);
100 /* Remove the old file */
101 rc = rb->remove(fname);
102 if(rc < 0) {
103 return 10*rc - 9;
106 /* Replace the old file with the new */
107 rc = rb->rename(tmpname, fname);
108 if(rc < 0) {
109 return 10*rc - 9;
112 return 0;
115 static void fileerror(int rc)
117 rb->splashf(HZ*2, "File error: %d", rc);
120 static const unsigned char empty_id3_header[] =
122 'I', 'D', '3', 0x04, 0x00, 0x00,
123 0x00, 0x00, 0x1f, 0x76 /* Size is 4096 minus 10 bytes for the header */
126 static bool vbr_fix(const char *selected_file)
128 struct mp3entry entry;
129 int fd;
130 int rc;
131 int flen;
132 int num_frames;
133 int numbytes;
134 int framelen;
135 int unused_space;
137 rb->lcd_clear_display();
138 rb->lcd_puts_scroll(0, 0, selected_file);
139 rb->lcd_update();
141 xingupdate(0);
143 rc = rb->mp3info(&entry, selected_file);
144 if(rc < 0) {
145 fileerror(rc);
146 return true;
149 fd = rb->open(selected_file, O_RDWR);
150 if(fd < 0) {
151 fileerror(fd);
152 return true;
155 flen = rb->lseek(fd, 0, SEEK_END);
157 xingupdate(0);
159 num_frames = rb->count_mp3_frames(fd, entry.first_frame_offset,
160 flen, xingupdate, audiobuf, audiobuflen);
162 if(num_frames) {
163 /* Note: We don't need to pass a template header because it will be
164 taken from the mpeg stream */
165 framelen = rb->create_xing_header(fd, entry.first_frame_offset,
166 flen, xingbuf, num_frames, 0,
167 0, xingupdate, true,
168 audiobuf, audiobuflen);
170 /* Try to fit the Xing header first in the stream. Replace the existing
171 VBR header if there is one, else see if there is room between the
172 ID3 tag and the first MP3 frame. */
173 if(entry.first_frame_offset - entry.id3v2len >=
174 (unsigned int)framelen) {
175 DEBUGF("Using existing space between ID3 and first frame\n");
177 /* Seek to the beginning of the unused space */
178 rc = rb->lseek(fd, entry.id3v2len, SEEK_SET);
179 if(rc < 0) {
180 rb->close(fd);
181 fileerror(rc);
182 return true;
185 unused_space =
186 entry.first_frame_offset - entry.id3v2len - framelen;
188 /* Fill the unused space with 0's (using the MP3 buffer)
189 and write it to the file */
190 if(unused_space)
192 rb->memset(audiobuf, 0, unused_space);
193 rc = rb->write(fd, audiobuf, unused_space);
194 if(rc < 0) {
195 rb->close(fd);
196 fileerror(rc);
197 return true;
201 /* Then write the Xing header */
202 rc = rb->write(fd, xingbuf, framelen);
203 if(rc < 0) {
204 rb->close(fd);
205 fileerror(rc);
206 return true;
209 rb->close(fd);
210 } else {
211 /* If not, insert some space. If there is an ID3 tag in the
212 file we only insert just enough to squeeze the Xing header
213 in. If not, we insert an additional empty ID3 tag of 4K. */
215 rb->close(fd);
217 /* Nasty trick alert! The insert_data_in_file() function
218 uses the MP3 buffer when copying the data. We assume
219 that the ID3 tag isn't longer than 1MB so the xing
220 buffer won't be overwritten. */
222 if(entry.first_frame_offset) {
223 DEBUGF("Inserting %d bytes\n", framelen);
224 numbytes = framelen;
225 } else {
226 DEBUGF("Inserting 4096+%d bytes\n", framelen);
227 numbytes = 4096 + framelen;
229 rb->memset(audiobuf + 0x100000, 0, numbytes);
231 /* Insert the ID3 header */
232 rb->memcpy(audiobuf + 0x100000, empty_id3_header,
233 sizeof(empty_id3_header));
236 /* Copy the Xing header */
237 rb->memcpy(audiobuf + 0x100000 + numbytes - framelen,
238 xingbuf, framelen);
240 rc = insert_data_in_file(selected_file,
241 entry.first_frame_offset,
242 audiobuf + 0x100000, numbytes);
244 if(rc < 0) {
245 fileerror(rc);
246 return true;
250 xingupdate(100);
252 else
254 /* Not a VBR file */
255 DEBUGF("Not a VBR file\n");
256 rb->splash(HZ*2, "Not a VBR file");
259 return false;
262 enum plugin_status plugin_start(const void *parameter)
265 if (!parameter)
266 return PLUGIN_ERROR;
268 audiobuf = rb->plugin_get_audio_buffer(&audiobuflen);
270 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
271 rb->cpu_boost(true);
272 #endif
274 vbr_fix(parameter);
276 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
277 rb->cpu_boost(false);
278 #endif
279 return PLUGIN_OK;