Add AI to the pong plugin, to allow single-player operation.
[kugel-rb.git] / apps / plugins / vbrfix.c
blob98ca15b6a85770fcb80bd41c35e9157743de3a5c
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);
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);
169 /* Try to fit the Xing header first in the stream. Replace the existing
170 VBR header if there is one, else see if there is room between the
171 ID3 tag and the first MP3 frame. */
172 if(entry.first_frame_offset - entry.id3v2len >=
173 (unsigned int)framelen) {
174 DEBUGF("Using existing space between ID3 and first frame\n");
176 /* Seek to the beginning of the unused space */
177 rc = rb->lseek(fd, entry.id3v2len, SEEK_SET);
178 if(rc < 0) {
179 rb->close(fd);
180 fileerror(rc);
181 return true;
184 unused_space =
185 entry.first_frame_offset - entry.id3v2len - framelen;
187 /* Fill the unused space with 0's (using the MP3 buffer)
188 and write it to the file */
189 if(unused_space)
191 rb->memset(audiobuf, 0, unused_space);
192 rc = rb->write(fd, audiobuf, unused_space);
193 if(rc < 0) {
194 rb->close(fd);
195 fileerror(rc);
196 return true;
200 /* Then write the Xing header */
201 rc = rb->write(fd, xingbuf, framelen);
202 if(rc < 0) {
203 rb->close(fd);
204 fileerror(rc);
205 return true;
208 rb->close(fd);
209 } else {
210 /* If not, insert some space. If there is an ID3 tag in the
211 file we only insert just enough to squeeze the Xing header
212 in. If not, we insert an additional empty ID3 tag of 4K. */
214 rb->close(fd);
216 /* Nasty trick alert! The insert_data_in_file() function
217 uses the MP3 buffer when copying the data. We assume
218 that the ID3 tag isn't longer than 1MB so the xing
219 buffer won't be overwritten. */
221 if(entry.first_frame_offset) {
222 DEBUGF("Inserting %d bytes\n", framelen);
223 numbytes = framelen;
224 } else {
225 DEBUGF("Inserting 4096+%d bytes\n", framelen);
226 numbytes = 4096 + framelen;
228 rb->memset(audiobuf + 0x100000, 0, numbytes);
230 /* Insert the ID3 header */
231 rb->memcpy(audiobuf + 0x100000, empty_id3_header,
232 sizeof(empty_id3_header));
235 /* Copy the Xing header */
236 rb->memcpy(audiobuf + 0x100000 + numbytes - framelen,
237 xingbuf, framelen);
239 rc = insert_data_in_file(selected_file,
240 entry.first_frame_offset,
241 audiobuf + 0x100000, numbytes);
243 if(rc < 0) {
244 fileerror(rc);
245 return true;
249 xingupdate(100);
251 else
253 /* Not a VBR file */
254 DEBUGF("Not a VBR file\n");
255 rb->splash(HZ*2, "Not a VBR file");
258 return false;
261 enum plugin_status plugin_start(const void *parameter)
264 if (!parameter)
265 return PLUGIN_ERROR;
267 audiobuf = rb->plugin_get_audio_buffer(&audiobuflen);
269 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
270 rb->cpu_boost(true);
271 #endif
273 vbr_fix(parameter);
275 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
276 rb->cpu_boost(false);
277 #endif
278 return PLUGIN_OK;