Variable-Pitch Sine Wave Output
?
?

Keyboard Navigation

Global Keys

[, < / ], > Jump to previous / next episode
W, K, P / S, J, N Jump to previous / next timestamp
t / T Toggle theatre / SUPERtheatre mode
V Revert filter to original state Y Select link (requires manual Ctrl-c)

Menu toggling

q Quotes r References f Filter y Link c Credits

In-Menu and Index Controls

a
w
s
d
h j k l


Esc Close menu / unfocus timestamp

Quotes and References Menus and Index

Enter Jump to timestamp

Quotes, References and Credits Menus

o Open URL (in new tab)

Filter Menu

x, Space Toggle category and focus next
X, ShiftSpace Toggle category and focus previous
v Invert topics / media as per focus

Filter and Link Menus

z Toggle filter / linking mode

Credits Menu

Enter Open URL (in new tab)
1:14Review DirectSound init and square wave
1:14Review DirectSound init and square wave
1:14Review DirectSound init and square wave
2:28Story Time: The First Game Jam
2:28Story Time: The First Game Jam
2:28Story Time: The First Game Jam
8:56The moral of the story
8:56The moral of the story
8:56The moral of the story
11:26How to approach debugging the sound code
11:26How to approach debugging the sound code
11:26How to approach debugging the sound code
15:40Square vs Sine Wave
15:40Square vs Sine Wave
15:40Square vs Sine Wave
17:33Generating a Sine Wave
17:33Generating a Sine Wave
17:33Generating a Sine Wave
17:56Tangent: Intro to Floating Point
17:56Tangent: Intro to Floating Point
17:56Tangent: Intro to Floating Point
20:38C Libraries for sin
20:38C Libraries for sin
20:38C Libraries for sin
24:55Story Time: Before floating point hardware
24:55Story Time: Before floating point hardware
24:55Story Time: Before floating point hardware
26:43Tangent: Fixed-point math
26:43Tangent: Fixed-point math
26:43Tangent: Fixed-point math
32:35Tangent: IEEE Floating-point representation
32:35Tangent: IEEE Floating-point representation
32:35Tangent: IEEE Floating-point representation
42:15Implementing the Sine wave test tone
42:15Implementing the Sine wave test tone
42:15Implementing the Sine wave test tone
48:09Lets run it
48:09Lets run it
48:09Lets run it
49:01Debugging in earnest
49:01Debugging in earnest
49:01Debugging in earnest
57:39A different error
57:39A different error
57:39A different error
57:58Refactor for clarity
57:58Refactor for clarity
57:58Refactor for clarity
1:03:46Where did it go?
1:03:46Where did it go?
1:03:46Where did it go?
1:04:08Eureka!
1:04:08Eureka!
1:04:08Eureka!
1:05:22Victory
1:05:22Victory
1:05:22Victory
1:09:52Q&A
🗩
1:09:52Q&A
🗩
1:09:52Q&A
🗩
1:10:07In D this bug couldn't have happened. D always initializes variables.
1:10:07In D this bug couldn't have happened. D always initializes variables.
1:10:07In D this bug couldn't have happened. D always initializes variables.
1:13:36How do you know that ByteToLock is far enough ahead of the PlayCursor?
1:13:36How do you know that ByteToLock is far enough ahead of the PlayCursor?
1:13:36How do you know that ByteToLock is far enough ahead of the PlayCursor?
1:15:40Try [compiling with] -W3 or -W4
1:15:40Try [compiling with] -W3 or -W4
1:15:40Try [compiling with] -W3 or -W4
1:15:54Where do I look for standard C library docs?
1:15:54Where do I look for standard C library docs?
1:15:54Where do I look for standard C library docs?
1:16:39I think you can now remove the ByteToLock == PlayCursor case.
1:16:39I think you can now remove the ByteToLock == PlayCursor case.
1:16:39I think you can now remove the ByteToLock == PlayCursor case.
1:17:58Will we use the sin() in the actual game?
1:17:58Will we use the sin() in the actual game?
1:17:58Will we use the sin() in the actual game?
1:18:07Is autocomplete/intellisense a bad idea? You don't seem to use it.
1:18:07Is autocomplete/intellisense a bad idea? You don't seem to use it.
1:18:07Is autocomplete/intellisense a bad idea? You don't seem to use it.
1:19:25Re-explaining the last bug
1:19:25Re-explaining the last bug
1:19:25Re-explaining the last bug
1:20:50Is it possible for bits to spill over into neighboring variables? For example, when shifting.
1:20:50Is it possible for bits to spill over into neighboring variables? For example, when shifting.
1:20:50Is it possible for bits to spill over into neighboring variables? For example, when shifting.
1:26:26Will we use the same output buffer to overlay several sounds?
1:26:26Will we use the same output buffer to overlay several sounds?
1:26:26Will we use the same output buffer to overlay several sounds?
1:26:33Change tone frequency based on input, and the bug will resurface.
1:26:33Change tone frequency based on input, and the bug will resurface.
1:26:33Change tone frequency based on input, and the bug will resurface.
1:26:37Casey: That's a different bug, lets look at it.
1:26:37Casey: That's a different bug, lets look at it.
1:26:37Casey: That's a different bug, lets look at it.
1:30:43Fixed
1:30:43Fixed
1:30:43Fixed
1:30:57Diagramming the frequency change issue
1:30:57Diagramming the frequency change issue
1:30:57Diagramming the frequency change issue
1:35:18Let's map the pitch to the sticks.
1:35:18Let's map the pitch to the sticks.
1:35:18Let's map the pitch to the sticks.
1:37:00Theramin Simulator 2014 Tech Demo
1:37:00Theramin Simulator 2014 Tech Demo
1:37:00Theramin Simulator 2014 Tech Demo
1:37:13Let's lower the latency
1:37:13Let's lower the latency
1:37:13Let's lower the latency
1:42:49If we could live with a slightly less accurate sin, we could approximate it with polynomials.
1:42:49If we could live with a slightly less accurate sin, we could approximate it with polynomials.
1:42:49If we could live with a slightly less accurate sin, we could approximate it with polynomials.
1:45:08Will the art and audio be released into the public domain?
1:45:08Will the art and audio be released into the public domain?
1:45:08Will the art and audio be released into the public domain?
1:46:20Is it a good idea to use fixed point math for games that require deterministic simulation for multiplayer? Or can you use floating point across systems?
1:46:20Is it a good idea to use fixed point math for games that require deterministic simulation for multiplayer? Or can you use floating point across systems?
1:46:20Is it a good idea to use fixed point math for games that require deterministic simulation for multiplayer? Or can you use floating point across systems?
1:48:48Sometimes you use 'bool' and sometimes 'bool32'
1:48:48Sometimes you use 'bool' and sometimes 'bool32'
1:48:48Sometimes you use 'bool' and sometimes 'bool32'
1:49:30Are you going to use ETW to log context switches for the game?
1:49:30Are you going to use ETW to log context switches for the game?
1:49:30Are you going to use ETW to log context switches for the game?
1:49:42Is this the audio api we will be using to ship the game?
1:49:42Is this the audio api we will be using to ship the game?
1:49:42Is this the audio api we will be using to ship the game?
1:50:01Is it safe to call DirectSound without initializing COM?
1:50:01Is it safe to call DirectSound without initializing COM?
1:50:01Is it safe to call DirectSound without initializing COM?
1:50:47Can we do an episode on emacs?
1:50:47Can we do an episode on emacs?
1:50:47Can we do an episode on emacs?
1:51:33Fix: XInput - missing dependencies
1:51:33Fix: XInput - missing dependencies
1:51:33Fix: XInput - missing dependencies
1:53:45Fix: Arithmetic for stick values
1:53:45Fix: Arithmetic for stick values
1:53:45Fix: Arithmetic for stick values
2:01:55Sign off
🗩
2:01:55Sign off
🗩
2:01:55Sign off
🗩

Variable-Pitch Sine Wave Output

The parable of the Game Jam

Don't let this happen to you, kids. You need good audio hardware to debug audio code.

Debugging the audio code

Square vs Sine Waves

Because square waves are already pretty harsh, they prevent our ability to diagnose some audio bugs. A Sine wave is a "purer" tone, and will enhance our ear's ability to pick up on weirdness. The sin function, however, is defined to return a value between -1 and 1, so we need to talk how to represent fractional numbers on a computer.

Fixed-point arithmetic

Fixed point is just integer math. We define some number of bits at the low end of our integer to represent the fractional part of the number, and the remaining bits represent the whole part. Normal addition, subtraction, multiplication, and division work fine, although we need to be aware of the rounding characteristics of fixed-point when doing any numeric computation.

Fixed-point math was used more widely before computers commonly had floating point hardware. Today every computer, GPU, and phone has very strong floating point capabilities, and so it is the defacto way to do numerics on a modern computer.

Floating-point representation

Floating-point is a more complicated (although very rigorously defined) way to represent fractional values. It approaches the problem by dividing the available bits into:

Such that the value represented is given by (sign)(mantissa * 2^exponent). This allows us to preserve a consistent number of bits of precision (like "significant figures" from your physics class), given by the size of the mantissa, regardless of the scale of our numbers, given by the exponent. This means that values representable by floating point will be more densely packed near zero, and more sparse near the limits.

Floating Point values come in a few different precisions: float (single-precision, 32-bit), double (double-precision, 64-bit), and long double (128-bit). We will rely on single-precision floats almost exclusively, because they are good enough, and often we can operate on them twice as quickly as doubles.

Generating a sine-wave test tone

For the test code, we use the c standard sinf function. It's defined in math.h. Its defined to accept a float "angle" and return a float in the range [-1.0f, 1.0f]. The angle is a function of:

When we set the tone frequency, we calculate its period in samples, and call it WavePeriod.

The "angle" is then given by 2.0f*PI*((float)RunningSampleIndex / (float)WavePeriod).

The SampleValue is given by the sinf(angle) * Volume.

Smoothing the waveform on frequency change

When you change the frequency with the current code, you'll end up with an artifact. To combat this, you need to track an additional value in your synth, basically your progress through the period of the wave, here called tSine. Incrementally accumulate it per sample written:

tSine += 2.0f*Pi32*1.0f/(float)WavePeriod // tSine = 2*Pi*how many "WavePeriods" we've played since we started
        

Then just use it as the angle for the SampleValue calculation.

SampleValue = sinf(tSine) * ToneVolume;
        

Additional Fixes