// // minidexed.cpp // // MiniDexed - Dexed FM synthesizer for bare metal Raspberry Pi // Copyright (C) 2022 The MiniDexed Team // // 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 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 General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // #include "minidexed.h" #include #include #include LOGMODULE ("minidexed"); CMiniDexed::CMiniDexed (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOManager *pGPIOManager) : CDexedAdapter (CConfig::MaxNotes, pConfig->GetSampleRate ()), m_pConfig (pConfig), m_UI (this, pGPIOManager, pConfig), m_PCKeyboard (this), m_SerialMIDI (this, pInterrupt, pConfig), m_bUseSerial (false), m_GetChunkTimer ("GetChunk", 1000000U * pConfig->GetChunkSize ()/2 / pConfig->GetSampleRate ()), m_bProfileEnabled (m_pConfig->GetProfileEnabled ()) { for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) { m_pMIDIKeyboard[i] = new CMIDIKeyboard (this, pConfig, i); assert (m_pMIDIKeyboard[i]); } }; bool CMiniDexed::Initialize (void) { if (!m_UI.Initialize ()) { return false; } m_SysExFileLoader.Load (); if (m_SerialMIDI.Initialize ()) { LOGNOTE ("Serial MIDI interface enabled"); m_bUseSerial = true; } activate (); ProgramChange (0); setTranspose (24); return true; } void CMiniDexed::Process (bool bPlugAndPlayUpdated) { for (unsigned i = 0; i < CConfig::MaxUSBMIDIDevices; i++) { assert (m_pMIDIKeyboard[i]); m_pMIDIKeyboard[i]->Process (bPlugAndPlayUpdated); } m_PCKeyboard.Process (bPlugAndPlayUpdated); if (m_bUseSerial) { m_SerialMIDI.Process (); } m_UI.Process (); if (m_bProfileEnabled) { m_GetChunkTimer.Dump (); } } CSysExFileLoader *CMiniDexed::GetSysExFileLoader (void) { return &m_SysExFileLoader; } void CMiniDexed::BankSelectLSB (unsigned nBankLSB) { if (nBankLSB > 127) { return; } m_SysExFileLoader.SelectVoiceBank (nBankLSB); m_UI.BankSelected (nBankLSB); } void CMiniDexed::ProgramChange (unsigned nProgram) { if (nProgram > 31) { return; } uint8_t Buffer[156]; m_SysExFileLoader.GetVoice (nProgram, Buffer); loadVoiceParameters (Buffer); m_UI.ProgramChanged (nProgram); } //// PWM ////////////////////////////////////////////////////////////////////// CMiniDexedPWM::CMiniDexedPWM (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOManager *pGPIOManager) : CMiniDexed (pConfig, pInterrupt, pGPIOManager), CPWMSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), pConfig->GetChunkSize ()) { } bool CMiniDexedPWM::Initialize (void) { if (!CMiniDexed::Initialize ()) { return false; } return Start (); } unsigned CMiniDexedPWM::GetChunk (u32 *pBuffer, unsigned nChunkSize) { if (m_bProfileEnabled) { m_GetChunkTimer.Start (); } unsigned nResult = nChunkSize; int16_t SampleBuffer[nChunkSize/2]; getSamples (nChunkSize/2, SampleBuffer); for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer { s32 nSample = SampleBuffer[i++]; nSample += 32768; nSample *= GetRangeMax()/2; nSample /= 32768; *pBuffer++ = nSample; // 2 stereo channels *pBuffer++ = nSample; } if (m_bProfileEnabled) { m_GetChunkTimer.Stop (); } return nResult; }; //// I2S ////////////////////////////////////////////////////////////////////// CMiniDexedI2S::CMiniDexedI2S (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOManager *pGPIOManager, CI2CMaster *pI2CMaster) : CMiniDexed (pConfig, pInterrupt, pGPIOManager), CI2SSoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), pConfig->GetChunkSize (), false, pI2CMaster, pConfig->GetDACI2CAddress ()) { } bool CMiniDexedI2S::Initialize (void) { if (!CMiniDexed::Initialize ()) { return false; } return Start (); } unsigned CMiniDexedI2S::GetChunk (u32 *pBuffer, unsigned nChunkSize) { if (m_bProfileEnabled) { m_GetChunkTimer.Start (); } unsigned nResult = nChunkSize; int16_t SampleBuffer[nChunkSize/2]; getSamples (nChunkSize/2, SampleBuffer); for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer { s32 nSample = SampleBuffer[i++]; nSample <<= 8; *pBuffer++ = nSample; // 2 stereo channels *pBuffer++ = nSample; } if (m_bProfileEnabled) { m_GetChunkTimer.Stop (); } return nResult; }; //// HDMI ///////////////////////////////////////////////////////////////////// CMiniDexedHDMI::CMiniDexedHDMI (CConfig *pConfig, CInterruptSystem *pInterrupt, CGPIOManager *pGPIOManager) : CMiniDexed (pConfig, pInterrupt, pGPIOManager), CHDMISoundBaseDevice (pInterrupt, pConfig->GetSampleRate (), pConfig->GetChunkSize ()) { } bool CMiniDexedHDMI::Initialize (void) { if (!CMiniDexed::Initialize ()) { return false; } return Start (); } unsigned CMiniDexedHDMI::GetChunk(u32 *pBuffer, unsigned nChunkSize) { if (m_bProfileEnabled) { m_GetChunkTimer.Start (); } unsigned nResult = nChunkSize; int16_t SampleBuffer[nChunkSize/2]; getSamples (nChunkSize/2, SampleBuffer); unsigned nFrame = 0; for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer { s32 nSample = SampleBuffer[i++]; nSample <<= 8; nSample = ConvertIEC958Sample (nSample, nFrame); if (++nFrame == IEC958_FRAMES_PER_BLOCK) { nFrame = 0; } *pBuffer++ = nSample; // 2 stereo channels *pBuffer++ = nSample; } if (m_bProfileEnabled) { m_GetChunkTimer.Stop(); } return nResult; };