raldus/roland

View on GitHub
src/cpc/sound.cpp

Summary

Maintainability
Test Coverage
/***************************************************************************
 *   Copyright (C) by Fred Klaus                                           *
 *       development@fkweb.de                                              *
 *                                                                         *
 *   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.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "sound.h"
#include "cpc.h"
#include <cmath>
#include <iostream>

namespace cpcx
{

    extern Cpc cpc;

    const tUWORD Sound::mAmplitudesAY[16] =
        { 0, 836, 1212, 1773, 2619, 3875, 5397, 8823, 10392, 16706, 23339,
                29292, 36969, 46421, 55195, 65535 };

    const tDWORD Sound::mFreqTable[5] =
        { 11025, 22050, 44100, 48000, 96000 };

    Sound::Sound(Psg *psg) : mPsg(psg)
    {
        init(psg);
    }

    void Sound::init(Psg *psg)
    {
        mSndEnabled = true;
        mSndPlaybackRate = 1;
        mSndBits = 1;
        mSndStereo = 1;
        mSndVolume = 90;
        mSndDevice = 0;
        mSndBufferSize = 0;
        mSndBufferPtr = 0;
        mCycleCountInit.both = CYCLE_COUNT_INIT;

        mBufferFull = false;

        if (psg)  mPsg = psg;
        if (mPsg) mPsg->init();
        else return;

        for (int n = 0; n < 16; n++)
        {
            setAYRegister(
                n, mPsg->registerAY(n)); // init sound emulation with valid values
        }
    }

    /*
    inline void Sound::setMixerRegister(tUBYTE value)
    {
        mPsg->setMixer(value);
        mTonEnA = value & 1 ? false : true;
        mNoiseEnA = value & 8 ? false : true;
        mTonEnB = value & 2 ? false : true;
        mNoiseEnB = value & 16 ? false : true;
        mTonEnC = value & 4 ? false : true;
        mNoiseEnC = value & 32 ? false : true;
    }



    inline void Sound::setAmplA(tUBYTE value)
    {
        mPsg->setAmplitudeA(value);
        mEnvelopeEnA = value & 16 ? false : true;
    }



    inline void Sound::setAmplB(tUBYTE value)
    {
        mPsg->setAmplitudeB(value);
        mEnvelopeEnB = value & 16 ? false : true;
    }



    inline void Sound::setAmplC(tUBYTE value)
    {
        mPsg->setAmplitudeC(value);
        mEnvelopeEnC = value & 16 ? false : true;
    }
    */

    void Sound::caseEnvType0_3__9()
    {
        if (mPsg->firstPeriod())
        {
            mPsg->setAmplitudeEnv(mPsg->amplitudeEnv() - 1);
            if (!mPsg->amplitudeEnv())
            {
                mPsg->setFirstPeriod(false);
            }
        }
    }

    void Sound::caseEnvType4_7__15()
    {
        if (mPsg->firstPeriod())
        {
            mPsg->setAmplitudeEnv(mPsg->amplitudeEnv() + 1);
            if (mPsg->amplitudeEnv() == 32)
            {
                mPsg->setFirstPeriod(false);
                mPsg->setAmplitudeEnv(0);
            }
        }
    }

    void Sound::caseEnvType8()
    {
        mPsg->setAmplitudeEnv((mPsg->amplitudeEnv() - 1) & 31);
    }

    void Sound::caseEnvType10()
    {
        if (mPsg->firstPeriod())
        {
            mPsg->setAmplitudeEnv(mPsg->amplitudeEnv() - 1);
            if (mPsg->amplitudeEnv() == -1)
            {
                mPsg->setFirstPeriod(false);
                mPsg->setAmplitudeEnv(0);
            }
        }
        else
        {
            mPsg->setAmplitudeEnv(mPsg->amplitudeEnv() + 1);
            if (mPsg->amplitudeEnv() == 32)
            {
                mPsg->setFirstPeriod(true);
                mPsg->setAmplitudeEnv(31);
            }
        }
    }

    void Sound::caseEnvType11()
    {
        if (mPsg->firstPeriod())
        {
            mPsg->setAmplitudeEnv(mPsg->amplitudeEnv() - 1);
            if (mPsg->amplitudeEnv() == -1)
            {
                mPsg->setFirstPeriod(false);
                mPsg->setAmplitudeEnv(31);
            }
        }
    }

    void Sound::caseEnvType12()
    {
        mPsg->setAmplitudeEnv((mPsg->amplitudeEnv() + 1) & 31);
    }

    void Sound::caseEnvType13()
    {
        if (mPsg->firstPeriod())
        {
            mPsg->setAmplitudeEnv(mPsg->amplitudeEnv() + 1);
            if (mPsg->amplitudeEnv() == 32)
            {
                mPsg->setFirstPeriod(false);
                mPsg->setAmplitudeEnv(31);
            }
        }
    }

    void Sound::caseEnvType14()
    {
        if (!mPsg->firstPeriod())
        {
            mPsg->setAmplitudeEnv(mPsg->amplitudeEnv() - 1);
            if (mPsg->amplitudeEnv() == -1)
            {
                mPsg->setFirstPeriod(true);
                mPsg->setAmplitudeEnv(0);
            }
        }
        else
        {
            mPsg->setAmplitudeEnv(mPsg->amplitudeEnv() + 1);
            if (mPsg->amplitudeEnv() == 32)
            {
                mPsg->setFirstPeriod(false);
                mPsg->setAmplitudeEnv(31);
            }
        }
    }

    /*
    inline void Sound::setEnvelopeRegister(tUBYTE value)
    {
        mEnvelopeCounter.s.high = 0;
        mPsg->setFirstPeriod(true);
        if (!(value & 4))
        {
            mPsg->setAmplitudeEnv(32);
        }
        else
        {
            mPsg->setAmplitudeEnv(-1);
        }
        mPsg->setEnvType(value);
        switch (value)
        {
            case 0:
            case 1:
            case 2:
            case 3:
            case 9:
                mCaseEnvType = &Sound::caseEnvType0_3__9;
                break;
            case 4:
            case 5:
            case 6:
            case 7:
            case 15:
                mCaseEnvType = &Sound::caseEnvType4_7__15;
                break;
            case 8:
                mCaseEnvType = &Sound::caseEnvType8;
                break;
            case 10:
                mCaseEnvType = &Sound::caseEnvType10;
                break;
            case 11:
                mCaseEnvType = &Sound::caseEnvType11;
                break;
            case 12:
                mCaseEnvType = &Sound::caseEnvType12;
                break;
            case 13:
                mCaseEnvType = &Sound::caseEnvType13;
                break;
            case 14:
                mCaseEnvType = &Sound::caseEnvType14;
                break;
        }
    }

    void Sound::setAYRegister(int num, tUBYTE value)
    {
        switch(num)
        {
            case 13:
                setEnvelopeRegister(value & 15);
                break;
            case 1:
            case 3:
            case 5:
                mPsg->setRegisterAY(num, value & 15);
                break;
            case 6:
                mPsg->setNoise(value & 31);
                break;
            case 7:
                setMixerRegister(value & 63);
                break;
            case 8:
                setAmplA(value & 31);
                break;
            case 9:
                setAmplB(value & 31);
                break;
            case 10:
                setAmplC(value & 31);
                break;
            case 0:
            case 2:
            case 4:
            case 11:
            case 12:
                mPsg->setRegisterAY(num, value);
                break;
        }
    }
    */

    inline void Sound::synthesizerLogicQ()
    {
        // std::cerr << "synthesizer 3b *************" << std::endl;
        mTonCounterA.s.high++;
        if (mTonCounterA.s.high >= mPsg->tonALoW()) ///< @todo check this !!!
        {
            mTonCounterA.s.high = 0;
            mTonA ^= 1;
        }
        mTonCounterB.s.high++;
        if (mTonCounterB.s.high >= mPsg->tonBLoW()) ///< @todo check this !!!
        {
            mTonCounterB.s.high = 0;
            mTonB ^= 1;
        }
        mTonCounterC.s.high++;
        if (mTonCounterC.s.high >= mPsg->tonCLoW()) ///< @todo check this !!!
        {
            mTonCounterC.s.high = 0;
            mTonC ^= 1;
        }
        mNoiseCounter.s.high++;
        if ((!(mNoiseCounter.s.high & 1)) &&
            (mNoiseCounter.s.high >= (mPsg->noise() << 1)))
        {
            mNoiseCounter.s.high = 0;
            mNoise.seed = (((((mNoise.seed >> 13) ^ (mNoise.seed >> 16)) & 1) ^ 1) |
                           mNoise.seed << 1) &
                          0x1ffff;
        }
        if (!mEnvelopeCounter.s.high)
        {
            (this->*mCaseEnvType)();
        }
        mEnvelopeCounter.s.high++;
        if (mEnvelopeCounter.s.high >= mPsg->envelopeLoW()) ///< @todo check this !!!
        {
            mEnvelopeCounter.s.high = 0;
        }
    }

    inline void Sound::synthesizerMixerQ()
    {
        int LevL, LevR, k;

        LevL = mTapeLevel ? mLevelTape : 0; // start with the tape signal
        /*
        if (snd_pp_device)
        {
            LevL += mLevelPP[cpc.printer_port];
        }
        */

        LevR = LevL;
        if (mTonEnA)
        {
            if ((!mEnvelopeEnA) || (mPsg->tonALoW() > 4)) ///< @todo check this !!!
            {
                k = mTonA;
            }
            else
            {
                k = 1;
            }
        }
        else
        {
            k = 1;
        }
        if (mNoiseEnA)
        {
            k &= mNoise.s.val;
        }
        if (k)
        {
            if (mEnvelopeEnA)
            {
                LevL += mLevelAL[mPsg->amplitudeA() * 2 + 1];
                LevR += mLevelAR[mPsg->amplitudeA() * 2 + 1];
            }
            else
            {
                LevL += mLevelAL[mPsg->amplitudeEnv()];
                LevR += mLevelAR[mPsg->amplitudeEnv()];
            }
        }

        if (mTonEnB)
        {
            if ((!mEnvelopeEnB) || (mPsg->tonBLoW() > 4)) ///< @todo check this !!!
            {
                k = mTonB;
            }
            else
            {
                k = 1;
            }
        }
        else
        {
            k = 1;
        }
        if (mNoiseEnB)
        {
            k &= mNoise.s.val;
        }
        if (k)
        {
            if (mEnvelopeEnB)
            {
                LevL += mLevelBL[mPsg->amplitudeB() * 2 + 1];
                LevR += mLevelBR[mPsg->amplitudeB() * 2 + 1];
            }
            else
            {
                LevL += mLevelBL[mPsg->amplitudeEnv()];
                LevR += mLevelBR[mPsg->amplitudeEnv()];
            }
        }

        if (mTonEnC)
        {
            if ((!mEnvelopeEnC) || (mPsg->tonCLoW() > 4)) ///< @todo check this !!!
            {
                k = mTonC;
            }
            else
            {
                k = 1;
            }
        }
        else
        {
            k = 1;
        }
        if (mNoiseEnC)
        {
            k &= mNoise.s.val;
        }
        if (k)
        {
            if (mEnvelopeEnC)
            {
                LevL += mLevelCL[mPsg->amplitudeC() * 2 + 1];
                LevR += mLevelCR[mPsg->amplitudeC() * 2 + 1];
            }
            else
            {
                LevL += mLevelCL[mPsg->amplitudeEnv()];
                LevR += mLevelCR[mPsg->amplitudeEnv()];
            }
        }

        mLeftChan += LevL;
        mRightChan += LevR;
    }

    void Sound::synthesizerStereo16()
    {
        int tickcounter = 0;
        while (mLoopCount.s.high)
        {
            synthesizerLogicQ();
            synthesizerMixerQ();
            tickcounter++;
            mLoopCount.s.high--;
        }
        mLoopCount.both += mLoopCountInit;
        tREGPAIR val;
        val.w.l = mLeftChan / tickcounter;
        val.w.h = mRightChan / tickcounter;
        *(tDWORD *)mSndBufferPtr = val.d; // @todo check this,write to mixing buffer
                                         // ########################
        mSndBufferPtr += 4;
        mLeftChan = 0;
        mRightChan = mLeftChan;
        if (mSndBufferPtr >= mSndBufferEnd)
        {
            mSndBufferPtr = mSndBuffer;
            mBufferFull = true;
        }
    }

    void Sound::synthesizerStereo8()
    {
        int tickcounter = 0;
        while (mLoopCount.s.high)
        {
            synthesizerLogicQ();
            synthesizerMixerQ();
            tickcounter++;
            mLoopCount.s.high--;
        }
        mLoopCount.both += mLoopCountInit;
        tREGPAIR val;
        val.b.l = 128 + mLeftChan / tickcounter;
        val.b.h = 128 + mRightChan / tickcounter;
        setBufferPtrW(val.w.l); // @todo check this,write to mixing buffer
        mSndBufferPtr += 2;
        mLeftChan = 0;
        mRightChan = mLeftChan;
        if (mSndBufferPtr >= mSndBufferEnd)
        {
            mSndBufferPtr = mSndBuffer;
            mBufferFull = true;
        }
    }

    inline void Sound::synthesizerMixerQMono()
    {
        int Lev, k;

        Lev = mTapeLevel ? mLevelTape : 0; // start with the tape signal
        if (device())
        {
            // @todo fix this: Lev += mLevelPP[cpc.printer_port];
        }

        if (mTonEnA)
        {
            if ((!mEnvelopeEnA) || (mPsg->tonALoW() > 4)) ///< @todo check this !!!
            {
                k = mTonA;
            }
            else
            {
                k = 1;
            }
        }
        else
        {
            k = 1;
        }
        if (mNoiseEnA)
        {
            k &= mNoise.s.val;
        }
        if (k)
        {
            if (mEnvelopeEnA)
            {
                Lev += mLevelAL[mPsg->amplitudeA() * 2 + 1];
            }
            else
            {
                Lev += mLevelAL[mPsg->amplitudeEnv()];
            }
        }

        if (mTonEnB)
        {
            if ((!mEnvelopeEnB) || (mPsg->tonBLoW() > 4)) ///< @todo check this !!!
            {
                k = mTonB;
            }
            else
            {
                k = 1;
            }
        }
        else
        {
            k = 1;
        }
        if (mNoiseEnB)
        {
            k &= mNoise.s.val;
        }
        if (k)
        {
            if (mEnvelopeEnB)
            {
                Lev += mLevelBL[mPsg->amplitudeB() * 2 + 1];
            }
            else
            {
                Lev += mLevelBL[mPsg->amplitudeEnv()];
            }
        }

        if (mTonEnC)
        {
            if ((!mEnvelopeEnC) || (mPsg->tonCLo() > 4)) ///< @todo check this !!!
            {
                k = mTonC;
            }
            else
            {
                k = 1;
            }
        }
        else
        {
            k = 1;
        }
        if (mNoiseEnC)
        {
            k &= mNoise.s.val;
        }
        if (k)
        {
            if (mEnvelopeEnC)
            {
                Lev += mLevelCL[mPsg->amplitudeC() * 2 + 1];
            }
            else
            {
                Lev += mLevelCL[mPsg->amplitudeEnv()];
            }
        }

        mLeftChan += Lev;
    }

    void Sound::synthesizerMono16()
    {
        int tickcounter = 0;
        while (mLoopCount.s.high)
        {
            synthesizerLogicQ();
            synthesizerMixerQMono();
            tickcounter++;
            mLoopCount.s.high--;
        }
        mLoopCount.both += mLoopCountInit;
        setBufferPtrW(mLeftChan /
                      tickcounter); // @todo check this,write to mixing buffer
        mSndBufferPtr += 2;
        mLeftChan = 0;
        if (mSndBufferPtr >= mSndBufferEnd)
        {
            mSndBufferPtr = mSndBuffer;
            mBufferFull = true;
        }
    }

    void Sound::synthesizerMono8()
    {
        int tickcounter = 0;
        while (mLoopCount.s.high)
        {
            synthesizerLogicQ();
            synthesizerMixerQMono();
            tickcounter++;
            mLoopCount.s.high--;
        }
        mLoopCount.both += mLoopCountInit;
        setBufferPtrU(128 +
                      mLeftChan /
                          tickcounter); // @todo check this,write to mixing buffer
        mSndBufferPtr++;
        mLeftChan = 0;
        if (mSndBufferPtr >= mSndBufferEnd)
        {
            mSndBufferPtr = mSndBuffer;
            mBufferFull = true;
        }
    }

    void Sound::calculateLevelTables()
    {
        int i, b, l, r;
        int mIndexA, mIndexB, mIndexC;
        double k;

        mIndexA = mIndexAL;
        mIndexB = mIndexBL;
        mIndexC = mIndexCL;
        l = mIndexA + mIndexB + mIndexC;
        r = mIndexAR + mIndexBR + mIndexCR;
        if (stereo())
        {
            if (l < r)
            {
                l = r;
            }
        }
        else
        {
            l += r;
            mIndexA += mIndexAR;
            mIndexB += mIndexBR;
            mIndexC += mIndexCR;
        }
        if (l == 0)
        {
            l++;
        }
        if (!bits())
        { // 8 bits per sample?
            r = 127;
        }
        else
        {
            r = 32767;
        }
        l = 255 * r / l;
        for (i = 0; i < 16; i++)
        {
            b = (int)rint(mIndexA / 255.0 * mAmplitudesAY[i]);
            b = (int)rint(b / 65535.0 * l);
            mLevelAL[i * 2] = b;
            mLevelAL[i * 2 + 1] = b;
            b = (int)rint(mIndexAR / 255.0 * mAmplitudesAY[i]);
            b = (int)rint(b / 65535.0 * l);
            mLevelAR[i * 2] = b;
            mLevelAR[i * 2 + 1] = b;
            b = (int)rint(mIndexB / 255.0 * mAmplitudesAY[i]);
            b = (int)rint(b / 65535.0 * l);
            mLevelBL[i * 2] = b;
            mLevelBL[i * 2 + 1] = b;
            b = (int)rint(mIndexBR / 255.0 * mAmplitudesAY[i]);
            b = (int)rint(b / 65535.0 * l);
            mLevelBR[i * 2] = b;
            mLevelBR[i * 2 + 1] = b;
            b = (int)rint(mIndexC / 255.0 * mAmplitudesAY[i]);
            b = (int)rint(b / 65535.0 * l);
            mLevelCL[i * 2] = b;
            mLevelCL[i * 2 + 1] = b;
            b = (int)rint(mIndexCR / 255.0 * mAmplitudesAY[i]);
            b = (int)rint(b / 65535.0 * l);
            mLevelCR[i * 2] = b;
            mLevelCR[i * 2 + 1] = b;
        }
        k = exp(volume() * log(2) / mPreAmpMax) - 1;
        for (i = 0; i < 32; i++)
        {
            mLevelAL[i] = (int)rint(mLevelAL[i] * k);
            mLevelAR[i] = (int)rint(mLevelAR[i] * k);
            mLevelBL[i] = (int)rint(mLevelBL[i] * k);
            mLevelBR[i] = (int)rint(mLevelBR[i] * k);
            mLevelCL[i] = (int)rint(mLevelCL[i] * k);
            mLevelCR[i] = (int)rint(mLevelCR[i] * k);
        }
        if (!bits())
        { // 8 bits per sample?
            mLevelTape = -(int)rint((TAPE_VOLUME / 2) * k);
        }
        else
        {
            mLevelTape = -(int)rint((TAPE_VOLUME * 128) * k);
        }
        for (i = 0, b = 255; i < 256; i++)
        { // calculate the 256 levels of the Digiblaster/Soundplayer
            mLevelPP[i] = -(int)rint(((b << 8) / 65535.0 * l) * k);
            b--;
        }
    }

    void Sound::resetAYChipEmulation()
    {
        mTonCounterA.both = 0;
        mTonCounterB.both = 0;
        mTonCounterC.both = 0;
        mNoiseCounter.both = 0;
        mEnvelopeCounter.both = 0;
        mTonA = 0;
        mTonB = 0;
        mTonC = 0;
        mLeftChan = 0;
        mRightChan = 0;
        mNoise.seed = 0xffff;
    }

    void Sound::initAYCounterVars()
    {
        mCycleCountInit.both = (tINT64)rint(
            (4000000 * ((cpc.speed() * 25) / 100.0)) / mFreqTable[playbackRate()] *
            4294967296.0); // number of Z80 cycles per sample

        mLoopCountInit = (tINT64)rint(
            1000000.0 / (4000000.0 * ((cpc.speed() * 25) / 100.0)) / 8.0 *
            mCycleCountInit.both); // number of AY counter increments per sample
        mLoopCount.both = mLoopCountInit;
    }

    void Sound::initAY()
    {
        mIndexAL = 255;
        mIndexAR = 13;
        mIndexBL = 170;
        mIndexBR = 170;
        mIndexCL = 13;
        mIndexCR = 255;
        mPreAmpMax = 100;

        calculateLevelTables();
        initAYCounterVars();
        resetAYChipEmulation();

        if (stereo()) // stereo mode?
        {
            if (bits())
                setSynthesizer(&Sound::synthesizerStereo16); // 16 bits per sample?
            else
                setSynthesizer(&Sound::synthesizerStereo8); // 8 bits
        }
        else // mono
        {
            if (bits())
                setSynthesizer(&Sound::synthesizerMono16); // 16 bits per sample?
            else
                setSynthesizer(&Sound::synthesizerMono8); // 8 bits
        }
    }

} // cpc