1 module mp3decoderex;
2 
3 public import mp3decoder;
4 
5 struct mp3dec_file_info_t {
6 	mp3d_sample_t* buffer;
7 	size_t samples; /* channels included, byte size = samples*sizeof(short) */
8 	int channels, hz, layer, avg_bitrate_kbps;
9 }
10 
11 alias MP3D_PROGRESS_CB = int function(void* user_data, size_t file_size, size_t offset, ref mp3dec_frame_info_t info);
12 
13 alias MP3D_ITERATE_CB = int function(void* user_data, ref const(ubyte)* frame, int frame_size, size_t offset, ref mp3dec_frame_info_t info);
14 
15 mp3dec_file_info_t mp3dec_load(ref mp3dec_t dec, string file_name, MP3D_PROGRESS_CB progress_cb, void* user_data) {
16 	import std.mmfile;
17 	auto file = new MmFile(file_name, MmFile.Mode.read, 0, null, 0);
18 	scope (exit) {
19 		file.destroy();
20 	}
21 	mp3dec_file_info_t info;
22 	mp3dec_load_buf(dec, cast(ubyte[]) file[0 .. $], info, progress_cb, user_data);
23 	return info;
24 }
25 
26 int mp3dec_iterate(string file_name, MP3D_ITERATE_CB callback, void* user_data) {
27 	import std.mmfile;
28 	auto file = new MmFile(file_name, MmFile.Mode.read, 0, null, 0);
29 	scope (exit) {
30 		file.destroy();
31 	}
32 	mp3dec_iterate_buf(cast(ubyte[]) file[0 .. $], callback, user_data);
33 	return 0;
34 }
35 
36 private size_t mp3dec_skip_id3v2(const(ubyte)* buf, size_t buf_size) {
37 	import core.stdc..string : strncmp;
38 	if (buf_size > 10 && !strncmp(cast(char*) buf, "ID3", 3)) {
39 		return (((buf[6] & 0x7f) << 21) | ((buf[7] & 0x7f) << 14) | ((buf[8] & 0x7f) << 7) | (buf[9] & 0x7f)) + 10;
40 	}
41 	return 0;
42 }
43 
44 private void mp3dec_load_buf(ref mp3dec_t dec, const(ubyte)[] buffer, ref mp3dec_file_info_t info, MP3D_PROGRESS_CB progress_cb, void* user_data) {
45 	import core.stdc..string : memcpy, memset;
46 	import core.stdc.stdlib : malloc, realloc;
47 	auto buf_size = buffer.length;
48 	auto buf = buffer.ptr;
49 	size_t orig_buf_size = buf_size;
50 	mp3d_sample_t[MINIMP3_MAX_SAMPLES_PER_FRAME] pcm;
51 	mp3dec_frame_info_t frame_info;
52 	memset(&info, 0, info.sizeof);
53 	memset(&frame_info, 0, frame_info.sizeof);
54 	/* skip id3v2 */
55 	size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size);
56 	if (id3v2size > buf_size)
57 		return;
58 	buf += id3v2size;
59 	buf_size -= id3v2size;
60 	/* try to make allocation size assumption by first frame */
61 	mp3dec_init(dec);
62 	int samples;
63 	do {
64 		samples = mp3dec_decode_frame(dec, buf, cast(int) buf_size, pcm.ptr, &frame_info);
65 		buf += frame_info.frame_bytes;
66 		buf_size -= frame_info.frame_bytes;
67 		if (samples)
68 			break;
69 	}
70 	while (frame_info.frame_bytes);
71 	if (!samples)
72 		return;
73 	samples *= frame_info.channels;
74 	size_t allocated = (buf_size / frame_info.frame_bytes) * samples * mp3d_sample_t.sizeof + MINIMP3_MAX_SAMPLES_PER_FRAME * mp3d_sample_t.sizeof;
75 	info.buffer = cast(mp3d_sample_t*) malloc(allocated);
76 	if (!info.buffer)
77 		return;
78 	info.samples = samples;
79 	memcpy(info.buffer, pcm.ptr, info.samples * mp3d_sample_t.sizeof);
80 	/* save info */
81 	info.channels = frame_info.channels;
82 	info.hz = frame_info.hz;
83 	info.layer = frame_info.layer;
84 	size_t avg_bitrate_kbps = frame_info.bitrate_kbps;
85 	size_t frames = 1;
86 	/* decode rest frames */
87 	int frame_bytes;
88 	do {
89 		if ((allocated - info.samples * mp3d_sample_t.sizeof) < MINIMP3_MAX_SAMPLES_PER_FRAME * mp3d_sample_t.sizeof) {
90 			allocated *= 2;
91 			info.buffer = cast(mp3d_sample_t*) realloc(info.buffer, allocated);
92 		}
93 		samples = mp3dec_decode_frame(dec, buf, cast(int) buf_size, info.buffer + info.samples, &frame_info);
94 		frame_bytes = frame_info.frame_bytes;
95 		buf += frame_bytes;
96 		buf_size -= frame_bytes;
97 		if (samples) {
98 			if (info.hz != frame_info.hz || info.layer != frame_info.layer)
99 				break;
100 			if (info.channels && info.channels != frame_info.channels) {
101 				version (MINIMP3_ALLOW_MONO_STEREO_TRANSITION) {
102 					info.channels = 0; /* mark file with mono-stereo transition */
103 				}
104 				else {
105 					break;
106 				}
107 			}
108 			info.samples += samples * frame_info.channels;
109 			avg_bitrate_kbps += frame_info.bitrate_kbps;
110 			frames++;
111 			if (progress_cb)
112 				progress_cb(user_data, orig_buf_size, orig_buf_size - buf_size, frame_info);
113 		}
114 	}
115 	while (frame_bytes);
116 	/* reallocate to normal buffer size */
117 	if (allocated != info.samples * mp3d_sample_t.sizeof)
118 		info.buffer = cast(mp3d_sample_t*) realloc(info.buffer, info.samples * mp3d_sample_t.sizeof);
119 	info.avg_bitrate_kbps = cast(int)(avg_bitrate_kbps / frames);
120 }
121 
122 private void mp3dec_iterate_buf(const(ubyte)[] buffer, MP3D_ITERATE_CB callback, void* user_data) {
123 	import core.stdc..string : memset;
124 	auto buf_size = buffer.length;
125 	auto buf = buffer.ptr;
126 	if (!callback)
127 		return;
128 	mp3dec_frame_info_t frame_info;
129 	memset(&frame_info, 0, frame_info.sizeof);
130 	/* skip id3v2 */
131 	size_t id3v2size = mp3dec_skip_id3v2(buf, buf_size);
132 	if (id3v2size > buf_size)
133 		return;
134 	const(ubyte)* orig_buf = buf;
135 	buf += id3v2size;
136 	buf_size -= id3v2size;
137 	do {
138 		int free_format_bytes = 0, frame_size = 0;
139 		int i = mp3d_find_frame(buf, cast(int) buf_size, &free_format_bytes, &frame_size);
140 		buf += i;
141 		buf_size -= i;
142 		if (i && !frame_size)
143 			continue;
144 		if (!frame_size)
145 			break;
146 		const(ubyte)* hdr = buf;
147 		frame_info.channels = HDR_IS_MONO(hdr) ? 1 : 2;
148 		frame_info.hz = hdr_sample_rate_hz(hdr);
149 		frame_info.layer = 4 - HDR_GET_LAYER(hdr);
150 		frame_info.bitrate_kbps = hdr_bitrate_kbps(hdr);
151 		frame_info.frame_bytes = frame_size;
152 
153 		if (callback(user_data, hdr, frame_size, hdr - orig_buf, frame_info))
154 			break;
155 		buf += frame_size;
156 		buf_size -= frame_size;
157 	}
158 	while (1);
159 }