Updated NCSF player
This commit is contained in:
parent
9eb0037780
commit
7896e73080
29 changed files with 2842 additions and 1396 deletions
|
@ -1,220 +0,0 @@
|
|||
/*
|
||||
* String class in ANSI (or rather, current Windows code page), UTF-8, and UTF-16
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-25
|
||||
*/
|
||||
|
||||
#ifndef BIGSSTRING_H
|
||||
#define BIGSSTRING_H
|
||||
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include "UtfConverter.h"
|
||||
#include "UTFEncodeDecode.h"
|
||||
|
||||
class String
|
||||
{
|
||||
public:
|
||||
String(const std::locale &L = std::locale::classic()) : ansi(""), utf8(""), utf16(L""), loc(L) { }
|
||||
String(const char *new_str, bool is_utf8 = false, const std::locale &L = std::locale::classic()) : ansi(is_utf8 ? DecodeFromUTF8(new_str, L) : new_str), utf8(is_utf8 ? new_str : EncodeToUTF8(new_str, L)),
|
||||
utf16(UtfConverter::FromUtf8(utf8)), loc(L) { }
|
||||
String(const std::string &new_str, bool is_utf8 = false, const std::locale &L = std::locale::classic()) : ansi(is_utf8 ? DecodeFromUTF8(new_str, L) : new_str), utf8(is_utf8 ? new_str : EncodeToUTF8(new_str, L)),
|
||||
utf16(UtfConverter::FromUtf8(utf8)), loc(L) { }
|
||||
String(const wchar_t *new_wstr, const std::locale &L = std::locale::classic()) : ansi(DecodeFromUTF8(UtfConverter::ToUtf8(new_wstr), L)), utf8(UtfConverter::ToUtf8(new_wstr)), utf16(new_wstr), loc(L) { }
|
||||
String(const std::wstring &new_wstr, const std::locale &L = std::locale::classic()) : ansi(DecodeFromUTF8(UtfConverter::ToUtf8(new_wstr), L)), utf8(UtfConverter::ToUtf8(new_wstr)), utf16(new_wstr), loc(L) { }
|
||||
bool empty() const { return this->utf8.empty(); }
|
||||
std::string GetAnsi() const { return this->ansi; }
|
||||
const char *GetAnsiC() const { return this->ansi.c_str(); }
|
||||
std::string GetStr() const { return this->utf8; }
|
||||
const char *GetStrC() const { return this->utf8.c_str(); }
|
||||
std::wstring GetWStr() const { return this->utf16; }
|
||||
const wchar_t *GetWStrC() const { return this->utf16.c_str(); }
|
||||
bool operator==(const String &str2) const
|
||||
{
|
||||
return this->utf8 == str2.utf8;
|
||||
}
|
||||
String &operator=(const std::string &new_str)
|
||||
{
|
||||
this->ansi = DecodeFromUTF8(new_str, this->loc);
|
||||
this->utf8 = new_str;
|
||||
this->utf16 = UtfConverter::FromUtf8(new_str);
|
||||
return *this;
|
||||
}
|
||||
String &operator=(const std::wstring &new_wstr)
|
||||
{
|
||||
this->utf8 = UtfConverter::ToUtf8(new_wstr);
|
||||
this->ansi = DecodeFromUTF8(this->utf8, this->loc);
|
||||
this->utf16 = new_wstr;
|
||||
return *this;
|
||||
}
|
||||
String &operator=(const String &new_string)
|
||||
{
|
||||
if (this != &new_string)
|
||||
{
|
||||
this->ansi = new_string.ansi;
|
||||
this->utf8 = new_string.utf8;
|
||||
this->utf16 = new_string.utf16;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
String &SetISO8859_1(const std::string &new_str)
|
||||
{
|
||||
this->ansi = new_str;
|
||||
this->utf8 = EncodeToUTF8(new_str, this->loc);
|
||||
this->utf16 = UtfConverter::FromUtf8(this->utf8);
|
||||
return *this;
|
||||
}
|
||||
String operator+(const String &str2) const
|
||||
{
|
||||
String new_string = String(*this);
|
||||
new_string.ansi.append(str2.ansi);
|
||||
new_string.utf8.append(str2.utf8);
|
||||
new_string.utf16.append(str2.utf16);
|
||||
return new_string;
|
||||
}
|
||||
String operator+(char chr) const
|
||||
{
|
||||
String new_string = String(*this);
|
||||
char str2[] = { chr, 0 };
|
||||
new_string.ansi.append(str2);
|
||||
std::string new_utf8 = EncodeToUTF8(str2, this->loc);
|
||||
new_string.utf8.append(new_utf8);
|
||||
new_string.utf16.append(UtfConverter::FromUtf8(new_utf8));
|
||||
return new_string;
|
||||
}
|
||||
String operator+(wchar_t wchr) const
|
||||
{
|
||||
String new_string = String(*this);
|
||||
wchar_t wstr2[] = { wchr, 0 };
|
||||
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
|
||||
new_string.ansi.append(DecodeFromUTF8(new_utf8, this->loc));
|
||||
new_string.utf8.append(new_utf8);
|
||||
new_string.utf16.append(wstr2);
|
||||
return new_string;
|
||||
}
|
||||
String operator+(const char *str2) const
|
||||
{
|
||||
String new_string = String(*this);
|
||||
new_string.ansi.append(DecodeFromUTF8(str2, this->loc));
|
||||
new_string.utf8.append(str2);
|
||||
new_string.utf16.append(UtfConverter::FromUtf8(str2));
|
||||
return new_string;
|
||||
}
|
||||
String operator+(const std::string &str2) const
|
||||
{
|
||||
String new_string = String(*this);
|
||||
new_string.ansi.append(DecodeFromUTF8(str2, this->loc));
|
||||
new_string.utf8.append(str2);
|
||||
new_string.utf16.append(UtfConverter::FromUtf8(str2));
|
||||
return new_string;
|
||||
}
|
||||
String operator+(const wchar_t *wstr2) const
|
||||
{
|
||||
String new_string = String(*this);
|
||||
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
|
||||
new_string.ansi.append(DecodeFromUTF8(new_utf8, this->loc));
|
||||
new_string.utf8.append(new_utf8);
|
||||
new_string.utf16.append(wstr2);
|
||||
return new_string;
|
||||
}
|
||||
String operator+(const std::wstring &wstr2) const
|
||||
{
|
||||
String new_string = String(*this);
|
||||
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
|
||||
new_string.ansi.append(DecodeFromUTF8(new_utf8, this->loc));
|
||||
new_string.utf8.append(new_utf8);
|
||||
new_string.utf16.append(wstr2);
|
||||
return new_string;
|
||||
}
|
||||
String &operator+=(const String &str2)
|
||||
{
|
||||
if (this != &str2)
|
||||
{
|
||||
this->ansi.append(str2.ansi);
|
||||
this->utf8.append(str2.utf8);
|
||||
this->utf16.append(str2.utf16);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
String &operator+=(char chr)
|
||||
{
|
||||
char str2[] = { chr, 0 };
|
||||
this->ansi.append(str2);
|
||||
std::string new_utf8 = EncodeToUTF8(str2, this->loc);
|
||||
this->utf8.append(new_utf8);
|
||||
this->utf16.append(UtfConverter::FromUtf8(new_utf8));
|
||||
return *this;
|
||||
}
|
||||
String &operator+=(wchar_t wchr)
|
||||
{
|
||||
wchar_t wstr2[] = { wchr, 0 };
|
||||
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
|
||||
this->ansi.append(DecodeFromUTF8(new_utf8, this->loc));
|
||||
this->utf8.append(new_utf8);
|
||||
this->utf16.append(wstr2);
|
||||
return *this;
|
||||
}
|
||||
String &operator+=(const char *str2)
|
||||
{
|
||||
this->ansi.append(DecodeFromUTF8(str2, this->loc));
|
||||
this->utf8.append(str2);
|
||||
this->utf16.append(UtfConverter::FromUtf8(str2));
|
||||
return *this;
|
||||
}
|
||||
String &operator+=(const std::string &str2)
|
||||
{
|
||||
this->ansi.append(DecodeFromUTF8(str2, this->loc));
|
||||
this->utf8.append(str2);
|
||||
this->utf16.append(UtfConverter::FromUtf8(str2));
|
||||
return *this;
|
||||
}
|
||||
String &operator+=(const wchar_t *wstr2)
|
||||
{
|
||||
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
|
||||
this->ansi.append(DecodeFromUTF8(new_utf8, this->loc));
|
||||
this->utf8.append(new_utf8);
|
||||
this->utf16.append(wstr2);
|
||||
return *this;
|
||||
}
|
||||
String &operator+=(const std::wstring &wstr2)
|
||||
{
|
||||
std::string new_utf8 = UtfConverter::ToUtf8(wstr2);
|
||||
this->ansi.append(DecodeFromUTF8(new_utf8, this->loc));
|
||||
this->utf8.append(new_utf8);
|
||||
this->utf16.append(wstr2);
|
||||
return *this;
|
||||
}
|
||||
String &AppendISO8859_1(const std::string &str2)
|
||||
{
|
||||
this->ansi.append(str2);
|
||||
std::string new_utf8 = EncodeToUTF8(str2, this->loc);
|
||||
this->utf8.append(new_utf8);
|
||||
this->utf16.append(UtfConverter::FromUtf8(new_utf8));
|
||||
return *this;
|
||||
}
|
||||
String Substring(size_t pos = 0, size_t n = std::string::npos) const
|
||||
{
|
||||
String new_string = String(*this);
|
||||
new_string.ansi.substr(pos, n);
|
||||
new_string.utf8.substr(pos, n);
|
||||
if (n == std::string::npos)
|
||||
n = std::wstring::npos;
|
||||
new_string.utf16.substr(pos, n);
|
||||
return new_string;
|
||||
}
|
||||
void CopyToString(wchar_t *str, bool = false) const
|
||||
{
|
||||
wcscpy(str, utf16.c_str());
|
||||
}
|
||||
void CopyToString(char *str, bool use_utf8 = false) const
|
||||
{
|
||||
strcpy(str, (use_utf8 ? utf8 : ansi).c_str());
|
||||
}
|
||||
protected:
|
||||
std::string ansi, utf8;
|
||||
std::wstring utf16;
|
||||
std::locale loc;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* SSEQ Player - Channel structures
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-23
|
||||
* Last modification on 2014-10-23
|
||||
*
|
||||
* Adapted from source code of FeOS Sound System
|
||||
* By fincs
|
||||
|
@ -12,7 +12,6 @@
|
|||
*/
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cstdlib>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include "Channel.h"
|
||||
|
@ -46,8 +45,8 @@ TempSndReg::TempSndReg() : CR(0), SOURCE(nullptr), TIMER(0), REPEAT_POINT(0), LE
|
|||
}
|
||||
|
||||
bool Channel::initializedLUTs = false;
|
||||
double Channel::cosine_lut[Channel::COSINE_RESOLUTION];
|
||||
double Channel::lanczos_lut[Channel::LANCZOS_SAMPLES + 1];
|
||||
double Channel::sinc_lut[Channel::SINC_SAMPLES + 1];
|
||||
double Channel::window_lut[Channel::SINC_SAMPLES + 1];
|
||||
|
||||
#ifndef M_PI
|
||||
static const double M_PI = 3.14159265358979323846;
|
||||
|
@ -73,18 +72,22 @@ Channel::Channel() : chnId(-1), tempReg(), state(CS_NONE), trackId(-1), prio(0),
|
|||
this->clearHistory();
|
||||
if (!this->initializedLUTs)
|
||||
{
|
||||
for (unsigned i = 0; i < COSINE_RESOLUTION; ++i)
|
||||
this->cosine_lut[i] = (1.0 - std::cos((static_cast<double>(i) / COSINE_RESOLUTION) * M_PI)) * 0.5;
|
||||
double dx = static_cast<double>(LANCZOS_WIDTH) / LANCZOS_SAMPLES, x = 0.0;
|
||||
for (unsigned i = 0; i <= LANCZOS_SAMPLES; ++i, x += dx)
|
||||
this->lanczos_lut[i] = std::abs(x) < LANCZOS_WIDTH ? sinc(x) * sinc(x / LANCZOS_WIDTH) : 0.0;
|
||||
double dx = static_cast<double>(SINC_WIDTH) / SINC_SAMPLES, x = 0.0;
|
||||
for (unsigned i = 0; i <= SINC_SAMPLES; ++i, x += dx)
|
||||
{
|
||||
double y = x / SINC_WIDTH;
|
||||
this->sinc_lut[i] = std::abs(x) < SINC_WIDTH ? sinc(x) : 0.0;
|
||||
this->window_lut[i] = (0.40897 + 0.5 * std::cos(M_PI * y) + 0.09103 * std::cos(2 * M_PI * y));
|
||||
}
|
||||
this->initializedLUTs = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_UpdateVol
|
||||
void Channel::UpdateVol(const Track &trk)
|
||||
{
|
||||
int finalVol = trk.ply->masterVol;
|
||||
finalVol += trk.ply->sseqVol;
|
||||
finalVol += Cnv_Sust(trk.vol);
|
||||
finalVol += Cnv_Sust(trk.expr);
|
||||
if (finalVol < -AMPL_K)
|
||||
|
@ -92,11 +95,13 @@ void Channel::UpdateVol(const Track &trk)
|
|||
this->extAmpl = finalVol;
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_UpdatePan
|
||||
void Channel::UpdatePan(const Track &trk)
|
||||
{
|
||||
this->extPan = trk.pan;
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_UpdateTune
|
||||
void Channel::UpdateTune(const Track &trk)
|
||||
{
|
||||
int tune = (static_cast<int>(this->key) - static_cast<int>(this->orgKey)) * 64;
|
||||
|
@ -104,6 +109,7 @@ void Channel::UpdateTune(const Track &trk)
|
|||
this->extTune = tune;
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_UpdateMod
|
||||
void Channel::UpdateMod(const Track &trk)
|
||||
{
|
||||
this->modType = trk.modType;
|
||||
|
@ -113,6 +119,7 @@ void Channel::UpdateMod(const Track &trk)
|
|||
this->modDelay = trk.modDelay;
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_UpdatePorta
|
||||
void Channel::UpdatePorta(const Track &trk)
|
||||
{
|
||||
this->manualSweep = false;
|
||||
|
@ -135,11 +142,12 @@ void Channel::UpdatePorta(const Track &trk)
|
|||
else
|
||||
{
|
||||
int sq_time = static_cast<uint32_t>(trk.portaTime) * static_cast<uint32_t>(trk.portaTime);
|
||||
long abs_sp = std::abs((long)this->sweepPitch);
|
||||
this->sweepLen = (uint32_t)((abs_sp * sq_time) >> 11);
|
||||
int abs_sp = std::abs(this->sweepPitch);
|
||||
this->sweepLen = (abs_sp * sq_time) >> 11;
|
||||
}
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_Release
|
||||
void Channel::Release()
|
||||
{
|
||||
this->noteLength = -1;
|
||||
|
@ -147,6 +155,7 @@ void Channel::Release()
|
|||
this->state = CS_RELEASE;
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_Kill
|
||||
void Channel::Kill()
|
||||
{
|
||||
this->state = CS_NONE;
|
||||
|
@ -173,6 +182,7 @@ static inline int getModFlag(int type)
|
|||
}
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_UpdateTracks
|
||||
void Channel::UpdateTrack()
|
||||
{
|
||||
if (!this->ply)
|
||||
|
@ -182,11 +192,11 @@ void Channel::UpdateTrack()
|
|||
if (trkn == -1)
|
||||
return;
|
||||
|
||||
auto &trackFlags = this->ply->tracks[trkn].updateFlags;
|
||||
auto &trk = this->ply->tracks[trkn];
|
||||
auto &trackFlags = trk.updateFlags;
|
||||
if (trackFlags.none())
|
||||
return;
|
||||
|
||||
auto &trk = this->ply->tracks[trkn];
|
||||
if (trackFlags[TUF_LEN])
|
||||
{
|
||||
int st = this->state;
|
||||
|
@ -405,7 +415,7 @@ static inline uint16_t Timer_Adjust(uint16_t basetmr, int pitch)
|
|||
tmr <<= shift;
|
||||
}
|
||||
else
|
||||
return 0x10;
|
||||
return 0xFFFF;
|
||||
|
||||
if (tmr < 0x10)
|
||||
return 0x10;
|
||||
|
@ -425,6 +435,7 @@ static inline int calcVolDivShift(int x)
|
|||
return 4;
|
||||
}
|
||||
|
||||
// Original FSS Function: Snd_UpdChannel
|
||||
void Channel::Update()
|
||||
{
|
||||
// Kill active channels that aren't physically active
|
||||
|
@ -457,10 +468,17 @@ void Channel::Update()
|
|||
this->state = CS_ATTACK;
|
||||
// Fall down
|
||||
case CS_ATTACK:
|
||||
this->ampl = (static_cast<int>(this->ampl) * static_cast<int>(this->attackLvl)) / 255;
|
||||
{
|
||||
int newAmpl = this->ampl;
|
||||
int oldAmpl = this->ampl >> 7;
|
||||
do
|
||||
newAmpl = (newAmpl * static_cast<int>(this->attackLvl)) / 256;
|
||||
while ((newAmpl >> 7) == oldAmpl);
|
||||
this->ampl = newAmpl;
|
||||
if (!this->ampl)
|
||||
this->state = CS_DECAY;
|
||||
break;
|
||||
}
|
||||
case CS_DECAY:
|
||||
{
|
||||
this->ampl -= static_cast<int>(this->decayRate);
|
||||
|
@ -474,10 +492,11 @@ void Channel::Update()
|
|||
}
|
||||
case CS_RELEASE:
|
||||
this->ampl -= static_cast<int>(this->releaseRate);
|
||||
if (this->ampl > AMPL_THRESHOLD)
|
||||
break;
|
||||
this->Kill();
|
||||
return;
|
||||
if (this->ampl <= AMPL_THRESHOLD)
|
||||
{
|
||||
this->Kill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bModulation && this->modDelayCnt < this->modDelay)
|
||||
|
@ -504,7 +523,7 @@ void Channel::Update()
|
|||
modParam = Cnv_Sine(this->modCounter >> 8) * this->modRange * this->modDepth;
|
||||
|
||||
if (!this->modType)
|
||||
modParam = static_cast<int>(static_cast<int64_t>(modParam * 60) >> 14);
|
||||
modParam = static_cast<int64_t>(modParam * 60) >> 14;
|
||||
else
|
||||
// This ugly formula whose exact meaning and workings I cannot figure out is used for volume/pan modulation.
|
||||
modParam = ((modParam & ~0xFC000000) >> 8) | ((((modParam < 0 ? -1 : 0) << 6) | (static_cast<uint32_t>(modParam) >> 26)) << 18);
|
||||
|
@ -555,8 +574,7 @@ void Channel::Update()
|
|||
if (bModulation && this->modType == 1)
|
||||
totalVol += modParam;
|
||||
totalVol += AMPL_K;
|
||||
if (totalVol < 0)
|
||||
totalVol = 0;
|
||||
clamp(totalVol, 0, AMPL_K);
|
||||
|
||||
cr &= ~(SOUND_VOL(0x7F) | SOUND_VOLDIV(3));
|
||||
cr |= SOUND_VOL(static_cast<int>(getvoltbl[totalVol]));
|
||||
|
@ -580,10 +598,7 @@ void Channel::Update()
|
|||
if (bModulation && this->modType == 2)
|
||||
realPan += modParam;
|
||||
realPan += 64;
|
||||
if (realPan < 0)
|
||||
realPan = 0;
|
||||
else if (realPan > 127)
|
||||
realPan = 127;
|
||||
clamp(realPan, 0, 127);
|
||||
|
||||
cr &= ~SOUND_PAN(0x7F);
|
||||
cr |= SOUND_PAN(realPan);
|
||||
|
@ -607,8 +622,8 @@ static const int16_t wavedutytbl[8][8] =
|
|||
{ -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF, -0x7FFF }
|
||||
};
|
||||
|
||||
// Linear and Cosine interpolation code originally from DeSmuME
|
||||
// B-spline and Osculating come from Olli Niemitalo:
|
||||
// Linear interpolation code originally from DeSmuME
|
||||
// Legrange comes from Olli Niemitalo:
|
||||
// http://www.student.oulu.fi/~oniemita/dsp/deip.pdf
|
||||
int32_t Channel::Interpolate()
|
||||
{
|
||||
|
@ -617,68 +632,51 @@ int32_t Channel::Interpolate()
|
|||
|
||||
const auto &data = &this->sampleHistory[this->sampleHistoryPtr + 16];
|
||||
|
||||
if (this->ply->interpolation == INTERPOLATION_LANCZOS)
|
||||
if (this->ply->interpolation == INTERPOLATION_SINC)
|
||||
{
|
||||
double kernel[LANCZOS_WIDTH * 2], kernel_sum = 0.0;
|
||||
int i = LANCZOS_WIDTH, shift = static_cast<int>(std::floor(ratio * LANCZOS_RESOLUTION));
|
||||
int step = this->reg.sampleIncrease > 1.0 ? static_cast<int>((1.0 / this->reg.sampleIncrease) * LANCZOS_RESOLUTION) : LANCZOS_RESOLUTION;
|
||||
long shift_adj = shift * step / LANCZOS_RESOLUTION;
|
||||
for (; i >= -static_cast<int>(LANCZOS_WIDTH - 1); --i)
|
||||
double kernel[SINC_WIDTH * 2], kernel_sum = 0.0;
|
||||
int i = SINC_WIDTH, shift = static_cast<int>(std::floor(ratio * SINC_RESOLUTION));
|
||||
int step = this->reg.sampleIncrease > 1.0 ? static_cast<int>(SINC_RESOLUTION / this->reg.sampleIncrease) : SINC_RESOLUTION;
|
||||
int shift_adj = shift * step / SINC_RESOLUTION;
|
||||
const int window_step = SINC_RESOLUTION;
|
||||
for (; i >= -static_cast<int>(SINC_WIDTH - 1); --i)
|
||||
{
|
||||
long pos = i * step;
|
||||
kernel_sum += kernel[i + LANCZOS_WIDTH - 1] = this->lanczos_lut[std::abs(shift_adj - pos)];
|
||||
int pos = i * step;
|
||||
int window_pos = i * window_step;
|
||||
kernel_sum += kernel[i + SINC_WIDTH - 1] = this->sinc_lut[std::abs(shift_adj - pos)] * this->window_lut[std::abs(shift - window_pos)];
|
||||
}
|
||||
double sum = 0.0;
|
||||
for (i = 0; i < static_cast<int>(LANCZOS_WIDTH * 2); ++i)
|
||||
sum += data[i - static_cast<int>(LANCZOS_WIDTH) + 1] * kernel[i];
|
||||
for (i = 0; i < static_cast<int>(SINC_WIDTH * 2); ++i)
|
||||
sum += data[i - static_cast<int>(SINC_WIDTH) + 1] * kernel[i];
|
||||
return static_cast<int32_t>(sum / kernel_sum);
|
||||
}
|
||||
else if (this->ply->interpolation > INTERPOLATION_COSINE)
|
||||
else if (this->ply->interpolation > INTERPOLATION_LINEAR)
|
||||
{
|
||||
double c0, c1, c2, c3, c4, c5;
|
||||
|
||||
if (this->ply->interpolation > INTERPOLATION_4POINTBSPLINE)
|
||||
if (this->ply->interpolation == INTERPOLATION_6POINTLEGRANGE)
|
||||
{
|
||||
if (this->ply->interpolation == INTERPOLATION_6POINTBSPLINE)
|
||||
{
|
||||
double ym2py2 = data[-2] + data[2], ym1py1 = data[-1] + data[1];
|
||||
double y2mym2 = data[2] - data[-2], y1mym1 = data[1] - data[-1];
|
||||
double sixthym1py1 = 1 / 6.0 * ym1py1;
|
||||
c0 = 1 / 120.0 * ym2py2 + 13 / 60.0 * ym1py1 + 0.55 * data[0];
|
||||
c1 = 1 / 24.0 * y2mym2 + 5 / 12.0 * y1mym1;
|
||||
c2 = 1 / 12.0 * ym2py2 + sixthym1py1 - 0.5 * data[0];
|
||||
c3 = 1 / 12.0 * y2mym2 - 1 / 6.0 * y1mym1;
|
||||
c4 = 1 / 24.0 * ym2py2 - sixthym1py1 + 0.25 * data[0];
|
||||
c5 = 1 / 120.0 * (data[3] - data[-2]) + 1 / 24.0 * (data[-1] - data[2]) + 1 / 12.0 * (data[1] - data[0]);
|
||||
return static_cast<int32_t>(((((c5 * ratio + c4) * ratio + c3) * ratio + c2) * ratio + c1) * ratio + c0);
|
||||
}
|
||||
else // INTERPOLATION_6POINTOSCULATING
|
||||
{
|
||||
ratio -= 0.5;
|
||||
double even1 = data[-2] + data[3], odd1 = data[-2] - data[3];
|
||||
double even2 = data[-1] + data[2], odd2 = data[-1] - data[2];
|
||||
double even3 = data[0] + data[1], odd3 = data[0] - data[1];
|
||||
c0 = 0.01171875 * even1 - 0.09765625 * even2 + 0.5859375 * even3;
|
||||
c1 = 0.2109375 * odd2 - 281 / 192.0 * odd3 - 13 / 384.0 * odd1;
|
||||
c2 = 0.40625 * even2 - 17 / 48.0 * even3 - 5 / 96.0 * even1;
|
||||
c3 = 0.1875 * odd1 - 53 / 48.0 * odd2 + 2.375 * odd3;
|
||||
c4 = 1 / 48.0 * even1 - 0.0625 * even2 + 1 / 24.0 * even3;
|
||||
c5 = 25 / 24.0 * odd2 - 25 / 12.0 * odd3 - 5 / 24.0 * odd1;
|
||||
return static_cast<int32_t>(((((c5 * ratio + c4) * ratio + c3) * ratio + c2) * ratio + c1) * ratio + c0);
|
||||
}
|
||||
ratio -= 0.5;
|
||||
double even1 = data[-2] + data[3], odd1 = data[-2] - data[3];
|
||||
double even2 = data[-1] + data[2], odd2 = data[-1] - data[2];
|
||||
double even3 = data[0] + data[1], odd3 = data[0] - data[1];
|
||||
c0 = 0.01171875 * even1 - 0.09765625 * even2 + 0.5859375 * even3;
|
||||
c1 = 25 / 384.0 * odd2 - 1.171875 * odd3 - 0.0046875 * odd1;
|
||||
c2 = 0.40625 * even2 - 17 / 48.0 * even3 - 5 / 96.0 * even1;
|
||||
c3 = 1 / 48.0 * odd1 - 13 / 48.0 * odd2 + 17 / 24.0 * odd3;
|
||||
c4 = 1 / 48.0 * even1 - 0.0625 * even2 + 1 / 24.0 * even3;
|
||||
c5 = 1 / 24.0 * odd2 - 1 / 12.0 * odd3 - 1 / 120.0 * odd1;
|
||||
return static_cast<int32_t>(((((c5 * ratio + c4) * ratio + c3) * ratio + c2) * ratio + c1) * ratio + c0);
|
||||
}
|
||||
else // INTERPOLATION_4POINTBSPLINE
|
||||
else // INTERPOLATION_4POINTLEAGRANGE
|
||||
{
|
||||
double ym1py1 = data[-1] + data[1];
|
||||
c0 = 1 / 6.0 * ym1py1 + 2 / 3.0 * data[0];
|
||||
c1 = 0.5 * (data[1] - data[-1]);
|
||||
c2 = 0.5 * ym1py1 - data[0];
|
||||
c3 = 0.5 * (data[0] - data[1]) + 1 / 6.0 * (data[2] - data[-1]);
|
||||
c0 = data[0];
|
||||
c1 = data[1] - 1 / 3.0 * data[-1] - 0.5 * data[0] - 1 / 6.0 * data[2];
|
||||
c2 = 0.5 * (data[-1] + data[1]) - data[0];
|
||||
c3 = 1 / 6.0 * (data[2] - data[-1]) + 0.5 * (data[0] - data[1]);
|
||||
return static_cast<int32_t>(((c3 * ratio + c2) * ratio + c1) * ratio + c0);
|
||||
}
|
||||
}
|
||||
else if (this->ply->interpolation == INTERPOLATION_COSINE)
|
||||
return static_cast<int32_t>(data[0] + this->cosine_lut[static_cast<unsigned>(ratio * COSINE_RESOLUTION)] * (data[1] - data[0]));
|
||||
else // INTERPOLATION_LINEAR
|
||||
return static_cast<int32_t>(data[0] + ratio * (data[1] - data[0]));
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* SSEQ Player - Channel structures
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-02
|
||||
* Last modification on 2014-09-17
|
||||
*
|
||||
* Adapted from source code of FeOS Sound System
|
||||
* By fincs
|
||||
|
@ -11,8 +11,7 @@
|
|||
* http://desmume.org/
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_CHANNEL_H
|
||||
#define SSEQPLAYER_CHANNEL_H
|
||||
#pragma once
|
||||
|
||||
#include <bitset>
|
||||
#include <tuple>
|
||||
|
@ -136,18 +135,17 @@ struct Channel
|
|||
int16_t sampleHistory[64];
|
||||
|
||||
/*
|
||||
* Lookup tables for the cosine and Lanczos Sinc interpolations, to
|
||||
* Lookup tables for the Sinc interpolation, to
|
||||
* avoid the need to call the sin/cos functions all the time.
|
||||
* These are static as they will not change between channels or runs
|
||||
* of the program.
|
||||
*/
|
||||
static bool initializedLUTs;
|
||||
static const unsigned COSINE_RESOLUTION = 8192;
|
||||
static const unsigned LANCZOS_RESOLUTION = 8192;
|
||||
static const unsigned LANCZOS_WIDTH = 8;
|
||||
static const unsigned LANCZOS_SAMPLES = LANCZOS_RESOLUTION * LANCZOS_WIDTH;
|
||||
static double cosine_lut[COSINE_RESOLUTION];
|
||||
static double lanczos_lut[LANCZOS_SAMPLES + 1];
|
||||
static const unsigned SINC_RESOLUTION = 8192;
|
||||
static const unsigned SINC_WIDTH = 8;
|
||||
static const unsigned SINC_SAMPLES = SINC_RESOLUTION * SINC_WIDTH;
|
||||
static double sinc_lut[SINC_SAMPLES + 1];
|
||||
static double window_lut[SINC_SAMPLES + 1];
|
||||
|
||||
Channel();
|
||||
|
||||
|
@ -165,5 +163,3 @@ struct Channel
|
|||
void IncrementSample();
|
||||
void clearHistory();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,602 +0,0 @@
|
|||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
Conversions between UTF32, UTF-16, and UTF-8. Source code file.
|
||||
Author: Mark E. Davis, 1994.
|
||||
Rev History: Rick McGowan, fixes & updates May 2001.
|
||||
Sept 2001: fixed const & error conditions per
|
||||
mods suggested by S. Parent & A. Lillich.
|
||||
June 2002: Tim Dodd added detection and handling of incomplete
|
||||
source sequences, enhanced error detection, added casts
|
||||
to eliminate compiler warnings.
|
||||
July 2003: slight mods to back out aggressive FFFE detection.
|
||||
Jan 2004: updated switches in from-UTF8 conversions.
|
||||
Oct 2004: updated to use UNI_MAX_LEGAL_UTF32 in UTF-32 conversions.
|
||||
|
||||
See the header file "ConvertUTF.h" for complete documentation.
|
||||
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
#include "ConvertUTF.h"
|
||||
|
||||
static const int halfShift = 10; /* used for shifting by 10 bits */
|
||||
|
||||
static const UTF32 halfBase = 0x0010000UL;
|
||||
static const UTF32 halfMask = 0x3FFUL;
|
||||
|
||||
static const UTF32 UNI_SUR_HIGH_START = 0xD800;
|
||||
static const UTF32 UNI_SUR_HIGH_END = 0xDBFF;
|
||||
static const UTF32 UNI_SUR_LOW_START = 0xDC00;
|
||||
static const UTF32 UNI_SUR_LOW_END = 0xDFFF;
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF32toUTF16(const UTF32 **sourceStart, const UTF32 *sourceEnd, UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags)
|
||||
{
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF32 *source = *sourceStart;
|
||||
UTF16 *target = *targetStart;
|
||||
while (source < sourceEnd)
|
||||
{
|
||||
if (target >= targetEnd)
|
||||
{
|
||||
result = targetExhausted;
|
||||
break;
|
||||
}
|
||||
UTF32 ch = *source++;
|
||||
if (ch <= UNI_MAX_BMP) /* Target is a character <= 0xFFFF */
|
||||
{
|
||||
/* UTF-16 surrogate values are illegal in UTF-32; 0xffff or 0xfffe are both reserved values */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
|
||||
{
|
||||
if (flags == strictConversion)
|
||||
{
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
else
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
else
|
||||
*target++ = static_cast<UTF16>(ch); /* normal case */
|
||||
}
|
||||
else if (ch > UNI_MAX_LEGAL_UTF32)
|
||||
{
|
||||
if (flags == strictConversion)
|
||||
result = sourceIllegal;
|
||||
else
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* target is a character in range 0xFFFF - 0x10FFFF. */
|
||||
if (target + 1 >= targetEnd)
|
||||
{
|
||||
--source; /* Back up source pointer! */
|
||||
result = targetExhausted;
|
||||
break;
|
||||
}
|
||||
ch -= halfBase;
|
||||
*target++ = static_cast<UTF16>((ch >> halfShift) + UNI_SUR_HIGH_START);
|
||||
*target++ = static_cast<UTF16>((ch & halfMask) + UNI_SUR_LOW_START);
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF16toUTF32(const UTF16 **sourceStart, const UTF16 *sourceEnd, UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags)
|
||||
{
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF16 *source = *sourceStart;
|
||||
UTF32 *target = *targetStart;
|
||||
while (source < sourceEnd)
|
||||
{
|
||||
const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
|
||||
UTF32 ch = *source++;
|
||||
/* If we have a surrogate pair, convert to UTF32 first. */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END)
|
||||
{
|
||||
/* If the 16 bits following the high surrogate are in the source buffer... */
|
||||
if (source < sourceEnd)
|
||||
{
|
||||
UTF32 ch2 = *source;
|
||||
/* If it's a low surrogate, convert to UTF32. */
|
||||
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END)
|
||||
{
|
||||
ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase;
|
||||
++source;
|
||||
}
|
||||
else if (flags == strictConversion) /* it's an unpaired high surrogate */
|
||||
{
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else /* We don't have the 16 bits following the high surrogate. */
|
||||
{
|
||||
--source; /* return to the high surrogate */
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (flags == strictConversion)
|
||||
{
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)
|
||||
{
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (target >= targetEnd)
|
||||
{
|
||||
source = oldSource; /* Back up source pointer! */
|
||||
result = targetExhausted;
|
||||
break;
|
||||
}
|
||||
*target++ = ch;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Index into the table below with the first byte of a UTF-8 sequence to
|
||||
* get the number of trailing bytes that are supposed to follow it.
|
||||
* Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
|
||||
* left as-is for anyone who may want to do such conversion, which was
|
||||
* allowed in earlier algorithms.
|
||||
*/
|
||||
static const char trailingBytesForUTF8[256] = {
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
|
||||
};
|
||||
|
||||
/*
|
||||
* Magic values subtracted from a buffer value during UTF8 conversion.
|
||||
* This table contains as many values as there might be trailing bytes
|
||||
* in a UTF-8 sequence.
|
||||
*/
|
||||
static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
|
||||
|
||||
/*
|
||||
* Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
|
||||
* into the first byte, depending on how many bytes follow. There are
|
||||
* as many entries in this table as there are UTF-8 sequence types.
|
||||
* (I.e., one byte sequence, two byte... etc.). Remember that sequencs
|
||||
* for *legal* UTF-8 will be 4 or fewer bytes total.
|
||||
*/
|
||||
static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/* The interface converts a whole buffer to avoid function-call overhead.
|
||||
* Constants have been gathered. Loops & conditionals have been removed as
|
||||
* much as possible for efficiency, in favor of drop-through switches.
|
||||
* (See "Note A" at the bottom of the file for equivalent code.)
|
||||
* If your compiler supports it, the "isLegalUTF8" call can be turned
|
||||
* into an inline function.
|
||||
*/
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF16toUTF8(const UTF16 **sourceStart, const UTF16 *sourceEnd, UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags)
|
||||
{
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF16 *source = *sourceStart;
|
||||
UTF8 *target = *targetStart;
|
||||
while (source < sourceEnd)
|
||||
{
|
||||
const UTF32 byteMask = 0xBF, byteMark = 0x80;
|
||||
const UTF16 *oldSource = source; /* In case we have to back up because of target overflow. */
|
||||
UTF32 ch = *source++;
|
||||
/* If we have a surrogate pair, convert to UTF32 first. */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END)
|
||||
{
|
||||
/* If the 16 bits following the high surrogate are in the source buffer... */
|
||||
if (source < sourceEnd)
|
||||
{
|
||||
UTF32 ch2 = *source;
|
||||
/* If it's a low surrogate, convert to UTF32. */
|
||||
if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END)
|
||||
{
|
||||
ch = ((ch - UNI_SUR_HIGH_START) << halfShift) + (ch2 - UNI_SUR_LOW_START) + halfBase;
|
||||
++source;
|
||||
}
|
||||
else if (flags == strictConversion) /* it's an unpaired high surrogate */
|
||||
{
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else /* We don't have the 16 bits following the high surrogate. */
|
||||
{
|
||||
--source; /* return to the high surrogate */
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (flags == strictConversion)
|
||||
{
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)
|
||||
{
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Figure out how many bytes the result will require */
|
||||
unsigned short bytesToWrite = 0;
|
||||
if (ch < 0x80)
|
||||
bytesToWrite = 1;
|
||||
else if (ch < 0x800)
|
||||
bytesToWrite = 2;
|
||||
else if (ch < 0x10000)
|
||||
bytesToWrite = 3;
|
||||
else if (ch < 0x110000)
|
||||
bytesToWrite = 4;
|
||||
else
|
||||
{
|
||||
bytesToWrite = 3;
|
||||
ch = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
target += bytesToWrite;
|
||||
if (target > targetEnd)
|
||||
{
|
||||
source = oldSource; /* Back up source pointer! */
|
||||
target -= bytesToWrite;
|
||||
result = targetExhausted;
|
||||
break;
|
||||
}
|
||||
switch (bytesToWrite) /* note: everything falls through. */
|
||||
{
|
||||
case 4: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 3: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 2: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 1: *--target = static_cast<UTF8>(ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
target += bytesToWrite;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Utility routine to tell whether a sequence of bytes is legal UTF-8.
|
||||
* This must be called with the length pre-determined by the first byte.
|
||||
* If not calling this from ConvertUTF8to*, then the length can be set by:
|
||||
* length = trailingBytesForUTF8[*source]+1;
|
||||
* and the sequence is illegal right away if there aren't that many bytes
|
||||
* available.
|
||||
* If presented with a length > 4, this returns false. The Unicode
|
||||
* definition of UTF-8 goes up to 4-byte sequences.
|
||||
*/
|
||||
|
||||
static bool isLegalUTF8(const UTF8 *source, int length)
|
||||
{
|
||||
const UTF8 *srcptr = source + length;
|
||||
UTF8 a;
|
||||
switch (length)
|
||||
{
|
||||
default: return false;
|
||||
/* Everything else falls through when "true"... */
|
||||
case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
|
||||
case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
|
||||
case 2: if ((a = (*--srcptr)) > 0xBF) return false;
|
||||
|
||||
switch (*source)
|
||||
{
|
||||
/* no fall-through in this inner switch */
|
||||
case 0xE0: if (a < 0xA0) return false; break;
|
||||
case 0xED: if (a > 0x9F) return false; break;
|
||||
case 0xF0: if (a < 0x90) return false; break;
|
||||
case 0xF4: if (a > 0x8F) return false; break;
|
||||
default: if (a < 0x80) return false;
|
||||
}
|
||||
|
||||
case 1: if (*source >= 0x80 && *source < 0xC2) return false;
|
||||
}
|
||||
if (*source > 0xF4)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
/*
|
||||
* Exported function to return whether a UTF-8 sequence is legal or not.
|
||||
* This is not used here; it's just exported.
|
||||
*/
|
||||
bool isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd)
|
||||
{
|
||||
int length = trailingBytesForUTF8[*source] + 1;
|
||||
if (source + length > sourceEnd)
|
||||
return false;
|
||||
return isLegalUTF8(source, length);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF8toUTF16(const UTF8 **sourceStart, const UTF8 *sourceEnd, UTF16 **targetStart, UTF16 *targetEnd, ConversionFlags flags)
|
||||
{
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF8 *source = *sourceStart;
|
||||
UTF16 *target = *targetStart;
|
||||
while (source < sourceEnd)
|
||||
{
|
||||
UTF32 ch = 0;
|
||||
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
||||
if (source + extraBytesToRead >= sourceEnd)
|
||||
{
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
}
|
||||
/* Do this check whether lenient or strict */
|
||||
if (!isLegalUTF8(source, extraBytesToRead + 1))
|
||||
{
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* The cases all fall through. See "Note A" below.
|
||||
*/
|
||||
switch (extraBytesToRead)
|
||||
{
|
||||
case 5: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
|
||||
case 4: ch += *source++; ch <<= 6; /* remember, illegal UTF-8 */
|
||||
case 3: ch += *source++; ch <<= 6;
|
||||
case 2: ch += *source++; ch <<= 6;
|
||||
case 1: ch += *source++; ch <<= 6;
|
||||
case 0: ch += *source++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[extraBytesToRead];
|
||||
|
||||
if (target >= targetEnd)
|
||||
{
|
||||
source -= extraBytesToRead + 1; /* Back up source pointer! */
|
||||
result = targetExhausted;
|
||||
break;
|
||||
}
|
||||
if (ch <= UNI_MAX_BMP) /* Target is a character <= 0xFFFF */
|
||||
{
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
|
||||
{
|
||||
if (flags == strictConversion)
|
||||
{
|
||||
source -= extraBytesToRead + 1; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
else
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
else
|
||||
*target++ = static_cast<UTF16>(ch); /* normal case */
|
||||
}
|
||||
else if (ch > UNI_MAX_UTF16)
|
||||
{
|
||||
if (flags == strictConversion)
|
||||
{
|
||||
result = sourceIllegal;
|
||||
source -= extraBytesToRead + 1; /* return to the start */
|
||||
break; /* Bail out; shouldn't continue */
|
||||
}
|
||||
else
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* target is a character in range 0xFFFF - 0x10FFFF. */
|
||||
if (target + 1 >= targetEnd)
|
||||
{
|
||||
source -= extraBytesToRead + 1; /* Back up source pointer! */
|
||||
result = targetExhausted;
|
||||
break;
|
||||
}
|
||||
ch -= halfBase;
|
||||
*target++ = static_cast<UTF16>((ch >> halfShift) + UNI_SUR_HIGH_START);
|
||||
*target++ = static_cast<UTF16>((ch & halfMask) + UNI_SUR_LOW_START);
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF32toUTF8(const UTF32 **sourceStart, const UTF32 *sourceEnd, UTF8 **targetStart, UTF8 *targetEnd, ConversionFlags flags)
|
||||
{
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF32 *source = *sourceStart;
|
||||
UTF8 *target = *targetStart;
|
||||
while (source < sourceEnd)
|
||||
{
|
||||
const UTF32 byteMask = 0xBF, byteMark = 0x80;
|
||||
UTF32 ch = *source++;
|
||||
if (flags == strictConversion)
|
||||
{
|
||||
/* UTF-16 surrogate values are illegal in UTF-32 */
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
|
||||
{
|
||||
--source; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Figure out how many bytes the result will require. Turn any
|
||||
* illegally large UTF32 things (> Plane 17) into replacement chars.
|
||||
*/
|
||||
unsigned short bytesToWrite = 0;
|
||||
if (ch < 0x80)
|
||||
bytesToWrite = 1;
|
||||
else if (ch < 0x800)
|
||||
bytesToWrite = 2;
|
||||
else if (ch < 0x10000)
|
||||
bytesToWrite = 3;
|
||||
else if (ch <= UNI_MAX_LEGAL_UTF32)
|
||||
bytesToWrite = 4;
|
||||
else
|
||||
{
|
||||
bytesToWrite = 3;
|
||||
ch = UNI_REPLACEMENT_CHAR;
|
||||
result = sourceIllegal;
|
||||
}
|
||||
|
||||
target += bytesToWrite;
|
||||
if (target > targetEnd)
|
||||
{
|
||||
--source; /* Back up source pointer! */
|
||||
target -= bytesToWrite;
|
||||
result = targetExhausted;
|
||||
break;
|
||||
}
|
||||
switch (bytesToWrite) /* note: everything falls through. */
|
||||
{
|
||||
case 4: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 3: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 2: *--target = static_cast<UTF8>((ch | byteMark) & byteMask); ch >>= 6;
|
||||
case 1: *--target = static_cast<UTF8>(ch | firstByteMark[bytesToWrite]);
|
||||
}
|
||||
target += bytesToWrite;
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart, const UTF8 *sourceEnd, UTF32 **targetStart, UTF32 *targetEnd, ConversionFlags flags)
|
||||
{
|
||||
ConversionResult result = conversionOK;
|
||||
const UTF8 *source = *sourceStart;
|
||||
UTF32 *target = *targetStart;
|
||||
while (source < sourceEnd)
|
||||
{
|
||||
UTF32 ch = 0;
|
||||
unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
|
||||
if (source + extraBytesToRead >= sourceEnd)
|
||||
{
|
||||
result = sourceExhausted;
|
||||
break;
|
||||
}
|
||||
/* Do this check whether lenient or strict */
|
||||
if (!isLegalUTF8(source, extraBytesToRead + 1))
|
||||
{
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* The cases all fall through. See "Note A" below.
|
||||
*/
|
||||
switch (extraBytesToRead)
|
||||
{
|
||||
case 5: ch += *source++; ch <<= 6;
|
||||
case 4: ch += *source++; ch <<= 6;
|
||||
case 3: ch += *source++; ch <<= 6;
|
||||
case 2: ch += *source++; ch <<= 6;
|
||||
case 1: ch += *source++; ch <<= 6;
|
||||
case 0: ch += *source++;
|
||||
}
|
||||
ch -= offsetsFromUTF8[extraBytesToRead];
|
||||
|
||||
if (target >= targetEnd)
|
||||
{
|
||||
source -= extraBytesToRead + 1; /* Back up the source pointer! */
|
||||
result = targetExhausted;
|
||||
break;
|
||||
}
|
||||
if (ch <= UNI_MAX_LEGAL_UTF32)
|
||||
{
|
||||
/*
|
||||
* UTF-16 surrogate values are illegal in UTF-32, and anything
|
||||
* over Plane 17 (> 0x10FFFF) is illegal.
|
||||
*/
|
||||
if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)
|
||||
{
|
||||
if (flags == strictConversion)
|
||||
{
|
||||
source -= extraBytesToRead + 1; /* return to the illegal value itself */
|
||||
result = sourceIllegal;
|
||||
break;
|
||||
}
|
||||
else
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
else
|
||||
*target++ = ch;
|
||||
}
|
||||
else /* i.e., ch > UNI_MAX_LEGAL_UTF32 */
|
||||
{
|
||||
result = sourceIllegal;
|
||||
*target++ = UNI_REPLACEMENT_CHAR;
|
||||
}
|
||||
}
|
||||
*sourceStart = source;
|
||||
*targetStart = target;
|
||||
return result;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
Note A.
|
||||
The fall-through switches in UTF-8 reading code save a
|
||||
temp variable, some decrements & conditionals. The switches
|
||||
are equivalent to the following loop:
|
||||
{
|
||||
int tmpBytesToRead = extraBytesToRead+1;
|
||||
do {
|
||||
ch += *source++;
|
||||
--tmpBytesToRead;
|
||||
if (tmpBytesToRead) ch <<= 6;
|
||||
} while (tmpBytesToRead > 0);
|
||||
}
|
||||
In UTF-8 writing code, the switches on "bytesToWrite" are
|
||||
similarly unrolled loops.
|
||||
|
||||
--------------------------------------------------------------------- */
|
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
* Copyright 2001-2004 Unicode, Inc.
|
||||
*
|
||||
* Disclaimer
|
||||
*
|
||||
* This source code is provided as is by Unicode, Inc. No claims are
|
||||
* made as to fitness for any particular purpose. No warranties of any
|
||||
* kind are expressed or implied. The recipient agrees to determine
|
||||
* applicability of information provided. If this file has been
|
||||
* purchased on magnetic or optical media from Unicode, Inc., the
|
||||
* sole remedy for any claim will be exchange of defective media
|
||||
* within 90 days of receipt.
|
||||
*
|
||||
* Limitations on Rights to Redistribute This Code
|
||||
*
|
||||
* Unicode, Inc. hereby grants the right to freely use the information
|
||||
* supplied in this file in the creation of products supporting the
|
||||
* Unicode Standard, and to make copies of this file in any form
|
||||
* for internal or external distribution as long as this notice
|
||||
* remains attached.
|
||||
*/
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
|
||||
Conversions between UTF32, UTF-16, and UTF-8. Header file.
|
||||
|
||||
Several funtions are included here, forming a complete set of
|
||||
conversions between the three formats. UTF-7 is not included
|
||||
here, but is handled in a separate source file.
|
||||
|
||||
Each of these routines takes pointers to input buffers and output
|
||||
buffers. The input buffers are const.
|
||||
|
||||
Each routine converts the text between *sourceStart and sourceEnd,
|
||||
putting the result into the buffer between *targetStart and
|
||||
targetEnd. Note: the end pointers are *after* the last item: e.g.
|
||||
*(sourceEnd - 1) is the last item.
|
||||
|
||||
The return result indicates whether the conversion was successful,
|
||||
and if not, whether the problem was in the source or target buffers.
|
||||
(Only the first encountered problem is indicated.)
|
||||
|
||||
After the conversion, *sourceStart and *targetStart are both
|
||||
updated to point to the end of last text successfully converted in
|
||||
the respective buffers.
|
||||
|
||||
Input parameters:
|
||||
sourceStart - pointer to a pointer to the source buffer.
|
||||
The contents of this are modified on return so that
|
||||
it points at the next thing to be converted.
|
||||
targetStart - similarly, pointer to pointer to the target buffer.
|
||||
sourceEnd, targetEnd - respectively pointers to the ends of the
|
||||
two buffers, for overflow checking only.
|
||||
|
||||
These conversion functions take a ConversionFlags argument. When this
|
||||
flag is set to strict, both irregular sequences and isolated surrogates
|
||||
will cause an error. When the flag is set to lenient, both irregular
|
||||
sequences and isolated surrogates are converted.
|
||||
|
||||
Whether the flag is strict or lenient, all illegal sequences will cause
|
||||
an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
|
||||
or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
|
||||
must check for illegal sequences.
|
||||
|
||||
When the flag is set to lenient, characters over 0x10FFFF are converted
|
||||
to the replacement character; otherwise (when the flag is set to strict)
|
||||
they constitute an error.
|
||||
|
||||
Output parameters:
|
||||
The value "sourceIllegal" is returned from some routines if the input
|
||||
sequence is malformed. When "sourceIllegal" is returned, the source
|
||||
value will point to the illegal value that caused the problem. E.g.,
|
||||
in UTF-8 when a sequence is malformed, it points to the start of the
|
||||
malformed sequence.
|
||||
|
||||
Author: Mark E. Davis, 1994.
|
||||
Rev History: Rick McGowan, fixes & updates May 2001.
|
||||
Fixes & updates, Sept 2001.
|
||||
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
#ifndef _CONVERTUTF_H_
|
||||
#define _CONVERTUTF_H_
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
The following 4 definitions are compiler-specific.
|
||||
The C standard does not guarantee that wchar_t has at least
|
||||
16 bits, so wchar_t is no less portable than unsigned short!
|
||||
All should be unsigned values to avoid sign extension during
|
||||
bit mask & shift operations.
|
||||
------------------------------------------------------------------------ */
|
||||
|
||||
typedef unsigned long UTF32; /* at least 32 bits */
|
||||
typedef unsigned short UTF16; /* at least 16 bits */
|
||||
typedef unsigned char UTF8; /* typically 8 bits */
|
||||
|
||||
/* Some fundamental constants */
|
||||
static const UTF32 UNI_REPLACEMENT_CHAR = 0x0000FFFD;
|
||||
static const UTF32 UNI_MAX_BMP = 0x0000FFFF;
|
||||
static const UTF32 UNI_MAX_UTF16 = 0x0010FFFF;
|
||||
static const UTF32 UNI_MAX_UTF32 = 0x7FFFFFFF;
|
||||
static const UTF32 UNI_MAX_LEGAL_UTF32 = 0x0010FFFF;
|
||||
|
||||
enum ConversionResult
|
||||
{
|
||||
conversionOK, /* conversion successful */
|
||||
sourceExhausted, /* partial character in source, but hit end */
|
||||
targetExhausted, /* insuff. room in target for conversion */
|
||||
sourceIllegal /* source sequence is illegal/malformed */
|
||||
};
|
||||
|
||||
enum ConversionFlags
|
||||
{
|
||||
strictConversion,
|
||||
lenientConversion
|
||||
};
|
||||
|
||||
ConversionResult ConvertUTF8toUTF16(const UTF8 **, const UTF8 *, UTF16 **, UTF16 *, ConversionFlags);
|
||||
|
||||
ConversionResult ConvertUTF16toUTF8(const UTF16 **, const UTF16 *, UTF8 **, UTF8 *, ConversionFlags);
|
||||
|
||||
ConversionResult ConvertUTF8toUTF32(const UTF8 **, const UTF8 *, UTF32 **, UTF32 *, ConversionFlags);
|
||||
|
||||
ConversionResult ConvertUTF32toUTF8(const UTF32 **, const UTF32 *, UTF8 **, UTF8 *, ConversionFlags);
|
||||
|
||||
ConversionResult ConvertUTF16toUTF32(const UTF16 **, const UTF16 *, UTF32 **, UTF32 *, ConversionFlags);
|
||||
|
||||
ConversionResult ConvertUTF32toUTF16(const UTF32 **, const UTF32 *, UTF16 **, UTF16 *, ConversionFlags);
|
||||
|
||||
bool isLegalUTF8Sequence(const UTF8 *, const UTF8 *);
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#endif
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT FAT (File Allocation Table) Section structures
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-21
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_FATSECTION_H
|
||||
#define SSEQPLAYER_FATSECTION_H
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
@ -29,5 +28,3 @@ struct FATSection
|
|||
|
||||
void Read(PseudoFile &file);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT INFO Entry structures
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-21
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_INFOENTRY_H
|
||||
#define SSEQPLAYER_INFOENTRY_H
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
@ -50,5 +49,3 @@ struct INFOEntryWAVEARC : INFOEntry
|
|||
|
||||
void Read(PseudoFile &file);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT INFO Section structures
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-21
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_INFOSECTION_H
|
||||
#define SSEQPLAYER_INFOSECTION_H
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "INFOEntry.h"
|
||||
|
@ -33,5 +32,3 @@ struct INFOSection
|
|||
|
||||
void Read(PseudoFile &file);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - Nintendo DS Standard Header structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-21
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_NDSSTDHEADER_H
|
||||
#define SSEQPLAYER_NDSSTDHEADER_H
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
@ -22,5 +21,3 @@ struct NDSStdHeader
|
|||
void Read(PseudoFile &file);
|
||||
void Verify(const std::string &typeToCheck, uint32_t magicToCheck);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* SSEQ Player - Player structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-01
|
||||
* Last modification on 2014-10-23
|
||||
*
|
||||
* Adapted from source code of FeOS Sound System
|
||||
* By fincs
|
||||
|
@ -11,13 +11,23 @@
|
|||
#include "Player.h"
|
||||
#include "common.h"
|
||||
|
||||
Player::Player() : prio(0), nTracks(0), tempo(0), tempoCount(0), tempoRate(0), masterVol(0), sseq(nullptr), sampleRate(0), interpolation(INTERPOLATION_NONE)
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && !defined(_LIBCPP_VERSION)
|
||||
std::locale::id std::codecvt<char16_t, char, mbstate_t>::id;
|
||||
std::locale::id std::codecvt<char32_t, char, mbstate_t>::id;
|
||||
#endif
|
||||
|
||||
Player::Player() : prio(0), nTracks(0), tempo(0), tempoCount(0), tempoRate(0), masterVol(0), sseqVol(0), sseq(nullptr), sampleRate(0), interpolation(INTERPOLATION_NONE)
|
||||
{
|
||||
memset(this->trackIds, 0, sizeof(this->trackIds));
|
||||
for (size_t i = 0; i < 16; ++i)
|
||||
{
|
||||
this->channels[i].chnId = i;
|
||||
this->channels[i].ply = this;
|
||||
}
|
||||
memset(this->variables, -1, sizeof(this->variables));
|
||||
}
|
||||
|
||||
// Original FSS Function: Player_Setup
|
||||
bool Player::Setup(const SSEQ *sseqToPlay)
|
||||
{
|
||||
this->sseq = sseqToPlay;
|
||||
|
@ -30,21 +40,7 @@ bool Player::Setup(const SSEQ *sseqToPlay)
|
|||
this->nTracks = 1;
|
||||
this->trackIds[0] = firstTrack;
|
||||
|
||||
auto pData = &this->sseq->data[0];
|
||||
if (*pData == 0xFE)
|
||||
for (pData += 3; *pData == 0x93; ) // Prepare extra tracks
|
||||
{
|
||||
++pData;
|
||||
int tNum = read8(&pData);
|
||||
auto pos = &this->sseq->data[read24(&pData)];
|
||||
int newTrack = this->TrackAlloc();
|
||||
if (newTrack == -1)
|
||||
continue;
|
||||
this->tracks[newTrack].Init(newTrack, this, pos, tNum);
|
||||
this->trackIds[this->nTracks++] = newTrack;
|
||||
}
|
||||
|
||||
this->tracks[firstTrack].startPos = this->tracks[firstTrack].pos = pData;
|
||||
this->tracks[firstTrack].startPos = this->tracks[firstTrack].pos = &this->sseq->data[0];
|
||||
|
||||
this->secondsPerSample = 1.0 / this->sampleRate;
|
||||
|
||||
|
@ -53,16 +49,19 @@ bool Player::Setup(const SSEQ *sseqToPlay)
|
|||
return true;
|
||||
}
|
||||
|
||||
// Original FSS Function: Player_ClearState
|
||||
void Player::ClearState()
|
||||
{
|
||||
this->tempo = 120;
|
||||
this->tempoCount = 0;
|
||||
this->tempoRate = 0x100;
|
||||
this->masterVol = 0; // this is actually the highest level
|
||||
memset(this->variables, -1, sizeof(this->variables));
|
||||
this->secondsIntoPlayback = 0;
|
||||
this->secondsUntilNextClock = SecondsPerClockCycle;
|
||||
}
|
||||
|
||||
// Original FSS Function: Player_FreeTracks
|
||||
void Player::FreeTracks()
|
||||
{
|
||||
for (uint8_t i = 0; i < this->nTracks; ++i)
|
||||
|
@ -70,6 +69,7 @@ void Player::FreeTracks()
|
|||
this->nTracks = 0;
|
||||
}
|
||||
|
||||
// Original FSS Function: Player_Stop
|
||||
void Player::Stop(bool bKillSound)
|
||||
{
|
||||
this->ClearState();
|
||||
|
@ -92,11 +92,12 @@ void Player::Stop(bool bKillSound)
|
|||
this->FreeTracks();
|
||||
}
|
||||
|
||||
// Original FSS Function: Chn_Alloc
|
||||
int Player::ChannelAlloc(int type, int priority)
|
||||
{
|
||||
static const uint8_t pcmChnArray[] = { 4, 5, 6, 7, 2, 0, 3, 1, 8, 9, 10, 11, 14, 12, 15, 13 };
|
||||
static const uint8_t psgChnArray[] = { 13, 12, 11, 10, 9, 8 };
|
||||
static const uint8_t noiseChnArray[] = { 15, 14 };
|
||||
static const uint8_t psgChnArray[] = { 8, 9, 10, 11, 12, 13 };
|
||||
static const uint8_t noiseChnArray[] = { 14, 15 };
|
||||
static const uint8_t arraySizes[] = { sizeof(pcmChnArray), sizeof(psgChnArray), sizeof(noiseChnArray) };
|
||||
static const uint8_t *const arrayArray[] = { pcmChnArray, psgChnArray, noiseChnArray };
|
||||
|
||||
|
@ -121,13 +122,13 @@ int Player::ChannelAlloc(int type, int priority)
|
|||
|
||||
if (curChnNo == -1 || priority < this->channels[curChnNo].prio)
|
||||
return -1;
|
||||
this->channels[curChnNo].ply = this;
|
||||
this->channels[curChnNo].noteLength = -1;
|
||||
this->channels[curChnNo].vol = 0;
|
||||
this->channels[curChnNo].vol = 0x7FF;
|
||||
this->channels[curChnNo].clearHistory();
|
||||
return curChnNo;
|
||||
}
|
||||
|
||||
// Original FSS Function: Track_Alloc
|
||||
int Player::TrackAlloc()
|
||||
{
|
||||
for (int i = 0; i < FSS_MAXTRACKS; ++i)
|
||||
|
@ -144,6 +145,7 @@ int Player::TrackAlloc()
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Original FSS Function: Player_Run
|
||||
void Player::Run()
|
||||
{
|
||||
while (this->tempoCount > 240)
|
||||
|
@ -163,6 +165,7 @@ void Player::UpdateTracks()
|
|||
this->tracks[i].updateFlags.reset();
|
||||
}
|
||||
|
||||
// Original FSS Function: Snd_Timer
|
||||
void Player::Timer()
|
||||
{
|
||||
this->UpdateTracks();
|
||||
|
@ -173,14 +176,6 @@ void Player::Timer()
|
|||
this->Run();
|
||||
}
|
||||
|
||||
template<typename T1, typename T2> static inline void clamp(T1 &valueToClamp, const T2 &minValue, const T2 &maxValue)
|
||||
{
|
||||
if (valueToClamp < minValue)
|
||||
valueToClamp = minValue;
|
||||
else if (valueToClamp > maxValue)
|
||||
valueToClamp = maxValue;
|
||||
}
|
||||
|
||||
static inline int32_t muldiv7(int32_t val, uint8_t mul)
|
||||
{
|
||||
return mul == 127 ? val : ((val * mul) >> 7);
|
||||
|
@ -219,9 +214,6 @@ void Player::GenerateSamples(std::vector<uint8_t> &buf, unsigned offset, unsigne
|
|||
}
|
||||
}
|
||||
|
||||
leftChannel = muldiv7(leftChannel, 127 - this->masterVol);
|
||||
rightChannel = muldiv7(rightChannel, 127 - this->masterVol);
|
||||
|
||||
clamp(leftChannel, -0x8000, 0x7FFF);
|
||||
clamp(rightChannel, -0x8000, 0x7FFF);
|
||||
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
/*
|
||||
* SSEQ Player - Player structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-01
|
||||
* Last modification on 2014-10-18
|
||||
*
|
||||
* Adapted from source code of FeOS Sound System
|
||||
* By fincs
|
||||
* https://github.com/fincs/FSS
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_PLAYER_H
|
||||
#define SSEQPLAYER_PLAYER_H
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <bitset>
|
||||
|
@ -22,13 +21,14 @@ struct Player
|
|||
{
|
||||
uint8_t prio, nTracks;
|
||||
uint16_t tempo, tempoCount, tempoRate /* 8.8 fixed point */;
|
||||
int16_t masterVol;
|
||||
int16_t masterVol, sseqVol;
|
||||
|
||||
const SSEQ *sseq;
|
||||
|
||||
uint8_t trackIds[FSS_TRACKCOUNT];
|
||||
Track tracks[FSS_MAXTRACKS];
|
||||
Channel channels[16];
|
||||
int16_t variables[32];
|
||||
|
||||
uint32_t sampleRate;
|
||||
Interpolation interpolation;
|
||||
|
@ -50,5 +50,3 @@ struct Player
|
|||
std::bitset<16> mutes;
|
||||
void GenerateSamples(std::vector<uint8_t> &buf, unsigned offset, unsigned samples);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT SBNK (Sound Bank) structures
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-21
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_SBNK_H
|
||||
#define SSEQPLAYER_SBNK_H
|
||||
#pragma once
|
||||
|
||||
#include "SWAR.h"
|
||||
#include "INFOEntry.h"
|
||||
|
@ -57,5 +56,3 @@ struct SBNK
|
|||
|
||||
void Read(PseudoFile &file);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-30
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_SDAT_H
|
||||
#define SSEQPLAYER_SDAT_H
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include "SSEQ.h"
|
||||
|
@ -27,5 +26,3 @@ private:
|
|||
SDAT(const SDAT &);
|
||||
SDAT &operator=(const SDAT &);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT SSEQ (Sequence) structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-21
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_SSEQ_H
|
||||
#define SSEQPLAYER_SSEQ_H
|
||||
#pragma once
|
||||
|
||||
#include "SBNK.h"
|
||||
#include "INFOEntry.h"
|
||||
|
@ -28,5 +27,3 @@ struct SSEQ
|
|||
|
||||
void Read(PseudoFile &file);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT SWAR (Wave Archive) structures
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-21
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_SWAR_H
|
||||
#define SSEQPLAYER_SWAR_H
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "SWAV.h"
|
||||
|
@ -29,5 +28,3 @@ struct SWAR
|
|||
|
||||
void Read(PseudoFile &file);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT SWAV (Waveform/Sample) structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-10
|
||||
* Last modification on 2013-04-12
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
|
@ -62,20 +62,21 @@ static inline void DecodeADPCMNibble(int32_t nibble, int32_t &stepIndex, int32_t
|
|||
predictedValue = 0x7FFF;
|
||||
}
|
||||
|
||||
void SWAV::DecodeADPCM(const std::vector<uint8_t> &origData)
|
||||
void SWAV::DecodeADPCM(const uint8_t *origData, uint32_t len)
|
||||
{
|
||||
int32_t predictedValue = origData[0] | (origData[1] << 8);
|
||||
int32_t stepIndex = origData[2] | (origData[3] << 8);
|
||||
auto finalData = &this->data[0];
|
||||
|
||||
for (long i = 0, len = origData.size() - 4; i < len; ++i)
|
||||
for (uint32_t i = 0; i < len; ++i)
|
||||
{
|
||||
int32_t nibble = origData[i + 4] & 0x0F;
|
||||
DecodeADPCMNibble(nibble, stepIndex, predictedValue);
|
||||
this->data[2 * i] = predictedValue;
|
||||
finalData[2 * i] = predictedValue;
|
||||
|
||||
nibble = (origData[i + 4] >> 4) & 0x0F;
|
||||
DecodeADPCMNibble(nibble, stepIndex, predictedValue);
|
||||
this->data[2 * i + 1] = predictedValue;
|
||||
finalData[2 * i + 1] = predictedValue;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +115,7 @@ void SWAV::Read(PseudoFile &file)
|
|||
{
|
||||
// IMA ADPCM -> PCM signed 16-bit
|
||||
this->data.resize((origData.size() - 4) * 2, 0);
|
||||
this->DecodeADPCM(origData);
|
||||
this->DecodeADPCM(&origData[0], origData.size() - 4);
|
||||
--this->loopOffset;
|
||||
this->loopOffset *= 8;
|
||||
this->nonLoopLength *= 8;
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT SWAV (Waveform/Sample) structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-10
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_SWAV_H
|
||||
#define SSEQPLAYER_SWAV_H
|
||||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
@ -26,7 +25,5 @@ struct SWAV
|
|||
SWAV();
|
||||
|
||||
void Read(PseudoFile &file);
|
||||
void DecodeADPCM(const std::vector<uint8_t> &data);
|
||||
void DecodeADPCM(const uint8_t *origData, uint32_t len);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
/*
|
||||
* SSEQ Player - SDAT SYMB (Symbol/Filename) Section structures
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-21
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Nintendo DS Nitro Composer (SDAT) Specification document found at
|
||||
* http://www.feshrine.net/hacking/doc/nds-sdat.html
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_SYMBSECTION_H
|
||||
#define SSEQPLAYER_SYMBSECTION_H
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include "common.h"
|
||||
|
@ -35,5 +34,3 @@ struct SYMBSection
|
|||
|
||||
void Read(PseudoFile &file);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
/*
|
||||
* SSEQ Player - Track structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-01
|
||||
* Last modification on 2014-10-23
|
||||
*
|
||||
* Adapted from source code of FeOS Sound System
|
||||
* By fincs
|
||||
* https://github.com/fincs/FSS
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
#include "Track.h"
|
||||
#include "Player.h"
|
||||
#include "common.h"
|
||||
|
@ -17,6 +18,7 @@ Track::Track()
|
|||
this->Zero();
|
||||
}
|
||||
|
||||
// Original FSS Function: Player_InitTrack
|
||||
void Track::Init(uint8_t handle, Player *player, const uint8_t *dataPos, int n)
|
||||
{
|
||||
this->trackId = handle;
|
||||
|
@ -35,9 +37,11 @@ void Track::Zero()
|
|||
this->ply = nullptr;
|
||||
|
||||
this->startPos = this->pos = nullptr;
|
||||
memset(this->stack, 0, sizeof(this->stack));
|
||||
std::fill_n(&this->stack[0], FSS_TRACKSTACKSIZE, StackValue());
|
||||
this->stackPos = 0;
|
||||
memset(this->loopCount, 0, sizeof(this->loopCount));
|
||||
this->overriding() = false;
|
||||
this->lastComparisonResult = true;
|
||||
|
||||
this->wait = 0;
|
||||
this->patch = 0;
|
||||
|
@ -56,6 +60,7 @@ void Track::Zero()
|
|||
this->updateFlags.reset();
|
||||
}
|
||||
|
||||
// Original FSS Function: Track_ClearState
|
||||
void Track::ClearState()
|
||||
{
|
||||
this->state.reset();
|
||||
|
@ -71,8 +76,7 @@ void Track::ClearState()
|
|||
this->portaKey = 60;
|
||||
this->portaTime = 0;
|
||||
this->sweepPitch = 0;
|
||||
this->vol = 64;
|
||||
this->expr = 127;
|
||||
this->vol = this->expr = 127;
|
||||
this->pan = 0;
|
||||
this->pitchBendRange = 2;
|
||||
this->pitchBend = this->transpose = 0;
|
||||
|
@ -82,16 +86,18 @@ void Track::ClearState()
|
|||
this->modType = 0;
|
||||
this->modRange = 1;
|
||||
this->modSpeed = 16;
|
||||
this->modDelay = 10;
|
||||
this->modDelay = 0;
|
||||
this->modDepth = 0;
|
||||
}
|
||||
|
||||
// Original FSS Function: Track_Free
|
||||
void Track::Free()
|
||||
{
|
||||
this->state.reset();
|
||||
this->updateFlags.reset();
|
||||
}
|
||||
|
||||
// Original FSS Function: Note_On
|
||||
int Track::NoteOn(int key, int vel, int len)
|
||||
{
|
||||
auto sbnk = this->ply->sseq->bank;
|
||||
|
@ -210,6 +216,7 @@ int Track::NoteOn(int key, int vel, int len)
|
|||
return nCh;
|
||||
}
|
||||
|
||||
// Original FSS Function: Note_On_Tie
|
||||
int Track::NoteOnTie(int key, int vel)
|
||||
{
|
||||
// Find an existing note
|
||||
|
@ -245,6 +252,7 @@ int Track::NoteOnTie(int key, int vel)
|
|||
return i;
|
||||
}
|
||||
|
||||
// Original FSS Function: Track_ReleaseAllNotes
|
||||
void Track::ReleaseAllNotes()
|
||||
{
|
||||
for (int i = 0; i < 16; ++i)
|
||||
|
@ -257,6 +265,9 @@ void Track::ReleaseAllNotes()
|
|||
|
||||
enum SseqCommand
|
||||
{
|
||||
SSEQ_CMD_ALLOCTRACK = 0xFE, // Silently ignored
|
||||
SSEQ_CMD_OPENTRACK = 0x93,
|
||||
|
||||
SSEQ_CMD_REST = 0x80,
|
||||
SSEQ_CMD_PATCH = 0x81,
|
||||
SSEQ_CMD_PAN = 0xC0,
|
||||
|
@ -298,11 +309,173 @@ enum SseqCommand
|
|||
SSEQ_CMD_RANDOM = 0xA0,
|
||||
SSEQ_CMD_PRINTVAR = 0xD6,
|
||||
SSEQ_CMD_IF = 0xA2,
|
||||
SSEQ_CMD_UNSUP1 = 0xA1,
|
||||
SSEQ_CMD_UNSUP2_LO = 0xB0,
|
||||
SSEQ_CMD_UNSUP2_HI = 0xBD
|
||||
SSEQ_CMD_FROMVAR = 0xA1,
|
||||
SSEQ_CMD_SETVAR = 0xB0,
|
||||
SSEQ_CMD_ADDVAR = 0xB1,
|
||||
SSEQ_CMD_SUBVAR = 0xB2,
|
||||
SSEQ_CMD_MULVAR = 0xB3,
|
||||
SSEQ_CMD_DIVVAR = 0xB4,
|
||||
SSEQ_CMD_SHIFTVAR = 0xB5,
|
||||
SSEQ_CMD_RANDVAR = 0xB6,
|
||||
SSEQ_CMD_CMP_EQ = 0xB8,
|
||||
SSEQ_CMD_CMP_GE = 0xB9,
|
||||
SSEQ_CMD_CMP_GT = 0xBA,
|
||||
SSEQ_CMD_CMP_LE = 0xBB,
|
||||
SSEQ_CMD_CMP_LT = 0xBC,
|
||||
SSEQ_CMD_CMP_NE = 0xBD,
|
||||
|
||||
SSEQ_CMD_MUTE = 0xD7 // Unsupported
|
||||
};
|
||||
|
||||
static const uint8_t VariableByteCount = 1 << 7;
|
||||
static const uint8_t ExtraByteOnNoteOrVarOrCmp = 1 << 6;
|
||||
|
||||
static inline uint8_t SseqCommandByteCount(int cmd)
|
||||
{
|
||||
if (cmd < 0x80)
|
||||
return 1 | VariableByteCount;
|
||||
else
|
||||
switch (cmd)
|
||||
{
|
||||
case SSEQ_CMD_REST:
|
||||
case SSEQ_CMD_PATCH:
|
||||
return VariableByteCount;
|
||||
|
||||
case SSEQ_CMD_PAN:
|
||||
case SSEQ_CMD_VOL:
|
||||
case SSEQ_CMD_MASTERVOL:
|
||||
case SSEQ_CMD_PRIO:
|
||||
case SSEQ_CMD_NOTEWAIT:
|
||||
case SSEQ_CMD_TIE:
|
||||
case SSEQ_CMD_EXPR:
|
||||
case SSEQ_CMD_LOOPSTART:
|
||||
case SSEQ_CMD_TRANSPOSE:
|
||||
case SSEQ_CMD_PITCHBEND:
|
||||
case SSEQ_CMD_PITCHBENDRANGE:
|
||||
case SSEQ_CMD_ATTACK:
|
||||
case SSEQ_CMD_DECAY:
|
||||
case SSEQ_CMD_SUSTAIN:
|
||||
case SSEQ_CMD_RELEASE:
|
||||
case SSEQ_CMD_PORTAKEY:
|
||||
case SSEQ_CMD_PORTAFLAG:
|
||||
case SSEQ_CMD_PORTATIME:
|
||||
case SSEQ_CMD_MODDEPTH:
|
||||
case SSEQ_CMD_MODSPEED:
|
||||
case SSEQ_CMD_MODTYPE:
|
||||
case SSEQ_CMD_MODRANGE:
|
||||
case SSEQ_CMD_PRINTVAR:
|
||||
case SSEQ_CMD_MUTE:
|
||||
return 1;
|
||||
|
||||
case SSEQ_CMD_ALLOCTRACK:
|
||||
case SSEQ_CMD_TEMPO:
|
||||
case SSEQ_CMD_SWEEPPITCH:
|
||||
case SSEQ_CMD_MODDELAY:
|
||||
return 2;
|
||||
|
||||
case SSEQ_CMD_GOTO:
|
||||
case SSEQ_CMD_CALL:
|
||||
case SSEQ_CMD_SETVAR:
|
||||
case SSEQ_CMD_ADDVAR:
|
||||
case SSEQ_CMD_SUBVAR:
|
||||
case SSEQ_CMD_MULVAR:
|
||||
case SSEQ_CMD_DIVVAR:
|
||||
case SSEQ_CMD_SHIFTVAR:
|
||||
case SSEQ_CMD_RANDVAR:
|
||||
case SSEQ_CMD_CMP_EQ:
|
||||
case SSEQ_CMD_CMP_GE:
|
||||
case SSEQ_CMD_CMP_GT:
|
||||
case SSEQ_CMD_CMP_LE:
|
||||
case SSEQ_CMD_CMP_LT:
|
||||
case SSEQ_CMD_CMP_NE:
|
||||
return 3;
|
||||
|
||||
case SSEQ_CMD_OPENTRACK:
|
||||
return 4;
|
||||
|
||||
case SSEQ_CMD_FROMVAR:
|
||||
return 1 | ExtraByteOnNoteOrVarOrCmp; // Technically 2 bytes with an additional 1, leaving 1 off because we will be reading it to determine if the additional byte is needed
|
||||
|
||||
case SSEQ_CMD_RANDOM:
|
||||
return 4 | ExtraByteOnNoteOrVarOrCmp; // Technically 5 bytes with an additional 1, leaving 1 off because we will be reading it to determine if the additional byte is needed
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static auto varFuncSet = [](int16_t, int16_t value) { return value; };
|
||||
static auto varFuncAdd = [](int16_t var, int16_t value) -> int16_t { return var + value; };
|
||||
static auto varFuncSub = [](int16_t var, int16_t value) -> int16_t { return var - value; };
|
||||
static auto varFuncMul = [](int16_t var, int16_t value) -> int16_t { return var * value; };
|
||||
static auto varFuncDiv = [](int16_t var, int16_t value) -> int16_t { return var / value; };
|
||||
static auto varFuncShift = [](int16_t var, int16_t value) -> int16_t
|
||||
{
|
||||
if (value < 0)
|
||||
return var >> -value;
|
||||
else
|
||||
return var << value;
|
||||
};
|
||||
static auto varFuncRand = [](int16_t, int16_t value) -> int16_t
|
||||
{
|
||||
if (value < 0)
|
||||
return -(std::rand() % (-value + 1));
|
||||
else
|
||||
return std::rand() % (value + 1);
|
||||
};
|
||||
|
||||
static inline std::function<int16_t (int16_t, int16_t)> VarFunc(int cmd)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case SSEQ_CMD_SETVAR:
|
||||
return varFuncSet;
|
||||
case SSEQ_CMD_ADDVAR:
|
||||
return varFuncAdd;
|
||||
case SSEQ_CMD_SUBVAR:
|
||||
return varFuncSub;
|
||||
case SSEQ_CMD_MULVAR:
|
||||
return varFuncMul;
|
||||
case SSEQ_CMD_DIVVAR:
|
||||
return varFuncDiv;
|
||||
case SSEQ_CMD_SHIFTVAR:
|
||||
return varFuncShift;
|
||||
case SSEQ_CMD_RANDVAR:
|
||||
return varFuncRand;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static auto compareFuncEq = [](int16_t a, int16_t b) { return a == b; };
|
||||
static auto compareFuncGe = [](int16_t a, int16_t b) { return a >= b; };
|
||||
static auto compareFuncGt = [](int16_t a, int16_t b) { return a > b; };
|
||||
static auto compareFuncLe = [](int16_t a, int16_t b) { return a <= b; };
|
||||
static auto compareFuncLt = [](int16_t a, int16_t b) { return a < b; };
|
||||
static auto compareFuncNe = [](int16_t a, int16_t b) { return a != b; };
|
||||
|
||||
static inline std::function<bool (int16_t, int16_t)> CompareFunc(int cmd)
|
||||
{
|
||||
switch (cmd)
|
||||
{
|
||||
case SSEQ_CMD_CMP_EQ:
|
||||
return compareFuncEq;
|
||||
case SSEQ_CMD_CMP_GE:
|
||||
return compareFuncGe;
|
||||
case SSEQ_CMD_CMP_GT:
|
||||
return compareFuncGt;
|
||||
case SSEQ_CMD_CMP_LE:
|
||||
return compareFuncLe;
|
||||
case SSEQ_CMD_CMP_LT:
|
||||
return compareFuncLt;
|
||||
case SSEQ_CMD_CMP_NE:
|
||||
return compareFuncNe;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Original FSS Function: Track_Run
|
||||
void Track::Run()
|
||||
{
|
||||
// Indicate "heartbeat" for this track
|
||||
|
@ -323,13 +496,17 @@ void Track::Run()
|
|||
|
||||
while (!this->wait)
|
||||
{
|
||||
int cmd = read8(pData);
|
||||
int cmd;
|
||||
if (this->overriding())
|
||||
cmd = this->overriding.cmd;
|
||||
else
|
||||
cmd = read8(pData);
|
||||
if (cmd < 0x80)
|
||||
{
|
||||
// Note on
|
||||
int key = cmd + this->transpose;
|
||||
int vel = read8(pData);
|
||||
int len = readvl(pData);
|
||||
int vel = this->overriding.val(pData, read8, true);
|
||||
int len = this->overriding.val(pData, readvl);
|
||||
if (this->state[TS_NOTEWAIT])
|
||||
this->wait = len;
|
||||
if (this->state[TS_TIEBIT])
|
||||
|
@ -338,18 +515,33 @@ void Track::Run()
|
|||
this->NoteOn(key, vel, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
int value;
|
||||
switch (cmd)
|
||||
{
|
||||
//-----------------------------------------------------------------
|
||||
// Main commands
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
case SSEQ_CMD_OPENTRACK:
|
||||
{
|
||||
int tNum = read8(pData);
|
||||
auto trackPos = &this->ply->sseq->data[read24(pData)];
|
||||
int newTrack = this->ply->TrackAlloc();
|
||||
if (newTrack != -1)
|
||||
{
|
||||
this->ply->tracks[newTrack].Init(newTrack, this->ply, trackPos, tNum);
|
||||
this->ply->trackIds[this->ply->nTracks++] = newTrack;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SSEQ_CMD_REST:
|
||||
this->wait = readvl(pData);
|
||||
this->wait = this->overriding.val(pData, readvl);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_PATCH:
|
||||
this->patch = readvl(pData);
|
||||
this->patch = this->overriding.val(pData, readvl);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_GOTO:
|
||||
|
@ -357,29 +549,32 @@ void Track::Run()
|
|||
break;
|
||||
|
||||
case SSEQ_CMD_CALL:
|
||||
{
|
||||
const uint8_t *dest = &this->ply->sseq->data[read24(pData)];
|
||||
this->stack[this->stackPos++] = *pData;
|
||||
*pData = dest;
|
||||
value = read24(pData);
|
||||
if (this->stackPos < FSS_TRACKSTACKSIZE)
|
||||
{
|
||||
const uint8_t *dest = &this->ply->sseq->data[value];
|
||||
this->stack[this->stackPos++] = StackValue(STACKTYPE_CALL, *pData);
|
||||
*pData = dest;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SSEQ_CMD_RET:
|
||||
*pData = this->stack[--this->stackPos];
|
||||
if (this->stackPos && this->stack[this->stackPos - 1].type == STACKTYPE_CALL)
|
||||
*pData = this->stack[--this->stackPos].dest;
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_PAN:
|
||||
this->pan = read8(pData) - 64;
|
||||
this->pan = this->overriding.val(pData, read8) - 64;
|
||||
this->updateFlags.set(TUF_PAN);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_VOL:
|
||||
this->vol = read8(pData);
|
||||
this->vol = this->overriding.val(pData, read8);
|
||||
this->updateFlags.set(TUF_VOL);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_MASTERVOL:
|
||||
this->ply->masterVol = Cnv_Sust(read8(pData));
|
||||
this->ply->masterVol = Cnv_Sust(this->overriding.val(pData, read8));
|
||||
for (uint8_t i = 0; i < this->ply->nTracks; ++i)
|
||||
this->ply->tracks[this->ply->trackIds[i]].updateFlags.set(TUF_VOL);
|
||||
break;
|
||||
|
@ -399,7 +594,7 @@ void Track::Run()
|
|||
break;
|
||||
|
||||
case SSEQ_CMD_EXPR:
|
||||
this->expr = read8(pData);
|
||||
this->expr = this->overriding.val(pData, read8);
|
||||
this->updateFlags.set(TUF_VOL);
|
||||
break;
|
||||
|
||||
|
@ -412,19 +607,24 @@ void Track::Run()
|
|||
return;
|
||||
|
||||
case SSEQ_CMD_LOOPSTART:
|
||||
this->loopCount[this->stackPos] = read8(pData);
|
||||
this->stack[this->stackPos++] = *pData;
|
||||
value = this->overriding.val(pData, read8);
|
||||
if (this->stackPos < FSS_TRACKSTACKSIZE)
|
||||
{
|
||||
this->loopCount[this->stackPos] = value;
|
||||
this->stack[this->stackPos++] = StackValue(STACKTYPE_LOOP, *pData);
|
||||
}
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_LOOPEND:
|
||||
if (this->stackPos)
|
||||
if (this->stackPos && this->stack[this->stackPos - 1].type == STACKTYPE_LOOP)
|
||||
{
|
||||
const uint8_t *rPos = this->stack[this->stackPos - 1];
|
||||
const uint8_t *rPos = this->stack[this->stackPos - 1].dest;
|
||||
uint8_t &nR = this->loopCount[this->stackPos - 1];
|
||||
uint8_t prevR = nR;
|
||||
if (prevR && !--nR)
|
||||
if (!prevR || --nR)
|
||||
*pData = rPos;
|
||||
else
|
||||
--this->stackPos;
|
||||
*pData = rPos;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -433,11 +633,11 @@ void Track::Run()
|
|||
//-----------------------------------------------------------------
|
||||
|
||||
case SSEQ_CMD_TRANSPOSE:
|
||||
this->transpose = read8(pData);
|
||||
this->transpose = this->overriding.val(pData, read8);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_PITCHBEND:
|
||||
this->pitchBend = read8(pData);
|
||||
this->pitchBend = this->overriding.val(pData, read8);
|
||||
this->updateFlags.set(TUF_TIMER);
|
||||
break;
|
||||
|
||||
|
@ -451,19 +651,19 @@ void Track::Run()
|
|||
//-----------------------------------------------------------------
|
||||
|
||||
case SSEQ_CMD_ATTACK:
|
||||
this->a = read8(pData);
|
||||
this->a = this->overriding.val(pData, read8);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_DECAY:
|
||||
this->d = read8(pData);
|
||||
this->d = this->overriding.val(pData, read8);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_SUSTAIN:
|
||||
this->s = read8(pData);
|
||||
this->s = this->overriding.val(pData, read8);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_RELEASE:
|
||||
this->r = read8(pData);
|
||||
this->r = this->overriding.val(pData, read8);
|
||||
break;
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
|
@ -482,12 +682,12 @@ void Track::Run()
|
|||
break;
|
||||
|
||||
case SSEQ_CMD_PORTATIME:
|
||||
this->portaTime = read8(pData);
|
||||
this->portaTime = this->overriding.val(pData, read8);
|
||||
// Update here?
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_SWEEPPITCH:
|
||||
this->sweepPitch = read16(pData);
|
||||
this->sweepPitch = this->overriding.val(pData, read16);
|
||||
// Update here?
|
||||
break;
|
||||
|
||||
|
@ -496,12 +696,12 @@ void Track::Run()
|
|||
//-----------------------------------------------------------------
|
||||
|
||||
case SSEQ_CMD_MODDEPTH:
|
||||
this->modDepth = read8(pData);
|
||||
this->modDepth = this->overriding.val(pData, read8);
|
||||
this->updateFlags.set(TUF_MOD);
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_MODSPEED:
|
||||
this->modSpeed = read8(pData);
|
||||
this->modSpeed = this->overriding.val(pData, read8);
|
||||
this->updateFlags.set(TUF_MOD);
|
||||
break;
|
||||
|
||||
|
@ -516,37 +716,97 @@ void Track::Run()
|
|||
break;
|
||||
|
||||
case SSEQ_CMD_MODDELAY:
|
||||
this->modDelay = read16(pData);
|
||||
this->modDelay = this->overriding.val(pData, read16);
|
||||
this->updateFlags.set(TUF_MOD);
|
||||
break;
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Randomness-related commands
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
case SSEQ_CMD_RANDOM:
|
||||
{
|
||||
this->overriding() = true;
|
||||
this->overriding.cmd = read8(pData);
|
||||
if ((this->overriding.cmd >= SSEQ_CMD_SETVAR && this->overriding.cmd <= SSEQ_CMD_CMP_NE) || this->overriding.cmd < 0x80)
|
||||
this->overriding.extraValue = read8(pData);
|
||||
int16_t minVal = read16(pData);
|
||||
int16_t maxVal = read16(pData);
|
||||
this->overriding.value = (std::rand() % (maxVal - minVal + 1)) + minVal;
|
||||
break;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
// Variable-related commands
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
case SSEQ_CMD_RANDOM: // TODO
|
||||
*pData += 5;
|
||||
case SSEQ_CMD_FROMVAR:
|
||||
this->overriding() = true;
|
||||
this->overriding.cmd = read8(pData);
|
||||
if ((this->overriding.cmd >= SSEQ_CMD_SETVAR && this->overriding.cmd <= SSEQ_CMD_CMP_NE) || this->overriding.cmd < 0x80)
|
||||
this->overriding.extraValue = read8(pData);
|
||||
this->overriding.value = this->ply->variables[read8(pData)];
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_PRINTVAR: // TODO
|
||||
*pData += 1;
|
||||
break;
|
||||
|
||||
case SSEQ_CMD_UNSUP1: // TODO
|
||||
case SSEQ_CMD_SETVAR:
|
||||
case SSEQ_CMD_ADDVAR:
|
||||
case SSEQ_CMD_SUBVAR:
|
||||
case SSEQ_CMD_MULVAR:
|
||||
case SSEQ_CMD_DIVVAR:
|
||||
case SSEQ_CMD_SHIFTVAR:
|
||||
case SSEQ_CMD_RANDVAR:
|
||||
{
|
||||
int t = read8(pData);
|
||||
if (t >= SSEQ_CMD_UNSUP2_LO && t <= SSEQ_CMD_UNSUP2_HI)
|
||||
*pData += 1;
|
||||
*pData += 1;
|
||||
int8_t varNo = this->overriding.val(pData, read8, true);
|
||||
value = this->overriding.val(pData, read16);
|
||||
if (cmd == SSEQ_CMD_DIVVAR && !value) // Division by 0, skip it to prevent crashing
|
||||
break;
|
||||
this->ply->variables[varNo] = VarFunc(cmd)(this->ply->variables[varNo], value);
|
||||
break;
|
||||
}
|
||||
|
||||
case SSEQ_CMD_IF: // TODO
|
||||
//-----------------------------------------------------------------
|
||||
// Conditional-related commands
|
||||
//-----------------------------------------------------------------
|
||||
|
||||
case SSEQ_CMD_CMP_EQ:
|
||||
case SSEQ_CMD_CMP_GE:
|
||||
case SSEQ_CMD_CMP_GT:
|
||||
case SSEQ_CMD_CMP_LE:
|
||||
case SSEQ_CMD_CMP_LT:
|
||||
case SSEQ_CMD_CMP_NE:
|
||||
{
|
||||
int8_t varNo = this->overriding.val(pData, read8, true);
|
||||
value = this->overriding.val(pData, read16);
|
||||
this->lastComparisonResult = CompareFunc(cmd)(this->ply->variables[varNo], value);
|
||||
break;
|
||||
}
|
||||
|
||||
case SSEQ_CMD_IF:
|
||||
if (!this->lastComparisonResult)
|
||||
{
|
||||
int nextCmd = read8(pData);
|
||||
uint8_t cmdBytes = SseqCommandByteCount(nextCmd);
|
||||
bool variableBytes = !!(cmdBytes & VariableByteCount);
|
||||
bool extraByte = !!(cmdBytes & ExtraByteOnNoteOrVarOrCmp);
|
||||
cmdBytes &= ~(VariableByteCount | ExtraByteOnNoteOrVarOrCmp);
|
||||
if (extraByte)
|
||||
{
|
||||
int extraCmd = read8(pData);
|
||||
if ((extraCmd >= SSEQ_CMD_SETVAR && extraCmd <= SSEQ_CMD_CMP_NE) || extraCmd < 0x80)
|
||||
++cmdBytes;
|
||||
}
|
||||
*pData += cmdBytes;
|
||||
if (variableBytes)
|
||||
readvl(pData);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (cmd >= SSEQ_CMD_UNSUP2_LO && cmd <= SSEQ_CMD_UNSUP2_HI) // TODO
|
||||
*pData += 3;
|
||||
*pData += SseqCommandByteCount(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmd != SSEQ_CMD_RANDOM && cmd != SSEQ_CMD_FROMVAR)
|
||||
this->overriding() = false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,55 @@
|
|||
/*
|
||||
* SSEQ Player - Track structure
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-01
|
||||
* Last modification on 2014-10-13
|
||||
*
|
||||
* Adapted from source code of FeOS Sound System
|
||||
* By fincs
|
||||
* https://github.com/fincs/FSS
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_TRACK_H
|
||||
#define SSEQPLAYER_TRACK_H
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <bitset>
|
||||
#include "consts.h"
|
||||
|
||||
struct Player;
|
||||
|
||||
enum StackType
|
||||
{
|
||||
STACKTYPE_CALL,
|
||||
STACKTYPE_LOOP
|
||||
};
|
||||
|
||||
struct StackValue
|
||||
{
|
||||
StackType type;
|
||||
const uint8_t *dest;
|
||||
|
||||
StackValue() : type(STACKTYPE_CALL), dest(nullptr) { }
|
||||
StackValue(StackType newType, const uint8_t *newDest) : type(newType), dest(newDest) { }
|
||||
};
|
||||
|
||||
struct Override
|
||||
{
|
||||
bool overriding;
|
||||
int cmd;
|
||||
int value;
|
||||
int extraValue;
|
||||
|
||||
Override() : overriding(false) { }
|
||||
bool operator()() const { return this->overriding; }
|
||||
bool &operator()() { return this->overriding; }
|
||||
int val(const uint8_t **pData, std::function<int (const uint8_t **)> reader, bool returnExtra = false)
|
||||
{
|
||||
if (this->overriding)
|
||||
return returnExtra ? this->extraValue : this->value;
|
||||
else
|
||||
return reader(pData);
|
||||
}
|
||||
};
|
||||
|
||||
struct Track
|
||||
{
|
||||
int8_t trackId;
|
||||
|
@ -26,9 +60,11 @@ struct Track
|
|||
|
||||
const uint8_t *startPos;
|
||||
const uint8_t *pos;
|
||||
const uint8_t *stack[FSS_TRACKSTACKSIZE];
|
||||
StackValue stack[FSS_TRACKSTACKSIZE];
|
||||
uint8_t stackPos;
|
||||
uint8_t loopCount[FSS_TRACKSTACKSIZE];
|
||||
Override overriding;
|
||||
bool lastComparisonResult;
|
||||
|
||||
int wait;
|
||||
uint16_t patch;
|
||||
|
@ -58,5 +94,3 @@ struct Track
|
|||
void ReleaseAllNotes();
|
||||
void Run();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
// original code is from here: http://www.codeproject.com/Tips/197097/Converting-ANSI-to-Unicode-and-back
|
||||
// License: The Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
|
||||
|
||||
#include "UTFEncodeDecode.h"
|
||||
#include "UtfConverter.h"
|
||||
|
||||
std::string EncodeToUTF8(const std::string &source, const std::locale &L)
|
||||
{
|
||||
try
|
||||
{
|
||||
return UtfConverter::ToUtf8(cp_converter<>(L).widen(source));
|
||||
}
|
||||
catch (const std::runtime_error &)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
std::string DecodeFromUTF8(const std::string &source, const std::locale &L)
|
||||
{
|
||||
try
|
||||
{
|
||||
return cp_converter<>(L).narrow(UtfConverter::FromUtf8(source));
|
||||
}
|
||||
catch (const std::runtime_error &)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
// original code is from here: http://www.codeproject.com/Tips/197097/Converting-ANSI-to-Unicode-and-back
|
||||
// License: The Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
|
||||
|
||||
#ifndef UTFENCODEDECODE_H
|
||||
#define UTFENCODEDECODE_H
|
||||
|
||||
#include <string>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
std::string EncodeToUTF8(const std::string &, const std::locale & = std::locale::classic());
|
||||
std::string DecodeFromUTF8(const std::string &, const std::locale & = std::locale::classic());
|
||||
|
||||
template<size_t buf_size = 100> class cp_converter
|
||||
{
|
||||
const std::locale loc;
|
||||
public:
|
||||
cp_converter(const std::locale &L = std::locale::classic()) : loc(L) { }
|
||||
inline std::wstring widen(const std::string &in)
|
||||
{
|
||||
return this->convert<char, wchar_t>(in);
|
||||
}
|
||||
inline std::string narrow(const std::wstring &in)
|
||||
{
|
||||
return this->convert<wchar_t, char>(in);
|
||||
}
|
||||
private:
|
||||
typedef std::codecvt<wchar_t, char, mbstate_t> codecvt_facet;
|
||||
|
||||
// widen
|
||||
inline codecvt_facet::result cv(const codecvt_facet &facet, mbstate_t &s, const char *f1, const char *l1, const char *&n1, wchar_t *f2, wchar_t *l2, wchar_t *&n2) const
|
||||
{
|
||||
return facet.in(s, f1, l1, n1, f2, l2, n2);
|
||||
}
|
||||
|
||||
// narrow
|
||||
inline codecvt_facet::result cv(const codecvt_facet &facet, mbstate_t &s, const wchar_t *f1, const wchar_t *l1, const wchar_t *&n1, char *f2, char *l2, char *&n2) const
|
||||
{
|
||||
return facet.out(s, f1, l1, n1, f2, l2, n2);
|
||||
}
|
||||
|
||||
template<class ct_in, class ct_out> std::basic_string<ct_out> convert(const std::basic_string<ct_in> &in)
|
||||
{
|
||||
auto &facet = std::use_facet<codecvt_facet>(this->loc);
|
||||
std::basic_stringstream<ct_out> os;
|
||||
ct_out buf[buf_size];
|
||||
mbstate_t state = {0};
|
||||
codecvt_facet::result result;
|
||||
const ct_in *ipc = &in[0];
|
||||
do
|
||||
{
|
||||
ct_out *opc = nullptr;
|
||||
result = this->cv(facet, state, ipc, &in[0] + in.length(), ipc, buf, buf + buf_size, opc);
|
||||
os << std::basic_string<ct_out>(buf, opc - buf);
|
||||
} while (ipc < &in[0] + in.length() && result != codecvt_facet::error);
|
||||
if (result != codecvt_facet::ok)
|
||||
throw std::runtime_error("result is not ok!");
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,69 +0,0 @@
|
|||
// original code is from here: http://www.codeproject.com/Articles/17573/Convert-Between-std-string-and-std-wstring-UTF-8-a
|
||||
// License: The Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include "ConvertUTF.h"
|
||||
|
||||
namespace UtfConverter
|
||||
{
|
||||
std::wstring FromUtf8(const std::string &utf8string)
|
||||
{
|
||||
auto widesize = utf8string.length();
|
||||
auto result = std::vector<wchar_t>(widesize + 1, L'\0');
|
||||
auto orig = std::vector<char>(widesize + 1, '\0');
|
||||
std::copy(utf8string.begin(), utf8string.end(), orig.begin());
|
||||
auto *sourcestart = reinterpret_cast<const UTF8 *>(&orig[0]), *sourceend = sourcestart + widesize;
|
||||
ConversionResult res;
|
||||
if (sizeof(wchar_t) == 2)
|
||||
{
|
||||
auto *targetstart = reinterpret_cast<UTF16 *>(&result[0]), *targetend = targetstart + widesize;
|
||||
res = ConvertUTF8toUTF16(&sourcestart, sourceend, &targetstart, targetend, strictConversion);
|
||||
*targetstart = 0;
|
||||
unsigned end = targetstart - reinterpret_cast<UTF16 *>(&result[0]);
|
||||
result.erase(result.begin() + end, result.end());
|
||||
}
|
||||
else if (sizeof(wchar_t) == 4)
|
||||
{
|
||||
auto *targetstart = reinterpret_cast<UTF32 *>(&result[0]), *targetend = targetstart + widesize;
|
||||
res = ConvertUTF8toUTF32(&sourcestart, sourceend, &targetstart, targetend, strictConversion);
|
||||
*targetstart = 0;
|
||||
unsigned long end = targetstart - reinterpret_cast<UTF32 *>(&result[0]);
|
||||
result.erase(result.begin() + end, result.end());
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("UtfConverter::FromUtf8: sizeof(wchar_t) is not 2 or 4.");
|
||||
if (res != conversionOK)
|
||||
throw std::runtime_error("UtfConverter::FromUtf8: Conversion failed.");
|
||||
return std::wstring(result.begin(), result.end());
|
||||
}
|
||||
|
||||
std::string ToUtf8(const std::wstring &widestring)
|
||||
{
|
||||
auto widesize = widestring.length(), utf8size = (sizeof(wchar_t) == 2 ? 3 : 4) * widesize + 1;
|
||||
auto result = std::vector<char>(utf8size, '\0');
|
||||
auto orig = std::vector<wchar_t>(widesize + 1, L'\0');
|
||||
std::copy(widestring.begin(), widestring.end(), orig.begin());
|
||||
auto *targetstart = reinterpret_cast<UTF8 *>(&result[0]), *targetend = targetstart + utf8size;
|
||||
ConversionResult res;
|
||||
if (sizeof(wchar_t) == 2)
|
||||
{
|
||||
auto *sourcestart = reinterpret_cast<const UTF16 *>(&orig[0]), *sourceend = sourcestart + widesize;
|
||||
res = ConvertUTF16toUTF8(&sourcestart, sourceend, &targetstart, targetend, strictConversion);
|
||||
}
|
||||
else if (sizeof(wchar_t) == 4)
|
||||
{
|
||||
auto *sourcestart = reinterpret_cast<const UTF32 *>(&orig[0]), *sourceend = sourcestart + widesize;
|
||||
res = ConvertUTF32toUTF8(&sourcestart, sourceend, &targetstart, targetend, strictConversion);
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("UtfConverter::ToUtf8: sizeof(wchar_t) is not 2 or 4.");
|
||||
if (res != conversionOK)
|
||||
throw std::runtime_error("UtfConverter::ToUtf8: Conversion failed.");
|
||||
*targetstart = 0;
|
||||
auto end = targetstart - reinterpret_cast<UTF8 *>(&result[0]);
|
||||
result.erase(result.begin() + end, result.end());
|
||||
return std::string(result.begin(), result.end());
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
// original code is from here: http://www.codeproject.com/Articles/17573/Convert-Between-std-string-and-std-wstring-UTF-8-a
|
||||
// License: The Code Project Open License (CPOL) http://www.codeproject.com/info/cpol10.aspx
|
||||
|
||||
#ifndef UTFCONVERTER_H
|
||||
#define UTFCONVERTER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace UtfConverter
|
||||
{
|
||||
std::wstring FromUtf8(const std::string &);
|
||||
std::string ToUtf8(const std::wstring &);
|
||||
}
|
||||
|
||||
#endif
|
2098
Frameworks/SSEQPlayer/SSEQPlayer/codecvt.h
Normal file
2098
Frameworks/SSEQPlayer/SSEQPlayer/codecvt.h
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,15 +1,14 @@
|
|||
/*
|
||||
* SSEQ Player - Common functions
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-3
|
||||
* Last modification on 2014-10-18
|
||||
*
|
||||
* Some code from FeOS Sound System
|
||||
* By fincs
|
||||
* https://github.com/fincs/FSS
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_COMMON_H
|
||||
#define SSEQPLAYER_COMMON_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -143,11 +142,15 @@ inline int Cnv_Attack(int attk)
|
|||
0x5C, 0x64, 0x6D, 0x74, 0x7B, 0x7F, 0x84, 0x89, 0x8F
|
||||
};
|
||||
|
||||
if (attk & 0x80) // Supposedly invalid value...
|
||||
attk = 0; // Use apparently correct default
|
||||
return attk >= 0x6D ? lut[0x7F - attk] : 0xFF - attk;
|
||||
}
|
||||
|
||||
inline int Cnv_Fall(int fall)
|
||||
{
|
||||
if (fall & 0x80) // Supposedly invalid value...
|
||||
fall = 0; // Use apparently correct default
|
||||
if (fall == 0x7F)
|
||||
return 0xFFFF;
|
||||
else if (fall == 0x7E)
|
||||
|
@ -158,6 +161,33 @@ inline int Cnv_Fall(int fall)
|
|||
return (0x1E00 / (0x7E - fall)) & 0xFFFF;
|
||||
}
|
||||
|
||||
inline int Cnv_Scale(int scale)
|
||||
{
|
||||
static const int16_t lut[] =
|
||||
{
|
||||
-32768, -421, -361, -325, -300, -281, -265, -252,
|
||||
-240, -230, -221, -212, -205, -198, -192, -186,
|
||||
-180, -175, -170, -165, -161, -156, -152, -148,
|
||||
-145, -141, -138, -134, -131, -128, -125, -122,
|
||||
-120, -117, -114, -112, -110, -107, -105, -103,
|
||||
-100, -98, -96, -94, -92, -90, -88, -86,
|
||||
-85, -83, -81, -79, -78, -76, -74, -73,
|
||||
-71, -70, -68, -67, -65, -64, -62, -61,
|
||||
-60, -58, -57, -56, -54, -53, -52, -51,
|
||||
-49, -48, -47, -46, -45, -43, -42, -41,
|
||||
-40, -39, -38, -37, -36, -35, -34, -33,
|
||||
-32, -31, -30, -29, -28, -27, -26, -25,
|
||||
-24, -23, -23, -22, -21, -20, -19, -18,
|
||||
-17, -17, -16, -15, -14, -13, -12, -12,
|
||||
-11, -10, -9, -9, -8, -7, -6, -6,
|
||||
-5, -4, -3, -3, -2, -1, -1, 0
|
||||
};
|
||||
|
||||
if (scale & 0x80) // Supposedly invalid value...
|
||||
scale = 0x7F; // Use apparently correct default
|
||||
return lut[scale];
|
||||
}
|
||||
|
||||
inline int Cnv_Sust(int sust)
|
||||
{
|
||||
static const int16_t lut[] =
|
||||
|
@ -180,6 +210,8 @@ inline int Cnv_Sust(int sust)
|
|||
-10, -8, -7, -6, -4, -3, -1, 0
|
||||
};
|
||||
|
||||
if (sust & 0x80) // Supposedly invalid value...
|
||||
sust = 0x7F; // Use apparently correct default
|
||||
return lut[sust];
|
||||
}
|
||||
|
||||
|
@ -238,4 +270,11 @@ inline int readvl(const uint8_t **ppData)
|
|||
return x;
|
||||
}
|
||||
|
||||
#endif
|
||||
// Clamp a value between a minimum and maximum value
|
||||
template<typename T1, typename T2> inline void clamp(T1 &valueToClamp, const T2 &minValue, const T2 &maxValue)
|
||||
{
|
||||
if (valueToClamp < minValue)
|
||||
valueToClamp = minValue;
|
||||
else if (valueToClamp > maxValue)
|
||||
valueToClamp = maxValue;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* SSEQ Player - Constants/Macros
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-04-12
|
||||
* Last modification on 2014-09-08
|
||||
*
|
||||
* Adapted from source code of FeOS Sound System
|
||||
* By fincs
|
||||
|
@ -11,8 +11,7 @@
|
|||
* http://devkitpro.org/
|
||||
*/
|
||||
|
||||
#ifndef SSEQPLAYER_CONSTS_H
|
||||
#define SSEQPLAYER_CONSTS_H
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
@ -55,11 +54,7 @@ enum Interpolation
|
|||
{
|
||||
INTERPOLATION_NONE,
|
||||
INTERPOLATION_LINEAR,
|
||||
INTERPOLATION_COSINE,
|
||||
INTERPOLATION_4POINTBSPLINE,
|
||||
INTERPOLATION_6POINTOSCULATING,
|
||||
INTERPOLATION_6POINTBSPLINE,
|
||||
INTERPOLATION_LANCZOS
|
||||
INTERPOLATION_4POINTLEGRANGE,
|
||||
INTERPOLATION_6POINTLEGRANGE,
|
||||
INTERPOLATION_SINC
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
/*
|
||||
* Common conversion functions
|
||||
* By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
|
||||
* Last modification on 2013-03-30
|
||||
* Last modification on 2014-09-24
|
||||
*/
|
||||
|
||||
#ifndef _CONVERT_H_
|
||||
#define _CONVERT_H_
|
||||
#pragma once
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <typeinfo>
|
||||
#include <locale>
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && !defined(_LIBCPP_VERSION)
|
||||
# include "wstring_convert.h"
|
||||
# include "codecvt.h"
|
||||
#else
|
||||
# include <codecvt>
|
||||
#endif
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include "BigSString.h"
|
||||
|
||||
/*
|
||||
* The following exception class and the *stringify and convert* functions are
|
||||
|
@ -130,7 +134,7 @@ public:
|
|||
|
||||
static unsigned long StringToMS(const std::wstring &time)
|
||||
{
|
||||
return ConvertFuncs::StringToMS(String(time).GetAnsi());
|
||||
return ConvertFuncs::StringToMS(ConvertFuncs::WStringToString(time));
|
||||
}
|
||||
|
||||
static std::string MSToString(unsigned long time)
|
||||
|
@ -149,8 +153,18 @@ public:
|
|||
|
||||
static std::wstring MSToWString(unsigned long time)
|
||||
{
|
||||
return String(ConvertFuncs::MSToString(time)).GetWStr();
|
||||
return ConvertFuncs::StringToWString(ConvertFuncs::MSToString(time));
|
||||
}
|
||||
|
||||
static std::wstring StringToWString(const std::string &str)
|
||||
{
|
||||
static std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
|
||||
return conv.from_bytes(str);
|
||||
}
|
||||
|
||||
static std::string WStringToString(const std::wstring &wstr)
|
||||
{
|
||||
static std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
|
||||
return conv.to_bytes(wstr);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
183
Frameworks/SSEQPlayer/SSEQPlayer/wstring_convert.h
Normal file
183
Frameworks/SSEQPlayer/SSEQPlayer/wstring_convert.h
Normal file
|
@ -0,0 +1,183 @@
|
|||
// This comes from llvm's libcxx project. I've copied the code from there (with very minor modifications) for use with GCC and Clang when libcxx isn't being used.
|
||||
|
||||
#if (defined(__GNUC__) || defined(__clang__)) && !defined(_LIBCPP_VERSION)
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <locale>
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template<class _Codecvt, class _Elem = wchar_t, class _Wide_alloc = allocator<_Elem>, class _Byte_alloc = allocator<char>> class wstring_convert
|
||||
{
|
||||
public:
|
||||
typedef basic_string<char, char_traits<char>, _Byte_alloc> byte_string;
|
||||
typedef basic_string<_Elem, char_traits<_Elem>, _Wide_alloc> wide_string;
|
||||
typedef typename _Codecvt::state_type state_type;
|
||||
typedef typename wide_string::traits_type::int_type int_type;
|
||||
|
||||
private:
|
||||
byte_string __byte_err_string_;
|
||||
wide_string __wide_err_string_;
|
||||
_Codecvt *__cvtptr_;
|
||||
state_type __cvtstate_;
|
||||
size_t __cvtcount_;
|
||||
|
||||
wstring_convert(const wstring_convert &__wc);
|
||||
wstring_convert& operator=(const wstring_convert &__wc);
|
||||
public:
|
||||
wstring_convert(_Codecvt *__pcvt = new _Codecvt) : __cvtptr_(__pcvt), __cvtstate_(), __cvtcount_(0) { }
|
||||
wstring_convert(_Codecvt *__pcvt, state_type __state) : __cvtptr_(__pcvt), __cvtstate_(__state), __cvtcount_(0) { }
|
||||
wstring_convert(const byte_string &__byte_err, const wide_string &__wide_err = wide_string()) : __byte_err_string_(__byte_err), __wide_err_string_(__wide_err), __cvtptr_(new _Codecvt), __cvtstate_(), __cvtcount_(0) { }
|
||||
wstring_convert(wstring_convert &&__wc) : __byte_err_string_(move(__wc.__byte_err_string_)), __wide_err_string_(move(__wc.__wide_err_string_)), __cvtptr_(__wc.__cvtptr_), __cvtstate_(__wc.__cvtstate_), __cvtcount_(__wc.__cvtstate_)
|
||||
{
|
||||
__wc.__cvtptr_ = nullptr;
|
||||
}
|
||||
~wstring_convert() { delete this->__cvtptr_; }
|
||||
|
||||
wide_string from_bytes(char __byte) { return from_bytes(&__byte, &__byte + 1); }
|
||||
wide_string from_bytes(const char *__ptr) { return from_bytes(__ptr, __ptr + char_traits<char>::length(__ptr)); }
|
||||
wide_string from_bytes(const byte_string &__str) { return from_bytes(__str.data(), __str.data() + __str.size()); }
|
||||
wide_string from_bytes(const char *__frm, const char *__frm_end)
|
||||
{
|
||||
this->__cvtcount_ = 0;
|
||||
if (this->__cvtptr_)
|
||||
{
|
||||
wide_string __ws(2 * (__frm_end - __frm), _Elem());
|
||||
if (__frm != __frm_end)
|
||||
__ws.resize(__ws.capacity());
|
||||
auto __r = codecvt_base::ok;
|
||||
auto __st = this->__cvtstate_;
|
||||
if (__frm != __frm_end)
|
||||
{
|
||||
auto __to = &__ws[0];
|
||||
auto __to_end = __to + __ws.size();
|
||||
const char *__frm_nxt;
|
||||
do
|
||||
{
|
||||
_Elem *__to_nxt;
|
||||
__r = this->__cvtptr_->in(__st, __frm, __frm_end, __frm_nxt, __to, __to_end, __to_nxt);
|
||||
this->__cvtcount_ += __frm_nxt - __frm;
|
||||
if (__frm_nxt == __frm)
|
||||
__r = codecvt_base::error;
|
||||
else if (__r == codecvt_base::noconv)
|
||||
{
|
||||
__ws.resize(__to - &__ws[0]);
|
||||
// This only gets executed if _Elem is char
|
||||
__ws.append(reinterpret_cast<const _Elem *>(__frm), reinterpret_cast<const _Elem *>(__frm_end));
|
||||
__frm = __frm_nxt;
|
||||
__r = codecvt_base::ok;
|
||||
}
|
||||
else if (__r == codecvt_base::ok)
|
||||
{
|
||||
__ws.resize(__to_nxt - &__ws[0]);
|
||||
__frm = __frm_nxt;
|
||||
}
|
||||
else if (__r == codecvt_base::partial)
|
||||
{
|
||||
ptrdiff_t __s = __to_nxt - &__ws[0];
|
||||
__ws.resize(2 * __s);
|
||||
__to = &__ws[0] + __s;
|
||||
__to_end = &__ws[0] + __ws.size();
|
||||
__frm = __frm_nxt;
|
||||
}
|
||||
} while (__r == codecvt_base::partial && __frm_nxt < __frm_end);
|
||||
}
|
||||
if (__r == codecvt_base::ok)
|
||||
return __ws;
|
||||
}
|
||||
if (this->__wide_err_string_.empty())
|
||||
throw range_error("wstring_convert: from_bytes error");
|
||||
return this->__wide_err_string_;
|
||||
}
|
||||
|
||||
byte_string to_bytes(_Elem __wchar) { return to_bytes(&__wchar, &__wchar + 1); }
|
||||
byte_string to_bytes(const _Elem *__wptr) { return to_bytes(__wptr, __wptr + char_traits<_Elem>::length(__wptr)); }
|
||||
byte_string to_bytes(const wide_string &__wstr) { return to_bytes(__wstr.data(), __wstr.data() + __wstr.size()); }
|
||||
byte_string to_bytes(const _Elem *__frm, const _Elem *__frm_end)
|
||||
{
|
||||
this->__cvtcount_ = 0;
|
||||
if (this->__cvtptr_)
|
||||
{
|
||||
byte_string __bs(2 * (__frm_end - __frm), char());
|
||||
if (__frm != __frm_end)
|
||||
__bs.resize(__bs.capacity());
|
||||
auto __r = codecvt_base::ok;
|
||||
auto __st = this->__cvtstate_;
|
||||
if (__frm != __frm_end)
|
||||
{
|
||||
auto __to = &__bs[0];
|
||||
auto __to_end = __to + __bs.size();
|
||||
const _Elem *__frm_nxt;
|
||||
do
|
||||
{
|
||||
char *__to_nxt;
|
||||
__r = this->__cvtptr_->out(__st, __frm, __frm_end, __frm_nxt, __to, __to_end, __to_nxt);
|
||||
this->__cvtcount_ += __frm_nxt - __frm;
|
||||
if (__frm_nxt == __frm)
|
||||
__r = codecvt_base::error;
|
||||
else if (__r == codecvt_base::noconv)
|
||||
{
|
||||
__bs.resize(__to - &__bs[0]);
|
||||
// This only gets executed if _Elem is char
|
||||
__bs.append(reinterpret_cast<const char *>(__frm), reinterpret_cast<const char *>(__frm_end));
|
||||
__frm = __frm_nxt;
|
||||
__r = codecvt_base::ok;
|
||||
}
|
||||
else if (__r == codecvt_base::ok)
|
||||
{
|
||||
__bs.resize(__to_nxt - &__bs[0]);
|
||||
__frm = __frm_nxt;
|
||||
}
|
||||
else if (__r == codecvt_base::partial)
|
||||
{
|
||||
ptrdiff_t __s = __to_nxt - &__bs[0];
|
||||
__bs.resize(2 * __s);
|
||||
__to = &__bs[0] + __s;
|
||||
__to_end = &__bs[0] + __bs.size();
|
||||
__frm = __frm_nxt;
|
||||
}
|
||||
} while (__r == codecvt_base::partial && __frm_nxt < __frm_end);
|
||||
}
|
||||
if (__r == codecvt_base::ok)
|
||||
{
|
||||
auto __s = __bs.size();
|
||||
__bs.resize(__bs.capacity());
|
||||
auto __to = &__bs[0] + __s;
|
||||
auto __to_end = __to + __bs.size();
|
||||
do
|
||||
{
|
||||
char *__to_nxt;
|
||||
__r = this->__cvtptr_->unshift(__st, __to, __to_end, __to_nxt);
|
||||
if (__r == codecvt_base::noconv)
|
||||
{
|
||||
__bs.resize(__to - &__bs[0]);
|
||||
__r = codecvt_base::ok;
|
||||
}
|
||||
else if (__r == codecvt_base::ok)
|
||||
__bs.resize(__to_nxt - &__bs[0]);
|
||||
else if (__r == codecvt_base::partial)
|
||||
{
|
||||
ptrdiff_t __sp = __to_nxt - &__bs[0];
|
||||
__bs.resize(2 * __sp);
|
||||
__to = &__bs[0] + __sp;
|
||||
__to_end = &__bs[0] + __bs.size();
|
||||
}
|
||||
} while (__r == codecvt_base::partial);
|
||||
if (__r == codecvt_base::ok)
|
||||
return __bs;
|
||||
}
|
||||
}
|
||||
if (this->__byte_err_string_.empty())
|
||||
throw range_error("wstring_convert: to_bytes error");
|
||||
return this->__byte_err_string_;
|
||||
}
|
||||
|
||||
size_t converted() const noexcept { return this->__cvtcount_; }
|
||||
state_type state() const { return this->__cvtstate_; }
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
Loading…
Reference in a new issue