PokittoLib is the library needed for programming the Pokitto DIY game console (www.pokitto.com)

Dependents:   YATTT sd_map_test cPong SnowDemo ... more

PokittoLib

Library for programming Pokitto hardware

How to Use

  1. Import this library to online compiler (see button "import" on the right hand side
  2. DO NOT import mbed-src anymore, a better version is now included inside PokittoLib
  3. Change My_settings.h according to your project
  4. Start coding!

POKITTO_HW/HWSound.cpp

Committer:
Pokitto
Date:
2019-12-25
Revision:
71:531419862202
Parent:
67:068fa6345036

File content as of revision 71:531419862202:

/**************************************************************************/
/*!
    @file     HWSound.cpp
    @author   Jonne Valola

    @section LICENSE

    Software License Agreement (BSD License)

    Copyright (c) 2016, Jonne Valola
    All rights reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions are met:
    1. Redistributions of source code must retain the above copyright
    notice, this list of conditions and the following disclaimer.
    2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions and the following disclaimer in the
    documentation and/or other materials provided with the distribution.
    3. Neither the name of the copyright holders nor the
    names of its contributors may be used to endorse or promote products
    derived from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY
    EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
    DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**************************************************************************/

#include "mbed.h"
#include "HWSound.h"
//#include "MCP4018.h"
#include "SoftwareI2C.h"
#include "Pokitto_settings.h"
#include "PokittoDisk.h"
#include "PokittoGlobs.h"
#include "Synth.h"
#include "timer_11u6x.h"
#include "clock_11u6x.h"
#include "HWLCD.h"
//#include "beat_11025.h"


Pokitto::Sound __shw;

using namespace Pokitto;

#ifndef POK_SIM
#if POK_ENABLE_SOUND
pwmout_t* obj = &audiopwm;
#endif
#endif

/** Sound Variables **/
#if (POK_STREAMING_MUSIC > 0)

#if POK_HIGH_RAM == HIGH_RAM_MUSIC
unsigned char *buffers[4] = {
    (unsigned char *) 0x20000000,
    (unsigned char *) 0x20000400,
    (unsigned char *) 0x20004000,
    (unsigned char *) 0x20004400
};
#else
    unsigned char buffers[4][BUFFER_SIZE];
#endif

    volatile int currentBuffer = 0, oldBuffer = 0;
    volatile int bufindex = 0, vol=1;
    volatile unsigned char * currentPtr;
    volatile unsigned char * endPtr;
    int8_t streamvol=3;
    uint32_t streamcounter=0;
    uint8_t streamstep=0;
#endif

#if POK_ENABLE_SOUND > 0
	pwmout_t Pokitto::audiopwm; // this way (instead of PwmOut class) pwm doesn't start screaming until it is initialized !
    //Ticker Pokitto::audio;

using namespace Pokitto;


/** stream output and status */
uint8_t Pokitto::streambyte,Pokitto::streamon;

uint8_t soundbuf[SBUFSIZE];
uint8_t Pokitto::HWvolume=0;
uint16_t soundbufindex;
uint8_t* soundbufptr;

bool volpotError=false; //test for broken MCP4018

uint16_t soundbyte;


#if POK_USE_DAC > 0
#if POK_BOARDREV == 1
 /** 2-layer board rev 1.3 **/
 DigitalOut dac0(P1_6);
 DigitalOut dac1(P1_0);
 DigitalOut dac2(P1_16);
 DigitalOut dac3(P0_19);
 DigitalOut dac4(P0_17);
 DigitalOut dac5(P1_12);
 DigitalOut dac6(P1_15);
 DigitalOut dac7(P1_8);
#else
 /** 4-layer board rev 2.1 **/
 DigitalOut dac0(P1_28);
 DigitalOut dac1(P1_29);
 DigitalOut dac2(P1_30);
 DigitalOut dac3(P1_31);
 /* has daniel made a mistake ?*/
 DigitalOut dac4(P2_20);
 DigitalOut dac5(P2_21);
 DigitalOut dac6(P2_22);
 DigitalOut dac7(P2_23);

 DigitalOut amp(P1_17);

#endif // POK_BOARDREV
#endif // POK_USE_DAC

#if POK_BOARDREV == 2
//MCP4018 volpot(P0_5,P0_4);

/**
 * @brief	Handle interrupt from 32-bit timer 0
 * @return	Nothing
 */

uint32_t p=0;
uint8_t pixx=0, pixy=0;

extern "C" void TIMER32_0_IRQHandler(void)
{
	if (Chip_TIMER_MatchPending(LPC_TIMER32_0, 1)) {
		Chip_TIMER_ClearMatch(LPC_TIMER32_0, 1);
		//pokSoundBufferedIRQ();
		#if POK_GBSOUND > 0
    	/** GAMEBUINO SOUND **/
    	Pokitto::audio_IRQ();
    	#else
    	/** NOT GAMEBUINO SOUND **/
            #if POK_SOUND_BUFFERED
                pokSoundBufferedIRQ();
            #else
                pokSoundIRQ();
            #endif
		#endif
	}
}


void initHWvolumecontrol() {
    HWvolume=0;
   	volpotError=true;
    //if (volpot.put(HWvolume)) volpotError=true; //try if MCP4018 answers
    setHWvolume(discrete_vol_hw_levels[VOLUME_STARTUP>>5]);
}

int Pokitto::setHWvolume(uint8_t v) {
    HWvolume=v;
    if (HWvolume>127) HWvolume=127;
    //HWvolume = 0x7F&v;
    //if (!volpotError) return volpot.put(HWvolume); //use normal I2C
    /* fallback method for broken MCP4018 */
    SoftwareI2C swvolpot(P0_4, P0_5); //swapped SDA,SCL
    swvolpot.write(0x5e,HWvolume);
	return HWvolume;
}

uint8_t Pokitto::getHWvolume() {
    return HWvolume;
}

void Pokitto::changeHWvolume(int8_t v) {
    int temp  = HWvolume + v;
    if (temp < 0) temp = 0; //prevent volume "looparound" than can make a massive crack
    if (temp > 127) temp = 127;
    setHWvolume(temp);
}

 uint8_t Pokitto::ampIsOn() {
    return amp;
 }

 void Pokitto::ampEnable(uint8_t v) {
    if (v>1) v=1; // limit against funny values
    amp=v;
 }
#endif // POK_BOARDREV == 2

void Pokitto::dac_write(uint8_t value) {
    #if POK_USE_DAC > 0
    #if POK_BOARDREV == 1 // was 1
    if (value & 1) SET_DAC0 else CLR_DAC0;
    value >>= 1;
    if (value & 1) SET_DAC1 else CLR_DAC1;
    value >>= 1;
    if (value & 1) SET_DAC2 else CLR_DAC2;
    value >>= 1;
    if (value & 1) SET_DAC3 else CLR_DAC3;
    value >>= 1;
    if (value & 1) SET_DAC4 else CLR_DAC4;
    value >>= 1;
    if (value & 1) SET_DAC5 else CLR_DAC5;
    value >>= 1;
    if (value & 1) SET_DAC6 else CLR_DAC6;
    value >>= 1;
    if (value & 1) SET_DAC7 else CLR_DAC7;
    #else

    //val = value<<28; //lower 4 bits go higher - because port mask is used, no AND is needed to clear bits
    //val += value<<(15-4); //higher 4 bits go lower. No need to shift by 15 because bits are in the higher nibble
    /* daniel has made a mistake with ports */
    //val = ((value&0x70)<<(28-4)); //higher 4 bits go higher - because port mask is used, no AND is needed to clear bits
    //val += value<<(15); //lower 4 bits go lower. No need to shift by 15 because bits are in the higher nibble
    //SET_MASK_DAC;
    //LPC_GPIO_PORT->MPIN[1] = val; // write bits to port
    //CLR_MASK_DAC;
    /* fixed here */
    #define MASKED_DAC 0
    #if MASKED_DAC
    uint32_t val;
    val=value;
    SET_MASK_DAC_LO;
    LPC_GPIO_PORT->MPIN[1] = val<<28; // write lower 4 bits to port
    CLR_MASK_DAC_LO;
    SET_MASK_DAC_HI;
    LPC_GPIO_PORT->MPIN[2] = val<<(20-4); // write bits to port
    CLR_MASK_DAC_HI;
    #else
    if (value & 1) SET_DAC0 else CLR_DAC0;
    value >>= 1;
    if (value & 1) SET_DAC1 else CLR_DAC1;
    value >>= 1;
    if (value & 1) SET_DAC2 else CLR_DAC2;
    value >>= 1;
    if (value & 1) SET_DAC3 else CLR_DAC3;
    value >>= 1;
    if (value & 1) SET_DAC4 else CLR_DAC4;
    value >>= 1;
    if (value & 1) SET_DAC5 else CLR_DAC5;
    value >>= 1;
    if (value & 1) SET_DAC6 else CLR_DAC6;
    value >>= 1;
    if (value & 1) SET_DAC7 else CLR_DAC7;
    #endif //MASKED_DAC
    //CLR_MASK_DAC;
    #endif // BOARDREV
    #endif
}

void Pokitto::soundInit() {
    soundInit(false);
}

/** SOUND INIT **/
void Pokitto::soundInit(uint8_t reinit) {
    #ifdef XPERIMENTAL
    mbed::DigitalOut expr4(EXT4);
    expr4=0;
    #endif
    #if POK_ENABLE_SOUND > 0
    uint32_t timerFreq;
    #if POK_USE_PWM
    if (!reinit) {
    pwmout_init(&audiopwm,POK_AUD_PIN);
    pwmout_period_us(&audiopwm,POK_AUD_PWM_US); //was 31us
    pwmout_write(&audiopwm,0.1f);
    }
    #endif

    //#if POK_GBSOUND > 0
    /** GAMEBUINO SOUND **/
    //audio.attach_us(&audio_IRQ, 1000000/(POK_AUD_FREQ>>0));
    //#else
    /** NOT GAMEBUINO SOUND **/
    //audio.attach_us(&pokSoundBufferedIRQ, 1000000/(POK_AUD_FREQ>>0));
     /* Initialize 32-bit timer 0 clock */
	Chip_TIMER_Init(LPC_TIMER32_0);

    /* Timer rate is system clock rate */
	timerFreq = Chip_Clock_GetSystemClockRate();

	/* Timer setup for match and interrupt at TICKRATE_HZ */
	Chip_TIMER_Reset(LPC_TIMER32_0);

	/* Enable both timers to generate interrupts when time matches */
	Chip_TIMER_MatchEnableInt(LPC_TIMER32_0, 1);

    /* Setup 32-bit timer's duration (32-bit match time) */
	Chip_TIMER_SetMatch(LPC_TIMER32_0, 1, (timerFreq / POK_AUD_FREQ));

	/* Setup both timers to restart when match occurs */
	Chip_TIMER_ResetOnMatchEnable(LPC_TIMER32_0, 1);

	/* Start both timers */
	Chip_TIMER_Enable(LPC_TIMER32_0);

	/* Clear both timers of any pending interrupts */
    #define TIMER_32_0_IRQn 18
	NVIC_ClearPendingIRQ((IRQn_Type)TIMER_32_0_IRQn);

    /* Redirect IRQ vector - Jonne*/
    NVIC_SetVector((IRQn_Type)TIMER_32_0_IRQn, (uint32_t)&TIMER32_0_IRQHandler);

	/* Enable both timer interrupts */
	NVIC_EnableIRQ((IRQn_Type)TIMER_32_0_IRQn);
    //#endif // POK_GAMEBUINO_SUPPORT

    //emptySong();
    //emptyOscillators();
    //emptyBlocks();
    //emptyPatches();
    #ifdef TEST_SOUND
        testOsc();
    #endif // TEST_SOUND
    #if POK_BOARDREV == 2
        //initHWvolumecontrol();
    #endif

    #if POK_ENABLE_SYNTH
        emptyOscillators();
    #endif


    #endif // POK_ENABLE_SOUND

}


uint8_t Pokitto::streamPaused() {
    return !streamon;
}

void Pokitto::pauseStream() {
    streamon=0;
}

void Pokitto::playStream() {
    streamon=1;
}


void pokPauseStream() {
    streamon=0;
}

void pokPlayStream() {
    streamon=1;
}



inline void pokSoundBufferedIRQ() {
           uint32_t output = soundbuf[soundbufindex+=Pokitto::streamon];
           if (soundbufindex==SBUFSIZE) soundbufindex=0;
           //if (p==sizeof(beat_11025_raw)) p=0;
           //soundbuf[soundbufindex++] = output;
           //uint32_t t_on = (uint32_t)(((obj->pwm->MATCHREL0)*output)>>8); //cut out float
           //obj->pwm->MATCHREL1 = t_on;
           output *= discrete_vol_multipliers[discrete_vol];
           output >>= 8;
           dac_write(output);

           //setDRAMpoint(pixx, pixy);
}

inline void pokSoundIRQ() {
    #if POK_ENABLE_SOUND > 0
    //#define TICKY 0xFFFF //160
    //#define INCY 409
    uint32_t output=0;uint32_t op;
    //streamon=1;
    //if (test==TICKY) test=0;
    //if (test<(TICKY/2)) { tpin=1; pwmout_write(&audiopwm,(float)0/(float)255);}//dac_write(0);}
    //else {tpin=0; pwmout_write(&audiopwm,(float)255/(float)255);}//dac_write(64);}
    //test+=INCY;
    //return;
    #ifndef POK_SIM
        #if POK_USE_PWM
        pwmout_t* obj = &audiopwm;
        #endif
    #endif
    #if POK_STREAMING_MUSIC > 0
        #if POK_STREAMFREQ_HALVE
        streamstep = 1-streamstep;
        #else
        streamstep = 1;
        #endif // POK_STREAMFREQ_HALVE
        #ifndef PROJ_SDFS_STREAMING
        	streamon=1; // force enable stream
        #endif
        streamstep &= streamon; // streamon is used to toggle SD music streaming on and off
        if (streamstep) {
            output = (*currentPtr++);
	    if( Pokitto::Sound::sfxDataPtr != Pokitto::Sound::sfxEndPtr ){
			#ifdef PROJ_SDFS_STREAMING
				int32_t s = (int32_t(output) + int32_t(*Pokitto::Sound::sfxDataPtr++)) - 128;
			#else
				int32_t s = (127 + int32_t(*Pokitto::Sound::sfxDataPtr++)) - 128;
			#endif
			if( s < 0 ) s = 0;
			else if( s > 255 ) s = 255;
			output = s;
	    	}
    	if(streamvol && streamon) {
                output >>= 3-streamvol;
                streambyte = output;
            } else {
                streambyte = 0; // duty cycle
                output = 0;
            }
            if (currentPtr >= endPtr)
            {
            currentBuffer++;
            if (currentBuffer==4) currentBuffer=0;
            currentPtr = buffers[currentBuffer];
            endPtr = currentPtr + BUFFER_SIZE;
            }
        }
    #endif // POK_STREAMING_MUSIC

    /** DO ADDITIONAL SOUND PROCESSING (NOT STREAM) OF SOUND HERE **/

    #if POK_ENABLE_SYNTH
        /** if song is being played from sd **/
        if (playing) {
                notetick++;
                updatePlayback();
        }
        /** oscillators update **/
        osc1.count += osc1.cinc + (osc1.pitchbend); // counts to 65535 and overflows to zero WAS 8 !
        osc2.count += osc2.cinc + (osc2.pitchbend); // counts to 65535 and overflows to zero
        osc3.count += osc3.cinc + (osc3.pitchbend); // counts to 65535 and overflows to zero
        #if POK_ALT_MIXING > 0 // heaviest cpu load, recalculate envelopes on each cycle
        uint32_t o = 0;
        Marr[3]();
        Marr[2]();
        Marr[1]();
        if (tick==0) Marr[0]();
        #else
        Marr[tick](); // call mixing function
        #endif // ALT_MIXING
        --tick;

        /** mixing oscillator output **/

        op = (uint32_t) ((osc1.output)*(osc1.vol>>8))>>9;// >> 2 osc1.vol Marr;
        op += (uint32_t) ((osc2.output)*(osc2.vol>>8))>>9;// >> 2 osc1.vol Marr;
        op += (uint32_t) ((osc3.output)*(osc3.vol>>8))>>9;// >> 2 osc1.vol Marr;
        op *= discrete_vol_multipliers[discrete_vol];
        op >>= 8;
        output = op & 0xFF;

    #endif // POK_ENABLE_SYNTH

    #ifndef POK_SIM
    /** HARDWARE **/
        #if POK_ENABLE_SOUND > 0
            #if POK_STREAMING_MUSIC > 0
                /** sound is enabled, streaming is enabled */
                #if POK_STREAM_TO_DAC > 0
                    /** stream goes to DAC */
                    #if POK_USE_DAC > 0
                    uint32_t sbyte = streambyte;
                    sbyte *= discrete_vol_multipliers[discrete_vol];
                    sbyte >>= 8;
                    dac_write((uint8_t)sbyte); // duty cycle
                    #endif // POK_USE_DAC
                #else
                    /** stream goes to PWM */
                    if (streamstep) {
                            //pwmout_write(&audiopwm,(float)streambyte/(float)255);
                            #if POK_USE_PWM
                            uint32_t sbyte = streambyte;
                            sbyte *= discrete_vol_multipliers[discrete_vol];
                            sbyte >>= 8;
                            uint32_t t_on = (uint32_t)((((obj->pwm->MATCHREL0)*sbyte)>>8)); //cut out float
                            obj->pwm->MATCHREL1 = t_on;
                            #endif
                            //dac_write((uint8_t)streambyte); // duty cycle
                    }
                #endif // POK_STREAM_TO_DAC
            #endif // POK_STREAMING_MUSIC
            #if POK_STREAM_TO_DAC > 0
                /** synth goes to PWM */
                //pwmout_write(&audiopwm,(float)output/(float)255);
                #if POK_USE_PWM
                    op = output;
                    op *= discrete_vol_multipliers[discrete_vol];
                    op >>= 8;
                    uint32_t t_on = (uint32_t)((((obj->pwm->MATCHREL0)*op)>>8)); //cut out float
                    obj->pwm->MATCHREL1 = t_on;
                #endif
            #else // POK_STREAMING_MUSIC
                op = output;
                op *= discrete_vol_multipliers[discrete_vol];
                op >>= 8;
                dac_write((uint8_t)op); // SYNTH to DAC
            #endif
            soundbyte = (output+streambyte)>>1;
            soundbuf[soundbufindex++]=soundbyte;
            if (soundbufindex==256) soundbufindex=0;
        #endif //POK_ENABLE_SOUND
    #endif // HARDWARE
#endif //POK_ENABLE_SOUND
}


void Pokitto::updateSDAudioStream() {
    if (streamPaused()) return;

    #if POK_STREAMING_MUSIC > 0
    if (oldBuffer != currentBuffer) {
        if (currentBuffer==0) fileReadBytes(&buffers[3][0],BUFFER_SIZE);
        else if (currentBuffer==1) fileReadBytes(&buffers[0][0],BUFFER_SIZE);
        else if (currentBuffer==2) fileReadBytes(&buffers[1][0],BUFFER_SIZE);
        else fileReadBytes(&buffers[2][0],BUFFER_SIZE);
        oldBuffer = currentBuffer;
        streamcounter += BUFFER_SIZE;
    } else return;

    #ifndef POK_SIM
    if ( streamcounter > fs.fsize - (BUFFER_SIZE*6)) {
    #else
    if ( streamcounter > getFileLength() - (BUFFER_SIZE*6)) {
    #endif
        streamcounter=0;
        #if POK_STREAM_LOOP
        fileRewind();
        #else
        pokPauseStream();
        #endif
    }
    #endif
}


#endif // POK_ENABLE_SOUND