210 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include <fstream>
 | 
						|
#include <cstring>
 | 
						|
 | 
						|
#include "midi.h"
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
#include "cpu.h"
 | 
						|
#include "memory.h"
 | 
						|
#include "timing.h"
 | 
						|
#include "audio.h"
 | 
						|
 | 
						|
#include <SDL.h>
 | 
						|
#endif
 | 
						|
 | 
						|
 | 
						|
namespace Midi
 | 
						|
{
 | 
						|
    bool loadFile(const std::string& filepath, uint8_t* midiBuffer, int& midiBufferSize, GtMidiHdr* gtMidiHdr)
 | 
						|
    {
 | 
						|
        // Read
 | 
						|
        std::ifstream infile(filepath, std::ios::binary | std::ios::in);
 | 
						|
        if(!infile.is_open())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Midi::loadFile() : failed to open file '%s' for reading\n", filepath.c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        // Process header if required
 | 
						|
        uint16_t streamSize = 0;
 | 
						|
        if(gtMidiHdr)
 | 
						|
        {
 | 
						|
            infile.read((char *)gtMidiHdr, GTMIDI_HDR_SIZE);
 | 
						|
            if(infile.bad())
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Midi::loadFile() : read error of header in file '%s'\n", filepath.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            if(strncmp((char *)gtMidiHdr->_tag, GTMIDI_TAG_STRING, GTMIDI_TAG_SIZE) != 0)
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Midi::loadFile() : bad header tag in file '%s'\n", filepath.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
            streamSize = (gtMidiHdr->_hiSize <<8) + gtMidiHdr->_loSize;
 | 
						|
        }
 | 
						|
 | 
						|
        infile.read((char *)midiBuffer, MIDI_MAX_BUFFER_SIZE - 1);
 | 
						|
        if(infile.bad())
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Midi::loadFile() : read error of stream in file '%s'\n", filepath.c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        midiBufferSize = int(infile.gcount());
 | 
						|
        if(gtMidiHdr)
 | 
						|
        {
 | 
						|
            if(midiBufferSize != streamSize)
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Midi::loadFile() : buffer stream size %d does not match header stream size %d in file '%s'\n", midiBufferSize, streamSize, filepath.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            if(midiBufferSize < int(GTMIDI_STREAM_OFFSET))
 | 
						|
            {
 | 
						|
                fprintf(stderr, "Midi::loadFile() : malformed header in file '%s'\n", filepath.c_str());
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        if(midiBufferSize >= MIDI_MAX_BUFFER_SIZE)
 | 
						|
        {
 | 
						|
            fprintf(stderr, "Midi::loadFile() : midi data too large in file '%s'\n", filepath.c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
#ifndef STAND_ALONE
 | 
						|
    bool _hasVolume = false;
 | 
						|
 | 
						|
    uint8_t* _midiBase = nullptr;
 | 
						|
    uint8_t* _midiStream = nullptr;
 | 
						|
    uint8_t* _midiPaused = nullptr;
 | 
						|
 | 
						|
    uint8_t _midiDelay = 0;
 | 
						|
    uint16_t _midiStreamSize = 0;
 | 
						|
 | 
						|
 | 
						|
    uint8_t* getStream(void) {return _midiStream;}
 | 
						|
 | 
						|
    bool setStream(const std::string* filenamePtr, uint8_t* midiBuffer, uint16_t midiBufferSize)
 | 
						|
    {
 | 
						|
        if(midiBuffer == nullptr) return false;
 | 
						|
 | 
						|
        Audio::initialiseChannels();
 | 
						|
 | 
						|
        GtMidiHdr* gtMidiHdr = (GtMidiHdr*)midiBuffer;
 | 
						|
        if(strncmp((char *)gtMidiHdr->_tag, GTMIDI_TAG_STRING, GTMIDI_TAG_SIZE) != 0)
 | 
						|
        {
 | 
						|
            _midiBase = nullptr;
 | 
						|
            _midiStream = nullptr;
 | 
						|
            _midiPaused = nullptr;
 | 
						|
            if(filenamePtr) fprintf(stderr, "Midi::setStream() : malformed midi header in file '%s'\n", filenamePtr->c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        _hasVolume = bool(gtMidiHdr->_hasVolume);
 | 
						|
        _midiStreamSize = (gtMidiHdr->_hiSize <<8) + gtMidiHdr->_loSize;
 | 
						|
        if(_midiStreamSize == 0  ||  _midiStreamSize != midiBufferSize - GTMIDI_STREAM_OFFSET)
 | 
						|
        {
 | 
						|
            _midiBase = nullptr;
 | 
						|
            _midiStream = nullptr;
 | 
						|
            _midiPaused = nullptr;
 | 
						|
            if(filenamePtr) fprintf(stderr, "Midi::setStream() : malformed midi header in file '%s'\n", filenamePtr->c_str());
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
 | 
						|
        _midiBase = midiBuffer + GTMIDI_STREAM_OFFSET;
 | 
						|
        _midiStream = midiBuffer + GTMIDI_STREAM_OFFSET;
 | 
						|
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    void stop(void)
 | 
						|
    {
 | 
						|
        _hasVolume = false;
 | 
						|
        _midiBase = nullptr;
 | 
						|
        _midiStream = nullptr;
 | 
						|
        _midiDelay = 0;
 | 
						|
        _midiStreamSize = 0;
 | 
						|
 | 
						|
        Audio::initialiseChannels();
 | 
						|
    }
 | 
						|
 | 
						|
    void pause(bool enable)
 | 
						|
    {
 | 
						|
        if(enable)
 | 
						|
        {
 | 
						|
            _midiPaused = _midiStream;
 | 
						|
            _midiStream = nullptr;
 | 
						|
        }
 | 
						|
        else
 | 
						|
        {
 | 
						|
            _midiStream = _midiPaused;
 | 
						|
            _midiPaused = nullptr;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    void play(void)
 | 
						|
    {
 | 
						|
        if(_midiStream == nullptr) return;
 | 
						|
 | 
						|
        // Tick audio
 | 
						|
        Cpu::setRAM(GIGA_SOUND_TIMER, 2);
 | 
						|
 | 
						|
        if(_midiDelay) _midiDelay--; 
 | 
						|
 | 
						|
        while(_midiDelay == 0)
 | 
						|
        {
 | 
						|
            if(_midiStream - _midiBase >= _midiStreamSize)
 | 
						|
            {
 | 
						|
                _midiStream = nullptr; //_midiBase;
 | 
						|
                break;
 | 
						|
            }
 | 
						|
 | 
						|
            uint8_t command = *_midiStream++;
 | 
						|
            if(command & 0x80)
 | 
						|
            {
 | 
						|
                // Start note
 | 
						|
                if((command & 0xF0) == MIDI_CMD_START_NOTE)
 | 
						|
                {
 | 
						|
                    // Midi note converted to Giga note
 | 
						|
                    uint8_t channel = command & GIGA_CHANNELS_MASK;  // spec supports up to 16 channels, Gigatron supports 4
 | 
						|
                    uint16_t note = *_midiStream++;
 | 
						|
                    note = (note - 11) * 2;
 | 
						|
                    note = Cpu::getROM16(note + 0x0900, 1);
 | 
						|
                    Cpu::setRAM16(GIGA_CH0_KEY_L + channel*GIGA_CHANNEL_OFFSET, note);
 | 
						|
 | 
						|
                    // Midi volume
 | 
						|
                    if(_hasVolume)
 | 
						|
                    {
 | 
						|
                        uint8_t volume = *_midiStream++;
 | 
						|
                        Cpu::setRAM(GIGA_CH0_WAV_A + channel*GIGA_CHANNEL_OFFSET, volume);
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                // Stop note
 | 
						|
                else if((command & 0xF0) == MIDI_CMD_STOP_NOTE)
 | 
						|
                {
 | 
						|
                    uint8_t channel = command & GIGA_CHANNELS_MASK;  // spec supports up to 16 channels, Gigatron supports 4
 | 
						|
                    Cpu::setRAM16(GIGA_CH0_KEY_L + channel*GIGA_CHANNEL_OFFSET, 0x0000);
 | 
						|
                }
 | 
						|
                // New segment address
 | 
						|
                else if((command & 0xF0) == MIDI_CMD_JMP_SEG)
 | 
						|
                {
 | 
						|
                    uint16_t segment = *_midiStream++;
 | 
						|
                    segment |= HI_MASK((*_midiStream++) <<8);
 | 
						|
                    _midiStream = (uint8_t*)(uintptr_t)segment;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            // Delay n*16.66666667 milliseconds where n = 8bit value
 | 
						|
            else
 | 
						|
            {
 | 
						|
                _midiDelay = command;
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
#endif
 | 
						|
}
 |