// --------------------------------------------------------------------------
// Name: sndg72x.cpp
// Purpose:
// Date: 08/26/1999
// Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999
// CVSID: $Id: sndg72x.cpp,v 1.3 2004/06/16 15:22:59 ABX Exp $
// --------------------------------------------------------------------------
#ifdef __GNUG__
#pragma implementation "sndg72x.cpp"
#endif

#include "wx/wxprec.h"

#ifndef WX_PRECOMP
    #include "wx/defs.h"
#endif

#include "wx/mmedia/sndbase.h"
#include "wx/mmedia/sndfile.h"
#include "wx/mmedia/sndpcm.h"
#include "wx/mmedia/sndg72x.h"
#include "wx/mmedia/internal/g72x.h"

// --------------------------------------------------------------------------
// wxSoundFormatG72X
// --------------------------------------------------------------------------

wxSoundFormatG72X::wxSoundFormatG72X()
        : m_srate(22050)
{
}

wxSoundFormatG72X::~wxSoundFormatG72X()
{
}

void wxSoundFormatG72X::SetSampleRate(wxUint32 srate)
{
    m_srate = srate;
}

wxUint32 wxSoundFormatG72X::GetSampleRate() const
{
    return m_srate;
}

void wxSoundFormatG72X::SetG72XType(wxSoundG72XType type)
{
    m_g72x_type = type;
}

wxSoundFormatBase *wxSoundFormatG72X::Clone() const
{
    wxSoundFormatG72X *g72x = new wxSoundFormatG72X();

    g72x->m_srate = m_srate;
    g72x->m_g72x_type = m_g72x_type;
    return g72x;
}

wxUint32 wxSoundFormatG72X::GetTimeFromBytes(wxUint32 bytes) const
{
    int n_bits;

    switch (m_g72x_type) {
        case wxSOUND_G721:
            n_bits = 4;
            break;
        case wxSOUND_G723_24:
            n_bits = 3;
            break;
        case wxSOUND_G723_40:
            n_bits = 5;
            break;
        default:
            n_bits = 0;
            break;
    }
    return (wxUint32)((bytes / m_srate) * n_bits) / 8;
}

wxUint32 wxSoundFormatG72X::GetBytesFromTime(wxUint32 time) const
{
    int n_bits;
    
    switch (m_g72x_type) {
        case wxSOUND_G721:
            n_bits = 4;
            break;
        case wxSOUND_G723_24:
            n_bits = 3;
            break;
        case wxSOUND_G723_40:
            n_bits = 5;
            break;
        default:
            n_bits = 0;
    }
    return (wxUint32)((time * m_srate * n_bits) / 8);
}

bool wxSoundFormatG72X::operator !=(const wxSoundFormatBase& frmt2) const
{
    wxSoundFormatG72X *g72x = (wxSoundFormatG72X *)&frmt2;
    
    if (frmt2.GetType() != wxSOUND_G72X)
        return true;
    
    return (g72x->m_srate != m_srate || g72x->m_g72x_type != m_g72x_type);
}

// --------------------------------------------------------------------------
// wxSoundStreamG72X
// --------------------------------------------------------------------------

wxSoundStreamG72X::wxSoundStreamG72X(wxSoundStream& sndio)
        : wxSoundStreamCodec(sndio)
{
    // PCM converter
    m_router = new wxSoundRouterStream(sndio);
    m_state  = new g72state;
    g72x_init_state(m_state);
}

wxSoundStreamG72X::~wxSoundStreamG72X()
{
    delete m_router;
}

wxSoundStream& wxSoundStreamG72X::Read(void *buffer, wxUint32 len)
{
    wxUint16 *old_linear;
    register wxUint16 *linear_buffer;
    register wxUint32 real_len;
    register wxUint32 countdown = len;
    
    real_len = (len * 8 / m_n_bits);
    
    old_linear = linear_buffer = new wxUint16[real_len];
    
    m_router->Read(linear_buffer, real_len);
    
    real_len = (wxUint32)(m_router->GetLastAccess() * ((float)m_n_bits / 8));
    if (!real_len)
        return *m_router;
    
    m_io_buffer = (wxUint8 *)buffer; 
    m_current_b_pos = 0;
    
    while (countdown != 0) {
        PutBits(m_coder(*linear_buffer++, AUDIO_ENCODING_LINEAR, m_state));
        countdown--;
    }
    m_lastcount = real_len;
    m_snderror = m_router->GetError();
    
    delete[] old_linear;
    
    return *this;
}

wxSoundStream& wxSoundStreamG72X::Write(const void *buffer, wxUint32 len)
{
    wxUint16 *old_linear;
    register wxUint16 *linear_buffer;
    register wxUint32 countdown = len;
    register wxUint32 real_len;
    
    // Compute the real length (PCM format) to sendt to the sound card
    real_len = (len * m_n_bits / 8);
    
    // Allocate a temporary buffer
    old_linear = linear_buffer = new wxUint16[real_len];
    
    // Bad, we override the const
    m_io_buffer = (wxUint8 *)buffer;
    m_current_b_pos = 0;
    
    // Decode the datas
    while (countdown != 0) {
        *linear_buffer++ = m_decoder(GetBits(), AUDIO_ENCODING_LINEAR, m_state);
        countdown--;
    }
    m_lastcount = len;
    
    // Send them to the sound card
    m_router->Write(old_linear, real_len);
    
    // Destroy the temporary buffer
    delete[] old_linear;
    
    return *m_router;
}

bool wxSoundStreamG72X::SetSoundFormat(const wxSoundFormatBase& format)
{
    if (format.GetType() != wxSOUND_G72X) {
        m_snderror = wxSOUND_INVFRMT;
        return false;
    }
    
    wxSoundFormatPcm pcm;
    wxSoundFormatG72X *g72x;
    
    wxSoundStreamCodec::SetSoundFormat(format);
    
    g72x = (wxSoundFormatG72X *)m_sndformat;
    
    // Set PCM as the output format of the codec
    pcm.SetSampleRate(g72x->GetSampleRate());
    pcm.SetBPS(16);
    pcm.SetChannels(1); // Only mono supported
    pcm.Signed(true);
    pcm.SetOrder(wxBYTE_ORDER);

    // Look for the correct codec to use and set its bit width
    switch (g72x->GetG72XType()) {
        case wxSOUND_G721:
            m_n_bits  = 4;
            m_coder   = g721_encoder;
            m_decoder = g721_decoder;
            break;
        case wxSOUND_G723_24:
            m_n_bits  = 3;
            m_coder   = g723_24_encoder;
            m_decoder = g723_24_decoder;
            break;
        case wxSOUND_G723_40:
            m_n_bits  = 5;
            m_coder   = g723_40_encoder;
            m_decoder = g723_40_decoder;
            break;
    }

    // Let the router finish the work
    m_router->SetSoundFormat(pcm);
    
    return true;
}

#define BYTE_SIZE 8

wxUint8 wxSoundStreamG72X::GetBits()
{
    register wxUint8 bits;

    // We have two bytes to compute
    if (m_current_b_pos < m_n_bits) {
        register wxUint8 b_left;
        
        // TRANSLATE the mask
        m_current_mask >>= m_current_b_pos;
        
        // GET the last bits: 0001..1
        bits = (m_current_byte & m_current_mask) << (m_n_bits - m_current_b_pos);
        
        // GEN: 1. n times .1000
        b_left = BYTE_SIZE-m_n_bits;
        m_current_mask = ((1 << m_n_bits) - 1) << b_left;
        
        // GET the next byte
        m_current_byte = *m_io_buffer++;
        
        register wxUint8 tmp_mask;
        
        // COMPUTE a new temporary mask to get the last bits
        b_left = m_n_bits - b_left;
        tmp_mask = (1 << b_left) - 1;
        // TRANSLATE the old mask to get ready for the next time
        m_current_mask >>= b_left;
        
        // COMPUTE the new bit position
        b_left = BYTE_SIZE - b_left; 
        m_current_b_pos = b_left;
        tmp_mask <<= b_left;
        
        // GET the last bits
        bits |= (m_current_byte & tmp_mask) >> b_left;
    } else {
        m_current_mask >>= m_n_bits;
        m_current_b_pos -= m_n_bits;
        bits = (m_current_byte & m_current_mask) >> m_current_b_pos;
    }
    return bits;
}

void wxSoundStreamG72X::PutBits(wxUint8 bits)
{
    if (m_current_b_pos < m_n_bits) {
        register wxUint8 tmp_mask;
        register wxUint8 diff;
        
        diff = m_n_bits - m_current_b_pos;
        // Pack bits and put the byte in the buffer
        m_current_byte |= bits >> diff;
        *m_io_buffer++ = m_current_byte;
        
        // Gen a mask
        tmp_mask = ~((1 << diff) - 1);
        
        m_current_b_pos = BYTE_SIZE - (m_n_bits - m_current_b_pos);
        
        m_current_byte = (bits & (tmp_mask)) << m_current_b_pos;
    } else {
        m_current_b_pos -= m_n_bits;
        bits           <<= m_current_b_pos;
        m_current_byte |= bits;
    }
}
