A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 David Bryant
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#ifdef RB_PROFILE
23#include "lib/profile_plugin.h"
24#endif
25
26#include <codecs/libwavpack/wavpack.h>
27
28
29
30#define SAMPLES_PER_BLOCK 22050
31
32static char *audiobuf;
33static ssize_t audiobuflen;
34
35static struct wav_header {
36 char ckID [4]; /* RIFF chuck header */
37 int32_t ckSize;
38 char formType [4];
39
40 char fmt_ckID [4]; /* format chunk header */
41 int32_t fmt_ckSize;
42 ushort FormatTag, NumChannels;
43 uint32_t SampleRate, BytesPerSecond;
44 ushort BlockAlign, BitsPerSample;
45
46 char data_ckID [4]; /* data chunk header */
47 int32_t data_ckSize;
48} raw_header, native_header;
49
50#define WAV_HEADER_FORMAT "4L44LSSLLSS4L"
51
52static void wvupdate (int32_t start_tick,
53 int32_t sample_rate,
54 uint32_t total_samples,
55 uint32_t samples_converted,
56 uint32_t bytes_read,
57 uint32_t bytes_written)
58{
59 int32_t elapsed_ticks = *rb->current_tick - start_tick;
60 int compression = 0, progress = 0, realtime = 0;
61
62 if (total_samples)
63 progress = (int)(((int64_t) samples_converted * 100 +
64 (total_samples/2)) / total_samples);
65
66 if (elapsed_ticks)
67 realtime = (int)(((int64_t) samples_converted * 100 * HZ /
68 sample_rate + (elapsed_ticks/2)) / elapsed_ticks);
69
70 if (bytes_read)
71 compression = (int)(((int64_t)(bytes_read - bytes_written) * 100 +
72 (bytes_read/2)) / bytes_read);
73
74 rb->lcd_putsf(0, 2, "elapsed time: %ld secs",
75 (long)(elapsed_ticks + (HZ/2)) / HZ);
76 rb->lcd_putsf(0, 4, "progress: %d%%", progress);
77 rb->lcd_putsf(0, 6, "realtime: %d%% ", realtime);
78 rb->lcd_putsf(0, 8, "compression: %d%% ", compression);
79
80 rb->lcd_update();
81}
82
83#define TEMP_SAMPLES 4096
84
85static int32_t temp_buffer [TEMP_SAMPLES] IDATA_ATTR;
86
87static int wav2wv(const char *infile)
88{
89 int in_fd, out_fd, num_chans, error = false, last_buttons;
90 uint32_t total_bytes_read = 0, total_bytes_written = 0;
91 uint32_t total_samples, samples_remaining;
92 int32_t *input_buffer = (int32_t *) audiobuf;
93 unsigned char *output_buffer = (unsigned char *)(audiobuf + 0x100000);
94 char outfile[MAX_PATH];
95 const char *inextension;
96 char *outextension;
97 WavpackConfig config;
98 WavpackContext *wpc;
99 int32_t start_tick;
100
101 rb->lcd_clear_display();
102 rb->lcd_puts_scroll(0, 0, (unsigned char *)infile);
103 rb->lcd_update();
104
105 last_buttons = rb->button_status ();
106 start_tick = *rb->current_tick;
107 inextension = infile + rb->strlen(infile) - 3;
108 if (rb->strcasecmp (inextension, "wav")) {
109 rb->splash(HZ*2, "only for wav files!");
110 return 1;
111 }
112
113 in_fd = rb->open(infile, O_RDONLY);
114
115 if (in_fd < 0) {
116 rb->splash(HZ*2, "could not open file!");
117 return true;
118 }
119
120 if (rb->read (in_fd, &raw_header, sizeof (raw_header)) != sizeof (raw_header)) {
121 rb->splash(HZ*2, "could not read file!");
122 rb->close (in_fd);
123 return true;
124 }
125
126 total_bytes_read += sizeof (raw_header);
127 rb->memcpy (&native_header, &raw_header, sizeof (raw_header));
128 little_endian_to_native (&native_header, WAV_HEADER_FORMAT);
129
130 if (rb->strncmp (native_header.ckID, "RIFF", 4) ||
131 rb->strncmp (native_header.fmt_ckID, "fmt ", 4) ||
132 rb->strncmp (native_header.data_ckID, "data", 4) ||
133 native_header.FormatTag != 1 || native_header.BitsPerSample != 16) {
134 rb->splash(HZ*2, "incompatible wav file!");
135 rb->close (in_fd);
136 return true;
137 }
138
139 wpc = WavpackOpenFileOutput ();
140
141 rb->memset (&config, 0, sizeof (config));
142 config.bits_per_sample = 16;
143 config.bytes_per_sample = 2;
144 config.sample_rate = native_header.SampleRate;
145 num_chans = config.num_channels = native_header.NumChannels;
146 total_samples = native_header.data_ckSize / native_header.BlockAlign;
147
148/* config.flags |= CONFIG_HIGH_FLAG; */
149
150 if (!WavpackSetConfiguration (wpc, &config, total_samples)) {
151 rb->splash(HZ*2, "internal error!");
152 rb->close (in_fd);
153 return true;
154 }
155
156 WavpackAddWrapper (wpc, &raw_header, sizeof (raw_header));
157
158 rb->strcpy(outfile, infile);
159 outextension = outfile + rb->strlen(outfile) - 3;
160 outextension[1] = outextension[2];
161 outextension[2] = 0;
162 out_fd = rb->creat(outfile, 0666);
163
164 if (out_fd < 0) {
165 rb->splash(HZ*2, "could not create file!");
166 rb->close (in_fd);
167 return true;
168 }
169
170 wvupdate (start_tick, native_header.SampleRate, total_samples, 0, 0, 0);
171
172 for (samples_remaining = total_samples; samples_remaining;) {
173 uint32_t samples_count, samples_to_pack, bytes_count;
174 int cnt, buttons;
175 int32_t value, *lp;
176 signed char *cp;
177
178 samples_count = SAMPLES_PER_BLOCK;
179
180 if (samples_count > samples_remaining)
181 samples_count = samples_remaining;
182
183 bytes_count = samples_count * num_chans * 2;
184
185 if (rb->read (in_fd, input_buffer, bytes_count) != (int32_t) bytes_count) {
186 rb->splash(HZ*2, "could not read file!");
187 error = true;
188 break;
189 }
190
191 total_bytes_read += bytes_count;
192 WavpackStartBlock (wpc, output_buffer, output_buffer + 0x100000);
193 samples_to_pack = samples_count;
194 cp = (signed char *) input_buffer;
195
196 while (samples_to_pack) {
197 uint32_t samples_this_pass = TEMP_SAMPLES / num_chans;
198
199 if (samples_this_pass > samples_to_pack)
200 samples_this_pass = samples_to_pack;
201
202 lp = temp_buffer;
203 cnt = samples_this_pass;
204
205 if (num_chans == 2)
206 while (cnt--) {
207 value = *cp++ & 0xff;
208 value += *cp++ << 8;
209 *lp++ = value;
210 value = *cp++ & 0xff;
211 value += *cp++ << 8;
212 *lp++ = value;
213 }
214 else
215 while (cnt--) {
216 value = *cp++ & 0xff;
217 value += *cp++ << 8;
218 *lp++ = value;
219 }
220
221 if (!WavpackPackSamples (wpc, temp_buffer, samples_this_pass)) {
222 rb->splash(HZ*2, "internal error!");
223 error = true;
224 break;
225 }
226
227 samples_to_pack -= samples_this_pass;
228 }
229
230 if (error)
231 break;
232
233 bytes_count = WavpackFinishBlock (wpc);
234
235 if (rb->write (out_fd, output_buffer, bytes_count) != (int32_t) bytes_count) {
236 rb->splash(HZ*2, "could not write file!");
237 error = true;
238 break;
239 }
240
241 total_bytes_written += bytes_count;
242 samples_remaining -= samples_count;
243
244 wvupdate (start_tick, native_header.SampleRate, total_samples,
245 total_samples - samples_remaining, total_bytes_read, total_bytes_written);
246
247 buttons = rb->button_status ();
248
249 if (last_buttons == BUTTON_NONE && buttons != BUTTON_NONE) {
250 rb->splash(HZ*2, "operation aborted!");
251 error = true;
252 break;
253 }
254 else
255 last_buttons = buttons;
256 }
257
258 rb->close (out_fd);
259 rb->close (in_fd);
260
261 if (error) {
262 rb->remove (outfile);
263 }
264 else
265 rb->splash(HZ*3, "operation successful");
266
267 rb->reload_directory();
268 return error;
269}
270
271enum plugin_status plugin_start(const void *parameter)
272{
273 if (!parameter)
274 return PLUGIN_ERROR;
275
276 audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuflen);
277
278 if (audiobuflen < 0x200000) {
279 rb->splash(HZ*2, "not enough memory!");
280 return PLUGIN_ERROR;
281 }
282
283#ifdef HAVE_ADJUSTABLE_CPU_FREQ
284 rb->cpu_boost(true);
285#endif
286
287 wav2wv (parameter);
288
289#ifdef HAVE_ADJUSTABLE_CPU_FREQ
290 rb->cpu_boost(false);
291#endif
292 return PLUGIN_OK;
293}