1 module pxtone.pulse.oggv;
2 
3 version (pxINCLUDE_OGGVORBIS):
4 import core.stdc.config;
5 import derelict.vorbis.codec;
6 import derelict.vorbis.file;
7 
8 import pxtone.descriptor;
9 import pxtone.error;
10 import pxtone.pulse.pcm;
11 
12 import std.stdio;
13 
14 struct OVMEM {
15 	const(ubyte)[] p_buf; // ogg vorbis-data on memory.s
16 	int size; //
17 	int pos; // reading position.
18 }
19 
20 // 4 callbacks below:
21 
22 extern (C) size_t _mread(void* p, size_t size, size_t nmemb, void* p_void) nothrow @system {
23 	OVMEM* pom = cast(OVMEM*) p_void;
24 
25 	if (!pom) {
26 		return -1;
27 	}
28 	if (pom.pos >= pom.size) {
29 		return 0;
30 	}
31 	if (pom.pos == -1) {
32 		return 0;
33 	}
34 
35 	int left = pom.size - pom.pos;
36 
37 	if (cast(int)(size * nmemb) >= left) {
38 		p[0 .. pom.size - pom.pos] = cast(void[])pom.p_buf[pom.pos .. pom.size];
39 		pom.pos = pom.size;
40 		return left / size;
41 	}
42 
43 	p[0 .. nmemb * size] = cast(void[])pom.p_buf[pom.pos .. pom.pos + nmemb * size];
44 	pom.pos += cast(int)(nmemb * size);
45 
46 	return nmemb;
47 }
48 
49 extern (C) int _mseek(void* p_void, long offset, int mode) nothrow @system {
50 	int newpos;
51 	OVMEM* pom = cast(OVMEM*) p_void;
52 
53 	if (!pom || pom.pos < 0) {
54 		return -1;
55 	}
56 	if (offset < 0) {
57 		pom.pos = -1;
58 		return -1;
59 	}
60 
61 	switch (mode) {
62 	case SEEK_SET:
63 		newpos = cast(int) offset;
64 		break;
65 	case SEEK_CUR:
66 		newpos = pom.pos + cast(int) offset;
67 		break;
68 	case SEEK_END:
69 		newpos = pom.size + cast(int) offset;
70 		break;
71 	default:
72 		return -1;
73 	}
74 	if (newpos < 0) {
75 		return -1;
76 	}
77 
78 	pom.pos = newpos;
79 
80 	return 0;
81 }
82 
83 extern (C) c_long _mtell(void* p_void) nothrow @system {
84 	OVMEM* pom = cast(OVMEM*) p_void;
85 	if (!pom) {
86 		return -1;
87 	}
88 	return pom.pos;
89 }
90 
91 extern (C) int _mclose_dummy(void* p_void) nothrow @system {
92 	OVMEM* pom = cast(OVMEM*) p_void;
93 	if (!pom) {
94 		return -1;
95 	}
96 	return 0;
97 }
98 
99 /////////////////
100 // global
101 /////////////////
102 
103 struct pxtnPulse_Oggv {
104 private:
105 	int _ch;
106 	int _sps2;
107 	int _smp_num;
108 	int _size;
109 	ubyte[] _p_data;
110 
111 	bool _SetInformation() nothrow @system {
112 		bool b_ret = false;
113 
114 		OVMEM ovmem;
115 		ovmem.p_buf = _p_data;
116 		ovmem.pos = 0;
117 		ovmem.size = _size;
118 
119 		// set callback func.
120 		ov_callbacks oc;
121 		oc.read_func = &_mread;
122 		oc.seek_func = &_mseek;
123 		oc.close_func = &_mclose_dummy;
124 		oc.tell_func = &_mtell;
125 
126 		OggVorbis_File vf;
127 
128 		vorbis_info* vi;
129 
130 		switch (ov_open_callbacks(&ovmem, &vf, null, 0, oc)) {
131 		case OV_EREAD:
132 			goto End; //{printf("A read from media returned an error.\n");exit(1);}
133 		case OV_ENOTVORBIS:
134 			goto End; //{printf("Bitstream is not Vorbis data. \n");exit(1);}
135 		case OV_EVERSION:
136 			goto End; //{printf("Vorbis version mismatch. \n");exit(1);}
137 		case OV_EBADHEADER:
138 			goto End; //{printf("Invalid Vorbis bitstream header. \n");exit(1);}
139 		case OV_EFAULT:
140 			goto End; //{printf("Internal logic fault; indicates a bug or heap/stack corruption. \n");exit(1);}
141 		default:
142 			break;
143 		}
144 
145 		vi = ov_info(&vf, -1);
146 
147 		_ch = vi.channels;
148 		_sps2 = vi.rate;
149 		_smp_num = cast(int) ov_pcm_total(&vf, -1);
150 
151 		// end.
152 		ov_clear(&vf);
153 
154 		b_ret = true;
155 
156 	End:
157 		return b_ret;
158 
159 	}
160 
161 public:
162 	 ~this() nothrow @system {
163 		Release();
164 	}
165 
166 	void Decode(pxtnPulse_PCM* p_pcm) const @system {
167 		OggVorbis_File vf;
168 		vorbis_info* vi;
169 		ov_callbacks oc;
170 
171 		OVMEM ovmem;
172 		int current_section;
173 		ubyte[4096] pcmout = 0; //take 4k out of the data segment, not the stack
174 
175 		ovmem.p_buf = _p_data;
176 		ovmem.pos = 0;
177 		ovmem.size = _size;
178 
179 		// set callback func.
180 		oc.read_func = &_mread;
181 		oc.seek_func = &_mseek;
182 		oc.close_func = &_mclose_dummy;
183 		oc.tell_func = &_mtell;
184 
185 		switch (ov_open_callbacks(&ovmem, &vf, null, 0, oc)) {
186 		case OV_EREAD:
187 			throw new PxtoneException("A read from media returned an error");
188 		case OV_ENOTVORBIS:
189 			throw new PxtoneException("Bitstream is not Vorbis data");
190 		case OV_EVERSION:
191 			throw new PxtoneException("Vorbis version mismatch");
192 		case OV_EBADHEADER:
193 			throw new PxtoneException("Invalid Vorbis bitstream header");
194 		case OV_EFAULT:
195 			throw new PxtoneException("Internal logic fault; indicates a bug or heap/stack corruption");
196 		default:
197 			break;
198 		}
199 
200 		vi = ov_info(&vf, -1);
201 
202 		{
203 			int smp_num = cast(int) ov_pcm_total(&vf, -1);
204 			uint bytes;
205 
206 			bytes = vi.channels * 2 * smp_num;
207 
208 			p_pcm.Create(vi.channels, vi.rate, 16, smp_num);
209 		}
210 		// decode..
211 		{
212 			int ret = 0;
213 			ubyte* p = p_pcm.get_p_buf().ptr;
214 			do {
215 				ret = cast(int)ov_read(&vf, cast(byte*) pcmout.ptr, 4096, 0, 2, 1, &current_section);
216 				if (ret > 0) {
217 					p[0 .. ret] = pcmout[0 .. ret]; //fwrite( pcmout, 1, ret, of );
218 				}
219 				p += ret;
220 			}
221 			while (ret);
222 		}
223 
224 		// end.
225 		ov_clear(&vf);
226 	}
227 
228 	void Release() nothrow @system {
229 		_p_data = null;
230 		_ch = 0;
231 		_sps2 = 0;
232 		_smp_num = 0;
233 		_size = 0;
234 	}
235 
236 	bool GetInfo(int* p_ch, int* p_sps, int* p_smp_num) nothrow @safe {
237 		if (!_p_data) {
238 			return false;
239 		}
240 
241 		if (p_ch) {
242 			*p_ch = _ch;
243 		}
244 		if (p_sps) {
245 			*p_sps = _sps2;
246 		}
247 		if (p_smp_num) {
248 			*p_smp_num = _smp_num;
249 		}
250 
251 		return true;
252 	}
253 
254 	int GetSize() const nothrow @safe {
255 		if (!_p_data) {
256 			return 0;
257 		}
258 		return cast(int)(int.sizeof * 4 + _size);
259 	}
260 
261 	void ogg_write(ref pxtnDescriptor desc) const @system {
262 		desc.w_asfile(_p_data);
263 	}
264 
265 	void ogg_read(ref pxtnDescriptor desc) @system {
266 		_size = desc.get_size_bytes();
267 		if (!(_size)) {
268 			throw new PxtoneException("desc r");
269 		}
270 		_p_data = new ubyte[](_size);
271 		scope(failure) {
272 			_p_data = null;
273 			_size = 0;
274 		}
275 		desc.r(_p_data);
276 		if (!_SetInformation()) {
277 			throw new PxtoneException("_SetInformation");
278 		}
279 	}
280 
281 	void pxtn_write(ref pxtnDescriptor p_doc) const @system {
282 		if (!_p_data) {
283 			throw new PxtoneException("No data");
284 		}
285 
286 		p_doc.w_asfile(_ch);
287 		p_doc.w_asfile(_sps2);
288 		p_doc.w_asfile(_smp_num);
289 		p_doc.w_asfile(_size);
290 		p_doc.w_asfile(_p_data);
291 	}
292 
293 	void pxtn_read(ref pxtnDescriptor p_doc) @system {
294 		p_doc.r(_ch);
295 		p_doc.r(_sps2);
296 		p_doc.r(_smp_num);
297 		p_doc.r(_size);
298 
299 		if (!_size) {
300 			throw new PxtoneException("Invalid size read");
301 		}
302 
303 		_p_data = new ubyte[](_size);
304 		scope(failure) {
305 			_p_data = null;
306 			_size = 0;
307 		}
308 		p_doc.r(_p_data);
309 	}
310 
311 	bool Copy(ref pxtnPulse_Oggv p_dst) const nothrow @system {
312 		p_dst.Release();
313 		if (!_p_data) {
314 			return true;
315 		}
316 
317 		p_dst._p_data = new ubyte[](_size);
318 		if (!(p_dst._p_data)) {
319 			return false;
320 		}
321 		p_dst._p_data[0 .. _size] = _p_data[0 .. _size];
322 
323 		p_dst._ch = _ch;
324 		p_dst._sps2 = _sps2;
325 		p_dst._size = _size;
326 		p_dst._smp_num = _smp_num;
327 
328 		return true;
329 	}
330 }