shithub: cstory

ref: 1b9c2d36629a7df8671f273c33bd68f33ddfe6e6
dir: /src/Organya.cpp/

View raw version
// THIS IS DECOMPILED PROPRIETARY CODE - USE AT YOUR OWN RISK.
//
// The original code belongs to Daisuke "Pixel" Amaya.
//
// Modifications and custom code are under the MIT licence.
// See LICENCE.txt for details.

// Some of the original source code for this file can be found here:
// https://github.com/shbow/organya/blob/master/source/OrgFile.cpp
// https://github.com/shbow/organya/blob/master/source/OrgPlay.cpp
// https://github.com/shbow/organya/blob/master/source/Sound.cpp
// https://github.com/shbow/organya/blob/master/source/WinTimer.cpp

#include "Organya.h"

#include <stddef.h>
#include <stdlib.h>
#include <string.h>

#ifdef FIX_BUGS
// The original source code forgot to set this (you can tell because, in the original EXE,
// the DSBUFFERDESC structs in this file have a different size to the ones in Sound.cpp)
#define DIRECTSOUND_VERSION 0x500
#endif
#include <dsound.h>

#include "WindowsWrapper.h"

#include "Sound.h"

#define PANDUMMY 0xFF
#define VOLDUMMY 0xFF
#define KEYDUMMY 0xFF

#define ALLOCNOTE 4096

#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[MAXTRACK];
} 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 InitMusicData(const char *path);
} ORGDATA;

LPDIRECTSOUNDBUFFER lpORGANBUFFER[8][8][2] = {NULL};

/////////////////////////////////////////////
//■オルガーニャ■■■■■■■■■■■■/////// (Organya)
/////////////////////

// Wave playing and loading
typedef struct
{
	short wave_size;
	short oct_par;
	short oct_size;
} OCTWAVE;

OCTWAVE oct_wave[8] =
{
	{ 256,  1,  4 }, // 0 Oct
	{ 256,  2,  8 }, // 1 Oct
	{ 128,  4, 12 }, // 2 Oct
	{ 128,  8, 16 }, // 3 Oct
	{  64, 16, 20 }, // 4 Oct
	{  32, 32, 24 }, // 5 Oct
	{  16, 64, 28 }, // 6 Oct
	{   8,128, 32 }, // 7 Oct
};

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)
{
	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;

	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(dsbd));

			dsbd.dwSize = sizeof(dsbd);
			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
			wp = (BYTE*)malloc(data_size);
			wp_sub = wp;
			wav_tp = 0;

			for (i = 0; i < data_size; i++)
			{
				work = *(wavep + wav_tp);
				work += 0x80;

				*wp_sub = (BYTE)work;

				wav_tp += 0x100 / wave_size;
				if (wav_tp > 0xFF)
					wav_tp -= 0x100;

				wp_sub++;
			}

			// データの転送 (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_MAJOR_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);
			free(wp);
		}
	}

	return TRUE;
}

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));	// 1000を+αのデフォルト値とする (1000 is the default value for + α)
}

BOOL g_mute[MAXTRACK];	// Used by the debug Mute menu
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)	// 512がMAXで256がノーマル (512 is MAX and 256 is normal)
{
	if (lpDS == NULL)
		return;

	if (old_key[track] != KEYDUMMY)
		lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->SetPan((pan_tbl[pan] - 0x100) * 10);
}

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] != KEYDUMMY)
		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:	// 停止 (Stop)
				if (old_key[track] != 0xFF)
				{
					lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Stop();
					lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->SetCurrentPosition(0);
				}
				break;

			case 1: // 再生 (Playback)
				break;

			case 2:	// 歩かせ停止 (Stop playback)
				if (old_key[track] != 0xFF)
				{
					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)	// 新規鳴らす (New sound)
				{
					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)	// 同じ音 (Same sound)
				{
					// 今なっているのを歩かせ停止 (Stop playback now)
					lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Play(0, 0, 0);
					key_twin[track]++;
					if (key_twin[track] > 1)
						key_twin[track] = 0;
					lpORGANBUFFER[track][key / 12][key_twin[track]]->Play(0, 0, DSBPLAY_LOOPING);
				}
				else	// 違う音を鳴らすなら (If you make a different sound)
				{
					lpORGANBUFFER[track][old_key[track] / 12][key_twin[track]]->Play(0, 0, 0);	// 今なっているのを歩かせ停止 (Stop playback now)
					key_twin[track]++;
					if (key_twin[track] > 1)
						key_twin[track] = 0;
					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;
		}
	}
}

// オルガーニャオブジェクトを開放 (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)
		{
			lpORGANBUFFER[track][i][0]->Release();
			lpORGANBUFFER[track][i][0] = NULL;
		}
		if (lpORGANBUFFER[track][i][1] != NULL)
		{
			lpORGANBUFFER[track][i][1]->Release();
			lpORGANBUFFER[track][i][1] = NULL;
		}
	}
}

// 波形データをロード (Load waveform data)
signed char wave_data[100][0x100];

BOOL InitWaveData100(void)
{
	HRSRC hrscr;
	DWORD *lpdword;	// リソースのアドレス (Resource address)

	if (lpDS == NULL)
		return FALSE;

	// リソースの検索 (Search for resources)
	hrscr = FindResourceA(NULL, "WAVE100", "WAVE");

	if (hrscr == NULL)
		return FALSE;

	// リソースのアドレスを取得 (Get resource address)
	lpdword = (DWORD*)LockResource(LoadResource(NULL, hrscr));
	memcpy(wave_data, lpdword, 100 * 0x100);

	return TRUE;
}

// 波形を100個の中から選択して作成 (Select from 100 waveforms to create)
BOOL MakeOrganyaWave(signed char track, signed char wave_no, signed char pipi)
{
	if (lpDS == NULL)
		return FALSE;

	if (wave_no > 99)
		return FALSE;

	ReleaseOrganyaObject(track);
	MakeSoundObject8(wave_data[wave_no], track, pipi);

	return TRUE;
}

/////////////////////////////////////////////
//■オルガーニャドラムス■■■■■■■■/////// (Organya drums)
/////////////////////

void ChangeDramFrequency(unsigned char key, signed char track)
{
	if (lpDS == NULL)
		return;

	lpSECONDARYBUFFER[150 + track]->SetFrequency(key * 800 + 100);
}

void ChangeDramPan(unsigned char pan, signed char track)
{
	if (lpDS == NULL)
		return;

	lpSECONDARYBUFFER[150 + track]->SetPan((pan_tbl[pan] - 0x100) * 10);
}

void ChangeDramVolume(long volume, signed char track)
{
	if (lpDS == NULL)
		return;

	lpSECONDARYBUFFER[150 + track]->SetVolume((volume - 0xFF) * 8);
}

// サウンドの再生 (Play sound)
void PlayDramObject(unsigned char key, int mode, signed char track)
{
	if (lpDS == NULL)
		return;

	if (lpSECONDARYBUFFER[150 + track] != NULL)
	{
		switch (mode)
		{
			case 0:	// 停止 (Stop)
				lpSECONDARYBUFFER[150 + track]->Stop();
				lpSECONDARYBUFFER[150 + track]->SetCurrentPosition(0);
				break;

			case 1:	// 再生 (Playback)
				lpSECONDARYBUFFER[150 + track]->Stop();
				lpSECONDARYBUFFER[150 + track]->SetCurrentPosition(0);
				ChangeDramFrequency(key, track);	// 周波数を設定して (Set the frequency)
				lpSECONDARYBUFFER[150 + track]->Play(0, 0, 0);
				break;

			case 2:	// 歩かせ停止 (Stop playback)
				break;

			case -1:
				break;
		}
	}
}

ORGDATA org_data;

OrgData::OrgData(void)
{
	for (int 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;

	for (int 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)
{
	char str[32];	// Leftover debug junk
	int i;

	if (flag & SETGRID)	// グリッドを有効に (Enable grid)
	{
		info.dot = mi->dot;
		info.line = mi->line;
	}

	if (flag & SETWAIT)
	{
		info.wait = mi->wait;
		itoa(mi->wait, str, 10);	// Leftover debug junk
	}

	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;	 // Just sets info.tdata[i].pipi to itself (SETPIPI already sets pipi, so maybe this line shouldn't be here in the first place)
		}
	}

	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;
		}
	}
}

char pass[7] = "Org-01";
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(NULL, 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);

	// 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);
	}

	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);	// The original code used the wrong types
VOID CALLBACK TimerProc(UINT uTID,UINT uMsg,DWORD_PTR dwUser,DWORD_PTR dwParam1,DWORD_PTR dwParam2);
BOOL QuitMMTimer();

/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/
//グローバル変数 (Global variable)
/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/
UINT ExactTime   = 13;	// 最小精度 (Minimum accuracy)
UINT TimerID;
BOOL bTimer;

/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/
// タイマー精度を設定する。 (Set timer accuracy.)
// この関数はアプリケーション初期化時に一度呼び出す。 (This function is called once when the application is initialized.)
/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/
BOOL InitMMTimer(void)
{
	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 = MMSYSERR_NOERROR;
	ExactTime = dwTimer;

	// タイマーを生成する (Generate timer)
	TimerID = timeSetEvent
	(
		dwTimer,                   // タイマー時間 (Timer time)
		10,                        // 許容できるタイマー精度 (Acceptable timer accuracy)
		TimerProc,                 // コールバックプロシージャ (Callback procedure)
		0,                         // ユーザーがコールバック関数の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;

	bTimer = TRUE;

	return TRUE;
}

/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/
// タイマーのコールバック関数 (Timer callback function)
/*■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■*/
//VOID CALLBACK TimerProc(UINT uTID,UINT uMsg,DWORD dwUser,DWORD dwParam1,DWORD dwParam2)	// The original code used the wrong types
VOID CALLBACK TimerProc(UINT uTID,UINT uMsg,DWORD_PTR dwUser,DWORD_PTR dwParam1,DWORD_PTR dwParam2)
{
	(void)uTID;
	(void)uMsg;
	(void)dwUser;
	(void)dwParam1;
	(void)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(void)
{
	MMRESULT ret;

	if (!bTimer)
		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;

	bTimer = FALSE;

	return TRUE;
}

// Play data
long PlayPos;	// Called 'play_p' in the source code release
NOTELIST *np[MAXTRACK];
long now_leng[MAXMELODY];

int Volume = 100;
int TrackVol[MAXTRACK];
BOOL bFadeout = FALSE;

void OrgData::PlayData(void)
{
	int i;

	// Handle fading out
	if (bFadeout && Volume)
		Volume -= 2;
	if (Volume < 0)
		Volume = 0;

	// メロディの再生 (Play melody)
	for (i = 0; i < MAXMELODY; i++)
	{
		if (np[i] != NULL && PlayPos == np[i]->x)
		{
			if (!g_mute[i] && np[i]->y != KEYDUMMY)	// 音が来た。 (The sound has come.)
			{
				PlayOrganObject(np[i]->y, -1, i, info.tdata[i].freq);
				now_leng[i] = np[i]->length;
			}

			if (np[i]->pan != PANDUMMY)
				ChangeOrganPan(np[i]->y, np[i]->pan, i);
			if (np[i]->volume != VOLDUMMY)
				TrackVol[i] = np[i]->volume;

			np[i] = np[i]->to;	// 次の音符を指す (Points to the next note)
		}

		if (now_leng[i] == 0)
			PlayOrganObject(0, 2, i, info.tdata[i].freq);

		if (now_leng[i] > 0)
			now_leng[i]--;

		if (np[i])
			ChangeOrganVolume(np[i]->y, TrackVol[i] * Volume / 0x7F, i);
	}

	// ドラムの再生 (Drum playback)
	for (i = MAXMELODY; i < MAXTRACK; i++)
	{
		if (np[i] != NULL && PlayPos == np[i]->x)	// 音が来た。 (The sound has come.)
		{
			if (np[i]->y != KEYDUMMY && !g_mute[i])	// ならす (Tame)
				PlayDramObject(np[i]->y, 1, i - MAXMELODY);

			if (np[i]->pan != PANDUMMY)
				ChangeDramPan(np[i]->pan, i - MAXMELODY);
			if (np[i]->volume != VOLDUMMY)
				TrackVol[i] = np[i]->volume;

			np[i] = np[i]->to;	// 次の音符を指す (Points to the next note)
		}

		if (np[i])
			ChangeDramVolume(TrackVol[i] * Volume / 0x7F, i - MAXMELODY);
	}

	// Looping
	PlayPos++;
	if (PlayPos >= info.end_x)
	{
		PlayPos = info.repeat_x;
		SetPlayPointer(PlayPos);
	}
}

void OrgData::SetPlayPointer(long x)
{
	for (int i = 0; i < MAXTRACK; i++)
	{
		np[i] = info.tdata[i].note_list;
		while (np[i] != NULL && np[i]->x < x)
			np[i] = np[i]->to;	// 見るべき音符を設定 (Set note to watch)
	}

	PlayPos = x;
}

// 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
BOOL LoadOrganya(const char *name)
{
	if (lpDS == NULL)
		return FALSE;

	if (!org_data.InitMusicData(name))
		return FALSE;

	Volume = 100;
	bFadeout = 0;

#ifdef FIX_BUGS
	return TRUE;
#else
	return FALSE;	// Err... isn't this meant to be 'TRUE'?
#endif
}

void SetOrganyaPosition(unsigned int x)
{
	if (lpDS == NULL)
		return;

	org_data.SetPlayPointer(x);
	Volume = 100;
	bFadeout = FALSE;
}

unsigned int GetOrganyaPosition(void)
{
	if (lpDS == NULL)
		return 0;

	return PlayPos;
}

void PlayOrganyaMusic(void)
{
	if (lpDS == NULL)
		return;

	QuitMMTimer();
	InitMMTimer();
	StartTimer(org_data.info.wait);
}

BOOL ChangeOrganyaVolume(signed int volume)
{
	if (lpDS == NULL)
		return FALSE;

	if (volume < 0 || volume > 100)
		return FALSE;

	Volume = volume;
	return TRUE;
}

void StopOrganyaMusic(void)
{
	if (lpDS == NULL)
		return;

	// Stop timer
	QuitMMTimer();

	// Stop notes
	for (int i = 0; i < MAXMELODY; i++)
		PlayOrganObject(0, 2, i, 0);

	memset(old_key, 255, sizeof(old_key));
	memset(key_on, 0, sizeof(key_on));
	memset(key_twin, 0, sizeof(key_twin));

	// Put the main thread to sleep for 100 milliseconds... but why?
	// Really, what's the point? All this does is cause an annoying
	// stutter when a new song loads.
	// I'd guess it avoids a race-condition with the Organya thread,
	// but the earlier QuitMMTimer call already disables it.
	Sleep(100);
}

void SetOrganyaFadeout(void)
{
	bFadeout = TRUE;
}

void EndOrganya(void)
{
	if (lpDS == NULL)
		return;

	// End timer
	QuitMMTimer();

	// Release everything related to org
	org_data.ReleaseNote();

	for (int i = 0; i < MAXMELODY; i++)
	{
		PlayOrganObject(0, 0, i, 0);
		ReleaseOrganyaObject(i);
	}
}