1 module pxtone.woice;
2 // '12/03/03 pxtnWoice.
3 
4 import pxtone.pxtn;
5 
6 import pxtone.descriptor;
7 import pxtone.error;
8 import pxtone.evelist;
9 import pxtone.pulse.noise;
10 import pxtone.pulse.noisebuilder;
11 import pxtone.pulse.oscillator;
12 import pxtone.pulse.pcm;
13 import pxtone.pulse.oggv;
14 import pxtone.woiceptv;
15 
16 enum pxtnMAX_TUNEWOICENAME = 16; // fixture.
17 
18 enum pxtnMAX_UNITCONTROLVOICE = 2; // max-woice per unit
19 
20 enum pxtnBUFSIZE_TIMEPAN = 0x40;
21 enum pxtnBITPERSAMPLE = 16;
22 
23 enum PTV_VOICEFLAG_WAVELOOP = 0x00000001;
24 enum PTV_VOICEFLAG_SMOOTH = 0x00000002;
25 enum PTV_VOICEFLAG_BEATFIT = 0x00000004;
26 enum PTV_VOICEFLAG_UNCOVERED = 0xfffffff8;
27 
28 enum PTV_DATAFLAG_WAVE = 0x00000001;
29 enum PTV_DATAFLAG_ENVELOPE = 0x00000002;
30 enum PTV_DATAFLAG_UNCOVERED = 0xfffffffc;
31 
32 immutable _code = "PTVOICE-";
33 
34 enum pxtnWOICETYPE {
35 	None = 0,
36 	PCM,
37 	PTV,
38 	PTN,
39 	OGGV,
40 }
41 
42 enum pxtnVOICETYPE {
43 	Coodinate = 0,
44 	Overtone,
45 	Noise,
46 	Sampling,
47 	OggVorbis,
48 }
49 
50 struct pxtnVOICEINSTANCE {
51 	int smp_head_w;
52 	int smp_body_w;
53 	int smp_tail_w;
54 	ubyte[] p_smp_w;
55 
56 	ubyte[] p_env;
57 	int env_size;
58 	int env_release;
59 }
60 
61 struct pxtnVOICEENVELOPE {
62 	int fps;
63 	int head_num;
64 	int body_num;
65 	int tail_num;
66 	pxtnPOINT[] points;
67 }
68 
69 struct pxtnVOICEWAVE {
70 	int num;
71 	int reso; // COODINATERESOLUTION
72 	pxtnPOINT[] points;
73 }
74 
75 struct pxtnVOICEUNIT {
76 	int basic_key;
77 	int volume;
78 	int pan;
79 	float tuning;
80 	uint voice_flags;
81 	uint data_flags;
82 
83 	pxtnVOICETYPE type;
84 	pxtnPulse_PCM p_pcm;
85 	pxtnPulse_Noise p_ptn;
86 	version (pxINCLUDE_OGGVORBIS) {
87 		pxtnPulse_Oggv p_oggv;
88 	}
89 
90 	pxtnVOICEWAVE wave;
91 	pxtnVOICEENVELOPE envelope;
92 }
93 
94 struct pxtnVOICETONE {
95 	double smp_pos;
96 	float offset_freq;
97 	int env_volume;
98 	int life_count;
99 	int on_count;
100 
101 	int smp_count;
102 	int env_start;
103 	int env_pos;
104 	int env_release_clock;
105 
106 	int smooth_volume;
107 }
108 
109 private void _Voice_Release(pxtnVOICEUNIT* p_vc, pxtnVOICEINSTANCE* p_vi) nothrow @system {
110 	if (p_vc) {
111 		p_vc.envelope.points = null;
112 		p_vc.envelope = pxtnVOICEENVELOPE.init;
113 		p_vc.wave.points = null;
114 		p_vc.wave = pxtnVOICEWAVE.init;
115 	}
116 	if (p_vi) {
117 		p_vi.p_env = null;
118 		p_vi.p_smp_w = null;
119 		*p_vi = pxtnVOICEINSTANCE.init;
120 	}
121 }
122 
123 void _UpdateWavePTV(pxtnVOICEUNIT* p_vc, pxtnVOICEINSTANCE* p_vi, int ch, int sps, int bps) nothrow @system {
124 	double work, osc;
125 	int long_;
126 	int[2] pan_volume = [64, 64];
127 	bool b_ovt;
128 
129 	pxtnPulse_Oscillator osci;
130 
131 	if (ch == 2) {
132 		if (p_vc.pan > 64) {
133 			pan_volume[0] = (128 - p_vc.pan);
134 		}
135 		if (p_vc.pan < 64) {
136 			pan_volume[1] = (p_vc.pan);
137 		}
138 	}
139 
140 	osci.ReadyGetSample(p_vc.wave.points, p_vc.wave.num, p_vc.volume, p_vi.smp_body_w, p_vc.wave.reso);
141 
142 	if (p_vc.type == pxtnVOICETYPE.Overtone) {
143 		b_ovt = true;
144 	} else {
145 		b_ovt = false;
146 	}
147 
148 	//  8bit
149 	if (bps == 8) {
150 		ubyte* p = cast(ubyte*) p_vi.p_smp_w;
151 		for (int s = 0; s < p_vi.smp_body_w; s++) {
152 			if (b_ovt) {
153 				osc = osci.GetOneSample_Overtone(s);
154 			} else {
155 				osc = osci.GetOneSample_Coodinate(s);
156 			}
157 			for (int c = 0; c < ch; c++) {
158 				work = osc * pan_volume[c] / 64;
159 				if (work > 1.0) {
160 					work = 1.0;
161 				}
162 				if (work < -1.0) {
163 					work = -1.0;
164 				}
165 				long_ = cast(int)(work * 127);
166 				p[s * ch + c] = cast(ubyte)(long_ + 128);
167 			}
168 		}
169 
170 		// 16bit
171 	} else {
172 		short* p = cast(short*) p_vi.p_smp_w;
173 		for (int s = 0; s < p_vi.smp_body_w; s++) {
174 			if (b_ovt) {
175 				osc = osci.GetOneSample_Overtone(s);
176 			} else {
177 				osc = osci.GetOneSample_Coodinate(s);
178 			}
179 			for (int c = 0; c < ch; c++) {
180 				work = osc * pan_volume[c] / 64;
181 				if (work > 1.0) {
182 					work = 1.0;
183 				}
184 				if (work < -1.0) {
185 					work = -1.0;
186 				}
187 				long_ = cast(int)(work * 32767);
188 				p[s * ch + c] = cast(short) long_;
189 			}
190 		}
191 	}
192 }
193 
194 // 24byte =================
195 struct _MATERIALSTRUCT_PCM {
196 	ushort x3x_unit_no;
197 	ushort basic_key;
198 	uint voice_flags;
199 	ushort ch;
200 	ushort bps;
201 	uint sps;
202 	float tuning = 0.0;
203 	uint data_size;
204 }
205 
206 /////////////
207 // matePTN
208 /////////////
209 
210 // 16byte =================
211 struct _MATERIALSTRUCT_PTN {
212 	ushort x3x_unit_no;
213 	ushort basic_key;
214 	uint voice_flags;
215 	float tuning = 0.0;
216 	int rrr; // 0: -v.0.9.2.3
217 	// 1:  v.0.9.2.4-
218 }
219 
220 /////////////////
221 // matePTV
222 /////////////////
223 
224 // 24byte =================
225 struct _MATERIALSTRUCT_PTV {
226 	ushort x3x_unit_no;
227 	ushort rrr;
228 	float x3x_tuning = 0.0;
229 	int size;
230 }
231 
232 //////////////////////
233 // mateOGGV
234 //////////////////////
235 
236 // 16byte =================
237 struct _MATERIALSTRUCT_OGGV {
238 	ushort xxx; //ch;
239 	ushort basic_key;
240 	uint voice_flags;
241 	float tuning = 0.0;
242 }
243 
244 ////////////////////////
245 // publics..
246 ////////////////////////
247 
248 struct pxtnWoice {
249 private:
250 	int _voice_num;
251 
252 	char[pxtnMAX_TUNEWOICENAME + 1] _name_buf;
253 	uint _name_size;
254 
255 	pxtnWOICETYPE _type = pxtnWOICETYPE.None;
256 	pxtnVOICEUNIT[] _voices;
257 	pxtnVOICEINSTANCE[] _voinsts;
258 
259 	float _x3x_tuning;
260 	int _x3x_basic_key; // tuning old-fmt when key-event
261 
262 public:
263 
264 	 ~this() nothrow @system {
265 		Voice_Release();
266 	}
267 
268 	int get_voice_num() const nothrow @safe {
269 		return _voice_num;
270 	}
271 
272 	float get_x3x_tuning() const nothrow @safe {
273 		return _x3x_tuning;
274 	}
275 
276 	int get_x3x_basic_key() const nothrow @safe {
277 		return _x3x_basic_key;
278 	}
279 
280 	pxtnWOICETYPE get_type() const nothrow @safe {
281 		return _type;
282 	}
283 
284 	inout(pxtnVOICEUNIT)* get_voice(int idx) inout nothrow @safe {
285 		if (idx < 0 || idx >= _voice_num) {
286 			return null;
287 		}
288 		return &_voices[idx];
289 	}
290 
291 	const(pxtnVOICEINSTANCE)* get_instance(int idx) const nothrow @safe {
292 		if (idx < 0 || idx >= _voice_num) {
293 			return null;
294 		}
295 		return &_voinsts[idx];
296 	}
297 
298 	bool set_name_buf(const(char)[] name) nothrow @safe {
299 		if (!name || name.length < 0 || name.length > pxtnMAX_TUNEWOICENAME) {
300 			return false;
301 		}
302 		_name_buf[] = 0;
303 		_name_size = cast(uint)name.length;
304 		if (name.length) {
305 			_name_buf[0 .. name.length] = name;
306 		}
307 		return true;
308 	}
309 
310 	const(char)[] get_name_buf() const return nothrow @safe {
311 		return _name_buf[0 .. _name_size];
312 	}
313 
314 	bool is_name_buf() const nothrow @safe {
315 		if (_name_size > 0) {
316 			return true;
317 		}
318 		return false;
319 	}
320 
321 	void Voice_Allocate(int voice_num) @system {
322 		Voice_Release();
323 
324 		scope(failure) {
325 			Voice_Release();
326 		}
327 		_voices = new pxtnVOICEUNIT[](voice_num);
328 		_voinsts = new pxtnVOICEINSTANCE[](voice_num);
329 		_voice_num = voice_num;
330 
331 		for (int i = 0; i < voice_num; i++) {
332 			pxtnVOICEUNIT* p_vc = &_voices[i];
333 			p_vc.basic_key = EVENTDEFAULT_BASICKEY;
334 			p_vc.volume = 128;
335 			p_vc.pan = 64;
336 			p_vc.tuning = 1.0f;
337 			p_vc.voice_flags = PTV_VOICEFLAG_SMOOTH;
338 			p_vc.data_flags = PTV_DATAFLAG_WAVE;
339 			p_vc.p_pcm = pxtnPulse_PCM.init;
340 			p_vc.p_ptn = pxtnPulse_Noise.init;
341 			version (pxINCLUDE_OGGVORBIS) {
342 				p_vc.p_oggv = pxtnPulse_Oggv.init;
343 			}
344 			p_vc.envelope = pxtnVOICEENVELOPE.init;
345 		}
346 	}
347 
348 	void Voice_Release() nothrow @system {
349 		for (int v = 0; v < _voice_num; v++) {
350 			_Voice_Release(&_voices[v], &_voinsts[v]);
351 		}
352 		_voices = null;
353 		_voinsts = null;
354 		_voice_num = 0;
355 	}
356 
357 	bool Copy(pxtnWoice* p_dst) const @system {
358 		bool b_ret = false;
359 		int v, num;
360 		size_t size;
361 		const(pxtnVOICEUNIT)* p_vc1 = null;
362 		pxtnVOICEUNIT* p_vc2 = null;
363 
364 		p_dst.Voice_Allocate(_voice_num);
365 		scope(failure) {
366 			p_dst.Voice_Release();
367 		}
368 
369 		p_dst._type = _type;
370 
371 		p_dst._name_buf = _name_buf;
372 
373 		for (v = 0; v < _voice_num; v++) {
374 			p_vc1 = &_voices[v];
375 			p_vc2 = &p_dst._voices[v];
376 
377 			p_vc2.tuning = p_vc1.tuning;
378 			p_vc2.data_flags = p_vc1.data_flags;
379 			p_vc2.basic_key = p_vc1.basic_key;
380 			p_vc2.pan = p_vc1.pan;
381 			p_vc2.type = p_vc1.type;
382 			p_vc2.voice_flags = p_vc1.voice_flags;
383 			p_vc2.volume = p_vc1.volume;
384 
385 			// envelope
386 			p_vc2.envelope.body_num = p_vc1.envelope.body_num;
387 			p_vc2.envelope.fps = p_vc1.envelope.fps;
388 			p_vc2.envelope.head_num = p_vc1.envelope.head_num;
389 			p_vc2.envelope.tail_num = p_vc1.envelope.tail_num;
390 			num = p_vc2.envelope.head_num + p_vc2.envelope.body_num + p_vc2.envelope.tail_num;
391 			size = pxtnPOINT.sizeof * num;
392 			p_vc2.envelope.points = new pxtnPOINT[](size / pxtnPOINT.sizeof);
393 			if (!p_vc2.envelope.points) {
394 				goto End;
395 			}
396 			p_vc2.envelope.points[0 .. size] = p_vc1.envelope.points[0 .. size];
397 
398 			// wave
399 			p_vc2.wave.num = p_vc1.wave.num;
400 			p_vc2.wave.reso = p_vc1.wave.reso;
401 			size = pxtnPOINT.sizeof * p_vc2.wave.num;
402 			p_vc2.wave.points = new pxtnPOINT[](size / pxtnPOINT.sizeof);
403 			if (!p_vc2.wave.points) {
404 				goto End;
405 			}
406 			p_vc2.wave.points[0 .. size] = p_vc1.wave.points[0 .. size];
407 
408 			p_vc1.p_pcm.Copy(p_vc2.p_pcm);
409 			if (!p_vc1.p_ptn.Copy(p_vc2.p_ptn)) {
410 				goto End;
411 			}
412 			version (pxINCLUDE_OGGVORBIS) {
413 				if (!p_vc1.p_oggv.Copy(p_vc2.p_oggv)) {
414 					goto End;
415 				}
416 			}
417 		}
418 
419 		b_ret = true;
420 	End:
421 		if (!b_ret) {
422 			p_dst.Voice_Release();
423 		}
424 
425 		return b_ret;
426 	}
427 
428 	void Slim() nothrow @system {
429 		for (int i = _voice_num - 1; i >= 0; i--) {
430 			bool b_remove = false;
431 
432 			if (!_voices[i].volume) {
433 				b_remove = true;
434 			}
435 
436 			if (_voices[i].type == pxtnVOICETYPE.Coodinate && _voices[i].wave.num <= 1) {
437 				b_remove = true;
438 			}
439 
440 			if (b_remove) {
441 				_Voice_Release(&_voices[i], &_voinsts[i]);
442 				_voice_num--;
443 				for (int j = i; j < _voice_num; j++) {
444 					_voices[j] = _voices[j + 1];
445 				}
446 				_voices[_voice_num] = pxtnVOICEUNIT.init;
447 			}
448 		}
449 	}
450 
451 	void read(ref pxtnDescriptor desc, pxtnWOICETYPE type) @system {
452 		switch (type) {
453 			// PCM
454 		case pxtnWOICETYPE.PCM: {
455 				pxtnVOICEUNIT* p_vc;
456 				Voice_Allocate(1);
457 				p_vc = &_voices[0];
458 				p_vc.type = pxtnVOICETYPE.Sampling;
459 				p_vc.p_pcm.read(desc);
460 				// if under 0.005 sec, set LOOP.
461 				if (p_vc.p_pcm.get_sec() < 0.005f) {
462 					p_vc.voice_flags |= PTV_VOICEFLAG_WAVELOOP;
463 				} else {
464 					p_vc.voice_flags &= ~PTV_VOICEFLAG_WAVELOOP;
465 				}
466 				_type = pxtnWOICETYPE.PCM;
467 			}
468 			break;
469 
470 			// PTV
471 		case pxtnWOICETYPE.PTV: {
472 				PTV_Read(desc);
473 			}
474 			break;
475 
476 			// PTN
477 		case pxtnWOICETYPE.PTN:
478 			Voice_Allocate(1);
479 			{
480 				pxtnVOICEUNIT* p_vc = &_voices[0];
481 				p_vc.type = pxtnVOICETYPE.Noise;
482 				p_vc.p_ptn.read(desc);
483 				_type = pxtnWOICETYPE.PTN;
484 			}
485 			break;
486 
487 			// OGGV
488 		case pxtnWOICETYPE.OGGV:
489 			version (pxINCLUDE_OGGVORBIS) {
490 				Voice_Allocate(1);
491 				{
492 					pxtnVOICEUNIT* p_vc;
493 					p_vc = &_voices[0];
494 					p_vc.type = pxtnVOICETYPE.OggVorbis;
495 					p_vc.p_oggv.ogg_read(desc);
496 					_type = pxtnWOICETYPE.OGGV;
497 				}
498 				break;
499 			} else {
500 				throw new PxtoneException("Ogg Vorbis support is required");
501 			}
502 
503 		default:
504 			throw new PxtoneException("Unknown woice type");
505 		}
506 	}
507 
508 	bool PTV_Write(ref pxtnDescriptor p_doc, int* p_total) const @system {
509 		bool b_ret = false;
510 		const(pxtnVOICEUNIT)* p_vc = null;
511 		uint work = 0;
512 		int v = 0;
513 		int total = 0;
514 
515 		p_doc.w_asfile(_code);
516 		p_doc.w_asfile(_version);
517 		p_doc.w_asfile(total);
518 
519 		work = 0;
520 
521 		// p_ptv. (5)
522 		p_doc.v_w_asfile(work, total);
523 		p_doc.v_w_asfile(work, total);
524 		p_doc.v_w_asfile(work, total);
525 		p_doc.v_w_asfile(_voice_num, total);
526 
527 		for (v = 0; v < _voice_num; v++) {
528 			// p_ptvv. (9)
529 			p_vc = &_voices[v];
530 			if (!p_vc) {
531 				goto End;
532 			}
533 
534 			p_doc.v_w_asfile(p_vc.basic_key, total);
535 			p_doc.v_w_asfile(p_vc.volume, total);
536 			p_doc.v_w_asfile(p_vc.pan, total);
537 			work = *(cast(uint*)&p_vc.tuning);
538 			p_doc.v_w_asfile(work, total);
539 			p_doc.v_w_asfile(p_vc.voice_flags, total);
540 			p_doc.v_w_asfile(p_vc.data_flags, total);
541 
542 			if (p_vc.data_flags & PTV_DATAFLAG_WAVE) {
543 				_Write_Wave(p_doc, p_vc, total);
544 			}
545 			if (p_vc.data_flags & PTV_DATAFLAG_ENVELOPE) {
546 				_Write_Envelope(p_doc, p_vc, total);
547 			}
548 		}
549 
550 		// total size
551 		p_doc.seek(pxtnSEEK.cur, -(total + 4));
552 		p_doc.w_asfile(total);
553 		p_doc.seek(pxtnSEEK.cur, total);
554 
555 		if (p_total) {
556 			*p_total = 16 + total;
557 		}
558 		b_ret = true;
559 	End:
560 
561 		return b_ret;
562 	}
563 
564 	void PTV_Read(ref pxtnDescriptor p_doc) @system {
565 		pxtnVOICEUNIT* p_vc = null;
566 		ubyte[8] code = 0;
567 		int version_ = 0;
568 		int work1 = 0;
569 		int work2 = 0;
570 		int total = 0;
571 		int num = 0;
572 
573 		p_doc.r(code[]);
574 		p_doc.r(version_);
575 		if (code[0 .. 8] != _code) {
576 			throw new PxtoneException("inv code");
577 		}
578 		p_doc.r(total);
579 		if (version_ > _version) {
580 			throw new PxtoneException("fmt new");
581 		}
582 
583 		// p_ptv. (5)
584 		p_doc.v_r(_x3x_basic_key);
585 		p_doc.v_r(work1);
586 		p_doc.v_r(work2);
587 		if (work1 || work2) {
588 			throw new PxtoneException("fmt unknown");
589 		}
590 		p_doc.v_r(num);
591 		Voice_Allocate(num);
592 
593 		for (int v = 0; v < _voice_num; v++) {
594 			// p_ptvv. (8)
595 			p_vc = &_voices[v];
596 			if (!p_vc) {
597 				throw new PxtoneException("FATAL");
598 			}
599 			p_doc.v_r(p_vc.basic_key);
600 			p_doc.v_r(p_vc.volume);
601 			p_doc.v_r(p_vc.pan);
602 			p_doc.v_r(work1);
603 			p_vc.tuning = *(cast(float*)&work1);
604 			p_doc.v_r(*cast(int*)&p_vc.voice_flags);
605 			p_doc.v_r(*cast(int*)&p_vc.data_flags);
606 
607 			// no support.
608 			if (p_vc.voice_flags & PTV_VOICEFLAG_UNCOVERED) {
609 				throw new PxtoneException("fmt unknown");
610 			}
611 			if (p_vc.data_flags & PTV_DATAFLAG_UNCOVERED) {
612 				throw new PxtoneException("fmt unknown");
613 			}
614 			if (p_vc.data_flags & PTV_DATAFLAG_WAVE) {
615 				_Read_Wave(p_doc, p_vc);
616 			}
617 			if (p_vc.data_flags & PTV_DATAFLAG_ENVELOPE) {
618 				_Read_Envelope(p_doc, p_vc);
619 			}
620 		}
621 		_type = pxtnWOICETYPE.PTV;
622 	}
623 
624 	void io_matePCM_w(ref pxtnDescriptor p_doc) const @system {
625 		const pxtnPulse_PCM* p_pcm = &_voices[0].p_pcm;
626 		const(pxtnVOICEUNIT)* p_vc = &_voices[0];
627 		_MATERIALSTRUCT_PCM pcm;
628 
629 		pcm.sps = cast(uint) p_pcm.get_sps();
630 		pcm.bps = cast(ushort) p_pcm.get_bps();
631 		pcm.ch = cast(ushort) p_pcm.get_ch();
632 		pcm.data_size = cast(uint) p_pcm.get_buf_size();
633 		pcm.x3x_unit_no = cast(ushort) 0;
634 		pcm.tuning = p_vc.tuning;
635 		pcm.voice_flags = p_vc.voice_flags;
636 		pcm.basic_key = cast(ushort) p_vc.basic_key;
637 
638 		uint size = cast(uint)(_MATERIALSTRUCT_PCM.sizeof + pcm.data_size);
639 		p_doc.w_asfile(size);
640 		p_doc.w_asfile(pcm);
641 		p_doc.w_asfile(p_pcm.get_p_buf());
642 	}
643 
644 	void io_matePCM_r(ref pxtnDescriptor p_doc) @system {
645 		_MATERIALSTRUCT_PCM pcm;
646 		int size = 0;
647 
648 		p_doc.r(size);
649 		p_doc.r(pcm);
650 
651 		if ((cast(int) pcm.voice_flags) & PTV_VOICEFLAG_UNCOVERED) {
652 			throw new PxtoneException("fmt unknown");
653 		}
654 
655 		Voice_Allocate(1);
656 		scope(failure) {
657 			Voice_Release();
658 		}
659 
660 		{
661 			pxtnVOICEUNIT* p_vc = &_voices[0];
662 
663 			p_vc.type = pxtnVOICETYPE.Sampling;
664 
665 			p_vc.p_pcm.Create(pcm.ch, pcm.sps, pcm.bps, pcm.data_size / (pcm.bps / 8 * pcm.ch));
666 			p_doc.r(p_vc.p_pcm.get_p_buf()[0 .. pcm.data_size]);
667 			_type = pxtnWOICETYPE.PCM;
668 
669 			p_vc.voice_flags = pcm.voice_flags;
670 			p_vc.basic_key = pcm.basic_key;
671 			p_vc.tuning = pcm.tuning;
672 			_x3x_basic_key = pcm.basic_key;
673 			_x3x_tuning = 0;
674 		}
675 	}
676 
677 	void io_matePTN_w(ref pxtnDescriptor p_doc) const @system {
678 		_MATERIALSTRUCT_PTN ptn;
679 		const(pxtnVOICEUNIT)* p_vc;
680 		int size = 0;
681 
682 		// ptv -------------------------
683 		ptn.x3x_unit_no = cast(ushort) 0;
684 
685 		p_vc = &_voices[0];
686 		ptn.tuning = p_vc.tuning;
687 		ptn.voice_flags = p_vc.voice_flags;
688 		ptn.basic_key = cast(ushort) p_vc.basic_key;
689 		ptn.rrr = 1;
690 
691 		// pre
692 		p_doc.w_asfile(size);
693 		p_doc.w_asfile(ptn);
694 		size += _MATERIALSTRUCT_PTN.sizeof;
695 		p_vc.p_ptn.write(p_doc, &size);
696 		p_doc.seek(pxtnSEEK.cur, cast(int)(-size - int.sizeof));
697 		p_doc.w_asfile(size);
698 		p_doc.seek(pxtnSEEK.cur, size);
699 	}
700 
701 	void io_matePTN_r(ref pxtnDescriptor p_doc) @system {
702 		_MATERIALSTRUCT_PTN ptn;
703 		int size = 0;
704 
705 		scope(failure) {
706 			Voice_Release();
707 		}
708 		p_doc.r(size);
709 		p_doc.r(ptn);
710 
711 		if (ptn.rrr > 1) {
712 			throw new PxtoneException("fmt unknown");
713 		} else if (ptn.rrr < 0) {
714 			throw new PxtoneException("fmt unknown");
715 		}
716 
717 		Voice_Allocate(1);
718 
719 		{
720 			pxtnVOICEUNIT* p_vc = &_voices[0];
721 
722 			p_vc.type = pxtnVOICETYPE.Noise;
723 			p_vc.p_ptn.read(p_doc);
724 			_type = pxtnWOICETYPE.PTN;
725 			p_vc.voice_flags = ptn.voice_flags;
726 			p_vc.basic_key = ptn.basic_key;
727 			p_vc.tuning = ptn.tuning;
728 		}
729 
730 		_x3x_basic_key = ptn.basic_key;
731 		_x3x_tuning = 0;
732 	}
733 
734 	bool io_matePTV_w(ref pxtnDescriptor p_doc) const @system {
735 		_MATERIALSTRUCT_PTV ptv;
736 		int head_size = _MATERIALSTRUCT_PTV.sizeof + int.sizeof;
737 		int size = 0;
738 
739 		// ptv -------------------------
740 		ptv.x3x_unit_no = cast(ushort) 0;
741 		ptv.x3x_tuning = 0; //1.0f;//p_w.tuning;
742 		ptv.size = 0;
743 
744 		// pre write
745 		p_doc.w_asfile(size);
746 		p_doc.w_asfile(ptv);
747 		if (!PTV_Write(p_doc, &ptv.size)) {
748 			return false;
749 		}
750 
751 		p_doc.seek(pxtnSEEK.cur, -(ptv.size + head_size));
752 
753 		size = cast(int)(ptv.size + _MATERIALSTRUCT_PTV.sizeof);
754 		p_doc.w_asfile(size);
755 		p_doc.w_asfile(ptv);
756 
757 		p_doc.seek(pxtnSEEK.cur, ptv.size);
758 
759 		return true;
760 	}
761 
762 	void io_matePTV_r(ref pxtnDescriptor p_doc) @system {
763 		_MATERIALSTRUCT_PTV ptv;
764 		int size = 0;
765 
766 		p_doc.r(size);
767 		p_doc.r(ptv);
768 		if (ptv.rrr) {
769 			throw new PxtoneException("fmt unknown");
770 		}
771 		PTV_Read(p_doc);
772 
773 		if (ptv.x3x_tuning != 1.0) {
774 			_x3x_tuning = ptv.x3x_tuning;
775 		} else {
776 			_x3x_tuning = 0;
777 		}
778 	}
779 
780 	version (pxINCLUDE_OGGVORBIS) {
781 		bool io_mateOGGV_w(ref pxtnDescriptor p_doc) const @system {
782 			if (!_voices) {
783 				return false;
784 			}
785 
786 			_MATERIALSTRUCT_OGGV mate;
787 			const(pxtnVOICEUNIT)* p_vc = &_voices[0];
788 
789 			int oggv_size = p_vc.p_oggv.GetSize();
790 
791 			mate.tuning = p_vc.tuning;
792 			mate.voice_flags = p_vc.voice_flags;
793 			mate.basic_key = cast(ushort) p_vc.basic_key;
794 
795 			uint size = cast(uint)(_MATERIALSTRUCT_OGGV.sizeof + oggv_size);
796 			p_doc.w_asfile(size);
797 			p_doc.w_asfile(mate);
798 			p_vc.p_oggv.pxtn_write(p_doc);
799 
800 			return true;
801 		}
802 
803 		void io_mateOGGV_r(ref pxtnDescriptor p_doc) @system {
804 			_MATERIALSTRUCT_OGGV mate;
805 			int size = 0;
806 
807 			p_doc.r(size);
808 			p_doc.r(mate);
809 
810 			if ((cast(int) mate.voice_flags) & PTV_VOICEFLAG_UNCOVERED) {
811 				throw new PxtoneException("fmt unknown");
812 			}
813 
814 			Voice_Allocate(1);
815 			scope(failure) {
816 				Voice_Release();
817 			}
818 
819 			{
820 				pxtnVOICEUNIT* p_vc = &_voices[0];
821 				p_vc.type = pxtnVOICETYPE.OggVorbis;
822 
823 				p_vc.p_oggv.pxtn_read(p_doc);
824 
825 				p_vc.voice_flags = mate.voice_flags;
826 				p_vc.basic_key = mate.basic_key;
827 				p_vc.tuning = mate.tuning;
828 			}
829 
830 			_x3x_basic_key = mate.basic_key;
831 			_x3x_tuning = 0;
832 			_type = pxtnWOICETYPE.OGGV;
833 		}
834 	}
835 
836 	void Tone_Ready_sample(const pxtnPulse_NoiseBuilder ptn_bldr) @system {
837 		pxtnVOICEINSTANCE* p_vi = null;
838 		pxtnVOICEUNIT* p_vc = null;
839 		pxtnPulse_PCM pcm_work;
840 
841 		int ch = 2;
842 		int sps = 44100;
843 		int bps = 16;
844 
845 		for (int v = 0; v < _voice_num; v++) {
846 			p_vi = &_voinsts[v];
847 			p_vi.p_smp_w = null;
848 			p_vi.smp_head_w = 0;
849 			p_vi.smp_body_w = 0;
850 			p_vi.smp_tail_w = 0;
851 		}
852 		scope (failure) {
853 			for (int v = 0; v < _voice_num; v++) {
854 				p_vi = &_voinsts[v];
855 				p_vi.p_smp_w = null;
856 				p_vi.smp_head_w = 0;
857 				p_vi.smp_body_w = 0;
858 				p_vi.smp_tail_w = 0;
859 			}
860 		}
861 
862 		for (int v = 0; v < _voice_num; v++) {
863 			p_vi = &_voinsts[v];
864 			p_vc = &_voices[v];
865 
866 			switch (p_vc.type) {
867 			case pxtnVOICETYPE.OggVorbis:
868 
869 				version (pxINCLUDE_OGGVORBIS) {
870 					p_vc.p_oggv.Decode(&pcm_work);
871 					pcm_work.Convert(ch, sps, bps);
872 					p_vi.smp_head_w = pcm_work.get_smp_head();
873 					p_vi.smp_body_w = pcm_work.get_smp_body();
874 					p_vi.smp_tail_w = pcm_work.get_smp_tail();
875 					p_vi.p_smp_w = cast(ubyte[]) pcm_work.Devolve_SamplingBuffer();
876 					break;
877 				} else {
878 					throw new PxtoneException("Ogg Vorbis support is required");
879 				}
880 
881 			case pxtnVOICETYPE.Sampling:
882 
883 				p_vc.p_pcm.Copy(pcm_work);
884 				pcm_work.Convert(ch, sps, bps);
885 				p_vi.smp_head_w = pcm_work.get_smp_head();
886 				p_vi.smp_body_w = pcm_work.get_smp_body();
887 				p_vi.smp_tail_w = pcm_work.get_smp_tail();
888 				p_vi.p_smp_w = cast(ubyte[]) pcm_work.Devolve_SamplingBuffer();
889 				break;
890 
891 			case pxtnVOICETYPE.Overtone:
892 			case pxtnVOICETYPE.Coodinate: {
893 					p_vi.smp_body_w = 400;
894 					int size = p_vi.smp_body_w * ch * bps / 8;
895 					p_vi.p_smp_w = new ubyte[](size);
896 					p_vi.p_smp_w[0 .. size] = 0x00;
897 					_UpdateWavePTV(p_vc, p_vi, ch, sps, bps);
898 					break;
899 				}
900 
901 			case pxtnVOICETYPE.Noise: {
902 					pxtnPulse_PCM p_pcm = ptn_bldr.BuildNoise(p_vc.p_ptn, ch, sps, bps);
903 					p_vi.p_smp_w = cast(ubyte[]) p_pcm.Devolve_SamplingBuffer();
904 					p_vi.smp_body_w = p_vc.p_ptn.get_smp_num_44k();
905 					break;
906 				}
907 			default:
908 				break;
909 			}
910 		}
911 	}
912 
913 	void Tone_Ready_envelope(int sps) @system {
914 		int e = 0;
915 		pxtnPOINT[] p_point = null;
916 
917 		scope(failure) {
918 			for (int v = 0; v < _voice_num; v++) {
919 				_voinsts[v].p_env = null;
920 			}
921 		}
922 		for (int v = 0; v < _voice_num; v++) {
923 			pxtnVOICEINSTANCE* p_vi = &_voinsts[v];
924 			pxtnVOICEUNIT* p_vc = &_voices[v];
925 			pxtnVOICEENVELOPE* p_enve = &p_vc.envelope;
926 			int size = 0;
927 
928 			p_vi.p_env = null;
929 
930 			if (p_enve.head_num) {
931 				for (e = 0; e < p_enve.head_num; e++) {
932 					size += p_enve.points[e].x;
933 				}
934 				p_vi.env_size = cast(int)(cast(double) size * sps / p_enve.fps);
935 				if (!p_vi.env_size) {
936 					p_vi.env_size = 1;
937 				}
938 
939 				p_vi.p_env = new ubyte[](p_vi.env_size);
940 				p_point = new pxtnPOINT[](p_enve.head_num);
941 
942 				// convert points.
943 				int offset = 0;
944 				int head_num = 0;
945 				for (e = 0; e < p_enve.head_num; e++) {
946 					if (!e || p_enve.points[e].x || p_enve.points[e].y) {
947 						offset += cast(int)(cast(double) p_enve.points[e].x * sps / p_enve.fps);
948 						p_point[e].x = offset;
949 						p_point[e].y = p_enve.points[e].y;
950 						head_num++;
951 					}
952 				}
953 
954 				pxtnPOINT start;
955 				e = start.x = start.y = 0;
956 				for (int s = 0; s < p_vi.env_size; s++) {
957 					while (e < head_num && s >= p_point[e].x) {
958 						start.x = p_point[e].x;
959 						start.y = p_point[e].y;
960 						e++;
961 					}
962 
963 					if (e < head_num) {
964 						p_vi.p_env[s] = cast(ubyte)(start.y + (p_point[e].y - start.y) * (s - start.x) / (p_point[e].x - start.x));
965 					} else {
966 						p_vi.p_env[s] = cast(ubyte) start.y;
967 					}
968 				}
969 
970 				p_point = null;
971 			}
972 
973 			if (p_enve.tail_num) {
974 				p_vi.env_release = cast(int)(cast(double) p_enve.points[p_enve.head_num].x * sps / p_enve.fps);
975 			} else {
976 				p_vi.env_release = 0;
977 			}
978 		}
979 		p_point = null;
980 	}
981 
982 	void Tone_Ready(const pxtnPulse_NoiseBuilder ptn_bldr, int sps) @system {
983 		Tone_Ready_sample(ptn_bldr);
984 		Tone_Ready_envelope(sps);
985 	}
986 }