| ... | ... |
@@ -52,6 +52,9 @@ v1.9.1 - 2014-10-05 - Fixed volume issues that stemmed from how FeOS Sound |
| 52 | 52 |
- Added a clone of DeSmuME's Sound View that only shows up |
| 53 | 53 |
in debug builds, was used to help me identify the above |
| 54 | 54 |
issue to fix it. |
| 55 |
+ v1.10 - 2014-10-13 - Implemented the random, variable, and conditional |
|
| 56 |
+ commands. |
|
| 57 |
+ - Fixed loop counter. |
|
| 55 | 58 |
|
| 56 | 59 |
This is a Winamp plugin to play NCSF files. NCSF is a PSF-style music format that |
| 57 | 60 |
uses SDAT files from Nintendo DS ROMs as it's "program". |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* SSEQ Player - Player structure |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-05-07 |
|
| 4 |
+ * Last modification on 2014-10-13 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Adapted from source code of FeOS Sound System |
| 7 | 7 |
* By fincs |
| ... | ... |
@@ -16,6 +16,7 @@ Player::Player() : prio(0), nTracks(0), tempo(0), tempoCount(0), tempoRate(0), m |
| 16 | 16 |
memset(this->trackIds, 0, sizeof(this->trackIds)); |
| 17 | 17 |
for (size_t i = 0; i < 16; ++i) |
| 18 | 18 |
this->channels[i].chnId = i; |
| 19 |
+ memset(this->variables, -1, sizeof(this->variables)); |
|
| 19 | 20 |
} |
| 20 | 21 |
|
| 21 | 22 |
bool Player::Setup(const SSEQ *sseqToPlay) |
| ... | ... |
@@ -57,6 +58,7 @@ void Player::ClearState() |
| 57 | 58 |
this->tempoCount = 0; |
| 58 | 59 |
this->tempoRate = 0x100; |
| 59 | 60 |
this->masterVol = 0; // this is actually the highest level |
| 61 |
+ memset(this->variables, -1, sizeof(this->variables)); |
|
| 60 | 62 |
} |
| 61 | 63 |
|
| 62 | 64 |
void Player::FreeTracks() |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* SSEQ Player - Player structure |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2014-09-08 |
|
| 4 |
+ * Last modification on 2014-10-13 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Adapted from source code of FeOS Sound System |
| 7 | 7 |
* By fincs |
| ... | ... |
@@ -26,6 +26,7 @@ struct Player |
| 26 | 26 |
uint8_t trackIds[FSS_TRACKCOUNT]; |
| 27 | 27 |
Track tracks[FSS_MAXTRACKS]; |
| 28 | 28 |
Channel channels[16]; |
| 29 |
+ int16_t variables[32]; |
|
| 29 | 30 |
|
| 30 | 31 |
uint32_t sampleRate; |
| 31 | 32 |
Interpolation interpolation; |
| ... | ... |
@@ -1,13 +1,15 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* SSEQ Player - Track structure |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2014-10-05 |
|
| 4 |
+ * Last modification on 2014-10-13 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Adapted from source code of FeOS Sound System |
| 7 | 7 |
* By fincs |
| 8 | 8 |
* https://github.com/fincs/FSS |
| 9 | 9 |
*/ |
| 10 | 10 |
|
| 11 |
+#include <functional> |
|
| 12 |
+#include <cstdlib> |
|
| 11 | 13 |
#include "Track.h" |
| 12 | 14 |
#include "Player.h" |
| 13 | 15 |
#include "common.h" |
| ... | ... |
@@ -35,9 +37,11 @@ void Track::Zero() |
| 35 | 37 |
this->ply = nullptr; |
| 36 | 38 |
|
| 37 | 39 |
this->startPos = this->pos = nullptr; |
| 38 |
- memset(this->stack, 0, sizeof(this->stack)); |
|
| 40 |
+ std::fill_n(&this->stack[0], FSS_TRACKSTACKSIZE, StackValue()); |
|
| 39 | 41 |
this->stackPos = 0; |
| 40 | 42 |
memset(this->loopCount, 0, sizeof(this->loopCount)); |
| 43 |
+ this->overriding() = false; |
|
| 44 |
+ this->lastComparisonResult = this->processCommand = true; |
|
| 41 | 45 |
|
| 42 | 46 |
this->wait = 0; |
| 43 | 47 |
this->patch = 0; |
| ... | ... |
@@ -298,11 +302,95 @@ enum SseqCommand |
| 298 | 302 |
SSEQ_CMD_RANDOM = 0xA0, |
| 299 | 303 |
SSEQ_CMD_PRINTVAR = 0xD6, |
| 300 | 304 |
SSEQ_CMD_IF = 0xA2, |
| 301 |
- SSEQ_CMD_UNSUP1 = 0xA1, |
|
| 302 |
- SSEQ_CMD_UNSUP2_LO = 0xB0, |
|
| 303 |
- SSEQ_CMD_UNSUP2_HI = 0xBD |
|
| 305 |
+ SSEQ_CMD_FROMVAR = 0xA1, |
|
| 306 |
+ SSEQ_CMD_SETVAR = 0xB0, |
|
| 307 |
+ SSEQ_CMD_ADDVAR = 0xB1, |
|
| 308 |
+ SSEQ_CMD_SUBVAR = 0xB2, |
|
| 309 |
+ SSEQ_CMD_MULVAR = 0xB3, |
|
| 310 |
+ SSEQ_CMD_DIVVAR = 0xB4, |
|
| 311 |
+ SSEQ_CMD_SHIFTVAR = 0xB5, |
|
| 312 |
+ SSEQ_CMD_RANDVAR = 0xB6, |
|
| 313 |
+ SSEQ_CMD_CMP_EQ = 0xB8, |
|
| 314 |
+ SSEQ_CMD_CMP_GE = 0xB9, |
|
| 315 |
+ SSEQ_CMD_CMP_GT = 0xBA, |
|
| 316 |
+ SSEQ_CMD_CMP_LE = 0xBB, |
|
| 317 |
+ SSEQ_CMD_CMP_LT = 0xBC, |
|
| 318 |
+ SSEQ_CMD_CMP_NE = 0xBD, |
|
| 319 |
+ |
|
| 320 |
+ SSEQ_CMD_MUTE = 0xD7 // Unsupported |
|
| 304 | 321 |
}; |
| 305 | 322 |
|
| 323 |
+static auto varFuncSet = [](int16_t, int16_t value) { return value; };
|
|
| 324 |
+static auto varFuncAdd = [](int16_t var, int16_t value) -> int16_t { return var + value; };
|
|
| 325 |
+static auto varFuncSub = [](int16_t var, int16_t value) -> int16_t { return var - value; };
|
|
| 326 |
+static auto varFuncMul = [](int16_t var, int16_t value) -> int16_t { return var * value; };
|
|
| 327 |
+static auto varFuncDiv = [](int16_t var, int16_t value) -> int16_t { return var / value; };
|
|
| 328 |
+static auto varFuncShift = [](int16_t var, int16_t value) -> int16_t |
|
| 329 |
+{
|
|
| 330 |
+ if (value < 0) |
|
| 331 |
+ return var >> -value; |
|
| 332 |
+ else |
|
| 333 |
+ return var << value; |
|
| 334 |
+}; |
|
| 335 |
+static auto varFuncRand = [](int16_t, int16_t value) -> int16_t |
|
| 336 |
+{
|
|
| 337 |
+ if (value < 0) |
|
| 338 |
+ return -(std::rand() % (-value + 1)); |
|
| 339 |
+ else |
|
| 340 |
+ return std::rand() % (value + 1); |
|
| 341 |
+}; |
|
| 342 |
+ |
|
| 343 |
+static inline std::function<int16_t (int16_t, int16_t)> VarFunc(int cmd) |
|
| 344 |
+{
|
|
| 345 |
+ switch (cmd) |
|
| 346 |
+ {
|
|
| 347 |
+ case SSEQ_CMD_SETVAR: |
|
| 348 |
+ return varFuncSet; |
|
| 349 |
+ case SSEQ_CMD_ADDVAR: |
|
| 350 |
+ return varFuncAdd; |
|
| 351 |
+ case SSEQ_CMD_SUBVAR: |
|
| 352 |
+ return varFuncSub; |
|
| 353 |
+ case SSEQ_CMD_MULVAR: |
|
| 354 |
+ return varFuncMul; |
|
| 355 |
+ case SSEQ_CMD_DIVVAR: |
|
| 356 |
+ return varFuncDiv; |
|
| 357 |
+ case SSEQ_CMD_SHIFTVAR: |
|
| 358 |
+ return varFuncShift; |
|
| 359 |
+ case SSEQ_CMD_RANDVAR: |
|
| 360 |
+ return varFuncRand; |
|
| 361 |
+ default: |
|
| 362 |
+ return nullptr; |
|
| 363 |
+ } |
|
| 364 |
+} |
|
| 365 |
+ |
|
| 366 |
+static auto compareFuncEq = [](int16_t a, int16_t b) { return a == b; };
|
|
| 367 |
+static auto compareFuncGe = [](int16_t a, int16_t b) { return a >= b; };
|
|
| 368 |
+static auto compareFuncGt = [](int16_t a, int16_t b) { return a > b; };
|
|
| 369 |
+static auto compareFuncLe = [](int16_t a, int16_t b) { return a <= b; };
|
|
| 370 |
+static auto compareFuncLt = [](int16_t a, int16_t b) { return a < b; };
|
|
| 371 |
+static auto compareFuncNe = [](int16_t a, int16_t b) { return a != b; };
|
|
| 372 |
+ |
|
| 373 |
+static inline std::function<bool (int16_t, int16_t)> CompareFunc(int cmd) |
|
| 374 |
+{
|
|
| 375 |
+ switch (cmd) |
|
| 376 |
+ {
|
|
| 377 |
+ case SSEQ_CMD_CMP_EQ: |
|
| 378 |
+ return compareFuncEq; |
|
| 379 |
+ case SSEQ_CMD_CMP_GE: |
|
| 380 |
+ return compareFuncGe; |
|
| 381 |
+ case SSEQ_CMD_CMP_GT: |
|
| 382 |
+ return compareFuncGt; |
|
| 383 |
+ case SSEQ_CMD_CMP_LE: |
|
| 384 |
+ return compareFuncLe; |
|
| 385 |
+ case SSEQ_CMD_CMP_LT: |
|
| 386 |
+ return compareFuncLt; |
|
| 387 |
+ case SSEQ_CMD_CMP_NE: |
|
| 388 |
+ return compareFuncNe; |
|
| 389 |
+ default: |
|
| 390 |
+ return nullptr; |
|
| 391 |
+ } |
|
| 392 |
+} |
|
| 393 |
+ |
|
| 306 | 394 |
void Track::Run() |
| 307 | 395 |
{
|
| 308 | 396 |
// Indicate "heartbeat" for this track |
| ... | ... |
@@ -323,21 +411,40 @@ void Track::Run() |
| 323 | 411 |
|
| 324 | 412 |
while (!this->wait) |
| 325 | 413 |
{
|
| 326 |
- int cmd = read8(pData); |
|
| 414 |
+ int cmd; |
|
| 415 |
+ if (this->overriding()) |
|
| 416 |
+ cmd = this->overriding.cmd; |
|
| 417 |
+ else |
|
| 418 |
+ cmd = read8(pData); |
|
| 327 | 419 |
if (cmd < 0x80) |
| 328 | 420 |
{
|
| 329 | 421 |
// Note on |
| 330 | 422 |
int key = cmd + this->transpose; |
| 331 |
- int vel = read8(pData); |
|
| 332 |
- int len = readvl(pData); |
|
| 333 |
- if (this->state[TS_NOTEWAIT]) |
|
| 334 |
- this->wait = len; |
|
| 335 |
- if (this->state[TS_TIEBIT]) |
|
| 336 |
- this->NoteOnTie(key, vel); |
|
| 423 |
+ int vel; |
|
| 424 |
+ int len; |
|
| 425 |
+ if (this->overriding()) |
|
| 426 |
+ {
|
|
| 427 |
+ vel = this->overriding.extraValue; |
|
| 428 |
+ len = this->overriding.value; |
|
| 429 |
+ } |
|
| 337 | 430 |
else |
| 338 |
- this->NoteOn(key, vel, len); |
|
| 431 |
+ {
|
|
| 432 |
+ vel = read8(pData); |
|
| 433 |
+ len = readvl(pData); |
|
| 434 |
+ } |
|
| 435 |
+ if (this->processCommand) |
|
| 436 |
+ {
|
|
| 437 |
+ if (this->state[TS_NOTEWAIT]) |
|
| 438 |
+ this->wait = len; |
|
| 439 |
+ if (this->state[TS_TIEBIT]) |
|
| 440 |
+ this->NoteOnTie(key, vel); |
|
| 441 |
+ else |
|
| 442 |
+ this->NoteOn(key, vel, len); |
|
| 443 |
+ } |
|
| 339 | 444 |
} |
| 340 | 445 |
else |
| 446 |
+ {
|
|
| 447 |
+ int value; |
|
| 341 | 448 |
switch (cmd) |
| 342 | 449 |
{
|
| 343 | 450 |
//----------------------------------------------------------------- |
| ... | ... |
@@ -345,86 +452,156 @@ void Track::Run() |
| 345 | 452 |
//----------------------------------------------------------------- |
| 346 | 453 |
|
| 347 | 454 |
case SSEQ_CMD_REST: |
| 348 |
- this->wait = readvl(pData); |
|
| 455 |
+ if (this->overriding()) |
|
| 456 |
+ value = this->overriding.value; |
|
| 457 |
+ else |
|
| 458 |
+ value = readvl(pData); |
|
| 459 |
+ if (this->processCommand) |
|
| 460 |
+ this->wait = value; |
|
| 349 | 461 |
break; |
| 350 | 462 |
|
| 351 | 463 |
case SSEQ_CMD_PATCH: |
| 352 |
- this->patch = readvl(pData); |
|
| 464 |
+ if (this->overriding()) |
|
| 465 |
+ value = this->overriding.value; |
|
| 466 |
+ else |
|
| 467 |
+ value = readvl(pData); |
|
| 468 |
+ if (this->processCommand) |
|
| 469 |
+ this->patch = value; |
|
| 353 | 470 |
break; |
| 354 | 471 |
|
| 355 | 472 |
case SSEQ_CMD_GOTO: |
| 356 |
- *pData = &this->ply->sseq->data[read24(pData)]; |
|
| 473 |
+ value = read24(pData); |
|
| 474 |
+ if (this->processCommand) |
|
| 475 |
+ *pData = &this->ply->sseq->data[value]; |
|
| 357 | 476 |
break; |
| 358 | 477 |
|
| 359 | 478 |
case SSEQ_CMD_CALL: |
| 360 |
- {
|
|
| 361 |
- const uint8_t *dest = &this->ply->sseq->data[read24(pData)]; |
|
| 362 |
- this->stack[this->stackPos++] = *pData; |
|
| 363 |
- *pData = dest; |
|
| 479 |
+ value = read24(pData); |
|
| 480 |
+ if (this->processCommand && this->stackPos < FSS_TRACKSTACKSIZE) |
|
| 481 |
+ {
|
|
| 482 |
+ const uint8_t *dest = &this->ply->sseq->data[value]; |
|
| 483 |
+ this->stack[this->stackPos++] = StackValue(STACKTYPE_CALL, *pData); |
|
| 484 |
+ *pData = dest; |
|
| 485 |
+ } |
|
| 364 | 486 |
break; |
| 365 |
- } |
|
| 366 | 487 |
|
| 367 | 488 |
case SSEQ_CMD_RET: |
| 368 |
- *pData = this->stack[--this->stackPos]; |
|
| 489 |
+ if (this->processCommand && this->stackPos && this->stack[this->stackPos - 1].type == STACKTYPE_CALL) |
|
| 490 |
+ *pData = this->stack[--this->stackPos].dest; |
|
| 369 | 491 |
break; |
| 370 | 492 |
|
| 371 | 493 |
case SSEQ_CMD_PAN: |
| 372 |
- this->pan = read8(pData) - 64; |
|
| 373 |
- this->updateFlags.set(TUF_PAN); |
|
| 494 |
+ if (this->overriding()) |
|
| 495 |
+ value = this->overriding.value; |
|
| 496 |
+ else |
|
| 497 |
+ value = read8(pData); |
|
| 498 |
+ if (this->processCommand) |
|
| 499 |
+ {
|
|
| 500 |
+ this->pan = value - 64; |
|
| 501 |
+ this->updateFlags.set(TUF_PAN); |
|
| 502 |
+ } |
|
| 374 | 503 |
break; |
| 375 | 504 |
|
| 376 | 505 |
case SSEQ_CMD_VOL: |
| 377 |
- this->vol = read8(pData); |
|
| 378 |
- this->updateFlags.set(TUF_VOL); |
|
| 506 |
+ if (this->overriding()) |
|
| 507 |
+ value = this->overriding.value; |
|
| 508 |
+ else |
|
| 509 |
+ value = read8(pData); |
|
| 510 |
+ if (this->processCommand) |
|
| 511 |
+ {
|
|
| 512 |
+ this->vol = value; |
|
| 513 |
+ this->updateFlags.set(TUF_VOL); |
|
| 514 |
+ } |
|
| 379 | 515 |
break; |
| 380 | 516 |
|
| 381 | 517 |
case SSEQ_CMD_MASTERVOL: |
| 382 |
- this->ply->masterVol = Cnv_Sust(read8(pData)); |
|
| 383 |
- for (uint8_t i = 0; i < this->ply->nTracks; ++i) |
|
| 384 |
- this->ply->tracks[this->ply->trackIds[i]].updateFlags.set(TUF_VOL); |
|
| 518 |
+ if (this->overriding()) |
|
| 519 |
+ value = this->overriding.value; |
|
| 520 |
+ else |
|
| 521 |
+ value = read8(pData); |
|
| 522 |
+ if (this->processCommand) |
|
| 523 |
+ {
|
|
| 524 |
+ this->ply->masterVol = Cnv_Sust(value); |
|
| 525 |
+ for (uint8_t i = 0; i < this->ply->nTracks; ++i) |
|
| 526 |
+ this->ply->tracks[this->ply->trackIds[i]].updateFlags.set(TUF_VOL); |
|
| 527 |
+ } |
|
| 385 | 528 |
break; |
| 386 | 529 |
|
| 387 | 530 |
case SSEQ_CMD_PRIO: |
| 388 |
- this->prio = this->ply->prio + read8(pData); |
|
| 531 |
+ value = read8(pData); |
|
| 532 |
+ if (this->processCommand) |
|
| 533 |
+ this->prio = this->ply->prio + value; |
|
| 389 | 534 |
// Update here? |
| 390 | 535 |
break; |
| 391 | 536 |
|
| 392 | 537 |
case SSEQ_CMD_NOTEWAIT: |
| 393 |
- this->state.set(TS_NOTEWAIT, !!read8(pData)); |
|
| 538 |
+ value = read8(pData); |
|
| 539 |
+ if (this->processCommand) |
|
| 540 |
+ this->state.set(TS_NOTEWAIT, !!value); |
|
| 394 | 541 |
break; |
| 395 | 542 |
|
| 396 | 543 |
case SSEQ_CMD_TIE: |
| 397 |
- this->state.set(TS_TIEBIT, !!read8(pData)); |
|
| 398 |
- this->ReleaseAllNotes(); |
|
| 544 |
+ value = read8(pData); |
|
| 545 |
+ if (this->processCommand) |
|
| 546 |
+ {
|
|
| 547 |
+ this->state.set(TS_TIEBIT, !!value); |
|
| 548 |
+ this->ReleaseAllNotes(); |
|
| 549 |
+ } |
|
| 399 | 550 |
break; |
| 400 | 551 |
|
| 401 | 552 |
case SSEQ_CMD_EXPR: |
| 402 |
- this->expr = read8(pData); |
|
| 403 |
- this->updateFlags.set(TUF_VOL); |
|
| 553 |
+ if (this->overriding()) |
|
| 554 |
+ value = this->overriding.value; |
|
| 555 |
+ else |
|
| 556 |
+ value = read8(pData); |
|
| 557 |
+ if (this->processCommand) |
|
| 558 |
+ {
|
|
| 559 |
+ this->expr = value; |
|
| 560 |
+ this->updateFlags.set(TUF_VOL); |
|
| 561 |
+ } |
|
| 404 | 562 |
break; |
| 405 | 563 |
|
| 406 | 564 |
case SSEQ_CMD_TEMPO: |
| 407 |
- this->ply->tempo = read16(pData); |
|
| 565 |
+ value = read16(pData); |
|
| 566 |
+ if (this->processCommand) |
|
| 567 |
+ this->ply->tempo = value; |
|
| 408 | 568 |
break; |
| 409 | 569 |
|
| 410 | 570 |
case SSEQ_CMD_END: |
| 411 |
- this->state.set(TS_END); |
|
| 412 |
- return; |
|
| 571 |
+ if (this->processCommand) |
|
| 572 |
+ {
|
|
| 573 |
+ this->state.set(TS_END); |
|
| 574 |
+ return; |
|
| 575 |
+ } |
|
| 576 |
+ break; |
|
| 413 | 577 |
|
| 414 | 578 |
case SSEQ_CMD_LOOPSTART: |
| 415 |
- this->loopCount[this->stackPos] = read8(pData); |
|
| 416 |
- this->stack[this->stackPos++] = *pData; |
|
| 579 |
+ if (this->overriding()) |
|
| 580 |
+ value = this->overriding.value; |
|
| 581 |
+ else |
|
| 582 |
+ value = read8(pData); |
|
| 583 |
+ if (this->processCommand && this->stackPos < FSS_TRACKSTACKSIZE) |
|
| 584 |
+ {
|
|
| 585 |
+ this->loopCount[this->stackPos] = value; |
|
| 586 |
+ this->stack[this->stackPos++] = StackValue(STACKTYPE_LOOP, *pData); |
|
| 587 |
+ } |
|
| 417 | 588 |
break; |
| 418 | 589 |
|
| 419 | 590 |
case SSEQ_CMD_LOOPEND: |
| 420 |
- if (this->stackPos) |
|
| 591 |
+ if (this->processCommand && this->stackPos && this->stack[this->stackPos - 1].type == STACKTYPE_LOOP) |
|
| 421 | 592 |
{
|
| 422 |
- const uint8_t *rPos = this->stack[this->stackPos - 1]; |
|
| 593 |
+ const uint8_t *rPos = this->stack[this->stackPos - 1].dest; |
|
| 423 | 594 |
uint8_t &nR = this->loopCount[this->stackPos - 1]; |
| 424 | 595 |
uint8_t prevR = nR; |
| 425 |
- if (prevR && !--nR) |
|
| 426 |
- --this->stackPos; |
|
| 427 |
- *pData = rPos; |
|
| 596 |
+ if (!prevR) |
|
| 597 |
+ *pData = rPos; |
|
| 598 |
+ else |
|
| 599 |
+ {
|
|
| 600 |
+ if (--nR) |
|
| 601 |
+ *pData = rPos; |
|
| 602 |
+ else |
|
| 603 |
+ --this->stackPos; |
|
| 604 |
+ } |
|
| 428 | 605 |
} |
| 429 | 606 |
break; |
| 430 | 607 |
|
| ... | ... |
@@ -433,17 +610,33 @@ void Track::Run() |
| 433 | 610 |
//----------------------------------------------------------------- |
| 434 | 611 |
|
| 435 | 612 |
case SSEQ_CMD_TRANSPOSE: |
| 436 |
- this->transpose = read8(pData); |
|
| 613 |
+ if (this->overriding()) |
|
| 614 |
+ value = this->overriding.value; |
|
| 615 |
+ else |
|
| 616 |
+ value = read8(pData); |
|
| 617 |
+ if (this->processCommand) |
|
| 618 |
+ this->transpose = value; |
|
| 437 | 619 |
break; |
| 438 | 620 |
|
| 439 | 621 |
case SSEQ_CMD_PITCHBEND: |
| 440 |
- this->pitchBend = read8(pData); |
|
| 441 |
- this->updateFlags.set(TUF_TIMER); |
|
| 622 |
+ if (this->overriding()) |
|
| 623 |
+ value = this->overriding.value; |
|
| 624 |
+ else |
|
| 625 |
+ value = read8(pData); |
|
| 626 |
+ if (this->processCommand) |
|
| 627 |
+ {
|
|
| 628 |
+ this->pitchBend = value; |
|
| 629 |
+ this->updateFlags.set(TUF_TIMER); |
|
| 630 |
+ } |
|
| 442 | 631 |
break; |
| 443 | 632 |
|
| 444 | 633 |
case SSEQ_CMD_PITCHBENDRANGE: |
| 445 |
- this->pitchBendRange = read8(pData); |
|
| 446 |
- this->updateFlags.set(TUF_TIMER); |
|
| 634 |
+ value = read8(pData); |
|
| 635 |
+ if (this->processCommand) |
|
| 636 |
+ {
|
|
| 637 |
+ this->pitchBendRange = value; |
|
| 638 |
+ this->updateFlags.set(TUF_TIMER); |
|
| 639 |
+ } |
|
| 447 | 640 |
break; |
| 448 | 641 |
|
| 449 | 642 |
//----------------------------------------------------------------- |
| ... | ... |
@@ -451,19 +644,39 @@ void Track::Run() |
| 451 | 644 |
//----------------------------------------------------------------- |
| 452 | 645 |
|
| 453 | 646 |
case SSEQ_CMD_ATTACK: |
| 454 |
- this->a = read8(pData); |
|
| 647 |
+ if (this->overriding()) |
|
| 648 |
+ value = this->overriding.value; |
|
| 649 |
+ else |
|
| 650 |
+ value = read8(pData); |
|
| 651 |
+ if (this->processCommand) |
|
| 652 |
+ this->a = value; |
|
| 455 | 653 |
break; |
| 456 | 654 |
|
| 457 | 655 |
case SSEQ_CMD_DECAY: |
| 458 |
- this->d = read8(pData); |
|
| 656 |
+ if (this->overriding()) |
|
| 657 |
+ value = this->overriding.value; |
|
| 658 |
+ else |
|
| 659 |
+ value = read8(pData); |
|
| 660 |
+ if (this->processCommand) |
|
| 661 |
+ this->d = value; |
|
| 459 | 662 |
break; |
| 460 | 663 |
|
| 461 | 664 |
case SSEQ_CMD_SUSTAIN: |
| 462 |
- this->s = read8(pData); |
|
| 665 |
+ if (this->overriding()) |
|
| 666 |
+ value = this->overriding.value; |
|
| 667 |
+ else |
|
| 668 |
+ value = read8(pData); |
|
| 669 |
+ if (this->processCommand) |
|
| 670 |
+ this->s = value; |
|
| 463 | 671 |
break; |
| 464 | 672 |
|
| 465 | 673 |
case SSEQ_CMD_RELEASE: |
| 466 |
- this->r = read8(pData); |
|
| 674 |
+ if (this->overriding()) |
|
| 675 |
+ value = this->overriding.value; |
|
| 676 |
+ else |
|
| 677 |
+ value = read8(pData); |
|
| 678 |
+ if (this->processCommand) |
|
| 679 |
+ this->r = value; |
|
| 467 | 680 |
break; |
| 468 | 681 |
|
| 469 | 682 |
//----------------------------------------------------------------- |
| ... | ... |
@@ -471,23 +684,41 @@ void Track::Run() |
| 471 | 684 |
//----------------------------------------------------------------- |
| 472 | 685 |
|
| 473 | 686 |
case SSEQ_CMD_PORTAKEY: |
| 474 |
- this->portaKey = read8(pData) + this->transpose; |
|
| 475 |
- this->state.set(TS_PORTABIT); |
|
| 476 |
- // Update here? |
|
| 687 |
+ value = read8(pData); |
|
| 688 |
+ if (this->processCommand) |
|
| 689 |
+ {
|
|
| 690 |
+ this->portaKey = value + this->transpose; |
|
| 691 |
+ this->state.set(TS_PORTABIT); |
|
| 692 |
+ // Update here? |
|
| 693 |
+ } |
|
| 477 | 694 |
break; |
| 478 | 695 |
|
| 479 | 696 |
case SSEQ_CMD_PORTAFLAG: |
| 480 |
- this->state.set(TS_PORTABIT, !!read8(pData)); |
|
| 481 |
- // Update here? |
|
| 697 |
+ value = read8(pData); |
|
| 698 |
+ if (this->processCommand) |
|
| 699 |
+ {
|
|
| 700 |
+ this->state.set(TS_PORTABIT, !!value); |
|
| 701 |
+ // Update here? |
|
| 702 |
+ } |
|
| 482 | 703 |
break; |
| 483 | 704 |
|
| 484 | 705 |
case SSEQ_CMD_PORTATIME: |
| 485 |
- this->portaTime = read8(pData); |
|
| 706 |
+ if (this->overriding()) |
|
| 707 |
+ value = this->overriding.value; |
|
| 708 |
+ else |
|
| 709 |
+ value = read8(pData); |
|
| 710 |
+ if (this->processCommand) |
|
| 711 |
+ this->portaTime = value; |
|
| 486 | 712 |
// Update here? |
| 487 | 713 |
break; |
| 488 | 714 |
|
| 489 | 715 |
case SSEQ_CMD_SWEEPPITCH: |
| 490 |
- this->sweepPitch = read16(pData); |
|
| 716 |
+ if (this->overriding()) |
|
| 717 |
+ value = this->overriding.value; |
|
| 718 |
+ else |
|
| 719 |
+ value = read16(pData); |
|
| 720 |
+ if (this->processCommand) |
|
| 721 |
+ this->sweepPitch = value; |
|
| 491 | 722 |
// Update here? |
| 492 | 723 |
break; |
| 493 | 724 |
|
| ... | ... |
@@ -496,57 +727,158 @@ void Track::Run() |
| 496 | 727 |
//----------------------------------------------------------------- |
| 497 | 728 |
|
| 498 | 729 |
case SSEQ_CMD_MODDEPTH: |
| 499 |
- this->modDepth = read8(pData); |
|
| 500 |
- this->updateFlags.set(TUF_MOD); |
|
| 730 |
+ if (this->overriding()) |
|
| 731 |
+ value = this->overriding.value; |
|
| 732 |
+ else |
|
| 733 |
+ value = read8(pData); |
|
| 734 |
+ if (this->processCommand) |
|
| 735 |
+ {
|
|
| 736 |
+ this->modDepth = value; |
|
| 737 |
+ this->updateFlags.set(TUF_MOD); |
|
| 738 |
+ } |
|
| 501 | 739 |
break; |
| 502 | 740 |
|
| 503 | 741 |
case SSEQ_CMD_MODSPEED: |
| 504 |
- this->modSpeed = read8(pData); |
|
| 505 |
- this->updateFlags.set(TUF_MOD); |
|
| 742 |
+ if (this->overriding()) |
|
| 743 |
+ value = this->overriding.value; |
|
| 744 |
+ else |
|
| 745 |
+ value = read8(pData); |
|
| 746 |
+ if (this->processCommand) |
|
| 747 |
+ {
|
|
| 748 |
+ this->modSpeed = value; |
|
| 749 |
+ this->updateFlags.set(TUF_MOD); |
|
| 750 |
+ } |
|
| 506 | 751 |
break; |
| 507 | 752 |
|
| 508 | 753 |
case SSEQ_CMD_MODTYPE: |
| 509 |
- this->modType = read8(pData); |
|
| 510 |
- this->updateFlags.set(TUF_MOD); |
|
| 754 |
+ value = read8(pData); |
|
| 755 |
+ if (this->processCommand) |
|
| 756 |
+ {
|
|
| 757 |
+ this->modType = value; |
|
| 758 |
+ this->updateFlags.set(TUF_MOD); |
|
| 759 |
+ } |
|
| 511 | 760 |
break; |
| 512 | 761 |
|
| 513 | 762 |
case SSEQ_CMD_MODRANGE: |
| 514 |
- this->modRange = read8(pData); |
|
| 515 |
- this->updateFlags.set(TUF_MOD); |
|
| 763 |
+ value = read8(pData); |
|
| 764 |
+ if (this->processCommand) |
|
| 765 |
+ {
|
|
| 766 |
+ this->modRange = value; |
|
| 767 |
+ this->updateFlags.set(TUF_MOD); |
|
| 768 |
+ } |
|
| 516 | 769 |
break; |
| 517 | 770 |
|
| 518 | 771 |
case SSEQ_CMD_MODDELAY: |
| 519 |
- this->modDelay = read16(pData); |
|
| 520 |
- this->updateFlags.set(TUF_MOD); |
|
| 772 |
+ if (this->overriding()) |
|
| 773 |
+ value = this->overriding.value; |
|
| 774 |
+ else |
|
| 775 |
+ value = read16(pData); |
|
| 776 |
+ if (this->processCommand) |
|
| 777 |
+ {
|
|
| 778 |
+ this->modDelay = value; |
|
| 779 |
+ this->updateFlags.set(TUF_MOD); |
|
| 780 |
+ } |
|
| 521 | 781 |
break; |
| 522 | 782 |
|
| 523 | 783 |
//----------------------------------------------------------------- |
| 524 |
- // Variable-related commands |
|
| 784 |
+ // Randomness-related commands |
|
| 525 | 785 |
//----------------------------------------------------------------- |
| 526 | 786 |
|
| 527 |
- case SSEQ_CMD_RANDOM: // TODO |
|
| 528 |
- *pData += 5; |
|
| 787 |
+ case SSEQ_CMD_RANDOM: |
|
| 788 |
+ {
|
|
| 789 |
+ if (this->processCommand) |
|
| 790 |
+ this->overriding() = true; |
|
| 791 |
+ this->overriding.cmd = read8(pData); |
|
| 792 |
+ if ((this->overriding.cmd >= SSEQ_CMD_SETVAR && this->overriding.cmd <= SSEQ_CMD_CMP_NE) || this->overriding.cmd < 0x80) |
|
| 793 |
+ this->overriding.extraValue = read8(pData); |
|
| 794 |
+ int16_t minVal = read16(pData); |
|
| 795 |
+ int16_t maxVal = read16(pData); |
|
| 796 |
+ this->overriding.value = (std::rand() % (maxVal - minVal + 1)) + minVal; |
|
| 529 | 797 |
break; |
| 798 |
+ } |
|
| 530 | 799 |
|
| 531 |
- case SSEQ_CMD_PRINTVAR: // TODO |
|
| 532 |
- *pData += 1; |
|
| 800 |
+ //----------------------------------------------------------------- |
|
| 801 |
+ // Variable-related commands |
|
| 802 |
+ //----------------------------------------------------------------- |
|
| 803 |
+ |
|
| 804 |
+ case SSEQ_CMD_FROMVAR: |
|
| 805 |
+ if (this->processCommand) |
|
| 806 |
+ this->overriding() = true; |
|
| 807 |
+ this->overriding.cmd = read8(pData); |
|
| 808 |
+ if ((this->overriding.cmd >= SSEQ_CMD_SETVAR && this->overriding.cmd <= SSEQ_CMD_CMP_NE) || this->overriding.cmd < 0x80) |
|
| 809 |
+ this->overriding.extraValue = read8(pData); |
|
| 810 |
+ this->overriding.value = this->ply->variables[read8(pData)]; |
|
| 811 |
+ break; |
|
| 812 |
+ |
|
| 813 |
+ case SSEQ_CMD_SETVAR: |
|
| 814 |
+ case SSEQ_CMD_ADDVAR: |
|
| 815 |
+ case SSEQ_CMD_SUBVAR: |
|
| 816 |
+ case SSEQ_CMD_MULVAR: |
|
| 817 |
+ case SSEQ_CMD_DIVVAR: |
|
| 818 |
+ case SSEQ_CMD_SHIFTVAR: |
|
| 819 |
+ case SSEQ_CMD_RANDVAR: |
|
| 820 |
+ {
|
|
| 821 |
+ int8_t varNo; |
|
| 822 |
+ if (this->overriding()) |
|
| 823 |
+ {
|
|
| 824 |
+ varNo = this->overriding.extraValue; |
|
| 825 |
+ value = this->overriding.value; |
|
| 826 |
+ } |
|
| 827 |
+ else |
|
| 828 |
+ {
|
|
| 829 |
+ varNo = read8(pData); |
|
| 830 |
+ value = read16(pData); |
|
| 831 |
+ } |
|
| 832 |
+ if (cmd == SSEQ_CMD_DIVVAR && !value) // Division by 0, skip it to prevent crashing |
|
| 833 |
+ break; |
|
| 834 |
+ if (this->processCommand) |
|
| 835 |
+ this->ply->variables[varNo] = VarFunc(cmd)(this->ply->variables[varNo], value); |
|
| 533 | 836 |
break; |
| 837 |
+ } |
|
| 534 | 838 |
|
| 535 |
- case SSEQ_CMD_UNSUP1: // TODO |
|
| 839 |
+ //----------------------------------------------------------------- |
|
| 840 |
+ // Conditional-related commands |
|
| 841 |
+ //----------------------------------------------------------------- |
|
| 842 |
+ |
|
| 843 |
+ case SSEQ_CMD_CMP_EQ: |
|
| 844 |
+ case SSEQ_CMD_CMP_GE: |
|
| 845 |
+ case SSEQ_CMD_CMP_GT: |
|
| 846 |
+ case SSEQ_CMD_CMP_LE: |
|
| 847 |
+ case SSEQ_CMD_CMP_LT: |
|
| 848 |
+ case SSEQ_CMD_CMP_NE: |
|
| 536 | 849 |
{
|
| 537 |
- int t = read8(pData); |
|
| 538 |
- if (t >= SSEQ_CMD_UNSUP2_LO && t <= SSEQ_CMD_UNSUP2_HI) |
|
| 539 |
- *pData += 1; |
|
| 540 |
- *pData += 1; |
|
| 850 |
+ int8_t varNo; |
|
| 851 |
+ if (this->overriding()) |
|
| 852 |
+ {
|
|
| 853 |
+ varNo = this->overriding.extraValue; |
|
| 854 |
+ value = this->overriding.value; |
|
| 855 |
+ } |
|
| 856 |
+ else |
|
| 857 |
+ {
|
|
| 858 |
+ varNo = read8(pData); |
|
| 859 |
+ value = read16(pData); |
|
| 860 |
+ } |
|
| 861 |
+ if (this->processCommand) |
|
| 862 |
+ this->lastComparisonResult = CompareFunc(cmd)(this->ply->variables[varNo], value); |
|
| 541 | 863 |
break; |
| 542 | 864 |
} |
| 543 | 865 |
|
| 544 |
- case SSEQ_CMD_IF: // TODO |
|
| 866 |
+ case SSEQ_CMD_IF: |
|
| 867 |
+ this->processCommand = this->lastComparisonResult; |
|
| 868 |
+ break; |
|
| 869 |
+ |
|
| 870 |
+ case SSEQ_CMD_PRINTVAR: |
|
| 871 |
+ ++*pData; |
|
| 545 | 872 |
break; |
| 546 | 873 |
|
| 547 |
- default: |
|
| 548 |
- if (cmd >= SSEQ_CMD_UNSUP2_LO && cmd <= SSEQ_CMD_UNSUP2_HI) // TODO |
|
| 549 |
- *pData += 3; |
|
| 874 |
+ case SSEQ_CMD_MUTE: // UNSUPPORTED |
|
| 875 |
+ ++*pData; |
|
| 550 | 876 |
} |
| 877 |
+ } |
|
| 878 |
+ |
|
| 879 |
+ if (cmd != SSEQ_CMD_RANDOM && cmd != SSEQ_CMD_FROMVAR) |
|
| 880 |
+ this->overriding() = false; |
|
| 881 |
+ if (cmd != SSEQ_CMD_IF) |
|
| 882 |
+ this->processCommand = true; |
|
| 551 | 883 |
} |
| 552 | 884 |
} |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* SSEQ Player - Track structure |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2014-09-08 |
|
| 4 |
+ * Last modification on 2014-10-13 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Adapted from source code of FeOS Sound System |
| 7 | 7 |
* By fincs |
| ... | ... |
@@ -15,6 +15,33 @@ |
| 15 | 15 |
|
| 16 | 16 |
struct Player; |
| 17 | 17 |
|
| 18 |
+enum StackType |
|
| 19 |
+{
|
|
| 20 |
+ STACKTYPE_CALL, |
|
| 21 |
+ STACKTYPE_LOOP |
|
| 22 |
+}; |
|
| 23 |
+ |
|
| 24 |
+struct StackValue |
|
| 25 |
+{
|
|
| 26 |
+ StackType type; |
|
| 27 |
+ const uint8_t *dest; |
|
| 28 |
+ |
|
| 29 |
+ StackValue() : type(STACKTYPE_CALL), dest(nullptr) { }
|
|
| 30 |
+ StackValue(StackType newType, const uint8_t *newDest) : type(newType), dest(newDest) { }
|
|
| 31 |
+}; |
|
| 32 |
+ |
|
| 33 |
+struct Override |
|
| 34 |
+{
|
|
| 35 |
+ bool overriding; |
|
| 36 |
+ int cmd; |
|
| 37 |
+ int value; |
|
| 38 |
+ int extraValue; |
|
| 39 |
+ |
|
| 40 |
+ Override() : overriding(false) { }
|
|
| 41 |
+ bool operator()() const { return this->overriding; }
|
|
| 42 |
+ bool &operator()() { return this->overriding; }
|
|
| 43 |
+}; |
|
| 44 |
+ |
|
| 18 | 45 |
struct Track |
| 19 | 46 |
{
|
| 20 | 47 |
int8_t trackId; |
| ... | ... |
@@ -25,9 +52,12 @@ struct Track |
| 25 | 52 |
|
| 26 | 53 |
const uint8_t *startPos; |
| 27 | 54 |
const uint8_t *pos; |
| 28 |
- const uint8_t *stack[FSS_TRACKSTACKSIZE]; |
|
| 55 |
+ StackValue stack[FSS_TRACKSTACKSIZE]; |
|
| 29 | 56 |
uint8_t stackPos; |
| 30 | 57 |
uint8_t loopCount[FSS_TRACKSTACKSIZE]; |
| 58 |
+ Override overriding; |
|
| 59 |
+ bool lastComparisonResult; |
|
| 60 |
+ bool processCommand; |
|
| 31 | 61 |
|
| 32 | 62 |
int wait; |
| 33 | 63 |
uint16_t patch; |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* xSF - NCSF configuration |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2014-10-05 |
|
| 4 |
+ * Last modification on 2014-10-13 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Partially based on the vio*sf framework |
| 7 | 7 |
*/ |
| ... | ... |
@@ -21,7 +21,7 @@ enum |
| 21 | 21 |
|
| 22 | 22 |
unsigned XSFConfig::initSampleRate = 44100; |
| 23 | 23 |
std::string XSFConfig::commonName = "NCSF Decoder"; |
| 24 |
-std::string XSFConfig::versionNumber = "1.9.1"; |
|
| 24 |
+std::string XSFConfig::versionNumber = "1.10"; |
|
| 25 | 25 |
unsigned XSFConfig_NCSF::initInterpolation = 4; |
| 26 | 26 |
std::string XSFConfig_NCSF::initMutes = "0000000000000000"; |
| 27 | 27 |
|
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* xSF - NCSF Player |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2014-10-05 |
|
| 4 |
+ * Last modification on 2014-10-13 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Partially based on the vio*sf framework |
| 7 | 7 |
* |
| ... | ... |
@@ -10,6 +10,8 @@ |
| 10 | 10 |
*/ |
| 11 | 11 |
|
| 12 | 12 |
#include <memory> |
| 13 |
+#include <cstdlib> |
|
| 14 |
+#include <ctime> |
|
| 13 | 15 |
#include <zlib.h> |
| 14 | 16 |
#include "convert.h" |
| 15 | 17 |
#include "XSFPlayer_NCSF.h" |
| ... | ... |
@@ -165,6 +167,8 @@ bool XSFPlayer_NCSF::Load() |
| 165 | 167 |
soundViewThreadHandle = CreateThread(nullptr, 0, soundViewThread, this, 0, nullptr); |
| 166 | 168 |
#endif |
| 167 | 169 |
|
| 170 |
+ std::srand(static_cast<unsigned>(std::time(nullptr))); |
|
| 171 |
+ |
|
| 168 | 172 |
PseudoFile file; |
| 169 | 173 |
file.data = &this->sdatData; |
| 170 | 174 |
this->sdat.reset(new SDAT(file, this->sseq)); |