Platform library for RETRO

Dependents:   RETRO_RickGame

Sound.h

Committer:
Architect
Date:
2015-03-01
Revision:
0:6f26c31d8573

File content as of revision 0:6f26c31d8573:

/*
 * (C) Copyright 2015 Valentin Ivanov. All rights reserved.
 *
 * This file is part of the RetroPlatform Library
 *
 * The RetroPlatform Library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 *
 * This library is inspired by Gamebuino Library (http://gamebuino.com)
 * from Aurélien Rodot. 
 */
#ifndef SOUND_H
#define SOUND_H

#include <mbed.h>

//commands
#define CMD_VOLUME 0
#define CMD_INSTRUMENT 1
#define CMD_SLIDE 2
#define CMD_ARPEGGIO 3
#define CMD_TREMOLO 4


#define NUM_CHANNELS 3 //number of sound channels (no more than 4)

#define VOLUME_GLOBAL_MAX 1

//7=instrument volume 9=note volume
#define VOLUME_CHANNEL_MAX 255/NUM_CHANNELS/VOLUME_GLOBAL_MAX/7/9

#define boolean bool

//A set of sequences
typedef struct {
    uint16_t *Data;
    uint8_t Cursor;
    bool IsPlaying;
} Track;

//A sequence of commands and notes
typedef struct {
    uint16_t *Data;
    bool Looping;
    uint16_t Cursor;
    bool IsPlaying;
} Sequence;

//A note of selected pitch, duration and volume
typedef struct {
    uint8_t Pitch;
    uint8_t Duration;
    int8_t  Volume;
    bool    IsPlaying;
} Note;

typedef struct {
    int8_t Counter;
    int8_t volumeSlideStepDuration;
    int8_t volumeSlideStepSize;
    uint8_t arpeggioStepDuration;
    int8_t arpeggioStepSize;
    uint8_t tremoloStepDuration;
    int8_t tremoloStepSize;

} Command;

typedef struct {
    uint16_t **SequenceSet;
    uint16_t **InstrumentSet;
    
    uint8_t Volume;
    uint8_t Count;
    bool    State;
    uint8_t HalfPeriod;
    uint8_t OutputVolume;
    uint8_t Output;
    bool    Noise;

    
    
    int8_t SequencePitch;
} AudioChannel;

typedef struct {
    uint16_t *Data;
    uint8_t Length;
    uint8_t Looping;
    uint16_t Cursor;
    uint8_t NextChange;
} Instrument;

class Sound {
    
    Ticker timer;
    PwmOut speaker;
    
    Track tracks[NUM_CHANNELS];
    Sequence sequences[NUM_CHANNELS];
    Command commands[NUM_CHANNELS];
    Instrument instruments[NUM_CHANNELS];
    
public:
    Sound();
    void initialize();
    
    void update()
    {
        updateTracks();
        updateSequences();
        updateNotes();
    }
    
    void playTrack(const uint16_t* track, uint8_t channel);
    void updateTrack(uint8_t channel);
    void stopTrack(uint8_t channel);
    void stopTracks() {
        for(uint8_t channel=0; channel<NUM_CHANNELS; channel++)
            stopTrack(channel);
    }
    void updateTracks() {
        for(uint8_t channel=0; channel<NUM_CHANNELS; channel++)
            updateTrack(channel);
    }    
    
    void changeSequenceSet(const uint16_t* const* patterns, uint8_t channel);
    
    bool isTrackPlaying(uint8_t channel)
    {
        return tracks[channel].IsPlaying;
    }
    
    void playSequence(const uint16_t* pattern, uint8_t channel);
    void changeInstrumentSet(const uint16_t* const* instruments, uint8_t channel);
    void updateSequence(uint8_t channel);
    void stopSequence(uint8_t channel);
    void stopSequences() {
        for(uint8_t channel=0; channel<NUM_CHANNELS; channel++)
            stopSequence(channel);
    }
    void updateSequences() {
        for(uint8_t channel=0; channel<NUM_CHANNELS; channel++)
            updateSequence(channel);
    }
    
    
    void playNote(uint8_t pitch, uint8_t duration, uint8_t channel);
    void updateNote(uint8_t i);
    void stopNote(uint8_t channel);
    void updateNotes() {
        for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++)
            updateNote(channel);
    }
    void stopNotes() {
        for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++)
            stopNote(channel);
    }    

    void command(uint8_t cmd, uint8_t X, int8_t Y, uint8_t channel);
    
    uint8_t outputPitch[NUM_CHANNELS];
    int8_t outputVolume[NUM_CHANNELS];

    void setVolume(int8_t volume);
    uint8_t getVolume();
    void setVolume(int8_t volume, uint8_t channel);
    uint8_t getVolume(uint8_t channel);
    
    
    uint8_t prescaler;

    void setChannelHalfPeriod(uint8_t channel, uint8_t halfPeriod);

    
    uint8_t globalVolume;
    uint8_t volumeMax;
    
#if (NUM_CHANNELS > 0)
    //tracks data
    uint16_t **patternSet[NUM_CHANNELS];
    int8_t patternPitch[NUM_CHANNELS];

    // pattern data
    uint16_t **instrumentSet[NUM_CHANNELS];

    // note data
    uint8_t notePitch[NUM_CHANNELS];
    uint8_t noteDuration[NUM_CHANNELS];
    int8_t noteVolume[NUM_CHANNELS];
    boolean notePlaying[NUM_CHANNELS];
      
    
    //current step data
    int8_t stepVolume[NUM_CHANNELS];
    uint8_t stepPitch[NUM_CHANNELS];
    
    uint8_t chanVolumes[NUM_CHANNELS];
    
    
    uint8_t _chanCount[NUM_CHANNELS]; //counts until the next change of the waveform
    boolean _chanState[NUM_CHANNELS]; //if the waveform is currently high or low
    uint8_t _chanHalfPeriod[NUM_CHANNELS]; //duration of half the period of the waveform
    uint8_t _chanOutputVolume[NUM_CHANNELS]; //amplitude of the outputted waveform
    uint8_t _chanOutput[NUM_CHANNELS]; //current value of the outputted waveform
    boolean _chanNoise[NUM_CHANNELS]; //if a random value should be added to the waveform to generate noise
    
#endif
    void updateOutput();
    
private:
    void generateOutput();    
};

#endif  /* SOUND_H */