Commits

spencercw committed d1b9528

#39 Implement WASAPI sound driver.

Comments (0)

Files changed (14)

 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sdcc", "sdcc", "{318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "config", "third_party\sdcc\config.vcxproj", "{EA41B63D-76C6-B813-E599-AF5F008BDE4F}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "config", "third_party\sdcc\config.vcxproj", "{138E781D-0EF1-0EB5-30B4-62679FFC496B}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asgb", "third_party\sdcc\sdas\asgb\asgb.vcxproj", "{87AB8AFB-4210-1384-5848-F90A36E6F60A}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "asgb", "third_party\sdcc\sdas\asgb\asgb.vcxproj", "{810C589A-BD01-0E5C-E1C3-BEECBC550C44}"
 	ProjectSection(ProjectDependencies) = postProject
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F} = {EA41B63D-76C6-B813-E599-AF5F008BDE4F}
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B} = {138E781D-0EF1-0EB5-30B4-62679FFC496B}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makebin", "third_party\sdcc\support\makebin\makebin.vcxproj", "{E9C4C803-8C50-0C43-3027-84991DE738FB}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "makebin", "third_party\sdcc\support\makebin\makebin.vcxproj", "{CB29E14C-8B38-70B3-51DE-434474A2B958}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "librarian", "third_party\sdcc\support\librarian\librarian.vcxproj", "{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "librarian", "third_party\sdcc\support\librarian\librarian.vcxproj", "{577CB83E-41DD-5DA9-7129-93D6C83205D7}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "z80a", "third_party\sdcc\src\z80\z80a.vcxproj", "{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "z80a", "third_party\sdcc\src\z80\z80a.vcxproj", "{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "z80", "third_party\sdcc\src\z80\z80.vcxproj", "{DB0EEE50-838B-E0E4-9701-15723B6392B4}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "z80", "third_party\sdcc\src\z80\z80.vcxproj", "{FDDC166E-126C-4942-EF9E-ECBA77641941}"
 	ProjectSection(ProjectDependencies) = postProject
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F} = {EA41B63D-76C6-B813-E599-AF5F008BDE4F}
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232} = {DCF0BBB8-2A9F-C960-0F58-F36D703CD232}
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6} = {1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B} = {138E781D-0EF1-0EB5-30B4-62679FFC496B}
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE} = {7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92} = {BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yacc", "third_party\sdcc\src\yacc.vcxproj", "{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "yacc", "third_party\sdcc\src\yacc.vcxproj", "{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "src", "third_party\sdcc\src\src.vcxproj", "{2CA9A94E-367A-6137-4189-8C5C15EFE681}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "src", "third_party\sdcc\src\src.vcxproj", "{CF4EE703-788F-55F9-CBB4-729413075598}"
 	ProjectSection(ProjectDependencies) = postProject
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F} = {EA41B63D-76C6-B813-E599-AF5F008BDE4F}
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4} = {DB0EEE50-838B-E0E4-9701-15723B6392B4}
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6} = {1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B} = {138E781D-0EF1-0EB5-30B4-62679FFC496B}
+		{FDDC166E-126C-4942-EF9E-ECBA77641941} = {FDDC166E-126C-4942-EF9E-ECBA77641941}
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92} = {BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdcppa", "third_party\sdcc\support\cpp\sdcppa.vcxproj", "{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdcppa", "third_party\sdcc\support\cpp\sdcppa.vcxproj", "{34EA8818-25C0-6495-0210-411D02F93A60}"
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdcpp", "third_party\sdcc\support\cpp\sdcpp.vcxproj", "{9231D57F-C0D3-8C7F-849E-C83455E97F76}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sdcpp", "third_party\sdcc\support\cpp\sdcpp.vcxproj", "{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}"
 	ProjectSection(ProjectDependencies) = postProject
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30} = {ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}
+		{34EA8818-25C0-6495-0210-411D02F93A60} = {34EA8818-25C0-6495-0210-411D02F93A60}
 	EndProjectSection
 EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aslink", "third_party\sdcc\sdas\linksrc\aslink.vcxproj", "{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}"
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "aslink", "third_party\sdcc\sdas\linksrc\aslink.vcxproj", "{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}"
 	ProjectSection(ProjectDependencies) = postProject
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F} = {EA41B63D-76C6-B813-E599-AF5F008BDE4F}
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B} = {138E781D-0EF1-0EB5-30B4-62679FFC496B}
 	EndProjectSection
 EndProject
 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gbdk", "gbdk", "{CD8488DE-0838-46D1-980C-3E325A7CF560}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gbz80", "third_party\gbdk\libc\gbz80.vcxproj", "{49C9510A-C9BD-4199-9602-A724CC257817}"
 	ProjectSection(ProjectDependencies) = postProject
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681} = {2CA9A94E-367A-6137-4189-8C5C15EFE681}
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76} = {9231D57F-C0D3-8C7F-849E-C83455E97F76}
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0} = {7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A} = {87AB8AFB-4210-1384-5848-F90A36E6F60A}
+		{CF4EE703-788F-55F9-CBB4-729413075598} = {CF4EE703-788F-55F9-CBB4-729413075598}
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB} = {931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7} = {577CB83E-41DD-5DA9-7129-93D6C83205D7}
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44} = {810C589A-BD01-0E5C-E1C3-BEECBC550C44}
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "maccer", "third_party\gbdk\maccer\maccer.vcxproj", "{5F04981B-7F84-4440-8BC3-D250AABDACE0}"
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gb", "third_party\gbdk\libc\gb\gb.vcxproj", "{72BF5C65-4B6D-4A4A-A958-6F64EDD14E65}"
 	ProjectSection(ProjectDependencies) = postProject
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681} = {2CA9A94E-367A-6137-4189-8C5C15EFE681}
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76} = {9231D57F-C0D3-8C7F-849E-C83455E97F76}
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0} = {7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A} = {87AB8AFB-4210-1384-5848-F90A36E6F60A}
+		{CF4EE703-788F-55F9-CBB4-729413075598} = {CF4EE703-788F-55F9-CBB4-729413075598}
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB} = {931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7} = {577CB83E-41DD-5DA9-7129-93D6C83205D7}
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44} = {810C589A-BD01-0E5C-E1C3-BEECBC550C44}
 	EndProjectSection
 EndProject
 Global
 		{C482DE3A-7E8A-46E3-8808-9AB6B297DBB9}.Release|Win32.Build.0 = Release|Win32
 		{C482DE3A-7E8A-46E3-8808-9AB6B297DBB9}.Release|x64.ActiveCfg = Release|x64
 		{C482DE3A-7E8A-46E3-8808-9AB6B297DBB9}.Release|x64.Build.0 = Release|x64
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F}.Debug|Win32.ActiveCfg = Debug|Win32
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F}.Debug|Win32.Build.0 = Debug|Win32
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F}.Debug|x64.ActiveCfg = Debug|x64
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F}.Debug|x64.Build.0 = Debug|x64
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F}.Release|Win32.ActiveCfg = Release|Win32
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F}.Release|Win32.Build.0 = Release|Win32
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F}.Release|x64.ActiveCfg = Release|x64
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F}.Release|x64.Build.0 = Release|x64
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A}.Debug|Win32.ActiveCfg = Debug|Win32
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A}.Debug|Win32.Build.0 = Debug|Win32
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A}.Debug|x64.ActiveCfg = Debug|x64
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A}.Debug|x64.Build.0 = Debug|x64
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A}.Release|Win32.ActiveCfg = Release|Win32
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A}.Release|Win32.Build.0 = Release|Win32
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A}.Release|x64.ActiveCfg = Release|x64
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A}.Release|x64.Build.0 = Release|x64
-		{E9C4C803-8C50-0C43-3027-84991DE738FB}.Debug|Win32.ActiveCfg = Debug|Win32
-		{E9C4C803-8C50-0C43-3027-84991DE738FB}.Debug|Win32.Build.0 = Debug|Win32
-		{E9C4C803-8C50-0C43-3027-84991DE738FB}.Debug|x64.ActiveCfg = Debug|x64
-		{E9C4C803-8C50-0C43-3027-84991DE738FB}.Debug|x64.Build.0 = Debug|x64
-		{E9C4C803-8C50-0C43-3027-84991DE738FB}.Release|Win32.ActiveCfg = Release|Win32
-		{E9C4C803-8C50-0C43-3027-84991DE738FB}.Release|Win32.Build.0 = Release|Win32
-		{E9C4C803-8C50-0C43-3027-84991DE738FB}.Release|x64.ActiveCfg = Release|x64
-		{E9C4C803-8C50-0C43-3027-84991DE738FB}.Release|x64.Build.0 = Release|x64
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}.Debug|Win32.ActiveCfg = Debug|Win32
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}.Debug|Win32.Build.0 = Debug|Win32
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}.Debug|x64.ActiveCfg = Debug|x64
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}.Debug|x64.Build.0 = Debug|x64
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}.Release|Win32.ActiveCfg = Release|Win32
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}.Release|Win32.Build.0 = Release|Win32
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}.Release|x64.ActiveCfg = Release|x64
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0}.Release|x64.Build.0 = Release|x64
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}.Debug|Win32.ActiveCfg = Debug|Win32
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}.Debug|Win32.Build.0 = Debug|Win32
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}.Debug|x64.ActiveCfg = Debug|x64
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}.Debug|x64.Build.0 = Debug|x64
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}.Release|Win32.ActiveCfg = Release|Win32
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}.Release|Win32.Build.0 = Release|Win32
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}.Release|x64.ActiveCfg = Release|x64
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232}.Release|x64.Build.0 = Release|x64
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4}.Debug|Win32.ActiveCfg = Debug|Win32
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4}.Debug|Win32.Build.0 = Debug|Win32
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4}.Debug|x64.ActiveCfg = Debug|x64
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4}.Debug|x64.Build.0 = Debug|x64
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4}.Release|Win32.ActiveCfg = Release|Win32
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4}.Release|Win32.Build.0 = Release|Win32
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4}.Release|x64.ActiveCfg = Release|x64
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4}.Release|x64.Build.0 = Release|x64
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}.Debug|Win32.ActiveCfg = Debug|Win32
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}.Debug|Win32.Build.0 = Debug|Win32
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}.Debug|x64.ActiveCfg = Debug|x64
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}.Debug|x64.Build.0 = Debug|x64
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}.Release|Win32.ActiveCfg = Release|Win32
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}.Release|Win32.Build.0 = Release|Win32
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}.Release|x64.ActiveCfg = Release|x64
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6}.Release|x64.Build.0 = Release|x64
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681}.Debug|Win32.ActiveCfg = Debug|Win32
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681}.Debug|Win32.Build.0 = Debug|Win32
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681}.Debug|x64.ActiveCfg = Debug|x64
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681}.Debug|x64.Build.0 = Debug|x64
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681}.Release|Win32.ActiveCfg = Release|Win32
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681}.Release|Win32.Build.0 = Release|Win32
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681}.Release|x64.ActiveCfg = Release|x64
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681}.Release|x64.Build.0 = Release|x64
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}.Debug|Win32.ActiveCfg = Debug|Win32
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}.Debug|Win32.Build.0 = Debug|Win32
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}.Debug|x64.ActiveCfg = Debug|x64
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}.Debug|x64.Build.0 = Debug|x64
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}.Release|Win32.ActiveCfg = Release|Win32
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}.Release|Win32.Build.0 = Release|Win32
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}.Release|x64.ActiveCfg = Release|x64
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30}.Release|x64.Build.0 = Release|x64
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76}.Debug|Win32.ActiveCfg = Debug|Win32
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76}.Debug|Win32.Build.0 = Debug|Win32
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76}.Debug|x64.ActiveCfg = Debug|x64
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76}.Debug|x64.Build.0 = Debug|x64
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76}.Release|Win32.ActiveCfg = Release|Win32
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76}.Release|Win32.Build.0 = Release|Win32
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76}.Release|x64.ActiveCfg = Release|x64
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76}.Release|x64.Build.0 = Release|x64
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}.Debug|Win32.ActiveCfg = Debug|Win32
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}.Debug|Win32.Build.0 = Debug|Win32
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}.Debug|x64.ActiveCfg = Debug|x64
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}.Debug|x64.Build.0 = Debug|x64
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}.Release|Win32.ActiveCfg = Release|Win32
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}.Release|Win32.Build.0 = Release|Win32
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}.Release|x64.ActiveCfg = Release|x64
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5}.Release|x64.Build.0 = Release|x64
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B}.Debug|Win32.ActiveCfg = Debug|Win32
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B}.Debug|Win32.Build.0 = Debug|Win32
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B}.Debug|x64.ActiveCfg = Debug|x64
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B}.Debug|x64.Build.0 = Debug|x64
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B}.Release|Win32.ActiveCfg = Release|Win32
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B}.Release|Win32.Build.0 = Release|Win32
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B}.Release|x64.ActiveCfg = Release|x64
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B}.Release|x64.Build.0 = Release|x64
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44}.Debug|Win32.ActiveCfg = Debug|Win32
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44}.Debug|Win32.Build.0 = Debug|Win32
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44}.Debug|x64.ActiveCfg = Debug|x64
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44}.Debug|x64.Build.0 = Debug|x64
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44}.Release|Win32.ActiveCfg = Release|Win32
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44}.Release|Win32.Build.0 = Release|Win32
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44}.Release|x64.ActiveCfg = Release|x64
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44}.Release|x64.Build.0 = Release|x64
+		{CB29E14C-8B38-70B3-51DE-434474A2B958}.Debug|Win32.ActiveCfg = Debug|Win32
+		{CB29E14C-8B38-70B3-51DE-434474A2B958}.Debug|Win32.Build.0 = Debug|Win32
+		{CB29E14C-8B38-70B3-51DE-434474A2B958}.Debug|x64.ActiveCfg = Debug|x64
+		{CB29E14C-8B38-70B3-51DE-434474A2B958}.Debug|x64.Build.0 = Debug|x64
+		{CB29E14C-8B38-70B3-51DE-434474A2B958}.Release|Win32.ActiveCfg = Release|Win32
+		{CB29E14C-8B38-70B3-51DE-434474A2B958}.Release|Win32.Build.0 = Release|Win32
+		{CB29E14C-8B38-70B3-51DE-434474A2B958}.Release|x64.ActiveCfg = Release|x64
+		{CB29E14C-8B38-70B3-51DE-434474A2B958}.Release|x64.Build.0 = Release|x64
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7}.Debug|Win32.ActiveCfg = Debug|Win32
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7}.Debug|Win32.Build.0 = Debug|Win32
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7}.Debug|x64.ActiveCfg = Debug|x64
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7}.Debug|x64.Build.0 = Debug|x64
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7}.Release|Win32.ActiveCfg = Release|Win32
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7}.Release|Win32.Build.0 = Release|Win32
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7}.Release|x64.ActiveCfg = Release|x64
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7}.Release|x64.Build.0 = Release|x64
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}.Debug|Win32.ActiveCfg = Debug|Win32
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}.Debug|Win32.Build.0 = Debug|Win32
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}.Debug|x64.ActiveCfg = Debug|x64
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}.Debug|x64.Build.0 = Debug|x64
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}.Release|Win32.ActiveCfg = Release|Win32
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}.Release|Win32.Build.0 = Release|Win32
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}.Release|x64.ActiveCfg = Release|x64
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE}.Release|x64.Build.0 = Release|x64
+		{FDDC166E-126C-4942-EF9E-ECBA77641941}.Debug|Win32.ActiveCfg = Debug|Win32
+		{FDDC166E-126C-4942-EF9E-ECBA77641941}.Debug|Win32.Build.0 = Debug|Win32
+		{FDDC166E-126C-4942-EF9E-ECBA77641941}.Debug|x64.ActiveCfg = Debug|x64
+		{FDDC166E-126C-4942-EF9E-ECBA77641941}.Debug|x64.Build.0 = Debug|x64
+		{FDDC166E-126C-4942-EF9E-ECBA77641941}.Release|Win32.ActiveCfg = Release|Win32
+		{FDDC166E-126C-4942-EF9E-ECBA77641941}.Release|Win32.Build.0 = Release|Win32
+		{FDDC166E-126C-4942-EF9E-ECBA77641941}.Release|x64.ActiveCfg = Release|x64
+		{FDDC166E-126C-4942-EF9E-ECBA77641941}.Release|x64.Build.0 = Release|x64
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}.Debug|Win32.ActiveCfg = Debug|Win32
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}.Debug|Win32.Build.0 = Debug|Win32
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}.Debug|x64.ActiveCfg = Debug|x64
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}.Debug|x64.Build.0 = Debug|x64
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}.Release|Win32.ActiveCfg = Release|Win32
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}.Release|Win32.Build.0 = Release|Win32
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}.Release|x64.ActiveCfg = Release|x64
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92}.Release|x64.Build.0 = Release|x64
+		{CF4EE703-788F-55F9-CBB4-729413075598}.Debug|Win32.ActiveCfg = Debug|Win32
+		{CF4EE703-788F-55F9-CBB4-729413075598}.Debug|Win32.Build.0 = Debug|Win32
+		{CF4EE703-788F-55F9-CBB4-729413075598}.Debug|x64.ActiveCfg = Debug|x64
+		{CF4EE703-788F-55F9-CBB4-729413075598}.Debug|x64.Build.0 = Debug|x64
+		{CF4EE703-788F-55F9-CBB4-729413075598}.Release|Win32.ActiveCfg = Release|Win32
+		{CF4EE703-788F-55F9-CBB4-729413075598}.Release|Win32.Build.0 = Release|Win32
+		{CF4EE703-788F-55F9-CBB4-729413075598}.Release|x64.ActiveCfg = Release|x64
+		{CF4EE703-788F-55F9-CBB4-729413075598}.Release|x64.Build.0 = Release|x64
+		{34EA8818-25C0-6495-0210-411D02F93A60}.Debug|Win32.ActiveCfg = Debug|Win32
+		{34EA8818-25C0-6495-0210-411D02F93A60}.Debug|Win32.Build.0 = Debug|Win32
+		{34EA8818-25C0-6495-0210-411D02F93A60}.Debug|x64.ActiveCfg = Debug|x64
+		{34EA8818-25C0-6495-0210-411D02F93A60}.Debug|x64.Build.0 = Debug|x64
+		{34EA8818-25C0-6495-0210-411D02F93A60}.Release|Win32.ActiveCfg = Release|Win32
+		{34EA8818-25C0-6495-0210-411D02F93A60}.Release|Win32.Build.0 = Release|Win32
+		{34EA8818-25C0-6495-0210-411D02F93A60}.Release|x64.ActiveCfg = Release|x64
+		{34EA8818-25C0-6495-0210-411D02F93A60}.Release|x64.Build.0 = Release|x64
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}.Debug|Win32.ActiveCfg = Debug|Win32
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}.Debug|Win32.Build.0 = Debug|Win32
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}.Debug|x64.ActiveCfg = Debug|x64
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}.Debug|x64.Build.0 = Debug|x64
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}.Release|Win32.ActiveCfg = Release|Win32
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}.Release|Win32.Build.0 = Release|Win32
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}.Release|x64.ActiveCfg = Release|x64
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB}.Release|x64.Build.0 = Release|x64
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}.Debug|Win32.ActiveCfg = Debug|Win32
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}.Debug|Win32.Build.0 = Debug|Win32
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}.Debug|x64.ActiveCfg = Debug|x64
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}.Debug|x64.Build.0 = Debug|x64
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}.Release|Win32.ActiveCfg = Release|Win32
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}.Release|Win32.Build.0 = Release|Win32
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}.Release|x64.ActiveCfg = Release|x64
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA}.Release|x64.Build.0 = Release|x64
 		{49C9510A-C9BD-4199-9602-A724CC257817}.Debug|Win32.ActiveCfg = Debug|Win32
 		{49C9510A-C9BD-4199-9602-A724CC257817}.Debug|Win32.Build.0 = Debug|Win32
 		{49C9510A-C9BD-4199-9602-A724CC257817}.Debug|x64.ActiveCfg = Debug|x64
 		{1738D5F6-ED1E-47E0-B2F0-456864B93C1E} = {C7EBDEED-384A-453D-B21E-18EFFDFD0D1E}
 		{1DC41A27-9A43-412D-ABDB-90E6A427A228} = {99F2E5DC-7F2B-4A6C-9567-9616923B46B1}
 		{3D185A1F-893F-43D7-9C0E-84218717FDD0} = {99F2E5DC-7F2B-4A6C-9567-9616923B46B1}
-		{EA41B63D-76C6-B813-E599-AF5F008BDE4F} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{87AB8AFB-4210-1384-5848-F90A36E6F60A} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{E9C4C803-8C50-0C43-3027-84991DE738FB} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{7C8D2BCF-9326-96CB-38B9-E9B9B9CDB5F0} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{DCF0BBB8-2A9F-C960-0F58-F36D703CD232} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{DB0EEE50-838B-E0E4-9701-15723B6392B4} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{1B30CBBB-439F-8DE9-86D8-7A074E12F9E6} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{2CA9A94E-367A-6137-4189-8C5C15EFE681} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{ACBF1FE5-DEC3-8681-3CD1-9D552A4D5B30} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{9231D57F-C0D3-8C7F-849E-C83455E97F76} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
-		{E92FA78D-64F0-EE65-45ED-F4CA76CBCAF5} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{138E781D-0EF1-0EB5-30B4-62679FFC496B} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{810C589A-BD01-0E5C-E1C3-BEECBC550C44} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{CB29E14C-8B38-70B3-51DE-434474A2B958} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{577CB83E-41DD-5DA9-7129-93D6C83205D7} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{7EDC23EC-33F9-C9CB-B609-11ECF72A2FAE} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{FDDC166E-126C-4942-EF9E-ECBA77641941} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{BD79F6A5-99CA-6749-2EA4-2D7CD9101C92} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{CF4EE703-788F-55F9-CBB4-729413075598} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{34EA8818-25C0-6495-0210-411D02F93A60} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{931608A7-ED61-32BB-5C0A-BBD09A5EA6AB} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
+		{2E9D2E31-EF26-FD46-6FAF-E50E885E33BA} = {318C7CCA-CB33-4798-BCCE-EBB4ADE05C3C}
 		{49C9510A-C9BD-4199-9602-A724CC257817} = {CD8488DE-0838-46D1-980C-3E325A7CF560}
 		{5F04981B-7F84-4440-8BC3-D250AABDACE0} = {CD8488DE-0838-46D1-980C-3E325A7CF560}
 		{72BF5C65-4B6D-4A4A-A958-6F64EDD14E65} = {CD8488DE-0838-46D1-980C-3E325A7CF560}

gb_emulator/gb_emulator.vcxproj

       <Optimization>Disabled</Optimization>
       <TreatWarningAsError>true</TreatWarningAsError>
       <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\libsndfile\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>DEBUG;ENABLE_SNDFILE_WINDOWS_PROTOTYPES;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>DEBUG;ENABLE_SNDFILE_WINDOWS_PROTOTYPES;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <LargeAddressAware>true</LargeAddressAware>
       <AdditionalLibraryDirectories>$(OutDir);$(SolutionDir)third_party\libsndfile\x86;$(BOOST_ROOT)\lib</AdditionalLibraryDirectories>
-      <AdditionalDependencies>hqx.lib;libprotobuf.lib;libsndfile-1.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>avrt.lib;hqx.lib;libprotobuf.lib;libsndfile-1.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SubSystem>Windows</SubSystem>
     </Link>
     <PostBuildEvent>
       <Optimization>Disabled</Optimization>
       <TreatWarningAsError>true</TreatWarningAsError>
       <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\libsndfile\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>DEBUG;ENABLE_SNDFILE_WINDOWS_PROTOTYPES;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>DEBUG;ENABLE_SNDFILE_WINDOWS_PROTOTYPES;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <LargeAddressAware>true</LargeAddressAware>
       <AdditionalLibraryDirectories>$(OutDir);$(SolutionDir)third_party\libsndfile\x64;$(BOOST_ROOT)\lib64</AdditionalLibraryDirectories>
-      <AdditionalDependencies>hqx.lib;libprotobuf.lib;libsndfile-1.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>avrt.lib;hqx.lib;libprotobuf.lib;libsndfile-1.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SubSystem>Windows</SubSystem>
     </Link>
     <PostBuildEvent>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <TreatWarningAsError>true</TreatWarningAsError>
       <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\libsndfile\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>ENABLE_SNDFILE_WINDOWS_PROTOTYPES;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ENABLE_SNDFILE_WINDOWS_PROTOTYPES;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <OptimizeReferences>true</OptimizeReferences>
       <LargeAddressAware>true</LargeAddressAware>
       <AdditionalLibraryDirectories>$(OutDir);$(SolutionDir)third_party\libsndfile\x86;$(BOOST_ROOT)\lib</AdditionalLibraryDirectories>
-      <AdditionalDependencies>hqx.lib;libprotobuf.lib;libsndfile-1.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>avrt.lib;hqx.lib;libprotobuf.lib;libsndfile-1.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SubSystem>Windows</SubSystem>
     </Link>
     <PostBuildEvent>
       <IntrinsicFunctions>true</IntrinsicFunctions>
       <TreatWarningAsError>true</TreatWarningAsError>
       <AdditionalIncludeDirectories>$(ProjectDir);$(ProjectDir)include;$(SolutionDir)third_party\hqx\include;$(SolutionDir)third_party\libsndfile\include;$(SolutionDir)third_party\protobuf\src;$(SolutionDir)third_party\sdl\include;$(BOOST_ROOT)</AdditionalIncludeDirectories>
-      <PreprocessorDefinitions>ENABLE_SNDFILE_WINDOWS_PROTOTYPES;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0501;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>ENABLE_SNDFILE_WINDOWS_PROTOTYPES;GB_EMULATOR_EXPORTS;_SCL_SECURE_NO_WARNINGS;WIN32_LEAN_AND_MEAN;_WIN32_WINNT=0x0600;%(PreprocessorDefinitions)</PreprocessorDefinitions>
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
       <OptimizeReferences>true</OptimizeReferences>
       <LargeAddressAware>true</LargeAddressAware>
       <AdditionalLibraryDirectories>$(OutDir);$(SolutionDir)third_party\libsndfile\x64;$(BOOST_ROOT)\lib64</AdditionalLibraryDirectories>
-      <AdditionalDependencies>hqx.lib;libprotobuf.lib;libsndfile-1.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>avrt.lib;hqx.lib;libprotobuf.lib;libsndfile-1.lib;sdl.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <SubSystem>Windows</SubSystem>
     </Link>
     <PostBuildEvent>
     <ClInclude Include="include\gb_emulator\gb_memory.hpp" />
     <ClInclude Include="include\gb_emulator\gb_sound.hpp" />
     <ClInclude Include="include\gb_emulator\gb_sound_sdl.h" />
+    <ClInclude Include="include\gb_emulator\gb_sound_wasapi.h" />
+    <ClInclude Include="include\gb_emulator\gb_sound_wasapi_renderer.h" />
     <ClInclude Include="include\gb_emulator\gb_timers.hpp" />
     <ClInclude Include="include\gb_emulator\gb_video.hpp" />
     <ClInclude Include="src\gb_sound_tables.h" />
     <ClCompile Include="src\gb_memory.cpp" />
     <ClCompile Include="src\gb_sound.cpp" />
     <ClCompile Include="src\gb_sound_sdl.cpp" />
+    <ClCompile Include="src\gb_sound_wasapi.cpp" />
+    <ClCompile Include="src\gb_sound_wasapi_renderer.cpp" />
     <ClCompile Include="src\gb_timers.cpp" />
     <ClCompile Include="src\gb_video.cpp" />
   </ItemGroup>

gb_emulator/gb_emulator.vcxproj.filters

     <ClInclude Include="include\gb_emulator\gb_sound_sdl.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\gb_emulator\gb_sound_wasapi.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="include\gb_emulator\gb_sound_wasapi_renderer.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="gb.pb.cc">
     <ClCompile Include="src\gb_sound_sdl.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\gb_sound_wasapi.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="src\gb_sound_wasapi_renderer.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="gb.proto">

gb_emulator/include/gb_emulator/constants.hpp

 const unsigned CPU_CLOCK                = 4194304;   /* CPU clock frequency */
 const unsigned CYCLES_PER_FRAME         = 70224;     /* Number of clock cycles per frame */
 const double   FRAME_DURATION           = static_cast<double>(CYCLES_PER_FRAME) / CPU_CLOCK;  /* Frame duration in seconds */
-const unsigned SAMPLE_RATE              = 48000;     /* Audio sample rate */
-const double   SAMPLE_CYCLES            = CPU_CLOCK / 4. / SAMPLE_RATE;  /* Number of CPU cycles per audio sample */
 
 /* Memory map */
 const uint16_t BIOS1_END                = 0x0100;

gb_emulator/include/gb_emulator/gb.hpp

 #include <gb_emulator/gb_input.hpp>
 #include <gb_emulator/gb_memory.hpp>
 #include <gb_emulator/gb_sound_sdl.h>
+#include <gb_emulator/gb_sound_wasapi.h>
 #include <gb_emulator/gb_timers.hpp>
 #include <gb_emulator/gb_video.hpp>
 
 	friend class GbMemory;
 	friend class GbSound;
 	friend class GbSoundSdl;
+	friend class GbSoundWasapi;
 	friend class GbVideo;
 	friend class GbDebugger;
 
 	GbCpu cpu_;
 	GbTimers timers_;
 	GbInput input_;
-	GbSoundSdl sound_;
+	GbSoundWasapi sound_;
 	GbVideo video_;
 	GbMemory mem_;
 	GbDebugger debugger_;

gb_emulator/include/gb_emulator/gb_sound.hpp

 
 #include <stdint.h>
 
-#include <boost/circular_buffer.hpp>
 #include <boost/filesystem/path.hpp>
 #include <boost/shared_ptr.hpp>
 
 	virtual ~GbSound();
 
 	//! Generates a single sample.
-	virtual int poll() = 0;
+	/**
+	 * The base class implementation does nothing except return the number of CPU cycles that should
+	 * be executed before poll() is called again.
+	 */
+	virtual int poll();
+
+	//! Checks for and handles any events then returns.
+	/**
+	 * The base class implementation does nothing.
+	 */
+	virtual void pollEvents();
 
 	//! Handles a write to a sound register.
 	void writeIoPort(uint8_t ptr, uint8_t val);
 	void load(const GbSoundData &data);
 
 protected:
-	static const unsigned SOUND_BUF_SIZE = 4096;
-
 	Gb &gb_;
 
 	// Values of registers that are persisted across power cycles
 
 	// Sample rate of the audio device
 	unsigned sampleRate_;
+	// The number of CPU cycles per audio sample
+	double sampleCycles_;
 	// Fractional number of CPU cycles left after a sample has been generated. The sample rate does
 	// not divide evenly into the CPU clock frequency so the number of cycles between samples will
 	// be rounded to the nearest integral value and the excess stored in this variable. It will then
 	// Current index into the LFSR counter array
 	unsigned counterIndex4_;
 
-	// Buffer of samples used by the sound callback
-	boost::circular_buffer<int16_t> soundBuf_;
-
 	// Adjusts the given envelope parameters
 	void adjustEnvelope(uint8_t &envelope, bool envelopeDirection, double envelopeStep,
 		double &envelopeCountdown);

gb_emulator/include/gb_emulator/gb_sound_sdl.h

 #ifndef GB_SOUND_SDL_H_706545C0_1824_11E1_A650_0002A5D5C51B
 #define GB_SOUND_SDL_H_706545C0_1824_11E1_A650_0002A5D5C51B
 
+#include <stdint.h>
+
+#include <boost/circular_buffer.hpp>
+
 #include <gb_emulator/gb_sound.hpp>
 
+//! SDL sound driver.
 class GbSoundSdl: public GbSound
 {
 public:
 	int poll();
 
 private:
+	// Buffer of samples ready to be played
+	boost::circular_buffer<int16_t> soundBuf_;
+
 	// Sound callback which copies samples from soundBuf_ into the device buffer
 	static void soundCallback(void *context, uint8_t *stream, int len);
 	void doSoundCallback(uint8_t *stream, int len);

gb_emulator/include/gb_emulator/gb_sound_wasapi.h

+/*  Copyright © 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GB_SOUND_WASAPI_H_C83C9EC0_1828_11E1_989A_0002A5D5C51B
+#define GB_SOUND_WASAPI_H_C83C9EC0_1828_11E1_989A_0002A5D5C51B
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include <gb_emulator/gb_sound.hpp>
+
+class GbSoundWasapiRenderer;
+
+//! WASAPI sound driver.
+class GbSoundWasapi: public GbSound
+{
+public:
+	//! Constructor; opens the sound device.
+	GbSoundWasapi(Gb &gb);
+
+	//! Destructor.
+	~GbSoundWasapi();
+
+	//! Generates a single sample.
+	int poll();
+
+	//! Checks for and handles any events then returns.
+	void pollEvents();
+
+private:
+	// The audio renderer
+	ATL::CComPtr<GbSoundWasapiRenderer> renderer_;
+
+	// Disabled operations
+	GbSoundWasapi(const GbSoundWasapi &);
+	GbSoundWasapi & operator=(const GbSoundWasapi &);
+};
+
+#endif

gb_emulator/include/gb_emulator/gb_sound_wasapi_renderer.h

+/*  Copyright © 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GB_SOUND_WASAPI_RENDERER_H_CAEF2BE0_1839_11E1_8753_0002A5D5C51B
+#define GB_SOUND_WASAPI_RENDERER_H_CAEF2BE0_1839_11E1_8753_0002A5D5C51B
+
+#include <stdint.h>
+
+#include <boost/circular_buffer.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <audiopolicy.h>
+#include <mmdeviceapi.h>
+
+//! Audio renderer for the GbSoundWasapi driver.
+class GbSoundWasapiRenderer: public IAudioSessionEvents, public IMMNotificationClient
+{
+public:
+	//! Default constructor.
+	GbSoundWasapiRenderer();
+
+	//! Initialises the renderer.
+	/**
+	 * \param device The audio endpoint to render to.
+	 */
+	void initialise(ATL::CComPtr<IMMDevice> device);
+
+	//! Checks for and handles any events then returns.
+	void pollEvents();
+
+	//! Pushes the given sample into the internal buffer to be played back.
+	void pushSample(double left, double right);
+
+	//! Returns the device sample rate.
+	uint32_t sampleRate() const;
+
+	////////////////////////////////////////////////////////////////////////////////////////////////
+	// IAudioSessionEvents
+
+	STDMETHOD(OnChannelVolumeChanged)(DWORD ChannelCount, float *NewChannelVolumes,
+		DWORD ChangedChannel, LPCGUID EventContext);
+	STDMETHOD(OnDisplayNameChanged)(const wchar_t *NewDisplayName, const GUID *EventContext);
+	STDMETHOD(OnGroupingParamChanged)(const GUID *NewGroupingParam, const GUID *EventContext);
+	STDMETHOD(OnIconPathChanged)(const wchar_t *NewIconPath, const GUID *EventContext);
+	STDMETHOD(OnSessionDisconnected)(AudioSessionDisconnectReason DisconnectReason);
+	STDMETHOD(OnSimpleVolumeChanged)(float NewVolume, BOOL NewMute, const GUID *EventContext);
+	STDMETHOD(OnStateChanged)(AudioSessionState NewState);
+
+	////////////////////////////////////////////////////////////////////////////////////////////////
+	// IMMNotificationClient
+
+	STDMETHOD(OnDefaultDeviceChanged)(EDataFlow flow, ERole role,
+		const wchar_t *pwstrDefaultDevice);
+	STDMETHOD(OnDeviceAdded)(const wchar_t *pwstrDeviceId);
+	STDMETHOD(OnDeviceRemoved)(const wchar_t *pwstrDeviceId);
+	STDMETHOD(OnDeviceStateChanged)(const wchar_t *pwstrDeviceId, DWORD dwNewState);
+	STDMETHOD(OnPropertyValueChanged)(const wchar_t *pwstrDeviceId, const PROPERTYKEY key);
+
+	////////////////////////////////////////////////////////////////////////////////////////////////
+	// IUnknown
+
+	STDMETHOD_(ULONG, AddRef)();
+	STDMETHOD(QueryInterface)(REFIID iid, void **pvObject);
+	STDMETHOD_(ULONG, Release)();
+
+private:
+	// Sample render formats
+	enum RenderFormat
+	{
+		RENDER_INT16,
+		RENDER_FLOAT
+	};
+
+	// The COM reference count
+	unsigned long refCount_;
+
+	// The audio endpoint
+	ATL::CComPtr<IMMDeviceEnumerator> devices_;
+	ATL::CComPtr<IMMDevice> device_;
+	ATL::CComPtr<IAudioClient> audioClient_;
+	ATL::CComPtr<IAudioRenderClient> renderClient_;
+	ATL::CComPtr<IAudioSessionControl> audioSessionControl_;
+
+	// The current render format type
+	boost::shared_ptr<WAVEFORMATEX> mixFormat_;
+	RenderFormat renderFormat_;
+	uint32_t samplesPerPeriod_;
+	uint32_t bufferSize_;
+
+	// Flag indicating if we are currently in the process of switching the render stream
+	bool switchingStream_;
+
+	// Audio event HANDLEs
+	boost::shared_ptr<void> audioSamplesReadyEvent_;
+	boost::shared_ptr<void> streamSwitchEvent_;
+	boost::shared_ptr<void> streamSwitchCompleteEvent_;
+
+	// MMCSS handle
+	boost::shared_ptr<void> mmcssHandle_;
+
+	// Sound buffers. Only one of these is used at any time based on the hardware sample format
+	boost::circular_buffer<int16_t> soundBufInt16_;
+	boost::circular_buffer<float> soundBufFloat_;
+
+	// Initialises the audio client
+	void initialiseAudioClient();
+
+	// Handles a stream switch event
+	void handleStreamSwitch();
+
+	// Sends a period's worth of samples to the audio device
+	void sendSamples();
+	
+	// Disabled operations
+	GbSoundWasapiRenderer(const GbSoundWasapiRenderer &);
+	GbSoundWasapiRenderer & operator=(const GbSoundWasapiRenderer &);
+};
+
+#endif

gb_emulator/src/gb.cpp

 	// Enter the main loop
 	int videoCycles = HBLANK_CYCLES;
 	int timerCycles = DIVIDER_CYCLES;
-	int soundCycles = static_cast<int>(SAMPLE_CYCLES);
+	int soundCycles = 0;
 	for (;;)
 	{
 		// Update the debugger
 		timeTmp = timeNow;
 		// Subtract 1 frames' worth of counts
 		timeNow.QuadPart -= frameDuration.QuadPart;
+
+		// Check for any audio events
+		sound_.pollEvents();
 	}
 	while (timeNow.QuadPart <= timeLast_.QuadPart);
 	excessFrames_ = realDuration - (timeTmp.QuadPart - timeLast_.QuadPart);

gb_emulator/src/gb_sound.cpp

 
 GbSound::GbSound(Gb &gb):
 	gb_(gb),
-	excessCycles_(SAMPLE_CYCLES - static_cast<unsigned>(SAMPLE_CYCLES)),
+	sampleRate_(0),
+	sampleCycles_(0),
+	excessCycles_(0),
 	gbFrequency1_(0),
 	gbFrequency2_(0),
 	gbFrequency3_(0),
 	outLevel3_(0),
 	counterData4_(lfsr15),
 	counterDataSize4_(32768),
-	counterIndex4_(0),
-	soundBuf_(SOUND_BUF_SIZE)
+	counterIndex4_(0)
 {
 	memset(wavePattern3_, 0, 0x10);
 	memset(savedRegisters, 0xff, 5);
 {
 }
 
+int GbSound::poll()
+{
+	double realCycles = sampleCycles_;
+	if (gb_.mem_.ioPorts[KEY1] & 0x80)
+	{
+		realCycles *= 2;
+	}
+	realCycles += excessCycles_;
+
+	int cycles = static_cast<int>(realCycles);
+	excessCycles_ = realCycles - cycles;
+	return cycles;
+}
+
+void GbSound::pollEvents()
+{
+}
+
 void GbSound::writeIoPort(uint8_t ptr, uint8_t val)
 {
 	if (ptr != NR52 && gb_.mem_.ioPorts[NR52] & 0x80)

gb_emulator/src/gb_sound_sdl.cpp

 #pragma warning(pop)
 #endif
 
-#include <gb_emulator/constants.hpp>
 #include <gb_emulator/gb.hpp>
 
 using boost::circular_buffer;
 using std::runtime_error;
 using std::string;
 
+static const unsigned SOUND_BUF_SIZE = 8192;
+
 struct SdlAudioLock
 {
 	SdlAudioLock()
 };
 
 GbSoundSdl::GbSoundSdl(Gb &gb):
-GbSound(gb)
+GbSound(gb),
+soundBuf_(SOUND_BUF_SIZE)
 {
 	// Open the device
 	if (!SDL_WasInit(SDL_INIT_AUDIO))
 	}
 
 	SDL_AudioSpec fmt, actFmt;
-	fmt.freq = SAMPLE_RATE;
+	fmt.freq = 48000;
 	fmt.format = AUDIO_S16;
 	fmt.channels = 2;
 	fmt.samples = 512;
 
 	// Save the sample rate
 	sampleRate_ = actFmt.freq;
+	sampleCycles_ = CPU_CLOCK / 4. / sampleRate_;
 }
 
 int GbSoundSdl::poll()
 	}
 
 	// Return the number of cycles to execute before being called again
-	double realCycles = SAMPLE_CYCLES;
-	if (gb_.mem_.ioPorts[KEY1] & 0x80)
-	{
-		realCycles *= 2;
-	}
-	realCycles += excessCycles_;
-
-	int cycles = static_cast<int>(realCycles);
-	excessCycles_ = realCycles - cycles;
-	return cycles;
+	return GbSound::poll();
 }
 
 void GbSoundSdl::soundCallback(void *context, uint8_t *stream, int len)

gb_emulator/src/gb_sound_wasapi.cpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <gb_emulator/gb_sound_wasapi.h>
+
+#include <stdexcept>
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include <gb_emulator/constants.hpp>
+#include <gb_emulator/gb.hpp>
+#include <gb_emulator/gb_sound_wasapi_renderer.h>
+
+using boost::lexical_cast;
+using std::runtime_error;
+using std::string;
+
+GbSoundWasapi::GbSoundWasapi(Gb &gb):
+GbSound(gb)
+{
+	// Initialise COM
+	HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to initialise COM: " + lexical_cast<string>(hr));
+	}
+
+	// Get the audio endpoint
+	CComPtr<IMMDeviceEnumerator> devices;
+	hr = devices.CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get audio device enumerator: " + lexical_cast<string>(hr));
+	}
+
+	CComPtr<IMMDevice> device;
+	hr = devices->GetDefaultAudioEndpoint(eRender, eConsole, &device);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get audio device: " + lexical_cast<string>(hr));
+	}
+
+	// Create the audio renderer
+	{
+		GbSoundWasapiRenderer *tmp = new GbSoundWasapiRenderer();
+		renderer_.Attach(tmp);
+	}
+	renderer_->initialise(device);
+
+	sampleRate_ = renderer_->sampleRate();
+	sampleCycles_ = CPU_CLOCK / 4. / sampleRate_;
+}
+
+GbSoundWasapi::~GbSoundWasapi()
+{
+	// Uninitialise COM
+	CoUninitialize();
+}
+
+int GbSoundWasapi::poll()
+{
+	// Generate the sample
+	double left, right;
+	generateSample(left, right);
+	renderer_->pushSample(left, right);
+
+	// Check for any audio events
+	renderer_->pollEvents();
+
+	// Return the number of cycles to execute before being called again
+	return GbSound::poll();
+}
+
+void GbSoundWasapi::pollEvents()
+{
+	renderer_->pollEvents();
+}

gb_emulator/src/gb_sound_wasapi_renderer.cpp

+/*  Copyright � 2011 Chris Spencer <spencercw@gmail.com>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include <gb_emulator/gb_sound_wasapi_renderer.h>
+
+#include <algorithm>
+#include <stdexcept>
+#include <string>
+
+#include <boost/lexical_cast.hpp>
+
+#include <avrt.h>
+
+using ATL::CComPtr;
+using boost::circular_buffer;
+using boost::lexical_cast;
+using boost::shared_ptr;
+using std::min;
+using std::runtime_error;
+using std::string;
+
+GbSoundWasapiRenderer::GbSoundWasapiRenderer():
+refCount_(1),
+renderFormat_(RENDER_INT16),
+samplesPerPeriod_(0),
+bufferSize_(0),
+switchingStream_(false)
+{
+}
+
+void GbSoundWasapiRenderer::initialise(CComPtr<IMMDevice> device)
+{
+	device_ = device;
+
+	// Create the audio events
+	audioSamplesReadyEvent_.reset(CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE),
+		CloseHandle);
+	streamSwitchEvent_.reset(CreateEventEx(NULL, NULL, 0, EVENT_MODIFY_STATE | SYNCHRONIZE),
+		CloseHandle);
+	streamSwitchCompleteEvent_.reset(CreateEventEx(NULL, NULL,
+		CREATE_EVENT_INITIAL_SET | CREATE_EVENT_MANUAL_RESET, EVENT_MODIFY_STATE | SYNCHRONIZE),
+		CloseHandle);
+
+	if (!audioSamplesReadyEvent_ || !streamSwitchEvent_ || !streamSwitchCompleteEvent_)
+	{
+		throw runtime_error("failed to create audio events: " +
+			lexical_cast<string>(GetLastError()));
+	}
+
+	// Get an IAudioClient for the audio endpoint
+	HRESULT hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
+		reinterpret_cast<void **>(&audioClient_));
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to activate audio client: " + lexical_cast<string>(hr));
+	}
+
+	hr = devices_.CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get audio device enumerator: " + lexical_cast<string>(hr));
+	}
+
+	// Get the format to use to render samples
+	{
+		WAVEFORMATEX *tmp;
+		hr = audioClient_->GetMixFormat(&tmp);
+		if (FAILED(hr))
+		{
+			throw runtime_error("failed to get mix format on the audio client: " +
+				lexical_cast<string>(hr));
+		}
+		mixFormat_.reset(tmp, CoTaskMemFree);
+	}
+
+	WAVEFORMATEXTENSIBLE *mixFormatEx = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(mixFormat_.get());
+	if (mixFormat_->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
+		(mixFormat_->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+		mixFormatEx->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))
+	{
+		renderFormat_ = RENDER_FLOAT;
+	}
+	else if (mixFormat_->wFormatTag == WAVE_FORMAT_PCM ||
+		(mixFormat_->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
+		mixFormatEx->SubFormat == KSDATAFORMAT_SUBTYPE_PCM))
+	{
+		if (mixFormat_->wBitsPerSample == 16)
+		{
+			renderFormat_ = RENDER_INT16;
+		}
+		else
+		{
+			throw runtime_error("unknown audio render type");
+		}
+	}
+	else
+	{
+		throw runtime_error("unknown audio render type");
+	}
+
+	// Initialise the audio client
+	initialiseAudioClient();
+
+	// Initialise stream switching
+	hr = audioClient_->GetService(IID_PPV_ARGS(&audioSessionControl_));
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get audio session control: " + lexical_cast<string>(hr));
+	}
+
+	// Register for session and endpoint change notifications
+	hr = audioSessionControl_->RegisterAudioSessionNotification(this);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to register for stream switch notifications: " +
+			lexical_cast<string>(hr));
+	}
+
+	hr = devices_->RegisterEndpointNotificationCallback(this);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to register for stream switch notifications: " +
+			lexical_cast<string>(hr));
+	}
+
+	// Enable MMCSS on this thread
+	{
+		DWORD taskIndex = 0;
+		mmcssHandle_.reset(AvSetMmThreadCharacteristics(L"Playback", &taskIndex),
+			AvRevertMmThreadCharacteristics);
+		if (!mmcssHandle_)
+		{
+			throw runtime_error("failed to enable MMCSS: " + lexical_cast<string>(GetLastError()));
+		}
+	}
+
+	// Half-fill the buffer with silence to avoid glitching
+	uint8_t *data;
+	hr = renderClient_->GetBuffer(bufferSize_, &data);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get audio data buffer: " + lexical_cast<string>(hr));
+	}
+
+	hr = renderClient_->ReleaseBuffer(bufferSize_, AUDCLNT_BUFFERFLAGS_SILENT);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to release audio data buffer: " + lexical_cast<string>(hr));
+	}
+
+	// Begin playback
+	hr = audioClient_->Start();
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to start audio client: " + lexical_cast<string>(hr));
+	}
+}
+
+void GbSoundWasapiRenderer::pollEvents()
+{
+	HANDLE waitArray[] = {
+		streamSwitchEvent_.get(),
+		audioSamplesReadyEvent_.get() };
+
+	DWORD result = WaitForMultipleObjects(2, waitArray, FALSE, 0);
+	switch (result)
+	{
+	case WAIT_OBJECT_0 + 0:  // streamSwitchEvent_
+		handleStreamSwitch();
+		break;
+
+	case WAIT_OBJECT_0 + 1:  // audioSamplesReadyEvent_
+		sendSamples();
+		break;
+	}
+}
+
+void GbSoundWasapiRenderer::pushSample(double left, double right)
+{
+	switch (renderFormat_)
+	{
+	case RENDER_FLOAT:
+		left  /= 0xffff;
+		right /= 0xffff;
+
+		switch (mixFormat_->nChannels)
+		{
+		case 1:
+			left += right;
+			left /= 2;
+			soundBufFloat_.push_back(static_cast<float>(left));
+			break;
+
+		case 2:
+			soundBufFloat_.push_back(static_cast<float>(left));
+			soundBufFloat_.push_back(static_cast<float>(right));
+			break;
+
+		default:
+			soundBufFloat_.push_back(static_cast<float>(left));
+			soundBufFloat_.push_back(static_cast<float>(right));
+			
+			for (int16_t i = 2; i != mixFormat_->nChannels; ++i)
+			{
+				soundBufFloat_.push_back(0);
+			}
+			break;
+		}
+		break;
+
+	case RENDER_INT16:
+		switch (mixFormat_->nChannels)
+		{
+		case 1:
+			left += right;
+			left /= 2;
+			soundBufInt16_.push_back(static_cast<int16_t>(left));
+			break;
+
+		case 2:
+			soundBufInt16_.push_back(static_cast<int16_t>(left));
+			soundBufInt16_.push_back(static_cast<int16_t>(right));
+			break;
+
+		default:
+			soundBufInt16_.push_back(static_cast<int16_t>(left));
+			soundBufInt16_.push_back(static_cast<int16_t>(right));
+			
+			for (int16_t i = 2; i != mixFormat_->nChannels; ++i)
+			{
+				soundBufInt16_.push_back(0);
+			}
+			break;
+		}
+		break;
+
+	default:
+		assert(!"unknown render format");
+	}
+}
+
+uint32_t GbSoundWasapiRenderer::sampleRate() const
+{
+	return mixFormat_->nSamplesPerSec;
+}
+
+void GbSoundWasapiRenderer::initialiseAudioClient()
+{
+	HRESULT hr = audioClient_->Initialize(AUDCLNT_SHAREMODE_SHARED,
+		AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST, 0, 0, mixFormat_.get(),
+		NULL);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to initialise audio client: " + lexical_cast<string>(hr));
+	}
+
+	hr = audioClient_->GetBufferSize(&bufferSize_);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get audio client buffer size: " + lexical_cast<string>(hr));
+	}
+
+	hr = audioClient_->SetEventHandle(audioSamplesReadyEvent_.get());
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to set audio event: " + lexical_cast<string>(hr));
+	}
+
+	hr = audioClient_->GetService(IID_PPV_ARGS(&renderClient_));
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get render client: " + lexical_cast<string>(hr));
+	}
+
+	// Allocate the intermediate buffer. We want space for 3 periods' worth of data. Initially
+	// fill it half full with silence to avoid glitches when we start playback.
+	switch (renderFormat_)
+	{
+	case RENDER_FLOAT:
+		soundBufFloat_.set_capacity(4 * bufferSize_ * mixFormat_->nChannels);
+		soundBufFloat_.clear();
+		soundBufInt16_.resize(0);
+		break;
+
+	case RENDER_INT16:
+		soundBufInt16_.set_capacity(4 * bufferSize_ * mixFormat_->nChannels);
+		soundBufInt16_.clear();
+		soundBufFloat_.resize(0);
+		break;
+
+	default:
+		assert(!"unknown render format");
+	}
+}
+
+void GbSoundWasapiRenderer::handleStreamSwitch()
+{
+	// When a stream switch is triggered several things happen:
+	// 1. Stop the current renderer.
+	// 2. Release resources associated with the renderer.
+	// 3. Wait for the default device to change, or abort if this doesn't happen.
+	// 4. Get the new default audio endpoint.
+	// 5. Get a new audio client for the endpoint.
+	// 6. Get the new mix format and adjust for any changes.
+	// 7. Reinitialise the audio client.
+	// 8. Reregister the events
+
+	assert(switchingStream_);
+
+	// 1. Stop rendering
+	HRESULT hr = audioClient_->Stop();
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to stop audio client during stream switch: " +
+			lexical_cast<string>(hr));
+	}
+
+	// 2. Release resources
+	hr = audioSessionControl_->UnregisterAudioSessionNotification(this);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to unregister from stream switch notifications during stream "
+			"switch: " + lexical_cast<string>(hr));
+	}
+
+	audioSessionControl_.Release();
+	renderClient_.Release();
+	audioClient_.Release();
+	device_.Release();
+
+	// 3. Wait for the default device to change
+	DWORD result = WaitForSingleObject(streamSwitchCompleteEvent_.get(), 500);
+	if (result == WAIT_TIMEOUT)
+	{
+		throw runtime_error("stream switch timeout");
+	}
+
+	// 4. Get the new default audio endpoint
+	hr = devices_->GetDefaultAudioEndpoint(eRender, eConsole, &device_);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get new default device during stream switch: " +
+			lexical_cast<string>(hr));
+	}
+
+	// 5. Get a new audio client for the endpoint
+	hr = device_->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL,
+		reinterpret_cast<void **>(&audioClient_));
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to activate audio client on the new endpoint: " +
+			lexical_cast<string>(hr));
+	}
+
+	// 6. Get the new mix format
+	{
+		WAVEFORMATEX *tmp;
+		hr = audioClient_->GetMixFormat(&tmp);
+		if (FAILED(hr))
+		{
+			throw runtime_error("failed to get mix format on the new audio client: " +
+				lexical_cast<string>(hr));
+		}
+		mixFormat_.reset(tmp, CoTaskMemFree);
+	}
+	// TODO: Update the sample rate in GbSound
+
+	// 7. Reinitialise the audio client
+	initialiseAudioClient();
+
+	// 8. Reregister for stream switching events
+	hr = audioClient_->GetService(IID_PPV_ARGS(&audioSessionControl_));
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get audio session control on new client: " +
+			lexical_cast<string>(hr));
+	}
+
+	hr = audioSessionControl_->RegisterAudioSessionNotification(this);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to register for stream switch notifications on new client: " +
+			lexical_cast<string>(hr));
+	}
+
+	// Finished; resume playback
+	ResetEvent(streamSwitchCompleteEvent_.get());
+	hr = audioClient_->Start();
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to start the new audio client: " + lexical_cast<string>(hr));
+	}
+
+	switchingStream_ = false;
+}
+
+void GbSoundWasapiRenderer::sendSamples()
+{
+	// Determine how much space is left in the buffer
+	uint32_t padding;
+	HRESULT hr = audioClient_->GetCurrentPadding(&padding);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get the audio padding size: " + lexical_cast<string>(hr));
+	}
+	uint32_t framesAvailable = bufferSize_ - padding;
+	if (!framesAvailable)
+	{
+		return;
+	}
+
+	// If we have one period's worth of data copy it into the buffer, otherwise skip this pass
+	size_t size;
+	const uint8_t *range1, *range2;
+	size_t range1Size, range2Size;
+
+	switch (renderFormat_)
+	{
+	case RENDER_FLOAT:
+		{
+			size = soundBufFloat_.size();
+
+			circular_buffer<float>::array_range array1 = soundBufFloat_.array_one();
+			circular_buffer<float>::array_range array2 = soundBufFloat_.array_two();
+
+			range1 = reinterpret_cast<uint8_t *>(array1.first);
+			range2 = reinterpret_cast<uint8_t *>(array2.first);
+
+			range1Size = array1.second * sizeof(float);
+			range2Size = array2.second * sizeof(float);
+		}
+		break;
+
+	case RENDER_INT16:
+		{
+			size = soundBufInt16_.size();
+
+			circular_buffer<int16_t>::array_range array1 = soundBufInt16_.array_one();
+			circular_buffer<int16_t>::array_range array2 = soundBufInt16_.array_two();
+
+			range1 = reinterpret_cast<uint8_t *>(array1.first);
+			range2 = reinterpret_cast<uint8_t *>(array2.first);
+
+			range1Size = array1.second * sizeof(int16_t);
+			range2Size = array2.second * sizeof(int16_t);
+		}
+		break;
+
+	default:
+		assert(!"unknown render format");
+		return;
+	}
+
+	uint32_t frames = static_cast<uint32_t>(size / mixFormat_->nChannels);
+	if (!frames)
+	{
+		return;
+	}
+	assert(range1Size);
+
+	// Get the data buffer to fill
+	frames = (min)(frames, framesAvailable);
+	uint8_t *data;
+	hr = renderClient_->GetBuffer(frames, &data);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to get audio data buffer: " + lexical_cast<string>(hr));
+	}
+
+	// Copy the sound data into the buffer
+	size_t remaining = frames * mixFormat_->nBlockAlign;
+	size_t copySize = (min)(remaining, range1Size);
+	memcpy(data, range1, copySize);
+	remaining -= copySize;
+
+	if (remaining && range2Size)
+	{
+		data += copySize;
+		copySize = (min)(remaining, range2Size);
+		memcpy(data, range2, copySize);
+		remaining -= copySize;
+	}
+
+	assert(!remaining);
+	switch (renderFormat_)
+	{
+	case RENDER_FLOAT:
+		soundBufFloat_.erase_begin(frames * mixFormat_->nChannels);
+		break;
+
+	case RENDER_INT16:
+		soundBufInt16_.erase_begin(frames * mixFormat_->nChannels);
+		break;
+
+	default:
+		assert(!"unknown render format");
+	}
+
+	// Release the buffer
+	hr = renderClient_->ReleaseBuffer(frames, 0);
+	if (FAILED(hr))
+	{
+		throw runtime_error("failed to release audio data buffer: " + lexical_cast<string>(hr));
+	}
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// IAudioSessionEvents
+
+HRESULT GbSoundWasapiRenderer::OnChannelVolumeChanged(DWORD /*ChannelCount*/,
+	float * /*NewChannelVolumes*/, DWORD /*ChangedChannel*/, LPCGUID /*EventContext*/)
+{
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnDisplayNameChanged(const wchar_t * /*NewDisplayName*/,
+	const GUID * /*EventContext*/)
+{
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnGroupingParamChanged(const GUID * /*NewGroupingParam*/,
+	const GUID * /*EventContext*/)
+{
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnIconPathChanged(const wchar_t * /*NewIconPath*/,
+	const GUID * /*EventContext*/)
+{
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason)
+{
+	switch (DisconnectReason)
+	{
+	case DisconnectReasonDeviceRemoval:
+		// The device we are rendering to has been removed so set the stream switch event. The
+		// stream switch complete event will be set when OnDefaultDeviceChanged is called at which
+		// point rendering will continue.
+		switchingStream_ = true;
+		SetEvent(streamSwitchEvent_.get());
+		break;
+
+	case DisconnectReasonFormatChanged:
+		// The format of the device has changed so set the stream switch event to restart rendering
+		// with the new format. Also set the stream switch complete event because we're not
+		// expecting to see a default device change as well.
+		switchingStream_ = true;
+		SetEvent(streamSwitchEvent_.get());
+		SetEvent(streamSwitchCompleteEvent_.get());
+	}
+
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnSimpleVolumeChanged(float /*NewVolume*/, BOOL /*NewMute*/,
+	const GUID * /*EventContext*/)
+{
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnStateChanged(AudioSessionState /*NewState*/)
+{
+	return S_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// IMMNotificationClient
+
+HRESULT GbSoundWasapiRenderer::OnDefaultDeviceChanged(EDataFlow flow, ERole role,
+	const wchar_t * /*pwstrDefaultDevice*/)
+{
+	if (flow == eRender && role == eConsole)
+	{
+		// The default device for our role has changed so initiate a stream switch
+		if (!switchingStream_)
+		{
+			switchingStream_ = true;
+			SetEvent(streamSwitchEvent_.get());
+		}
+
+		// Set the stream switch complete event to indicate it is ok to re-initialise the audio
+		// renderer
+		SetEvent(streamSwitchCompleteEvent_.get());
+	}
+
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnDeviceAdded(const wchar_t * /*pwstrDeviceId*/)
+{
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnDeviceRemoved(const wchar_t * /*pwstrDeviceId*/)
+{
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnDeviceStateChanged(const wchar_t * /*pwstrDeviceId*/,
+	DWORD /*dwNewState*/)
+{
+	return S_OK;
+}
+
+HRESULT GbSoundWasapiRenderer::OnPropertyValueChanged(const wchar_t * /*pwstrDeviceId*/,
+	const PROPERTYKEY /*key*/)
+{
+	return S_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+// IUnknown
+
+ULONG GbSoundWasapiRenderer::AddRef()
+{
+	return InterlockedIncrement(&refCount_);
+}
+
+HRESULT GbSoundWasapiRenderer::QueryInterface(REFIID iid, void **pvObject)
+{
+	if (!pvObject)
+	{
+		return E_POINTER;
+	}
+	*pvObject = NULL;
+
+	if (iid == IID_IUnknown)
+	{
+		*pvObject = static_cast<IUnknown *>(static_cast<IAudioSessionEvents *>(this));
+	}
+	else if (iid == __uuidof(IAudioSessionEvents))
+	{
+		*pvObject = static_cast<IAudioSessionEvents *>(this);
+	}
+	else if (iid == __uuidof(IMMNotificationClient))
+	{
+		*pvObject = static_cast<IMMNotificationClient *>(this);
+	}
+	else
+	{
+		return E_NOINTERFACE;
+	}
+
+	AddRef();
+	return S_OK;
+}
+
+ULONG GbSoundWasapiRenderer::Release()
+{
+	ULONG result = InterlockedDecrement(&refCount_);
+	if (!result)
+	{
+		delete this;
+	}
+	return result;
+}