foresthillのブログ

ホームページ・CMS制作からアプリ開発、ゲーム製作となんでもござれ。

【iPhoneアプリ】【Swift】SolPlayer開発記(5)再生機能を作り込む(シークバーと連動させる)

すべての音源をソルフェジオに変える音楽プレイヤー、
「SolPlayer」の開発記、第5弾。(ここまでコピペ)

前回の記事では、「iPhone内にあるファイルを読み込む方法」を解説しました。

foresthill.hatenablog.com


今回は、「再生機能を作り込み、シークバーと連動させる」方法を紹介します。

実装方法

思いのほか、この機能も苦労しました。

redmineの記録を観ると、予定3時間に対し、実際にかかった時間は14時間。
再生機能の実装も含めての時間ですが、「予定よりかなりかかったなぁ」という印象汗

まぁこれを読んでくれているあなたは30ふん以内でry


シークバーと連動させる

さて、それでは実装方法を紹介します。

例のごとく他力本願です。

asmz.hatenablog.jp

※一応、さくっとURLを紹介していますが、
このページにたどり着くまで結構時間かかってます汗

上記ページすべてをコピペ参考にできますが、
特に「任意の再生位置からの音声再生(シーク機能)」の情報が重要です。

一応、コードを提示。

画面側(ViewController)

毎秒ごとの処理
    //シークバー
    @IBOutlet weak var timeSlider: UISlider!
    //現在時刻
    @IBOutlet weak var nowTimeLabel: UILabel!
    //残り時刻
    @IBOutlet weak var endTimeLabel: UILabel!

    //タイマー
    var timer: NSTimer!

(中略)

    /** 
     初期処理
     */
    override func viewDidLoad() {

       //タイマーをセット
       timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(ViewController.didEverySecondPassed), userInfo: nil, repeats: true)

    }

(中略)

    /**
     毎秒ごとに行われる処理(timerで管理)
     */
    func didEverySecondPassed(){
        
        let current = solPlayer.currentPlayTime()
        
        //現在時刻と残り時刻を更新
        nowTimeLabel.text = formatTimeString(current)
        endTimeLabel.text = "-" + formatTimeString(Float(solPlayer.duration) - current)
        
        //スライダー値を更新
        timeSlider.value = current
    }


(中略)


    /**
     再生時間のスライダーが変更された時(Action→ValueChanged)
     - parameter sender: UISlider
     */
    @IBAction func timeSliderAction(sender: UISlider) {
        solPlayer.timeShift(timeSlider.value)
    }

処理(ロジック)側(独自クラス)

/** 
 SolffegioPlayer本体(音源再生を管理する)
 */
class SolPlayer {

    //プレイヤー
    var audioPlayerNode: AVAudioPlayerNode! = AVAudioPlayerNode()
    
    //音源ファイル
    var audioFile: AVAudioFile!

    //サンプルレート
    var sampleRate: Double!

    //時間をずらした時の辻褄あわせ
    var offset = 0.0

(中略)

    /**
     現在の再生時刻を返すメソッド
    */
    func currentPlayTime() -> Float {
                   
            //サンプルレートが0の時は再生時間を取得しない(というかできない)
            if(sampleRate == 0){
                return 0
            }

            //便宜上分かりやすく書いてみる
            let nodeTime = audioPlayerNode.lastRenderTime
            let playerTime = audioPlayerNode.playerTimeForNodeTime(nodeTime!)
            let nowPlayTime = (Double(playerTime!.sampleTime) / sampleRate)
            
            return (Float)(currentTime + offset)
                    
    }

    /**
     シークバーを動かした時の処理
     */
    func timeShift(current: Float){
        
        //退避
        offset = Double(current)                        
        
        //シーク位置(AVAudioFramePosition)取得
        let restartPosition = AVAudioFramePosition(Float(sampleRate) * current)
        
        //残り時間取得(sec)
        let remainSeconds = Float(self.duration) - current
        
        //残りフレーム数(AVAudioFrameCount)取得
        let remainFrames = AVAudioFrameCount(Float(sampleRate) * remainSeconds)
        
        audioPlayerNode.stop()
        
        if remainFrames > 100 {
            //指定の位置から再生するようスケジューリング
            audioPlayerNode.scheduleSegment(audioFile, startingFrame: restartPosition, frameCount: remainFrames, atTime: nil, completionHandler: nil)
        }
        
        audioPlayerNode.play()
                
    }


とりあえず、こんな感じです。
今回のテーマに関係ない個所は端折ってますので、
このまま書いても動かない可能性大です汗

なんとなく、書き方の雰囲気だけでも伝われば幸いです。
(全ソースコードは、今後GitHubで公開しようと思っています)

なお処理(ロジック)に関しては、便宜上わけてはいますが
好みによってはすべてViewControllerに記載しても構いません。

後で見返して、説明不足な点については今後追記したいと思います。

以上です。



次回は、「画面が表示されていない(または画面ロック)時にも、音楽を再生する方法」を紹介します。(要はバックグラウンド再生ですね)
お楽しみに。

【Xcode8対応】「Code signing is required for product type 'Application' in SDK 'iOS 10.0' 」とか出て困っている人へ

ソフトウェアアップデートしたら、
いつの間にかXcode8に。

で、Xcode8でビルドしたら「Code signing is required for product type 'Application' in SDK 'iOS 10.0' 」
とか出て、ビルドが通らない…

そんな人へ。

約二時間ほど探し回って、スタフロも4〜5個の記事見た結果…

Code signing is required for product type 'Application' in SDK 'iOS 10.0'
http://stackoverflow.com/questions/37806538/code-signing-is-required-for-product-type-application-in-sdk-ios-10-0-stic

stackoverflow.com


私は、これで動きました。
ホントありがとう!Thanks.って感じです。

参考になれば。

しかしAppleェ…orz

「Really apple?This was suppose to make our lives easier?」

ほんこれ。

【iPhoneアプリ】【Swift】SolPlayer開発記(4)iPhone内にあるファイルを読み込む方法

すべての音源をソルフェジオに変える音楽プレイヤー、
「SolPlayer」の開発記、第4弾。(ここまでコピペ)

前回の記事では、AVAudioEngineを使って曲を再生する方法を紹介しました。

前回の記事↓
foresthill.hatenablog.com

今回は、iPhone内にあるファイルを読み込む」方法を紹介します。

実装方法

私がアホなせいかもしれませんが、ここは意外と苦労しました。
redmineのチケット記録を観ると、予定工数が1に対し、実工数は6。(時間単位)

まぁこれを読んでくれているあなたは工数1以内で行けると思います^^b

さて、それでは実際の実装方法を紹介します。

メディアピッカー(MediaPicker)を使う

iPhone内にあるファイルを読み込む方法は、
ズバリ「メディアピッカー(MediaPicker)」を使うこと。

他にもいくつか方法はあるみたいですが、
ひと通り試した結果、自分にはこの方法が一番やりやすかったです。

このメディアピッカーを使うことで、読み込み処理を
自分で実装する必要がなくなります。

あとこちらでやるべきことは、
このメディアピッカーを呼び出す処理と、
そこから情報を取得する処理です。

さて、それでは例のごとく、他力本願でやっていきます。

nackpan.net

上記記事を参考に、SolPlayerに実装したコードが以下となります。

必要な実装としては、細かく言えば以下のようになります。

  1. クラスの定義に「MPMediaPickerControllerDelegate」を付け加える
  2. 以下の3つの処理を追加
    1. メディアピッカー(MediaPicker)を呼び出す
    2. メディアピッカー(MediaPicker)でファイルが選択された後
    3. メディアピッカー(MediaPicker)で「キャンセル」が押されたとき
1.クラス定義
import UIKit
import MediaPlayer
import AVFoundation
 
class ViewController: UIViewController, MPMediaPickerControllerDelegate {
2-1.メディアピッカーを呼び出す
    @IBAction func addSong(sender: UIButton) {
        //MPMediaPickerControllerのインスタンス作成
        let picker = MPMediaPickerController()
        //pickerのデリゲートを設定
        picker.delegate = self
        //複数選択を可にする(true/falseで設定)
        picker.allowsPickingMultipleItems = true
        //AssetURLが読み込めない音源は表示しない
        picker.showsItemsWithProtectedAssets = false
        //CloudItemsもAssetURLが読み込めないので表示しない
        picker.showsCloudItems = false
        //ピッカーを表示する
        presentViewController(picker, animated:true, completion: nil)
    }
    
2-2.メディアピッカーで選択された情報を取り出す
    //メディアアイテムピッカーでアイテムを選択完了した時に呼び出される(必須)
    func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {

        //playlistにmediaItemを追加
        mediaItemCollection.items.forEach { (mediaItem) in
            playlist?.append(mediaItem)
        }

        //ピッカーを閉じ、破棄する
        self.dismissViewControllerAnimated(true, completion: nil)
    }

mediaItemCollectionに選択されたファイルが、[MediaItem](MediaItemの配列)という形で入っています。
それを自作したプレイリスト(playlist)という配列型のデータに追加しています。

2-3.メディアピッカーで「キャンセル」された時
    //選択がキャンセルされた場合に呼ばれる
    func mediaPickerDidCancel(mediaPicker: MPMediaPickerController) {
        // ピッカーを閉じ、破棄する
        dismissViewControllerAnimated(true, completion: nil)
    }

補足

ただ、このメディアピッカーを使う際に、
いくつか補足や注意点があります。

ファイル読み込みに関する注意点

複数選択可にする

参考サイトでは複数選択を不可(false)にしていましたが、
使ってみた感じ、複数選択できたほうが何かと便利なので、
「true(可)」にしています。

//複数選択を可にする(true/falseで設定)
picker.allowsPickingMultipleItems = true
AssetURLを読み込めない音源を非表示にする

AssetURLを読み込めない音源ファイルは、そもそもAVAudioPlayerNodeで再生できません。
なので、ファイル自体を選択できないように、メディアピッカー上でも非表示にしちゃいます。

(ちなみに参考元のサイトでは、1つだけ音源を読み込み、その音源のAssetURLがnilの場合
再生しないという挙動にしているため、エラーは起きないようになっています)

//AssetURLが読み込めない音源は表示しない
picker.showsItemsWithProtectedAssets = false

//CloudItemsもAssetURLが読み込めないので表示しない
picker.showsCloudItems = false

MediaItemに入っている情報

メディアピッカーでファイルを選択すると
MediaItemというクラスで取得できます。

この、MediaItemに入っている情報と、
入っていない情報があるので、

取得できる情報
  • assetURL(音源のURL)※一番重要
  • persistentID(ファイルを一意に特定するID)
  • title(タイトル)
  • artist(アーティスト名)
  • albumTitle(アルバム名)
  • artwork(アートワーク:画像)
  • その他もろもろ
取得できない情報
  • 再生時間

「再生時間こそ欲しいんだよ!」ってところで
イライラしたつまづいたので、ここにその実装方法を記しておきます。

呼び出しメソッド

    func getDuration() -> Double {
        
        if self.assetURL != nil {
            do {
                let audioFile = try AVAudioFile(forReading: assetURL!)
                
                //サンプルレートの取得
                let sampleRate = audioFile.fileFormat.sampleRate
                
                //再生時間
                return Double(audioFile.length) / sampleRate
                
            } catch {
               //
            }
        }
        
        return 0.0
        
    }

これを、再生時間を入れたい変数(フィールド)に入れます。

(実装例)

self.duration = getDuration()


以上、「iPhone内にあるファイルを読み込む方法」でした。


おまけ

ちなみに、実際にSolPlayerに実装した内容は、
もう少しカスタマイズしています。

    //メディアアイテムピッカーでアイテムを選択完了した時に呼び出される(必須)
    func mediaPicker(mediaPicker: MPMediaPickerController, didPickMediaItems mediaItemCollection: MPMediaItemCollection) {

        //AppDelegateのインスタンスを取得しplayListを格納
        let appDelegate:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate

        //playlistにmediaItemを追加
        mediaItemCollection.items.forEach { (mediaItem) in
            playlist?.append(Song(mediaItem: mediaItem))
        }

        appDelegate.playlist = playlist

        //print("playlist=\(playlist)")

        //ピッカーを閉じ、破棄する
        self.dismissViewControllerAnimated(true, completion: nil)

        //tableviewの更新
        tableView.reloadData()

    }

付け加えた処理としては、

  1. playlistをAppDelegate上で持つようにした → 画面をまたいでplaylistが使える
  2. mediaItemをSongという自作クラスで詰め替えた → 機能を追加する際にもろもろ便利
  3. tableViewを更新 → playlistの内容がtableViewに表示されるようにしているため

こちらに関しては、もし気が向いたら詳細を解説したいと思います。
興味がある方はコメントください。

以上です。

【iPhoneアプリ】【Swift】SolPlayer開発記(3)AVAudioEngineでリアルタイムでピッチを変更する

 

SolPlayer

SolPlayer

  • Naoya Morioka
  • ミュージック
  • 無料

 

すべての音源をソルフェジオに変える

SolPlayerの開発記3回目。

 

前回の記事はこちら↓

foresthill.hatenablog.com

 

 

今回は、このアプリのキモである

「ソルフェジオモード」の実装方法について解説します。

 

このソルフェジオモード、前回も紹介したとおり

一言で言えば「ピッチを特定の周波数に変換する」機能です。

 

 

 

課題は2つあります。

(1)技術的にどう実装するか

(2)ピッチをどれだけ変更すればよいのか

 

 

これを、一つ一つ具体的に解説していきます。

 

(1)技術的にどう実装するか

以下の3つの手順で実装します。

1.ピッチを変換する部品(クラス)をつくる

2.各部品(クラス)をつなぐ

3.再生する

 

前回紹介したサイトのコードを

dev.classmethod.jp

 

1.ピッチ変換するメソッドを作る

まず最初に、ピッチ変換をするためのメソッドを作ります。

このメソッドは、簡略に言えば「画面上のスイッチを押された時にピッチを変更する」という処理を行うものです。

※今回の説明では、画面側の処理とかはすべて省略します。

 

具体的には、AVAudioUnitTimePitchというクラスの変数(timePitch)を作ります。

そして、timePitchのフィールド「.pitch」に、ピッチ変更する値を指定します。

 

    var timePitch: AVAudioUnitTimePitch! = AVAudioUnitTimePitch()
    
    (中略)
    
    /**
     ソルフェジオモードon/off(ピッチ変更)処理
     */
    func pitchChange(solSwitch: Bool){
        
        if(solSwitch){
            switch userConfigManager.solMode {
            case 1:
                timePitch.pitch = 15.66738339053706   //17:440Hz→444.34Hz 16:440Hz→444.09Hz
                break
            case 2:
                timePitch.pitch = -31.76665363343202   //-32:440Hz→431.941776Hz
                break
            default:
                timePitch.pitch = 0
            }
        } else {
            timePitch.pitch = 0
        }
        
    }
    

 

 指定している値は「15.66738339053706」「-31.76665363343202」「0」の3つ。

 

「0」はなんとなく想像がつくかと思いますが(デフォルト値)、 前述の二つは何の数字なのか?なぜこの値なのか?という疑問が出てくると思います。 これについては後述します。

 

2.つなぐ

 AudioEngineで音を出力するために、各部品(クラス)を「つなぐ」という作業が必要です。

「つなぐ」ことで、読み込んだ音源にエフェクトを加えて(今回はピッチを変えて)出力することができます。

 

実際のコードですが、前回紹介したサイトでは以下のような記述がありました。これは、エフェクトを入れずにつないだ例です。

 

 Player Node → Mixer Node → Output Node   

    // [2] AVAudioPlayerNode オブジェクトを準備する
    self.audioPlayerNode = [AVAudioPlayerNode new];
    [self.engine attachNode:self.audioPlayerNode];
    
    // [3] Node 同士を繋ぐ
    AVAudioMixerNode *mixerNode = [self.engine mainMixerNode];
    [self.engine connect:self.audioPlayerNode
                      to:mixerNode
                  format:self.audioFile.processingFormat];

 

これをSwiftで書き換えるとこうなります。

//AVKit
    var audioEngine: AVAudioEngine!
    var audioPlayerNode: AVAudioPlayerNode!
    var audioFile: AVAudioFile!
    
    (中略)

    // [2] AVAudioPlayerNode オブジェクトを準備する
    self.audioPlayerNode =  AVAudioPlayerNode()
    self.audioEngine.attachNode(audioPlayerNode)
    
    // [3] Node 同士を繋ぐ
    let mixerNode: AVAudioMixerNode = self.audioEngine.mainMixerNode
    self.audioEngine.connect(audioPlayerNode, to:audioEngine.mainMixerNode, format:self.audioFile.processingFormat)
    
    
    

注)上記の例ではAudioEngineの変数をengine → audioEngineに変えています

 

これで音源を再生することができますが、今回はソルフェジオ変換(ピッチ変換)処理を加えたいので、ここにpitch変化するための処理を挟みこみます。 

 

 Player Node → ピッチ変換(AVAudioUnitTimePitch) →Mixer Node → Output Node   

//AVKit
    var audioEngine: AVAudioEngine!
    var audioPlayerNode: AVAudioPlayerNode!
    var audioFile: AVAudioFile!
    var timePitch: AVAudioUnitTimePitch! //「1.ピッチ変換」の変数
    
    (中略)

    // [2] AVAudioPlayerNode オブジェクトを準備する
    self.audioPlayerNode =  AVAudioPlayerNode()
    self.audioEngine.attachNode(audioPlayerNode)
    
    // [3] Node 同士を繋ぐ
    let mixerNode: AVAudioMixerNode = self.audioEngine.mainMixerNode
    self.audioEngine.connect(audioPlayerNode, to:timePitch, format:self.audioFile.processingFormat)
    self.audioEngine.connect(timePitch, to:audioEngine.mainMixerNode, format:self.audioFile.processingFormat)
    
    

 

こうすることで、エフェクトがかかった(ピッチ変換された)音が出力されるようになります。

 

以下、上記と同じ処理を若干汎用性を高めた記述を紹介します(もっとキレイな書き方があれば教えて下さい)

    //AVKit
    var audioEngine: AVAudioEngine!
    var audioPlayerNode: AVAudioPlayerNode! = AVAudioPlayerNode()
    var audioFile: AVAudioFile!
    
    (中略)

        audioPlayerNode = AVAudioPlayerNode()

        //アタッチリスト
        var attachList:Array = [audioPlayerNode, reverbEffect, timePitch]
        
        //AVAudioEngineにアタッチ
        for i in 0 ... attachList.count-1 {
            audioEngine.attachNode(attachList[i])
            if(i >= 1){
                audioEngine.connect(attachList[i-1], to:attachList[i], format:audioFile.processingFormat)
            }
        }
        //ミキサー出力
        audioEngine.connect(attachList.last!, to:audioEngine.mainMixerNode, format:audioFile.processingFormat)
        


 

アタッチリストにエフェクト(他にもリバーブなどがあります)を加えていけば、AudioPlayerNodeで出力される際に付加するエフェクトを追加できます。

var attachList:Array = [audioPlayerNode, reverbEffect, timePitch]

 

3.再生

上記が完了したら、audioEngineを起動した後(audioEngine.start)、audioPlayerNodeを再生(audioPlayerNode.play)します。



(中略)

//AVAudioEngineの開始
audioEngine.prepare()
do {
try audioEngine.start()
audioPlayerNode.play()
} catch {
}

 

これで、ピッチ変換された音が再生できます。 

 

(2)ピッチをどれだけ変更すればよいのか

さて、それでは上記の記事内ででてきたピッチ変換の値「15.66738339053706」「-31.76665363343202」について説明します。

 

一般に、音声編集ソフト(Audacity等)を使ったピッチ変換では、「変換前の周波数(440Hz)」と「変換後の周波数(444Hz)」をヘルツで指定するだけで、ピッチを変換することができます。

 

が、今回ピッチ変換に使用したAVAudioUnitTimePitchというクラスでは、「ヘルツ(Hz)」ではなく、「セント」という単位を使います。

 

この「セント」が曲者で、けっこう計算式が複雑なんですね。

詳細は以下記事をご覧ください。 

 

何セント変えればソルフェジオ周波数になるのか?(セント/ヘルス変換)

100セントで半音、
1200セントで1オクターブとのこと。

セント・ヘルツ 換算表 (cent/Hz)

b3a4s4s.web.fc2.com

 

b3a4s4s.web.fc2.com

 

ざっくりと説明すると、「1200セントで1オクターブ変わる」んですが。。

1セントは「1/1200」ではなく、「log(1200)」(1200乗すると2倍になる)んです。

(計算式が複雑になるため、詳細は割愛します汗)

 

で、上記のようなサイトを参考に計算した結果、

 

・440→444Hzに変換するためには「15.66738339053706」セント

・440→432Hzに変換するためには「-31.76665363343202」セント

 

ピッチをずらす必要があることが判明しました。

 

これが、(1)で出てきた謎の値の種明かしです。

 

※432Hzについては、今後どこかで解説するかもしれません

 

 

 

 …とりあえず、今回の記事はこんな感じです。

 

記載が至らない箇所はいくつもあるかと思いますが、

不明点などあればお気軽にコメントいただければ幸いです。

  

次回は、、どうしましょう?汗 未定です。。

とりあえず、引き続きAVAudioEngine等を使った開発に関する記事を掲載します。

 

それでは、また。 

 

 

追記(2016/09/24)

ちなみに、ピッチではなく再生速度を変更する際も、全く同じクラス(AVAudioUnitTimePitch)でできます。

 

具体的には、AVAudioUnitTimePitchのフィールド「.rate」に、ピッチ変更する値を指定します。

 

(実装例)

    var timePitch: AVAudioUnitTimePitch! = AVAudioUnitTimePitch()
/** 再生スピード変更処理 - parameter speedSliderValue(画面の再生速度スライダーから) */ func speedChange(speedSliderValue: Float){
timePitch.rate = speedSliderValue }

 

【iPhoneアプリ】【Swift】SolPlayer開発記(2)AVAudioKitで音声ファイルを再生する

SolPlayer

SolPlayer

  • Naoya Morioka
  • ミュージック
  • 無料

 

すべてをソルフェジオ音源に変えるiPhoneアプリ

「SolPlayer」(ソルプレイヤー)をリリースしました。

 

前回の記事はこちら↓

foresthill.hatenablog.com

 

前回のおさらいは以下です。

・ソルフェジオ化とは、すなわち440Hz→444Hz(432Hz)へのピッチ変換

・実現するには、iPhone上でピッチ変換する必要がある

iPhoneアプリ開発においては、「AVAudioEngine」というすごいエンジンが用意されている。

・上記エンジンを使えば、iPhone上でピッチ変換することが可能

 

まずはAVAudioEngineで音楽を再生する

さて。それではAVAudioEngineを使って

実際にソルフェジオを実装する方法を紹介します。

 

AVAuidoEngineを使えば、iPhone上でピッチ変換し、

ソルフェジオ化することが可能です。

 

…が、そもそも音源を再生できなければ話になりません。

再生機能がないと、せっかくソルフェジオ音源に変換しても聴けませんからねorz。

 

というわけで、まずはAudioEngineで

音源を再生する方法を紹介します。

 

AVAudioKit - 基本は3点セット

AVAudioEngineを使って再生するためには、

基本的には「AVKit」というライブラリを使います。

 

このライブラリの中で、

・AVAudioEngine

・AVAudioSession

・AVAudioPlayerNode

という3つのクラスを主に使って、

音楽を再生していきます。

 

詳しくはこちら。

dev.classmethod.jp

  

※また、これからは「AVAudioEngine」を使って再生、という表現ではなく、

「AVAudioKit」を使って再生」という表現に変えていきます。

 

AVKitを使った再生方法について

さて、それではこのAVAudioEngineもとい

AVKitを使った音楽再生をしてみましょう。

dev.classmethod.jp

 

(「他人の記事の紹介ばっかじゃねーか!」という感じですが。。

基本、他人のフンドシで相撲を取るのがスタイルです笑)

 

まずは上記サイトのソースコードをそのまま書いてみてください。

AVKitで再生することができるはずです。

 

とりあえず[1]〜[8](実際に使うのは[1]〜[6])までやってみてください。

 

なお、[9]以降の「実装(バッファから再生する)」については

今回の開発記では解説しません。

 

 

なぜ、AVPlayerViewControllerを使わないのか?

なお、Swiftには「AVPlayerViewController」というクラスが用意されていて、

一瞬でオーディオプレイヤー機能を実装することができます。

 

www.dorapro.co.jp

 

ただ、このやり方だと今回の「AVAudioEngine」を使った

リアルタイムでのピッチ変換ができないっぽいんです。

 

(ただ自分が紐つけ方がわからなかっただけで、

実際はできるかもしれませんが汗)

 

なので、今回は「AVPlayerViewController」は使わず、

機能も画面も、独自で一から実装していく方法を取ります。

 

次回は本当に解説します笑

さて、再生もできたことですし(何も解説してないけどね^^;)

次回は「ソルフェジオモード」の実装方法を解説します。

(次回はちゃんと解説します笑)

 

お楽しみに。

 

 

すべてをソルフェジオ音源に変えるiPhoneアプリ「SolPlayer」開発記(1)

 

SolPlayer

SolPlayer

  • Naoya Morioka
  • ミュージック
  • 無料

 

すべてをソルフェジオ音源に変えるiPhoneアプリ
「SolPlayer」(ソルプレイヤー)をリリースしました。
 
iPhoneアプリ(Swift)開発者のため、
今回から何度かに分けて記事にしたいと思います。

「SolPlayer」について

「SolPlayer」の名前は、
「Solfeggio Player(ソルフェジオ・プレイヤー)」を縮めたもの。
 
ヒーリング音楽などではすでに取り入れられている「ソルフェジオ音源」ですが、
ロックやポップス、レゲエやヒップホップ、R&Bそしてクラシックなどでは
まだまだ「ソルフェジオ音源」でというのはなかなかないです。
 
その「ソルフェジオ音源」を、
すべての音楽ジャンルで聴けるようにしたい!
 
というのが、「SolPlayer」開発のきっかけです。
 

そもそも「ソルフェジオ」とは何か? 

冒頭でも紹介しましたが、このアプリのコンセプトは
すべてをソルフェジオ音源に変えるiPhoneアプリ
 
でも、そもそも「ソルフェジオ」って何?
と思われているかもしれません。
 
なので、まずは「ソルフェジオ」について
解説したいと思います…が、
 
あまりくどくど語るのも面倒くさ本題からずれてしまうので、
以下の記事を紹介するにとどめておきます。
 
 
 
 
ざっくりいえば、
 
「現代のA=440Hz基準で作られた音楽ではなく、
基準をA=444Hz(C=528Hz)、あるいはA=432Hzにすることで、
人の耳・身体・DNAにとって良い影響をおよぼす音楽(音源)」。
 
それが「ソルフェジオ音楽(音源)」です。
 
こんな書籍もあるので、もし興味があれば読んでみてください。 
ジョン・レノンを殺した凶気の調律A=440Hz 人間をコントロールする「国際標準音」に隠された謀略() (超知ライブラリー 73)

ジョン・レノンを殺した凶気の調律A=440Hz 人間をコントロールする「国際標準音」に隠された謀略() (超知ライブラリー 73)

 

 

 

ソルフェジオモードの実装方法 

では、一般的な音源を「ソルフェジオ音源」に変換するために、
一体何をすればよいのか?
 
いきなり、そのネタばらしします。
 
こちらを御覧ください。
 
 
何をやっているか?
 
それは、
「ピッチを変えて、440Hz→432Hzとなるように音の高さをに変換している」
というだけ。 実にシンプル。
 
ただ、毎回毎回Audacityを立ち上げて変換するのは面倒…
って思いませんか?たった数ステップですが…それでも、
何十、何百という音源を変換するとなると、けっこう大変です。
’少なくとも私は面倒です笑)
 
そこで!なんとか楽できないか…ということで
作ったのが今回の「SolPlayer」…というわけです。
 

iPhoneアプリ上でピッチ変換することができるのか?

ソルフェジオ機能を実装するということは、
つまり「ピッチ変換機能」を実装するということ。
 
しかし、そもそもできるのか?
自分も、手をつけるまでは不安でした。
 
結論から言うと、「できます」。
 (リリースしてるんだから当然ですが)
 
ただ、上記で紹介したソフト、
Audacityだとこの「ピッチ変換」に十秒以上かかります。
 
しかも、ある程度高機能な音声編集ソフトに
搭載されている「ピッチ変換」機能…
 
それをiPhone上で、ピッチ変換できるのか? 
できるのであれば、どう実現するか…という点
 
「もしかしたら、ピッチ変換機能を実現する
音楽再生のエンジンを自作しなければならないのでは?」
 
そんな不安も抱いていました。
 
というのも、アプリ開発において、
フレームワークを使ってアプリを作る(一般的なアプリ開発)」というのと、
「映像・音楽のエンジンを作る」というのでは、難易度が桁違いだからです。
 
車の製造に例えるなら、

・一般のアプリ開発は、車のパーツを組み立てる「ライン工」
・映像・音楽のエンジンを作る人は、まさに車のエンジンをつくる「エンジニア」
それも、5〜10年以上の知識と経験がある、めちゃめちゃ腕のよいエンジニア(たとえば年収1000万円超えるような)」
 
という感じです。
難易度が桁違いに違うわけです。
 

エンジンは既に用意されていた!!

しかしながら…予想に反して、
それは、驚くほどアッサリ解決しました。 
 
いろいろ調べた結果、
「AVAudioEngine」というクラスを使えば、
超簡単に実現できることが判明しました。
 
かなり綺麗ににピッチが変更できます。
しかもリアルタイムに!です。
 
Protools」というプロ御用達のDAWソフトでも、
リアルタイム変換ではないのに、
 
このiPhone上で動く「AVAudioEngine」は、
ほとんど劣化することなく、
リアルタイムにピッチ変換できるんです!!
 
これはAppleの技術の粋を集めたものです。
 
おそらく、開発に数千万〜数億円、
期間にして半年〜1年以上はかかっているのではないでしょうか。
 
それを、われわれiPhone開発者は自由に使えるわけです。
驚いてください!
 
(この驚きを、この凄さを伝えきれないのがもどかしい。。)
 
…とまあ、ちょっと熱くなってしまいましたが汗
 
次回以降、この「AudioEngine」を使って
リアルタイムにピッチ変換、ひいては「ソルフェジオ」機能を実装する方法を紹介します。
 
お楽しみに!