ハイライト
第 3 章では、プレイヤーはスワン リールのリズムを次のように変換する必要があります。
Pitch、NoteProtocolそして一定間隔Timer、次に使用しますToneOutputさまざまな長さの音符を演奏します。
主要内容
第 2 章では、1 つの音符の長さ、固定間隔のみを扱います。Timerチャレンジを完了するには十分です。第 3 章でトカゲの小屋に戻った後、スワンの新しい巻物では、プレイヤーはリズムの違いでメロディーを演奏する必要があります。四分音符、二分音符、付点二分音符はすべて可能です。元のループは同じビートに従って進むだけで、長い音に遭遇すると表現力を失います。
Apple はまず問題を 2 つのモデルに分類します。Pitch伝える責任があるToneOutputどの周波数を再生するか、NoteProtocol音のトーンと長さを説明する役割を果たします。長さは 4 分音符の倍数で表現されるため、2 分音符が返されます。2.0、8分音符が戻ります0.5。
難点はタイマーです。逐語草案は明らかに次のことを思い出させます。ToneOutputやめるよう求められるまで声を出し続けます。このセッションのメソッドは、Timerサポートされている最も短いノート間隔に固定され、長いノートは複数のピッチ コマンドに分割されます。このようにして、ループはシンプルなままですが、さまざまな長さの音符を表現できます。
詳細
ピッチを使用して周波数を説明します
(01:50)Music.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、現在の再生位置を公開するのに最適です。 開始方法: 再生ループの隣に現在のインデックス、ピッチ周波数、残りのピッチ数を記録します。これにより、学習者は細分化後の実際の再生シーケンスを見ることができます。
関連セッション
- Swan’s Quest、第 2 章: トーンの時間 — まずは学習してください
ToneOutput、Timerそして固定長ノートの演奏方法。 - Swan’s Quest、第 4 章: シーケンスの完了 — シーケンサー、MIDI ノート、トラックへの音楽的な挑戦を続けます。
- Swan’s Quest、第 1 章: 暗闇の声 — Swan’s Quest シリーズの最初に戻って、プレイグラウンド ブックのチャレンジ構造を理解します。
- iPad および Mac 用の Swift Playgrounds コンテンツを作成する — 同様のクロスプラットフォームのプレイグラウンド ブックを作成したい場合は、引き続きこの本をご覧ください。
- Swift Playgrounds で SwiftUI ビューを構築する — Swift Playground でライブ ビューを編集、実行、プレビューする方法を学びます。
コメント
GitHub Issues · utterances