A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 296 lines 8.3 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2004 Linus Nielsen Feltzing 11 * 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. 16 * 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 23 24 25static char *audiobuf; 26static size_t audiobuflen; 27unsigned char xingbuf[1500]; 28char tmpname[MAX_PATH]; 29static long last_talk = 0; 30 31static void xingupdate(int percent) 32{ 33 rb->lcd_putsf(0, 1, "%d%%", percent); 34 rb->lcd_update(); 35 if (rb->global_settings->talk_menu) 36 { 37 long now = *(rb->current_tick) / HZ; 38 if (now - last_talk >= 5) 39 { 40 rb->talk_value_decimal(percent, UNIT_PERCENT, 0, false); 41 last_talk = now; 42 } 43 } 44} 45 46static int insert_data_in_file(const char *fname, int fpos, char *buf, int num_bytes) 47{ 48 int readlen; 49 int rc; 50 int orig_fd, fd; 51 52 rb->snprintf(tmpname, MAX_PATH, "%s.tmp", fname); 53 54 orig_fd = rb->open(fname, O_RDONLY); 55 if(orig_fd < 0) { 56 return 10*orig_fd - 1; 57 } 58 59 fd = rb->creat(tmpname, 0666); 60 if(fd < 0) { 61 rb->close(orig_fd); 62 return 10*fd - 2; 63 } 64 65 /* First, copy the initial portion (the ID3 tag) */ 66 if(fpos) { 67 readlen = rb->read(orig_fd, audiobuf, fpos); 68 if(readlen < 0) { 69 rb->close(fd); 70 rb->close(orig_fd); 71 return 10*readlen - 3; 72 } 73 74 rc = rb->write(fd, audiobuf, readlen); 75 if(rc < 0) { 76 rb->close(fd); 77 rb->close(orig_fd); 78 return 10*rc - 4; 79 } 80 } 81 82 /* Now insert the data into the file */ 83 rc = rb->write(fd, buf, num_bytes); 84 if(rc < 0) { 85 rb->close(orig_fd); 86 rb->close(fd); 87 return 10*rc - 5; 88 } 89 90 /* Copy the file */ 91 do { 92 readlen = rb->read(orig_fd, audiobuf, audiobuflen); 93 if(readlen < 0) { 94 rb->close(fd); 95 rb->close(orig_fd); 96 return 10*readlen - 7; 97 } 98 99 rc = rb->write(fd, audiobuf, readlen); 100 if(rc < 0) { 101 rb->close(fd); 102 rb->close(orig_fd); 103 return 10*rc - 8; 104 } 105 } while(readlen > 0); 106 107 rb->close(fd); 108 rb->close(orig_fd); 109 110 /* Remove the old file */ 111 rc = rb->remove(fname); 112 if(rc < 0) { 113 return 10*rc - 9; 114 } 115 116 /* Replace the old file with the new */ 117 rc = rb->rename(tmpname, fname); 118 if(rc < 0) { 119 return 10*rc - 9; 120 } 121 122 return 0; 123} 124 125static void fileerror(int rc) 126{ 127 if (rb->global_settings->talk_menu) { 128 rb->talk_id(LANG_FILE_ERROR, true); 129 rb->talk_value_decimal(rc, UNIT_INT, 0, true); 130 rb->talk_force_enqueue_next(); 131 } 132 133 rb->splashf(HZ*2, rb->str(LANG_FILE_ERROR), rc); /* (voiced above) */ 134} 135 136static const unsigned char empty_id3_header[] = 137{ 138 'I', 'D', '3', 0x04, 0x00, 0x00, 139 0x00, 0x00, 0x1f, 0x76 /* Size is 4096 minus 10 bytes for the header */ 140}; 141 142static bool vbr_fix(const char *selected_file) 143{ 144 struct mp3entry entry; 145 int fd; 146 int rc; 147 int num_frames; 148 int numbytes; 149 int framelen; 150 int unused_space; 151 152 rb->lcd_clear_display(); 153 rb->lcd_puts_scroll(0, 0, selected_file); 154 rb->lcd_update(); 155 156 xingupdate(0); 157 158 rc = rb->get_metadata(&entry, -1, selected_file); 159 if(!rc) { 160 fileerror(rc); 161 return true; 162 } 163 164 fd = rb->open(selected_file, O_RDWR); 165 if(fd < 0) { 166 fileerror(fd); 167 return true; 168 } 169 170 xingupdate(0); 171 172 num_frames = rb->count_mp3_frames(fd, entry.first_frame_offset, 173 entry.filesize, xingupdate, audiobuf, audiobuflen); 174 175 if(num_frames) { 176 /* Note: We don't need to pass a template header because it will be 177 taken from the mpeg stream */ 178 framelen = rb->create_xing_header(fd, entry.first_frame_offset, 179 entry.filesize, xingbuf, num_frames, 0, 180 0, xingupdate, true, 181 audiobuf, audiobuflen); 182 183 /* Try to fit the Xing header first in the stream. Replace the existing 184 VBR header if there is one, else see if there is room between the 185 ID3 tag and the first MP3 frame. */ 186 if(entry.first_frame_offset - entry.id3v2len >= 187 (unsigned int)framelen) { 188 DEBUGF("Using existing space between ID3 and first frame\n"); 189 190 /* Seek to the beginning of the unused space */ 191 rc = rb->lseek(fd, entry.id3v2len, SEEK_SET); 192 if(rc < 0) { 193 rb->close(fd); 194 fileerror(rc); 195 return true; 196 } 197 198 unused_space = 199 entry.first_frame_offset - entry.id3v2len - framelen; 200 201 /* Fill the unused space with 0's (using the MP3 buffer) 202 and write it to the file */ 203 if(unused_space) 204 { 205 rb->memset(audiobuf, 0, unused_space); 206 rc = rb->write(fd, audiobuf, unused_space); 207 if(rc < 0) { 208 rb->close(fd); 209 fileerror(rc); 210 return true; 211 } 212 } 213 214 /* Then write the Xing header */ 215 rc = rb->write(fd, xingbuf, framelen); 216 if(rc < 0) { 217 rb->close(fd); 218 fileerror(rc); 219 return true; 220 } 221 222 rb->close(fd); 223 } else { 224 /* If not, insert some space. If there is an ID3 tag in the 225 file we only insert just enough to squeeze the Xing header 226 in. If not, we insert an additional empty ID3 tag of 4K. */ 227 228 rb->close(fd); 229 230 /* Nasty trick alert! The insert_data_in_file() function 231 uses the MP3 buffer when copying the data. We assume 232 that the ID3 tag isn't longer than 1MB so the xing 233 buffer won't be overwritten. */ 234 235 if(entry.first_frame_offset) { 236 DEBUGF("Inserting %d bytes\n", framelen); 237 numbytes = framelen; 238 } else { 239 DEBUGF("Inserting 4096+%d bytes\n", framelen); 240 numbytes = 4096 + framelen; 241 242 rb->memset(audiobuf + 0x100000, 0, numbytes); 243 244 /* Insert the ID3 header */ 245 rb->memcpy(audiobuf + 0x100000, empty_id3_header, 246 sizeof(empty_id3_header)); 247 } 248 249 /* Copy the Xing header */ 250 rb->memcpy(audiobuf + 0x100000 + numbytes - framelen, 251 xingbuf, framelen); 252 253 rc = insert_data_in_file(selected_file, 254 entry.first_frame_offset, 255 audiobuf + 0x100000, numbytes); 256 257 if(rc < 0) { 258 fileerror(rc); 259 return true; 260 } 261 } 262 263 xingupdate(100); 264 } 265 else 266 { 267 rb->close(fd); 268 269 /* Not a VBR file */ 270 DEBUGF("Not a VBR file\n"); 271 rb->splash(HZ*2, ID2P(LANG_NOT_A_VBR_FILE)); 272 } 273 274 return false; 275} 276 277enum plugin_status plugin_start(const void *parameter) 278{ 279 last_talk = *(rb->current_tick) / HZ; 280 281 if (!parameter) 282 return PLUGIN_ERROR; 283 284 audiobuf = rb->plugin_get_audio_buffer(&audiobuflen); 285 286#ifdef HAVE_ADJUSTABLE_CPU_FREQ 287 rb->cpu_boost(true); 288#endif 289 290 vbr_fix(parameter); 291 292#ifdef HAVE_ADJUSTABLE_CPU_FREQ 293 rb->cpu_boost(false); 294#endif 295 return PLUGIN_OK; 296}