2 ==============================================================================
\r
4 Copyright 2005-11 by Satoshi Fujiwara.
\r
6 async can be redistributed and/or modified under the terms of the
\r
7 GNU General Public License, as published by the Free Software Foundation;
\r
8 either version 2 of the License, or (at your option) any later version.
\r
10 async is distributed in the hope that it will be useful,
\r
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 GNU General Public License for more details.
\r
15 You should have received a copy of the GNU General Public License
\r
16 along with async; if not, visit www.gnu.org/licenses or write to the
\r
17 Free Software Foundation, Inc., 59 Temple Place, Suite 330,
\r
18 Boston, MA 02111-1307 USA
\r
20 ==============================================================================
\r
25 #define _CRTDBG_MAP_ALLOC
\r
27 #define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
\r
29 #include "sf_memory.h"
\r
30 #include "audio_base.h"
\r
32 //#include "application.h"
\r
33 //using namespace boost;
\r
34 using namespace std;
\r
35 using namespace Windows::Foundation;
\r
36 using namespace Windows::Foundation::Collections;
\r
37 using namespace Windows::Devices::Enumeration;
\r
38 using namespace Windows::Media::Devices;
\r
39 using namespace Concurrency;
\r
40 using namespace Platform;
\r
41 using namespace Microsoft::WRL;
\r
44 const wstring wasapi_device_manager::base_directory_(L"."/* Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data() */);
\r
45 const int wasapi_device_manager::sample_rates[NUM_SAMPLE_RATE] = {8000,11025,16000,22050,24000,32000,44100,48000,88200,96000,176400,192000};
\r
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}};
\r
48 void make_wave_format(WAVEFORMATEXTENSIBLE& format,int sample_rate,int channels,bits_pair b,uint32_t type,const GUID& sub_type)
\r
50 ZeroMemory(&format,sizeof(WAVEFORMATEXTENSIBLE));
\r
51 format.Format.wFormatTag = type;
\r
52 format.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
\r
53 format.SubFormat = sub_type;
\r
54 format.Format.nSamplesPerSec = sample_rate;
\r
55 format.Format.nChannels = channels;
\r
56 format.Format.wBitsPerSample = b.bits_per_sample;
\r
57 format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * format.Format.nChannels;
\r
58 format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * format.Format.nBlockAlign;
\r
59 format.Samples.wValidBitsPerSample = b.valid_bits_per_sample;
\r
61 format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
\r
64 void make_wave_format(WAVEFORMATEX& format,int sample_rate,int channels,int bits,uint32_t type)
\r
66 ZeroMemory(&format,sizeof(WAVEFORMATEX));
\r
67 format.wFormatTag = type;
\r
68 format.nSamplesPerSec = sample_rate;
\r
69 format.nChannels = channels;
\r
70 format.wBitsPerSample = bits;
\r
71 format.nBlockAlign = (format.wBitsPerSample / 8) * format.nChannels;
\r
72 format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
\r
75 struct prop_variant
\r
79 PropVariantInit(&value_);
\r
84 PropVariantClear(&value_);
\r
87 PROPVARIANT* get(){ return &value_;};
\r
89 PROPVARIANT* operator &(){return get();}
\r
91 operator PROPVARIANT*() {return get();}
\r
98 wasapi_device_manager::wasapi_device_manager()
\r
99 : output_device_index_(-1),input_device_index_(-1),output_enum_completed_(false),input_enum_completed_(false)
\r
102 config_path_.append(base_directory_).append(L"\\wasapi_device_manager.config.xml");
\r
103 boost::filesystem::wpath config_path(config_path_);
\r
106 if(boost::filesystem::exists(config_path))
\r
109 boost::filesystem::wifstream f(config_path);
\r
110 boost::archive::xml_wiarchive ar(f);
\r
111 ar & boost::serialization::make_nvp("output_device_id",output_id_);
\r
112 ar & boost::serialization::make_nvp("input_device_id" ,input_id_);
\r
115 output_id_ = input_id_ = L"";
\r
116 boost::filesystem::remove(config_path);
\r
120 default_output_id_ = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default)->Data();
\r
121 default_input_id_ = MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default)->Data();
\r
123 //watcher_ = DeviceInformation::CreateWatcher();
\r
124 //adapter_ = ref new typed_event_handler_adapter<DeviceWatcher^,DeviceInformation^>(boost::bind(&wasapi_device_manager::added,this,_1,_2));
\r
125 // watcher_->Added += adapter_->get();
\r
126 //watcher_->Added += (ref new typed_event_handler_adapter<DeviceWatcher^,DeviceInformation^>(boost::bind(&wasapi_device_manager::added,this,_1,_2)))->get();
\r
127 //watcher_->Start();
\r
129 output_watcher_adapter_ = ref new DeviceWatcherAdapter(en::DeviceClass::AudioRender);
\r
130 output_watcher_adapter_->added().connect(boost::bind(&wasapi_device_manager::output_added,this,_1,_2));
\r
131 output_watcher_adapter_->enumration_completed().connect(boost::bind(&wasapi_device_manager::output_enumeration_completed,this,_1,_2));
\r
132 output_watcher_adapter_->removed().connect(boost::bind(&wasapi_device_manager::output_removed,this,_1,_2));
\r
133 output_watcher_adapter_->updated().connect(boost::bind(&wasapi_device_manager::output_updated,this,_1,_2));
\r
134 output_watcher_adapter_->stopped().connect(boost::bind(&wasapi_device_manager::output_stopped,this,_1,_2));
\r
135 output_watcher_adapter_->start();
\r
137 input_watcher_adapter_ = ref new DeviceWatcherAdapter(en::DeviceClass::AudioCapture);
\r
138 input_watcher_adapter_->added().connect(boost::bind(&wasapi_device_manager::input_added,this,_1,_2));
\r
139 input_watcher_adapter_->enumration_completed().connect(boost::bind(&wasapi_device_manager::input_enumeration_completed,this,_1,_2));
\r
140 input_watcher_adapter_->removed().connect(boost::bind(&wasapi_device_manager::input_removed,this,_1,_2));
\r
141 input_watcher_adapter_->updated().connect(boost::bind(&wasapi_device_manager::input_updated,this,_1,_2));
\r
142 input_watcher_adapter_->stopped().connect(boost::bind(&wasapi_device_manager::input_stopped,this,_1,_2));
\r
143 input_watcher_adapter_->start();
\r
147 int wasapi_device_manager::get_device_infos(DeviceClass data_flow, std::vector<device_info>& infos,const std::wstring& idd)
\r
151 task<DeviceInformationCollection^> t((DeviceInformation::FindAllAsync(data_flow)));
\r
153 DeviceInformationCollection^ collection = t.get();
\r
154 int current_index = 0;
\r
155 bool found = false;
\r
156 Platform::String^ default_id = ref new Platform::String();
\r
159 case Windows::Devices::Enumeration::DeviceClass::AudioRender:
\r
160 default_id = MediaDevice::GetDefaultAudioRenderId(AudioDeviceRole::Default);
\r
162 case Windows::Devices::Enumeration::DeviceClass::AudioCapture:
\r
163 default_id = MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default);
\r
168 for(int i = 0 ,end = collection->Size;i < end;++i)
\r
170 DeviceInformation^ info = collection->GetAt(i);
\r
171 infos.push_back(device_info(info));
\r
172 if(info->Id == default_id)
\r
174 infos[i].is_default_ = true;
\r
177 if(idd == wstring(info->Id->Data()) )
\r
179 infos[i].is_selected_ = true;
\r
187 for(int i = 0,end = infos.size();i < end;++i)
\r
189 if(infos[i].is_default_)
\r
192 infos[i].is_selected_ = true;
\r
197 return current_index;
\r
200 wasapi_device_manager::~wasapi_device_manager()
\r
202 // stop_watching();
\r
205 wasapi_device_manager::device_info::device_info
\r
206 (DeviceInformation^ info)
\r
207 : id_(info->Id->Data()),
\r
208 name_(info->Name->Data()),
\r
209 display_name_(dynamic_cast<String^>(info->Properties->Lookup(L"System.ItemNameDisplay"))->Data()),
\r
210 is_enabled_(info->IsEnabled),is_selected_(false),is_default_(info->IsDefault)
\r
212 boost::filesystem::wpath
\r
213 config_path(wasapi_device_manager::base_directory() + L"\\" + Windows::Foundation::Uri::EscapeComponent(info->Id)->Data() + L".xml");
\r
214 if(boost::filesystem::exists(config_path))
\r
217 boost::filesystem::wifstream ifile(config_path);
\r
218 boost::archive::xml_wiarchive ar(ifile);
\r
219 ar >> BOOST_SERIALIZATION_NVP(params);
\r
222 // ファイル読み込みに問題がある場合はファイルを消す。
\r
223 boost::filesystem::remove(config_path);
\r
227 wdout << L"================================================" << std::endl;
\r
228 wdout << id_ << L"\n" << display_name_ << std::endl;
\r
229 wdout << params.latency << std::endl;
\r
230 wdout << L"================================================" << std::endl;
\r
235 void wasapi_device_manager::stop_watching()
\r
237 input_watcher_adapter_->stop();
\r
238 output_watcher_adapter_->stop();
\r
241 void wasapi_device_manager::save_params()
\r
244 // stop_watching();
\r
248 boost::filesystem::wpath config_path(config_path_);
\r
249 boost::filesystem::wofstream f(config_path,std::ios_base::out | std::ios_base::trunc);
\r
250 boost::archive::xml_woarchive ar(f);
\r
251 ar & boost::serialization::make_nvp("output_device_id",current_output_device().id_);
\r
252 ar & boost::serialization::make_nvp("input_device_id" ,current_input_device().id_);
\r
258 for(device_info& inf : output_device_infos_)
\r
262 boost::filesystem::wpath
\r
263 config_path(wasapi_device_manager::base_directory() + L"\\" + Windows::Foundation::Uri::EscapeComponent(ref new Platform::String(inf.id_.c_str()))->Data() + L".xml");
\r
264 boost::filesystem::wofstream ofile(config_path,std::ios_base::out | ios_base::trunc);
\r
265 boost::archive::xml_woarchive ar(ofile);
\r
266 ar << BOOST_SERIALIZATION_NVP(inf.params);
\r
274 for(device_info& inf : input_device_infos_)
\r
278 boost::filesystem::wpath
\r
279 config_path(wasapi_device_manager::base_directory() + L"\\" + Windows::Foundation::Uri::EscapeComponent(ref new Platform::String(inf.id_.c_str()))->Data() + L".xml");
\r
280 boost::filesystem::wofstream ofile(config_path,std::ios_base::out | ios_base::trunc);
\r
281 boost::archive::xml_woarchive ar(ofile);
\r
282 ar << BOOST_SERIALIZATION_NVP(inf.params);
\r
291 wasapi_device_manager::device_info::~device_info()
\r
295 void wasapi_device_manager::output_added(Windows::Devices::Enumeration::DeviceWatcher^ sender, Windows::Devices::Enumeration::DeviceInformation^ deviceInfo)
\r
297 device_info info(deviceInfo);
\r
299 IAudioClient2Ptr c;
\r
302 ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOpPtr;
\r
303 ComPtr<ActivateAudioInterfaceCompletionHandler> handler(Make<ActivateAudioInterfaceCompletionHandler>());
\r
304 HRESULT hr = ActivateAudioInterfaceAsync(info.id_.c_str(),__uuidof(IAudioClient2),nullptr,handler.Get(),asyncOpPtr.GetAddressOf());
\r
307 if(handler->ResultCode() == S_OK)
\r
309 c = handler->AudioClient();
\r
311 throw win32_error_exception(handler->ResultCode());
\r
314 throw win32_error_exception(handler->ResultCode());
\r
317 c->GetDevicePeriod(&(info.latency_default_),&(info.latency_minimum_));
\r
318 if(info.params.latency == 0
\r
319 || info.params.latency < info.latency_minimum_ )
\r
321 info.params.latency = info.latency_default_;
\r
325 wdout << info.name_ << endl;
\r
326 wdout << boost::wformat(L"latency default:%d min:%d") % info.latency_default_ % info.latency_minimum_ << endl;
\r
328 for(int bits = 0;bits < NUM_SAMPLE_BITS;++bits)
\r
330 for(int channel = 1;channel < 3;++channel)
\r
332 for(int rate = 0;rate < NUM_SAMPLE_RATE;++rate)
\r
334 sf::co_task_memory<WAVEFORMATEXTENSIBLE> a,a1;
\r
335 WAVEFORMATEXTENSIBLE f;
\r
337 // make_wave_fomat(f,sample_rates[rate],channel,sample_bits[bits]);
\r
338 if(sample_bits[bits].valid_bits_per_sample == WAVE_FORMAT_IEEE_FLOAT)
\r
340 bits_pair b ={sample_bits[bits].bits_per_sample,sample_bits[bits].bits_per_sample};
\r
341 make_wave_format(f,sample_rates[rate],channel,b,WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
\r
343 make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_PCM);
\r
347 HRESULT hr = c->IsFormatSupported(
\r
348 AUDCLNT_SHAREMODE_EXCLUSIVE,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a));
\r
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;
\r
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;
\r
355 // make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE);
\r
357 hr = c->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a1));
\r
361 [AUDCLNT_SHAREMODE_SHARED]
\r
362 [ sample_bits[bits].bits_per_sample ]
\r
363 [sample_bits[bits].valid_bits_per_sample]
\r
364 [channel][sample_rates[rate]]
\r
367 wdout << boost::wformat(L"|share|bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
\r
368 % sample_bits[bits].bits_per_sample
\r
369 % sample_bits[bits].valid_bits_per_sample
\r
370 % channel % sample_rates[rate]
\r
371 % (hr == S_OK?L"OK":L"NG") << endl;
\r
379 wdout << "-------------------------------" << std::endl;
\r
383 if(output_id_ == deviceInfo->Id->Data())
\r
385 info.is_selected_ = true;
\r
387 if(default_output_id_ == deviceInfo->Id->Data())
\r
389 info.is_default_ = true;
\r
391 output_device_infos_.push_back(info);
\r
394 wdout << deviceInfo->Name->Data() << std::endl;
\r
398 void wasapi_device_manager::output_enumeration_completed(en::DeviceWatcher^ sender, Platform::Object^ obj)
\r
401 for(int i = 0;i < output_device_infos_.size();++i)
\r
403 device_info& inf(output_device_infos_[i]);
\r
404 if(inf.is_selected_ && inf.is_enabled_)
\r
406 output_device_index_ = i;
\r
410 if(output_device_index_ == -1)
\r
412 for(int i = 0;i < output_device_infos_.size();++i)
\r
414 device_info& inf(output_device_infos_[i]);
\r
415 if(inf.is_default_)
\r
417 output_device_index_ = i;
\r
418 inf.is_selected_ = true;
\r
423 output_enum_completed_ = true;
\r
426 void wasapi_device_manager::output_updated(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
\r
430 void wasapi_device_manager::output_removed(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
\r
434 void wasapi_device_manager::output_stopped(en::DeviceWatcher^ sender, Platform::Object^ obj)
\r
438 void wasapi_device_manager::input_added(Windows::Devices::Enumeration::DeviceWatcher^ sender, Windows::Devices::Enumeration::DeviceInformation^ deviceInfo)
\r
440 device_info info(deviceInfo);
\r
441 IAudioClient2Ptr c;
\r
443 // THROW_IF_ERR(ActivateAudioInterface(info.id_.c_str(),__uuidof(IAudioClient2),&c) );
\r
445 ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOpPtr;
\r
446 // IActivateAudioInterfaceAsyncOperation* asyncOp;
\r
447 ComPtr<ActivateAudioInterfaceCompletionHandler> handler(Make<ActivateAudioInterfaceCompletionHandler>());
\r
448 HRESULT hr = ActivateAudioInterfaceAsync(info.id_.c_str(),__uuidof(IAudioClient2),nullptr,handler.Get(),asyncOpPtr.GetAddressOf());
\r
449 // asyncOpPtr.Attach(asyncOp);
\r
452 if(handler->ResultCode() == S_OK)
\r
454 c = handler->AudioClient();
\r
456 throw win32_error_exception(handler->ResultCode());
\r
459 throw win32_error_exception(handler->ResultCode());
\r
462 c->GetDevicePeriod(&(info.latency_default_),&(info.latency_minimum_));
\r
463 if(info.params.latency == 0 || info.params.latency < info.latency_minimum_ ){
\r
464 info.params.latency = info.latency_default_;
\r
467 wdout << info.display_name_ << endl;
\r
468 wdout << boost::wformat(L"latency default:%d min:%d") % info.latency_default_ % info.latency_minimum_ << endl;
\r
470 for(int bits = 0;bits < NUM_SAMPLE_BITS;++bits)
\r
472 for(int channel = 1;channel < 3;++channel)
\r
474 for(int rate = 0;rate < NUM_SAMPLE_RATE;++rate)
\r
476 sf::co_task_memory<WAVEFORMATEXTENSIBLE> a,a1;
\r
477 WAVEFORMATEXTENSIBLE f;
\r
479 // make_wave_fomat(f,sample_rates[rate],channel,sample_bits[bits]);
\r
480 if(sample_bits[bits].valid_bits_per_sample == WAVE_FORMAT_IEEE_FLOAT)
\r
482 bits_pair b ={sample_bits[bits].bits_per_sample,sample_bits[bits].bits_per_sample};
\r
483 make_wave_format(f,sample_rates[rate],channel,b,WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_IEEE_FLOAT);
\r
485 make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE,KSDATAFORMAT_SUBTYPE_PCM);
\r
489 HRESULT hr = c->IsFormatSupported(
\r
490 AUDCLNT_SHAREMODE_EXCLUSIVE,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a));
\r
494 [AUDCLNT_SHAREMODE_EXCLUSIVE]
\r
495 [ sample_bits[bits].bits_per_sample ]
\r
496 [sample_bits[bits].valid_bits_per_sample]
\r
498 [sample_rates[rate]] = 0;
\r
500 wdout << boost::wformat(L"|exc |bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
\r
501 % sample_bits[bits].bits_per_sample
\r
502 % sample_bits[bits].valid_bits_per_sample
\r
503 % channel % sample_rates[rate]
\r
504 % (hr == S_OK?L"OK":L"NG") << endl;
\r
507 // make_wave_format(f,sample_rates[rate],channel,sample_bits[bits],WAVE_FORMAT_EXTENSIBLE);
\r
509 hr = c->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,reinterpret_cast<WAVEFORMATEX*>(&f),reinterpret_cast<WAVEFORMATEX**>(&a1));
\r
513 [AUDCLNT_SHAREMODE_SHARED]
\r
514 [sample_bits[bits].bits_per_sample ]
\r
515 [sample_bits[bits].valid_bits_per_sample]
\r
517 [sample_rates[rate]] = 0;
\r
519 wdout << boost::wformat(L"|share|bits:%02d|vbits:%02d|channel:%02d|rate:%06d|%s|")
\r
520 % sample_bits[bits].bits_per_sample
\r
521 % sample_bits[bits].valid_bits_per_sample
\r
523 % sample_rates[rate]
\r
524 % (hr == S_OK?L"OK":L"NG") << endl;
\r
532 wdout << "-------------------------------" << std::endl;
\r
536 if(input_id_ == deviceInfo->Id->Data())
\r
538 info.is_selected_ = true;
\r
540 if(default_input_id_ == deviceInfo->Id->Data())
\r
542 info.is_default_ = true;
\r
545 input_device_infos_.push_back(info);
\r
548 // if(deviceInfo->IsEnabled){
\r
549 wdout << deviceInfo->Name->Data() << std::endl;
\r
551 // auto i = deviceInfo->Properties->First();
\r
552 // while(i->HasCurrent)
\r
554 //// wdout << iCurrent->Key->Data() << L"," << i->Current->Value->ToString()->Data() << std::endl;
\r
562 void wasapi_device_manager::input_enumeration_completed(en::DeviceWatcher^ sender, Platform::Object^ obj)
\r
565 for(int i = 0;i < input_device_infos_.size();++i)
\r
567 device_info& inf(input_device_infos_[i]);
\r
568 if(inf.is_selected_ && inf.is_enabled_)
\r
570 input_device_index_ = i;
\r
574 if(input_device_index_ == -1)
\r
576 for(int i = 0;i < input_device_infos_.size();++i)
\r
578 device_info& inf(input_device_infos_[i]);
\r
579 if(inf.is_default_)
\r
581 input_device_index_ = i;
\r
582 inf.is_selected_ = true;
\r
586 input_enum_completed_ = true;
\r
589 void wasapi_device_manager::input_updated(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
\r
593 void wasapi_device_manager::input_removed(en::DeviceWatcher^ sender, en::DeviceInformationUpdate^ deviceInfo)
\r
597 void wasapi_device_manager::input_stopped(en::DeviceWatcher^ sender, Platform::Object^ obj)
\r