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

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

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

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

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

Q&A

解決済

2回答

780閲覧

BPMカウントアプリのコードを修正したいです。

退会済みユーザー

退会済みユーザー

総合スコア0

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Swift

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

0グッド

0クリップ

投稿2020/07/06 11:53

編集2020/07/06 13:48

閲覧ありがとうございます。
曲に合わせて自分でタップし、そのBPMを表示するアプリを作成したいと考えています。
タップするとランダムに数字が表示されるだけで肝心のBPMカウント機能が実装できません。おそらく計算式に問題があるんだと思います。不慣れで恐縮なのですが、何かアドバイス頂けないでしょうか。

該当のソースコード

import UIKit
class ViewController: UIViewController {

var count: Double = 0.00 var timer: Timer! var BPM: Double! @IBOutlet var timerLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBAction func start() { timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(ViewController.update), userInfo: nil, repeats: true) if count > 0.00{ count = 60/count timerLabel.text = String(format: "%.2f", count) } } @IBAction func stop() { timer.invalidate() } @objc func update() { count = count + 0.01

イメージ説明
}
}

試したこと

Githubやcocoa podsでソースコードを探し見比べたりしたのですが、原因を見つけられませんでした。

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

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

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

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

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

guest

回答2

0

丸投げ回答です。

swift

1import UIKit 2 3class ViewController: UIViewController { 4 5 //BPM表示用ラベル 6 @IBOutlet var labelBpm: UILabel! 7 8 //開始時間 9 var startDate = Date.distantPast 10 11 //タップ回数 12 var count = -1 13 14 //タップ処理 15 @IBAction func tapped(_ sender: UIButton) { 16 17 count += 1 18 19 if count == 0 { 20 //最初のタップ 21 22 //開始時間を保存 23 startDate = Date() 24 25 //ラベル更新用のタイマーを起動 26 Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { (timer) in 27 //この中は1秒毎に呼ばれる 28 29 //経過時間 (60で割って分単位にしている) 30 let t = timer.fireDate.timeIntervalSince(self.startDate) / 60 31 32 //BPM = タップ数(回) / 経過時間(分) 33 let bpm = Double(self.count) / t 34 35 //ラベル更新 36 self.labelBpm.text = String(format: "%.1fBPM", bpm) 37 } 38 } 39 } 40 41}

投稿2020/07/07 05:02

編集2020/07/07 05:04
fuzzball

総合スコア16731

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

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

退会済みユーザー

退会済みユーザー

2020/07/07 07:13 編集

コード付きのご回答本当にありがとうございます。とても分かりやすく大いに参考にさせていただきました。
guest

0

ベストアンサー

オリジナルのコードに基づく修正結果は後半に追記しました。


BPM = Beats per Minute なので、一分間の拍数(タップ回数)を計測すれば良いことになります。

作られたプログラムだと、割り込みを 0.01秒ごとにかけてタップ回数を計測するような流れになっていますが(それ以上深く読んでいませんので、根本的な原因まではみていませんが)、上記の理屈を素直に解釈すれば、1分後に起きる割り込みをかけて、その間に実行されたタップ回数を計測すれば良いということになります。

もっとも、1分間ずっとタップするのもしんどいので、たとえば10秒間タップし、タップされた回数を6倍することで、BPM を推測することも可能かと思います。

整理すれば

  • 割り込み周期が10秒のタイマを作る
  • 割り込み周期内に計測用のボタンが押されたら、その回数を記録する
  • 10秒後、割り込みが発生した段階で計測用のボタンが押された回数を6倍し、表示する

という流れにすれば、とりあえず BPM を計算するようなアプリは作れます。

ただし、このアプリには次のような問題も含まれています。

  • 現状では、スタートボタンを押した瞬間から計測を開始している。したがって、スタートボタンを押した後、計測用のタップボタンを押すまでの空白時間も計測に含まれるため、より正確に計測するのであればタイマ割り込みを開始するタイミングを考慮する必要がある。
  • 10秒間に押されたボタンの回数から BPM を推測する方法を採用しているが、実は厳密に BPM を計算することはできない。この方法だと拍数が遅くなればなるほど誤差が含まれてしまう。より正確に計測するのであれば、拍ごとの経過時間を計測し、そこから BPM を計算する必要がある(この場合は 0.1 秒周期などの割り込みを掛けて計算する必要もあるでしょう)。
  • あと何秒で計測が終了するのか分からないので、使い慣れていないと不安になる。
  • リアルタイムで BPM を表示するのであれば、やはり割り込み方法を変更する必要がある。

上記の内容をクリアすることこそ、独自の BPM カウントアプリを作る醍醐味だとおもいますので、ぜひ挑戦していただければと思います。

Swift

1import UIKit 2 3class ViewController: UIViewController { 4 // MARK: BPM 表示用のボタン 5 @IBOutlet weak var bpmLabel: UILabel! 6 // MARK: 計測開始用のボタン 7 @IBOutlet weak var startButton: UIButton! 8 // MARK: 計測用(タップ開始)のボタン 9 @IBOutlet weak var tapButton: UIButton! 10 11 // MARK: タイマのインスタンスを代入するための変数 12 // オプショナル型にする案もあるが、とりあえずはインスタンスを代入しておく。 13 var timer: Timer = Timer() 14 // MARK: タップ回数 15 var count = 0 16 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 // Do any additional setup after loading the view. 20 21 // タップ用のボタンは隠しておく 22 tapButton.isHidden = true 23 bpmLabel.text = "0" 24 } 25 26 // MARK: スタートボタンを押したときの処理 27 @IBAction func startButtonPressed(_ sender: Any) { 28 // スタートボタンを押したら、スタートボタンは隠し、タップボタンを表示する 29 tapButton.isHidden = false 30 startButton.isHidden = true 31 32 // ラベルに「計測中」と表示する 33 bpmLabel.text = "計測中" 34 35 // カウント回数を初期化 36 count = 0 37 38 // タイマのインスタンスを作成。 39 // 今回は10秒間計測し、計測終了したら calcBPM() を呼び出す。呼び出しは一回のみ(リピートしない) 40 // TODO: いまの作りだと、スタートボタンを押したらすぐに計測を開始してしまう。タップボタンを押した瞬間から計測したいのであれば、適切な場所に移動させる。 41 timer = Timer.scheduledTimer(timeInterval: 10.0, target: self, selector: #selector(calcBPM), userInfo: nil, repeats: false) 42 } 43 44 // MARK: タップボタンを押すたびに count をカウントアップする 45 @IBAction func tapButton(_ sender: Any) { 46 count += 1 47 } 48 49 // MARK: BPM を計算する処理 50 @objc func calcBPM() { 51 // TODO: リピートしないタイマであれば、自分で invalidate() を呼ぶ必要はない 52 // timer.invalidate() 53 54 // 隠していたボタンを表示し、表示していたボタンを隠す 55 startButton.isHidden = false 56 tapButton.isHidden = true 57 58 // 10秒間のタップ回数を6倍することで BPM を見積もる 59 let estimatedBPM = count * 6 60 61 bpmLabel.text = "BPMは (estimatedBPM) です" 62 } 63}

追記

思ったように動かない、というコードをきちんとみてみました。

タップ間の経過時間を計算した値で 60 を割ることによってリアルタイムで BPM を表示したいのだと理解しました(上で指摘した項目のうち、リアルタイムで計測することを実現しようとしている)。

思うように動かない理由は

  • 初回だけで良い Timer のインスタンスを毎回作っている(作っても多分動くが意味がない)
  • BPM に入れるべき計算結果を count に入れて上書きしている
  • タップごとに count をリセットしなければいけないのにリセットしていない

などがあります。

これらを修正すれば、概ね元の構造を残したまま動くようにすることは可能です。

Swift

1import UIKit 2 3class ViewController: UIViewController { 4 5 var count: Double = 0.00 6 7 var timer: Timer! 8 var BPM: Double! 9 10 @IBOutlet var timerLabel: UILabel! 11 12 override func viewDidLoad() { 13 super.viewDidLoad() 14 // Do any additional setup after loading the view. 15 } 16 17 @IBAction func start() { 18 // MARK: タイマは初回だけ起動 19 if timer == nil { 20 timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(ViewController.update), userInfo: nil, repeats: true) 21 } else { 22 // 秒数を count で割った値を BMP に代入 23 BPM = 60 / count 24 timerLabel.text = String(format: "%.2f", BPM) 25 // count はゼロにする 26 count = 0 27 } 28 } 29 30 @IBAction func stop() { 31 timer.invalidate() 32 } 33 34 @objc func update() { 35 count = count + 0.01 36 } 37}

投稿2020/07/07 03:07

編集2020/07/07 05:21
TsukubaDepot

総合スコア5086

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

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

退会済みユーザー

退会済みユーザー

2020/07/07 07:23 編集

こんなに丁寧なご回答本当にありがとうございます。とても参考になりました。考え方なども教えて頂いたので別のアプリ作成でも活かしたいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問