mirror of https://github.com/probonopd/MiniDexed
Move RPi code from Synth_Dexed to MiniDexed, thanks @rsta2
Move the Raspberry Pi specific code from the classes AudioSynthDexed* from the Synth_Dexed project to the MiniDexed project https://github.com/probonopd/MiniDexed/issues/8#issuecomment-1048590076pull/29/head
parent
1648a06612
commit
08be352d24
@ -1 +1 @@ |
||||
Subproject commit cdaf1f6cdeb38e9f5cfb6f2d8bb93d7933153ab6 |
||||
Subproject commit 86dba9826c21295e72a8a38b17e1f2c001579310 |
@ -0,0 +1,232 @@ |
||||
//
|
||||
// minidexed.cpp
|
||||
//
|
||||
#include "minidexed.h" |
||||
#include <circle/devicenameservice.h> |
||||
|
||||
#define MIDI_NOTE_OFF 0b1000 |
||||
#define MIDI_NOTE_ON 0b1001 |
||||
|
||||
CMiniDexed *CMiniDexed::s_pThis = 0; |
||||
|
||||
bool CMiniDexed::Initialize (void) |
||||
{ |
||||
if (!m_Serial.Initialize(31250)) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
m_bUseSerial = true; |
||||
|
||||
activate(); |
||||
|
||||
return true; |
||||
} |
||||
|
||||
void CMiniDexed::Process(boolean bPlugAndPlayUpdated) |
||||
{ |
||||
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
|
||||
|
||||
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) |
||||
{ |
||||
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; |
||||
} |
||||
return(nResult); |
||||
}; |
||||
|
||||
bool CMiniDexedI2S::Initialize (void) |
||||
{ |
||||
if (!CMiniDexed::Initialize()) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
return Start (); |
||||
} |
||||
|
||||
unsigned CMiniDexedI2S::GetChunk(u32 *pBuffer, unsigned nChunkSize) |
||||
{ |
||||
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; |
||||
} |
||||
return(nResult); |
||||
}; |
||||
|
||||
bool CMiniDexedHDMI::Initialize (void) |
||||
{ |
||||
if (!CMiniDexed::Initialize()) |
||||
{ |
||||
return false; |
||||
} |
||||
|
||||
return Start (); |
||||
} |
||||
|
||||
unsigned CMiniDexedHDMI::GetChunk(u32 *pBuffer, unsigned nChunkSize) |
||||
{ |
||||
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; |
||||
} |
||||
return(nResult); |
||||
}; |
@ -0,0 +1,97 @@ |
||||
//
|
||||
// minidexed.h
|
||||
//
|
||||
#ifndef _minidexed_h |
||||
#define _minidexed_h |
||||
|
||||
#include <synth_dexed.h> |
||||
#include <stdint.h> |
||||
#include <math.h> |
||||
#include <circle/interrupt.h> |
||||
#include <circle/i2cmaster.h> |
||||
#include <circle/usb/usbmidi.h> |
||||
#include <circle/serial.h> |
||||
#include <circle/types.h> |
||||
#include <circle/pwmsoundbasedevice.h> |
||||
#include <circle/i2ssoundbasedevice.h> |
||||
#include <circle/hdmisoundbasedevice.h> |
||||
#include "pckeyboard.h" |
||||
|
||||
#define SAMPLE_RATE 48000 |
||||
|
||||
#define CHUNK_SIZE 2048 |
||||
#define CHUNK_SIZE_HDMI (384 * 10) |
||||
|
||||
#define DAC_I2C_ADDRESS 0 // I2C slave address of the DAC (0 for auto probing)
|
||||
|
||||
class CMiniDexed : public Dexed |
||||
{ |
||||
public: |
||||
CMiniDexed(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt) |
||||
: Dexed(max_notes,(int)sample_rate), |
||||
m_pMIDIDevice (0), |
||||
m_PCKeyboard (this), |
||||
m_Serial (pInterrupt, TRUE), |
||||
m_bUseSerial (FALSE), |
||||
m_nSerialState (0) |
||||
{ |
||||
s_pThis = this; |
||||
}; |
||||
|
||||
virtual bool Initialize (void); |
||||
void Process(boolean bPlugAndPlayUpdated); |
||||
|
||||
protected: |
||||
static void MIDIPacketHandler (unsigned nCable, u8 *pPacket, unsigned nLength); |
||||
static void KeyStatusHandlerRaw (unsigned char ucModifiers, const unsigned char RawKeys[6]); |
||||
static void USBDeviceRemovedHandler (CDevice *pDevice, void *pContext); |
||||
CUSBMIDIDevice * volatile m_pMIDIDevice; |
||||
CPCKeyboard m_PCKeyboard; |
||||
CSerialDevice m_Serial; |
||||
boolean m_bUseSerial; |
||||
unsigned m_nSerialState; |
||||
u8 m_SerialMessage[3]; |
||||
|
||||
static CMiniDexed *s_pThis; |
||||
}; |
||||
|
||||
class CMiniDexedPWM : public CMiniDexed, public CPWMSoundBaseDevice |
||||
{ |
||||
public: |
||||
CMiniDexedPWM(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt) |
||||
: CMiniDexed(max_notes,(int)sample_rate, pInterrupt), |
||||
CPWMSoundBaseDevice (pInterrupt, sample_rate, CHUNK_SIZE) |
||||
{ |
||||
} |
||||
|
||||
bool Initialize (void); |
||||
unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); |
||||
}; |
||||
|
||||
class CMiniDexedI2S : public CMiniDexed, public CI2SSoundBaseDevice |
||||
{ |
||||
public: |
||||
CMiniDexedI2S(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt, CI2CMaster *pI2CMaster) |
||||
: CMiniDexed(max_notes,(int)sample_rate, pInterrupt), |
||||
CI2SSoundBaseDevice (pInterrupt, sample_rate, CHUNK_SIZE, FALSE, pI2CMaster, DAC_I2C_ADDRESS) |
||||
{ |
||||
} |
||||
|
||||
bool Initialize (void); |
||||
unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); |
||||
}; |
||||
|
||||
class CMiniDexedHDMI : public CMiniDexed, public CHDMISoundBaseDevice |
||||
{ |
||||
public: |
||||
CMiniDexedHDMI(uint8_t max_notes, uint16_t sample_rate, CInterruptSystem *pInterrupt) |
||||
: CMiniDexed(max_notes,(int)sample_rate, pInterrupt), |
||||
CHDMISoundBaseDevice (pInterrupt, sample_rate, CHUNK_SIZE_HDMI) |
||||
{ |
||||
} |
||||
|
||||
bool Initialize (void); |
||||
unsigned GetChunk (u32 *pBuffer, unsigned nChunkSize); |
||||
}; |
||||
|
||||
#endif |
@ -0,0 +1,186 @@ |
||||
//
|
||||
// pckeyboard.cpp
|
||||
//
|
||||
// MiniSynth Pi - A virtual analogue synthesizer for Raspberry Pi
|
||||
// Copyright (C) 2017-2020 R. Stange <rsta2@o2online.de>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
#include "pckeyboard.h" |
||||
#include "minidexed.h" |
||||
#include <circle/devicenameservice.h> |
||||
#include <circle/util.h> |
||||
#include <assert.h> |
||||
|
||||
struct TKeyInfo |
||||
{ |
||||
char KeyCode; // upper case letter or digit
|
||||
u8 KeyNumber; // MIDI number
|
||||
}; |
||||
|
||||
// KeyCode is valid for standard QWERTY keyboard
|
||||
static TKeyInfo KeyTable[] = |
||||
{ |
||||
{',', 72}, // C4
|
||||
{'M', 71}, // B4
|
||||
{'J', 70}, // A#4
|
||||
{'N', 69}, // A4
|
||||
{'H', 68}, // G#3
|
||||
{'B', 67}, // G3
|
||||
{'G', 66}, // F#3
|
||||
{'V', 65}, // F3
|
||||
{'C', 64}, // E3
|
||||
{'D', 63}, // D#3
|
||||
{'X', 62}, // D3
|
||||
{'S', 61}, // C#3
|
||||
{'Z', 60}, // C3
|
||||
{'U', 59}, // B3
|
||||
{'7', 58}, // A#3
|
||||
{'Y', 57}, // A3
|
||||
{'6', 56}, // G#2
|
||||
{'T', 55}, // G2
|
||||
{'5', 54}, // F#2
|
||||
{'R', 53}, // F2
|
||||
{'E', 52}, // E2
|
||||
{'3', 51}, // D#2
|
||||
{'W', 50}, // D2
|
||||
{'2', 49}, // C#2
|
||||
{'Q', 48} // C2
|
||||
}; |
||||
|
||||
CPCKeyboard *CPCKeyboard::s_pThis = 0; |
||||
|
||||
CPCKeyboard::CPCKeyboard (CMiniDexed *pSynthesizer) |
||||
: m_pSynthesizer (pSynthesizer), |
||||
m_pKeyboard (0) |
||||
{ |
||||
s_pThis = this; |
||||
|
||||
memset (m_LastKeys, 0, sizeof m_LastKeys); |
||||
} |
||||
|
||||
CPCKeyboard::~CPCKeyboard (void) |
||||
{ |
||||
m_pSynthesizer = 0; |
||||
|
||||
s_pThis = 0; |
||||
} |
||||
|
||||
void CPCKeyboard::Process (boolean bPlugAndPlayUpdated) |
||||
{ |
||||
if (!bPlugAndPlayUpdated) |
||||
{ |
||||
return; |
||||
} |
||||
|
||||
if (m_pKeyboard == 0) |
||||
{ |
||||
m_pKeyboard = |
||||
(CUSBKeyboardDevice *) CDeviceNameService::Get ()->GetDevice ("ukbd1", FALSE); |
||||
if (m_pKeyboard != 0) |
||||
{ |
||||
m_pKeyboard->RegisterKeyStatusHandlerRaw (KeyStatusHandlerRaw); |
||||
|
||||
m_pKeyboard->RegisterRemovedHandler (DeviceRemovedHandler); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void CPCKeyboard::KeyStatusHandlerRaw (unsigned char ucModifiers, const unsigned char RawKeys[6]) |
||||
{ |
||||
assert (s_pThis != 0); |
||||
assert (s_pThis->m_pSynthesizer != 0); |
||||
|
||||
// report released keys
|
||||
for (unsigned i = 0; i < 6; i++) |
||||
{ |
||||
u8 ucKeyCode = s_pThis->m_LastKeys[i]; |
||||
if ( ucKeyCode != 0 |
||||
&& !FindByte (RawKeys, ucKeyCode, 6)) |
||||
{ |
||||
u8 ucKeyNumber = GetKeyNumber (ucKeyCode); |
||||
if (ucKeyNumber != 0) |
||||
{ |
||||
s_pThis->m_pSynthesizer->keyup (ucKeyNumber); |
||||
} |
||||
} |
||||
} |
||||
|
||||
// report pressed keys
|
||||
for (unsigned i = 0; i < 6; i++) |
||||
{ |
||||
u8 ucKeyCode = RawKeys[i]; |
||||
if ( ucKeyCode != 0 |
||||
&& !FindByte (s_pThis->m_LastKeys, ucKeyCode, 6)) |
||||
{ |
||||
u8 ucKeyNumber = GetKeyNumber (ucKeyCode); |
||||
if (ucKeyNumber != 0) |
||||
{ |
||||
s_pThis->m_pSynthesizer->keydown (ucKeyNumber, 100); |
||||
} |
||||
} |
||||
} |
||||
|
||||
memcpy (s_pThis->m_LastKeys, RawKeys, sizeof s_pThis->m_LastKeys); |
||||
} |
||||
|
||||
u8 CPCKeyboard::GetKeyNumber (u8 ucKeyCode) |
||||
{ |
||||
char chKey; |
||||
if (0x04 <= ucKeyCode && ucKeyCode <= 0x1D) |
||||
{ |
||||
chKey = ucKeyCode-'\x04'+'A'; // key code of 'A' is 0x04
|
||||
} |
||||
else if (0x1E <= ucKeyCode && ucKeyCode <= 0x26) |
||||
{ |
||||
chKey = ucKeyCode-'\x1E'+'1'; // key code of '1' is 0x1E
|
||||
} |
||||
else if (ucKeyCode == 0x36) |
||||
{ |
||||
chKey = ','; // key code of ',' is 0x36
|
||||
} |
||||
else |
||||
{ |
||||
return 0; |
||||
} |
||||
|
||||
for (unsigned i = 0; i < sizeof KeyTable / sizeof KeyTable[0]; i++) |
||||
{ |
||||
if (KeyTable[i].KeyCode == chKey) |
||||
{ |
||||
return KeyTable[i].KeyNumber; |
||||
} |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
boolean CPCKeyboard::FindByte (const u8 *pBuffer, u8 ucByte, unsigned nLength) |
||||
{ |
||||
while (nLength-- > 0) |
||||
{ |
||||
if (*pBuffer++ == ucByte) |
||||
{ |
||||
return TRUE; |
||||
} |
||||
} |
||||
|
||||
return FALSE; |
||||
} |
||||
|
||||
void CPCKeyboard::DeviceRemovedHandler (CDevice *pDevice, void *pContext) |
||||
{ |
||||
assert (s_pThis != 0); |
||||
s_pThis->m_pKeyboard = 0; |
||||
} |
@ -0,0 +1,56 @@ |
||||
//
|
||||
// pckeyboard.h
|
||||
//
|
||||
// MiniSynth Pi - A virtual analogue synthesizer for Raspberry Pi
|
||||
// Copyright (C) 2017-2020 R. Stange <rsta2@o2online.de>
|
||||
//
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
#ifndef _pckeyboard_h |
||||
#define _pckeyboard_h |
||||
|
||||
#include <circle/usb/usbkeyboard.h> |
||||
#include <circle/device.h> |
||||
#include <circle/types.h> |
||||
|
||||
class CMiniDexed; |
||||
|
||||
class CPCKeyboard |
||||
{ |
||||
public: |
||||
CPCKeyboard (CMiniDexed *pSynthesizer); |
||||
~CPCKeyboard (void); |
||||
|
||||
void Process (boolean bPlugAndPlayUpdated); |
||||
|
||||
private: |
||||
static void KeyStatusHandlerRaw (unsigned char ucModifiers, const unsigned char RawKeys[6]); |
||||
|
||||
static u8 GetKeyNumber (u8 ucKeyCode); |
||||
|
||||
static boolean FindByte (const u8 *pBuffer, u8 ucByte, unsigned nLength); |
||||
|
||||
static void DeviceRemovedHandler (CDevice *pDevice, void *pContext); |
||||
|
||||
private: |
||||
CMiniDexed *m_pSynthesizer; |
||||
|
||||
CUSBKeyboardDevice * volatile m_pKeyboard; |
||||
|
||||
u8 m_LastKeys[6]; |
||||
|
||||
static CPCKeyboard *s_pThis; |
||||
}; |
||||
|
||||
#endif |
Loading…
Reference in new issue