Skip to content

Commit

Permalink
Prototype WAV export added.
Browse files Browse the repository at this point in the history
- Note that the Altirra POKEY emulation is not compatible currently
  • Loading branch information
VinsCool committed Mar 11, 2023
1 parent b45dac6 commit 5aeebd6
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 9 deletions.
10 changes: 10 additions & 0 deletions cpp_src/Atari6502.cpp
Expand Up @@ -169,6 +169,16 @@ void Atari_PlayRMT()
C6502_JSR(&adr,&a,&x,&y,&cycles); //adr,A,X,Y
}

void Atari_SetPokey()
{
if (!g_is6502) return;

WORD adr = RMT_SETPOKEY;
BYTE a = 0, x = 0, y = 0;
int cycles = (g_ntsc) ? MAXSCREENCYCLES_NTSC : MAXSCREENCYCLES_PAL;
C6502_JSR(&adr, &a, &x, &y, &cycles);
}

void Atari_Silence()
{
if (!g_is6502) return;
Expand Down
1 change: 1 addition & 0 deletions cpp_src/Atari6502.h
Expand Up @@ -39,6 +39,7 @@ extern int SaveBinaryBlock(std::ofstream& out, unsigned char* memory, WORD fromA
extern int Atari_LoadRMTRoutines();
extern int Atari_InitRMTRoutine();
extern void Atari_PlayRMT();
extern void Atari_SetPokey();
extern void Atari_Silence();
extern void Atari_SetTrack_NoteInstrVolume(int t,int n,int i,int v);
extern void Atari_SetTrack_Volume(int t,int v);
Expand Down
15 changes: 12 additions & 3 deletions cpp_src/General.h
Expand Up @@ -168,6 +168,8 @@
#define IOTYPE_LZSS_SAP 12
#define IOTYPE_LZSS_XEX 13

#define IOTYPE_WAV 20

#define IOTYPE_TMC 101 //import TMC

#define IOINSTR_RTI 1 //corresponding IOTYPE_RMT
Expand Down Expand Up @@ -311,18 +313,25 @@
"SAP file + LZSS driver (*.sap)|*.sap|" \
"XEX Atari executable + LZSS driver (*.xex)|*.xex|" \
"Relocatable ASM for RMTPlayer (*.asm)|*.asm|" \
"WAV audio file (*.wav)|*.wav|" \
"|"
#define FILE_EXPORT_FILTER_IDX_STRIPPED_RMT 1
#define FILE_EXPORT_FILTER_IDX_SIMPLE_ASM 2
#define FILE_EXPORT_FILTER_IDX_SAPR 3
#define FILE_EXPORT_FILTER_IDX_LZSS 4
#define FILE_EXPORT_FILTER_IDX_SAP 5
#define FILE_EXPORT_FILTER_IDX_XEX 6
//#define FILE_EXPORT_FILTER_IDX_RELOC_ASM 7
//#define FILE_EXPORT_FILTER_IDX_MIN FILE_EXPORT_FILTER_IDX_STRIPPED_RMT
//#define FILE_EXPORT_FILTER_IDX_MAX FILE_EXPORT_FILTER_IDX_RELOC_ASM
//#define FILE_EXPORT_EXTENSIONS_ARRAY { ".rmt",".asm",".sapr",".lzss",".sap",".xex",".asm" };
//#define FILE_EXPORT_EXTENSIONS_LENGTH_ARRAY { 4, 4, 5, 5, 4, 4, 4}
#define FILE_EXPORT_FILTER_IDX_RELOC_ASM 7
#define FILE_EXPORT_FILTER_IDX_WAV 8
#define FILE_EXPORT_FILTER_IDX_MIN FILE_EXPORT_FILTER_IDX_STRIPPED_RMT
#define FILE_EXPORT_FILTER_IDX_MAX FILE_EXPORT_FILTER_IDX_RELOC_ASM
#define FILE_EXPORT_EXTENSIONS_ARRAY { ".rmt",".asm",".sapr",".lzss",".sap",".xex",".asm" };
#define FILE_EXPORT_EXTENSIONS_LENGTH_ARRAY { 4, 4, 5, 5, 4, 4, 4}
#define FILE_EXPORT_FILTER_IDX_MAX FILE_EXPORT_FILTER_IDX_WAV
#define FILE_EXPORT_EXTENSIONS_ARRAY { ".rmt",".asm",".sapr",".lzss",".sap",".xex",".asm",".wav" };
#define FILE_EXPORT_EXTENSIONS_LENGTH_ARRAY { 4, 4, 5, 5, 4, 4, 4, 4}

// ----------------------------------------------------------------------------
// Pokey play to buffer
Expand Down
90 changes: 84 additions & 6 deletions cpp_src/IO_Song.cpp
Expand Up @@ -26,6 +26,8 @@
#include "ChannelControl.h"
#include "RmtMidi.h"

#include "Wavefile.h"

extern CInstruments g_Instruments;
extern CTrackClipboard g_TrackClipboard;
extern CXPokey g_Pokey;
Expand Down Expand Up @@ -548,6 +550,7 @@ void CSong::FileExportAs()
if (m_lastExportType == IOTYPE_LZSS_SAP) dlg.m_ofn.nFilterIndex = FILE_EXPORT_FILTER_IDX_SAP;
if (m_lastExportType == IOTYPE_LZSS_XEX) dlg.m_ofn.nFilterIndex = FILE_EXPORT_FILTER_IDX_XEX;
if (m_lastExportType == IOTYPE_ASM_RMTPLAYER) dlg.m_ofn.nFilterIndex = FILE_EXPORT_FILTER_IDX_RELOC_ASM;
if (m_lastExportType == IOTYPE_WAV) dlg.m_ofn.nFilterIndex = FILE_EXPORT_FILTER_IDX_WAV;

// If not ok, nothing will be saved
if (dlg.DoModal() == IDOK)
Expand Down Expand Up @@ -608,6 +611,11 @@ void CSong::FileExportAs()
case FILE_EXPORT_FILTER_IDX_RELOC_ASM: // Relocatable ASM for RMTPlayer
m_lastExportType = IOTYPE_ASM_RMTPLAYER;
break;

case FILE_EXPORT_FILTER_IDX_WAV:
m_lastExportType = IOTYPE_WAV;
break;

}

// Save the file using the set parameters
Expand Down Expand Up @@ -1314,14 +1322,9 @@ bool CSong::ExportV2(std::ofstream& ou, int iotype, LPCTSTR filename)
case IOTYPE_ASM_RMTPLAYER: return ExportAsRelocatableAsmForRmtPlayer(ou, &exportDesc);
case IOTYPE_SAPR: return ExportSAP_R(ou);
case IOTYPE_LZSS: return ExportLZSS(ou, filename);
/*
if (MessageBox(g_hwnd, "Process using ExportCompactLZSS?\nMany files will be created at once.", "ExportLZSS", MB_YESNO | MB_ICONINFORMATION) == IDYES)
return ExportCompactLZSS(ou, filename); // This is used for experimental stuff, however it does output valid LZSS data if needed
else
return ExportLZSS(ou, filename); // Original LZSS export method
*/
case IOTYPE_LZSS_SAP: return ExportLZSS_SAP(ou);
case IOTYPE_LZSS_XEX: return ExportLZSS_XEX(ou);
case IOTYPE_WAV: return ExportWav(ou, filename);
}

return false; // Failed
Expand Down Expand Up @@ -1594,5 +1597,80 @@ bool CSong::WriteToXEX(struct TExportMetadata* metadata)
metadata->displayRasterbar = dlg.m_meter;
metadata->autoRegion = dlg.m_region_auto;

return true;
}

bool CSong::ExportWav(std::ofstream& ou, LPCTSTR filename)
{
CWaveFile wavefile{};

BYTE* buffer = NULL;
BYTE* streambuffer = NULL;
int length = 0, frames = 0, offset = 0;
int frameSize = (g_tracks4_8 == 8) ? 18 : 9; // SAP-R bytes to copy, Stereo doubles the number

ou.close(); // hack, just to be able to actually use the filename for now...

if (!wavefile.OpenFile((LPTSTR)filename, OUTPUTFREQ, BITRESOLUTION, CHANNELS))
{
MessageBox(g_hwnd, "Wav file could not be created!", "ExportWav", MB_ICONWARNING);
return false;
}

// Dump the POKEY registers from full song playback
DumpSongToPokeyBuffer();

Atari_InitRMTRoutine(); // Reset the Atari memory
SetChannelOnOff(-1, 1); // Unmute all channels

// Create the sound buffer to copy from and to
buffer = new BYTE[BUFFER_SIZE];
memset(buffer, 0x80, BUFFER_SIZE);

// Busy writing! TODO: Fix the timing overlap causing conflicts
g_PokeyStream.SetState(g_PokeyStream.STREAM_STATE::WRITE);

while (frames < g_PokeyStream.GetFirstCountPoint())
{
// Copy the SAP-R bytes to g_atarimem for this frame
streambuffer = g_PokeyStream.GetStreamBuffer() + frames * frameSize;

//for (int i = 0; i < frameSize; i++)
//{
// g_atarimem[0xd200 + i] = streambuffer[i];
//}

g_atarimem[RMTPLAYR_TRACKN_AUDF + 0] = streambuffer[0x00];
g_atarimem[RMTPLAYR_TRACKN_AUDF + 1] = streambuffer[0x02];
g_atarimem[RMTPLAYR_TRACKN_AUDF + 2] = streambuffer[0x04];
g_atarimem[RMTPLAYR_TRACKN_AUDF + 3] = streambuffer[0x06];
g_atarimem[RMTPLAYR_TRACKN_AUDC + 0] = streambuffer[0x01];
g_atarimem[RMTPLAYR_TRACKN_AUDC + 1] = streambuffer[0x03];
g_atarimem[RMTPLAYR_TRACKN_AUDC + 2] = streambuffer[0x05];
g_atarimem[RMTPLAYR_TRACKN_AUDC + 3] = streambuffer[0x07];
g_atarimem[RMTPLAYR_V_AUDCTL] = streambuffer[0x08];

// Fill the POKEY buffer with 1 rendered chunk
g_Pokey.RenderSoundV2(m_instrumentSpeed, buffer, length);

//Sleep(16);

// Write the buffer to WAV file
wavefile.WriteWave(buffer, length);

// Update the PokeyStream offset for the next frame
//frames += frameSize;
frames++;
}

// Clear the SAP-R dumper memory and reset RMT routines
g_PokeyStream.FinishedRecording();

// Finished doing WAV things...
wavefile.CloseFile();

// Also make sure to delete the buffer once it's no longer needed
delete buffer;

return true;
}
2 changes: 2 additions & 0 deletions cpp_src/Rmt.vcxproj
Expand Up @@ -511,6 +511,7 @@
</ClCompile>
<ClCompile Include="TuningDlg.cpp" />
<ClCompile Include="Undo.cpp" />
<ClCompile Include="WaveFile.cpp" />
<ClCompile Include="XPokey.cpp">
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">_DEBUG;WIN32;_WINDOWS;NO_CONSOL_SOUND;NO_VOL_ONLY;STEREO;_MBCS;_AFXDLL</PreprocessorDefinitions>
<PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">_DEBUG;WIN32;_WINDOWS;NO_CONSOL_SOUND;NO_VOL_ONLY;STEREO;_MBCS;_AFXDLL</PreprocessorDefinitions>
Expand Down Expand Up @@ -564,6 +565,7 @@
<ClInclude Include="StdAfx.h" />
<ClInclude Include="TuningDlg.h" />
<ClInclude Include="Undo.h" />
<ClInclude Include="WaveFile.h" />
<ClInclude Include="XPokey.h" />
</ItemGroup>
<ItemGroup>
Expand Down
6 changes: 6 additions & 0 deletions cpp_src/Rmt.vcxproj.filters
Expand Up @@ -165,6 +165,9 @@
<ClCompile Include="PokeyStream.cpp">
<Filter>Source Files\Sound Generator</Filter>
</ClCompile>
<ClCompile Include="WaveFile.cpp">
<Filter>Source Files\Sound Generator</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ConfigDlg.h">
Expand Down Expand Up @@ -263,6 +266,9 @@
<ClInclude Include="RmtAtariBinaries.h">
<Filter>Source Files\IO - Load/Save/Export/Import/Midi</Filter>
</ClInclude>
<ClInclude Include="WaveFile.h">
<Filter>Source Files\Sound Generator</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="res\Rmt.ico">
Expand Down
2 changes: 2 additions & 0 deletions cpp_src/Song.h
Expand Up @@ -237,6 +237,8 @@ class CSong
bool ExportLZSS_SAP(std::ofstream& ou);
bool ExportLZSS_XEX(std::ofstream& ou);

bool ExportWav(std::ofstream& ou, LPCTSTR filename);

void DumpSongToPokeyBuffer(int playmode = MPLAY_SONG, int songline = 0, int trackline = 0);
int BruteforceOptimalLZSS(unsigned char* src, int srclen, unsigned char* dst);

Expand Down
73 changes: 73 additions & 0 deletions cpp_src/WaveFile.cpp
@@ -0,0 +1,73 @@

#include "StdAfx.h"
#include "WaveFile.h"

bool CWaveFile::OpenFile(LPTSTR Filename, int SampleRate, int SampleSize, int Channels)
{
int nError;

WaveFormat.wf.wFormatTag = WAVE_FORMAT_PCM;
WaveFormat.wf.nChannels = Channels;
WaveFormat.wf.nSamplesPerSec = SampleRate;
WaveFormat.wBitsPerSample = SampleSize;
WaveFormat.wf.nBlockAlign = (WaveFormat.wBitsPerSample / 8) * WaveFormat.wf.nChannels;
WaveFormat.wf.nAvgBytesPerSec = WaveFormat.wf.nSamplesPerSec * WaveFormat.wf.nBlockAlign;

hmmioOut = mmioOpen(Filename, NULL, MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);

ckOutRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
ckOutRIFF.cksize = 0;

nError = mmioCreateChunk(hmmioOut, &ckOutRIFF, MMIO_CREATERIFF);

if (nError != MMSYSERR_NOERROR)
return false;

ckOut.ckid = mmioFOURCC('f', 'm', 't', ' ');
ckOut.cksize = sizeof(PCMWAVEFORMAT);

nError = mmioCreateChunk(hmmioOut, &ckOut, 0);

if (nError != MMSYSERR_NOERROR)
return false;

mmioWrite(hmmioOut, (HPSTR)&WaveFormat, sizeof(PCMWAVEFORMAT));
mmioAscend(hmmioOut, &ckOut, 0);

ckOut.ckid = mmioFOURCC('d', 'a', 't', 'a');
ckOut.cksize = 0;

nError = mmioCreateChunk(hmmioOut, &ckOut, 0);

if (nError != MMSYSERR_NOERROR)
return false;

mmioGetInfo(hmmioOut, &mmioinfoOut, 0);

return true;
}

void CWaveFile::CloseFile()
{
mmioinfoOut.dwFlags |= MMIO_DIRTY;
mmioSetInfo(hmmioOut, &mmioinfoOut, 0);
mmioAscend(hmmioOut, &ckOut, 0);
mmioAscend(hmmioOut, &ckOutRIFF, 0);
mmioSeek(hmmioOut, 0, SEEK_SET);
mmioDescend(hmmioOut, &ckOutRIFF, NULL, 0);
mmioClose(hmmioOut, 0);
}

void CWaveFile::WriteWave(BYTE* Data, int Size)
{
for (int i = 0; i < Size; i++)
{
if (mmioinfoOut.pchNext == mmioinfoOut.pchEndWrite)
{
mmioinfoOut.dwFlags |= MMIO_DIRTY;
mmioAdvance(hmmioOut, &mmioinfoOut, MMIO_WRITE);
}
*((BYTE*)mmioinfoOut.pchNext) = *((BYTE*)Data + i);
mmioinfoOut.pchNext++;
}
}
18 changes: 18 additions & 0 deletions cpp_src/WaveFile.h
@@ -0,0 +1,18 @@
#pragma once

#include "StdAfx.h"
#include <mmsystem.h>

class CWaveFile
{
public:
bool OpenFile(LPTSTR Filename, int SampleRate, int SampleSize, int Channels);
void CloseFile();
void WriteWave(BYTE* Data, int Size);

private:
PCMWAVEFORMAT WaveFormat;
MMCKINFO ckOutRIFF, ckOut;
MMIOINFO mmioinfoOut;
HMMIO hmmioOut;
};
28 changes: 28 additions & 0 deletions cpp_src/XPokey.cpp
Expand Up @@ -213,6 +213,34 @@ BOOL CXPokey::RenderSound1_50(int instrspeed)
return 0;
}

// Initial WAV recorder process
// NOTE: This does NOT work with the Altirra plugin due to it hijacking the soundbuffer with its own thing...
void CXPokey::RenderSoundV2(int instrspeed, BYTE* buffer, int& length)
{
int rendersize = CHUNK_SIZE;
int renderpartsize = 0;
int renderoffset = 0;

for (; instrspeed > 0; instrspeed--)
{
Atari_SetPokey();
MemToPokey();
renderpartsize = (rendersize / instrspeed) & 0xfffe;

switch (m_soundDriverId)
{
case SOUND_DRIVER_SA_POKEY:
Pokey_Process(buffer + renderoffset, (unsigned short)renderpartsize);
rendersize -= renderpartsize;
renderoffset += renderpartsize;
break;
}
}

// Copy the actually generated sample data to buffer
length = renderoffset;
}

BOOL CXPokey::InitSound()
{
if (m_soundDriverId || m_pokey_dll) DeInitSound(); // Just in case, everything must be cleared before initialising
Expand Down
1 change: 1 addition & 0 deletions cpp_src/XPokey.h
Expand Up @@ -35,6 +35,7 @@ class CXPokey
BOOL DeInitSound();
BOOL ReInitSound();
BOOL RenderSound1_50(int instrspeed);
void RenderSoundV2(int instrspeed, BYTE* buffer, int& length);
void MemToPokey();
bool IsSoundDriverLoaded() { return m_soundDriverId; }

Expand Down

0 comments on commit 5aeebd6

Please sign in to comment.