ストリーミングはほとんどのブラウザと
Developerアプリで視聴できます。
-
Swan's Quest 第3章:意味のあるスクロール
Swift Playgroundsはすべての世代に向けて4章から成るインタラクティブな冒険、"Swan's Quest"をお届けします。ミュージシャンのみなさん、出番です!第3章ではヒーローがミステリアスな音のスクロールを発見します。そのデコードができるのはあなただけです。(音符が読めない?心配ご無用。賢いLizardが横でアシストしてくれます。これは、楽譜のように大事にとっておきたい体験となるでしょう。) 少しの理論を学び、異なる長さの音作りをマスターしましょう。そうすれば、あなたは、ヒーローが音楽と対面し・・・次のクエストへと向かう手助けができるかもしれません。 Swan's QuestはiPadとMacのSwift Playgroundsのために作成されており、Sonic Workshop、Sensor Arcade、Augmented RealityなどのPlaygroundsの多くで教育的体験を生むフレームワークとリソースを統合しています。Playgroundを作成するには、"Create Swift Playgrounds content for iPad and Mac"をご覧ください。 Developer Forumに立ち寄るのも忘れずに。Swan's Questへのご意見をぜひお聞かせください。
リソース
関連ビデオ
WWDC20
-
ダウンロード
こんにちは WWDCへようこそ “Swan's Quest 第3章:意味のあるスクロール” こんにちは WWDC Swan's Questです 司会のロブです 私たちのチームが皆さんにお届けする― Swift Playgroundsチャレンジの 内側をご案内します 準備はいいですか? このエピソードは 前回学んだ TimerとToneOutputの続きです このエピソード3では 再びトカゲの小屋から― スワンから授かった 次の巻物の謎解きを続けましょう 見てのお楽しみですが― このチャレンジをクリアするには テンポ そして― 長さの違う音の演奏方法を 理解しなければなりません まずは 音楽のしくみの 集中コースから 4分音符と2分音符の 違いを学習します 2回目のチャレンジでは 音の長さは1種類だけでした ここでは タイマーの復習と 長さの違う音を出す方法について説明します 最後に お待ちかねのサイドクエストで まとめます リズムについてお話しします これまでのエピソードで ピッチの違う音を出す方法を説明しました スワンの巻物にあるように 長さの違う音の 演奏方法を学ぶ必要があります 楽譜が苦手な人もいるので― 例として 3種類の音の長さについて 説明します こちらは4分音符 2分音符 そして 付点2分音符 テンポの多くは4分音符で定義されるので 基本的にこれを使います 2分音符は 長さが2倍― 付点2分音符は 4分音符の3倍の長さです スワンが求める曲のテンポは 1分あたり120ビート つまり 1分間に4分音符が 120個あります そこから タイマーの インターバルを計算して― 巻物にある それぞれの長さの音を演奏します Music.swiftには 皆さんが実装できる プロトコルを含めておきました 最初は“Pitch” これはToneOutputの周波数をキャプチャします この例のようになります スワンの巻物にある それぞれの ピッチ用に“case”を追加します
次に 音を特定の長さの “tone”として表現します 長さは 4分音符のかけ算になります 例えば 2分音符なら 2.0を返し― 8分音符なら0.5を返します この例のようになります ここでも それぞれの 音の長さについて― caseをenumに追加します 次に タイマーのコードを更新します それでは スティーブンを呼びましょう
ロブの言うとおり タイマーのコードを更新しなければ― 音の長さを変えられません 始める前に コードを更新して 先ほどロブが紹介したタイプを使ってみます まず サンプルを更新して 今定義したピッチと音のタイプを使います あいにく そのままでは動きません タイマーのTimeIntervalに 音の長さが反映されていないためです ToneOutputは連続したトーンを出力し ストップと言われるまで続けます タイマーの更新は これを利用します まず タイマーのインターバルを 一番短い音の長さの時間にセットします これには NoteProtocolを更新して プロパティの― shortestSupportedNoteLengthを 追加します それから 音の実装を更新します スワンの巻物では 4分音符が一番短いので― シンプルにするために 4分音符を選びます 最後に タイマーのインターバルを― “一番短い音の長さ”掛ける“欲しいテンポ”に 更新します この例では 1分あたり120ビート つまり 4分音符1つの長さは 500ミリ秒です 次に もっと長い音を ピッチの 平坦化された配列に分割します “音を分割する” 各タイマーインターバル用に ToneOutput つまり― ピッチの指示のセットを送ります ここでは 一番短い音が4分音符なので 送る指示は1つです 同じように 2分音符には 指示を2つ送ります 付点2分音符なら 指示を3つ送るようにします
音を分割するために 別のメソッドを NoteProtocolに実装します subdivideはピッチの配列を返し これを集めてToneOutputに送ります タイマーのループでは まず 分割されたピッチの配列を作成します 次に タイマーのループを更新して― 音の配列の代わりに pitchesをイテレートします 完成したこちらの例に 必要なAPIがすべて表示されています これで変更は完了です ロブ まだサイドクエストはある? もちろんだよ スティーブン その前に ご忠告を このサイドクエストには チャレンジのネタバレがあります 先にチャレンジを完成させたい皆さんは ビデオを一時停止して― チャレンジが完成してから 再開してください 幸運を!
では 準備はいいですか? スワンのチャレンジは “歓喜の歌”を演奏することでした 巻物がすべての音符を提供していました コードを更新して 低音域のコードを 演奏できるか試してみてください 1つだけ ヒントを差し上げます “複数のタイマー” 複数のタイマーを使って トーンを重ねてレイヤー化できます このエピソードには Swan's Questの― 3番目のチャレンジを達成する ヒントがありました リズムとテンポを定義する方法を ご説明しました 次に 単一の 固定された長さのタイマーを― 分割されたピッチと併せて使い 長さの違う音を演奏する方法を学びました 幸運を祈ります そしてぜひ― フォーラムに参加して 皆さんの サイドクエストの答えを共有してください お待ちしています
-
-
2:03 - Example Pitch Implementation
// Example Pitch implementation public enum Pitch: Double, PitchProtocol { case a4 = 440.0 var frequency: Double { return self.rawValue } }
-
2:09 - NoteProtocol
// 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 } }
-
2:24 - Example Note implementation
// 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 } } }
-
2:51 - Play more than one tone redux
// Play more than one tone redux let toneOutput = ToneOutput() let notes = [Note.quarter(pitch: .a4), .half(pitch: .c4), .quarter(pitch: .a4)] var index = 0 Timer.scheduledTimer(withTimeInterval: 0.4, repeats: true) { timer in guard index < tones.count else { timer.invalidate() owner.endPerformance() return } toneOutput.play(tone: tones[toneIndex].tone) index += 1 }
-
3:18 - Updating NoteProtocol
// 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 } }
-
3:36 - Updating the Timer interval
// Play more than one tone redux let toneOutput = ToneOutput() let notes = [Note.quarter(pitch: .a4), .half(pitch: .c4), .quarter(pitch: .a4)] var index = 0 let interval = TimeInterval(Note.shortestSupportedNoteLength * 0.5) // 120 BPM Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { timer in guard index < tones.count else { timer.invalidate() owner.endPerformance() return } toneOutput.play(tone: tones[toneIndex].tone) index += 1 }
-
4:15 - Adding subdivide to NoteProtocol
// 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] }
-
4:30 - Putting it all together
// 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 }
-
-
特定のトピックをお探しの場合は、上にトピックを入力すると、関連するトピックにすばやく移動できます。
クエリの送信中にエラーが発生しました。インターネット接続を確認して、もう一度お試しください。