20191209のiOSに関する記事は16件です。

ARKitのはじめかた その3「オブジェクトを配置する(ARKit2版)」

はじめに

こちらの続きです▶︎ ARKitのはじめかた その2「オブジェクトを配置する(ARKit1版)」

こんにちは!
ARKitのまとめ記事 にて書いた実装方法について
次はARKit2時代から使われるようになった「オブジェクト配置の方法」を書きます。
前の記事の方法より汎用性が高いのでなるべくこちらの方法を推奨します。

ゴール

タッチした場所に飛行機が出てきます。
iOS のファイル (2).gif

前提

ARKitのはじめかた その1「5分で出来るARアプリ」で作成した環境をベースとします。
※依存関係は無いのでオブジェクトを置き換えれば他でも使えると思います。

コード

ViewController.swift
import UIKit
import SceneKit
import ARKit

class ViewController: UIViewController, ARSCNViewDelegate {
   @IBOutlet var sceneView: ARSCNView!

     override func viewDidLoad() {
       super.viewDidLoad()

       sceneView.delegate = self
       sceneView.scene = SCNScene()
   }

   override func viewWillAppear(_ animated: Bool) {
       super.viewWillAppear(animated)

       let configuration = ARWorldTrackingConfiguration()
       sceneView.session.run(configuration)

       let gesture = UITapGestureRecognizer(target: self, action:#selector(onTap))
       self.sceneView.addGestureRecognizer(gesture)
   }

    @objc func onTap(sender: UITapGestureRecognizer) {

        let pos = sender.location(in: sceneView)
        let results = sceneView.hitTest(pos, types: .featurePoint)
        if !results.isEmpty {
            let anchor = ARAnchor(name:"shipAnchor",
                                  transform:results.first!.worldTransform)
            sceneView.session.add(anchor: anchor)
        }
    }


   // MARK: - ARSCNViewDelegate

    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
        if anchor.name == "shipAnchor" {
            guard let scene = SCNScene(named: "ship.scn",inDirectory: "art.scnassets") else { return }
            let shipNode = (scene.rootNode.childNode(withName: "ship", recursively: false))!
               node.addChildNode(shipNode)
        }
   }

}

解説

1.空のシーンを読み込んで、iPhoneの画面に反映させます。※前回と一緒です

    override func viewDidLoad() {
        super.viewDidLoad()

        sceneView.delegate = self
        sceneView.scene = SCNScene()
        //画面に、何もオブジェクトが無い空のシーン(映像空間)を適用させます。

        let gesture = UITapGestureRecognizer(target: self, action:#selector(onTap))
        self.sceneView.addGestureRecognizer(gesture)
        //タップジェスチャーを追加
    }

2.現実とカメラ越しの映像を連携させます。(AR機能をONにする)※前回と一緒です

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        let configuration = ARWorldTrackingConfiguration()
        //AR環境の設定ファイル作ります。これによりAR機能が使えるようになります。

        sceneView.session.run(configuration)
        //画面の設定にARの設定を反映させます。
    }

3.タッチした場所にARAnchorを配置します。

    @objc func onTap(sender: UITapGestureRecognizer) {
    //画面にタッチした時に発動します。タッチした場所を、senderという変数に入れます。

        let pos = sender.location(in: sceneView)
        //一番始めにタッチしたsceneView上の場所をposとします。

        let results = sceneView.hitTest(pos, types: .featurePoint)
        //posの延長線上にある特徴点を手前から順番にresultsに入れます。

        if !results.isEmpty {
            let anchor = ARAnchor(name:"shipAnchor",transform:results.first!.worldTransform)
            //resultsがあれば、ARAnchorを作り、一番初め(手前)の特徴点の場所を代入します。
            sceneView.session.add(anchor: anchor)
           //ARanchor(shipAnchorという名前)を配置します。
        }
    }

4.ARanchor上にオブジェクトを表示します。

   // MARK: - ARSCNViewDelegate

    func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
    //Anchorが追加される度に呼び出されるrendererを追加します。
        if anchor.name == "shipAnchor" {
            guard let scene = SCNScene(named: "ship.scn",inDirectory: "art.scnassets") else { return }
            let shipNode = (scene.rootNode.childNode(withName: "ship", recursively: false))!
            node.addChildNode(shipNode)
            //追加されたARAnchorの名前が"shipAnchor"であれば、ARAnchor上にshipNodeを配置します。
        }
   }

■rendererとは?
ARSCNView上でイベントが発生した際に自動的に処理を行う関数です。
今回のようにARAnchorが追加された場合だけでなく、フレーム単位での処理やsessionを管理するものなどがあります。
参考:ARSCNViewDelegate 配下のAPIです。

なぜこの方法がふさわしいか

ARKit2では、"ARWorldMap"というARKitで取得した情報を他のデバイスと共有したり過去の情報を再現出来る方法が発表されました。
20180605174208.png

この際、ARAnchorは環境に紐づいた情報として記録し共有されるので、ある机の上にあったARAnchorは多少違う位置で復元しても同じ環境として認識されれば 同じ机の上に表示されます。
ARKit1時代の方法では、環境ではなくデバイスを中心とした位置関係で記録されているので、デバイスの1m前に置いたオブジェクトを記録して復元しても、復元した時の1m前に表示されていました。
また、他のデバイスと空間を共有するには、何か共通の基準となる物が必要となる為、自分で基準を作る(共有したい2つのiPhoneの場所を一度合わせる等)のでなければ、ARAnchorを利用する事が簡単かつ汎用性が高い手法となります。
参考:ARKitでグラフィティアートをして、ARWorldMapで共有する

ただし、そこまで環境と合致する必要がないオブジェクト(すぐ消えるシューティングの弾や、個人で表示したり再度表示しないモノなど)は、従来通りのARKit1の方法の方が良いです。

まとめ

ARKit2時代はこの方法がメインとなりました。
また、ImageTrackingやFaceTrackingも何か基準になるもの場所をARAnchorとして捉え、その上にオブジェクト表示する方法です。
ARKit3で出てきたCollaborative Sessionなども有効に活用する為には、この方法を基準に開発する必要があります。(私はARKit1をやっと理解した瞬間にARKit2の必要性が分かり手戻りした記憶があります。。)

次回は、2019年に出てきたRealityKitを使ったオブジェクト配置の方法を書こうと思います。ARKitのはじめかたシリーズは次で最終回です。

ここまで読んで頂きありがとうございました!

続きはこちら▶︎ ARKitのはじめかた その4「オブジェクトを配置する(ARKit3版)」

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS13のMFMailComposeViewControllerにつまずいた

はじめに

もうネタがない...と思いつつ iOS13 の MFMailComposeViewController につまずいたという噂を聞いたので色々動かしたみました。

動かしてみたところナビゲーションバーのカスタマイズがうまくいかない:confused:

検証

AppDelegate で下記のように全体のナビゲーションバーをカスタムしてみる。

検証1

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  // Override point for customization after application launch.
  UINavigationBar.appearance().tintColor = .white
  UINavigationBar.appearance().barTintColor = .red
  UINavigationBar.appearance().titleTextAttributes = [.foregroundColor : UIColor.white]
  UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor : UIColor.white]
  return true
}

MFMailComposeViewController の表示は下記。

if MFMailComposeViewController.canSendMail() {
  let mailVC = MFMailComposeViewController()
  mailVC.mailComposeDelegate = self
  mailVC.setToRecipients(["piyo@example.com "])
  mailVC.setSubject("Singleton")
  mailVC.setMessageBody("しんぐるしんぐるとんとんとん", isHTML: false)
  present(mailVC, animated: true)
} else {
  print("Mail services are not available")
}

結果1

結果は下記のような表示。(実機が iPod touch と iPad しかないので大きさがちょっと...:see_no_evil:)

iOS12 iOS13
12_1 13_1

うーん両方思った結果とは違う...:neutral_face:

検証2

AppDelegate はそのまま MFMailComposeViewController の表示はをちょっと修正。

if MFMailComposeViewController.canSendMail() {
  let mailVC = MFMailComposeViewController()
  mailVC.navigationBar.tintColor = .white // ここ追加
  mailVC.navigationBar.titleTextAttributes = [.foregroundColor : UIColor.white] // ここ追加
  mailVC.navigationBar.largeTitleTextAttributes = [.foregroundColor : UIColor.white] // ここ追加
  mailVC.mailComposeDelegate = self
  mailVC.setToRecipients(["piyo@example.com "])
  mailVC.setSubject("Singleton")
  mailVC.setMessageBody("しんぐるしんぐるとんとんとん", isHTML: false)
  present(mailVC, animated: true)
} else {
  print("Mail services are not available")
}

結果2

結果は下記のような表示。

iOS12 iOS13
12_2 13_2

うーんボタンは変わったけど両方思った結果とは違う...:neutral_face:

iOS12 でタイトルの色が変わらない件に関しては下記がヒットした。が、解決策は見当たらず:neutral_face:

検証3

iOS12 のタイトルはもうあきらめよう:grimacing:

iOS13 をどうするか...??色々調べてみると iOS13 から UINavigationBarappearance の設定が変わった模様(参考

AppDelegate を下記のように書き換えます。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
  // Override point for customization after application launch.
  if #available(iOS 13.0, *) {
    let appearance = UINavigationBarAppearance()
    let button = UIBarButtonItemAppearance(style: .plain)
    button.normal.titleTextAttributes = [.foregroundColor: UIColor.white]
    appearance.buttonAppearance = button
    let done = UIBarButtonItemAppearance(style: .done)
    done.normal.titleTextAttributes = [.foregroundColor: UIColor.white]
    appearance.doneButtonAppearance = done
    appearance.backgroundColor = .red
    appearance.titleTextAttributes = [.foregroundColor : UIColor.white]
    appearance.largeTitleTextAttributes = [.foregroundColor : UIColor.white]
    UINavigationBar.appearance().standardAppearance = appearance
    UINavigationBar.appearance().scrollEdgeAppearance = appearance
  } else {
    // Fallback on earlier versions
    UINavigationBar.appearance().tintColor = .white
    UINavigationBar.appearance().barTintColor = .red
    UINavigationBar.appearance().titleTextAttributes = [.foregroundColor : UIColor.white]
    UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor : UIColor.white]
  }
  return true
}

MFMailComposeViewController の表示はもちょっと修正。

if MFMailComposeViewController.canSendMail() {
  let mailVC = MFMailComposeViewController()
  if #available(iOS 13.0, *) {
  } else {
    mailVC.navigationBar.tintColor = .white
    mailVC.navigationBar.titleTextAttributes = [.foregroundColor : UIColor.white]
    mailVC.navigationBar.largeTitleTextAttributes = [.foregroundColor : UIColor.white]
  }
  mailVC.mailComposeDelegate = self
  mailVC.setToRecipients(["piyo@example.com "])
  mailVC.setSubject("Singleton")
  mailVC.setMessageBody("しんぐるしんぐるとんとんとん", isHTML: false)
  present(mailVC, animated: true)
} else {
  print("Mail services are not available")
}

結果3

結果は下記のような表示。

iOS12 iOS13
12_2 13_3

はい、だめーーー:neutral_face:

iOS13 が結局変わらない:neutral_face:

結論

MFMailComposeViewController のカスタマイズはあきらめよう!!ナビゲーションバーのカスタマイズはカスタムクラスなりで対応し MFMailComposeViewController に影響が出ないようにしよう:fist:

さいごに

appearanceMFMailComposeViewControllerUIActivityViewController などに思わぬ影響が出ていい思い出がないので個人的には撲滅したいです:expressionless:

(けど UIView.appearance().isExclusiveTouch = true はたまに使ったりします:speak_no_evil:)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSアプリでよくある上タブ作ってみた

コード:https://github.com/Yaruki00/UnderBarTabView
PodsやCarthage対応はできてないので、もし使いたい場合はファイル引っこ抜いてください:crying_cat_face:
ほら、カスタマイズしやすいし・・・ね・・・:joy_cat:

機能

normal.gifinfinite.gif

  • 現在選択している箇所に下線がつきます。
  • タブのサイズは、固定と文字列によって可変の2通りから選べます。
  • 無限にループするか、しないかを選べます。
  • 余白やフォント、色を多少調整することができます。
  • 作ったのはタブの部分だけなので、コンテンツ部分やそれとの連携は自作する必要があります。

だいたいの作り

UICollectionViewをベースにしており、タブの分だけセルを生成し並べています。
無限スクロールはタブを3セット用意して、スクロールのアニメーション終わったタイミングでスクロール位置を真ん中のセットに戻すことで実現しています。
余白や色、フォント等の設定は別クラスになっており、セットアップ時に渡します。

無限スクロールの参考:
https://techblog.zozo.com/entry/tab_page_viewcontroller

既知の問題

セルの読み込みが遅いときがある

プログラムからタブをスクロールさせる場合(UICollectionViewscrollToItem(at:at:animated:)とか)、画面外のセルの読み込みが遅い場合があります。
どうもアニメーションが終わってから読み込みが走っているように見えますが、部分的にしか起きないしよくわからないです。わかる方いましたら是非教えて下さい!

参考(?):
https://living-sun.com/ja/swift/824352-in-auto-scrolling-uicollectionview-cellforitematindexpath-not-triggered-by-contentoffset-swift-uicollectionview-tvos.html

スクロールが部分的にぎこちない

無限ループするバージョンでは場所によってスクロールの速度が不自然な場合があります。
スクロール位置を調整するところでなにかおかしくなっていると思うのですが、ちゃんとした原因はまだつかめていないです。

役に立つかも知れない情報

セルの座標を取得する

下線を動かす時にセルの座標を取得していますが、UICollectionViewLayout
layoutAttributesForItem(at:)を使用することで取得することができます。

UICollectionViewのリロードが終わってから処理を行いたい

UIViewanimate(withDuration:animations:completion:)を使用することでリロードの完了を待つことができます。UICollectionViewのExtensionで書いておくと便利かも知れません。

UIView.animate(
    withDuration: 0.0,
    animations: {
        self.collectionView.reloadData()
    },
    completion: { _ in
        // reloadData()後に行いたい処理
    }
)

参考:
https://qiita.com/ponkichi4/items/d5d46556773a6bc98f9c

おわりに

この手のライブラリは探せばたくさん見つかるのですが、勉強も兼ねて自作してみました?
シンプルな機能で良いのであれば自作してもそこまで手間ではないのでアリかなと思いました?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ML Kit for Firebase テキスト認識を使ってOCR処理をしてみた

ML Kit for Firebase

OCR

活字の文書の画像(通常イメージスキャナーで取り込まれる)を文字コードの列に変換するソフトウェアである。

ML Kit for Firebase

ML Kit は、Google の機械学習の機能を Android アプリや iOS アプリとして提供するモバイル SDK です。

テキスト認識

ML Kit のテキスト認識 API を使用すると、ラテン語由来の言語のテキストを認識できます(クラウドベースのテキスト認識を使用すると、より多くの言語のテキストを認識できます)。

テキスト認識はクレジット カードや領収書、名刺の面倒なデータ入力を自動化できます。クラウドベースの API を使用すると、ドキュメントの写真からテキストを抽出することもできます。これにより、アクセシビリティの向上やドキュメントの翻訳を行うことができます。たとえば、列車上の番号を読み取り、アプリで実際の対象物を追跡することもできます。

メ◯カリとかの本人確認機能とかOCR機能使ってそう。ML Kit for Firebaseを利用すれば、簡単にOCR処理できるっぽいのでやってみました。

事前準備

Firebase プロジェクトの作成

google-services.json が無いと動かないので、Firebase Console でプロジェクトを作成し、アプリを追加します。アプリの追加が完了すると、google-services.json が作られるので、ダウンロードしてアプリプロジェクトに追加します。この辺は Firebase そもそもの使い方になるので割愛します。

クラウドベースの API を有効化

今回はクラウドベースモデルを使用するので、クラウドベースの API を有効にします。
Firebase プロジェクトのプランを Blaze プランにアップグレードし、[クラウドベースの API を有効化]からクラウドベースの API を有効にします。

ML Kit ライブラリのインストール

ML Kit ライブラリのインストールは CocoaPods を使用するとよいでしょう。Podfile に ML Kit ライブラリを含めます。テキスト認識の on-device API も使えるようにしておくとなると、最低限必要なのは以下の3つになります。

pod 'Firebase/Analytics'
pod 'Firebase/MLVision'
pod 'Firebase/MLVisionTextModel'

ベースアプリの準備

まずはカメラで写真を撮るアプリを作成します。

スクリーンショット 2019-12-09 19.25.37.png

UIはこんな感じ。Take Photo ボタンでカメラを起動して、Recognized Text View に認識した文字列を描画します。

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var recognizedTextView: UITextView!

    @IBAction func takePhotoButtonTouchUpInside(_ sender: Any) {

        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            let imagePickerController = UIImagePickerController()
            imagePickerController.sourceType = .camera
            imagePickerController.delegate = self
            self.present(imagePickerController, animated: true, completion: nil)
        }

    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

}

ViewControllerの実装はこんな感じ。

extension ViewController: UIImagePickerControllerDelegate {

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

        guard let image = info[.originalImage] as? UIImage else {
            return
        }

        picker.dismiss(animated: true, completion: nil)

    }

    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {

        picker.dismiss(animated: true, completion: nil)

    }

}

extension ViewController: UINavigationControllerDelegate {

}

UIImagePickerControllerDelegate と UINavigationControllerDelegate の実装はこんな感じ。

テキスト認識の処理を実装

いよいよ本題。
まずは Firebase をインポート。

import Firebase

次にテキスト認識の処理メソッドを ViewController に実装。

private func recognizeTextInCloud(in image: UIImage) {

        let metadata = VisionImageMetadata()
        metadata.orientation = .rightTop

        let visionImage = VisionImage(image: image)
        visionImage.metadata = metadata

        let options = VisionCloudTextRecognizerOptions()

        let cloudTextRecognizer = Vision.vision().cloudTextRecognizer(options: options)

        cloudTextRecognizer.process(visionImage) { text, error in
            guard error == nil, let text = text else {
                print("Text recognizer failed with error: " + "\(error?.localizedDescription ?? "No Results")")
                return
            }

            self.recognizedTextView.text = text.text
        }

    }

はい、たったコレだけ。上記のコードではクラウドベースのテキスト認識になります。
on-device でテキスト認識する場合は、TextRecognizer オブジェクトのインスタンスを生成する際に、cloudTextRecognizer(options:) の変わりに onDeviceTextRecognizer() を使うだけです。

あとはカメラで撮影した写真をテキスト認識の処理メソッドに通すだけ。

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

    guard let image = info[.originalImage] as? UIImage else {
        return
    }

    self.recognizeTextInCloud(in: image)

    picker.dismiss(animated: true, completion: nil)

}

こんな感じ。

動かしてみる

IMG_0158.PNG

こんな感じの写真を撮ってみると、、、

IMG_0159.PNG

記号とかが怪しいけど、かなり正確に文字認識してます!
コレでメル◯リ風の本人確認機能に近いことはできるんじゃないでしょうか!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

extensionをふんだんに使ってコードを整理しよう

iOS Advent Calendar 2019も折り返しに来ました。担当の@417_72kiです。

protocol毎にextensionでコードブロックを分けるという有名(?)なhackがありますが、
今回はprotocol以外でも積極的にextensionで分けていこうぜ!っていう記事を書きました。

注意

どう呼べばいいか困ったものについて、この記事では以下のように定義しています。

  • definition block -> class/struct/enum宣言したブロック( class Hoge {~}で囲まれたブロック )
  • extension block -> extension宣言したブロック ( extension Hoge {~} で囲まれたブロック )

以下、上記の言葉は太字で記述していきます。

ベタ書きされたコード

例としてこんなFatViewControllerを考えます(アーキテクチャの話は一旦無視します)。

ベタ書きされたFatViewController
FatViewController.swift
class FatViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    private var _items: [Item]?

    var items: [Item] {
        get { _items ?? [] }
        set { _items = newValue }
    }

    var isEmpty: Bool { items.isEmpty }

    @IBOutlet private weak var tableView: UITableView!
    @IBOutlet private weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        setup()
    }

    private func setup() {
        tableView.dataSource = self
        tableView.delegate = self
    }

    private func doSomething(with item: Item) {
        print(item.name)
    }

    func reloadView() {
        tableView.reloadData()
    }

    // MARK: - UITableViewDataSource
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = items[indexPath.row].name

        return cell
    }

    // MARK: - UITableViewDelegate
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        doSomething(with: items[indexPath.row])
    }

}

ステップ1: protocolを分割する

これについては参考記事があるのでここでの解説は割愛します。

protocol分割されたFatViewController
FatViewController.swift
class FatViewController: UIViewController {
    private var _items: [Item]?

    var items: [Item] {
        get { _items ?? [] }
        set { _items = newValue }
    }

    var isEmpty: Bool { items.isEmpty }

    @IBOutlet private weak var tableView: UITableView!
    @IBOutlet private weak var button: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        setup()
    }

    private func setup() {
        tableView.dataSource = self
        tableView.delegate = self
    }

    private func doSomething(with item: Item) {
        print(item.name)
    }

    func reloadView() {
        tableView.reloadData()
    }
}

// MARK: - UITableViewDataSource
extension FatViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = items[indexPath.row].name

        return cell
    }
}

// MARK: - UITableViewDelegate
extension FatViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        doSomething(with: items[indexPath.row])
    }
}

ちなみに、protocol functionextensionに切り出したら// MARK: - ~を消している記事をよく見かけますが、筆者はあえて各extension blockに対して// MARK: - ~を付けるようにしています。
理由は後述します。

ステップ2: functionを分割する

functionは基本的にextensionに切り出すことができます。
classにおけるoverride functionは例外で、definition blockにしか定義できないため、// MARK:で区切ります。

function分割されたFatViewController
FatViewController.swift
class FatViewController: UIViewController {
    private var _items: [Item]?

    var items: [Item] {
        get { _items ?? [] }
        set { _items = newValue }
    }

    var isEmpty: Bool { items.isEmpty }

    @IBOutlet private weak var tableView: UITableView!
    @IBOutlet private weak var button: UIButton!

    // MARK: Life cycles
    override func viewDidLoad() {
        super.viewDidLoad()

        setup()
    }
}

// MARK: - Functions
extension FatViewController {
    private func setup() {
        tableView.dataSource = self
        tableView.delegate = self
    }

    private func doSomething(with item: Item) {
        print(item.name)
    }

    func reloadView() {
        tableView.reloadData()
    }
}

// MARK: - UITableViewDataSource
extension FatViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = items[indexPath.row].name

        return cell
    }
}

// MARK: - UITableViewDelegate
extension FatViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        doSomething(with: items[indexPath.row])
    }
}

ステップ3: computed propertyを分割する

propertyのうち、computed propertyextensionに切り出すことができます。
stored propertydefinition blockにしか定義できないため、必要に応じて// MARK:で区切ったりします。

computed property分割されたFatViewController
FatViewController.swift
class FatViewController: UIViewController {
    // MARK: Private properties
    private var _items: [Item]?

    // MARK: Outlets
    @IBOutlet private weak var tableView: UITableView!
    @IBOutlet private weak var button: UIButton!

    // MARK: Life cycles
    override func viewDidLoad() {
        super.viewDidLoad()

        setup()
    }
}

// MARK: - Computed properties
extension FatViewController {
    var items: [Item] {
        get { _items ?? [] }
        set { _items = newValue }
    }

    var isEmpty: Bool { items.isEmpty }
}

// MARK: - Functions
extension FatViewController {
    private func setup() {
        tableView.dataSource = self
        tableView.delegate = self
    }

    private func doSomething(with item: Item) {
        print(item.name)
    }

    func reloadView() {
        tableView.reloadData()
    }
}

// MARK: - UITableViewDataSource
extension FatViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = items[indexPath.row].name

        return cell
    }
}

// MARK: - UITableViewDelegate
extension FatViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        doSomething(with: items[indexPath.row])
    }
}

ステップ4: accessibleごとに分割する

extensionにもAccess Levelを設定することができます。
ブロック内のAccess Levelはブロック本体のAccess Level以下になります
(ただし、トップレベルのブロックにおけるprivatefileprivateと同義になります)。

そこで、分割したextensionを更にAccess Levelごとに分割することで、ブロック単位でAccess Levelを設定できるとともにprivateの付け忘れから開放されます。

accessibleごとに分割されたFatViewController

privatecomputed propertyの良い例が思いつかなかったため、ここではfunctionだけ対応しています
FatViewController.swift
class FatViewController: UIViewController {

    // MARK: Private properties
    private var _items: [Item]?

    // MARK: Outlets
    @IBOutlet private weak var tableView: UITableView!
    @IBOutlet private weak var button: UIButton!

    // MARK: Life cycles
    override func viewDidLoad() {
        super.viewDidLoad()

        setup()
    }
}

// MARK: - Computed properties
extension FatViewController {
    var items: [Item] {
        get { _items ?? [] }
        set { _items = newValue }
    }

    var isEmpty: Bool { items.isEmpty }
}

// MARK: - Public Functions
extension FatViewController {
    func reloadView() {
        tableView.reloadData()
    }
}

// MARK: - Private Functions
private extension FatViewController {
    func setup() {
        tableView.dataSource = self
        tableView.delegate = self
    }

    func doSomething(with item: Item) {
        print(item.name)
    }
}

// MARK: - UITableViewDataSource
extension FatViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        items.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)

        cell.textLabel?.text = items[indexPath.row].name

        return cell
    }
}

// MARK: - UITableViewDelegate
extension FatViewController: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        doSomething(with: items[indexPath.row])
    }
}

他にextensionに切り出せるもの

クラス定数(static let)
FatViewController.swift
// MARK: - Constants
extension FatViewController {
    static let initialItems: [Item] = [] // -> OK
    let initialItems: [Item] = [] // -> NG
}

同じ定数でもインスタンス定数の方はextensionに切り出せません。
不思議ですね(棒

inner class/struct/enum
FatViewController.swift
// MARK: - State
extension FatViewController {
    enum State {
        case hoge
        case fuga
        case foo
        case bar
    }
}

// MARK: -
extension FatViewController.State {
    var isHoge: Bool { self == .hoge }
}

もちろんネストされた型に対してもextensionを貼ることができますし、
その中に更にネスト型を定義することもできます。
ブロックのネストを増やすことなく型のネストを増やせるので、複雑なJSONからCodableなstructを組み立てる時に重宝します。

ちなみに、BuildConfig.swiftという自作ツールで生成されるSwiftファイルもこの手法を使っています。
よかったら実際に使ってみて生成されたコードを見てみてください(宣伝

その他

実装する機会が減ってきたのでここでは触れませんが、convenience initializerもextensionで定義することができます。

// MARK:とパンくずリストとMinimap

Xcodeのエディタ領域上部にあるパンくずリストで、ファイル名の次の要素を開くとDocument Itemsが表示されます。
(^+6でも開きます)

// MARK: 見出し名を使うことでこのDocument Itemsに見出しを付けることができます。
また、 MARK: - 見出し名とすることで、見出しの前に区切り線を付けることができます。
image.png

更に、Xcode11で登場したminimapでは// MARK: 見出し名を付けた所に見出しが表示されるようになります。
Document Itemsと同様、こちらも-付きMARKにすると区切り線が付きます。

image.png

先述の、筆者が全ブロックにMARK: - 見出し名を付ける理由がこれです。
見出しが付くおかげでコードの構造がパッと見で分かるようになりますね。

まとめ

extensionを活用してコードブロックを整理することで、各ブロックに役割を持たせる事ができます。
また、MARKコメントとminimapとの組み合わせでコードの見通しも良くなってDXも爆アゲです。

この1年ずっとこの手法を使っていますが今の所デメリットが見つかっていないので、
誰かこの手法で困ったことがあったら教えていただきたいです(※他の言語ではできないみたいなのは除く)。

それでは皆様、良いお年を!✋

参考

Using Swift Extensions The “Wrong” Way - Natasha The Robot(@NatashaTheRobot)
【Swift】Protocolごとにextensionで切り分けて実装するワケ(@ktanaka117)
[Xcode 8] Swiftのドキュメントコメントについての簡潔なまとめ(@y-some)
What's New in Xcode 11(@akatsuki174)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AudioUnit(iOS)を20個以上用いて「ラウドネス周波数特性補正付き音楽再生アプリ」を独学で造り上げた奮闘記 [ その後 -2 ]

A. 副 題 と 履 歴

A-1. 副 題

  • 「Apple の Version-Up という荒波」に挑む老人の挑戦(トライ)と「音楽再生 App の周辺事情」を記す。

A-2. 履 歴

  • この投稿文は、2019年12月09日にQiitaへ投稿した記事です。
  • 2019年12月09日に「W-3. 始めたWeb記事収集の手法」の「サンプルコード」内に「<」のミスが有り修正しました。

B. はじめに

B-1. 昨年の春と秋の Version-Up に付いて

  • イャ〜ァ〜、昨年の春と秋の Version-Up には参ったです。ややこしい変更をやってくれるから解決に時間が掛かること、大変でした。
  • 特に、2018年秋の「iOS12.0と Xcode10.0の Version-Up 」では、"私のApp"も大きな影響を受けてしまいました。
  • 結局、原因の分析と修正作業に手間取り、完了が2019年の3月半ばに成ってしまいました。
  • もっとも、何だかんだとあってコードの修正に取り掛かれたのが年が開けてからの作業に成ってしまったのも原因なのですが・・・。
  • 先に結論を申し上げると、一連の Version-Up で「Apple MusicとiCloudの仕様を「MPMusicPlayerController」と「MPMediaPickerController」に持ち込んで来た」と感じました。
  • しかし、春を過ぎた時点のうわさから2019年秋の「iPhone及びiPadのOS大改変」の為の段階的な変更だったのかな ? とも思慮しています。

B-2. 本投稿の事情

  • 本投稿は、以前に著者がQiitaさんに投稿した3件の記事に沿って展開しています。
    (1)『MPMusicPlayerControllerに挙動不審』。なお、本投稿内では『挙動不審-記事』と略記
    (2)『AudioUnit(iOS)を20個以上用いて「ラウドネス周波数特性補正付き音楽再生アプリ」を独学で造り上げた奮闘記』。なお、本投稿内では『奮闘記-元記事』と略記
    (3)『AudioUnit(iOS)を20個以上用いて「ラウドネス周波数特性補正付き音楽再生アプリ」を独学で造り上げた奮闘記 [その後-1]』。なお、本投稿内では『その後-1記事』と略記
    (4)本記事は、『その後-2記事』と略記
    です。これらには、本投稿最後にリンクを用意しました。
  • また、提示した図の中に記した「丸囲み数字」を文章内では (1), (2)、・・・の様に表記してあります。 「丸囲み数字」が文中では読みにくかったためです。また、少しでも読み易くと文字に色付けを多用しま した。
  • さらに、『奮闘記-元記事』で医療関係へのリンクで御迷惑をお掛けした様ですので、本投稿では迷惑をかけそうなサイトの「参考資料名とリンク先」を著者の判断で提示しない事とさせて頂きました。ただし、今までと本投稿の参考資料は「ちょっとしつこい Web 検索」で普通に見つけたものです。

C.「iOS 11, 12」のVersion-Upでの影響と対応

  • この項では、時間( iOS の Version )軸ではなく"私のApp"内のコントロール系の順に症状単位で説明しています。
  • また、分析らしき記載もしていますが、Appleが提供している「プログラムの中身が公開されていない」ものを使用している部分のため、現れた症状と修正の内容からの「著者の推測」でしかありません。

C-1. 選曲リストで最初にピックアップする曲

  • [以前] : 「MediaPicker(選曲リスト)」で最初に再生するのは当然「選曲リスト」の一番目の曲でした。
  • しかし、選曲していない曲が最初に再生されるように成りました。その曲は、iTunesでAppleから購入したものでした。
  • [対応] : 「MediaPicker」が終了し、「Play」に移行すると直ぐに「STOP」→「skipToNext(次の曲に移動)」のプログラムを追加して、この不要な再生をスキップする対策を施しました。勿論、使用した再生リストには、この曲は登録されていませんでした。
  • [結果] : さらに少しの工夫が必要でしたが、この問題は解決(影響を排除)出来ました。

C-2. 曲データーの読み込みタイミング

  • [以前] : 「MediaPicker(曲リスト)」から「Play」に移行した後なら再生している曲の「曲名などの保存されている全てのデーター」にいつでもアクセスすることが出来ました。
  • しかし、再生している途中でデーターを呼び出す動作をさせると、次の曲のデーターが表示されるように成りました。
  • [対応] : この症状の動作を確認するのに、「 NSLog ( @" " ) ; 」と、注目するいくつかのアイテムを呼び出すコードを多数配置し、NSLogに書き出させることで、進行を確認しました。作業としては、iPhoneをMacに接続したままで、プログラムの変更とNSLogを追加or削除したAppを再インストールし、iPhoneで曲をPlayして、MacでNSLogを確認する作業を繰り返して、原因を徐々に絞り込むものです。とても、手間の掛かる作業でした。
  • [結果] : 「AssetURL(音楽ファイルのURL)」にアクセスしたのちに、次の曲のデーターに置き換わっている事が確認出来ました。これにより、AssetURLにアクセスする前に必要なデーターを取得、或いは別名で保存するコードを追加して解決させました。
  • 実は事前に、この現象に関するWeb記事を確認していました。しかし、コントロール系が異なるため詳細を確認してから修正しないと泥沼にハマりそうだったので、自分でひとつひとつ確認しながらの修正を行いました。
  • この変更は、Playしている間に「Apple MusicやiCloud」から次の曲のデーターを確実にダウンロードするための仕様であろうと思慮しましたが、下方に記載の「E-2の(7)」も関係していそうです。別途、裏で読み込んで仮保存して準備する方法もある様に思うのですが・・・。
  • なお、NSLogと同じエリアに同時に表示されるエラーメッセージの意味をまとめてある資料(2012年)へのリンクを用意しました。アプリ開発の実機テストを始めた頃に探し当てたものです。 < 参考資料-C21 >

C-3. 曲の移動

  • [以前] : 「MPMusicPlayerController」にある「skipToNext(次の曲に移動)」や「skipToPrevious(前の曲に移動)」、「skipToBeginning(再生中の曲の先頭から再生を再開)」などを実行して、Playすると簡単に選曲リスト内の曲を前後に移動して、再生することが出来ました。
  • しかし、マァ〜なんとも不可思議な動きをする様に成りました。ともかく、移動することは移動するのですが「三歩下がって一歩前に出ると前の曲にたどり着く」と言った感じです。ただし、選曲リストを順番通りに自動的に再生する方法では、不具合の発生はありませんでした。
  • [対応] : 実は、2018年春のバージョンアップでこの症状に気づいた時には、現象を解消するためだけの作業を主として行なった為に、結構ややこしいプログラムになってしまいましたが、一応当初の動作を確保していました。しかし、この現象が2018年秋の Version-Up でも解消されなかった為に、前項 C-2 と同様「 NSLog ( @" " ) ; 」を使って動作を詳細に確認して、コントロールプログラムの一部を再構成しました。
  • [結果] : 「skipTo・・・と AssetURLアクセス」を空打ちするコード(NextとPreviousで異なる)を追加することで、プログラムが整理されてシンプルなものに成りました。勿論、前項 C-2 での「音源データーの入れ換わり」が影響したと思慮しました。
  • また、「MPMusicPlayerController」側のコードを実行させて自分のコードに戻るタイミングで、動作がスムースに移行しないような感触だったので、幾つかの工夫を施しました。
  • ちなみに、2019年8月(iOS12.4)時点のiPhone内のMusicでは、「◀︎◀︎」を一度クリックすると再生中の曲の頭に戻り、「二回目のクリック」又は「ゆっくり二回クリック」すると一曲前の曲に移動します。以前には、ほとんど正常に動かない時期があったと記憶しています。一般的な、リストの順に聴き流す使用では、使わない機能ではありますが・・・。

C-4. 古くに登録したアルバムイメージが表示されない

  • [症状] : 古くに著者がCDからWAVファイルに起こし、スキャナーでアルバムイメージを用意したものを、著者のMacでiTunesを経由してiPhoneに登録した曲で、"私のApp"でも発生しました。また、iPhoneの音楽再生App「Music」では、強力なぼかしが入って表示されていました(これが事前告知だったのかな ? )。
  • MacのiTunesのデーターファイルを保存してある「Machintosh HD → ユーザ → ミージック」の中にあるデーターとiPhoneの症状とを比較して調べた結果、2016年5月(著者の場合)以前に登録したもので発生していました。
  • [対応] : この症状は、例えばiTunesスタートの頃(2001年)は「アルバムイメージと他の音源データー」が別々に保存されていて、のちに「アルバムイメージが他の音源データーの中に同居」したもの(一つのファイルとして)との併用になり、「Apple Music」のスタートに関連して「アルバムイメージが他の音源データーの中に同居」したものだけを使用する方式へと、iPhoneが変化して来たものと想像しています。
  • これらから、「2016年5月」以前に登録したアルバムイメージが表示されなくなったと思慮しました。
  • [結果] : 現に、「 iTunesでアートワークイメージを表示し → 画面をコピー → アートワークイメージ部分を切り出し(MacではFinderで可能) → 適当な名前を付けてファイルを保存」し、iTunesの「アートワークを追加」でイメージファイルを追加し、iPhoneに上書き保存すると、復活しました。
  • 又、別途保存してあったメージを、再保存した物でも復活しました。なお、著者は「音楽データー」を一台のポータブルHDに整理して保存しています。以前に、アメリカの著名な方が「iCloudに保存していた大量の音楽データーが消えた」と言う大騒ぎの記事を読んだことがあったので・・・。
  • [参考] : 前記の「独立して保存されていたイメージファィル」は、「Machintosh HD → ユーザ → ミージック → iTunes → Album Artwork」の中に「---.itc」というプロテクトファイルとして保存されています。詳細は、各自が「.itc」でWeb検索を ! 。また、画像サイズは、320×320から600×600ピクセル程度が適当らしいです。ちなみに、著者は320×320です。

C-5. "私のApp"の iOS-12 への Version-Up の結果

  • [上記以外] : "私のApp"の取説的な文章を「"Deprecated"(非推奨/ 廃止予定)のUIWebViewからTexViewに置き換える」などの変更を加えました。
  • [結果] : 当初のデザイン(動作)を取り戻し、iPhone のパフォーマンスアップのおかげでよりスムースに動作するようになりました。"私のApp"でBackground再生をしながら、Web検索や写真を閲覧してバカバカ切り替えても何の支障も有りません。

D."私のApp"の「iOS 13」のVersion-Upへの対応

  • すでに、「iOS 13、iPadOS 13、macOS Catalina」が提供されています。しかし、著者は「リリースされても、不具合の修正が行われ落ち着いてから対応する」派です。迷惑を掛けますが、まだポツリポツリと Xcode を含めバージョンアップが行われていますし、不具合の情報も投稿されています。
  • 前回の2度のバージョンアップでも「通常使う曲リストの順に再生することに問題は発生しません」でした。また、iPhone をお持ちだが「New iOS にバージョンアップはしない or 出来ない」方は、今の内に"私のApp"の導入を・・・。
  • また、最悪でも「iPhone の MUSIC で再生は出来るはず」ですので、音楽が聴けない状態が回避出来るからでもあります。それを含めて「Macの音楽再生リストを共有」した App にしています。
  • 準備の一部として、実際に「MUSIC」で「iTunes」と同じ作業をしてみましたが、基本同じでした。失敗したら消去して、再チャレンジ出来ます。失敗は、作業時間を失うだけで、多くの発見と知識を与えてくれます。

E. 2019年秋のOS変更と Version-Up の関連情報を調査

E-1. 報じられている macOS への Version-Up の注目点

  • 下準備として関連しそうな部分の情報を収集し調査していましたので、記しておきます。
  • 「support.apple.com」で発表の「Macで今後予定されているiTunesの変更点について」で注目したのは、(なお、現在この「support.apple.com」のページは、新しいタイトル・内容に改編されています。)
  • (1) これまでご愛用いただいていたiTunesの機能は、iTunes Storeも含めてそのすべてが、それぞれの機能性にもっと特化し、macOS Catalina用にデザインを刷新した3つの新しいAppに受け継がれます。
    (2) macOS Catalina では、取り込んだ音楽も読み込んだ音楽も、購入したメディアも、そのすべてにこれまでよりずっとすばやく、簡単にアクセスできます。
    (3) 新たに登場した App は、iOSにも同じAppがあり、どちらもまったく同じようにメディアを整理してくれます。
    (4) メディアコレクション全体が新しい拠点を得て、新しいApple Music、Apple TV、Apple Podcast App に自動的に移行します。
    (5) また、以前に iTunes Storeで購入したコンテンツや同期したライブラリは、どのデバイスにおいても変化しません
    (記載文の一部を抜粋)
  • 上記(2)の記載により「自分でCDから起こした曲も今まで通り使えるよ!」の意味と解釈しました。であれば、"なにより何より" です。
  • で、「iTunes」が3つに分解されて、音楽再生を扱うAppが「Music」の名称になり、操作画面が変更される程度なのでしょうか ? 。

E-2. 報じられているiOS13への Version-Up の注目点

  • 「iOS13 プレビュー」で発表の「iOS 13で利用できる新機能」で注目したのは、 < 参考資料-E21 >
  • ミュージックの項目では、
    (6) 音楽に合わせて歌詞を表示 : お気に入りの音楽の再生に合わせて歌詞を表示するように設定できます。音楽に合わせて歌詞が一行ごとに表示されるので、今歌われている歌詞と次に続く歌詞をいつでも把握でき、歌詞をスクロールしてタップすれば、お気に入り部分のメロディーに瞬時に移動できます。美しいアニメーションで、お気に入りの曲を追うのが楽しくなるでしょう。
    (7) 次の曲 : ワンタップするだけで、次に再生される曲のリストのほか、その曲がどのプレイリスト、アルバム、ステーションから再生されているかをチェックできます。次の曲をいつでも確認でき、再生の順番も簡単に変更できます。
    ヘルスケアの項目では、
    (8) 環境音レベル : Apple Watch上のNoiseアプリケーションから届く通知と環境音レベルを確認できます。環境音レベルは、「OK」または「大」の2つに分類されたデシベルレベルで表示されます。
    (9) ヘッドフォン・オーディオ・レベル : あなたが使うヘッドフォンのオーディオレベルを、「OK」または「大」の2つに分類されるデシベルレベルで表示します。
    (10) オージオグラム : 聴力検査からのオージオグラムを確認できます。
    (著者追記 ; オージオグラム == X軸に周波数、Y軸に音圧を用いた聴力検査の結果を示すグラフのこと。
    (記載文の一部を抜粋)
  • 上記のミュージックの項目で注目したのは、(7)の記載にある「再生中に次の曲のデーターを表示出来る」との記載です。これは、「C-3. 曲の移動」で示した"私のApp"で発生した不具合の原因となった変更だったのでしょうか ? 。
  • こんな目的であったのなら、「再生中のリスト内のデーターに曲番号でアクセス出来る機能を追加する」だけで、他のAppに影響なく可能で有り、将来の機能拡張も幅広く出来たのに・・・。と、著者は考えます(著者の不勉強で本当は既に出来たのかな ? 、とすれば前回の変更は必要なかったのですが ? ? ? )。
  • でも、Appleは「iPhoneをどこでもカラオケマシーンにも変身させたい」のでしょうか ? 。
  • また、ヘルスケアの項目では「ヘルスケアAppをドンドン増やすゾ」と言う意思表示を感じる。失礼、全ては「iPhoneの機能・用途を広げるゾ」ですね !

E-3. その他のWeb記事で気になった情報

  • ファイル操作用に「ファイル」Appが登場する。Lightning端子とUSBやSDカードなどをアダプターて接続すれば、アクセス出来るとのこと。しかし、Macとは異なり「Safari」からのみの限定機能との記載もある。
  • 新しいmacOSで、iTunesが「MUSIC」に変わっても「CDリッピング機能」が「CDプレーヤー」を接続すると使用出来るように成るとの記載も有りました。

  • [私の願い] : それは、iPhone本体にSDカードが直接マウント出来る様になる事です。だって、どのアダプターもダサイし、ゴテゴテになりスマートホンじゃあ無くなるからです。iPad でも欲しいな ! 。もちろん下心あってですが・・・。きっと、ほとんどのユーザーとプログラマーの願いかも・・・。

E-4. AppleのDeveloper Documentationさんへのお願い

  • 「C」及び「D」の項目に関連して、Developerサイトにある「MPMusicPlayerController」などのドキュメントで、新たな変更が発生していないかを検索・確認をしてみました。
  • その中で、"Deprecated"(非推奨/ 廃止予定)が多数記載されていますが、登録や実施の「日付 or OS の Version 」の記載が無いために、一つ一つ確認する必要が有り時間と「?」が多かった。ぜひ、予告を含め「日付 or OS の Version 」を記載して頂きたい。どこかに、この件に関する項目のリストはないだろか ? ? ? 。
  • 結果としては、新しいものがあるのか無いのか解らなかった。

F. 知らなかった音楽配信App「Music FM」の存在

F-1. なぜ高校生は「Music FM」で音楽を聴くのか

  • 『なぜ高校生は「Music FM」で音楽を聴くのか(CNET Japan 2019-06-22)』と題された記事で、次のような記載を見つけました。 < 参考資料-F11 >
  • ・CNET Japanとテスティーの「音楽」に関する調査(2018年1月)を見ると、音楽を聴く頻度を「毎日」と答えた10代男女は50~60%に上った。
    ・一方、音楽視聴の際に利用するサービスは、「無料サービス」という回答が10代女性は91.7%、10代男性は80.4% と極めて高く、楽曲課金や月額課金サービスの利用率は1割前後と低い。
    利用アプリは、YouTubeが6割以上、そして「Music Box」が10代女性で42.1%、 10代男性で35.2%となっていた。Music boxとは現在の「Music FM」であり、両者は 同じものと考えていいようだ。
    ・ただし、多くの子達が「違法」という認識はあるようで、「やっぱりやばい?」と聞いていた。
    大学でも大半の子がMusicFMを使っているという。また、ある業界人から「DJに興味があると言っていたのに、その子が音源にYouTubeを使っていて音質にこだわりがないのに驚いた」という話も聞いた。
    ・Music FM人気の理由は、やはりいわゆるギガ消費と関係があるようだ。先程の調査結果のように、YouTubeなどで動画とともに音楽を楽しむこともあるが、音楽を聴いている間は他のアプリなどは使えないし、Wi-Fiが使えるところでなければ通信量がかさんでしまう。
    ・一方このアプリなら、ダウンロードして聴くことができ、バックグラウンド再生もできるというわけだ。
    (記載文の一部を抜粋)
  • あったんですね、こういうアプリが ! ! ! 。知りませんでした。そして居ました、音質を気にしない(解らない)子が ! ! ! 。でも、この事を指摘した人がいたということから、ここでは良しとしましょう。

F-2.「Music FM」の追放をアップルに要望

  • また、『App Storeからの「Music FM」追放を--日本レコード協会や LINEらがアップルに要望書』(CNET Japan 2019-07-11)では、次のような記載が有ります。 < 参考資料-F21 >
  • ・一般社団法人日本レコード協会は7月11日、一般社団法人日本音楽事業者協会、一般社団法人日本音楽出版社協会、一般社団法人日本音楽制作者連盟の4団体に加え、AWA、KKBOX、LINE MUSIC、楽天の4社とともに、アップルに対し、著作権者および著作隣接権者などの管理者が想定しない態様による音楽配信アプリ(無許諾音楽アプリ)の対策強化について、要望書を6月28日付で提出したと発表した。
    ・これは、「Music FM」など無許諾で音源を配信する音楽アプリのApp Storeでの配信停止を求めるもの。
    ・Music FMは、中国を中心に2012年ごろから「Music Box」として出回っているもので、アーティストなどの著作権者や事業者に許諾を取っていないまま楽曲を配信している。
    ・大規模アンケート調査(15~59歳の男女、有効回答数は22万8613人)によると、「いまスマホで音楽を楽しむとき、最もよく使っているものは?」という質問に対 し、全体の11%がMUSIC FMを挙げている。さらに、10代全体に絞ると32%まで上昇する。
    ・日本における音楽配信の市場規模は645億円で、前年比113%と5年連続で増加傾向にある。そのうち、ストリーミングサービスが349億円で、配信全体の5割を超え るところまで伸びてきている。
    なお、海外での無許諾アプリについては、フリーミアムモデルのSpotifyが登場してから一掃されたという。広告付きであれば無料で聴くことができ、Spotifyに勝る無料アプ リがないというのが実情のようだ。
    (著者追記 ; フリーミアムモデル == 基本サービスを無料で,付加的なサービスを有料で提供する)
    (記載文の一部を抜粋)
  • 上記の記事を読み始めると、「Music FM」で素直に頭に浮かぶ国名がそのままでしたね。このコンテンツ主催者は、完全無料のコンテンツで何をしようとしているのですかね。収入は CM ? 。その目的が、心配です。日本の各団体は、そこの追求をしていないのでしょうか ? 。忖度して、マル秘かな ? 。
  • また、Qiitaにも「Music FMがApp Storeから追放された。Music FM消えた後、代わりになる音楽アプリって?」(Qiita, 2019-07-11 )と題する記事が投稿されていました。見逃した方は、こちらから。 < 参考資料-F22 >
  • でも、前記の要望書を出した日本レコード協会とそうそうたるメンバーが顔を揃えている中で、AppleやGoogle、JASRACなどへ「他力本願」と「モグラたたき」をするばかりですか ? 。
  • 日本の音楽関連団体とレーベル各社は、携帯(スマホ含む)キャリア各社も巻き込んで日本の若者達の為に、「将来の音楽愛好家やミュージシャン、そして彼らの若き日の思い出造り」による未来の音楽の発展と市場確保の為に、日本独自の音楽配信コンテンツを立ち上げることは出来ないのでしょうか ? 。
  • 若者達にとって問題なのは、「ギガ消費などの出費」なのは明確ですよね ! 。キャリア各社が「加入者の年齢などによる無料 or 安価な有料の制限(区分)を付けた音楽専用サイト(保存可能な音楽専用)で、「災害時の無料 Wi-Fi の 00000JAPAN」のようなWi-Fiチャンネルを提供し、運営費と著作権使用料はSpotifyの様にCM料や「JASRAC の著作物資料の提出がないために分配できずに残った分配保留金や商標音楽登録料(下記)」の一部で賄い、余分な費用(DJや喋り)を排除して、音楽業界が共同で管理・運営する」こともアリなのでは ? 。「ヤマハさんや楽器、オーディオ、音楽イベント会社」さらにその周辺の音楽関連会社などもCMを出してくれるかも ? 。(注)災害時の無料 Wi-Fiには、セキュリティに脆弱性があるようなので改善したものを使う必要があるようです。詳細は、各自でWeb検索を・・・
  • どこかの国のように、「文句と権利、御都合(主義)」ばかり主張していても、未来は開けないのでは ! 。このままでは、学校(小、中、大、専門)の音楽授業やクラブ活動にも、「権利の手」が伸びて来そうだよね !
    ▲ 既にありました。『「校歌歌詞を「式次第に掲載できない」? ネット物議も、JASRAC見解「使えないという指導はしない」(J-CASTニュース, 2019-9-27)」』に顛末が・・・。もっとも、学校と作詞家の当事者間問題でもありそうですが・・。 < 参考資料-F23 >
  • CD屋さんやレンタル店はドンドン数が減っているようだし、個人が出展する中古CD通販サイトは活況なのかな ? 。
  • 「資本+技術」を持ったAppleやGoogleが日本のレーベルに進出して来る時代もアリかな ? 。結果、音楽家や聴く側にいいことがあるかも ? 。(著作権とは関係無く)
  • また、「Appleのプロテクト有り音楽ファイル( .m4p )」でさえ他の形式へ変換するAppが登場している現状もある。
  • もっとも、どんなプロテクトもイヤホン出力(アナログ)からAD変換(デジタル化)すれば、昔の「カセットtoカセット(Wカセット機)」より高品質なコピーが出来る根源的な技術的特性を内在しているし、もちろん知識が有れば「デジタルtoデジタル」も「いわんやをや」ですよね。
  • 取り締まっても、需要がある限り「分散し、地下に潜り、闇(国外)に隠れる」のを招くだけではないのかなア〜 ! ! !
  • ちなみに、著者の『奮闘記-元記事』で紹介した『参考資料-J71,/iTunes プレビュー』で2019年07月19日に検索したところ、
    ⚫︎「Music Box Player」などの名称のAppが7件
    ⚫︎「Music FM」の名称のAppが1件、「Music FM Recife」などの名称のAppが3件
    の掲載があるのを確認出来ました。

G. 音楽の著作権とJASRACの関係

G-1. 音楽の著作権とJASRAC(日本音楽著作権協会)
  • こんな記事も見つけました。『「JASRACは何と戦っているのだろうか」(日経ビジネス, 2019-07-12)』もありました。なお、賛否色々あるようですが、記事へのリンクだけを貼っておきます。 < 参考資料-G11 >
  • また、別の記事では、『JASRAC、溜まった分配保留金で新事業…浅石理事長「トップランナーの責任果たす」(2019-08-12, 弁護士ドットコムニュース)』と題するものです。 < 参考資料-G12 >
  • この記事での新事業は、「アジア・太平洋地域の著作権管理団体への支援と専門弁護士を増やしたい」と「日本でも、対象機器の公平な見直し(スマホへの私的録音録画補償金制度の導入)」などと記載されています。
  • なお、JASRACの定款について、次の記載がありました。
  • 音楽の著作物の著作権を保護し、あわせて音楽の著作物の 利用の円滑を図り、もって音楽文化の普及発展に寄与することを目的とする」と書いています(定款3条)
  • 一応、「音楽著作権とJASRACの関係」の資料として次のリンクも提示しておきます。 < 参考資料-G13 >
  • しかし、前記の E-2 の 参考資料-E21 で示した資料には、次のような記載もあったのが救いです。
  • ただしお金がない10代でも、「好きなミュージシャンはCDを買うしライブも行く」という子は多い。「大ファンだからCD2枚買った」という子もいるほどだ。本当に好きな場合は大画面で見たり、音質にこだわったり、モノとして所有したいと思うことが多いのだ。
    (記載文の一部を抜粋)
  • でも、E-2〜E-4項の記事や記載を読んでいて、プラスチックの塊の様なCDは「メディアとして今後どんな方向に変化して行くのかナァ〜」と疑問が浮かんだ。ICカード と Web経由の併用 ? ? ? 。こんなニュースも出ていました、「新車から消えるCD/DVDプレーヤー もはや不要なのか? 新型「カローラ」にもなし (乗りものニュース, 2019-09-22)」 < 参考資料-G14 >
  • もしくは、「FMラジオ受信機(アナログ方式)をスマホに内臓してもらう」かな ? ? ? 。でも、2019年9月の台風15号で被災した千葉県関連のNHK災害ニューステロップ(関東エリアだけだったかも)に、沢山の「ローカルFM局」の名前が出ていて「アナログFM局がこんな形で進化し根付いていた」ことに驚かされました。FM-AMラジオを何処にしまったかな ? 。なお、デジタルラジオは高価なだけで上手く動作しないらしいけど、市販の受信機もあるようなので解決されたのかな ? 。 < 参考資料-G15 >
  • と書いておいたら、「エフエム東京 デジタル放送子会社巡り不正会計(2019-08-21,日本経済新聞)」なる記事が出ていて、「i-dioはテレビ放送の地上デジタル化で空いた周波数帯の一部を使ったデジタル方式の新しい放送サービスだ」と書いてあった。知らなかったです。知ってました ? 、「デジタル方式の新しい放送(V-Lowマルチメディア放送)」を ! ! ! 。詳細は、各自で・・・。
  • もう一つ疑問が、「合法的に音楽を楽しむ人には、スマホへの私的録音録画補償金制度は著作権料の二(多?)重取り」にならないのかナァ〜 ? 。聞かない人は、もっと・・・? 。裏付けとなる使用状況の統計結果は ? 。
  • 前出の 参考資料-E42 の後半の部分で、CISAC(著作権協会国際連合)会長 (ジャン・ミシェル・ジャール、エレクトロニックミュージシャン・作曲家) の発言として、
  • ジャン・ミシェル・ジャールは会見で、自身のスマホをかかげて、「これがなんで10万円で 売れるんだ」と聞き返したうえで次のように話しています。「電話と通信だけなら、5万円くらいで十分ですよね。でも、いろいろなものが付加されて います。その1つとして、音楽を聞いたり、発信したりする機能も入っています。ヨーロッパの企業は、その責任として、創作者に対価還元しようと、ストリーミングの時代になっても、ちゃんと補償金を払っています。ぜひ日本の人たちにもわかっていただきたい」
    (記載文の一部を抜粋)
  • 上記の意見は、乱暴で盲目的な利己主義ではないかい ? 、「一般のスマホ使用者が、音楽再生のために5万円余分に払っていると言うのかい」とツッコミたくなる。そして、この屁理屈に乗っかって日本でも導入するの ? ? ? 。
  • もっとも、補償金が一台あたり「何円 or 何 % 、著作権料との関係は ? 」の確たる情報が無い(不勉強かな ? )ので、曖昧な意見でしか無いのですが・・・。
  • スマホの機能は、Webによる情報収集、メール、電話、ゲーム、写真や映像の撮影、お財布代わり、ヘルスケア、他にもいっぱい機能があってのスマホではないの ? 、汗と涙のApp開発者の努力もあってね ! ! ! 。少なくとも、音楽再生専用機ではないし、スマホはガラ携帯が発展したものですよね。ウオークマンを引き継いだのは、iPod だよね(歴史的には2000年代初めのiPod → 2007年発売のiPhoneですが) 。"それでも"であるなら、音楽再生Appのプリインストールを止めてもらうか ? 、声がAppleに届けばだが ! 。使わない人に、迷惑を掛けるから !
  • 著者の青春時代だった昭和の「演歌、GS、フォーク、ディスコ音楽、アイドルポップス」と続いた音楽全盛の時代(オーディオも)が懐かしいナァ〜。Jazzも、映画音楽も、ポールモーリアも、ひょこりひょうたん島やアトムのアニメソングもあったし、まだ歌えるぞ ! 。もちろん、クラッシックもあったしね。世界中の音楽が聴けた、青春時代の最高の思い出だ !
  • あの頃は、音楽業界も利益があり、うまく分配出来ていたんだよね〜 ? きっと !

G-2. 著作権に対する著者の妄想

  • 著作権を「独創性の保護(著作)」と「創造物への対価(作品音源)」、さらに「使用実施権への対価(演奏、再生)」を別々なものと捉えて区別する必要があるのではないだろうか ? 。例えば、
  • ⚫︎「独自性の保護」は、主著作権者の「死後 or 公表」から50年まで(現状維持)。
    ⚫︎「創造物への対価」は、「最長期間が主著作権者夫婦(登録時のみ)両方の死まで」 or 「主著作権者が決めた期日まで (最長期間以内、無しも可能) 」のいずれかを選択する。
    ⚫︎ 主著作権者以外の権者は、「公表から20年まで」と考えます。ここでの、主著作権者とは、作詞, 作曲, 編曲, 主歌唱の権利者(全て個人)のことです。
    ⚫︎「使用実施権への対価」は、主著作権者の「死後 or 公表」から50年まで(現状維持)とする。 これなら、校歌などの「金はいらネェ〜が、名誉は守りた〜い」が可能だし、時代に合わない「孫,子の代まで」を排除出来る。
    ⚫︎「法人の商標(テーマ曲等)に相当する音楽」は、「公表から20年まで」以後は、毎年「音楽著作権協会」に相応の商標音楽登録料(高額)を納めることで、著作権を守る事が出来る。そして、「この商標音楽登録料と通常の著作権料」は、前記した「音楽配信コンテンツの運営費」に提供する。
  • なお、特許権では、
  • ⚫︎「特許の成立・非成立にかかわらず、申請以後に同じ技術内容で他人が特許を取得出来ない」。
    ⚫︎「特許登録が成立した場合、特許申請日から最大20年で特許料を納めた期間のみです。

G-3. JASRAC への感想

  • とは言え、「JASRAC」さんも法に基づき委託された案件をしくしくとこなしていらっしゃるだけなんだろうとも思います。でも、法律にも解釈に幅があり、その解釈を「JASRAC」さんが決めていい訳でも無いと思う。
  • 例えば、前記したJASRACの理事長さんの発言にある『溜まった分配保留金で新事業、トップランナーの責任果たす』や『スマホへの私的録音録画補償金制度』の発言が、「JASRACの定款3条」からも奇異なものに感じるのは著者だけだろうか ? 。

H. ギガ消費と著作権料を分析

H-1.「ギガ消費と著作権料」を金額で分析

  • 見つけた記事は、「スマートフォン利用者の月額通信料金の実情をさぐる(2019年公開版)、(Yahoo!ニュース 2019年08月17日付)」です。なお、出典元に関して「(注)本記事は【ガベージニュース】 に掲載した記事に一部加筆・変更をしたものです。」との記載が有りますが、元記事を確認出来ず、リンクを用意出来ませんでした。ただし、記事にあるグラフに数値の記載があったため、著者が一部をグラフにしたものを資料として用意しました。
  • と、Qiitaの「あなたは音楽CD派 ? それともストリーミングサービス派 ? 音楽業界の動向をJMPで分析してみた(Qiita, 2019-05-17に更新)」の記事です。記事は、「データ分析と可視化」が主題のもので、たまたま選んだ数値資料が「日本レコード協会の売上年次推移」であったものと思慮しまが、引用させて頂きました。 < 参考資料-H11 >
  • ■ 図-H11. スマホの月額通信料と、レコード協会の売上及び著作権使用料徴収額 (著者の加工と追記有り)
    fig-h11.png ■ 図-H11の(1)は、2019年公開版のスマホの主要キャリアでの月額通信料の資料から一部データーを使って著者がグラフにしたものです。(2)は、前記のQiita記事の「日本レコード協会の売上」グラフに、JASRACの2007、2018年度の徴収額を追記したものです。
  • 図-H11の(1)は、主要キャリアでの80才以上までを11区分で示されているものですが、50代以下をピックアップしました。13〜19才の中学生から大学2年生ぐらいの範囲では、月額約5000円程度であり、男女全体平均の5,952円の84%と大差無い「ギガ消費(金額)」になっています。
  • ちなみに、別途の集計では「MVNO (いわゆる格安の仮想移動体通信事業者)」の使用者の料金が 2,264円(男女全体平均)であり、主要キャリアの 5,952円(男女全体平均)の38%となっています。
  • 図-H11の(2)の「日本レコード協会の売上」は2008年あたりから長期低下傾向があり、2015年からは下げ止まりの傾向が見られます。ただし、各項目に含まれる詳細な区分が明確ではないために安易な評価は出来ませんが、2014年あたりから伸びているストリーミングの増加傾向は、JASRACのおかげなのでしょうか ? 。
  • JASRACの徴収額は、前出の< 参考資料-G12 >の後半に下記の記載があり、数値を取り上げました。
  • JASRACの2018年度の徴収額は、2007年度の1156億円に次ぐ1155億円で、史上2 番目・・・。 (記載文の一部を抜粋)
  • グラフでは、JASRACの徴収額がレコード協会のオーディオレコードの金額を追い越しそうな勢いですね !
  • なお、上記グラフは、各自での分析と評価をお願いします。
  • その他、「私的録音録画補償金制度」に関連したものを参考に提示して置きます。注目は、(2) の「現行の補償金システムが抱えるクリエーターへの適切な対価還元について」での問題点の指摘です。
    (1)『JASRAC ホームページ内の「私的録音・録画補償金制度」』のページを見つけました。 < 参考資料-H12 >
    (2)『「著作権法を見直し私的録音録画補償金ではなくフェアユース規定での運用がクリエーターにも消費者にもベスト:旅人目線のデジタルレポ 中山智」 (Engadget 日本版 2019年04月02日付)』がありました。この記事では『文化庁の「文化審議会著作権分科会」で、補償金対象を拡大するかどうかの審議報告として結論は出ず』だったそうです。 < 参考資料-H13 >
    (3)「これって著作権的に大丈夫? 17を疑問をJASRACにぶつけてみた! (DiGiRECO 2018年08月03日付)」と題するインタビュー記事です。 < 参考資料-H14 >

H-2. 著者の音楽再生(鑑賞)での音源(File)収集の方向性

  • 著者は、自身が特許という権利に守られている立場ですから当然ながら著作権も尊重する立場です。ただ、著作権の「権利範囲 と形態 (権利金額や集金方法) が正確には把握出来ていない」ことも自覚しています。
  • 前記した"私のApp"のバージョンアップ完了後の3月に、著者も「Apple Music」に体験入学(トライアル加入、無料期間中に脱退)してみました。
  • 脱退の理由は単純で、脱退したら利用(入手)した曲の全てが消えてしまう事に気付いたからです。仮に、病気でもしてダラダラした日々を過ごすようになったら、溜め込んだつもりの「好きな音楽が聴けなくなる」システムだからです。もちろん、他の音楽配信サイトも似たような問題を抱えているのでしょうが・・・? 。
  • 金の切れ目が縁の切れ目、ボケてIDを忘れたら解約も出来ないのですが、スマホは携帯としての契約(キャリアとの契約)を終了しても充電すれば何時迄もAppも、セキュリティアップデートが続いていれば Wi-Fi によるWebも安心して使えます。ま、色々な考えがあるとは思いますが・・・。
  • なにも、CDのコレクションをしたい訳では無いのです。CD購入やレンタルCD及び他の方法で収集する昔からの合法的な方法で、ボチボチHDに貯めて行きます。好きな歌手や曲は、何度聴いても飽きないものです。
  • もちろん、「Music FM 」に手を出す気はありません。また、常時Web接続するやり方(ギガ浪費)は嫌いです。なお、「Spotify」に付いては、もう少し情報を集めてから考えます。
  • ただし、「もし、日本でスマホに私的録音録画補償金制度が適用される」ようになった段階で、Appleさんが「Apple Music」に一定期間加入し特定の年齢に達したら、本人名義のiPhoneやMacで「ファイルとして保存したAppleのプロテクト有り音楽ファイル( .m4p )」が「Music AppとFormatConverter ("私のApp"で使っているAudioUnit)、及びAppleが用意している音楽再生Playerとで再生出来る」ように変更されるのなら再加入すると思います。だって、iPhoneを買った時点と(.m4p)をダウンロードした段階で著作権料に相当する料金を支払っているのだろうから・・・。もっとも、私の理解が正しければですが ? 。

H-3. この項のオマケ

  • 2019年8月24日夜にNHKで「ドキュメント 矢沢永吉 もうすぐ 70 歳」と題する放送がありました。その中での矢沢永吉の発言です。
  • 「日本は今、ガキばかりの音楽だから、大人の音楽をつくらなければならないんだ。」
  • また、民放に出演した時に「レコードからカセット、CDへと変わり、今はネットの時代ですが・・・」との質問に、矢沢さんは「時代の変化に合わせた活動をしているし、今後も同じだ」(要点のみ)と発言していました。
  • レコード制作各社さん、「(矢沢) よろしく ! ! ! 」。(Try-Jizy) 待ってるよ、ハイレゾじゃなくてね !

I. "私のApp"の行方

  • "私のApp"や理論を広げるには、まだまだハードルが有りそうですね ! ! !
  • まず、「音楽を聴くには音源(音楽ファイル)が優先」だし、「各音楽配信サイトはプロテクトファイルやそれぞれの再生リスト(曲リスト)を使っていて、他のAppからアクセスが出来るかどうか不明」だし、「MacやPCを使ってファイル移動」をする方法も考えられるが、スマホとコンピューターの両方を持っていないと出来ないし、手間と知識が必要だし・・・。良い"だし"が欲しいナァ〜。
  • さらに、二十歳以下のユーザーに"私のApp"は対象外かな ? 。Music (iPhobeのApp)が「外部 App を出力に挿入すること(Inter-Appの機能)を許可」してくれれば、iPhoneには使えるのですが、だめだろうなァ〜 ! ! ! 。Android は、イヤホン出力のレベルやイヤホンの感度と特性がメーカー毎にバラバラだろうし(調べた事は無い)。
  • マァー、オーディオと音楽のマニア達と己の夢の為に頑張ろう・・・ボッチ ボッチと。

J. JEITAの「新ヘッドホン・イヤホンの測定方法」から学ぶ

J-1. JEITA(一般社団法人 電子情報技術産業協会)の新規格とは

  • 『各社バラバラに計測「ヘッドホンのハイレゾ測定法」がついに統一? JEITA(一般社団法人 電子情報技術産業協会)の新規格を聞く(PHILEWEB 2016年06月10日付)』の記事が出ていました。 < 参考資料-J11/Page-3 >
  • 記事は、『JEITAが「ハイレゾ対応」ヘッドホン・イヤホンのための新たな測定方法を規格化し、発表した』というものです。
  • ■ 図-J11. JEITAのハイレゾ対応ヘッドホン・イヤホンの測定方法 (著者が再構成)
    fig-j11.png ■ 上記図-J11の(1)は、JEITAが規格の一部として示した「IEC 60318-7」です。(2)は、同記事内の「測定方法と結果の表示方法」を図で示したものをまとめたものです。なお、JEITAの規格は自由音場(無響室)での測定を用ています。
  • 記事の記載では、「100Hzから10kHzの帯域においてはIEC 60318-7に規定されたダミーヘッドの自由音場レスポンス(0度方向)を満足すること」とあります。10kHz以上の特性に注目していたので、Web検索をしましたが残念ながら見つけられませんでした。また、このグラフの低い方の共振周波数が2.5kHzである事に注目しておいてください。
  • なお、『SONYが、プロフェッショナルスタジオモニターヘッドホン「MDR-CD900ST」の後継機として、ハイレゾに対応した「MDR-M1ST」を2019年8月23日に発売した』との多数の記事が出ました。各記事には、「再生周波数帯域 5~80,000Hz(JEITA)、IEC(国際電気標準会議)規格による測定値です」と記載はあるのですが、SONY社のページを含め周波数特性のグラフは記載されていませんでした。10kHz以上の特性が示されていることを期待したのですが・・・。
  • 特徴的なのは、右側の図-J11の(2)に示した測定結果の表示方法です。それは、ダミーヘッド(HATS)で測定したデーターを標準特性である図-J11の(1)の逆特性フィルターで処理する事で、見慣れているスピーカーの周波数特性と同様に、基準特性と同じだと水平なグラフとして表現出来る様に工夫している事です。
  • [参考] :「IEC」とは国際電気標準会議(IEC == International Electrotechnical Commission)で、各国の代表的な標準化機関によって組織されている「電気通信分野をのぞく電気・電子分野について、国際的な標準化」を行っている非政府国際機関です。

J-2. ダミーヘッドを見てみる

  • 参考として、Webで見つけた音響用「ダミーヘッド」の外観を提示します。
  • ■ 図-J21. ダミーヘッドの外観 (著者がWebで収集)
    fig-j21.png ■ 図-J21に示したダミーヘッドには、測定専用、バイノーラル(立体感が得られる)録音用、さらに両方の機能を有したものがあります。
  • ダミーヘッドは、人間の頭部の形状や耳介、外耳道などが再現されており、鼓膜の位置にマイクが仕込まれているものです。これにより、人間に聞こえる音を電気信号として取り出そうしています。さらに、人間の皮膚に近い素材で作られたものもあるようで、まだまだ進化するでしょう。

J-3. 「IECの規格」の元となったと思われる特性曲線

  • この特性が見たくて、Web検索を頑張りました。見つけたのは、「フラットな音色のヘッドホンとは? (Sandal Audio 2015年11月18日付)」と言う記事です。相当、業界内部の情報に詳しい方(開発技術者 or 研究評論家 ? )のブログです。 < 参考資料-J31 >
  • なお、「参考資料-J31」内のグラフでは周波数軸(X軸)に「20, 200, 2000, 20000」を使ってあり、通常のオーディオで使う「20, 100, 1k, 10k, 20k」と異なっていて他のグラフとの比較に不便だったので、著者が下記の図-J33に示した方法により周波数軸を変換して再作成してあります。そして、曲線どうしのレベルを比較するために 1kHz の値を 0dB とする Y軸方向での調整を行いました。結果、提示したグラフがオリジナルと多少の誤差がありますが、傾向を比較するには十分と思慮しました。
  • また、予備知識として「ダミーヘッドで測定する環境に対して2種類」が提唱されています。
    (1)フリーフィールド特性 (Free Field / FFと略記 / 自由音場) : スピーカーと同じように反響の無い「無響室」で測定。
    (2)ディフューズフィールド特性 (Diffuse Field / DFと略記 / 拡散音場) : 風呂場のように周囲の反響が多い環境で測定。
  • 前記した「参考資料-J31」に記載のグラフを元に「FF曲線とDF曲線」を重ねて、特性の違いを確認します。
    ■ 図-J31. FF曲線とDF曲線の特性を比較 (著者の合成と加工あり)
    fig-j31.png ■ 上記図-J31は、青色の曲線がフリーフィルド特性で、緑色がディフューズフィールド特性です。
  • 図-F31では、緑色のDF曲線を2dB 程度プラス方向に移動させると5kHz 以下の帯域では大差の無い特性になっていると読み取れる。
  • 逆に、5kHz 以上の帯域では、青色のFF曲線が大きく上下に変化しているのに対し、緑色のDF曲線は多少の暴れはあるが徐々に低下する特性となっていて、大きな違いを読み取れる。
  • ただし、FF曲線の大きな上下変動は下記する図-K11 の (3) の高い方の外耳道共振の特性が出ていると解釈することも出来るが、DF曲線でこの特徴が見られ無いのは拡散音場が影響してるのか ? 。 しかし、両方の曲線が 7kHz 以上で周波数幅の狭い暴れが多数あるのは、耳介などによる反射波などが影響しているのだろうか ? 。
  • 次に、JEITA規格の一部である「IEC 60318-7」の特性の上に、「 FF曲線とDF曲線、さらにハーマン・ターゲットカーブ」を重ねるて見ました。
  • なお、「ハーマン・ターゲットカーブ」とは「ハーマン・グループの技術者オリーヴ氏」が推奨・公開して多くの注目を浴びている特性だそうです(参考資料-J31 の後半に記載あり)。
    ■ 図-J32. 「IEC 60318-7の規格」と関係する曲線を比較する (著者の合成と加工あり)
    fig-j32.png ■ 上記図-J32は、赤色の曲線が「IEC 60318-7の規格」の特性、青色の曲線がフリーフィルド特性で、緑色がディフューズフィールド特性、黒色がハーマン・ターゲットカーブです。
  • 図-J32では、IECの規格曲線とそれ以外の曲線間の周波数軸でのズレが気になります。また、ハーマンの特性で 1kHz 以下がフラットに近いこと(±2dB程度)や高域の特性に好感が持てます。
  • 3kHzでは、見事に3つのカーブの特徴が現れているが、聴く音楽の種類や言語(地域)による好き嫌いが発生するかな ? 。ただ、ハーマン曲線の 5kHz 以上をこんなに穏やかな(暴れが無い)特性には出来ないのでは ? 。
  • 次に示すのは、この項で使った周波数軸(X軸)の変換方法です。
    ■ 図-J33. 図-J31,J32の周波数軸(X軸)の変換方法 (著者作成)
    fig-j33.png ■ 上記図-J33の上半分(黒色)は、「参考資料-J31」での「X軸の目盛表示」をExcelで再現したもので、下半分(赤色)は、オーディオで通常使われる「X軸の目盛表示」をExcelで作ったものです。
  • 著者は、Excelを使ったグラフを外部に出す時には「グラフを画面コピー → Image Fileに変換 → イメージ加工で20Hz未満を削除し(消す)体裁を整える」の工程を行い標準的なオーディオグラフのフォーマットに合わせています。また、グラフ間での比較が必要な時には、Y軸の目盛間隔も揃えるようにしています。

K. 聴覚の共振を学ぶ

K-1. 外耳道共振と聴覚の構造

  • 外耳道共振は、人間の聴覚(耳)の耳介から鼓膜までの間にある管(外耳道)で発生する空気の共振(共鳴)によって周波数特性か変化するとされている現象です。
  • この原理を説明するために引用したのは、「テクノロジー | なぜ6kHzに強烈な音圧のピークを生じるのか? | ヘッドホン音質革命/音茶楽 Sound Customize」に記されている図です。 < 参考資料-K11 >
  • なお、資料のタイトルに「6kHzに強烈な音圧のピーク」とあるのは、提示した図の後に「カナル型(密閉型、耳栓型)イヤホン装着時」の解説があり、それが解説の本題だからです。これは、外耳道にイヤホンを挿入することで、「外耳道共振の特性や共振周波数」が変化する現象のことです。興味がある方は、ご一読を !
  • 次は、聴覚の外耳道共振を学びます。
    ■ 図-K11. 外耳道共振の原理 (著者の加工あり)
    fig-k11.png ■ 上記図-K11の(1)は、耳介と外耳道のイラストです。(2)は、空気振動が鼓膜側(右側)を閉鎖したチューブ(横向き)内で起こる共振の様子を示しているイラストです。(3)は、外耳道の共振による周波数特性です。
  • K11の(1)では、外耳道「長さが 25〜30mm、直径 6〜8mm」の少し湾曲したチーブ状をしていて、奥が鼓膜によって閉鎖された形状になっています。
  • (2)では、一方の端が閉鎖された管での「閉管の固有振動」という原理に基づく空気振動を表現しています。
  • (3)では、この空気振動(共振)による音圧(音量)の変化を表す周波数特性を表現しています。

K-2. 聴覚の構造を知る

  • さらに下図では、聴覚の構造に関わるイラストと写真を提示します。
    ■ 図-K21. 音の通り道としての聴覚の構造 (著者の収集、加工あり)
    fig-k21.png ■ 上記図-K21の(1)は、聴覚部分の構造を示す垂直断面のイラストです。(2)は、聴覚から口までの範囲を垂直断面で示したイラストです。(3)の上部に示したのは、鼓膜の写真です。(3)の下部は、外耳道の断面構造を猿のサンプルで示されたものです。
  • 図-K21の(1)は、「耳介 → 外耳道 → 鼓膜 → 耳小骨 → 蝸牛」と続く聴覚のメイン通りと、鼓膜の内側にある「中耳腔と耳管」を示すイラストです。また、中耳腔には蝸牛を通過した振動が蝸牛窓から放出されています。
  • (2)では、耳管が「開閉弁を持つ耳管咽頭口から鼻腔内」につながる様子を示しています。耳管咽頭口には開閉弁があり、「普段閉ざされていてバイキンなどの侵入防ぐ」役割と、「開くことで外気圧と中耳腔内の気圧差を無くする」働きがあるそうです。例えば、飛行機に乗った時に発生する不快な耳の圧迫感を感じた時にする「耳ヌキ」をした時に開くとのことです。
  • (3)の上部は、鼓膜の写真ですがその奥に写り込んでいるのが「耳小骨」であるとの説明があります。鼓膜が透き通るくらいに薄い膜で出来ていることがわかり、「体内の神秘」を感じます。大切にしましょう。
  • (3)の下部は、外耳道の断面を示しているもので、単なる丸いチューブでは無いことがわかります。
  • ここで「聴覚の構造」を取り上げたのは、鼓膜の奥にある「中耳腔や耳管による空間・構造による特性への影響」が「外耳道の共振」と異なり、聴覚の特性に反映(議論)されていない事に疑問を感じたからです。単に、影響が「無視出来るほど小さい」のであればいいのですが・・・。残念ながら、このことの議論(記事)を見つけることは出来ませんでした。

K-3. 外耳道の共振周波数を資料から確認する

  • 気になっていたのが、前記した図-J11の(1)の『JEITAが規格の一部として示した「IEC 60318-7」の低い方の外耳道共振周波数が2.5kHzである』ことです。
  • また、比較する測定資料として選んだのは、
    (1)『奮闘記-元記事』の図-E01で提示した「等ラウドネス曲線(ISO 226:2003)」です。
    (2)「3D スキャナ・3D プリンタを利用した個人の頭部伝達関数の取得 (第23回日本バーチャルリアリティ学会大会論文集、2018-09、秋田大学)」の論文にある「図5:頭部モデルと本人の HRTF の比較の"(c) Sub2"」のグラフです。 < 参考資料-K31 >
    (3)「実耳応答でヘッドホンとイヤホンの周波数特性を測ってみるの巻 (ソノベ、2012-08-19、ブログかな?)」の記事にある「(5)外耳道単体での応答((1)-(2))」を選びました。 < 参考資料-K32 >
  • ■ 図-K31. 外耳道の共振周波数を他の測定データーで確認する (著者の加工と追加あり)
    fig-k31.png ■ 上記図-K31の(1)は、「等ラウドネス曲線(ISO 226:2003)」です。(2)は、「参考資料-K31」からのグラフです。(3)は、「参考資料-K32」からのものです。なお、(1)から(3)には、低い方の外耳道共振周波数の位置に縦の赤色破線を著者が追記しました。(4)は、外耳道の長さと共振周波数の関係をグラフにしたものです。
  • 図-K31の(1)は、「等ラウドネス曲線(ISO 226:2003)」で、「外耳道共振周波数」としては最も感度が高い部分である 3.15kHz と思われます。
  • (2)は、参考資料-K31 の「3Dスキャナを用いて頭部伝達関数を取得する研究論文の中で、耳の部分が交換できる構造を持つダミーヘッドを用いて、耳の部分を被験者から得たデーターを元に再現し、特性を測定している論文」の中に記載されていた測定グラフの一つを引用したものです。グラフでは、「外耳道共振周波数」が 3.5kHz あたりと読み取れます。
  • (3)では、参考資料-K32 の人間の実際の耳(実耳)に小さい特殊なMICを外耳道に挿入するなどして、「鼓膜直前の実耳応答」を測定したグラフの一つを引用しました。グラフでは、「外耳道共振周波数」が 3kHz あたりと読み取れます。
  • また、前記で示した図-J31や図-J32でも外耳道共振周波数が 3kHz あたりに成っています。
  • (4)は、前記した図-K11での共振の原理から外耳道の長さと共振周波数の関係をグラフにしたものです。緑色で示した従来の共振周波数と黒色で表した「IEC 60318-7」の共振周波数との違いをグラフで確認してみたものです。2つの共振周波数の違いは、全体から見れば些細な違いでもあるし、国際的なデーターとしては不思議な相違と思われます。
  • 例えば、「IEC 60318-7」が体格が大きい欧米人を中心としたデーターを元にした値( 2.5kHz の外耳道の長さ = 34mm )であり、「ISO 226:2003」では全体(世界)的な平均的データー(外耳道の長さ= 25〜30mm)による違いと解釈すればいいのかな ? 。
  • また、図-K31の(2)と(3)を取上げたのは、測定方法の違いにより「中耳腔や耳管による空間・構造による特性への影響」が読み取れないかと思ったのですが、残念ながら少しの違いはあるのですが、明確に説明出来る違いを見出せませんでした。
  • ただ、外耳道共振周波数の違いが聴いてわかるかというと「微妙な違い」とも思いますが・・・。

L. ヘッドホン・イヤホンの測定データで確認

L-1. ヘッドホンとイヤホンの分類

  • 近年の「ヘッドホンとイヤホン」は、分類の垣根が曖昧になっています。基本は、ヘッドホンが左右をつなぐアームがあるもので、イヤホンにはアームが無くてケーブルのみがつながっているものです。
  • また、「ヘッドホンとイヤホン」共に周囲の音を遮断する為や内部の音楽が外部に漏れなくすることを重視した密閉タイプと、どちらにもある程度の漏れを許容する解放型タイプのものがあります。さらに、ご存知のように周囲の騒音を打ち消す(ノイズキャンセリング)機能を持ったものまであります。
  • 以下では、それぞれの基本的な機能で分類し、聴覚との関わる部分に注目した考察を進めます。
    ■ 図-L11. ヘッドホンとイヤホンの分類 (著者作成)
    fig-l11.png ■ 上記図-L11では、一般的なヘッドホンとイヤホンでの断面構造のイラストを示しています。(1)は密閉型、(2)では解放型のヘッドホンです。(3)は密閉型、(4)では解放型のイヤホンを示しています。
  • 図-L11の(1)に示すのは、小型のスピーカーユニットの入ったハウジングで耳介を含む聴覚全体を覆い、周囲を頭部に密着することで周囲の音を遮断し内部の音が外部に漏れなくしている密閉型ヘッドホンの断面です。
  • (2)に示すのは、小型のスピーカーユニットの入ったハウジングを耳介に乗せて、耳介に触れる部分にはソフトタッチで音を通すスポンジなどで構成している。これにより、内外どちらにもある程度の音漏れを許容している構造の解放型ヘッドホンの断面です。
  • (3)に示すのは、ヘッドホンよりさらに小さなスピーカーユニットを用いてハウジングを小型にし、音の出口をゴム質(図では茶色)のもので外耳道に密着して挿入し挿入することで、周囲の音を遮断し内部の音が外部に漏れなくしている構造の密閉型(耳栓型)イヤホンの断面です。
  • (4)に示すのは、解放型ヘッドホンを小型にして、耳介の内側で外耳道を覆うようにする構造で、内外どちらにもある程度の音漏れを許容しているイヤホンの断面です。
  • また最近は、全てのタイプでスピーカーユニットの振動を制御して周波数特性を改善する目的でユニット背面から外部に小さな解放穴を設置しているものもあります。

L-2. 測定結果で周波数特性の傾向を確認

  • まず示すのが、著者が"私のApp"の開発始めた頃にiPhone周辺の情報収集している段階で、付属していたEarPodsを含め手持ちのものや秋葉原で新たに購入したイヤホンの特性を調べた時の「私的に旧来の方法で測定した周波数特性 (グラフ全体が緑)」です。EarPodsとMDR-CD900以外は、現在はほとんどが「販売を終了」したモデルです。
  • なお、手持ちの「MDR-CD900」も測定してありました。ただし、ダミーヘッドよる測定では無く、古い方法での測定なため、イヤホンとマイクの位置関係を示す画像を添付しました。この測定方法では、外耳道共振は反映されていません。
  • また、EarPodsとMDR-CD900には、専門設備で測定したと思慮されるグラフも掲載しました。
  • さらに各グラフには、特性の比較の目安として「IEC 60318-7の規格」の上限と下限の特性曲線を破線で記入しました。
  • さらに各グラフには、特性の比較の目安として「IEC 60318-7の規格」の上限と下限の特性曲線を破線で記入しました。
  • ■ 図-L21. 解放型イヤホンの周波数特性 (著者による測定)
    fig-l21.png ■ 図-L21は、標準的な解放型イヤホンとして選んだものです。
  • 低域はフラットで、高域はレベルが高く出ています。これは、マイクの位置が中央過ぎたのが原因であり「耳に実装時には、外耳道とは芯が外れる位置関係になるはず」であり、もっと低いレベルで「音に色をつける程度のレベル」であろうと思います。
  • ■ 図-L22. 密閉型イヤホンの周波数特性 (著者による測定)
    fig-l22.png ■ 図-L22は、標準的な密閉型(耳栓型)イヤホンとして選んだものです。赤の曲線は、イヤホンとマイクとを実耳に挿入した状態にまねてビニールチューブで密着して接続した特性です。緑の曲線は、イヤホンの音の出口の前に近接してマイクを配置した時の特性です。
  • 図-L22の赤線では、55Hzあたりで +8dB 程度のピークを持つた広い範囲の低域がブーストがされた特性である。逆に高域では、3.5kHz に外耳道共振を補正するようなブーストが見られますが、全体としてはおとなしい特性となっています。
  • また、緑の曲線はマイクとの間が密封されていないために、低域が拾えない or 回り込みによる打ち消し作用が働くなどしていて、高域だけが聞こえる状態になっている。
  • ■ 図-L23. BOSE IE イヤホンの周波数特性 (著者による測定)
    fig-l23.png ■ 図-L23は、BOSE社独特の特徴を持った製品です。強いて分類するなら、密閉型と解放型の中間で密閉型により近いイヤホンです。測定時には、イヤホンの音の出口に MIC を密着させました。
  • この製品は随分前に購入した古いもので、曲線からもわかるように低音がよく出るイヤホンとして知られていました。しかし、少し大きな音で聴くと「低音が出すぎる」との評判もありました。
  • これは、特定の音圧で「固定したラウドネス(聴覚)補正」を施した設計になっていて、設定した音圧よりも大きい音で聴くと過剰な補正となり「出過ぎる」という症状が発生していたのだと思います。
  • また、感度の悪いイヤホンなので「CRによるフィルター回路が入っているのでは ? 」と邪推していましたが、故障しないので分解して確認するチャンスがありませんでした。
  • このイヤホンは、「 EarPods 」に影響を与え、「著者の理論にヒントを与えてくれた」製品だと思慮しています。ただし、現行の製品がどのような特性になっているかは確認出来ていません。
  • 次は、EarPodsです。
    ■ 図-L24. EarPodsの周波数特性 (著者による測定)
    fig-l24.png ■ 図-L24は、この製品も独自の特徴を持ったもので、解放型に分類されるかな ? 。測定時には、イヤホンの音の出口に MIC を密着させました。
  • EarPodsは、構造的には前出の「BOSE IE」を開発した技術者が加わって、3Dプリンターを駆使した試作を繰り返して完成させたと思わせる製品です。なお、構造の詳細は下記の「T. EarPodsの特徴を解剖する」で確認して下さい。
  • 高域の盛り上がった周波数特性は、イヤホンの音の出口が外耳道に正対しておらず、耳介に反射させて外耳道に導く構造のために、ロスする分を余分にブーストした特性になっていると思慮しました。なお、iPhone のイヤホン出力はフラットな特性です。
  • 次に示すのは、『アップルの新イヤホン「EarPods」音漏れは確実に減った! (2012-09-23, ASCII.jp)』の記事から周波数特性 ( R-ch ) を引用しました。 < 参考資料-L21 >
  • 加えて、「FC2 イヤホン測定結果置き場 (ブログ)」というサイトで見つけた「インピーダンス特性 ( R-ch ) 」を下方に記載しました。 < 参考資料-L22 >
    ■ 図-L25. 参考資料-L21とL22 からのEarPodsの特性 (著者の合成と加工あり)
    fig-L25.png ■ 図-L25は、測定方法に関して参考資料では「イヤホンは「疑似耳」に装着され、実装状態を模した形で計測される」と『100Hz以下の計測値に乱れがあるのは「オープンエア型のため、低い周波数のノイズが混入」が影響している』の記載とがある。なお、赤色の曲線が周波数特性で、青色の曲線がインピーダンス特性を示しています。
  • この赤色曲線は、イヤホンの「疑似耳」での測定であることからダミーヘッドによる測定と同等であり、100Hz 〜 5kHz の間が見事に破線で示した「IEC 60318-7」の許容範囲に周波数特性が収まっている。
  • また、5kHz 以上も良い感じに成っている。ガ、何ということでしょう「 EarPods の外耳道共振周波数が 2.5kHz 近く」になってるぞ ! ! ! 。ハテ ハテ ハテ・・・ハテナです。ただ考えられるのは、EarPods からの音が「一度耳介に反射して外耳道に入る構造」であることや、「外耳道と EarPods の間に隙間がある」ことなどで「外耳道が延長されるような現象が発生している」可能性もある。結果、これらの影響で「外耳道共振周波数が低くなった」とも考えられます。
  • 下方の青色の曲線で示すのは「インピーダンス曲線」で、「水平な直線に近い特性」となっていることから「大きな共振構造は無い」と思われます。
  • 次に示すのは、国内のスタジオ向けのヘッドフォンとして有名な SONY 社のヘッドホンの特性です。
  • ただ、残念なのが「著者所有のものが 1985 年頃から発売された「 MDR-CD900 」であり、 比較のために引用したデーターが 1989 年から後継機として発売された「MDR-CD900ST 」であることです。
    ■ 図-L26. MDR-CD900 の周波数特性 (著者による測定)
    fig-l26.png ■ 図-L26は、密閉型の標準的ヘッドフォンとして選んだ、著者所有品の周波数特性です。赤の曲線は、CD 盤を流用してヘッドパッドに密着させました。さらに、CD盤 中央の穴にビニールチューブで穴とのサイズ合わせをしたマイクを挿入して「密閉した状態」を模して測定した周波数特性です。緑の曲線は、ヘッドパッの中央にマイクを近接配置したオープンな状態の周波数特性です。
  • 「密閉した状態(赤色線)」での周波数特性では、多くの暴れが見られるのは硬いCD盤による反射やヘッドパットへの密着不足が影響しているものと感じています。でも、下記の図-L27 の周波数特性と比べると 1kHz から減少傾向が始まっていることや 10kHzあたりにピークがあるなど共通点も見られる。
  • オープンな状態の周波数特性である緑の曲線では、500Hz 以下がグラフの範囲外に低下して表示が無くなっている。これは、内部のスピーカーユニットの背面からの音がヘッドパットのスポンジ部分を通過してマイクの側に放出される構造であることから、打ち消しが発生していると想像されます。 4kHz 以上は、下の図-G27 の周波数特性と傾向が似ている。なお、構造の詳細は下記の「T-3. EarPodsの構造を確認」で確認して下さい。
  • 下図に示すMDR-CD900ST のデーターは、「ヘッドホン・イヤホン 周波数特性 測定結果 CD試聴記 (ブログかな ? )」から引用しました。 < 参考資料-L23 >
    ■ 図-L27. 参考資料-L23 からのMDR-CD900STの周波数特性 (著者の合成と加工あり)
    fig-l27.png ■ 図-L27では、参考資料-L23に測定方法を「簡易的に IEC60318-1 規格の人工耳に似せたアダプター(カプラー)を使って測定しています」と「測定環境」の項に記載があり、前記の図-L26 での著者の家内工作による測定よりは十分に信頼出来る測定結果と思います。赤色の曲線が周波数特性で、青色の曲線がインピーダンス特性を示しています。
  • 赤色の周波数特性は、1kHz 〜 4kHz の落ち込みが気になる特性と成っています。また、青色のインピーダンス特性では、75Hz に低域補正の共振があり、「随分共振で稼いでいるな〜」が感想です。 外耳道共振の位置( 3.2kHz ) にも大きな共振があるが、周波数特性ではディプ(凹み)に成っていて ? です。グラフでの特性の良し悪しの判断はともかく、聴いて感じていた音の特性が現れているのかな ? と感じました。
  • [ L-2. 項の位置付け ] : 提示した特性グラフと分析は「あくまで、個人ベースによるもの」で他意はありません。Web検索すると「多数の測定データーと試聴批評」を見つけられますのでご確認頂いた上で、「各自によるご判断」をお願いします。

M. イヤホンをスピーカーから学ぶ

M-1. スピーカーBOXの構造と動作

  • ここでは、低音再生とスピーカーBOX(エンクロージャー)に注目します。
    ■ 図-M11. スピーカーの断面図で構造と動作を考察 (著者作成)
    fig-m11.png ■ 図-M11は、スピーカーボックスの低音部分の構造を示しています。
  • スピーカーで難しいのが、低音部分です。その原因は、スピーカーユニットの前面に出るのと同じ音波が「+−の位相が逆な音波」が背面にも出ることです。そして、低音では波長が長い( 100Hz = 3.4m )ため前面の音と背面から出る音が逆位相(同じような距離)であれば打ち消しあう現象が発生して聞こえなくなるためです。だからと言って、背面の音を遮断するために小さなBOXに入れるとスピーカーが自由に振動出来なくなる症状が発生します。
  • この問題を解決すべく先人達が工夫したB0Xの一部が、前記の図-M11 です。
  • 図-M11の(1),(2),(3)は、背面に出る音を大きなサイズの平板や箱状構造にして背面に音波が抜けて前面に回り込まない(前面で音が混ざらない)ように考えられたものです。
  • (4)は、密閉型と呼ばれるものですが、中高音用で使用されますが低音では使われません。ただし、リスニングルームの後ろの部屋をスピーカーBOXとして使うというスケールなら O K です。
  • (5)と(6)は、共振現象を使って「位相を反転させる」ことで前面に音を返すタイプのものです。
    (5)の共振ダクト型に付いては、M3 の項で説明する「バスレフスピーカー」或いは「位相反転型」と呼ばれ、現在市販されている小型スピーカーのほとんどがこの形式です。
    (6)は、共振ダクトの代わりにスビーカーと同じサイズのドロンコーンと呼ばれる、スピーカーとしての駆動に必要な「コイルと磁石」が無く、振動板に重りのような物を取り付けたもので、共振現象を発生させています。
  • (7)と(8)は、ホーンと呼ばれる形状でロード(負荷)を掛けて制御しているものです。(7)ではスピーカーの前面に、(8)では背面に配置しています。

M-2. スピーカーボックスを歴代の有名な機種で分類

  • 次に示すのは、スピーカの外観からBOXのタイプを確認します
    ■ 図-M21. スピーカの外観でタイプを確認 (著者がWebで収集)
    fig-m21.png ■ 図-M21は、スピーカーボックスの外観で、歴代の有名な機種を大まかなに分類したものです。なお、実際の大きさ(高さ)は「(1)は 815mm、(2)は 2,760mm、(3)は 1,067mm、(4)は 1,051mm、(5)と(6)が400mm 」程度です。
  • 図-M21の(1)は、ドイツのシーメンス社製「オイロダイン(劇場用,1945年〜)」という機種です。劇場用とあるように、劇場のプロセミアム(ステージ両サイドの柱)や天井、映画スクリーンの裏に前述の平面バッフルの要素(背面に出た音を前面に回り込ませない)で使う機種です。そのため、スピーカーにBOXは無くてフレーム構造に成っており、建物の開口に固定して使われました。
  • (2)と(3)は、米国のアルテック社製の「(2)が A2 と (3)が A7」でフロントショートホーンを使った機種です。著者が大学1年生の時に東京で見た「エルビス オン ステージ」という映画で写っていたのを覚えています。その後、日本で現物に触って聴く機会もありました。A7 などのシリーズは、暫くステージ用 PAスピーカーとして君臨した時代がありました。
  • 著者が(2)と(3)で注目していたのは、このタイプではウーハーの背面音が前面下方の解放部から放出される構造であることです。これにより、前面音はホーンにより遠くに放射して、背面音はスピーカーの近くで分散することで打ち消しを減らすことを狙った構造ではないかと思慮しています。著者の記憶では、高音と同じように低音もキレがよかったと記憶しています。ただし、これらのスピーカーを聴くには「ひろ〜い空間が必要」で、日本家屋では本領を発揮で出来ません。
  • (4)は、JBL社製の「Model 4343」(4ウェイ,4スピーカー,バスレフ型)です。この機種には、2つの丸い穴の共振ダクトが設置してあります。録音スタジオのモニターや熱心なマニアに愛用され、一時代を築きました。現在もアレンジされた機種がスタジオのモニターとして各社から発売になっています。
  • (5)は Fender 社製のギターアンプと(6)が同ベースアンプです。ご存知だろうか ? ギターアンプは「後面解放型」で、ベースアンプは「共振ダクト型」であることを? 。理由は簡単で、ギターは低域がそれほど低くなく、BOXの背面の1/3程度を解放することで音のキレがよくなるように設計され、ベースアンプは「背面を塞いた共振ダクト型」を用いることで、帯域をより低くまで広げてベースらしく聞こえるようにデザインされています。
  • (7)は、近年PAスピーカーの主流となっているタイプです。特徴は、少し小さいサイズのスピーカーを一列に並べた形でBOXに納めた物を、一列に並べているのが特徴です。昔は、16〜20cmのフルレンジスピーカーを縦に6〜8個並べて細長いBOXに入れた「シュアー社やビンソン社(ウル覚え)」のものが有名で、「トーンゾイレ or ラインアレイ」と呼んで有名でしたが、そこらへんから発展したものと思います。
  • 図-M21の(1)〜(6)は、著者が二十代の頃に「触り、生音を聴いた」ことがあるもので、「大切な思い出 & 自慢」です。また、(7)のタイプも聴いたことはあるのですが、中高音は(1)〜(3)のホーン型が持つ「すっ飛んでくる」ような鳴り方が好きです。
  • ザッとの説明になりました。詳しくは、Wikipediaの「スピーカー」の項などでご確認ください。 < 参考資料-M21 >

M-3. スピーカーが持つ共振現象と音波の動き

  • 下図に示すのは、共振ダクト型の動作原理を簡単に説明した物です。
    ■ 図-M31. 共振ダクト型スピーカーの原理を考察 (著者作成)
    fig-m31.png ■ 図-M31は、一般的なダイナミックスピーカーと共振ダクト型BOXによる共振の動作を説明した図です。なお、記載のグラフのX軸が周波数で、Y軸が音の大きさや位相、インピーダンスを示しています。
  • 図-M31の(1)は、ダイナミックスピーカーの断面図です。磁石と磁気回路によって形成されたすき間に振動板(コーン紙)と一体となったボーイスコイルを挿入し、コイルに流す電流によって振動板を動かして、音に変換しています。
  • 結果、振動板の前面と後面で「赤と青で示した波形」で示すように逆位相( ± が逆)の音波が生成されます
  • (2)は、共振ダクト型スピーカーBOXにスピーカーを設置した時のスピーカー(緑線の曲線)とダクトからの音(橙色の曲線)で各々の周波数特性を示しています。Bの橙色の曲線では、共振ダクトの共振によって出来た狭い範囲の音が出力されていることが解ります。
  • (3)では、振動板と共振ダクトからの音が空気中で合成されて、赤色の曲線と特性となり、少し低域が広がっています。
  • (4)は、(3)の現象が起きた時の音の「位相とボーイスコイルのインピーダンス」の特性を示しています。
  • これらの事から、共振ダクト型スピーカーでは「低域を少し広くする利点」と「共振周波数近辺で位相が乱れる」という2つの現象が発生していることが解ります。
  • 次の図では、スピーカーの共振と抱える負の部分を考察します。
    ■ 図-M32. 共振現象の考察 (著者作成と参考資料-H21より合成と加工あり)
    fig-M32.png ■ 図-M32は、共振現象の気になる部分を図示したものです。(2)では、L = 100mH, C = 10μF で固定し、R と Q (Q は共振特性の鋭さを表す) の関係を示しています。
     ▪️ R = 10 Ω → Q = 10 → 赤色線
     ▪️ R = 20 Ω → Q = 5 → 青色線
     ▪️ R = 50 Ω → Q = 2 → 緑色線
    なお、(2)は、「共振回路を詳しく解説している記事の図7.11」のグラフを引用・合成しました。  < 参考資料-M31/Page-6 >
    □ また、同グラフのY軸がアドミタンス(Admittance)で記載されていますが「インピーダンスとアドミタンスは逆数の関係」であり、Y軸の上方がインピーダンスが小さくなる表現に成っています。
    □ ちなみに、
     ▪️ L ⇒ 電磁誘導, inductor / インダクタ or コイル, Lenz の法則の"L" (周波数が高い方が通りにくい)
     ▪️ C ⇒ 静電容量, capacitance / コンデンサ (周波数が低い方が通りにくい)
     ▪️ R ⇒ 電気抵抗, resistance / 抵抗 (周波数に影響されない)
     ▪️ E ⇒ 電圧, electromotive force (電流を流そうとする力の強さ)
     ▪️ I ⇒ 電流, intensity of electric current (電流の強さ)
     ▪️ Q ⇒ Q値, Quality factor / 品質係数 (周波数帯域幅の逆数)
  • 図-M32の(1)は、共振ダクト型スピーカーの共振動作を分析・説明する為に電気回路に置き換えた物です。
  • 共振の要素は、スピーカーユニットの振動板と一体となったダンパーやエッジなどの可動部分による「スピーカーユニットの等価回路」と共振ダクトに起因する「共振ダクトの等価回路」があり「 L, C, R 」による電子回路の共振で表現されています。ただし、等価回路はいろんな表現方法がありますが、示したのは最も簡易なものです。また、図では回路が相互に影響しあいます。
  • (2)は、「 L, C, R 」の要素により構成された直列共振回路で、「 R の値」のみを変えることで「 Q 」を変更した時に得られる周波数特性の変化を示したものです。
  • (3)は、スピーカーユニット自身や共振ダクトが持つ共振が抱える負の部分を強調して示しています。それは、青色で示した入力信号が共振によって、赤色で示すように「レベルが大きく」なるだけではなく「振動時間」も長くしてしまう可能性があり、結果「キレの悪い再生音」に変えてしまう可能性があることを表現しています。
  • そもそも、楽器や声は、「共振(共鳴)を利用して音を大きく(波高を高くする)し、響かせる(時間軸を長くする)のが原理の音源である」と思います。しかし、オーディオ機器でのそれは「追加や誇張」として働く場合があると考えます。もちろん、周波数特性を補正する役割や環境(ミキシングルームと反射や残響時間が極端に異なる無響室での再生等)などによる物理的理由で不足する場合や個人的趣向で必要な場合も考えられるのではあるが・・・。

N. スピーカーのダンピングファクターを学ぶ

N-1. ダンピングファクターと低音再生

  • ここでは、スピーカー にパワーアンプを接続した時に「アンプのスピーカーに対する制動力」の性能を表す「ダンピングファクター(DF)」という数値 (0.1〜100以上 程度) があります。
     ♦ 計算式は「DF = (スピーカーのインピーダンス) ÷ (アンプの出力インピーダンス)」です。
    (なお、スピーカーのインピーダンスには「公称インピーダンス」を使用)
  • 理屈としては、「DF が大きいほど定電圧駆動に近づき、逆に DF が小さいほど定電流駆動に近づく」です。
  • 実際には、次の考え方で大別されます。
    (a) 真空管アンプ → 出力インピーダンスが大きい → DFが1桁程度と小さい → 豊かな低音。
    ⚫︎ 例 ; 300Bシングルアンプ(三極真空管,NF無し)「ダンピングファクター = 3.0, 全高調波歪率 1kHz1W時 0.4%以下, 再生周波数帯域(-3dB) 5Hz~22KHz」。
    (b) トランジスターアンプ → 出力インピーダンスが小さい → DFが2桁以上と大きい → 切れのいい低音。
    ⚫︎ 例 ; 日本メーカー製トランジスターアンプ「8Ω 110 W+110 W, 20 –20 kHz, THD+N= 0.1 %, Damping Factor 350 (RL=8 Ω, 1 kHz)」。
  • もちろん、例外や「トランジスターアンプとスピーカーの間に抵抗を挿入してDFを小さくする」などの細工をする場合も有ります。ただし、メーカーがスピーカーを開発(デザイン,調整)した当時のアンプの DF データーも考慮する必要があると思います。

N-2. ダンピングファクターと等価回路

  • 以下に、DF を理解する為の図と等価回路を示す。
    ■ 図-N21. ダンピングファクターの定義と等価回路(著者作成)
    fig-n21.png ■ 図-N21で、(1)はDFの定義を示す図と等価回路です。(2)はスピーカーケーブルの等価回路です。
  • 図-N21の(1)では、パワーアンプの内部出力インピーダンス(Zamp)とスピーカーのインピーダンス(Zsp)とによって、ダンピングファクター(DF)を求める考え方を示しています。
  • また、(1)と(2)にはスピーカーとスピーカーケーブルの簡略化した等価回路を示しています。ただし、日本サイズの家屋で使うスピーカーケーブルは、「無駄に長くない、線間容量( C ) が少ない、細い銅線を束ねたもの(撚り線)」であれば神経質になる必要はないと著者は考えています。

N-3. ダンピングファクターと周波数特性

  • 次に、「ダンピングファクターと周波数特性の関係」に付いて、図-N31の(1)に「スピーカーの物理学 III ダイナミックスピーカーの諸特性 (2014-07-28)」より引用したグラフ。  < 参考資料-N31/Page-6 >
  • さらに、図-N31の(2)に「音はダンピングファクターで変わる (創造の館 音楽苦楽部 2018-01-22 更新)」より引用したグラフを用いて説明を加えます。 < 参考資料-N32 >
    ■ 図-N31. ダンピングファクターと周波数特性(引用グラフと著者加工あり)
    fig-n31.png ■ 図-N31で、(1)は、共振ダクト(バスレフ)型2ウエースピーカー(詳細は記載なし)を駆動するアンプの出力インピーダンスを変える事で、ダンピングファクターを変化させた場合の音圧周波数特性です。なお、DFは、上からDF=0.1, 0.5, 1, 5, 100 であり、60Hz 前後に大きく特徴があり、200Hz 以上にも「2-WayNetwork」の影響も含めた特性の変化が現れています。なお、「Network (クロスオーバーネットワーク, 音域分割回路)」とは「L, C, R 」による周波数帯域を分岐する回路のことです。
    □ (2)の左側は、密閉型3ウエースピーカー(詳細は記載なし)を駆動するアンプの出力インピーダンスを変える事で、ダンピングファクターを変化させた場合の音圧周波数特性です。なお、DFは、上からDF= 0 (十分に小さいと解釈した), 0.1, 0.3, 1, 3, 10, 無限大(十分に大きいと解釈した)であり、40Hz, 500Hz, 5kHz 前後に「3-WayNetwork」の影響も含めた変化が現れています。
    □ (2)の右側は、密閉型3ウエースピーカーのインピーダンス特性です。
  • (1)の共振ダクト型2ウエースピーカーでのダンピングファクター(DF)を変化させた場合の音圧周波数特性グラフからは、「DFが小さいと低域が盛り上がり、大きいと低域が抑えられる」ことが見て取れます。さらにグラフからは、低域部分に関しては「DF = 5 」辺りが最適値(調整されている)と思われます。さらに、DFの大小は 200Hz 以上の周波数帯域にも影響を与えていることがわかりますが、残念なことにグラフが不鮮明なため詳細は読み取れません。でも、「 300Hz〜5kHz 」の違いは聴き分けることが出来る範囲の変化と思われます。
  • (2)の左側グラフでは、(1)と同様の特徴が読み取れ、「DF = 3 」辺りが最適値(調整されている)と思われる。(2)の右側グラフからは、左側と同じ周波数にピークがあることが確認出来る。
  • 図-N31 からは、「Network」を使ったスピーカーでは、DFは「低域のみならず高域の周波数特性にも影響を与える」ことがわかります。

N-4. 見つけたダンピングファクターの実験

  • 次に示すのは、「ダンピングファクター」を波形という視点から測定をした面白い実験データーです。
  • 引用したのは、「ダンピングファクタって、実際の所どうよ? (2009-06-13, kyonkyon's blog)」からです。 < 参考資料-N41 >
    ■ 図-N41. ダンピングファクターを波形で分析 (著者の合成と加工あり)
    fig-n41.png ■ 図-N41で使われているスピーカーユニットは、「2226H 」と記されてあります。
     ▪️JBL-2226Hの主な規格は、「口径:380mm, 公称インピーダンス : 8Ω, 許容入力:600W, 推奨最高クロスオーバー周波数 : 1.2kHz, fs(自由空間におけるドライバの共振周波数) : 40Hz、ボイスコイルの直流抵抗 : 5.0Ω」(著者調べ)です。
     ▪️記事にはスピーカーボックスの詳細は記載がありません。
     ▪️アンプ(BTL-AMP)は自作らしいのですが、「DF= 38 ( 10~10kHz ) が判明」していて、逆算で「出力インピーダンスは 0.21Ω」とのことです。
    □ 図-N41の(1)は、測定に使った入力信号の波形を示しています。なお、「31.5Hz 間欠サイン波(2波ON、24波OFF)を使う」との記載があります。
    □ (2)は、測定系のブロック図を示しています。ただし、測定に BTL-AMP(位相が逆な2つのアンプで一本のスピーカーを鳴らす方式) を使っていますが、このブロック図ではシングル出力のAMPに置き換えました。
    □ (3), (4)では、資料から DF が「0.4, 3.3, 38」の3本の曲線を選び、Zampを下記計算式で求めました。
     ♦ Zamp = { Ramp ( Ampの出力インピーダンス = 0.21Ω) + Rdf (ダンピングファクター調整用抵抗 ) }
  • (3)では、「スピーカーに入力される波形」です。このグラフからは「波形の立ち上がりと立ち下がりに多少の乱れが見られますが、3本の曲線間に大きな違いは見られません。
  • (4)では、「スピーカーからの音をマイクでピックアップした波形」です。著者が注目したのがこの曲線です。
  • グラフ上に著者が追記した「(3)側を基準にした 31.5Hz の時間軸を示す緑の点線」で(4)の曲線を確認すると、「2.5波形(周波数が高い)」であり、それぞれの波長も異なっています。単純に言えば、歪んでいる。
  • しかし、図-N41の実験の音源は「0Vでスタートし、0Vで終わる 31.5Hz (サイン波)のたった2波形 (2 ÷ 31.5Hz = 0.064秒)」のとても短い信号を用いていることに疑問はあります。マァー、スピーカーを引っ叩いて動きを見てみたと考えるのがいいかも ? 。
  • でも、これはこれで興味深い結果(データー)を提供してくれていると思います。願わくば、「スピーカーの電流波形」も見たかったナァ 〜 〜 〜 ! ! !スピーカーの振動板は、電圧ではなく電流の作用で動いています。

N-5. 分析と考察への補助資料

  • 次の図-N51は、著者の分析と考察への補助資料として用意しました。
    ■ 図-N51. スピーカーの特性と構成要素 (著者の合成と加工あり)
    fig-n51.png ■ 図-N51の(1)は、「2226 Specifications」と題されたJBLが発行した思われる資料からの引用です。ただし、2000Hz 以下の周波数範囲のみを記載しました。その他、次の説明があり要約を記します。
     ▪️低域の実線は、280リットルの密閉エンクロージャーでの周波数特性。
     ▪️低域の Enclosure (破線)は、40Hzに調整された開口付 140リットルのエンクロージャーでの周波数特性。
     ▪️高域の On Axis (実線)は、スピーカーの中心軸上の周波数特性。
     ▪️高域の Off Axis (破線)は、スピーカーの中心軸から45度ずれた位置での周波数特性。
     ▪️Impedance 特性(青色線)は、ユニットのみ(エンクロージャー無し)の測定で、共振周波数は、約 40Hz です。
    □ (2)の左は、「スピーカー全体の断面図」で、(6)の右は「中央部分の拡大図」で構造を示しています。
    青色で示すのが「振動を空気に伝える」役割の「振動板とセンターキャップ、ボビン、ボーイスコイル」で、振動板に接着されて一体になっています。
     ▪️赤色で示すのが「エッジとダンパ」で「振動板とフレームとをアイソレーションしつつ、スムースな振動のサポートと、信号がない時には所定の位置で静止させる」役割があります。
     ▪️緑色で示すのは、「フレーム」で「スピーカーの構造を構成するかなめ」です。
     ▪️黒色で示すのが「磁気を出す磁石」で、その磁気は「灰色で示す磁気回路(磁気を通す道)」によって誘導して、ボーイスコイルがある磁気回路のギャップ部分に磁石の磁気を集中させています。
     ▪️橙色で示す「ボーイスコイル」は、パワーアンプからの「電流と磁気によるフレミングの法則」に従い振動板を振動させて、空気振動(音)に変換しています。

N-6. 著者の浅知恵で分析と考察

  • 以下は、著者の浅知恵での分析と能書き(考察)です。
  • (a) 図-N41 の(4)の曲線を確認すると、「2.5波形(周波数が高い)」ことからは、
     ♦「 ( 2.5 ÷ 2 × 31.5Hz ) = 39.375 ≒ 40Hz = fs (自由空間で 2226H の共振周波数 ≒ 40Hz)」
    という不思議な計算が成り立つ。これは、スピーカーの共振周波数に引き込まれる現象なのだろうか ? 。
    ⚫︎ ちょっと残念なのが、記事に使ったエンクロージャーを含むスピーカーのデーターが無いことです。追加資料として、図-N51の(5)に著者が見つけたスピーカーの特性グラフを提示しました。
    (b) 図-M32の(1)や図-N21の(1)で示したスピーカーの等価回路でのユニットの抵抗がメーカー仕様書で「ボイスコイルの直流抵抗 : 5.0Ω」となっおり、アンプの出力インピーダンスとに相当する Zamp は、ボイスコイルの直流抵抗に直列接続となり、結果的に加算されることを考慮すると、
     ♦ DF = 38 → 0.21Ω + 5Ω = 5.21Ω → 青線
     ♦ DF = 3.3 → 2.41Ω + 5Ω = 7.21Ω → 赤線
     ♦ DF = 0.4 → 20.0Ω + 5Ω = 25.0Ω → 黒線
    となり、このことからグラフを見直すと、
    ⚫︎ 図-N41の(3),(4)の曲線が「青線と赤線が近接し、黒線が少し離れている」のが見て取れます。
    ⚫︎ 同様に、31.5Hz の2波形が終了した後の収束する部分の波形でも同様な傾向が見られます。
    ⚫︎ ただし、DF を大きくしたからといって、収束波形がすぐに収まるようには見えない。
    ⚫︎ しかし、この違いを「聴覚は聴き分けることが出来る」のかな ? 。
    (c) 図-N41の(3)や(4)に現れた波形の乱れは、「スピーカーが発する逆起電力が主因で起っている現象」だと考えられています。
    ⚫︎ まず、「磁気の中に電線がある」と、止まっていたものが動く or 動いていたものが止まる or 動く方向やスピードが変わる時「電線に電流(オープン回路では電圧)」が発生します。これは、「ダイナミックマイクや発電機(動き→電気)」と「スピーカーやモーター(電気→動き)」に共通する原理です。
    ⚫︎ この時スピーカなどでは、外部要因で発生した電流はアンプから印加した電流に加算もしくは減算されます。結果、図-N41の(3)や(4)に見られる波形が乱れる現象として現れます。
    (d) また、図-N31での(2)の右側のインピーダンス特性に変動がある時や「L, C」成分がある時にも位相変化に伴う乱れが発生します。前記した図-N21の(1)の「スピーカーの等価回路」や図-N21の(2)に示す単純な電線にさえ「L, C, R」(値は小さい) 成分を内包しています。
    [ 考察のまとめ ] ダンピングファクター(DF) は、3 〜 30 程度であれば良いのではないかな ? 。ただし、クロスオーバーネットワークを使ったスピーカーでは DF によって中高音の周波数特性にも影響が出るので、測定によって決める方法やメーカーの推奨する値を選択する必要があるように思います。少なくとも、「 DF が大きければ良い」は自分で測定しながらチューンナップした場合以外は間違いかな ? 。なお、スピーカーの公称インピーダンを 8 Ω とすると「アンプの内部出力インピーダンス」は「 2.7 Ω (DF=3) 〜 0.27 Ω (DF=30)」になります。ただし、使う抵抗は「巻線抵抗は L 分が含まれる」場合がありますので、「無誘導巻きの抵抗」が適していると考えます。

O. ダイナミックスピーカーの動作原理を確認

  • 次にダイナミックスピーカーの駆動部である「磁気と電流」について考察します。
    ■ 図-O01. スピーカーにおける磁気と電流を考える(著者作成)
    fig-o01.png ■ 図-O01の(1)は、棒磁石に砂鉄を振りかた「磁力線」の写真です。幼き頃に、教科書で見たり実験して遊びましたよね。著者がWeb検索で見つけたものを記載しました。(2)と(3)は、「磁界と電線により構成された平面イメージを用いて磁界中の電線に流れる電流と電線の動き」を説明するために著者が用意したものです。あくまでも、説明のためのイメージです。
    □ 以下では、永久磁石のように「磁気を保持する硬質磁性材」を磁石、「コアや鉄心(軟鉄)、磁気回路」で使う「磁気は通すが保持しない軟質磁性材」を磁気回路材と表記します。
  • 図-O01の(1)では、「磁力線(磁束)」が綺麗に並んであたかも固定されているように見えますが、ここに他の磁石や磁気回路材を近づけると磁力線が簡単に変形してしまいます。
  • 著者が社会人に成り立ての頃に、スピーカーの磁石で遊んでいるとアルニコ磁石とフェライト磁石に違いがあることに気が付きました。アルニコ磁石の磁力が出ている面を小さいドライバーでなぞると両端以外はスムースに移動するのに対し、フェライト磁石ではぎこちなく引っかかりながら移動したのです。そこで、フェライト磁石に磁気回路材を乗せて同じことをするとスムースに移動するようになりました。当時著者は、フェライト磁石の「磁束に根っこが生えている」と感じたものでした。
  • 当時著者は、「永久磁石から磁気回路材で磁気を導いた磁界」の中で、ボーイスコイルの電線に流れる電流によって振動が正確(線形)に動作するためには「固定された磁界」の方が良いのではと考えました。もっとも、2つの磁力がどのくらいの力関係にあるのかも知らないで想像しているだけだし、ボーイスコイルが動くための「空間(隙間)」が必要であり、程度の差になってしまうのですが・・・。
  • (2)では、磁界の中に電線(銅線)を宙に浮いているような状態で電流を流した時の「電流と電線の移動」をイメージで示しています。目的は、勿論「電流と電線の移動」がリンク(相似)しているかを確認するためです。残念ながら、赤色で示す電流では赤矢印の方向にしか動かず、青で示す電流も同様に青矢印の方向でしか動きません。すなわち、電線の移動は電流波形とはリンク出来ません。
  • (3)では、(2)にバネを追加して電線が常に磁界のセンター方向に力が加わるようにしました。これだと、「電線の電流により発生する力とバネのセンター方向に加わる力との力関係(バランス)で電線が移動する」ために、「電線が電流波形とリンクして動く」との考えを図示しています。
  • ダイナミックスピーカーには、このバネに相当するものが2つあります。一方は、「エッジとダンパ」であり、他方は「振動板とセンターキャップ」です。特に気になるのが、センターキャップの内側の閉空間です。なお、大きなサイズのウーハーには、開口があるものもあり、閉空間が出来るのを回避するためと思われます。
  • 「エッジとダンパ」の役目は、主として「振動板とフレームとをアイソレーションしつつ、スムースな振動のサポートと、信号がない時には所定の位置で静止させる」役割ですが、低い周波数であればバネの役割を果たすとは思ますが高音域では無理なように思えます。
  • 「振動板とセンターキャップ」は、動くと「前方の空気が気圧が高くなり、後方の空気が薄くなる」ことで「空気によって押し戻す力」が発生すると考えられます。それは、「傘をとじたままで長手方向に動かす時には小さい力で済むが、傘を開いて長手方向に動かすには結構大きな力が必要となる」ことでも分かると思います。空気を感じる瞬間です。だから、「エッジ」のほとんどを切り取ったスピーカーでも音は出ます。しかし、エッジや振動板、センターキャップには前後に出る逆位相の音波をアイソレーションする役割も有ります。

P. スピーカーの振幅 ⇔ 音圧 ⇔ 気圧の関係を物理量で確認

P-1. 音圧とスピーカーの振幅の関係を確認

  • 次は、スピーカーの振動板は実際にどのくらい動くのかに付いてです。すでに「参考資料-H31」として紹介した「スピーカーの物理学 III ダイナミックスピーカーの諸特性 (2014-07-28)」の「 4 ページの表 III-1」にリストで記載がありました。 < 参考資料-P11/Page-4 >
  • そこで、記載されたリストをグラフにして確認してみます。
  • ■ 図-P11. ダイナミックスピーカーの振動幅 (引用資料より著者作成)
    fig-p11.png ■ 図-P11で引用した参考資料では、
     ▪️スピーカーの中心軸上 1m での音圧を「80, 100 dB (SPL)」の2値で記載があります。
     ▪️周波数は、「30, 60, 100 Hz」で記載があります。
     ▪️スピーカーの口径は、「20, 25, 30, 38 cm 」の4種類に付いて記載があります。
    □ グラフでは、
     ▪️(1)では「周波数と最大振幅」を振幅幅の大きい 20 cm 口径の数値を取り上げました。
     ▪️(2)では「スピーカーの口径と最大振幅」を振幅幅の大きい 30 Hz の信号の数値を取り上げました。
     ▪️なお、最大振幅(Y軸)は対数目盛にしました。
  • 図-P11の(1)の範囲で確認出来るのは、同じ口径のスピーカーでは、「音圧が大きい方が振幅も大きい」や「同じ音圧なら、周波数が低い方が振幅は大きい」などが読み取れます。
  • (2)の範囲で確認出来るのは、同じ周波数では、「同じ音圧を出すのであれば、口径が小さい方が振幅は大きい」などが読み取れます。
  • ただし、(1)と(2)の曲線が直線的に見えますが、引用資料に計算式の記載がありますのでご確認下さい。
  • また、もう少し高い周波数からは振動板の分割振動などの、低域とは異なる振動領域に入ります。
  • [ 参 考 ] 引用資料では、
     ♦ 「音圧:100dB、口径:20cm、振動数:30Hz → 最大振幅 = 13.3 mm」
     ♦ 「音圧:80dB、口径:20cm、振動数:30Hz → 最大振幅 = 1.33 mm」
     ♦ 「音圧:100dB、口径:38cm、振動数:30Hz → 最大振幅 = 3.68 mm」です。
  • [ 教 訓 ] スピーカーは、床や台に振動が伝わらないようにアイソレーションして、振動板の振動力に負けないようにBOXを重量物にしっかり固定しましょう。逆に、ゴムボールなどで浮かせる考え方も有りますが・・・。

P-2. 音圧と気圧の関係を確認

  • さらに、人間が聞く事が出来る音圧と気圧との物理量としての関係を調べてみました。
    ■ 図-P21. 音圧と気圧とを物理量で比較 (著者作成)
    fig-p21.png ■ 図-P21の(1)は、音圧と気圧を共通単位である「Pa(パスカル)」を用いて比較するための途中計算を表にしたものです。とにかく、桁数が多いために手計算では、正誤のチェックもままならないために Excel で計算し、グラフにしました。ミスがあったらゴメン・・・。なお、基本数値は Wikipedia などを参考にしました。
    □ (2)は、dB単位で表す音圧値をX軸に、Pa単位で表す音圧の値をY軸(対数目盛)としたグラフです。
    □ (3)は、dB単位で表す音圧値をX軸に、Pa値の音圧値を1気圧のPa値で割り算して、その比を求めた値をY軸(対数目盛)としたグラフです。
  • 出て来た数字は、私たちの日常的な「音圧 60dB 」では大気圧に対して音波による気圧の変化は「0.0000002 倍 (5,000,000.分の1)」と微々たるものでした。しかし、「音圧 120dB 」では「0.0002 倍 (5,000.分の1)」と徐々に大気圧に近づいて来ます。正直、著者としてもこのグラフを作って見て「音の大きさ」とは何かを感じる事が出来たような気がします。

Q. 見える化してスピーカーの動作を確認

Q-1. 音波の強弱を見える化してスピーカーとボックスの動作を確認

  • 次に示すのは、音を可視化技術を使って見せてくれるものです。「音の可視化を利用したバスレフ型スピーカの振る舞いの検討 (時期不明、東京電機大学)」と題する研究論文から引用しました。 < 参考資料-Q11 >
  • ■ 図-Q11. スピーカの振る舞いを可視化技術で見る (著者の加工あり)
    fig-q11.png ■ 図-Q11の測定では、バスレフ型スピーカーには「 BOSE 101MM (232(W)×154(H)×152(D)mm, 6Ω, 45Wrms(IEC268-5), 11.5cmフルレンジドライバー×1) 」が使用されています。また、このスピーカーには「図-Q31 の (3)」に示すように「イコライザー回路」が内蔵されているのですが、その取り扱い(そのまま使用 or バイパス)に付いての記載がありませんが、著者は「図-H42の(3)の周波数特性からバイパスして測定している」と考えています。なお、測定方法については、説明が長く成りますので参考資料で御確認下さい。
  • 図-Q11の(1)は、200Hz におけるスピーカボックス内外の音圧を明暗(白色は音圧が高く、黒色に近づくに従い音圧が低下)で示している。図では、スピーカーの近傍とが同じ様に音圧が高いく、離れるに従い音圧が低くなっていく様子が確認出来ます。
  • (2)は、左から「70Hz, 120Hz, 200Hz」の信号を使い、「上の段がポートを閉じた場合、下の段が開いた状態」の時の音圧の違いを示しています。注目は、上段ではスピーカーの前の音圧が高い(白)部分の位置に大差はありませんが、下の段のポートが開いた状態では、「70Hz がポート側に白が集まり、120Hz はスピーカーとポートがほぼ等しく、200Hz ではスピーカー側に偏っています。
  • (2)に関して著者は、以下のように判断しました。
    (a) 70Hz では、ポートの影響が大きく働き、スピーカーからの音が小さく(灰色)てもポートからの音圧が高い状態(白色)になり、スピーカー前が黒っぽく見えている。なお、スピーカーからの音が小さくなった理由を「ヘルムホルツ共振が発生し振動板の動きが抑圧された」と論文に記載がある。
    (b) 120Hz では、ポートからの音がスピーカーとほぼ等しい音圧と位相の音が放射されているように見える。 (c) 200Hz では、ポートからの音がスピーカーとは異なり、音圧小さくと位相もずれた音が放射されているように見える。
  • (3)は、測定に使用されたスピーカーの外観です。

Q-2. 音波の波紋を見える化してスピーカーとボックスの動作を確認

  • 図-Q21は、前記と同じ 参考資料-Q11 にある別のデーターで共振ダクトの動作を確認します。
    ■ 図-Q21. 共振ダクトの動作を見える化して確認する (著者の加工あり)
    fig-q21.png ■ 図-Q21では、1470 Hzの信号を使い、(1)はポートを閉じて、(2)ではポートを開いて測定している。(3)は、このスピーカーの周波数特性で、実線で示したのがポートを開いた時、点線で示したのがポートを閉じた時の特性です。
  • 図-Q21で 1,470 Hz による測定であることを参考資料では、「このスピーカのダクトは長さ約10 cmの円筒である。この10 cmという長さは 1,470 Hzの波の半波長より少し短いぐらいであり、開口端補正を考慮すれば 1,470 Hz付近の成分がこの管で共鳴して強調されていることが考えられる。そして、強調された音がポートから再放射されると考えられる。」と記載されている。
  • さらに、「この時、振動板の中心とポート中心の距離は約 11 cm で、振動板から出た音がポートの前で位相がほぼ 反転する。その結果、ダクト内部では振動板から到 達した逆位相の音が共振し強調され、ポートから再 放射されることで、正負二重音源となり、振動板と ポートを結ぶ線の垂直二等分面で、1,470 Hz付近の 音が低下する、と考えられる。」と分析してある。
  • 図-Q21の(1)では、ポートが閉じているために同心状に広がり、SPの後方が少し音圧が低くなっている。まァ、想像通りですね。
  • (2)では、ポートが開いているために音の広がりが乱れており、特にポート側の半分が乱れているのはポートから放出される音波原因と判断出来る。
  • (3)が BOSE 101MM の周波数特性で、赤破線で示した「1,470Hz 付近に深い谷が観測され」ている事に気付き、図-Q21 の分析を行ったようです。

Q-3. BOSE 101MM の電気特性とナゾの電球

  • で、次に示すのは BOSE 101MM の一般的な特性データーを確認する過程で見つけたものです。
  • 資料は「BOSEの音はなぜガサツなのか!?~11.5cmフルレンジの秘密に迫る (2017-08-31, 創造の館 音楽苦楽部)」のタイトルがあるものです。 < 参考資料-Q31 >
    ■ 図-Q31. BOSE 101MM の電気特性を確認する (著者の加工あり)
    fig-q31.png ■ 図-Q31に示したのは、(1)がインピーダンス特性で「ピンク色線がノーマル(通常状態)、青色線がポートを閉鎖した場合、緑色線がユニットを取り出してユニット単品状態」での測定グラフです。(2)は、エンクロージャーに内蔵されているイコライザーの周波数特性で「青色線が出力にスピーカーユニットを負荷とした、赤色線が 3.9ohm の抵抗を負荷とした」とした場合の周波数特性です。(3)は、イコライザー基板の外観です。
  • 図-Q31の(1)では、「ノーマルやポート閉鎖」の特性は「アンプ出力とスピーカーユニットとの間にイコライザーが挿入されている」ために通常のインピーダンス特性の見方では判断出来ない。ただ、(1)の凸位置と(2)の凸位置が同じ周波数になっているから何らか共振が発生しているものと思われる。ただし、緑色線のユニット単品状態の測定では「11.5cmユニット裸のfoは約100Hz、インピーダンスは1.5Ω 」との記載があるが、これはこのまま受け取っていいと思います。
  • (2)では、赤色線の抵抗負荷の曲線からはイコライザーで 2kHz を -5dB させる特性で「中域を落として高域を高めることで聴感上のバランスをとっている」と記事では推測している。青色線のスピーカーを負荷とした場合では、(1)での指摘通り原因を特定出来ない。
  • 記事では、「イコライザー回路でユニークなのが電球の存在。ローパス回路の中に挿入されていて、入力レベルに比例して低音を抑制する仕組みになっている。同時に入力保護も兼ねる。」との記載があり、チョと調べてみました。

Q-4. BOSEスピーカー内蔵のユニークなイコライザー

  • そこで見つけたのが、「BOSEスピーカー内蔵のナゾの電球回路 (2013-11-30, ABCDEFG日記)」と題された記事です。 < 参考資料-Q41 >
  • その記事中に『この回路を「パッシブダイナミックイコライザー」という名称で特許申請が出されている』旨の記載があり、特許検索をしてみました。結果は、確かに2005年に出願されていましたが、調べた範囲では登録にはなっていませんでした。ちなみに、「パッシブダイナミックイコライザ ー」は日本語で「受動的に動作するイコライザー」です。
  • 特許出願文からキーとなる語句を拾ってみました。
    (a) 低域のみをラウドネス補正する。
    (b) ランプは電流に比例して発熱し、その抵抗値が大きくなる。(著者追記 : 発熱による抵抗値の変化で低域の周波数特性を変えている)
  • 著者が特許出願したのが2012年(BOSEさんの 7 年後)で、内容は "私のApp" と同じ「ラウドネス補正」に関するものでした。しかし、特許出願の準備をしていた頃には、スミから隅まで類似特許がないか特許検索をしたつもりでしたがピックアップ出来ていませんでした。
  • この回路から、参考資料-H32 に記載されていた「ユニークなのが電球で、低音を抑制する仕組みと同時に入力保護も兼ねる」の指摘は正しかったことになります。原理を簡単に説明すると、「SPに入る電流(POWER)が大きくなる → SPに直列に挿入したランプが発熱 → 発熱によって抵抗値が大きくなる → SPの電流が減りPOWERが低下する」です。
  • なお、ランプはコイル(低い周波数を通し易い)と直列に接続されています。ただ、これだけでは「ラウドネス補正」を正確に行うのは難しいと思いますが、それなりの効果はあると思います。(著者の叫び ; ラウドネス補正はそんな簡単じやないぞ、でも沢山売れているね ! ! ! )
  • しかし、こういう手法は日本のメーカーさんは不得意なんですよね ! 。。著者は、米国の他社製品でもパワーオーバーによる破損を防ぐために同様のランプを使っているのを見た事があります。さらに、アンプとスピーカーが一体となった業務用音響機器メーカー製品では、「Amp出力のアース側で、スピーカーとの間に 0.1Ω ぐらいの抵抗を挿入して出力電流を検知し、クロスオーバー周波数を自動で変更したり、リミッターを働かせて、スピーカーの破損を防ぐ機能がある」ものも見ました。そういえば、「シュアーのボーカルマスターも電源トランスの容量を小さく抑えて、パワーオーバー時にアンプの電源電圧が低下することでスピーカーの破損が起きない工夫がしてあった」ような記憶が・・・(古い古い話です)。
  • 業務用機材は壊れないのが一番ですから、こういう工夫は著者はアリだと思います。でも、今記憶を辿った機器がみんな外国(米国)製品の話だったのはなぜかな ? 。「故障すると中を開けて、原因を確かめたくなるのが著者の悪い癖です」が、いい面もあるでしょ !

Q-5. 振動板を見える化して分割振動を確認

  • 次は、『古くから言われていたのに、意外に知られていない「スピーカーの分割振動」』を紹介します。
  • 説明の為に引用したのが、「スピーカーの物理学 III ダイナミックスピーカーの諸特性 (2014-07-28)」と題された記事です。下図では、振動板の動きをイラストで示しています。 < 参考資料-Q51/Page-7,8 >
    ■ 図-Q51. 分割振動を断面で確認する (著者の加工あり)
    fig-q51.PNG ■ 図-Q51は、(1)では、振動板の断面図を用いてボーイスコイルに電流が流れて移動する振動板の様子を示しています。図では、点線が振動板のホームポジションを表し、実線がコイルの電流により移動した振動板の位置を示しています。(2)での断面図は(1)と同様に振動板の位置を示し、円で示したのは振動板の正面から見た振動による変位を「"+"の部分が前面へ、"-"が背面への移動」としてイラストで示しています。なお、(a)では、「同心円 2 分割振動」というタイプの分割振動、(b)では、「同心円 3 分割振動」を示し、(c)では、「非同心円の分割振動」を示している。なお、参考資料の説明文と図面との間に差異があったため「"+"と"−"」の一部を変更(修正 ? )しました。
  • 図-Q51の(1)では、図では右に行くに従いコイルの電流が増加し、ボーイスコイルの移動量が徐々に増えて、変位は中心から周辺へ広がりながら移動ているのが見て取れます。しかし、移動量が増すに従って両端から少しづつ変形(移動量が同じではない)しているのが確認できます。
  • (2)では、主として振動板を面として捉えて分割振動を分析してある。もちろん、断面と表裏一体ですが「同心円分割振動」は理解できますが、「非同心円の分割振動」はイメージしにくい。スピーカー ユニットを垂直に設置した時に発生し易いなどの条件があるのだろうか ? 、難解です。
  • なお、分割振動が起きる原因は「振動板の材質や形状、サイズ」と共に「振動板を支えているダンパーやエッジ、センターキャップ、ボーイスコイル部分の構造、磁気回路、空気の粘性 (構成部位の名称は図-N51参照)」などによる影響もあり、研究も行われているようです。
  • そう言えば、平板スピーカーとか、ハニカムコーンなどの材料や形状に工夫を凝らした製品が、次々に出てきた時代も有りましたね !

Q-6. 振動板をプログラム解析して分割振動を確認

  • せめて、この現象を具体的に提示してくれるものはないかとWebでしつこく調べました。
  • 見つけたのは、「空気の粘性減衰がコーンスピーカの振動と音響特性に及ぼす影響に関する研究 (2017-08-31,埼玉工業大学 大学院)」と題した 120 ページの大論文です。 < 参考資料-Q61/Page-18〜21など >
  • 下図に示すのは、「空気の 粘性減衰を考慮した振動と音響の強連成解析プログラムを使った解析」による画像と思われます。なお、論文に掲載してある 12 個の中の 6 個を引用し掲載しました。
    ■ 図-Q61. 振動板(コーン紙)の変位を見える化して確認 (著者が選択/配置/加工)
    fig-q61.png ■ 図-Q61で提示の画像は、左側 4 個と右側 2 個とで「色による振動の変位量」が異なっています、詳細は参考資料で御確認下さい。また、振動にはピストン運動(一体として振動する)と分割振動(場所によって異なる振動をする)」の2種類ありますが、本文では総称して分割振動と表記しました。
  • 図-Q61では、「周波数が倍々に近くなる様にと、より特徴的な模様」を選択しました。なんとも「異次元で、綺麗な模様に成る」のでしょう。周波数が高くなるに従い模様(分割振動)が細分化されて行くのも分かります。
  • 図-Q61では単一の周波数を使っていますが、「音楽再生」の様に広い範囲に多数の異なった周波数の音が混じった音源を使ったら、どんな模様になるのかは想像も出来ないですね。
  • これが「電気信号がスピーカーで音に変換される仕組み」です。

Q-7. スピーカーの項まとめ

  • 以上のスピーカーの仕組みからは、スピーカーの再現力には限界があると考えられますが、人間の「曖昧さを含めると良く出来ている」と言えるのかな ? 。
  • スピーカーの将来を思うに、「固体が体積を変えることによって音波を生み出す振動板の発見や発明」がなされるまでは、よくも悪くも現状維持かな ? !
  • おそらく皆さんは、「スピーカーは、パワー・アンプによって思い通りに振動板を制御して動かし、空気の振動(音波)に変換している」と想像していたと思いますが、大間違いでしたね !
  • 聴覚の音が聴こえる仕組みと共に、スピーカーの動作も不思議の世界ですね ! ! !

R. MICの特性も見てみる

  • ついでと言っては何ですが、ここまで来たらこれも提示しておくべきだろうと、次の図にマイクの周波数特性を用意しました。SHURE社のサイトからの引用です。
    ■ 図-R01. ダイナミックマイクの特性 (著者の加工あり)
    fig-r01.png ■ 図-R01は、赤色線が「主としてボーカルに使うマイクの BETA 58 」で、緑色線が「主として楽器で使うマイクの SM57」で、いずれも PA 業界ではスタンダードな製品です。本当は、指向性も重要なファクターなのですが割愛させて頂きます。
  • で、図の周波数特性はチットもフラットではないでしょう。これは、フラットに出来ないのではなくて、このような特性にした方が「良く売れる」、失礼、「いい感じに(らしく)聴こえる」ようになるんですね。このようにいろんな音の加工をして「いい感じ」の音を創っています。
  • もちろん、フラットな特性のマイクも有り、測定やクラッシックの録音に使われています。

S. アンプの動作を確認

S-1. スピーカーを駆動するアンプの動作を確認

  • 今度は、スピーカーを駆動するアンプについて調べてみます。著者は、真空管アンプからアナログIC を使ったものまで多くのアンプ作りを経験して来ました。しかし、収集していた資料が手元に残していなかったのでWeb検索で見つけたものの中から一般的に知られている図を用います。
    ■ 図-S11. アンプの動作を調べる (Webで収集した資料に著者の加工あり)
    fig-s11.png ■ 図-S11の(1)は、三極真空管 ( 2A3 ) の電気特性です。(2)は、三極管を使った最もシンプルなパワーアンプの回路図の一例です。
    □ 真空管の原理は、G (グリッド)の電圧によって出力電流を制御し、P (プレート)が出力を担当し、K (カソード)は熱電子を放出して P に電流を与えているとともに G の基準電圧となっています。なお、示した三極管が直熱タイプのために、ヒーター(熱源)と K とを兼ねています。
    □ (3)は、トランジスターの電気特性です。(4)は、トランジスターを使った OP-AMP という 増幅用 IC の基本回路図です。赤色で示したトランジスターとその左側が増幅を担当し、緑色が出力の電流ブースターとして働きます。実際の回路は、もっと沢山のトランジスターが使われています。
    □ トランジスターの原理は、B (ベース)に入力される電流によって、出力となる C (コレクタ)の電流を制御し、B の基準電圧となる E (エミッタ)に B と C 両方の電流が流れます。
    □ (1)と(3)の両グラフ共に、X軸には「各素子が出力として使う回路に加える直流電圧」を目盛りとし、Y軸には「出力に流れる電流」が割り当ててあります。また、(1)の各曲線右端に記してある数字は K に対する G の電圧であり、(3)のそれは B から E に流れる電流の値です。
    □ (5)は、青色曲線で示すのが一般的な OP-AMP のオープン回路での周波数特性です。赤色曲線は、(6)の OP-AMP を使った回路で得られる10倍増幅( +20 dB )に設定した時の周波数特性です。
    □ (6)は、OP-AMP に外付けで3本の抵抗を使った単純な増幅回路図を示しています。増幅率は、RsとRfの抵抗値を図中に記した計算式で決まります。
  • 図-S11の(1)と(3)のグラフで注目するのは曲線の傾きの違いです。(1)の三極真空管での出力電流は、G の電圧でも変化するしX軸の E の電圧でも変化することが読み取れます。この特徴は、出力に入って来る信号に反応出来ることを意味し、前記した「300Bシングルアンプ(三極真空管,NF無し)「ダンピングファクター = 3.0」の「 DF = 3.0 (出力インピーダンス = 2.7 Ω)」に表れています。
  • 逆に、(3)では全ての曲線が水平に近いトランジスターでの出力電流は、C の電圧にはほとんど反応せずに B の電流でのみで出力電流を可変出来ることが読み取れます。ただし、(4)の緑色のトランジスターのような使い方をすると E の電圧変化が「 B - E 」間の電圧変化となり、結果 B の電流か変化して C の電流が変わることになりますが、トランジスターの非線形特性によって真空管のような結果は得られません。
  • なお、(1)の緑色の直線は、真空管が安全な動作範囲を超えずに最適な動作をするように設計した「ロードライン」と呼ばれるものです。真空管は、緑色の線上の B 点を中心に A〜C の線上を移動して動作します。また、(2)の出力トランスは真空管側とスピーカー側とを直流分は通さずに交流のみを通す動作とインピーダンスの変換をしてくれます。最近は、電源トランスしか目にしませんが交流的には大変面白い動作をしてくれます。昔は、トランジスターアンプにも使われていました。
  • (4)は、OP-AMP の基本回路図ですが、基本的にはほとんどのパワーアンプも規模の大小による違いが有りますが似たり寄ったりです。
  • (5)は、OP-AMP の青色で示す NF (NF: Negative Feedback, 負帰還)無しの周波数特性と増幅率では、使用出来ないために NF という奥の手を使って必要な帯域の周波数特性と増幅率を確保出来るように調整して使います。(5)の赤色曲線は、増幅率 10 倍( +20 dB )に設定した時の 100kHzまでフラットな周波数特性曲線を示しています。もちろん、真空管アンプでも NF を使うことが出来ます。特に、トランジスターに近い動作をする 5 極真空管などに使いますし、歪み率などのアンプの特性を劇的によくしてくれもします。
  • しかし、設計が悪いと NF による大きな落とし穴も待っています。それは、トランジスターアンプが台頭して来た頃にアンプがよく故障し、その原因がアンプの発振によるものが多かったのです。
  • 大きな電圧の出力信号を、入力感度の高い入力回路部分に接続してあることで、位相補正の設計ミスやトランジスターの発熱による熱でトランジスターの温度係数による特性の変化が影響し、発振を起こしていたのです。現在のメーカー製アンプでは、発生しない(未調査)とは思うのですが自作のものは注意して下さい。音が濁ったりフアフアして聴こえ、最悪電源が落ちます。
  • 自作のアンプであれば、容量負荷テストなどがありますが危険が伴いますので、トライする人は自分でよく調査してから行って下さい。
  • 他には、出力が特定レベルを超えると発振する「飛び付き発振」などがあります。これは、アースの引き回しや入力系と出力系配線の接近、電源関係のコンデンサー不良などの原因があります。
  • また、メーカー製のアンプは、改造やケースの蓋を開けた痕跡があるとメーカー保証と修理をしてくれない場合がありますのでご注意を ! ! !
  • 以上、スピーカーからアンプの項を読まれて「アンプが、自由にスピーカーを操っているとする考えが間違っていた」ことに気付いて頂けましたか。ただ、アンプによって音質を含めいろいろな変化があるのは事実ではありますが・・・。
  • チョット内容がマニアックに成ってしまいました。もし、あなたがアンプを選ぶ機会があったら、極端にデーターが良いものやパッと出のものは避けて、長く売れているものを専門誌やWebでの評価も調べて選びましょう。
  • でも、真空管が持つ特性・特徴って好きだ ナ〜、音質も・・・。でも、無い物ねだりのために「人気の真空管は一本で軽く10万円」を超しますよ ! ! !

S-2. アナログアンプとオブジェクト指向を比較

  • アナログアンプの世界に長く生きて来た著者には、「図-S11の(4),(6)のOP-AMPは、プログラミングで使われているオブジェクト指向の object 」そのものだと感じています。特に「AudioUnit」は、これが無かったら「"私のApp"を途中で諦めていただろう」とさえ思っています。
  • もちろん、OP-AMP にも色んな特徴を持ったものが有りますし、多数の OP-AMP を使って一つの機能を作り出したりもします。
  • 例えば、ミックス回路は一個の OP-AMP に多数の抵抗を追加する簡単な回路で多チャンネルのミックスが出来ます。これをプログラムで行うと簡単な数値の足し算の組み合わせで行うことが出来ます。
  • 複数の OP-AMP に、コンデンサーと抵抗、ボリューム(可変抵抗器)を接続すると「イコライザー」が出来ます。プログラムでは、時間軸(音声データーのメモリーの前後位置)での値と数値計算の組み合わせで行うことが出来ます。
  • イコライザーの設計は、OP-AMP 回路では「中心周波数( f0 )、帯域幅( Q )、可変幅( dB値 )を決めることから始まります。プログラムでは「それぞれの数値を指定されている項目に数値入力 」をするプログラムを書くだけです。
  • アナログでは、ミックス回路やイコライザ回路のひとかたまりを「ユニットやブロック」と呼びます。
  • 著者は、この「ユニットやブロック」をプログラミングで「オブジェクトと称している」と捉えています。その一つが「AudioUnit」です。
  • 「オブジェクト」と呼ぶ範囲は大小色々あり、小さなオブジェクトを組み合わせた物も「一つのオブジェクト」と分類しているとも捉えています。また、音楽の曲リストを使えるようにしてくれる「MPMediaPickerController」も「オブジェクト」の一つと捉えています。
  • でも、この「オブジェクト」なら浅知恵でもプログラムを組む事が出来ますし、便利です。

T. EarPodsの特徴を解剖する

T-1. EarPodsの歴史

  • まず、EarPodsにも新旧の二種類があるとの事で比較してみます。引用させて頂いたのは、『新標準イヤフォン「EarPods」を試してみました!どっ しりとした安心感のある低音が特徴。(2012-09-13, AppBank)』の記事からです。 < 参考資料-T11 >
    ■ 図-T11. 新旧のEarPodsの外観比較 (著者の加工あり)
    fig-t11.png ■ 図-T11の(1)は、2012年9月頃まで発売していたEarPodsの外観です。(2)は、2012年9月頃から発売になった現行のEarPodsの外観です。
  • 図-T11の(1)は、外観からは標準的な解放型イヤホンに見えますが、ケーブルが出ている部分の太さや上部の丸穴が点々と並ぶ様子は、現行EarPodsにどの様に受け継がれたのか興味深いものが有ります。残念ながら、著者は現物に接するチャンスが有りませんでした。
  • (2)は、現行のEarPodsですが、見た目のデザインが素晴らしいですよね。以下の項で、故障したものを分解して分析しましたので話を進めます。

T-2. EarPodsの外回りを確認

  • 次に示すのが、現行EarPods (以下から"EarPods"と記す)の外回りを確認します。
    ■ 図-T21. EarPodsの外回り (著者作成と加工あり)
    fig-t21.png ■ 図-T21の(1)は、耳の位置での頭部水平断面にEarPodsの断面を重ねて、EarPodsの装着状態を断面を示しています。(2)は、EarPodsを耳に装着した頭部の外観です。なお、写真は前記の「参考資料-T11」から引用しました。(3)は、「3.5Φコネクター」の端子機能(用途)を示しています。左側がEarPods、右側が通常のオーディオで使っているものです。
  • 図-T21の(1)では、EarPodsの右下矢印の部分から音が外耳道を通り鼓膜に届きます。図では表現しきれなかったのですが、耳介とEarPodsとの間に少し隙間が有ります。この隙間が、重要な役割を果たしていると見ています。要するに、解放型イヤホンであると考えています。その証拠は、装着しても周囲の音が普通に聞こえている事です。音漏れもするんですが・・・。
  • (2)からは、「何とまア〜、耳がイヤホンをうまく収める構造になっていたもんだなア〜」と関心しています。「Good job ! 」ですね。でも、イヤホンの上部と頭部との間に隙間があるのです。
  • (3)は、対応する iPhone が少なくなった昨今の「3.5Φ イヤホンコネクター」の端子機能を示しています。まだ、このタイプのEarPodsも現役で販売されているので、iPhone を使っていないが「チョット聴いて見たい人」への情報です。EarPods を一般的なイヤホンとして使うには、「3.5Φステレオ(4極) から 3.5Φステレオ(3極)」に変換するアダプターが必要です。秋葉原の部品店で販売(中国製)しています。他の変換が出来るタイプもあ有ります。著者も所持しています。ただし、Webでは「極端に安い EarPods」の広告を見掛けますが、偽物にはご注意を ! ! !

T-3. EarPodsの構造を確認

  • さて、いよいよ本丸に近づいて来ました。次に示すのは、いずれも著者の所持品を分解して構造を調べた結果の発表です。
    ■ 図-T31. EarPods と MDR-CD900 の構造 (著者作成)
    fig-t31.png ■ 図-T31は、(1)が MDR-CD900 (密閉型)の断面構造を示すイラストです。(2)は、MDR-CD900 の内部写真です。(3)は、EarPods (開放型)の断面構造を示すイラストです。(4)は、EarPods の外観と分解した各パーツ毎の写真です。なお、MDR-CD900 は「著者所有の 1985 年頃から発売された」古いものです。
  • 図-T31の(1)の MDR-CD900 の断面図では、緑色で示すスピーカーユニットの前面(右側)はスリットを通し耳に音を入力し、ユニットの背面(左側)は密閉されたボディ内に音を放出しています。なお、背面のすぐ後ろには吸音材があり、横(図では上下)方向はスリットを通し耳当ての内側に背面に音が放出され、スポンジで吸音し、ビニールレザーで密閉している。密閉型ヘッドホンです。
  • (2)の左上は、耳に当たる側の外観です。右上は、スピーカーと一体になっている部分を取り外したボディ部分の外観です。左下は、スピーカーと一体になっている部分の耳側の外観で、右下がその背面の外観です。
  • (3)の上側は、EarPods の上部から見た平面断面図で、下側が側面から見た垂直断面図です。図では、緑色で示すのがスピーカーユニットで、赤色線が内部の構造材で、黒色線がボディの構造材、薄い青色が音が通過する開口になっています。いずれにしても、大変複雑な構造をしています。
  • (4)は、まず右側2列では EarPods を構成している部品の外観写真です。左側上下は、 EarPods の外観と開口ですが、「青色の矢印と番号で示した開口のそれぞれの役割」を指で塞ぐいだ時の音の変化を示します。
  •  ⚫︎ 矢印-1の開口は、外耳道に音を送り出しています。
     ⚫︎ 矢印-2の開口は、低域が少し下がり、中高域が強調されます。
     ⚫︎ 矢印-3,4の開口は、低域がいなくなります。
     ⚫︎ 矢印-5の開口は、あまり変化を感じられません。穴の位置の関係で確認しづらい。
  • そうなんですよ ! ! ! 。iPhone をお持ちの方は、指で出来る範囲ですからご確認を !
  • 結果からは、矢印-1の開口以外はスピーカーの背面からの音を、ロード(負荷)を掛けながら外部へ開放することで音質調整が行われているようです。開口を塞がないように、ご注意下さい。

U. EarPodsから発見

U-1. EarPodsの分解で見つけた抵抗

  • 次に示すのは、EarPods のスピーカーの背面で見つけた「小ちゃなチップ抵抗」です。
    ■ 図-U11. EarPods の SP に抵抗発見 (著者作成)
    fig-u11.png ■ 図-U11は、EarPods の SP の背面にある小さなチップ抵抗の写真です。小さすぎて上手く撮影出来ませんでした。ご容赦を・・・。
  • SP の背面がプリント基板になっていているのに気付き、接着剤などを剥がして発見しました。
  • テスターで確認したところ、約 11 Ωの抵抗でした。イヤホンコードとボーイスコイルの片側の回路に挿入してあります。設置位置がら考えると、「出力がショートした時のイヤホンアンプの保護抵抗」なら「iPhone 本体内」のはずですから、「音質調整のため」と考えられます。
  • 「EarPodsのインピーダンスが 42 Ω ぐらい」であることと、「SP の背面の約 11 Ωの抵抗をイヤホンアンプの内部インピーダンス」と仮定して、ダンピングファクターを求めると、
     ♦ Zsp = (42Ω - 11Ω ) = 31 Ω
     ♦ Zamp = 11 Ω
     ♦ DF = ( Zsp ÷ Zamp ) = ( 31 ÷ 11 ) = 2.8 (ダンピングファクター)
    と計算出来ます。なお、iPhoneのイヤホンアンプ IC の Zamp は、11 Ω より十分に小さいと考えています。
  • どこかで見た DF の値です。「300Bシングル真空管アンプ(NF無し)のダンピングファクター = 3.0」です。
  • おそらく、EarPodsの「デザインや音質」の決定には名のある「オーディオの専門家や音楽関係者」による「助言やチェック」が入っていて、それなりの段階を踏んで造り上げられているだろうと思慮します。
  • そうでないと、現行EarPoda発売からやがて 8 年も経過するのに、Apple が使い続ける訳が無いと思います。

U-2. EarPodsで見つけた現象と仮説

  • [ 実験-1 ] EarPods で音楽を聴いている時に、耳を覆う様に手を被せたたら「低音がいなく」なりました。手は、水をすくう様にして隙間が出来ない様にしました(完全密閉ではない)。この現象は、EarPodsの開口からの逆位相の音が外耳道にも入り「低音が打ち消された」と解釈しました。なお、音を止めて同じ様に耳を覆て「密閉度を上げる」ようにすると「血流音」の様な音が聞こえます。
  • [ 実験-2 ] それならと、手の代わりに「MDR-CD900 (電気的接続無し)」を被せてみました。ナントイウコトデショー、今度は「低音が強調されて、バンバンボンボン」聴こえるではないですか ! ? 。最初は、「MDR-CD900 のスピーカーが、 EarPods の開口からの音に共振して低音を強調している」と考えました。しかし、「MDR-CD900 をほんの少し浮かして隙間を開ける」と「バンバンボンボンが治る」ではないですか。
  • また、「MDR-CD900 (電気的接続無し)」のみを装着(密閉空間)してボディを指の皮膚の部分で叩くと、「ボンボン」と大きく聞こえますが、少し浮かせる(開放空間)と「BON BON」が「pon pon」に変わり小さな音になりました。
  • この現象から著者は「仮説」を立てました、「聴覚は、外耳道を狭い密閉空間にすると聴覚の特性が変化してしまう構造を持っている」です。おそらく、この「密閉空間内での振動を鼓膜が全てを受け止めてしまう」ためだろうと解釈しました。あえて言うなら、「音楽を聞く時に閉ざしてはいけない、少なくとも聴覚にとっての日常ではない」と思慮しました。
  • とは言え、騒音から耳を守らないといけない仕事の方もいるだろうし、長く使っている人では「脳が学習」してくれて普通に聞くことが出来る方もいるだろうとは思う。
  • で、こんな記事も見つけました。新発売の AirPods Pro に関するもので「なぜ、AirPods Pro がこんなにいいのか? 仕組みを解説しよう」と題した記事です。 < 参考資料-U21 >
  • ・普通、インイヤー型のヘッドフォンイヤホンを耳に入れると、なんというか閉塞感というか、場合によっては耳の中に圧力がかかった状態になる。AirPods Proにはこれがない。
    ・実は、AirPods Proの内部には筒抜けになっている部分があって、外の音は、一部直接入ってくる。これは耳の内部に圧力をかけないようにするためだ。(記載文の一部を抜粋、修正アリ)
  • との記載があります。おそらく「鼓膜を密閉空間に置かない」の思想があり、 EarPods で込められた設計思想を AirPods-Pro もしっかり受け継いでいる結果だと思います。残念ながら、購入予定はありませんが分解するチャンスは来るかナァ〜。

V. 見つけた興味深かったこと

V-1. 『奮闘記-元記事』の提案と似た実験をされた嬉しい Web 記事

  • 「ヘッドホンのfinalが音響講座を開講、エージングの効果は本当にある? 数値化できない音の印象をどう伝える?(ASCII.jp, 2019-06-11)」と題された記事で、6月8日にfinalブランドを展開するS'NEXTにより開催された「イヤホン・ヘッドホンを楽しむための音響講座」に関する記事です。
  • 記事中で注目したのは、音楽を16bitと8bitとで音質を聴き比べた部分です。 < 参考資料-V11/Page-4 >
  • ・講座では、48kHz/16bitと48kHz/8bitの音楽を順番に再生して違いが感じられるかを体験するデモも用意された。
    ・音に関心が高い人が集まっていることもあり、変化を感じた人が大半を占めたが、8bitの音源も音楽としては十分成立していて、実際に聴いてみると数字から感じるほどの差はないと思えたのは確かだ。
    (記載文の一部を抜粋)
  • 上記の体験デモの詳細は分かりませんが、著者の『奮闘記-元記事』で『N-4. ビット不足を体感する』で提案したものと考え方が似ていて、結果もほぼ同じなので嬉しい記事でした。
  • 残念ながら「final」さんとの交流も無いし、著者のような年寄には、こういう大勢に参加して頂ける実験を行う機会が無いものですから・・・。
  • 勿論、著者だけが気付いた現象では無いので、「final」さんの独自思想に基づいて以前から行われている体験デモとも思慮されますが、著者にとっては嬉しい記事です。

V-2. チコちゃんに叱られそうなので調べたこと

  • 2019年10月18日にNHKが放送した「チコちゃんに叱られる」で「1つのスピーカーでいろんな音が同時に出せるのは…なぜ?」というテーマが取り上げられました。
  • 番組内の解説を要約すると、
    (a) どれだけ多くの音が混ざっていても、同時に聞こえる音の波形は1つ(1本)だけ。
    (b) スピーカーは波形通りに空気を振動させる。だから色々な音を再現出来る。
    (c) 人間の耳には、鼓膜の奥に蝸牛という聴覚の器官があり、蝸牛内にある内有毛細胞で音を感知する。
    (d) 内有毛細胞は、蝸牛内の位置によって周波数別のセンサーの役割をし、周波数ごとの大きさに分解する。
    (e) 分解されたデーターは、別々な電気信号として脳に伝えられる。
    (f) 脳は、送られたデーターから、それぞれの声や楽器を個別の音として判別(認識)している。
    (g) チコちゃんの答えは「耳で音をバラバラにしているから」でした。
    でした。これらは、個々にはみんな正しいと思います。
  • しかし、(a)は空気の疎密波であり「音を一点で捉えると一瞬一瞬の状態(空気圧)は一つであり、その状態を時間を追って結んで行く方法では「波形は1つ(1本)だけ」になるのは当然です。
  • (b)は、(a)で手に入れた信号を利用して振動板を動かし、空気の疎密状態(疎密波)を再構築(再現)しています。パーフェクトな再現とは言えないにしても、強いて選べばこれが正解に近いか ?
  • (c)〜(g)はオーケストラを生で聴いた時にも働く聴覚と脳とで構築された機能であり、スピーカーで聴いた時のみに働くわけではない。当然、(g)のチコちゃんの答えはこの範囲に入ってしまいます。
  • これらから、残念ながら「1つのスピーカーでいろんな音が同時に出せるのは…なぜ?」の答えには成っていないと思います。
  • 著者が考えた答は、『「波の重ね合わせの原理」と「波の独立性」が成立する範囲の空気振動(大気の密度の変化)を音として利用し、聴いている』からです。さらに、『マイクとスピーカーによって空気の疎密状態を再構築している』も重要な要素です。このことは、「音を電気的に加減算(mixing)によって音の合成や加工が成立していることが証明している」し、逆に「有用性」も示しています。
  • 以下で著者の回答の根拠を、図を用いて説明します。なお、図-M01の(2) は、たまたま見つけた物理の解説サイトから引用し、加工をしました。ただし、引用元(学校や受験生向け)のURLの公開は、迷惑を掛けるといけないので控えさせて頂きます。
    ■ 図-V21. 波の重ね合わせの原理などを考察 (著者作成と加工あり)
    fig-v21.png ■ 図-V21の(1)は、静かな水面に水滴や小石が落ちて出来た波紋(水面波) を捉えた写真です。音波をイメージするのには役立つと思います。
    □ ただし、水面の波の形状は、表面張力や円運動などの多数の要素か影響しているために、厳密には「疎密波である音波の正弦波」とは異なっています。しかし、「波の重ね合わせ」に付いては同じような理論に基づいていると捉えていいようです。
    (2)は、水面の波の重なり合いで盛り上がっている部分のみを「断面のイメージ(イラスト)」にしたものです。
    (3)は、電気信号による「50Hzと130Hzのサイン波(Sin波)」のそれぞれと、2つの信号を電気的に加算(Mix)した波形を示しています。
  • 参考にさせて頂いた資料やWeb検索で見つけた「波紋と音波」の特徴的な特性は、
  • (A)「波の重ね合わせの原理」: 2つ以上の波が重なり合うと、「波の変位はそれらの波の変位を足し合わせた」ものになる。
    (B)「波の独立性」: 波が重なり合った部分で合成波になり、その後は元の形を保ったまますり抜けて行く。
    (C)「波の重ね合わせの原理や独立性の崩壊」: 波長に比べて振幅が大きすぎる波では、これらの原理が成立しなくなるとのことです。
    なお、A,Bは、高校物理、Cは、波動方程式の世界からです。
    ⚫︎ (C-1) 著者は、この崩壊現象を「積木を同じ高かさで、底辺を広く積んだもの(富士山型)と、底辺が狭いもの(ポール型)とでどちらが崩れやすいか」をイメージして解釈しました。ただし、「水の表面張力や重力と空気の湿度、粘性、風の影響」などを含む「波を伝える物質(媒質)の性質や作用、構成する要素」によっても崩壊し易さが異なるだろうと考えます。
    ⚫︎ (C-2) でも、とても面白い現象だと思う。音波では周波数が高くなるとより低いレベル(周波数に反比例)で崩壊が始まることになるのではないかとも思う。勿論、痕跡は残るだろうし、波が砕ける歪み波が発生するとも思慮する。
    ⚫︎ (C-3) この現象が、『聴覚の周波数特性(等ラスドネス曲線)と関係がある』のか、それとも「奮闘記-元記事」の図-N22の(2)のすぐ下に記載した『オーケストラのように楽器編成が多い音楽を 長い時間幅でFFT分析した時の特性である「-3dB/Octの特性」』に表れているのか、など興味津々です。残念ながら、崩壊が始まるレベルを含むこれ以上の具体的情報は見つけられませんでした。
    ⚫︎ 参考として、「津波は、波でなく流れに近い」、「海の通常の波は、静かな水面の波に比べもっと複雑である」などの説明も有りました。
  • 図-V21の(1)では、上記の「波の重ね合わせの原理」や「波の独立性」などを、「波が同心円状に広がって行く様子」や「広がることでエネルギーが分散して波高が減衰して行く様子」によって垣間見る事が出来ます。
  • また、この波紋を音波に見立てると「波紋の中に置いたマイクでピックアップした信号で、静かな水面に設置したスピーカーを鳴らした状態として想定すると、波紋はスピーカーの指向性の範囲に扇状に広がり、その振幅はマイクの位置(一点)の波の動きと同じ(相似)振幅になります」。なお、広がりは時間軸です。
  • (2)では、波を断面で示したイメージ図で、緑色の曲線によって赤色曲線と青色曲線による「波の重ね合わせの原理」を示し、動きを伴った赤色と青色の曲線が「波の独立性」を示しています。
  • (3)では、目で確認出来ない音波を電気信号の「50HzのSin波と130HzのSin波、電気的に加算(単純な足し算です)した波形」を用いて「波の重ね合わせの原理」を時間軸によって示しています。
  • さらに(3)では、「ⒸからⒶの波形を引くとⒷの波形が残る」或いは「ⒸからⒷの波形を引くとⒶの波形が残る」ことを容易に想像する事が出来ることから、「波の独立性」の意味と動作を確認出来る。
  • 私達は、「波の重ね合わせの原理」と「波の独立性」という自然現象のおかげで、スピーカーやイヤホンで音楽を楽しむ事が出来ていたのですね。「自然の不思議が、また1つ・・・」です。アリガタヤ・アリガタヤ

V-3. 光を99.99%吸収する素材

  • 『光を吸収する「暗黒」素材=映像機器など応用期待-産総研など (JIJI.COM 2019年05月16日の記事)』のタイトルで、『当たった光をほぼ100%吸収する「究極の暗黒シート」を産業技術総合研究所などのチームが開発した。』の記事です。 < 参考資料-V31 >
  • ・通常の塗装などの吸収率は99%程度。産総研はカーボンナノチューブを使った素材で吸収率99.9%を実現していたが、耐久性などの面で実用化は難しかった。
    ・開発したものは、可視光で99.5%以上、サーモグラフィーなどで使われる熱赤外線では99.9%以上の吸収率が得られた。(記載文の一部を抜粋)
  • この話は、「奮闘記-元記事」の中の、『O. 聴覚の構造と聴こえる仕組みの不思議』の『O-1. 視覚の不思議』で、「聴覚の音量調整機能」の話の導入部分に、比較対象として「視覚の明るさ調整機能」のことを取り上げていたので、ちょっ気になり取り上げました。
  • とは言っても、記事の中の「暗黒素材」そのものに付いてでは無く、「99.9%」の「 % 」の単位です。
  • まず、下図の黒い部分をしばらく注視してみて下さい。黒い帯状だったものが、段階的に変化しているのが確認出来るようになります。
  • 次に下方の白色部分に目を移します。何が見えて、どう変化して行くかを体感して見て下さい。次に、その逆を・・・。
    ■ 図-V31. 視覚の明るさ調整機能を体感する (著者作成)
    fig-v31.png ■ 図-V31の上半分は、外枠の黒い部分が[R=0,G=0,B=0]で、
    その内側では、左側から[R=G=G=3]、[R=G=G=6]、[R=G=G=9]、・・・[R=G=G=45]と RGB の各値が 3 ずつ増加するように設定してあります。
    □ 下半分は、外枠の白い部分が[R=0,G=0,B=255]で、
    その内側では、左側から[R=G=G=210]、[R=G=G=213]、[R=G=G=216]、・・・[R=G=G=252]と RGB の各値が 3 ずつ増加するように設定してあります。
    □ この「 3 」ずつの変化は、 RGB で指定出来る値が「 0〜255 」の整数であるために、「 % 」に刺激されて「 256の 1/100 を四捨五入して 3 」として「 1 % 」を想定して選びました。
  • 著者のモニターは、古い液晶ですが「この「 1 % 」を識別できますし、視線を上下することで、「視覚の明るさ調整機能」が徐々に変化して行くことも確認出来ます。
  • 聴覚では、この変化を実感しにくいのですが、「聴覚の音量調整機能」も同じように働いていると思慮されます。
  • ただし、あくまでモニター画面の特性(輝度)内の比較であり、「暗黒素材」の記事の「 % 」で示す比較対象とは異なります。
  • 技術的なことはここからどうぞ  < 参考資料-V32 /EIZO (株) HP >
  • なお、「暗闇で目が慣れるメカニズムを解明 阪大研究チーム、関係分子を特定」(2019-11-07、毎日新聞)という記事が Web で出ていました。興味がある方は、上記タイトルで Web 検索をして下さい。沢山出て来ます。
  • ・『光を感じる網膜の視細胞の中で、トランスデューシン α (Tα) というたん ぱく質の移動に関わる分子を突き止め、この分子を作れないように したマウスで実験した。すると、暗い場所で目の感度が上がらない一方、強い光を当てても視細胞の死滅を通常のマウスの半分ほどに防げた』。(記載文の一部を抜粋)
  • 視覚の分野にも新しい波が押し寄せているんですね ! ! !

W. その他おまけ

W-1. ハマってしまった「HTMLとJavaScript」の世界

  • そもそもは、HTML の使い方をもう少し勉強しておこうと「タグや特殊文字、等々」を自分が使い易いようにまとめようとトライを始めたのが切っ掛けでした。
  • たくさんのサイトを見て回り、自分のレベル合いそうなサイトの記事から一句ずつコピーし、「コピーペーストで使えるような Sample Code」と共に HTML のリストにしてまとめています。
  • ただ、「CSS」を使うと編集が分散して面倒なので、CSSを使わない記法を優先して収集・整理しています。本投稿も HTML だけで、ひとまず仕上げて確認してから投稿しています。
  • まだ、志し半ばですが作業に集中出来なくなった時に目先を変えるリフレッシュを兼ねています。
  • その作業の中で、JavaScript にも触れる機会があり、随分 HTML の使える範囲が広がることに気が付きチト取り憑かれました。

W-2. 追い打ちを掛けて来た「Adobe Brackets」

  • 「HTMLとJavaScript で音楽再生をする記事」はチェックしていたのですが、Web 記事で「 html + css + JavaScript で開発されている、コードエディター Adobe Brackets が無償で配布されている」との記事を見つけ、ビックリです。
  • ただ、「Atom などのコ ードエディターでも採用されている」と記載されているのを見つけ、以前に導入したことがある「Atom + Markdown 」を思い出しました。実は、Windows 育ちの著者の古い脳みそでは操作性(特にファイル操作)に馴染めなくて「 CotEditor 」に移行した経緯から、一応ダウンロードして開いてみましたが、使っていません。
  • でも、でも、ですよ「なんか、新しい 0S が現れた」ようでチョット興奮しました。「html + css + JavaScript」は、「Windows, Mac, iPhone, スマホ」の全てで動作すだろうと、新共通 OS の様に感じたからです。
  • ただ、Web の記事を HTMLファイルとして保存し、エディターで開くとコードが読めてしまうのと同じような欠点もあるのかナァ〜と思慮しています。でも、タクサン興味深々です。

W-3. 始めたWeb記事収集の手法

  • もう一年以上に成りますが、「Qiitaに掲載された新着記事を日時別に集めてます。」という、「Qiitaさんへの投稿記事をリスト化して提供して頂けているサイト」を毎日欠かさず見るようにしています。
  • 以前は、気になった記事を「PDFに変換して保存」していたものが溜まる一方だったのに、この「Qiitaさんの記事リスト」の発見以来保存量が急速に増えるようになりました。
  • そこで、下記に示す「サンプルコード と 画面表示結果」に示すようなものを、大した知識もないのに「 H T M L 」で作り、「Web記事保管庫」と命名し、整理しながら保存しています。以外に便利ですよ !
  • もちろん、いろんなジャンルのニュースなどの記事も保存しているのですが、ニュースサイトなどの転載記事は短期で消えてしまいますので、元記事を見つけて保存する必要があります。
  • 使い方は、「U_R_L」の部分にアクセス先の「URL」(Safariでは、ファイル ⇒ 場所を開く)を、「T_I_T_L_E」の部分に「記事のタイトル」に加えて「記事の記載日やサイト名」などを加えて上書き保存します。
  • 「ジジー」でも、気になった記事は覚えているのですが、いざ鎌倉(探す)という時になると見つからない事が多いので・・・。ただ、目新しい方法では無いでしょうが著者の近況として記します。

■ HTML サンプルコード
(コピーペースト用。インデントは必要に応じ各自でお願いします。)

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>Web記事保管庫</title>
</head>
<style>
</style>
<!-- これは非表示コメント / 不要になったら削除 -->
<!-- エディターの設定 ⇒ LF, UTF-8, HTML -->   
<body>

<blockquote><!-- 左側のスペース確保用 -->

<h2><b>◎ Webアクセスリスト</b></h2>
<h3>A; Qiita-プログラミング記事投稿サイト</h3>
<h4><ol type="1">
<li><a href="https://github.com/yyano/Qiita-News#Qiita" target="_blank"><b>Qiita記事・全部チェックサイト (日付・時間別のリスト)</b></a></li>
<li><a href="https://qiita.com/tbpgr/items/989c6badefff69377da7" target="_blank"><b>Markdown記法 サンプル集 (Qiita範囲、概要、Qiita, 2018-12-26 更新)</b></a></li>
<li><a href="https://qiita.com/Qiita/items/c686397e4a0f4f11683d" target="_blank"><b>Markdown記法 チートシート (Qiita範囲、詳細、Qiita, 2019-10-24 更新)</b></a></li>
<li><a href="https://qiita.com/APO/items/f2212bee7f0b2c69228f" target="_blank"><b>Qiitaで使えるHTMLタグは? (Qiita, 2018-01-20 更新)</b></a></li>
</ol></h4>

<h3>B; 渋野 日向子 追っ掛け</h3>
<h4><ol type="1">
<li><a href="https://www.lpga.or.jp/tournament/lpgatour/" target="_blank"><b>日本女子ゴルフ協会-HP</b></a></li>
◎ 注意 : 各ページでは、「リーダーボード」と「ラウンド番号」を選択<br>
<li><a href="https://www.lpga.or.jp/tournament/player/20191152" target="_blank"><b>【2019】第38回大王製紙エリエールレディスオープン (11-21〜11-24, 愛媛県)</b></a></li>
<li><a href="https://www.lpga.or.jp/tournament/info/20190033" target="_blank"><b>【2019】公式戦 LPGAツアーチャンピオンシップリコーカップ (11-28~12-01, 宮崎県)</b></a></li>
<li><a href="U_R_L" target="_blank"><b>T_I_T_L_E</b></a></li><!-- コピぺで行追加 -->
</ol></h4>

<h3>C; Group TITLE</h3><!-- コピぺでGroup追加 -->
<h4><ol type="1">
<li><a href="U_R_L" target="_blank"><b>T_I_T_L_E</b></a></li><!-- コピぺで行追加 -->
<li><a href="U_R_L" target="_blank"><b>T_I_T_L_E</b></a></li><!-- コピぺで行追加 -->
</ol></h4>

</blockquote>

</body>
</html>

■ 画面表示結果
(ここからもアクセス出来ます。文字サイズの指定は外しました。左の縦線は表示されません。)

◎ Webアクセスリスト

A; Qiita-プログラミング記事投稿サイト
  1. Qiita記事・全部チェックサイト (日付・時間別のリスト)
  2. Markdown記法 サンプル集 (Qiita範囲、概要、Qiita, 2018-12-26 更新)
  3. Markdown記法 チートシート (Qiita範囲、詳細、Qiita, 2019-10-24 更新)
  4. Qiitaで使えるHTMLタグは? (Qiita, 2018-01-20 更新)
B; 渋野 日向子 追っ掛け
  1. 日本女子ゴルフ協会-HP
  2. ◎ 注意 : 各ページでは、「リーダーボード」と「ラウンド番号」を選択
  3. 【2019】第38回大王製紙エリエールレディスオープン (11-21〜11-24, 愛媛県)
  4. 【2019】公式戦 LPGAツアーチャンピオンシップリコーカップ (11-28~12-01, 宮崎県)
  5. T_I_T_L_E
C; Group TITLE
  1. T_I_T_L_E
  2. T_I_T_L_E


  • ハードディスク(著者はUSB)に優しい情報収集方法です。資料を、本棚に並べて置く感覚で使えます。
  • 残念ながら、今年の大会が終了しましたが「しぶこチャ〜ン、来年も Web で追い掛けるよ」! ! !

W-4. CotEditorに「等幅フォント」を導入

  • どうにも、字数合わせをするのに不便で「Ricty Diminished Reguler」と言う「日本語等幅フォント」を CotEditor に導入しました。きっかけは、「日本語等幅フォント (Qiita、2017-01-09更新)」の記事を見つけたからでした。 < 参考資料-W41 >
    「CotEditor」にも沢山のフォントが入っているのですが、よく解らなくてこれを選びました。
  • また、本投稿の図やグラフの画像処理に使っている「Windows 7 (サポート終了で Web 接続を止めた)」に入っている「ペイント」に、「MS UI Gothic」と言うフォントも使いはじめました。パーフェクトではないですが 16 ポイント以下ではドットによるフォントに近く、標準で搭載されていました。グラフの目盛りなどの小さい数字や文字の記入に役立っています。
  • その他、背景を少し暗く(RGB 235,234,235)して目に優しくしました。また、この長ったらしい投稿を書いている途中で「CotEditor」の「インスペクター」⇒「アウトライン」で選べる機能で、ページ内を項目単位で移動出来る仕組みを見つけて便利に使っています。

W-5. USBメモリーに異常発生

  • 残念なことに「macOS Catalina」にバージョンアップしてから、USBメモリーに「CotEditor」から読み出しは出来るが、「上書き保存が出来ない」と「自動バックアップ機能がアクセス不良で App が止まる」症状が出ています。他の一部の App でも同じ症状が出ますので「Catalina」による「セキュリティ or アクセス権」が関係しての症状だと感じています。ただし、「Numbers 6.2.1」では発生しません。
  • なお、USBメモリー は当初 Windows で使っていたもので、「macOS Catalina の Mac mini」と「サポート外となった Hight Sierra の MacBook」、さらに「Windows 7」 との間で使い回していますが、「macOS Catalina」のみで発生しています。
  • 現状の解決策として、「macOS Catalina」でも「USBへの、ファイルのコピーとペーストは双方向に可能」なことを利用して、「USB と Catalina の desktop」に同じファイルを置き、「Catalinaでは desktop のファイル」で作業し、編集作業をしたファイルを 他方にもコピーペーストをする方法で使っています。
  • そこで調べてみると、「macOS 10.15 CatalinaではシステムボリュームがRead-Onlyになり、アプリはユーザーデータのみにアクセス可能に。(2019-06-05、AAPL Ch)」の記事を見つけました。 < 参考資料-W51 >
  • 記事では、
  • ・Catalina ではセキュリティ機能の強化の一環として、macOS のシステムボリュームとユーザーデータボリュームを分け、システムボリュームは Read-Only にしてサードパーティアプリが macOS のシステムへアクセスできないようにする。(記載文の一部を抜粋)
  • 著者の知識では判然としませんが、単純に USB には設定していないはずのアクセス権の問題か ? 。確かに、自動バックアップ機能がアクセス不良で CotEditor が止まる時に「フォルダーに書き込むためのアクセス権がありません」と警告が出ます。
  • USBメモリーも「Macintosh HD のシステムボリューム」と同じ扱いに成っているのでしょうか ? 。情報を待ちます。
  • やはり、 USB での使い回しに手間が掛かるのは不便ですね ! ! ! 。ま、安全が一番大切ではありますが・・・。

XYZ. 最後に

  • またも、長文と乱文になりお詫び申し上げます。お付き合い頂いき有り難う御座いました。
  • 今回は、オーディオの技術論が多くなりました。プログラミングにも歴史と共に奥深い「プログラミング技術」があるのだろうとは思います。初心者の著者にも感じるところはあるのですが、記事として書けるほどの知識がないので・・・。
  • ただ、記載部分は少ないのですが 2018 年の暮れから「HTMLとJavaScript」への「興味とその可能性」に惹かれ、道草をしながら 2019 年春の "私のApp" のバージョンアップとこの記事を仕上げました。
  • で、Qiitaを愛読しているジジーから若い技術者の皆さんに一言です。
    ♡ プログラミングの世界は、古い FORTRAN や BASIC の時代を中心に同心状に拡大していっている様に感じますが、まずは1つの言語を制覇しましょう。
    ♡ 次は、同心円の中心に向かって言語を学び制覇しましょう。たどり着くのは、Mac や Windows の OS かもしれませんが・・・。単に使い易くしただけの中心を背にした言語は、視野を狭くします。
    ♡ そして、ヒューマンインターフェイスを学びましょう。取り扱い説明書を読まないと使えない時代は終わっています。纏(まと)めるセンスもとても大切です(チョット、Apple さんみたいな表現ですが)。
  • 例えば、最近気付いたのですが Qiita さんの記事の一番左上の「ユーザーアイコンやハンドルネーム」(呼び方が正しくないかも ? )をクリックすると、その方の投稿履歴が表示され、隣の日付にカーソルを置くと「最初の投稿日」が表示されます。また、最下方の小さい文字帯には Qiita さんの資料にアクセス出来るアクセスポイントが並んでいます。
  • 奥ゆかしいと言えばそうなんですが、こういう形で「 Qiita さんの沢山の機能に気付きづらいのは勿体無い」と思います。ハデハデやゴテゴテは嫌いですし、「シンプル イズ ベスト」の考え方も有りますが ?!
  • 共通アイテムが無いので、「見本の1つかな ? 」と記載しました。Qiita さん御免なさい。もっとも、知ってしまえば気にならないのですが・・・。なお、Qiita さんのページデザインが変更になった(2019-12-02,「『すべての記事』へのリンクが消滅した」の記事より)ようです。変わっていました。
  • ちなみに、前記の「渋野 日向子 追っ掛け」で幾つものサイトを廻りましたが、「日本女子ゴルフ協会のHP 」が一番使い易かったです。難点は、民放のテレビ中継があると途中から「リアルタイム速報は、本日のテレビ放送終了まで停止する」場合が有ることです。残念ながら、今年の大会が終了しました。しぶこちゃん、来年もガンバ ! ! !
  • もっとも、"私のApp" でも、Appを終了するコマンドを仕込んでありますが、隠してある訳では無くて、内臓の「System Info (取説)」には理由と使い方を記載してあります。

  • 若者よ、「大志に情熱をプラスし、天狗にならず、コツコツと、やり遂げる」を胸に頑張ってね ! ! !
  • お付き合い、有難う御座いました。なお、"私のApp" に少しだけでも興味をお持ち頂いた方には、下記の「のアクセス案内」からアクセス出来ますが、「2019年秋のバージョンアップ」への対応は出来ていません。

以上 著者 : Try-Jizy (トライ・ジジー / Susumu Taniguchi) / (2019-12-03 脱稿)

著者記事等へのアクセス案内

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSでもマルチモジュール化したい!

この記事は iOS #2 Advent Calendar 2019 の12日目です。
今年の1年間、ぼくが興味を持ち続けてきたiOSアプリのマルチモジュール化について、検討したことをここに書き出します :slight_smile:

マルチモジュール化とは

タイトルからさらっと「マルチモジュール化」という言葉を使ってしまいましたが、ここでは、ライブラリの仕組みを使って複数のモジュールで構成されたiOSアプリにすることを、マルチモジュール化と呼ぶことにします。逆にマルチモジュール化していないアプリをモノリシックなアプリと呼ぶことにします。

ライブラリと言えば、OSSとして公開されている汎用的で再利用可能なものが真っ先に頭に浮かびますが、ここではそういったものを使っているどうかは関係なくて、開発中のアプリの処理を積極的にライブラリとして切り出して(=モジュール化)、そのアプリ内で利用してゆくことを考えます。

イメージ

モジュールの分割方針

モジュールに切り出すと言っても、どこをどう切り出せばいいでしょうか。

ここでSwiftの言語特性を考えてみます。Swiftのアクセスコントロールにはモジュールを境界とするもの(open, public)があるので、モジュールの外に見せる機能とモジュール内だけで隠す実装をコードレベルでコントロールできます。ですから、各モジュールごとに独立した責務を持たせるようにし、アクセスコントロールを使って独立性を高める、という方針で分割すると良いでしょう。

具体的に、どういった単位で分割するのが良いかというのは、アプリの設計と密接に絡む問題…というより設計そのものなので、一般的な答えを出すことはできません。ですが、後に述べるメリット・デメリットを考えながら、いくつかの方向性は考えられます。

機能単位で分割

アプリがある程度の独立した複数の機能から構成されているのなら、その機能単位でモジュールにするというのは直感的でわかりやすい分割方針です。

機能単位での分割例

レイヤー単位で分割

MVVM、MVP、Clean Architecture、VIPER、…などの設計パターンを用いるのなら、そのレイヤー単位でモジュールを分割するのも良いと思います。こういった設計パターンに共通する根本の考え方は、各レイヤーで責務を分離し、それぞれを疎結合にしておくことです。ですから、レイヤーでモジュールを分割するというのは意に適っています。

レイヤー単位での分割例

機能×レイヤー

上記の組み合わせで、大きくは機能で分割し、そこから必要に応じてレイヤーに分割するというのもあると思います。モジュール数が増えることになりますが、アプリの規模が大きいのならそれは自然なことだと思います。逆に規模の小さいアプリで無理して分割するのはオーバーキル感があるので、そこはアプリに合わせて調整が必要です。

いろんな分割例

モジュールに分ける意味のひとつが責務の分割です。責務を分割しておけば、他への影響を最小限に抑えてモジュールを更新できるので、最初は機能で分割しておいて、ある機能の規模が大きくなってきたところで、それをさらに分割するというのも良いと思います。

マルチモジュール化のメリット

ぼくがマルチモジュール化のメリットとして期待している点は、次の3つです。

  • 開発時間の短縮
  • 影響範囲の最小化
  • コンフリクトの減少

開発時間の短縮

モジュールごとに、テストターゲットを用意したり、そのモジュールの動作を確認するための小さなアプリターゲットを用意することで、そのモジュールだけをビルドして開発を行うことができるようになると考えています。

何よりも、モジュールだけのコンパイルで済むのでビルド時間が短縮されますが、テストに関してもそのモジュールのものだけを走らせることでその時間が短縮されます。機能の実装や修正を行う際には、何度もビルド&確認を繰り返すので、アプリの規模がある程度大きくなると、その効果は大きいと思います。

コード変更時の、アプリ全体の差分ビルドも、もしかすると速くなるかもしれないのですが、その点は検証できていません。

影響範囲の最小化

コンポーネントが相互に依存し合っていたりして密に結合した状態になっていると、どこかを変更する際の影響範囲が広がってしまい、変更作業が大きくなったり、気づかないバグを生みやすくなります。

疎結合なコンポーネントの集まりとしてうまく設計していれば、モノリシックなアプリであってもどこかを変更する際の影響範囲を小さくできます。
しかし、本来は特定の機能の実装だけのために作ってあったはずのクラスが他の実装でも使われてしまったり、コンポーネント内でのみ利用するためのメソッドが気づけば外から呼ばれてしまっていたりといった事故は起こり得ます。特に、チームに複数の開発者がいる場合は、意思疎通のズレによってそういうことが起こりやすくなります。

モジュール化すれば、 openpublic 以外のアクセス修飾子を持つクラスやメソッドは、モジュール外には隠されます。アクセス修飾子をつけなければ internal として扱われるので、意識的にアクセス修飾子で指定したものだけがモジュール外に公開されるようになります。
そもそも、そのモジュールをimportしなければ、公開されているものを使うこともできません。
意図せず誤って使ってしまうことがない、ということをコンパイラに保証してもらえるので安心感があります。

また、モジュールが他のモジュールに依存する際には、単方向の依存になります。モジュール間の依存関係もはっきりさせることができます。

コンフリクトの減少

これはやや副次的な効果ですが、モジュールごとにプロジェクトファイルを分けておけば、プロジェクトファイル変更時(ファイル追加など)のコンフリクトが少なくなるのではないかと思っています。

マルチモジュール化のデメリット

もちろん、マルチモジュール化することのデメリットもあります。対象のアプリの機能性・規模によっては、マルチモジュール化のデメリットの方が大きくなる場合もあるので、少なくとも、最初から完璧なものを求めるのはやめた方がいいと思います。

モジュール分割の難しさ

前にも書いたとおり、モジュールをどう分割するかの一般的な解はありません。また、抽象化を突き詰めて分割しすぎると、モジュール同士の依存関係が複雑になって、モジュールを管理するのが難しくなります。
分割しない場合は考えなくてもよかったことを考えなければいけない分、手間が増えるのは間違いないと思います。

抽象度が高くなる分、複雑になる

マルチモジュールのメリットを活用しようとすると、モジュール内の実装は隠して、提供する機能だけを外側に見せるようにしたくなります。つまり、モジュールが提供するインタフェースの抽象度を高くすることになります。

また、モジュールの依存関係は循環しないようにする必要があるので、モジュールが相互に依存しあうことはできません。これを解決するために外側から依存を注入する方法を使うことになるでしょう。
例えば、モジュールAが外部から提供してほしい機能をプロトコルとして定義し、モジュールBでそのプロトコルに準拠したクラスを作成、インスタンス化して、モジュールAに引き渡すというものです。この場合、モジュールAはモジュールBの存在を知らなくてよいので、モジュールAからモジュールBへの依存は必要ありません。

このように抽象度をあげて結合を疎にすることは、実際の処理がどこに実装されているのかがわかりにくくなるという副作用があります。モジュール間を接続するコードも増えるので、必要以上に抽象度を上げることは、ただ単にプログラムを複雑にしてしまう危険性があります。

2つのライブラリの種類

モジュールを作成する際に、Xcodeで新規プロジェクト(または新規ターゲット)としてサクッと作ることのできるライブラリには次の2種類があります。

  • Framework
  • Static Library

ライブラリの種類

Framework

フレームワークは、ライブラリとその他のリソースを1つにまとめたものです。

Dynamicフレームワーク

これを選択して作られるフレームワークにはDynamicライブラリが入っているので、種別としてはDynamicフレームワークです。Dynamicフレームワークは、Embedded Framework(アプリにくっついていくフレームワーク)としてiOS 8から使えるようになりました。
なお、Carthageが扱うのもDynamicフレームワーク、CocoaPodsで use_frameworks! をつけたときもDynamicフレームワークが使われます。

Dynamicライブラリはアプリのメインターゲット(アプリのプログラム)とは別のファイルとして分かれていて、実行時に動的にロードされます。

Dynamicフレームワーク

macOSでは、特定の機能でしか使わないモジュールをDynamicライブラリにすることで、実際に必要になるまでそれをロードしないようにできます。そうすることで起動時間を短縮できます。また、Dynamicライブラリをプラグインのように追加したり差し替えたりすることができます。
しかし、iOSでは起動時にすべてのDynamicライブラリがロードされます。また、ストアに申請するアプリにくっついていく形式しかサポートされていないため、Dynamicライブラリだけを差し替えたりはできません。
ですから、残念ながら、macOSの場合のような利点はありません。

むしろ、Dynamicライブラリの数が多いと逆に起動時間が遅くなります。実際、 WWDC 2016のセッション ではDynamicフレームワークは6個程度にとどめるように案内されています。

So you absolutely can and should use some, but it's good to try to target a limited number, we would, I would say off hand, a good target's about a half a dozen.

翌年の WWDC 2017のセッション でも、できるだけ少なくするようアドバイスされています。

So last year I said do less, and I'm going to say that again this year and I'm always going to say that because the less you do, the faster we can launch.

And no matter how much we speed things up, if we have less work, it's going to go faster.

And the advice is basically the same. You should use fewer dylibs, if you can, you should embed fewer dylibs.

Dynamicフレームワークが使えるようになったiOS 8では、同時にExtension(Today Extensionや、Share Extensionなど)が登場しました。
アプリ本体(メインターゲット)とExtensionで共通する処理を、Dynamicフレームワークにすることにより、アプリサイズの肥大化を抑えられます。おそらく、iOSのDynamicフレームワークはこの使い方を主な目的としているのでしょう。

アプリサイズの削減

モジュールの数が多くなりそうなら、Dynamicフレームワークは避けた方がいいでしょう。

Static Library

SwiftはXcode 6で登場しましたが、SwiftのStaticライブラリはXcode 9になって、やっと作れるようになりました。

Staticライブラリは、ビルド時のリンクフェーズでアプリのメインターゲットに取り込まれます。

Staticライブラリ

このため、Dynamicフレームワークと違って、出来上がる構成はモノリシックなアプリの場合と同じです。起動速度の低下も意識する必要はありません。

ただ、Staticライブラリはフレームワークと違ってライブラリそのものです。つまり、リソースを持つことができません。UIに絡む処理をモジュールで提供したい場合は、Storyboardや画像、文字列など、関連するリソースをモジュールに含めたいところですが、残念なことにそれができません。

リソースについてはメインターゲットに持たせるか、Dynamicフレームワークを使うことになるでしょう。

プロジェクトの構成

モジュールを含むXcodeプロジェクトの構成について考えます。方法はいろいろあるのですが、そのうちのいくつかを紹介します。

1プロジェクト内にターゲットを追加する構成

プロジェクトは1つで、その中に、モジュールのターゲットを追加する構成です。

マルチターゲット構成

シンプルで良い構成だと思います。

この構成で注意したいところは「ターゲットを間違えないこと」です。
ファイルを追加するときに、正しいターゲットにチェックが入っていることを確認したり、ファイルを編集しているときについうっかり右のInspectorでターゲットを変更しないように注意しましょう。

multi_target2.jpg  multi_target3.jpg

なお、この構成ではプロジェクトファイルは1つになるので、メリットとして挙げた「コンフリクトの減少」は期待できません。

これらのターゲット間違いやコンフリクトを避けるために、XcodeGenを使うという解決策が、以下のクックパッドさんのブログで紹介されています。

XcodeGenによる新時代のiOSプロジェクト管理
https://techlife.cookpad.com/entry/2019/04/26/110000

サブプロジェクトの利用

モジュールごとにプロジェクトファイルを分けて、依存するプロジェクトをサブプロジェクトとして参照する構成です。

サブプロジェクト

各モジュールのプロジェクトが分かれているので、モジュール単体のプロジェクトを開いて開発を行うこともできます。
また、モジュール単体でプロジェクトになっているので、アプリのプロジェクトと完全に独立した別のプロジェクトからもそのモジュールをサブプロジェクトとして参照できます。ですから、アプリのプロジェクトには影響を与えずに、そのモジュール専用のテストアプリを作ったりすることもできます。

この構成の注意点は、プロジェクト設定がモジュールごとに分かれることです。アプリ全体でコンパイルオプションを変更したい場合は、それぞれのモジュールのプロジェクト設定を変更してまわる必要があります。

パッケージマネージャの利用

各モジュールをCocoaPods、Carthage、Swift Package Manager(以下SwiftPM)といったパッケージマネージャに対応するパッケージとして開発する方法もできると思います。
CocoaPodsは プライベートなSpec Repoを作ることができますし 、CarthageやSwiftPMでもプライベートなgitリポジトリを参照できるので、OSSとして公開するつもりがなくてもこれらのパッケージを作って使うことは可能です。

しかし、パッケージ開発の手順が必要な分、どうしても煩雑になるので、この選択はちょっと微妙かなと思っています。

ただ、SwiftPMには、近い将来、 Staticライブラリであってもリソースを使えるようになる可能性 があるので、ちょっと期待しています。

まとめ&参考資料

iOSアプリのマルチモジュール化について、

  • モジュールの切り出し方法
  • メリット・デメリット
  • ライブラリの種類の違い
  • プロジェクト構成

という視点で、これまでに検討してきた内容を書きました。
と言っても、検討結果を晒しただけであって、実践はまだそれほど進んでいないのが現状です。実際にマルチモジュール化してみると新たな別の発見があるかもしれないと思っています。

そもそもは、DroidKaigi 2018で「マルチモジュールのすゝめ」というセッションを聞いて、Androidではそういう開発手法の波があるのかと知りました。

今年になって、DroidKaigi 2019の「multi-module Androidアプリケーション」というセッションを見て、iOSでも同じことができないだろうかと考え始めました。このセッションでは40個のモジュールで構成されたプロジェクトが紹介されていました(ちなみに、DroidKaigi 2019ではこの他にもマルチモジュール絡みのセッションが多かった印象)。

その少し後に、Cookpad TechConf 2019で「〜霞が関〜 クックパッドiOSアプリの破壊と創造、そして未来」というセッションがあったことを知り、iOSでもマルチモジュール化の波は来てるじゃん!と確信しました。

ということで、これまでの検討では先人が公開してくれた下記の資料の影響を受けています :slight_smile:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

運転免許証の中身について+読み取るときの注意点

運転免許証の中身について+読み取るときの注意点

こんにちは…でいい時間なのかな?

10月あたりにiOSアプリの開発環境に触る機会がありCoreNFCを用いる事になったのでせっかくだから、得た知見を保存するためにQiitaに投稿を決意しました。

今回はCoreNFC関連ということで、現在treastrain氏(https://qiita.com/treastrain) が現在取り扱っている運転免許証についての注意事項等を記載していきます。

運転免許証の仕様について

 取得できる内容についてや、Application Protocol Data Unit (APDU) コマンドについてはまた機会があれば記載させていただきます。(参照する場合、章-ページで表させていただきます)

 今回フォーカスを当てるのはデータの形式についてです

書いてる途中に変換処理を書いた記事が公開されていたのでソフトめに仕様について説明できればと思います

説明する要素

  • JIS X 0201
  • JIS X 0208
  • JPEG2000

JIS関連

JIS X 0201

  • 日本産業企画(JIS)に収録されている文字コード規格

    • 制定しているのは日本産業標準調査会(JISC)
  • 7ビット及び8ビットを用いた情報交換用符号化集合

  • ASCⅡ文字集合をベースに作成されている

主に使う要素は

  • 数字:0x30(0)~0x39(9)
  • 英字
    • 大文字:0x41(A)~0x5A(Z)
    • 小文字:0x61(a)~0x7A(z)
  • 片仮名:0xB1(ア)~0xDD(ン)

これらがメインになると思います。

運転免許証の処理では主に数字を取り扱いますが、(DF01/EF07格納)署名検証の発行者名や主体者名は英字スペースドットマークが利用されるので注意(技術仕様2-9ページ参照)

JIS X 0208

  • 0201と同様にJISが制定している
  • 7ビット及び8ビットの2バイト情報交換用符号化漢字集合
    • 漢字集合:漢字6,335文字及びラテン文字、ひらがな等の524文字の非漢字が含まれる集合のこと
  • 符号化方式には以下3種類を用いてる
    • Shift-JIS
    • EUC-JP
    • ISO-2022-JP
  • 住所や本籍、名前や免許条件に用いられる
  • JIS X 0201と比べると、利用できる文字が多い
  • 1983年と1990年にJIS83, JIS90制定時に一部文字が変更されている
  • 実装例はこちら を参照してください…

JPEG 2000

  • Joint Photographic Experts Groupによって開発された画像のデータフォーマット形式(公式
  • Macでは標準だが他OSだと標準対応していない
    • 2000年ころに規格されているが2019年現在でも少ない ⇒ 通常のJPEGよりも処理時間が増加されることが対応されない原因?
  • JPEGとは圧縮アルゴリズムを離散コサイン変換からウェーブレット変換に変えた関係で、ブロックノイズを解消することができている
  • WindowsやAndroidで利用するには時間がかかりそうだが、iOS開発であればUIImageViewにバイナリを突っ込むだけで表示が可能

読み取るときの注意点

 すでにいろいろなところで書かれているが、パスワード(PIN)を間違えないこと!!!3回失敗することで免許証の中身がロックされて、解除には各都道府県公安委員会の権限になる(仕様2-5ページ参照)

失効された免許証とかでテストをしている人は…復帰が効かない可能性が高い(あくまで予想だが、すでに失効したものを復帰させる理由がないですし…)。プログラムとして事前にできそうなノーマルな対応策は

  • 各PIN認証でエラーを確認して、どちらを間違えたかを確認できるようにする
  • 桁数が違った場合はPIN認証前に処理を止める(事前に止めることで再試行の回数を減らさない)

 おそらく、ほとんどの方が思いつくであろう物ですが、保険及び自分の再確認のために記載しておきます。

まとめ

  • 運転免許証の内部データはバイナリだけでなく他の仕様も存在する
  • 特にJPEG2000はiOS標準対応しているため、他OS等よりは開発の容易さが高いと思われる
  • PINは3回失敗でロック。解除の手続きが面倒時間がかかることもあるので注意する

 簡易に記載しましたが「CoreNFC」ができて初めて使用を見て「なんだこの符号フォーマット!?!?」といったかたの一助になればと思います。何かありましたらコメントまでお願いします。遅筆ですがお返事致します

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSのショートカットを使ってWebページのスクリーンショットを撮る

前提

この記事はペパボアドベントカレンダーの9日目の記事です。Gitについて何か書くと宣言していましたが、個人的に面白かったネタが降ってきたので急遽変更して、iOSのショートカットAppを使ってみた記事を書くことにしました。

ショートカットAppって?

iOS13から標準搭載になったアプリです。iOSの挙動をある程度自動化できるスクリプトを組めます。今回はこれを使ってWebページのスクショを取って写真に保存する、ということをやっていきたいと思います。

ショートカットAppの概要

ビジュアルな感じでスクリプトを組めるアプリです。
IMG_1064.png
アクションと呼ばれるパーツを組み合わせていって、やりたいことを実現します。
基本的にアクションには入力と出力があり、入力をアクションによって変化させ出力にし、その出力を他のアクションの入力に渡すことで更に変化をさせ、といった感じで普通なら何手順もかかる作業を自動化していきます。
注意するべきところは出力されたファイル形式と入力として受け付けているファイル形式が一致しているかどうかです。
そのアクションがどんな入力を受け付けていてどんな出力を出すのかはインフォメーションのアイコンをタップすれば表示されるので、それで確かめましょう。

いろいろできそう

また、ファイル形式は結構多彩なものがあって、画像や動画のほか、json(辞書と呼ばれているのがそれです)があったり、ファイルのBase64エンコードがあったりします。更にWeb->WebリクエストというカテゴリにURLの内容を取得するというアクションがあるのですが、GET以外(POSTとかPUTとか)が扱えたりヘッダがいじれたりします。
そして、それらを効率よく扱うために、スクリプティングというカテゴリの中には変数もあります。
あとは、わかるな?
まぁこの辺りつつくと面白いんだけど適当な題材がわからなかったので次行きます。

Webページのスクショを撮りたい!

って、このくらいはアクションに用意されてるでしょ?と思ったら用意されていませんでした。。。。なので、Webページのスクショを撮るショートカットを作ります。
スクショくらいショートカットするもんでもないのでは?とお思いのみなさま、このスクショはただのスクショではございません。Webページの全体がjpegで保存されるという不思議なスクショでございます。そんなに不思議でもないかもしれませんが、普通にiOSのスクショを使っていると撮ることのできないスクショにございます。というわけで、ちょっとお付き合いいただけると嬉しいです。

具体的レシピ

【追記】ここで解説しているレシピとはだいぶ違ういい感じのショートカットが出来たので追記しました。一番下のスクリーンショットを見ていただくのが早いです。ここからの文章は、アクションを設置したりする手順の参考になるかと思って残しておきますが、それが分かってるのなら追記だけ読んでいただけたらと思います。

さて、まずは左上にあるカテゴリから、Webを選択します。
そしたらWebリクエストというサブカテゴリに、『Webページの内容を取得』というものがあるので、それを右側のステージにドラッグします。入力という欄が空欄ですが、そこをタップすると下の方に変数、クリップボード、ショートカットの入力、毎回尋ねる、現在の日付などという入力に使えるリソースが表示されますので、今回は毎回尋ねるを設定します。
次に、Safariのサブカテゴリにある『Webページを表示』を選択し右にドラッグします。そこの入力には、上で取得したWebページの内容を設定しましょう。ちなみにこの『Webページを表示』をしないで直接取得したWebページの内容を次のPDF作成に回すと、JavaScriptで展開されるDOMとかが表示されないのでご注意です。
次に、トップに戻って左上の書類カテゴリから、プリントというサブカテゴリにある『PDFを作成』を選び右にドラッグします。入力は、Webページの内容です。
そうしたら、今度はメディアカテゴリからイメージというサブカテゴリの中の『イメージを変換』を選択し、PDFをJPEGに変換します。Webページの内容にもよりますが、PDFになった時点で数ページ分に別れたものになっています。それをJPEGにしてもやっぱり数ページ分に別れたものになってしまっているので、次にそれらを結合します。
今度はメディア->イメージ編集カテゴリ内の、イメージを結合を選択します。入力は変換済みのイメージで、縦方向に結合します。
最後に、メディア->写真カテゴリの『写真をアルバムに保存』で、結合済みのイメージを最近の項目に保存することで、スクショが写真に保存されるようになります。
完成したら、右下に小さくある実行ボタンを押してみましょう。無事に動くと思います。

完成したスクショはこちら

縮小してあります。
SS_Qiita.jpg
一つ難点を述べるとしたら、PDFで数ページ分に別れたものを結合しているので、一番下にページの余白ができてしまうことです。
うまいこと計算してこの余白を削除できたらなー、と思います。
あと、『Webページのを表示』するときにいちいち完了ボタンを押さなくてはならないのも自動化できないかなぁと。
それと、一番最初にURLは毎回尋ねるようにしてありますが、ここもお好みで変更したらもっと便利になると思います。

まとめ

これでWebページ全体がJPEGになっているという不思議なスクショが撮れました。iOS実機でのWebページのスクショを撮りたい!という需要がどれだけあるかはわかりませんが、最初の方にも書いた通りJSONやらPOSTやらも使えるくらい強力なアクションがまだまだ埋もれていますので、いい感じなショートカットはいくらでも作れるのではないかと思います。
Enjoy!

追記

https://codeday.me/jp/qa/20190628/1124103.html
こちらのページにある方法を使うと、Safariで共有ボタンを押したときにこのショートカットを表示することができます。
編集中のショートカットの上部にある名前の横に丸…ボタンがあるので、そこをタップすると詳細設定欄が出てきます。そこから共有シートに表示ボタンをオンにして、共有シートタイプは選択を全て外してからSafariのWebページのみ選択します。
そうすると、変数にショートカットの入力というものが使えるようになりますので、そこからPDFを作成するとログイン状態も保持したスクリーンショットが取れます。
最終的なショートカットは以下になります。
IMG_1106.png
ちなみに、スクリーンショット下部の余白を削除するというやつですが、Webページ上でJavaScriptを実行してコンテンツの高さなどを取得してなんとかできないかと試行錯誤していたのですが、RetinaディスプレイだとJavaScriptで得られるコンテンツの高さと実際の解像度が別物なので、苦戦しています。
ここら辺知見のある方いらっしゃいましたら、コメントいただければ幸いです。
また、全体的なユーザガイドはこちらになります。
https://support.apple.com/ja-jp/guide/shortcuts/welcome/ios
ちゃんと日本語に翻訳されてる!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOSのショートカットを使ってWebページのスクショを撮る

前提

この記事はペパボアドベントカレンダーの9日目の記事です。Gitについて何か書くと宣言していましたが、個人的に面白かったネタが降ってきたので急遽変更して、iOSのショートカットAppを使ってみた記事を書くことにしました。

ショートカットAppって?

iOS13から標準搭載になったアプリです。iOSの挙動をある程度自動化できるスクリプトを組めます。今回はこれを使ってWebページのスクショを取って写真に保存する、ということをやっていきたいと思います。

ショートカットAppの概要

ビジュアルな感じでスクリプトを組めるアプリです。
IMG_1064.png
アクションと呼ばれるパーツを組み合わせていって、やりたいことを実現します。
基本的にアクションには入力と出力があり、入力をアクションによって変化させ出力にし、その出力を他のアクションの入力に渡すことで更に変化をさせ、といった感じで普通なら何手順もかかる作業を自動化していきます。
注意するべきところは出力されたファイル形式と入力として受け付けているファイル形式が一致しているかどうかです。
そのアクションがどんな入力を受け付けていてどんな出力を出すのかはインフォメーションのアイコンをタップすれば表示されるので、それで確かめましょう。

いろいろできそう

また、ファイル形式は結構多彩なものがあって、画像や動画のほか、json(辞書と呼ばれているのがそれです)があったり、ファイルのBase64エンコードがあったりします。更にWeb->WebリクエストというカテゴリにURLの内容を取得するというアクションがあるのですが、GET以外(POSTとかPUTとか)が扱えたりヘッダがいじれたりします。
そして、それらを効率よく扱うために、スクリプティングというカテゴリの中には変数もあります。
あとは、わかるな?
まぁこの辺りつつくと面白いんだけど適当な題材がわからなかったので次行きます。

Webページのスクショを撮りたい!

って、このくらいはアクションに用意されてるでしょ?と思ったら用意されていませんでした。。。。なので、Webページのスクショを撮るショートカットを作ります。
スクショくらいショートカットするもんでもないのでは?とお思いのみなさま、このスクショはただのスクショではございません。Webページの全体がjpegで保存されるという不思議なスクショでございます。そんなに不思議でもないかもしれませんが、普通にiOSのスクショを使っていると撮ることのできないスクショにございます。というわけで、ちょっとお付き合いいただけると嬉しいです。

具体的レシピ

さて、まずは左上にあるカテゴリから、Webを選択します。
そしたらWebリクエストというサブカテゴリに、『Webページの内容を取得』というものがあるので、それを右側のステージにドラッグします。入力という欄が空欄ですが、そこをタップすると下の方に変数、クリップボード、ショートカットの入力、毎回尋ねる、現在の日付などという入力に使えるリソースが表示されますので、今回は毎回尋ねるを設定します。
次に、Safariのサブカテゴリにある『Webページを表示』を選択し右にドラッグします。そこの入力には、上で取得したWebページの内容を設定しましょう。ちなみにこの『Webページを表示』をしないで直接取得したWebページの内容を次のPDF作成に回すと、JavaScriptで展開されるDOMとかが表示されないのでご注意です。
次に、トップに戻って左上の書類カテゴリから、プリントというサブカテゴリにある『PDFを作成』を選び右にドラッグします。入力は、Webページの内容です。
そうしたら、今度はメディアカテゴリからイメージというサブカテゴリの中の『イメージを変換』を選択し、PDFをJPEGに変換します。Webページの内容にもよりますが、PDFになった時点で数ページ分に別れたものになっています。それをJPEGにしてもやっぱり数ページ分に別れたものになってしまっているので、次にそれらを結合します。
今度はメディア->イメージ編集カテゴリ内の、イメージを結合を選択します。入力は変換済みのイメージで、縦方向に結合します。
最後に、メディア->写真カテゴリの『写真をアルバムに保存』で、結合済みのイメージを最近の項目に保存することで、スクショが写真に保存されるようになります。
完成したら、右下に小さくある実行ボタンを押してみましょう。無事に動くと思います。

完成したスクショはこちら

縮小してあります。
SS_Qiita.jpg
一つ難点を述べるとしたら、PDFで数ページ分に別れたものを結合しているので、一番下にページの余白ができてしまうことです。
うまいこと計算してこの余白を削除できたらなー、と思います。
あと、『Webページのを表示』するときにいちいち完了ボタンを押さなくてはならないのも自動化できないかなぁと。
それと、一番最初にURLは毎回尋ねるようにしてありますが、ここもお好みで変更したらもっと便利になると思います。

まとめ

これでWebページ全体がJPEGになっているという不思議なスクショが撮れました。iOS実機でのWebページのスクショを撮りたい!という需要がどれだけあるかはわかりませんが、最初の方にも書いた通りJSONやらPOSTやらも使えるくらい強力なアクションがまだまだ埋もれていますので、いい感じなショートカットはいくらでも作れるのではないかと思います。
Enjoy!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】デバイス(ローカル)にデータを保存する方法

iOSのアプリでは
重いリソース(大きいデータや画像など)を外部から毎回取得してくると
パフォーマンスや通信量に負担がかかってしまうということもあり
端末(ローカル)にデータを保存して
同じデータの場合は端末上のデータを利用することがあります。

そしてその中でも
データの種類や使用用途によって
保存方法や保存場所も変える必要があります。

これは
扱いやすさという点だけではなく
アプリ審査のリジェクトを防ぐという点でも
必要になってきます。

今回は
端末にデータを保存する方法にはどんなものがあるのか?
どうやってデータは保存されているのか?
どういうデータをどういう方法で保存する必要があるのか?

などについて見ていきたいと思います。

今回取り上げるのは下記の4つです。

  • UserDefaults
  • ディスク上のファイル
  • Keychain
  • Database

UserDefaults

アプリ内の
Library/Preferencesディレクトリに
plistファイルとしてデータを保存しています。

https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html#//apple_ref/doc/uid/TP40010672-CH2-SW1

データの読み書きは速いか?

ディスクへの書き込みが発生するため
それなりのコストはかかりますが
アプリ起動時にUserDefaultsはメモリ上に展開されるので
データの読み込みは速いです。

どういうデータを保存するか?

boolなどのプリミティブ型を使用して
アプリのユーザーの設定やユーザー体験を向上させるような
データを保存するのに向いています。

メモリに展開されるので
あまり大きなデータを保存してしまうと
端末メモリを圧迫してしまいます。

保存したデータはいつ削除されるか?

アプリが削除されると消えます。

注意点

UserDefaultsは値をそのまま保存しており
plistの中身を書き変えされてしまうリスクもあります。

そのため個人を特定できるようなセキュアな値を保存してはいけません。
(emailアドレスやパスワードなど)

使い方

UserDefaultsにはデフォルトのstandardという
staticなプロパティを利用することができます。

UserDefaults.standard.set(true, forKey: "isLoggedIn")
let isLoggedIn = UserDefaults.standard.bool(forKey: "isLoggedIn")

また
独自のUserDefaultsのインスタンスを生成することもできます。

let myUserDefaults = UserDefaults("suiteName: com.myapp.myUserDefaults")
myUserDefaults.set(true, forKey: "isLoggedIn")
let isLoggedIn = myUserDefaults.bool(forKey: "isLoggedIn")

詳細はドキュメントや多くの実装がありますので
そちらを参照してください。
https://developer.apple.com/documentation/foundation/userdefaults

WWDC2019では
Swift5.1から導入されたProperty Wrappersを利用して
簡単にアクセスできる例も紹介されていました。

https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#user-defaults
https://developer.apple.com/videos/play/wwdc2019/402

ディスク上のファイル

アプリ内の特定のディレクトリへ
ファイルとしてデータを保存することができます。

データの読み書きは速いか?

UserDefaultsに比べると
扱っているデータも大きく
読み込みは遅くなる傾向にあります。

どういうデータを保存するか?

ユーザーが作成したテキストやPDF
ダウンロードした画像や動画
ネットワークから受け取ったレスポンス(JSONをエンコードしたもの)
などUserDefaulsよりも大きな容量のデータを保存します。

保存したデータはいつ削除されるか?

アプリが削除されるのと同時に削除されます。

注意点

UserDefaultsと同様に保存されるデータは自動で暗号化されません。

端末内から見られるリスクに加え
iCloudを経由して見られてしまう可能性があります。

そのため個人を特定できるようなセキュアな値を保存してはいけません。
(emailアドレスやパスワードなど)

また保存するディレクトリによって保存できる
データの種類が変わってきます。
これに違反するとアプリの審査でリジェクトされる可能性があります。

まずは下記のような種類があります。
大まかにまとめると下記のようになります。

ディレクトリ 保存内容 データの読み込み データの書き込み iTunes、iCloudへのバックアップ
Documents ユーザ作成のコンテンツ(文書や画像、動画など)
Documents/Inbox 他のアプリから受け取ったファイル ○(削除のみ)
Library ユーザ作成以外に保存したいファイル ○(Cache以外)
Library/Cache キャッシュでなど自動削除されてもよいファイル ×
tmp アプリ起動中など一時的に使用するファイル ×

https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html

さらにガイドラインには下記のように書かれています。


1.
ユーザが作成した文書やその他のデータ
アプリで再生成できないようなデータはDocumentsディレクトリに保存する。


2.
再ダウンロードや再生成可能なデータはLibrary/Cachesディレクトリに保存する。

例:
漫画や雑誌、マップアプリなどで使われるデータベースのキャッシュファイルなど


3.
一時的に保存が必要なものはtmpディレクトリに保存する。
不要になった際には削除をして端末の空きスペースを圧迫させないこと。


4.
もし特定のファイルで端末の空きスペースが少ない場合でも
削除されないようにしたい場合は
"do not back up"属性を設定すること。
これはどのディレクトリにのファイルでも有効になる。

https://developer.apple.com/documentation/foundation/urlresourcekey/1408756-isexcludedfrombackupkey

ただし空きスペースを使用し続けているため
監視を続けて定期的に削除すること。

例:
再生成できるけどアプリを正しく動作させるのに必要なものや
オフライン時でもユーザが使用できるようにしたいものなど。


https://developer.apple.com/icloud/documentation/data-storage/index.html

使い方

FileManagerを使用します。

do {
    let fileManager = FileManager.default
    let docs = try fileManager.url(for: .documentDirectory,
                                   in: .userDomainMask,
                                   appropriateFor: nil, create: false)
    let path = docs.appendingPathComponent("myFile.txt")
    let data = "Hello, world!".data(using: .utf8)!

    fileManager.createFile(atPath: path.absoluteString,
                           contents: data, attributes: nil)
} catch {
    print(error)
}

Keychain

データの読み書きは速いか?

パフォーマンスが良くないといった情報は見つかりませんでしたが
暗号化や復号することを考えるとUserDefaultsと比べて多少はコストが増えると考えています。
(もしそういう情報がありましたら教えて頂けましたらうれしいです??‍♂️)

どういうデータを保存するか?

データを暗号化できるため
emailやOAuthのトークンなどセキュアな小さい情報を
保存するのみ主に使用されます。

保存したデータはいつ削除されるか?

アプリを削除してもデータは残ります。
削除をするためには自身でAPIを呼び出して削除する必要があります。

let status = SecItemDelete(query as CFDictionary)
guard status == errSecSuccess || status == errSecItemNotFound else { 
    throw KeychainError.unhandledError(status: status) 
}

https://developer.apple.com/documentation/security/keychain_services/keychain_items/updating_and_deleting_keychain_items

注意点

Keychainはプロビジョニングファイルと紐づけて暗号化されており
セキュリティは強固だとは思いますが
それでもと端末上にデータを保存するため
情報が見られてしまう可能性は考慮した方が良いかもしれません。

使い方

多くのKeychainのAPIはC言語ベースで書かれており
扱いづらい部分があります。

https://developer.apple.com/documentation/security/keychain_services

Appleはサンプルコードで利用方法を紹介していますが
かなり古いコードです。(Swift3)
https://developer.apple.com/library/archive/samplecode/GenericKeychain/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007797-Intro-DontLinkElementID_2

自身で扱いやすいラッパークラスを生成したり
すでに便利なライブラリもたくさんありますので
そちらを活用するのが良いかもしれません。

ライブラリ例:
https://github.com/kishikawakatsumi/KeychainAccess

Databases

Core DataやRealm、SQLiteなど
特定のフォーマットやアクセス方法で
端末上のデータを扱います。


Core Dataに関しては正確にはDatabaseというご意見もあるようですが
同じように扱われるという点から例として挙げさせて頂いています。
https://davedelong.com/blog/2018/05/09/the-laws-of-core-data/

データの読み書きは速いか?

データはDocumentsディレクトリに保存されることが多く
ディスク上のファイルへの読み書きと同様に
UserDefaultsに比べると時間がかかります。

どういうデータを保存するか?

大きいデータや
将来的にデータの数がどんどん増えそうなデータを
配列として保存することに適しています。

特に
クエリを利用して
特定条件のデータを取得できたり
データ同士の関連性を持つことができるため
構造化されたデータが扱いやすくなっています。

例:
APIから取得したJSONデータを
独自のモデルにデコードしたものなど

保存したデータはいつ削除されるか?

アプリが削除されると消えます。

注意点

データはDocumentsディレクトリに
保存されることが多く
データが見られてしまう可能性があるため
セキュアな情報を保存することには向いていません。

またスレッドセーフでない場合もあるため
複数スレッドから同時にアクセスする場合は
自分でコントロールする必要があります。

使い方

基本的にはSQLでアクセスするところを
利用しやすいようにラップしたAPIが提供されています。
具体的な利用方法はDatabaseによって異なりますので
APIのドキュメントなどをご参照ください。

Core Data
https://developer.apple.com/documentation/coredata

Realm
https://realm.io/docs/swift/latest/api/

SQLite
https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Index.md#sqliteswift-documentation

まとめ

端末(ローカル)にデータを保存する方法を見ていきました。
それぞれ特徴によって
保存するべきデータの種類が変わってきたり
保存先も変わってきます。

特にユーザを特定できるような個人情報に関しては
情報漏洩など犯罪に関わってしまうリスクもあるため
細心の注意が必要になります。

近年ではJWTなどを利用することで
個人情報を直接保持するリスクを
減らしていけるので
そういった仕組みは活用していきたいですね?

何か間違いなどございましたら
ご指摘して頂けますとうれしいです??‍♂️

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TestFlightを今更まとめてみた

こんにちわ。iOSアプリ開発をしている@haseken_devです。
iOS Advent Calendar 2019 の 10日目を担当させていただきました!

1. はじめに

この記事はAppleが提供するTestFlightについて、今更ながらまとめてみました。
今回のこの記事を書いてみようと思った経緯は、こんな感じです。

  • Flutterなど、マルチプラットフォームが採用される流れの中で、元々Androidアプリを開発していた同僚の方から、TestFlightについてよく相談されることがあった
  • (特に新卒入社など)iOSのアプリ開発担当者でもTestFlightを使う機会がなく、相談されることがあった

今更だとは思ったんですが、iOS 13で追加された新機能などもあったので、まとめてみようと思いました。
これから触ってみよう、利用を検討してみようとする方の参考になれば嬉しいです。

2. イメージする対象の方

本記事の対象者は下記のような方をイメージしています。

  • TestFlight聞いたことはあるが、具体的に何ができるのかをわかっていない方
  • これからTestFlightなどのbeta app配信サービスの利用を検討している方

3. 目次

  • TestFlightとは
  • TestFlightのテスター・ベータApp配布方法
    • 内部テスター
    • 外部テスター
    • パブリックリンク
  • iOS 13から追加されたTestFlight便利機能
    • テスト内容表示
    • スクリーンショット付きfeedback
    • クラッシュログ送信機能
  • TestFlightのメリット・デメリット
  • 最後に
  • 参考

3.1. TestFlightとは

TestFlightとは、Appleが提供するベータ版ビルドの配信サービスになります。
invite.PNG

招待されたテスターは、ベータ版ビルドをAppleが配信するTestFlight Appからインストールし、試すことが可能になります。
招待はEメールまたはパブリックリンクを介して受けることができます。

また、ベータ版ビルドがアップデートされた場合は、通知も届く様になるので、
更新し忘れがなくなり、配信側もわざわざ連絡する必要がありません。

また、TestFlightはiOS beta SDKでビルドしたappもテスト対象になります。
ただし、テスト可能になるにはiOS betaが配信されてから少し時間がかかる場合があります。
invite.PNG → invite.PNG
(招待メールの「View in TestFlight」をタップし、TestFlight Appで「同意する」をタップすればインストール可能になります。)

3.2. TestFlightのテスター・ベータApp配布方法

TestFlightのテスターは内部テスターと外部テスターの2種類存在します。
また外部テスターの配信方法は2つあります。

  • 内部テスター
    チーム内の最大25名までテストすることが可能です。
    また、各メンバーは最大30台のデバイスでテストが可能です。
    テスターはチーム内の下記の役割を割り当てられている必要があります。

    • Admin
    • Technical
    • App Manager
    • Developer
    • Marketer
  • 外部テスター
    チーム外のメンバーもテストが可能です。
    人数は最大10,000名までテスト可能です。
    ただし、テスト開始には、「App Store Reviewガイドライン」に従っているかどうかのAppleの審査が必要になります。

  • パブリックリンク
    パブリックリンクは2018年9月から利用可能になった、外部テスター向けの比較的新しい配信方法になります。
    リンクを公開するだけで、不特定多数のユーザーをテスターとして招待することが可能になります。
    そのため、これまでの方法ではメールアドレスを管理する必要があったのが、なくなりました。
    なお、リンクごとに招待できるユーザー数を設定することも可能です。
    ただし、こちらの方法も外部テスターと同様の審査が必要となります。

3.3. 配布方法まとめ

ここまでお話ししたテスターの種類を表でまとめてみます。

種類 内容 利用できそうなシーン
内部テスター ・チーム内の該当役割メンバー
・最大25名
・最大30台/名のデバイスでテスト可能
・社内でのbetaテスト
- 新規アプリとか新規機能
外部テスター ・チーム外の人向け
・最大10,000人
・Appleの審査が必要
・社外へのバイナリ配布
・ユーザーへのbeta機能・新規アプリのbeta版配布
パブリックリンク ・不特定多数のユーザー(人数制限可能、最大10,000人)
・メールアドレスの管理不要
・Appleの審査が必要
・外部テスターと同様?

それぞれテスターの種類に応じて、利用できるシーンがあるかと思います。
担当する案件ではどのテスター配布が良いか、ご検討ください。

ただ、内部テスター以外は審査もあり、少し手間と時間がかかること、そしてパブリックリンクはメールアドレスの管理が不要な反面、リンクが流出してしまうと、DLが容易にできてしまう点には、注意が必要そうです。

なお、最近配信されたドラクエウォークはTestFlightでbeta版が配信されていたようです。
このように、TestFlightを利用することで、配信前の機能やアプリをユーザーにテストしてもらうことも強みです。

4. iOS 13からのTestFlight便利機能

ここからは、iOS 13から追加されたTestFlightの便利機能を紹介していきます。

4.1. テスト内容表示機能

配信されたappを初回立ち上げ時に、テスト内容が表示される様になりました。
今までは、TestFlight app内で表示されていましたが、この機能によってより簡単にテスト内容が確認できる様になりました。
テストの内容はApp Store Connectから編集が可能です。
起動画面.PNG
(起動時に上記スクリーンショットの様にテスト内容が表示されます。)

4.2. ベータAppからのフィードバックの送信

これまで、TestFlight appからフィードバックを送ることができましたが、iOS 13からスクリーンショット付きのフィードバックをappから離れることなく送ることができる様になりました。

操作は簡単で、
1. appのスクリーンショットを撮る
2. 画面左下に出てくるサムネイルをタップし、必要に応じてマークアップでスクリーンショットを編集
3.「完了」->「ベータフィードバックを共有」をタップし、必要に応じてフィードバックコメントを編集して、「送信」を押すだけです。
App.PNG → MarkUp.PNG → Comment.PNG
(取り直したので、3枚目のマークアップがちょっと違います。ごめんなさい。。)

フィードバックはApp Store Connectから確認することが可能で、app Ver、ビルドVer、送り元のデバイス、iOS Verでフィルタが可能です。
またフィードバックには送り元の端末の情報が含まれているため、調査にも役立ちます。
(通信事業者、ディスク空き容量、バッテリー残量など)

4.3. クラッシュ情報の送信

iOS 13からクラッシュログ情報の送信方法も簡単になりました。
ベータappを試しているときにクラッシュすると、ホーム画面でクラッシュ情報を送信するかどうかのアラートが表示されます。
テスターはアラートの「共有」をタップし、必要に応じてコメントを追加して「送信」をタップするだけです。
crash.PNG → crash_comment.PNG

ログはApp Store Connectで確認が可能となり、デバイス情報が確認可能です。
また、クラッシュログファイルもDL可能なため、クラッシュした箇所の確認が簡単に行えることが魅力です。
beta機能等を試す上で、クラッシュフィードバックがすぐに送れる、また調査がより簡単に行えることはかなりの強みになると思います。

スクリーンショット 2019-12-09 2.22.54.png
(ログの一部になります。この様にクラッシュログをすぐに見ることができるため、調査が捗ります。)

5.TestFlightのメリット、デメリット

TestFlightのメリット、デメリットをまとめてみます。

  • メリット

    • テスト用に端末を
iOS Developer Centerに登録する必要がない
    • TestFlight Appがあれば、すぐにテストAppをインストールできる
    • クラッシュログやfeedbackを送りやすい、調査しやすい
    • テストappのアップデート通知がやりやすい
(Push通知が届く)
  • デメリット

    • Appleの外部テスター審査には1日程度かかる(ただし、審査はApp Verごとに必要
・ビルドVerごとには審査不要)
    • バイナリサイズによってはアップロードが遅い
    • JIRA連携とかができないので、ちょっと不便(App Store Connectのクラッシュログ閲覧→チケット発行とか本当はしたい・・・)
    • Appleのプラットフォームにしか配信できない

6. 最後に

TestFlightについて、iOS 13から登場した新機能を含めてまとめてみました。

TestFlightも頻繁にアップデートされ、機能も追加されており、個人的には結構好きでよく利用しています。
今後、さらにマルチプラットフォームな開発が進んで行くことも予想されますので、TestFlightでもマルチプラットフォーム配信対応されて欲しいな、と期待しております。

app開発者は様々なベータappの配信ツールがある中で、どれを利用するかの判断が求められると思いますが、判断材料として本記事が参考になれば幸いです。
今後のTestFlightのさらなるアップデートにも期待したいと思います!

7. 参考

TestFlightでAppをテストする
TestFlightでベータ版テストが簡単に

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

iOS開発完全に理解したのその一歩先へ...

この記事は CBcloud Advent Calendar 2019 4日目の記事です。
おそらくiOS/Androidエンジニアとして、CBcloudに2019/5/1から参画しましたが、
なぜか今はPM業のようなことをやっているentakuです。(この話は後日書こうとおもいます。)
念のため言っておくと、今の働き方に満足しているので、その辺は問題ないのです。

0. 自己紹介

entakuです。経歴は下記のようになっています。
なんちゃってSE
->NWエンジニア[SIer]
->
サーバーサイドエンジニア[SIer] ・java/PHP..etc
->
iOS/Android アプリ開発[スタートアップ]
->
Vue / Android アプリ開発 @ CBCloud

 最近は「ユーザーを動かす開発者」でありたいなと思います。

直近ではiOS開発が好きで下記のような活動をしていました。

一人iOSエンジニアでも気持ちよく開発できるiOSアプリ開発者になる!
iosの開発を始めたあの日の僕に伝えたいこと。

このQiitaでは「iOS開発完全に理解した」から、どのようにしてその先

1. 設計の知識を深める

iOS開発を行っていると、こういうときどう書けばいいんだ..という様々な場面に巡り会います。
これは公式で指標となりうる設計フレームワークが無いこと、また

僕は「iOSアプリ設計パターン入門」という本をiOSエンジニアの友人から勧められ購入しました。

特に第1章「設計するということ」今後のiOS開発において不変的な考え方が述べられていると思います 
また、設計をただ紹介するだけで無く、

  • 今までのやり方のどんな問題があり
  • その問題はなぜ起こっていて
  • どのようなアプローチで解決できるのか?

がまとめられている、iOS開発に欠かせない内容だと思います。

また

  • 今までのやり方のどんな問題があり の箇所は、 一度iOS開発をした方は共感する部分が多いと思いますし、一歩先に進むためにはよいものだと思っています!

詳細な内容はぜひ購入して読んでいただきたいです!

※peaksリンク iOSアプリ設計パターン入門

2. RxSwiftへの挑戦

なぜRxSwiftを使うのか?

正直僕自身もRxSwiftの名前に先行して利用していた部分がありますので、僕の理解の範囲で書きます

基本的にRxSwiftでは下記の2つの処理をViewModelで繋げます。

- ユーザーの動作(input)
- APIなどデータを取得する処理(output)

下記の例では僕が書いたViewModelの簡単な例を載せております。

「viewDidLoad」がViewControllerから呼び出された際に、「viewDidLoadSubject」内の処理が呼び出されます。
ViewControllerでは「teams」を監視した処理を「viewModel.teams」内に書いており、何を検知した際にどのような処理を行うのか?をわかりやすく分離してあります。

class SportTalkListViewModel {

    override func viewDidLoad() {
        super.viewDidLoad()


        viewModel.viewDidLoad.onNext(())
        bindViewModel()
    }
    func bindViewModel() {


        viewModel.teams
            .bind(to: tableView.rx.items(cellIdentifier: "SportTalkListCell", cellType: SportTalkListCell.self)) { _, element, cell in
                cell.team = element
            }.disposed(by: disposeBag)

    }

class SportTalkListViewModel {

    private let disposeBag = DisposeBag()

    // input property
    var viewDidLoad: AnyObserver<Void>


    // output property
    var teams: Observable<[Team]>


    init(provider: TeamsProviderProvider = TeamsProviderProvider()) {
        let teamsRelay:BehaviorRelay = BehaviorRelay<[Team]>(value: [])
        let viewDidLoadSubject = PublishSubject<Void>()

        teams = teamsRelay.asObservable()
        viewDidLoad = viewDidLoadSubject.asObserver()


        viewDidLoadSubject.asObservable()
            .flatMap{ _ in provider.getTeams() }
            .flatMap { teams -> Observable<[Team]> in

                return .just(teams.teams)
            }.bind(to: teamsRelay)
            .disposed(by: disposeBag)


    }


正直このようなシンプルな構成では効力を発揮しませんが、画面内で多くの「ユーザーの動作」と「APIなどデータを取得する処理」が存在する場合、処理の変更や追加がしやすいです。

どのようにしてRxSwiftを学んだか??

これは強く断言できますが、
RxSwift経験者にマンツーマンで教えてもらうことがもっとも良い方法です!
僕自身も経験者に聴きながら、書きながら覚えていきました。
RxSwiftは自分でパーツを作りながら、少しずつできるようになっていくようなイメージで取り組んでいます。
この自分のパーツを増やしていく感覚が大切かなと考えています。

とても拙いアプリではありますが、下記でRxSwiftを利用した簡単すぎるアプリを作りました
footballアプリ(GitHub)

3. SwiftUIへの挑戦

ついについにiOSにも宣言的UIの潮流がきました!
正直AndroidのXMLで書けるUIに長らく嫉妬していましたね...

1.HotReloadでUIがリアルタイムかつ複数パターン確認できるようになったこと
2.TableViewなど固有Viewではなくなったところ

下記はいままでTableViewCellで表されていたものの一例です。
HStack (X軸を基準として横並びにViewを並べるレイアウト)を使って比較的自由なレイアウトができます。

struct TeamRow: View {

    var team: Team

    var body: some View {
        HStack {
            team.image
                .resizable()
                .frame(width: 50, height: 50)

            Text(team.name)

            Spacer()
        }
    }
}

struct TeamRow_Previews: PreviewProvider {
    static var previews: some View {

        Group {
            TeamRow(team: sampleTeams[0])
            TeamRow(team: sampleTeams[1])
        }
        .previewLayout(.fixed(width: 300, height: 70))
    }
}

SwiftUI公式
SwiftUIでつくったもの

4. まとめ

今年は多くの時間をiOS開発に使えたとは言い難いですが、自分なりにできる範囲でiOS開発を楽しみました?

多くの時間をiOSアプリ開発に注力できなかったとしても、一歩先へ行けるように来年も楽しんでアプリ開発します

この記事がどこかのiOSアプリ開発者の新しい一歩に役に立つことができたらとてもうれしいです!
お気軽にご意見やコメントください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プロパティやメソッドの並び順テンプレート(Swift)

はじめに

SwiftLintには、クラスやプロパティなどの並び順を強制するルールがあります。

ルール名 概要
file_types_order ファイル内の型の並び順を強制する
type_contents_order 型内のプロパティやメソッドの並び順を強制する

これらのルールを適用することで、必然的にコードが整理されて可読性が上がります。

しかし、並び順を覚えるのは大変です。
ぱっと見でどこに何を書けば(書いてあるか)わかるよう、テンプレートを作成しました。

ルールの例からほぼ変えていないので、公式を見るだけでもいいと思います。
https://github.com/realm/SwiftLint/blob/0.38.0/Rules.md#file-types-order
https://github.com/realm/SwiftLint/blob/0.38.0/Rules.md#type-contents-order

自分ルール

同じグループの場合、アクセス修飾子の大きいものから順に書きます。
( openpublicinternalfileprivateprivate )

アクセス修飾子も同じ場合、読み下しやすい順に書きます。

class TestViewController: UIViewController {

    // MARK: Other Methods

    // privateよりinternalメソッドから先に書く
    func hoge() {}

    // 呼び出し側から先に書くと読み下しやすい
    private func foo() {
        bar()
    }

    private func bar() { }
}

準拠したプロトコルのメソッドは、 // MARK: {プロトコル名} としてOther Methodsの上に書きます。

// MARK: Protocols

protocol TestViewControllerDelegate: AnyObject {
    func didPressTrackedButton()
}

// MARK: Main Type

class TestViewController: UIViewController, TestViewControllerDelegate {
    // MARK: TestViewControllerDelegate

    func didPressTrackedButton() {
        // some code
    }

    // MARK: Other Methods

}

環境

  • SwiftLint:0.38.0

テンプレート

// MARK: Protocols

protocol TestViewControllerDelegate: AnyObject {
    func didPressTrackedButton()
}

// MARK: Main Type

class TestViewController: UIViewController, TestViewControllerDelegate {
    // MARK: Type Aliases

    // MARK: Classes

    // MARK: Structs

    // MARK: Enums

    // MARK: Stored Type Properties

    // MARK: Stored Instance Properties

    // MARK: Computed Instance Properties

    // MARK: IBOutlets

    // MARK: Initializers

    // MARK: Type Methods

    // MARK: View Life-Cycle Methods

    // MARK: IBActions

    // MARK: TestViewControllerDelegate

    // MARK: Other Methods

    // MARK: Subscripts

}

// MARK: Extensions

extension TestViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        return UITableViewCell()
    }
}

おわりに

Xcode上で新しくファイルを追加したら、まず上記のコメントをコピペするのがオススメです。
コーディング後、未実装のグループのコメントを削除すると見通しがよくなります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Bitrise CLIでiOSターゲットのUnityプロジェクトをアーカイブ

はじめに

こんにちわ、 @kiy0p0n です。
Diverse Advent Calendar 2019の9日目の記事です。

前書き

今回はUnityでモバイルゲーム開発の際のCIについての話題です。
JenkinsでiOS/Androidのデプロイを自動化する記事はたくさんあるのですが、サーバーレスでスクリプトを書かずとも手軽にCI始めたいなと思った際に、bitriseでiOS/Androidのデプロイを自動化できるのでは?
ということをちょっと調べて実際にやってみた内容を今回は記事としてまとめました。

やったこと

bitrise CLIを用いてiOSターゲットのUnityプロジェクトのビルド ~ ipa生成

環境

env version
OS macOS Catalina(10.15.1)
bitrise CLI 1.36.0
unity 2019.1
xcode 11.2

フロー

  1. bitrise CLIのインストール
  2. Unityプロジェクト
    • ビルドスクリプトの追加
    • プロジェクト設定
    • ビルドステップの追加
  3. ipaアーカイブステップの追加

内容

1. bitrise CLIのインストール

homebrewでインストールします。
その後、セットアップコマンドでツール群をインストールします。

$ brew update && brew install bitrise
# install log
$ bitrise -v
1.36.0
$ bitrise setup
# setup log

bitriseの処理は bitrise.yml に記述していきます。
次の内容の bitrise.yml を作成し、実行してみます。
workflows でワークフローを定義します。ワークフローはいくつかの処理を行うステップをまとめたものです。
以下での test はワークフロー名になります。実行時にワークフローを指定します。

format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

workflows:
  test:
    steps:
      inputs:
      - content: "echo 'Hello World!'"

testワークフローの実行結果
$ tree .
.
└── bitrise.yml
$ cat bitrise.yml
format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

workflows:
  test:
    steps:
    - script@1.1.5:
        inputs:
        - content: "echo 'Hello World!'"
$ bitrise run test

  ██████╗ ██╗████████╗██████╗ ██╗███████╗███████╗
  ██╔══██╗██║╚══██╔══╝██╔══██╗██║██╔════╝██╔════╝
  ██████╔╝██║   ██║   ██████╔╝██║███████╗█████╗
  ██╔══██╗██║   ██║   ██╔══██╗██║╚════██║██╔══╝
  ██████╔╝██║   ██║   ██║  ██║██║███████║███████╗
  ╚═════╝ ╚═╝   ╚═╝   ╚═╝  ╚═╝╚═╝╚══════╝╚══════╝

  version: 1.36.0

INFO[22:43:09] bitrise runs in Secret Filtering mode
INFO[22:43:09] Running workflow: test

Switching to workflow: test

+------------------------------------------------------------------------------+
| (0) script@1.1.5                                                             |
+------------------------------------------------------------------------------+
| id: script                                                                   |
| version: 1.1.5                                                               |
| collection: https://github.com/bitrise-io/bitrise-steplib.git                |
| toolkit: bash                                                                |
| time: 2019-12-08T22:43:11+09:00                                              |
+------------------------------------------------------------------------------+
|                                                                              |
Hello World!
|                                                                              |
+---+---------------------------------------------------------------+----------+
| ✓ | script@1.1.5                                                  | 2.56 sec |
+---+---------------------------------------------------------------+----------+


+------------------------------------------------------------------------------+
|                               bitrise summary                                |
+---+---------------------------------------------------------------+----------+
|   | title                                                         | time (s) |
+---+---------------------------------------------------------------+----------+
| ✓ | script@1.1.5                                                  | 2.56 sec |
+---+---------------------------------------------------------------+----------+
| Total runtime: 2.56 sec                                                      |
+------------------------------------------------------------------------------+


Submitting anonymized usage informations...
For more information visit:
https://github.com/bitrise-io/bitrise-plugins-analytics/blob/master/README.md

2. Unityプロジェクト

ビルドスクリプトの追加

Unityはコマンドラインから実行できようになっています。
Unity マニュアル - コマンドライン引数
マニュアルにライセンスのやりとり方法が書いてありますので、Proを使う場合は参考にしてください。

以下のコマンドで、プロジェクト内のスクリプトを実行できます。
スクリプトにビルド処理を記述することで、Unity Editorを起動せずにビルドを行うことができます。

$ /Applications/Unity/Unity.app/Contents/MacOS/Unity \
    -nographics \
    -quit \
    -batchmode \
    -logFile \
    -projectPath #{プロジェクトパス} \
    -executeMethod #{実行するスクリプトのクラス}.#{実行する関数}
    # 追加のオプションがある場合は続けて記述

ビルドを行うクラスを実装します。
ビルド先のオプションを -output で受け取れるようにしてます。

Assets/Bitrise.cs

#if UNITY_EDITOR
using UnityEditor;
using System.Linq;
using System;

class Bitrise
{
    public static void Build()
    {
        BuildPlayerOptions buildPlayerOptions = new BuildPlayerOptions();
        string[] args = Environment.GetCommandLineArgs();
        for (int i = 0; i < args.Length; i++)
        {
            if (args[i].Equals("-output"))
                buildPlayerOptions.locationPathName = args[i + 1];
        }
        var activeScenes = EditorBuildSettings.scenes.Where(s => s.enabled).Select(s => s.path).ToArray();
        buildPlayerOptions.scenes = activeScenes;
        buildPlayerOptions.target = BuildTarget.iOS;
        buildPlayerOptions.options = BuildOptions.None;
        BuildPipeline.BuildPlayer(buildPlayerOptions);
    }
}
#endif

バッチモードでのビルド実行
$ pwd
/Users/kiy0p0n/deploy-test
$ tree "Assets"
Assets
├── Bitrise.cs
├── Bitrise.cs.meta
├── Scenes
│   ├── Test.unity
│   └── Test.unity.meta
└── Scenes.meta

1 directory, 5 files
$ /Applications/Unity/Unity.app/Contents/MacOS/Unity \
    -nographics \
    -quit \
    -batchmode \
    -logFile \
    -projectPath /Users/kiy0p0n/deploy-test \
    -executeMethod Bitrise.Build
    -output /Users/kiy0p0n/deploy-test/Build
# build log

プロジェクト設定

Unity上でiOSの署名周りの設定をする場合は参考にしてください。
Unity上でなくとも、bitrise CLIのapiアーカイブのステップでも設定可能です。
わかりづらい項目だけピックアップして記載してます。
Project Settings > Player > iOS Traget(タブ選択) > Other Settings から署名に必要なパラメータを設定します。

Singing TeamID

Singing TeamID を設定します。TeamIDはApple Developer - Accountページから取得できます。
フォーマット例: AS345R67R1

iOS Provisioning Profile

任意のProvisioning Profileを設定する場合は、以下のどちらかでUUIDを設定できます。

  • Provisioning Profileをダウンロードし、 Browse ボタンでダウンロードした .mobileprovision ファイルを選択
  • Provisioning Profileをダウンロードし、テキストエディタで開き、 UUID の値を取得する

フォーマット例: c5be4123-1234-4f9d-9843-0d9be985a068

ビルドステップの追加

bitrise.yml にUnityプロジェクトのビルドのステップを追加します。

format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

workflows:
  test:
    steps:
    - script@1.1.5:
        inputs:
        - content: |
            #!/bin/bash
            /Applications/Unity/Unity.app/Contents/MacOS/Unity
                -nographics
                -quit
                -batchmode
                -logFile
                -projectPath /Users/kiy0p0n/deploy-test
                -executeMethod Bitrise.Build
                -output /Users/kiy0p0n/deploy-test/Build

3. ipaアーカイブステップの追加

bitriseのツールに xcode-achive というアーカイブの記述を簡易にできるモノがあるので利用します。
基本的な記述は以下のようになります。

    - xcode-archive@2.7.0:
        inputs:
        - output_tool: xcodebuild
        - export_method: development
        - output_dir: #{ipaの出力先}
        - project_path: #{xcodeproj or xcworkspaceのパス}
        - scheme: #{アーカイブするスキーマ}
        - team_id: #{署名時のチームID}
        - force_provisioning_profile: #{任意のProvisioning ProfileのUUID}
        # その他のオプション

team_idforce_provisioning_profile オプションは、Unity上で署名関連の設定が済んでいる場合は不要です。Team IDやProvisioning ProfileのUUIDは、プロジェクト設定と同じ形式のモノになります。
細かいその他オプションはソース元のステップを参照してください。

まとめ

最終的な bitrise.yml とワークフローの実行

$ cat bitrise.yml
format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git

workflows:
  test:
    steps:
    - script@1.1.5:
        inputs:
        - content: |
            #!/bin/bash
            /Applications/Unity/Unity.app/Contents/MacOS/Unity
                -nographics
                -quit
                -batchmode
                -logFile
                -projectPath /Users/kiy0p0n/deploy-test
                -executeMethod Bitrise.Build
                -output /Users/kiy0p0n/deploy-test/Build
    - xcode-archive@2.7.0:
        inputs:
        - output_tool: xcodebuild
        - export_method: development
        - output_dir: /Users/kiy0p0n/deploy-test
        - project_path: /Users/kiy0p0n/deploy-test/Build/Unity-iPhone.xcodeproj

        - scheme: Unity-iPhone
        - compile_bitcode: "no"
$ bitrise run test
# 以下ビルドログ

最後に

手元でコマンド一つでipaまでビルドできました!
BitriseにはdeploygateなどのデプロイツールへのアップロードやSlackといったチャットツールへの投稿を簡易に設定できるツールも用意されているので、色々利用してデプロイ周りを便利にしていきたいです。
また、Bitriseの公式Blogにて、BitriseでUnityビルドができる方法が紹介されてるので、今回の続きとして、ローカル環境ではなくクラウドで実行できるようにしたいです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UIButtonをaddSubViewする時はsuperviewを適切に選択すべし(戒め)

ある日のこと……

画像の上にボタンを置いて、ボタンタップ時に処理を追加しようとした。

作りたいもの

完成図

Simulator Screen Shot - iPhone 11 Pro Max - 2019-12-08 at 20.50.46.png

早速作ってみる

コードを書いた

これで完了!……と思うじゃん?

ViewController.swift
import UIKit

final class ViewController: UIViewController {
    private lazy var imageView: UIImageView = {
        let imageView = UIImageView()
        // 本当は画像を置いていたのだが、サンプル用のコードなのでbackgroundColorで妥協
        imageView.backgroundColor = UIColor.systemPink.withAlphaComponent(0.5)
        imageView.layer.cornerRadius = 16
        imageView.clipsToBounds = true
        imageView.translatesAutoresizingMaskIntoConstraints = false
        return imageView
    }()

    private lazy var button: UIButton = {
        let button = UIButton()
        button.setTitle("Please tap!!", for: .normal)
        button.setTitleColor(.blue, for: .normal)
        button.titleLabel?.font = .systemFont(ofSize: 24)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
        return button
    }()

    private func layoutImageView() {
        view.addSubview(imageView)
        NSLayoutConstraint.activate([
            imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            imageView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
            imageView.heightAnchor.constraint(equalTo: view.widthAnchor, multiplier: 9 / 16),
        ])
    }

    private func layoutButton() {
        imageView.addSubview(button)
        NSLayoutConstraint.activate([
            button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
    }

    @objc
    private func buttonTapped() {
        print("button tapped!!")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        layoutImageView()
        layoutButton()
    }
}

しかし…

⌘+Rを押してビルドされるのを待ち、いざ、ボタンをタップ!!
……あれ?コンソールに何も出てこないぞ??どうやらボタンがタップできていないらしい。
何が悪いんだ?

原因究明

UIViewには、isUserInteractionEnabledというプロパティがある

isUserInteractionEnabledはタップなどのイベントを有効にするかどうかのプロパティである。
で、このデフォルト値はtrueであったはず。なんたって、特にUIButtonはタップイベントを検知するためのUIパーツだし。

念のため、ドキュメントを確認しよう

isUserInteractionEnabled - UIView | Apple Developer Documentation

The default value of this property is true.

ですよねー。
……と思っていたら、下の項目にこんなことが。

Some UIKit subclasses override this property and return a different default value. See the documentation for that class to determine if it returns a different value.

なるほど。UIKitのサブクラスの中にはデフォルト値がtrueではないものがあると。

念のため、UIButtonのドキュメントも見てみましょう。
UIButton - UIKit | Apple Developer Documentation
当然ですが、isUserInteractionEnabledに関する項目はない。
うーん……?

DeveloperサイトをisUserInteractionEnabledでググってみた

スクリーンショット 2019-12-09 0.06.53.png

そういえば、UIButtonのsuperViewをUIImageViewにしたぞ……?
superViewをUIViewに変更して再度ビルドして試してみる。

-        imageView.addSubview(button)
+        view.addSubview(button)

今度はちゃんとタップが検出され、コンソールにbutton tapped!!と表示された!

何故ボタンのタップが検出されなかったか

理由が明記された文章は探した限りだと見つかりませんでした」
ただ、UIImageViewのsubViewだったUIButtonのタップが検出されなかったのに、UIViewのsubViewであったUIButtonのタップが検出された事実を考えれば、あるviewのisUserInteractionEnabledがfalseの場合、そのsubviewのisUserInteractionEnabledがtrueであってもタップは検出されなくなってしまうようです。

解決策

画像の上にボタンを置きたくなることはあると思います。その場合、簡単な対処法としては

  1. UIImageViewのisUserInteractionEnabledをtrueに変更する
  2. UIButtonのsuperViewをUIImageViewではないものにする

の2つが考えられます。どちらを選択するかは、Viewの構造によって適切に選択すると良いと思います。
上記の場合は、viewをsuperviewにする後者よりは、UIImageViewをsuperviewとする前者の方がよい気がします。
ちなみに今回私がハマったのは、UICollectionViewCell上に置いたUIImageViewとその上のUIButtonだったため、後者を選択しCellをsuperviewとしました。

Debug View Hierarchyだとどうしてもbuttonが最も手前に見えるので一瞬しっくりこないのですが、まあsubviewなのだからそういうものなのかもしれませんね?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む