You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
398 lines
10 KiB
398 lines
10 KiB
/*
|
|
* Copyright (C) 2002-2004,2006-2009 by Jonathan Naylor G4KLX
|
|
*
|
|
* 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 2 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, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "WAVFileWriter.h"
|
|
|
|
#if defined(__WINDOWS__)
|
|
|
|
const int WAVE_FORMAT_IEEE_FLOAT = 3;
|
|
|
|
CWAVFileWriter::CWAVFileWriter(const wxString& fileName, unsigned int sampleRate, unsigned int channels, unsigned int sampleWidth, unsigned int blockSize) :
|
|
m_fileName(fileName),
|
|
m_sampleRate(sampleRate),
|
|
m_channels(channels),
|
|
m_sampleWidth(sampleWidth),
|
|
m_blockSize(blockSize),
|
|
m_buffer8(NULL),
|
|
m_buffer16(NULL),
|
|
m_buffer32(NULL),
|
|
m_handle(NULL),
|
|
m_parent(),
|
|
m_child()
|
|
{
|
|
wxASSERT(sampleRate > 0U);
|
|
wxASSERT(channels == 1U || channels == 2U);
|
|
wxASSERT(sampleWidth == 8U || sampleWidth == 16U || sampleWidth == 32U);
|
|
wxASSERT(blockSize > 0U);
|
|
|
|
m_buffer8 = new wxUint8[blockSize * channels];
|
|
m_buffer16 = new wxInt16[blockSize * channels];
|
|
m_buffer32 = new wxFloat32[blockSize * channels];
|
|
}
|
|
|
|
CWAVFileWriter::~CWAVFileWriter()
|
|
{
|
|
delete[] m_buffer8;
|
|
delete[] m_buffer16;
|
|
delete[] m_buffer32;
|
|
}
|
|
|
|
bool CWAVFileWriter::open()
|
|
{
|
|
m_handle = ::mmioOpen(LPWSTR(m_fileName.c_str()), 0, MMIO_WRITE | MMIO_CREATE | MMIO_ALLOCBUF);
|
|
if (m_handle == NULL) {
|
|
wxLogError(wxT("WAVFileWriter: could not open the file %s\n"), m_fileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
m_parent.fccType = mmioFOURCC('W', 'A', 'V', 'E');
|
|
m_parent.cksize = 0;
|
|
|
|
MMRESULT res = ::mmioCreateChunk(m_handle, &m_parent, MMIO_CREATERIFF);
|
|
if (res != MMSYSERR_NOERROR) {
|
|
wxLogError(wxT("WAVFileWriter: could not write to file %s\n"), m_fileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
m_child.ckid = mmioFOURCC('f', 'm', 't', ' ');
|
|
m_child.cksize = sizeof(WAVEFORMATEX);
|
|
|
|
res = ::mmioCreateChunk(m_handle, &m_child, 0);
|
|
if (res != MMSYSERR_NOERROR) {
|
|
wxLogError(wxT("WAVFileWriter: could not write to the file %s\n"), m_fileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
WAVEFORMATEX format;
|
|
format.wBitsPerSample = m_sampleWidth;
|
|
if (m_sampleWidth == 8U || m_sampleWidth == 16U)
|
|
format.wFormatTag = WAVE_FORMAT_PCM;
|
|
else
|
|
format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
|
format.nChannels = m_channels;
|
|
format.nSamplesPerSec = m_sampleRate;
|
|
format.nAvgBytesPerSec = m_sampleRate * m_channels * m_sampleWidth / 8;
|
|
format.nBlockAlign = m_channels * m_sampleWidth / 8;
|
|
format.cbSize = 0;
|
|
|
|
LONG n = ::mmioWrite(m_handle, (CHAR *)&format, sizeof(WAVEFORMATEX));
|
|
if (n != sizeof(WAVEFORMATEX)) {
|
|
wxLogError(wxT("WAVFileWriter: could not write to the file %s\n"), m_fileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
::mmioAscend(m_handle, &m_child, 0);
|
|
|
|
m_child.ckid = mmioFOURCC('d', 'a', 't', 'a');
|
|
m_child.cksize = 0;
|
|
|
|
res = ::mmioCreateChunk(m_handle, &m_child, 0);
|
|
if (res != MMSYSERR_NOERROR) {
|
|
wxLogError(wxT("WAVFileWriter: could not write to the file %s\n"), m_fileName.c_str());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CWAVFileWriter::write(const float* buffer, unsigned int length)
|
|
{
|
|
wxASSERT(m_handle != NULL);
|
|
wxASSERT(buffer != NULL);
|
|
wxASSERT(length > 0U);
|
|
|
|
unsigned int i;
|
|
LONG bytes = 0L;
|
|
LONG n = 0L;
|
|
|
|
switch (m_sampleWidth) {
|
|
case 8U:
|
|
switch (m_channels) {
|
|
case 1U:
|
|
for (i = 0U; i < length; i++)
|
|
m_buffer8[i] = wxUint8(buffer[i] * 128.0F + 127.0F);
|
|
break;
|
|
case 2U:
|
|
for (i = 0U; i < (length * 2U); i++)
|
|
m_buffer8[i] = wxUint8(buffer[i] * 128.0F + 127.0F);
|
|
break;
|
|
}
|
|
|
|
bytes = length * m_channels * sizeof(wxUint8);
|
|
|
|
n = ::mmioWrite(m_handle, (char *)m_buffer8, bytes);
|
|
|
|
break;
|
|
|
|
case 16U:
|
|
switch (m_channels) {
|
|
case 1U:
|
|
for (i = 0U; i < length; i++)
|
|
m_buffer16[i] = wxInt16(buffer[i] * 32768.0F);
|
|
break;
|
|
case 2U:
|
|
for (i = 0U; i < (length * 2U); i++)
|
|
m_buffer16[i] = wxInt16(buffer[i] * 32768.0F);
|
|
break;
|
|
}
|
|
|
|
bytes = length * m_channels * sizeof(wxInt16);
|
|
|
|
n = ::mmioWrite(m_handle, (char *)m_buffer16, bytes);
|
|
|
|
break;
|
|
|
|
case 32U:
|
|
switch (m_channels) {
|
|
case 1U:
|
|
for (i = 0U; i < length; i++)
|
|
m_buffer32[i] = wxFloat32(buffer[i]);
|
|
break;
|
|
case 2U:
|
|
// Swap I and Q
|
|
for (i = 0U; i < length; i++) {
|
|
m_buffer32[i * 2U + 0U] = wxFloat32(buffer[i * 2U + 1U]);
|
|
m_buffer32[i * 2U + 1U] = wxFloat32(buffer[i * 2U + 0U]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
bytes = length * m_channels * sizeof(wxFloat32);
|
|
|
|
n = ::mmioWrite(m_handle, (char *)m_buffer32, bytes);
|
|
|
|
break;
|
|
}
|
|
|
|
return n == bytes;
|
|
}
|
|
|
|
void CWAVFileWriter::close()
|
|
{
|
|
wxASSERT(m_handle != NULL);
|
|
|
|
::mmioAscend(m_handle, &m_child, 0);
|
|
::mmioAscend(m_handle, &m_parent, 0);
|
|
|
|
::mmioClose(m_handle, 0);
|
|
m_handle = NULL;
|
|
}
|
|
|
|
unsigned int CWAVFileWriter::getSampleRate() const
|
|
{
|
|
return m_sampleRate;
|
|
}
|
|
|
|
unsigned int CWAVFileWriter::getChannels() const
|
|
{
|
|
return m_channels;
|
|
}
|
|
|
|
#else
|
|
|
|
CWAVFileWriter::CWAVFileWriter(const wxString& fileName, unsigned int sampleRate, unsigned int channels, unsigned int sampleWidth, unsigned int blockSize) :
|
|
m_fileName(fileName),
|
|
m_sampleRate(sampleRate),
|
|
m_channels(channels),
|
|
m_sampleWidth(sampleWidth),
|
|
m_blockSize(blockSize),
|
|
m_buffer8(NULL),
|
|
m_buffer16(NULL),
|
|
m_buffer32(NULL),
|
|
m_file(NULL),
|
|
m_offset1(0),
|
|
m_offset2(0),
|
|
m_length(0U)
|
|
{
|
|
wxASSERT(sampleRate > 0U);
|
|
wxASSERT(channels == 1U || channels == 2U);
|
|
wxASSERT(sampleWidth == 8U || sampleWidth == 16U || sampleWidth == 32U);
|
|
wxASSERT(blockSize > 0U);
|
|
|
|
m_buffer8 = new wxUint8[channels * blockSize];
|
|
m_buffer16 = new wxInt16[channels * blockSize];
|
|
m_buffer32 = new wxFloat32[channels * blockSize];
|
|
}
|
|
|
|
CWAVFileWriter::~CWAVFileWriter()
|
|
{
|
|
delete[] m_buffer8;
|
|
delete[] m_buffer16;
|
|
delete[] m_buffer32;
|
|
}
|
|
|
|
bool CWAVFileWriter::open()
|
|
{
|
|
m_length = 0U ;
|
|
|
|
m_file = new wxFFile(m_fileName.c_str(), wxT("wb"));
|
|
|
|
bool ret = m_file->IsOpened();
|
|
if (!ret) {
|
|
wxLogError(wxT("WAVFileWriter: could not open the file %s in WAVFileWriter"), m_fileName.c_str());
|
|
delete m_file;
|
|
m_file = NULL;
|
|
return false;
|
|
}
|
|
|
|
m_file->Write("RIFF", 4); // 4 bytes, file signature
|
|
|
|
m_offset1 = m_file->Tell();
|
|
|
|
wxUint32 uint32 = 0;
|
|
m_file->Write(&uint32, sizeof(wxUint32)); // 4 bytes, length of file, filled in later
|
|
|
|
m_file->Write("WAVE", 4); // 4 bytes, RIFF file type
|
|
|
|
m_file->Write("fmt ", 4); // 4 bytes, chunk signature
|
|
|
|
uint32 = wxUINT32_SWAP_ON_BE(wxUint32(16));
|
|
m_file->Write(&uint32, sizeof(wxUint32)); // 4 bytes, length of "fmt " chunk
|
|
|
|
wxUint16 uint16;
|
|
if (m_sampleWidth == 8U || m_sampleWidth == 16U)
|
|
uint16 = wxUINT16_SWAP_ON_BE(wxUint16(1)); // 2 bytes, integer PCM/uncompressed
|
|
else
|
|
uint16 = wxUINT16_SWAP_ON_BE(wxUint16(3)); // 2 bytes, float PCM/uncompressed
|
|
m_file->Write(&uint16, sizeof(uint16));
|
|
|
|
uint16 = wxUINT16_SWAP_ON_BE(wxUint16(m_channels));
|
|
m_file->Write(&uint16, sizeof(uint16)); // 2 bytes, no of channels
|
|
|
|
uint32 = wxUINT32_SWAP_ON_BE(wxUint32(m_sampleRate));
|
|
m_file->Write(&uint32, sizeof(wxUint32)); // 4 bytes, sample rate
|
|
|
|
uint32 = wxUINT32_SWAP_ON_BE(wxUint32(m_sampleRate * m_channels * m_sampleWidth / 8U));
|
|
m_file->Write(&uint32, sizeof(wxUint32)); // 4 bytes, average bytes per second
|
|
|
|
uint16 = wxUINT16_SWAP_ON_BE(wxUint16(m_channels * m_sampleWidth / 8U));
|
|
m_file->Write(&uint16, sizeof(uint16)); // 2 bytes, block alignment
|
|
|
|
uint16 = wxUINT16_SWAP_ON_BE(wxUint16(m_sampleWidth));
|
|
m_file->Write(&uint16, sizeof(uint16)); // 2 bytes, significant bits per sample
|
|
|
|
m_file->Write("data", 4); // 4 bytes, chunk signature
|
|
|
|
m_offset2 = m_file->Tell();
|
|
|
|
uint32 = 0U;
|
|
m_file->Write(&uint32, sizeof(wxUint32)); // 4 bytes, length of "data" chunk, filled in later
|
|
|
|
return true;
|
|
}
|
|
|
|
bool CWAVFileWriter::write(const wxFloat32* buffer, unsigned int length)
|
|
{
|
|
wxASSERT(m_file != NULL);
|
|
wxASSERT(buffer != NULL);
|
|
wxASSERT(length > 0U && length <= m_blockSize);
|
|
|
|
unsigned int bytes = 0U;
|
|
unsigned int i;
|
|
size_t n = 0UL;
|
|
|
|
switch (m_sampleWidth) {
|
|
case 8U:
|
|
switch (m_channels) {
|
|
case 1U:
|
|
for (i = 0U; i < length; i++)
|
|
m_buffer8[i] = wxUint8(buffer[i] * 128.0F + 127.0F);
|
|
break;
|
|
case 2U:
|
|
for (i = 0U; i < (length * 2U); i++)
|
|
m_buffer8[i] = wxUint8(buffer[i] * 128.0F + 127.0F);
|
|
break;
|
|
}
|
|
|
|
bytes = length * m_channels * sizeof(wxUint8);
|
|
|
|
n = m_file->Write(m_buffer8, bytes);
|
|
|
|
break;
|
|
|
|
case 16U:
|
|
switch (m_channels) {
|
|
case 1U:
|
|
for (i = 0U; i < length; i++)
|
|
m_buffer16[i] = wxInt16(buffer[i] * 32768.0F);
|
|
break;
|
|
case 2U:
|
|
for (i = 0U; i < (length * 2U); i++)
|
|
m_buffer16[i] = wxInt16(buffer[i] * 32768.0F);
|
|
break;
|
|
}
|
|
|
|
bytes = length * m_channels * sizeof(wxInt16);
|
|
|
|
n = m_file->Write(m_buffer16, bytes);
|
|
|
|
break;
|
|
|
|
case 32U:
|
|
switch (m_channels) {
|
|
case 1U:
|
|
for (i = 0U; i < length; i++)
|
|
m_buffer32[i] = wxFloat32(buffer[i]);
|
|
break;
|
|
case 2U:
|
|
// Swap I and Q
|
|
for (i = 0U; i < length; i++) {
|
|
m_buffer32[i * 2U + 0U] = wxFloat32(buffer[i * 2U + 1U]);
|
|
m_buffer32[i * 2U + 1U] = wxFloat32(buffer[i * 2U + 0U]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
bytes = length * m_channels * sizeof(wxFloat32);
|
|
|
|
n = m_file->Write(m_buffer32, bytes);
|
|
|
|
break;
|
|
}
|
|
|
|
m_length += n;
|
|
|
|
return n == bytes;
|
|
}
|
|
|
|
void CWAVFileWriter::close()
|
|
{
|
|
wxASSERT(m_file != NULL);
|
|
|
|
if ((m_length % 2U) != 0U) {
|
|
unsigned char c = 0U;
|
|
m_file->Write(&c, 1);
|
|
}
|
|
|
|
wxUint32 length = wxUINT32_SWAP_ON_BE(m_length);
|
|
|
|
m_file->Seek(m_offset2);
|
|
m_file->Write(&length, sizeof(wxUint32));
|
|
|
|
length = wxUINT32_SWAP_ON_BE(m_length + 36U);
|
|
|
|
m_file->Seek(m_offset1);
|
|
m_file->Write(&length, sizeof(wxUint32));
|
|
|
|
m_file->Close();
|
|
delete m_file;
|
|
m_file = NULL;
|
|
}
|
|
|
|
#endif
|