diff --git a/QuintetPlayer.pro b/QuintetPlayer.pro index bc49497..578ada7 100644 --- a/QuintetPlayer.pro +++ b/QuintetPlayer.pro @@ -28,13 +28,16 @@ SOURCES += \ src/mainwindow.cpp \ src/clHCA.cpp \ src/utils.cpp \ - src/HCAStreamChannel.cpp + src/HCAStreamChannel.cpp \ + src/HCADecodeService.cpp HEADERS += \ src/mainwindow.h \ src/clHCA.h \ src/utils.h \ src/HCAStreamChannel.h \ + src/HCADecodeService.h \ + src/Semaphore.h \ bass/bass.h \ bass/bassmix.h diff --git a/src/HCADecodeService.cpp b/src/HCADecodeService.cpp new file mode 100644 index 0000000..6685664 --- /dev/null +++ b/src/HCADecodeService.cpp @@ -0,0 +1,145 @@ +#include +#include "bass.h" +#include "HCADecodeService.h" +#include "clHCA.h" + +// Currently multithreaded decode is bugged, please use single thread for now +HCADecodeService::HCADecodeService(unsigned int numthreads) + : mainsem(numthreads), + datasem{ 0 }, + numchannels{0}, + workingrequest{nullptr}, + channels{nullptr}, + shutdown{false} +{ + if(numthreads > 0) + { + this->numthreads = numthreads; + } + else + { + this->numthreads = 1; + } + workingblocks = new int[numthreads]; + for (unsigned int i = 0; i < numthreads; ++i) + { + workersem.emplace_back(0); + worker_threads.emplace_back(&HCADecodeService::Decode_Thread, this, i); + workingblocks[i] = -1; + } + dispatchthread = std::thread{ &HCADecodeService::Main_Thread, this }; +} + +HCADecodeService::~HCADecodeService() +{ + shutdown = true; + datasem.notify(); + dispatchthread.join(); +} + +void HCADecodeService::cancel_decode(void* ptr) +{ + mutex.lock(); + for (auto it = filelist.begin(); it != filelist.end(); ++it) + { + if (it->first.first == ptr) + { + filelist.erase(it); + break; + } + } + if (workingrequest == ptr) + { + blocks.clear(); + } + mutex.unlock(); + for (unsigned int i = 0; i < numthreads; ++i) + { + while (workingblocks[i] != -1); // busy wait until threads are finished + } +} + +std::pair HCADecodeService::decode(const std::string& filename, DWORD samplenum) +{ + clHCA hca(0xBC731A85, 0x0002B875); + void* wavptr = nullptr; + size_t sz = 0; + hca.Analyze(wavptr, sz, filename.c_str()); + if (wavptr != nullptr) + { + unsigned int blocknum = samplenum/(1024 * hca.get_channelCount()); + mutex.lock(); + filelist[std::make_pair(wavptr, blocknum)] = std::move(hca); + mutex.unlock(); + datasem.notify(); + } + return std::pair(wavptr, sz); +} + +void HCADecodeService::Main_Thread() +{ + while (true) + { + datasem.wait(); + if (shutdown) + { + break; + } + mutex.lock(); + auto it = filelist.begin(); + workingrequest = it->first.first; + workingfile = std::move(it->second); + unsigned blocknum = it->first.second; + filelist.erase(it); + numchannels = workingfile.get_channelCount(); + channels = new clHCA::stChannel[numchannels * numthreads]; + workingfile.PrepDecode(channels, numthreads); + unsigned int blockCount = workingfile.get_blockCount(); + for (unsigned int i = blocknum, j = 0; j < blockCount; ++i, ++j) + { + blocks.push_back(i % blockCount); + } + while (!blocks.empty()) + { + mutex.unlock(); + mainsem.wait(); + for (unsigned int i = 0; i < numthreads; ++i) + { + mutex.lock(); + if (workingblocks[i] == -1 && !blocks.empty()) + { + workingblocks[i] = blocks.front(); + blocks.pop_front(); + workersem[i].notify(); + } + mutex.unlock(); + } + mainsem.notify(); + mutex.lock(); + } + mutex.unlock(); + for (unsigned int i = 0; i < numthreads; ++i) + { + while (workingblocks[i] != -1); // busy wait until threads are finished + } + delete[] channels; + } + for (int i = 0; i < numthreads; ++i) + { + workersem[i].notify(); + worker_threads[i].join(); + } +} + +void HCADecodeService::Decode_Thread(int id) +{ + workersem[id].wait(); + while (workingblocks[id] != -1) + { + mainsem.wait(); + workingfile.AsyncDecode(channels + (id * numchannels), workingblocks[id], (unsigned char*)workingrequest); + workingblocks[id] = -1; + mainsem.notify(); + workersem[id].wait(); + } +} diff --git a/src/HCADecodeService.h b/src/HCADecodeService.h new file mode 100644 index 0000000..94326cc --- /dev/null +++ b/src/HCADecodeService.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include +#include +#include +#include "Semaphore.h" +#include "clHCA.h" + +class HCADecodeService +{ +public: + HCADecodeService(unsigned int num_threads); + ~HCADecodeService(); + void cancel_decode(void* ptr); + std::pair decode(const std::string& hcafilename, DWORD samplenum); + //void wait_for_finish(); +private: + void Decode_Thread(int id); + void Main_Thread(); + clHCA workingfile; + unsigned int numthreads, requestnum, numchannels; + void* workingrequest; + std::thread dispatchthread; + std::deque worker_threads; + std::map, clHCA> filelist; + std::deque blocks; + int* workingblocks; + std::deque workersem; + Semaphore mainsem, datasem; + std::mutex mutex; + clHCA::stChannel* channels; + bool shutdown; +}; + diff --git a/src/HCAStreamChannel.cpp b/src/HCAStreamChannel.cpp index ea6c164..1c148e4 100644 --- a/src/HCAStreamChannel.cpp +++ b/src/HCAStreamChannel.cpp @@ -3,8 +3,9 @@ -HCAStreamChannel::HCAStreamChannel() +HCAStreamChannel::HCAStreamChannel(HCADecodeService* dec) { + this->dec = dec; this->flags = 0; ptr = nullptr; size = 0; @@ -16,7 +17,7 @@ HCAStreamChannel::HCAStreamChannel(const HCAStreamChannel& other) { if (other.ptr == nullptr) { - HCAStreamChannel(); + HCAStreamChannel(nullptr); return; } size = other.size; @@ -25,10 +26,11 @@ HCAStreamChannel::HCAStreamChannel(const HCAStreamChannel& other) __load(); } -HCAStreamChannel::HCAStreamChannel(const std::string& filename) +HCAStreamChannel::HCAStreamChannel(HCADecodeService* dec, const std::string& filename) { + this->dec = dec; this->flags = 0; - load(filename); + load(filename, 0); } @@ -37,20 +39,49 @@ HCAStreamChannel::~HCAStreamChannel() unload(); } +HCAStreamChannel& HCAStreamChannel::operator=(HCAStreamChannel&& other) +{ + unload(); + if (this != &other) + { + ptr = other.ptr; + size = other.size; + playback_channel = other.playback_channel; + decode_channel = other.decode_channel; + flags = other.flags; + other.ptr = nullptr; + other.size = 0; + other.playback_channel = 0; + other.decode_channel = 0; + } + return *this; +} + void HCAStreamChannel::unload() { - destroy_channels(); + dec->cancel_decode(ptr); + if (playback_channel != 0) { BASS_ChannelStop(playback_channel); playback_channel = 0; } + if (decode_channel != 0) { BASS_ChannelStop(decode_channel); decode_channel = 0; } if (ptr != nullptr) { delete[] ptr; ptr = nullptr; } size = 0; } bool HCAStreamChannel::load(const std::string& filename) { - clHCA hca(0xBC731A85, 0x0002B875); - ptr = hca.DecodeToMemory(size, filename.c_str()); + auto pair = dec->decode(filename, 0); + ptr = pair.first; + size = pair.second; return __load(); } +bool HCAStreamChannel::load(const std::string& filename, DWORD samplenum) +{ + auto pair = dec->decode(filename, samplenum); + ptr = pair.first; + size = pair.second; + return __load(); +} + bool HCAStreamChannel::__load() { if (ptr == nullptr) @@ -58,18 +89,28 @@ bool HCAStreamChannel::__load() playback_channel = 0; decode_channel = 0; return false; - } - make_channels(); - if (playback_channel == 0 || decode_channel == 0) + } + playback_channel = BASS_StreamCreateFile(TRUE, ptr, 0, size, flags); + if (playback_channel == 0) { - destroy_channels(); + dec->cancel_decode(ptr); delete[] ptr; ptr = nullptr; - playback_channel = 0; decode_channel = 0; size = 0; return false; - } + } + decode_channel = BASS_StreamCreateFile(TRUE, ptr, 0, size, flags | BASS_STREAM_DECODE); + if (decode_channel == 0) + { + dec->cancel_decode(ptr); + delete[] ptr; + ptr = nullptr; + BASS_ChannelStop(playback_channel); + playback_channel = 0; + size = 0; + return false; + } return true; } diff --git a/src/HCAStreamChannel.h b/src/HCAStreamChannel.h index b6b946f..ac90f9b 100644 --- a/src/HCAStreamChannel.h +++ b/src/HCAStreamChannel.h @@ -2,33 +2,19 @@ #include #include "bass.h" +#include "HCADecodeService.h" class HCAStreamChannel { public: - HCAStreamChannel(); - HCAStreamChannel(const HCAStreamChannel& other); - HCAStreamChannel& operator=(HCAStreamChannel&& other) - { - unload(); - if (this != &other) - { - ptr = other.ptr; - size = other.size; - playback_channel = other.playback_channel; - decode_channel = other.decode_channel; - flags = other.flags; - other.ptr = nullptr; - other.size = 0; - other.playback_channel = 0; - other.decode_channel = 0; - } - return *this; - } - HCAStreamChannel(const std::string& filename); + HCAStreamChannel(HCADecodeService* dec); + HCAStreamChannel(const HCAStreamChannel& other); + HCAStreamChannel(HCADecodeService* dec, const std::string& filename); + HCAStreamChannel& operator=(HCAStreamChannel&& other); ~HCAStreamChannel(); void unload(); - bool load(const std::string& filename); + bool load(const std::string& filename); + bool load(const std::string& filename, DWORD samplenum); bool valid(); DWORD get_playback_channel(); DWORD get_decode_channel(); @@ -37,6 +23,7 @@ class HCAStreamChannel void make_channels(); private: bool __load(); + HCADecodeService* dec; void* ptr; size_t size; DWORD playback_channel, decode_channel; diff --git a/src/Semaphore.h b/src/Semaphore.h new file mode 100644 index 0000000..5c1a8ec --- /dev/null +++ b/src/Semaphore.h @@ -0,0 +1,36 @@ +#pragma once +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#include +#include + +class Semaphore { +public: + Semaphore(int count_ = 0) + : count(count_) {} + + inline void notify() + { + std::unique_lock lock(mtx); + count++; + cv.notify_one(); + } + + inline void wait() + { + std::unique_lock lock(mtx); + + while (count == 0) { + cv.wait(lock); + } + count--; + } + +private: + std::mutex mtx; + std::condition_variable cv; + int count; +}; + +#endif //SEMAPHORE_H \ No newline at end of file diff --git a/src/clHCA.cpp b/src/clHCA.cpp index b087360..90d822b 100644 --- a/src/clHCA.cpp +++ b/src/clHCA.cpp @@ -5,6 +5,7 @@ #include "clHCA.h" #include #include +#include //-------------------------------------------------- // インライン関数 @@ -22,7 +23,60 @@ inline unsigned int ceil2(unsigned int a, unsigned int b) { return (b>0) ? (a / // コンストラクタ //-------------------------------------------------- clHCA::clHCA(unsigned int ciphKey1, unsigned int ciphKey2) : - _ciph_key1(ciphKey1), _ciph_key2(ciphKey2), _ath(), _cipher() {} + _ciph_key1(ciphKey1), _ciph_key2(ciphKey2), _ath(), _cipher() { + hcafileptr = nullptr; +} + +clHCA & clHCA::operator=(clHCA && other) +{ + if(hcafileptr != nullptr) + { + delete[] hcafileptr; + } + hcafileptr = other.hcafileptr; + other.hcafileptr = nullptr; + _version = other._version; + _dataOffset = other._dataOffset; + _channelCount = other._channelCount; + _samplingRate = other._samplingRate; + _blockCount = other._blockCount; + _muteHeader = other._muteHeader; + _muteFooter = other._muteFooter; + _blockSize = other._blockSize; + _comp_r01 = other._comp_r01; + _comp_r02 = other._comp_r02; + _comp_r03 = other._comp_r03; + _comp_r04 = other._comp_r04; + _comp_r05 = other._comp_r05; + _comp_r06 = other._comp_r06; + _comp_r07 = other._comp_r07; + _comp_r08 = other._comp_r08; + _comp_r09 = other._comp_r09; + _vbr_r01 = other._vbr_r01; + _vbr_r02 = other._vbr_r02; + _ath_type = other._ath_type; + _loopStart = other._loopStart; + _loopEnd = other._loopEnd; + _loopCount = other._loopCount; + _loop_r01 = other._loop_r01; + _loopFlg = other._loopFlg; + _ciph_type = other._ciph_type; + _ciph_key1 = other._ciph_key1; + _ciph_key2 = other._ciph_key2; + _rva_volume = other._rva_volume; + _comm_len = other._comm_len; + _ath = other._ath; + _cipher = other._cipher; + return *this; +} + +clHCA::~clHCA() +{ + if (hcafileptr != nullptr) + { + delete[] hcafileptr; + } +} //-------------------------------------------------- // HCAチェック @@ -455,6 +509,171 @@ bool clHCA::Decrypt(const char *filenameHCA) { return true; } +//-------------------------------------------------- +// Analyze and store information about HCA +//------------------------------------------------- +bool clHCA::Analyze(void*& wavptr, size_t& sz, const char* filenameHCA) +{ + wavptr = nullptr; + sz = 0; + // チェック + if (!(filenameHCA))return false; + + // HCAファイルを開く + FILE *fp; + if (fopen_s(&fp, filenameHCA, "rb"))return false; + + // Analyze + + // チェック + int mode = 16; + int loop = 0; + if (!(fp && (mode == 0 || mode == 8 || mode == 16 || mode == 24 || mode == 32) && loop >= 0)) + { + fclose(fp); + return false; + } + + // + FILE *fp1 = (FILE *)fp; + unsigned int address = ftell(fp1); + + // ヘッダチェック + stHeader header; + memset(&header, 0, sizeof(header)); + fread(&header, sizeof(header), 1, fp1); + if (!CheckFile(&header, sizeof(header))) + { + fclose(fp); + return false; + } + + // ヘッダ解析 + header.dataOffset = bswap(header.dataOffset); + unsigned char *data1 = new unsigned char[header.dataOffset]; + if (!data1) { fclose(fp1); return false; } + fseek(fp1, address, SEEK_SET); + fread(data1, header.dataOffset, 1, fp1); + if (!Decode(data1, header.dataOffset, 0)) { delete[] data1; + fclose(fp); return false; } + + // WAVEヘッダを書き込み + struct stWAVEHeader { + char riff[4]; + unsigned int riffSize; + char wave[4]; + char fmt[4]; + unsigned int fmtSize; + unsigned short fmtType; + unsigned short fmtChannelCount; + unsigned int fmtSamplingRate; + unsigned int fmtSamplesPerSec; + unsigned short fmtSamplingSize; + unsigned short fmtBitCount; + }wavRiff = { 'R','I','F','F',0,'W','A','V','E','f','m','t',' ',0x10,0,0,0,0,0,0 }; + struct stWAVEsmpl { + char smpl[4]; + unsigned int smplSize; + unsigned int manufacturer; + unsigned int product; + unsigned int samplePeriod; + unsigned int MIDIUnityNote; + unsigned int MIDIPitchFraction; + unsigned int SMPTEFormat; + unsigned int SMPTEOffset; + unsigned int sampleLoops; + unsigned int samplerData; + unsigned int loop_Identifier; + unsigned int loop_Type; + unsigned int loop_Start; + unsigned int loop_End; + unsigned int loop_Fraction; + unsigned int loop_PlayCount; + }wavSmpl = { 's','m','p','l',0x3C,0,0,0,0x3C,0,0,0,1,0x18,0,0,0,0,0,0 }; + struct stWAVEnote { + char note[4]; + unsigned int noteSize; + unsigned int dwName; + }wavNote = { 'n','o','t','e',0,0 }; + struct stWAVEdata { + char data[4]; + unsigned int dataSize; + }wavData = { 'd','a','t','a',0 }; + wavRiff.fmtType = (mode>0) ? 1 : 3; + wavRiff.fmtChannelCount = _channelCount; + wavRiff.fmtBitCount = (mode>0) ? mode : 32; + wavRiff.fmtSamplingRate = _samplingRate; + wavRiff.fmtSamplingSize = wavRiff.fmtBitCount / 8 * wavRiff.fmtChannelCount; + wavRiff.fmtSamplesPerSec = wavRiff.fmtSamplingRate*wavRiff.fmtSamplingSize; + if (_loopFlg) { + wavSmpl.samplePeriod = (unsigned int)(1 / (double)wavRiff.fmtSamplingRate * 1000000000); + wavSmpl.loop_Start = _loopStart * 0x80 * 8 + _muteFooter;//※計算方法不明 + wavSmpl.loop_End = (_loopEnd + 1) * 0x80 * 8 - 1;//※計算方法不明 + wavSmpl.loop_PlayCount = (_loopCount == 0x80) ? 0 : _loopCount; + } + else if (loop) { + wavSmpl.loop_Start = 0; + wavSmpl.loop_End = (_blockCount + 1) * 0x80 * 8 - 1;//※計算方法不明 + _loopStart = 0; + _loopEnd = _blockCount; + } + if (_comm_comment) { + wavNote.noteSize = 4 + _comm_len + 1; + if (wavNote.noteSize & 3)wavNote.noteSize += 4 - (wavNote.noteSize & 3); + } + wavData.dataSize = _blockCount * 0x80 * 8 * wavRiff.fmtSamplingSize + (wavSmpl.loop_End - wavSmpl.loop_Start)*loop; + wavRiff.riffSize = 0x1C + ((_loopFlg && !loop) ? sizeof(wavSmpl) : 0) + (_comm_comment ? 8 + wavNote.noteSize : 0) + sizeof(wavData) + wavData.dataSize; + + sz = _blockCount * 8 * 128 * _channelCount * 2 + sizeof(wavRiff) + sizeof(wavData); + wavptr = new char[sz]; + memset(wavptr, 0, sz); + int seekhead = 0; + for (int i = 0; i < sizeof(wavRiff); ++i) + { + ((char*)wavptr)[seekhead++] = ((char*)&wavRiff)[i]; + } + for (int i = 0; i < sizeof(wavData); ++i) + { + ((char*)wavptr)[seekhead++] = ((char*)(&wavData))[i]; + } + delete[] data1; + hcafileptr = new unsigned char[_blockCount * _blockSize]; + fread(hcafileptr, _blockCount, _blockSize, fp1); + // 閉じる + fclose(fp); + return true; +} + +void clHCA::AsyncDecode(stChannel* channelsOffset, unsigned int blocknum, unsigned char* outputwavptr) +{ + int seekhead = 0; + outputwavptr += 2 * blocknum * 8 * 128 * _channelCount + 44; + unsigned char* data = (unsigned char*)hcafileptr + (blocknum * _blockSize); + // if(((unsigned char *)data)[_blockSize-2]==0x5E)_asm int 3 + _cipher.Mask(data, _blockSize); + clData d(data, _blockSize); + int magic = d.GetBit(16);//0xFFFF固定 + if (magic == 0xFFFF) { + int a = (d.GetBit(9) << 8) - d.GetBit(7); + for (unsigned int i = 0; i<_channelCount; i++)channelsOffset[i].Decode1(&d, _comp_r09, a, _ath.GetTable()); + for (int i = 0; i<8; i++) { + for (unsigned int j = 0; j<_channelCount; j++)channelsOffset[j].Decode2(&d); + for (unsigned int j = 0; j<_channelCount; j++)channelsOffset[j].Decode3(_comp_r09, _comp_r08, _comp_r07 + _comp_r06, _comp_r05); + for (unsigned int j = 0; j<_channelCount - 1; j++)channelsOffset[j].Decode4(i, _comp_r05 - _comp_r06, _comp_r06, _comp_r07); + for (unsigned int j = 0; j<_channelCount; j++)channelsOffset[j].Decode5(i); + } + } + for (int i = 0; i<8; i++) { + for (int j = 0; j<0x80; j++) { + for (unsigned int k = 0; k<_channelCount; k++) { + float f = channelsOffset[k].wave[i][j]; + if (f>1) { f = 1; } + else if (f<-1) { f = -1; } + DecodeToMemory_DecodeMode16bit(f, outputwavptr, seekhead); + } + } + } +} //-------------------------------------------------- // デコードしてWAVEファイルに保存 @@ -476,18 +695,18 @@ bool clHCA::DecodeToWavefile(const char *filenameHCA, const char *filenameWAV, f return true; } -void* clHCA::DecodeToMemory(size_t& sz, const char *filenameHCA, int mode, int loop) { +void* clHCA::DecodeToMemory(size_t& sz, const char *filenameHCA, float volume, int mode, int loop) { - void* ptr = NULL; + void* ptr = nullptr; // チェック - if (!(filenameHCA))return NULL; + if (!(filenameHCA))return nullptr; // HCAファイルを開く FILE *fp; - if (fopen_s(&fp, filenameHCA, "rb"))return NULL; + if (fopen_s(&fp, filenameHCA, "rb"))return nullptr; // 保存 - ptr = DecodeToMemoryStream(sz, fp, mode, loop); + ptr = DecodeToMemoryStream(sz, fp, volume, mode, loop); // 閉じる fclose(fp); @@ -495,7 +714,7 @@ void* clHCA::DecodeToMemory(size_t& sz, const char *filenameHCA, int mode, int l return ptr; } -void* clHCA::DecodeToMemoryStream(size_t& sz, void *fpHCA, int mode, int loop) { +void* clHCA::DecodeToMemoryStream(size_t& sz, void *fpHCA, float volume, int mode, int loop) { // チェック if (!(fpHCA && (mode == 0 || mode == 8 || mode == 16 || mode == 24 || mode == 32) && loop >= 0))return NULL; @@ -795,6 +1014,18 @@ bool clHCA::DecodeToWavefileStream(void *fpHCA, const char *filenameWAV, float v return true; } +unsigned int clHCA::get_channelCount() const +{ + return _channelCount; +} +unsigned int clHCA::get_blockCount() const +{ + return _blockCount; +} +unsigned int clHCA::get_blockSize() const +{ + return _blockSize; +} bool clHCA::DecodeToWavefile_Decode(void *fp1, void *fp2, unsigned int address, unsigned int count, void *data, void *modeFunction) { float f; fseek((FILE *)fp1, address, SEEK_SET); @@ -1259,6 +1490,36 @@ bool clHCA::Decode(void *data, unsigned int size, unsigned int address) { return true; } +bool clHCA::PrepDecode(stChannel* channels, unsigned int numthreads) +{ + memset(channels, 0, sizeof(stChannel) * numthreads * _channelCount); + if (!(_comp_r01 == 1 && _comp_r02 == 15))return false; + _comp_r09 = ceil2(_comp_r05 - (_comp_r06 + _comp_r07), _comp_r08); + char r[0x10]; memset(r, 0, sizeof(r)); + unsigned int b = _channelCount / _comp_r03; + if (_comp_r07&&b>1) { + char *c = r; + for (unsigned int i = 0; i<_comp_r03; i++, c += b) { + switch (b) { + case 2:c[0] = 1; c[1] = 2; break; + case 3:c[0] = 1; c[1] = 2; break; + case 4:c[0] = 1; c[1] = 2; if (_comp_r04 == 0) { c[2] = 1; c[3] = 2; }break; + case 5:c[0] = 1; c[1] = 2; if (_comp_r04 <= 2) { c[3] = 1; c[4] = 2; }break; + case 6:c[0] = 1; c[1] = 2; c[4] = 1; c[5] = 2; break; + case 7:c[0] = 1; c[1] = 2; c[4] = 1; c[5] = 2; break; + case 8:c[0] = 1; c[1] = 2; c[4] = 1; c[5] = 2; c[6] = 1; c[7] = 2; break; + } + } + } + for (unsigned int i = 0; i < _channelCount * numthreads; i++) + { + channels[i].type = r[i % _channelCount]; + channels[i].value3 = &channels[i].value[_comp_r06 + _comp_r07]; + channels[i].count = _comp_r06 + ((r[i % _channelCount] != 2) ? _comp_r07 : 0); + } + return true; +} + //-------------------------------------------------- // デコード第一段階 // ベースデータの読み込み @@ -1643,8 +1904,8 @@ void clHCA::stChannel::Decode5(int index) { } float *w = s; s = d; d = w; } - d = wav2; - for (int i = 0; i<0x80; i++)*(d++) = *(s++); + memcpy(wav2, s, 0x80 * sizeof(float)); + //for (int i = 0; i<0x80; i++)*(d++) = *(s++); s = (float *)list3Int; d = wave[index]; s1 = &wav2[0x40]; s2 = wav3; for (int i = 0; i<0x40; i++)*(d++) = *(s1++)**(s++) + *(s2++); diff --git a/src/clHCA.h b/src/clHCA.h index 708d424..cee063d 100644 --- a/src/clHCA.h +++ b/src/clHCA.h @@ -1,105 +1,108 @@ #pragma once //-------------------------------------------------- -// HCA(High Compression Audio)繧ッ繝ゥ繧ケ +// HCA(High Compression Audio)クラス //-------------------------------------------------- class clHCA { public: clHCA(unsigned int ciphKey1 = 0xBC731A85, unsigned int ciphKey2 = 0x0002B875); + clHCA& operator=(clHCA&& other); + ~clHCA(); - // HCA繝√ぉ繝繧ッ + // HCAチェック static bool CheckFile(void *data, unsigned int size); - // 繝√ぉ繝繧ッ繧オ繝 + // チェックサム static unsigned short CheckSum(void *data, int size, unsigned short sum = 0); - // 繝倥ャ繝諠蝣ア繧偵さ繝ウ繧ス繝シ繝ォ蜃コ蜉 + // ヘッダ情報をコンソール出力 bool PrintInfo(const char *filenameHCA); - // 蠕ゥ蜿キ蛹 + // 復号化 bool Decrypt(const char *filenameHCA); - // Decode and place in memory, returning a pointer to the WAV data. Remember to free it when done. 16 bit only at the moment, but can easily be modified to support more bit depths. - void* DecodeToMemory(size_t& sz, const char *filenameHCA, int mode = 16, int loop = 0); - void* DecodeToMemoryStream(size_t& sz, void *fpHCA, int mode = 16, int loop = 0); - - // 繝繧ウ繝シ繝峨@縺ヲWAVE繝輔ぃ繧、繝ォ縺ォ菫晏ュ - bool DecodeToWavefile(const char *filenameHCA, const char *filenameWAV, float volume = 1, int mode = 16, int loop = 0); + // デコードしてWAVEファイルに保存 + void* DecodeToMemory(size_t& sz, const char *filenameHCA, float volume = 1, int mode = 16, int loop = 0); + bool DecodeToWavefile(const char *filenameHCA, const char *filenameWAV, float volume = 1, int mode = 16, int loop = 0); + void* DecodeToMemoryStream(size_t& sz, void *fpHCA, float volume = 1, int mode = 16, int loop = 0); bool DecodeToWavefileStream(void *fpHCA, const char *filenameWAV, float volume = 1, int mode = 16, int loop = 0); - // 繧ィ繝ウ繧ウ繝シ繝峨@縺ヲHCA繝輔ぃ繧、繝ォ縺ォ菫晏ュ + // エンコードしてHCAファイルに保存 //bool EncodeFromWavefile(const char *filenameWAV,const char *filenameHCA,float volume=1); //bool EncodeFromWavefileStream(void *fpWAV,const char *filenameHCA,float volume=1); + unsigned int get_channelCount() const; + unsigned int get_blockCount() const; + unsigned int get_blockSize() const; private: - struct stHeader {//繝輔ぃ繧、繝ォ諠蝣ア (蠢鬆) + struct stHeader {//ファイル情報 (必須) unsigned int hca; // 'HCA' - unsigned short version; // 繝舌シ繧ク繝ァ繝ウ縲W1.3縺ィv2.0縺ョ蟄伜惠繧堤「コ隱 - unsigned short dataOffset; // 繝繝シ繧ソ繧ェ繝輔そ繝繝 + unsigned short version; // バージョン。v1.3とv2.0の存在を確認 + unsigned short dataOffset; // データオフセット }; - struct stFormat {//繝輔か繝シ繝槭ャ繝域ュ蝣ア (蠢鬆) + struct stFormat {//フォーマット情報 (必須) unsigned int fmt; // 'fmt' - unsigned int channelCount : 8; // 繝√Ε繝ウ繝阪Ν謨ー 1ス16 - unsigned int samplingRate : 24; // 繧オ繝ウ繝励Μ繝ウ繧ー繝ャ繝シ繝 1ス0x7FFFFF - unsigned int blockCount; // 繝悶Ο繝繧ッ謨ー 0莉・荳 - unsigned short muteHeader; // 蜈磯ュ縺ョ辟。髻ウ驛ィ蛻(繝悶Ο繝繧ッ謨ー*0x400+0x80) - unsigned short muteFooter; // 譛ォ蟆セ縺ョ辟。髻ウ繧オ繝ウ繝励Ν謨ー + unsigned int channelCount : 8; // チャンネル数 1〜16 + unsigned int samplingRate : 24; // サンプリングレート 1〜0x7FFFFF + unsigned int blockCount; // ブロック数 0以上 + unsigned short muteHeader; // 先頭の無音部分(ブロック数*0x400+0x80) + unsigned short muteFooter; // 末尾の無音サンプル数 }; - struct stCompress {//蝨ァ邵ョ諠蝣ア (蝨ァ邵ョ諠蝣ア縺九ョ繧ウ繝シ繝画ュ蝣ア縺ョ縺ゥ縺。繧峨°荳縺、縺悟ソ鬆) + struct stCompress {//圧縮情報 (圧縮情報かデコード情報のどちらか一つが必須) unsigned int comp; // 'comp' - unsigned short blockSize; // 繝悶Ο繝繧ッ繧オ繧、繧コ(CBR縺ョ縺ィ縺阪↓譛牙柑シ) 8ス0xFFFF縲0縺ョ縺ィ縺阪ッVBR - unsigned char r01; // 荳肴(1) 0ス柮02 v2.0迴セ蝨ィ1縺ョ縺ソ蟇セ蠢 - unsigned char r02; // 荳肴(15) r01ス0x1F v2.0迴セ蝨ィ15縺ョ縺ソ蟇セ蠢 - unsigned char r03; // 荳肴(1)(1) - unsigned char r04; // 荳肴(1)(0) - unsigned char r05; // 荳肴(0x80)(0x80) - unsigned char r06; // 荳肴(0x80)(0x20) - unsigned char r07; // 荳肴(0)(0x20) - unsigned char r08; // 荳肴(0)(8) - unsigned char reserve1; // 莠育エ - unsigned char reserve2; // 莠育エ + unsigned short blockSize; // ブロックサイズ(CBRのときに有効?) 8〜0xFFFF、0のときはVBR + unsigned char r01; // 不明(1) 0〜r02 v2.0現在1のみ対応 + unsigned char r02; // 不明(15) r01〜0x1F v2.0現在15のみ対応 + unsigned char r03; // 不明(1)(1) + unsigned char r04; // 不明(1)(0) + unsigned char r05; // 不明(0x80)(0x80) + unsigned char r06; // 不明(0x80)(0x20) + unsigned char r07; // 不明(0)(0x20) + unsigned char r08; // 不明(0)(8) + unsigned char reserve1; // 予約 + unsigned char reserve2; // 予約 }; - struct stDecode {//繝繧ウ繝シ繝画ュ蝣ア (蝨ァ邵ョ諠蝣ア縺九ョ繧ウ繝シ繝画ュ蝣ア縺ョ縺ゥ縺。繧峨°荳縺、縺悟ソ鬆) + struct stDecode {//デコード情報 (圧縮情報かデコード情報のどちらか一つが必須) unsigned int dec; // 'dec' - unsigned short blockSize; // 繝悶Ο繝繧ッ繧オ繧、繧コ(CBR縺ョ縺ィ縺阪↓譛牙柑シ) 8ス0xFFFF縲0縺ョ縺ィ縺阪ッVBR - unsigned char r01; // 荳肴(1) 0ス柮02 v2.0迴セ蝨ィ1縺ョ縺ソ蟇セ蠢 - unsigned char r02; // 荳肴(15) r01ス0x1F v2.0迴セ蝨ィ15縺ョ縺ソ蟇セ蠢 - unsigned char count1; // type0縺ィtype1縺ョ謨ー-1 - unsigned char count2; // type2縺ョ謨ー-1 - unsigned char r03 : 4; // 荳肴(0) - unsigned char r04 : 4; // 荳肴(0) 0縺ッ1縺ォ菫ョ豁」縺輔l繧 - unsigned char enableCount2; // count2繧剃スソ縺繝輔Λ繧ー + unsigned short blockSize; // ブロックサイズ(CBRのときに有効?) 8〜0xFFFF、0のときはVBR + unsigned char r01; // 不明(1) 0〜r02 v2.0現在1のみ対応 + unsigned char r02; // 不明(15) r01〜0x1F v2.0現在15のみ対応 + unsigned char count1; // type0とtype1の数-1 + unsigned char count2; // type2の数-1 + unsigned char r03 : 4; // 不明(0) + unsigned char r04 : 4; // 不明(0) 0は1に修正される + unsigned char enableCount2; // count2を使うフラグ }; - struct stVBR {//蜿ッ螟峨ン繝繝医Ξ繝シ繝域ュ蝣ア (蟒豁「シ) + struct stVBR {//可変ビットレート情報 (廃止?) unsigned int vbr; // 'vbr' - unsigned short r01; // 荳肴 0ス0x1FF - unsigned short r02; // 荳肴 + unsigned short r01; // 不明 0〜0x1FF + unsigned short r02; // 不明 }; - struct stATH {//ATH繝繝シ繝悶Ν諠蝣ア (v2.0縺九i蟒豁「シ) + struct stATH {//ATHテーブル情報 (v2.0から廃止?) unsigned int ath; // 'ath' - unsigned short type; // 繝繝シ繝悶Ν縺ョ遞ョ鬘(0:蜈ィ縺ヲ0 1:繝繝シ繝悶Ν1) + unsigned short type; // テーブルの種類(0:全て0 1:テーブル1) }; - struct stLoop {//繝ォ繝シ繝玲ュ蝣ア + struct stLoop {//ループ情報 unsigned int loop; // 'loop' - unsigned int start; // 繝ォ繝シ繝鈴幕蟋九ヶ繝ュ繝繧ッ繧、繝ウ繝繝繧ッ繧ケ 0ス柩oopEnd - unsigned int end; // 繝ォ繝シ繝礼オゆコ繝悶Ο繝繧ッ繧、繝ウ繝繝繧ッ繧ケ loopStartス(stFormat::blockCount-1) - unsigned short count; // 繝ォ繝シ繝怜屓謨ー 0x80縺ァ辟。髯舌Ν繝シ繝 - unsigned short r01; // 荳肴(0x226) + unsigned int start; // ループ開始ブロックインデックス 0〜loopEnd + unsigned int end; // ループ終了ブロックインデックス loopStart〜(stFormat::blockCount-1) + unsigned short count; // ループ回数 0x80で無限ループ + unsigned short r01; // 不明(0x226) }; - struct stCipher {//證怜捷繝繝シ繝悶Ν諠蝣ア + struct stCipher {//暗号テーブル情報 unsigned int ciph; // 'ciph' - unsigned short type; // 證怜捷蛹悶ョ遞ョ鬘(0:證怜捷蛹悶↑縺 1:骰オ縺ェ縺玲囓蜿キ蛹 0x38:骰オ縺ゅj證怜捷蛹) + unsigned short type; // 暗号化の種類(0:暗号化なし 1:鍵なし暗号化 0x38:鍵あり暗号化) }; - struct stRVA {//逶ク蟇セ繝懊Μ繝・繝シ繝隱ソ遽諠蝣ア + struct stRVA {//相対ボリューム調節情報 unsigned int rva; // 'rva' - float volume; // 繝懊Μ繝・繝シ繝 + float volume; // ボリューム }; - struct stComment {//繧ウ繝。繝ウ繝域ュ蝣ア + struct stComment {//コメント情報 unsigned int comm; // 'comm' - unsigned char len; // 繧ウ繝。繝ウ繝医ョ髟キ縺包シ + unsigned char len; // コメントの長さ? //char comment[]; }; - struct stPadding {//繝代ョ繧」繝ウ繧ー + struct stPadding {//パディング unsigned int pad; // 'pad' }; unsigned int _version; @@ -166,6 +169,7 @@ class clHCA { int _size; int _bit; }; + public: struct stChannel { float block[0x80]; float base[0x80]; @@ -184,7 +188,13 @@ class clHCA { void Decode3(unsigned int a, unsigned int b, unsigned int c, unsigned int d); void Decode4(int index, unsigned int a, unsigned int b, unsigned int c); void Decode5(int index); - }_channel[0x10]; + }; + bool PrepDecode(stChannel* channels, unsigned int numthreads); + bool Analyze(void*& wavptr, size_t& sz, const char* filenameHCA); + void AsyncDecode(stChannel* channelsOffset, unsigned int blocknum, unsigned char* outputwavptr); + private: + stChannel _channel[0x10]; + unsigned char* hcafileptr; bool Decode(void *data, unsigned int size, unsigned int address); bool DecodeToMemory_Decode(void *fp1, unsigned int address, unsigned int count, void *data, void *modeFunction, void* ptr, int& seekhead); bool DecodeToWavefile_Decode(void *fp1, void *fp2, unsigned int address, unsigned int count, void *data, void *modeFunction); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5246361..42c7234 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -11,7 +11,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), - ui(new Ui::MainWindow) + ui(new Ui::MainWindow), + dec(1) { ui->setupUi(this); @@ -39,11 +40,11 @@ MainWindow::MainWindow(QWidget *parent) : idolVol = 0.6; solo = false; - bgm = HCAStreamChannel(); + bgm = new HCAStreamChannel(&dec); currSong = ""; for(int i = 0; i < NUM_IDOLS; ++i) { - idols[i] = HCAStreamChannel(); + idols[i] = new HCAStreamChannel(&dec); currIdols[i] = ""; idolimg[i]->setScaledContents(true); } @@ -72,6 +73,11 @@ MainWindow::MainWindow(QWidget *parent) : MainWindow::~MainWindow() { + delete bgm; + for(int i = 0; i < NUM_IDOLS; ++i) + { + delete idols[i]; + } delete ui; } @@ -85,16 +91,16 @@ void MainWindow::timerEvent(QTimerEvent *event) void MainWindow::updateUIPosition() { - DWORD len = BASS_ChannelGetLength(bgm.get_decode_channel(), BASS_POS_BYTE); - DWORD pos = BASS_ChannelGetPosition(bgm.get_decode_channel(), BASS_POS_BYTE); + DWORD len = BASS_ChannelGetLength(bgm->get_decode_channel(), BASS_POS_BYTE); + DWORD pos = BASS_ChannelGetPosition(bgm->get_decode_channel(), BASS_POS_BYTE); if(!ui->positionSlider->isSliderDown()) { ui->positionSlider->blockSignals(true); ui->positionSlider->setValue((double)pos/len*500); ui->positionSlider->blockSignals(false); } - DWORD p = BASS_ChannelBytes2Seconds(bgm.get_decode_channel(),pos); - DWORD l = BASS_ChannelBytes2Seconds(bgm.get_decode_channel(),len); + DWORD p = BASS_ChannelBytes2Seconds(bgm->get_decode_channel(),pos); + DWORD l = BASS_ChannelBytes2Seconds(bgm->get_decode_channel(),len); QString result; QTextStream(&result) << "Position: " << p/60 << QString(":%1").arg(p%60, 2, 10, QChar('0')) << "/" << l/60 << QString(":%1").arg(l%60, 2, 10, QChar('0')); ui->statusBar->showMessage(result); @@ -105,7 +111,7 @@ void MainWindow::updateIdolActivity() DWORD pos; for(int i = 0; i < NUM_IDOLS; ++i) { - if(idols[i].get_decode_channel() == 0) + if(idols[i]->get_decode_channel() == 0) { idolactivity[i]->setDisabled(true); idolactivity[i]->setChecked(false); @@ -114,7 +120,7 @@ void MainWindow::updateIdolActivity() { idolactivity[i]->setEnabled(true); VolumePan vp; - pos = BASS_ChannelGetPosition(idols[i].get_decode_channel() , BASS_POS_BYTE) / 2; + pos = BASS_ChannelGetPosition(idols[i]->get_decode_channel() , BASS_POS_BYTE) / 2; DWORD last_pos = 0; for (std::pair pos_vp : idolInfo[i].second) { @@ -137,7 +143,7 @@ void MainWindow::updateControls() void MainWindow::setBGMVol(int value) { bgmVol = value/100.; - BASS_ChannelSetAttribute(bgm.get_decode_channel(),BASS_ATTRIB_VOL,bgmVol); + BASS_ChannelSetAttribute(bgm->get_decode_channel(),BASS_ATTRIB_VOL,bgmVol); } void MainWindow::setIdolVol(int value) @@ -145,7 +151,7 @@ void MainWindow::setIdolVol(int value) idolVol = value/100.; for(int i = 0; i < NUM_IDOLS; ++i) { - fuzzy_adjust_vol_pan(idols[i].get_decode_channel(), idolInfo[i]); + fuzzy_adjust_vol_pan(idols[i]->get_decode_channel(), idolInfo[i]); } } @@ -184,12 +190,12 @@ void MainWindow::setBGM(const QString& qStr) std::string name = qStr.toUtf8().constData(); currSong = readablesong_to_filename[name]; BASS_ChannelPause(mix_stream); - BASS_Mixer_ChannelRemove(bgm.get_decode_channel()); - bgm.unload(); - bgm.load("res/" + currSong + "/bgm.hca"); - BASS_Mixer_StreamAddChannel(mix_stream, bgm.get_decode_channel(), 0); - BASS_Mixer_ChannelSetPosition(bgm.get_decode_channel(), 0, BASS_POS_BYTE | BASS_POS_MIXER_RESET); - BASS_ChannelSetAttribute(bgm.get_decode_channel(),BASS_ATTRIB_VOL,bgmVol); + BASS_Mixer_ChannelRemove(bgm->get_decode_channel()); + bgm->unload(); + bgm->load("res/" + currSong + "/bgm.hca"); + BASS_Mixer_StreamAddChannel(mix_stream, bgm->get_decode_channel(), 0); + BASS_Mixer_ChannelSetPosition(bgm->get_decode_channel(), 0, BASS_POS_BYTE | BASS_POS_MIXER_RESET); + BASS_ChannelSetAttribute(bgm->get_decode_channel(),BASS_ATTRIB_VOL,bgmVol); parse_control_file(idolInfo, "res/" + currSong + "/control" + (solo?"solo":"") + ".txt", idolVol); for(int i = 0; i < NUM_IDOLS; ++i) { @@ -199,26 +205,26 @@ void MainWindow::setBGM(const QString& qStr) void MainWindow::setPosition(int value) { - DWORD len = BASS_ChannelGetLength(bgm.get_decode_channel(), BASS_POS_BYTE); + DWORD len = BASS_ChannelGetLength(bgm->get_decode_channel(), BASS_POS_BYTE); for(int i = 0; i < NUM_IDOLS; ++i) { - BASS_ChannelSetPosition(idols[i].get_decode_channel(), (long double)len / 2 / ui->positionSlider->maximum() * value, BASS_POS_BYTE); - fuzzy_adjust_vol_pan(idols[i].get_decode_channel(), idolInfo[i]); + BASS_ChannelSetPosition(idols[i]->get_decode_channel(), (long double)len / 2 / ui->positionSlider->maximum() * value, BASS_POS_BYTE); + fuzzy_adjust_vol_pan(idols[i]->get_decode_channel(), idolInfo[i]); } - BASS_ChannelSetPosition(bgm.get_decode_channel(), (long double)len / ui->positionSlider->maximum() * value, BASS_POS_BYTE); + BASS_ChannelSetPosition(bgm->get_decode_channel(), (long double)len / ui->positionSlider->maximum() * value, BASS_POS_BYTE); } void MainWindow::play() { // Check if we're at the end - QWORD pos = BASS_ChannelGetLength(bgm.get_decode_channel(),BASS_POS_BYTE); - if(pos == BASS_ChannelGetPosition(bgm.get_decode_channel(),BASS_POS_BYTE)) + QWORD pos = BASS_ChannelGetLength(bgm->get_decode_channel(),BASS_POS_BYTE); + if(pos == BASS_ChannelGetPosition(bgm->get_decode_channel(),BASS_POS_BYTE)) { BASS_ChannelPause(mix_stream); - BASS_ChannelSetPosition(bgm.get_decode_channel(), 0, BASS_POS_BYTE); + BASS_ChannelSetPosition(bgm->get_decode_channel(), 0, BASS_POS_BYTE); for(int i = 0; i < NUM_IDOLS; ++i) { - BASS_ChannelSetPosition(idols[i].get_decode_channel(), 0, BASS_POS_BYTE); + BASS_ChannelSetPosition(idols[i]->get_decode_channel(), 0, BASS_POS_BYTE); } } // Clear buffer if player was paused @@ -237,17 +243,17 @@ void MainWindow::pause() void MainWindow::reset() { // Check if we're at the end so we don't automatically play after reset - QWORD pos = BASS_ChannelGetLength(bgm.get_decode_channel(),BASS_POS_BYTE); - if(pos == BASS_ChannelGetPosition(bgm.get_decode_channel(),BASS_POS_BYTE)) + QWORD pos = BASS_ChannelGetLength(bgm->get_decode_channel(),BASS_POS_BYTE); + if(pos == BASS_ChannelGetPosition(bgm->get_decode_channel(),BASS_POS_BYTE)) { BASS_ChannelPause(mix_stream); } // Set positions and flush buffer for(int i = 0; i < NUM_IDOLS; ++i) { - BASS_Mixer_ChannelSetPosition(idols[i].get_decode_channel(), 0, BASS_POS_BYTE); + BASS_Mixer_ChannelSetPosition(idols[i]->get_decode_channel(), 0, BASS_POS_BYTE); } - BASS_Mixer_ChannelSetPosition(bgm.get_decode_channel(), 0, BASS_POS_BYTE | BASS_POS_MIXER_RESET); + BASS_Mixer_ChannelSetPosition(bgm->get_decode_channel(), 0, BASS_POS_BYTE | BASS_POS_MIXER_RESET); } void MainWindow::save() @@ -261,11 +267,11 @@ void MainWindow::save() DWORD idoldecodechannels[NUM_IDOLS]; for(int i = 0; i < NUM_IDOLS; ++i) { - idoldecodechannels[i] = idols[i].get_decode_channel(); + idoldecodechannels[i] = idols[i]->get_decode_channel(); } // Stream needs to be paused else the output will be garbled BASS_ChannelPause(mix_stream); - export_to_wav(bgm.get_decode_channel(), idoldecodechannels, bgmVol, idolVol, idolInfo, filename); + export_to_wav(bgm->get_decode_channel(), idoldecodechannels, bgmVol, idolVol, idolInfo, filename); reset(); } @@ -274,9 +280,10 @@ void MainWindow::setIdol(int index) QString filename = QString::fromStdString("res/img/" + currIdols[index] + ".png"); idolpixmap[index] = QPixmap(filename); idolimg[index]->setPixmap(idolpixmap[index]); - DWORD oldchan = idols[index].get_decode_channel(); - HCAStreamChannel&& hcastream = HCAStreamChannel(); - hcastream.load("res/" + currSong + "/" + currIdols[index] + ".hca"); + DWORD oldchan = idols[index]->get_decode_channel(); + HCAStreamChannel&& hcastream = HCAStreamChannel(&dec); + DWORD pos = BASS_ChannelGetPosition(bgm->get_decode_channel(), BASS_POS_BYTE); + hcastream.load("res/" + currSong + "/" + currIdols[index] + ".hca", pos/4); if(solo && index != 2) { hcastream.destroy_channels(); @@ -284,7 +291,7 @@ void MainWindow::setIdol(int index) else { set_auto_vol_pan(idolInfo[index], hcastream.get_decode_channel()); - DWORD position = BASS_ChannelGetPosition(bgm.get_decode_channel(), BASS_POS_BYTE); + DWORD position = BASS_ChannelGetPosition(bgm->get_decode_channel(), BASS_POS_BYTE); BASS_ChannelSetPosition(hcastream.get_decode_channel(), position / 2, BASS_POS_BYTE); fuzzy_adjust_vol_pan(hcastream.get_decode_channel(), idolInfo[index]); BASS_Mixer_StreamAddChannel(idol_mix_stream, hcastream.get_decode_channel(), 0); @@ -294,7 +301,7 @@ void MainWindow::setIdol(int index) { BASS_Mixer_ChannelRemove(oldchan); } - idols[index] = std::move(hcastream); + *idols[index] = std::move(hcastream); } void MainWindow::setUnit(bool checked) @@ -305,9 +312,9 @@ void MainWindow::setUnit(bool checked) for(int i = 0; i < NUM_IDOLS; ++i) { // Don't unload, rather just cleanup channels - BASS_Mixer_ChannelRemove(idols[i].get_decode_channel()); - idols[i].destroy_channels(); - idols[i].make_channels(); + BASS_Mixer_ChannelRemove(idols[i]->get_decode_channel()); + idols[i]->destroy_channels(); + idols[i]->make_channels(); idolInfo[i].second.clear(); } // Set control and volume @@ -315,10 +322,10 @@ void MainWindow::setUnit(bool checked) DWORD idoldecodechannels[NUM_IDOLS]; for(int i = 0; i < NUM_IDOLS; ++i) { - idoldecodechannels[i] = idols[i].get_decode_channel(); - DWORD position = BASS_ChannelGetPosition(bgm.get_decode_channel(), BASS_POS_BYTE); - BASS_ChannelSetPosition(idols[i].get_decode_channel(), position / 2, BASS_POS_BYTE); - BASS_Mixer_StreamAddChannel(idol_mix_stream, idols[i].get_decode_channel(), 0); + idoldecodechannels[i] = idols[i]->get_decode_channel(); + DWORD position = BASS_ChannelGetPosition(bgm->get_decode_channel(), BASS_POS_BYTE); + BASS_ChannelSetPosition(idols[i]->get_decode_channel(), position / 2, BASS_POS_BYTE); + BASS_Mixer_StreamAddChannel(idol_mix_stream, idols[i]->get_decode_channel(), 0); fuzzy_adjust_vol_pan(idoldecodechannels[i], idolInfo[i]); } set_auto_vol_pan_all(idolInfo, idoldecodechannels); @@ -333,20 +340,20 @@ void MainWindow::setSolo(bool checked) for(int i = 0; i < NUM_IDOLS; ++i) { // Don't unload, rather just cleanup channels - BASS_Mixer_ChannelRemove(idols[i].get_decode_channel()); - idols[i].destroy_channels(); + BASS_Mixer_ChannelRemove(idols[i]->get_decode_channel()); + idols[i]->destroy_channels(); if(i == 2) { - idols[i].make_channels(); - DWORD position = BASS_ChannelGetPosition(bgm.get_decode_channel(), BASS_POS_BYTE); - BASS_ChannelSetPosition(idols[i].get_decode_channel(), position / 2, BASS_POS_BYTE); + idols[i]->make_channels(); + DWORD position = BASS_ChannelGetPosition(bgm->get_decode_channel(), BASS_POS_BYTE); + BASS_ChannelSetPosition(idols[i]->get_decode_channel(), position / 2, BASS_POS_BYTE); } - BASS_Mixer_StreamAddChannel(idol_mix_stream, idols[i].get_decode_channel(), 0); + BASS_Mixer_StreamAddChannel(idol_mix_stream, idols[i]->get_decode_channel(), 0); idolInfo[i].second.clear(); } // Set control and volume parse_control_file(idolInfo, "res/" + currSong + "/controlsolo.txt", idolVol); - fuzzy_adjust_vol_pan(idols[2].get_decode_channel(), idolInfo[2]); - set_auto_vol_pan(idolInfo[2], idols[2].get_decode_channel()); + fuzzy_adjust_vol_pan(idols[2]->get_decode_channel(), idolInfo[2]); + set_auto_vol_pan(idolInfo[2], idols[2]->get_decode_channel()); } } diff --git a/src/mainwindow.h b/src/mainwindow.h index dec2c1c..977c156 100644 --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -5,9 +5,10 @@ #include #include #include +#include #include "HCAStreamChannel.h" #include "utils.h" -#include "unordered_map" +#include "HCADecodeService.h" namespace Ui { class MainWindow; @@ -49,8 +50,8 @@ public slots: double idolVol; int updateTimerId; bool solo; - HCAStreamChannel bgm; - HCAStreamChannel idols[NUM_IDOLS]; + HCAStreamChannel* bgm; + HCAStreamChannel* idols[NUM_IDOLS]; ControlInfo idolInfo[NUM_IDOLS]; HSTREAM mix_stream, idol_mix_stream; std::string currSong, currIdols[NUM_IDOLS]; @@ -59,6 +60,7 @@ public slots: QCheckBox* idolactivity[NUM_IDOLS]; QLabel* idolimg[NUM_IDOLS]; QPixmap idolpixmap[NUM_IDOLS]; + HCADecodeService dec; }; #endif // MAINWINDOW_H