New makefile solution: A single invocation of 'make' to build the entire tree. Fully...
[kugel-rb.git] / apps / plugins / vbrfix.c
blob8313994e29455bd298d0f89a18da6af6daf87a7c
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 const struct plugin_api* rb;
27 static char *audiobuf;
28 static ssize_t audiobuflen;
29 unsigned char xingbuf[1500];
30 char tmpname[MAX_PATH];
32 static void xingupdate(int percent)
34 char buf[32];
36 rb->snprintf(buf, 32, "%d%%", percent);
37 rb->lcd_puts(0, 1, buf);
38 rb->lcd_update();
41 static int insert_data_in_file(const char *fname, int fpos, char *buf, int num_bytes)
43 int readlen;
44 int rc;
45 int orig_fd, fd;
47 rb->snprintf(tmpname, MAX_PATH, "%s.tmp", fname);
49 orig_fd = rb->open(fname, O_RDONLY);
50 if(orig_fd < 0) {
51 return 10*orig_fd - 1;
54 fd = rb->creat(tmpname);
55 if(fd < 0) {
56 rb->close(orig_fd);
57 return 10*fd - 2;
60 /* First, copy the initial portion (the ID3 tag) */
61 if(fpos) {
62 readlen = rb->read(orig_fd, audiobuf, fpos);
63 if(readlen < 0) {
64 rb->close(fd);
65 rb->close(orig_fd);
66 return 10*readlen - 3;
69 rc = rb->write(fd, audiobuf, readlen);
70 if(rc < 0) {
71 rb->close(fd);
72 rb->close(orig_fd);
73 return 10*rc - 4;
77 /* Now insert the data into the file */
78 rc = rb->write(fd, buf, num_bytes);
79 if(rc < 0) {
80 rb->close(orig_fd);
81 rb->close(fd);
82 return 10*rc - 5;
85 /* Copy the file */
86 do {
87 readlen = rb->read(orig_fd, audiobuf, audiobuflen);
88 if(readlen < 0) {
89 rb->close(fd);
90 rb->close(orig_fd);
91 return 10*readlen - 7;
94 rc = rb->write(fd, audiobuf, readlen);
95 if(rc < 0) {
96 rb->close(fd);
97 rb->close(orig_fd);
98 return 10*rc - 8;
100 } while(readlen > 0);
102 rb->close(fd);
103 rb->close(orig_fd);
105 /* Remove the old file */
106 rc = rb->remove(fname);
107 if(rc < 0) {
108 return 10*rc - 9;
111 /* Replace the old file with the new */
112 rc = rb->rename(tmpname, fname);
113 if(rc < 0) {
114 return 10*rc - 9;
117 return 0;
120 static void fileerror(int rc)
122 rb->splashf(HZ*2, "File error: %d", rc);
125 static const unsigned char empty_id3_header[] =
127 'I', 'D', '3', 0x04, 0x00, 0x00,
128 0x00, 0x00, 0x1f, 0x76 /* Size is 4096 minus 10 bytes for the header */
131 static bool vbr_fix(const char *selected_file)
133 struct mp3entry entry;
134 int fd;
135 int rc;
136 int flen;
137 int num_frames;
138 int numbytes;
139 int framelen;
140 int unused_space;
142 rb->lcd_clear_display();
143 rb->lcd_puts_scroll(0, 0, selected_file);
144 rb->lcd_update();
146 xingupdate(0);
148 rc = rb->mp3info(&entry, selected_file);
149 if(rc < 0) {
150 fileerror(rc);
151 return true;
154 fd = rb->open(selected_file, O_RDWR);
155 if(fd < 0) {
156 fileerror(fd);
157 return true;
160 flen = rb->lseek(fd, 0, SEEK_END);
162 xingupdate(0);
164 num_frames = rb->count_mp3_frames(fd, entry.first_frame_offset,
165 flen, xingupdate);
167 if(num_frames) {
168 /* Note: We don't need to pass a template header because it will be
169 taken from the mpeg stream */
170 framelen = rb->create_xing_header(fd, entry.first_frame_offset,
171 flen, xingbuf, num_frames, 0,
172 0, xingupdate, true);
174 /* Try to fit the Xing header first in the stream. Replace the existing
175 VBR header if there is one, else see if there is room between the
176 ID3 tag and the first MP3 frame. */
177 if(entry.first_frame_offset - entry.id3v2len >=
178 (unsigned int)framelen) {
179 DEBUGF("Using existing space between ID3 and first frame\n");
181 /* Seek to the beginning of the unused space */
182 rc = rb->lseek(fd, entry.id3v2len, SEEK_SET);
183 if(rc < 0) {
184 rb->close(fd);
185 fileerror(rc);
186 return true;
189 unused_space =
190 entry.first_frame_offset - entry.id3v2len - framelen;
192 /* Fill the unused space with 0's (using the MP3 buffer)
193 and write it to the file */
194 if(unused_space)
196 rb->memset(audiobuf, 0, unused_space);
197 rc = rb->write(fd, audiobuf, unused_space);
198 if(rc < 0) {
199 rb->close(fd);
200 fileerror(rc);
201 return true;
205 /* Then write the Xing header */
206 rc = rb->write(fd, xingbuf, framelen);
207 if(rc < 0) {
208 rb->close(fd);
209 fileerror(rc);
210 return true;
213 rb->close(fd);
214 } else {
215 /* If not, insert some space. If there is an ID3 tag in the
216 file we only insert just enough to squeeze the Xing header
217 in. If not, we insert an additional empty ID3 tag of 4K. */
219 rb->close(fd);
221 /* Nasty trick alert! The insert_data_in_file() function
222 uses the MP3 buffer when copying the data. We assume
223 that the ID3 tag isn't longer than 1MB so the xing
224 buffer won't be overwritten. */
226 if(entry.first_frame_offset) {
227 DEBUGF("Inserting %d bytes\n", framelen);
228 numbytes = framelen;
229 } else {
230 DEBUGF("Inserting 4096+%d bytes\n", framelen);
231 numbytes = 4096 + framelen;
233 rb->memset(audiobuf + 0x100000, 0, numbytes);
235 /* Insert the ID3 header */
236 rb->memcpy(audiobuf + 0x100000, empty_id3_header,
237 sizeof(empty_id3_header));
240 /* Copy the Xing header */
241 rb->memcpy(audiobuf + 0x100000 + numbytes - framelen,
242 xingbuf, framelen);
244 rc = insert_data_in_file(selected_file,
245 entry.first_frame_offset,
246 audiobuf + 0x100000, numbytes);
248 if(rc < 0) {
249 fileerror(rc);
250 return true;
254 xingupdate(100);
256 else
258 /* Not a VBR file */
259 DEBUGF("Not a VBR file\n");
260 rb->splash(HZ*2, "Not a VBR file");
263 return false;
266 enum plugin_status plugin_start(const struct plugin_api* api, const void *parameter)
268 rb = api;
270 if (!parameter)
271 return PLUGIN_ERROR;
273 audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuflen);
275 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
276 rb->cpu_boost(true);
277 #endif
279 vbr_fix(parameter);
281 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
282 rb->cpu_boost(false);
283 #endif
284 return PLUGIN_OK;