質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

Q&A

解決済

1回答

3386閲覧

AVQueuePlayerを用いた音声ファイルの連続再生

Nefytus

総合スコア12

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

iPhone

iPhoneとは、アップル社が開発・販売しているスマートフォンです。 同社のデジタルオーディオプレーヤーiPodの機能、電話機能、インターネットやメールなどのWeb通信機能の3つをドッキングした機器です。

0グッド

0クリップ

投稿2017/02/22 07:23

編集2017/02/23 00:29

あらかじめアプリ内に用意された複数の音声ファイルを、AVQueuePlayerを使って連続再生するアプリを作成しています。
こちらの、Appleが提供しているコードを使用させていただいています。
Apple - AVFoundation Looping Player

ここでお尋ねしたいのが、addItemというボタンを使用せずに、アプリを起動した時点ですでにアプリ内に用意された全ての音声ファイルがキューにセットされている状態にする方法です。
アプリ起動後、再生ボタンを押すとセットされた音声ファイルが再生されるようにしたいです。

以下にある、addItemのボタンの機能を参考にするのかと考えていますが、自動で全音声ファイルをキューに加える方法が分からず、質問させていただきました。

Swift

1@IBAction func addItemToQueueButtonPressed(_ sender: UIButton) { 2 let alertTitle = NSLocalizedString("popover.title.addItem", comment: "Title of popover that adds items to the queue") 3 4 let alertMessage = NSLocalizedString("popover.message.addItem", comment: "Message on popover that adds items to the queue") 5 6 let alertController = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .actionSheet) 7 8 // Populate the sheet with the titles of the assets we have loaded. 9 for (loadedAssetTitle, loadedAsset) in loadedAssets { 10 let alertAction = UIAlertAction(title:loadedAssetTitle, style: .default) { [unowned self] alertAction in 11 let oldItems = self.player.items() 12 13 let newPlayerItem = AVPlayerItem(asset: loadedAsset) 14 15 self.player.insert(newPlayerItem, after: nil) 16 17 self.queueDidChangeWithOldPlayerItems(oldPlayerItems: oldItems, newPlayerItems: self.player.items()) 18 } 19 20 alertController.addAction(alertAction) 21 } 22 23 let cancelActionTitle = NSLocalizedString("popover.title.cancel", comment: "Title of popover cancel action") 24 25 let cancelAction = UIAlertAction(title: cancelActionTitle, style: .cancel, handler: nil) 26 27 alertController.addAction(cancelAction) 28 29 presentModalPopoverAlertController(alertController: alertController, sender: sender) 30 }

教えていただいたように一番下の部分に追加したところ、19個用意した音声ファイルが複数回選択され、全部で189個のキューが自動で設定されるようになりました。
これについては、for文の部分で回数の設定などを行うことで19個のキューにすることができるのでしょうか?

Swift

1class PlayerViewController: UIViewController, UICollectionViewDataSource { 2 static let assetKeysRequiredToPlay = [ 3 "playable", 4 "hasProtectedContent" 5 ] 6 7 let player = AVQueuePlayer() 8 9 var currentTime: Double { 10 get { 11 return CMTimeGetSeconds(player.currentTime()) 12 } 13 14 set { 15 let newTime = CMTimeMakeWithSeconds(newValue, 1) 16 player.seek(to: newTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero) 17 } 18 } 19 20 var duration: Double { 21 guard let currentItem = player.currentItem else { return 0.0 } 22 23 return CMTimeGetSeconds(currentItem.duration) 24 } 25 26 var rate: Float { 27 get { 28 return player.rate 29 } 30 31 set { 32 player.rate = newValue 33 } 34 } 35 36 var playerLayer: AVPlayerLayer? { 37 return playerView.playerLayer 38 } 39 40 /* 41 A formatter for individual date components used to provide an appropriate 42 value for the `startTimeLabel` and `durationLabel`. 43 */ 44 let timeRemainingFormatter: DateComponentsFormatter = { 45 let formatter = DateComponentsFormatter() 46 formatter.zeroFormattingBehavior = .pad 47 formatter.allowedUnits = [.minute, .second] 48 49 return formatter 50 }() 51 52 var timeObserverToken: Any? 53 54 var assetTitlesAndThumbnails: [URL: (title: String, thumbnail: UIImage)] = [:] 55 56 var loadedAssets = [String: AVURLAsset]() 57 58 @IBOutlet weak var timeSlider: UISlider! 59 @IBOutlet weak var startTimeLabel: UILabel! 60 @IBOutlet weak var durationLabel: UILabel! 61 @IBOutlet weak var rewindButton: UIButton! 62 @IBOutlet weak var playPauseButton: UIButton! 63 @IBOutlet weak var fastForwardButton: UIButton! 64 @IBOutlet weak var clearButton: UIButton! 65 @IBOutlet weak var collectionView: UICollectionView! 66 @IBOutlet weak var queueLabel: UILabel! 67 @IBOutlet weak var playerView: PlayerView! 68 69 override func viewWillAppear(_ animated: Bool) { 70 super.viewWillAppear(animated) 71 72 73 addObserver(self, forKeyPath: #keyPath(PlayerViewController.player.currentItem.duration), options: [.new, .initial], context: &playerViewControllerKVOContext) 74 addObserver(self, forKeyPath: #keyPath(PlayerViewController.player.rate), options: [.new, .initial], context: &playerViewControllerKVOContext) 75 addObserver(self, forKeyPath: #keyPath(PlayerViewController.player.currentItem.status), options: [.new, .initial], context: &playerViewControllerKVOContext) 76 addObserver(self, forKeyPath: #keyPath(PlayerViewController.player.currentItem), options: [.new, .initial], context: &playerViewControllerKVOContext) 77 78 playerView.playerLayer.player = player 79 80 let manifestURL = Bundle.main.url(forResource: "MediaManifest", withExtension: "json")! 81 asynchronouslyLoadURLAssetsWithManifestURL(jsonURL: manifestURL) 82 83 let interval = CMTimeMake(1, 1) 84 timeObserverToken = player.addPeriodicTimeObserver(forInterval: interval, queue: DispatchQueue.main) { [unowned self] time in 85 let timeElapsed = Float(CMTimeGetSeconds(time)) 86 87 self.timeSlider.value = Float(timeElapsed) 88 self.startTimeLabel.text = self.createTimeString(time: timeElapsed) 89 } 90 91 } 92 93 override func viewDidDisappear(_ animated: Bool) { 94 super.viewDidDisappear(animated) 95 96 if let timeObserverToken = timeObserverToken { 97 player.removeTimeObserver(timeObserverToken) 98 self.timeObserverToken = nil 99 } 100 101 player.pause() 102 103 removeObserver(self, forKeyPath: #keyPath(PlayerViewController.player.currentItem.duration), context: &playerViewControllerKVOContext) 104 removeObserver(self, forKeyPath: #keyPath(PlayerViewController.player.rate), context: &playerViewControllerKVOContext) 105 removeObserver(self, forKeyPath: #keyPath(PlayerViewController.player.currentItem.status), context: &playerViewControllerKVOContext) 106 removeObserver(self, forKeyPath: #keyPath(PlayerViewController.player.currentItem), context: &playerViewControllerKVOContext) 107 } 108 109 func asynchronouslyLoadURLAsset(asset: AVURLAsset, title: String, thumbnailResourceName: String) { 110 111 asset.loadValuesAsynchronously(forKeys: PlayerViewController.assetKeysRequiredToPlay) { 112 113 114 DispatchQueue.main.async() { 115 116 117 118 for key in PlayerViewController.assetKeysRequiredToPlay { 119 var error: NSError? 120 121 if asset.statusOfValue(forKey: key, error: &error) == .failed { 122 let stringFormat = NSLocalizedString("error.asset_%@_key_%@_failed.description", comment: "Can't use this AVAsset because one of it's keys failed to load") 123 124 let message = String.localizedStringWithFormat(stringFormat, title, key) 125 126 self.handleError(with: message, error: error) 127 128 return 129 } 130 } 131 132 133 if !asset.isPlayable || asset.hasProtectedContent { 134 let stringFormat = NSLocalizedString("error.asset_%@_not_playable.description", comment: "Can't use this AVAsset because it isn't playable or has protected content") 135 136 let message = String.localizedStringWithFormat(stringFormat, title) 137 138 self.handleError(with: message) 139 140 return 141 } 142 143 144 self.loadedAssets[title] = asset 145 146 let name = (thumbnailResourceName as NSString).deletingPathExtension 147 let type = (thumbnailResourceName as NSString).pathExtension 148 let path = Bundle.main.path(forResource: name, ofType: type)! 149 150 let thumbnail = UIImage(contentsOfFile: path)! 151 152 self.assetTitlesAndThumbnails[asset.url] = (title, thumbnail) 153 154 for (loadedAssetTitle, loadedAsset) in self.loadedAssets { 155 let newPlayerItem = AVPlayerItem(asset: loadedAsset) 156 self.player.insert(newPlayerItem, after: nil) 157 } 158 159 } 160 } 161 }

よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「Apple - AVFoundation Looping Player」のサンプルの中のどこに
質問のソースコードのaddItemToQueueButtonPressedメソッドがあるのか
見つかりませんでしたが、質問のソースコードの中で音声ファイルをキューに加える処理は

let newPlayerItem = AVPlayerItem(asset: loadedAsset) self.player.insert(newPlayerItem, after: nil)

の部分になると思いますので、loadedAssetsに入っている全ファイルをキューに加えるなら

for (loadedAssetTitle, loadedAsset) in loadedAssets { let newPlayerItem = AVPlayerItem(asset: loadedAsset) self.player.insert(newPlayerItem, after: nil) }

という感じだろうと思います。
そして、それをアプリ起動時に実施したいなら、初期起動されるViewControllerの
viewDidLoadあたりで実施すればよいと思います。
もちろんその処理の前にloadedAssetsやself.playerを初期化しておく必要があります。

投稿2017/02/22 23:44

TakeOne

総合スコア6299

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Nefytus

2017/02/23 00:37 編集

ご丁寧に教えていただきありがとうございます。 もしよろしければお尋ねしたいのですが、 元の質問にも追記させていただいたように、キューが189個になるのはfor文の書き方で修正できるものでしょうか?
TakeOne

2017/02/23 02:36 編集

先にloadedAssetsを作ってから後でそれに対応するAVPlayerItemを作るのではなくて、loadedAssetsを作りながらAVPlayerItemも一緒に作るのであれば、そんなやり方しちゃダメだと思います。 今は、loadedAssetsに入っているファイルを毎回全部追加していますから、1+2+3+4+...+17+18+19=190個の追加になってしまうのだと思います。 そのような構造の中でAVPlayerItemを作るのであれば、回答に示したfor文は不要で ``` let newPlayerItem = AVPlayerItem(asset: asset) self.player.insert(newPlayerItem, after: nil) ``` とするだけでよさそうに思います。 自分が作ったプログラムがどのように動いているか、print()を出力したり、ブレークポイントを張ってステップ実行したりして、きちんと把握することをお勧めします。
Nefytus

2017/02/23 05:18

形も不格好で、自分自身がよく把握できていないものを作ってしまいお恥ずかしいです。 教えていただいたように、次に生かせるような作り方をしようと思います。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問