MPEGPlayer: Add a second layer of caching to help speed up byte-wise scanning and...
[kugel-rb.git] / apps / plugins / vbrfix.c
blobba13dc53f9706b2b1ab35cfef2613cbcafaad1d6
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"
23 PLUGIN_HEADER
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 char buf[32];
34 rb->snprintf(buf, 32, "%d%%", percent);
35 rb->lcd_puts(0, 1, buf);
36 rb->lcd_update();
39 static int insert_data_in_file(const char *fname, int fpos, char *buf, int num_bytes)
41 int readlen;
42 int rc;
43 int orig_fd, fd;
45 rb->snprintf(tmpname, MAX_PATH, "%s.tmp", fname);
47 orig_fd = rb->open(fname, O_RDONLY);
48 if(orig_fd < 0) {
49 return 10*orig_fd - 1;
52 fd = rb->creat(tmpname, 0666);
53 if(fd < 0) {
54 rb->close(orig_fd);
55 return 10*fd - 2;
58 /* First, copy the initial portion (the ID3 tag) */
59 if(fpos) {
60 readlen = rb->read(orig_fd, audiobuf, fpos);
61 if(readlen < 0) {
62 rb->close(fd);
63 rb->close(orig_fd);
64 return 10*readlen - 3;
67 rc = rb->write(fd, audiobuf, readlen);
68 if(rc < 0) {
69 rb->close(fd);
70 rb->close(orig_fd);
71 return 10*rc - 4;
75 /* Now insert the data into the file */
76 rc = rb->write(fd, buf, num_bytes);
77 if(rc < 0) {
78 rb->close(orig_fd);
79 rb->close(fd);
80 return 10*rc - 5;
83 /* Copy the file */
84 do {
85 readlen = rb->read(orig_fd, audiobuf, audiobuflen);
86 if(readlen < 0) {
87 rb->close(fd);
88 rb->close(orig_fd);
89 return 10*readlen - 7;
92 rc = rb->write(fd, audiobuf, readlen);
93 if(rc < 0) {
94 rb->close(fd);
95 rb->close(orig_fd);
96 return 10*rc - 8;
98 } while(readlen > 0);
100 rb->close(fd);
101 rb->close(orig_fd);
103 /* Remove the old file */
104 rc = rb->remove(fname);
105 if(rc < 0) {
106 return 10*rc - 9;
109 /* Replace the old file with the new */
110 rc = rb->rename(tmpname, fname);
111 if(rc < 0) {
112 return 10*rc - 9;
115 return 0;
118 static void fileerror(int rc)
120 rb->splashf(HZ*2, "File error: %d", rc);
123 static const unsigned char empty_id3_header[] =
125 'I', 'D', '3', 0x04, 0x00, 0x00,
126 0x00, 0x00, 0x1f, 0x76 /* Size is 4096 minus 10 bytes for the header */
129 static bool vbr_fix(const char *selected_file)
131 struct mp3entry entry;
132 int fd;
133 int rc;
134 int flen;
135 int num_frames;
136 int numbytes;
137 int framelen;
138 int unused_space;
140 rb->lcd_clear_display();
141 rb->lcd_puts_scroll(0, 0, selected_file);
142 rb->lcd_update();
144 xingupdate(0);
146 rc = rb->mp3info(&entry, selected_file);
147 if(rc < 0) {
148 fileerror(rc);
149 return true;
152 fd = rb->open(selected_file, O_RDWR);
153 if(fd < 0) {
154 fileerror(fd);
155 return true;
158 flen = rb->lseek(fd, 0, SEEK_END);
160 xingupdate(0);
162 num_frames = rb->count_mp3_frames(fd, entry.first_frame_offset,
163 flen, xingupdate);
165 if(num_frames) {
166 /* Note: We don't need to pass a template header because it will be
167 taken from the mpeg stream */
168 framelen = rb->create_xing_header(fd, entry.first_frame_offset,
169 flen, xingbuf, num_frames, 0,
170 0, xingupdate, true);
172 /* Try to fit the Xing header first in the stream. Replace the existing
173 VBR header if there is one, else see if there is room between the
174 ID3 tag and the first MP3 frame. */
175 if(entry.first_frame_offset - entry.id3v2len >=
176 (unsigned int)framelen) {
177 DEBUGF("Using existing space between ID3 and first frame\n");
179 /* Seek to the beginning of the unused space */
180 rc = rb->lseek(fd, entry.id3v2len, SEEK_SET);
181 if(rc < 0) {
182 rb->close(fd);
183 fileerror(rc);
184 return true;
187 unused_space =
188 entry.first_frame_offset - entry.id3v2len - framelen;
190 /* Fill the unused space with 0's (using the MP3 buffer)
191 and write it to the file */
192 if(unused_space)
194 rb->memset(audiobuf, 0, unused_space);
195 rc = rb->write(fd, audiobuf, unused_space);
196 if(rc < 0) {
197 rb->close(fd);
198 fileerror(rc);
199 return true;
203 /* Then write the Xing header */
204 rc = rb->write(fd, xingbuf, framelen);
205 if(rc < 0) {
206 rb->close(fd);
207 fileerror(rc);
208 return true;
211 rb->close(fd);
212 } else {
213 /* If not, insert some space. If there is an ID3 tag in the
214 file we only insert just enough to squeeze the Xing header
215 in. If not, we insert an additional empty ID3 tag of 4K. */
217 rb->close(fd);
219 /* Nasty trick alert! The insert_data_in_file() function
220 uses the MP3 buffer when copying the data. We assume
221 that the ID3 tag isn't longer than 1MB so the xing
222 buffer won't be overwritten. */
224 if(entry.first_frame_offset) {
225 DEBUGF("Inserting %d bytes\n", framelen);
226 numbytes = framelen;
227 } else {
228 DEBUGF("Inserting 4096+%d bytes\n", framelen);
229 numbytes = 4096 + framelen;
231 rb->memset(audiobuf + 0x100000, 0, numbytes);
233 /* Insert the ID3 header */
234 rb->memcpy(audiobuf + 0x100000, empty_id3_header,
235 sizeof(empty_id3_header));
238 /* Copy the Xing header */
239 rb->memcpy(audiobuf + 0x100000 + numbytes - framelen,
240 xingbuf, framelen);
242 rc = insert_data_in_file(selected_file,
243 entry.first_frame_offset,
244 audiobuf + 0x100000, numbytes);
246 if(rc < 0) {
247 fileerror(rc);
248 return true;
252 xingupdate(100);
254 else
256 /* Not a VBR file */
257 DEBUGF("Not a VBR file\n");
258 rb->splash(HZ*2, "Not a VBR file");
261 return false;
264 enum plugin_status plugin_start(const void *parameter)
267 if (!parameter)
268 return PLUGIN_ERROR;
270 audiobuf = rb->plugin_get_audio_buffer(&audiobuflen);
272 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
273 rb->cpu_boost(true);
274 #endif
276 vbr_fix(parameter);
278 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
279 rb->cpu_boost(false);
280 #endif
281 return PLUGIN_OK;