diff --git a/help/CommandLine.html b/help/CommandLine.html
index b277ce8d..6b8b802d 100644
--- a/help/CommandLine.html
+++ b/help/CommandLine.html
@@ -203,9 +203,9 @@
-wav-speaker <file.wav>
Save the speaker audio to a .wav file.
- Warning: there's no file size limit, so it just keeps saving until AppleWin exits (~5MB per minute).
+ Warning: there's no file size limit, so it just keeps saving until AppleWin exits (~10MB per minute).
- Save the Mockingboard audio to a .wav file.
+ Save the Mockingboard audio (but not speech) to a .wav file.
-wav-mockingboard <file.wav>
Warning: there's no file size limit, so it just keeps saving until AppleWin exits (~10MB per minute).
diff --git a/source/Speaker.cpp b/source/Speaker.cpp
index 4d3d7ee3..2fcdd1cc 100644
--- a/source/Speaker.cpp
+++ b/source/Speaker.cpp
@@ -53,7 +53,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// their buffers are running low.
//
-static const unsigned short g_nSPKR_NumChannels = 1;
+static const unsigned short g_nSPKR_NumChannels = 2;
static const DWORD g_dwDSSpkrBufferSize = MAX_SAMPLES * sizeof(short) * g_nSPKR_NumChannels;
//-------------------------------------
@@ -64,7 +64,7 @@ static short* g_pSpeakerBuffer = NULL;
const short SPKR_DATA_INIT = (short)0x8000;
short g_nSpeakerData = SPKR_DATA_INIT;
-static UINT g_nBufferIdx = 0;
+static UINT g_nBufferIdx = 0; // Sample index
static short* g_pRemainderBuffer = NULL;
static UINT g_nRemainderBufferSize; // Setup in SpkrInitialize()
@@ -101,6 +101,11 @@ void Spkr_OutputToRiff(void)
g_bSpkrOutputToRiff = true;
}
+UINT Spkr_GetNumChannels(void)
+{
+ return g_nSPKR_NumChannels;
+}
+
//=============================================================================
static void DisplayBenchmarkResults ()
@@ -269,7 +274,7 @@ void SpkrInitialize ()
{
InitRemainderBuffer();
- g_pSpeakerBuffer = new short [SPKR_SAMPLE_RATE]; // Buffer can hold a max of 1 seconds worth of samples
+ g_pSpeakerBuffer = new short [SPKR_SAMPLE_RATE * g_nSPKR_NumChannels]; // Buffer can hold a max of 1 seconds worth of samples
}
}
@@ -341,8 +346,19 @@ static void UpdateRemainderBuffer(ULONG* pnCycleDiff)
nSampleMean += (signed long) g_pRemainderBuffer[i];
nSampleMean /= (signed long) g_nRemainderBufferSize;
- if(g_nBufferIdx < SPKR_SAMPLE_RATE-1)
- g_pSpeakerBuffer[g_nBufferIdx++] = DCFilter( (short)nSampleMean );
+ if (g_nBufferIdx < SPKR_SAMPLE_RATE - 1)
+ {
+ if (g_nSPKR_NumChannels == 1)
+ {
+ g_pSpeakerBuffer[g_nBufferIdx] = DCFilter((short)nSampleMean);
+ }
+ else
+ {
+ g_pSpeakerBuffer[g_nBufferIdx * 2 + 0] = 0x0000;
+ g_pSpeakerBuffer[g_nBufferIdx * 2 + 1] = DCFilter((short)nSampleMean);
+ }
+ g_nBufferIdx++;
+ }
}
}
}
@@ -359,8 +375,19 @@ static void UpdateSpkr()
ULONG nCyclesRemaining = (ULONG) ((double)nCycleDiff - (double)nNumSamples * g_fClksPerSpkrSample);
- while((nNumSamples--) && (g_nBufferIdx < SPKR_SAMPLE_RATE-1))
- g_pSpeakerBuffer[g_nBufferIdx++] = DCFilter(g_nSpeakerData);
+ while ((nNumSamples--) && (g_nBufferIdx < SPKR_SAMPLE_RATE - 1))
+ {
+ if (g_nSPKR_NumChannels == 1)
+ {
+ g_pSpeakerBuffer[g_nBufferIdx] = DCFilter(g_nSpeakerData);
+ }
+ else
+ {
+ g_pSpeakerBuffer[g_nBufferIdx * 2 + 0] = 0x0000;
+ g_pSpeakerBuffer[g_nBufferIdx * 2 + 1] = DCFilter(g_nSpeakerData);
+ }
+ g_nBufferIdx++;
+ }
ReinitRemainderBuffer(nCyclesRemaining); // Partially fill 1Mhz sample buffer
}
@@ -454,7 +481,7 @@ void SpkrUpdate (DWORD totalcycles)
nSamplesUsed = Spkr_SubmitWaveBuffer(g_pSpeakerBuffer, g_nBufferIdx);
_ASSERT(nSamplesUsed <= g_nBufferIdx);
- memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], g_nBufferIdx-nSamplesUsed); // FIXME-TC: _Size * 2
+ memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], (g_nBufferIdx - nSamplesUsed) * sizeof(short) * g_nSPKR_NumChannels);
g_nBufferIdx -= nSamplesUsed;
}
}
@@ -470,7 +497,7 @@ void SpkrUpdate_Timer()
nSamplesUsed = Spkr_SubmitWaveBuffer_FullSpeed(g_pSpeakerBuffer, g_nBufferIdx);
_ASSERT(nSamplesUsed <= g_nBufferIdx);
- memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], g_nBufferIdx-nSamplesUsed); // FIXME-TC: _Size * 2 (GH#213?)
+ memmove(g_pSpeakerBuffer, &g_pSpeakerBuffer[nSamplesUsed], (g_nBufferIdx - nSamplesUsed) * sizeof(short) * g_nSPKR_NumChannels);
g_nBufferIdx -= nSamplesUsed;
}
}
@@ -559,7 +586,7 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
if(nBytesRemaining < g_dwDSSpkrBufferSize / 4)
{
// < 1/4 of play-buffer remaining (need *more* data)
- nNumPadSamples = ((g_dwDSSpkrBufferSize / 4) - nBytesRemaining) / sizeof(short);
+ nNumPadSamples = ((g_dwDSSpkrBufferSize / 4) - nBytesRemaining) / (sizeof(short) * g_nSPKR_NumChannels);
if(nNumPadSamples > nNumSamples)
nNumPadSamples -= nNumSamples;
@@ -574,15 +601,15 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
UINT nBytesFree = g_dwDSSpkrBufferSize - nBytesRemaining; // Calc free buffer space
ULONG nNumSamplesToUse = nNumSamples + nNumPadSamples;
- if(nNumSamplesToUse * sizeof(short) > nBytesFree)
- nNumSamplesToUse = nBytesFree / sizeof(short);
+ if (nNumSamplesToUse * sizeof(short) * g_nSPKR_NumChannels > nBytesFree)
+ nNumSamplesToUse = nBytesFree / (sizeof(short) * g_nSPKR_NumChannels);
//
if(nNumSamplesToUse >= 128) // Limit the buffer unlock/locking to a minimum
{
hr = DSGetLock(SpeakerVoice.lpDSBvoice,
- dwByteOffset, (DWORD)nNumSamplesToUse * sizeof(short),
+ dwByteOffset, (DWORD)nNumSamplesToUse * sizeof(short) * g_nSPKR_NumChannels,
&pDSLockedBuffer0, &dwDSLockedBufferSize0,
&pDSLockedBuffer1, &dwDSLockedBufferSize1);
if (FAILED(hr))
@@ -597,15 +624,15 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
{
//LogOutput("[Submit_FS] C=%08X, PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X ***\n", nDbgSpkrCnt, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor-dwCurrentPlayCursor, dwByteOffset, nNumSamples);
- if(nNumSamples*sizeof(short) <= dwDSLockedBufferSize0)
+ if (nNumSamples * sizeof(short) * g_nSPKR_NumChannels <= dwDSLockedBufferSize0)
{
- dwBufferSize0 = nNumSamples*sizeof(short);
+ dwBufferSize0 = nNumSamples * sizeof(short) * g_nSPKR_NumChannels;
dwBufferSize1 = 0;
}
else
{
dwBufferSize0 = dwDSLockedBufferSize0;
- dwBufferSize1 = nNumSamples*sizeof(short) - dwDSLockedBufferSize0;
+ dwBufferSize1 = nNumSamples * sizeof(short) * g_nSPKR_NumChannels - dwDSLockedBufferSize0;
if(dwBufferSize1 > dwDSLockedBufferSize1)
dwBufferSize1 = dwDSLockedBufferSize1;
@@ -613,15 +640,15 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
memcpy(pDSLockedBuffer0, &pSpeakerBuffer[0], dwBufferSize0);
if (g_bSpkrOutputToRiff)
- RiffPutSamples(pDSLockedBuffer0, dwBufferSize0/sizeof(short));
- nNumSamples = dwBufferSize0/sizeof(short);
+ RiffPutSamples(pDSLockedBuffer0, dwBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels));
+ nNumSamples = dwBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels);
if(pDSLockedBuffer1 && dwBufferSize1)
{
memcpy(pDSLockedBuffer1, &pSpeakerBuffer[dwDSLockedBufferSize0/sizeof(short)], dwBufferSize1);
if (g_bSpkrOutputToRiff)
- RiffPutSamples(pDSLockedBuffer1, dwBufferSize1/sizeof(short));
- nNumSamples += dwBufferSize1/sizeof(short);
+ RiffPutSamples(pDSLockedBuffer1, dwBufferSize1 / (sizeof(short) * g_nSPKR_NumChannels));
+ nNumSamples += dwBufferSize1 / (sizeof(short) * g_nSPKR_NumChannels);
}
}
@@ -634,16 +661,42 @@ static ULONG Spkr_SubmitWaveBuffer_FullSpeed(short* pSpeakerBuffer, ULONG nNumSa
if(dwBufferSize0)
{
- std::fill_n(pDSLockedBuffer0, dwBufferSize0/sizeof(short), DCFilter(g_nSpeakerData));
+ const UINT numSamples = dwBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels);
+ if (g_nSPKR_NumChannels == 1)
+ {
+ std::fill_n(pDSLockedBuffer0, numSamples, DCFilter(g_nSpeakerData));
+ }
+ else
+ {
+ for (UINT i = 0; i < numSamples; i++)
+ {
+ pDSLockedBuffer0[i * 2 + 0] = 0x0000;
+ pDSLockedBuffer0[i * 2 + 1] = DCFilter(g_nSpeakerData);
+ }
+ }
+
if (g_bSpkrOutputToRiff)
- RiffPutSamples(pDSLockedBuffer0, dwBufferSize0/sizeof(short));
+ RiffPutSamples(pDSLockedBuffer0, numSamples);
}
if(pDSLockedBuffer1)
{
- std::fill_n(pDSLockedBuffer1, dwBufferSize1/sizeof(short), DCFilter(g_nSpeakerData));
+ const UINT numSamples = dwBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels);
+ if (g_nSPKR_NumChannels == 1)
+ {
+ std::fill_n(pDSLockedBuffer1, numSamples, DCFilter(g_nSpeakerData));
+ }
+ else
+ {
+ for (UINT i = 0; i < numSamples; i++)
+ {
+ pDSLockedBuffer1[i * 2 + 0] = 0x0000;
+ pDSLockedBuffer1[i * 2 + 1] = DCFilter(g_nSpeakerData);
+ }
+ }
+
if (g_bSpkrOutputToRiff)
- RiffPutSamples(pDSLockedBuffer1, dwBufferSize1/sizeof(short));
+ RiffPutSamples(pDSLockedBuffer1, numSamples);
}
}
@@ -777,7 +830,7 @@ static ULONG Spkr_SubmitWaveBuffer(short* pSpeakerBuffer, ULONG nNumSamples)
//LogOutput("[Submit] C=%08X, PC=%08X, WC=%08X, Diff=%08X, Off=%08X, NS=%08X +++\n", nDbgSpkrCnt, dwCurrentPlayCursor, dwCurrentWriteCursor, dwCurrentWriteCursor-dwCurrentPlayCursor, dwByteOffset, nNumSamplesToUse);
hr = DSGetLock(SpeakerVoice.lpDSBvoice,
- dwByteOffset, (DWORD)nNumSamplesToUse * sizeof(short),
+ dwByteOffset, (DWORD)nNumSamplesToUse * sizeof(short) * g_nSPKR_NumChannels,
&pDSLockedBuffer0, &dwDSLockedBufferSize0,
&pDSLockedBuffer1, &dwDSLockedBufferSize1);
if (FAILED(hr))
@@ -788,13 +841,13 @@ static ULONG Spkr_SubmitWaveBuffer(short* pSpeakerBuffer, ULONG nNumSamples)
memcpy(pDSLockedBuffer0, &pSpeakerBuffer[0], dwDSLockedBufferSize0);
if (g_bSpkrOutputToRiff)
- RiffPutSamples(pDSLockedBuffer0, dwDSLockedBufferSize0/sizeof(short));
+ RiffPutSamples(pDSLockedBuffer0, dwDSLockedBufferSize0 / (sizeof(short) * g_nSPKR_NumChannels));
if(pDSLockedBuffer1)
{
memcpy(pDSLockedBuffer1, &pSpeakerBuffer[dwDSLockedBufferSize0/sizeof(short)], dwDSLockedBufferSize1);
if (g_bSpkrOutputToRiff)
- RiffPutSamples(pDSLockedBuffer1, dwDSLockedBufferSize1/sizeof(short));
+ RiffPutSamples(pDSLockedBuffer1, dwDSLockedBufferSize1 / (sizeof(short) * g_nSPKR_NumChannels));
}
// Commit sound buffer
@@ -901,7 +954,7 @@ bool Spkr_DSInit()
SpeakerVoice.bIsSpeaker = true;
- HRESULT hr = DSGetSoundBuffer(&SpeakerVoice, DSBCAPS_CTRLVOLUME, g_dwDSSpkrBufferSize, SPKR_SAMPLE_RATE, 1, "Spkr");
+ HRESULT hr = DSGetSoundBuffer(&SpeakerVoice, DSBCAPS_CTRLVOLUME, g_dwDSSpkrBufferSize, SPKR_SAMPLE_RATE, g_nSPKR_NumChannels, "Spkr");
if (FAILED(hr))
{
LogFileOutput("Spkr_DSInit: DSGetSoundBuffer failed (%08X)\n", hr);
diff --git a/source/Speaker.h b/source/Speaker.h
index 013ff618..16146b55 100644
--- a/source/Speaker.h
+++ b/source/Speaker.h
@@ -31,6 +31,7 @@ void Spkr_Unmute();
bool Spkr_IsActive();
bool Spkr_DSInit();
void Spkr_OutputToRiff(void);
+UINT Spkr_GetNumChannels(void);
void SpkrSaveSnapshot(class YamlSaveHelper& yamlSaveHelper);
void SpkrLoadSnapshot(class YamlLoadHelper& yamlLoadHelper);
diff --git a/source/Windows/AppleWin.cpp b/source/Windows/AppleWin.cpp
index a1ec1818..22592b5c 100644
--- a/source/Windows/AppleWin.cpp
+++ b/source/Windows/AppleWin.cpp
@@ -627,7 +627,7 @@ static void OneTimeInitialization(HINSTANCE passinstance)
// Currently only support one RIFF file
if (!g_cmdLine.wavFileSpeaker.empty())
{
- if (RiffInitWriteFile(g_cmdLine.wavFileSpeaker.c_str(), SPKR_SAMPLE_RATE, 1))
+ if (RiffInitWriteFile(g_cmdLine.wavFileSpeaker.c_str(), SPKR_SAMPLE_RATE, Spkr_GetNumChannels()))
Spkr_OutputToRiff();
}
else if (!g_cmdLine.wavFileMockingboard.empty())