/* * 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