OSDN Git Service

キャプチャ部分のバグ修正。
[winaudioj/wasapi2.git] / wasapi2 / async_reader.cpp
1 /*\r
2 ==============================================================================\r
3 \r
4 This file is part of the async\r
5 Copyright 2005-11 by Satoshi Fujiwara.\r
6 \r
7 async can be redistributed and/or modified under the terms of the\r
8 GNU General Public License, as published by the Free Software Foundation;\r
9 either version 2 of the License, or (at your option) any later version.\r
10 \r
11 async is distributed in the hope that it will be useful,\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14 GNU General Public License for more details.\r
15 \r
16 You should have received a copy of the GNU General Public License\r
17 along with async; if not, visit www.gnu.org/licenses or write to the\r
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, \r
19 Boston, MA 02111-1307 USA\r
20 \r
21 ==============================================================================\r
22 */\r
23 \r
24 // SDK\82Ì\83T\83\93\83v\83\8b\82ð\89ü\91¢\82µ\82½\8dì\82Á\82½\82à\82Ì\81B\93à\97e\82Í\82Ù\82Ú\82»\82Ì\82Ü\82Ü\82¾\82ª\81A\94ñ\93¯\8aú\93Ç\82Ý\8d\9e\82Ý\82É\r
25 // \91Î\89\9e\82³\82¹\82½\81B\r
26 \r
27 #include "stdafx.h"\r
28 #if _DEBUG\r
29 #define _CRTDBG_MAP_ALLOC\r
30 #include <crtdbg.h>\r
31 #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)\r
32 #endif\r
33 #include <stdio.h>\r
34 #include <assert.h>\r
35 #include <wtypes.h>\r
36 #include <mmreg.h>\r
37 #include <ks.h>\r
38 #include <ksmedia.h>\r
39 #include "stdafx.h"\r
40 #include "async_reader.h"\r
41 using namespace std;\r
42 \r
43 namespace sf\r
44 {\r
45   typedef struct\r
46   {\r
47     ULONG Riff4CC;      // "RIFF" 4-character code\r
48     ULONG FileSize;     // total file size in bytes\r
49     ULONG Wave4CC;      // "WAVE" 4-character code\r
50     ULONG Fmt4CC;       // "fmt " 4-character code\r
51     ULONG FormatSize;   // wave format size in bytes\r
52   } FileHeader;\r
53 \r
54   typedef struct\r
55   {\r
56     ULONG ChunkType;\r
57     ULONG ChunkSize;\r
58   } ChunkHeader;\r
59 \r
60   // Any file smaller than this cannot possibly contain wave data.\r
61 #define MIN_WAVE_FILE_SIZE (sizeof(FileHeader)+sizeof(PCMWAVEFORMAT)+sizeof(ChunkHeader)+1)\r
62 \r
63   // Macro to build FOURCC from first four characters in ASCII string\r
64 #define FOURCC(s)  ((ULONG)(s[0] | (s[1]<<8) | (s[2]<<16) | (s[3]<<24)))\r
65 \r
66   //\r
67   // Constructor -- Open wave file and parse file header.\r
68   //\r
69   async_reader::async_reader(const std::wstring file_name, bool repeat_mode) \r
70     : \r
71     stream_status_(false),\r
72     data_chunk_position(0),\r
73     total_data_bytes_(0),\r
74     data_bytes_remaining_(0),\r
75     offset_(0),\r
76     reader_repeat_mode_(repeat_mode),\r
77     async_reading_(false)\r
78   {\r
79 \r
80     // \83C\83x\83\93\83g\83I\83u\83W\83F\83N\83g\82Ì\8f\89\8aú\89»\r
81     ZeroMemory(&wfx_, sizeof(wfx_));\r
82     event_.reset(::CreateEventEx(NULL, NULL, CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE));\r
83 \r
84     // OVERLAPPED\8d\\91¢\91Ì\82Ì\8f\89\8aú\89»\r
85     ZeroMemory(&overlapped_,sizeof(overlapped_));\r
86     overlapped_.hEvent = event_.get();\r
87 \r
88 \r
89     try{\r
90       // \83t\83@\83C\83\8b\83I\81[\83v\83\93\r
91       file_.reset(CreateFile(file_name.c_str(),GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY | FILE_FLAG_OVERLAPPED,NULL));\r
92       if(file_.get() == INVALID_HANDLE_VALUE)\r
93       {\r
94         throw win32_error_exception(GetLastError());\r
95       }\r
96 \r
97       // \83t\83@\83C\83\8b\83w\83b\83_\82Ì\93Ç\82Ý\8d\9e\82Ý\r
98       FileHeader fileHdr;\r
99       read_data_sync(reinterpret_cast<BYTE*>(&fileHdr),sizeof(fileHdr));\r
100 \r
101       // \83w\83b\83_\82Ì\83`\83F\83b\83N\r
102       if (fileHdr.Riff4CC != FOURCC("RIFF") ||\r
103         fileHdr.FileSize < MIN_WAVE_FILE_SIZE ||\r
104         fileHdr.Wave4CC != FOURCC("WAVE") ||\r
105         fileHdr.Fmt4CC != FOURCC("fmt ") ||\r
106         fileHdr.FormatSize < sizeof(PCMWAVEFORMAT))\r
107       {\r
108         throw file_error_exception(std::wstring(L"\95s\96¾\82È.WAV\83t\83@\83C\83\8b\83t\83H\81[\83}\83b\83g\82Å\82·\81B"));\r
109       }\r
110 \r
111       // \83t\83H\81[\83}\83b\83g\83f\83X\83N\83\8a\83v\83^\82Ì\93Ç\82Ý\8d\9e\82Ý\r
112       read_data_sync(reinterpret_cast<BYTE*>(&wfx_),min(fileHdr.FormatSize,sizeof(wfx_)));\r
113       //offset += min(fileHdr.FormatSize,sizeof(wfx_));\r
114 \r
115       // Skip over any padding at the end of the format in the format chunk.\r
116       if (fileHdr.FormatSize > sizeof(wfx_))\r
117       {\r
118         offset_ += (fileHdr.FormatSize - sizeof(wfx_));\r
119       }\r
120 \r
121       // If format type is PCMWAVEFORMAT, convert to valid WAVEFORMATEX structure.\r
122       if (wfx_.Format.wFormatTag == WAVE_FORMAT_PCM)\r
123       {\r
124         wfx_.Format.cbSize = 0;\r
125       }\r
126 \r
127       // If format type is WAVEFORMATEX, convert to WAVEFORMATEXTENSIBLE.\r
128       if (wfx_.Format.wFormatTag == WAVE_FORMAT_PCM ||\r
129         wfx_.Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)\r
130       {\r
131         if (wfx_.Format.wFormatTag == WAVE_FORMAT_PCM)\r
132         {\r
133           wfx_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;\r
134         }\r
135         else\r
136         {\r
137           wfx_.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;\r
138         }\r
139         wfx_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;\r
140 \r
141         // Note that the WAVEFORMATEX structure is valid for\r
142         // representing wave formats with only 1 or 2 channels.\r
143         if (wfx_.Format.nChannels == 1)\r
144         {\r
145           wfx_.dwChannelMask = SPEAKER_FRONT_CENTER;\r
146         }\r
147         else if (wfx_.Format.nChannels == 2)\r
148         {\r
149           wfx_.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;\r
150         }\r
151         else\r
152         {\r
153           throw file_error_exception(std::wstring(L"\83T\83|\81[\83g\82µ\82Ä\82¢\82È\82¢.WAV\83t\83@\83C\83\8b\82Å\82·\81B"));\r
154         }\r
155         wfx_.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);\r
156         wfx_.Samples.wValidBitsPerSample = wfx_.Format.wBitsPerSample;\r
157       }\r
158 \r
159       // This wave file reader understands only PCM and IEEE float formats.\r
160       if (wfx_.Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE ||\r
161         wfx_.SubFormat != KSDATAFORMAT_SUBTYPE_PCM &&\r
162         wfx_.SubFormat != KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)\r
163       {\r
164         throw file_error_exception(std::wstring(L"\83T\83|\81[\83g\82µ\82Ä\82¢\82È\82¢.WAV\83t\83@\83C\83\8b\82Å\82·\81B"));\r
165       }\r
166 \r
167       // \83f\81[\83^\83`\83\83\83\93\83N\82ð\8c\9f\8dõ\82·\82é\81B\82»\82Ì\91¼\82Ì\83`\83\83\83\93\83N\82Í\93Ç\82Ý\94ò\82Î\82·\81B\r
168       ChunkHeader chunkHdr;   /// \83`\83\83\83\93\83N\83w\83b\83_\r
169       for (;;)\r
170       {\r
171         // Read header at start of next chunk of file.\r
172         data_chunk_position = offset_;\r
173         read_data_sync(reinterpret_cast<BYTE*>(&chunkHdr),sizeof(ChunkHeader));\r
174         if (chunkHdr.ChunkType == FOURCC("data"))\r
175         {\r
176           break;  // found start of data chunk\r
177         }\r
178       }\r
179 \r
180       // We've found the start of the data chunk. We're reader_ready to start\r
181       // playing wave data...\r
182       total_data_bytes_ = chunkHdr.ChunkSize;\r
183       data_bytes_remaining_ = total_data_bytes_;\r
184       if (total_data_bytes_ == 0)\r
185       {\r
186         throw file_error_exception(std::wstring(L"\83t\83@\83C\83\8b\83T\83C\83Y\82ª\95s\90³\82Å\82·\81B"));\r
187       }\r
188       stream_status_ = true;\r
189     } catch (exception& e) {\r
190       stream_status_ = false;\r
191       throw;\r
192     }\r
193   }\r
194 \r
195   //\r
196   // Destructor\r
197   //\r
198   async_reader::~async_reader()\r
199   {\r
200     // \8ec\82Á\82Ä\82¢\82éI/O\82Í\83L\83\83\83\93\83Z\83\8b\82·\82é\r
201     if(async_reading_){\r
202       DWORD numbytes_copied;\r
203       DWORD res = GetOverlappedResult(file_.get(),&overlapped_,(LPDWORD)&numbytes_copied,FALSE);\r
204       if(!res)\r
205       {\r
206         switch(GetLastError())\r
207         {\r
208         case ERROR_IO_INCOMPLETE:\r
209         case ERROR_IO_PENDING:\r
210           ::CancelIoEx(file_.get(),&overlapped_);\r
211           WaitForSingleObject(event_.get(),INFINITE);\r
212           break;\r
213         }\r
214       }\r
215     }\r
216     \r
217     // \83t\83@\83C\83\8b\82ð\83N\83\8d\81[\83Y\82·\82é\r
218     if (file_)\r
219     {\r
220       file_.release();\r
221       //      fclose(file_);\r
222     }\r
223   }\r
224 \r
225   //\r
226   // Reset the file pointer to the start of the wave data.\r
227   //\r
228   void async_reader::reset_data_position()\r
229   {\r
230     if (!stream_status_ )\r
231     {\r
232       throw file_error_exception(std::wstring(L"\83t\83@\83C\83\8b\82ª\83I\81[\83v\83\93\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ\81B"));\r
233     }\r
234 \r
235     // Read the header for the data chunk.\r
236     ChunkHeader chunkHdr;\r
237     offset_ = data_chunk_position;\r
238     read_data_sync(reinterpret_cast<BYTE*>(&chunkHdr),sizeof(chunkHdr));\r
239 \r
240     // Sanity check: The chunk header shouldn't have changed.\r
241     if (chunkHdr.ChunkType != FOURCC("data") ||\r
242       chunkHdr.ChunkSize != total_data_bytes_)\r
243     {\r
244       throw file_error_exception(std::wstring(L"\95s\90³\82ÈWAV\83t\83@\83C\83\8b\82Å\82·\81B"));\r
245     }\r
246 \r
247     data_bytes_remaining_ = total_data_bytes_;\r
248 \r
249   }\r
250 \r
251   void async_reader::seek(uint64_t pos)\r
252   {\r
253     if (!stream_status_ )\r
254     {\r
255       throw file_error_exception(std::wstring(L"\83t\83@\83C\83\8b\82ª\83I\81[\83v\83\93\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ\81B"));\r
256     }\r
257     pos = (pos / get_wave_format().Format.nBlockAlign) * get_wave_format().Format.nBlockAlign;\r
258     data_bytes_remaining_ = total_data_bytes_ - pos;\r
259     offset_ = pos + data_chunk_position + sizeof(ChunkHeader);\r
260     overlapped_.Pointer = (PVOID)pos;\r
261   }\r
262 \r
263 \r
264   //\r
265   // Load next block of wave data from file into playback buffer.\r
266   // In repeat mode, when we reach the end of the wave data in the\r
267   // file, we just reset the file pointer back to the start of the\r
268   // data and continue filling the caller's buffer until it is full.\r
269   // In single-reader_read_file mode, once we reach the end of the wave data in\r
270   // the file, we just fill the buffer with silence instead of with\r
271   // real data.\r
272   //\r
273   void async_reader::read_data(BYTE *buffer, uint64_t numbytes_to_copy)\r
274   {\r
275     assert(async_reading_ == false);\r
276 \r
277     if (!stream_status_ )\r
278     {\r
279       throw file_error_exception(std::wstring(L"\83t\83@\83C\83\8b\82ª\83I\81[\83v\83\93\82³\82ê\82Ä\82¢\82Ü\82¹\82ñ\81B"));\r
280     }\r
281 \r
282     if (buffer == NULL)\r
283     {\r
284       throw file_error_exception(L"\83o\83b\83t\83@\83A\83h\83\8c\83X\82ª\96³\8cø\82Å\82·\81B");\r
285     }\r
286 \r
287     if (numbytes_to_copy == 0)\r
288     {\r
289       throw file_error_exception(L"\93Ç\82Ý\8d\9e\82Ý\83o\83C\83g\90\94\82Ì\8ew\92è\82ª0\82Å\82·\81B");\r
290     }\r
291 \r
292     ULONG numbytes_copied;\r
293     overlapped_.Pointer = (PVOID)offset_;\r
294     int result = ReadFile(file_.get(),buffer,numbytes_to_copy,&numbytes_copied,&overlapped_);\r
295     if(!result)\r
296     {\r
297       DWORD err = GetLastError(); \r
298       switch(err)\r
299       {\r
300       case ERROR_HANDLE_EOF:\r
301         {\r
302           if(numbytes_to_copy > numbytes_copied)\r
303           {\r
304             BYTE silence = (wfx_.Format.wBitsPerSample==8) ? 0x80 : 0;\r
305             memset(buffer + numbytes_copied, silence, numbytes_to_copy - numbytes_copied);\r
306           }\r
307           offset_ += numbytes_copied; \r
308           async_reading_ = false;\r
309 \r
310           if(reader_repeat_mode_){\r
311             reset_data_position();\r
312           }\r
313           throw file_eof_exception(std::wstring(L"\83t\83@\83C\83\8b\82Ì\8fI\82í\82è\82É\92B\82µ\82½\82½\82ß\81A\82±\82ê\88È\8fã\83f\81[\83^\93Ç\82Ý\8d\9e\82Þ\82±\82Æ\82ª\82Å\82«\82Ü\82¹\82ñ\81B"));\r
314         }\r
315         break;\r
316       case ERROR_IO_PENDING:\r
317         //{\r
318         //  DWORD res = GetOverlappedResult(file_.get(),&overlapped_,&num_of_read,TRUE);\r
319         //  if(!res)\r
320         //  {\r
321         //    throw file_error_exception(win32_error_exception::get_last_error_str());\r
322         //  } else {\r
323         //    if(res != numbytes_to_copy)\r
324         //    {\r
325         //      throw file_error_exception(std::wstring(L"\93Ç\82Ý\8d\9e\82ñ\82¾\83f\81[\83^\83T\83C\83Y\82ª\8d\87\82¢\82Ü\82¹\82ñ\81B"));\r
326         //    }\r
327         //  }\r
328         //}\r
329         async_reading_ = true;\r
330         break;\r
331       default:\r
332         throw file_error_exception(win32_error_exception::get_last_error_str(err));\r
333         break;\r
334       }\r
335     } else {\r
336       data_bytes_remaining_ -= numbytes_copied;\r
337       offset_ += numbytes_copied;\r
338       async_reading_ = false;\r
339       //current += numbytes_copied;\r
340     }\r
341 \r
342 \r
343     //if(numbytes_copied > data_bytes_remaining_)\r
344     //{\r
345     //  if (fread(current, 1, data_bytes_remaining_, file_) != data_bytes_remaining_)\r
346     //  {\r
347     //    throw exception(L"\95s\90³\82ÈWAV\83t\83@\83C\83\8b\82Å\82·\81B");\r
348     //  }\r
349     //  current += data_bytes_remaining_;\r
350     //  numbytes_copied -= data_bytes_remaining_;\r
351     //  data_bytes_remaining_ = 0;\r
352 \r
353     //  // The file pointer now sits at the end of the data chunk.\r
354     //  // Are we operating in repeat mode?\r
355     //  if (!reader_repeat_mode_)\r
356     //  {\r
357     //    // Nope, we're operating in single-reader_read_file mode. Fill\r
358     //    // the rest of the buffer with silence and return.\r
359     //    BYTE silence = (wfx_.Format.wBitsPerSample==8) ? 0x80 : 0;\r
360     //    memset(current, silence, numbytes_copied);\r
361     //    return;  // yup, we're done\r
362     //  }\r
363     //  // Yes, we're operating in repeat mode, so loop back to\r
364     //  // the start of the wave data in the file's data chunk\r
365     //  // and continue loading data into the caller's buffer.\r
366     //  reset_data_position();\r
367 \r
368     //}\r
369 \r
370     //assert(numbytes_copied > 0);\r
371     //assert(numbytes_copied <= data_bytes_remaining_);\r
372 \r
373     //// The remainder of the data chunk is big enough to\r
374     //// completely fill the remainder of the caller's buffer.\r
375     //if (fread(buffer, 1, numbytes_copied, file_) != numbytes_copied)\r
376     //{\r
377     //  throw exception(L"\95s\90³\82ÈWAV\83t\83@\83C\83\8b\82Å\82·\81B");\r
378     //}\r
379     //data_bytes_remaining_ -= numbytes_copied;\r
380     //current += numbytes_copied;\r
381   }\r
382 \r
383   void async_reader::read_data_sync(BYTE *buffer, uint64_t numbytes_to_copy)\r
384   {\r
385     uint32_t numbytes_copied(0);\r
386     overlapped_.Pointer = (PVOID)offset_;\r
387 \r
388     int result = ReadFile(file_.get(),buffer, numbytes_to_copy, (LPDWORD)&numbytes_copied, &overlapped_);\r
389 \r
390     if(!result)\r
391     {\r
392       uint32_t err = GetLastError(); \r
393       switch(err)\r
394       {\r
395       case ERROR_HANDLE_EOF:\r
396         {\r
397           offset_ += numbytes_copied;\r
398           //if(reader_repeat_mode_){\r
399           //  reset_data_position();\r
400           //}\r
401           throw file_eof_exception(std::wstring(L"\83t\83@\83C\83\8b\82Ì\8fI\82í\82è\82É\92B\82µ\82½\82½\82ß\81A\83w\83b\83_\82ð\93Ç\82Ý\8d\9e\82Þ\82±\82Æ\82ª\82Å\82«\82Ü\82¹\82ñ\81B"));\r
402         }\r
403         break;\r
404       case ERROR_IO_PENDING:\r
405         {\r
406           DWORD res = GetOverlappedResult(file_.get(),&overlapped_,(LPDWORD)&numbytes_copied,TRUE);\r
407           if(!res)\r
408           {\r
409             throw file_error_exception(win32_error_exception::get_last_error_str());\r
410           } else {\r
411             if(numbytes_copied != numbytes_to_copy)\r
412             {\r
413               throw file_error_exception(std::wstring(L"\93Ç\82Ý\8d\9e\82ñ\82¾\83f\81[\83^\83T\83C\83Y\82ª\8d\87\82¢\82Ü\82¹\82ñ\81B"));\r
414             }\r
415             offset_ += numbytes_copied;\r
416           }\r
417         }\r
418         break;\r
419       default:\r
420           throw file_error_exception(win32_error_exception::get_last_error_str(err));\r
421         break;\r
422       }\r
423     } else {\r
424       offset_ += numbytes_copied;\r
425     }\r
426   };\r
427 \r
428   void async_reader::wait(int timer)\r
429   {\r
430     if(!async_reading_)\r
431     {\r
432       return;\r
433     }\r
434 \r
435     uint32_t numbytes_copied(0);\r
436     DWORD res = GetOverlappedResult(file_.get(),&overlapped_,(LPDWORD)&numbytes_copied,TRUE);\r
437     // \83G\83\89\81[\83`\83F\83b\83N\r
438     if(!res)\r
439     {\r
440       DWORD err = GetLastError();\r
441       switch (err) \r
442       { \r
443       case ERROR_HANDLE_EOF: \r
444         { \r
445           // Handle an end of file\r
446           //if(reader_repeat_mode_){\r
447           //  reset_data_position();\r
448           //}\r
449           throw file_eof_exception(win32_error_exception::get_last_error_str(err));\r
450           break;\r
451         } \r
452       default:\r
453         {\r
454           throw file_error_exception(win32_error_exception::get_last_error_str(err));\r
455         }\r
456       }\r
457     }\r
458 \r
459     ResetEvent(event_.get());\r
460     offset_ += numbytes_copied;\r
461     data_bytes_remaining_ -= numbytes_copied;\r
462     if(data_bytes_remaining_ <= 0 && reader_repeat_mode_)\r
463     {\r
464       reset_data_position();\r
465     }\r
466     async_reading_ = false;\r
467   };\r
468 \r
469 \r
470 }\r