• R/O
  • HTTP
  • SSH
  • HTTPS

wasapi2: Commit

wasapiを実験するプロジェクトです。


Commit MetaInfo

Revisiond312260236d979ba5136674b5cec3b81625f7d99 (tree)
Time2012-11-10 14:44:00
Authorsfpg <sfpg@user...>
Commitersfpg

Log Message

Windows8上で動作するようにバグ修正。キャプチャ部分はまだ修正は完了していない。

Change Summary

Incremental Difference

--- a/SF.props
+++ b/SF.props
@@ -3,10 +3,10 @@
33 <ImportGroup Label="PropertySheets" />
44 <PropertyGroup Label="UserMacros" />
55 <PropertyGroup>
6- <IncludePath>H:\libs\DirectXTK\Inc;H:\libs\boost_1_51_0\boost_1_51_0;H:\libs\xbyak\xbyak;H:\libs\zlib125\zlib-1.2.5;H:\libs\lpng150\lpng150;$(IncludePath)</IncludePath>
6+ <IncludePath>H:\libs\DirectXTK\Inc;H:\libs\boost\include\boost-1_52;H:\libs\xbyak\xbyak;H:\libs\zlib125\zlib-1.2.5;H:\libs\lpng150\lpng150;$(IncludePath)</IncludePath>
77 </PropertyGroup>
88 <PropertyGroup>
9- <LibraryPath>H:\libs\zlib125\zlib-1.2.5;$(LibraryPath)</LibraryPath>
9+ <LibraryPath>H:\libs\zlib125\zlib-1.2.5;H:\libs\boost\lib;$(LibraryPath)</LibraryPath>
1010 </PropertyGroup>
1111 <ItemDefinitionGroup>
1212 <ClCompile>
--- a/wasapi2/application.cpp
+++ b/wasapi2/application.cpp
@@ -94,7 +94,6 @@ namespace sf {
9494 return 0;
9595 }
9696
97- // プロセスの優先順位
9897 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
9998
10099 // コモンコントロールの初期化
@@ -108,9 +107,15 @@ namespace sf {
108107
109108 // COMの初期化
110109 sf::com_initialize init(0,multi_threaded);
110+
111+ // プロセスの優先順位
112+ sf::av_mm_thread_characteristics avmm(wstring(L"Pro Audio"));
113+ avmm.set_priority(AVRT_PRIORITY_HIGH);
114+
111115 // アプリケーションIDの登録
112116 sf::throw_if_err<application::exception>()(SetCurrentProcessExplicitAppUserModelID(app_id_.c_str()));
113117 timeBeginPeriod(1);
118+ sf::wasapi_device_manager::instance()->wait_enum_devices();
114119 sf::wasapi_device_manager::instance()->current_output_device();
115120
116121 // ウィンドウの作成
@@ -119,10 +124,10 @@ namespace sf {
119124
120125
121126 // bool a = reader_ringbuffer_.is_lock_free();
122- // wdout << ( ? L"**** true ****":L"---- false ----") << std::endl;
123- // mixer スレッドの起動
124- //mixer_thread_.execute();
125- // ファイルリーダースレッドの起動
127+ /// wdout << ( ? L"**** true ****":L"---- false ----") << std::endl;
128+ // mixer スレッドの起動
129+ mixer_thread_.execute();
130+ // ファイルリーダースレッドの起動
126131 reader_thread_.execute();
127132
128133 input_thread_.execute();
@@ -136,10 +141,10 @@ namespace sf {
136141 output_thread_.wait_status(output_thread_t::status_device_config_ok);
137142 apply_output_device_config
138143 (wasapi_device_manager::instance()->current_output_device_index(),
139- wasapi_device_manager::instance()->current_output_device().params);
144+ wasapi_device_manager::instance()->current_output_device().params);
140145 apply_input_device_config
141146 (wasapi_device_manager::instance()->current_input_device_index(),
142- wasapi_device_manager::instance()->current_input_device().params);
147+ wasapi_device_manager::instance()->current_input_device().params);
143148 } catch (...) {
144149
145150 }
@@ -150,16 +155,17 @@ namespace sf {
150155 // スレッドを終了する
151156 reader_thread_.change_status(reader_thread_t::status_exit);
152157 input_thread_.change_status(input_thread_t::status_exit);
153- // mixer_thread_.change_status(mixer_thread_t::status_exit);
158+ mixer_thread_.change_status(mixer_thread_t::status_exit);
154159 output_thread_.change_status(output_thread_t::status_exit);
155160
156- // スレッド終了待ち
157- //mixer_thread_.join();
161+ // スレッド終了待ち
162+ mixer_thread_.join();
158163 reader_thread_.join();
159164 output_thread_.join();
160165 input_thread_.join();
161-
166+
162167 // WASAPIをシャットダウンする
168+ wasapi_device_manager::instance()->save_params();
163169 wasapi_device_manager::instance().reset();
164170
165171 return ret;
@@ -169,8 +175,8 @@ namespace sf {
169175 {
170176 try {
171177 reader_thread_.setup(file_path);
172- window_->reader_ready();
173- } catch (win32_error_exception& e) {
178+ window_->reader_ready();
179+ } catch (win32_error_exception& e) {
174180 window_->message_box((boost::wformat(L"ファイル読み込み時にエラーが発生しました。%s") % e.error()).str(),wstring(L"ファイル読込エラー"));
175181 }
176182 }
@@ -178,34 +184,34 @@ namespace sf {
178184 void application::reader_read_file()
179185 {
180186 try {
181- reader_thread_.read_file();
182- window_->reader_read_file();
183- } catch (win32_error_exception& e) {
187+ reader_thread_.read_file();
188+ window_->reader_read_file();
189+ } catch (win32_error_exception& e) {
184190 window_->message_box((boost::wformat(L"再生開始時にエラーが発生しました。%s") % e.error()).str(),wstring(L"再生エラー"));
185191 }
186192 }
187-
193+
188194 void application::reader_pause()
189195 {
190196 try {
191- reader_thread_.pause();
192- if(reader_thread_.status() == reader_thread_t::status_pause_ok)
193- {
194- window_->reader_pause();
195- } else {
196- window_->reader_read_file();
197- }
198- } catch (win32_error_exception& e) {
197+ reader_thread_.pause();
198+ if(reader_thread_.status() == reader_thread_t::status_pause_ok)
199+ {
200+ window_->reader_pause();
201+ } else {
202+ window_->reader_read_file();
203+ }
204+ } catch (win32_error_exception& e) {
199205 window_->message_box((boost::wformat(L"一時停止時にエラーが発生しました。%s") % e.error()).str(),wstring(L"一時停止エラー"));
200206 }
201207 }
202-
208+
203209 void application::reader_stop()
204210 {
205211 try {
206- reader_thread_.stop();
207- window_->reader_stop();
208- } catch (win32_error_exception& e) {
212+ reader_thread_.stop();
213+ window_->reader_stop();
214+ } catch (win32_error_exception& e) {
209215 window_->message_box((boost::wformat(L"停止時にエラーが発生しました。%s") % e.error()).str(),wstring(L"停止エラー"));
210216 }
211217 }
@@ -221,8 +227,8 @@ namespace sf {
221227 int reader_status = reader_thread_.status();
222228 reader_thread_.change_and_wait(reader_thread_t::status_config,reader_thread_t::status_config_ok);
223229
224- // int mixer_status = mixer_thread_.status();
225- // mixer_thread_.change_and_wait(mixer_thread_t::status_config,mixer_thread_t::status_config_ok);
230+ // int mixer_status = mixer_thread_.status();
231+ // mixer_thread_.change_and_wait(mixer_thread_t::status_config,mixer_thread_t::status_config_ok);
226232
227233 int input_status = input_thread_.status();
228234 input_thread_.change_and_wait(input_thread_t::status_pause,input_thread_t::status_pause_ok);
@@ -230,8 +236,8 @@ namespace sf {
230236 // 出力の変更
231237 output_thread_.apply_config(device_index,params);
232238
233- // mixer_thread_.init_buffer();
234- // mixer_thread_.change_and_wait(mixer_thread_t::status_process,mixer_thread_t::status_processing);
239+ // mixer_thread_.init_buffer();
240+ // mixer_thread_.change_and_wait(mixer_thread_t::status_process,mixer_thread_t::status_processing);
235241
236242 reader_thread_.init_buffer();
237243 reader_thread_.change_and_wait(reader_thread_t::status_ready,reader_thread_t::status_ready_ok);
--- a/wasapi2/audio_base.h
+++ b/wasapi2/audio_base.h
@@ -61,7 +61,9 @@ struct get_buffer
6161
6262 ~get_buffer()
6363 {
64- audio_base_.release_buffer(buffer_size_);
64+ if(buffer_ptr_ != nullptr && buffer_size_ != 0){
65+ audio_base_.release_buffer(buffer_size_);
66+ }
6567 }
6668
6769 operator BYTE*(){return buffer_ptr_;}
--- a/wasapi2/input_thread.cpp
+++ b/wasapi2/input_thread.cpp
@@ -40,167 +40,172 @@ using namespace std;
4040
4141 namespace sf {
4242
43-void input_thread_t::thread_main()
44-{
45- // COMの初期化
46- sf::com_initialize init(0,multi_threaded);
47-
48- // MMCSSの初期化
49- sf::av_mm_thread_characteristics avmm(wstring(L"Pro Audio"));
50- avmm.set_priority(AVRT_PRIORITY_HIGH);
51-
52- //// input デバイスの初期化
53- //wasapi_device_manager::ptr m(wasapi_device_manager::instance());
54- //apply_config_(
55- // m->current_input_device_index(),
56- // m->current_input_device().params
57- //);
58- change_status(status_pause);
59- application& app(*application::instance());
60- int status = STATUS_ERROR;
61- BYTE *buffer = 0;
62- index_ = 0;
63- int source_counter ,dest_counter;
64- source_counter = dest_counter = 0;
65- //// 出力デバイスが稼働するまで待つ
66- //app.output_thread().wait_status(output_thread_t::status_processing,10);
67- //apply_config_(
68- //wasapi_device_manager::instance()->current_input_device_index(),
69- //wasapi_device_manager::instance()->current_input_device().params);
70- change_status(status_device_config);
71- while(status = status_.load(),status != status_exit)
43+ void input_thread_t::thread_main()
7244 {
73- // イベントを待つ
74- switch(status)
45+ // COMの初期化
46+ sf::com_initialize init(0,multi_threaded);
47+
48+ // MMCSSの初期化
49+ sf::av_mm_thread_characteristics avmm(wstring(L"Pro Audio"));
50+ avmm.set_priority(AVRT_PRIORITY_HIGH);
51+
52+ //// input デバイスの初期化
53+ //wasapi_device_manager::ptr m(wasapi_device_manager::instance());
54+ //apply_config_(
55+ // m->current_input_device_index(),
56+ // m->current_input_device().params
57+ //);
58+ change_status(status_pause);
59+ application& app(*application::instance());
60+ int status = STATUS_ERROR;
61+ BYTE *buffer = 0;
62+ index_ = 0;
63+ int source_counter ,dest_counter;
64+ source_counter = dest_counter = 0;
65+ //// 出力デバイスが稼働するまで待つ
66+ //app.output_thread().wait_status(output_thread_t::status_processing,10);
67+ //apply_config_(
68+ //wasapi_device_manager::instance()->current_input_device_index(),
69+ //wasapi_device_manager::instance()->current_input_device().params);
70+ change_status(status_device_config);
71+ while(status = status_.load(),status != status_exit)
7572 {
76- case status_device_config:
77- if(wasapi_input_)
73+ // イベントを待つ
74+ switch(status)
7875 {
79- wasapi_input_->stop();
80- dest_counter = 0;
81- }
82- change_status(status_device_config_ok);
83- break;
84- case status_process:
85- if(!wasapi_input_->is_start()){
86- wasapi_input_->start();
87- }
88- change_status(status_processing);
89- case status_processing:
90- {
91- wasapi_input_->wait();// 入力待ち
92- get_buffer g(*wasapi_input_);// キャプチャバッファの取得
93- if(g.size() && g != 0)
76+ case status_device_config:
77+ if(wasapi_input_)
9478 {
95- source_counter = 0;
96- while(source_counter != g.size())
97- {
98- BYTE * src = g + source_counter * wasapi_input_->get_frame_size();
99- int size_byte_src = g.size_byte() - source_counter * wasapi_input_->get_frame_size();
100- int size_src = g.size() - source_counter;
101-
102- BYTE * dest = buffer_[index_].get() + dest_counter * app.output_device().get_frame_size();
103- int size_byte_dest = app.output_device().get_buffer_byte_size() - dest_counter * app.output_device().get_frame_size();
104- int size_dest = app.output_device().get_buffer_size() - dest_counter;
105-
106- if(size_src <= size_dest)
107- {
108- ::CopyMemory(dest,src,size_byte_src);
109- source_counter += size_src;
110- dest_counter += size_src;
111- } else if(size_src > size_dest)
112- {
113- ::CopyMemory(dest,src,size_byte_dest);
114- source_counter += size_dest;
115- dest_counter += size_dest;
116- }
117- if(dest_counter == app.output_device().get_buffer_size())
118- {
119- if(ringbuffer_.enqueue(buffer_[index_].get()))
120- {
121- index_ = (index_ + 1) & (buffer_.size() - 1);
122- }
123- dest_counter = 0;
124- }
125- }
79+ wasapi_input_->stop();
80+ dest_counter = 0;
12681 }
82+ change_status(status_device_config_ok);
83+ break;
84+ case status_process:
85+ if(!wasapi_input_->is_start()){
86+ wasapi_input_->start();
87+ }
88+ change_status(status_processing);
89+ case status_processing:
90+ {
91+ wasapi_input_->wait();// 入力待ち
92+ //try{
93+ // get_buffer g(*wasapi_input_);// キャプチャバッファの取得
94+ // if(g.size() && g != 0)
95+ // {
96+ // source_counter = 0;
97+ // while(source_counter != g.size())
98+ // {
99+ // BYTE * src = g + source_counter * wasapi_input_->get_frame_size();
100+ // int size_byte_src = g.size_byte() - source_counter * wasapi_input_->get_frame_size();
101+ // int size_src = g.size() - source_counter;
102+
103+ // BYTE * dest = buffer_[index_].get() + dest_counter * app.output_device().get_frame_size();
104+ // int size_byte_dest = app.output_device().get_buffer_byte_size() - dest_counter * app.output_device().get_frame_size();
105+ // int size_dest = app.output_device().get_buffer_size() - dest_counter;
106+
107+ // if(size_src <= size_dest)
108+ // {
109+ // ::CopyMemory(dest,src,size_byte_src);
110+ // source_counter += size_src;
111+ // dest_counter += size_src;
112+ // } else if(size_src > size_dest)
113+ // {
114+ // ::CopyMemory(dest,src,size_byte_dest);
115+ // source_counter += size_dest;
116+ // dest_counter += size_dest;
117+ // }
118+ // if(dest_counter == app.output_device().get_buffer_size())
119+ // {
120+ // if(ringbuffer_.enqueue(buffer_[index_].get()))
121+ // {
122+ // index_ = (index_ + 1) & (buffer_.size() - 1);
123+ // }
124+ // dest_counter = 0;
125+ // }
126+ // }
127+ // }
128+ //} catch (...) {
129+ // ;
130+ //}
131+ }
132+
133+ break;
134+ case status_pause:
135+ if(wasapi_input_)
136+ {
137+ wasapi_input_->stop();
138+ }
139+ change_status(status_pause_ok);
140+ break;
141+ default:
142+ WaitForSingleObject(event_.get(),WAIT_TIMEOUT_DEFAULT);
143+ break;
127144 }
128- break;
129- case status_pause:
130- if(wasapi_input_)
131- {
132- wasapi_input_->stop();
133- }
134- change_status(status_pause_ok);
135- break;
136- default:
137- WaitForSingleObject(event_.get(),WAIT_TIMEOUT_DEFAULT);
138- break;
139145 }
140- }
141146 loop_end:
142- ;
143- DOUT(L"##### input_threadは終了!" << endl);
144- if(wasapi_input_ && wasapi_input_->is_start())
145- {
146- wasapi_input_->stop();
147- }
148- wasapi_input_.reset();
149-};
150-
151-void input_thread_t::apply_config(int device_index,wasapi_device_manager::device_info::params_t& params)
152-{
153- if(status() != status_device_config_ok){
154- change_and_wait(status_device_config,status_device_config_ok);
155- }
156- apply_config_(device_index,params);
157- init_buffer();
158- change_and_wait(status_process,status_processing);
159-}
160-
161-void input_thread_t::apply_config_(int device_index,wasapi_device_manager::device_info::params_t& params)
162-{
163-
164- WAVEFORMATEXTENSIBLE form;
165- if(wasapi_input_)
166- {
167- if(wasapi_input_->is_start())
147+ ;
148+ DOUT(L"##### input_threadは終了!" << endl);
149+ if(wasapi_input_ && wasapi_input_->is_start())
168150 {
169151 wasapi_input_->stop();
170152 }
171153 wasapi_input_.reset();
172154 };
173155
174- //bits_pair bits = {params.bits,params.valid_bits};
175- //make_wave_format(form,params.sample_rate,params.channel,bits);
156+ void input_thread_t::apply_config(int device_index,wasapi_device_manager::device_info::params_t& params)
157+ {
158+ if(status() != status_device_config_ok){
159+ change_and_wait(status_device_config,status_device_config_ok);
160+ }
161+ apply_config_(device_index,params);
162+ init_buffer();
163+ change_and_wait(status_process,status_processing);
164+ }
165+
166+ void input_thread_t::apply_config_(int device_index,wasapi_device_manager::device_info::params_t& params)
167+ {
176168
177- try {
178- if(params.exclusive_mode)
169+ WAVEFORMATEXTENSIBLE form;
170+ if(wasapi_input_)
179171 {
180- if(params.event_mode){
181- wasapi_input_.reset(new sf::wasapi_capture_exclusive_event(device_index,params));
182- } else {
183- wasapi_input_.reset(new sf::wasapi_capture_exclusive_timer(device_index,params));
184- };
185- } else {
186- if(params.event_mode)
172+ if(wasapi_input_->is_start())
173+ {
174+ wasapi_input_->stop();
175+ }
176+ wasapi_input_.reset();
177+ };
178+
179+ //bits_pair bits = {params.bits,params.valid_bits};
180+ //make_wave_format(form,params.sample_rate,params.channel,bits);
181+
182+ try {
183+ if(params.exclusive_mode)
187184 {
188- wasapi_input_.reset(new sf::wasapi_capture_shared_event(device_index,params));
185+ if(params.event_mode){
186+ wasapi_input_.reset(new sf::wasapi_capture_exclusive_event(device_index,params));
187+ } else {
188+ wasapi_input_.reset(new sf::wasapi_capture_exclusive_timer(device_index,params));
189+ };
189190 } else {
190- wasapi_input_.reset(new sf::wasapi_capture_shared_timer(device_index,params));
191+ if(params.event_mode)
192+ {
193+ wasapi_input_.reset(new sf::wasapi_capture_shared_event(device_index,params));
194+ } else {
195+ wasapi_input_.reset(new sf::wasapi_capture_shared_timer(device_index,params));
196+ }
191197 }
192- }
193- } catch (win32_error_exception& e)
194- {
198+ } catch (win32_error_exception& e)
199+ {
195200
196- //window_->message_box((boost::wformat(L"WASAPI初期化エラーが発生しました。設定パラメータを見なおしてください。%s") % e.error()).str(),wstring(L"WASAPI初期化エラー"));
197- throw;
198- }
201+ //window_->message_box((boost::wformat(L"WASAPI初期化エラーが発生しました。設定パラメータを見なおしてください。%s") % e.error()).str(),wstring(L"WASAPI初期化エラー"));
202+ throw;
203+ }
199204
200- wasapi_device_manager::instance()->select_input_device(device_index);
201- wasapi_device_manager::instance()->current_input_device().params = params;
205+ wasapi_device_manager::instance()->select_input_device(device_index);
206+ wasapi_device_manager::instance()->current_input_device().params = params;
202207
203-}
208+ }
204209
205210 //void input_thread_t::init_buffer()
206211 //{
--- a/wasapi2/stdafx.h
+++ b/wasapi2/stdafx.h
@@ -13,7 +13,7 @@
1313 // STL
1414
1515 #define DIRECTINPUT_VERSION 0x0800
16-#define BOOST_ALL_NO_LIB
16+//#define BOOST_ALL_NO_LIB
1717
1818 #include <stdint.h>
1919 #include <tchar.h>
--- a/wasapi2/wasapi.h
+++ b/wasapi2/wasapi.h
@@ -85,6 +85,11 @@ namespace sf {
8585
8686 ref class DeviceWatcherAdapter
8787 {
88+ public:
89+ virtual ~DeviceWatcherAdapter()
90+ {
91+ added_.disconnect_all_slots();
92+ };
8893 internal:
8994 DeviceWatcherAdapter(en::DeviceClass deviceClass = en::DeviceClass::AudioRender)
9095 {
@@ -95,7 +100,6 @@ namespace sf {
95100 watcher_->Removed += ref new f::TypedEventHandler<en::DeviceWatcher^ , en::DeviceInformationUpdate^ >(this,&DeviceWatcherAdapter::Removed);
96101 watcher_->Stopped += ref new f::TypedEventHandler<en::DeviceWatcher^ , Platform::Object^ >(this,&DeviceWatcherAdapter::Stopped);
97102 }
98-
99103 boost::signals2::signal<void (en::DeviceWatcher^ sender
100104 , en::DeviceInformation^ deviceInterface) >& added(){return added_;}
101105 boost::signals2::signal<void (en::DeviceWatcher^ sender,
@@ -108,11 +112,11 @@ namespace sf {
108112 Platform::Object^ obj) >& stopped() {return stopped_;}
109113 void start()
110114 {
111- watcher_->Start();
115+ watcher_->Start();
112116 }
113117 void stop()
114118 {
115- watcher_->Stop();
119+ watcher_->Stop();
116120 }
117121 private:
118122 void Added(en::DeviceWatcher^ sender, en::DeviceInformation^ deviceInterface)
@@ -246,7 +250,20 @@ namespace sf {
246250 int current_output_device_index() const {return output_device_index_;}
247251 int current_input_device_index() const {return input_device_index_;}
248252 static const std::wstring & base_directory() {return base_directory_;};
249- private:
253+
254+ // 列挙が関連するまで待つ
255+ void wait_enum_devices()
256+ {
257+ while(!output_enum_completed_ || !input_enum_completed_)
258+ {
259+ Sleep(1);
260+ }
261+ }
262+
263+ void stop_watching();
264+ void save_params();
265+
266+ private:
250267 void output_added(Windows::Devices::Enumeration::DeviceWatcher^ sender, Windows::Devices::Enumeration::DeviceInformation^ deviceInfo);
251268 void output_enumeration_completed(en::DeviceWatcher^ sender, Platform::Object^ obj);
252269 void output_updated(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo);
@@ -260,15 +277,30 @@ namespace sf {
260277 void input_stopped(en::DeviceWatcher^ sender, Platform::Object^ obj);
261278
262279 int get_device_infos(Windows::Devices::Enumeration::DeviceClass data_flow, std::vector<device_info>& infos,const std::wstring& idd);
263- int output_device_index_;
280+
281+
282+ int output_device_index_;
264283 int input_device_index_;
265284 std::vector<device_info> output_device_infos_;
266285 std::vector<device_info> input_device_infos_;
267286 static const std::wstring base_directory_;
268287 std::wstring config_path_;
269- DeviceWatcherAdapter^ output_watcher_adapter_;
288+ // 出力デバイス監視アダプタ
289+ DeviceWatcherAdapter^ output_watcher_adapter_;
290+
291+ // 入力デバイス監視アダプタ
270292 DeviceWatcherAdapter^ input_watcher_adapter_;
271- bool enum_completed_;
293+
294+ // 出力デバイスの列挙が完了したか
295+ bool output_enum_completed_;
296+ // 入力デバイスの列挙が完了したか
297+ bool input_enum_completed_;
298+
299+ std::wstring output_id_;
300+ std::wstring input_id_;
301+ std::wstring default_output_id_;
302+ std::wstring default_input_id_;
303+
272304 };
273305
274306 // タイマーモードのポリシークラス
@@ -422,6 +454,7 @@ namespace sf {
422454 // 再生レイテンシ
423455 REFERENCE_TIME latency_;/* ms */
424456 REFERENCE_TIME actual_latency_;
457+
425458 };
426459
427460 typedef wasapi_base<wasapi_shared_policy,wasapi_timer_policy> wasapi_shared_timer;
@@ -461,6 +494,7 @@ namespace sf {
461494 hr_ = S_OK;
462495 // Get the pointer for the Audio Client
463496 punkAudioInterface->QueryInterface( IID_PPV_ARGS(&ptr_) );
497+ punkAudioInterface->Release();
464498 if( nullptr == ptr_ )
465499 {
466500 hr_ = E_FAIL;
--- a/wasapi2/wasapi2.vcxproj
+++ b/wasapi2/wasapi2.vcxproj
@@ -20,14 +20,14 @@
2020 <ConfigurationType>Application</ConfigurationType>
2121 <UseDebugLibraries>true</UseDebugLibraries>
2222 <CharacterSet>Unicode</CharacterSet>
23- <PlatformToolset>v110</PlatformToolset>
23+ <PlatformToolset>v120_CTP_Nov2012</PlatformToolset>
2424 </PropertyGroup>
2525 <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
2626 <ConfigurationType>Application</ConfigurationType>
2727 <UseDebugLibraries>false</UseDebugLibraries>
2828 <WholeProgramOptimization>true</WholeProgramOptimization>
2929 <CharacterSet>Unicode</CharacterSet>
30- <PlatformToolset>v110</PlatformToolset>
30+ <PlatformToolset>v120_CTP_Nov2012</PlatformToolset>
3131 </PropertyGroup>
3232 <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
3333 <ImportGroup Label="ExtensionSettings">
--- a/wasapi2/wasapi_manager.cpp
+++ b/wasapi2/wasapi_manager.cpp
@@ -41,474 +41,513 @@ using namespace Platform;
4141 using namespace Microsoft::WRL;
4242
4343 namespace sf {
44- const wstring wasapi_device_manager::base_directory_(L""/* Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data() */);
45- const int wasapi_device_manager::sample_rates[NUM_SAMPLE_RATE] = {8000,11025,16000,22050,24000,32000,44100,48000,88200,96000,176400,192000};
46- const bits_pair wasapi_device_manager::sample_bits[NUM_SAMPLE_BITS] = {{8,8},{16,16},{24,24},{32,24},{32,32},{32,WAVE_FORMAT_IEEE_FLOAT}};
47-
48- void make_wave_format(WAVEFORMATEXTENSIBLE& format,int sample_rate,int channels,bits_pair b,uint32_t type,const GUID& sub_type)
49- {
50- ZeroMemory(&format,sizeof(WAVEFORMATEXTENSIBLE));
51- format.Format.wFormatTag = type;
52- format.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
53- format.SubFormat = sub_type;
54- format.Format.nSamplesPerSec = sample_rate;
55- format.Format.nChannels = channels;
56- format.Format.wBitsPerSample = b.bits_per_sample;
57- format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * format.Format.nChannels;
58- format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign;
59- format.Samples.wValidBitsPerSample = b.valid_bits_per_sample;
60-
61- format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
62- };
63-
64- void make_wave_format(WAVEFORMATEX& format,int sample_rate,int channels,int bits,uint32_t type)
65- {
66- ZeroMemory(&format,sizeof(WAVEFORMATEX));
67- format.wFormatTag = type;
68- format.nSamplesPerSec = sample_rate;
69- format.nChannels = channels;
70- format.wBitsPerSample = bits;
71- format.nBlockAlign = (format.wBitsPerSample / 8) * format.nChannels;
72- format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
73- };
74-
75- struct prop_variant
76- {
77- prop_variant()
78- {
79- PropVariantInit(&value_);
80- }
81-
82- ~prop_variant()
83- {
84- PropVariantClear(&value_);
85- }
86-
87- PROPVARIANT* get(){ return &value_;};
88-
89- PROPVARIANT* operator &(){return get();}
90-
91- operator PROPVARIANT*() {return get();}
92-
93- private:
94- PROPVARIANT value_;
95- };
96-
97-
98- wasapi_device_manager::wasapi_device_manager()
99- : output_device_index_(0),input_device_index_(0),enum_completed_(false)
100- {
101-
102- config_path_.append(base_directory_).append(L"\\wasapi_device_manager.config.xml");
103- boost::filesystem::wpath config_path(config_path_);
104-
105- std::wstring output_id;
106- std::wstring input_id;
107-
108- if(boost::filesystem::exists(config_path))
109- {
110- try {
111- boost::filesystem::wifstream f(config_path);
112- boost::archive::xml_wiarchive ar(f);
113- ar & boost::serialization::make_nvp("output_device_id",output_id);
114- ar & boost::serialization::make_nvp("input_device_id" ,input_id);
115- } catch(...)
116- {
117- output_id = input_id = L"";
118- boost::filesystem::remove(config_path);
119- }
120- }
121-
122- //watcher_ = DeviceInformation::CreateWatcher();
123- //adapter_ = ref new typed_event_handler_adapter<DeviceWatcher^,DeviceInformation^>(boost::bind(&wasapi_device_manager::added,this,_1,_2));
124- // watcher_->Added += adapter_->get();
125- //watcher_->Added += (ref new typed_event_handler_adapter<DeviceWatcher^,DeviceInformation^>(boost::bind(&wasapi_device_manager::added,this,_1,_2)))->get();
126- //watcher_->Start();
127-
128- output_watcher_adapter_ = ref new DeviceWatcherAdapter(en::DeviceClass::AudioRender);
129- output_watcher_adapter_->added().connect(boost::bind(&wasapi_device_manager::output_added,this,_1,_2));
130- output_watcher_adapter_->enumration_completed().connect(boost::bind(&wasapi_device_manager::output_enumeration_completed,this,_1,_2));
131- output_watcher_adapter_->removed().connect(boost::bind(&wasapi_device_manager::output_removed,this,_1,_2));
132- output_watcher_adapter_->updated().connect(boost::bind(&wasapi_device_manager::output_updated,this,_1,_2));
133- output_watcher_adapter_->stopped().connect(boost::bind(&wasapi_device_manager::output_stopped,this,_1,_2));
134- output_watcher_adapter_->start();
44+ const wstring wasapi_device_manager::base_directory_(L"."/* Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data() */);
45+ const int wasapi_device_manager::sample_rates[NUM_SAMPLE_RATE] = {8000,11025,16000,22050,24000,32000,44100,48000,88200,96000,176400,192000};
46+ const bits_pair wasapi_device_manager::sample_bits[NUM_SAMPLE_BITS] = {{8,8},{16,16},{24,24},{32,24},{32,32},{32,WAVE_FORMAT_IEEE_FLOAT}};
47+
48+ void make_wave_format(WAVEFORMATEXTENSIBLE& format,int sample_rate,int channels,bits_pair b,uint32_t type,const GUID& sub_type)
49+ {
50+ ZeroMemory(&format,sizeof(WAVEFORMATEXTENSIBLE));
51+ format.Format.wFormatTag = type;
52+ format.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
53+ format.SubFormat = sub_type;
54+ format.Format.nSamplesPerSec = sample_rate;
55+ format.Format.nChannels = channels;
56+ format.Format.wBitsPerSample = b.bits_per_sample;
57+ format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * format.Format.nChannels;
58+ format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign;
59+ format.Samples.wValidBitsPerSample = b.valid_bits_per_sample;
60+
61+ format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
62+ };
63+
64+ void make_wave_format(WAVEFORMATEX& format,int sample_rate,int channels,int bits,uint32_t type)
65+ {
66+ ZeroMemory(&format,sizeof(WAVEFORMATEX));
67+ format.wFormatTag = type;
68+ format.nSamplesPerSec = sample_rate;
69+ format.nChannels = channels;
70+ format.wBitsPerSample = bits;
71+ format.nBlockAlign = (format.wBitsPerSample / 8) * format.nChannels;
72+ format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
73+ };
74+
75+ struct prop_variant
76+ {
77+ prop_variant()
78+ {
79+ PropVariantInit(&value_);
80+ }
81+
82+ ~prop_variant()
83+ {
84+ PropVariantClear(&value_);
85+ }
86+
87+ PROPVARIANT* get(){ return &value_;};
88+
89+ PROPVARIANT* operator &(){return get();}
90+
91+ operator PROPVARIANT*() {return get();}
92+
93+ private:
94+ PROPVARIANT value_;
95+ };
96+
97+
98+ wasapi_device_manager::wasapi_device_manager()
99+ : output_device_index_(-1),input_device_index_(-1),output_enum_completed_(false),input_enum_completed_(false)
100+ {
101+
102+ config_path_.append(base_directory_).append(L"\\wasapi_device_manager.config.xml");
103+ boost::filesystem::wpath config_path(config_path_);
104+
105+
106+ if(boost::filesystem::exists(config_path))
107+ {
108+ try {
109+ boost::filesystem::wifstream f(config_path);
110+ boost::archive::xml_wiarchive ar(f);
111+ ar & boost::serialization::make_nvp("output_device_id",output_id_);
112+ ar & boost::serialization::make_nvp("input_device_id" ,input_id_);
113+ } catch(...)
114+ {
115+ output_id_ = input_id_ = L"";
116+ boost::filesystem::remove(config_path);
117+ }
118+ }
119+
120+ default_output_id_ = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default)->Data();
121+ default_input_id_ = MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default)->Data();
122+
123+ //watcher_ = DeviceInformation::CreateWatcher();
124+ //adapter_ = ref new typed_event_handler_adapter<DeviceWatcher^,DeviceInformation^>(boost::bind(&wasapi_device_manager::added,this,_1,_2));
125+ // watcher_->Added += adapter_->get();
126+ //watcher_->Added += (ref new typed_event_handler_adapter<DeviceWatcher^,DeviceInformation^>(boost::bind(&wasapi_device_manager::added,this,_1,_2)))->get();
127+ //watcher_->Start();
128+
129+ output_watcher_adapter_ = ref new DeviceWatcherAdapter(en::DeviceClass::AudioRender);
130+ output_watcher_adapter_->added().connect(boost::bind(&wasapi_device_manager::output_added,this,_1,_2));
131+ output_watcher_adapter_->enumration_completed().connect(boost::bind(&wasapi_device_manager::output_enumeration_completed,this,_1,_2));
132+ output_watcher_adapter_->removed().connect(boost::bind(&wasapi_device_manager::output_removed,this,_1,_2));
133+ output_watcher_adapter_->updated().connect(boost::bind(&wasapi_device_manager::output_updated,this,_1,_2));
134+ output_watcher_adapter_->stopped().connect(boost::bind(&wasapi_device_manager::output_stopped,this,_1,_2));
135+ output_watcher_adapter_->start();
135136
136137 input_watcher_adapter_ = ref new DeviceWatcherAdapter(en::DeviceClass::AudioCapture);
137- input_watcher_adapter_->added().connect(boost::bind(&wasapi_device_manager::input_added,this,_1,_2));
138- input_watcher_adapter_->enumration_completed().connect(boost::bind(&wasapi_device_manager::input_enumeration_completed,this,_1,_2));
139- input_watcher_adapter_->removed().connect(boost::bind(&wasapi_device_manager::input_removed,this,_1,_2));
140- input_watcher_adapter_->updated().connect(boost::bind(&wasapi_device_manager::input_updated,this,_1,_2));
141- input_watcher_adapter_->stopped().connect(boost::bind(&wasapi_device_manager::input_stopped,this,_1,_2));
142- input_watcher_adapter_->start();
143-
144- //// IMMDeviceEnumeratorの取得
145- //THROW_IF_ERR(
146- // CoCreateInstance(
147- // __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
148- // IID_PPV_ARGS(&device_collection_)));
149- //task<DeviceInformationCollection^> t(DeviceInformation::FindAllAsync(DeviceClass::AudioRender));
150- //device_collection_ = t.get();
151-
152- // 出力エンドポイントの取得
153- /*
154- output_device_index_ = get_device_infos(Windows::Devices::Enumeration::DeviceClass::AudioRender,output_device_infos_,output_id);
155- // 出力エンドポイントの能力を調査する
156- for(int d = 0;d < output_device_infos_.size();++d)
157- {
158- IAudioClient2Ptr c;
159- // オーディオクライアントを取得
160- // THROW_IF_ERR(ActivateAudioInterface(output_device_infos_[d].id_.c_str(),__uuidof(IAudioClient2),&c));
161- {
162- ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOpPtr;
163- IActivateAudioInterfaceAsyncOperation* asyncOp;
164- ComPtr<ActivateAudioInterfaceCompletionHandler> handler(Make<ActivateAudioInterfaceCompletionHandler>());
165- HRESULT hr = ActivateAudioInterfaceAsync(output_device_infos_[d].id_.c_str(),__uuidof(IAudioClient2),nullptr,handler.Get(),&asyncOp);
166- asyncOpPtr.Attach(asyncOp);
167- if(SUCCEEDED(hr)){
168- handler->wait();
169- if(handler->ResultCode() == S_OK)
170- {
171- c = handler->AudioClient();
172- } else {
173- throw win32_error_exception(handler->ResultCode());
174- }
175- } else {
176- throw win32_error_exception(handler->ResultCode());
177- }
178- }
179- c->GetDevicePeriod(&(output_device_infos_[d].latency_default_),&(output_device_infos_[d].latency_minimum_));
180- if(output_device_infos_[d].params.latency == 0
181- || output_device_infos_[d].params.latency < output_device_infos_[d].latency_minimum_ )
182- {
183- output_device_infos_[d].params.latency = output_device_infos_[d].latency_default_;
184- }
185-
186- #ifdef _DEBUG
187- wdout << output_device_infos_[d].name_ << endl;
188- wdout << boost::wformat(L"latency default:%d min:%d") % output_device_infos_[d].latency_default_ % output_device_infos_[d].latency_minimum_ << endl;
189- #endif
190- for(int bits = 0;bits < NUM_SAMPLE_BITS;++bits)
191- {
192- for(int channel = 1;channel < 3;++channel)
193- {
194- for(int rate = 0;rate < NUM_SAMPLE_RATE;++rate)
195- {
196- sf::co_task_memory<WAVEFORMATEXTENSIBLE> a,a1;
197- WAVEFORMATEXTENSIBLE f;
198-
199- // make_wave_fomat(f,sample_rates[rate],channel,sample_bits[bits]);
200- if(sample_bits[bits].valid_bits_per_sample == WAVE_FORMAT_IEEE_FLOAT)
201- {
202- bits_pair b ={sample_bits[bits].bits_per_sample,sample_bits[bits].bits_per_sample};
203- make_wave_format(f,sample_rates[rate],channel,b,WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
204- } else {
205- make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_PCM);
206- }
207-
208- // 排他モード
209- HRESULT hr = c->IsFormatSupported(
210- AUDCLNT_SHAREMODE_EXCLUSIVE,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a));
211- if(hr == S_OK){
212- output_device_infos_[d].support_formats_[AUDCLNT_SHAREMODE_EXCLUSIVE][ sample_bits[bits].bits_per_sample ][sample_bits[bits].valid_bits_per_sample][channel][sample_rates[rate]] = 0;
213- #ifdef _DEBUG
214- wdout << boost::wformat(L"|exc |bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|") % sample_bits[bits].bits_per_sample % sample_bits[bits].valid_bits_per_sample % channel % sample_rates[rate] % (hr == S_OK?L"OK":L"NG") << endl;
215- #endif }
216-
217- // make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE);
218- // 共有モード
219- hr = c->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a1));
220- if(hr == S_OK){
221- output_device_infos_[d]
222- .support_formats_
223- [AUDCLNT_SHAREMODE_SHARED]
224- [ sample_bits[bits].bits_per_sample ]
225- [sample_bits[bits].valid_bits_per_sample]
226- [channel][sample_rates[rate]]
227- = 0;
228- #ifdef _DEBUG
229- wdout << boost::wformat(L"|share|bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
230- % sample_bits[bits].bits_per_sample
231- % sample_bits[bits].valid_bits_per_sample
232- % channel % sample_rates[rate]
233- % (hr == S_OK?L"OK":L"NG") << endl;
234-
235- #endif
236- }
237- }
238- }
239- }
240- #ifdef _DEBUG
241- wdout << "-------------------------------" << std::endl;
242- #endif
243- }
244- // safe_release(c);
245- }
246-
247- // 入力エンドポイントの取得
248- input_device_index_ = get_device_infos(DeviceClass::AudioCapture,input_device_infos_,input_id);
249- // 出力エンドポイントの能力を調査する
250- for(int d = 0;d < input_device_infos_.size();++d)
251- {
252- IAudioClient2Ptr c;
253- // オーディオクライアントを取得
254- // THROW_IF_ERR(ActivateAudioInterface(input_device_infos_[d].id_.c_str(),__uuidof(IAudioClient2),&c) );
255- {
256- ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOpPtr;
257- IActivateAudioInterfaceAsyncOperation* asyncOp;
258- ComPtr<ActivateAudioInterfaceCompletionHandler> handler(Make<ActivateAudioInterfaceCompletionHandler>());
259- HRESULT hr = ActivateAudioInterfaceAsync(input_device_infos_[d].id_.c_str(),__uuidof(IAudioClient2),nullptr,handler.Get(),&asyncOp);
260- asyncOpPtr.Attach(asyncOp);
261- if(SUCCEEDED(hr)){
262- handler->wait();
263- if(handler->ResultCode() == S_OK)
264- {
265- c = handler->AudioClient();
266- } else {
267- throw win32_error_exception(handler->ResultCode());
268- }
269- } else {
270- throw win32_error_exception(handler->ResultCode());
271- }
272- }
273- c->GetDevicePeriod(&(input_device_infos_[d].latency_default_),&(input_device_infos_[d].latency_minimum_));
274- if(input_device_infos_[d].params.latency == 0 || input_device_infos_[d].params.latency < input_device_infos_[d].latency_minimum_ ){
275- input_device_infos_[d].params.latency = input_device_infos_[d].latency_default_;
276- }
277-
278- #ifdef _DEBUG
279- wdout << input_device_infos_[d].display_name_ << endl;
280- wdout << boost::wformat(L"latency default:%d min:%d") % input_device_infos_[d].latency_default_ % input_device_infos_[d].latency_minimum_ << endl;
281- #endif
282- for(int bits = 0;bits < NUM_SAMPLE_BITS;++bits)
283- {
284- for(int channel = 1;channel < 3;++channel)
285- {
286- for(int rate = 0;rate < NUM_SAMPLE_RATE;++rate)
287- {
288- sf::co_task_memory<WAVEFORMATEXTENSIBLE> a,a1;
289- WAVEFORMATEXTENSIBLE f;
290-
291- // make_wave_fomat(f,sample_rates[rate],channel,sample_bits[bits]);
292- if(sample_bits[bits].valid_bits_per_sample == WAVE_FORMAT_IEEE_FLOAT)
293- {
294- bits_pair b ={sample_bits[bits].bits_per_sample,sample_bits[bits].bits_per_sample};
295- make_wave_format(f,sample_rates[rate],channel,b,WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
296- } else {
297- make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_PCM);
298- }
299-
300- // 排他モード
301- HRESULT hr = c->IsFormatSupported(
302- AUDCLNT_SHAREMODE_EXCLUSIVE,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a));
303- if(hr == S_OK){
304- input_device_infos_[d]
305- .support_formats_
306- [AUDCLNT_SHAREMODE_EXCLUSIVE]
307- [ sample_bits[bits].bits_per_sample ]
308- [sample_bits[bits].valid_bits_per_sample]
309- [channel]
310- [sample_rates[rate]] = 0;
311- #ifdef _DEBUG
312- wdout << boost::wformat(L"|exc |bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
313- % sample_bits[bits].bits_per_sample
314- % sample_bits[bits].valid_bits_per_sample
315- % channel % sample_rates[rate]
316- % (hr == S_OK?L"OK":L"NG") << endl;
317- #endif }
318-
319- // make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE);
320- // 共有モード
321- hr = c->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a1));
322- if(hr == S_OK){
323- input_device_infos_[d].
324- support_formats_
325- [AUDCLNT_SHAREMODE_SHARED]
326- [sample_bits[bits].bits_per_sample ]
327- [sample_bits[bits].valid_bits_per_sample]
328- [channel]
329- [sample_rates[rate]] = 0;
330- #ifdef _DEBUG
331- wdout << boost::wformat(L"|share|bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
332- % sample_bits[bits].bits_per_sample
333- % sample_bits[bits].valid_bits_per_sample
334- % channel
335- % sample_rates[rate]
336- % (hr == S_OK?L"OK":L"NG") << endl;
337-
338- #endif
339- }
340- }
341- }
342- }
343- #ifdef _DEBUG
344- wdout << "-------------------------------" << std::endl;
345- #endif
346- }
347- // safe_release(c);
348- }
349- */
350- }//
351-
352- int wasapi_device_manager::get_device_infos(DeviceClass data_flow, std::vector<device_info>& infos,const std::wstring& idd)
353- {
354-
355- // 対象デバイスを列挙する
356- task<DeviceInformationCollection^> t((DeviceInformation::FindAllAsync(data_flow)));
357- t.wait();
358- DeviceInformationCollection^ collection = t.get();
359- int current_index = 0;
360- bool found = false;
361- Platform::String^ default_id = ref new Platform::String();
362- switch(data_flow)
363- {
364- case Windows::Devices::Enumeration::DeviceClass::AudioRender:
365- default_id = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
366- break;
367- case Windows::Devices::Enumeration::DeviceClass::AudioCapture:
368- default_id = MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default);
369- break;
370- }
371-
372-
373- for(int i = 0 ,end = collection->Size;i < end;++i)
374- {
375- DeviceInformation^ info = collection->GetAt(i);
376- infos.push_back(device_info(info));
377- if(info->Id == default_id)
378- {
379- infos[i].is_default_ = true;
380- }
381-
382- if(idd == wstring(info->Id->Data()) )
383- {
384- infos[i].is_selected_ = true;
385- current_index = i;
386- found = true;
387- }
388- }
389-
390- if(!found)
391- {
392- for(int i = 0,end = infos.size();i < end;++i)
393- {
394- if(infos[i].is_default_)
395- {
396- current_index = i;
397- infos[i].is_selected_ = true;
398- }
399- }
400-
401- }
402- return current_index;
403- }
404-
405- wasapi_device_manager::~wasapi_device_manager()
406- {
407- input_watcher_adapter_->stop();
408- output_watcher_adapter_->stop();
409- //for(int i = 0; i <
410- //safe_release(device_collection_);
411- //safe_release(device_collection_);
412- //safe_release(device_collection_);
413-
414- // 設定の保存
415- try{
416- boost::filesystem::wpath config_path(config_path_);
417- boost::filesystem::wofstream f(config_path,std::ios_base::out | std::ios_base::trunc);
418- boost::archive::xml_woarchive ar(f);
419- ar & boost::serialization::make_nvp("output_device_id",current_output_device().id_);
420- ar & boost::serialization::make_nvp("input_device_id" ,current_input_device().id_);
421- }catch(...) {
422-
423- }
424-
425- }
426-
427- wasapi_device_manager::device_info::device_info
428- (DeviceInformation^ info)
429- : id_(info->Id->Data()),
430- name_(info->Name->Data()),
431- display_name_(dynamic_cast<String^>(info->Properties->Lookup(L"System.ItemNameDisplay"))->Data()),
432- is_enabled_(info->IsEnabled),is_selected_(false),is_default_(info->IsDefault)
433- {
434- boost::filesystem::wpath
435- config_path(wasapi_device_manager::base_directory() + L"\\" + id_ + L".xml");
436- if(boost::filesystem::exists(config_path))
437- {
438- try{
439- boost::filesystem::wifstream ifile(config_path);
440- boost::archive::xml_wiarchive ar(ifile);
441- ar >> BOOST_SERIALIZATION_NVP(params);
442- } catch (...)
443- {
444- // ファイル読み込みに問題がある場合はファイルを消す。
445- boost::filesystem::remove(config_path);
446- }
447- }
138+ input_watcher_adapter_->added().connect(boost::bind(&wasapi_device_manager::input_added,this,_1,_2));
139+ input_watcher_adapter_->enumration_completed().connect(boost::bind(&wasapi_device_manager::input_enumeration_completed,this,_1,_2));
140+ input_watcher_adapter_->removed().connect(boost::bind(&wasapi_device_manager::input_removed,this,_1,_2));
141+ input_watcher_adapter_->updated().connect(boost::bind(&wasapi_device_manager::input_updated,this,_1,_2));
142+ input_watcher_adapter_->stopped().connect(boost::bind(&wasapi_device_manager::input_stopped,this,_1,_2));
143+ input_watcher_adapter_->start();
144+
145+ }//
146+
147+ int wasapi_device_manager::get_device_infos(DeviceClass data_flow, std::vector<device_info>& infos,const std::wstring& idd)
148+ {
149+
150+ // 対象デバイスを列挙する
151+ task<DeviceInformationCollection^> t((DeviceInformation::FindAllAsync(data_flow)));
152+ t.wait();
153+ DeviceInformationCollection^ collection = t.get();
154+ int current_index = 0;
155+ bool found = false;
156+ Platform::String^ default_id = ref new Platform::String();
157+ switch(data_flow)
158+ {
159+ case Windows::Devices::Enumeration::DeviceClass::AudioRender:
160+ default_id = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
161+ break;
162+ case Windows::Devices::Enumeration::DeviceClass::AudioCapture:
163+ default_id = MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default);
164+ break;
165+ }
166+
167+
168+ for(int i = 0 ,end = collection->Size;i < end;++i)
169+ {
170+ DeviceInformation^ info = collection->GetAt(i);
171+ infos.push_back(device_info(info));
172+ if(info->Id == default_id)
173+ {
174+ infos[i].is_default_ = true;
175+ }
176+
177+ if(idd == wstring(info->Id->Data()) )
178+ {
179+ infos[i].is_selected_ = true;
180+ current_index = i;
181+ found = true;
182+ }
183+ }
184+
185+ if(!found)
186+ {
187+ for(int i = 0,end = infos.size();i < end;++i)
188+ {
189+ if(infos[i].is_default_)
190+ {
191+ current_index = i;
192+ infos[i].is_selected_ = true;
193+ }
194+ }
195+
196+ }
197+ return current_index;
198+ }
199+
200+ wasapi_device_manager::~wasapi_device_manager()
201+ {
202+ // stop_watching();
203+ }
204+
205+ wasapi_device_manager::device_info::device_info
206+ (DeviceInformation^ info)
207+ : id_(info->Id->Data()),
208+ name_(info->Name->Data()),
209+ display_name_(dynamic_cast<String^>(info->Properties->Lookup(L"System.ItemNameDisplay"))->Data()),
210+ is_enabled_(info->IsEnabled),is_selected_(false),is_default_(info->IsDefault)
211+ {
212+ boost::filesystem::wpath
213+ config_path(wasapi_device_manager::base_directory() + L"\\" + Windows::Foundation::Uri::EscapeComponent(info->Id)->Data() + L".xml");
214+ if(boost::filesystem::exists(config_path))
215+ {
216+ try{
217+ boost::filesystem::wifstream ifile(config_path);
218+ boost::archive::xml_wiarchive ar(ifile);
219+ ar >> BOOST_SERIALIZATION_NVP(params);
220+ } catch (...)
221+ {
222+ // ファイル読み込みに問題がある場合はファイルを消す。
223+ boost::filesystem::remove(config_path);
224+ }
225+ }
448226 #ifdef _DEBUG
449- wdout << L"================================================" << std::endl;
450- wdout << id_ << L"\n" << display_name_ << std::endl;
451- wdout << params.latency << std::endl;
452- wdout << L"================================================" << std::endl;
227+ wdout << L"================================================" << std::endl;
228+ wdout << id_ << L"\n" << display_name_ << std::endl;
229+ wdout << params.latency << std::endl;
230+ wdout << L"================================================" << std::endl;
453231 #endif
454232
455- }
456-
457- wasapi_device_manager::device_info::~device_info()
458- {
459- try {
460- boost::filesystem::wpath
461- config_path(wasapi_device_manager::base_directory() + L"\\" + id_ + L".xml");
462- boost::filesystem::wofstream ofile(config_path,std::ios_base::out | ios_base::trunc);
463- boost::archive::xml_woarchive ar(ofile);
464- ar << BOOST_SERIALIZATION_NVP(params);
465- } catch(...)
466- {
467-
468- }
469- }
470-
471- void wasapi_device_manager::output_added(Windows::Devices::Enumeration::DeviceWatcher^ sender, Windows::Devices::Enumeration::DeviceInformation^ deviceInfo)
472- {
473- std::vector<device_info> * infos = nullptr;
233+ }
234+
235+ void wasapi_device_manager::stop_watching()
236+ {
237+ input_watcher_adapter_->stop();
238+ output_watcher_adapter_->stop();
239+ }
240+
241+ void wasapi_device_manager::save_params()
242+ {
243+ // watcherの停止
244+ // stop_watching();
245+
246+ // 設定の保存
247+ try{
248+ boost::filesystem::wpath config_path(config_path_);
249+ boost::filesystem::wofstream f(config_path,std::ios_base::out | std::ios_base::trunc);
250+ boost::archive::xml_woarchive ar(f);
251+ ar & boost::serialization::make_nvp("output_device_id",current_output_device().id_);
252+ ar & boost::serialization::make_nvp("input_device_id" ,current_input_device().id_);
253+ }catch(...) {
254+
255+ }
256+
257+ // 出力デバイスパラメータの保存
258+ for(device_info& inf : output_device_infos_)
259+ {
260+
261+ try {
262+ boost::filesystem::wpath
263+ config_path(wasapi_device_manager::base_directory() + L"\\" + Windows::Foundation::Uri::EscapeComponent(ref new Platform::String(inf.id_.c_str()))->Data() + L".xml");
264+ boost::filesystem::wofstream ofile(config_path,std::ios_base::out | ios_base::trunc);
265+ boost::archive::xml_woarchive ar(ofile);
266+ ar << BOOST_SERIALIZATION_NVP(inf.params);
267+ } catch(...)
268+ {
269+
270+ }
271+ }
272+
273+ // 入力デバイスパラメータの保存
274+ for(device_info& inf : input_device_infos_)
275+ {
276+
277+ try {
278+ boost::filesystem::wpath
279+ config_path(wasapi_device_manager::base_directory() + L"\\" + Windows::Foundation::Uri::EscapeComponent(ref new Platform::String(inf.id_.c_str()))->Data() + L".xml");
280+ boost::filesystem::wofstream ofile(config_path,std::ios_base::out | ios_base::trunc);
281+ boost::archive::xml_woarchive ar(ofile);
282+ ar << BOOST_SERIALIZATION_NVP(inf.params);
283+ } catch(...)
284+ {
285+
286+ }
287+ }
288+
289+ }
290+
291+ wasapi_device_manager::device_info::~device_info()
292+ {
293+ }
294+
295+ void wasapi_device_manager::output_added(Windows::Devices::Enumeration::DeviceWatcher^ sender, Windows::Devices::Enumeration::DeviceInformation^ deviceInfo)
296+ {
297+ device_info info(deviceInfo);
298+
299+ IAudioClient2Ptr c;
300+ // オーディオクライアントを取得
301+ {
302+ ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOpPtr;
303+ ComPtr<ActivateAudioInterfaceCompletionHandler> handler(Make<ActivateAudioInterfaceCompletionHandler>());
304+ HRESULT hr = ActivateAudioInterfaceAsync(info.id_.c_str(),__uuidof(IAudioClient2),nullptr,handler.Get(),asyncOpPtr.GetAddressOf());
305+ if(SUCCEEDED(hr)){
306+ handler->wait();
307+ if(handler->ResultCode() == S_OK)
308+ {
309+ c = handler->AudioClient();
310+ } else {
311+ throw win32_error_exception(handler->ResultCode());
312+ }
313+ } else {
314+ throw win32_error_exception(handler->ResultCode());
315+ }
316+ }
317+ c->GetDevicePeriod(&(info.latency_default_),&(info.latency_minimum_));
318+ if(info.params.latency == 0
319+ || info.params.latency < info.latency_minimum_ )
320+ {
321+ info.params.latency = info.latency_default_;
322+ }
323+
474324 #ifdef _DEBUG
475-// if(deviceInfo->IsEnabled){
476- wdout << deviceInfo->Name->Data() << std::endl;
477-
478-// auto i = deviceInfo->Properties->First();
479-// while(i->HasCurrent)
480-// {
481-//// wdout << iCurrent->Key->Data() << L"," << i->Current->Value->ToString()->Data() << std::endl;
482-// wdout << i->
483-// i->MoveNext();
484-// }
485-// }
325+ wdout << info.name_ << endl;
326+ wdout << boost::wformat(L"latency default:%d min:%d") % info.latency_default_ % info.latency_minimum_ << endl;
327+#endif
328+ for(int bits = 0;bits < NUM_SAMPLE_BITS;++bits)
329+ {
330+ for(int channel = 1;channel < 3;++channel)
331+ {
332+ for(int rate = 0;rate < NUM_SAMPLE_RATE;++rate)
333+ {
334+ sf::co_task_memory<WAVEFORMATEXTENSIBLE> a,a1;
335+ WAVEFORMATEXTENSIBLE f;
336+
337+ // make_wave_fomat(f,sample_rates[rate],channel,sample_bits[bits]);
338+ if(sample_bits[bits].valid_bits_per_sample == WAVE_FORMAT_IEEE_FLOAT)
339+ {
340+ bits_pair b ={sample_bits[bits].bits_per_sample,sample_bits[bits].bits_per_sample};
341+ make_wave_format(f,sample_rates[rate],channel,b,WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
342+ } else {
343+ make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_PCM);
344+ }
345+
346+ // 排他モード
347+ HRESULT hr = c->IsFormatSupported(
348+ AUDCLNT_SHAREMODE_EXCLUSIVE,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a));
349+ if(hr == S_OK){
350+ info.support_formats_[AUDCLNT_SHAREMODE_EXCLUSIVE][ sample_bits[bits].bits_per_sample ][sample_bits[bits].valid_bits_per_sample][channel][sample_rates[rate]] = 0;
351+#ifdef _DEBUG
352+ wdout << boost::wformat(L"|exc |bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|") % sample_bits[bits].bits_per_sample % sample_bits[bits].valid_bits_per_sample % channel % sample_rates[rate] % (hr == S_OK?L"OK":L"NG") << endl;
353+#endif }
354+
355+ // make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE);
356+ // 共有モード
357+ hr = c->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a1));
358+ if(hr == S_OK){
359+ info
360+ .support_formats_
361+ [AUDCLNT_SHAREMODE_SHARED]
362+ [ sample_bits[bits].bits_per_sample ]
363+ [sample_bits[bits].valid_bits_per_sample]
364+ [channel][sample_rates[rate]]
365+ = 0;
366+#ifdef _DEBUG
367+ wdout << boost::wformat(L"|share|bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
368+ % sample_bits[bits].bits_per_sample
369+ % sample_bits[bits].valid_bits_per_sample
370+ % channel % sample_rates[rate]
371+ % (hr == S_OK?L"OK":L"NG") << endl;
372+
373+#endif
374+ }
375+ }
376+ }
377+ }
378+#ifdef _DEBUG
379+ wdout << "-------------------------------" << std::endl;
486380 #endif
487- }
381+ }
382+
383+ if(output_id_ == deviceInfo->Id->Data())
384+ {
385+ info.is_selected_ = true;
386+ }
387+ if(default_output_id_ == deviceInfo->Id->Data())
388+ {
389+ info.is_default_ = true;
390+ }
391+ output_device_infos_.push_back(info);
488392
489- void wasapi_device_manager::output_enumeration_completed(en::DeviceWatcher^ sender, Platform::Object^ obj)
490- {
491- }
393+#ifdef _DEBUG
394+ wdout << deviceInfo->Name->Data() << std::endl;
395+#endif
396+}
397+
398+void wasapi_device_manager::output_enumeration_completed(en::DeviceWatcher^ sender, Platform::Object^ obj)
399+{
400+
401+ for(int i = 0;i < output_device_infos_.size();++i)
402+ {
403+ device_info& inf(output_device_infos_[i]);
404+ if(inf.is_selected_ && inf.is_enabled_)
405+ {
406+ output_device_index_ = i;
407+ }
408+ }
409+
410+ if(output_device_index_ == -1)
411+ {
412+ for(int i = 0;i < output_device_infos_.size();++i)
413+ {
414+ device_info& inf(output_device_infos_[i]);
415+ if(inf.is_default_)
416+ {
417+ output_device_index_ = i;
418+ inf.is_selected_ = true;
419+ }
420+ }
421+ }
422+
423+ output_enum_completed_ = true;
424+}
425+
426+void wasapi_device_manager::output_updated(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
427+{
428+}
429+
430+void wasapi_device_manager::output_removed(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
431+{
432+}
433+
434+void wasapi_device_manager::output_stopped(en::DeviceWatcher^ sender, Platform::Object^ obj)
435+{
436+}
437+
438+void wasapi_device_manager::input_added(Windows::Devices::Enumeration::DeviceWatcher^ sender, Windows::Devices::Enumeration::DeviceInformation^ deviceInfo)
439+{
440+ device_info info(deviceInfo);
441+ IAudioClient2Ptr c;
442+ // オーディオクライアントを取得
443+ // THROW_IF_ERR(ActivateAudioInterface(info.id_.c_str(),__uuidof(IAudioClient2),&c) );
444+ {
445+ ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOpPtr;
446+// IActivateAudioInterfaceAsyncOperation* asyncOp;
447+ ComPtr<ActivateAudioInterfaceCompletionHandler> handler(Make<ActivateAudioInterfaceCompletionHandler>());
448+ HRESULT hr = ActivateAudioInterfaceAsync(info.id_.c_str(),__uuidof(IAudioClient2),nullptr,handler.Get(),asyncOpPtr.GetAddressOf());
449+// asyncOpPtr.Attach(asyncOp);
450+ if(SUCCEEDED(hr)){
451+ handler->wait();
452+ if(handler->ResultCode() == S_OK)
453+ {
454+ c = handler->AudioClient();
455+ } else {
456+ throw win32_error_exception(handler->ResultCode());
457+ }
458+ } else {
459+ throw win32_error_exception(handler->ResultCode());
460+ }
461+ }
462+ c->GetDevicePeriod(&(info.latency_default_),&(info.latency_minimum_));
463+ if(info.params.latency == 0 || info.params.latency < info.latency_minimum_ ){
464+ info.params.latency = info.latency_default_;
465+ }
466+#ifdef _DEBUG
467+ wdout << info.display_name_ << endl;
468+ wdout << boost::wformat(L"latency default:%d min:%d") % info.latency_default_ % info.latency_minimum_ << endl;
469+#endif
470+ for(int bits = 0;bits < NUM_SAMPLE_BITS;++bits)
471+ {
472+ for(int channel = 1;channel < 3;++channel)
473+ {
474+ for(int rate = 0;rate < NUM_SAMPLE_RATE;++rate)
475+ {
476+ sf::co_task_memory<WAVEFORMATEXTENSIBLE> a,a1;
477+ WAVEFORMATEXTENSIBLE f;
478+
479+ // make_wave_fomat(f,sample_rates[rate],channel,sample_bits[bits]);
480+ if(sample_bits[bits].valid_bits_per_sample == WAVE_FORMAT_IEEE_FLOAT)
481+ {
482+ bits_pair b ={sample_bits[bits].bits_per_sample,sample_bits[bits].bits_per_sample};
483+ make_wave_format(f,sample_rates[rate],channel,b,WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
484+ } else {
485+ make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_PCM);
486+ }
487+
488+ // 排他モード
489+ HRESULT hr = c->IsFormatSupported(
490+ AUDCLNT_SHAREMODE_EXCLUSIVE,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a));
491+ if(hr == S_OK){
492+ info
493+ .support_formats_
494+ [AUDCLNT_SHAREMODE_EXCLUSIVE]
495+ [ sample_bits[bits].bits_per_sample ]
496+ [sample_bits[bits].valid_bits_per_sample]
497+ [channel]
498+ [sample_rates[rate]] = 0;
499+#ifdef _DEBUG
500+ wdout << boost::wformat(L"|exc |bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
501+ % sample_bits[bits].bits_per_sample
502+ % sample_bits[bits].valid_bits_per_sample
503+ % channel % sample_rates[rate]
504+ % (hr == S_OK?L"OK":L"NG") << endl;
505+#endif }
506+
507+ // make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE);
508+ // 共有モード
509+ hr = c->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a1));
510+ if(hr == S_OK){
511+ info.
512+ support_formats_
513+ [AUDCLNT_SHAREMODE_SHARED]
514+ [sample_bits[bits].bits_per_sample ]
515+ [sample_bits[bits].valid_bits_per_sample]
516+ [channel]
517+ [sample_rates[rate]] = 0;
518+#ifdef _DEBUG
519+ wdout << boost::wformat(L"|share|bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
520+ % sample_bits[bits].bits_per_sample
521+ % sample_bits[bits].valid_bits_per_sample
522+ % channel
523+ % sample_rates[rate]
524+ % (hr == S_OK?L"OK":L"NG") << endl;
492525
493- void wasapi_device_manager::output_updated(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
494- {
495- }
526+#endif
527+ }
528+ }
529+ }
530+ }
531+#ifdef _DEBUG
532+ wdout << "-------------------------------" << std::endl;
533+#endif
534+}
496535
497- void wasapi_device_manager::output_removed(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
498- {
499- }
536+if(input_id_ == deviceInfo->Id->Data())
537+{
538+ info.is_selected_ = true;
539+}
540+if(default_input_id_ == deviceInfo->Id->Data())
541+{
542+ info.is_default_ = true;
543+}
500544
501- void wasapi_device_manager::output_stopped(en::DeviceWatcher^ sender, Platform::Object^ obj)
502- {
503- }
545+input_device_infos_.push_back(info);
504546
505- void wasapi_device_manager::input_added(Windows::Devices::Enumeration::DeviceWatcher^ sender, Windows::Devices::Enumeration::DeviceInformation^ deviceInfo)
506- {
507- std::vector<device_info> * infos = nullptr;
508547 #ifdef _DEBUG
509548 // if(deviceInfo->IsEnabled){
510- wdout << deviceInfo->Name->Data() << std::endl;
511-
549+wdout << deviceInfo->Name->Data() << std::endl;
550+
512551 // auto i = deviceInfo->Properties->First();
513552 // while(i->HasCurrent)
514553 // {
@@ -518,21 +557,44 @@ namespace sf {
518557 // }
519558 // }
520559 #endif
521- }
522-
523- void wasapi_device_manager::input_enumeration_completed(en::DeviceWatcher^ sender, Platform::Object^ obj)
524- {
525- }
526-
527- void wasapi_device_manager::input_updated(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
528- {
529- }
530-
531- void wasapi_device_manager::input_removed(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
532- {
533- }
534-
535- void wasapi_device_manager::input_stopped(en::DeviceWatcher^ sender, Platform::Object^ obj)
536- {
537- }
560+ }
561+
562+ void wasapi_device_manager::input_enumeration_completed(en::DeviceWatcher^ sender, Platform::Object^ obj)
563+ {
564+
565+ for(int i = 0;i < input_device_infos_.size();++i)
566+ {
567+ device_info& inf(input_device_infos_[i]);
568+ if(inf.is_selected_ && inf.is_enabled_)
569+ {
570+ input_device_index_ = i;
571+ }
572+ }
573+
574+ if(input_device_index_ == -1)
575+ {
576+ for(int i = 0;i < input_device_infos_.size();++i)
577+ {
578+ device_info& inf(input_device_infos_[i]);
579+ if(inf.is_default_)
580+ {
581+ input_device_index_ = i;
582+ inf.is_selected_ = true;
583+ }
584+ }
585+ }
586+ input_enum_completed_ = true;
587+ }
588+
589+ void wasapi_device_manager::input_updated(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
590+ {
591+ }
592+
593+ void wasapi_device_manager::input_removed(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
594+ {
595+ }
596+
597+ void wasapi_device_manager::input_stopped(en::DeviceWatcher^ sender, Platform::Object^ obj)
598+ {
599+ }
538600 }
\ No newline at end of file
Show on old repository browser