#include <iostream>
//#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include "Sounds.h"
#include "MemoryMapping.h"
BEGIN_NAMESPACE
#define MPEGLAYER3_WAVE_FORMAT 0x0055
#define MPEGLAYER3_WFX_EXTRA_BYTES 12
#define MPEGLAYER3_ID_MPEG 1
#define MPEGLAYER3_FLAG_PADDING_OFF 0x00000002
#define MPEGLAYER3_BLOCK_SIZE 522
struct Sound
{
MemoryMappingData* m_mappedFile;
HWAVEOUT m_waveHandle;
WAVEHDR m_waveHeader;
};
Sound* SoundFilePlay(const char* a_filename, SoundFormat a_fmt, int a_channels, int a_samples)
{
// Files over 4GiB won't work even for 64bit builds for various reasons such as WAVEHDR length field being a DWORD.
// If need to support very large audio files, then will need to consider loading and playing the audio in chunks.
MemoryMappingData* mapping = MemoryMapping_Open(a_filename);
Sound* sound = SoundPlayFromMemory(MemoryMapping_GetAddress(mapping), (size_t)MemoryMapping_GetSize(mapping), a_fmt, a_channels, a_samples);
if (sound)
sound->m_mappedFile = mapping;
return sound;
}
static void CALLBACK CallBackProc(HWAVEOUT hwo, UINT uMsg, DWORD, DWORD dwParam1, DWORD)
{
if (uMsg == WOM_DONE)
{
std::cout << "finished" << std::endl;
MMRESULT MMResult;
WAVEHDR *WaveHeader = (WAVEHDR *)dwParam1;
if((MMResult = waveOutUnprepareHeader(hwo, WaveHeader, sizeof(WAVEHDR))) != MMSYSERR_NOERROR)
{
char Text[256];
waveOutGetErrorTextA(MMResult, Text, sizeof(Text));
std::cout << "Failed to unprepare: " << Text << std::endl;
}
free(WaveHeader->lpData);
}
}
Sound* SoundPlayFromMemory(void* a_buffer, size_t a_bufferLength, SoundFormat a_fmt, int a_channels, int a_samples)
{
WAVEFORMATEX WavFormat = {
WAVE_FORMAT_PCM, /* wFormatTag; */
2, /* nChannels; */
44100, /* nSamplesPerSec; */
44100 * 2, /* nAvgBytesPerSec; */
2, /* nBlockAlign; */
16, /* wBitsPerSample; */
0, /* cbSize; */
};
struct MPEGLAYER3_WAVEFORMAT
{
WAVEFORMATEX wfx;
WORD wID;
DWORD fdwFlags;
WORD nBlockSize;
WORD nFramesPerBlock;
WORD nCodecDelay;
} Mp3Format = {
{
MPEGLAYER3_WAVE_FORMAT, /* wFormatTag; */
1, /* nChannels; */
44100, /* nSamplesPerSec; */
128 * (1024 / 8), /* nAvgBytesPerSec; */
1, /* nBlockAlign; */
0, /* wBitsPerSample; */
MPEGLAYER3_WFX_EXTRA_BYTES, /* cbSize; */
},
MPEGLAYER3_ID_MPEG, /* wID; */
MPEGLAYER3_FLAG_PADDING_OFF, /* fdwFlags; */
MPEGLAYER3_BLOCK_SIZE, /* nBlockSize; */
1, /* nFramesPerBlock; */
1393, /* nCodecDelay; */
};
Sound* sound = new Sound;
sound->m_mappedFile = 0;
WAVEFORMATEX *format = (a_fmt == SF_WAV) ? (WAVEFORMATEX *)&WavFormat : (WAVEFORMATEX *)&Mp3Format;
if (waveOutOpen(&sound->m_waveHandle, WAVE_MAPPER, format, (DWORD)CallBackProc, 0, CALLBACK_FUNCTION) != MMSYSERR_NOERROR)
{
std::cerr << "Failed to open Wave!" << std::endl;
return 0;
}
std::cerr << "open Wave!" << std::endl;
MMRESULT MMResult;
memset(&sound->m_waveHeader, 0, sizeof(sound->m_waveHeader));
a_buffer = (char*)a_buffer + 4; // Skip header for now
a_bufferLength -= 4;
sound->m_waveHeader.lpData = (char *)a_buffer;
sound->m_waveHeader.dwBufferLength = (DWORD)a_bufferLength;
sound->m_waveHeader.dwBytesRecorded = (DWORD)a_bufferLength;
sound->m_waveHeader.dwLoops = 1;
sound->m_waveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
if (waveOutPrepareHeader(sound->m_waveHandle, &sound->m_waveHeader, sizeof(sound->m_waveHeader)) != MMSYSERR_NOERROR)
{
std::cout << "Failed to prepare header!" << std::endl;
return 0;
}
std::cerr << "prepared header!" << std::endl;
if ((MMResult = waveOutWrite(sound->m_waveHandle, &sound->m_waveHeader, sizeof(sound->m_waveHeader))) != MMSYSERR_NOERROR)
{
char Text[256];
waveOutGetErrorTextA(MMResult, Text, sizeof(Text));
std::cout << "Failed to write: " << Text << std::endl;
return 0;
}
std::cerr << "wrote Wave!" << std::endl;
return sound;
}
void SoundWaitToFinish(Sound* a_sound)
{
while (!(a_sound->m_waveHeader.dwFlags & WHDR_DONE))
Sleep(100);
}
void SoundClose(Sound* a_sound)
{
MMRESULT MMResult;
if ((MMResult = waveOutClose(a_sound->m_waveHandle)) != MMSYSERR_NOERROR)
{
char Text[256];
waveOutGetErrorTextA(MMResult, Text, sizeof(Text));
std::cout << "Failed to close: " << Text << std::endl;
}
if (a_sound->m_mappedFile)
MemoryMapping_Close(a_sound->m_mappedFile);
delete a_sound;
}
END_NAMESPACE