Ayameを同梱するためソース管理
@@ -0,0 +1,248 @@ | ||
1 | +/** | |
2 | + * @file VoiceElement.h | |
3 | + * @brief 単一のサウンド管理クラス | |
4 | + * @author Sazanta | |
5 | + */ | |
6 | +#ifndef __VOICEELEMENT_H__ | |
7 | +#define __VOICEELEMENT_H__ | |
8 | +#include <windows.h> | |
9 | + | |
10 | +#define NUM_OF_SUB_VOLUME 0 //!< サブボリュームの数 | |
11 | + | |
12 | +const INT32 cnVoiceElm_VolumeMin = 0; //!< 最小ボリューム値 | |
13 | +const INT32 cnVoiceElm_VolumeMax = 128; //!< 最大ボリューム値 | |
14 | +const INT32 cnVoiceElm_VolumeRange = (cnVoiceElm_VolumeMax - cnVoiceElm_VolumeMin); //!< ボリューム範囲 | |
15 | +const INT32 cnVoiceElm_PanpotCenter = 0; //!< パンセンター値 | |
16 | +const INT32 cnVoiceElm_PanpotMaxR = 128; //!< 右パン最大値 | |
17 | +const INT32 cnVoiceElm_PanpotMaxL = -128; //!< 左パン最大値 | |
18 | +const INT32 cnVoiceElm_PanpotRange = (cnVoiceElm_PanpotMaxR - cnVoiceElm_PanpotMaxL); //!< パン範囲 | |
19 | +const INT32 cnVoiceElm_PanpotHalfRange = (cnVoiceElm_PanpotRange / 2); //!< パン範囲(ハーフ) | |
20 | + | |
21 | + | |
22 | +//! CVoiceElementクラス | |
23 | +/** | |
24 | + * このクラスは、サウンドのフェード・オートパンといった時間変位を行うためのクラスです。<br> | |
25 | + * ポーズ時にフェードを行う際、ボリュームを保存するので、ポーズ解除時に元のボリュームに戻すことが出来ます。<br> | |
26 | + * またマスターボリュームをサポートするための機構も備えています。<br> | |
27 | + * 1つのクラスにつき、1つのボイスを管理することが出来ます。<br> | |
28 | + * 単体でも使用することは出来ますが、通常はサウンドマネージャ等によって管理されます。<br> | |
29 | + * このクラス自体は、インターフェイスを提供するだけですので、実際にはこのクラスから派生したクラスを利用します。<br> | |
30 | + */ | |
31 | +class CVoiceElement | |
32 | +{ | |
33 | + class CFadeValue | |
34 | + { | |
35 | + private: | |
36 | + INT32 m_nNowValue; //!< 現在の値 | |
37 | + INT32 m_nNowTick; //!< 現在のタイム | |
38 | + INT32 m_nTotalTick; //!< 総タイム | |
39 | + INT32 m_nStartVal; //!< 開始値 | |
40 | + INT32 m_nDistVal; //!< 範囲値 | |
41 | + | |
42 | + public: | |
43 | + CFadeValue(void) { | |
44 | + m_nNowTick = 0; | |
45 | + } | |
46 | + ~CFadeValue(void) {} | |
47 | + | |
48 | + bool IsFade(void) const { | |
49 | + return (m_nNowTick > 0); | |
50 | + } | |
51 | + void ResetTime(void) { | |
52 | + m_nNowTick = 0; | |
53 | + } | |
54 | + INT32 GetNowValue(void) const { | |
55 | + return m_nNowValue; | |
56 | + } | |
57 | + void SetNowValue(INT32 nValue) { | |
58 | + m_nNowValue = nValue; | |
59 | + } | |
60 | + void SetParam(INT32 nTick, INT32 nStart, INT32 nEnd) { | |
61 | + m_nNowTick = nTick; | |
62 | + m_nTotalTick = nTick; | |
63 | + m_nStartVal = nEnd; | |
64 | + m_nDistVal = nStart - nEnd; | |
65 | + } | |
66 | + | |
67 | + INT32 GetCalcValue(void) const { | |
68 | + return ((m_nDistVal * m_nNowTick) / m_nTotalTick) + m_nStartVal; | |
69 | + } | |
70 | + bool Run(INT32 nTick, INT32& nValue) { | |
71 | + if (m_nNowTick <= 0) return false; | |
72 | + m_nNowTick -= nTick; | |
73 | + if (m_nNowTick < 0) { | |
74 | + m_nNowTick = 0; | |
75 | + } | |
76 | + nValue = GetCalcValue(); | |
77 | + return true; | |
78 | + } | |
79 | + }; | |
80 | + | |
81 | + protected: | |
82 | + UINT32 m_dwNumber; //!< ボイスナンバー | |
83 | + UINT32 m_dwUnique; //!< 識別ID | |
84 | + bool m_bFinishRequest; //!< フェード後終了要求 | |
85 | + bool m_bPauseRequest; //!< フェード後ポーズ要求 | |
86 | + bool m_bPause; //!< ポーズ中フラグ | |
87 | + bool m_bVolumeFade; //!< ボリュームフェード中フラグ | |
88 | + | |
89 | + CFadeValue m_cVolume; //!< ボリューム | |
90 | +#if defined(NUM_OF_SUB_VOLUME) && (NUM_OF_SUB_VOLUME > 0) | |
91 | + CFadeValue m_cSubVolume[NUM_OF_SUB_VOLUME]; //!< サブボリューム | |
92 | +#endif | |
93 | + CFadeValue m_cPanpot; //!< パンポット | |
94 | + | |
95 | + static INT32 m_snGlobalVol; //!< グローバルボリューム | |
96 | + | |
97 | + protected: | |
98 | + void AbortFade(void); | |
99 | + void InternalFade(INT32 nVolume, INT32 nTick = 0, INT32 nStartVolume = -1); | |
100 | + INT32 InternalGetVolume(void) const; | |
101 | + INT32 CalculateSubVolume(void) const; | |
102 | + void VolumeClamp(INT32& nVolume) const { | |
103 | + nVolume = (nVolume < cnVoiceElm_VolumeMin)? cnVoiceElm_VolumeMin : ((nVolume > cnVoiceElm_VolumeMax)? cnVoiceElm_VolumeMax : nVolume); | |
104 | + } | |
105 | + | |
106 | + virtual void _Release(void) {} | |
107 | + virtual void _SetPan(INT32 nPan) {} | |
108 | + virtual void _SetVolume(INT32 nVolume) {} | |
109 | + virtual void _SetSpeedRate(INT32 nRate) {} | |
110 | + virtual void _Pause(void) {} | |
111 | + virtual bool _Load(const char* pSoundData, UINT32 nDataSize, UINT32 nType) { | |
112 | + return false; | |
113 | + } | |
114 | + virtual bool _Load(const TCHAR* pFileName, UINT32 nType, UINT32 nOffset, UINT32 nDataSize) { | |
115 | + return false; | |
116 | + } | |
117 | + virtual bool _IsPlaying(void) const { | |
118 | + return false; | |
119 | + } | |
120 | + virtual bool _IsFinished(void) const { | |
121 | + return true; | |
122 | + } | |
123 | + virtual void _Play(UINT32 nLoop, UINT32 nLoopInSample = 0, UINT32 nStartPos = 0) {} | |
124 | + virtual void _Stop(void) {} | |
125 | + virtual void _Prefetch(void) {} | |
126 | + virtual void _Predecode(void) {} | |
127 | + | |
128 | + public: | |
129 | + CVoiceElement(void); | |
130 | + //! デストラクタ | |
131 | + virtual ~CVoiceElement(void) {} | |
132 | + | |
133 | + void Run(INT32 nTick); | |
134 | + | |
135 | + //! ボイスナンバーの設定 | |
136 | + void SetNumber(UINT32 dwNumber) { | |
137 | + m_dwNumber = dwNumber; | |
138 | + } | |
139 | + //! ボイスナンバーの取得 | |
140 | + UINT32 GetNumber(void) const { | |
141 | + return m_dwNumber; | |
142 | + } | |
143 | + | |
144 | + //! 識別IDの設定 | |
145 | + void SetUnique(UINT32 dwUnique) { | |
146 | + m_dwUnique = dwUnique; | |
147 | + } | |
148 | + //! 識別IDの取得 | |
149 | + UINT32 GetUnique(void) const { | |
150 | + return m_dwUnique; | |
151 | + } | |
152 | + | |
153 | + //! データの作成 | |
154 | + /** | |
155 | + * データの作成を行います。<br> | |
156 | + */ | |
157 | + bool Load(const char* pSoundData, UINT32 nDataSize, UINT32 nFlag) { | |
158 | + Release(); | |
159 | + return _Load(pSoundData, nDataSize, nFlag); | |
160 | + } | |
161 | + //! データの作成 | |
162 | + /** | |
163 | + * データの作成を行います。<br> | |
164 | + */ | |
165 | + bool Load(const TCHAR* pFileName, UINT32 nFlag, UINT32 nOffset, UINT32 nDataSize) { | |
166 | + Release(); | |
167 | + return _Load(pFileName, nFlag, nOffset, nDataSize); | |
168 | + } | |
169 | + void Release(void); | |
170 | + | |
171 | + //! 再生中かどうかチェック | |
172 | + /** | |
173 | + * 再生中かどうかを取得します。<br> | |
174 | + * | |
175 | + * @return 再生中ならtrue | |
176 | + */ | |
177 | + bool IsPlaying(void) const | |
178 | + { | |
179 | + return _IsPlaying(); | |
180 | + } | |
181 | + //! 停止完了チェック | |
182 | + /** | |
183 | + * 停止が完了したかどうかを取得します。<br> | |
184 | + * | |
185 | + * @return 停止完了ならtrue | |
186 | + */ | |
187 | + bool IsFinished(void) const; | |
188 | + //! フェード中かどうかチェック | |
189 | + /** | |
190 | + * フェード中かどうかを取得します。<br> | |
191 | + * | |
192 | + * @return フェード中ならtrue | |
193 | + */ | |
194 | + bool IsFade(void) const { | |
195 | + return m_cVolume.IsFade(); | |
196 | + } | |
197 | + //! ポーズ中かどうかチェック | |
198 | + /** | |
199 | + * ポーズ中かどうかを取得します。<br> | |
200 | + * | |
201 | + * @return ポーズ中ならtrue | |
202 | + */ | |
203 | + bool IsPause(void) const { | |
204 | + return m_bPause; | |
205 | + } | |
206 | + | |
207 | + //! マスターボリュームの反映 | |
208 | + /** | |
209 | + * マスターボリュームを即時反映させます。<br> | |
210 | + */ | |
211 | + void ReflectMasterVolume(void) { | |
212 | + if (IsFade() == false) { | |
213 | + SetVolume(-1); | |
214 | + } | |
215 | + } | |
216 | + INT32 GetVolume(void) const; | |
217 | + void SetVolume(INT32 nVolume, INT32 nTick = 0, INT32 nStartVolume = -1); | |
218 | + INT32 GetSubVolume(UINT32 nChannel) const; | |
219 | + void SetSubVolume(UINT32 nChannel, INT32 nVolume, INT32 nTick = 0, INT32 nStartVolume = -1); | |
220 | + | |
221 | + INT32 GetPan(void) const; | |
222 | + void SetPan(INT32 nPan, INT32 nTick = 0, INT32 nStartPan = -(cnVoiceElm_PanpotHalfRange + 1)); | |
223 | + | |
224 | + INT32 GetSpeedRate(void) const { | |
225 | + return 4096; | |
226 | + } | |
227 | + void SetSpeedRate(INT32 nRate) { | |
228 | + _SetSpeedRate(nRate); | |
229 | + } | |
230 | + | |
231 | + void Play(UINT32 dwLoop = 0, INT32 nVolume = -1, INT32 nFadeInTick = 0, UINT32 nLoopInSample = 0); | |
232 | + void Stop(INT32 nFadeOutTick = 0); | |
233 | + void Pause(bool bPause, INT32 nFadeTick = 0); | |
234 | + void Prefetch(void) { | |
235 | + _Prefetch(); | |
236 | + } | |
237 | + void Predecode(void) { | |
238 | + _Predecode(); | |
239 | + } | |
240 | + | |
241 | + static void SetGlobalVolume(INT32 nVolume) { | |
242 | + m_snGlobalVol = (nVolume < cnVoiceElm_VolumeMin)? cnVoiceElm_VolumeMin : ((nVolume > cnVoiceElm_VolumeMax)? cnVoiceElm_VolumeMax : nVolume); | |
243 | + } | |
244 | +}; | |
245 | + | |
246 | + | |
247 | +#endif // __VOICEELEMENT_H__ | |
248 | +/* Bottom of VoiceElement.h */ |
@@ -0,0 +1,86 @@ | ||
1 | +/** | |
2 | + * @file VoiceElementMidi.h | |
3 | + * @brief 単一のサウンド管理クラス | |
4 | + * @author Sazanta | |
5 | + */ | |
6 | +#ifndef __VOICEELEMENTMIDI_H__ | |
7 | +#define __VOICEELEMENTMIDI_H__ | |
8 | + | |
9 | +// インクルード | |
10 | +#include "midi.h" | |
11 | +#include "VoiceElement.h" | |
12 | + | |
13 | + | |
14 | +//! CVoiceElementMidiクラス | |
15 | +/** | |
16 | + * このクラスは、CMidiに対してのCVoiceElement操作を提供する派生クラスです。<br> | |
17 | + * MIDIファイルの再生を行います。<br> | |
18 | + */ | |
19 | +class CVoiceElementMidi : public CVoiceElement | |
20 | +{ | |
21 | + protected: | |
22 | + CMidi* m_pMidi; | |
23 | + | |
24 | + void _Release(void) { | |
25 | + if (m_pMidi != NULL) { | |
26 | + m_pMidi->Release(); | |
27 | + m_pMidi = NULL; | |
28 | + } | |
29 | + } | |
30 | + void _SetPan(INT32 nPan) { | |
31 | + if (m_pMidi != NULL) { | |
32 | + m_pMidi->SetMasterPanpot(64 + (INT32)(nPan / 2)); | |
33 | + } | |
34 | + } | |
35 | + void _SetVolume(INT32 nVolume) { | |
36 | + if (m_pMidi != NULL) { | |
37 | + m_pMidi->SetMasterVolume((UINT16)(nVolume * 2)); | |
38 | + } | |
39 | + } | |
40 | + void _Pause(void) { | |
41 | + if (m_pMidi != NULL) { | |
42 | + m_pMidi->Pause(); | |
43 | + } | |
44 | + } | |
45 | + bool _Load(const char* pSoundData, UINT32 nDataSize, UINT32 nFlag) { | |
46 | + if (m_pMidi == NULL) { | |
47 | + m_pMidi = new CMidi; | |
48 | + } | |
49 | + return m_pMidi->AttachMidiData((const unsigned char*)pSoundData, nDataSize); | |
50 | + } | |
51 | + bool _Load(const TCHAR* pFileName, UINT32 nFlag, UINT32 nOffset, UINT32 nDataSize) { | |
52 | + if (m_pMidi == NULL) { | |
53 | + m_pMidi = new CMidi; | |
54 | + } | |
55 | + return m_pMidi->AttachMidiFile(pFileName, nOffset, nDataSize); | |
56 | + } | |
57 | + bool _IsPlaying(void) const { | |
58 | + if (m_pMidi == NULL) return false; | |
59 | + return m_pMidi->IsPlaying(); | |
60 | + } | |
61 | + bool _IsFinished(void) const { | |
62 | + if (m_pMidi == NULL) return true; | |
63 | + return (IsPause() == false && m_pMidi->IsPlaying() == false); | |
64 | + } | |
65 | + void _Play(UINT32 nLoop, UINT32 nLoopInSample = 0, UINT32 nStartPos = 0) { | |
66 | + if (m_pMidi != NULL) { | |
67 | + m_pMidi->Play(nLoop); | |
68 | + } | |
69 | + } | |
70 | + void _Stop(void) { | |
71 | + if (m_pMidi != NULL) { | |
72 | + m_pMidi->Stop(); | |
73 | + } | |
74 | + } | |
75 | + public: | |
76 | + CVoiceElementMidi(void) { | |
77 | + m_pMidi = NULL; | |
78 | + } | |
79 | + virtual ~CVoiceElementMidi(void) { | |
80 | + _Release(); | |
81 | + } | |
82 | +}; | |
83 | + | |
84 | + | |
85 | +#endif // __VOICEELEMENTMIDI_H__ | |
86 | +/* Bottom of VoiceElementMidi.h */ |
@@ -0,0 +1,16 @@ | ||
1 | +require "mkmf" | |
2 | + | |
3 | +SYSTEM_LIBRARIES = [ | |
4 | + "dsound", | |
5 | + "ole32", | |
6 | +] | |
7 | + | |
8 | +SYSTEM_LIBRARIES.each do |lib| | |
9 | + have_library(lib) | |
10 | +end | |
11 | + | |
12 | +#ヘッダファイル足りてませんが調べるの面倒で(^-^; | |
13 | + | |
14 | +#have_func("rb_enc_str_new") | |
15 | + | |
16 | +create_makefile("ayame") |
@@ -0,0 +1,252 @@ | ||
1 | +/** | |
2 | + * @brief MIDI再生クラス | |
3 | + * @file midi.h | |
4 | + * @author sazanta | |
5 | + */ | |
6 | +#include <windows.h> | |
7 | +#include <mmsystem.h> | |
8 | + | |
9 | +class CMidiEvent; | |
10 | + | |
11 | +enum eEventType | |
12 | +{ | |
13 | + EVT_LOAD, | |
14 | + EVT_PLAY, | |
15 | + EVT_STOP, | |
16 | + EVT_PAUSE, | |
17 | + EVT_RELEASE, | |
18 | + EVT_MAX, | |
19 | +}; | |
20 | + | |
21 | +struct EVENT_QUE_DATA_LOAD | |
22 | +{ | |
23 | + int nType; // 0-Memory 1-File 2-Url | |
24 | + char *pData; // nTypeが0以外の場合はdeleteする | |
25 | + unsigned long nSize; | |
26 | + unsigned long nStart; | |
27 | +}; | |
28 | + | |
29 | +struct EVENT_QUE_DATA_PLAY | |
30 | +{ | |
31 | + unsigned long nStartPos; | |
32 | + unsigned long nLoopCount; | |
33 | +}; | |
34 | + | |
35 | +struct EVENT_QUE | |
36 | +{ | |
37 | + eEventType EventType; | |
38 | + bool bUse; | |
39 | + union{ | |
40 | + EVENT_QUE_DATA_LOAD Data_Load; | |
41 | + EVENT_QUE_DATA_PLAY Data_Play; | |
42 | + }; | |
43 | +}; | |
44 | + | |
45 | + | |
46 | +//! MIDI再生クラス | |
47 | +class CMidi | |
48 | +{ | |
49 | + protected: | |
50 | + CRITICAL_SECTION m_stCriticalSection; //!< クリティカルセクション | |
51 | + HANDLE m_hThread; //!< スレッドハンドル | |
52 | + unsigned long m_nThreadId; //!< スレッドID | |
53 | + HANDLE m_hEvent; //!< イベント | |
54 | + EVENT_QUE m_stQue[EVT_MAX]; //!< イベントキュー | |
55 | + | |
56 | + CMidiEvent* m_pHeader; //!< データ先頭アドレス | |
57 | + CMidiEvent* m_pNowPoint; //!< 現在のデータポインタ | |
58 | + union { | |
59 | + unsigned short m_nAttribute; //!< 属性 | |
60 | + struct { | |
61 | + unsigned short m_nAttr_Pause : 1; //!< ポーズ中フラグ | |
62 | + unsigned short m_nAttr_UpdateV : 1; //!< 音量更新フラグ | |
63 | + unsigned short m_nAttr_UpdateP : 1; //!< パンポット更新フラグ | |
64 | + unsigned short m_nIsPlayQue : 1; //!< 再生準備中フラグ | |
65 | + unsigned short m_nAttr_pad :12; | |
66 | + }; | |
67 | + }; | |
68 | + unsigned short m_nTimeBias; //!< 分解能バイアス | |
69 | + unsigned long m_nTime; //!< 分解能 | |
70 | + unsigned long m_nTempo; //!< テンポ | |
71 | + unsigned long m_nLoop; //!< ループ回数 | |
72 | + unsigned long m_nNowTime; //!< 現在の再生時間 | |
73 | + unsigned long m_nNowTimeRest; //!< 現在の再生余り時間 | |
74 | + unsigned long m_nNextTime; //!< 次のイベントを送出する時間 | |
75 | + unsigned long m_nPrevTime; //!< 前回処理を行った時間 | |
76 | + unsigned long m_nTimeRest; //!< 余り時間 | |
77 | + unsigned long m_nTotalTime; //!< トータル時間 | |
78 | + unsigned int m_nTimerID; //!< タイマーID | |
79 | + HMIDIOUT m_hMidiOut; | |
80 | + | |
81 | + unsigned char m_nPanpot[16]; //!< チャンネルパン | |
82 | + unsigned short m_nVolume[16]; //!< チャンネルボリューム | |
83 | + unsigned short m_nMasterVolume; //!< マスターボリューム | |
84 | + unsigned short m_nMuteFlag; //!< ミュートフラグ | |
85 | + unsigned char m_nMasterPanpot; //!< マスターパンポット | |
86 | + volatile bool m_bEnd; | |
87 | + | |
88 | + protected: | |
89 | + virtual ~CMidi(void); | |
90 | + bool AddQue(EVENT_QUE* pQue); | |
91 | + void _Release(void); | |
92 | + bool _AttachMidiFile(const char* pFileName, unsigned int nOfs, unsigned int nSize); | |
93 | + bool _AttachMidiData(const unsigned char* pData, unsigned int nSize); | |
94 | + bool _Play(unsigned int nLoop); | |
95 | + bool _Stop(void); | |
96 | + void _Pause(void); | |
97 | + void ReleaseMain(CMidiEvent* pEvent); | |
98 | + bool ReadTrack(unsigned char** ppData, CMidiEvent** lplpEvent, unsigned int& nSize); | |
99 | + CMidiEvent* Marge(CMidiEvent** lplpEvent, unsigned short nTrack); | |
100 | + unsigned char* ReadDelta(unsigned char* pData, unsigned long& nDelta, unsigned int& nSize); | |
101 | + unsigned long CalcTotalTime(void) const; | |
102 | + void ThreadMain(); | |
103 | + static void CALLBACK TimeCallbackProc(unsigned int nID, unsigned int nMsg, unsigned long nUser, unsigned long nReserve1, unsigned long nReserve2); | |
104 | + static unsigned long CALLBACK ThreadProc(void *pParameter); | |
105 | + | |
106 | + public: | |
107 | + CMidi(void); | |
108 | + | |
109 | + void Release(void); | |
110 | + bool AttachMidiFile(const TCHAR* pFileName, unsigned int nOfs = 0, unsigned int nSize = 0); | |
111 | + bool AttachMidiData(const unsigned char* pData, unsigned int nSize); | |
112 | + | |
113 | + bool Play(unsigned long nLoop = 0xffffffff); | |
114 | + bool Stop(void); | |
115 | + bool Pause(void); | |
116 | + //! 再生中かどうか | |
117 | + /** | |
118 | + * @return 再生中の場合はtrueを返す | |
119 | + */ | |
120 | + bool IsPlaying(void) const { | |
121 | + return (m_nIsPlayQue == true || m_pNowPoint != NULL); | |
122 | + } | |
123 | + //! 総時間の取得 | |
124 | + /** | |
125 | + * @return 総時間(単位はms) | |
126 | + */ | |
127 | + unsigned long GetTotalTime(void) const { | |
128 | + return (m_pHeader != NULL)? m_nTotalTime : 0; | |
129 | + } | |
130 | + //! 再生時間の取得 | |
131 | + /** | |
132 | + * @return 再生時間(単位はms) | |
133 | + */ | |
134 | + unsigned long GetPlayingTime(void) const { | |
135 | + return (m_pHeader != NULL)? m_nNowTime : 0; | |
136 | + } | |
137 | + | |
138 | + //! ループ回数の設定 | |
139 | + void SetLoopCounter(unsigned int nLoop) { | |
140 | + if (m_pHeader != NULL) { | |
141 | + m_nLoop = nLoop; | |
142 | + } | |
143 | + } | |
144 | + //! ループ回数の取得 | |
145 | + unsigned long GetLoopCounter(void) const { | |
146 | + return (m_pHeader != NULL)? m_nLoop : 0; | |
147 | + } | |
148 | + | |
149 | + unsigned short SetMuteFlag(unsigned short nFlag); | |
150 | + unsigned short SetMuteFlag(unsigned int nChannel, bool bFlag); | |
151 | + //! ミュートフラグの取得 | |
152 | + unsigned short GetMuteFlag(void) const { | |
153 | + return m_nMuteFlag; | |
154 | + } | |
155 | + | |
156 | + //! テンポスピードの設定 | |
157 | + /** | |
158 | + * @param nSpeed [in] テンポスピード(1000=1.0) | |
159 | + */ | |
160 | + void SetTimeSpeed(unsigned long nSpeed) { | |
161 | + m_nTimeBias = (unsigned short)((nSpeed == 0)? 1 : nSpeed); | |
162 | + } | |
163 | + //! テンポスピードの取得 | |
164 | + /** | |
165 | + * @return テンポスピード(1000=1.0) | |
166 | + */ | |
167 | + unsigned long GetTimeSpeed(void) const { | |
168 | + return m_nTimeBias; | |
169 | + } | |
170 | + | |
171 | + //! テンポの取得 | |
172 | + unsigned long GetRealTempo(void) const { | |
173 | + return m_nTempo; | |
174 | + } | |
175 | + //! テンポの取得 | |
176 | + unsigned long GetTempo(void) const { | |
177 | + return (60 * 1000000) / m_nTempo; | |
178 | + } | |
179 | + | |
180 | + //! マスターボリュームの設定 | |
181 | + /** | |
182 | + * @param nVol [in] マスターボリューム(0〜256) | |
183 | + */ | |
184 | + void SetMasterVolume(unsigned short nVol) { | |
185 | + m_nMasterVolume = ((nVol > 256)? 256 : nVol); | |
186 | + m_nAttr_UpdateV = 1; | |
187 | + } | |
188 | + //! マスターボリュームの取得 | |
189 | + unsigned short GetMasterVolume(void) const { | |
190 | + return m_nMasterVolume; | |
191 | + } | |
192 | + | |
193 | + //! チャンネルボリュームの設定 | |
194 | + /** | |
195 | + * 各チャンネルのボリュームを指定します。 | |
196 | + * 各チャンネルのボリュームはMIDIデータによって上書きされる可能性があります。 | |
197 | + * | |
198 | + * @param nChannel [in] チャンネル(0〜15) | |
199 | + * @param nVol [in] チャンネルボリューム(0〜127) | |
200 | + */ | |
201 | + void SetChannelVolume(unsigned long nChannel, unsigned short nVol) { | |
202 | + if (nChannel >= 16) return; | |
203 | + m_nVolume[nChannel] = (nVol > 127)? 127 : nVol; | |
204 | + m_nAttr_UpdateV = 1; | |
205 | + } | |
206 | + //! チャンネルボリュームの取得 | |
207 | + /** | |
208 | + * @return チャンネルボリューム | |
209 | + */ | |
210 | + unsigned short GetChannelVolume(unsigned long nChannel) const { | |
211 | + return (nChannel < 16)? m_nVolume[nChannel] : 100; | |
212 | + } | |
213 | + | |
214 | + //! マスターパンポットの設定 | |
215 | + /** | |
216 | + * @param nPan [in] マスターパンポット(0〜127) | |
217 | + */ | |
218 | + void SetMasterPanpot(unsigned char nPan) { | |
219 | + m_nMasterPanpot = ((nPan > 127)? 127 : nPan); | |
220 | + m_nAttr_UpdateP = 1; | |
221 | + } | |
222 | + //! マスターパンポットの取得 | |
223 | + unsigned char GetMasterPanpot(void) const { | |
224 | + return m_nMasterPanpot; | |
225 | + } | |
226 | + | |
227 | + //! チャンネルパンポットの設定 | |
228 | + /** | |
229 | + * 各チャンネルのパンポットを指定します。 | |
230 | + * 各チャンネルのパンポットはMIDIデータによって上書きされる可能性があります。 | |
231 | + * | |
232 | + * @param nChannel [in] チャンネル(0〜15) | |
233 | + * @param nPan [in] チャンネルパンポット(0〜127) | |
234 | + */ | |
235 | + void SetChannelPanpot(unsigned long nChannel, unsigned char nPan) { | |
236 | + if (nChannel >= 16) return; | |
237 | + m_nPanpot[nChannel] = (nPan > 127)? 127 : nPan; | |
238 | + m_nAttr_UpdateP = 1; | |
239 | + } | |
240 | + //! チャンネルパンポットの取得 | |
241 | + /** | |
242 | + * @return チャンネルパンポット | |
243 | + */ | |
244 | + unsigned char GetChannelPanpot(unsigned long nChannel) const { | |
245 | + return (nChannel < 16)? m_nPanpot[nChannel] : 64; | |
246 | + } | |
247 | + | |
248 | + static bool CheckMidiHeader(const unsigned char* pData); | |
249 | +}; | |
250 | + | |
251 | + | |
252 | +/* Bottom of midi.h */ |
@@ -0,0 +1,124 @@ | ||
1 | +/** | |
2 | + * Ayameマネージャ | |
3 | + */ | |
4 | + | |
5 | +#ifndef ___AYAMEMANAGER_H___ | |
6 | +#define ___AYAMEMANAGER_H___ | |
7 | + | |
8 | + | |
9 | +//==================================================================== | |
10 | +// INCLUDE | |
11 | +//==================================================================== | |
12 | +#include <windows.h> | |
13 | +#include "Ayame.h" | |
14 | + | |
15 | +#ifdef _UNICODE | |
16 | + #define DEFAULT_AYAME_PATH L"ayame.dll" | |
17 | +#else | |
18 | + #define DEFAULT_AYAME_PATH "ayame.dll" | |
19 | +#endif | |
20 | + | |
21 | +//! Ayameマネージャ | |
22 | +class CAyameManager | |
23 | +{ | |
24 | +protected: | |
25 | + HINSTANCE m_hAyame; | |
26 | + AYAME_INITIALIZE_PROC m_pAyameInitialize; | |
27 | + AYAME_UNINITIALIZE_PROC m_pAyameUninitialize; | |
28 | + AYAME_CREATE_FROM_FILE_PROC m_pAyameCreateFromFile; | |
29 | + AYAME_CREATE_FROM_MEMORY_PROC m_pAyameCreateFromMemory; | |
30 | + AYAME_CREATE_FROM_URL_PROC m_pAyameCreateFromUrl; | |
31 | + AYAME_GETERROR_PROC m_pAyameGetError; | |
32 | + | |
33 | +public: | |
34 | + //! コンストラクタ | |
35 | + CAyameManager() { | |
36 | + m_hAyame = NULL; | |
37 | + m_pAyameInitialize = NULL; | |
38 | + m_pAyameUninitialize = NULL; | |
39 | + m_pAyameCreateFromFile = NULL; | |
40 | + m_pAyameCreateFromMemory = NULL; | |
41 | + m_pAyameCreateFromUrl = NULL; | |
42 | + m_pAyameGetError = NULL; | |
43 | + } | |
44 | + //! デストラクタ | |
45 | + ~CAyameManager() { | |
46 | + Release(); | |
47 | + } | |
48 | + | |
49 | + //! DLLの読み込み | |
50 | + bool LoadDLL(HWND hWnd, TCHAR* pszPath = DEFAULT_AYAME_PATH, void **ppDirectSound = NULL) { | |
51 | + if ( m_hAyame != NULL ) | |
52 | + { | |
53 | + return false; | |
54 | + } | |
55 | + // DLLのロード | |
56 | + m_hAyame = ::LoadLibrary( pszPath ); | |
57 | + if ( m_hAyame == NULL ) return false; | |
58 | + | |
59 | + // 関数のロード | |
60 | + m_pAyameInitialize = (AYAME_INITIALIZE_PROC)::GetProcAddress( m_hAyame, "Ayame_Initialize" ); | |
61 | + m_pAyameUninitialize = (AYAME_UNINITIALIZE_PROC)::GetProcAddress( m_hAyame, "Ayame_Uninitialize" ); | |
62 | + m_pAyameCreateFromFile = (AYAME_CREATE_FROM_FILE_PROC)::GetProcAddress( m_hAyame, "Ayame_CreateInstanceFromFile" ); | |
63 | + m_pAyameCreateFromMemory = (AYAME_CREATE_FROM_MEMORY_PROC)::GetProcAddress( m_hAyame, "Ayame_CreateInstanceFromMemory" ); | |
64 | + m_pAyameCreateFromUrl = (AYAME_CREATE_FROM_URL_PROC)::GetProcAddress( m_hAyame, "Ayame_CreateInstanceFromUrl" ); | |
65 | + m_pAyameGetError = (AYAME_GETERROR_PROC)::GetProcAddress( m_hAyame, "Ayame_GetLastError" ); | |
66 | + | |
67 | + if ( m_pAyameInitialize == NULL || | |
68 | + m_pAyameUninitialize == NULL || | |
69 | + m_pAyameCreateFromFile == NULL || | |
70 | + m_pAyameCreateFromMemory == NULL || | |
71 | + m_pAyameCreateFromUrl == NULL || | |
72 | + m_pAyameGetError == NULL ) | |
73 | + { | |
74 | + ::FreeLibrary(m_hAyame); | |
75 | + m_hAyame = NULL; | |
76 | + return false; | |
77 | + } | |
78 | + | |
79 | + // ドライバーの初期化 | |
80 | + return m_pAyameInitialize( hWnd, ppDirectSound ); | |
81 | + } | |
82 | + | |
83 | + //! DLLの開放 | |
84 | + void Release() { | |
85 | + if ( m_hAyame != NULL ) | |
86 | + { | |
87 | + m_pAyameUninitialize(); | |
88 | + ::FreeLibrary(m_hAyame); | |
89 | + m_hAyame = NULL; | |
90 | + } | |
91 | + } | |
92 | + | |
93 | + CAyame *CreateInstanceFromFile( const TCHAR *pFileName, unsigned long Start = 0, unsigned long Size = 0, unsigned long Flag = AYAME_LOADFLAG_GLOBAL ) | |
94 | + { | |
95 | + if ( m_hAyame == NULL || pFileName == NULL ) | |
96 | + return NULL; | |
97 | + return m_pAyameCreateFromFile( pFileName, Start, Size, Flag ); | |
98 | + } | |
99 | + | |
100 | + CAyame *CreateInstanceFromMemory( const void *pData, unsigned long Size = 0, unsigned long Flag = AYAME_LOADFLAG_GLOBAL ) | |
101 | + { | |
102 | + if ( m_hAyame == NULL || pData == NULL ) | |
103 | + return NULL; | |
104 | + return m_pAyameCreateFromMemory( pData, Size, Flag ); | |
105 | + } | |
106 | + | |
107 | + CAyame *CreateInstanceFromUrl( const TCHAR *pUrl, unsigned long Start = 0, unsigned long Size = 0, unsigned long Flag = AYAME_LOADFLAG_GLOBAL ) | |
108 | + { | |
109 | + if ( m_hAyame == NULL || pUrl == NULL ) | |
110 | + return NULL; | |
111 | + return m_pAyameCreateFromUrl( pUrl, Start, Size, Flag ); | |
112 | + } | |
113 | + | |
114 | + bool GetLastError( TCHAR *pErrStr, unsigned long Size ) | |
115 | + { | |
116 | + if ( m_hAyame == NULL ) | |
117 | + return false; | |
118 | + return m_pAyameGetError( pErrStr, Size ); | |
119 | + } | |
120 | +}; | |
121 | + | |
122 | +#endif // ___AYAMEMANAGER_H___ | |
123 | + | |
124 | + |
@@ -0,0 +1,14 @@ | ||
1 | +/** | |
2 | + * @file VoiceElementAyame.cpp | |
3 | + * @brief 単一のサウンド管理クラス | |
4 | + * @author Sazanta | |
5 | + */ | |
6 | + | |
7 | +// インクルード | |
8 | +#include "VoiceElementAyame.h" | |
9 | + | |
10 | + | |
11 | +CAyameManager CVoiceElementAyame::m_sAyameMgr; | |
12 | + | |
13 | + | |
14 | +/* Bottom of VoiceElementAyame.cpp */ |
@@ -0,0 +1,461 @@ | ||
1 | +/* | |
2 | +################################### | |
3 | +# | |
4 | +# Ayame/Ruby 0.0.2 | |
5 | +# | |
6 | +################################### | |
7 | +*/ | |
8 | + | |
9 | +#include "ruby.h" | |
10 | +#include "VoiceElement.h" | |
11 | +#include "VoiceElementAyame.h" | |
12 | +#include "VoiceElementMidi.h" | |
13 | + | |
14 | +static VALUE cAyame; | |
15 | +static VALUE eAyameError; /* 例外 */ | |
16 | + | |
17 | +struct AyameData { | |
18 | + CVoiceElement* pVoiceElement; | |
19 | + __int64 start_tick; | |
20 | +}; | |
21 | + | |
22 | +static int g_isPerformanceCounter = 0; /* パフォーマンスカウンタがあったら1 */ | |
23 | +static __int64 g_OneSecondCount = 0; /* 一秒間にカウンタが数える数 */ | |
24 | + | |
25 | +static void InitSync( void ); | |
26 | +static __int64 GetSystemCounter( void ); | |
27 | + | |
28 | +#define AYAME_GET_STRUCT( v ) ((struct AyameData *)DATA_PTR( v )) | |
29 | + | |
30 | +/* 内部参照用リスト */ | |
31 | +static struct AyameList { | |
32 | + void **pointer; | |
33 | + int allocate_size; | |
34 | + int count; | |
35 | +} g_AyameList; | |
36 | + | |
37 | +static bool g_AyameShutdownFlag = false; | |
38 | + | |
39 | + | |
40 | +static void AddAyameList( struct AyameData *ad ) | |
41 | +{ | |
42 | + if( g_AyameList.allocate_size <= g_AyameList.count ) | |
43 | + { | |
44 | + g_AyameList.allocate_size = g_AyameList.allocate_size * 3 / 2; /* 1.5倍にする */ | |
45 | + g_AyameList.pointer = (void**)realloc( g_AyameList.pointer, sizeof( void* ) * g_AyameList.allocate_size ); | |
46 | + } | |
47 | + | |
48 | + g_AyameList.pointer[g_AyameList.count] = ad; | |
49 | + g_AyameList.count++; | |
50 | +} | |
51 | + | |
52 | +static void DeleteAyameList( struct AyameData *ad ) | |
53 | +{ | |
54 | + int i; | |
55 | + | |
56 | + for( i = 0; i < g_AyameList.count; i++ ) | |
57 | + { | |
58 | + if( g_AyameList.pointer[i] == ad ) | |
59 | + { | |
60 | + break; | |
61 | + } | |
62 | + } | |
63 | + if( i == g_AyameList.count ) | |
64 | + { | |
65 | + rb_raise( eAyameError, "内部エラー - DeleteAyameList" ); | |
66 | + } | |
67 | + | |
68 | + i++; | |
69 | + for( ; i < g_AyameList.count; i++ ) | |
70 | + { | |
71 | + g_AyameList.pointer[i - 1] = g_AyameList.pointer[i]; | |
72 | + } | |
73 | + | |
74 | + g_AyameList.count--; | |
75 | +} | |
76 | + | |
77 | + | |
78 | +static VALUE Ayame_dispose( VALUE self ) | |
79 | +{ | |
80 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
81 | + if( ad->pVoiceElement != NULL ) | |
82 | + { | |
83 | + if( !g_AyameShutdownFlag ) | |
84 | + { | |
85 | + ad->pVoiceElement->Release(); | |
86 | + } | |
87 | + delete ad->pVoiceElement; | |
88 | + ad->pVoiceElement = NULL; | |
89 | + DeleteAyameList( ad ); | |
90 | + } | |
91 | + return self; | |
92 | +} | |
93 | + | |
94 | +static void Ayame_release( struct AyameData *ad ) | |
95 | +{ | |
96 | + if( ad->pVoiceElement != NULL ) | |
97 | + { | |
98 | + if( !g_AyameShutdownFlag ) | |
99 | + { | |
100 | + ad->pVoiceElement->Release(); | |
101 | + } | |
102 | + delete ad->pVoiceElement; | |
103 | + ad->pVoiceElement = NULL; | |
104 | + DeleteAyameList( ad ); | |
105 | + } | |
106 | + free( ad ); | |
107 | +} | |
108 | + | |
109 | +static VALUE Ayame_allocate( VALUE klass ) | |
110 | +{ | |
111 | + struct AyameData *ad = (struct AyameData *)malloc( sizeof( struct AyameData ) ); | |
112 | + ad->pVoiceElement = NULL; | |
113 | + return Data_Wrap_Struct( klass, NULL, Ayame_release, ad ); | |
114 | +} | |
115 | + | |
116 | +static VALUE Ayame_initialize( VALUE self, VALUE vfilename ) | |
117 | +{ | |
118 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
119 | + | |
120 | + Check_Type(vfilename, T_STRING); | |
121 | + if( strstr( RSTRING_PTR( vfilename ), ".mid" ) != NULL ) | |
122 | + { | |
123 | + //midi | |
124 | + ad->pVoiceElement = new CVoiceElementMidi; | |
125 | + if( !ad->pVoiceElement->Load(RSTRING_PTR( vfilename ), 0, 0, 0) ) rb_raise( eAyameError, "%sのロードに失敗しました", vfilename ); | |
126 | + ad->pVoiceElement->SetVolume(100); | |
127 | + } | |
128 | + else | |
129 | + { | |
130 | + //ayame | |
131 | + ad->pVoiceElement = new CVoiceElementAyame; | |
132 | + if( !ad->pVoiceElement->Load(RSTRING_PTR( vfilename ), AYAME_LOADFLAG_GLOBAL | AYAME_LOADFLAG_STREAM, 0, 0) ) rb_raise( eAyameError, "%sのロードに失敗しました", vfilename ); | |
133 | + ad->pVoiceElement->SetVolume(100); | |
134 | + } | |
135 | + | |
136 | + AddAyameList( ad ); | |
137 | + | |
138 | + return self; | |
139 | +} | |
140 | + | |
141 | +static VALUE Ayame_load_from_memory( VALUE klass, VALUE vdata ) | |
142 | +{ | |
143 | + struct AyameData *ad; | |
144 | + VALUE vad; | |
145 | + | |
146 | + Check_Type(vdata, T_STRING); | |
147 | + vad = Ayame_allocate( klass ); | |
148 | + ad = AYAME_GET_STRUCT( vad ); | |
149 | + ad->pVoiceElement = new CVoiceElementAyame; | |
150 | + if( !ad->pVoiceElement->Load((char*)RSTRING_PTR( vdata ), RSTRING_LEN( vdata ), AYAME_LOADFLAG_GLOBAL | AYAME_LOADFLAG_STATIC) ) rb_raise( eAyameError, "ロードに失敗しました" ); | |
151 | + ad->pVoiceElement->SetVolume(100); | |
152 | + | |
153 | + AddAyameList( ad ); | |
154 | + | |
155 | + return vad; | |
156 | +} | |
157 | + | |
158 | +static VALUE Ayame_play( int argc, VALUE *argv, VALUE self ) | |
159 | +{ | |
160 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
161 | + | |
162 | + if( argc < 1 || argc > 3 ) rb_raise( rb_eArgError, "wrong number of arguments (%d for %d..%d)", argc, 1, 3 ); | |
163 | + | |
164 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
165 | + ad->pVoiceElement->Play( NUM2INT( argv[0] ) - 1, 100, argc < 2 || argv[1] == Qnil ? 0 : NUM2INT( argv[1] ) * 1000, argc < 3 || argv[2] == Qnil ? 0 : NUM2INT( argv[2] ) ); | |
166 | + ad->start_tick = GetSystemCounter(); | |
167 | + return self; | |
168 | +} | |
169 | + | |
170 | +static VALUE Ayame_stop( int argc, VALUE *argv, VALUE self ) | |
171 | +{ | |
172 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
173 | + | |
174 | + if( argc < 0 || argc > 1 ) rb_raise( rb_eArgError, "wrong number of arguments (%d for %d..%d)", argc, 0, 1 ); | |
175 | + | |
176 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
177 | + ad->pVoiceElement->Stop(argc == 0 || argv[0] == Qnil ? 0.0 : NUM2INT( argv[0] ) * 1000 ); | |
178 | + return self; | |
179 | +} | |
180 | + | |
181 | +static VALUE Ayame_set_volume( int argc, VALUE *argv, VALUE self ) | |
182 | +//( VALUE self, VALUE vvolume, VALUE vtick ) | |
183 | +{ | |
184 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
185 | + | |
186 | + if( argc < 1 || argc > 2 ) rb_raise( rb_eArgError, "wrong number of arguments (%d for %d..%d)", argc, 1, 2 ); | |
187 | + | |
188 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
189 | + ad->pVoiceElement->SetVolume( NUM2INT( argv[0] ), argc < 2 || argv[1] == Qnil ? 0 : NUM2INT( argv[1] ) * 1000.0 ); | |
190 | + return self; | |
191 | +} | |
192 | + | |
193 | +static VALUE Ayame_get_volume( VALUE self ) | |
194 | +{ | |
195 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
196 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
197 | + return INT2NUM(ad->pVoiceElement->GetVolume()); | |
198 | +} | |
199 | + | |
200 | +static VALUE Ayame_set_pan( int argc, VALUE *argv, VALUE self ) | |
201 | +//( VALUE self, VALUE vpan, VALUE vtick ) | |
202 | +{ | |
203 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
204 | + | |
205 | + if( argc < 1 || argc > 2 ) rb_raise( rb_eArgError, "wrong number of arguments (%d for %d..%d)", argc, 1, 2 ); | |
206 | + | |
207 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
208 | + ad->pVoiceElement->SetPan( NUM2INT( argv[0] ), argc < 2 || argv[1] == Qnil ? 0 : NUM2INT( argv[1] ) * 1000.0 ); | |
209 | + return self; | |
210 | +} | |
211 | + | |
212 | +static VALUE Ayame_get_pan( VALUE self ) | |
213 | +{ | |
214 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
215 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
216 | + return INT2NUM(ad->pVoiceElement->GetPan ()); | |
217 | +} | |
218 | + | |
219 | +static VALUE Ayame_pause( VALUE self, VALUE vtick ) | |
220 | +{ | |
221 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
222 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
223 | + ad->pVoiceElement->Pause( true, NUM2INT( vtick ) * 1000.0 ); | |
224 | + return self; | |
225 | +} | |
226 | + | |
227 | +static VALUE Ayame_resume( VALUE self, VALUE vtick ) | |
228 | +{ | |
229 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
230 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
231 | + ad->pVoiceElement->Pause( false, NUM2INT( vtick ) * 1000.0 ); | |
232 | + return self; | |
233 | +} | |
234 | + | |
235 | +static VALUE Ayame_prefetch( VALUE self ) | |
236 | +{ | |
237 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
238 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
239 | + ad->pVoiceElement->Prefetch(); | |
240 | + return self; | |
241 | +} | |
242 | + | |
243 | +static VALUE Ayame_predecode( VALUE self ) | |
244 | +{ | |
245 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
246 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
247 | + ad->pVoiceElement->Predecode(); | |
248 | + return self; | |
249 | +} | |
250 | + | |
251 | +static VALUE Ayame_IsFade( VALUE self ) | |
252 | +{ | |
253 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
254 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
255 | + if( ad->pVoiceElement->IsFade() ) | |
256 | + { | |
257 | + return Qtrue; | |
258 | + } | |
259 | + else | |
260 | + { | |
261 | + return Qfalse; | |
262 | + } | |
263 | +} | |
264 | + | |
265 | +static VALUE Ayame_IsPlay( VALUE self ) | |
266 | +{ | |
267 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
268 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
269 | + if( ad->pVoiceElement->IsPlaying() ) | |
270 | + { | |
271 | + return Qtrue; | |
272 | + } | |
273 | + else | |
274 | + { | |
275 | + return Qfalse; | |
276 | + } | |
277 | +} | |
278 | + | |
279 | +static VALUE Ayame_IsFinish( VALUE self ) | |
280 | +{ | |
281 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
282 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
283 | + if( ad->pVoiceElement->IsFinished() ) | |
284 | + { | |
285 | + return Qtrue; | |
286 | + } | |
287 | + else | |
288 | + { | |
289 | + return Qfalse; | |
290 | + } | |
291 | +} | |
292 | + | |
293 | +static VALUE Ayame_IsPause( VALUE self ) | |
294 | +{ | |
295 | + struct AyameData *ad = AYAME_GET_STRUCT( self ); | |
296 | + if( ad->pVoiceElement == NULL ) rb_raise( eAyameError, "disposed Ayame object" ); | |
297 | + if( ad->pVoiceElement->IsPause() ) | |
298 | + { | |
299 | + return Qtrue; | |
300 | + } | |
301 | + else | |
302 | + { | |
303 | + return Qfalse; | |
304 | + } | |
305 | +} | |
306 | + | |
307 | +static VALUE Ayame_update( VALUE klass ) | |
308 | +{ | |
309 | + int i; | |
310 | + | |
311 | + for( i = 0; i < g_AyameList.count; i++ ) | |
312 | + { | |
313 | + struct AyameData *ad = (struct AyameData *)g_AyameList.pointer[i]; | |
314 | + __int64 tick = GetSystemCounter(); | |
315 | + ad->pVoiceElement->Run((double)(tick - ad->start_tick) / (double)g_OneSecondCount * 1000.0); | |
316 | + ad->start_tick = tick; | |
317 | + } | |
318 | + return Qnil; | |
319 | +} | |
320 | + | |
321 | +/*-------------------------------------------------------------------- | |
322 | + (内部関数)フレーム調整用カウンタ値取得 | |
323 | + ---------------------------------------------------------------------*/ | |
324 | +static __int64 GetSystemCounter( void ) | |
325 | +{ | |
326 | + __int64 time; | |
327 | + | |
328 | + if( g_isPerformanceCounter == 1 ) | |
329 | + { | |
330 | + QueryPerformanceCounter( (LARGE_INTEGER *)&time ); | |
331 | + return time; | |
332 | + } | |
333 | + else | |
334 | + { | |
335 | + return timeGetTime(); | |
336 | + } | |
337 | +} | |
338 | + | |
339 | +/*-------------------------------------------------------------------- | |
340 | + (内部関数)フレーム調整初期化 | |
341 | + ---------------------------------------------------------------------*/ | |
342 | +static void InitSync( void ) | |
343 | +{ | |
344 | + timeBeginPeriod( 1 ); | |
345 | + | |
346 | + /* パフォーマンスカウンタの秒間カウント値取得 */ | |
347 | + if( QueryPerformanceFrequency( (LARGE_INTEGER *)&g_OneSecondCount ) ) | |
348 | + { | |
349 | + /* パフォーマンスカウンタがある場合 */ | |
350 | + g_isPerformanceCounter = 1; | |
351 | + } | |
352 | + else | |
353 | + { | |
354 | + /* パフォーマンスカウンタが無い場合 */ | |
355 | + g_isPerformanceCounter = 0; | |
356 | + g_OneSecondCount = 1000; | |
357 | + } | |
358 | +} | |
359 | + | |
360 | +static void Ayame_shutdown( VALUE obj ) | |
361 | +{ | |
362 | + CVoiceElementAyame::m_sAyameMgr.Release(); | |
363 | + CoUninitialize(); | |
364 | + g_AyameShutdownFlag = true; | |
365 | +} | |
366 | + | |
367 | + | |
368 | +/* | |
369 | +*************************************************************** | |
370 | +* | |
371 | +* Global functions | |
372 | +* | |
373 | +***************************************************************/ | |
374 | + | |
375 | +LRESULT CALLBACK DummyWndProc( HWND hWnd,UINT msg,UINT wParam,LONG lParam ) | |
376 | +{ | |
377 | + return DefWindowProc( hWnd, msg, wParam, lParam ); | |
378 | +} | |
379 | + | |
380 | + | |
381 | +extern "C" { | |
382 | + void Init_ayame(); | |
383 | +} | |
384 | + | |
385 | +void Init_ayame() | |
386 | +{ | |
387 | + HRESULT hr; | |
388 | + HWND hWnd; | |
389 | + WNDCLASSEX wcex; | |
390 | + RECT rect; | |
391 | + HINSTANCE hInstance; | |
392 | + | |
393 | + CoInitialize(NULL); | |
394 | + | |
395 | + /* インスタンスハンドル取得 */ | |
396 | + hInstance = (HINSTANCE)GetModuleHandle( NULL ); | |
397 | + | |
398 | + /* ウインドウ・クラスの登録 */ | |
399 | + wcex.cbSize = sizeof( WNDCLASSEX ); | |
400 | + wcex.style = 0; | |
401 | + wcex.lpfnWndProc = DefWindowProc; | |
402 | + wcex.cbClsExtra = 0; | |
403 | + wcex.cbWndExtra = 0; | |
404 | + wcex.hInstance = hInstance; | |
405 | + wcex.hIcon = 0; | |
406 | + wcex.hIconSm = 0; | |
407 | + wcex.hCursor = 0; | |
408 | + wcex.hbrBackground = 0; | |
409 | + wcex.lpszMenuName = NULL; | |
410 | + wcex.lpszClassName = "Ayame"; | |
411 | + | |
412 | + if( !RegisterClassEx( &wcex ) ) | |
413 | + { | |
414 | + rb_raise( eAyameError, "ウィンドウの初期化に失敗しました - RegusterClassEx" ); | |
415 | + } | |
416 | + | |
417 | + hWnd = CreateWindow("Ayame", "", WS_POPUP, 0, 0, 0, 0, 0, NULL, hInstance, NULL); | |
418 | + | |
419 | + /* 例外定義 */ | |
420 | + eAyameError = rb_define_class( "AyameError", rb_eRuntimeError ); | |
421 | + | |
422 | + if ( !CVoiceElementAyame::m_sAyameMgr.LoadDLL( hWnd, "Ayame.dll", 0 ) ) rb_raise( eAyameError, "Ayame.dllがロードできませんでした" ); | |
423 | + | |
424 | + /* Ayameクラス定義 */ | |
425 | + cAyame = rb_define_class ( "Ayame", rb_cObject ); | |
426 | + | |
427 | + /* Ayameクラスにメソッド登録*/ | |
428 | + rb_define_private_method( cAyame, "initialize", (VALUE(*)(...))Ayame_initialize, 1 ); | |
429 | + rb_define_singleton_method( cAyame, "load_from_memory", (VALUE(*)(...))Ayame_load_from_memory, 1 ); | |
430 | + rb_define_method( cAyame, "play", (VALUE(*)(...))Ayame_play, -1 ); | |
431 | + rb_define_method( cAyame, "stop", (VALUE(*)(...))Ayame_stop, -1 ); | |
432 | + rb_define_method( cAyame, "get_volume", (VALUE(*)(...))Ayame_get_volume, 0 ); | |
433 | + rb_define_method( cAyame, "set_volume", (VALUE(*)(...))Ayame_set_volume, -1 ); | |
434 | + rb_define_method( cAyame, "get_pan", (VALUE(*)(...))Ayame_get_pan, 0 ); | |
435 | + rb_define_method( cAyame, "set_pan", (VALUE(*)(...))Ayame_set_pan, -1 ); | |
436 | + rb_define_method( cAyame, "dispose", (VALUE(*)(...))Ayame_dispose, 0 ); | |
437 | + rb_define_method( cAyame, "pause", (VALUE(*)(...))Ayame_pause, 1 ); | |
438 | + rb_define_method( cAyame, "resume", (VALUE(*)(...))Ayame_resume, 1 ); | |
439 | + rb_define_method( cAyame, "prefetch", (VALUE(*)(...))Ayame_prefetch, 0 ); | |
440 | + rb_define_method( cAyame, "predecode", (VALUE(*)(...))Ayame_predecode, 0 ); | |
441 | + | |
442 | + rb_define_method( cAyame, "fading?", (VALUE(*)(...))Ayame_IsFade, 0 ); | |
443 | + rb_define_method( cAyame, "playing?", (VALUE(*)(...))Ayame_IsPlay, 0 ); | |
444 | + rb_define_method( cAyame, "finished?", (VALUE(*)(...))Ayame_IsFinish, 0 ); | |
445 | + rb_define_method( cAyame, "pausing?", (VALUE(*)(...))Ayame_IsPause, 0 ); | |
446 | + | |
447 | + rb_define_singleton_method( cAyame, "update", (VALUE(*)(...))Ayame_update, 0 ); | |
448 | + | |
449 | + /* Ayameオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 */ | |
450 | + rb_define_alloc_func( cAyame, Ayame_allocate ); | |
451 | + | |
452 | + /* 終了時に実行する関数 */ | |
453 | + rb_set_end_proc( (void(*)(VALUE))Ayame_shutdown, Qnil ); | |
454 | + | |
455 | + InitSync(); | |
456 | + | |
457 | + /* Ayameオブジェクトの内部参照リスト */ | |
458 | + g_AyameList.pointer = (void**)malloc( sizeof(void*) * 16 ); | |
459 | + g_AyameList.count = 0; | |
460 | + g_AyameList.allocate_size = 16; | |
461 | +} |
@@ -0,0 +1,365 @@ | ||
1 | +/** | |
2 | + * @file VoiceElement.cpp | |
3 | + * @brief 単一のサウンド管理クラス | |
4 | + * @author Sazanta | |
5 | + */ | |
6 | + | |
7 | +// インクルード | |
8 | +#include "VoiceElement.h" | |
9 | + | |
10 | + | |
11 | +INT32 CVoiceElement::m_snGlobalVol = cnVoiceElm_VolumeMax; | |
12 | + | |
13 | + | |
14 | +//! コンストラクタ | |
15 | +CVoiceElement::CVoiceElement(void) | |
16 | +{ | |
17 | + m_dwNumber = 0; | |
18 | + m_dwUnique = 0; | |
19 | + m_bFinishRequest = false; | |
20 | + m_bPauseRequest = false; | |
21 | + m_bPause = false; | |
22 | + m_bVolumeFade = false; | |
23 | + m_cVolume.SetNowValue(cnVoiceElm_VolumeMax); | |
24 | + m_cPanpot.SetNowValue(cnVoiceElm_PanpotCenter); | |
25 | +#if defined(NUM_OF_SUB_VOLUME) && (NUM_OF_SUB_VOLUME > 0) | |
26 | + for (int i = 0; i < NUM_OF_SUB_VOLUME; i++) { | |
27 | + m_cSubVolume[i].SetNowValue(cnVoiceElm_VolumeMax); | |
28 | + } | |
29 | +#endif | |
30 | +} | |
31 | + | |
32 | + | |
33 | +//! 定例処理 | |
34 | +/** | |
35 | + * 毎フレーム行う処理です。<br> | |
36 | + * あまり間隔が空かないよう呼び出してください。<br> | |
37 | + * | |
38 | + * @param nSyncTick [in] 前回からの経過時間[tick] | |
39 | + */ | |
40 | +void CVoiceElement::Run(INT32 nSyncTick) | |
41 | +{ | |
42 | + INT32 nValue; | |
43 | + | |
44 | + // オートパン処理 | |
45 | + if (m_cPanpot.Run(nSyncTick, nValue) == true) { | |
46 | + m_cPanpot.SetNowValue(nValue); | |
47 | + _SetPan(nValue); | |
48 | + } | |
49 | + | |
50 | + INT32 nSubVol = cnVoiceElm_VolumeMax; | |
51 | + bool bSubChange = false; | |
52 | +#if defined(NUM_OF_SUB_VOLUME) && (NUM_OF_SUB_VOLUME > 0) | |
53 | + // サブボリュームフェード処理 | |
54 | + for (int i = 0; i < NUM_OF_SUB_VOLUME; i++) { | |
55 | + if (m_cSubVolume[i].Run(nSyncTick, nValue) == true) { | |
56 | + nSubVol *= nValue; | |
57 | + nSubVol /= cnVoiceElm_VolumeRange; | |
58 | + m_cSubVolume[i].SetNowValue(nValue); | |
59 | + bSubChange = true; | |
60 | + } | |
61 | + } | |
62 | +#endif | |
63 | + | |
64 | + // メインボリュームフェード処理 | |
65 | + if (m_cVolume.Run(nSyncTick, nValue) == true) { | |
66 | + _SetVolume(nValue * m_snGlobalVol * nSubVol / (cnVoiceElm_VolumeRange * cnVoiceElm_VolumeRange)); | |
67 | + if (m_bVolumeFade == true) { | |
68 | + m_cVolume.SetNowValue(nValue); | |
69 | + } | |
70 | + if (m_cVolume.IsFade() == false) { | |
71 | + // フェード終了 | |
72 | + if (m_bFinishRequest == true) { | |
73 | + Stop(); | |
74 | + m_bFinishRequest = false; | |
75 | + } else if (m_bPauseRequest == true) { | |
76 | + _Pause(); | |
77 | + m_bPauseRequest = false; | |
78 | + m_bPause = true; | |
79 | + } | |
80 | + m_bVolumeFade = false; | |
81 | + } | |
82 | + } else { | |
83 | + if (bSubChange != false) { | |
84 | + _SetVolume(nValue * m_snGlobalVol * nSubVol / (cnVoiceElm_VolumeRange * cnVoiceElm_VolumeRange)); | |
85 | + } | |
86 | + } | |
87 | +} | |
88 | + | |
89 | + | |
90 | +//! データの解放 | |
91 | +/** | |
92 | + * データの解放を行います。<br> | |
93 | + */ | |
94 | +void CVoiceElement::Release(void) | |
95 | +{ | |
96 | + Stop(); | |
97 | + _Release(); | |
98 | +} | |
99 | + | |
100 | + | |
101 | +//! 再生終了チェック | |
102 | +/** | |
103 | + * 再生が終了しているかどうかを取得します。<br> | |
104 | + * | |
105 | + * @return 終了している場合はtrueを返す | |
106 | + */ | |
107 | +bool CVoiceElement::IsFinished(void) const | |
108 | +{ | |
109 | + if (_IsFinished() == true) { | |
110 | + return true; | |
111 | + } | |
112 | + if (m_bFinishRequest == true && IsFade() == false) { | |
113 | + return true; | |
114 | + } | |
115 | + | |
116 | + return false; | |
117 | +} | |
118 | + | |
119 | + | |
120 | +//! フェードの中断 | |
121 | +void CVoiceElement::AbortFade(void) | |
122 | +{ | |
123 | + if (IsFade() == true) { | |
124 | + m_cVolume.ResetTime(); | |
125 | + m_bVolumeFade = false; | |
126 | + } | |
127 | +} | |
128 | + | |
129 | + | |
130 | +void CVoiceElement::InternalFade(INT32 nVolume, INT32 nTick, INT32 nStartVolume) | |
131 | +{ | |
132 | + INT32 nSave = m_cVolume.GetNowValue(); | |
133 | + SetVolume(nVolume, nTick, nStartVolume); | |
134 | + m_cVolume.SetNowValue(nSave); | |
135 | + m_bVolumeFade = false; | |
136 | +} | |
137 | + | |
138 | + | |
139 | +INT32 CVoiceElement::InternalGetVolume(void) const | |
140 | +{ | |
141 | + return (m_cVolume.IsFade() == true && m_bVolumeFade == false)? m_cVolume.GetCalcValue() : m_cVolume.GetNowValue(); | |
142 | +} | |
143 | + | |
144 | + | |
145 | +INT32 CVoiceElement::CalculateSubVolume(void) const | |
146 | +{ | |
147 | + INT32 nSubVol = cnVoiceElm_VolumeMax; | |
148 | +#if defined(NUM_OF_SUB_VOLUME) && (NUM_OF_SUB_VOLUME > 0) | |
149 | + for (int i = 0; i < NUM_OF_SUB_VOLUME; i++) { | |
150 | + nSubVol *= m_cSubVolume[i].GetNowValue() / cnVoiceElm_VolumeRange; | |
151 | + } | |
152 | +#endif | |
153 | + | |
154 | + return nSubVol; | |
155 | +} | |
156 | + | |
157 | + | |
158 | +//! ボリュームの取得 | |
159 | +/** | |
160 | + * 現在のボリュームを取得します。<br> | |
161 | + * | |
162 | + * @return ボリューム値 | |
163 | + */ | |
164 | +INT32 CVoiceElement::GetVolume(void) const | |
165 | +{ | |
166 | + return m_cVolume.GetNowValue(); | |
167 | +} | |
168 | + | |
169 | + | |
170 | +//! ボリュームの設定 | |
171 | +/** | |
172 | + * ボリュームの設定を行います。<br> | |
173 | + * | |
174 | + * @param nVolume [in] ボリューム(0 〜 100) | |
175 | + * @param nTick [in] 遷移時間[tick] | |
176 | + * @param nStartVolume [in] スタートボリューム(省略した場合は現在値を参照) | |
177 | + */ | |
178 | +void CVoiceElement::SetVolume(INT32 nVolume, INT32 nTick, INT32 nStartVolume) | |
179 | +{ | |
180 | + VolumeClamp(nVolume); | |
181 | + m_bVolumeFade = false; | |
182 | + if (nTick > 0) { | |
183 | + if (nStartVolume < 0) { | |
184 | + nStartVolume = InternalGetVolume(); | |
185 | + } | |
186 | + m_cVolume.SetParam(nTick, nStartVolume, nVolume); | |
187 | + m_bVolumeFade = true; | |
188 | + nVolume = nStartVolume; | |
189 | + } else { | |
190 | + m_cVolume.ResetTime(); | |
191 | + } | |
192 | + | |
193 | + m_cVolume.SetNowValue(nVolume); | |
194 | + _SetVolume(nVolume * m_snGlobalVol * CalculateSubVolume() / (cnVoiceElm_VolumeRange * cnVoiceElm_VolumeRange)); | |
195 | +} | |
196 | + | |
197 | + | |
198 | +//! サブボリュームの取得 | |
199 | +/** | |
200 | + * サブボリューム値の取得を行います。 | |
201 | + * | |
202 | + * @param nChannel [in] サブボリュームチャンネル | |
203 | + * | |
204 | + * @return ボリューム値 | |
205 | + */ | |
206 | +INT32 CVoiceElement::GetSubVolume(UINT32 nChannel) const | |
207 | +{ | |
208 | +#if defined(NUM_OF_SUB_VOLUME) && (NUM_OF_SUB_VOLUME > 0) | |
209 | + return (nChannel < NUM_OF_SUB_VOLUME)? m_cSubVolume[nChannel].GetNowValue() : cnVoiceElm_VolumeMax; | |
210 | +#else | |
211 | + return cnVoiceElm_VolumeMax; | |
212 | +#endif | |
213 | +} | |
214 | + | |
215 | + | |
216 | +//! サブボリュームの設定 | |
217 | +/** | |
218 | + * サブボリューム値の設定を行います。 | |
219 | + * | |
220 | + * @param nChannel [in] サブボリュームチャンネル | |
221 | + * @param nVolume [in] ボリューム | |
222 | + * @param nTick [in] 遷移時間[tick] | |
223 | + * @param nStartVolume [in] スタートボリューム(省略した場合は現在値を参照) | |
224 | + */ | |
225 | +void CVoiceElement::SetSubVolume(UINT32 nChannel, INT32 nVolume, INT32 nTick, INT32 nStartVolume) | |
226 | +{ | |
227 | +#if defined(NUM_OF_SUB_VOLUME) && (NUM_OF_SUB_VOLUME > 0) | |
228 | + if (nChannel >= NUM_OF_SUB_VOLUME) return; | |
229 | + | |
230 | + VolumeClamp(nVolume); | |
231 | + if (nTick > 0) { | |
232 | + if (nStartVolume < 0) { | |
233 | + nStartVolume = (m_cSubVolume[nChannel].IsFade() == true)? m_cSubVolume[nChannel].GetCalcValue() : m_cSubVolume[nChannel].GetNowValue(); | |
234 | + } | |
235 | + m_cSubVolume[nChannel].SetParam(nTick, nStartVolume, nVolume); | |
236 | + nVolume = nStartVolume; | |
237 | + } else { | |
238 | + m_cSubVolume[nChannel].ResetTime(); | |
239 | + } | |
240 | + m_cSubVolume[nChannel].SetNowValue(nVolume); | |
241 | + ReflectMasterVolume(); | |
242 | +#endif | |
243 | +} | |
244 | + | |
245 | + | |
246 | +//! パンの取得 | |
247 | +/** | |
248 | + * 現在のパンを取得します。<br> | |
249 | + * | |
250 | + * @return パン値 | |
251 | + */ | |
252 | +INT32 CVoiceElement::GetPan(void) const | |
253 | +{ | |
254 | + return m_cPanpot.GetNowValue(); | |
255 | +} | |
256 | + | |
257 | + | |
258 | +//! パンの設定 | |
259 | +/** | |
260 | + * パンの設定を行います。<br> | |
261 | + * | |
262 | + * @param nPan [in] パン | |
263 | + * @param nTick [in] 遷移時間 | |
264 | + * @param nStartPan [in] スタートパン(省略した場合は現在値を参照) | |
265 | + */ | |
266 | +void CVoiceElement::SetPan(INT32 nPan, INT32 nTick, INT32 nStartPan) | |
267 | +{ | |
268 | + nPan = (nPan < cnVoiceElm_PanpotMaxL)? cnVoiceElm_PanpotMaxL : ((nPan > cnVoiceElm_PanpotMaxR)? cnVoiceElm_PanpotMaxR : nPan); | |
269 | + if (nTick > 0) { | |
270 | + if (nStartPan < cnVoiceElm_PanpotMaxL || nStartPan > cnVoiceElm_PanpotMaxR) { | |
271 | + nStartPan = m_cPanpot.GetNowValue(); | |
272 | + } | |
273 | + m_cPanpot.SetParam(nTick, nStartPan, nPan); | |
274 | + nPan = nStartPan; | |
275 | + } else { | |
276 | + m_cPanpot.ResetTime(); | |
277 | + } | |
278 | + m_cPanpot.SetNowValue(nPan); | |
279 | + _SetPan(nPan); | |
280 | +} | |
281 | + | |
282 | + | |
283 | +//! 再生 | |
284 | +/** | |
285 | + * 再生を行います。<br> | |
286 | + * nLoopに0を指定すると、1回演奏して終了します。<br> | |
287 | + * nLoopInSampleを0以外に設定すると、ループする際のループ開始ポイントを設定できます。<br> | |
288 | + * 単位はサンプルです。<br> | |
289 | + * | |
290 | + * @param nLoop [in] ループ回数(LOOP_INFINITYで無限ループ) | |
291 | + * @param nVolume [in] ボリューム | |
292 | + * @param nFadeInTick [in] フェードイン時間[tick] | |
293 | + * @param nLoopInSample [in] ループインサンプル | |
294 | + */ | |
295 | +void CVoiceElement::Play(UINT32 nLoop, INT32 nVolume, INT32 nFadeInTick, UINT32 nLoopInSample) | |
296 | +{ | |
297 | + Stop(); | |
298 | + SetVolume(nVolume, nFadeInTick, 0); | |
299 | + m_cVolume.SetNowValue(nVolume); | |
300 | + m_bVolumeFade = false; | |
301 | + | |
302 | + _Play(nLoop, nLoopInSample); | |
303 | +} | |
304 | + | |
305 | + | |
306 | +//! 停止 | |
307 | +/** | |
308 | + * 停止します。<br> | |
309 | + * フェードアウト時間が0でない場合、フェード終了後停止します。<br> | |
310 | + * | |
311 | + * @param fFadeOutTime [in] フェードアウト時間 | |
312 | + */ | |
313 | +void CVoiceElement::Stop(INT32 nFadeOutTick) | |
314 | +{ | |
315 | + m_bPauseRequest = false; | |
316 | + if (nFadeOutTick > 0 && m_bPause == false) { | |
317 | + InternalFade(0, nFadeOutTick); | |
318 | + m_bFinishRequest = true; | |
319 | + } else { | |
320 | + AbortFade(); | |
321 | + m_bPause = false; | |
322 | + _Stop(); | |
323 | + } | |
324 | +} | |
325 | + | |
326 | + | |
327 | +//! 一時停止 | |
328 | +/** | |
329 | + * 一時停止します。<br> | |
330 | + * | |
331 | + * @param bPause [in] ポーズする場合はtrue、ポーズを解除する場合はfalse | |
332 | + * @param fFadeTime [in] フェード時間 | |
333 | + */ | |
334 | +void CVoiceElement::Pause(bool bPause, INT32 nFadeTick) | |
335 | +{ | |
336 | + if (bPause == true) { | |
337 | + // ポーズする | |
338 | + if (m_bPauseRequest == false && IsPause() == false) { | |
339 | + m_bFinishRequest = false; | |
340 | + if (nFadeTick > 0) { | |
341 | + InternalFade(0, nFadeTick); | |
342 | + m_bPauseRequest = true; | |
343 | + } else { | |
344 | + AbortFade(); | |
345 | + _Pause(); | |
346 | + m_bPause = true; | |
347 | + } | |
348 | + } | |
349 | + } else { | |
350 | + // ポーズ解除する | |
351 | + if (m_bPauseRequest == true || IsPause() == true) { | |
352 | + if (IsPause() == true) { | |
353 | + m_bPause = false; | |
354 | + InternalFade(m_cVolume.GetNowValue(), nFadeTick, 0); | |
355 | + _Pause(); | |
356 | + } else { | |
357 | + InternalFade(m_cVolume.GetNowValue(), nFadeTick); | |
358 | + } | |
359 | + m_bPauseRequest = false; | |
360 | + } | |
361 | + } | |
362 | +} | |
363 | + | |
364 | + | |
365 | +/* Bottom of VoiceElement.cpp */ |
@@ -0,0 +1,69 @@ | ||
1 | +/** | |
2 | +*/ | |
3 | + | |
4 | +#ifndef ___AYAME_H___ | |
5 | +#define ___AYAME_H___ | |
6 | + | |
7 | + | |
8 | +//==================================================================== | |
9 | +// INCLUDE | |
10 | +//==================================================================== | |
11 | +#include <tchar.h> | |
12 | +#include <dsound.h> | |
13 | + | |
14 | + | |
15 | +//==================================================================== | |
16 | +// DEFINE | |
17 | +//==================================================================== | |
18 | +#define AYAME_LOOP_INFINITY (0xFFFFFFFF) ///< 無限ループ | |
19 | +#define AYAME_LOADFLAG_GLOBAL (1<<0) | |
20 | +#define AYAME_LOADFLAG_STREAM (0<<1) | |
21 | +#define AYAME_LOADFLAG_STATIC (1<<1) | |
22 | + | |
23 | + | |
24 | +//==================================================================== | |
25 | +// TYPEDEF | |
26 | +//==================================================================== | |
27 | +typedef bool (*AYAME_INITIALIZE_PROC)( HWND hWnd, void **ppDirectSound ); ///< ドライバー初期化 | |
28 | +typedef void (*AYAME_UNINITIALIZE_PROC)( void ); ///< ドライバー解放 | |
29 | +typedef bool (*AYAME_GETERROR_PROC)( TCHAR *pErrStr, unsigned long Size ); ///< エラー取得 | |
30 | +typedef class CAyame *(*AYAME_CREATE_FROM_FILE_PROC)( const TCHAR *pFileName, unsigned long Start, unsigned long Size, unsigned long Flag ); ///< ドライバーからオブジェクト取得 | |
31 | +typedef class CAyame *(*AYAME_CREATE_FROM_MEMORY_PROC)( const void *pData, unsigned long Size, unsigned long Flag ); ///< ドライバーからオブジェクト取得 | |
32 | +typedef class CAyame *(*AYAME_CREATE_FROM_URL_PROC)( const TCHAR *pUrl, unsigned long Start, unsigned long Size, unsigned long Flag ); ///< ドライバーからオブジェクト取得 | |
33 | + | |
34 | + | |
35 | +//==================================================================== | |
36 | +/** | |
37 | + @brief 再生用クラス | |
38 | + | |
39 | + DLL用に純粋仮想関数だけの中身のないクラスです | |
40 | +*/ | |
41 | +//==================================================================== | |
42 | +class CAyame | |
43 | +{ | |
44 | +protected: | |
45 | + virtual ~CAyame(){} ///< デストラクタ | |
46 | +public: | |
47 | + virtual bool __stdcall IsEmptyQueue( void ) = 0; ///< キューに登録があるかどうか | |
48 | + virtual bool __stdcall IsReady( void ) const = 0; ///< 初期化完了済みか? | |
49 | + virtual bool __stdcall IsNotDecoder( void ) const = 0; ///< デコーダが存在するかどうか? | |
50 | + virtual bool __stdcall IsPlayStarted( void ) const = 0; ///< 再生開始チェック | |
51 | + virtual bool __stdcall IsPlay( void ) const = 0; ///< 再生チェック | |
52 | + virtual bool __stdcall IsPause( void ) const = 0; ///< 一時停止中チェック | |
53 | + virtual bool __stdcall Prefetch( void ) = 0; ///< プリフェッチ | |
54 | + virtual bool __stdcall Predecode( void ) = 0; ///< プリデコード | |
55 | + virtual bool __stdcall Combine( CAyame *pAyame ) = 0; ///< 連結 | |
56 | + virtual bool __stdcall Play( unsigned long nLoopCount = 0, unsigned long nLoopInSample = 0, unsigned long nStartPos = 0 ) = 0; ///< 再生 | |
57 | + virtual bool __stdcall Stop( void ) = 0; ///< 停止 | |
58 | + virtual bool __stdcall Pause( void ) = 0; ///< 一時停止 | |
59 | + virtual bool __stdcall SetVolume( float fParam ) = 0; ///< ボリューム設定 | |
60 | + virtual bool __stdcall SetPan( float fParam ) = 0; ///< パン設定 | |
61 | + virtual bool __stdcall SetFrequencyRate( float fParam ) = 0; ///< 周波数設定 | |
62 | + virtual bool __stdcall Release( void ) = 0; ///< 解放 | |
63 | + virtual bool __stdcall AddRef( void ) = 0; ///< 参照カウントインクリメント | |
64 | + virtual unsigned long __stdcall GetPlayingPoint( void ) = 0; ///< おおよその再生位置の取得 | |
65 | + virtual unsigned long __stdcall GetTotalSample( void ) = 0; ///< トータルのサンプル数の取得 | |
66 | +}; | |
67 | + | |
68 | + | |
69 | +#endif // ___AYAME_H___ |
@@ -0,0 +1,95 @@ | ||
1 | +/** | |
2 | + * @file VoiceElementAyame.h | |
3 | + * @brief 単一のサウンド管理クラス | |
4 | + * @author Sazanta | |
5 | + */ | |
6 | +#ifndef __VOICEELEMENTAYAME_H__ | |
7 | +#define __VOICEELEMENTAYAME_H__ | |
8 | + | |
9 | +// インクルード | |
10 | +#include "AyameManager.h" | |
11 | +#include "VoiceElement.h" | |
12 | + | |
13 | + | |
14 | +//! CVoiceElementAyameクラス | |
15 | +/** | |
16 | + * このクラスは、Ayameを使用したCVoiceElement操作を提供する派生クラスです。<br> | |
17 | + */ | |
18 | +class CVoiceElementAyame : public CVoiceElement | |
19 | +{ | |
20 | + public: | |
21 | + static CAyameManager m_sAyameMgr; | |
22 | + | |
23 | + protected: | |
24 | + CAyame* m_pAyame; //!< Ayameインスタンス | |
25 | + | |
26 | + void _Release(void) { | |
27 | + if (m_pAyame != NULL) { | |
28 | + m_pAyame->Release(); | |
29 | + m_pAyame = NULL; | |
30 | + } | |
31 | + } | |
32 | + void _SetPan(INT32 nPan) { | |
33 | + if (m_pAyame != NULL) { | |
34 | + m_pAyame->SetPan((FLOAT)nPan * 100.0f * (1.0f / 128.0f)); | |
35 | + } | |
36 | + } | |
37 | + void _SetVolume(INT32 nVolume) { | |
38 | + if (m_pAyame != NULL) { | |
39 | + m_pAyame->SetVolume((FLOAT)nVolume * 100.0f * (1.0f / 128.0f)); | |
40 | + } | |
41 | + } | |
42 | + void _Pause(void) { | |
43 | + if (m_pAyame != NULL) { | |
44 | + m_pAyame->Pause(); | |
45 | + } | |
46 | + } | |
47 | + bool _Load(const char* pSoundData, UINT32 nDataSize, UINT32 nFlag) { | |
48 | + m_pAyame = m_sAyameMgr.CreateInstanceFromMemory(pSoundData, nDataSize, nFlag); | |
49 | + return (m_pAyame != NULL); | |
50 | + } | |
51 | + bool _Load(const TCHAR* pFileName, UINT32 nFlag, UINT32 nOffset, UINT32 nDataSize) { | |
52 | + m_pAyame = m_sAyameMgr.CreateInstanceFromFile((TCHAR*)pFileName, nOffset, nDataSize, nFlag); | |
53 | + return (m_pAyame != NULL); | |
54 | + } | |
55 | + bool _IsPlaying(void) const { | |
56 | + if (m_pAyame == NULL) return false; | |
57 | + return m_pAyame->IsPlay(); | |
58 | + } | |
59 | + bool _IsFinished(void) const { | |
60 | + if (m_pAyame == NULL) return true; | |
61 | + return (IsPause() == false && m_pAyame->IsPlay() == false); | |
62 | + } | |
63 | + void _Play(UINT32 nLoop, UINT32 nLoopInSample = 0, UINT32 nStartPos = 0) { | |
64 | + if (m_pAyame != NULL) { | |
65 | + m_pAyame->Play(nLoop, nLoopInSample, nStartPos); | |
66 | + } | |
67 | + } | |
68 | + void _Stop(void){ | |
69 | + if (m_pAyame != NULL) { | |
70 | + m_pAyame->Stop(); | |
71 | + } | |
72 | + } | |
73 | + void _Prefetch(void){ | |
74 | + if (m_pAyame != NULL) { | |
75 | + m_pAyame->Prefetch(); | |
76 | + } | |
77 | + } | |
78 | + void _Predecode(void){ | |
79 | + if (m_pAyame != NULL) { | |
80 | + m_pAyame->Predecode(); | |
81 | + } | |
82 | + } | |
83 | + | |
84 | + public: | |
85 | + CVoiceElementAyame(void) { | |
86 | + m_pAyame = NULL; | |
87 | + } | |
88 | + virtual ~CVoiceElementAyame(void) { | |
89 | +// _Release(); | |
90 | + } | |
91 | +}; | |
92 | + | |
93 | + | |
94 | +#endif // __VOICEELEMENTAYAME_H__ | |
95 | +/* Bottom of VoiceElementAyame.h */ |
@@ -0,0 +1,897 @@ | ||
1 | +/** | |
2 | + * @brief MIDI再生クラス | |
3 | + * @file midi.cpp | |
4 | + * @author sazanta | |
5 | + */ | |
6 | +#include "midi.h" | |
7 | + | |
8 | +#pragma comment (lib, "winmm.lib") | |
9 | + | |
10 | + | |
11 | +//! MIDIイベント保持クラス | |
12 | +class CMidiEvent | |
13 | +{ | |
14 | + public: | |
15 | + CMidiEvent* lpNext; // 次のイベントへのポインタ | |
16 | + unsigned long dwDelta; // デルタタイム | |
17 | + unsigned char state; // ステータスバイト | |
18 | + unsigned char data1; // 第一データバイト | |
19 | + unsigned char data2; // 第二データバイト | |
20 | + unsigned char type; // タイプ | |
21 | + unsigned char* lpData; // 可変長データ | |
22 | + long nData; // データ長 | |
23 | + | |
24 | + CMidiEvent(void) { | |
25 | + lpNext = NULL; | |
26 | + lpData = NULL; | |
27 | + nData = 0; | |
28 | + } | |
29 | + ~CMidiEvent(void) { | |
30 | + delete[] lpData; | |
31 | + } | |
32 | +}; | |
33 | + | |
34 | + | |
35 | +//! コンストラクタ | |
36 | +CMidi::CMidi(void) | |
37 | +{ | |
38 | + m_pHeader = NULL; | |
39 | + m_nAttribute = 0; | |
40 | + m_nTime = 120 * 1000; | |
41 | + m_nTimeBias = 1000; | |
42 | + m_nTempo = 500000; | |
43 | + m_nTimerID = 0; | |
44 | + m_hMidiOut = NULL; | |
45 | + for (int i = 0; i < 16; i++) { | |
46 | + m_nPanpot[i] = 64; | |
47 | + m_nVolume[i] = 100; | |
48 | + } | |
49 | + m_nMasterVolume = 256; | |
50 | + m_nMuteFlag = 0; | |
51 | + m_nMasterPanpot = 64; | |
52 | + | |
53 | + // クリティカルセクションの生成 | |
54 | + ::InitializeCriticalSection(&m_stCriticalSection); | |
55 | + ::ZeroMemory(&m_stQue, sizeof(m_stQue)); | |
56 | + m_hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); | |
57 | + m_bEnd = false; | |
58 | + // 処理スレッド生成 | |
59 | + m_hThread = ::CreateThread(NULL, 0, ThreadProc, this, 0, &m_nThreadId); | |
60 | +} | |
61 | + | |
62 | + | |
63 | +//! デストラクタ | |
64 | +CMidi::~CMidi(void) | |
65 | +{ | |
66 | + // クリティカルセクションの破棄 | |
67 | + ::DeleteCriticalSection(&m_stCriticalSection); | |
68 | + ::CloseHandle(m_hEvent); | |
69 | +} | |
70 | + | |
71 | + | |
72 | +//! MIDIデータの解放 | |
73 | +void CMidi::Release(void) | |
74 | +{ | |
75 | + ::EnterCriticalSection(&m_stCriticalSection); | |
76 | + m_bEnd = true; | |
77 | + ::SetEvent(m_hEvent); | |
78 | + ::LeaveCriticalSection( &m_stCriticalSection ); | |
79 | + ::WaitForSingleObject(m_hThread, INFINITE); | |
80 | +} | |
81 | + | |
82 | + | |
83 | +//! MIDIファイルのアタッチ | |
84 | +/** | |
85 | + * MIDIファイルのアタッチを行います。 | |
86 | + * | |
87 | + * @param pFileName [in] SMFファイルパス | |
88 | + * @param nOfs [in] SMFファイルオフセット | |
89 | + * @param nSize [in] SMFファイルサイズ(0の場合は自動計算) | |
90 | + * | |
91 | + * @return 正しくアタッチできた場合はtrueを返す | |
92 | + */ | |
93 | +bool CMidi::AttachMidiFile(const TCHAR* pFileName, unsigned int nOfs, unsigned int nSize) | |
94 | +{ | |
95 | + EVENT_QUE Que = { EVT_LOAD }; | |
96 | + | |
97 | + size_t nLen; | |
98 | +#ifdef _UNICODE | |
99 | + nLen = (wcslen(pFileName) + 1) * 2; | |
100 | +#else | |
101 | + nLen = strlen(pFileName) + 1; | |
102 | +#endif | |
103 | + Que.Data_Load.pData = new char[nLen]; | |
104 | + ::CopyMemory(Que.Data_Load.pData, pFileName, nLen); | |
105 | + Que.Data_Load.nSize = nSize; | |
106 | + Que.Data_Load.nStart = nOfs; | |
107 | + Que.Data_Load.nType = 1; | |
108 | + | |
109 | + // キューに追加 | |
110 | + return AddQue(&Que); | |
111 | +} | |
112 | + | |
113 | + | |
114 | +//! MIDIデータのアタッチ | |
115 | +/** | |
116 | + * MIDIデータのアタッチを行います。 | |
117 | + * 関数を抜けた後は、データを削除しても構いません。 | |
118 | + * | |
119 | + * @param pData [in] SMFデータポインタ | |
120 | + * @param nSize [in] SMFデータサイズ | |
121 | + * | |
122 | + * @return 正しくアタッチできた場合はtrueを返す | |
123 | + */ | |
124 | +bool CMidi::AttachMidiData(const unsigned char* pData, unsigned int nSize) | |
125 | +{ | |
126 | + EVENT_QUE Que = { EVT_LOAD }; | |
127 | + | |
128 | + Que.Data_Load.pData = new char[nSize]; | |
129 | + if (Que.Data_Load.pData == NULL) return false; | |
130 | + ::CopyMemory(Que.Data_Load.pData, pData, nSize); | |
131 | + Que.Data_Load.nSize = nSize; | |
132 | + Que.Data_Load.nType = 0; | |
133 | + | |
134 | + // キューに追加 | |
135 | + return AddQue(&Que); | |
136 | +} | |
137 | + | |
138 | + | |
139 | +//! 再生 | |
140 | +bool CMidi::Play(unsigned long nLoop) | |
141 | +{ | |
142 | + EVENT_QUE Que = { EVT_PLAY }; | |
143 | + | |
144 | + Que.Data_Play.nStartPos = 0; | |
145 | + Que.Data_Play.nLoopCount = nLoop; | |
146 | + | |
147 | + // キューに追加 | |
148 | + return AddQue(&Que); | |
149 | +} | |
150 | + | |
151 | + | |
152 | +//! 停止 | |
153 | +bool CMidi::Stop(void) | |
154 | +{ | |
155 | + EVENT_QUE Que = { EVT_STOP }; | |
156 | + | |
157 | + // キューに追加 | |
158 | + return AddQue(&Que); | |
159 | +} | |
160 | + | |
161 | + | |
162 | +//! ポーズ | |
163 | +bool CMidi::Pause(void) | |
164 | +{ | |
165 | + EVENT_QUE Que = { EVT_PAUSE }; | |
166 | + | |
167 | + // キューに追加 | |
168 | + return AddQue(&Que); | |
169 | +} | |
170 | + | |
171 | + | |
172 | +//! ミュートフラグの設定 | |
173 | +/** | |
174 | + * @param nFlag [in] ミュートするチャンネルの論理和 | |
175 | + * | |
176 | + * @return 設定を行う前の値 | |
177 | + */ | |
178 | +unsigned short CMidi::SetMuteFlag(unsigned short nFlag) | |
179 | +{ | |
180 | + unsigned short nOld = m_nMuteFlag; | |
181 | + if (m_nMuteFlag != nFlag) { | |
182 | + m_nMuteFlag = nFlag; | |
183 | + m_nAttr_UpdateV = 1; | |
184 | + } | |
185 | + | |
186 | + return nOld; | |
187 | +} | |
188 | + | |
189 | + | |
190 | +//! ミュートフラグの設定 | |
191 | +unsigned short CMidi::SetMuteFlag(unsigned int nChannel, bool bFlag) | |
192 | +{ | |
193 | + unsigned short nOld = m_nMuteFlag; | |
194 | + if (nChannel < 16) { | |
195 | + if (bFlag == false) m_nMuteFlag &= ~(1<<nChannel); | |
196 | + else m_nMuteFlag |= (1<<nChannel); | |
197 | + } | |
198 | + if (m_nMuteFlag != nOld) { | |
199 | + m_nAttr_UpdateV = 1; | |
200 | + } | |
201 | + | |
202 | + return nOld; | |
203 | +} | |
204 | + | |
205 | + | |
206 | +//! MIDIヘッダのチェック | |
207 | +/** | |
208 | + * @return MIDIヘッダが正しければtrueを返す | |
209 | + */ | |
210 | +bool CMidi::CheckMidiHeader(const unsigned char* pData) | |
211 | +{ | |
212 | + if (pData[0] != 'M' || pData[1] != 'T' || pData[2] != 'h' || pData[3] != 'd') { | |
213 | + return false; | |
214 | + } | |
215 | + pData += 4; | |
216 | + if (pData[0] != 0x00 || pData[1] != 0x00 || pData[2] != 0x00 || pData[3] != 0x06) { | |
217 | + return false; | |
218 | + } | |
219 | + | |
220 | + return true; | |
221 | +} | |
222 | + | |
223 | + | |
224 | +bool CMidi::AddQue(EVENT_QUE* pQue) | |
225 | +{ | |
226 | + // クリティカルセクションに入る | |
227 | + ::EnterCriticalSection(&m_stCriticalSection); | |
228 | + if (m_bEnd == true) { | |
229 | + // クリティカルセクションから抜ける | |
230 | + ::LeaveCriticalSection( &m_stCriticalSection ); | |
231 | + return true; | |
232 | + } | |
233 | + | |
234 | + bool bRet = false; | |
235 | + int nSlot = 0; | |
236 | + for (; ((nSlot < EVT_MAX) && (m_stQue[nSlot].bUse == true)); nSlot++) { | |
237 | + if (m_stQue[nSlot].EventType == pQue->EventType) { | |
238 | + // 先発の同じ要求は却下する | |
239 | + if (m_stQue[nSlot].EventType == EVT_LOAD && m_stQue[nSlot].Data_Load.nType != 0) { | |
240 | + delete[] m_stQue[nSlot].Data_Load.pData; | |
241 | + } | |
242 | + ::MoveMemory(&m_stQue[nSlot], &m_stQue[nSlot + 1], sizeof(EVENT_QUE) * (EVT_MAX - (nSlot + 1))); | |
243 | + m_stQue[EVT_MAX - 1].bUse = false; | |
244 | + nSlot--; | |
245 | + } | |
246 | + } | |
247 | + // キューは一杯ではない | |
248 | + if (nSlot != EVT_MAX) { | |
249 | + // データコピー | |
250 | + ::CopyMemory(&m_stQue[nSlot], pQue, sizeof(EVENT_QUE)); | |
251 | + // 使用中フラグON | |
252 | + m_stQue[nSlot].bUse = true; | |
253 | + // プレイキューの場合は、フラグを立てる | |
254 | + if (pQue->EventType == EVT_PLAY) m_nIsPlayQue = true; | |
255 | + // イベントON | |
256 | + ::SetEvent(m_hEvent); | |
257 | + // 処理成功 | |
258 | + bRet = true; | |
259 | + } | |
260 | + | |
261 | + // クリティカルセクションから抜ける | |
262 | + ::LeaveCriticalSection(&m_stCriticalSection); | |
263 | + | |
264 | + return bRet; | |
265 | +} | |
266 | + | |
267 | + | |
268 | +//! データの解放 | |
269 | +void CMidi::_Release(void) | |
270 | +{ | |
271 | + _Stop(); | |
272 | + if (m_pHeader != NULL) { | |
273 | + ReleaseMain(m_pHeader); | |
274 | + m_pHeader = NULL; | |
275 | + } | |
276 | +} | |
277 | + | |
278 | + | |
279 | +bool CMidi::_AttachMidiFile(const char* pFileName, unsigned int nOfs, unsigned int nSize) | |
280 | +{ | |
281 | + HANDLE hFile = ::CreateFile((LPCTSTR)pFileName, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | |
282 | + if (hFile == INVALID_HANDLE_VALUE) { | |
283 | + return false; | |
284 | + } | |
285 | + if (nSize == 0) { | |
286 | + nSize = ::GetFileSize(hFile, NULL); | |
287 | + if (nSize <= nOfs) { | |
288 | + ::CloseHandle(hFile); | |
289 | + return false; | |
290 | + } | |
291 | + nSize -= nOfs; | |
292 | + } | |
293 | + | |
294 | + char* pBuff = new char[nSize]; | |
295 | + if (pBuff == NULL) { | |
296 | + ::CloseHandle(hFile); | |
297 | + return false; | |
298 | + } | |
299 | + | |
300 | + if (::SetFilePointer(hFile, nOfs, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { | |
301 | + delete[] pBuff; | |
302 | + ::CloseHandle(hFile); | |
303 | + return false; | |
304 | + } | |
305 | + unsigned long nReadSize; | |
306 | + ::ReadFile(hFile, pBuff, nSize, &nReadSize, NULL); | |
307 | + ::CloseHandle(hFile); | |
308 | + if (nReadSize != nSize) { | |
309 | + delete[] pBuff; | |
310 | + return false; | |
311 | + } | |
312 | + bool bRet = _AttachMidiData((const unsigned char*)pBuff, nSize); | |
313 | + delete[] pBuff; | |
314 | + | |
315 | + return bRet; | |
316 | +} | |
317 | + | |
318 | + | |
319 | +bool CMidi::_AttachMidiData(const unsigned char* pData, unsigned int nSize) | |
320 | +{ | |
321 | + if (nSize <= 14 || CheckMidiHeader(pData) == false) { | |
322 | + // データ不正 | |
323 | + return false; | |
324 | + } | |
325 | + | |
326 | + _Release(); | |
327 | + unsigned char* pTmpData = (unsigned char*)(pData + 8); | |
328 | + unsigned short nFormat = ((unsigned short)pTmpData[0] << 8) | pTmpData[1]; | |
329 | + pTmpData += 2; | |
330 | + | |
331 | + unsigned short nTrack = ((unsigned short)pTmpData[0] << 8) | pTmpData[1]; | |
332 | + pTmpData += 2; | |
333 | + | |
334 | + unsigned short nTime = ((unsigned short)pTmpData[0] << 8) | pTmpData[1]; | |
335 | + if (nTime == 0 || nTrack > 128) { | |
336 | + // データ不正 | |
337 | + return false; | |
338 | + } | |
339 | + pTmpData += 2; | |
340 | + nSize -= 14; | |
341 | + | |
342 | + CMidiEvent* lpaEvent[128]; | |
343 | + for (int i = 0; i < nTrack; i++) { | |
344 | + if (ReadTrack(&pTmpData, &lpaEvent[i], nSize) == false) { | |
345 | + // トラック読み込み失敗 | |
346 | + for (int j = 0; j < (i + 1); j++) { | |
347 | + ReleaseMain(lpaEvent[j]); | |
348 | + } | |
349 | + return false; | |
350 | + } | |
351 | + } | |
352 | + | |
353 | + if (nFormat == 1 && nTrack != 1) { | |
354 | + if ((m_pHeader = Marge(lpaEvent, nTrack)) == NULL) { | |
355 | + return false; | |
356 | + } | |
357 | + } else { | |
358 | + m_pHeader = lpaEvent[0]; | |
359 | + } | |
360 | + m_nTempo = 500000; | |
361 | + m_nTime = nTime * 1000; | |
362 | + m_nTotalTime = CalcTotalTime(); | |
363 | + | |
364 | + return true; | |
365 | +} | |
366 | + | |
367 | + | |
368 | +bool CMidi::_Play(unsigned int nLoop) | |
369 | +{ | |
370 | + if (m_pNowPoint != NULL || m_pHeader == NULL) { | |
371 | + return false; | |
372 | + } | |
373 | + MMRESULT mr; | |
374 | + if (m_hMidiOut == NULL) { | |
375 | + mr = midiOutOpen(&m_hMidiOut, MIDIMAPPER, 0, 0, CALLBACK_NULL); | |
376 | + if (mr != MMSYSERR_NOERROR) { | |
377 | + return false; | |
378 | + } | |
379 | + } | |
380 | + TIMECAPS stCaps; | |
381 | + mr = timeGetDevCaps(&stCaps, sizeof(TIMECAPS)); | |
382 | + if (mr != TIMERR_NOERROR) { | |
383 | + return false; | |
384 | + } | |
385 | + m_pNowPoint = m_pHeader; | |
386 | + m_nTempo = 500000; // テンポのデフォルト値 | |
387 | + m_nNextTime = 0; | |
388 | + m_nPrevTime = timeGetTime(); | |
389 | + m_nNowTime = 0; | |
390 | + m_nNowTimeRest = 0; | |
391 | + m_nTimeRest = 0; | |
392 | + m_nLoop = nLoop; | |
393 | + for (int i = 0; i < 16; i++) { | |
394 | + m_nVolume[i] = 100; | |
395 | + } | |
396 | + m_nAttr_UpdateV = 1; | |
397 | + m_nAttr_UpdateP = 1; | |
398 | + m_nIsPlayQue = 0; | |
399 | + m_nTimerID = timeSetEvent(stCaps.wPeriodMin, 0, TimeCallbackProc, (unsigned long)this, TIME_PERIODIC); | |
400 | + if (m_nTimerID == NULL) { | |
401 | + return false; | |
402 | + } | |
403 | + | |
404 | + return true; | |
405 | +} | |
406 | + | |
407 | + | |
408 | +bool CMidi::_Stop(void) | |
409 | +{ | |
410 | + if (m_nTimerID != 0) { | |
411 | + timeKillEvent(m_nTimerID); | |
412 | + m_nTimerID = 0; | |
413 | + } | |
414 | + if (m_hMidiOut != NULL) { | |
415 | + midiOutReset(m_hMidiOut); // 再生中の音を消す | |
416 | + midiOutClose(m_hMidiOut); | |
417 | + m_hMidiOut = NULL; | |
418 | + } | |
419 | + m_nNowTime = 0; | |
420 | + m_pNowPoint = NULL; | |
421 | + m_nAttr_Pause = 0; | |
422 | + | |
423 | + return true; | |
424 | +} | |
425 | + | |
426 | + | |
427 | +void CMidi::_Pause(void) | |
428 | +{ | |
429 | + if (m_pNowPoint != NULL) { | |
430 | + m_nAttr_Pause = (m_nAttr_Pause == 0)? 1 : 0; | |
431 | + midiOutReset(m_hMidiOut); // 再生中の音を消す | |
432 | + m_nAttr_UpdateV = 1; | |
433 | + } | |
434 | +} | |
435 | + | |
436 | + | |
437 | +//! イベントクラスの解放 | |
438 | +void CMidi::ReleaseMain(CMidiEvent* pEvent) | |
439 | +{ | |
440 | + while (pEvent != NULL) { | |
441 | + CMidiEvent* pTmp = pEvent; | |
442 | + pEvent = pEvent->lpNext; | |
443 | + delete pTmp; | |
444 | + } | |
445 | +} | |
446 | + | |
447 | + | |
448 | +//! トラックの解析 | |
449 | +bool CMidi::ReadTrack(unsigned char** ppData, CMidiEvent** lplpEvent, unsigned int& nSize) | |
450 | +{ | |
451 | + unsigned char* pData = *ppData; | |
452 | + unsigned char statePrev = 0; // 前のイベントのステータスバイト | |
453 | + CMidiEvent* lpEvent; | |
454 | + | |
455 | + if (pData[0] != 'M' || pData[1] != 'T' || pData[2] != 'r' || pData[3] != 'k' || nSize <= 8) { | |
456 | + // データ不正 | |
457 | + *lplpEvent = NULL; | |
458 | + return false; | |
459 | + } | |
460 | + nSize -= 4 + 4; | |
461 | + unsigned long nLen = pData[7] | ((unsigned long)pData[6] << 8) | ((unsigned long)pData[5] << 16) | ((unsigned long)pData[4] << 24); | |
462 | + if (nLen > nSize) { | |
463 | + // データ不正 | |
464 | + *lplpEvent = NULL; | |
465 | + return false; | |
466 | + } | |
467 | + pData += 4 + 4; | |
468 | + | |
469 | + lpEvent = new CMidiEvent(); // 最初のイベントのメモリを確保 | |
470 | + *lplpEvent = lpEvent; // lplpEventは常に最初のイベントを指す | |
471 | + while (1) { | |
472 | + // デルタタイムを読み込む | |
473 | + if ((pData = ReadDelta(pData, lpEvent->dwDelta, nSize)) == NULL) { | |
474 | + // データ不正 | |
475 | + return false; | |
476 | + } | |
477 | + | |
478 | + lpEvent->state = *pData; | |
479 | + if ((lpEvent->state & 0x80) == 0) { // ランニングステータスか | |
480 | + lpEvent->state = statePrev; // 一つ前のイベントのステータスバイトを代入 | |
481 | + } else { | |
482 | + pData++; | |
483 | + nSize--; | |
484 | + } | |
485 | + | |
486 | + switch (lpEvent->state & 0xf0) { // ステータスバイトを基にどのイベントか判別 | |
487 | + case 0x80: | |
488 | + case 0x90: | |
489 | + case 0xa0: | |
490 | + case 0xb0: | |
491 | + case 0xe0: | |
492 | + lpEvent->data1 = *pData++; | |
493 | + lpEvent->data2 = *pData++; | |
494 | + nSize -= 2; | |
495 | + break; | |
496 | + case 0xc0: | |
497 | + case 0xd0: | |
498 | + lpEvent->data1 = *pData++; | |
499 | + lpEvent->data2 = 0; | |
500 | + nSize--; | |
501 | + break; | |
502 | + | |
503 | + case 0xf0: | |
504 | + unsigned long dw; | |
505 | + if (lpEvent->state == 0xf0 || lpEvent->state == 0xf7) { | |
506 | + // SysExイベント | |
507 | + if ((pData = ReadDelta(pData, dw, nSize)) == NULL) { | |
508 | + return false; | |
509 | + } | |
510 | + if (lpEvent->state == 0xf0) dw++; | |
511 | + lpEvent->nData = dw; | |
512 | + lpEvent->lpData = new unsigned char[dw]; | |
513 | + if (lpEvent->state == 0xf0) { | |
514 | + lpEvent->lpData[0] = 0xf0; // 可変長データの先頭は0xF0 | |
515 | + dw--; | |
516 | + memcpy(lpEvent->lpData + 1, pData, dw); | |
517 | + } else { | |
518 | + memcpy(lpEvent->lpData, pData, dw); | |
519 | + } | |
520 | + pData += dw; | |
521 | + nSize -= dw; | |
522 | + } else if (lpEvent->state == 0xff) { | |
523 | + // メタイベント | |
524 | + lpEvent->type = *pData++; // typeの取得 | |
525 | + nSize--; | |
526 | + if ((pData = ReadDelta(pData, dw, nSize)) == NULL) { | |
527 | + // データ不正 | |
528 | + return false; | |
529 | + } | |
530 | + | |
531 | + lpEvent->nData = dw; | |
532 | + if (dw != 0) { | |
533 | + lpEvent->lpData = new unsigned char[dw]; | |
534 | + memcpy(lpEvent->lpData, pData, dw); | |
535 | + pData += dw; | |
536 | + nSize -= dw; | |
537 | + } | |
538 | + | |
539 | + if (lpEvent->type == 0x2f) { | |
540 | + // トラックの終端 | |
541 | + *ppData = pData; | |
542 | + return true; | |
543 | + } | |
544 | + } | |
545 | + break; | |
546 | + | |
547 | + default: | |
548 | + // ステータス不正 | |
549 | + return false; | |
550 | + } | |
551 | + statePrev = lpEvent->state; | |
552 | + | |
553 | + lpEvent->lpNext = new CMidiEvent(); | |
554 | + lpEvent = lpEvent->lpNext; | |
555 | + if (lpEvent == NULL) { | |
556 | + // メモリ不足 | |
557 | + return false; | |
558 | + } | |
559 | + } | |
560 | + | |
561 | + return false; | |
562 | +} | |
563 | + | |
564 | + | |
565 | +//! デルタタイムの取得 | |
566 | +unsigned char* CMidi::ReadDelta(unsigned char* pData, unsigned long& nDelta, unsigned int& nSize) | |
567 | +{ | |
568 | + nDelta = 0; | |
569 | + for (int i = 0; i < sizeof(unsigned long); i++) { | |
570 | + unsigned char nTmp = *pData++; | |
571 | + if (nSize-- == 0) { | |
572 | + return NULL; | |
573 | + } | |
574 | + nDelta = (nDelta << 7) | (nTmp & 0x7f); | |
575 | + if ((nTmp & 0x80) == 0) { | |
576 | + break; // MSBが立っていないならば、次のバイトはデルタタイムではないので抜ける | |
577 | + } | |
578 | + } | |
579 | + | |
580 | + return pData; | |
581 | +} | |
582 | + | |
583 | + | |
584 | +//! トラックデータのマージ | |
585 | +CMidiEvent* CMidi::Marge(CMidiEvent** lplpEvent, unsigned short nTrack) | |
586 | +{ | |
587 | + unsigned long dwPrevAbsolute = 0; // 一つ前の絶対時間 | |
588 | + CMidiEvent* lpEvent = new CMidiEvent(); // 現在のイベント | |
589 | + unsigned long* lpdwTotal = new unsigned long[nTrack]; // 各トラックの絶対時間 | |
590 | + CMidiEvent** pEventBase = new CMidiEvent*[nTrack]; | |
591 | + | |
592 | + if (lpEvent == NULL || lpdwTotal == NULL || pEventBase == NULL) { | |
593 | + // メモリ不足 | |
594 | + delete lpEvent; | |
595 | + delete[] lpdwTotal; | |
596 | + delete[] pEventBase; | |
597 | + return NULL; | |
598 | + } | |
599 | + | |
600 | + memcpy(pEventBase, lplpEvent, sizeof(CMidiEvent*) * nTrack); | |
601 | + memset(lpdwTotal, 0, sizeof(unsigned long) * nTrack); | |
602 | + CMidiEvent* lpHead = lpEvent; | |
603 | + while (1) { | |
604 | + int nIndex = -1; // トラックのインデックス | |
605 | + unsigned long dwAbsolute = (unsigned long)-1; // 絶対時間(0xFFFFFFFF) | |
606 | + | |
607 | + // 最も絶対時間が低いイベントを見つける | |
608 | + for (int i = 0; i < nTrack; i++) { | |
609 | + if (pEventBase[i]->lpNext == NULL) { | |
610 | + continue; // トラックの終端まで走査した | |
611 | + } | |
612 | + | |
613 | + if ((lpdwTotal[i] + pEventBase[i]->dwDelta) < dwAbsolute) { | |
614 | + nIndex = i; | |
615 | + dwAbsolute = lpdwTotal[i] + pEventBase[i]->dwDelta; | |
616 | + } | |
617 | + } | |
618 | + | |
619 | + if (nIndex == -1) { | |
620 | + // 全てのトラックを走査したのでTrackOfEndを付加する | |
621 | + lpEvent->state = 0xff; | |
622 | + lpEvent->data1 = 0x2f; | |
623 | + lpEvent->data2 = 0x00; | |
624 | + lpEvent->dwDelta = 0; | |
625 | + break; | |
626 | + } | |
627 | + | |
628 | + if (pEventBase[nIndex]->state != 0xff || pEventBase[nIndex]->data1 != 0x2f) { // TrackOfEndははじく | |
629 | + lpEvent->state = pEventBase[nIndex]->state; | |
630 | + lpEvent->data1 = pEventBase[nIndex]->data1; | |
631 | + lpEvent->data2 = pEventBase[nIndex]->data2; | |
632 | + lpEvent->type = pEventBase[nIndex]->type; | |
633 | + lpEvent->nData = pEventBase[nIndex]->nData; | |
634 | + lpEvent->dwDelta = dwAbsolute - dwPrevAbsolute; | |
635 | + if (lpEvent->nData != 0) { | |
636 | + lpEvent->lpData = new unsigned char[lpEvent->nData]; | |
637 | + if (lpEvent->lpData == NULL) { | |
638 | + // メモリ不足 | |
639 | + ReleaseMain(lpHead); | |
640 | + for (int j = 0; j < nTrack; j++) { | |
641 | + ReleaseMain(pEventBase[j]); | |
642 | + } | |
643 | + delete[] lpdwTotal; | |
644 | + delete[] pEventBase; | |
645 | + return NULL; | |
646 | + } | |
647 | + memcpy(lpEvent->lpData, pEventBase[nIndex]->lpData, lpEvent->nData); | |
648 | + } | |
649 | + lpEvent->lpNext = new CMidiEvent(); | |
650 | + lpEvent = lpEvent->lpNext; | |
651 | + if (lpEvent == NULL) { | |
652 | + // メモリ不足 | |
653 | + ReleaseMain(lpHead); | |
654 | + for (int j = 0; j < nTrack; j++) { | |
655 | + ReleaseMain(pEventBase[j]); | |
656 | + } | |
657 | + delete[] lpdwTotal; | |
658 | + delete[] pEventBase; | |
659 | + return NULL; | |
660 | + } | |
661 | + } | |
662 | + dwPrevAbsolute = dwAbsolute; | |
663 | + lpdwTotal[nIndex] += pEventBase[nIndex]->dwDelta; // 各トラックの絶対時間を更新 | |
664 | + pEventBase[nIndex] = pEventBase[nIndex]->lpNext; | |
665 | + } | |
666 | + for (int j = 0; j < nTrack; j++) { | |
667 | + ReleaseMain(lplpEvent[j]); | |
668 | + } | |
669 | + delete[] pEventBase; | |
670 | + delete[] lpdwTotal; | |
671 | + | |
672 | + return lpHead; | |
673 | +} | |
674 | + | |
675 | + | |
676 | +//! 総時間の計算 | |
677 | +unsigned long CMidi::CalcTotalTime(void) const | |
678 | +{ | |
679 | + if (m_pHeader == NULL) { | |
680 | + return 0; | |
681 | + } | |
682 | + unsigned long nTotalTime = 0; | |
683 | + unsigned long nTempo = 500000; | |
684 | + unsigned long nRest = 0; | |
685 | + unsigned long nTime = m_nTime; | |
686 | + CMidiEvent* pData = m_pHeader; | |
687 | + while (pData != NULL) { | |
688 | + unsigned long nDelta = pData->dwDelta; | |
689 | + if (pData->state == 0xff && pData->type == 0x51) { | |
690 | + // セットテンポ | |
691 | + nTempo = (unsigned long)(pData->lpData[2] | ((unsigned long)pData->lpData[1] << 8) | ((unsigned long)pData->lpData[0] << 16)); | |
692 | + } | |
693 | + if (nDelta != 0 ) { | |
694 | + unsigned long nTmp = (nDelta * nTempo) + nRest; | |
695 | + unsigned long nMtime = nTmp / nTime; | |
696 | + nRest = nTmp % nTime; | |
697 | + nTotalTime += nMtime; | |
698 | + } | |
699 | + pData = pData->lpNext; | |
700 | + } | |
701 | + | |
702 | + return nTotalTime; | |
703 | +} | |
704 | + | |
705 | + | |
706 | +//! コールバック関数 | |
707 | +void CALLBACK CMidi::TimeCallbackProc(unsigned int nID, unsigned int nMsg, unsigned long nUser, unsigned long nReserve1, unsigned long nReserve2) | |
708 | +{ | |
709 | + CMidi* pClass = (CMidi*)nUser; | |
710 | + | |
711 | + if (pClass == NULL || pClass->m_pNowPoint == NULL) return; | |
712 | + | |
713 | + unsigned long nTime = timeGetTime(); | |
714 | + if (pClass->m_nAttr_Pause != 0) { | |
715 | + // ポーズ中 | |
716 | + pClass->m_nPrevTime = nTime; | |
717 | + return; | |
718 | + } | |
719 | + | |
720 | + // ボリューム設定 | |
721 | + if (pClass->m_nAttr_UpdateV != 0) { | |
722 | + for (int i = 0; i < 16; i++) { | |
723 | + unsigned long nVol; | |
724 | + if ((pClass->m_nMuteFlag & (1<<i)) == 0) { | |
725 | + nVol = ((pClass->m_nMasterVolume * pClass->m_nVolume[i]) << 8) & 0x7f0000; | |
726 | + } else { | |
727 | + nVol = 0; | |
728 | + } | |
729 | + midiOutShortMsg(pClass->m_hMidiOut, nVol | (0x07 << 8) | (0xb0 + i)); | |
730 | + } | |
731 | + pClass->m_nAttr_UpdateV = 0; | |
732 | + } | |
733 | + // パンポット設定 | |
734 | + if (pClass->m_nAttr_UpdateP != 0) { | |
735 | + for (int i = 0; i < 16; i++) { | |
736 | + long nPan = (long)pClass->m_nPanpot[i] + ((pClass->m_nMasterPanpot * 2) - 128); | |
737 | + nPan = (nPan < 0)? 0 : ((nPan > 127)? 127 : nPan); | |
738 | + midiOutShortMsg(pClass->m_hMidiOut, (nPan << 16) | (0x0a << 8) | (0xb0 + i)); | |
739 | + } | |
740 | + pClass->m_nAttr_UpdateP = 0; | |
741 | + } | |
742 | + | |
743 | + unsigned long nTmp = (((nTime - pClass->m_nPrevTime) * pClass->m_nTimeBias) + pClass->m_nNowTimeRest); | |
744 | + pClass->m_nNowTimeRest = (nTmp % 1000); | |
745 | + pClass->m_nNowTime += nTmp / 1000; | |
746 | + pClass->m_nPrevTime = nTime; | |
747 | + while (1) { | |
748 | + if (pClass->m_nNextTime > pClass->m_nNowTime) break; | |
749 | + if (pClass->m_pNowPoint->state == 0xff) { | |
750 | + // メタイベント | |
751 | + if (pClass->m_pNowPoint->type == 0x51) { | |
752 | + // セットテンポ | |
753 | + pClass->m_nTempo = (unsigned long)(pClass->m_pNowPoint->lpData[2] | ((unsigned long)pClass->m_pNowPoint->lpData[1] << 8) | ((unsigned long)pClass->m_pNowPoint->lpData[0] << 16)); | |
754 | + } | |
755 | + } else if (pClass->m_pNowPoint->state == 0xf0) { | |
756 | + // SysExイベント | |
757 | + MIDIHDR mh = {0}; | |
758 | + | |
759 | + mh.lpData = (LPSTR)pClass->m_pNowPoint->lpData; | |
760 | + mh.dwFlags = 0; | |
761 | + mh.dwBufferLength = pClass->m_pNowPoint->nData; | |
762 | + mh.dwBytesRecorded = pClass->m_pNowPoint->nData; | |
763 | + | |
764 | + midiOutPrepareHeader(pClass->m_hMidiOut, &mh, sizeof(MIDIHDR)); | |
765 | + midiOutLongMsg(pClass->m_hMidiOut, &mh, sizeof(MIDIHDR)); | |
766 | + while ((mh.dwFlags & MHDR_DONE) == 0); | |
767 | + midiOutUnprepareHeader(pClass->m_hMidiOut, &mh, sizeof(MIDIHDR)); | |
768 | + } else { | |
769 | + // MIDIイベント | |
770 | + unsigned char nStatus = pClass->m_pNowPoint->state; | |
771 | + unsigned long nData2 = pClass->m_pNowPoint->data2; | |
772 | + if ((nStatus & 0xf0) == 0xb0) { | |
773 | + int nCh = (nStatus & 0x0f); | |
774 | + if (pClass->m_pNowPoint->data1 == 0x07) { | |
775 | + // ボリューム | |
776 | + pClass->m_nVolume[nCh] = (unsigned short)nData2; | |
777 | + if ((pClass->m_nMuteFlag & (1<<nCh)) != 0) { | |
778 | + // ミュート | |
779 | + nData2 = 0; | |
780 | + } else { | |
781 | + // ベロシティ計算 | |
782 | + nData2 *= pClass->m_nMasterVolume; | |
783 | + nData2 >>= 8; | |
784 | + } | |
785 | + } else if (pClass->m_pNowPoint->data1 == 0x0a) { | |
786 | + // パンポット | |
787 | + pClass->m_nPanpot[nCh] = (unsigned char)nData2; | |
788 | + long nPan = (long)nData2 + ((pClass->m_nMasterPanpot * 2) - 128); | |
789 | + nPan = (nPan < 0)? 0 : ((nPan > 127)? 127 : nPan); | |
790 | + nData2 = (unsigned long)nPan; | |
791 | + } | |
792 | + } | |
793 | + unsigned long dwMsg = (unsigned long)(nStatus | ((unsigned long)pClass->m_pNowPoint->data1 << 8) | (nData2 << 16)); | |
794 | + midiOutShortMsg(pClass->m_hMidiOut, dwMsg); | |
795 | + } | |
796 | + pClass->m_pNowPoint = pClass->m_pNowPoint->lpNext; | |
797 | + if (pClass->m_pNowPoint != NULL) { | |
798 | + unsigned long nTmp = (pClass->m_pNowPoint->dwDelta * pClass->m_nTempo) + pClass->m_nTimeRest; | |
799 | + unsigned long nMtime = nTmp / pClass->m_nTime; | |
800 | + pClass->m_nTimeRest = nTmp % pClass->m_nTime; | |
801 | + if (nMtime != 0) { | |
802 | + pClass->m_nNextTime += nMtime; | |
803 | + } | |
804 | + } else { | |
805 | + // データ終了 | |
806 | + if ((pClass->m_nLoop + 1) > 1) { | |
807 | + pClass->m_nLoop--; | |
808 | + } | |
809 | + if (pClass->m_nLoop == 0) { | |
810 | + timeKillEvent(pClass->m_nTimerID); | |
811 | + pClass->m_nTimerID = 0; | |
812 | + break; | |
813 | + } else { | |
814 | + pClass->m_nNextTime = 0; | |
815 | + pClass->m_nNowTime = 0; | |
816 | + pClass->m_nNowTimeRest = 0; | |
817 | + pClass->m_nTimeRest = 0; | |
818 | + pClass->m_pNowPoint = pClass->m_pHeader; | |
819 | + } | |
820 | + } | |
821 | + } | |
822 | +} | |
823 | + | |
824 | + | |
825 | +void CMidi::ThreadMain() | |
826 | +{ | |
827 | + while (true) { | |
828 | + // イベント待ち | |
829 | + unsigned long Result = ::WaitForSingleObject(m_hEvent, INFINITE); | |
830 | + | |
831 | + if (m_bEnd == true) return; | |
832 | + | |
833 | + while (true) { | |
834 | + // クリティカルセクションに入る | |
835 | + ::EnterCriticalSection(&m_stCriticalSection); | |
836 | + | |
837 | + if (m_stQue[0].bUse == false) { | |
838 | + // クリティカルセクションから抜けてループからも抜ける | |
839 | + ::LeaveCriticalSection(&m_stCriticalSection); | |
840 | + break; | |
841 | + } | |
842 | + | |
843 | + EVENT_QUE Que; | |
844 | + EVENT_QUE* pQue = &Que; | |
845 | + ::CopyMemory(pQue, &m_stQue[0], sizeof(EVENT_QUE)); | |
846 | + | |
847 | + // キューを詰める | |
848 | + ::MoveMemory(&m_stQue[0], &m_stQue[1], sizeof(EVENT_QUE) * (EVT_MAX - 1)); | |
849 | + m_stQue[EVT_MAX - 1].bUse = false; | |
850 | + | |
851 | + // クリティカルセクションから抜ける | |
852 | + ::LeaveCriticalSection(&m_stCriticalSection); | |
853 | + // イベント処理 | |
854 | + switch (pQue->EventType) { | |
855 | + case EVT_LOAD: | |
856 | + if ( pQue->Data_Load.nType == 0 ) { | |
857 | + _AttachMidiData((unsigned char*)pQue->Data_Load.pData, pQue->Data_Load.nSize); | |
858 | + } else if ( pQue->Data_Load.nType == 1 ) { | |
859 | + _AttachMidiFile(pQue->Data_Load.pData, pQue->Data_Load.nStart, pQue->Data_Load.nSize); | |
860 | + } | |
861 | + delete[] pQue->Data_Load.pData; | |
862 | + break; | |
863 | + case EVT_PLAY: | |
864 | + _Play(pQue->Data_Play.nLoopCount); | |
865 | + break; | |
866 | + case EVT_STOP: | |
867 | + _Stop(); | |
868 | + break; | |
869 | + case EVT_PAUSE: | |
870 | + _Pause(); | |
871 | + break; | |
872 | + case EVT_RELEASE: | |
873 | + return; | |
874 | + } | |
875 | + } | |
876 | + } | |
877 | +} | |
878 | + | |
879 | + | |
880 | +//! スレッド処理 | |
881 | +unsigned long CALLBACK CMidi::ThreadProc(void* pParameter) | |
882 | +{ | |
883 | + CMidi* pMidi = (CMidi*)pParameter; | |
884 | + | |
885 | + // メイン関数呼び出し | |
886 | + pMidi->ThreadMain(); | |
887 | + pMidi->_Release(); | |
888 | + | |
889 | + // メモリから解放 | |
890 | + delete pMidi; | |
891 | + | |
892 | + // スレッド終了 | |
893 | + ExitThread(0); | |
894 | +} | |
895 | + | |
896 | + | |
897 | +/* Bottom of midi.h */ |