20210611のSwiftに関する記事は7件です。

[Swift]音声ファイルの位相を反転させる

はじめに ノイズキャンセリングやボーカル抽出には音声の位相反転を用います。 今回はSwiftを用いて位相反転を行う方法を紹介します。 逆位相に関する記事はこちら 本編 使用するフレームワークはAVFoundationとAccelerateです。 AVFoundationで音声ファイルを読み込み、Accelerateで位相反転処理を行う、といった流れです。 今回は簡単にPlaygroundを使用して実装していきます。 1. 位相反転させたい音声ファイルを用意 位相反転させたい音声ファイルを用意します。今回はwav形式で用意しました。 2. 音声ファイルの読み込み 音声ファイルを読み込みます。 AVFoundationを使用するのでimportしてください。 Urlは各自読み替えてください。 import AVFoundation let inputUrl = URL(fileURLWithPath: NSHomeDirectory()+"/Desktop/"+"input.wav") let outputUrl = URL(fileURLWithPath: NSHomeDirectory()+"/Desktop/"+"output.wav") let input = try! AVAudioFile(forReading: inputUrl,commonFormat: .pcmFormatFloat32, interleaved: false) 3. 音声データをバッファに読み込む 読み込んだ音声ファイルの中身を読み込むためのバッファと、処理を行った後に出力するためのバッファを用意します。 バッファを定義した後、実際にinputBufferには、先ほど読み込んだ音声ファイルから中身の音声データを読み込みます。 guard let inputBuffer = AVAudioPCMBuffer(pcmFormat: input.processingFormat, frameCapacity: AVAudioFrameCount(input.length)), let outputBuffer = AVAudioPCMBuffer(pcmFormat: input.processingFormat, frameCapacity: AVAudioFrameCount(input.length)) else{ fatalError() } do{ try input.read(into: inputBuffer) }catch{ print(error.localizedDescription) } 4.バッファから音声データ配列([[Float32]])を取り出す。 取り出します。(正確にはUnsafePointer<UnsafeMutablePointer<Float>>型です) 配列はDouble型の二次元配列となっており、チャンネルごとに音声データの振幅値が入っています。 let inputFloat32ChannelData = inputBuffer.floatChannelData! let outputFloat32ChannelData = outputBuffer.floatChannelData! 5.音声データを逆位相に変換 4で取り出した二次元配列に対し、チャンネルごとに音声データの配列のそれぞれの要素に-1をかければ、逆位相になります。 for文などで配列を走査してもできないことはありませんが、計算量が多く時間がかかりすぎるので、Accerelateフレームワークの関数を使うこととします。 vDSP_vsmulはFloat32型の配列に対してスカラー倍を行う関数です。 この関数の第4引数が出力したい配列ポインタを表すので、outputを指定します。 for channel in 0 ..< Int(inputBuffer.format.channelCount) { let input: UnsafeMutablePointer<Float32> = inputFloat32ChannelData[channel] let output: UnsafeMutablePointer<Float32> = outputFloat32ChannelData[channel] var scalar:Float = -1.0 vDSP_vsmul(input, 1, &scalar, output, 1, vDSP_Length(inputBuffer.frameLength)) } outputBuffer.frameLength = inputBuffer.frameLength 6.出力のフォーマットの設定 let settings: [String: Any] = [ AVFormatIDKey: outputBuffer.format.settings[AVFormatIDKey] ?? kAudioFormatLinearPCM, AVNumberOfChannelsKey: outputBuffer.format.settings[AVNumberOfChannelsKey] ?? 2, AVSampleRateKey: outputBuffer.format.settings[AVSampleRateKey] ?? 44100, AVLinearPCMBitDepthKey: outputBuffer.format.settings[AVLinearPCMBitDepthKey] ?? 16 ] 7.出力 先程のフォーマット設定に従って出力をおこないます。 すると、1で指定したoutputUrlの場所にファイルが生成されます。 do{ let output = try AVAudioFile(forWriting: outputUrl, settings: settings, commonFormat: .pcmFormatFloat32, interleaved: false) try output.write(from: outputBuffer) } catch{ print(error.localizedDescription) } 本当に逆位相になっているか確認 前の記事でも使用した、Audacityというソフトを使います。(winでもmacでも使えます) Audacityにinputファイルと生成されたoutputファイルをインポートして再生します。 きちんと反転できていれば、音が打ち消しあい、何も聞こえません。 さいごに 読んでいただきありがとうございました。 今回作成したコードは、以下のリポジトリにあります。 https://github.com/p-x9/InvertAudioPhase
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

よく見るSF Symbolsの名前をまとめてみた

SF Symbolとは? SF SymbolsはAppleが提供しているシンボルフォントです。iOSなどでの使用を念頭においてデザインされており、開発の場面で使う事も多いかと思います。 SwiftUIであれば、以下のようにして使うことができます。 SymbolSample.swift import SwiftUI struct SymbolTest: View { var body: some View { Image(systemName: "face.smiling") } } 各種Modifierを使って色や大きさを変えることができますし、そのままダークモードにも対応できるのが強みです。さらに数字を含むシンボルはローカライズに対応しているので、海外展開を考えるなら積極的に採用してもいいかもしれません。 基本的には公式サイトから一覧のアプリを入手して、目的のものを探すことになると思います。iOSのバージョンによっては使えない物もあるとはいえ、続々とシンボルが追加されています。 ただ、UIとして使う場合には多くの人が見慣れたものを使う方が操作しやすいのは明らかです。 ということで独断ではありますがよく見るシンボルの名前をまとめてみました。 共有ボタン square.and.arrow.up 主に何かを共有するときに使われているシンボルです。SF Symbolsの多くはこれに限らず、名前の末尾に.fillを加えることで塗りつぶしたものに変更できます。左の、塗りつぶしなしの方が多く使われている気がしますね。 送信ボタン paperplane TextFieldの横に配置されたりする送信ボタンに使われていることが多いです。こちらは塗りつぶしありの方がメジャーかもしれません。 ダウンロード arrow.down.to.line こちらもよく目にしますね。実は2種類あって、わかりにくいですが右の.altの方が若干矢印が長くなっています。 設定 gear こちらは設定画面を表すときによく使われる歯車のデザインです。ただ、より単純なデザインのgearshapeはiOS14以上でしか使えない模様。 検索 magnifyingglass 検索を表す時の虫眼鏡のようなデザイン。画像以外にも多くのバリエーションがあるので、検索するコンテンツごとに使い分けてもいいかもしれませんね。 履歴、最近の項目 clock よくある機能ですが、主に時計のデザインで表現されることが多いです。例えばMacのFinderの「最近の項目」にはこれが使われています。一番右のclock.arrow.circlepathはiOS14以上対応ではありますが、似たものがSpotifyで使われていました。 最後に アイコンの画像をプロジェクトに追加したりしなくても良いのは便利ですね。また、デフォルトのアプリと同じ意味合いで使えば操作しやすさにもつながるかと思います。 SF Symbolsは今後も追加されていくそうなので楽しみです!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【メモ】Swiftの"_" これの意味は?(アンダーバー,アンダースコア)

最近SwiftでiOSアプリ作りにハマっています。 使用PC: MacBook Air (11-inch, Mid 2013) OSバージョン: 11.4 Xcodeバージョン: 12.5 sample.swift let scene = GameScene(size:CGSize(width: 750, height: 1334)) こちらを書こうとした際にXcodeからこのような警告文が。 Initialization of immutable value 'scene' was never used; consider replacing with assignment to '_' or removing it Replace 'let scene' with '_' Replace 'let scene' with '_'と暗示され、Fixボタンを押してみると、 sample.swift _ = GameScene(size:CGSize(width: 750, height: 1334)) アンダースコアの意味は、メソッドから戻り値は返ってくるが、使用しない。という意味だそうです。 引数が明確な場合はアンダースコアを入れてあげると楽になります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】別のViewControllerのViewを使いたい!(ContainerView編)

はじめに 前回このような記事を投稿しました。親Viewに子Viewを追加したり消去したりして別のViewControllerのViewを使うというやり方です。 しかし、このやり方だと表示したいViewControllerの高さを変えたいとなった時にレイアウトがうまくいきません。(レイアウトを張っていないからですが、、、) 以下の緑のViewに赤いView Controllerの赤いView、青いView Controllerの青いViewを表示させようとすると、真ん中のlabelが中央に表示されません。これは、表示したいViewControllerのトップと緑のViewのトップが同じ設定になってるからですね。 無理矢理やる方法もありますが、もう少し上手い方法があるので、今回はそれを紹介します。 (僕の名誉のために、レイアウトをしっかり張って対応したものは以下に載せておきます(そもそも名誉なんかない)) 今回はcenterXAnchor必要ないですけどね。 では、本題に入りましょう。 今回作るもの 赤いところと青いところがContainerViewです。ボタンを押したらテーブルビューにそれぞれ値を追加していくというシンプルなアプリを考えていきましょう。 GitHub 実装 Storyboardの作成 階層は以下のようになっています。(オートレイアウトなどはGitHubを参考にしてください) ソースコード ContainerViewを切り替えたいのでContainerAとContainerBの親Viewを用意して、ContainerAとContainerをセグメントで切り替えます。 TopViewController import UIKit final class TopViewController: UIViewController { @IBOutlet private weak var containerView: UIView! @IBOutlet private weak var containerA: UIView! @IBOutlet private weak var containerB: UIView! @IBOutlet private weak var tableView: UITableView! private var containers = [UIView]() private var texts = [String]() override func viewDidLoad() { super.viewDidLoad() tableView.dataSource = self containers.append(containerA) containers.append(containerB) containerView.bringSubviewToFront(containerA) } override func prepare(for segue: UIStoryboardSegue, sender: Any?) { switch segue.identifier { case "ASegueID": let redVC = segue.destination as! RedViewController redVC.delegate = self case "BSegueID": let blueVC = segue.destination as! BlueViewController blueVC.delegate = self default: fatalError() } } @IBAction private func segmentDidTapped(_ sender: UISegmentedControl) { let currentContainerView = containers[sender.selectedSegmentIndex] containerView.bringSubviewToFront(currentContainerView) } } extension TopViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { texts.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "cell")! let text = texts[indexPath.row] cell.textLabel?.text = text return cell } } extension TopViewController: ContainerViewDelegate { func input(_ text: String) { texts.append(text) tableView.reloadData() } } RedViewController protocol ContainerViewDelegate: AnyObject { func input(_ text: String) } final class RedViewController: UIViewController { weak var delegate: ContainerViewDelegate? @IBAction private func inputButtonDidTapped(_ sender: Any) { delegate?.input("AAAAA") } } BlueViewController final class BlueViewController: UIViewController { weak var delegate: ContainerViewDelegate? @IBAction private func inputButtonDidTapped(_ sender: Any) { delegate?.input("BBBBB") } } 解説 テーブルビューやデリゲート周りの解説は省きます。 Containerを切り替えたいので、それを格納する配列を用意します。 private var containers = [UIView]() 先ほど用意した配列にContainerを格納し、初期起動した時はcontainerViewにcontainerAを一番前に表示させます。(bringSubviewToFront) override func viewDidLoad() { super.viewDidLoad() containers.append(containerA) containers.append(containerB) containerView.bringSubviewToFront(containerA) } そして、セグメントが選択された時に表示するcontainerを配列から持ってきて先ほどの(bringSubviewToFrontを使って一番前に表示させます。 @IBAction private func segmentDidTapped(_ sender: UISegmentedControl) { let currentContainerView = containers[sender.selectedSegmentIndex] containerView.bringSubviewToFront(currentContainerView) } あとはデリゲートやらプロトコルやらを使ってテーブルビューを更新してあげれば完成です。 ちなみに、ビューヒエラルキーはこのようになっています。 伝えたかったこと 今回、ContainerViewを使ってViewControllerの切り替えを行いましたが、addChildを使ったものよりも簡単ではなかったでしょうか。addChildは追加する前にremoveする必要があったり(addSubViewせずにinsertSubViewを使えばいいだけですが)、オートレイアウトを考えなければいけませんでした。ContainerViewを使うことで、簡単にViewControllerの切り替えができることが伝えたかったことです。(コードでやるならaddChildを使うのが良さそうですね) おわりに ドキュメントは目を通しておいてください。 ContainerView
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】Metal Best Practicesの解説(2) 永続オブジェクト

Metal Best Practicesは、iOS/MacOS/tvOSのAPIであるMetalを用いた設計のベストプラクティスガイドです。 本稿では、何回かに分けてこのガイドを読み解き、コード上での実験を交えて解説していきます。 読んでそのまま理解できそうなところは飛ばしますので、原文を読みながら原文のガイドとしてご利用下さい。 過去の記事一覧 Persistent Objects (永続オブジェクト) 永続オブジェクトを早期に作成して再利用します。すべてのレンダリングや計算のループでこれらのオブジェクトを作成するのは非効率です。次に、オブジェクトとそれぞれの作成タイミングを示します。 Metalに関するオブジェクトと作成のタイミング一覧 オブジェクト名 作成タイミング MTLDevice アプリ開始時にGPUごとに1つ作成する。 MTLCommandQueue アプリ開始時にGPUごとに最低1つ作成。各キューが異なる作業をする場合は、それぞれ作成する。 MTLLibrary なるべくビルド時に構築する。Xcodeはアプリのビルド時に自動的に.metalをコンパイルして、デフォルトライブラリにビルドする。newDefaultLibraryで取得できる。 MTLFunction 作成はライブラリのビルド時と同じ。複数のパイプラインで使用する場合も同じものを再利用する。 MTLRenderPipelineState, MTLComputePipelineState アプリ開始時に必要なパイプライン毎に作成する。 MTLBuffer, MTLTexture 静的データの場合は、最初に作ったものをなるべく再利用する。動的データの場合も、最初に領域を確保しておき、データを更新するようにする。トリプルバフッファリングを用いると、フレーム毎に新しいバッファを作成せずにプロセッサのアイドル時間を最小限に抑えることができる。 コードで試してみる こちらのリポジトリにサンプルコードがあります。 今回の記事用に、PersistentObjectsというサンプルがあるのでこれを使って計測していきます。 (実行イメージ) 毎フレームごとにオブジェクトを生成する場合 効率が悪いとされるオブジェクト作成処理を毎フレームごとに入れています。 ソースコードは、Pattern1_GenerateEveryFrameです。 MTLDevice、MTLCommandQueue、MTLRenderPipelineState, MTLTexture, MTLBufferなどベストプラクティスで再利用せよと書かれているものをほとんど再作成してみます。実際のコードは処理時間を計測するために、前後でos_signpost関数を実行しています。 PersistentObjects1MetalView.swiftv func draw(in view: MTKView) { guard let drawable = view.currentDrawable else {return} // wasteful processing start let metalDevice = MTLCreateSystemDefaultDevice()! let metalCommandQueue = metalDevice.makeCommandQueue()! buildPipeline() initTexture() makeBuffers() // wasteful processing end 計測してみます、 os_signpost関数で取得したパフォーマンスデータは、InstrumentsのMetal System Traceで確認できます。(Time Profilerでも確認できますが、Metal System Traceを使うとGPUの処理と並べて比較できます) Metal System Traceの使い方はこちらの記事をご覧ください。 計測結果は次のようになりました。 フレームごとの処理は次の順番になっています。 MetalPerformance(2.44ms) ・・・ 今回計測した処理 => Metal Application(452μs) ・・・ コマンドバッファの作成処理 => GPU(11.21ms) ・・・ GPU側の処理(VertexシェーダーやFragmentシェーダーの処理など) => Built-in Display(33.33ms) ・・・ 描画 60FPSを実現するためには、16.67ms間隔で描画する必要がありますが、今回は33msかかっていました。これは、描画のためのCPU+GPUの処理が16.67msに間に合わなかったためです。このうち、今回計測した処理が2.44ms占めているのでかなりコストが大きい処理です。 内訳をみていくと次のようにっていました。所要時間の大きい順に並べています。 作成コストの内訳 オブジェクト 所要時間 MTLCommandQueue 1,100μs MTLRenderPipelineState 887μs MTLTexture 328μs MTLBuffer 204μs MTLDevice 45μs 合計 2.86ms 1つ1つは短い処理ですが、『塵が積もれば山となる』ですね。 フレームレートはだいたい30FPSぐらいになっていました。 オブジェクトを再利用した場合を試してみる 今度はオブジェクトを最初に作成したら再利用しつづけるパターンで計測してみます。 ソースコードは、Pattern2_Reuseです。 計測結果は次のようになりました。 こちらは毎フレームにオブジェクトを作成しないので、その分早くなります。ところどころ16.67msで表示できていることがわかります。フレームレートはだいたい40FPSぐらいでした。 (実はGPUの処理もだいぶ時間がかかるので、60FPSは出せない作りになっています。) 結論 ベストプラクティスにあるとおり、永続オブジェクトを早期に作成して、再利用したほうが効率的ということがわかりました。 最後に iOSを使ったARやML、音声処理などの作品やサンプル、技術情報を発信しています。 作品ができたらTwitterで発信していきますのでフォローをお願いします? Twitterは作品や記事のリンクを貼っています。 https://twitter.com/jugemjugemjugem Qiitaは、iOS開発、とくにARや機械学習、グラフィックス処理、音声処理について発信しています。 https://qiita.com/TokyoYoshida Noteでは、連載記事を書いています。 https://note.com/tokyoyoshida Zennは機械学習が多めです。 https://zenn.dev/tokyoyoshida
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iPhone/iPad判別とiPadの向き判定

iPhone/iPad判別とiPadの向き判定 -iPhoneとiPadを判別 -iPadの場合はPortrait/Landscapeどちらか判定する。 if UIDevice.current.userInterfaceIdiom == .phone { // for iPhone } if UIDevice.current.userInterfaceIdiom == .pad { // for iPad let deviceOrientation: Int? = UIApplication.shared.windows.first?.windowScene?.interfaceOrientation.rawValue switch deviceOrientation { case 1, 2: // 1:Portrait, 2:Portrait Upside Down debugPrint("interfaceOrientation: Portrait") case 3, 4: // 3:Landscape Left, 4:Landscape Right debugPrint("interfaceOrientation: Landscape") default: debugPrint("unknown") } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIButtonの重複タップを回避する

UIButtonを連打 UIButtonを連打すると処理を複数回行ってしまう事象が発生してしまいました。 ユーザーに重複タップさせたくない。。。 結論から言うと一回押したら押せなくしてしまえばよいです。 ボタンをタップした ↓ 処理を行う の流れを ボタンをタップした ↓ ボタンをタップできなくする ↓ 処理を行う にすればよいです isEnabledでボタンを非活性化してあげます なので @IBOutlet weak var Button: UIButton! override func viewDidLoad() { super.viewDidLoad() } @IBAction func tapButton(_ sender: Any) { Button.isEnabled = false //処理を記述 } } 上のようにすればボタンタップ後、すぐにボタンが非活性化しているので 連打して重複処理が起こらずに済みます。 しかし、タップ後他画面へ遷移していれば良いですが 同じ画面表示だといつまで経ってもボタンがタップできないままになってしまいます。 一定時間経過後にボタンを活性化させてあげます。 DispatchQueue.main.asyncAfter(deadline: .now() + 1) { Button.isEnabled = true } これは「1秒後にボタンを活性化する」という処理になります。 最終的に @IBOutlet weak var Button: UIButton! override func viewDidLoad() { super.viewDidLoad() } @IBAction func tapButton(_ sender: Any) { Button.isEnabled = false //処理を記述 DispatchQueue.main.asyncAfter(deadline: .now() + 1){ Button.isEnabled = true } } これで、ボタン連打で重複処理してしまうことを回避できます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む