blob: fa05463d13699933fc02d51c7479b1d3380a2bfc [file] [log] [blame]
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +00001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2004 Linus Nielsen Feltzing
11 *
Daniel Stenberg2acc0ac2008-06-28 18:10:04 +000012 * 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.
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000016 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#include "plugin.h"
22
Jens Arnolda36b1d42006-01-15 18:20:18 +000023PLUGIN_HEADER
24
Steve Bavin65265772008-05-13 09:57:56 +000025static const struct plugin_api* rb;
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000026
Michael Sevakis26d242a2007-04-21 18:38:25 +000027static char *audiobuf;
28static ssize_t audiobuflen;
Linus Nielsen Feltzingd86a9ca2008-01-09 12:39:06 +000029unsigned char xingbuf[1500];
30char tmpname[MAX_PATH];
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000031
32static void xingupdate(int percent)
33{
34 char buf[32];
35
36 rb->snprintf(buf, 32, "%d%%", percent);
37 rb->lcd_puts(0, 1, buf);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000038 rb->lcd_update();
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000039}
40
Steve Bavin65265772008-05-13 09:57:56 +000041static int insert_data_in_file(const char *fname, int fpos, char *buf, int num_bytes)
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000042{
43 int readlen;
44 int rc;
45 int orig_fd, fd;
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000046
47 rb->snprintf(tmpname, MAX_PATH, "%s.tmp", fname);
48
49 orig_fd = rb->open(fname, O_RDONLY);
50 if(orig_fd < 0) {
51 return 10*orig_fd - 1;
52 }
53
Jens Arnold67eb1542007-02-01 23:08:15 +000054 fd = rb->creat(tmpname);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000055 if(fd < 0) {
56 rb->close(orig_fd);
57 return 10*fd - 2;
58 }
59
60 /* First, copy the initial portion (the ID3 tag) */
61 if(fpos) {
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +000062 readlen = rb->read(orig_fd, audiobuf, fpos);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000063 if(readlen < 0) {
64 rb->close(fd);
65 rb->close(orig_fd);
66 return 10*readlen - 3;
67 }
68
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +000069 rc = rb->write(fd, audiobuf, readlen);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000070 if(rc < 0) {
71 rb->close(fd);
72 rb->close(orig_fd);
73 return 10*rc - 4;
74 }
75 }
76
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;
83 }
84
85 /* Copy the file */
86 do {
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +000087 readlen = rb->read(orig_fd, audiobuf, audiobuflen);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000088 if(readlen < 0) {
89 rb->close(fd);
90 rb->close(orig_fd);
91 return 10*readlen - 7;
92 }
93
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +000094 rc = rb->write(fd, audiobuf, readlen);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +000095 if(rc < 0) {
96 rb->close(fd);
97 rb->close(orig_fd);
98 return 10*rc - 8;
99 }
100 } while(readlen > 0);
101
102 rb->close(fd);
103 rb->close(orig_fd);
104
105 /* Remove the old file */
106 rc = rb->remove(fname);
107 if(rc < 0) {
108 return 10*rc - 9;
109 }
110
111 /* Replace the old file with the new */
112 rc = rb->rename(tmpname, fname);
113 if(rc < 0) {
114 return 10*rc - 9;
115 }
116
117 return 0;
118}
119
120static void fileerror(int rc)
121{
Jens Arnold4d6374c2007-03-16 21:56:08 +0000122 rb->splash(HZ*2, "File error: %d", rc);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000123}
124
125static const unsigned char empty_id3_header[] =
126{
127 'I', 'D', '3', 0x04, 0x00, 0x00,
128 0x00, 0x00, 0x1f, 0x76 /* Size is 4096 minus 10 bytes for the header */
129};
130
Steve Bavin65265772008-05-13 09:57:56 +0000131static bool vbr_fix(const char *selected_file)
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000132{
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000133 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;
141
142 rb->lcd_clear_display();
143 rb->lcd_puts_scroll(0, 0, selected_file);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000144 rb->lcd_update();
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000145
146 xingupdate(0);
147
Thom Johansen294ec1d2007-09-19 10:40:55 +0000148 rc = rb->mp3info(&entry, selected_file);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000149 if(rc < 0) {
150 fileerror(rc);
151 return true;
152 }
153
154 fd = rb->open(selected_file, O_RDWR);
155 if(fd < 0) {
156 fileerror(fd);
157 return true;
158 }
159
160 flen = rb->lseek(fd, 0, SEEK_END);
161
162 xingupdate(0);
163
164 num_frames = rb->count_mp3_frames(fd, entry.first_frame_offset,
165 flen, xingupdate);
166
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,
Jens Arnoldba966c12005-09-15 05:29:26 +0000171 flen, xingbuf, num_frames, 0,
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000172 0, xingupdate, true);
173
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");
180
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;
187 }
188
189 unused_space =
190 entry.first_frame_offset - entry.id3v2len - framelen;
191
192 /* Fill the unused space with 0's (using the MP3 buffer)
193 and write it to the file */
194 if(unused_space)
195 {
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +0000196 rb->memset(audiobuf, 0, unused_space);
197 rc = rb->write(fd, audiobuf, unused_space);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000198 if(rc < 0) {
199 rb->close(fd);
200 fileerror(rc);
201 return true;
202 }
203 }
204
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;
211 }
212
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. */
218
219 rb->close(fd);
220
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. */
225
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;
232
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +0000233 rb->memset(audiobuf + 0x100000, 0, numbytes);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000234
235 /* Insert the ID3 header */
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +0000236 rb->memcpy(audiobuf + 0x100000, empty_id3_header,
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000237 sizeof(empty_id3_header));
238 }
239
240 /* Copy the Xing header */
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +0000241 rb->memcpy(audiobuf + 0x100000 + numbytes - framelen,
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000242 xingbuf, framelen);
243
244 rc = insert_data_in_file(selected_file,
245 entry.first_frame_offset,
Linus Nielsen Feltzingd34865a2005-04-05 11:33:58 +0000246 audiobuf + 0x100000, numbytes);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000247
248 if(rc < 0) {
249 fileerror(rc);
250 return true;
251 }
252 }
253
254 xingupdate(100);
255 }
256 else
257 {
258 /* Not a VBR file */
259 DEBUGF("Not a VBR file\n");
Jens Arnold4d6374c2007-03-16 21:56:08 +0000260 rb->splash(HZ*2, "Not a VBR file");
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000261 }
262
263 return false;
264}
265
Steve Bavin65265772008-05-13 09:57:56 +0000266enum plugin_status plugin_start(const struct plugin_api* api, const void *parameter)
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000267{
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000268 rb = api;
269
270 if (!parameter)
271 return PLUGIN_ERROR;
272
Michael Sevakis8676dc22007-04-21 19:07:15 +0000273 audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuflen);
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000274
Linus Nielsen Feltzing0ec1ad92005-06-30 10:45:53 +0000275#ifdef HAVE_ADJUSTABLE_CPU_FREQ
276 rb->cpu_boost(true);
277#endif
278
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000279 vbr_fix(parameter);
280
Linus Nielsen Feltzing0ec1ad92005-06-30 10:45:53 +0000281#ifdef HAVE_ADJUSTABLE_CPU_FREQ
282 rb->cpu_boost(false);
283#endif
Linus Nielsen Feltzinga6142ab2004-06-10 13:29:52 +0000284 return PLUGIN_OK;
285}