WWDC Quick Look 💓 By SwiftGGTeam
Swan's Quest, Chapter 2: A time for tones

Swan's Quest, Chapter 2: A time for tones

Watch original video

Highlight

The challenge in Chapter 2 is to use code to generate a tone of a specific frequency and play it through the device speaker. Different from “Detecting Sounds” in Chapter 1, this chapter requires you to “Generate Sounds”.

Core Content

The first chapter of Swan’s Quest introduces players to accessible interfaces. Chapter 2 brings the scene back to Lizard and Swan’s scroll puzzle: players have to play a string of musical notes for Swan in order to pass the challenge.

The hard part about this isn’t playing a sound file. The challenge requires you to generate tones according to frequency and then play them in sequence according to a fixed rhythm. The lecture compresses the entry point into two APIs:ToneOutputResponsible for speaking out,TimerResponsible for advancing the sequence of notes in time.

A single 440 Hz middle A rings easily. To really complete the challenge, you have to switch notes in a 400 millisecond beat loop. At the end, stop ToneOutput, cancel the timer, and callendPerformance()Tell Swan the show is done.

The final side quest continues to use the same code. Swan gave C-major scale, and the practice questions required changing it to F-major scale. The reasoning given in the speech is very specific: start from F4, reuse the existing frequency, and multiply the frequency by 2 when you need to raise the octave, for example, change the 440 Hz of the A4 into the 880 Hz of the A5.

Detailed Content

ToneOutput is responsible for generating continuous sound

01:09ToneOutputis the voice of this challenge. The presentation states that it generates 44,100 samples per second, allowing continuous sound to be heard by the human ear.

//  ToneOutput.swift

public class ToneOutput : AURenderCallbackDelegate {
    let sampleRate = 44100.0

    public func play(tone: Tone) { /**/ }

    public func stopTones() { /**/ }

    // ...

}

Key points:

  • sampleRate = 44100.0Corresponds to 44,100 samples a second in transcript. -play(tone:)Create a signal corresponding to tone. -stopTones()Stop the currently playing tone.
  • This type comes from Sonic Workshop and is put into Sonic Create for players to use in their own projects.

(01:30) PassToneOutputThe value isTone. It exposes only two core parameters: pitch and volume.

//  ToneOutput.swift

public struct Tone: Codable {
    public var pitch: Double
    public var volume: Double

    // ...
}

Key points:

  • pitchIt’s frequencyDoubleexpress. -volumeTooDouble
  • CodableLet tones be saved and passed around as simple data structures.
  • This allows the challenge code to not have to deal with the underlying audio sample directly.

Play a middle A first

(01:45) The first piece of runnable code only does one thing: createToneOutput, construct a tone of 440 Hz and volume 0.3, and then play it.

// Play a middle A

import SPCAudio

let toneOutput = ToneOutput()
let middleA = Tone(pitch: 440.0, volume: 0.3)
toneOutput.play(tone: middleA)

Key points:

  • import SPCAudioIntroduce the audio type required for the example. -ToneOutput()is the vocal object. -Tone(pitch: 440.0, volume: 0.3)Corresponds to 440 Hz middle A in transcript. -toneOutput.play(tone: middleA)will cause the playground to emit a continuous tone.

(02:21) Lecture reminder: If the Playground code does not stop running, this tone will keep playing. For exampleDispatchQueue.main.asyncAfterCalled after 400 millisecondsstopTones()

// Play a middle A

import SPCAudio

let toneOutput = ToneOutput()
let a4 = Tone(pitch: 440.0, volume: 0.3)
toneOutput.play(tone: a4)

DispatchQueue.main.asyncAfter(deadline: .now() + DispatchTimeInterval.milliseconds(400)) {
    toneOutput.stopTones()
}

Key points:

  • a4Still middle A at 440 Hz. -toneOutput.play(tone: a4)Start the sound first. -DispatchTimeInterval.milliseconds(400)Give the sound a short duration.
  • in closuretoneOutput.stopTones()Responsible for ending continuous playback.

Use Timer to string together multiple notes

(02:51) A single delay is suitable for stopping a tone. To play multiple notes continuously, Speech recommendsTimer, because it can repeatedly execute the same piece of logic at fixed intervals.

// Play more than one tone

let toneOutput = ToneOutput()
let tones = [
    Tone(pitch: 440.00, volume: 0.3),
    Tone(pitch: 493.88, volume: 0.3),
    Tone(pitch: 523.25, volume: 0.3)
]

var toneIndex = 0
Timer.scheduledTimer(withTimeInterval: 0.4, repeats: true) { timer in
    guard toneIndex < tones.count else {
        toneOutput.stopTones()
        timer.invalidate()
        owner.endPerformance()
        return
    }

    toneOutput.play(tone: tones[toneIndex])
    toneIndex += 1
}

Key points:

  • tonesSave the frequencies of middle A, middle B, and middle C. -toneIndexRecord which tone in the array is to be played next. -withTimeInterval: 0.4Plays the next note in the transcript every 400 milliseconds. -guard toneIndex < tones.countAfter reaching the end of the array, enter the ending branch. -toneOutput.stopTones()Stop audio output,timer.invalidate()Stop repeating timer. -owner.endPerformance()Tells the challenge system that the performance is complete and the player gains progress.

Core Takeaways

  1. Make a scale practice device What to do: Use the buttons to select a scale such as C-major, F-major, etc., then play each note in sequence. Why it’s worth doing: The session has given the tone array, the 0.4 second timer and the method of multiplying the frequency by 2 when rising an octave. How ​​to start: Reuse the official one firsttonesarray sumTimer.scheduledTimer, and then map different scales into differentTone(pitch:volume:)list.

  2. Do a listening memory challenge What to do: Play three to five tones and let the user repeat them in order. Why it’s worth doing: This is a direct extension of the “Play a string of musical notes for Swan” quest in Swan’s Quest. How ​​to start: UseToneOutput.play(tone:)To play the question, usetoneIndexControl the progress and call the end logic similar to the challenge after the user completes.

  3. Make a frequency experiment panel What it does: Provide pitch and volume inputs, allowing users to hear the results of different frequencies and different volumes. Why it’s worth doing:ToneThe public parameters are onlypitchandvolume, great for understanding frequency and loudness. How ​​to start: PutTone(pitch: 440.0, volume: 0.3)The two numbers in are replaced by control input, and a new one is generated before playing.Tone

  4. Make a side quest helper What to do: Rewrite C-major scale to F-major scale and mark the B-flat that needs to be added. Why it’s worth doing: The transcript clearly requires starting from F4, reusing C-major code, and doubling the frequency to get a higher octave. How ​​to start: Keep the original play loop first and only replace ittonesThe frequency in the array; when encountering a higher octave, multiply the original frequency by 2.

Comments

GitHub Issues · utterances