1 module test1.sampleman;
2 
3 import pixelperfectengine.concrete.window;
4 import pixelperfectengine.audio.base.types;
5 import pixelperfectengine.audio.base.func;
6 import pixelperfectengine.audio.base.config;
7 import std.math : floor, nearbyint;
8 import std.conv;
9 import test1.app;
10 import test1.editorevents;
11 
12 public class WaveformViewer : WindowElement {
13 	int[] waveform;
14 	public this(string source, Box position) {
15 		this.source = source;
16 		this.position = position;
17 	}
18 	public void setWaveform(const(ubyte)[] src, WaveFormat fmt) {
19 		DecoderWorkpad wp;
20 		switch (fmt.format) {		
21 			case AudioFormat.PCM:
22 				if (fmt.bitsPerSample == 8) {
23 					waveform.length = src.length;
24 					decode8bitPCM(cast(const(ubyte)[])src, waveform, wp);
25 				} else if (fmt.bitsPerSample == 16) {
26 					waveform.length = src.length / 2;
27 					decode16bitPCM(cast(const(short)[])src, waveform, wp);
28 				}
29 				break;
30 			case AudioFormat.ADPCM, AudioFormat.IMA_ADPCM:
31 				waveform.length = src.length * 2;
32 				decode4bitIMAADPCM(ADPCMStream(src.dup, src.length*2), waveform, wp);
33 				break;
34 			case AudioFormat.DIALOGIC_OKI_ADPCM, AudioFormat.OKI_ADPCM:
35 				waveform.length = src.length * 2;
36 				decode4bitDialogicADPCM(ADPCMStream(src.dup, src.length*2), waveform, wp);
37 				break;
38 			case AudioFormat.MULAW:
39 				waveform.length = src.length;
40 				decodeMuLawStream(cast(const(ubyte)[])src, waveform, wp);
41 				break;
42 			case AudioFormat.ALAW:
43 				waveform.length = src.length;
44 				decodeALawStream(cast(const(ubyte)[])src, waveform, wp);
45 				break;
46 			default:
47 				return;
48 		}
49 	}
50 	public override void draw() {
51 		if (parent is null) return;
52 		StyleSheet ss = getStyleSheet();
53 		parent.clearArea(position);
54 		parent.drawBox(position, 24);
55 		if (waveform.length) {
56 			real ratio = cast(real)waveform.length / position.width;
57 			const int divident = ushort.max / position.height() * -1;
58 			{
59 				Point o = Point(0, position.height / 2);
60 				parent.drawLine(position.cornerUL + o, position.cornerUR + o, 23);
61 			}
62 			Point p0 = Point(position.left, (waveform[0] / divident) + position.top + (position.height / 2));
63 			for (int i ; i < position.width - 1 ; i++) {
64 				/+Point p0 = 
65 						Point(i + position.left, 
66 						(waveform[cast(size_t)nearbyint(i * ratio)] / divident) + position.top + (position.height / 2));+/
67 				const Point p1 =
68 						Point(i + position.left + 1, 
69 						(waveform[cast(size_t)nearbyint(i * ratio) + 1] / divident) + position.top + (position.height / 2));
70 				parent.drawLine(p0, p1, 0x1F);
71 				p0 = p1;
72 			}
73 
74 			if (isFocused) {
75 				const int textPadding = ss.drawParameters["horizTextPadding"];
76 				parent.drawBoxPattern(position - textPadding, ss.pattern["blackDottedLine"]);
77 			}
78 		}
79 		if (state == ElementState.Disabled) {
80 			parent.bitBLTPattern(position, ss.getImage("ElementDisabledPtrn"));
81 		}
82 		if (onDraw !is null) {
83 			onDraw();
84 		}
85 	}
86 }
87 
88 public class SampleMan : Window {
89 	ListView listView_sampleList;
90 	Button button_load;
91 	Button button_slice;
92 	Button button_remove;
93 	Label label_info;
94 	Label label_format;
95 	Label label_slmpR;
96 	Label label_len;
97 	WaveformViewer wfv;
98 	
99 	AudioDevKit adk;
100 
101 	WaveFileData[] waveFileData;
102 	string path;
103 
104 	string moduleName;
105 	//TextBox textBox0;
106 	public this(AudioDevKit adk) {
107 		moduleName = adk.selectedModID;
108 		this.adk = adk;
109 
110 		super(Box(0, 0, 520, 322), "Sample manager ["d ~ moduleName.to!dstring ~ "]"d);
111 		listView_sampleList = new ListView(new ListViewHeader(16, [40, 120, 250], ["ID" ,"name" ,"file source"]), null, 
112 				"listView0", Box(5, 20, 335, 185));
113 		button_load = new Button("Load"d, "button0", Box(340, 20, 440, 40));
114 		button_slice = new Button("Slice"d, "button1", Box(340, 45, 440, 65));
115 		button_remove = new Button("Remove"d, "button0", Box(340, 70, 440, 90));
116 		label_info = new Label("Info:"d, "label0", Box(340, 95, 500, 115));
117 		label_format = new Label("Format:"d, "label1", Box(340, 118, 512, 138));
118 		label_slmpR = new Label("Orig. samplerate:"d, "label2", Box(340, 140, 512, 160));
119 		label_len = new Label("Length:"d, "label3", Box(340, 162, 512, 182));
120 		//textBox0 = new TextBox("Placeholder"d, "textBox0", Box(4, 190, 516, 318));
121 		wfv = new WaveformViewer("wfv", Box(4, 190, 516, 318));
122 
123 		addElement(listView_sampleList);
124 		addElement(button_load);
125 		addElement(button_slice);
126 		addElement(button_remove);
127 		addElement(label_info);
128 		addElement(label_format);
129 		addElement(label_slmpR);
130 		addElement(label_len);
131 		addElement(wfv);
132 
133 		button_load.onMouseLClick = &button_load_onClick;
134 		button_slice.onMouseLClick = &button_slice_onClick;
135 		button_remove.onMouseLClick = &button_remove_onClick;
136 		listView_sampleList.onItemSelect = &listView_sampleList_onSelect;
137 		listView_sampleList.onTextInput = &listView_sampleList_onTextEdit;
138 		listView_sampleList.editEnable = true;
139 
140 		refreshSampleList();
141 	}
142 	protected void refreshSampleList() {
143 		waveFileData = adk.mcfg.getWaveFileList(moduleName);
144 		listView_sampleList.clear();
145 		foreach (WaveFileData key; waveFileData) {
146 			listView_sampleList ~= new ListViewItem(16, [to!dstring(key.id), to!dstring(key.name), to!dstring(key.path)], 
147 					[TextInputFieldType.None, TextInputFieldType.ASCIIText, TextInputFieldType.None]);
148 		}
149 		listView_sampleList.refresh();
150 	}
151 	protected void listView_sampleList_onSelect(Event ev) {
152 		if (listView_sampleList.value >= 0) {
153 			import pixelperfectengine.audio.base.modulebase;
154 			ModuleConfig mcfg = adk.mcfg;
155 			AudioModule am = mcfg.getModule(moduleName);
156 			wfv.setWaveform(am.getWaveformData(listView_sampleList.value), am.getWaveformDataFormat(listView_sampleList.value));
157 			wfv.draw();
158 			WaveFormat wf = am.getWaveformDataFormat(listView_sampleList.value);
159 			dstring format;
160 			switch(wf.format) {
161 				case AudioFormat.PCM:
162 					if (wf.bitsPerSample == 8) {
163 						format = "PCM 8Bit U"d;
164 					} else if (wf.bitsPerSample == 16) {
165 						format = "PCM 16Bit S"d;
166 					}
167 					break;
168 				case AudioFormat.ADPCM, AudioFormat.IMA_ADPCM:
169 					format = "IMA ADPCM 4Bit"d;
170 					break;
171 				case AudioFormat.DIALOGIC_OKI_ADPCM, AudioFormat.OKI_ADPCM:
172 					format = "Dialogic ADPCM"d;
173 					break;
174 				case AudioFormat.MULAW:
175 					format = "Mu-Law ADPCM"d;
176 					break;
177 				case AudioFormat.ALAW:
178 					format = "A-Law ADPCM"d;
179 					break;
180 				default:
181 					format = "UNKNOWN";
182 					break;
183 			}
184 			label_format.setText("Format: "d ~ format);
185 			label_slmpR.setText("Orig. samplerate:"d ~ to!dstring(wf.samplerate));
186 			label_len.setText("Length:"d ~ to!dstring(wfv.waveform.length));
187 		}
188 	}
189 	protected void listView_sampleList_onTextEdit(Event ev) {
190 		CellEditEvent cev = cast(CellEditEvent)ev;
191 		if (listView_sampleList.value >= 0) {
192 			WaveFileData wfd = waveFileData[listView_sampleList.value];
193 			adk.eventStack.addToTop(new RenameSample(adk.mcfg, moduleName, wfd.id, to!string(cev.text.toDString)));
194 		}
195 	}
196 	protected void button_load_onClick(Event ev) {
197 		import pixelperfectengine.concrete.dialogs.filedialog;
198 		handler.addWindow(new FileDialog("Add sample"d, "sampleLoad", &onSampleLoad, 
199 				[FileDialog.FileAssociationDescriptor("Wave file", [".wav"]), 
200 				FileDialog.FileAssociationDescriptor("Dialogic ADPCM file", [".vox", ".ad4"])], "./"));
201 	}
202 	protected void onSampleLoad(Event ev) {
203 		import pixelperfectengine.concrete.dialogs.textinputdialog;
204 		FileEvent fev = cast(FileEvent)ev;
205 		path = fev.getFullPath();
206 		handler.addWindow(new TextInputDialog(Box.bySize(0, 0, 500, 120), &onSampleCreate, "sampleID", "Add sample", 
207 				"Sample ID?"));
208 	}
209 	protected void onSampleCreate(Text tx) {
210 		try {
211 			const int sampleID = to!int(tx.toDString);
212 			//check if sample exists
213 			foreach (WaveFileData key; waveFileData) {
214 				if (key.id == sampleID) return;
215 			}
216 			adk.eventStack.addToTop(new AddSampleFile(adk.mcfg, moduleName, sampleID, path));
217 			refreshSampleList();
218 		} catch (Exception e) {
219 
220 		}
221 	}
222 	protected void button_slice_onClick(Event ev) {
223 		handler.addWindow(new SliceDialog(&onSliceCreate));
224 	}
225 	protected void onSliceCreate(int id, int begin, int end) {
226 		if (listView_sampleList.value >= 0) {
227 			const int src = waveFileData[listView_sampleList.value].id;
228 			const int len = end - begin;
229 			adk.eventStack.addToTop(new AddSampleSlice(adk.mcfg, moduleName, id, src, begin, len));
230 			refreshSampleList();
231 		}
232 	}
233 	protected void button_remove_onClick(Event ev) {
234 		if (listView_sampleList.value >= 0) {
235 			const int selID = waveFileData[listView_sampleList.value].id;
236 			adk.eventStack.addToTop(new RemoveSample(adk.mcfg, moduleName, selID));
237 			refreshSampleList();
238 		}
239 	}
240 }
241 
242 public class SliceDialog : Window {
243 	Label label_newID;
244 	Label label_from;
245 	Label label_to;
246 	TextBox textBox_newID;
247 	TextBox textBox_from;
248 	TextBox textBox_to;
249 	Button button_create;
250 	void delegate(int id, int begin, int end) onCreate;
251 	public this(void delegate(int id, int begin, int end) onCreate) {
252 		this.onCreate = onCreate;
253 		super(Box.bySize(0,0,125,120), "Create new slice");
254 		label_newID = new Label("New ID:", "label_newID", Box(5,20,55,40));
255 		label_from = new Label("From:", "label_from", Box(5,45,55,65));
256 		label_to = new Label("To:", "label_to", Box(5,70,55,90));
257 		textBox_newID = new TextBox("", "textBox_newID", Box(55, 20, 120, 40));
258 		textBox_from = new TextBox("", "textBox_from", Box(55, 45, 120, 65));
259 		textBox_to = new TextBox("", "textBox_to", Box(55, 70, 120, 90));
260 		button_create = new Button("Create", "button_create", Box(60, 95, 120, 115));
261 
262 		textBox_newID.setFilter(TextInputFieldType.IntegerP);
263 		textBox_from.setFilter(TextInputFieldType.IntegerP);
264 		textBox_to.setFilter(TextInputFieldType.IntegerP);
265 
266 		addElement(label_newID);
267 		addElement(label_from);
268 		addElement(label_to);
269 		addElement(textBox_newID);
270 		addElement(textBox_from);
271 		addElement(textBox_to);
272 		addElement(button_create);
273 		button_create.onMouseLClick = &button_create_onClick;
274 	}
275 	protected void button_create_onClick(Event ev) {
276 		onCreate(to!int(textBox_newID.getText.toDString), to!int(textBox_from.getText.toDString), 
277 				to!int(textBox_to.getText.toDString));
278 		close();
279 	}
280 }