415 lines
10 KiB
C++
415 lines
10 KiB
C++
// 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 <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <dsound.h>
|
|
|
|
#include "WindowsWrapper.h"
|
|
|
|
#include "Organya.h"
|
|
#include "PixTone.h"
|
|
#include "Tags.h"
|
|
|
|
#define FREQUENCY 44100
|
|
|
|
LPDIRECTSOUND lpDS; // DirectSoundオブジェクト (DirectSound object)
|
|
LPDIRECTSOUNDBUFFER lpPRIMARYBUFFER; // 一時バッファ (Temporary buffer)
|
|
LPDIRECTSOUNDBUFFER lpSECONDARYBUFFER[SE_MAX];
|
|
|
|
// DirectSoundの開始 (Starting DirectSound)
|
|
BOOL InitDirectSound(HWND hwnd)
|
|
{
|
|
int i;
|
|
DSBUFFERDESC1 dsbd;
|
|
|
|
// DirectDrawの初期化 (DirectDraw initialization)
|
|
if (DirectSoundCreate(NULL, &lpDS, NULL) != DS_OK)
|
|
{
|
|
lpDS = NULL;
|
|
#ifndef FIX_BUGS
|
|
// This makes absolutely no sense here
|
|
StartOrganya(lpDS, "Org\\Wave.dat");
|
|
#endif
|
|
return FALSE;
|
|
}
|
|
|
|
lpDS->SetCooperativeLevel(hwnd, DSSCL_EXCLUSIVE);
|
|
|
|
// 一次バッファの初期化 (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;
|
|
|
|
StartOrganya(lpDS, "Org\\Wave.dat");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
// DirectSoundの終了 (Exit DirectSound)
|
|
void EndDirectSound(void)
|
|
{
|
|
int i;
|
|
|
|
if (lpDS == NULL)
|
|
return;
|
|
|
|
EndOrganya();
|
|
|
|
for (i = 0; i < SE_MAX; i++)
|
|
if (lpSECONDARYBUFFER[i] != NULL)
|
|
lpSECONDARYBUFFER[i]->Release();
|
|
|
|
if (lpPRIMARYBUFFER != NULL)
|
|
lpPRIMARYBUFFER->Release();
|
|
|
|
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[MAX_PATH];
|
|
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;
|
|
}
|
|
|
|
void PlaySoundObject(int no, int mode)
|
|
{
|
|
if (lpDS == NULL)
|
|
return;
|
|
|
|
if (lpSECONDARYBUFFER[no] != NULL)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case 0: // 停止 (Stop)
|
|
lpSECONDARYBUFFER[no]->Stop();
|
|
break;
|
|
|
|
case 1: // 再生 (Playback)
|
|
lpSECONDARYBUFFER[no]->Stop();
|
|
lpSECONDARYBUFFER[no]->SetCurrentPosition(0);
|
|
lpSECONDARYBUFFER[no]->Play(0, 0, 0);
|
|
break;
|
|
|
|
case -1:// ループ再生 (Loop playback)
|
|
lpSECONDARYBUFFER[no]->Play(0, 0, DSBPLAY_LOOPING);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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) // 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) // 512がMAXで256がノーマル (512 is MAX and 256 is normal)
|
|
{
|
|
if (lpDS == NULL)
|
|
return;
|
|
|
|
lpSECONDARYBUFFER[no]->SetPan((pan - 256) * 10);
|
|
}
|
|
|
|
// TODO - The stack frame for this function is inaccurate
|
|
int MakePixToneObject(const PIXTONEPARAMETER *ptp, int ptp_num, int no)
|
|
{
|
|
// For some reason, this function creates an entire WAV file header,
|
|
// when it only needs a WAVEFORMATEX
|
|
typedef struct WavHeader
|
|
{
|
|
char riff[4];
|
|
unsigned long wav_size;
|
|
char wave[4];
|
|
char fmt[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[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, riff, 4);
|
|
memcpy(wav_header.fmt, fmt, 4);
|
|
memcpy(wav_header.wave, wave, 4);
|
|
memcpy(wav_header.data, 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;
|
|
}
|
|
|
|
memset(pcm_buffer, 0x80, sample_count);
|
|
memset(mixed_pcm_buffer, 0x80, sample_count);
|
|
|
|
ptp_pointer = ptp;
|
|
|
|
for (i = 0; i < ptp_num; i++)
|
|
{
|
|
if (!MakePixelWaveData(ptp_pointer, pcm_buffer))
|
|
{
|
|
if (pcm_buffer)
|
|
free(pcm_buffer);
|
|
|
|
if (mixed_pcm_buffer)
|
|
free(mixed_pcm_buffer);
|
|
|
|
return -1;
|
|
}
|
|
|
|
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] = mixed_pcm_buffer[j] + pcm_buffer[j] - 0x80;
|
|
}
|
|
|
|
++ptp_pointer;
|
|
}
|
|
|
|
// 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];
|
|
|
|
LPVOID lpbuf1, lpbuf2;
|
|
DWORD dwbuf1, dwbuf2;
|
|
|
|
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;
|
|
}
|