ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
DriverKitによるオーディオドライバの作成
AudioDriverKit APIを使用して、Audio ServerプラグインとDriverKit Extensionを1つのパッケージに統合する方法を紹介します。インストーラパッケージの代わりにAppを使ってオーディオドライバのインストールをシンプル化し、Mac App Storeを通じてドライバを配布する方法について確認します。また、Core Audio HALがAudioDriverKitとどのように連携しているかを説明し、オーディオデバイスドライバのベストプラクティスを紹介します。
リソース
関連ビデオ
WWDC22
WWDC20
WWDC19
-
ダウンロード
こんにちはBaek San Changです Core Audio担当です DriverKitによる オーディオドライバの作成 新しい方法の話です その前に 仕組みをおさらいしておきましょう macOS Big Sur以前の オーディオサーバーのプラグインは ユーザークライアントからカーネル拡張を介して ハードウェアデバイスと通信する必要がありました macOS Big Surでは CoreAudio HALは DriverKit拡張の上に作った オーディオサーバーの プラグイン作成のためのサポートをしました プラグインとdextの間のレイヤーは カーネル機能拡張(kext)と 同じだが カーネルから ユーザースペースに移って セキュリティが向上しました DriverKitの詳細は 前のWWDC DriverKitの ビデオをご覧ください 今 ソリューションはオーディオ ドライバーの開発が カーネルから離れています 機能的なハードウェアオーディオドライバの実装は 2つの独立したコンポーネントが必要です オーディオサーバーのプラグインとドライバー拡張 これは開発を複雑にしリソースを増やします さらにオーバーヘッドと レイテンシーを増やすだろう macOS Montereyでは dextさえあれば プラグインは要らない AudioDriverKitは新しい DriverKitの枠組みで USBDriverKitか PCIDriverKitと一緒に オーディオドライバー拡張を書くものです この新しい枠組みは CoreAudio HALとの プロセス間通信を処理します dextしかなくても dextと オーディオサーバーの プラグインとの間の通信は要らなくなったのです DriverKitに集中できるのです
AudioDriverKitの拡張は Mac Appに内蔵されている もう別のインストーラーは要らないのです
ドライバーはすぐに読み込まれます 再起動は要りません
AudioDriverKitの良さを 知っていただけたと思います それでは 新しいオーディオ ドライバーを作りましょう オーディオドライバーの構成要素から 簡単にご説明します その後 dextを書く前に必要なことを話します
コードを書く準備ができたら dextの設定や初期化 デバイスやストリームの作成 その他のオーディオオブジェクトの作成方法 そして IOパスとタイムスタンプを説明します 最後に 設定変更の仕方を説明し dextのデモをお見せします
それでは 基本設計概念から始めましょう この図は AudioDriverKitの 枠組み使った ドライバー拡張とHALの 通信方法を示す図です AudioDriverKitの枠組みは CoreAudioと あなたのオーディオdext間の すべての通信に使用される プライベートユーザークライアントを作成します このユーザークライアントは 直接使用されるのではなく あなたのdextにも出てきません Appとdext間の通信に必要なプラグインや カスタムユーザークライアントがないはずです 必要ならばAppがdextと直接通信するために カスタムユーザークライアントを開けます 必要なエンタイトルメントの話を続けてします すべてのDriverKitドライバー拡張は DriverKitエンタイトルメントが必須だ AudioDriverKit dextも ユーザークライアント用の エンタイトルメントが要る DriverKitエンタイトルメントは 承認された全開発者に有効である トランスポート・ファミリーエンタイトルメントも 必要に応じて加えるべきです USBまたはPCIトランスポートの エンタイトルメントをまだ リクエストしていない場合は Appleの開発者向けサイトに リクエストしてください 注意してほしいのはここで紹介する サンプルコードはあくまでデモ用であり ハードウェアデバイスに関連付けられていない 仮想オーディオドライバーを作成するためです だから エンタイトルメントは付与されません 仮想オーディオドライバーや デバイスが必要な場合は オーディオサーバープラグインのリバーモデルが ずっと使われ続けます では dextのinfo.plistを 見てみましょう これらの設定に dextのIOKitPersonalitiesが 加えられる必要があります HALが必要とするIOUserAudio DriverUserClientを AudioDriverKitが作成します HALはユーザークライアント接続に要求される エンタイトルメントを持っています ここにSimpleAudio DriverUserClientの カスタムユーザークライアントの例があります 詳しい情報は AudioDriverKitTypes.hの ヘッダファイルを見てください 次は設定と初期化の話に 移ります オーディオdextを設定する第一段階は IOUserAudioDriverを サブクラス化して 仮想メソッドをオーバーライドすることです IOUserAudioDriverは IOServiceのサブクラスです カスタムの動作を実装するために必要な IOUserAudioオブジェクトを サブクラス化します そして設定して それらを IOUserAudioDriverに加える これから作成する IOUserAudioオブジェクトの 概要を示した図です SimpleAudioDriverはIOUser AudioDriverのサブクラスで dextへの入り口です SimpleAudioDriverは IOUserAudioDeviceの サブクラスである SimpleAudioDeviceを作る オーディオデバイスは調歩式 IO関連のメッセージや タイムスタンプと設定変更をすべて処理します SimpleAudioDeviceは様々な IOUserAudioObjectsを作る デバイスオブジェクトは OSTimerDispatchSourcesや OSActionsも作成し ハードウェアの割り込みや IOをシミュレートするトーン ジェネレーターを実装します IOUserAudioStreamは ストリームデバイスのものだ ストリームはオーディオIOに IOMemoryDescriptorを使用し HALにマッピングされます IOUserAudioVolumeLevel Controlはスカラー値 またはdB値を取るコントロールオブジェクトだ このコントロール値は入力 オーディオバッファにゲインを 適用するために使用されます IOUserAudioObjectsはIOUser AudioCustomProperties所有 SimpleAudioDeviceは カスタムプロパティと その修飾子としての文字列の 例を作り データ値を使う コードを見てみましょう SimpleAudioDriverはIOUser AudioDriverのサブクラスだ StartとStopとNewUserClient これらはIOServiceクラスの 仮想メソッドで ドライバーの オーバーライドが要ります StartDeviceとStopDeviceは IOUserAudioDriverからの IO関連の仮想メソッドです これらはHALの起動時またはIOの停止時に オーディオデバイスに呼びだされます これから デバイスやストリーム IOパス その他のオーディオオブジェクトの説明をします この例は NewUserClientを オーバーライドして ユーザークライアント接続を 作成する方法を示しています NewUserClientは クライアントプロセスが dextに接続する時に呼び出されます AudioDriverKitフレームワークは IOUserAudioDriver基底 クラスのNewUserClientを 呼び出して HALが必要とするユーザー クライアントを作成します これはCoreAudio HALに必要とされる IOUserAudioDriver UserClientを作成します カスタムユーザークライアントは IOService Createを 呼び出しても作成でき 先に追加したドライバー拡張 info.plistエントリから ユーザークライアントオブジェクトを作成します Startをオーバーライドし カスタムIOUserAudioDevice 作成方法を見てみましょう まず スーパークラスでStartを呼び出します その後SimpleAudio Deviceを割り当て いくつかの必要なパラメータで初期化します 初期化されたデバイスは AddObjectの呼び出しにより オーディオドライバーにその後 加えられます 最後にサービスを登録しドライバーの準備完了です さて あなたのドライバーが初期化されたので デバイスやストリーム 他の オーデオデバイスを作ろう IOUserAudioDeviceのサブ クラス化でカスタム動作取得
入力ストリーム ボリュームコントロール そして カスタムプロパティオブジェクトを作成しよう SimpleAudioDeviceのinit メソッドでデバイス設定方法 多様なオーディオオブジェクト 作成方法を示しています デバイスのサンプルレート関連情報は SetAvailableSample RatesとSetSampleRateを 呼び出すことで設定されます IOUserAudioStreamに渡される IOBufferMemory Descriptorを作成しましょう このメモリはCoreAudio HALにマッピングされ オーディオIOに使用されます メモリはハードウェアへの DMAに使用するIOメモリと 同じものが理想的です IOUserAudioStreamは作成した 入力ストリームの方向で指定して IOメモリディスクリプターを 渡すことで作成されます 機能する前にストリーム上でいくつかの追加設定を 行う必要があります ストリームフォーマットは フォーマットリスト作成で決定 IOUserAudio StreamBasicDescriptions サンプルレートやフォーマットID その他 必要なフォーマットの プロパティを指定します 前述のストリームフォーマットリストを渡して 利用可能なフォーマットを設定します そして現在のストリームの フォーマットをセットします 最後にAddStreamを呼び出してデバイスに 設定されたストリームを加えます 次に 音量調整の作成について説明します 音量調整オブジェクトを作成するために IOUserAudioLevelControl Createメソッドを 呼び出します これは設定可能な音量調整で 初期レベルは-6dBで 最大96dBまで調整できる コントロールの要素スコープ そしてクラスも 指定される必要があります コントロールオブジェクトを 最後にデバイスに追加します 音量調整のゲイン値は 入力ストリームのIOバッファに ゲインを適用することでIOパスで使用されます では デバイスのカスタム プロパティオブジェクトの 作成について説明します カスタムプロパティオブジェクトごとに プロパティアドレスが提供される必要があります グローバルスコープとメインの要素を持つ カスタムセレクタータイプを定義します 次に 定義したプロパティ アドレスを提供することで カスタムプロパティオブジェクトを作成します カスタムプロパティは設定可能で 修飾子とデータ値のタイプは共に文字列です では 修飾子とデータ値の OSStringを作成します そして それをカスタムプロパティにセットします 最後にカスタムプロパティをデバイスに加えます オーディオオブジェクト完成だ 続いて IOの話を始めます GetIOMemoryDescriptor メソッドは IOUserAudioStreamが使う IOMemoryDescriptorを返す IOMemoryDescriptorは ストリームの作成時に initメソッドに渡されてストリームは 新しいメモリ記述子でもアップデートされます メモリーはHALにマップされ オーディオIOに使用されます ストリームで使用される同じメモリ記述子は ハードウェアデバイスへの DMAのものと同じだと良い IOUserAudioClockDeviceは IOUserAudioDeviceの 基本クラスです UpdateCurrentZeroTimestamp GetCurrentZeroTimestampは ハードウェアデバイスからの タイムスタンプ処理に使う タイムスタンプはアトミックに処理され HALはサンプルのタイム ホストタイムペアを使用して IOの実行と同期を行います ハードウェアクロックのタイムスタンプを 可能な限り正確に追跡することが重要です それでは SimpleAudio Deviceクラスを見て IO関連のメソッドに焦点を当ててみましょう StartIOとStopIOはHALが IOを起動しようとする際に ドライバーから呼び出されます IOTimerDispatchSourceと OSActionを使ってハードウェア割り込みを シミュレートする privateメソッドが現れます これは入力IOバッファにゼロのタイムスタンプと オーディオデータ生成のため使用します この例はハードウェアデバイスに対して 実行されていないのでタイマーとアクションは ハードウェアの割り込みと DMAの代わりに使用されます StartIOはHALがデバイス上で IOを起動しようとする時に デバイスオブジェクトで呼び出されます ハードウェア上でIOを開始するために必要な 呼び出しはすべてここで行います その後 StartIOは 基本クラスで呼び出されます 次に入力ストリームの IOMemoryDescriptorを取得し CreateMappingを呼び出して IOMemoryMapを作成します バッファのアドレス 長さ オフセットはIOバッファに 音を発生させるために アクションが発生したハンドラで使用される StartTimersはタイムスタンプを生成し 入力オーディオバッファを 埋めるためのタイムソースと アクションを設定し有効に するために呼び出されます UpdateCurrentZeroTime stampはIOUserAudioDeviceの サンプルタイム・ホストタイムペアを アトミックに更新するために呼び出されます タイマーソースを有効にし mach_absolute_timeと デバイスから設定されたウェイクアップタイムを 設定します ZtsTimerOccurredアクションは デバイス上でのタイムスタンプの 更新のため ウェイクタイムに 基づいて呼び出されます ここでは紹介していませんが サンプルコードでは同様に 音生成タイマーやアクションを更新しています ゼロタイムスタンプアクションが生じると GetCurrentZeroTime stampを呼び出すことで 最後のゼロタイムスタンプの値が得られます これが最初のタイムスタンプならタイマーの mach_absolute_timeが アンカータイムです さもなくばタイムスタンプは ゼロタイムスタンプ期間と バッファごとに更新されます UpdateCurrentZeroTime stampの呼び出しは デバイスのタイムスタンプを更新するため HALが新しい値を使用できるようになります 次のゼロタイムスタンプで未来で起きるように ZTSタイマーを設定します
DMAをシミュレートするため タイマーが作動されると オーディオデータが入力IO バッファに書き込まれます まずはスタートIOの呼び出し時の 入力メモリマップが有効かどうか確認します メモリーマップバッファの長さ ストリームフォーマットを 使用して サンプル単位の長さを取得します ストリームは16ビットpcm サンプルフォーマットのみ サポートしているためバッファのアドレスと オフセットを取得し それを int16_tバッファポインタに これで 正弦波を発生させることにより 入力IOバッファを埋めることができます まず 入力された音量調整用 ゲインをスカラー値で取得 その後 音量調整のゲインを適用しながら 必要なサンプル数だけループさせて サイン音を発生させます 次に バッファをループさせ ラップアラウンドも考慮して チャンネル数に基づいて正弦波のサンプルを IOバッファに埋めます オーディオdextが設定されて IOを実行できるようになった 次の段階は デバイスとそのIO関連の状態を 更新するための変更の設定の仕方です 表示されたデバイスのメソッドは 要求と設定変更に使われます オーディオデバイスの状態を変更して IOや構造に影響を与える場合 RequestDeviceConfiguration Changeを呼び出して ドライバーが設定変更を要求する必要があります HALはIOのどんな作動も止めることができて ドライバーに前述のコードを呼び込みます この時に限り オーディオ デバイスはIO関連を更新する この一般的なシナリオは オーディオデバイスの現在の サンプルレートを更新したり ハードウェアデバイスの変更に対応して 現在のストリームフォーマットを変更します この図はデバイス設定の変更の イベントシーケンスを示しています ドライバーは最初に設定変更を要求します HALはリスナーに対して デバイスの設定変更が開始されることを通知します IOは実行中の場合はデバイス上で停止します デバイスの現在の状態がキャプチャーされます ドライバーはPerformDevice ConfigurationChangeです これはドライバーがデバイスやハードウェア上で 状態の変更が許されている時です 設定変更されれば デバイスの新しい状態がキャプチャーされます そして IOバッファのような すべてのIO関連の状態 またはサンプルレートが更新されます デバイスの状態に変更があれば クライアントリスナーに通知されます 設定変更前にIOが動作していた場合は デバイス上でIOが再起動されます 最後に HALがリスナーに 設定変更が完了したと知らせます ハードウェアのボトムアップ 設定変更をシミュレートする そのためにカスタムユーザー クライアントコマンドを使い dextのサンプルレート変更をトリガーします RequestDeviceConfiguration ChangeはHALの オーディオデバイス上の設定変更要求を知らせます 変更情報はどんな種類の OSObjectでも良いです これはカスタム設定変更のアクションと OSStringとしての情報変更のサンプルです 構成変更の実行を処理するために SingleAudioDeviceクラスは オーバーライドの必要がある PeformedeviceConfiguration Changeのメソッドで そして このコードはswitch文での 設定変更のアクションを処理します 設定変更が要求された時変更情報として 提供された同じOSString オブジェクトを記録します 次に 現在のサンプルレートを取得して 新しいレートをデバイスに設定します ストリームオブジェクト上で DeviceSampleRateChangedを 呼び出して オーディオストリームが今の ストリームフォーマットを 更新して サンプルレートの 変更に対応していることを 確認してください デバイスが直接処理しないその他の設定変更 アクションは基礎クラスにパスします これをMac上で確認しましょう SimpleAudioはドライバー 拡張をバンドルする サンプルコードのAppです オーディオドライバー拡張のインストール方法は Install Driverを押すだけです セキュリティ設定が表示されるはずです Allowを押すと オーディオドライバ拡張を 動的にロードするはずです 以前 これはリブートが要求されるため kextでは不可能でした よって SimpleAudioDeviceは サンプルレートフォーマットが 利用できて 音色選択のデータソースがあります 音量調整は同じコードで加えました これでQuickTimeが開けます オーディオデバイス上で録音もできます
@横下 @横下 さらに ボトムアップの設定 変更をテストするために dextと直接通信でき 音の周波数やサンプルレートを切り替えて その変更がAudio MIDIの設定にも 反映されるようにしています ドライバー拡張を削除するのは Appを消去するだけです
Audio MIDIセットアップでは 使用不可と表示されますね 最後にオーディオサーバーのプラグインと DriverKit拡張の状態をまとめました これもAudioServerPlugIn ドライバーの インターフェースも今後もサポートされる予定です 私は新しいAudioDriverKitの 枠組みを紹介し 新しいドライバーモデルの利点を説明しました AudioDriverKitの枠組みを 採用する方法を詳しく説明し すべてユーザースペースで動作する IOUserServiceベースの オーディオdextを 作成するサンプルコードを示しました XcodeとDriverKit SDKの 最新版をダウンロードしよう DriverKitがサポートする ハードウェアデバイスの AudioDriverKitをオーディオ デバイスに取り入れよう Appleのフィードバックアシスタントを通じて AudioDriverKitについて 何でも意見をお寄せください ありがとうございました [明るい音楽]
-
-
5:58 - Subclass of IOUserAudioDriver
// SimpleAudioDriver example, subclass of IOUserAudioDriver class SimpleAudioDriver: public IOUserAudioDriver { public: virtual bool init() override; virtual void free() override; virtual kern_return_t Start(IOService* provider) override; virtual kern_return_t Stop(IOService* provider) override; virtual kern_return_t NewUserClient(uint32_t in_type, IOUserClient** out_user_client) override; virtual kern_return_t StartDevice(IOUserAudioObjectID in_object_id, IOUserAudioStartStopFlags in_flags) override; virtual kern_return_t StopDevice(IOUserAudioObjectID in_object_id, IOUserAudioStartStopFlags in_flags) override;
-
6:27 - Override of IOService::NewUserClient
// SimpleAudioDriver example override of IOService::NewUserClient kern_return_t SimpleAudioDriver::NewUserClient_Impl(uint32_t in_type, IOUserClient** out_user_client) { kern_return_t error = kIOReturnSuccess; // Have the super class create the IOUserAudioDriverUserClient object if the type is // kIOUserAudioDriverUserClientType if (in_type == kIOUserAudioDriverUserClientType) { error = super::NewUserClient(in_type, out_user_client, SUPERDISPATCH); } else { IOService* user_client_service = nullptr; error = Create(this, "SimpleAudioDriverUserClientProperties", &user_client_service); FailIfError(error, , Failure, "failed to create the SimpleAudioDriver user-client"); *out_user_client = OSDynamicCast(IOUserClient, user_client_service); } return error; }
-
7:04 - SimpleAudioDevice::init
// SimpleAudioDevice::init, set device sample rates and create IOUserAudioStream object ... SetAvailableSampleRates(sample_rates, 2); SetSampleRate(kSampleRate_1); // Create the IOBufferMemoryDescriptor ring buffer for the input stream OSSharedPtr<IOBufferMemoryDescriptor> io_ring_buffer; const auto buffer_size_bytes = static_cast<uint32_t>(in_zero_timestamp_period * sizeof(uint16_t) * input_channels_per_frame); IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut, buffer_size_bytes, 0, io_ring_buffer.attach()); // Create input stream object and pass in the IO ring buffer memory descriptor ivars->m_input_stream = IOUserAudioStream::Create(in_driver, IOUserAudioStreamDirection::Input, io_ring_buffer.get()); ...
-
8:19 - SimpleAudioDevice::init continued
// SimpleAudioDevice::init continued IOUserAudioStreamBasicDescription input_stream_formats[2] = { kSampleRate_1, IOUserAudioFormatID::LinearPCM, static_cast<IOUserAudioFormatFlags>( IOUserAudioFormatFlags::FormatFlagIsSignedInteger | IOUserAudioFormatFlags::FormatFlagsNativeEndian), static_cast<uint32_t>(sizeof(int16_t)*input_channels_per_frame), 1, static_cast<uint32_t>(sizeof(int16_t)*input_channels_per_frame), static_cast<uint32_t>(input_channels_per_frame), 16 }, ... } ivars->m_input_stream->SetAvailableStreamFormats(input_stream_formats, 2); ivars->m_input_stream_format = input_stream_formats[0]; ivars->m_input_stream->SetCurrentStreamFormat(&ivars->m_input_stream_format); error = AddStream(ivars->m_input_stream.get());
-
8:50 - Create a input volume level control object
// Create volume control object for the input stream. ivars->m_input_volume_control = IOUserAudioLevelControl::Create(in_driver, true, -6.0, {-96.0, 0.0}, IOUserAudioObjectPropertyElementMain, IOUserAudioObjectPropertyScope::Input, IOUserAudioClassID::VolumeControl); // Add volume control to device error = AddControl(ivars->m_input_volume_control.get());
-
9:22 - Create custom property
// SimpleAudioDevice::init, Create custom property IOUserAudioObjectPropertyAddress prop_addr = { kSimpleAudioDriverCustomPropertySelector, IOUserAudioObjectPropertyScope::Global, IOUserAudioObjectPropertyElementMain }; custom_property = IOUserAudioCustomProperty::Create(in_driver, prop_addr, true, IOUserAudioCustomPropertyDataType::String, IOUserAudioCustomPropertyDataType::String); qualifier = OSSharedPtr( OSString::withCString(kSimpleAudioDriverCustomPropertyQualifier0), OSNoRetain); data = OSSharedPtr( OSString::withCString(kSimpleAudioDriverCustomPropertyDataValue0), OSNoRetain); custom_property->SetQualifierAndDataValue(qualifier.get(), data.get()); AddCustomProperty(custom_property.get());
-
10:47 - Subclass of IOUserAudioDevice
// SimpleAudioDevice example subclass of IOUserAudioDevice class SimpleAudioDevice: public IOUserAudioDevice { ... virtual kern_return_t StartIO(IOUserAudioStartStopFlags in_flags) final LOCALONLY; virtual kern_return_t StopIO(IOUserAudioStartStopFlags in_flags) final LOCALONLY; private: kern_return_t StartTimers() LOCALONLY; void StopTimers() LOCALONLY; void UpdateTimers() LOCALONLY; virtual void ZtsTimerOccurred(OSAction* action, uint64_t time) TYPE(IOTimerDispatchSource::TimerOccurred); virtual void ToneTimerOccurred(OSAction* action, uint64_t time) TYPE(IOTimerDispatchSource::TimerOccurred); void GenerateToneForInput(size_t in_frame_size) LOCALONLY; }
-
11:18 - StartIO
// StartIO kern_return_t SimpleAudioDevice::StartIO(IOUserAudioStartStopFlags in_flags) { __block kern_return_t error = kIOReturnSuccess; __block OSSharedPtr<IOMemoryDescriptor> input_iomd; ivars->m_work_queue->DispatchSync(^(){ // Tell IOUserAudioObject base class to start IO for the device error = super::StartIO(in_flags); if (error == kIOReturnSuccess) { // Get stream IOMemoryDescriptor, create mapping and store to ivars input_iomd = ivars->m_input_stream->GetIOMemoryDescriptor(); input_iomd->CreateMapping(0, 0, 0, 0, 0, ivars->m_input_memory_map.attach()); // Start timers to send timestamps and generate sine tone on the stream buffer StartTimers(); } }); return error; }
-
11:57 - StartTimers
kern_return_t SimpleAudioDevice::StartTimers() { ... // clear the device's timestamps UpdateCurrentZeroTimestamp(0, 0); auto current_time = mach_absolute_time(); auto wake_time = current_time + ivars->m_zts_host_ticks_per_buffer; // start the timer, the first time stamp will be taken when it goes off ivars->m_zts_timer_event_source->WakeAtTime(kIOTimerClockMachAbsoluteTime, wake_time, 0); ivars->m_zts_timer_event_source->SetEnable(true); ... }
-
12:27 - ZtsTimerOccurred
void SimpleAudioDevice::ZtsTimerOccurred_Impl(OSAction* action, uint64_t time) { ... GetCurrentZeroTimestamp(¤t_sample_time, ¤t_host_time); auto host_ticks_per_buffer = ivars->m_zts_host_ticks_per_buffer; if (current_host_time != 0) { current_sample_time += GetZeroTimestampPeriod(); current_host_time += host_ticks_per_buffer; } else { current_sample_time = 0; current_host_time = time; } // Update the device with the current timestamp UpdateCurrentZeroTimestamp(current_sample_time, current_host_time); // set the timer to go off in one buffer ivars->m_zts_timer_event_source->WakeAtTime(kIOTimerClockMachAbsoluteTime, current_host_time + host_ticks_per_buffer, 0); }
-
13:03 - GenerateToneForInput
void SimpleAudioDevice::GenerateToneForInput(size_t in_frame_size) { // Fill out the input buffer with a sine tone if (ivars->m_input_memory_map) { // Get the pointer to the IO buffer and use stream format information // to get buffer length const auto& format = ivars->m_input_stream_format; auto buffer_length = ivars->m_input_memory_map->GetLength() / (format.mBytesPerFrame / format.mChannelsPerFrame); auto num_samples = in_frame_size; auto buffer = reinterpret_cast<int16_t*>(ivars->m_input_memory_map->GetAddress() + ivars->m_input_memory_map->GetOffset()); ... }
-
13:30 - GenerateToneForInput continued
void SimpleAudioDevice::GenerateToneForInput(size_t in_frame_size) { ... auto input_volume_level = ivars->m_input_volume_control->GetScalarValue(); for (size_t i = 0; i < num_samples; i++) { float float_value = input_volume_level * sin(2.0 * M_PI * frequency * static_cast<double>(ivars->m_tone_sample_index) / format.mSampleRate); int16_t integer_value = FloatToInt16(float_value); for (auto ch_index = 0; ch_index < format.mChannelsPerFrame; ch_index++) { auto buffer_index = (format.mChannelsPerFrame * ivars->m_tone_sample_index + ch_index) % buffer_length; buffer[buffer_index] = integer_value; } ivars->m_tone_sample_index += 1; } }
-
14:02 - IOUserAudioClockDevice.h and IOUserAudioDevice.h
// IOUserAudioClockDevice.h and IOUserAudioDevice.h kern_return_t RequestDeviceConfigurationChange(uint64_t in_change_action, OSObject* in_change_info); virtual kern_return_t PerformDeviceConfigurationChange(uint64_t in_change_action, OSObject* in_change_info); virtual kern_return_t AbortDeviceConfigurationChange(uint64_t change_action, OSObject* in_change_info);
-
15:32 - HandleTestConfigChange
kern_return_t SimpleAudioDriver::HandleTestConfigChange() { auto change_info = OSSharedPtr(OSString::withCString("Toggle Sample Rate"), OSNoRetain); return ivars->m_simple_audio_device->RequestDeviceConfigurationChange( k_custom_config_change_action, change_info.get()); } class SimpleAudioDevice: public IOUserAudioDevice { ... virtual kern_return_t PerformDeviceConfigurationChange(uint64_t change_action, OSObject* in_change_info) final LOCALONLY; }
-
16:05 - PerformDeviceConfigurationChange
// In SimpleAudioDevice::PerformDeviceConfigurationChange kern_return_t ret = kIOReturnSuccess; switch (change_action) { case k_custom_config_change_action: { if (in_change_info) { auto change_info_string = OSDynamicCast(OSString, in_change_info); DebugMsg("%s", change_info_string->getCStringNoCopy()); } double rate_to_set = static_cast<uint64_t>(GetSampleRate()) != static_cast<uint64_t>(kSampleRate_1) ? kSampleRate_1 : kSampleRate_2; ret = SetSampleRate(rate_to_set); if (ret == kIOReturnSuccess) { // Update stream formats with the new rate ret = ivars->m_input_stream->DeviceSampleRateChanged(rate_to_set); } } break; default: ret = super::PerformDeviceConfigurationChange(change_action, in_change_info); break; } // Update the cached format: ivars->m_input_stream_format = ivars->m_input_stream->GetCurrentStreamFormat(); return ret; }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。