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

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

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

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

Swift

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

Q&A

解決済

2回答

687閲覧

Swift Xcode 11.5 録音アプリ 再生できない

Fuji-1996

総合スコア4

Xcode

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

Swift

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

0グッド

0クリップ

投稿2020/07/08 07:21

前提・実現したいこと

SwiftのXcode 11.5で録音アプリを作成しています。

録音の開始と停止は、問題なく機能しています。
しかし、録音した音源を再生しようとするとエラーが出てきました。

解決方法が分からず、質問するに至りました。
ご回答をよろしくお願いいたします。

発生している問題・エラーメッセージ

エラーメッセージ
Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value

該当のソースコード

エラーは、括弧を除くと下から3行目にある「soundPlayer.stop()」で発生しました。

Swift

1 2 3 4import UIKit 5import AVFoundation 6 7class ViewController: UIViewController, AVAudioRecorderDelegate, AVAudioPlayerDelegate { 8 9 10 @IBOutlet weak var recordBTN: UIButton! 11 @IBOutlet weak var playBTN: UIButton! 12 13 var soundRecorder : AVAudioRecorder! 14 var soundPlayer : AVAudioPlayer! 15 16 var fileName : String = "audioFile.m4a" 17 18 override func viewDidLoad() { 19 super.viewDidLoad() 20 // Do any additional setup after loading the view. 21 setupRecorder() 22 playBTN.isEnabled = false 23 } 24 25 func getDocumentsDirector() -> URL{ 26 let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) 27 return paths[0] 28 } 29 30 func setupRecorder(){ 31 let audioFilename = getDocumentsDirector().appendingPathComponent(fileName) 32 let recordSetting = [ AVFormatIDKey : kAudioFormatAppleLossless, 33 AVEncoderAudioQualityKey : AVAudioQuality.max.rawValue, 34 AVEncoderBitRateKey : 32000, 35 AVNumberOfChannelsKey : 2, 36 AVSampleRateKey : 44100.2 ] as [String : Any] 37 do{ 38 soundRecorder = try AVAudioRecorder(url: audioFilename, settings: recordSetting) 39 soundRecorder.delegate = self 40 soundRecorder.prepareToRecord() 41 }catch{ 42 print(error) 43 } 44 } 45 func setupPlayer(){ 46 let audioFilename = getDocumentsDirector().appendingPathComponent(fileName) 47 do{ 48 soundPlayer = try AVAudioPlayer(contentsOf: audioFilename) 49 soundPlayer.delegate = self 50 soundPlayer.prepareToPlay() 51 soundPlayer.volume = 1.0 52 }catch{ 53 print(error) 54 } 55 } 56 57 func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { 58 playBTN.isEnabled = true 59 } 60 61 func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { 62 recordBTN.isEnabled = true 63 playBTN.setTitle("play", for: .normal) 64 } 65 66 @IBAction func recordAct(_ sender: Any) { 67 if recordBTN.titleLabel?.text == "Record"{ 68 soundRecorder.record() 69 recordBTN.setTitle("Stop", for: .normal) 70 playBTN.isEnabled = false 71 }else{ 72 soundRecorder.stop() 73 recordBTN.setTitle("Record", for: .normal) 74 playBTN.isEnabled = true 75 } 76 } 77 @IBAction func playAct(_ sender: Any) { 78 if recordBTN.titleLabel?.text == "Play"{ 79 playBTN.setTitle("Stop", for: .normal) 80 recordBTN.isEnabled = false 81 setupPlayer() 82 soundPlayer.play() 83 }else{ 84       soundPlayer.stop() 85 playBTN.setTitle("Play", for: .normal) 86 recordBTN.isEnabled = true 87 } 88 } 89} 90

試したこと

**soundPlayer.stop()**自体を消した場合は、エラーは無くなりますが、
音声を聞くことはできませんでした。

録音しているときは、Diskのデータ通信速度が192KB/sで維持されていました。
また、録音を停止すると、0KB/sになりました。
このことから、録音はできていると考えています。

var fileName : String = "audioFile.m4a"
と定めたものの、audioFile.m4aの保存場所が分からなかったので、
保存できているかは、明らかではありません。

また、再生ボタンを押すと、エラーが発生します。

補足情報(FW/ツールのバージョンなど)

Main.storyboard>View Controller Scene>View Controllerを押して、
Show the identity inspector(新聞か画像のようなマーク)>Custom Class>Inherit Module From Targetにはチェックが入っています。

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

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

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

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

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

fuzzball

2020/07/08 07:44

再生しようとしているのに停止処理に入っているのがおかしいですね。
fuzzball

2020/07/08 07:56

保存場所は audioFilename を見ればわかります。
Fuji-1996

2020/07/13 08:08

回答をありがとうございます。 fuzzballさんの仰る、audioFilenameを探すことができませんでした。 Xcodeを使い始めて1週間程度なので、経験を積みながら探していこうと思います。 ありがとうございました。
guest

回答2

0

ベストアンサー

原因はここだと思います。

Sift

1 @IBAction func playAct(_ sender: Any) { 2 // 状態を判断するボタンが間違っている 3 //if recordBTN.titleLabel?.text == "Play"{ 4 if playBTN.titleLabel?.text == "Play" {

ボタンのラベル名で制御状態を判断しているようですが、状態を判断するボタンが間違っているようです。

ちなみに、再生終了後にボタンのラベルを書き換えていますが、そこも間違っているようです。

Swift

1 func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { 2 recordBTN.isEnabled = true 3 // 大文字始まりの Play にしなければ辻褄があわなくなる 4 //playBTN.setTitle("play", for: .normal) 5 playBTN.setTitle("Play", for: .normal) 6 }

ボタンのラベルを使って状態を制御することはできますが、個人的にはブール値(Bool型)の変数を準備し、その真偽値で状態を判断するのが良いかと思います。

また、文字列を状態判断に使うと今回のような typo 時にバグを生んでしまいますし、書き換え時も変更すべき場所を確認するのが大変なので、それも避けた方が良いかと思います(仮にも多言語対応するとなると収拾がつかないかもしれません)。

投稿2020/07/08 09:12

編集2020/07/08 09:16
TsukubaDepot

総合スコア5086

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

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

Fuji-1996

2020/07/13 08:12

ご回答をありがとうございます。 仰るとおり、 ①recordBTNではなく、playBTNという「指定場所」の間違い ②playではなく、Playという「大文字と小文字」の間違い の二つを変えることで、解決することができました。 的確な回答だったので、ベストアンサーに選ばせていただきました。 貴重なお時間を割いていただき、ありがとうございます。
guest

0

swift

1 override func viewDidLoad() { 2 super.viewDidLoad() 3 // Do any additional setup after loading the view. 4 setupRecorder() 5 playBTN.isEnabled = false 6 }

ここはsetupRecorder()を実行しますがsetupPlayer()を実行しません。そして

swift

1 @IBAction func playAct(_ sender: Any) { 2 if recordBTN.titleLabel?.text == "Play"{ 3 playBTN.setTitle("Stop", for: .normal) 4 recordBTN.isEnabled = false 5 setupPlayer() 6 soundPlayer.play() 7 }else{ 8      soundPlayer.stop() 9 playBTN.setTitle("Play", for: .normal) 10 recordBTN.isEnabled = true 11 } 12 }

この関数に訳がわかりませんけど、else枝に先に入り、setupPlayer()を実行しないまま、つまりsoundPlayerがまだnilのまま、soundPlayer.stop()を実行しています。ですからエラーが出ました。


検証できませんが、lazy varを用いると解決できると思います。

swift

1 lazy var soundPlayer : AVAudioPlayer! = { 2 let audioFilename = getDocumentsDirector().appendingPathComponent(fileName) 3 do{ 4 var soundPlayer = try AVAudioPlayer(contentsOf: audioFilename) 5 soundPlayer.delegate = self 6 soundPlayer.prepareToPlay() 7 soundPlayer.volume = 1.0 8 return soundPlayer 9 }catch{ 10 print(error) 11 } 12 }()

ちなみに、getDocumentsDirector()(DirectorよりDirectoryがあっていますが)ですから、録音したファイルは/Users/xxx/Documentsにあります。

投稿2020/07/08 08:07

YufanLou

総合スコア464

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

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

Fuji-1996

2020/07/13 08:18

ご回答をありがとうございます。 lazy varについて理解が追いついておらず、3つ目は正直分からなかったので、個人的にもうちょっと調べてみようと思います。 上の2つには私にも理解できる分かりやすい説明をいただき、ありがとうございます。 最初はnilの状態というのは、驚きの事実でした。 YufanLou さんの仰る、/Users/xxx/Documentsに録音したデータを見あたりませんでした。 Xcodeを使い始めて1週間程度なので、経験を積みながら探していこうと思います。 貴重なお時間を割いていただき、ありがとうございます。
YufanLou

2020/07/13 08:53

解決できたら何よりです。改めてxxxのところはご自身のユーザーネームを入れ替えて見てください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問