1 /*
2  * Copyright (C) 2015-2018, by Laszlo Szeremi under the Boost license.
3  *
4  * Pixel Perfect Engine, audio.pcm32 module
5  */
6 
7 module PixelPerfectEngine.audio.pcm32;
8 
9 import PixelPerfectEngine.audio.common;
10 import PixelPerfectEngine.audio.firFilter;
11 import PixelPerfectEngine.audio.envGen;
12 import PixelPerfectEngine.audio.lfo;
13 
14 import libPCM.codecs;
15 import libPCM.common;
16 
17 import std.bitmanip;
18 
19 /**
20  * Sampling synthesizer implementation. Has per channel FIR (1024 stage) and IIR (low-pass) filters, and four stereo outputs.
21  */
22 public class PCM32 : AbstractPPEFX{
23 	/**
24 	 * Defines the source of certain modifier values
25 	 */
26 	public enum ControlSource : ubyte{
27 		NULL			=	0,
28 		Pitch			=	1,
29 		Velocity		=	2,
30 		ExpressiveVal	=	3,
31 	}
32 	/**
33 	 * Per-octave dampening
34 	 */
35 	public enum Dampening : ubyte{
36 		DB00			=	0,
37 		DB15			=	1,
38 		DB30			=	2,
39 		DB60			=	3,
40 	}
41 	protected struct Channel{
42 		short delegate(ubyte* inputStream, DecoderWorkpad* workpad) codec;
43 		uint loopfrom, loopto;	///describe a loop cycle between the two values
44 		uint stepping;			///describes how much the sample should go forward, 65536 equals a whole step
45 		ulong forward;			///current position
46 		short[] intBuff;		///buffer for FIR-filters
47 		CodecType codecType;
48 		DecoderWorkpad workpad, secWorkpad;
49 		EnvelopeGenerator envGenA;
50 		EnvelopeGenerator envGenB;
51 		FiniteImpulseResponseFilter!(1024)[2] firFilter;
52 		LowFreqOsc!(256) lfo;
53 	}
54 	float sampleRate;
55 	int frameLength, bufferLength;
56 	version(X86){
57 		/**
58 		 * Contains IIR related registers in order for use in SSE2 applications
59 		 */
60 		protected struct IIRFilterBuff{
61 			float[4] b0a0;
62 			//float[4] x_n;
63 			float[4] b1a0;
64 			float[4] x_n_minus1;
65 			float[4] b2a0;
66 			float[4] x_n_minus2;
67 			float[4] a1a0;
68 			float[4] y_n_minus1;
69 			float[4] a2a0;
70 			float[4] y_n_minus2;
71 			//float[4] y_n;
72 		}
73 		IIRFilterBuff[8] iirFilters;
74 		void* iirFiltersPtr;
75 	}else{
76 		float[32] x_n_minus1, x_n_minus2, y_n_minus1, y_n_minus2, b0a0, b1a0, b2a0, a1a0, a2a0;
77 	}
78 	float[32][] y_n, x_n;
79 	
80 
81 	protected @nogc void calculateIIR(){
82 		version(X86){
83 			float* y_nptr = cast(float*)y_n.ptr;
84 			float* x_nptr = cast(float*)x_n.ptr;
85 			for(int i = frameLength * bufferLength ; i >= 0 ; i--){
86 				asm @nogc{
87 					mov		ECX, 8;
88 					//mov		ESI, iirFiltersPtr[EBP];
89 					mov		EDI, y_nptr;
90 					mov		EBX, x_nptr;
91 				iirLoop:
92 					movaps	XMM0, [ESI];//load b0/a0
93 					movaps	XMM1, [EBX];//load x_n
94 					mulps	XMM0, XMM1;	//(b0/a0) * x_n
95 					add		ESI, 16;	//offset ESI to b1a0
96 					movaps	XMM2, [ESI];//load b1a0
97 					add		ESI, 16;	//offset ESI to x_n_minus1
98 					movaps	XMM3, [ESI];//load x_n_minus1
99 					movaps	[ESI], XMM1;//store current x_n as x_n_minus1
100 					mulps	XMM2, XMM3;	//(b1/a0) * x_n_minus1
101 					addps	XMM0, XMM2;	//(b0/a0) * x_n + (b1/a0) * x_n_minus1
102 					add		ESI, 16;	//offset ESI to b2a0
103 					movaps	XMM2, [ESI];//load b2a0
104 					add		ESI, 16;	//offset ESI to x_n_minus2
105 					movaps	XMM4, [ESI];//load x_n_minus2
106 					movaps	[ESI], XMM3;//store current x_n_minus1 as x_n_minus2
107 					mulps	XMM2, XMM4;	//(b2/a0) * x_n_minus2
108 					addps	XMM0, XMM2;	//(b0/a0) * x_n + (b1/a0) * x_n_minus1 + (b2/a0) * x_n_minus2
109 					add		ESI, 16;	//offset ESI to a1a0
110 					movaps	XMM1, [ESI];//load a1a0
111 					add		ESI, 16;	//offset ESI to y_n_minus1
112 					movaps	XMM2, [ESI];//load y_n_minus1
113 					mulps	XMM1, XMM2;	//(a1/a0) * y_n_minus1
114 					subps	XMM0, XMM1;	//(b0/a0) * x_n + (b1/a0) * x_n_minus1 + (b2/a0) * x_n_minus2 - (a1/a0) * y_n_minus1
115 					add		ESI, 16;	//offset ESI to a2a0
116 					movaps	XMM1, [ESI];//load a2a0
117 					add		ESI, 16;	//offset ESI to y_n_minus2
118 					movaps	XMM3, [ESI];//load y_n_minus2
119 					movaps	[ESI], XMM2;//store y_n_minus1 as new y_n_minus2
120 					mulps	XMM1, XMM3;	//(a2/a0) * y_n_minus2
121 					subps	XMM0, XMM1;	//(b0/a0) * x_n + (b1/a0) * x_n_minus1 + (b2/a0) * x_n_minus2 - (a1/a0) * y_n_minus1 - (a2/a0) * y_n_minus2
122 					sub		ESI, 48;	//set back pointer to  y_n_minus1
123 					movaps	[ESI], XMM0;//store y_n as y_n_minus1
124 					movaps	[EDI], XMM0;//store y_n as output
125 					add		EDI, 16;
126 					add		ESI, 48;
127 					dec		ECX;
128 					cmp		ECX, 0;
129 					jne		iirLoop;
130 				}
131 				x_nptr += 32;
132 				y_nptr += 32;
133 			}
134 		}
135 	}
136 }
137