Slide 47 of 53

A sound is a shape

Sustained and struck, side by side
import hive.ui

type Msg { Tick }

type Car {
	seat:  Int
	x:     Float
	z:     Float
	speed: Float
	knock: Bool
}

// A func like every other shape-maker, and worth noticing that a sound is
// exactly as much of a value as a box is: sixty of these are built a second
// and thrown away, while the voice on the other end of the name goes on
// running the whole time.
func noises(cars: Car[]): ui.Shape[dyn] {
	mut ui.Shape[dyn] out = []
	for each car in cars {
		// Sustained: it lasts as long as the scene goes on naming it, and
		// bends with the speed rather than being restarted at a new one.
		append(out, ui.sound([
			ui.at(car.x, 0.6, car.z),
			ui.voice(ui.Voice.Engine()),
			ui.pitch(0.7 + car.speed / 90.0),
			ui.volume(0.6),
		], "engine-{car.seat}"))

		// Struck: it fires when the name arrives and is deaf to everything
		// after it. Letting the name go and bringing it back is a second one.
		if car.knock {
			append(out, ui.sound([
				ui.at(car.x, 0.6, car.z),
				ui.voice(ui.Voice.Impact()),
				ui.pitch(1.0),
				ui.volume(0.8),
			], "hit-{car.seat}"))
		}
	}
	return out
}

func view(cars: Car[dyn]): ui.View {
	solids := [ui.ground([ui.paint(ui.Tone.HEX("#3a3f45"))], 400.0, 400.0)]
	return ui.scene([
		ui.grow(1),
		ui.eye(0.0, 1.4, 6.0),
		ui.aim(0.0, 0.0),
		ui.background(ui.Tone.HEX("#8ec5ff")),
	], solids + noises(cars))
}

proc update(cars: Car[dyn], msg: Msg, envelope: hive.syslink.Envelope): Car[dyn] {
	return cars
}

proc main(): void {
	Car[dyn] grid = [Car(1, -2.0, 0.0, 40.0, false), Car(2, 2.0, -6.0, 62.0, true)]
	ui.window("Sound", view, update, grid)
}
sound(attrs, name) is a scene's eighth shape, and the one that is heard from where it stands instead of drawn where it stands. It takes at like every other shape and three of its own: voice(Voice) is which noise it is, pitch(Float) how high and volume(Float) how loud. Both of those are multiples of that voice at rest rather than hertz and decibels — what an engine sounds like at 1.0 is the renderer's business, and a program says twice as fast and half as loud, which is what it actually knows.A sound is listed beside the solids for one reason worth more than the oddity costs: a noise in a world has a place in it, and a place is what every other member of that set already knows how to carry. So it is positioned by the same at, written into the same frame, and arrives at the page with the very camera that has to hear it — where a module of its own would be a second description of where things are.The payload is a name, and the name is the shape's identity between frames. This is the one place audio cannot follow the rule the solids follow. A box is matched to whatever was in its slot last frame, because a view built by the same code lists the same things in the same order — and a box rebuilt anyway is a box nobody can tell was rebuilt. An oscillator rebuilt anyway is a click, sixty of them a second, and a slot cannot promise otherwise, because a scene is free to stop drawing the car in front and every sound after it would shift up a place and become a different engine. So a name that is in this frame and was in the last one carries on, a name that has just arrived starts, and a name that has gone fades out.Which of those a voice actually does belongs to the voice rather than to the program. Engine, Tyres, Brakes, Wind, Crowd and Music are sustained: they last as long as the scene goes on naming them. Impact and Chime are struck: they fire once, when the name arrives, and are deaf to everything after it — because a crash is a thing that happened rather than a thing that is happening, and a world of values has no other way to say so. Naming "hit-3" for as long as a car knows it was hit is one impact; letting the name go and bringing it back is a second one.Sounds are positioned and the listener is the scene's own eye and aim, so what is heard on the left is what is drawn on the left, and a car goes past your ear as well as your eye. Music is the exception, having nowhere to stand, so it is never panned. And nothing is downloaded for any of it: every voice is oscillators and filtered noise, for the same reason a Surface is a drawn pattern rather than an image — a built Hive program is one file. That buys something as well as costing something, since a voice made of a frequency can be bent, and an engine that is a recording of one speed played faster is an engine you can hear looping.