summaryrefslogtreecommitdiff
path: root/apps/metadata/mpc.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/metadata/mpc.c')
-rw-r--r--apps/metadata/mpc.c126
1 files changed, 115 insertions, 11 deletions
diff --git a/apps/metadata/mpc.c b/apps/metadata/mpc.c
index 3f8907a934..9894bb8b54 100644
--- a/apps/metadata/mpc.c
+++ b/apps/metadata/mpc.c
@@ -28,15 +28,48 @@
28#include "logf.h" 28#include "logf.h"
29#include "replaygain.h" 29#include "replaygain.h"
30 30
31static int set_replaygain(struct mp3entry* id3, bool album, long value, 31#include "fixedpoint.h"
32 long used) 32#include <stdlib.h>
33#include <stdbool.h>
34#include <inttypes.h>
35
36/* Needed for replay gain in sv8, please search MPC_OLD_GAIN_REF in libmusepack */
37#define SV8_TO_SV7_CONVERT_GAIN (64.82*100)
38
39static int set_replaygain_sv7(struct mp3entry* id3,
40 bool album,
41 long value,
42 long used)
33{ 43{
34 long gain = (int16_t) ((value >> 16) & 0xffff); 44 long gain = (int16_t) ((value >> 16) & 0xffff);
35 long peak = (uint16_t) (value & 0xffff); 45 long peak = (uint16_t) (value & 0xffff);
36 46
47 /* Remark: mpc sv7 outputs peak as amplitude, not as dB. The following
48 * useage of peak is not correct and needs to be fixed. */
49
37 /* We use a peak value of 0 to indicate a given gain type isn't used. */ 50 /* We use a peak value of 0 to indicate a given gain type isn't used. */
38 if (peak != 0) 51 if (peak != 0) {
39 { 52 /* Use the Xing TOC field to store ReplayGain strings for use in the
53 * ID3 screen, since Musepack files shouldn't need to use it in any
54 * other way.
55 */
56 used += parse_replaygain_int(album, gain * 512 / 100, peak << 9,
57 id3, id3->toc + used, sizeof(id3->toc) - used);
58 }
59
60 return used;
61}
62
63static int set_replaygain_sv8(struct mp3entry* id3,
64 bool album,
65 long gain,
66 long peak,
67 long used)
68{
69 gain = (long)(SV8_TO_SV7_CONVERT_GAIN - (gain*100./256.));
70
71 /* We use a peak value of 0 to indicate a given gain type isn't used. */
72 if (peak != 0) {
40 /* Use the Xing TOC field to store ReplayGain strings for use in the 73 /* Use the Xing TOC field to store ReplayGain strings for use in the
41 * ID3 screen, since Musepack files shouldn't need to use it in any 74 * ID3 screen, since Musepack files shouldn't need to use it in any
42 * other way. 75 * other way.
@@ -48,9 +81,23 @@ static int set_replaygain(struct mp3entry* id3, bool album, long value,
48 return used; 81 return used;
49} 82}
50 83
84static int sv8_get_size(uint8_t *buffer, int index, uint64_t *p_size)
85{
86 unsigned char tmp;
87 uint64_t size = 0;
88
89 do {
90 tmp = buffer[index++];
91 size = (size << 7) | (tmp & 0x7F);
92 } while((tmp & 0x80));
93
94 *p_size = size;
95 return index;
96}
97
51bool get_musepack_metadata(int fd, struct mp3entry *id3) 98bool get_musepack_metadata(int fd, struct mp3entry *id3)
52{ 99{
53 static const int32_t sfreqs_sv7[4] = { 44100, 48000, 37800, 32000 }; 100 static const int32_t sfreqs[4] = { 44100, 48000, 37800, 32000 };
54 uint32_t header[8]; 101 uint32_t header[8];
55 uint64_t samples = 0; 102 uint64_t samples = 0;
56 int i; 103 int i;
@@ -63,7 +110,6 @@ bool get_musepack_metadata(int fd, struct mp3entry *id3)
63 header[i] = letoh32(header[i]); 110 header[i] = letoh32(header[i]);
64 if (!memcmp(header, "MP+", 3)) { /* Compare to sig "MP+" */ 111 if (!memcmp(header, "MP+", 3)) { /* Compare to sig "MP+" */
65 unsigned int streamversion; 112 unsigned int streamversion;
66
67 header[0] = letoh32(header[0]); 113 header[0] = letoh32(header[0]);
68 streamversion = (header[0] >> 24) & 15; 114 streamversion = (header[0] >> 24) & 15;
69 if (streamversion == 7) { 115 if (streamversion == 7) {
@@ -71,20 +117,78 @@ bool get_musepack_metadata(int fd, struct mp3entry *id3)
71 unsigned int last_frame_samples = (header[5] >> 20) & 0x07ff; 117 unsigned int last_frame_samples = (header[5] >> 20) & 0x07ff;
72 unsigned int bufused = 0; 118 unsigned int bufused = 0;
73 119
74 id3->frequency = sfreqs_sv7[(header[2] >> 16) & 0x0003]; 120 id3->frequency = sfreqs[(header[2] >> 16) & 0x0003];
75 samples = (uint64_t)header[1]*1152; /* 1152 is mpc frame size */ 121 samples = (uint64_t)header[1]*1152; /* 1152 is mpc frame size */
76 if (gapless) 122 if (gapless)
77 samples -= 1152 - last_frame_samples; 123 samples -= 1152 - last_frame_samples;
78 else 124 else
79 samples -= 481; /* Musepack subband synth filter delay */ 125 samples -= 481; /* Musepack subband synth filter delay */
80 126
81 bufused = set_replaygain(id3, false, header[3], bufused); 127 bufused = set_replaygain_sv7(id3, false, header[3], bufused);
82 bufused = set_replaygain(id3, true, header[4], bufused); 128 bufused = set_replaygain_sv7(id3, true , header[4], bufused);
83 } else { 129 } else {
84 return false; /* only SV7 is allowed within a "MP+" signature */ 130 return false; /* only SV7 is allowed within a "MP+" signature */
85 } 131 }
86 } else if (!memcmp(header, "MPCK", 4)) { /* Compare to sig "MPCK" */ 132 } else if (!memcmp(header, "MPCK", 4)) { /* Compare to sig "MPCK" */
87 return false; /* SV8 is not supported yet */ 133 uint8_t sv8_header[32];
134 /* 4 bytes 'MPCK' */
135 lseek(fd, 4, SEEK_SET);
136 if (read(fd, sv8_header, 2) != 2) return false; /* read frame ID */
137 if (!memcmp(sv8_header, "SH", 2)) { /* Stream Header ID */
138 int32_t k = 0;
139 uint32_t streamversion;
140 uint64_t size = 0;
141
142 /* 4 bytes 'MPCK' + 4 bytes crc + 2 'SH' */
143 lseek(fd, 10, SEEK_SET);
144 if (read(fd, sv8_header, 32) != 32) return false;
145
146 /* dummy read the correct amount of bits within the header data. */
147 size = sv8_header[k++];
148
149 /* Read stream version */
150 streamversion = sv8_header[k++];
151 if (streamversion != 8) return false; /* Only SV8 is allowed. */
152
153 /* Number of samples */
154 k = sv8_get_size(sv8_header, k, &samples);
155
156 /* Number of leading zero-samples */
157 k = sv8_get_size(sv8_header, k, &size);
158
159 /* Sampling frequency */
160 id3->frequency = sfreqs[(sv8_header[k++] >> 5) & 0x0003];
161
162 /* Number of channels */
163 id3->channels = (sv8_header[k++] >> 4) + 1;
164
165 if (!memcmp(sv8_header+k, "RG", 2)) { /* Replay Gain ID */
166 long peak, gain;
167 int bufused = 0;
168
169 k += 2; /* 2 bytes 'RG' */
170
171 /* sv8_get_size must be called to skip the right amount of
172 * bits within the header data. */
173 k = sv8_get_size(sv8_header, k, &size);
174
175 /* Read and set replay gain */
176 if (sv8_header[k++] == 1) {
177 /* Title's peak and gain */
178 gain = (int16_t) ((sv8_header[k]<<8) + sv8_header[k+1]); k += 2;
179 peak = (uint16_t)((sv8_header[k]<<8) + sv8_header[k+1]); k += 2;
180 bufused += set_replaygain_sv8(id3, false, gain, peak, bufused);
181
182 /* Album's peak and gain */
183 gain = (int16_t) ((sv8_header[k]<<8) + sv8_header[k+1]); k += 2;
184 peak = (uint16_t)((sv8_header[k]<<8) + sv8_header[k+1]); k += 2;
185 bufused += set_replaygain_sv8(id3, true , gain, peak, bufused);
186 }
187 }
188 } else {
189 /* No sv8 stream header found */
190 return false;
191 }
88 } else { 192 } else {
89 return false; /* SV4-6 is not supported anymore */ 193 return false; /* SV4-6 is not supported anymore */
90 } 194 }