Day 9: SoundBuffer fails to lock on most calls

I've been pulling my hair out trying to figure out what I did wrong here. I've been following along with the videos and I can't figure this out. When I step through, lock fails with an invalid argument error most time except for a few rare occasions where it succeeds. The sound skips very often just like the original bug in the video but mine didn't get fixed by initializing BytesToWrite. Any help is appreciated. The sound emitted is also noticeably higher pitch than the one from the video even though I use the same Hz value.


Here is my fill buffer function:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
internal void win32FillSoundBuffer(win32_sound_output *SoundOutput, DWORD ByteToLock, DWORD BytesToWrite)
{
	VOID *Region1;
	DWORD Region1Size;
	VOID *Region2;
	DWORD Region2Size;
	
	
	HRESULT ERROR2 = GlobalSecondaryBuffer->Lock(ByteToLock, BytesToWrite, &Region1, &Region1Size, &Region2, &Region2Size, 0);
	
	if(SUCCEEDED(ERROR2))
	{	
		DWORD Region1SampleCount = Region1Size/SoundOutput->BytesPerSample;
		int16_t *SampleOut = (int16_t *)Region1;
		
		for(DWORD SampleIndex = 0; SampleIndex < Region1SampleCount; ++SampleIndex)
		{			
			float t = 2.0f*Pi*(float)SoundOutput->RunningSampleIndex/(float)SoundOutput->WavePeriod;
			float SineValue = sinf(t);
			int16_t SampleValue = (int16_t)(SineValue*SoundOutput->ToneVolume);
			*SampleOut++ = SampleValue;
			*SampleOut++ = SampleValue;
			++SoundOutput->RunningSampleIndex;
		}
		
		DWORD Region2SampleCount = Region2Size/SoundOutput->BytesPerSample;								
		SampleOut = (int16_t *)Region2;						
		for(DWORD SampleIndex = 0; SampleIndex < Region2SampleCount; ++SampleIndex)
		{
			float t = (float)SoundOutput->RunningSampleIndex/(float)SoundOutput->WavePeriod;
			float SineValue = sin(t);
			int16_t SampleValue = (int16_t)(SineValue*SoundOutput->ToneVolume);
			*SampleOut++ = SampleValue;
			*SampleOut++ = SampleValue;
			++SoundOutput->RunningSampleIndex;
		}
		GlobalSecondaryBuffer->Unlock(Region1, Region1Size, Region2, Region2Size);
	}
	else
	{
		OutputDebugStringA("Failed to lock");
	}
}


Here is my main function:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
int CALLBACK WinMain( _In_ HINSTANCE hInstance,
					  _In_ HINSTANCE hPrevInstance,
					  _In_ LPSTR     lpCmdLine,
					  _In_ int       nCmdShow)
{
	win32LoadXInput();
	
	WNDCLASS WindowClass = {};
	
	win32ResizeDIBSection(&GlobalBackbuffer, 1280, 720);
	
	WindowClass.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
	WindowClass.lpfnWndProc = win32MainWindowCallback;
	WindowClass.hInstance = hInstance;
	//WindowClass.hIcon;
	WindowClass.lpszClassName = "HandmadeHeroWindowClass";
	
	if(RegisterClass(&WindowClass))
	{	
		HWND WindowHandle = CreateWindowEx(0,
					           WindowClass.lpszClassName,
						   "Handmade Hero",
						   WS_OVERLAPPEDWINDOW|WS_VISIBLE,
						   CW_USEDEFAULT,
						   CW_USEDEFAULT,
						   CW_USEDEFAULT,
						   CW_USEDEFAULT,
						   0,
						   0,
						   hInstance,
						   0);
		if(WindowHandle)
		{	
			HDC DeviceContext = GetDC(WindowHandle);
			
			int XOffset = 0;
			int YOffset = 0;
			
			
			win32_sound_output SoundOutput = {};
			
			SoundOutput.SamplesPerSecond = 48000;			
			SoundOutput.Hz = 256;
			SoundOutput.ToneVolume = 3000;
			SoundOutput.RunningSampleIndex = 0;
			SoundOutput.WavePeriod = SoundOutput.SamplesPerSecond/SoundOutput.Hz;
			SoundOutput.BytesPerSample = sizeof(int16_t)*2;
			SoundOutput.SecondaryBufferSize = SoundOutput.SamplesPerSecond*SoundOutput.BytesPerSample;
			
			win32InitSound(WindowHandle, SoundOutput.SamplesPerSecond, SoundOutput.SecondaryBufferSize);
			win32FillSoundBuffer(&SoundOutput, 1, SoundOutput.SecondaryBufferSize);
			GlobalSecondaryBuffer->Play(0, 0 , DSBPLAY_LOOPING);
			
			Running = true;		
			while(Running)
			{
				MSG Message;
				while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE))
				{
					if(Message.message == WM_QUIT)
					{
						Running = false;
					}
					TranslateMessage(&Message);
					DispatchMessage(&Message);
				}
				
				for(DWORD Index = 0; Index<XUSER_MAX_COUNT; ++Index)
				{
					XINPUT_STATE ControllerState;
					if(XInputGetState(Index, &ControllerState) == ERROR_SUCCESS)
					{
						XINPUT_GAMEPAD *Pad = &ControllerState.Gamepad;
						
						bool Up = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_UP);
						bool Down = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN);
						bool Left = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT);
						bool Right = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT);
						bool Start = (Pad->wButtons & XINPUT_GAMEPAD_START);
						bool Back = (Pad->wButtons & XINPUT_GAMEPAD_BACK);
						bool LShoulder = (Pad->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
						bool RShoulder = (Pad->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
						bool AButton = (Pad->wButtons & XINPUT_GAMEPAD_A);
						bool BButton = (Pad->wButtons & XINPUT_GAMEPAD_B);
						bool XButton = (Pad->wButtons & XINPUT_GAMEPAD_X);
						bool YButton = (Pad->wButtons & XINPUT_GAMEPAD_Y);
						
						int16_t LStickX = Pad->sThumbLX;
						int16_t LStickY = Pad->sThumbLY;
						int16_t RStickX = Pad->sThumbRX;
						int16_t RStickY = Pad->sThumbRY;
							
						//XOffset += LStickX>>12;
						//YOffset += LStickY>>12;
						XOffset += RStickX>>12;
						YOffset += RStickY>>12;
						
						if(AButton)
						{
							YOffset +=2;
						}
					}
					else
					{
						
					}
					
				}
				
				XINPUT_VIBRATION Vibration;
				Vibration.wLeftMotorSpeed = 60000;
				Vibration.wRightMotorSpeed = 60000;
				XInputSetState(0, &Vibration);
				
				RenderWeirdGradient(&GlobalBackbuffer, XOffset, YOffset);
				
				DWORD PlayCursor;
				DWORD WriteCursor;
				
				HRESULT ERROR1 = GlobalSecondaryBuffer->GetCurrentPosition(&PlayCursor, &WriteCursor);
				
				if(SUCCEEDED(ERROR1))
				{
					
					DWORD ByteToLock = (SoundOutput.RunningSampleIndex*SoundOutput.BytesPerSample) % SoundOutput.SecondaryBufferSize;
					DWORD BytesToWrite;
					
					if(ByteToLock == PlayCursor)
					{			
						BytesToWrite = 0;				
					}
					else if(ByteToLock > PlayCursor)
					{
						BytesToWrite = (SoundOutput.SecondaryBufferSize - ByteToLock);
						BytesToWrite +=PlayCursor;
					}	
					else
					{
						BytesToWrite = PlayCursor - ByteToLock;
					}
					
					win32FillSoundBuffer(&SoundOutput, ByteToLock, BytesToWrite);
					
				}
				
				
				win32_window_dimension Dimensions = GetWindowDimension(WindowHandle);
				win32DisplayBufferInWindow(&GlobalBackbuffer, DeviceContext, Dimensions.Width, Dimensions.Height, 0, 0, Dimensions.Width, Dimensions.Height);
				
				
			}
			
		}
		else
		{
			//TODO : Logging
		}
	}
	else
	{	
		//TODO Logging
	}
	
	return(0);
}

Edited by Sebastian Colorado on Reason: To mark as solved
hmm I think in your win32FillBuffer, you forgot to times t by 2.0 and PI
1
			float t = (float)SoundOutput->RunningSampleIndex/(float)SoundOutput->WavePeriod;
Thanks for catching that but after fixing it, there are still skips and the buffer still doesn't lock with the same error code.
You may have a problem with the call
1
win32FillSoundBuffer(&SoundOutput, 1, SoundOutput.SecondaryBufferSize);

The original code is
1
Win32FillSoundBuffer(&SoundOutput, 0, SoundOutput.LatencySampleCount*SoundOutput.BytesPerSample);


The buffer is zero based and looks like the lock length exceeds the buffer size.

- tim
Fixing that still doesn't work. Stepping through the code, it looks like when BytesToWrite is 0 or big (about > 100,000 haven't tested it rigorously to see when it starts failing) then the buffer fails to lock and after a few loops, it seems to stay above that high value that causes the lock to fail

Edited by Sebastian Colorado on
I think there is a transcription problem here. The code you posted does not match the original code. Have you replicated the behavior of the original code?

I substituted the posted code into the original and added two diagnostic functions:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#ifdef _DEBUG
#include <sstream>
#include <iomanip>

void echo(const win32_sound_output& SoundOutput)
{
	std::stringstream os;
	os << "win32_sound_output\n"
		<< SoundOutput.SamplesPerSecond		<< "\n"
		<< SoundOutput.ToneHz				<< "\n"
		<< SoundOutput.ToneVolume			<< "\n"
		<< SoundOutput.RunningSampleIndex	<< "\n"
		<< SoundOutput.WavePeriod			<< "\n"
		<< SoundOutput.BytesPerSample		<< "\n"
		<< SoundOutput.SecondaryBufferSize	<< "\n"
		<< SoundOutput.tSine				<< "\n"
		<< SoundOutput.LatencySampleCount	<< "\n"
		;
	::OutputDebugStringA(os.str().c_str());
}
void echo(DWORD ByteToLock, DWORD BytesToWrite)
{
	std::stringstream os;
	os << "ByteToLock " << std::setw(7) << ByteToLock
		<< " "
		<< "BytesToWrite " << BytesToWrite
		<< "\n"
		;
	::OutputDebugStringA(os.str().c_str());
}
#endif

The original code outputs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// original
win32_sound_output
48000
256
3000
0
187
4
192000
0
3200
Primary buffer format was set.
Secondary buffer created successfully.
ByteToLock       0 BytesToWrite 12800
ByteToLock   12800 BytesToWrite 0
...

And the modified code outputs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// modified
win32_sound_output
48000
256
3000
0
187
4
0
0
0
Primary buffer format was set.
Secondary buffer created successfully.
ByteToLock       1 BytesToWrite 192000
ByteToLock  191996 BytesToWrite 4
...


Also, I put a break point on the "Failed to lock" message but it never triggered. This leads me to think that there are other parts in the code that did not get transcribed properly.

The calculation of the arguments to win32FillSoundBuffer are significantly different than the original code. The win32_sound_output structure is not fully initialized.

I would replicate the original behavior with the original code first.

- tim

Correction: I did get the break point to trigger when I let the process run for more than 5 seconds. It happens when the byte to lock equals the play cursor location. You can't do that. I think the calculation of the arguments is the culprit here.

Edited by Timothy McCarthy on Reason: errata
I've been trying to copy everything down exactly as Casey does in the videos but I'm sure I've made mistakes that I haven't caught. This is my source file so far and I've been going back and forth with my code and what's in the video but I haven't found the error.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
#include <windows.h>
#include <stdint.h>
#include <xinput.h>
#include <dsound.h>
#include <math.h>

#define internal static
#define local_persist static
#define global_variable static

#define Pi 3.14159265359f

struct win32_offscreen_buffer
{
	BITMAPINFO Info;
	void *Memory;
	int width;
	int height;
	int Pitch;
};

//TODO : this is global for now
global_variable bool Running;
global_variable win32_offscreen_buffer GlobalBackbuffer;
global_variable LPDIRECTSOUNDBUFFER GlobalSecondaryBuffer;


struct win32_window_dimension
{
	int Width;
	int Height;
};

#define X_INPUT_GET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_STATE *pState)
typedef X_INPUT_GET_STATE(x_input_get_state);
X_INPUT_GET_STATE(xInputGetStateStub)
{
	return(ERROR_DEVICE_NOT_CONNECTED);
}
global_variable x_input_get_state *XInputGetState_;
#define XInputGetState XInputGetState_


#define X_INPUT_SET_STATE(name) DWORD WINAPI name(DWORD dwUserIndex, XINPUT_VIBRATION *pVibration)
typedef X_INPUT_SET_STATE(x_input_set_state);
X_INPUT_SET_STATE(xInputSetStateStub)
{
	return(ERROR_DEVICE_NOT_CONNECTED);
}
global_variable x_input_set_state *XInputSetState_;
#define XInputSetState XInputSetState_

#define DIRECT_SOUND_CREATE(name) HRESULT WINAPI name(LPCGUID pcGuidDevice, LPDIRECTSOUND *ppDS, LPUNKNOWN pUnkOuter);
typedef DIRECT_SOUND_CREATE(direct_sound_create)



internal void win32LoadXInput(void)
{
	HMODULE XInputLibrary = LoadLibrary("xinput1_4.dll");
	if(!XInputLibrary)
	{
		XInputLibrary = LoadLibrary("xinput1_3.dll");
	}
	if(XInputLibrary)
	{
		XInputGetState = (x_input_get_state *)GetProcAddress(XInputLibrary, "XInputGetState");
		XInputSetState = (x_input_set_state *)GetProcAddress(XInputLibrary, "XInputSetState");
	}
}

internal void win32InitSound(HWND WindowHandle, int32_t BufferSize, int32_t SamplesPerSecond)
{
	HMODULE DSoundLibrary = LoadLibrary("dsound.dll");	
	if(DSoundLibrary)
	{
		direct_sound_create *DirectSoundCreate = (direct_sound_create *)GetProcAddress(DSoundLibrary, "DirectSoundCreate");
		LPDIRECTSOUND DirectSound;
		if (DirectSoundCreate && SUCCEEDED(DirectSoundCreate(0, &DirectSound, 0)))
		{			
			WAVEFORMATEX WaveFormat = {};
					WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
					WaveFormat.nChannels = 2;
					WaveFormat.nSamplesPerSec = SamplesPerSecond;
					WaveFormat.wBitsPerSample = 16;
					WaveFormat.nBlockAlign = (WaveFormat.nChannels*WaveFormat.wBitsPerSample)/8;
					WaveFormat.nAvgBytesPerSec = WaveFormat.nSamplesPerSec*WaveFormat.nBlockAlign;
					WaveFormat.cbSize = 0; 
					
			if(SUCCEEDED(DirectSound->SetCooperativeLevel(WindowHandle, DSSCL_PRIORITY)))
			{
				DSBUFFERDESC BufferDescription = {};
				BufferDescription.dwSize = sizeof(BufferDescription);
				BufferDescription.dwFlags = DSBCAPS_PRIMARYBUFFER;
				
				LPDIRECTSOUNDBUFFER PrimaryBuffer;
				if(SUCCEEDED(DirectSound->CreateSoundBuffer(&BufferDescription, &PrimaryBuffer, 0)))
				{
					
					if(SUCCEEDED(PrimaryBuffer->SetFormat(&WaveFormat)))
					{
						OutputDebugStringA("FormatSet");
					}
				}
				

			}
			
			DSBUFFERDESC BufferDescription = {};
			BufferDescription.dwSize = sizeof(BufferDescription);
			BufferDescription.dwFlags = 0;
			BufferDescription.dwBufferBytes = BufferSize;
			BufferDescription.lpwfxFormat = &WaveFormat;
			if(SUCCEEDED(DirectSound->CreateSoundBuffer(&BufferDescription, &GlobalSecondaryBuffer, 0)))
			{
				OutputDebugStringA("SecondaryBuffer CREATEd");
				
			}
		}
	}
}

internal win32_window_dimension GetWindowDimension(HWND WindowHandle)
{
	win32_window_dimension Dimensions;
	RECT ClientRect;
	GetClientRect(WindowHandle, &ClientRect);
	Dimensions.Width = ClientRect.right - ClientRect.left;
	Dimensions.Height = ClientRect.bottom - ClientRect.top;
	return(Dimensions);
}

internal void RenderWeirdGradient(win32_offscreen_buffer *Buffer,
								  int BlueOffset, int GreenOffset)
{
	int width = Buffer->width;
	int height = Buffer->height;
	
	uint8_t *Row = (uint8_t *)Buffer->Memory;
	for(int y = 0; y<height; ++y)
	{
		uint32_t *Pixel = (uint32_t *)Row;
		for(int x = 0; x < width; ++x)
		{
			uint8_t Blue = (x + BlueOffset);
			uint8_t Green = (y + GreenOffset);
			
			*Pixel++ = ((Green << 8)|Blue);
		}
		Row += Buffer->Pitch;
	}
}

internal void win32ResizeDIBSection(win32_offscreen_buffer *Buffer, int width, int height)
{
	if(Buffer->Memory)
	{
		VirtualFree(Buffer->Memory, 0, MEM_RELEASE);
	}
	
	Buffer->width = width;
	Buffer->height = height;
	int BytesPerPixel = 4;
	
	Buffer->Info.bmiHeader.biSize = sizeof(Buffer->Info.bmiHeader);
	Buffer->Info.bmiHeader.biWidth = width;
	Buffer->Info.bmiHeader.biHeight = -height;
	Buffer->Info.bmiHeader.biPlanes = 1;
	Buffer->Info.bmiHeader.biBitCount = 32;
	Buffer->Info.bmiHeader.biCompression = BI_RGB;
	
	int NumberOfPixels = width*height;
	int MemorySize = BytesPerPixel*NumberOfPixels;
	Buffer->Memory = VirtualAlloc(0, MemorySize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
	
	Buffer->Pitch = width*BytesPerPixel;
	
	
}
internal void win32DisplayBufferInWindow(win32_offscreen_buffer *Buffer, HDC DeviceContext, int WindowWidth, int WindowHeight, int x, int y, int width, int height)
{
	StretchDIBits(DeviceContext, /*x, y, width, height, x, y, width, height,*/ 0, 0, WindowWidth, WindowHeight, 0, 0, Buffer->width, Buffer->height, Buffer->Memory, &Buffer->Info, DIB_RGB_COLORS, SRCCOPY);
}

LRESULT CALLBACK win32MainWindowCallback(_In_ HWND   hwnd,
										 _In_ UINT   uMsg,
										 _In_ WPARAM wParam,
										 _In_ LPARAM lParam)
{
	LRESULT Result = 0;
	switch(uMsg)
	{
		case WM_SIZE:
		{
		}break;
		case WM_DESTROY:
		{
			Running = false;
		}break;
		case WM_SYSKEYDOWN:
		case WM_SYSKEYUP:
		case WM_KEYDOWN:
		case WM_KEYUP:
		{
			uint32_t VKCode = wParam;
			int32_t WasDown = lParam & (1<<30);
			int32_t IsDown = lParam & (1<<31); 
			if(VKCode == 'W')
			{
				OutputDebugStringA("Esc: ");
				if(IsDown)
				{
					OutputDebugStringA("IsDown ");
				}
				if(WasDown)
				{
					OutputDebugStringA("WasDown ");
				}
				OutputDebugStringA("\n");
			}
			else if(VKCode == 'A')
			{
				
			}
			int32_t AltKeyWasDown = lParam & (1<<29);
			if((VKCode == VK_F4) && AltKeyWasDown)
			{
				Running = false;
			}
		}break;
		case WM_CLOSE:
		{
			Running = false;
		}break;
		case WM_ACTIVATEAPP:
		{
		}break;
		case WM_PAINT:
		{
			PAINTSTRUCT Paint;
			HDC DeviceContext = BeginPaint(hwnd, &Paint);
			int x = Paint.rcPaint.left;
			int y = Paint.rcPaint.top;
			int Height = Paint.rcPaint.bottom - Paint.rcPaint.top;
			int Width  = Paint.rcPaint.right - Paint.rcPaint.left;
			
			win32_window_dimension Dimensions = GetWindowDimension(hwnd);
			win32DisplayBufferInWindow(&GlobalBackbuffer, DeviceContext, Dimensions.Width, Dimensions.Height, x, y, Width, Height);
			EndPaint(hwnd, &Paint);
		}break;
		default:
		{
			//OutputDebugStringA("default\n");
			Result = DefWindowProc(hwnd, uMsg, wParam, lParam);
		}break;
	}
	return(Result);
}

struct win32_sound_output
{
	int SamplesPerSecond;			
	int Hz;
	int16_t ToneVolume;
	uint32_t RunningSampleIndex;
	int WavePeriod;
	int BytesPerSample;
	int SecondaryBufferSize;
			
};

internal void win32FillSoundBuffer(win32_sound_output *SoundOutput, DWORD ByteToLock, DWORD BytesToWrite)
{
	VOID *Region1;
	DWORD Region1Size;
	VOID *Region2;
	DWORD Region2Size;
	
	
	HRESULT ERROR2 = GlobalSecondaryBuffer->Lock(ByteToLock, BytesToWrite, &Region1, &Region1Size, &Region2, &Region2Size, 0);
	
	if(SUCCEEDED(ERROR2))
	{	
		OutputDebugStringA("Locked");
		DWORD Region1SampleCount = Region1Size/SoundOutput->BytesPerSample;
		int16_t *SampleOut = (int16_t *)Region1;
		
		for(DWORD SampleIndex = 0; SampleIndex < Region1SampleCount; ++SampleIndex)
		{			
			float t = 2.0f*Pi*(float)SoundOutput->RunningSampleIndex/(float)SoundOutput->WavePeriod;
			float SineValue = sinf(t);
			int16_t SampleValue = (int16_t)(SineValue*SoundOutput->ToneVolume);
			*SampleOut++ = SampleValue;
			*SampleOut++ = SampleValue;
			++SoundOutput->RunningSampleIndex;
		}
		
		DWORD Region2SampleCount = Region2Size/SoundOutput->BytesPerSample;								
		SampleOut = (int16_t *)Region2;						
		for(DWORD SampleIndex = 0; SampleIndex < Region2SampleCount; ++SampleIndex)
		{
			float t = 2.0f*Pi*(float)SoundOutput->RunningSampleIndex/(float)SoundOutput->WavePeriod;
			float SineValue = sin(t);
			int16_t SampleValue = (int16_t)(SineValue*SoundOutput->ToneVolume);
			*SampleOut++ = SampleValue;
			*SampleOut++ = SampleValue;
			++SoundOutput->RunningSampleIndex;
		}
		GlobalSecondaryBuffer->Unlock(Region1, Region1Size, Region2, Region2Size);
	}
	else
	{
		OutputDebugStringA("Failed to lock\n");
	}
}

int CALLBACK WinMain( _In_ HINSTANCE hInstance,
					  _In_ HINSTANCE hPrevInstance,
					  _In_ LPSTR     lpCmdLine,
					  _In_ int       nCmdShow)
{
	win32LoadXInput();
	
	WNDCLASS WindowClass = {};
	
	win32ResizeDIBSection(&GlobalBackbuffer, 1280, 720);
	
	WindowClass.style = CS_HREDRAW|CS_VREDRAW|CS_OWNDC;
	WindowClass.lpfnWndProc = win32MainWindowCallback;
	WindowClass.hInstance = hInstance;
	//WindowClass.hIcon;
	WindowClass.lpszClassName = "HandmadeHeroWindowClass";
	
	if(RegisterClass(&WindowClass))
	{	
		HWND WindowHandle = CreateWindowEx(0,
										   WindowClass.lpszClassName,
										   "Handmade Hero",
										   WS_OVERLAPPEDWINDOW|WS_VISIBLE,
										   CW_USEDEFAULT,
										   CW_USEDEFAULT,
										   CW_USEDEFAULT,
										   CW_USEDEFAULT,
										   0,
										   0,
										   hInstance,
										   0);
		if(WindowHandle)
		{	
			HDC DeviceContext = GetDC(WindowHandle);
			
			int XOffset = 0;
			int YOffset = 0;
			
			
			win32_sound_output SoundOutput = {};
			
			SoundOutput.SamplesPerSecond = 48000;			
			SoundOutput.Hz = 256;
			SoundOutput.ToneVolume = 3000;
			SoundOutput.RunningSampleIndex = 0;
			SoundOutput.WavePeriod = SoundOutput.SamplesPerSecond/SoundOutput.Hz;
			SoundOutput.BytesPerSample = sizeof(int16_t)*2;
			SoundOutput.SecondaryBufferSize = SoundOutput.SamplesPerSecond*SoundOutput.BytesPerSample;
			
			win32InitSound(WindowHandle, SoundOutput.SamplesPerSecond, SoundOutput.SecondaryBufferSize);
			win32FillSoundBuffer(&SoundOutput, 0, SoundOutput.SecondaryBufferSize);
			GlobalSecondaryBuffer->Play(0, 0 , DSBPLAY_LOOPING);
			
			Running = true;		
			while(Running)
			{
				MSG Message;
				while(PeekMessage(&Message, 0, 0, 0, PM_REMOVE))
				{
					if(Message.message == WM_QUIT)
					{
						Running = false;
					}
					TranslateMessage(&Message);
					DispatchMessage(&Message);
				}
				
				for(DWORD Index = 0; Index<XUSER_MAX_COUNT; ++Index)
				{
					XINPUT_STATE ControllerState;
					if(XInputGetState(Index, &ControllerState) == ERROR_SUCCESS)
					{
						XINPUT_GAMEPAD *Pad = &ControllerState.Gamepad;
						
						bool Up = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_UP);
						bool Down = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_DOWN);
						bool Left = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_LEFT);
						bool Right = (Pad->wButtons & XINPUT_GAMEPAD_DPAD_RIGHT);
						bool Start = (Pad->wButtons & XINPUT_GAMEPAD_START);
						bool Back = (Pad->wButtons & XINPUT_GAMEPAD_BACK);
						bool LShoulder = (Pad->wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER);
						bool RShoulder = (Pad->wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER);
						bool AButton = (Pad->wButtons & XINPUT_GAMEPAD_A);
						bool BButton = (Pad->wButtons & XINPUT_GAMEPAD_B);
						bool XButton = (Pad->wButtons & XINPUT_GAMEPAD_X);
						bool YButton = (Pad->wButtons & XINPUT_GAMEPAD_Y);
						
						int16_t LStickX = Pad->sThumbLX;
						int16_t LStickY = Pad->sThumbLY;
						int16_t RStickX = Pad->sThumbRX;
						int16_t RStickY = Pad->sThumbRY;
							
						//XOffset += LStickX>>12;
						//YOffset += LStickY>>12;
						XOffset += RStickX>>12;
						YOffset += RStickY>>12;
						
						if(AButton)
						{
							YOffset +=2;
						}
					}
					else
					{
						
					}
					
				}
				
				XINPUT_VIBRATION Vibration;
				Vibration.wLeftMotorSpeed = 60000;
				Vibration.wRightMotorSpeed = 60000;
				XInputSetState(0, &Vibration);
				
				RenderWeirdGradient(&GlobalBackbuffer, XOffset, YOffset);
				
				DWORD PlayCursor;
				DWORD WriteCursor;
				
				HRESULT ERROR1 = GlobalSecondaryBuffer->GetCurrentPosition(&PlayCursor, &WriteCursor);
				
				if(SUCCEEDED(ERROR1))
				{
					
					DWORD ByteToLock = (SoundOutput.RunningSampleIndex*SoundOutput.BytesPerSample) % SoundOutput.SecondaryBufferSize;
					DWORD BytesToWrite;
					
					if(ByteToLock == PlayCursor)
					{			
						BytesToWrite = 0;				
					}
					else if(ByteToLock > PlayCursor)
					{
						BytesToWrite = (SoundOutput.SecondaryBufferSize - ByteToLock);
						BytesToWrite +=PlayCursor;
					}	
					else
					{
						BytesToWrite = PlayCursor - ByteToLock;
					}
					
					win32FillSoundBuffer(&SoundOutput, ByteToLock, BytesToWrite);
					
				}
				
				
				win32_window_dimension Dimensions = GetWindowDimension(WindowHandle);
				win32DisplayBufferInWindow(&GlobalBackbuffer, DeviceContext, Dimensions.Width, Dimensions.Height, 0, 0, Dimensions.Width, Dimensions.Height);
				
				
			}
			
		}
		else
		{
			//TODO : Logging
		}
	}
	else
	{	
		//TODO Logging
	}
	
	return(0);
}
If the scratching is similar to this https://www.youtube.com/watch?v=B20JEo6Dh6s then its probably just hardware differences, but yeah this is code from day 100ish I think so a lot of things are different.

For me that scratching just went away around the time we finished the Asset builder.. I just assumed it has to do with the speed of fetching the data.


I do remember the Pitch being higher as well, I thought that might be from the video compression for the episode tho.
It doesn't sound like the scratching from the background music, it sound like the skipping towards the end with the projectile's sound effect. And I still have the problem that the buffer doesn't lock so it doesn't get updated. Like I coded in the part where pressing the a button changes the frequency but it doesn't because even though the values are updated, once the fill buffer method is called, the locking fails and the buffer doesn't get filled.
Are you unlocking as well? if you don't then it might cause skipping as Casey realized.

Audio is a bit weird for me too. For some reason it was working before I move everything into different files. Now, my sound is a bit off and I don't know whats causing it. :angry:

Edited by popcorn on
I ran the code as provided and saw the repeated lock failure. When i noticed it happened on the first call to win32FillSoundBuffer I became suspicious that the buffer wasn't created correctly. Sure enough...

The API I have for Win32InitDSound
1
2
internal void
Win32InitDSound(HWND Window, int32 SamplesPerSecond, int32 BufferSize)


The posted API is
1
internal void win32InitSound(HWND WindowHandle, int32_t BufferSize, int32_t SamplesPerSecond)


OK, the argument order was altered. Did the call make the swap as well?
1
win32InitSound(WindowHandle, SoundOutput.SamplesPerSecond, SoundOutput.SecondaryBufferSize);

Oops!

The sound buffer was too small and chaos ensued.

When I changed the call arguents
1
win32InitSound(WindowHandle, SoundOutput.SecondaryBufferSize, SoundOutput.SamplesPerSecond);


things got much better results.

However, the lock will still fail when ByteToLock == PlayCursor.

- tim
Jeeze. Thank you, that has caused me so much trouble