見出し画像

ChatGPTに教えてもらえばAIアプリだって作れる様になるはず‼︎ 8.Basic Soundboard #機械学習による人間学習

はじめに

このブログでは、ChatGPTで作成した「AIアプリを作れる様になるまでの100個のアプリ案」を実際に作り、学んだことや失敗したこと、感じたことを記していきます。

各記事のページ下部にはChatGPTによるStep by Stepガイドを貼付し、学習内容をトレース出来る様にしています。

プログラミング初心者の方や新しくスキルを身に付けようとしている方の参考になれば幸いです。

(※本ブログはマガジン「ChatGPTに教えてもらえばにまとめてあります。)


今回のアプリ "8.Basic Soundboard"

(左) ChatGPT オリジナルver (右) オプション機能追加

概要 :さまざまなボタンをタップした際に効果音を再生する。

狙い : AVAudioPlayerとアセット(リソース)の配置・管理方法を学ぶ。

AVAudioPlayerとは「ファイルまたはバッファーからオーディオデータを再生するオブジェクト」とAppleのドキュメントに書いてありました。これを使えば、「mp3」や「wav」などの音声ファイルを再生することが出来るということです。

以下のリンクには様々なサンプルコードが貼ってありますが、プログラムは「習うより慣れろ」だと思うので、どんどん作っていきます。

気付いたこと、分かったことのメモ

import SwiftUI
// ②AVFoundationをインポート
import AVFoundation

struct ContentView: View {
    // audioPlayerをグローバルまたはstaticで保持する
// ③AVAudioPlayer型の変数を用意
    static var audioPlayer: AVAudioPlayer?

    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
// ④音を鳴らすボタンを用意
                Button("Play Sound 1") {
// ⑤ボタン内にアクションを設置
                    playSound(named: "sound1") // 例: "sound1.mp3"
                }
                .buttonStyle(.borderedProminent)
  • 音楽ファイルの再生の手順

    1. xcodeのプロジェクト内に音声ファイルを保存

    2. AVFoundationをインポート

    3. AVAudioPlayer型の変数を用意

    4. 音を鳴らすボタンを用意

    5. ボタンの{ … }(クロージャ)にplaySound( )を設置

    6. func playSound( ) { … }で{ … }内に処理内容を書く

// ⑥playSound( )の処理内容を書く
    func playSound(named fileName: String) {
        guard let url = Bundle.main.url(forResource: fileName, withExtension: "mp3") else {
            print("Sound file not found: \(fileName)")
            return
        }
        do {
            Self.audioPlayer = try AVAudioPlayer(contentsOf: url)
            Self.audioPlayer?.play()
        } catch {
            print("Failed to play sound \(fileName): \(error)")
        }
    }
  • func playSound(named fileName: String) { … }

    • playSoundはfunctionの名前で自由に付けられる。

    • fileNameを引数として{ … }内のコードを実行する。例えば、playSound(named: "sound1")を実行する場合、fileName = "sound1"として{ … }内が実行される。

  • Bundle.main.url(forResource: fileName, withExtension: "mp3")

    • アプリのメインバンドル(主にプロジェクト内のリソースファイルが配置される場所)から指定したリソースのURLを取得するためのメソッド。

      • fileName: リソースのファイル名(拡張子を除いた名前)

      • withExtension: リソースファイルの拡張子(“mp3” など)

    • playSound(named: "sound1")が実行される時は、メインバンドル内のsound1.mp3のURLが取得される。

  • AVAudioPlayer(contentsOf: url)

    • urlのオーディオプレイヤーが作られる。.play( )で再生される。

感想

AVAudioPlayerで音を鳴らす手順について学びました。スクリプト構成はボタンをレイアウトする、ボタンにfunctionを埋め込む、functionを定義するという構成で、これまでのアプリと大体一緒です。使用するアセットやオブジェクトを変えることで様々な機能が付与されています。

音声ファイルは保存する場所が間違っていると音が鳴らないので注意が必要です。私は、始めContentViewが入っているフォルダに音声ファイルを保存したのですが、再生されませんでした。ContentViewが入っているフォルダのさらに上、一番上のフォルダに保存することで音が鳴りました。

オプション機能ではDAWで自作したギターの音を再生出来る様にしました。
UIに凝って、ギターの弦やフレットのイラストを用いれば、ギターアプリが出来そうですし、他にも様々な音を保存してシンセサイザーを作ったり、マイク機能と組み合わせればチューナーアプリも作れそうなど、拡張のしがいがありそうなアプリでした。もっとSwiftに慣れたら挑戦してみようと思います。

この記事が参考になった方はスキ!、フォローお願いします。

ChatGPTの回答


以下は、進捗リストの8番目にある “Basic Soundboard” アプリを SwiftUI で構築するためのステップバイステップガイドです。
サウンドボードとは、複数のボタンやトリガーがあり、タップするたびに特定の効果音が再生されるアプリを指します。
ここでは iOS 上での音声再生に触れつつ、SwiftUIの基礎を練習できます。

1. 新しいSwiftUIプロジェクトを作成

  1. Xcodeを起動

    • アプリケーションフォルダからXcodeを起動します。

  2. 新しいプロジェクトを作成

    • Xcodeのウェルカム画面(またはメニュー)から “Create a new Xcode project…” を選択します。

  3. iOS Appテンプレートを選択

    • 左側で iOS を選択し、App を選んで Next をクリック。

  4. 4. プロジェクト設定

    • Product Name: BasicSoundboard

    • Team: Apple Developerアカウント、またはローカルテスト時は “None”

    • Organization Identifier: 例 com.yourname

    • Interface: SwiftUI

    • Language: Swift

    • Next を押して保存先を決め、Create をクリック。

2. サウンドファイルの準備

  1. サウンドファイルを用意

    • .mp3 や .wav の短いサウンドファイルをいくつか用意します。(ボタンのクリック音やビープ音など、サイズは小さめ推奨)

    • 例: button_tap.wav, cheer.mp3 など、わかりやすい名前を付けてください。

  2. XcodeのAssetsまたはプロジェクトフォルダに追加

    • オプションA: Xcode14以降の「Audio Catalog」を使う場合、Assets.xcassets 内にドラッグ&ドロップ。

    • オプションB: プロジェクトナビゲータで新たに “Sounds” グループを作成し、そこに音声ファイルをドラッグ&ドロップして “Copy items if needed” にチェック。

  3. リソースが正しくアプリに含まれるか確認

    • Audio Catalogを使わない場合、File Inspector の “Target Membership” でアプリターゲットにチェックが入っているか確認してください。

3. SwiftUIレイアウトとState

ステップ1: 各サウンド用のリストまたはボタンを作成

import SwiftUI

struct ContentView: View {
    @State private var isPlaying: Bool = false

    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Button("Play Sound 1") {
                    playSound(named: "sound1")
                }
                .buttonStyle(.borderedProminent)

                Button("Play Sound 2") {
                    playSound(named: "sound2")
                }
                .buttonStyle(.borderedProminent)

                // 他のサウンドボタンを追加してもOK...
            }
            .navigationTitle("Basic Soundboard")
        }
    }

    func playSound(named fileName: String) {
        // 後述で実装します
    }
}
  • 各ボタンは固有のサウンドファイル名(例: "sound1", "sound2")を playSound に渡すだけです。

ステップ2: AVFoundationでオーディオ再生

  • AVFoundationimport し、AVAudioPlayer を使ってアプリに含まれる音声ファイルを再生します。

import AVFoundation

var audioPlayer: AVAudioPlayer?

func playSound(named fileName: String) {
    guard let url = Bundle.main.url(forResource: fileName, withExtension: "mp3") else {
        print("Sound file not found: \(fileName)")
        return
    }

    do {
        audioPlayer = try AVAudioPlayer(contentsOf: url)
        audioPlayer?.play()
    } catch {
        print("Failed to play sound \(fileName): \(error)")
    }
}

メモ

  • ファイルが .wav なら、withExtension: "mp3" の部分を "wav" に変えてください。

  • Audio Catalogを使う場合は取得方法が異なることがあります(Bundle.main.url(forResource:)を利用しないなど)。ですが基本的には Bundle.main から読み込む方法が主流です。

4. 完成形コード例

最終的に、ContentView.swift を以下のように組み合わせます。

import SwiftUI
import AVFoundation

struct ContentView: View {
    // audioPlayerをグローバルまたはstaticで保持する
    static var audioPlayer: AVAudioPlayer?

    var body: some View {
        NavigationView {
            VStack(spacing: 20) {
                Button("Play Sound 1") {
                    playSound(named: "sound1") // 例: "sound1.mp3"
                }
                .buttonStyle(.borderedProminent)

                Button("Play Sound 2") {
                    playSound(named: "sound2") // 例: "sound2.mp3"
                }
                .buttonStyle(.borderedProminent)
                
                // 必要に応じてボタンやListを追加
                
                Spacer()
            }
            .navigationTitle("Basic Soundboard")
            .padding()
        }
    }

    func playSound(named fileName: String) {
        guard let url = Bundle.main.url(forResource: fileName, withExtension: "mp3") else {
            print("Sound file not found: \(fileName)")
            return
        }
        do {
            Self.audioPlayer = try AVAudioPlayer(contentsOf: url)
            Self.audioPlayer?.play()
        } catch {
            print("Failed to play sound \(fileName): \(error)")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

5. 実行とテスト

  1. シミュレーターと実機

    • シミュレーターではサウンド再生が正しく機能しない場合があります。

    • 実機(iPhone/iPad)でテストすると確実です。

  2. 音量に注意

    • デバイスやシミュレーターの音量がミュートではないかチェック。

  3. サウンドを追加する

    • 多数のボタンを作り、playSound(named: "...") を呼び出すだけで簡単にサウンドを増やせます。

6. オプション機能

  1. ListやGridを使う

    • サウンドの数が多い場合、ファイル名を配列で持ち、List や LazyVGrid で動的にボタンを生成すると便利。

  2. 再生/一時停止

    • 単一のAVAudioPlayerを管理し、途中で止めたり再開したりする機能を追加。

    • 現在の再生時間を表示するなど、進捗を可視化できます。

  3. 音量スライダー

    • audioPlayer?.volume (0.0〜1.0)を変更するためのUIを追加可能。

  4. 複数サウンドの同時再生

    • 重ねて再生するには、複数のAVAudioPlayerインスタンスや AVAudioEngine の利用が必要。

  5. Python Backendとの連携

    • サウンドボードで流す音声をサーバーからストリーミングしたり、追加の効果音をダウンロードしたりできる。

  6. UIカスタマイズ

    • ボタンに画像やアイコンを使い、外観を個性的に。

7. 結論

今回のBasic Soundboardアプリでは、タップごとに短いサウンドを再生することで、AVAudioPlayerの基礎とアプリ内リソース管理の方法が学べます。
そこから先は重ね再生音声ライブラリサーバー連携などの機能拡張で、より高度なサウンドアプリを目指せます。
ぜひ楽しみながら開発してみてください!

オプション機能:複数サウンドの同時再生


主な変更点

  • サウンドの数を増やし、ファイル名を配列で保持

    • ギター音で1オクターブ弾ける様にしてみました。音はDAWを使用し、各音階のMIDIデータをwavファイルで出力して作成しました。

  • 複数サウンドの同時再生

    • せっかくのギター音、和音を鳴らしたいと思ったので、同時再生出来る様にしました。配列に音を保存していくことで音が途切れるのを防いでいる様です。下記にChatGPTの解説を載せます。

複数の AVAudioPlayer インスタンスを使う
最も簡単な方法は再生するサウンドごとに個別のAVAudioPlayerインスタンスを用意し、再生するという手段です。例えば、同時に鳴らしたいたびに新規AVAudioPlayerを生成してplay()を呼び出せば、複数の音が同時再生できます。

なぜ配列を使う?
• AVAudioPlayerがインスタンススコープを失うと途中で解放される → 再生が止まる。
• 配列(@State private var activePlayers)に格納して保持することで、音の再生中はメモリ上に存在し続け、同時再生が継続されます。

メモリ・管理上の注意
• たくさんの音を乱発すると activePlayers にプレイヤーが増え続ける可能性があります。
• 音が再生終了したら自動的に解放したい場合は、再生終了をキャッチして配列から取り除くロジックを入れるとよいでしょう。
• 例: player.delegate = self で AVAudioPlayerDelegate の audioPlayerDidFinishPlaying を実装し、終了したインスタンスを activePlayers から remove する。

import SwiftUI
import AVFoundation

struct ContentView: View {
    
    @State private var activePlayers: [AVAudioPlayer] = []
    let fileNames = [
        "Guitar_C3",
        "Guitar_D3",
        "Guitar_E3",
        "Guitar_F3",
        "Guitar_G3",
        "Guitar_A3",
        "Guitar_B3",
        "Guitar_C4"
    ]
    
    var body: some View {
        NavigationView {
            VStack (spacing: 20) {
                ForEach (fileNames,id: \.self) {fileName in
                    Button(action: {
                        playSound(named: fileName)
                    }) {
                        Text(fileName.suffix(2))
                            .font(.title)
                            .frame(maxWidth: .infinity, minHeight: 50)
                            .padding(.vertical, 10)
                            .foregroundColor(.white)
                            .background(Color.black)
                    }
                    .buttonStyle(.plain)
                }
            }
        }
        .navigationTitle("Basic Soundboard")
    }
    
    func playSound(named fileName: String) {
        guard let url = Bundle.main.url(forResource: fileName, withExtension: "wav") else {
            print("Sound file not found: \(fileName)")
            return
        }
        
        if let player = try? AVAudioPlayer(contentsOf: url) {
            activePlayers.append(player)
            player.play()
        } else {
            print("Could not create AVAudioPlayer for \(fileName)")
        }
    }
}

#Preview {
    ContentView()
}

いいなと思ったら応援しよう!