コードシンタックス用記事。
aaa <| はてなブログの見たままモードでシンタックスハイライトを使う方法 www.dreamark.tokyo.hatena{ color:#fff; border:solid 1px #ccc; }public hatena() { system.out.println("aa"); }見たままじゃないほうがよくね?
ああ、書きやすい、書きやすい。
- あ
- q
- e
- a
- a
- b
【iPhoneアプリ】【Swift】SolPlayer開発記(5)再生機能を作り込む(シークバーと連動させる)
すべての音源をソルフェジオに変える音楽プレイヤー、
「SolPlayer」の開発記、第5弾。(ここまでコピペ)
前回の記事では、「iPhone内にあるファイルを読み込む方法」を解説しました。
今回は、「再生機能を作り込み、シークバーと連動させる」方法を紹介します。
実装方法
思いのほか、この機能も苦労しました。
redmineの記録を観ると、予定3時間に対し、実際にかかった時間は14時間。
再生機能の実装も含めての時間ですが、「予定よりかなりかかったなぁ」という印象汗
まぁこれを読んでくれているあなたは30ふん以内でry
シークバーと連動させる
さて、それでは実装方法を紹介します。
例のごとく他力本願です。
※一応、さくっと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
私は、これで動きました。
ホントありがとう!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)」を使うこと。
他にもいくつか方法はあるみたいですが、
ひと通り試した結果、自分にはこの方法が一番やりやすかったです。
このメディアピッカーを使うことで、読み込み処理を
自分で実装する必要がなくなります。
あとこちらでやるべきことは、
このメディアピッカーを呼び出す処理と、
そこから情報を取得する処理です。
さて、それでは例のごとく、他力本願でやっていきます。
上記記事を参考に、SolPlayerに実装したコードが以下となります。
必要な実装としては、細かく言えば以下のようになります。
- クラスの定義に「MPMediaPickerControllerDelegate」を付け加える
- 以下の3つの処理を追加
- メディアピッカー(MediaPicker)を呼び出す
- メディアピッカー(MediaPicker)でファイルが選択された後
- メディアピッカー(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() }
付け加えた処理としては、
- playlistをAppDelegate上で持つようにした → 画面をまたいでplaylistが使える
- mediaItemをSongという自作クラスで詰め替えた → 機能を追加する際にもろもろ便利
- tableViewを更新 → playlistの内容がtableViewに表示されるようにしているため
こちらに関しては、もし気が向いたら詳細を解説したいと思います。
興味がある方はコメントください。
以上です。
【iPhoneアプリ】【Swift】SolPlayer開発記(3)AVAudioEngineでリアルタイムでピッチを変更する
すべての音源をソルフェジオに変える
SolPlayerの開発記3回目。
前回の記事はこちら↓
今回は、このアプリのキモである
「ソルフェジオモード」の実装方法について解説します。
このソルフェジオモード、前回も紹介したとおり
一言で言えば「ピッチを特定の周波数に変換する」機能です。
課題は2つあります。
(1)技術的にどう実装するか
(2)ピッチをどれだけ変更すればよいのか
これを、一つ一つ具体的に解説していきます。
(1)技術的にどう実装するか
以下の3つの手順で実装します。
1.ピッチを変換する部品(クラス)をつくる
2.各部品(クラス)をつなぐ
3.再生する
前回紹介したサイトのコードを
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)
ざっくりと説明すると、「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で音声ファイルを再生する
すべてをソルフェジオ音源に変えるiPhoneアプリ、
「SolPlayer」(ソルプレイヤー)をリリースしました。
前回の記事はこちら↓
前回のおさらいは以下です。
・ソルフェジオ化とは、すなわち440Hz→444Hz(432Hz)へのピッチ変換
・実現するには、iPhone上でピッチ変換する必要がある
・iPhoneアプリ開発においては、「AVAudioEngine」というすごいエンジンが用意されている。
・上記エンジンを使えば、iPhone上でピッチ変換することが可能
まずはAVAudioEngineで音楽を再生する
さて。それではAVAudioEngineを使って
実際にソルフェジオを実装する方法を紹介します。
AVAuidoEngineを使えば、iPhone上でピッチ変換し、
ソルフェジオ化することが可能です。
…が、そもそも音源を再生できなければ話になりません。
再生機能がないと、せっかくソルフェジオ音源に変換しても聴けませんからねorz。
というわけで、まずはAudioEngineで
音源を再生する方法を紹介します。
AVAudioKit - 基本は3点セット
AVAudioEngineを使って再生するためには、
基本的には「AVKit」というライブラリを使います。
このライブラリの中で、
・AVAudioEngine
・AVAudioSession
・AVAudioPlayerNode
という3つのクラスを主に使って、
音楽を再生していきます。
詳しくはこちら。
※また、これからは「AVAudioEngine」を使って再生、という表現ではなく、
「AVAudioKit」を使って再生」という表現に変えていきます。
AVKitを使った再生方法について
さて、それではこのAVAudioEngineもとい
AVKitを使った音楽再生をしてみましょう。
(「他人の記事の紹介ばっかじゃねーか!」という感じですが。。
基本、他人のフンドシで相撲を取るのがスタイルです笑)
まずは上記サイトのソースコードをそのまま書いてみてください。
AVKitで再生することができるはずです。
とりあえず[1]〜[8](実際に使うのは[1]〜[6])までやってみてください。
なお、[9]以降の「実装(バッファから再生する)」については
今回の開発記では解説しません。
なぜ、AVPlayerViewControllerを使わないのか?
なお、Swiftには「AVPlayerViewController」というクラスが用意されていて、
一瞬でオーディオプレイヤー機能を実装することができます。
ただ、このやり方だと今回の「AVAudioEngine」を使った
リアルタイムでのピッチ変換ができないっぽいんです。
(ただ自分が紐つけ方がわからなかっただけで、
実際はできるかもしれませんが汗)
なので、今回は「AVPlayerViewController」は使わず、
機能も画面も、独自で一から実装していく方法を取ります。
次回は本当に解説します笑
さて、再生もできたことですし(何も解説してないけどね^^;)
次回は「ソルフェジオモード」の実装方法を解説します。
(次回はちゃんと解説します笑)
お楽しみに。
すべてをソルフェジオ音源に変えるiPhoneアプリ「SolPlayer」開発記(1)
「SolPlayer」について
そもそも「ソルフェジオ」とは何か?
ジョン・レノンを殺した凶気の調律A=440Hz 人間をコントロールする「国際標準音」に隠された謀略() (超知ライブラリー 73)
- 作者: レオナルド・G・ホロウィッツ,渡辺亜矢
- 出版社/メーカー: 徳間書店
- 発売日: 2012/02/29
- メディア: 単行本(ソフトカバー)
- 購入: 1人 クリック: 10回
- この商品を含むブログ (4件) を見る
ソルフェジオモードの実装方法
iPhoneアプリ上でピッチ変換することができるのか?
・一般のアプリ開発は、車のパーツを組み立てる「ライン工」
・映像・音楽のエンジンを作る人は、まさに車のエンジンをつくる「エンジニア」
難易度が桁違いに違うわけです。