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