Top secrets sources NedoPC pentevo

Rev

Blame | Last modification | View Log | Download | RSS feed | ?url?

#include "../std.h"

#include "../emul.h"
#include "../vars.h"

/*
   sound resampling core for Unreal Speccy project
   created under public domain license by SMT, jan.2006
*/


#include "sndrender.h"

//=============================================================================
unsigned SNDRENDER::render(     SNDOUT *src,
                                unsigned srclen,
                                unsigned clk_ticks,
                                bufptr_t dst_start
          )
//-----------------------------------------------------------------------------
{
    start_frame( dst_start);
    //-------------------------------------------------------------------------
    for (unsigned index = 0;    index < srclen;    index++)
    {
        // if (src[ index].timestamp > clk_ticks)
        //     continue; // wrong input data leads to crash
       
        update(    src[ index].timestamp,
                   src[ index].newvalue.ch.left,
                   src[ index].newvalue.ch.right
                );
    }
    //-------------------------------------------------------------------------
    return end_frame( clk_ticks);
}
//=============================================================================

const unsigned TICK_F = (1 << TICK_FF);

// b = 1+ln2(max_sndtick) = 1+ln2((max_sndfq*TICK_F)/min_intfq) = 1+ln2(48000*64/10) ~= 19.2;
// assert(b+MULT_C <= 32)

//=============================================================================
void SNDRENDER::start_frame( bufptr_t dst_start)
{
    SNDRENDER::dst_start = dstpos = dst_start;
    base_tick = tick;
    firstsmp = 4; //Alone Coder
}
//=============================================================================

//=============================================================================
void SNDRENDER::update( unsigned timestamp,
                        unsigned l,
                        unsigned r
         )
//-----------------------------------------------------------------------------
{
    //-------------------------------------------------------------------------
    if (!((l ^ mix_l) | (r ^ mix_r))) // (l == mix_l) && (r == mix_r)
        return;
    //-------------------------------------------------------------------------
    //[vv] unsigned endtick = (timestamp * mult_const) >> MULT_C;
    uint64_t endtick = (timestamp * (uint64_t)sample_rate * TICK_F) / clock_rate;
    flush( (unsigned) (base_tick + endtick) );
    mix_l = l;
    mix_r = r;  // ýòî îïÿòü çàïèñü "îòñòàåò" ?
}
//=============================================================================


//=============================================================================
unsigned SNDRENDER::end_frame( unsigned clk_ticks)
{
    // adjusting 'clk_ticks' with whole history
    // will fix accumulation of rounding errors
    // uint64_t endtick = ((passed_clk_ticks + clk_ticks) * mult_const) >> MULT_C;
    uint64_t endtick = ((passed_clk_ticks + clk_ticks) * (uint64_t)sample_rate * TICK_F) / clock_rate;
   
    flush( (unsigned) (endtick - passed_snd_ticks) );

    unsigned ready_samples = dstpos - dst_start;
    //---------------------------------------------------------------------
    #ifdef SND_EXTERNAL_BUFFER
        if ((int)ready_samples < 0)
            ready_samples += SND_EXTERNAL_BUFFER_SIZE;
    #endif
    //---------------------------------------------------------------------
    oldfrmleft = ((long)useleft + (long)olduseleft) / 2; //Alone Coder
    oldfrmright = ((long)useright + (long)olduseright) / 2; //Alone Coder

    tick -= (ready_samples << TICK_FF);
    passed_snd_ticks += (ready_samples << TICK_FF);
    passed_clk_ticks += clk_ticks;

   return ready_samples;
}
//=============================================================================



//=============================================================================
/*
unsigned SNDRENDER::end_empty_frame(unsigned clk_ticks)
{
    // adjusting 'clk_ticks' with whole history
    // will fix accumulation of rounding errors
    // uint64_t endtick = ((passed_clk_ticks + clk_ticks) * mult_const) >> MULT_C;
    uint64_t endtick = ((passed_clk_ticks + clk_ticks) * (uint64_t)sample_rate * TICK_F) / clock_rate;
    tick = (unsigned)(endtick-passed_snd_ticks); // flush(endtick-passed_snd_ticks);
    // todo: change dstpos!

    unsigned ready_samples = dstpos - dst_start;
    //-------------------------------------------------------------------------
    #ifdef SND_EXTERNAL_BUFFER
        if ((int)ready_samples < 0)
            ready_samples += SND_EXTERNAL_BUFFER_SIZE;
    #endif
    //-------------------------------------------------------------------------
    tick -= (ready_samples << TICK_FF);
    passed_snd_ticks += (ready_samples << TICK_FF);
    passed_clk_ticks += clk_ticks;

    return ready_samples;
}
*/

//=============================================================================



//=============================================================================
void SNDRENDER::set_timings(    unsigned clock_rate,
                                unsigned sample_rate
             )
//-----------------------------------------------------------------------------
{
    SNDRENDER::clock_rate = clock_rate;
    SNDRENDER::sample_rate = sample_rate;

    tick = 0;
    dstpos = dst_start = 0;
    passed_snd_ticks = passed_clk_ticks = 0;

//  mult_const = (unsigned) (((uint64_t)sample_rate << (MULT_C+TICK_FF)) / clock_rate);
}
//=============================================================================


static unsigned filter_diff[ TICK_F * 2];       // discrete-time step response
const double filter_sum_full = 1.0;
const double filter_sum_half = 0.5;

const unsigned filter_sum_full_u = (unsigned)(filter_sum_full * 0x10000),
               filter_sum_half_u = (unsigned)(filter_sum_half * 0x10000);
         
         
//=============================================================================
void SNDRENDER::flush(unsigned endtick)
{
    unsigned scale;
    //-------------------------------------------------------------------------
    if (!((endtick ^ tick) & ~(TICK_F - 1))) // (endtick / TICK_F) == (tick / TICK_F)
    {  
        // Îïòèìèçàöèÿ, äåöèìàöèÿ íå íóæíà,
        // ò.ê. ïîñëå äåöèìàöèè tick ñîâïàäåò ñ endtick
        // Âõîäíîé ñèãíàë èçìåíèëñÿ áûñòðåå ÷åì îäèí ïåðèîä Fs âûõîäíîãî ñèãíàëà
        // s1 - çíà÷åíèå âûõîäíîãî ñèãíàëà â ëåâîì óçëå äåöèìàöèè
        // s2 - çíà÷åíèå âûõîäíîãî ñèãíàëà â ïðàâîì óçëå äåöèìàöèè
        // mix - çíà÷åíèå âõîäíîãî ñèãíàëà â èíòåðâàëå [tick.. endtick)
       
        // same discrete as before
        //---------------------------------------------------------------------
        // Ïðàâàÿ òî÷êà âû÷èñëåííàÿ ïî ïåðåõîäíîé õàðàêòåðèñòèêå
        scale = filter_diff[ (endtick & (TICK_F - 1)) + TICK_F] -
                filter_diff[ (tick &    (TICK_F - 1)) + TICK_F];
        s2_l += mix_l * scale;
        s2_r += mix_r * scale;
        //---------------------------------------------------------------------
        // Ëåâàÿ òî÷êà âû÷èñëåííàÿ ïî ïåðåõîäíîé õàðàêòåðèñòèêå
        scale = filter_diff[ endtick & (TICK_F - 1)] -
                filter_diff[ tick &    (TICK_F - 1)];
        s1_l += mix_l * scale;
        s1_r += mix_r * scale;
        //---------------------------------------------------------------------
        tick = endtick;
        return;
    }
    //-------------------------------------------------------------------------

    // Îòðåçîê [TICK_F + (tick % TICK_F) ... 2*TICK_F-1]
    scale = filter_sum_full_u - filter_diff[ (tick & (TICK_F - 1)) + TICK_F]; // filter_sum_full_u = filter_diff[(TICK_F - 1) + TICK_F]

    unsigned sample_value;
    //-------------------------------------------------------------------------
    // è îíî òðåùèò íà saa1099 ïî÷àìóòî
    // èëè ýòî êîä òîëüêî äëÿ äèæèòàëà?
    if (conf.soundfilter)
    {
        // lame noise reduction by Alone Coder
        //---------------------------------------------------------------------
        int templeft = int( mix_l * scale + s2_l);
        /*
        olduseleft = useleft;
        if (firstsmp)
            useleft=oldfrmleft,firstsmp--;
        else
        */

        useleft = ((long)templeft + (long)oldleft) / 2;
       
        oldleft = templeft;
        //---------------------------------------------------------------------
        int tempright = int( mix_r * scale + s2_r);
        /*
        olduseright = useright;
        if (firstsmp)
            useright = oldfrmright, firstsmp--;
        else
        */

        useright = ((long)tempright + (long)oldright) / 2;
        oldright = tempright;
        //---------------------------------------------------------------------
        sample_value =  unsigned(    ( useleft >> 16) +
                        int( unsigned( useright) & 0xFFFF0000));
    }
    //-------------------------------------------------------------------------
    else
    {
        sample_value =  ( (mix_l * scale + s2_l) >> 16) +
                        ( (mix_r * scale + s2_r) & 0xFFFF0000);
    }
    //-------------------------------------------------------------------------
    // çíà÷åíèå ãðîìêîñòè òîëüêî â ìîìåíò èçìåíåíèÿ ?
    #ifdef SND_EXTERNAL_BUFFER
        SND_EXTERNAL_BUFFER[ dstpos].sample += sample_value;    //0001
        dstpos = (dstpos + 1) & (SND_EXTERNAL_BUFFER_SIZE - 1);
    //-------------------------------------------------------------------------
    #else
        dstpos->sample = sample_value;
        dstpos++;
    #endif
    //-------------------------------------------------------------------------
    // Îòðåçîê [tick % TICK_F ... TICK_F-1]
    scale = filter_sum_half_u - filter_diff[ tick & (TICK_F - 1)]; // filter_sum_half_u = filter_diff[TICK_F - 1]
    s2_l = s1_l + mix_l * scale;
    s2_r = s1_r + mix_r * scale;
    //-------------------------------------------------------------------------
    // Âûðàâíèâàíèå äî óçëà äåöèìàöèè
    tick = (tick | (TICK_F - 1)) + 1;
    //-------------------------------------------------------------------------
    // Öèêë äåöèìàöèè â TICK_F ðàç, tick óæå âûðîâíåí äî ñåòêè ñ øàãîì TICK_F
    // endtick / TICK_F - endtick ïîñëå äåöèìàöèè â TICK_F ðàç
    // tick / TICK_F - tick ïîñëå äåöèìàöèè â TICK_F ðàç
    if ((endtick ^ tick) & ~(TICK_F - 1)) // (endtick / TICK_F) != (tick / TICK_F)
    {
        // Îïòèìèçàöèÿ, ïðîâåðêà íà íåñîâïàäåíèå tick è endtick ïîñëå äåöèìàöèè
        // assume filter_coeff is symmetric
        // Îòðåçîê [0 ... TICK_F-1]
        unsigned val_l = mix_l * filter_sum_half_u;
        unsigned val_r = mix_r * filter_sum_half_u;
        //---------------------------------------------------------------------
        // Öèêë äåöèìàöèè, [tick/TICK_F ... endtick/TICK_F)
        do
        {
            //-----------------------------------------------------------------
            if (conf.soundfilter)
            {
                // lame noise reduction by Alone Coder
                //-------------------------------------------------------------
                int templeft = int( s2_l + val_l);
                /*
                olduseleft = useleft;
                if (firstsmp)
                    useleft = oldfrmleft, firstsmp--;
                else
                */

                useleft = ((long)templeft + (long)oldleft) / 2;
                oldleft = templeft;
                //-------------------------------------------------------------
                int tempright = int( s2_r + val_r);
                /*
                olduseright = useright;
                if (firstsmp)
                    useright=oldfrmright,firstsmp--;
                else
                */

                useright = ((long)tempright + (long)oldright) / 2;
                oldright = tempright;
                //-------------------------------------------------------------
                sample_value =  unsigned(    ( useleft >> 16) +
                                int( unsigned( useright) & 0xFFFF0000));
            }
            //-----------------------------------------------------------------
            else
            {
                sample_value =  ((s2_l + val_l) >> 16) +
                                ((s2_r + val_r) & 0xFFFF0000); // save s2+val
            }
            //-----------------------------------------------------------------
            // çàïîëíåíèå ïðîìåæóòî÷íûõ çíà÷åíèé?
            #ifdef SND_EXTERNAL_BUFFER
                SND_EXTERNAL_BUFFER[ dstpos].sample += sample_value;    //0002
                dstpos = (dstpos + 1) & (SND_EXTERNAL_BUFFER_SIZE - 1);
            //-----------------------------------------------------------------
            #else
                dstpos->sample = sample_value;
                dstpos++;
            #endif
            //-----------------------------------------------------------------
            tick += TICK_F;
            s2_l = val_l;
            s2_r = val_r; // s2=s1, s1=0;

        }
        while ((endtick ^ tick) & ~(TICK_F - 1)); // (endtick / TICK_F) != (tick / TICK_F)
        //---------------------------------------------------------------------
    }
    //-------------------------------------------------------------------------
    tick = endtick;

    scale = filter_diff[ (endtick & (TICK_F - 1)) + TICK_F] - filter_sum_half_u; // filter_sum_half_u = filter_diff[TICK_F - 1]
    s2_l += mix_l * scale;
    s2_r += mix_r * scale;

    scale = filter_diff[ endtick & (TICK_F - 1)];
    s1_l = mix_l * scale;
    s1_r = mix_r * scale;
}
//=============================================================================


// bw = 1*(10^-2)*pi rad/smp (-3dB)
// bw = 2*(10^-2)*pi rad/smp (-10dB)
// bw = 20 kHz (fs=44100 * 64 = 2822400)
// matlab: fvtool(filter_coeff)

// Ïàðàìåòðû ñèíòåçà ôèëüòðà:
// matlab: (fdatool/filterDesigner)
// FIR: Window (Hamming)
// order: 127
// Fs: 2822400
// Fc: 11025

const double filter_coeff[ TICK_F * 2] =
{
   // filter designed with Matlab's DSP toolbox
   0.000797243121022152, 0.000815206499600866, 0.000844792477531490, 0.000886460636664257,
   0.000940630171246217, 0.001007677515787512, 0.001087934129054332, 0.001181684445143001,
   0.001289164001921830, 0.001410557756409498, 0.001545998595893740, 0.001695566052785407,
   0.001859285230354019, 0.002037125945605404, 0.002229002094643918, 0.002434771244914945,
   0.002654234457752337, 0.002887136343664226, 0.003133165351783907, 0.003391954293894633,
   0.003663081102412781, 0.003946069820687711, 0.004240391822953223, 0.004545467260249598,
   0.004860666727631453, 0.005185313146989532, 0.005518683858848785, 0.005860012915564928,
   0.006208493567431684, 0.006563280932335042, 0.006923494838753613, 0.007288222831108771,
   0.007656523325719262, 0.008027428904915214, 0.008399949736219575, 0.008773077102914008,
   0.009145787031773989, 0.009517044003286715, 0.009885804729257883, 0.010251021982371376,
   0.010611648461991030, 0.010966640680287394, 0.011314962852635887, 0.011655590776166550,
   0.011987515680350414, 0.012309748033583185, 0.012621321289873522, 0.012921295559959939,
   0.013208761191466523, 0.013482842243062109, 0.013742699838008606, 0.013987535382970279,
   0.014216593638504731, 0.014429165628265581, 0.014624591374614174, 0.014802262449059521,
   0.014961624326719471, 0.015102178534818147, 0.015223484586101132, 0.015325161688957322,
   0.015406890226980602, 0.015468413001680802, 0.015509536233058410, 0.015530130313785910,
   0.015530130313785910, 0.015509536233058410, 0.015468413001680802, 0.015406890226980602,
   0.015325161688957322, 0.015223484586101132, 0.015102178534818147, 0.014961624326719471,
   0.014802262449059521, 0.014624591374614174, 0.014429165628265581, 0.014216593638504731,
   0.013987535382970279, 0.013742699838008606, 0.013482842243062109, 0.013208761191466523,
   0.012921295559959939, 0.012621321289873522, 0.012309748033583185, 0.011987515680350414,
   0.011655590776166550, 0.011314962852635887, 0.010966640680287394, 0.010611648461991030,
   0.010251021982371376, 0.009885804729257883, 0.009517044003286715, 0.009145787031773989,
   0.008773077102914008, 0.008399949736219575, 0.008027428904915214, 0.007656523325719262,
   0.007288222831108771, 0.006923494838753613, 0.006563280932335042, 0.006208493567431684,
   0.005860012915564928, 0.005518683858848785, 0.005185313146989532, 0.004860666727631453,
   0.004545467260249598, 0.004240391822953223, 0.003946069820687711, 0.003663081102412781,
   0.003391954293894633, 0.003133165351783907, 0.002887136343664226, 0.002654234457752337,
   0.002434771244914945, 0.002229002094643918, 0.002037125945605404, 0.001859285230354019,
   0.001695566052785407, 0.001545998595893740, 0.001410557756409498, 0.001289164001921830,
   0.001181684445143001, 0.001087934129054332, 0.001007677515787512, 0.000940630171246217,
   0.000886460636664257, 0.000844792477531490, 0.000815206499600866, 0.000797243121022152
};

// h - impulse response
// s - step response
// h[n] = s[n] - s[n-1]

// y[n] = sum(x[k]*h[n-k]) = sum(h[k]*x[n-k])
//         k                  k
// Äëÿ êóñî÷íî ïîñòîÿííîãî ñèãíàëà ïðèìåíèìà îïòèìèçàöèÿ â âû÷èñëåíèÿõ (íåò íåîáõîäèìîñòè âû÷èñëÿòü ïðîìåæóòî÷íûå òî÷êè)
// y[n] = y[k] + x*(s[n] - s[k])
// ãäå n è k - êîíöû èíòåðâàëà, íà êîòîðîì ñèãíàë x ïîñòîÿíåí

// Îáùèå ôîðìóëû:
// u - unit step
// s - step response
//                        inf
// x[n] = x[-inf] + sum((x[k]-x[k-1])u[n-k])
//                       -inf
//
//                        inf
// y[n] = x[-inf]s[inf] + sum(x[k]-x[k-1])s[n-k]
//                       -inf
// Èç ôîðìêë âèäíî, ÷òî çíà÷åíèÿ èìåþò òîëüêî ôðîíòû ñèãíàëà, òàì ãäå ñèãíàë ïîñòîÿííûé ñëàãàåûå îáðàùàþòñÿ â íîëü
// îòñþäà ñòàíîâèòñÿ âîçìîæíà êóñî÷íî-ïîñòîÿííàÿ îïòèìèçàöèÿ

// Äëÿ YM (çíà÷åíèÿ áåðóòñÿ èç êîíôèãà)
// äëÿ ïðèìåðà:
// clock_rate = 1750000 / 8 = 218750
//              1774400 / 8 = 221800
// sample_rate = 44100
// Ï× = sample_rate * 64 = 2822400
// Ïåðåíîñ âõîäíîãî ñèãíàëà íà Ï× äåëàåòñÿ äîìíîæåíèåì íà (sample_rate*64)/clock_rate =
// 12.9024 (äëÿ clock_rate = 1.7500) è 12.724977 (äëÿ clock_rate = 1.7744)
// Íà Ï× äåëàåòñÿ ôèëüòðàöèÿ è äåöèìàöèÿ â 64 ðàçà äî sample_rate

//=============================================================================
SNDRENDER::SNDRENDER()
{
   set_timings(SNDR_DEFAULT_SYSTICK_RATE, SNDR_DEFAULT_SAMPLE_RATE);

   static char diff_ready = 0;
   if (!diff_ready) {
      double sum = 0;
      for (unsigned i = 0; i < TICK_F*2; i++) { // calculate discrete-time step response
         filter_diff[i] = unsigned(sum * 0x10000);
         sum += filter_coeff[i]; // Òóò îøèáêà, ñóììó íàäî âû÷èñëÿòü äî ïîñòðîåíèÿ ïåðåõîäíîé õàðàêòåðèñòèêè, ÷òîáû filter_diff[0] == filter_coeff[0]
                                 // filter_diff[TICK_F - 1] == 0.5, filter_diff[2*TICK_F - 1] == 1.0
      }
      diff_ready = 1;
   }
}
//=============================================================================