OSDN Git Service

リセット
[momiji/momiji_main.git] / Core / Momiji.Core.Midi.In.cpp
1 /*
2 [momiji music component library]
3 ---------------------------------------------------------------------
4 Momiji.Core.Midi.In.cpp
5         midi input component.
6 ---------------------------------------------------------------------
7 Copyright (C) 2011 tyiki badwell {miria@users.sourceforge.jp}.
8
9 This program is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program.  If not, see <http://www.gnu.org/licenses/gpl-3.0.html>.
21 ---------------------------------------------------------------------
22 */
23 #include "StdAfx.h"
24
25 #include "Momiji.Interop.Winmm.h"
26 #include "Momiji.Core.Midi.In.h"
27 #include "Momiji.Core.Midi.Data.h"
28
29 namespace Momiji {
30 namespace Core {
31 namespace Midi {
32 namespace In {
33
34         void Device::OnEventHandler(
35                 System::Object^ sender,
36                 Interop::Winmm::DriverCallBack::DriverEventArgs^ args
37         )
38         {
39                 #ifdef _DEBUG
40                         System::Console::WriteLine("[{0}] [{1}] start", __FUNCTION__, System::Threading::Thread::CurrentThread->GetHashCode());
41                 #endif
42
43                 switch (args->uMsg)
44                 {
45                 case Interop::Winmm::DriverCallBack::MM_EXT_WINDOW_MESSAGE::MIM_OPEN:
46                         {
47                                 this->DoOpen(args->dw1, args->dw2);
48                                 break;
49                         }
50                 case Interop::Winmm::DriverCallBack::MM_EXT_WINDOW_MESSAGE::MIM_CLOSE:
51                         {
52                                 this->DoClose(args->dw1, args->dw2);
53                                 break;
54                         }
55                 case Interop::Winmm::DriverCallBack::MM_EXT_WINDOW_MESSAGE::MIM_DATA:
56                         {
57                                 this->DoData(args->dw1, args->dw2);
58                                 break;
59                         }
60                 case Interop::Winmm::DriverCallBack::MM_EXT_WINDOW_MESSAGE::MIM_LONGDATA:
61                         {
62                                 this->DoLongData(args->dw1, args->dw2);
63                                 break;
64                         }
65                 case Interop::Winmm::DriverCallBack::MM_EXT_WINDOW_MESSAGE::MIM_ERROR:
66                         {
67                                 this->DoError(args->dw1, args->dw2);
68                                 break;
69                         }
70                 case Interop::Winmm::DriverCallBack::MM_EXT_WINDOW_MESSAGE::MIM_LONGERROR:
71                         {
72                                 this->DoLongError(args->dw1, args->dw2);
73                                 break;
74                         }
75                 case Interop::Winmm::DriverCallBack::MM_EXT_WINDOW_MESSAGE::MIM_MOREDATA:
76                         {
77                                 this->DoMoreData(args->dw1, args->dw2);
78                                 break;
79                         }
80                 }
81                 #ifdef _DEBUG
82                         System::Console::WriteLine("[{0}] [{1}] end", __FUNCTION__, System::Threading::Thread::CurrentThread->GetHashCode());
83                 #endif
84         }
85
86         void Device::DoOpen(System::IntPtr dwParam1, System::IntPtr dwParam2)
87         {
88                 #ifdef _DEBUG
89                         System::Console::WriteLine("[{0}] [{1,8:X}][{2,8:X}]", __FUNCTION__, dwParam1, dwParam2);
90                 #endif
91                 this->OnOpen(this, System::EventArgs::Empty);
92         }
93
94         void Device::DoClose(System::IntPtr dwParam1, System::IntPtr dwParam2)
95         {
96                 #ifdef _DEBUG
97                         System::Console::WriteLine("[{0}] [{1,8:X}][{2,8:X}]", __FUNCTION__, dwParam1, dwParam2);
98                 #endif
99                 this->OnClose(this, System::EventArgs::Empty);
100         }
101
102         ///<summary>~</summary>
103         ///<remarks>~</remarks>
104         ///<param>~</param>
105         ///
106         void Device::DoData(System::IntPtr dwParam1, System::IntPtr dwParam2)
107         {
108                 #ifdef _DEBUG
109                         System::Console::WriteLine("[{0}] [{1,8:X}][{2,8:X}]", __FUNCTION__, dwParam1, dwParam2);
110                 #endif
111                 auto data = dwParam1.ToInt32();
112                 auto time = dwParam2.ToInt32();
113                 this->OnData(data, time);
114         }
115
116         void Device::DoLongData(System::IntPtr dwParam1, System::IntPtr dwParam2)
117         {
118                 #ifdef _DEBUG
119                         System::Console::WriteLine("[{0}] [{1,8:X}][{2,8:X}]", __FUNCTION__, dwParam1, dwParam2);
120                 #endif
121
122                 this->Unprepare(dwParam1);
123
124                 auto header = safe_cast<Interop::Winmm::MidiHeader^>(InteropServices::Marshal::PtrToStructure(dwParam1, Interop::Winmm::MidiHeader::typeid));
125
126                 #ifdef _DEBUG
127                         System::Console::WriteLine("[{0}] bytesRecorded {1}", __FUNCTION__, header->bytesRecorded);
128                 #endif
129
130                 if (header->bytesRecorded == 0)
131                 {
132                         #ifdef _DEBUG
133                         System::Console::WriteLine("[{0}] resetされたと判断して、再準備はしない", __FUNCTION__);
134                         #endif
135                 }
136                 else
137                 {
138                         auto data = gcnew array<System::Byte>(header->bytesRecorded);
139
140                         #ifdef _DEBUG
141                                 System::Console::Write("[{0}] ", __FUNCTION__);
142                                 for (System::UInt32 i = 0; i < header->bytesRecorded; i++)
143                                 {
144                                         System::Console::Write("[{0,2:X}]", InteropServices::Marshal::ReadByte(header->data,i));
145                                 }
146                                 System::Console::WriteLine();
147                         #endif
148
149                         InteropServices::Marshal::Copy( header->data, data, 0, data->Length );
150                         auto time = dwParam2.ToInt32();
151                         this->OnLongData(data, time);
152                         this->Prepare();
153                 }
154         }
155
156         void Device::DoError(System::IntPtr dwParam1, System::IntPtr dwParam2)
157         {
158                 #ifdef _DEBUG
159                         System::Console::WriteLine("[{0}] [{1,8:X}][{2,8:X}]", __FUNCTION__, dwParam1, dwParam2);
160                 #endif
161         }
162
163         void Device::DoLongError(System::IntPtr dwParam1, System::IntPtr dwParam2)
164         {
165                 #ifdef _DEBUG
166                         System::Console::WriteLine("[{0}] [{1,8:X}][{2,8:X}]", __FUNCTION__, dwParam1, dwParam2);
167                 #endif
168
169                 this->Unprepare(dwParam1);
170         }
171
172         void Device::DoMoreData(System::IntPtr dwParam1, System::IntPtr dwParam2)
173         {
174                 #ifdef _DEBUG
175                         System::Console::WriteLine("[{0}] [{1,8:X}][{2,8:X}]", __FUNCTION__, dwParam1, dwParam2);
176                 #endif
177
178                 this->Unprepare(dwParam1);
179
180                 auto header = safe_cast<Interop::Winmm::MidiHeader^>(InteropServices::Marshal::PtrToStructure(dwParam1, Interop::Winmm::MidiHeader::typeid));
181
182                 #ifdef _DEBUG
183                         System::Console::WriteLine("[{0}] bytesRecorded {1}", __FUNCTION__, header->bytesRecorded);
184                 #endif
185
186                 auto data = gcnew array<System::Byte>(header->bytesRecorded);
187
188                 #ifdef _DEBUG
189                         System::Console::Write("[{0}] ", __FUNCTION__);
190                         for (System::UInt32 i = 0; i < header->bytesRecorded; i++)
191                         {
192                                 System::Console::Write("[{0,2:X}]", InteropServices::Marshal::ReadByte(header->data,i));
193                         }
194                         System::Console::WriteLine();
195                 #endif
196
197                 InteropServices::Marshal::Copy( header->data, data, 0, data->Length );
198
199                 auto time = dwParam2.ToInt32();
200                 this->OnMoreData(data, time);
201                 this->Prepare();
202         }
203
204         System::UInt32 Device::GetNumDevices()
205         {
206                 return Interop::Winmm::Function::midiInGetNumDevs();
207         }
208
209         Interop::Winmm::MidiInCapabilities^ Device::GetCapabilities(
210                 const System::UInt32 deviceID
211         )
212         {
213                 auto caps = gcnew Interop::Winmm::MidiInCapabilities();
214                 #ifdef _DEBUG
215                         System::Console::WriteLine("[{0}] caps size {1}",__FUNCTION__, InteropServices::Marshal::SizeOf(caps));
216                 #endif
217                 auto mmResult = 
218                         Interop::Winmm::Function::midiInGetDevCaps(
219                                 deviceID, 
220                                 caps, 
221                                 InteropServices::Marshal::SizeOf(caps)
222                         );
223                 if (mmResult != Interop::Winmm::MMRESULT::NOERROR)
224                 {
225                         throw gcnew MidiInException(mmResult);
226                 }
227                 return caps;
228         }
229
230         Device::Device(
231                 const System::UInt32 deviceID
232         ):      _deviceID(deviceID)
233         {
234                 #ifdef _DEBUG
235                         System::Console::WriteLine("[{0}] deviceID {1}",__FUNCTION__, this->_deviceID);
236                 #endif
237                 this->Open();
238         }
239
240         Device::~Device()
241         {
242                 #ifdef _DEBUG
243                         System::Console::WriteLine("[{0}]",__FUNCTION__);
244                 #endif
245                 this->!Device();
246         }
247
248         Device::!Device()
249         {
250                 #ifdef _DEBUG
251                         System::Console::WriteLine("[{0}]",__FUNCTION__);
252                 #endif
253                 this->Close();
254         }
255
256         Interop::Winmm::MidiInCapabilities^ Device::GetCapabilities()
257         {
258                 return Device::GetCapabilities(this->_deviceID);
259         }
260
261         void Device::Open()
262         {
263                 #ifdef _DEBUG
264                         System::Console::WriteLine("[{0}]",__FUNCTION__);
265                 #endif
266
267                 this->_callBack = gcnew Core::Winmm::DriverCallBack(false); //イベントは同期で動かす
268                 this->_callBack->OnEvent += gcnew System::EventHandler<Interop::Winmm::DriverCallBack::DriverEventArgs^>(this, &Device::OnEventHandler);
269
270                 this->_headerPool =
271                         gcnew Core::Buffer::BufferPool<Interop::Winmm::MidiHeader^>(
272                                 2,              //2回分のバッファを用意
273                                 gcnew Core::Buffer::BufferPool<Interop::Winmm::MidiHeader^>::Allocator(this, &Device::AllocateHeader) 
274                         );
275
276                 this->_bufferPool =
277                         gcnew Core::Buffer::BufferPool<array<System::Byte>^>(
278                                 2,              //2回分のバッファを用意
279                                 gcnew Core::Buffer::BufferPool<array<System::Byte>^>::Allocator(this, &Device::AllocateBuffer) 
280                         );
281
282                 {
283                         auto mmResult = 
284                                 Interop::Winmm::Function::midiInOpen(
285                                         this->_handle,
286                                         this->_deviceID,
287                                         this->_callBack->GetDriverCallBackProc(),
288                                         System::IntPtr::Zero,
289                                         Interop::Winmm::DriverCallBack::TYPE::FUNCTION
290                                 );
291                         if (mmResult != Interop::Winmm::MMRESULT::NOERROR) {
292                                 throw gcnew MidiInException(mmResult);
293                         }
294                 }
295
296                 this->Prepare();
297
298                 {
299                         auto mmResult = Interop::Winmm::Function::midiInStart(this->_handle);
300                         if (mmResult != Interop::Winmm::MMRESULT::NOERROR)
301                         {
302                                 throw gcnew MidiInException(mmResult);
303                         }
304                 }
305
306                 #ifdef _DEBUG
307                         System::Console::WriteLine("[{0}] Handle invalid:{1} closed:{2}",__FUNCTION__, this->_handle->IsInvalid, this->_handle->IsClosed);
308                 #endif
309         }
310
311         void Device::Close()
312         {
313                 #ifdef _DEBUG
314                         System::Console::WriteLine("[{0}] Handle invalid:{1} closed:{2}",__FUNCTION__, this->_handle->IsInvalid, this->_handle->IsClosed);
315                 #endif
316                 if (
317                         !this->_handle->IsInvalid
318                 &&      !this->_handle->IsClosed
319                 )
320                 {
321                         {
322                                 auto mmResult = Interop::Winmm::Function::midiInStop(this->_handle);
323                                 if (mmResult != Interop::Winmm::MMRESULT::NOERROR)
324                                 {
325                                         throw gcnew MidiInException(mmResult);
326                                 }
327                                 #ifdef _DEBUG
328                                         System::Console::WriteLine("[{0}] midiInStop OK",__FUNCTION__);
329                                 #endif
330                         }
331
332                         {
333                                 auto mmResult = Interop::Winmm::Function::midiInReset(this->_handle);
334                                 if (mmResult != Interop::Winmm::MMRESULT::NOERROR)
335                                 {
336                                         throw gcnew MidiInException(mmResult);
337                                 }
338                                 #ifdef _DEBUG
339                                         System::Console::WriteLine("[{0}] midiInReset OK",__FUNCTION__);
340                                 #endif
341                         }
342
343                         //バッファの開放待ち
344                         while(this->_headerPool->IsBusy())
345                         {
346                                 #ifdef _DEBUG
347                                         System::Console::WriteLine("[{0}] wait for unprepare headers ...",__FUNCTION__);
348                                 #endif
349                                 System::Threading::Thread::Sleep(5);
350                         }
351
352                         this->_handle->Close();
353                 }
354                 else
355                 {
356                         #ifdef _DEBUG
357                                 System::Console::WriteLine("[{0}] openしていない状態なので、無視します。", __FUNCTION__);
358                         #endif
359                 }
360
361                 if (this->_callBack != nullptr)
362                 {
363                         this->_callBack->OnEvent -= gcnew System::EventHandler<Interop::Winmm::DriverCallBack::DriverEventArgs^>(this, &Device::OnEventHandler);
364                         delete this->_callBack;
365                 }
366
367                 if (this->_headerPool != nullptr)
368                 {
369                         delete this->_headerPool;
370                 }
371
372                 if (this->_bufferPool != nullptr)
373                 {
374                         delete this->_bufferPool;
375                 }
376         }
377
378         System::IntPtr Device::Prepare()
379         {
380                 #ifdef _DEBUG
381                         System::Console::WriteLine("[{0}]",__FUNCTION__);
382                 #endif
383
384                 auto header = this->_headerPool->Get();
385                 auto buffer = this->_bufferPool->Get();
386
387                 {
388                         auto midiHeader = header->GetBuffer();
389                         midiHeader->bufferLength = buffer->GetBuffer()->Length;
390                         midiHeader->data = buffer->GetBufferIntPtr();
391
392                         #ifdef _DEBUG
393                                 {
394                                         System::Console::WriteLine("[{0}] midiInPrepareHeader before", __FUNCTION__);
395                                         System::Console::WriteLine("[{0}] {1}", __FUNCTION__, midiHeader);
396                                 }
397                         #endif
398                 }
399
400                 {
401                         auto mmResult = 
402                                 Interop::Winmm::Function::midiInPrepareHeader(
403                                         this->_handle,
404                                         header->GetBufferIntPtr(),
405                                         InteropServices::Marshal::SizeOf(Interop::Winmm::MidiHeader::typeid)
406                                 );
407                         if (mmResult != Interop::Winmm::MMRESULT::NOERROR)
408                         {
409                                 this->_headerPool->Release(header);
410                                 this->_bufferPool->Release(buffer);
411
412                                 throw gcnew MidiInException(mmResult);
413                         }
414                 }
415                 #ifdef _DEBUG
416                         {
417                                 auto midiHeader = header->GetBuffer();
418                                 System::Console::WriteLine("[{0}] midiInPrepareHeader after", __FUNCTION__);
419                                 System::Console::WriteLine("[{0}] {1}", __FUNCTION__, midiHeader);
420                         }
421                 #endif
422
423                 {
424                         auto mmResult =
425                                 Interop::Winmm::Function::midiInAddBuffer(
426                                         this->_handle,
427                                         header->GetBufferIntPtr(),
428                                         InteropServices::Marshal::SizeOf(Interop::Winmm::MidiHeader::typeid)
429                                 );
430                         if (mmResult != Interop::Winmm::MMRESULT::NOERROR)
431                         {
432                                 System::Console::WriteLine((gcnew MidiInException(mmResult))->ToString());
433                         }
434                 }
435                 #ifdef _DEBUG
436                         {
437                                 auto midiHeader = header->GetBuffer();
438                                 System::Console::WriteLine("[{0}] midiInAddBuffer after", __FUNCTION__);
439                                 System::Console::WriteLine("[{0}] {1}", __FUNCTION__, midiHeader);
440                         }
441                 #endif
442
443                 return header->GetBufferIntPtr();
444         }
445
446         void Device::Unprepare(System::IntPtr headerPtr)
447         {
448                 #ifdef _DEBUG
449                         System::Console::WriteLine("[{0}]",__FUNCTION__);
450                 #endif
451
452                 auto header = this->_headerPool->GetBusy(headerPtr);
453                 
454                 #ifdef _DEBUG
455                         {
456                                 auto midiHeader = header->GetBuffer();
457                                 System::Console::WriteLine("[{0}] midiInUnprepareHeader before", __FUNCTION__);
458                                 System::Console::WriteLine("[{0}] {1}", __FUNCTION__, midiHeader);
459                         }
460                 #endif
461                         
462                 auto mmResult = 
463                         Interop::Winmm::Function::midiInUnprepareHeader(
464                                 this->_handle,
465                                 headerPtr,
466                                 InteropServices::Marshal::SizeOf(Interop::Winmm::MidiHeader::typeid)
467                         );
468                 if (mmResult != Interop::Winmm::MMRESULT::NOERROR)
469                 {
470                         throw gcnew MidiInException(mmResult);
471                 }
472
473                 auto bufferPtr = header->GetBuffer()->data;
474                 this->_headerPool->Release(header);
475
476                 auto buffer = this->_bufferPool->GetBusy(bufferPtr);
477                 this->_bufferPool->Release(buffer);
478
479                 #ifdef _DEBUG
480                         {
481                                 auto midiHeader = header->GetBuffer();
482                                 System::Console::WriteLine("[{0}] midiInUnprepareHeader after", __FUNCTION__);
483                                 System::Console::WriteLine("[{0}] {1}", __FUNCTION__, midiHeader);
484                         }
485                 #endif
486         }
487
488         Interop::Winmm::MidiHeader^ Device::AllocateHeader()
489         {
490                 return gcnew Interop::Winmm::MidiHeader();
491         }
492
493         array<System::Byte>^ Device::AllocateBuffer()
494         {
495                 return gcnew array<System::Byte>(256); //SMFとして最大長 + 1
496         }
497
498         System::String^ MidiInException::Initialize()
499         {
500                 auto errorMessage = System::String::Empty;
501                 auto buf = gcnew System::Text::StringBuilder(256);
502
503                 auto r = 
504                         Interop::Winmm::Function::midiInGetErrorText(
505                                 this->_mmResult, 
506                                 buf, 
507                                 buf->Capacity
508                         );
509                 if (r == Interop::Winmm::MMRESULT::NOERROR)
510                 {
511                         errorMessage = buf->ToString();
512                 }
513                 else
514                 {
515                         errorMessage = "不明なエラー";
516                 }
517
518                 return errorMessage + "[" + this->_mmResult.ToString("D") + "]";
519         }
520
521 }
522 }
523 }
524 }