OSDN Git Service

WASAPIの排他モード・共有モードを切り替えられるようにした。
[winaudioj/async.git] / async / wasapi_exclusive_timer.cpp
1 /*
2   ==============================================================================
3
4    Copyright 2005-11 by Satoshi Fujiwara.
5
6    async can be redistributed and/or modified under the terms of the
7    GNU General Public License, as published by the Free Software Foundation;
8    either version 2 of the License, or (at your option) any later version.
9
10    async is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with async; if not, visit www.gnu.org/licenses or write to the
17    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
18    Boston, MA 02111-1307 USA
19
20   ==============================================================================
21 */
22
23 #include "StdAfx.h"
24 #if _DEBUG
25 #define _CRTDBG_MAP_ALLOC
26 #include <crtdbg.h>
27 #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
28 #endif
29 #include "sf_memory.h"
30 #include "audio_base.h"
31 #include "wasapi.h"
32
33 namespace sf{
34   wasapi_exclusive_timer::wasapi_exclusive_timer(::WAVEFORMATEXTENSIBLE& wfx) 
35     : is_enabled_(false),position_(0),is_start_(false)/*,buffer_control_event_(::CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE))*/
36   {
37
38     try {
39       //      thread_priority_.set_priority(AVRT_PRIORITY_NORMAL);
40
41       // WASAPIの初期化処理
42
43       // IMMDeviceEnumeratorの取得
44       THROW_IF_ERR(
45         CoCreateInstance(
46         __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
47         IID_PPV_ARGS(&device_enumerator_)));
48
49       // デフォルトのオーディオデバイスを取得する
50       THROW_IF_ERR(
51         device_enumerator_
52         ->GetDefaultAudioEndpoint(eRender,eMultimedia,&current_device_)
53         );
54
55       // オーディオクライアントを取得
56       THROW_IF_ERR(
57         current_device_
58         ->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER,
59         NULL, reinterpret_cast<void **>(&audio_client_))
60         );
61
62       // 代替フォーマット定義
63       sf::co_task_memory<WAVEFORMATEX>  alt_format;
64
65       // 読みこもうとしているWAVファイルのフォーマットをサポートしているか?
66       THROW_IF_ERR(audio_client_->IsFormatSupported(
67         AUDCLNT_SHAREMODE_EXCLUSIVE ,&wfx.Format,&alt_format));
68       
69        // 再生クライアントの初期化
70
71       REFERENCE_TIME buffer_period =  latency_ms_ /* ms */ * 10000 ;
72
73       REFERENCE_TIME buffer_duration = buffer_period * periods_per_buffer_;
74
75       THROW_IF_ERR(audio_client_->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE  , 
76         AUDCLNT_STREAMFLAGS_NOPERSIST, 
77         buffer_duration, 
78         buffer_period,
79         &(wfx.Format), 
80         NULL));
81
82       // バッファサイズの取得
83       THROW_IF_ERR(audio_client_->GetBufferSize(&buffer_size_));
84
85       // 再生クライアントの取得
86       THROW_IF_ERR(audio_client_->GetService(IID_PPV_ARGS(&audio_render_client_)));
87
88       num_of_frames_ = wfx.Format.nBlockAlign;
89       mix_format_ = wfx;
90       is_enabled_ = true;
91     } catch (win32_error_exception& e)
92     {
93       exception_holder_.reset(new win32_error_exception(e.hresult()));
94       is_enabled_ = false;
95     } catch(...) {
96       is_enabled_ = false;
97     }
98   }
99
100   wasapi_exclusive_timer::~wasapi_exclusive_timer()
101   {
102     safe_release(audio_clock_adjustment_);
103     safe_release(audio_render_client_);
104
105     // WASAPIの終了処理
106     if(audio_client_)
107     {
108       audio_client_->Stop();
109       audio_client_->Reset();
110       audio_client_.Release();
111     }
112
113     safe_release(current_device_);
114     safe_release(device_enumerator_);
115   }
116
117   void wasapi_exclusive_timer::play_buffer(BYTE* source_buffer)
118   {
119     static const size_t inc  = (buffer_size_ * num_of_frames_) / (sizeof(short) * periods_per_buffer_);
120     static const size_t buffer_in_periods = buffer_size_ / periods_per_buffer_;
121     BYTE* buffer;
122
123     if(!is_start_)
124     {
125
126       // 最初にバッファを埋める
127       THROW_IF_ERR(audio_render_client_->GetBuffer(buffer_size_,&buffer));
128       ::CopyMemory(buffer,source_buffer,get_buffer_byte_size());
129
130       // レイテンシ時間*バッファ数分進める
131       THROW_IF_ERR(audio_render_client_->ReleaseBuffer(buffer_size_,0));
132
133       // 再生開始
134       THROW_IF_ERR(audio_client_->Start());
135       is_start_ = true;
136       return;
137     }
138
139
140     // レイテンシ時間だけ待つ
141     Sleep(latency_ms_);
142
143     uint32_t padding;
144     uint32_t frames_available;
145     uint32_t count_period = periods_per_buffer_;
146     while(count_period > 0)
147     {
148       // パディングを求める。
149       THROW_IF_ERR(audio_client_->GetCurrentPadding(&padding));
150       frames_available = buffer_size_ - padding;
151
152       // パディングを除いた部分のバッファを埋める。
153       // パディングを除いた部分のサイズがbuffer_in_periodsより小さい場合はつぎにまわす。
154       // パディングを除いた部分を一気に埋めようとしたけどできなかった。。
155       while(buffer_in_periods <= frames_available && count_period > 0 ) 
156       {
157         THROW_IF_ERR(audio_render_client_->GetBuffer(buffer_in_periods,&buffer));
158         ::CopyMemory(buffer,source_buffer,buffer_in_periods *  num_of_frames_);
159         THROW_IF_ERR(audio_render_client_->ReleaseBuffer(buffer_in_periods,0));
160         // レイテンシ時間だけ進める
161         source_buffer += buffer_in_periods * num_of_frames_;
162         --count_period;
163         // パディングを再度求める
164         THROW_IF_ERR(audio_client_->GetCurrentPadding(&padding));
165         frames_available = buffer_size_ - padding;
166       }
167
168       if(count_period > 0)
169       {
170         Sleep(latency_ms_);
171       }
172     }
173   }
174   void wasapi_exclusive_timer::reset()
175   {
176     THROW_IF_ERR(audio_client_->Reset());
177   }
178   void wasapi_exclusive_timer::stop() {
179     //再生停止
180     if(is_start_)
181     {
182       THROW_IF_ERR(audio_client_->Stop());
183       reset();
184       is_start_ = false;
185
186     };
187   }
188 }