| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* SSEQ Player - Channel structures |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-04-23 |
|
| 4 |
+ * Last modification on 2013-04-26 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Adapted from source code of FeOS Sound System |
| 7 | 7 |
* By fincs |
| ... | ... |
@@ -44,7 +44,7 @@ TempSndReg::TempSndReg() : CR(0), SOURCE(nullptr), TIMER(0), REPEAT_POINT(0), LE |
| 44 | 44 |
|
| 45 | 45 |
bool Channel::initializedLUTs = false; |
| 46 | 46 |
double Channel::cosine_lut[Channel::COSINE_RESOLUTION]; |
| 47 |
-double Channel::lanczos_lut[Channel::LANCZOS_SAMPLES]; |
|
| 47 |
+double Channel::lanczos_lut[Channel::LANCZOS_SAMPLES + 1]; |
|
| 48 | 48 |
|
| 49 | 49 |
#ifndef M_PI |
| 50 | 50 |
static const double M_PI = 3.14159265358979323846; |
| ... | ... |
@@ -65,7 +65,7 @@ Channel::Channel() : chnId(-1), tempReg(), state(CS_NONE), trackId(-1), prio(0), |
| 65 | 65 |
for (unsigned i = 0; i < COSINE_RESOLUTION; ++i) |
| 66 | 66 |
this->cosine_lut[i] = (1.0 - std::cos((static_cast<double>(i) / COSINE_RESOLUTION) * M_PI)) * 0.5; |
| 67 | 67 |
double dx = static_cast<double>(LANCZOS_WIDTH) / LANCZOS_SAMPLES, x = 0.0; |
| 68 |
- for (unsigned i = 0; i < LANCZOS_SAMPLES; ++i, x += dx) |
|
| 68 |
+ for (unsigned i = 0; i <= LANCZOS_SAMPLES; ++i, x += dx) |
|
| 69 | 69 |
this->lanczos_lut[i] = std::abs(x) < LANCZOS_WIDTH ? sinc(x) * sinc(x / LANCZOS_WIDTH) : 0.0; |
| 70 | 70 |
this->initializedLUTs = true; |
| 71 | 71 |
} |
| ... | ... |
@@ -604,37 +604,48 @@ int32_t Channel::Interpolate() |
| 604 | 604 |
double ratio = this->reg.samplePosition; |
| 605 | 605 |
ratio -= static_cast<int32_t>(ratio); |
| 606 | 606 |
|
| 607 |
- const auto &data = &this->sampleHistory[this->sampleHistoryPtr + 5]; |
|
| 608 |
- int32_t a = data[0], b = data[1]; |
|
| 607 |
+ const auto &data = &this->sampleHistory[this->sampleHistoryPtr + 16]; |
|
| 609 | 608 |
|
| 610 |
- double c0, c1, c2, c3, c4, c5; |
|
| 611 |
- if (this->ply->interpolation > INTERPOLATION_COSINE) |
|
| 609 |
+ if (this->ply->interpolation == INTERPOLATION_LANCZOS) |
|
| 612 | 610 |
{
|
| 613 |
- int32_t c = data[2], z = data[-1]; |
|
| 611 |
+ double kernel[LANCZOS_WIDTH * 2], kernel_sum = 0.0; |
|
| 612 |
+ int i = LANCZOS_WIDTH, shift = static_cast<int>(std::floor(ratio * LANCZOS_RESOLUTION)); |
|
| 613 |
+ int step = this->reg.sampleIncrease > 1.0 ? static_cast<int>(LANCZOS_RESOLUTION / this->reg.sampleIncrease) : LANCZOS_RESOLUTION; |
|
| 614 |
+ for (; i >= -static_cast<int>(LANCZOS_WIDTH - 1); --i) |
|
| 615 |
+ {
|
|
| 616 |
+ int pos = i * step; |
|
| 617 |
+ kernel_sum += kernel[i + LANCZOS_WIDTH - 1] = this->lanczos_lut[std::abs(shift - pos)]; |
|
| 618 |
+ } |
|
| 619 |
+ double sum = 0.0; |
|
| 620 |
+ for (i = 0; i < static_cast<int>(LANCZOS_WIDTH * 2); ++i) |
|
| 621 |
+ sum += data[i - static_cast<int>(LANCZOS_WIDTH) + 1] * kernel[i]; |
|
| 622 |
+ return static_cast<int32_t>(sum / kernel_sum); |
|
| 623 |
+ } |
|
| 624 |
+ else if (this->ply->interpolation > INTERPOLATION_COSINE) |
|
| 625 |
+ {
|
|
| 626 |
+ double c0, c1, c2, c3, c4, c5; |
|
| 614 | 627 |
|
| 615 | 628 |
if (this->ply->interpolation > INTERPOLATION_4POINTBSPLINE) |
| 616 | 629 |
{
|
| 617 |
- int32_t d = data[3], y = data[-2]; |
|
| 618 |
- |
|
| 619 | 630 |
if (this->ply->interpolation == INTERPOLATION_6POINTBSPLINE) |
| 620 | 631 |
{
|
| 621 |
- double ym2py2 = y + c, ym1py1 = z + b; |
|
| 622 |
- double y2mym2 = c - y, y1mym1 = b - z; |
|
| 632 |
+ double ym2py2 = data[-2] + data[2], ym1py1 = data[-1] + data[1]; |
|
| 633 |
+ double y2mym2 = data[2] - data[-2], y1mym1 = data[1] - data[-1]; |
|
| 623 | 634 |
double sixthym1py1 = 1 / 6.0 * ym1py1; |
| 624 |
- c0 = 1 / 120.0 * ym2py2 + 13 / 60.0 * ym1py1 + 0.55 * a; |
|
| 635 |
+ c0 = 1 / 120.0 * ym2py2 + 13 / 60.0 * ym1py1 + 0.55 * data[0]; |
|
| 625 | 636 |
c1 = 1 / 24.0 * y2mym2 + 5 / 12.0 * y1mym1; |
| 626 |
- c2 = 1 / 12.0 * ym2py2 + sixthym1py1 - 0.5 * a; |
|
| 637 |
+ c2 = 1 / 12.0 * ym2py2 + sixthym1py1 - 0.5 * data[0]; |
|
| 627 | 638 |
c3 = 1 / 12.0 * y2mym2 - 1 / 6.0 * y1mym1; |
| 628 |
- c4 = 1 / 24.0 * ym2py2 - sixthym1py1 + 0.25 * a; |
|
| 629 |
- c5 = 1 / 120.0 * (d - y) + 1 / 24.0 * (z - c) + 1 / 12.0 * (b - a); |
|
| 639 |
+ c4 = 1 / 24.0 * ym2py2 - sixthym1py1 + 0.25 * data[0]; |
|
| 640 |
+ c5 = 1 / 120.0 * (data[3] - data[-2]) + 1 / 24.0 * (data[-1] - data[2]) + 1 / 12.0 * (data[1] - data[0]); |
|
| 630 | 641 |
return static_cast<int32_t>(((((c5 * ratio + c4) * ratio + c3) * ratio + c2) * ratio + c1) * ratio + c0); |
| 631 | 642 |
} |
| 632 |
- else if (this->ply->interpolation == INTERPOLATION_6POINTOSCULATING) |
|
| 643 |
+ else // INTERPOLATION_6POINTOSCULATING |
|
| 633 | 644 |
{
|
| 634 | 645 |
ratio -= 0.5; |
| 635 |
- double even1 = y + d, odd1 = y - d; |
|
| 636 |
- double even2 = z + c, odd2 = z - c; |
|
| 637 |
- double even3 = a + b, odd3 = a - b; |
|
| 646 |
+ double even1 = data[-2] + data[3], odd1 = data[-2] - data[3]; |
|
| 647 |
+ double even2 = data[-1] + data[2], odd2 = data[-1] - data[2]; |
|
| 648 |
+ double even3 = data[0] + data[1], odd3 = data[0] - data[1]; |
|
| 638 | 649 |
c0 = 0.01171875 * even1 - 0.09765625 * even2 + 0.5859375 * even3; |
| 639 | 650 |
c1 = 0.2109375 * odd2 - 281 / 192.0 * odd3 - 13 / 384.0 * odd1; |
| 640 | 651 |
c2 = 0.40625 * even2 - 17 / 48.0 * even3 - 5 / 96.0 * even1; |
| ... | ... |
@@ -643,34 +654,21 @@ int32_t Channel::Interpolate() |
| 643 | 654 |
c5 = 25 / 24.0 * odd2 - 25 / 12.0 * odd3 - 5 / 24.0 * odd1; |
| 644 | 655 |
return static_cast<int32_t>(((((c5 * ratio + c4) * ratio + c3) * ratio + c2) * ratio + c1) * ratio + c0); |
| 645 | 656 |
} |
| 646 |
- else // INTERPOLATION_6POINTLANCZOS |
|
| 647 |
- {
|
|
| 648 |
- double kernel[6], kernel_sum = 0.0; |
|
| 649 |
- int i = 3, shift = static_cast<int>(std::floor(ratio * LANCZOS_RESOLUTION)); |
|
| 650 |
- for (; i >= -2; --i) |
|
| 651 |
- {
|
|
| 652 |
- int pos = i * LANCZOS_RESOLUTION; |
|
| 653 |
- kernel_sum += kernel[i + 2] = this->lanczos_lut[std::abs(shift - pos)]; |
|
| 654 |
- } |
|
| 655 |
- for (i = 0; i < 6; ++i) |
|
| 656 |
- kernel[i] /= kernel_sum; |
|
| 657 |
- return static_cast<int32_t>(y * kernel[0] + z * kernel[1] + a * kernel[2] + b * kernel[3] + c * kernel[4] + d * kernel[5]); |
|
| 658 |
- } |
|
| 659 | 657 |
} |
| 660 | 658 |
else // INTERPOLATION_4POINTBSPLINE |
| 661 | 659 |
{
|
| 662 |
- double ym1py1 = z + b; |
|
| 663 |
- c0 = 1 / 6.0 * ym1py1 + 2 / 3.0 * a; |
|
| 664 |
- c1 = 0.5 * (b - z); |
|
| 665 |
- c2 = 0.5 * ym1py1 - a; |
|
| 666 |
- c3 = 0.5 * (a - b) + 1 / 6.0 * (c - z); |
|
| 660 |
+ double ym1py1 = data[-1] + data[1]; |
|
| 661 |
+ c0 = 1 / 6.0 * ym1py1 + 2 / 3.0 * data[0]; |
|
| 662 |
+ c1 = 0.5 * (data[1] - data[-1]); |
|
| 663 |
+ c2 = 0.5 * ym1py1 - data[0]; |
|
| 664 |
+ c3 = 0.5 * (data[0] - data[1]) + 1 / 6.0 * (data[2] - data[-1]); |
|
| 667 | 665 |
return static_cast<int32_t>(((c3 * ratio + c2) * ratio + c1) * ratio + c0); |
| 668 | 666 |
} |
| 669 | 667 |
} |
| 670 | 668 |
else if (this->ply->interpolation == INTERPOLATION_COSINE) |
| 671 |
- return static_cast<int32_t>(a + this->cosine_lut[static_cast<unsigned>(ratio * COSINE_RESOLUTION)] * (b - a)); |
|
| 669 |
+ return static_cast<int32_t>(data[0] + this->cosine_lut[static_cast<unsigned>(ratio * COSINE_RESOLUTION)] * (data[1] - data[0])); |
|
| 672 | 670 |
else // INTERPOLATION_LINEAR |
| 673 |
- return static_cast<int32_t>(a + ratio * (b - a)); |
|
| 671 |
+ return static_cast<int32_t>(data[0] + ratio * (data[1] - data[0])); |
|
| 674 | 672 |
} |
| 675 | 673 |
|
| 676 | 674 |
int32_t Channel::GenerateSample() |
| ... | ... |
@@ -732,9 +730,9 @@ void Channel::IncrementSample() |
| 732 | 730 |
|
| 733 | 731 |
while (loc != newloc) |
| 734 | 732 |
{
|
| 735 |
- this->sampleHistory[this->sampleHistoryPtr] = this->sampleHistory[this->sampleHistoryPtr + 8] = this->reg.source->dataptr[loc++]; |
|
| 733 |
+ this->sampleHistory[this->sampleHistoryPtr] = this->sampleHistory[this->sampleHistoryPtr + 32] = this->reg.source->dataptr[loc++]; |
|
| 736 | 734 |
|
| 737 |
- this->sampleHistoryPtr = (this->sampleHistoryPtr + 1) & 7; |
|
| 735 |
+ this->sampleHistoryPtr = (this->sampleHistoryPtr + 1) & 31; |
|
| 738 | 736 |
|
| 739 | 737 |
if (loc >= this->reg.totalLength) |
| 740 | 738 |
loc -= this->reg.length; |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* SSEQ Player - Channel structures |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-04-23 |
|
| 4 |
+ * Last modification on 2013-04-26 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Adapted from source code of FeOS Sound System |
| 7 | 7 |
* By fincs |
| ... | ... |
@@ -132,7 +132,7 @@ struct Channel |
| 132 | 132 |
* simplify the case of wrapping. Thanks to kode54 for providing this. |
| 133 | 133 |
*/ |
| 134 | 134 |
uint32_t sampleHistoryPtr; |
| 135 |
- int16_t sampleHistory[16]; |
|
| 135 |
+ int16_t sampleHistory[64]; |
|
| 136 | 136 |
|
| 137 | 137 |
/* |
| 138 | 138 |
* Lookup tables for the cosine and Lanczos Sinc interpolations, to |
| ... | ... |
@@ -143,10 +143,10 @@ struct Channel |
| 143 | 143 |
static bool initializedLUTs; |
| 144 | 144 |
static const unsigned COSINE_RESOLUTION = 8192; |
| 145 | 145 |
static const unsigned LANCZOS_RESOLUTION = 8192; |
| 146 |
- static const unsigned LANCZOS_WIDTH = 3; |
|
| 146 |
+ static const unsigned LANCZOS_WIDTH = 8; |
|
| 147 | 147 |
static const unsigned LANCZOS_SAMPLES = LANCZOS_RESOLUTION * LANCZOS_WIDTH; |
| 148 | 148 |
static double cosine_lut[COSINE_RESOLUTION]; |
| 149 |
- static double lanczos_lut[LANCZOS_SAMPLES]; |
|
| 149 |
+ static double lanczos_lut[LANCZOS_SAMPLES + 1]; |
|
| 150 | 150 |
|
| 151 | 151 |
Channel(); |
| 152 | 152 |
|
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* SSEQ Player - Constants/Macros |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-04-23 |
|
| 4 |
+ * Last modification on 2013-04-26 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Adapted from source code of FeOS Sound System |
| 7 | 7 |
* By fincs |
| ... | ... |
@@ -59,7 +59,7 @@ enum Interpolation |
| 59 | 59 |
INTERPOLATION_4POINTBSPLINE, |
| 60 | 60 |
INTERPOLATION_6POINTOSCULATING, |
| 61 | 61 |
INTERPOLATION_6POINTBSPLINE, |
| 62 |
- INTERPOLATION_6POINTLANCZOS |
|
| 62 |
+ INTERPOLATION_LANCZOS |
|
| 63 | 63 |
}; |
| 64 | 64 |
|
| 65 | 65 |
#endif |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* xSF - NCSF configuration |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-04-23 |
|
| 4 |
+ * Last modification on 2013-04-26 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Partially based on the vio*sf framework |
| 7 | 7 |
*/ |
| ... | ... |
@@ -42,7 +42,7 @@ public: |
| 42 | 42 |
|
| 43 | 43 |
unsigned XSFConfig::initSampleRate = 44100; |
| 44 | 44 |
std::wstring XSFConfig::commonName = L"NCSF Decoder"; |
| 45 |
-std::wstring XSFConfig::versionNumber = L"1.6"; |
|
| 45 |
+std::wstring XSFConfig::versionNumber = L"1.7"; |
|
| 46 | 46 |
unsigned XSFConfig_NCSF::initInterpolation = 5; |
| 47 | 47 |
std::wstring XSFConfig_NCSF::initMutes = L"0000000000000000"; |
| 48 | 48 |
|
| ... | ... |
@@ -101,7 +101,7 @@ INT_PTR CALLBACK XSFConfig_NCSF::ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARA |
| 101 | 101 |
SendMessageW(GetDlgItem(hwndDlg, idInterpolation), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"4-point, 3rd-order B-spline")); |
| 102 | 102 |
SendMessageW(GetDlgItem(hwndDlg, idInterpolation), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"6-point, 5th-order Osculating")); |
| 103 | 103 |
SendMessageW(GetDlgItem(hwndDlg, idInterpolation), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"6-point, 5th-order B-spline")); |
| 104 |
- SendMessageW(GetDlgItem(hwndDlg, idInterpolation), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"6-point Lanczos (Sinc)")); |
|
| 104 |
+ SendMessageW(GetDlgItem(hwndDlg, idInterpolation), CB_ADDSTRING, 0, reinterpret_cast<LPARAM>(L"Lanczos (Sinc)")); |
|
| 105 | 105 |
SendMessageW(GetDlgItem(hwndDlg, idInterpolation), CB_SETCURSEL, this->interpolation, 0); |
| 106 | 106 |
// Mutes |
| 107 | 107 |
for (int x = 0, numMutes = this->mutes.size(); x < numMutes; ++x) |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* xSF - NCSF Player |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-04-23 |
|
| 4 |
+ * Last modification on 2013-04-26 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Partially based on the vio*sf framework |
| 7 | 7 |
* |
| ... | ... |
@@ -101,11 +101,13 @@ bool XSFPlayer_NCSF::LoadNCSF() |
| 101 | 101 |
|
| 102 | 102 |
XSFPlayer_NCSF::XSFPlayer_NCSF(const std::string &filename) : XSFPlayer() |
| 103 | 103 |
{
|
| 104 |
+ this->uses32BitSamplesClampedTo16Bit = true; |
|
| 104 | 105 |
this->xSF.reset(new XSFFile(filename, 8, 12)); |
| 105 | 106 |
} |
| 106 | 107 |
|
| 107 | 108 |
XSFPlayer_NCSF::XSFPlayer_NCSF(const std::wstring &filename) : XSFPlayer() |
| 108 | 109 |
{
|
| 110 |
+ this->uses32BitSamplesClampedTo16Bit = true; |
|
| 109 | 111 |
this->xSF.reset(new XSFFile(filename, 8, 12)); |
| 110 | 112 |
} |
| 111 | 113 |
|
| ... | ... |
@@ -130,14 +132,6 @@ bool XSFPlayer_NCSF::Load() |
| 130 | 132 |
return XSFPlayer::Load(); |
| 131 | 133 |
} |
| 132 | 134 |
|
| 133 |
-template<typename T1, typename T2> static inline void clamp(T1 &valueToClamp, const T2 &minValue, const T2 &maxValue) |
|
| 134 |
-{
|
|
| 135 |
- if (valueToClamp < minValue) |
|
| 136 |
- valueToClamp = minValue; |
|
| 137 |
- else if (valueToClamp > maxValue) |
|
| 138 |
- valueToClamp = maxValue; |
|
| 139 |
-} |
|
| 140 |
- |
|
| 141 | 135 |
static inline int32_t muldiv7(int32_t val, uint8_t mul) |
| 142 | 136 |
{
|
| 143 | 137 |
return mul == 127 ? val : ((val * mul) >> 7); |
| ... | ... |
@@ -182,13 +176,14 @@ void XSFPlayer_NCSF::GenerateSamples(std::vector<uint8_t> &buf, unsigned offset, |
| 182 | 176 |
leftChannel = muldiv7(leftChannel, this->sseqVol); |
| 183 | 177 |
rightChannel = muldiv7(rightChannel, this->sseqVol); |
| 184 | 178 |
|
| 185 |
- clamp(leftChannel, -0x8000, 0x7FFF); |
|
| 186 |
- clamp(rightChannel, -0x8000, 0x7FFF); |
|
| 187 |
- |
|
| 188 | 179 |
buf[offset++] = leftChannel & 0xFF; |
| 189 | 180 |
buf[offset++] = (leftChannel >> 8) & 0xFF; |
| 181 |
+ buf[offset++] = (leftChannel >> 16) & 0xFF; |
|
| 182 |
+ buf[offset++] = (leftChannel >> 24) & 0xFF; |
|
| 190 | 183 |
buf[offset++] = rightChannel & 0xFF; |
| 191 | 184 |
buf[offset++] = (rightChannel >> 8) & 0xFF; |
| 185 |
+ buf[offset++] = (rightChannel >> 16) & 0xFF; |
|
| 186 |
+ buf[offset++] = (rightChannel >> 24) & 0xFF; |
|
| 192 | 187 |
|
| 193 | 188 |
if (this->secondsIntoPlayback > this->secondsUntilNextClock) |
| 194 | 189 |
{
|
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* xSF - Common functions |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-04-23 |
|
| 4 |
+ * Last modification on 2013-04-26 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Partially based on the vio*sf framework |
| 7 | 7 |
*/ |
| ... | ... |
@@ -66,6 +66,15 @@ template<typename T> inline T NextHighestPowerOf2(T value) |
| 66 | 66 |
return value + 1; |
| 67 | 67 |
} |
| 68 | 68 |
|
| 69 |
+// Clamp a value between a minimum and maximum value |
|
| 70 |
+template<typename T1, typename T2> inline void clamp(T1 &valueToClamp, const T2 &minValue, const T2 &maxValue) |
|
| 71 |
+{
|
|
| 72 |
+ if (valueToClamp < minValue) |
|
| 73 |
+ valueToClamp = minValue; |
|
| 74 |
+ else if (valueToClamp > maxValue) |
|
| 75 |
+ valueToClamp = maxValue; |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 69 | 78 |
inline bool FileExists(const std::string &filename) |
| 70 | 79 |
{
|
| 71 | 80 |
std::ifstream file((filename.c_str())); |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* xSF - Core Player |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-04-23 |
|
| 4 |
+ * Last modification on 2013-04-26 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Partially based on the vio*sf framework |
| 7 | 7 |
*/ |
| ... | ... |
@@ -14,13 +14,14 @@ |
| 14 | 14 |
extern XSFConfig *xSFConfig; |
| 15 | 15 |
|
| 16 | 16 |
XSFPlayer::XSFPlayer() : xSF(), sampleRate(0), detectedSilenceSample(0), detectedSilenceSec(0), skipSilenceOnStartSec(5), lengthSample(0), fadeSample(0), currentSample(0), |
| 17 |
- prevSampleL(CHECK_SILENCE_BIAS), prevSampleR(CHECK_SILENCE_BIAS), lengthInMS(-1), fadeInMS(-1), volume(1.0), ignoreVolume(false) |
|
| 17 |
+ prevSampleL(CHECK_SILENCE_BIAS), prevSampleR(CHECK_SILENCE_BIAS), lengthInMS(-1), fadeInMS(-1), volume(1.0), ignoreVolume(false), uses32BitSamplesClampedTo16Bit(false) |
|
| 18 | 18 |
{
|
| 19 | 19 |
} |
| 20 | 20 |
|
| 21 | 21 |
XSFPlayer::XSFPlayer(const XSFPlayer &xSFPlayer) : xSF(new XSFFile()), sampleRate(xSFPlayer.sampleRate), detectedSilenceSample(xSFPlayer.detectedSilenceSample), detectedSilenceSec(xSFPlayer.detectedSilenceSec), |
| 22 | 22 |
skipSilenceOnStartSec(xSFPlayer.skipSilenceOnStartSec), lengthSample(xSFPlayer.lengthSample), fadeSample(xSFPlayer.fadeSample), currentSample(xSFPlayer.currentSample), prevSampleL(xSFPlayer.prevSampleL), |
| 23 |
- prevSampleR(xSFPlayer.prevSampleR), lengthInMS(xSFPlayer.lengthInMS), fadeInMS(xSFPlayer.fadeInMS), volume(xSFPlayer.volume), ignoreVolume(xSFPlayer.ignoreVolume) |
|
| 23 |
+ prevSampleR(xSFPlayer.prevSampleR), lengthInMS(xSFPlayer.lengthInMS), fadeInMS(xSFPlayer.fadeInMS), volume(xSFPlayer.volume), ignoreVolume(xSFPlayer.ignoreVolume), |
|
| 24 |
+ uses32BitSamplesClampedTo16Bit(xSFPlayer.uses32BitSamplesClampedTo16Bit) |
|
| 24 | 25 |
{
|
| 25 | 26 |
*this->xSF = *xSFPlayer.xSF; |
| 26 | 27 |
} |
| ... | ... |
@@ -45,6 +46,7 @@ XSFPlayer &XSFPlayer::operator=(const XSFPlayer &xSFPlayer) |
| 45 | 46 |
this->fadeInMS = xSFPlayer.fadeInMS; |
| 46 | 47 |
this->volume = xSFPlayer.volume; |
| 47 | 48 |
this->ignoreVolume = xSFPlayer.ignoreVolume; |
| 49 |
+ this->uses32BitSamplesClampedTo16Bit = xSFPlayer.uses32BitSamplesClampedTo16Bit; |
|
| 48 | 50 |
} |
| 49 | 51 |
return *this; |
| 50 | 52 |
} |
| ... | ... |
@@ -54,17 +56,33 @@ bool XSFPlayer::FillBuffer(std::vector<uint8_t> &buf, unsigned &samplesWritten) |
| 54 | 56 |
bool endFlag = false; |
| 55 | 57 |
unsigned detectSilence = xSFConfig->GetDetectSilenceSec(); |
| 56 | 58 |
unsigned pos = 0, bufsize = buf.size() >> 2; |
| 59 |
+ std::vector<uint8_t> trueBuffer; |
|
| 60 |
+ if (this->uses32BitSamplesClampedTo16Bit) |
|
| 61 |
+ trueBuffer.resize(bufsize << 3); |
|
| 62 |
+ else |
|
| 63 |
+ trueBuffer.resize(bufsize << 2); |
|
| 64 |
+ auto longBuffer = std::vector<uint8_t>(bufsize << 3); |
|
| 65 |
+ int32_t *bufLong = reinterpret_cast<int32_t *>(&longBuffer[0]); |
|
| 57 | 66 |
while (pos < bufsize) |
| 58 | 67 |
{
|
| 59 | 68 |
unsigned remain = bufsize - pos, offset = pos; |
| 60 |
- this->GenerateSamples(buf, pos << 1, remain); |
|
| 69 |
+ this->GenerateSamples(trueBuffer, pos << (this->uses32BitSamplesClampedTo16Bit ? 2 : 1), remain); |
|
| 70 |
+ if (this->uses32BitSamplesClampedTo16Bit) |
|
| 71 |
+ {
|
|
| 72 |
+ int32_t *trueBufLong = reinterpret_cast<int32_t *>(&trueBuffer[0]); |
|
| 73 |
+ std::copy(&trueBufLong[0], &trueBufLong[bufsize << 1], &bufLong[0]); |
|
| 74 |
+ } |
|
| 75 |
+ else |
|
| 76 |
+ {
|
|
| 77 |
+ int16_t *trueBufShort = reinterpret_cast<int16_t *>(&trueBuffer[0]); |
|
| 78 |
+ std::copy(&trueBufShort[0], &trueBufShort[bufsize << 1], &bufLong[0]); |
|
| 79 |
+ } |
|
| 61 | 80 |
if (detectSilence || skipSilenceOnStartSec) |
| 62 | 81 |
{
|
| 63 | 82 |
unsigned skipOffset = 0; |
| 64 | 83 |
for (unsigned ofs = 0; ofs < remain; ++ofs) |
| 65 | 84 |
{
|
| 66 |
- short *bufShort = reinterpret_cast<short *>(&buf[0]); |
|
| 67 |
- unsigned long sampleL = bufShort[2 * (offset + ofs)], sampleR = bufShort[2 * (offset + ofs) + 1]; |
|
| 85 |
+ uint32_t sampleL = bufLong[2 * (offset + ofs)], sampleR = bufLong[2 * (offset + ofs) + 1]; |
|
| 68 | 86 |
bool silence = (sampleL + CHECK_SILENCE_BIAS + CHECK_SILENCE_LEVEL) - this->prevSampleL <= CHECK_SILENCE_LEVEL * 2 && |
| 69 | 87 |
(sampleR + CHECK_SILENCE_BIAS + CHECK_SILENCE_LEVEL) - this->prevSampleR <= CHECK_SILENCE_LEVEL * 2; |
| 70 | 88 |
|
| ... | ... |
@@ -101,9 +119,9 @@ bool XSFPlayer::FillBuffer(std::vector<uint8_t> &buf, unsigned &samplesWritten) |
| 101 | 119 |
{
|
| 102 | 120 |
if (skipOffset) |
| 103 | 121 |
{
|
| 104 |
- auto tmpBuf = std::vector<uint8_t>(2 * skipOffset); |
|
| 105 |
- memcpy(&tmpBuf[0], &buf[offset + 2 * skipOffset], 2 * skipOffset); |
|
| 106 |
- memcpy(&buf[offset], &tmpBuf[0], 2 * skipOffset); |
|
| 122 |
+ auto tmpBuf = std::vector<int32_t>((bufsize - skipOffset) << 1); |
|
| 123 |
+ std::copy(&bufLong[(offset + skipOffset) << 1], &bufLong[bufsize << 1], &tmpBuf[0]); |
|
| 124 |
+ std::copy(&tmpBuf[0], &tmpBuf[(bufsize - skipOffset) << 1], &bufLong[offset << 1]); |
|
| 107 | 125 |
pos += skipOffset; |
| 108 | 126 |
} |
| 109 | 127 |
else |
| ... | ... |
@@ -112,6 +130,19 @@ bool XSFPlayer::FillBuffer(std::vector<uint8_t> &buf, unsigned &samplesWritten) |
| 112 | 130 |
} |
| 113 | 131 |
else |
| 114 | 132 |
pos += remain; |
| 133 |
+ if (pos < bufsize) |
|
| 134 |
+ {
|
|
| 135 |
+ if (this->uses32BitSamplesClampedTo16Bit) |
|
| 136 |
+ {
|
|
| 137 |
+ int32_t *trueBufLong = reinterpret_cast<int32_t *>(&trueBuffer[0]); |
|
| 138 |
+ std::copy(&bufLong[0], &bufLong[bufsize << 1], &trueBufLong[0]); |
|
| 139 |
+ } |
|
| 140 |
+ else |
|
| 141 |
+ {
|
|
| 142 |
+ int16_t *trueBufShort = reinterpret_cast<int16_t *>(&trueBuffer[0]); |
|
| 143 |
+ std::copy(&bufLong[0], &bufLong[bufsize << 1], &trueBufShort[0]); |
|
| 144 |
+ } |
|
| 145 |
+ } |
|
| 115 | 146 |
} |
| 116 | 147 |
|
| 117 | 148 |
/* Detect end of song */ |
| ... | ... |
@@ -133,27 +164,42 @@ bool XSFPlayer::FillBuffer(std::vector<uint8_t> &buf, unsigned &samplesWritten) |
| 133 | 164 |
if (!this->ignoreVolume && (!fEqual(this->volume, 1.0) || !fEqual(xSFConfig->GetVolume(), 1.0))) |
| 134 | 165 |
{
|
| 135 | 166 |
double scale = this->volume * xSFConfig->GetVolume(); |
| 136 |
- short *bufShort = reinterpret_cast<short *>(&buf[0]); |
|
| 137 | 167 |
for (unsigned ofs = 0; ofs < bufsize; ++ofs) |
| 138 | 168 |
{
|
| 139 |
- double s1 = bufShort[2 * ofs] * scale, s2 = bufShort[2 * ofs + 1] * scale; |
|
| 140 |
- if (s1 > 0x7FFF) |
|
| 141 |
- s1 = 0x7FFF; |
|
| 142 |
- else if (s1 < -0x8000) |
|
| 143 |
- s1 = -0x8000; |
|
| 144 |
- if (s2 > 0x7FFF) |
|
| 145 |
- s2 = 0x7FFF; |
|
| 146 |
- else if (s2 < -0x8000) |
|
| 147 |
- s2 = -0x8000; |
|
| 148 |
- bufShort[2 * ofs] = static_cast<short>(s1); |
|
| 149 |
- bufShort[2 * ofs + 1] = static_cast<short>(s2); |
|
| 169 |
+ double s1 = bufLong[2 * ofs] * scale, s2 = bufLong[2 * ofs + 1] * scale; |
|
| 170 |
+ if (!this->uses32BitSamplesClampedTo16Bit) |
|
| 171 |
+ {
|
|
| 172 |
+ clamp(s1, -0x7FFF, 0x8000); |
|
| 173 |
+ clamp(s2, -0x7FFF, 0x8000); |
|
| 174 |
+ } |
|
| 175 |
+ bufLong[2 * ofs] = static_cast<int32_t>(s1); |
|
| 176 |
+ bufLong[2 * ofs + 1] = static_cast<int32_t>(s2); |
|
| 177 |
+ } |
|
| 178 |
+ } |
|
| 179 |
+ |
|
| 180 |
+ if (this->uses32BitSamplesClampedTo16Bit) |
|
| 181 |
+ {
|
|
| 182 |
+ int16_t *bufShort = reinterpret_cast<int16_t *>(&buf[0]); |
|
| 183 |
+ for (unsigned ofs = 0; ofs < bufsize; ++ofs) |
|
| 184 |
+ {
|
|
| 185 |
+ int32_t s1 = bufLong[2 * ofs], s2 = bufLong[2 * ofs + 1]; |
|
| 186 |
+ clamp(s1, -0x7FFF, 0x8000); |
|
| 187 |
+ clamp(s2, -0x7FFF, 0x8000); |
|
| 188 |
+ bufShort[2 * ofs] = static_cast<int16_t>(s1); |
|
| 189 |
+ bufShort[2 * ofs + 1] = static_cast<int16_t>(s2); |
|
| 150 | 190 |
} |
| 151 | 191 |
} |
| 192 |
+ else |
|
| 193 |
+ {
|
|
| 194 |
+ int16_t *trueBufShort = reinterpret_cast<int16_t *>(&trueBuffer[0]); |
|
| 195 |
+ std::copy(&bufLong[0], &bufLong[bufsize << 1], &trueBufShort[0]); |
|
| 196 |
+ std::copy(&trueBuffer[0], &trueBuffer[bufsize << 2], &buf[0]); |
|
| 197 |
+ } |
|
| 152 | 198 |
|
| 153 | 199 |
/* Fading */ |
| 154 | 200 |
if (!xSFConfig->GetPlayInfinitely() && this->fadeSample && this->currentSample + bufsize >= this->lengthSample) |
| 155 | 201 |
{
|
| 156 |
- short *bufShort = reinterpret_cast<short *>(&buf[0]); |
|
| 202 |
+ int16_t *bufShort = reinterpret_cast<int16_t *>(&buf[0]); |
|
| 157 | 203 |
for (unsigned ofs = 0; ofs < bufsize; ++ofs) |
| 158 | 204 |
{
|
| 159 | 205 |
if (this->currentSample + ofs >= this->lengthSample && this->currentSample + ofs < this->lengthSample + this->fadeSample) |
| ... | ... |
@@ -1,7 +1,7 @@ |
| 1 | 1 |
/* |
| 2 | 2 |
* xSF - Core Player |
| 3 | 3 |
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com] |
| 4 |
- * Last modification on 2013-04-23 |
|
| 4 |
+ * Last modification on 2013-04-26 |
|
| 5 | 5 |
* |
| 6 | 6 |
* Partially based on the vio*sf framework |
| 7 | 7 |
*/ |
| ... | ... |
@@ -19,15 +19,15 @@ |
| 19 | 19 |
class XSFPlayer |
| 20 | 20 |
{
|
| 21 | 21 |
protected: |
| 22 |
- static const unsigned CHECK_SILENCE_BIAS = 0x8000000; |
|
| 23 |
- static const unsigned CHECK_SILENCE_LEVEL = 7; |
|
| 22 |
+ static const uint32_t CHECK_SILENCE_BIAS = 0x8000000; |
|
| 23 |
+ static const uint32_t CHECK_SILENCE_LEVEL = 7; |
|
| 24 | 24 |
|
| 25 | 25 |
std::unique_ptr<XSFFile> xSF; |
| 26 | 26 |
unsigned sampleRate, detectedSilenceSample, detectedSilenceSec, skipSilenceOnStartSec, lengthSample, fadeSample, currentSample; |
| 27 |
- unsigned long prevSampleL, prevSampleR; |
|
| 27 |
+ uint32_t prevSampleL, prevSampleR; |
|
| 28 | 28 |
int lengthInMS, fadeInMS; |
| 29 | 29 |
double volume; |
| 30 |
- bool ignoreVolume; |
|
| 30 |
+ bool ignoreVolume, uses32BitSamplesClampedTo16Bit; |
|
| 31 | 31 |
|
| 32 | 32 |
XSFPlayer(); |
| 33 | 33 |
XSFPlayer(const XSFPlayer &xSFPLayer); |