WWDC Quick Look 💓 By SwiftGGTeam
Swan's Quest, Chapter 3: The notable scroll

Swan's Quest, Chapter 3: The notable scroll

元の動画を見る

ハイライト

第 3 章では、プレイヤーはスワン リールのリズムを次のように変換する必要があります。PitchNoteProtocolそして一定間隔Timer、次に使用しますToneOutputさまざまな長さの音符を演奏します。

主要内容

第 2 章では、1 つの音符の長さ、固定間隔のみを扱います。Timerチャレンジを完了するには十分です。第 3 章でトカゲの小屋に戻った後、スワンの新しい巻物では、プレイヤーはリズムの違いでメロディーを演奏する必要があります。四分音符、二分音符、付点二分音符はすべて可能です。元のループは同じビートに従って進むだけで、長い音に遭遇すると表現力を失います。

Apple はまず問題を 2 つのモデルに分類します。Pitch伝える責任があるToneOutputどの周波数を再生するか、NoteProtocol音のトーンと長さを説明する役割を果たします。長さは 4 分音符の倍数で表現されるため、2 分音符が返されます。2.0、8分音符が戻ります0.5

難点はタイマーです。逐語草案は明らかに次のことを思い出させます。ToneOutputやめるよう求められるまで声を出し続けます。このセッションのメソッドは、Timerサポートされている最も短いノート間隔に固定され、長いノートは複数のピッチ コマンドに分割されます。このようにして、ループはシンプルなままですが、さまざまな長さの音符を表現できます。

詳細

ピッチを使用して周波数を説明します

01:50Music.swiftプレイヤーが実装するために予約されているプロトコルがあります。最初のステップは、リールの各ピッチを周波数にマッピングすることです。ToneOutput使用。

// Example Pitch implementation

public enum Pitch: Double, PitchProtocol {
    case a4 = 440.0

    var frequency: Double {
        return self.rawValue
    }
}

キーポイント:

  • Pitch従うPitchProtocol、使用Double生の値は周波数を保持します。
  • .a4440.0 に対応する逐語的スクリプトでは、プレーヤーはスワン リールの各ピッチにケースを追加する必要があります。
  • frequency生の値を直接返します。次のようになります。Toneこの周波数が読み取られます。

NoteProtocol を使用して音符の長さを記述する

(02:08) ピッチが解決された後、音符がどのくらい続くかを表現する必要もあります。公式クリップはノートを 2 つの属性に分割します: 再生用tone、四分音符を基準にlength

// Music.swift

public protocol NoteProtocol {

    /// Play this Note through a ToneOutput
    var tone: Tone { get }

    /// The duration of this Note as a multiple of quarter notes,
    /// e.g., a half note is 2.0, an eighth note is 0.5
    var length: Float { get }
}

キーポイント:

  • tone贈り物ですToneOutput音のオブジェ。
  • length四分音符をベースにすると二分音符は2.0、8分音符は0.5
  • このプロトコルにより、同じメロディー配列でピッチとリズムの両方の情報を運ぶことができます。

公式の音符の実装では、まず最小の形式である 4 分音符のケースをカバーし、ピッチを次のように変換します。Tone、固定長に戻ります。

// Example Note implementation

public enum Note: NoteProtocol {
    case quarter(pitch: Pitch)

    var tone: Tone {
        switch self {
        case .quarter(let pitch):
            return Tone(pitch: pitch.frequency, volume: 0.3)
        }
    }

    var length: Float {
        switch self {
        case .quarter(_):
            return 1.0
        }
    }
}

キーポイント:

  • quarter(pitch:)ピッチを 4 分音符にラップします。
  • toneピッチを使用するfrequency作成するTone、ボリュームは次のように設定されています。0.3
  • length四分音符の返品1.0、プレーヤーはリール内の他の長さのケースを追加し続ける必要があります。

固定タイマー間隔

(03:18) オリジナルTimer音符の長さは自動的には認識されません。解決策は、まずサポートされている最短の音符の長さをプロトコルに伝え、次にこの値を使用してタイマー間隔を計算することです。スピーカーは例として 120 BPM を使用し、4 分音符の長さは 500 ミリ秒です。

// Music.swift

public protocol NoteProtocol {

    /// Play this Note through a ToneOutput
    var tone: Tone { get }

    /// The duration of this Note as a multiple of quarter notes,
    /// e.g., a half note is 2.0, an eighth note is 0.5
    var length: Float { get }

    /// Length of the smallest Note supported
    static var shortestSupportedNoteLength: Float { get }
}

キーポイント:

  • shortestSupportedNoteLengthタイプレベルの属性であり、すべてのノートが同じ最短単位を共有します。
  • 逐語原稿の白鳥スクロールは最短単位として 4 分音符を使用しているため、例では 4 分音符を選択しています。
  • Timer以降、この最短ユニットの押下がトリガーとなり、ピッチコマンドを繰り返すことで長い音を表現します。

長い音符をピッチコマンドに分割する

(04:15) 長い音符は一連のピッチに分割する必要があります。 4 分音符は 1 つのコマンドを与え、2 分音符は 2 つのコマンドを与え、付点 2 分音符は 3 つのコマンドを与えます。このステップの正式名称はsubdivide()

// Music.swift

public protocol NoteProtocol {
    associatedtype PitchType: PitchProtocol

    /// Play this Note through a ToneOutput
    var tone: Tone { get }

    /// The duration of this Note as a multiple of quarter notes,
    /// e.g., a half note is 2.0, an eighth note is 0.5
    var length: Float { get }

    /// Length of the smallest Note supported
    static var shortestSupportedNoteLength: Float { get }

    /// Subdivide into a series pitches, according to the shortest
    /// supported note
    func subdivide() -> [PitchType]
}

キーポイント:

  • associatedtype PitchTypeノートプロトコルを特定のピッチタイプに接続します。
  • subdivide()ピッチの配列を返します。その長さは、音符の長さとサポートされている最短の長さによって決まります。
  • タイマー ループはノートを直接消費しなくなり、拡張されたピッチを消費します。

再生ループを完了する

(04:30) 最後のステップは、すべてのノートを次のように展開することです。pitches、その後、一定の間隔を置きますTimer一つずつプレイしてください。

// Play more than one tone redux

let toneOutput = ToneOutput()
let notes = [Note.quarter(pitch: .a4), .half(pitch: .a4), .quarter(pitch: .a4)]
var pitches = [Pitch]()
for note in notes {
    pitches.append(contentsOf: note.subdivide())
}
var index = 0

let interval = TimeInterval(Note.shortestSupportedNoteLength * 0.5)
Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { timer in
    guard index < pitches.count else {
        timer.invalidate()
        owner.endPerformance()
        return
    }
    toneOutput.play(tone: Tone(pitch: pitches[index].frequency, volume: 0.3))
    index += 1
}

キーポイント:

  • notes元のスコア構造を維持し、pitches再生前の展開結果です。
  • for note in notesそれぞれの音符を入れるsubdivide()結果は同じ配列に追加されます。
  • interval使用shortestSupportedNoteLength * 0.5、そのままのドラフトの 120 BPM の例に対応します。
  • guard index < pitches.count終了時間に達したらタイマーを停止し、パフォーマンスを終了します。
  • toneOutput.play現在の音程を押すたびにfrequencyサウンド、サイクルバイindex += 1どうぞ。

重要ポイント

  • やるべきこと: リズム練習の遊び場を作ります。 実行する価値がある理由: このセッションでは、4 分音符、2 分音符、付点 2 分音符の長さの関係を明確に説明しました。 開始方法: を使用しますPitchProtocolそしてNoteProtocol少数のピッチと長さを定義し、次を使用します。ToneOutputユーザーがアレンジしたノートを演奏します。
  • やるべきこと: 楽譜チェッカーを作成します。 やる価値がある理由: スワンのチャレンジでは、プレイヤーはリール上の「歓喜の歌」を再生可能なコードに変換する必要があります。 開始方法: まずユーザーが入力した音符配列を渡しますsubdivide()展開して、展開されたピッチ番号が予想されるビートと一致するかどうかを確認します。
  • やるべきこと: メロディーにベースパートを追加します。 実行する価値がある理由: このサイド クエストでは、複数のタイマーを使用してトーンを積み重ね、メイン メロディーの下にベース コードを置くことができることを明確に思い出させます。 開始方法: メインメロディーのタイマーを維持し、ベースノート用に 2 番目のセットを準備します。pitches2 番目のタイマーは同じテンポベースを使用します。
  • 内容: 再生デバッグ出力を作成します。 実行する価値がある理由: 使用できる公式の完全なクリップindexひとつずつ進めてpitches、現在の再生位置を公開するのに最適です。 開始方法: 再生ループの隣に現在のインデックス、ピッチ周波数、残りのピッチ数を記録します。これにより、学習者は細分化後の実際の再生シーケンスを見ることができます。

関連セッション

コメント

GitHub Issues · utterances