// -------------------------------------------------------------------------- // Name: sndcpcm.cpp // Purpose: // Date: 08/11/1999 // Author: Guilhem Lavaux (C) 1999, 2000 // CVSID: $Id: sndcpcm.cpp 35650 2005-09-23 12:56:45Z MR $ // wxWindows licence // -------------------------------------------------------------------------- #include "wx/wxprec.h" #ifndef WX_PRECOMP #include "wx/defs.h" #include "wx/debug.h" #include "wx/log.h" #endif #ifdef __BORLANDC__ #pragma hdrstop #endif #include "wx/mmedia/sndbase.h" #include "wx/mmedia/sndpcm.h" #include "wx/mmedia/sndcpcm.h" wxSoundStreamPcm::wxSoundStreamPcm(wxSoundStream& sndio) : wxSoundStreamCodec(sndio) { m_function_in = NULL; m_function_out = NULL; m_prebuffer = NULL; m_prebuffer_size = 0; m_best_size = 0; } wxSoundStreamPcm::~wxSoundStreamPcm() { if (m_prebuffer) delete[] m_prebuffer; } wxUint32 wxSoundStreamPcm::GetBestSize() const { return m_best_size; } // ----------------------------------------------------------------------- // "Converters" definitions/implementations // ----------------------------------------------------------------------- #define DEFINE_CONV(name, input_type, output_type, convert) \ static void Convert_##name(const void *buf_in, void *buf_out, wxUint32 len) \ {\ register input_type src; \ register const input_type *t_buf_in = (input_type *)buf_in; \ register output_type *t_buf_out = (output_type *)buf_out; \ \ while (len > 0) { \ src = *t_buf_in++; \ *t_buf_out++ = convert; \ len -= sizeof(input_type); \ } \ } // TODO: define converters for all other formats (32, 24) DEFINE_CONV(8_8_sign, wxUint8, wxUint8, (src ^ 0x80)) DEFINE_CONV(8_16, wxUint8, wxUint16, (((wxUint16)src) << 8)) DEFINE_CONV(8_16_swap, wxUint8, wxUint16, (src)) DEFINE_CONV(8_16_sign, wxUint8, wxUint16, (((wxUint16)(src ^ 0x80)) << 8)) DEFINE_CONV(8_16_sign_swap, wxUint8, wxUint16, (src ^ 0x80)) DEFINE_CONV(16_8, wxUint16, wxUint8, (wxUint8)(src >> 8)) DEFINE_CONV(16_8_sign, wxUint16, wxUint8, (wxUint8)((src >> 8) ^ 0x80)) DEFINE_CONV(16_swap_8, wxUint16, wxUint8, (wxUint8)(src & 0xff)) DEFINE_CONV(16_swap_8_sign, wxUint16, wxUint8, (wxUint8)((src & 0xff) ^ 0x80)) //DEFINE_CONV(24_8, wxUint32, wxUint8, (wxUint8)(src >> 16)) //DEFINE_CONV(24_8_sig, wxUint32, wxUint8, (wxUint8)((src >> 16) ^ 0x80)) //DEFINE_CONV(32_8, wxUint32, wxUint8, (wxUint8)(src >> 24)) DEFINE_CONV(16_sign, wxUint16, wxUint16, (src ^ 0x8000)) DEFINE_CONV(16_swap, wxUint16, wxUint16, (((src & 0xff) << 8) | ((src >> 8) & 0xff))) // Problem. DEFINE_CONV(16_swap_16_sign, wxUint16, wxUint16, ((((src & 0xff) << 8) | ((src >> 8) & 0xff)) ^ 0x80)) // DEFINE_CONV(16_sign_16_swap, wxUint16, wxUint16, ((((src & 0xff) << 8) | ((src >> 8) & 0xff)) ^ 0x8000)) DEFINE_CONV(16_swap_16_sign_swap, wxUint16, wxUint16, (src ^ 0x80)) // ----------------------------------------------------------------------- // Main PCM stream converter table // ----------------------------------------------------------------------- // Definition // XX -> YY // XX -> YY sign // // XX swapped -> YY // XX swapped -> YY sign // // XX swapped -> YY swapped // XX swapped -> YY swapped sign // // XX stereo -> YY mono // XX stereo -> YY mono sign // // XX swapped stereo -> YY swapped mono // XX swapped stereo -> YY swapped mono sign // // XX swapped stereo -> YY swapped mono // XX swapped stereo -> YY swapped mono sign static wxSoundStreamPcm::ConverterType s_converters[4][3][2] = { { { NULL, Convert_8_8_sign /* 8 -> 8 sign */ }, { NULL, NULL }, { NULL, NULL } }, { { Convert_8_16, /* 8 -> 16 */ Convert_8_16_sign /* 8 -> 16 sign */ }, { Convert_8_16_swap, /* 8 -> 16 swapped */ Convert_8_16_sign_swap /* 8 -> 16 sign swapped */ }, { NULL, NULL } }, { { Convert_16_8, /* 16 -> 8 */ Convert_16_8_sign /* 16 -> 8 sign */ }, { Convert_16_swap_8, /* 16 swapped -> 8 */ Convert_16_swap_8_sign /* 16 swapped -> 8 sign */ }, { NULL, NULL }, }, { { NULL, /* 16 -> 16 */ Convert_16_sign /* 16 -> 16 sign */ }, { Convert_16_swap, /* 16 swapped -> 16 */ Convert_16_swap_16_sign /* 16 swapped -> 16 sign */ }, { NULL, Convert_16_swap_16_sign_swap /* 16 swapped -> 16 sign swapped */ } } }; // This is the buffer size multiplier. It gives the needed size of the output size. static float s_converters_multip[] = {1, 2, 0.5, 1}; // // TODO: Read() and Write() aren't really safe. If you give it a buffer which // is not aligned on 2, you may crash (See converter.def). // wxSoundStream& wxSoundStreamPcm::Read(void *buffer, wxUint32 len) { wxUint32 in_bufsize; // We must have a multiple of 2 len &= 0x01; if (!m_function_in) { m_sndio->Read(buffer, len); m_lastcount = m_sndio->GetLastAccess(); m_snderror = m_sndio->GetError(); return *this; } in_bufsize = GetReadSize(len); if (len <= m_best_size) { m_sndio->Read(m_prebuffer, in_bufsize); m_snderror = m_sndio->GetError(); if (m_snderror != wxSOUND_NOERROR) { m_lastcount = 0; return *this; } m_function_in(m_prebuffer, buffer, m_sndio->GetLastAccess()); } else { char *temp_buffer; temp_buffer = new char[in_bufsize]; m_sndio->Read(temp_buffer, in_bufsize); m_snderror = m_sndio->GetError(); if (m_snderror != wxSOUND_NOERROR) { m_lastcount = 0; return *this; } m_function_in(temp_buffer, buffer, m_sndio->GetLastAccess()); delete[] temp_buffer; } m_lastcount = (wxUint32)(m_sndio->GetLastAccess() * m_multiplier_in); return *this; } wxSoundStream& wxSoundStreamPcm::Write(const void *buffer, wxUint32 len) { wxUint32 out_bufsize; if (!m_function_out) { m_sndio->Write(buffer, len); m_lastcount = m_sndio->GetLastAccess(); m_snderror = m_sndio->GetError(); return *this; } out_bufsize = GetWriteSize(len); if (len <= m_best_size) { out_bufsize = GetWriteSize(len); m_function_out(buffer, m_prebuffer, len); m_sndio->Write(m_prebuffer, out_bufsize); m_snderror = m_sndio->GetError(); if (m_snderror != wxSOUND_NOERROR) { m_lastcount = 0; return *this; } } else { char *temp_buffer; temp_buffer = new char[out_bufsize]; m_function_out(buffer, temp_buffer, len); m_sndio->Write(temp_buffer, out_bufsize); m_snderror = m_sndio->GetError(); if (m_snderror != wxSOUND_NOERROR) { m_lastcount = 0; return *this; } delete[] temp_buffer; } m_lastcount = (wxUint32)(m_sndio->GetLastAccess() / m_multiplier_out); return *this; } bool wxSoundStreamPcm::SetSoundFormat(const wxSoundFormatBase& format) { wxSoundFormatBase *new_format; wxSoundFormatPcm *pcm_format, *pcm_format2; if (m_sndio->SetSoundFormat(format)) { m_function_out = NULL; m_function_in = NULL; return true; } if (format.GetType() != wxSOUND_PCM) { m_snderror = wxSOUND_INVFRMT; return false; } if (m_sndformat) delete m_sndformat; new_format = m_sndio->GetSoundFormat().Clone(); pcm_format = (wxSoundFormatPcm *)&format; pcm_format2 = (wxSoundFormatPcm *)new_format; #if 0 // ---------------------------------------------------- // Test whether we need to resample if (pcm_format->GetSampleRate() != pcm_format2->GetSampleRate()) { wxUint32 src_rate, dst_rate; src_rate = pcm_format->GetSampleRate(); dst_rate = pcm_format2->GetSampleRate(); m_needResampling = true; if (src_rate < dst_rate) m_expandSamples = true; else m_expandSamples = false; m_pitch = (src_rate << FLOATBITS) / dst_rate; } #endif // ---------------------------------------------------- // Select table to use: // * 8 bits -> 8 bits // * 16 bits -> 8 bits // * 8 bits -> 16 bits // * 16 bits -> 16 bits int table_no, table_no2; int i_sign, i_swap; switch (pcm_format->GetBPS()) { case 8: table_no = 0; break; case 16: table_no = 1; break; default: // TODO: Add something here: error, log, ... return false; } switch (pcm_format2->GetBPS()) { case 8: table_no2 = 0; break; case 16: table_no2 = 1; break; default: // TODO: Add something here: error, log, ... return false; } if (pcm_format2->Signed() != pcm_format->Signed()) i_sign = 1; else i_sign = 0; #define MY_ORDER wxBYTE_ORDER #if wxBYTE_ORDER == wxLITTLE_ENDIAN #define OTHER_ORDER wxBIG_ENDIAN #else #define OTHER_ORDER wxLITTLE_ENDIAN #endif // -------------------------------------------------------- // Find the good converter ! if (pcm_format->GetOrder() == OTHER_ORDER) { if (pcm_format->GetOrder() == pcm_format2->GetOrder()) i_swap = 2; else i_swap = 1; } else { if (pcm_format->GetOrder() == pcm_format2->GetOrder()) i_swap = 0; else i_swap = 1; } m_function_out = s_converters[table_no*2+table_no2][i_swap][i_sign]; m_function_in = s_converters[table_no2*2+table_no][i_swap][i_sign]; m_multiplier_out = s_converters_multip[table_no*2+table_no2]; m_multiplier_in = s_converters_multip[table_no2*2+table_no2]; if (m_prebuffer) delete[] m_prebuffer; // We try to minimize the need for dynamic memory allocation by preallocating a buffer. But // to be sure it will be efficient we minimize the best size. if (m_multiplier_in < m_multiplier_out) { m_prebuffer_size = (wxUint32)(m_sndio->GetBestSize() * m_multiplier_out); m_best_size = (wxUint32)(m_sndio->GetBestSize() * m_multiplier_in); } else { m_prebuffer_size = (wxUint32)(m_sndio->GetBestSize() * m_multiplier_in); m_best_size = (wxUint32)(m_sndio->GetBestSize() * m_multiplier_out); } m_prebuffer = new char[m_prebuffer_size]; bool SetSoundFormatReturn; SetSoundFormatReturn = m_sndio->SetSoundFormat(*new_format); wxASSERT( SetSoundFormatReturn ); wxUnusedVar( SetSoundFormatReturn ); m_sndformat = new_format; return true; } wxUint32 wxSoundStreamPcm::GetWriteSize(wxUint32 len) const { // For the moment, it is simple but next time it will become more complicated // (Resampling) return (wxUint32)(len * m_multiplier_out); } wxUint32 wxSoundStreamPcm::GetReadSize(wxUint32 len) const { return (wxUint32)(len / m_multiplier_in); } // Resampling engine. NOT FINISHED and NOT INCLUDED but this is a first DRAFT. #if 0 #define FLOATBITS 16 #define INTBITS 16 #define FLOATMASK 0xffff #define INTMASK 0xffff0000 void ResamplingShrink_##DEPTH##(const void *source, void *destination, wxUint32 len) { wxUint##DEPTH## *source_data, *dest_data; wxUint32 pos; source_data = (wxUint##DEPTH## *)source; dest_data = (wxUint##DEPTH## *)destination; pos = m_saved_pos; while (len > 0) { // Increment the position in the input buffer pos += m_pitch; if (pos & INTMASK) { pos &= FLOATMASK; *dest_data ++ = *source_data; } len--; source_data++; } m_saved_pos = pos; } #endif