12-polyphonic "chiptune" MIDI synthesizer for LPC1768 (Standalone version)

Dependencies:   ClockControl PowerControl mbed

GeminiCore.cpp

Committer:
kayekss
Date:
2014-12-23
Revision:
6:cda45a5e723e
Parent:
4:b2423ad4b248

File content as of revision 6:cda45a5e723e:

#include "mbed.h"
#include "GeminiCore.h"

/** Constructor of class GeminiCore */
GeminiCore::GeminiCore(uint8_t numInstruments, uint16_t samplingRate) {
    this->numInstruments = numInstruments;
    
    // Instantiate instrument list
    this->instrumentList = new Instrument[numInstruments];
    for (uint8_t i = 0; i < numInstruments; i++) {
        this->instrumentList[i].enable();
        this->instrumentList[i].setSamplingRate(samplingRate);
        this->instrumentList[i].setWave(Wavetable::waveDefList[4]);
    }
    
    // Allocate bytes to preserve previous samples
    prevSample = new uint8_t[numInstruments];
    
    // Initialize random seeds
    this->x8 = 77u;
    this->x16 = 5501u;
}

/** Destructor of class GeminiCore */
GeminiCore::~GeminiCore() {
    delete[] this->instrumentList;
    delete[] this->prevSample;
}

/** Make a sample - should be called periodically depending on the sampling rate */
uint16_t GeminiCore::makeSample(note_t* noteSent) {
    Instrument* iThInst;
    Wavetable::wave_t iThWave;
    int32_t     sample;
    int32_t     iThSample;
    uint16_t    phasePrev;
    uint32_t    duration;
    uint8_t     decayRatio;
    
    sample = 0;
    for (uint8_t i = 0; i < numInstruments; i++) {
        iThInst = &instrumentList[i];
        iThWave = iThInst->getWave();
        
        // Do nothing if the instrument is disabled or not sounding
        if (!iThInst->isEnable() || iThInst->getFrequency() == 0) {
            continue;
        }
        
        duration = iThInst->getDuration();
        if (duration >> iThWave.decaySpeed >= 255 - iThWave.sustainLevel) {
            decayRatio = iThWave.sustainLevel;
        } else {
            decayRatio = 255 - (duration >> iThWave.decaySpeed);
        }
        // Skip/deallocate note when it is completely decayed
        if (decayRatio == 0) {
            noteSent[i].noteOnMs = 0;
            noteSent[i].channel = 0;
            noteSent[i].noteNumber = 0;
            noteSent[i].velocity = 0;
            iThInst->setFrequency(0);
            continue;
        }
        
        switch (iThWave.wavetype) {
        case Wavetable::Noise:
            phasePrev = iThInst->getPhase();
            iThInst->advancePhase();
            
            // If necessary, generate new random value
            if ((iThInst->getPhase() >> 8) - (phasePrev >> 8) > 0) {
                prevSample[i] = rand16() >> 8;
            }
            iThSample = prevSample[i];
            break;
        case Wavetable::LowPeriodNoise:
            phasePrev = iThInst->getPhase();
            iThInst->advancePhase();
            
            // If necessary, generate new random value
            if ((iThInst->getPhase() >> 8) - (phasePrev >> 8) > 0) {
                prevSample[i] = rand8();
            }
            iThSample = prevSample[i];
            break;
        default:
            iThInst->advancePhase();
            iThSample = Wavetable::waveTableList[iThWave.wavetype][iThInst->getPhase() >> 8] - 128;
        }
        sample += iThSample * iThInst->getMasterVolume() * decayRatio;
    }
    sample >>= 10;
    if (sample >  32767) sample =  32767;
    if (sample < -32768) sample = -32768;
    return (uint16_t) (sample + 32768);
}

/** Enable i-th Instrument */
bool GeminiCore::enable(uint8_t i) {
    Instrument* iThInst = NULL;
    
    if (i >= numInstruments || instrumentList == NULL) {
        return false;
    }
    iThInst = &instrumentList[i];
    if (iThInst == NULL) {
        return false;
    }
    
    iThInst->enable();
    return true;
}

/** Disable i-th Instrument */
bool GeminiCore::disable(uint8_t i) {
    Instrument* iThInst = NULL;
    
    if (i >= numInstruments || instrumentList == NULL) {
        return false;
    }
    iThInst = &instrumentList[i];
    if (iThInst == NULL) {
        return false;
    }
    
    iThInst->disable();
    return true;
}

/** Note on i-th Instrument */
bool GeminiCore::noteOn(uint8_t i, uint16_t frequency, uint8_t velocity) {
    Instrument* iThInst = NULL;
    
    if (i >= numInstruments || instrumentList == NULL) {
        return false;
    }
    iThInst = &instrumentList[i];
    if (iThInst == NULL) {
        return false;
    }
    
    iThInst->resetDuration();
    iThInst->setFrequency(frequency);
    iThInst->setVelocity(velocity);
    return true;
}

/** Note off i-th Instrument */
bool GeminiCore::noteOff(uint8_t i) {
    Instrument* iThInst = NULL;
    
    if (i >= numInstruments || instrumentList == NULL) {
        return false;
    }
    iThInst = &instrumentList[i];
    if (iThInst == NULL) {
        return false;
    }
    
    iThInst->setFrequency(0); 
    return true;
}

/** Set volume (0..127) to i-th Instrument */
bool GeminiCore::volume(uint8_t i, uint8_t vo) {
    Instrument* iThInst = NULL;
    
    if (i >= numInstruments || instrumentList == NULL) {
        return false;
    }
    iThInst = &instrumentList[i];
    if (iThInst == NULL) {
        return false;
    }
    
    iThInst->setVolume(vo << 1);  // 0..127 => 0..254
    return true;
}

/** Set expression (0..127) to i-th Instrument */
bool GeminiCore::expression(uint8_t i, uint8_t ex) {
    Instrument* iThInst = NULL;
    
    if (i >= numInstruments || instrumentList == NULL) {
        return false;
    }
    iThInst = &instrumentList[i];
    if (iThInst == NULL) {
        return false;
    }
    
    iThInst->setExpression(ex << 1);  // 0..127 => 0..254
    return true;
}

bool GeminiCore::pitchBend(uint8_t i, int16_t pb) {
    // @TODO
    return true;
}

/** Set wave parameters to i-th Instrument */
bool GeminiCore::setWave(uint8_t i, Wavetable::wave_t w) {
    Instrument* iThInst = NULL;
    
    if (i >= numInstruments || instrumentList == NULL) {
        return false;
    }
    iThInst = &instrumentList[i];
    if (iThInst == NULL) {
        return false;
    }
    
    iThInst->setWave(w);
    return true;
}

/** Get instrument list */
Instrument* GeminiCore::getInstrumentList() {
    return this->instrumentList;
}

/** Generate an 8-bit random number */
uint8_t GeminiCore::rand8() {
    x8 ^= x8 << 7;
    x8 ^= x8 >> 5;
    x8 ^= x8 << 3;
    return x8;
}

/** Generate a 16-bit random number */
uint16_t GeminiCore::rand16() {
    x16 ^= x16 << 13;
    x16 ^= x16 >> 9;
    x16 ^= x16 << 7;
    return x16;
}