You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
MiniDexed/src/minidexed.cpp

357 lines
6.5 KiB

//
// minidexed.cpp
//
#include "minidexed.h"
#include "perftimer.h"
#include <circle/devicenameservice.h>
#include <stdio.h>
#define MIDI_DUMP
#define PROFILE
#define MIDI_NOTE_OFF 0b1000
#define MIDI_NOTE_ON 0b1001
#define MIDI_AFTERTOUCH 0xA0
#define MIDI_CONTROL_CHANGE 0xB0
#define MIDI_CC_BANK_SELECT_MSB 0 // TODO: not supported
#define MIDI_CC_BANK_SELECT_LSB 32
#define MIDI_PROGRAM_CHANGE 0xC0
#define MIDI_PITCH_BEND 0xE0
CMiniDexed *CMiniDexed::s_pThis = 0;
extern uint8_t voices_bank[1][32][156];
#ifdef PROFILE
CPerformanceTimer GetChunkTimer ("GetChunk", 1000000U * CHUNK_SIZE/2 / SAMPLE_RATE);
#endif
bool CMiniDexed::Initialize (void)
{
m_SysExFileLoader.Load ();
if (!m_Serial.Initialize(31250))
{
return false;
}
if (!m_LCD.Initialize ())
{
return FALSE;
}
m_bUseSerial = true;
activate();
return true;
}
void CMiniDexed::Process(boolean bPlugAndPlayUpdated)
{
#ifdef PROFILE
GetChunkTimer.Dump ();
#endif
if (m_pMIDIDevice != 0)
{
return;
}
if (bPlugAndPlayUpdated)
{
m_pMIDIDevice =
(CUSBMIDIDevice *) CDeviceNameService::Get ()->GetDevice ("umidi1", FALSE);
if (m_pMIDIDevice != 0)
{
m_pMIDIDevice->RegisterRemovedHandler (USBDeviceRemovedHandler);
m_pMIDIDevice->RegisterPacketHandler (MIDIPacketHandler);
return;
}
}
m_PCKeyboard.Process (bPlugAndPlayUpdated);
if (!m_bUseSerial)
{
return;
}
// Read serial MIDI data
u8 Buffer[20];
int nResult = m_Serial.Read (Buffer, sizeof Buffer);
if (nResult <= 0)
{
return;
}
// Process MIDI messages
// See: https://www.midi.org/specifications/item/table-1-summary-of-midi-message
for (int i = 0; i < nResult; i++)
{
u8 uchData = Buffer[i];
switch (m_nSerialState)
{
case 0:
MIDIRestart:
if ((uchData & 0xE0) == 0x80) // Note on or off, all channels
{
m_SerialMessage[m_nSerialState++] = uchData;
}
break;
case 1:
case 2:
if (uchData & 0x80) // got status when parameter expected
{
m_nSerialState = 0;
goto MIDIRestart;
}
m_SerialMessage[m_nSerialState++] = uchData;
if (m_nSerialState == 3) // message is complete
{
MIDIPacketHandler (0, m_SerialMessage, sizeof m_SerialMessage);
m_nSerialState = 0;
}
break;
default:
assert (0);
break;
}
}
}
void CMiniDexed::MIDIPacketHandler (unsigned nCable, u8 *pPacket, unsigned nLength)
{
assert (s_pThis != 0);
// The packet contents are just normal MIDI data - see
// https://www.midi.org/specifications/item/table-1-summary-of-midi-message
#ifdef MIDI_DUMP
switch (nLength)
{
case 1:
printf ("MIDI %u: %02X\n", nCable, (unsigned) pPacket[0]);
break;
case 2:
printf ("MIDI %u: %02X %02X\n", nCable,
(unsigned) pPacket[0], (unsigned) pPacket[1]);
break;
case 3:
printf ("MIDI %u: %02X %02X %02X\n", nCable,
(unsigned) pPacket[0], (unsigned) pPacket[1], (unsigned) pPacket[2]);
break;
}
#endif
if (pPacket[0] == MIDI_CONTROL_CHANGE)
{
if (pPacket[1] == MIDI_CC_BANK_SELECT_LSB)
{
if (pPacket[2] > 127)
{
return;
}
printf ("Select voice bank %u\n", (unsigned) pPacket[2]);
s_pThis->m_SysExFileLoader.SelectVoiceBank (pPacket[2]);
}
return;
}
if (pPacket[0] == MIDI_PROGRAM_CHANGE)
{
if(pPacket[1] > 31) {
return;
}
printf ("Loading voice %u\n", (unsigned) pPacket[1]);
uint8_t Buffer[156];
s_pThis->m_SysExFileLoader.GetVoice (pPacket[1], Buffer);
s_pThis->loadVoiceParameters(Buffer);
char buf_name[11];
memset(buf_name, 0, 11); // Initialize with 0x00 chars
s_pThis->setName(buf_name);
printf ("%s\n", buf_name);
// Print to optional HD44780 display
s_pThis->LCDWrite("\x1B[?25l"); // cursor off
CString String;
String.Format ("\n\r%i\n\r%s", pPacket[1], buf_name);
s_pThis->LCDWrite ((const char *) String);
return;
}
if (pPacket[0] == MIDI_PITCH_BEND)
{
s_pThis->setPitchbend((unsigned) pPacket[1]);
return;
}
if (nLength < 3)
{
return;
}
u8 ucStatus = pPacket[0];
//u8 ucChannel = ucStatus & 0x0F;
u8 ucType = ucStatus >> 4;
u8 ucKeyNumber = pPacket[1];
u8 ucVelocity = pPacket[2];
if (ucType == MIDI_NOTE_ON)
{
s_pThis->keydown(ucKeyNumber,ucVelocity);
}
else if (ucType == MIDI_NOTE_OFF)
{
s_pThis->keyup(ucKeyNumber);
}
}
void CMiniDexed::USBDeviceRemovedHandler (CDevice *pDevice, void *pContext)
{
if (s_pThis->m_pMIDIDevice == (CUSBMIDIDevice *) pDevice)
{
s_pThis->m_pMIDIDevice = 0;
}
}
bool CMiniDexedPWM::Initialize (void)
{
if (!CMiniDexed::Initialize())
{
return false;
}
return Start ();
}
unsigned CMiniDexedPWM::GetChunk(u32 *pBuffer, unsigned nChunkSize)
{
#ifdef PROFILE
GetChunkTimer.Start();
#endif
unsigned nResult = nChunkSize;
int16_t int16_buf[nChunkSize/2];
getSamples(nChunkSize/2, int16_buf);
for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer
{
s32 nSample = int16_buf[i++];
nSample += 32768;
nSample *= GetRangeMax()/2;
nSample /= 32768;
*pBuffer++ = nSample; // 2 stereo channels
*pBuffer++ = nSample;
}
#ifdef PROFILE
GetChunkTimer.Stop();
#endif
return(nResult);
};
bool CMiniDexedI2S::Initialize (void)
{
if (!CMiniDexed::Initialize())
{
return false;
}
return Start ();
}
unsigned CMiniDexedI2S::GetChunk(u32 *pBuffer, unsigned nChunkSize)
{
#ifdef PROFILE
GetChunkTimer.Start();
#endif
unsigned nResult = nChunkSize;
int16_t int16_buf[nChunkSize/2];
getSamples(nChunkSize/2, int16_buf);
for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer
{
s32 nSample = int16_buf[i++];
nSample <<= 8;
*pBuffer++ = nSample; // 2 stereo channels
*pBuffer++ = nSample;
}
#ifdef PROFILE
GetChunkTimer.Stop();
#endif
return(nResult);
};
bool CMiniDexedHDMI::Initialize (void)
{
if (!CMiniDexed::Initialize())
{
return false;
}
return Start ();
}
unsigned CMiniDexedHDMI::GetChunk(u32 *pBuffer, unsigned nChunkSize)
{
#ifdef PROFILE
GetChunkTimer.Start();
#endif
unsigned nResult = nChunkSize;
int16_t int16_buf[nChunkSize/2];
unsigned nFrame = 0;
getSamples(nChunkSize/2, int16_buf);
for (unsigned i = 0; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer
{
s32 nSample = int16_buf[i++];
nSample <<= 8;
nSample = ConvertIEC958Sample (nSample, nFrame);
if (++nFrame == IEC958_FRAMES_PER_BLOCK)
nFrame = 0;
*pBuffer++ = nSample; // 2 stereo channels
*pBuffer++ = nSample;
}
#ifdef PROFILE
GetChunkTimer.Stop();
#endif
return(nResult);
};
void CMiniDexed::LCDWrite (const char *pString)
{
m_LCD.Write (pString, strlen (pString));
}