From 470c5d86d5ec428c8c372e9050c707d7cba369e6 Mon Sep 17 00:00:00 2001 From: Clownacy Date: Wed, 28 Aug 2019 07:55:42 +0100 Subject: [PATCH] ASM-accurate Sound.cpp and Organya.cpp (except for one function) --- msvc2003/CSE2.vcproj | 8 +- msvc2003/devilution/comparer-config.toml | 115 ++- src/Main.cpp | 8 +- src/Organya.cpp | 1118 +++++++++++++++------- src/Organya.h | 47 +- src/Sound.cpp | 578 ++++++----- src/Sound.h | 47 +- 7 files changed, 1207 insertions(+), 714 deletions(-) diff --git a/msvc2003/CSE2.vcproj b/msvc2003/CSE2.vcproj index 8d75e74d..395a721a 100644 --- a/msvc2003/CSE2.vcproj +++ b/msvc2003/CSE2.vcproj @@ -34,7 +34,7 @@ Name="VCCustomBuildTool"/> #include "SDL.h" +#include "SDL_syswm.h" #include "WindowsWrapper.h" @@ -347,11 +348,16 @@ int main(int argc, char *argv[]) CortBox(&clip_rect, 0x000000); PutBitmap3(&clip_rect, (WINDOW_WIDTH - 64) / 2, (WINDOW_HEIGHT - 8) / 2, &loading_rect, SURFACE_ID_LOADING); + SDL_SysWMinfo wmInfo; + SDL_VERSION(&wmInfo.version); + SDL_GetWindowWMInfo(gWindow, &wmInfo); + ghWnd = wmInfo.info.win.window; + // Draw to screen if (Flip_SystemTask(ghWnd)) { // Initialize sound - InitDirectSound(); + InitDirectSound(ghWnd); // Initialize joystick if (config.bJoystick && InitDirectInput()) diff --git a/src/Organya.cpp b/src/Organya.cpp index 57aab2b4..8c721ec3 100644 --- a/src/Organya.cpp +++ b/src/Organya.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "SDL.h" #include "WindowsWrapper.h" @@ -24,73 +26,111 @@ #define MAXMELODY 8 #define MAXDRAM 8 -SOUNDBUFFER *lpORGANBUFFER[8][8][2] = {NULL}; -SOUNDBUFFER **lpDRAMBUFFER = &lpSECONDARYBUFFER[0x96]; +#define ALLOCNOTE 4096 -MUSICINFO info; +#define DEFVOLUME 200//255はVOLDUMMY。MAXは254 +#define DEFPAN 6 + +//曲情報をセットする時のフラグ +#define SETALL 0xffffffff//全てをセット +#define SETWAIT 0x00000001 +#define SETGRID 0x00000002 +#define SETALLOC 0x00000004 +#define SETREPEAT 0x00000008 +#define SETFREQ 0x00000010 +#define SETWAVE 0x00000020 +#define SETPIPI 0x00000040 + +typedef struct ORGANYATRACK +{ + unsigned short freq; // +α周波数(1000がDefault) (+ α frequency (1000 is Default)) + unsigned char wave_no; // 波形No (Waveform No) + unsigned char pipi; // ☆ + unsigned short note_num; // 音符の数 (Number of notes) +} ORGANYATRACK; + +typedef struct ORGANYADATA +{ + unsigned short wait; + unsigned char line; + unsigned char dot; + long repeat_x; // リピート (repeat) + long end_x; // 曲の終わり(リピートに戻る) (End of song (return to repeat)) + ORGANYATRACK tdata[MAXTRACK]; +} ORGANYADATA; + +// Below are Organya song data structures +typedef struct NOTELIST +{ + NOTELIST *from; // Previous address + NOTELIST *to; // Next address + + long x; // Position + unsigned char length; // Sound length + unsigned char y; // Sound height + unsigned char volume; // Volume + unsigned char pan; +} NOTELIST; + +// Track data * 8 +typedef struct TRACKDATA +{ + unsigned short freq; // Frequency (1000 is default) + unsigned char wave_no; // Waveform No. + signed char pipi; + + NOTELIST *note_p; + NOTELIST *note_list; +} TRACKDATA; + +// Unique information held in songs +typedef struct MUSICINFO +{ + unsigned short wait; + unsigned char line; // Number of lines in one measure + unsigned char dot; // Number of dots per line + unsigned short alloc_note; // Number of allocated notes + long repeat_x; // Repeat + long end_x; // End of song (Return to repeat) + TRACKDATA tdata[16]; +} MUSICINFO; + +// メインクラス。このアプリケーションの中心。(クラスってやつを初めて使う) (Main class. The heart of this application. (Class is used for the first time)) +typedef struct OrgData +{ + OrgData(); // コンストラクタ (Constructor) +// ~OrgData(); // デストラクタ (Destructor) + MUSICINFO info; + char track; + char mute[MAXTRACK]; + unsigned char def_pan; + unsigned char def_volume; + void InitOrgData(void); + void GetMusicInfo(MUSICINFO *mi); // 曲情報を取得 (Get song information) + // 曲情報を設定。flagは設定アイテムを指定 (Set song information. flag specifies the setting item) + BOOL SetMusicInfo(MUSICINFO *mi,unsigned long flag); + BOOL NoteAlloc(unsigned short note_num); // 指定の数だけNoteDataの領域を確保 (Allocate the specified number of NoteData areas.) + void ReleaseNote(void); // NoteDataを開放 (Release NoteData) + // 以下は再生 (The following is playback) + void PlayData(void); + void SetPlayPointer(long x); // 再生ポインターを指定の位置に設定 (Set playback pointer to specified position) + // 以下はファイル関係 (The following are related to files) + BOOL OrgData::InitMusicData(const char *path); +} ORGDATA; + +ORGDATA org_data; + +LPDIRECTSOUNDBUFFER lpORGANBUFFER[8][8][2] = {NULL}; int gTrackVol[MAXTRACK]; int gOrgVolume = 100; BOOL bFadeout = FALSE; +BOOL g_mute[MAXTRACK]; // Used by the debug Mute menu -BOOL OrganyaNoteAlloc(unsigned short alloc) -{ - for (int j = 0; j < MAXTRACK; j++) - { - info.tdata[j].wave_no = 0; - info.tdata[j].note_list = NULL; - info.tdata[j].note_p = new NOTELIST[alloc]; - if (info.tdata[j].note_p == NULL) - { - for (int i = 0; i < MAXTRACK; i++) - { - if (info.tdata[i].note_p != NULL) - { - delete[] info.tdata[i].note_p; - info.tdata[j].note_p = NULL; // Uses j instead of i - } - } - - return FALSE; - } - - for (int i = 0; i < alloc; i++) - { - (info.tdata[j].note_p + i)->from = NULL; - (info.tdata[j].note_p + i)->to = NULL; - (info.tdata[j].note_p + i)->length = 0; - (info.tdata[j].note_p + i)->pan = PANDUMMY; - (info.tdata[j].note_p + i)->volume = VOLDUMMY; - (info.tdata[j].note_p + i)->y = KEYDUMMY; - } - } - - for (int j = 0; j < MAXMELODY; j++) - MakeOrganyaWave(j, info.tdata[j].wave_no, info.tdata[j].pipi); - // for(int j = 0; j < MAXDRAM; j++) - // InitDramObject(j); - - // this->track = 0; - - return FALSE; -} - -void OrganyaReleaseNote() -{ - for (int i = 0; i < MAXTRACK; i++) - { - if (info.tdata[i].note_p != NULL) - { -#ifdef FIX_BUGS - delete[] info.tdata[i].note_p; -#else - delete info.tdata[i].note_p; // Should be delete[] -#endif - info.tdata[i].note_p = NULL; - } - } -} +///////////////////////////////////////////// +//■オルガーニャ■■■■■■■■■■■■/////// (Organya) +///////////////////// // Wave playing and loading typedef struct @@ -100,7 +140,7 @@ typedef struct short oct_size; } OCTWAVE; -OCTWAVE oct_wave[8] = +static const OCTWAVE oct_wave[8] = { { 256, 1, 4 }, // 0 Oct { 256, 2, 8 }, // 1 Oct @@ -112,88 +152,141 @@ OCTWAVE oct_wave[8] = { 8,128, 32 }, // 7 Oct }; +static WAVEFORMATEX format_tbl2 = {WAVE_FORMAT_PCM, 1, 22050, 22050, 1, 8, 0}; // 22050HzのFormat + +// In the original source code, format_tbl2 was a raw array of bytes, as seen below +// BYTE format_tbl2[] = {0x01,0x00,0x01,0x00,0x22,0x56,0x00,0x00,0x22,0x56,0x00,0x00,0x01,0x00,0x08,0x00,0x00,0x00}; // 22050HzのFormat + BOOL MakeSoundObject8(signed char *wavep, signed char track, signed char pipi) { - for (int j = 0; j < 8; j++) - { - for (int k = 0; k < 2; k++) - { - size_t wave_size = oct_wave[j].wave_size; - size_t data_size = pipi ? wave_size * oct_wave[j].oct_size : wave_size; + DWORD i,j,k; + unsigned long wav_tp; // WAVテーブルをさすポインタ (Pointer to WAV table) + DWORD wave_size; // 256; + DWORD data_size; + BYTE *wp; + BYTE *wp_sub; + int work; + // セカンダリバッファの生成 (Create secondary buffer) + DSBUFFERDESC dsbd; - // Create sound buffer - lpORGANBUFFER[track][j][k] = new SOUNDBUFFER(data_size); + if (lpDS == NULL) + return FALSE; + + for (j = 0; j < 8; j++) + { + for (k = 0; k < 2; k++) + { + wave_size = oct_wave[j].wave_size; + + if (pipi) + data_size = wave_size * oct_wave[j].oct_size; + else + data_size = wave_size; + + ZeroMemory(&dsbd, sizeof(DSBUFFERDESC)); + + dsbd.dwSize = sizeof(DSBUFFERDESC); + dsbd.dwBufferBytes = data_size; + dsbd.lpwfxFormat = &format_tbl2; + dsbd.dwFlags = DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; + + if(lpDS->CreateSoundBuffer(&dsbd, &lpORGANBUFFER[track][j][k], NULL) != DS_OK) // j = se_no + return FALSE; // Get wave data - unsigned char *wp = new unsigned char[data_size]; - unsigned char *wp_sub = wp; - size_t wav_tp = 0; + wp = (BYTE*)malloc(data_size); + wp_sub = wp; + wav_tp = 0; - for (size_t i = 0; i < data_size; i++) + for (i = 0; i < data_size; i++) { - unsigned char work = *(wavep + wav_tp); + work = *(wavep + wav_tp); work += 0x80; - *wp_sub = work; + *wp_sub = (BYTE)work; wav_tp += 0x100 / wave_size; - if (wav_tp >= 0x100) + if (wav_tp > 0xFF) wav_tp -= 0x100; wp_sub++; } - // Copy wave data to sound buffer - unsigned char *buf; - lpORGANBUFFER[track][j][k]->Lock(&buf, NULL); - memcpy(buf, wp, data_size); - lpORGANBUFFER[track][j][k]->Unlock(); + // データの転送 (Data transfer) + LPVOID lpbuf1, lpbuf2; + DWORD dwbuf1, dwbuf2=0; + HRESULT hr; + + hr = lpORGANBUFFER[track][j][k]->Lock(0, data_size, &lpbuf1, &dwbuf1, &lpbuf2, &dwbuf2, 0); + + if (hr != DS_OK) + { +#ifdef FIX_BUGS + free(wp); // The updated Organya source code includes this fix +#endif + return FALSE; + } + + CopyMemory(lpbuf1, (BYTE*)wp, dwbuf1); + + if (dwbuf2 != 0) + CopyMemory(lpbuf2, (BYTE*)wp+dwbuf1, dwbuf2); + + lpORGANBUFFER[track][j][k]->Unlock(lpbuf1, dwbuf1, lpbuf2, dwbuf2); lpORGANBUFFER[track][j][k]->SetCurrentPosition(0); - delete[] wp; + free(wp); } } return TRUE; } -// Playing melody tracks -short freq_tbl[12] = {262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494}; +static const short freq_tbl[12] = {262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494}; void ChangeOrganFrequency(unsigned char key, signed char track, long a) { + if (lpDS == NULL) + return; + for (int j = 0; j < 8; j++) - { for (int i = 0; i < 2; i++) - { - lpORGANBUFFER[track][j][i]->SetFrequency(((oct_wave[j].wave_size * freq_tbl[key]) * oct_wave[j].oct_par) / 8 + (a - 1000)); - } - } + lpORGANBUFFER[track][j][i]->SetFrequency(((oct_wave[j].wave_size * freq_tbl[key]) * oct_wave[j].oct_par) / 8 + (a - 1000)); // 1000を+αのデフォルト値とする (1000 is the default value for + α) } -short pan_tbl[13] = {0, 43, 86, 129, 172, 215, 256, 297, 340, 383, 426, 469, 512}; -unsigned char old_key[MAXTRACK] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; -unsigned char key_on[MAXTRACK] = {0}; -unsigned char key_twin[MAXTRACK] = {0}; +const short pan_tbl[13] = {0, 43, 86, 129, 172, 215, 256, 297, 340, 383, 426, 469, 512}; +unsigned char old_key[MAXTRACK] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // 再生中の音 (Sound being played) +unsigned char key_on[MAXTRACK]; // キースイッチ (Key switch) +unsigned char key_twin[MAXTRACK]; // 今使っているキー(連続時のノイズ防止の為に二つ用意) (Currently used keys (prepared for continuous noise prevention)) -void ChangeOrganPan(unsigned char key, unsigned char pan, signed char track) +void ChangeOrganPan(unsigned char key, unsigned char pan, signed char track) // 512がMAXで256がノーマル (512 is MAX and 256 is normal) { + if (lpDS == NULL) + return; + if (old_key[track] != PANDUMMY) lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->SetPan((pan_tbl[pan] - 0x100) * 10); } -void ChangeOrganVolume(int no, long volume, signed char track) +void ChangeOrganVolume(int no, long volume, signed char track) // 300がMAXで300がノーマル (300 is MAX and 300 is normal) { + if (lpDS == NULL) + return; + if (old_key[track] != VOLDUMMY) lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->SetVolume((volume - 0xFF) * 8); } +// サウンドの再生 (Play sound) void PlayOrganObject(unsigned char key, int mode, signed char track, long freq) { + if (lpDS == NULL) + return; + if (lpORGANBUFFER[track][key / 12][key_twin[track]] != NULL) { switch (mode) { - case 0: + case 0: // 停止 (Stop) if (old_key[track] != 0xFF) { lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Stop(); @@ -201,51 +294,56 @@ void PlayOrganObject(unsigned char key, int mode, signed char track, long freq) } break; - case 1: + case 1: // 再生 (Playback) break; - case 2: + case 2: // 歩かせ停止 (Stop playback) if (old_key[track] != 0xFF) { - lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Play(false); + lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Play(0, 0, 0); old_key[track] = 0xFF; } break; case -1: - if (old_key[track] == 0xFF) + if (old_key[track] == 0xFF) // 新規鳴らす (New sound) { - ChangeOrganFrequency(key % 12, track, freq); - lpORGANBUFFER[track][key / 12][key_twin[track]]->Play(true); + ChangeOrganFrequency(key % 12, track, freq); // 周波数を設定して (Set the frequency) + lpORGANBUFFER[track][key / 12][key_twin[track]]->Play(0, 0, DSBPLAY_LOOPING); old_key[track] = key; key_on[track] = 1; } - else if (key_on[track] == 1 && old_key[track] == key) + else if (key_on[track] == 1 && old_key[track] == key) // 同じ音 (Same sound) { - lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Play(false); + // 今なっているのを歩かせ停止 (Stop playback now) + lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Play(0, 0, 0); key_twin[track]++; - if (key_twin[track] == 2) + if (key_twin[track] > 1) key_twin[track] = 0; - lpORGANBUFFER[track][key / 12][key_twin[track]]->Play(true); + lpORGANBUFFER[track][key / 12][key_twin[track]]->Play(0, 0, DSBPLAY_LOOPING); } - else + else // 違う音を鳴らすなら (If you make a different sound) { - lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Play(false); + lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Play(0, 0, 0); // 今なっているのを歩かせ停止 (Stop playback now) key_twin[track]++; - if (key_twin[track] == 2) + if (key_twin[track] > 1) key_twin[track] = 0; - ChangeOrganFrequency(key % 12, track, freq); - lpORGANBUFFER[track][key / 12][key_twin[track]]->Play(true); + ChangeOrganFrequency(key % 12, track, freq); // 周波数を設定して (Set the frequency) + lpORGANBUFFER[track][key / 12][key_twin[track]]->Play(0, 0, DSBPLAY_LOOPING); old_key[track] = key; } + break; } } } -// Release tracks +// オルガーニャオブジェクトを開放 (Open Organya object) void ReleaseOrganyaObject(signed char track) { + if (lpDS == NULL) + return; + for (int i = 0; i < 8; i++) { if (lpORGANBUFFER[track][i][0] != NULL) @@ -261,96 +359,537 @@ void ReleaseOrganyaObject(signed char track) } } -// Handling WAVE100 +// 波形データをロード (Load waveform data) signed char wave_data[100][0x100]; -BOOL InitWaveData100() +BOOL InitWaveData100(void) { - HRSRC hrscr = FindResourceA(NULL, "WAVE100", "WAVE"); + HRSRC hrscr; + DWORD *lpdword; // リソースのアドレス (Resource address) + + if (lpDS == NULL) + return FALSE; + + // リソースの検索 (Search for resources) + hrscr = FindResourceA(NULL, "WAVE100", "WAVE"); if (hrscr == NULL) return FALSE; - const unsigned char *data = (unsigned char*)LockResource(LoadResource(NULL, hrscr)); - - memcpy(wave_data, data, 100 * 0x100); + // リソースのアドレスを取得 (Get resource address) + lpdword = (DWORD*)LockResource(LoadResource(NULL, hrscr)); + memcpy(wave_data, lpdword, 100 * 0x100); return TRUE; } -// Create org wave +// 波形を100個の中から選択して作成 (Select from 100 waveforms to create) BOOL MakeOrganyaWave(signed char track, signed char wave_no, signed char pipi) { - if (wave_no > 99) - { - printf("WARNING: track %d has out-of-range wave_no %d\n", track, wave_no); + if (lpDS == NULL) + return FALSE; + + if (wave_no > 99) return FALSE; - } ReleaseOrganyaObject(track); MakeSoundObject8(wave_data[wave_no], track, pipi); + return TRUE; } -// Dram +///////////////////////////////////////////// +//■オルガーニャドラムス■■■■■■■■/////// (Organya drums) +///////////////////// + void ChangeDramFrequency(unsigned char key, signed char track) { - lpDRAMBUFFER[track]->SetFrequency(key * 800 + 100); + if (lpDS == NULL) + return; + + lpSECONDARYBUFFER[150 + track]->SetFrequency(key * 800 + 100); } void ChangeDramPan(unsigned char pan, signed char track) { - lpDRAMBUFFER[track]->SetPan((pan_tbl[pan] - 0x100) * 10); + if (lpDS == NULL) + return; + + lpSECONDARYBUFFER[150 + track]->SetPan((pan_tbl[pan] - 0x100) * 10); } void ChangeDramVolume(long volume, signed char track) { - lpDRAMBUFFER[track]->SetVolume((volume - 0xFF) * 8); + if (lpDS == NULL) + return; + + lpSECONDARYBUFFER[150 + track]->SetVolume((volume - 0xFF) * 8); } +// サウンドの再生 (Play sound) void PlayDramObject(unsigned char key, int mode, signed char track) { - switch (mode) + if (lpDS == NULL) + return; + + if (lpSECONDARYBUFFER[150 + track] != NULL) { - case 0: - lpDRAMBUFFER[track]->Stop(); - lpDRAMBUFFER[track]->SetCurrentPosition(0); - break; + switch (mode) + { + case 0: // 停止 (Stop) + lpSECONDARYBUFFER[150 + track]->Stop(); + lpSECONDARYBUFFER[150 + track]->SetCurrentPosition(0); + break; - case 1: - lpDRAMBUFFER[track]->Stop(); - lpDRAMBUFFER[track]->SetCurrentPosition(0); - ChangeDramFrequency(key, track); - lpDRAMBUFFER[track]->Play(false); - break; + case 1: // 再生 (Playback) + lpSECONDARYBUFFER[150 + track]->Stop(); + lpSECONDARYBUFFER[150 + track]->SetCurrentPosition(0); + ChangeDramFrequency(key, track); // 周波数を設定して () + lpSECONDARYBUFFER[150 + track]->Play(0, 0, 0); + break; - case 2: - break; + case 2: // 歩かせ停止 (Stop playback) + break; - case -1: - break; + case -1: + break; + } } } +OrgData::OrgData() +{ + int i; + + for (i = 0; i < MAXTRACK; i++) + { + info.tdata[i].note_list = NULL; + info.tdata[i].note_p = NULL; + } +} + +void OrgData::InitOrgData(void) +{ + track = 0; + info.alloc_note = ALLOCNOTE; // とりあえず10000個確保 (For the time being, secure 10,000 pieces) + info.dot = 4; + info.line = 4; + info.wait = 128; + info.repeat_x = info.dot * info.line * 0; + info.end_x = info.dot * info.line * 255; + + int i; + for (i = 0; i < MAXTRACK; i++) + { + info.tdata[i].freq = 1000; + info.tdata[i].wave_no = 0; + info.tdata[i].pipi = 0; + } + + NoteAlloc(info.alloc_note); + SetMusicInfo(&info, SETALL); + + def_pan = DEFPAN; + def_volume = DEFVOLUME; +} + +// 曲情報を設定。flagはアイテムを指定 (Set song information. flag specifies an item) +BOOL OrgData::SetMusicInfo(MUSICINFO *mi, unsigned long flag) +{ +#ifndef FIX_BUGS // Leftover debug junk + char str[32]; +#endif + int i; + + if (flag & SETGRID) // グリッドを有効に (Enable grid) + { + info.dot = mi->dot; + info.line = mi->line; + } + + if (flag & SETWAIT) + { + info.wait = mi->wait; +#ifndef FIX_BUGS + itoa(mi->wait, str, 10); +#endif + } + + if (flag & SETREPEAT) + { + info.repeat_x = mi->repeat_x; + info.end_x = mi->end_x; + } + + if (flag & SETFREQ) + { + for (i = 0; i < MAXMELODY; i++) + { + info.tdata[i].freq = mi->tdata[i].freq; + info.tdata[i].pipi = info.tdata[i].pipi; + } + } + + if (flag & SETWAVE) + for (i = 0; i < MAXTRACK; i++) + info.tdata[i].wave_no = mi->tdata[i].wave_no; + + if (flag & SETPIPI) + for (i = 0; i < MAXTRACK; i++) + info.tdata[i].pipi = mi->tdata[i].pipi; + + return TRUE; +} + +// 指定の数だけNoteDataの領域を確保(初期化) (Allocate the specified number of NoteData areas (initialization)) +BOOL OrgData::NoteAlloc(unsigned short alloc) +{ + int i,j; + + for (j = 0; j < MAXTRACK; j++) + { + info.tdata[j].wave_no = 0; + info.tdata[j].note_list = NULL; // コンストラクタにやらせたい (I want the constructor to do it) + info.tdata[j].note_p = (NOTELIST*)malloc(sizeof(NOTELIST) * alloc); + + if (info.tdata[j].note_p == NULL) + { + for (i = 0; i < MAXTRACK; i++) + { + if (info.tdata[i].note_p != NULL) + { + free(info.tdata[i].note_p); +#ifdef FIX_BUGS + info.tdata[i].note_p = NULL; +#else + info.tdata[j].note_p = NULL; // Uses j instead of i +#endif + } + } + + return FALSE; + } + + for (i = 0; i < alloc; i++) + { + (info.tdata[j].note_p + i)->from = NULL; + (info.tdata[j].note_p + i)->to = NULL; + (info.tdata[j].note_p + i)->length = 0; + (info.tdata[j].note_p + i)->pan = PANDUMMY; + (info.tdata[j].note_p + i)->volume = VOLDUMMY; + (info.tdata[j].note_p + i)->y = KEYDUMMY; + } + } + + for (j = 0; j < MAXMELODY; j++) + MakeOrganyaWave(j, info.tdata[j].wave_no, info.tdata[j].pipi); + + track = 0; // 今はここに書いておく (Write here now) + + return TRUE; +} + +// NoteDataを開放 (Release NoteData) +void OrgData::ReleaseNote(void) +{ + for (int i = 0; i < MAXTRACK; i++) + { + if (info.tdata[i].note_p != NULL) + { + free(info.tdata[i].note_p); + info.tdata[i].note_p = NULL; + } + } +} + +static const char pass[7] = "Org-01"; +static const char pass2[7] = "Org-02"; // Pipi + +BOOL OrgData::InitMusicData(const char *path) +{ + ORGANYADATA org_data; + NOTELIST *np; + int i,j; + char pass_check[6]; + char ver = 0; + + HRSRC hrscr = FindResourceA(0, path, "ORG"); + if (hrscr == NULL) + return FALSE; + + unsigned char *p = (unsigned char*)LockResource(LoadResource(0, hrscr)); + + memcpy(&pass_check[0], p, 6); + p += 6; + + if(memcmp(pass_check, pass, 6) == 0) + ver = 1; + if(memcmp(pass_check, pass2, 6) == 0) + ver = 2; + + if(ver == 0) + return FALSE; + + // 曲情報の読み込み (Loading song information) + memcpy(&org_data, p, sizeof(ORGANYADATA)); + p += sizeof(ORGANYADATA); + + // 曲の情報を設定 (Set song information) + info.wait = org_data.wait; + info.line = org_data.line; + info.dot = org_data.dot; + info.repeat_x = org_data.repeat_x; + info.end_x = org_data.end_x; + + for (i = 0; i < MAXTRACK; i++) + { + info.tdata[i].freq = org_data.tdata[i].freq; + + if (ver == 1) + info.tdata[i].pipi = 0; + else + info.tdata[i].pipi = org_data.tdata[i].pipi; + + info.tdata[i].wave_no = org_data.tdata[i].wave_no; + } + + // 音符のロード (Loading notes) + for (j = 0; j < MAXTRACK; j++) + { + // 最初の音符はfromがNULLとなる (The first note has from as NULL) + if (org_data.tdata[j].note_num == 0) + { + info.tdata[j].note_list = NULL; + continue; + } + + // リストを作る (Make a list) + np = info.tdata[j].note_p; + info.tdata[j].note_list = info.tdata[j].note_p; + np->from = NULL; + np->to = (np + 1); + np++; + + for (i = 1; i < org_data.tdata[j].note_num; i++) + { + np->from = (np - 1); + np->to = (np + 1); + np++; + } + + // 最後の音符のtoはNULL (The last note to is NULL) + np--; + np->to = NULL; + + // 内容を代入 (Assign content) + np = info.tdata[j].note_p; // X座標 (X coordinate) + for (i = 0; i < org_data.tdata[j].note_num; i++) + { + memcpy(&np->x, p, sizeof(long)); + p += sizeof(long); + np++; + } + + np = info.tdata[j].note_p; // Y座標 (Y coordinate) + for (i = 0; i < org_data.tdata[j].note_num; i++) + { + memcpy(&np->y, p, sizeof(unsigned char)); + p += sizeof(unsigned char); + np++; + } + + np = info.tdata[j].note_p; // 長さ (Length) + for (i = 0; i < org_data.tdata[j].note_num; i++) + { + memcpy(&np->length, p, sizeof(unsigned char)); + p += sizeof(unsigned char); + np++; + } + + np = info.tdata[j].note_p; // ボリューム (Volume) + for (i = 0; i < org_data.tdata[j].note_num; i++) + { + memcpy(&np->volume, p, sizeof(unsigned char)); + p += sizeof(unsigned char); + np++; + } + + np = info.tdata[j].note_p; // パン (Pan) + for (i = 0; i < org_data.tdata[j].note_num; i++) + { + memcpy(&np->pan, p, sizeof(unsigned char)); + p += sizeof(unsigned char); + np++; + } + } + + // データを有効に (Enable data) + for (j = 0; j < MAXMELODY; j++) + MakeOrganyaWave(j,info.tdata[j].wave_no, info.tdata[j].pipi); +#ifndef FIX_BUGS + // Pixel ripped out some code so he could use PixTone sounds as drums, but he left this dead code + for (j = MAXMELODY; j < MAXTRACK; j++) + { + i = info.tdata[j].wave_no; + //InitDramObject(dram_name[i], j - MAXMELODY); + } +#endif + + SetPlayPointer(0); // 頭出し (Cue) + + return TRUE; +} + +// 曲情報を取得 (Get song information) +void OrgData::GetMusicInfo(MUSICINFO *mi) +{ + mi->dot = info.dot; + mi->line = info.line; + mi->alloc_note = info.alloc_note; + mi->wait = info.wait; + mi->repeat_x = info.repeat_x; + mi->end_x = info.end_x; + + for (int i = 0; i < MAXTRACK; i++) + { + mi->tdata[i].freq = info.tdata[i].freq; + mi->tdata[i].wave_no = info.tdata[i].wave_no; + mi->tdata[i].pipi = info.tdata[i].pipi; + } +} + +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +//プロトタイプ宣言 (prototype declaration) +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ + +BOOL InitMMTimer(); +BOOL StartTimer(DWORD dwTimer); +VOID CALLBACK TimerProc(UINT uTID,UINT uMsg,DWORD dwUser,DWORD dwParam1,DWORD dwParam2); +BOOL QuitMMTimer(); + +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +//グローバル変数 (Global variable) +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +static UINT ExactTime = 13; // 最小精度 (Minimum accuracy) +static UINT TimerID = NULL; +static BOOL nameless_flag; + +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +// タイマー精度を設定する。 (Set timer accuracy.) +// この関数はアプリケーション初期化時に一度呼び出す。 (This function is called once when the application is initialized.) +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +BOOL InitMMTimer() +{ + TIMECAPS tc; + MMRESULT ret; + + // タイマーの精度情報を取得する (Get timer accuracy information) + ret = timeGetDevCaps(&tc,sizeof(TIMECAPS)); + if (ret != TIMERR_NOERROR) + return FALSE; + + if (ExactTime < tc.wPeriodMin) + ExactTime = tc.wPeriodMin; + + // この精度で初期化する (Initialize with this precision) + ret = timeBeginPeriod(ExactTime); + if (ret != TIMERR_NOERROR) + return FALSE; + + return TRUE; +} + +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +// タイマーを起動する。 (Start the timer.) +// dwTimer 設定するタイマー間隔 (dwTimer Timer interval to be set) +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +BOOL StartTimer(DWORD dwTimer) +{ + MMRESULT ret = NULL; + ExactTime = dwTimer; + + // タイマーを生成する (Generate timer) + TimerID = timeSetEvent + ( + dwTimer, // タイマー時間 (Timer time) + 10, // 許容できるタイマー精度 (Acceptable timer accuracy) + (LPTIMECALLBACK)TimerProc, // コールバックプロシージャ (Callback procedure) + NULL, // ユーザーがコールバック関数のdwUserに送る情報値 (Information value sent by user to dwUser in callback function) + TIME_PERIODIC // タイマー時間毎にイベントを発生させる (Generate an event every timer time) + ); + + if (ret != TIMERR_NOERROR) + return FALSE; + + nameless_flag = TRUE; + + return TRUE; +} + +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +// タイマーのコールバック関数 (Timer callback function) +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +VOID CALLBACK TimerProc(UINT uTID,UINT uMsg,DWORD dwUser,DWORD dwParam1,DWORD dwParam2) +{ + DWORD dwNowTime; + dwNowTime = timeGetTime(); + //=================================================================================== + // ここにユーザー定義のソースを書く。 (Write user-defined source here.) + // 基本的に関数を呼び出すだけで処理は他の関数でするべきだろう。 (Basically just call a function and the process should be another function.) + //=================================================================================== + org_data.PlayData(); +} + +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +// タイマーリソースを開放する。 (Release timer resources.) +// アプリケーション終了時に一度呼び出す。 (Call once when the application ends.) +/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/ +BOOL QuitMMTimer() +{ + MMRESULT ret; + + if (!nameless_flag) + return FALSE; + + if(TimerID != TIMERR_NOERROR) + { + // タイマーを使用中なら終了させる (Terminate timer if in use) + ret = timeKillEvent(TimerID); + if (ret != TIMERR_NOERROR) + return FALSE; + } + + // タイマーリソースを開放する (Release timer resources) + ret = timeEndPeriod(ExactTime); + if (ret != TIMERR_NOERROR) + return FALSE; + + nameless_flag = FALSE; + + return TRUE; +} + // Play data long play_p; NOTELIST *play_np[MAXTRACK]; -long now_leng[MAXMELODY] = {0}; +long now_leng[MAXMELODY]; -void OrganyaPlayData() +void OrgData::PlayData() { + int i; + // Handle fading out if (bFadeout && gOrgVolume) gOrgVolume -= 2; if (gOrgVolume < 0) gOrgVolume = 0; - // Play melody - for (int i = 0; i < MAXMELODY; i++) + // メロディの再生 (Play melody) + for (i = 0; i < MAXMELODY; i++) { if (play_np[i] != NULL && play_p == play_np[i]->x) { - if (play_np[i]->y != KEYDUMMY) + if (!g_mute[i] && play_np[i]->y != KEYDUMMY) // 音が来た。 (The sound has come.) { PlayOrganObject(play_np[i]->y, -1, i, info.tdata[i].freq); now_leng[i] = play_np[i]->length; @@ -361,7 +900,7 @@ void OrganyaPlayData() if (play_np[i]->volume != VOLDUMMY) gTrackVol[i] = play_np[i]->volume; - play_np[i] = play_np[i]->to; + play_np[i] = play_np[i]->to; // 次の音符を指す (Points to the next note) } if (now_leng[i] == 0) @@ -371,14 +910,15 @@ void OrganyaPlayData() now_leng[i]--; if (play_np[i]) - ChangeOrganVolume(play_np[i]->y, gOrgVolume * gTrackVol[i] / 0x7F, i); + ChangeOrganVolume(play_np[i]->y, gTrackVol[i] * gOrgVolume / 0x7F, i); } - for (int i = MAXMELODY; i < MAXTRACK; i++) + // ドラムの再生 (Drum playback) + for (i = MAXMELODY; i < MAXTRACK; i++) { - if (play_np[i] != NULL && play_p == play_np[i]->x) + if (play_np[i] != NULL && play_p == play_np[i]->x) // 音が来た。 (The sound has come.) { - if (play_np[i]->y != KEYDUMMY) + if (play_np[i]->y != KEYDUMMY && !g_mute[i]) // ならす (Tame) PlayDramObject(play_np[i]->y, 1, i - MAXMELODY); if (play_np[i]->pan != PANDUMMY) @@ -386,11 +926,11 @@ void OrganyaPlayData() if (play_np[i]->volume != VOLDUMMY) gTrackVol[i] = play_np[i]->volume; - play_np[i] = play_np[i]->to; + play_np[i] = play_np[i]->to; // 次の音符を指す (Points to the next note) } if (play_np[i]) - ChangeDramVolume(gOrgVolume * gTrackVol[i] / 0x7F, i - MAXMELODY); + ChangeDramVolume(gTrackVol[i] * gOrgVolume / 0x7F, i - MAXMELODY); } // Looping @@ -402,193 +942,98 @@ void OrganyaPlayData() } } -void SetPlayPointer(long x) +void OrgData::SetPlayPointer(long x) { for (int i = 0; i < MAXTRACK; i++) { play_np[i] = info.tdata[i].note_list; while (play_np[i] != NULL && play_np[i]->x < x) - play_np[i] = play_np[i]->to; + play_np[i] = play_np[i]->to; // 見るべき音符を設定 (Set note to watch) } play_p = x; } -#define READ_LE16(pointer) pointer[0] | (pointer[1] << 8); pointer += 2; -#define READ_LE32(pointer) pointer[0] | (pointer[1] << 8) | (pointer[2] << 16) | (pointer[3] << 24); pointer += 4; +// Start and end organya +BOOL StartOrganya(LPDIRECTSOUND _lpDS, const char *path_wave) // Both arguments are ignored for some reason +{ + if (lpDS == NULL) + return FALSE; + + if (!InitWaveData100()) + return FALSE; + + org_data.InitOrgData(); + + return TRUE; +} // Load organya file -void LoadOrganya(const char *name) +BOOL LoadOrganya(const char *name) { - // Unload previous things - OrganyaReleaseNote(); - memset(&info, 0, sizeof(info)); - OrganyaNoteAlloc(0xFFFF); + if (lpDS == NULL) + return FALSE; - // Stop currently playing notes - memset(play_np, 0, sizeof(play_np)); - memset(old_key, 0xFF, sizeof(old_key)); - memset(key_on, 0, sizeof(key_on)); - memset(key_twin, 0, sizeof(key_twin)); - memset(now_leng, 0, sizeof(now_leng)); + if (!org_data.InitMusicData(name)) + return FALSE; - // Open file - HRSRC hrscr = FindResourceA(NULL, name, "ORG"); + gOrgVolume = 100; + bFadeout = 0; - if (hrscr == NULL) - return; - - const unsigned char *p = (unsigned char*)LockResource(LoadResource(NULL, hrscr)); - - // Version Check - unsigned char ver = 0; - char pass_check[6]; - - memcpy(pass_check, p, 6); - p += 6; - - if (!memcmp(pass_check, "Org-01", 6)) - ver = 1; - - if (!memcmp(pass_check, "Org-02", 6)) - ver = 2; - - // if (!memcmp(pass_check, "Org-03", 6)) - // ver = 2; - - if (!ver) - { - printf("Failed to open .org, invalid version %s", pass_check); - return; - } - - // Set song information - info.wait = READ_LE16(p); - info.line = *p++; - info.dot = *p++; - info.repeat_x = READ_LE32(p); - info.end_x = READ_LE32(p); - - for (int i = 0; i < 16; i++) - { - info.tdata[i].freq = READ_LE16(p); - info.tdata[i].wave_no = *p++; - const signed char pipi = *p++; - info.tdata[i].pipi = ver == 1 ? 0 : pipi; - info.tdata[i].note_num = READ_LE16(p); - } - - // Load notes - NOTELIST *np; - - for (int j = 0; j < 16; j++) - { - // The first note from is NULL - if (info.tdata[j].note_num == 0) - { - info.tdata[j].note_list = NULL; - continue; - } - - // Make note list - np = info.tdata[j].note_p; - info.tdata[j].note_list = info.tdata[j].note_p; - np->from = NULL; - np->to = (np + 1); - np++; - - for (int i = 1; i < info.tdata[j].note_num; i++) - { - np->from = (np - 1); - np->to = (np + 1); - np++; - } - - // The last note to is NULL - np--; - np->to = NULL; - - // Set note properties - np = info.tdata[j].note_p; // X position - for (int i = 0; i < info.tdata[j].note_num; i++) - { - np->x = READ_LE32(p); - np++; - } - - np = info.tdata[j].note_p; // Y position - for (int i = 0; i < info.tdata[j].note_num; i++) - { - np->y = *p++; - np++; - } - - np = info.tdata[j].note_p; // Length - for (int i = 0; i < info.tdata[j].note_num; i++) - { - np->length = *p++; - np++; - } - - np = info.tdata[j].note_p; // Volume - for (int i = 0; i < info.tdata[j].note_num; i++) - { - np->volume = *p++; - np++; - } - - np = info.tdata[j].note_p; // Pan - for (int i = 0; i < info.tdata[j].note_num; i++) - { - np->pan = *p++; - np++; - } - } - - // Create waves - for (int j = 0; j < 8; j++) - MakeOrganyaWave(j, info.tdata[j].wave_no, info.tdata[j].pipi); - - // Reset position - SetPlayPointer(0); - - // Set as loaded - info.loaded = TRUE; +#ifdef FIX_BUGS + return TRUE; +#else + return FALSE; // Err... isn't this meant to be 'TRUE'? +#endif } void SetOrganyaPosition(unsigned int x) { - SetPlayPointer(x); + if (lpDS == NULL) + return; + + org_data.SetPlayPointer(x); gOrgVolume = 100; bFadeout = FALSE; } -unsigned int GetOrganyaPosition() +unsigned int GetOrganyaPosition(void) { + if (lpDS == NULL) + return 0; + return play_p; } -void PlayOrganyaMusic() +void PlayOrganyaMusic(void) { - // Start timer - OrganyaStartTimer(info.wait); + if (lpDS == NULL) + return; + + QuitMMTimer(); + InitMMTimer(); + StartTimer(org_data.info.wait); } BOOL ChangeOrganyaVolume(signed int volume) { - if (volume >= 0 && volume <= 100) - { - gOrgVolume = volume; - return TRUE; - } + if (lpDS == NULL) + return FALSE; - return FALSE; + if (volume < 0 || volume > 100) + return FALSE; + + gOrgVolume = volume; + return TRUE; } void StopOrganyaMusic() { + if (lpDS == NULL) + return; + // Stop timer - OrganyaEndTimer(); + QuitMMTimer(); // Stop notes for (int i = 0; i < MAXMELODY; i++) @@ -597,6 +1042,8 @@ void StopOrganyaMusic() memset(old_key, 255, sizeof(old_key)); memset(key_on, 0, sizeof(key_on)); memset(key_twin, 0, sizeof(key_twin)); + + Sleep(100); } void SetOrganyaFadeout() @@ -604,71 +1051,20 @@ void SetOrganyaFadeout() bFadeout = TRUE; } -// Org timer -SDL_Thread *OrganyaTimer = NULL; -BOOL bEndTimer = FALSE; - -int OrganyaPlayTimer(void *ptr) -{ - SDL_SetThreadPriority(SDL_THREAD_PRIORITY_HIGH); - - // Set time for next step to play - Uint32 NextTick = SDL_GetTicks() + info.wait; - - while (bEndTimer == FALSE) - { - if (info.loaded) - { - // Play music - OrganyaPlayData(); - - // Wait until this step is over - while (NextTick > SDL_GetTicks()) - SDL_Delay(1); - - // Get time for next step to play - while (NextTick <= SDL_GetTicks()) - NextTick += info.wait; - } - else - { - // Wait until the org is loaded - SDL_Delay(1); - } - } - - return 0; -} - -void OrganyaStartTimer(unsigned int wait) -{ - OrganyaEndTimer(); - bEndTimer = FALSE; - OrganyaTimer = SDL_CreateThread(OrganyaPlayTimer, "OrganyaPlayTimer", (void*)NULL); -} - -void OrganyaEndTimer() -{ - bEndTimer = TRUE; // Tell thread to end - SDL_WaitThread(OrganyaTimer, NULL); // Wait for thread to end - OrganyaTimer = NULL; -} - -// Start and end organya -void StartOrganya() -{ - // Initialize org stuff - InitWaveData100(); -} - void EndOrganya() { + if (lpDS == NULL) + return; + // End timer - OrganyaEndTimer(); + QuitMMTimer(); // Release everything related to org - OrganyaReleaseNote(); + org_data.ReleaseNote(); for (int i = 0; i < MAXMELODY; i++) + { + PlayOrganObject(0, 0, i, 0); ReleaseOrganyaObject(i); + } } diff --git a/src/Organya.h b/src/Organya.h index d6429a85..ca31c853 100644 --- a/src/Organya.h +++ b/src/Organya.h @@ -1,57 +1,18 @@ #pragma once +#include + #include "WindowsWrapper.h" -//Below are Organya song data structures -struct NOTELIST -{ - NOTELIST *from; //Previous address - NOTELIST *to; //Next address - - long x; //Position - unsigned char length; //Sound length - unsigned char y; //Sound height - unsigned char volume; //Volume - unsigned char pan; -}; - -//Track data * 8 -struct TRACKDATA -{ - unsigned short freq; //Frequency (1000 is default) - unsigned char wave_no; //Waveform No. - signed char pipi; - unsigned short note_num; //Number of notes - - NOTELIST *note_p; - NOTELIST *note_list; -}; - -//Unique information held in songs -struct MUSICINFO -{ - unsigned short wait; - BOOL loaded; - BOOL playing; - unsigned char line; //Number of lines in one measure - unsigned char dot; //Number of dots per line - unsigned short alloc_note; //Number of allocated notes - long repeat_x; //Repeat - long end_x; //End of song (Return to repeat) - TRACKDATA tdata[16]; -}; - BOOL MakeOrganyaWave(signed char track, signed char wave_no, signed char pipi); void OrganyaPlayData(); void SetPlayPointer(long x); -void LoadOrganya(const char *name); +BOOL LoadOrganya(const char *name); void SetOrganyaPosition(unsigned int x); unsigned int GetOrganyaPosition(); void PlayOrganyaMusic(); BOOL ChangeOrganyaVolume(signed int volume); void StopOrganyaMusic(); void SetOrganyaFadeout(); -void OrganyaStartTimer(unsigned int wait); -void OrganyaEndTimer(); -void StartOrganya(); +BOOL StartOrganya(LPDIRECTSOUND lpDS, const char *wave_filename); void EndOrganya(); diff --git a/src/Sound.cpp b/src/Sound.cpp index 973c78d9..556dfc87 100644 --- a/src/Sound.cpp +++ b/src/Sound.cpp @@ -1,6 +1,15 @@ // Some of the original source code for this file can be found here: // https://github.com/shbow/organya/blob/master/source/Sound.cpp +/* +TODO - Code style +Pixel's code was *extremely* Windows-centric, to the point of using +things like ZeroMemory and LPCSTR instead of standard things like +memset and const char*. For now, the decompilation is accurate despite +not using these since they're just macros that evaluate to the portable +equivalents. +*/ + #include "Sound.h" #include @@ -8,353 +17,394 @@ #include #include -#include "SDL.h" +#include #include "WindowsWrapper.h" +#include "CommonDefines.h" #include "Organya.h" #include "PixTone.h" +#include "Tags.h" #define FREQUENCY 44100 -#ifdef RASPBERRY_PI -#define STREAM_SIZE 0x400 // Larger buffer to prevent stutter -#else -#define STREAM_SIZE 0x100 // FREQUENCY/200 rounded to the nearest power of 2 (SDL2 *needs* a power-of-2 buffer size) -#endif +LPDIRECTSOUND lpDS; // DirectSoundオブジェクト (DirectSound object) +LPDIRECTSOUNDBUFFER lpPRIMARYBUFFER; // 一時バッファ (Temporary buffer) +LPDIRECTSOUNDBUFFER lpSECONDARYBUFFER[SE_MAX]; -#define clamp(x, y, z) (((x) > (z)) ? (z) : ((x) < (y)) ? (y) : (x)) - -//Audio device -SDL_AudioDeviceID audioDevice; - -//Keep track of all existing sound buffers -SOUNDBUFFER *soundBuffers; - -//Sound buffer code -SOUNDBUFFER::SOUNDBUFFER(size_t bufSize) +// DirectSoundの開始 (Starting DirectSound) +BOOL InitDirectSound(HWND hwnd) { - //Lock audio buffer - SDL_LockAudioDevice(audioDevice); + int i; + DSBUFFERDESC1 dsbd; - //Set parameters - size = bufSize; - - playing = false; - looping = false; - looped = false; - - frequency = 0.0; - volume = 1.0; - volume_l = 1.0; - volume_r = 1.0; - samplePosition = 0.0; - - //Create waveform buffer - data = new unsigned char[bufSize]; - memset(data, 0x80, bufSize); - - //Add to buffer list - this->next = soundBuffers; - soundBuffers = this; - - //Unlock audio buffer - SDL_UnlockAudioDevice(audioDevice); -} - -SOUNDBUFFER::~SOUNDBUFFER() -{ - //Lock audio buffer - SDL_LockAudioDevice(audioDevice); - - //Free buffer - if (data) - delete[] data; - - //Remove from buffer list - for (SOUNDBUFFER **soundBuffer = &soundBuffers; *soundBuffer != NULL; soundBuffer = &(*soundBuffer)->next) + // DirectDrawの初期化 (DirectDraw initialization) + if (DirectSoundCreate(NULL, &lpDS, NULL) != DS_OK) { - if (*soundBuffer == this) - { - *soundBuffer = this->next; - break; - } - } - - //Unlock audio buffer - SDL_UnlockAudioDevice(audioDevice); -} - -void SOUNDBUFFER::Release() -{ - //TODO: find a better and more stable(?) way to handle this function - delete this; -} - -void SOUNDBUFFER::Lock(unsigned char **outBuffer, size_t *outSize) -{ - SDL_LockAudioDevice(audioDevice); - - if (outBuffer != NULL) - *outBuffer = data; - - if (outSize != NULL) - *outSize = size; -} - -void SOUNDBUFFER::Unlock() -{ - SDL_UnlockAudioDevice(audioDevice); -} - -void SOUNDBUFFER::SetCurrentPosition(unsigned long dwNewPosition) -{ - SDL_LockAudioDevice(audioDevice); - samplePosition = dwNewPosition; - SDL_UnlockAudioDevice(audioDevice); -} - -void SOUNDBUFFER::SetFrequency(unsigned long dwFrequency) -{ - SDL_LockAudioDevice(audioDevice); - frequency = (double)dwFrequency; - SDL_UnlockAudioDevice(audioDevice); -} - -float MillibelToVolume(long lVolume) -{ - //Volume is in hundredths of decibels, from 0 to -10000 - lVolume = clamp(lVolume, (long)-10000, (long)0); - return (float)pow(10.0, lVolume / 2000.0); -} - -void SOUNDBUFFER::SetVolume(long lVolume) -{ - SDL_LockAudioDevice(audioDevice); - volume = MillibelToVolume(lVolume); - SDL_UnlockAudioDevice(audioDevice); -} - -void SOUNDBUFFER::SetPan(long lPan) -{ - SDL_LockAudioDevice(audioDevice); - volume_l = MillibelToVolume(-lPan); - volume_r = MillibelToVolume(lPan); - SDL_UnlockAudioDevice(audioDevice); -} - -void SOUNDBUFFER::Play(bool bLooping) -{ - SDL_LockAudioDevice(audioDevice); - playing = true; - looping = bLooping; - SDL_UnlockAudioDevice(audioDevice); -} - -void SOUNDBUFFER::Stop() -{ - SDL_LockAudioDevice(audioDevice); - playing = false; - SDL_UnlockAudioDevice(audioDevice); -} - -void SOUNDBUFFER::Mix(float *buffer, size_t frames) -{ - if (!playing) //This sound buffer isn't playing - return; - - for (size_t i = 0; i < frames; ++i) - { - const double freqPosition = frequency / FREQUENCY; //This is added to position at the end - - //Get the in-between sample this is (linear interpolation) - const float sample1 = ((looped || ((size_t)samplePosition) >= 1) ? data[(size_t)samplePosition] : 128.0f); - const float sample2 = ((looping || (((size_t)samplePosition) + 1) < size) ? data[(((size_t)samplePosition) + 1) % size] : 128.0f); - - //Interpolate sample - const float subPos = (float)fmod(samplePosition, 1.0); - const float sampleA = sample1 + (sample2 - sample1) * subPos; - - //Convert sample to float32 - const float sampleConvert = (sampleA - 128.0f) / 128.0f; - - //Mix - *buffer++ += (float)(sampleConvert * volume * volume_l); - *buffer++ += (float)(sampleConvert * volume * volume_r); - - //Increment position - samplePosition += freqPosition; - - if (samplePosition >= size) - { - if (looping) - { - samplePosition = fmod(samplePosition, size); - looped = true; - } - else - { - samplePosition = 0.0; - playing = false; - looped = false; - break; - } - } - } -} - -//Sound mixer -void AudioCallback(void *userdata, Uint8 *stream, int len) -{ - (void)userdata; - - float *buffer = (float*)stream; - const size_t frames = len / (sizeof(float) * 2); - - //Clear stream - for (size_t i = 0; i < frames * 2; ++i) - buffer[i] = 0.0f; - - //Mix sounds to primary buffer - for (SOUNDBUFFER *sound = soundBuffers; sound != NULL; sound = sound->next) - sound->Mix(buffer, frames); -} - -//Sound things -SOUNDBUFFER* lpSECONDARYBUFFER[SE_MAX]; - -BOOL InitDirectSound() -{ - //Init sound - SDL_InitSubSystem(SDL_INIT_AUDIO); - - //Open audio device - SDL_AudioSpec want, have; - - //Set specifications we want - SDL_memset(&want, 0, sizeof(want)); - want.freq = FREQUENCY; - want.format = AUDIO_F32; - want.channels = 2; - want.samples = STREAM_SIZE; - want.callback = AudioCallback; - - audioDevice = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0); - - if (audioDevice == 0) - { - printf("Failed to open audio device\nSDL Error: %s\n", SDL_GetError()); + lpDS = NULL; + StartOrganya(lpDS, "Org\\Wave.dat"); return FALSE; } - //Unpause audio device - SDL_PauseAudioDevice(audioDevice, 0); + lpDS->SetCooperativeLevel(hwnd, DSSCL_EXCLUSIVE); - for (unsigned int i = 0; i < SE_MAX; ++i) + // 一次バッファの初期化 (Initializing the primary buffer) + ZeroMemory(&dsbd, sizeof(DSBUFFERDESC1)); + dsbd.dwSize = sizeof(DSBUFFERDESC1); + dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME; + lpDS->CreateSoundBuffer((DSBUFFERDESC*)&dsbd, &lpPRIMARYBUFFER, NULL); + + for (i = 0; i < SE_MAX; i++) lpSECONDARYBUFFER[i] = NULL; - //Start organya - StartOrganya(); + StartOrganya(lpDS, "Org\\Wave.dat"); + return TRUE; } -void EndDirectSound() +// DirectSoundの終了 (Exit DirectSound) +void EndDirectSound(void) { + int i; + + if (lpDS == NULL) + return; + EndOrganya(); - for (unsigned int i = 0; i < SE_MAX; ++i) - if (lpSECONDARYBUFFER[i]) + for (i = 0; i < SE_MAX; i++) + if (lpSECONDARYBUFFER[i] != NULL) lpSECONDARYBUFFER[i]->Release(); - SDL_QuitSubSystem(SDL_INIT_AUDIO); + if (lpPRIMARYBUFFER != NULL) + lpPRIMARYBUFFER->Release(); - SDL_CloseAudioDevice(audioDevice); + if (lpDS != NULL) + lpDS->Release(); + lpDS = NULL; +} + +// サウンドの設定 (Sound settings) +BOOL InitSoundObject(LPCSTR resname, int no) +{ + HRSRC hrscr; + DSBUFFERDESC1 dsbd; + DWORD *lpdword; // リソースのアドレス (Resource address) + + if (lpDS == NULL) + return TRUE; + + // リソースの検索 (Search for resources) + if ((hrscr = FindResourceA(NULL, resname, "WAVE")) == NULL) + return FALSE; + + // リソースのアドレスを取得 (Get resource address) + lpdword = (DWORD*)LockResource(LoadResource(NULL, hrscr)); + + // 二次バッファの生成 (Create secondary buffer) + ZeroMemory(&dsbd, sizeof(DSBUFFERDESC1)); + dsbd.dwSize = sizeof(DSBUFFERDESC1); + dsbd.dwFlags = DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; + dsbd.dwBufferBytes = *(DWORD*)((BYTE*)lpdword+0x36); // WAVEデータのサイズ (WAVE data size) + dsbd.lpwfxFormat = (LPWAVEFORMATEX)(lpdword+5); + + if (lpDS->CreateSoundBuffer((DSBUFFERDESC*)&dsbd, &lpSECONDARYBUFFER[no], NULL) != DS_OK) + return FALSE; + + LPVOID lpbuf1, lpbuf2; + DWORD dwbuf1, dwbuf2; + + // 二次バッファのロック (Secondary buffer lock) + lpSECONDARYBUFFER[no]->Lock(0, *(DWORD*)((BYTE*)lpdword+0x36), &lpbuf1, &dwbuf1, &lpbuf2, &dwbuf2, 0); + + // 音源データの設定 (Sound source data settings) + CopyMemory(lpbuf1, (BYTE*)lpdword+0x3A, dwbuf1); + + if (dwbuf2 != 0) + CopyMemory(lpbuf2, (BYTE*)lpdword+0x3A+dwbuf1, dwbuf2); + + // 二次バッファのロック解除 (Unlock secondary buffer) + lpSECONDARYBUFFER[no]->Unlock(lpbuf1, dwbuf1, lpbuf2, dwbuf2); + + return TRUE; +} + +BOOL LoadSoundObject(LPCSTR file_name, int no) +{ + char path[PATH_LENGTH]; + DWORD i; + DWORD file_size = 0; + char check_box[58]; + FILE *fp; + HANDLE hFile; + + sprintf(path, "%s\\%s", gModulePath, file_name); + + if (lpDS == NULL) + return TRUE; + + hFile = CreateFileA(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return FALSE; + + file_size = GetFileSize(hFile, NULL); + CloseHandle(hFile); + + if ((fp = fopen(path, "rb")) == NULL) + return FALSE; + + for (i = 0; i < 58; i++) + fread(&check_box[i], sizeof(char), 1, fp); + + if (check_box[0] != 'R') + return FALSE; + if (check_box[1] != 'I') + return FALSE; + if (check_box[2] != 'F') + return FALSE; + if (check_box[3] != 'F') + return FALSE; + + DWORD *wp; + wp = (DWORD*)malloc(file_size); // ファイルのワークスペースを作る (Create a file workspace) + fseek(fp, 0, SEEK_SET); + + for (i = 0; i < file_size; i++) + fread((BYTE*)wp+i, sizeof(BYTE), 1, fp); + + fclose(fp); + + // セカンダリバッファの生成 (Create secondary buffer) + DSBUFFERDESC1 dsbd; + ZeroMemory(&dsbd, sizeof(DSBUFFERDESC1)); + dsbd.dwSize = sizeof(DSBUFFERDESC1); + dsbd.dwFlags = DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; + dsbd.dwBufferBytes = *(DWORD*)((BYTE*)wp+0x36); // WAVEデータのサイズ (WAVE data size) + dsbd.lpwfxFormat = (LPWAVEFORMATEX)(wp+5); + + if (lpDS->CreateSoundBuffer((DSBUFFERDESC*)&dsbd, &lpSECONDARYBUFFER[no], NULL) != DS_OK) + { +#ifdef FIX_BUGS + free(wp); // The updated Organya source code includes this fix +#endif + return FALSE; + } + + LPVOID lpbuf1, lpbuf2; + DWORD dwbuf1, dwbuf2; + + HRESULT hr; + hr = lpSECONDARYBUFFER[no]->Lock(0, *(DWORD*)((BYTE*)wp+0x36), &lpbuf1, &dwbuf1, &lpbuf2, &dwbuf2, 0); + + if (hr != DS_OK) + { +#ifdef FIX_BUGS + free(wp); // The updated Organya source code includes this fix +#endif + return FALSE; + } + + CopyMemory(lpbuf1, (BYTE*)wp+0x3A, dwbuf1); // +3aはデータの頭 (+ 3a is the head of the data) + + if (dwbuf2 != 0) + CopyMemory(lpbuf2, (BYTE*)wp+0x3A+dwbuf1, dwbuf2); + + lpSECONDARYBUFFER[no]->Unlock(lpbuf1, dwbuf1, lpbuf2, dwbuf2); + + free(wp); + + return TRUE; } -//Sound effects playing void PlaySoundObject(int no, int mode) { - if (lpSECONDARYBUFFER[no]) + if (lpDS == NULL) + return; + + if (lpSECONDARYBUFFER[no] != NULL) { switch (mode) { - case 0: + case 0: // 停止 (Stop) lpSECONDARYBUFFER[no]->Stop(); break; - case 1: + case 1: // 再生 (Playback) lpSECONDARYBUFFER[no]->Stop(); lpSECONDARYBUFFER[no]->SetCurrentPosition(0); - lpSECONDARYBUFFER[no]->Play(false); + lpSECONDARYBUFFER[no]->Play(0, 0, 0); break; - case -1: - lpSECONDARYBUFFER[no]->Play(true); + case -1:// ループ再生 (Loop playback) + lpSECONDARYBUFFER[no]->Play(0, 0, DSBPLAY_LOOPING); break; } } } -void ChangeSoundFrequency(int no, unsigned long rate) +void ChangeSoundFrequency(int no, DWORD rate) // 100がMIN9999がMAXで2195?がノーマル (100 is MIN, 9999 is MAX, and 2195 is normal) { + if (lpDS == NULL) + return; + lpSECONDARYBUFFER[no]->SetFrequency((rate * 10) + 100); } -void ChangeSoundVolume(int no, long volume) +void ChangeSoundVolume(int no, long volume) // 300がMAXで300がノーマル (300 is MAX and 300 is normal) { + if (lpDS == NULL) + return; + lpSECONDARYBUFFER[no]->SetVolume((volume - 300) * 8); } -void ChangeSoundPan(int no, long pan) +void ChangeSoundPan(int no, long pan) // 512がMAXで256がノーマル (512 is MAX and 256 is normal) { + if (lpDS == NULL) + return; + lpSECONDARYBUFFER[no]->SetPan((pan - 256) * 10); } int MakePixToneObject(const PIXTONEPARAMETER *ptp, int ptp_num, int no) { - int sample_count = 0; - for (int i = 0; i < ptp_num; ++i) + typedef struct WavHeader { - if (ptp[i].size > sample_count) - sample_count = ptp[i].size; + char riff_header[4]; + unsigned long wav_size; + char wav_header[4]; + char fmt_header[4]; + unsigned long fmt_chunk_size; + unsigned short audio_format; + unsigned short num_channels; + unsigned long sample_rate; + unsigned long byte_rate; + unsigned short sample_alignment; + unsigned short bit_depth; + char data_header[4]; + unsigned long data_bytes; + } WavHeader; + + int i; + int j; + DSBUFFERDESC1 dsbd; + WavHeader wav_header; + const PIXTONEPARAMETER *ptp_pointer; + int sample_count; + unsigned char *pcm_buffer; + unsigned char *mixed_pcm_buffer; + + if (lpDS == NULL) + return 0; + + const char *riff = "RIFF"; + const char *fmt = "fmt "; + const char *wave = "WAVE"; + const char *data = "data"; + + wav_header.bit_depth = 8; + wav_header.sample_rate = 22050; + wav_header.num_channels = 1; + wav_header.audio_format = WAVE_FORMAT_PCM; + wav_header.fmt_chunk_size = 16; + memcpy(wav_header.riff_header, riff, 4); + memcpy(wav_header.fmt_header, fmt, 4); + memcpy(wav_header.wav_header, wave, 4); + memcpy(wav_header.data_header, data, 4); + wav_header.sample_alignment = (wav_header.bit_depth / 8) * wav_header.num_channels; + wav_header.byte_rate = (wav_header.bit_depth / 8) * wav_header.num_channels * wav_header.sample_rate; + wav_header.data_bytes = wav_header.sample_alignment * ptp->size; + wav_header.wav_size = wav_header.data_bytes + 36; + + ptp_pointer = ptp; + sample_count = 0; + + for (i = 0; i < ptp_num; i++) + { + if (ptp_pointer->size > sample_count) + sample_count = ptp_pointer->size; + + ++ptp_pointer; + } + + ZeroMemory(&dsbd, sizeof(DSBUFFERDESC1)); + dsbd.dwSize = sizeof(DSBUFFERDESC1); + dsbd.dwFlags = DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY; + dsbd.dwBufferBytes = sample_count; + dsbd.lpwfxFormat = (WAVEFORMATEX*)&wav_header.audio_format; + + if (lpDS->CreateSoundBuffer((DSBUFFERDESC*)&dsbd, &lpSECONDARYBUFFER[no], 0) != DS_OK) + return -1; + + pcm_buffer = mixed_pcm_buffer = NULL; + + pcm_buffer = (unsigned char*)malloc(sample_count); + mixed_pcm_buffer = (unsigned char*)malloc(sample_count); + + if (pcm_buffer == NULL || mixed_pcm_buffer == NULL) + { + if (pcm_buffer != NULL) + free(pcm_buffer); + + if (mixed_pcm_buffer != NULL) + free(mixed_pcm_buffer); + + return -1; } - unsigned char *pcm_buffer = (unsigned char*)malloc(sample_count); - unsigned char *mixed_pcm_buffer = (unsigned char*)malloc(sample_count); memset(pcm_buffer, 0x80, sample_count); memset(mixed_pcm_buffer, 0x80, sample_count); - for (int i = 0; i < ptp_num; ++i) + ptp_pointer = ptp; + + for (i = 0; i < ptp_num; i++) { - if (!MakePixelWaveData(&ptp[i], pcm_buffer)) + if (!MakePixelWaveData(ptp_pointer, pcm_buffer)) { - free(pcm_buffer); - free(mixed_pcm_buffer); + if (pcm_buffer) + free(pcm_buffer); + + if (mixed_pcm_buffer) + free(mixed_pcm_buffer); + return -1; } - for (int j = 0; j < ptp[i].size; ++j) + for (j = 0; j < ptp_pointer->size; j++) { if (pcm_buffer[j] + mixed_pcm_buffer[j] - 0x100 < -0x7F) mixed_pcm_buffer[j] = 0; else if (pcm_buffer[j] + mixed_pcm_buffer[j] - 0x100 > 0x7F) mixed_pcm_buffer[j] = 0xFF; else - mixed_pcm_buffer[j] += pcm_buffer[j] + -0x80; + mixed_pcm_buffer[j] = mixed_pcm_buffer[j] + pcm_buffer[j] - 0x80; } + + ++ptp_pointer; } - lpSECONDARYBUFFER[no] = new SOUNDBUFFER(sample_count); + // Maybe this used to be something to prevent audio popping? + mixed_pcm_buffer[0] = mixed_pcm_buffer[0]; + mixed_pcm_buffer[sample_count - 1] = mixed_pcm_buffer[sample_count - 1]; - unsigned char *buf; - lpSECONDARYBUFFER[no]->Lock(&buf, NULL); - memcpy(buf, mixed_pcm_buffer, sample_count); - lpSECONDARYBUFFER[no]->Unlock(); - lpSECONDARYBUFFER[no]->SetFrequency(22050); + LPVOID lpbuf1, lpbuf2; + DWORD dwbuf1, dwbuf2; - free(pcm_buffer); - free(mixed_pcm_buffer); + lpSECONDARYBUFFER[no]->Lock(0, sample_count, &lpbuf1, &dwbuf1, &lpbuf2, &dwbuf2, 0); + + CopyMemory(lpbuf1, mixed_pcm_buffer, dwbuf1); + + if (dwbuf2 != 0) + CopyMemory(lpbuf2, mixed_pcm_buffer + dwbuf1, dwbuf2); + + lpSECONDARYBUFFER[no]->Unlock(lpbuf1, dwbuf1, lpbuf2, dwbuf2); + + if (pcm_buffer != NULL) + free(pcm_buffer); + + if (mixed_pcm_buffer != NULL) + free(mixed_pcm_buffer); return sample_count; } diff --git a/src/Sound.h b/src/Sound.h index 3e5a9583..7d396dbb 100644 --- a/src/Sound.h +++ b/src/Sound.h @@ -2,52 +2,19 @@ #include +#include + #include "WindowsWrapper.h" #include "PixTone.h" -class SOUNDBUFFER -{ - public: - SOUNDBUFFER(size_t bufSize); - ~SOUNDBUFFER(); - - void Release(); - - void Lock(unsigned char **buffer, size_t *size); - void Unlock(); - - void SetCurrentPosition(unsigned long dwNewPosition); - void SetFrequency(unsigned long dwFrequency); - void SetVolume(long lVolume); - void SetPan(long lPan); - void Play(bool bLooping); - void Stop(); - - void Mix(float *buffer, size_t frames); - - SOUNDBUFFER *next; - - private: - unsigned char *data; - size_t size; - - bool playing; - bool looping; - bool looped; - - double frequency; - double volume; - double volume_l; - double volume_r; - double samplePosition; -}; - #define SE_MAX 160 // According to the Organya source code release, this is the real name for this constant -extern SOUNDBUFFER* lpSECONDARYBUFFER[SE_MAX]; -BOOL InitDirectSound(); -void EndDirectSound(); +extern LPDIRECTSOUND lpDS; +extern LPDIRECTSOUNDBUFFER lpSECONDARYBUFFER[SE_MAX]; + +BOOL InitDirectSound(HWND hwnd); +void EndDirectSound(void); void PlaySoundObject(int no, int mode); void ChangeSoundFrequency(int no, unsigned long rate); void ChangeSoundVolume(int no, long volume);