1 /*
2  * Copyright (C) 2018-2020 Rerrah
3  *
4  * Permission is hereby granted, free of charge, to any person
5  * obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without
7  * restriction, including without limitation the rights to use,
8  * copy, modify, merge, publish, distribute, sublicense, and/or sell
9  * copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following
11  * conditions:
12  *
13  * The above copyright notice and this permission notice shall be
14  * included in all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23  * OTHER DEALINGS IN THE SOFTWARE.
24  */
25 
26 #include "export_container.hpp"
27 #include "export_handler.hpp"
28 #include <algorithm>
29 
30 namespace chip
31 {
~ExportContainerInterface()32 	ExportContainerInterface::~ExportContainerInterface() {}
33 
34 	//******************************//
WavExportContainer()35 	WavExportContainer::WavExportContainer()
36 	{
37 	}
38 
recordRegisterChange(uint32_t offset,uint8_t value)39 	void WavExportContainer::recordRegisterChange(uint32_t offset, uint8_t value)
40 	{
41 		(void)offset;
42 		(void)value;
43 	}
44 
recordStream(int16_t * stream,size_t nSamples)45 	void WavExportContainer::recordStream(int16_t* stream, size_t nSamples)
46 	{
47 		std::copy(stream, stream + (nSamples << 1), std::back_inserter(samples_));
48 	}
49 
empty() const50 	bool WavExportContainer::empty() const
51 	{
52 		return samples_.empty();
53 	}
54 
clear()55 	void WavExportContainer::clear()
56 	{
57 		samples_.clear();
58 	}
59 
getStream() const60 	std::vector<int16_t> WavExportContainer::getStream() const
61 	{
62 		return samples_;
63 	}
64 
65 	//******************************//
VgmExportContainer(int target,uint32_t intrRate)66 	VgmExportContainer::VgmExportContainer(int target, uint32_t intrRate)
67 		: target_(target),
68 		  lastWait_(0),
69 		  totalSampCnt_(0),
70 		  intrRate_(intrRate),
71 		  isSetLoop_(false),
72 		  loopPoint_(0)
73 	{
74 	}
75 
recordRegisterChange(uint32_t offset,uint8_t value)76 	void VgmExportContainer::recordRegisterChange(uint32_t offset, uint8_t value)
77 	{
78 		if (lastWait_) setWait();
79 
80 		const int fm = target_ & Export_FmMask;
81 		const int ssg = target_ & Export_SsgMask;
82 
83 		const uint8_t cmdSsg =
84 				(ssg != Export_InternalSsg) ? 0xa0
85 											: (fm == Export_YM2608) ? 0x56
86 																	: (fm == Export_YM2203) ? 0x55
87 																							: 0x00;
88 		const uint8_t cmdFmPortA =
89 				(fm == Export_YM2608) ? 0x56
90 									  : (fm == Export_YM2612) ? 0x52
91 															  : (fm == Export_YM2203) ? 0x55
92 																					  : 0x00;
93 		const uint8_t cmdFmPortB =
94 				(fm == Export_YM2608) ? 0x57
95 									  : (fm == Export_YM2612) ? 0x53
96 															  : 0x00;
97 
98 		if (cmdSsg && offset < 0x10) {
99 			buf_.push_back(cmdSsg);
100 			buf_.push_back(offset);
101 			buf_.push_back(value);
102 		}
103 		else if (cmdFmPortA && (offset & 0x100) == 0) {
104 			bool compatible = true;
105 
106 			if (offset == 0x28) { // Key register
107 				if (fm == Export_YM2203 && (value & 7) >= 3)
108 					compatible = false;
109 			}
110 			else if (offset == 0x29) // Mode register
111 				compatible = fm == Export_YM2608;
112 			else if ((offset & 0xf0) == 0x10) // Rhythm section
113 				compatible = fm == Export_YM2608;
114 
115 			if (compatible) {
116 				buf_.push_back(cmdFmPortA);
117 				buf_.push_back(offset & 0xff);
118 				buf_.push_back(value);
119 			}
120 		}
121 		else if (cmdFmPortB && (offset & 0x100) != 0) {
122 			bool compatible = true;
123 
124 			if (offset < 0x10) // ADPCM section
125 				compatible = fm == Export_YM2608;
126 
127 			if (compatible) {
128 				buf_.push_back(cmdFmPortB);
129 				buf_.push_back(offset & 0xff);
130 				buf_.push_back(value);
131 			}
132 		}
133 	}
134 
recordStream(int16_t * stream,size_t nSamples)135 	void VgmExportContainer::recordStream(int16_t* stream, size_t nSamples)
136 	{
137 		(void)stream;
138 		lastWait_ += nSamples;
139 		totalSampCnt_ += nSamples;
140 	}
141 
clear()142 	void VgmExportContainer::clear()
143 	{
144 		buf_.clear();
145 		lastWait_ = 0;
146 		totalSampCnt_ = 0;
147 		isSetLoop_ = false;
148 		loopPoint_ = 0;
149 	}
150 
empty() const151 	bool VgmExportContainer::empty() const
152 	{
153 		return (buf_.empty() || lastWait_ != 0);
154 	}
155 
getData()156 	std::vector<uint8_t> VgmExportContainer::getData()
157 	{
158 		if (lastWait_) setWait();
159 		return buf_;
160 	}
161 
getSampleLength() const162 	size_t VgmExportContainer::getSampleLength() const
163 	{
164 		return totalSampCnt_;
165 	}
166 
setLoopPoint()167 	size_t VgmExportContainer::setLoopPoint()
168 	{
169 		if (lastWait_) setWait();
170 		isSetLoop_ = true;
171 		return loopPoint_;
172 	}
173 
forceMoveLoopPoint()174 	size_t VgmExportContainer::forceMoveLoopPoint()
175 	{
176 		loopPoint_ = buf_.size();
177 		return loopPoint_;
178 	}
179 
setDataBlock(std::vector<uint8_t> data)180 	void VgmExportContainer::setDataBlock(std::vector<uint8_t> data)
181 	{
182 		buf_.push_back(0x67);
183 		buf_.push_back(0x66);
184 		buf_.push_back(0x81);
185 		size_t blockSize = data.size() + 8;
186 		buf_.push_back(blockSize & 0xff);
187 		buf_.push_back((blockSize >> 8) & 0xff);
188 		buf_.push_back((blockSize >> 16) & 0xff);
189 		buf_.push_back(blockSize >> 24);
190 		buf_.push_back(data.size() & 0xff);
191 		buf_.push_back((data.size() >> 8) & 0xff);
192 		buf_.push_back((data.size() >> 16) & 0xff);
193 		buf_.push_back(data.size() >> 24);
194 		buf_.resize(buf_.size() + 4);	// Start address is 0
195 		std::copy(data.begin(), data.end(), std::back_inserter(buf_));
196 	}
197 
setWait()198 	void VgmExportContainer::setWait()
199 	{
200 		while (lastWait_) {
201 			uint32_t sub;
202 
203 			if (intrRate_ == 50) {
204 				if (lastWait_ > 65535) {
205 					uint32_t tmp = lastWait_ - 65535;
206 					if (tmp <= 882) {
207 						//65535 - (882 - tmp)
208 						sub = 64653 + tmp;
209 					}
210 					else if (tmp <= 1764) {
211 						//65535 - (1764 - tmp)
212 						sub = 63771 + tmp;
213 					}
214 					else if (tmp <= 2646) {
215 						//65535 - (2646 - tmp)
216 						sub = 62889 + tmp;
217 					}
218 					else {
219 						sub = 65535;
220 					}
221 					buf_.push_back(0x61);
222 					buf_.push_back(sub & 0x00ff);
223 					buf_.push_back(sub >> 8);
224 				}
225 				else {
226 					if (lastWait_ <= 16) {
227 						buf_.push_back(0x70 | (lastWait_ - 1));
228 					}
229 					else if (lastWait_ > 2646) {
230 						buf_.push_back(0x61);
231 						buf_.push_back(lastWait_ & 0x00ff);
232 						buf_.push_back(lastWait_ >> 8);
233 					}
234 					else if (lastWait_ == 2646) {
235 						buf_.push_back(0x63);
236 						buf_.push_back(0x63);
237 						buf_.push_back(0x63);
238 					}
239 					else if (1764 <= lastWait_ && lastWait_ <= 1780) {
240 						uint32_t tmp = lastWait_ - 1764;
241 						buf_.push_back(0x63);
242 						buf_.push_back(0x63);
243 						if (tmp) buf_.push_back(0x70 | (tmp - 1));
244 					}
245 					else if (882 <= lastWait_ && lastWait_ <= 898) {
246 						uint32_t tmp = lastWait_ - 882;
247 						buf_.push_back(0x63);
248 						if (tmp) buf_.push_back(0x70 | (tmp - 1));
249 					}
250 					else {
251 						buf_.push_back(0x61);
252 						buf_.push_back(lastWait_ & 0x00ff);
253 						buf_.push_back(lastWait_ >> 8);
254 					}
255 					sub = lastWait_;
256 				}
257 			}
258 			else if (intrRate_ == 60) {
259 				if (lastWait_ > 65535) {
260 					uint32_t tmp = lastWait_ - 65535;
261 					if (tmp <= 735) {
262 						//65535 - (735 - tmp)
263 						sub = 64800 + tmp;
264 					}
265 					else if (tmp <= 1470) {
266 						//65535 - (1470 - tmp)
267 						sub = 64065 + tmp;
268 					}
269 					else if (tmp <= 2205) {
270 						//65535 - (2205 - tmp)
271 						sub = 63330 + tmp;
272 					}
273 					else {
274 						sub = 65535;
275 					}
276 					buf_.push_back(0x61);
277 					buf_.push_back(sub & 0x00ff);
278 					buf_.push_back(sub >> 8);
279 				}
280 				else {
281 					if (lastWait_ <= 16) {
282 						buf_.push_back(0x70 | (lastWait_ - 1));
283 					}
284 					else if (lastWait_ > 2205) {
285 						buf_.push_back(0x61);
286 						buf_.push_back(lastWait_ & 0x00ff);
287 						buf_.push_back(lastWait_ >> 8);
288 					}
289 					else if (lastWait_ == 2205) {
290 						buf_.push_back(0x62);
291 						buf_.push_back(0x62);
292 						buf_.push_back(0x62);
293 					}
294 					else if (1470 <= lastWait_ && lastWait_ <= 1486) {
295 						uint32_t tmp = lastWait_ - 1470;
296 						buf_.push_back(0x62);
297 						buf_.push_back(0x62);
298 						if (tmp) buf_.push_back(0x70 | (tmp - 1));
299 					}
300 					else if (735 <= lastWait_ && lastWait_ <= 751) {
301 						uint32_t tmp = lastWait_ - 735;
302 						buf_.push_back(0x62);
303 						if (tmp) buf_.push_back(0x70 | (tmp - 1));
304 					}
305 					else {
306 						buf_.push_back(0x61);
307 						buf_.push_back(lastWait_ & 0x00ff);
308 						buf_.push_back(lastWait_ >> 8);
309 					}
310 					sub = lastWait_;
311 				}
312 			}
313 			else {
314 				if (lastWait_ > 65535) {
315 					sub = 65535;
316 					buf_.push_back(0x61);
317 					buf_.push_back(sub & 0x00ff);
318 					buf_.push_back(sub >> 8);
319 				}
320 				else {
321 					buf_.push_back(0x61);
322 					buf_.push_back(lastWait_ & 0x00ff);
323 					buf_.push_back(lastWait_ >> 8);
324 				}
325 				sub = lastWait_;
326 			}
327 
328 			lastWait_ -= sub;
329 		}
330 
331 		if (!isSetLoop_) loopPoint_ = buf_.size();
332 	}
333 
334 	//******************************//
S98ExportContainer(int target)335 	S98ExportContainer::S98ExportContainer(int target)
336 		: target_(target),
337 		  lastWait_(0),
338 		  totalSampCnt_(0),
339 		  isSetLoop_(false),
340 		  loopPoint_(0)
341 	{
342 	}
343 
recordRegisterChange(uint32_t offset,uint8_t value)344 	void S98ExportContainer::recordRegisterChange(uint32_t offset, uint8_t value)
345 	{
346 		if (lastWait_) setWait();
347 
348 		const int fm = target_ & Export_FmMask;
349 		const int ssg = target_ & Export_SsgMask;
350 
351 		const uint8_t cmdSsg =
352 				(ssg != Export_InternalSsg) ? (fm == Export_NoneFm) ? 0x01 : 0x02
353 																	: (fm == Export_YM2608) ? 0x00
354 																							: (fm == Export_YM2203) ? 0x00
355 																													: 0xff;
356 		const uint8_t cmdFmPortA =
357 				(fm != Export_NoneFm) ? 0x00 : 0xff;
358 		const uint8_t cmdFmPortB =
359 				(fm == Export_YM2608 || fm == Export_YM2612) ? 0x01 : 0xff;
360 
361 		if (cmdSsg != 0xff && offset < 0x10) {
362 			buf_.push_back(cmdSsg);
363 			buf_.push_back(offset);
364 			buf_.push_back(value);
365 		}
366 		else if (cmdFmPortA != 0xff && (offset & 0x100) == 0) {
367 			bool compatible = true;
368 
369 			if (offset == 0x28) { // Key register
370 				if (fm == Export_YM2203 && (value & 7) >= 3)
371 					compatible = false;
372 			}
373 			else if (offset == 0x29) // Mode register
374 				compatible = fm == Export_YM2608;
375 			else if ((offset & 0xf0) == 0x10) // Rhythm section
376 				compatible = fm == Export_YM2608;
377 
378 			if (compatible) {
379 				buf_.push_back(cmdFmPortA);
380 				buf_.push_back(offset & 0xff);
381 				buf_.push_back(value);
382 			}
383 		}
384 		else if (cmdFmPortB != 0xff && (offset & 0x100) != 0) {
385 			bool compatible = true;
386 
387 			if (offset < 0x10) // ADPCM section
388 				compatible = fm == Export_YM2608;
389 
390 			if (compatible) {
391 				buf_.push_back(cmdFmPortB);
392 				buf_.push_back(offset & 0xff);
393 				buf_.push_back(value);
394 			}
395 		}
396 	}
397 
recordStream(int16_t * stream,size_t nSamples)398 	void S98ExportContainer::recordStream(int16_t* stream, size_t nSamples)
399 	{
400 		(void)stream;
401 		lastWait_ += nSamples;
402 		totalSampCnt_ += nSamples;
403 	}
404 
clear()405 	void S98ExportContainer::clear()
406 	{
407 		buf_.clear();
408 		lastWait_ = 0;
409 		totalSampCnt_ = 0;
410 		isSetLoop_ = false;
411 		loopPoint_ = 0;
412 	}
413 
empty() const414 	bool S98ExportContainer::empty() const
415 	{
416 		return (buf_.empty() || lastWait_ != 0);
417 	}
418 
getData()419 	std::vector<uint8_t> S98ExportContainer::getData()
420 	{
421 		if (lastWait_) setWait();
422 		return buf_;
423 	}
424 
getSampleLength() const425 	size_t S98ExportContainer::getSampleLength() const
426 	{
427 		return totalSampCnt_;
428 	}
429 
setLoopPoint()430 	size_t S98ExportContainer::setLoopPoint()
431 	{
432 		if (lastWait_) setWait();
433 		isSetLoop_ = true;
434 		return loopPoint_;
435 	}
436 
forceMoveLoopPoint()437 	size_t S98ExportContainer::forceMoveLoopPoint()
438 	{
439 		loopPoint_ = buf_.size();
440 		return loopPoint_;
441 	}
442 
setWait()443 	void S98ExportContainer::setWait()
444 	{
445 		if (lastWait_ == 1) {
446 			buf_.push_back(0xff);
447 		}
448 		else {
449 			buf_.push_back(0xfe);
450 			lastWait_ -= 2;
451 			do {
452 				uint8_t b = lastWait_ & 0x7f;
453 				lastWait_ >>= 7;
454 				if (lastWait_ > 0) b |= 0x80;
455 				buf_.push_back(b);
456 			} while (lastWait_ > 0);
457 		}
458 		if (!isSetLoop_) loopPoint_ = buf_.size();
459 		lastWait_ = 0;
460 	}
461 }
462