-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
waveOutPrepareHeader error in Blackthorne #270
Comments
If resampler is not present, flush output before changing PCM parameters.
Please try again with latest code. |
I still see problems # 1 and # 2 with 2979ba4. I wasn't able to get crash however. |
Race condition happens at exactly the same place as before. |
I think the only proper long-term solution is to integrate resample code in a way similar to softfloat. |
Yes, my fix only handles the PCM part of the sound emulation. I'll have a look how to stop the FM and speaker input of the mixer code. You are right, the real bugfix for all platforms would be some resample code in Bochs. Since the default sample rate is 44100 Hz (CD audio quality), resampling for 11025 and 22050 Hz should be relatively easy to implement. |
Blackthorne uses not 11025 and 22050, but 11111 and 22222. |
Arbitrary resampling can be done with filter banks somehow. |
I decided to make small program demonstrating arbitrary resampling. Source codeusing System;
using System.Collections.Generic;
using System.IO;
namespace ArbResamp
{
class Program
{
Program()
{
Resample("sine_1000", 2222, 3000);
Resample("bt33", 11111, 44100);
}
void Resample(string fileIn, double sampleRate1, double sampleRate2)
{
Console.Write("Resampling " + fileIn + "...");
double[] inData = SignalStoD(LoadWav(fileIn + ".wav"));
double intCounter = 0.0;
double intAdjust = sampleRate1 / sampleRate2;
const int filterLength = 511;
double k = 2 * Math.PI / (filterLength + 1);
int n = filterLength / 2;
var resampled = new List<double>();
for (int i = 0; i < inData.Length; i++)
{
while (intCounter < 1.0)
{
double sum = 0;
double t = -n - intCounter;
for (int j = 0; j < filterLength; j++)
{
double lpf = 1.0;
if (t != 0.0)
lpf = Sinc(t * Math.PI);
double inSample = 0.0;
int sampleIndex = i + j - n;
if (sampleIndex >= 0 && sampleIndex < inData.Length)
inSample = inData[sampleIndex];
double hannWindow = 0.5 - 0.5 * Math.Cos(k * (j + 1));
sum += lpf * hannWindow * inSample;
t++;
}
if (sum < -1.0)
sum = -1.0;
else if (sum > 1.0)
sum = 1.0;
resampled.Add(sum);
intCounter += intAdjust;
}
intCounter -= 1.0;
}
SaveWav(fileIn + "_rs.wav", SignalDtoS(resampled.ToArray(), true), (int)sampleRate2);
Console.WriteLine(" Done");
}
double Sinc(double x)
{
if (x == 0.0)
return 1.0;
return Math.Sin(x) / x;
}
double[] SignalStoD(short[] signal)
{
double[] signalD = new double[signal.Length];
for (int i = 0; i < signal.Length; i++)
signalD[i] = signal[i] / 32768.0;
return signalD;
}
short[] SignalDtoS(double[] signal, bool dither = false)
{
Random rnd = new Random(12345);
short[] signalS = new short[signal.Length];
if (dither)
{
for (int i = 0; i < signal.Length; i++)
{
double r = (rnd.NextDouble() + rnd.NextDouble()) - 1;
signalS[i] = Convert.ToInt16(signal[i] * 32766.0 + r);
}
}
else
{
for (int i = 0; i < signal.Length; i++)
signalS[i] = Convert.ToInt16(signal[i] * 32767.0);
}
return signalS;
}
short[] LoadWav(string path)
{
FileStream fs = File.Open(path, FileMode.Open);
long sampleCount = (fs.Length - 0x2C) / 2;
fs.Seek(0x2C, SeekOrigin.Begin);
byte[] smpBuf = new byte[2];
short[] signal = new short[sampleCount];
for (int i = 0; i < sampleCount; i++)
{
fs.Read(smpBuf, 0, 2);
signal[i] = BitConverter.ToInt16(smpBuf, 0);
}
fs.Close();
return signal;
}
void SaveWav(string path, short[] signal, int sampleRate)
{
FileStream fs = File.Open(path, FileMode.Create);
fs.Write(new byte[] { 0x52, 0x49, 0x46, 0x46 }, 0, 4); // "RIFF"
fs.Write(BitConverter.GetBytes((uint)(36 + signal.Length * 2)), 0, 4);
fs.Write(new byte[] { 0x57, 0x41, 0x56, 0x45 }, 0, 4); // "WAVE"
fs.Write(new byte[] { 0x66, 0x6D, 0x74, 0x20 }, 0, 4); // "fmt"
fs.Write(BitConverter.GetBytes((uint)(16)), 0, 4);
fs.Write(BitConverter.GetBytes((ushort)(1)), 0, 2);
fs.Write(BitConverter.GetBytes((ushort)(1)), 0, 2); // mono
fs.Write(BitConverter.GetBytes((uint)(sampleRate)), 0, 4); // Hz
fs.Write(BitConverter.GetBytes((uint)(sampleRate * 2)), 0, 4);
fs.Write(BitConverter.GetBytes((ushort)(2)), 0, 2);
fs.Write(BitConverter.GetBytes((ushort)(16)), 0, 2); // bps
fs.Write(new byte[] { 0x64, 0x61, 0x74, 0x61 }, 0, 4); // "data"
fs.Write(BitConverter.GetBytes((uint)(signal.Length * 2)), 0, 4);
foreach (short v in signal)
fs.Write(BitConverter.GetBytes(v), 0, 2);
fs.Close();
}
static void Main(string[] args)
{
new Program();
}
}
} Upon execution, it resamples two files:
Quality of resampling can be estimated by looking at spectrograms made by SoX: Two effects can be seen there:
These spectrograms were made with Visible artifacts, however, does not mean audible artifacts, so with this option delay, quality and CPU usage can be controlled at the same time. Since this program is small, it is also slow. With polyphase filter banks results of costly |
When playing Blackthorne game on Windows host, race condition in sound code may happen.
It results in one of three consequences:
waveOutPrepareHeader(): error = 5
messages are displayed.Problem reproduces most reliably in such scenario:
bochs_bthorne.mp4
During its execution, game switches between 11kHz and 22kHz output, which generates race condition between
bx_soundlow_waveout_win_c::set_pcm_params
(waveOutOpen
) andbx_soundlow_waveout_win_c::output
(waveOutPrepareHeader
) functions.Test files: bthorne.zip.
Version: 1ff88fd.
The text was updated successfully, but these errors were encountered: