From 29aa449e18d4876d5ec72e3410f3f3fad874614b Mon Sep 17 00:00:00 2001 From: Rene Stange Date: Thu, 1 Dec 2022 15:42:49 +0100 Subject: [PATCH] Modifications for Circle 45 * Support USB sound cards on Raspberry Pi 4 * Use separate Circle sound library * Allow to use system option USE_USB_FIQ (non-default) * Update README --- README.md | 10 ++++- src/Makefile | 1 + src/kernel.cpp | 6 +++ src/minisynth.cpp | 90 ++++++++++++++++++++++++++++++++++++++++ src/minisynth.h | 28 ++++++++++++- src/serialmididevice.cpp | 7 +++- 6 files changed, 137 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6b2c884..05790c1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ MiniSynth Pi is a polyphonic virtual analog audio synthesizer, running bare meta You have to attach an USB MIDI keyboard controller (which supports the USB Audio Class MIDI specification) or an USB PC keyboard to your Raspberry Pi to play on it. Alternatively you can feed serial MIDI data (at 31250 Bps) into GPIO15 (Broadcom numbering). Normally you will need some external circuit to be able to attach a device with serial MIDI interface. -The audio signal is normally available on the 3.5mm headphones jack (I2S usage see below). Thus Raspberry Pi models without headphones jack (e.g. Raspberry Pi Zero) are not supported. The graphical user interface (GUI) of MiniSynth Pi can be controlled using a standard USB mouse, the official Raspberry Pi touch screen or an USB HID-class touch screen in digitizer mode. +The audio signal is normally available on the 3.5mm headphones jack (I2S and USB usage see below). Thus Raspberry Pi models without headphones jack (e.g. Raspberry Pi Zero) are not supported. The graphical user interface (GUI) of MiniSynth Pi can be controlled using a standard USB mouse, the official Raspberry Pi touch screen or an USB HID-class touch screen in digitizer mode. This version of MiniSynth Pi can be configured, so that it can be used with an external I2S interface. The audio signal is then available via this interface. MiniSynth Pi has been tested with the following I2S interfaces: @@ -24,6 +24,8 @@ This version of MiniSynth Pi can be configured, so that it can be used with an e Other I2S interfaces with these DACs may be compatible too. The I2C slave address of the DAC is auto-probed (0x4C, 0x4D or 0x1A). +On the Raspberry Pi 4 and 400 also external USB sound cards can be used. + Please note that the included reverb effect module is experimental, because it generates some noise, when no note is played. Just leave the reverb volume (wet/dry ratio) at 0% to eliminate it, if it disturbs. Getting @@ -77,6 +79,10 @@ If you want to use an I2S interface, you have to modify to file *cmdline.txt* on sounddev=sndi2s +If you want to use an USB sound card (on the Raspberry Pi 4 and 400 only), you have to attach it, before the system is started. The file *cmdline.txt* must contain the following option: + + sounddev=sndusb + Put the SD card into the card reader of your Raspberry Pi. USB Touch Screen Calibration @@ -102,7 +108,7 @@ Before powering on your Raspberry Pi, the following devices have to be attached: * HDMI display (1920x1080 pixels max.) * USB MIDI keyboard controller, USB PC keyboard or device with serial MIDI interface (at GPIO15, requires external circuit) * Standard USB mouse (if touch screen is not used) -* Headphones or amplifier (on the 3.5mm jack or via external I2S interface) +* Headphones or amplifier (on the 3.5mm jack or via external I2S interface or USB sound card) MiniSynth Pi starts in about four seconds. It is controlled using the following GUI (*MAIN* tab): diff --git a/src/Makefile b/src/Makefile index 6740d23..b8e2c89 100644 --- a/src/Makefile +++ b/src/Makefile @@ -19,6 +19,7 @@ LIBS = $(CIRCLEHOME)/addon/lvgl/liblvgl.a \ $(CIRCLEHOME)/lib/usb/libusb.a \ $(CIRCLEHOME)/lib/input/libinput.a \ $(CIRCLEHOME)/lib/fs/libfs.a \ + $(CIRCLEHOME)/lib/sound/libsound.a \ $(CIRCLEHOME)/lib/libcircle.a include $(CIRCLEHOME)/app/Rules.mk diff --git a/src/kernel.cpp b/src/kernel.cpp index 5af6e68..dfdac38 100644 --- a/src/kernel.cpp +++ b/src/kernel.cpp @@ -118,6 +118,12 @@ boolean CKernel::Initialize (void) m_pSynthesizer = new CMiniSynthesizerI2S (&m_Config, &m_Interrupt, &m_I2CMaster); } +#if RASPPI >= 4 + else if (strcmp (pSoundDevice, "sndusb") == 0) + { + m_pSynthesizer = new CMiniSynthesizerUSB (&m_Config, &m_Interrupt); + } +#endif else { m_pSynthesizer = new CMiniSynthesizerPWM (&m_Config, &m_Interrupt); diff --git a/src/minisynth.cpp b/src/minisynth.cpp index 1b4b928..084e797 100644 --- a/src/minisynth.cpp +++ b/src/minisynth.cpp @@ -355,3 +355,93 @@ unsigned CMiniSynthesizerI2S::GetChunk (u32 *pBuffer, unsigned nChunkSize) return nResult; } + +//// USB ////////////////////////////////////////////////////////////////////// + +#if RASPPI >= 4 + +CMiniSynthesizerUSB::CMiniSynthesizerUSB (CSynthConfig *pConfig, + CInterruptSystem *pInterrupt) +: CMiniSynthesizer (pConfig, pInterrupt), + CUSBSoundBaseDevice (SAMPLE_RATE), + m_nMinLevel (GetRangeMin ()+1), + m_nMaxLevel (GetRangeMax ()-1), + m_bChannelsSwapped (AreChannelsSwapped ()) +{ +} + +boolean CMiniSynthesizerUSB::Start (void) +{ + return CUSBSoundBaseDevice::Start (); +} + +boolean CMiniSynthesizerUSB::IsActive (void) +{ + return CUSBSoundBaseDevice::IsActive (); +} + +unsigned CMiniSynthesizerUSB::GetChunk (s16 *pBuffer, unsigned nChunkSize) +{ +#ifdef SHOW_STATUS + unsigned nTicks = CTimer::GetClockTicks (); +#endif + + GlobalLock (); + + unsigned nResult = nChunkSize; + + float fVolumeLevel = m_fVolume * m_nMaxLevel; + + for (; nChunkSize > 0; nChunkSize -= 2) // fill the whole buffer + { + m_VoiceManager.NextSample (); + + float fLevelLeft = m_VoiceManager.GetOutputLevelLeft (); + int nLevelLeft = (int) (fLevelLeft*fVolumeLevel); + if (nLevelLeft > (int) m_nMaxLevel) + { + nLevelLeft = m_nMaxLevel; + } + else if (nLevelLeft < m_nMinLevel) + { + nLevelLeft = m_nMinLevel; + } + + float fLevelRight = m_VoiceManager.GetOutputLevelRight (); + int nLevelRight = (int) (fLevelRight*fVolumeLevel); + if (nLevelRight > (int) m_nMaxLevel) + { + nLevelRight = m_nMaxLevel; + } + else if (nLevelRight < m_nMinLevel) + { + nLevelRight = m_nMinLevel; + } + + // for 2 stereo channels + if (!m_bChannelsSwapped) + { + *pBuffer++ = (s16) nLevelLeft; + *pBuffer++ = (s16) nLevelRight; + } + else + { + *pBuffer++ = (s16) nLevelRight; + *pBuffer++ = (s16) nLevelLeft; + } + } + +#ifdef SHOW_STATUS + nTicks = CTimer::GetClockTicks () - nTicks; + if (nTicks > m_nMaxDelayTicks) + { + m_nMaxDelayTicks = nTicks; + } +#endif + + GlobalUnlock (); + + return nResult; +} + +#endif diff --git a/src/minisynth.h b/src/minisynth.h index 56e98fa..536efc7 100644 --- a/src/minisynth.h +++ b/src/minisynth.h @@ -22,8 +22,9 @@ #include #include -#include -#include +#include +#include +#include #include #include #include "synthconfig.h" @@ -134,4 +135,27 @@ class CMiniSynthesizerI2S : public CMiniSynthesizer, public CI2SSoundBaseDevice boolean m_bChannelsSwapped; }; +//// USB ////////////////////////////////////////////////////////////////////// + +#if RASPPI >= 4 + +class CMiniSynthesizerUSB : public CMiniSynthesizer, public CUSBSoundBaseDevice +{ +public: + CMiniSynthesizerUSB (CSynthConfig *pConfig, CInterruptSystem *pInterrupt); + + boolean Start (void); + boolean IsActive (void); + +private: + unsigned GetChunk (s16 *pBuffer, unsigned nChunkSize); + +private: + int m_nMinLevel; + int m_nMaxLevel; + boolean m_bChannelsSwapped; +}; + +#endif + #endif diff --git a/src/serialmididevice.cpp b/src/serialmididevice.cpp index d34d4d0..4c8c81c 100644 --- a/src/serialmididevice.cpp +++ b/src/serialmididevice.cpp @@ -2,7 +2,7 @@ // serialmididevice.cpp // // MiniSynth Pi - A virtual analogue synthesizer for Raspberry Pi -// Copyright (C) 2017-2020 R. Stange +// Copyright (C) 2017-2022 R. Stange // // 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 @@ -18,11 +18,16 @@ // along with this program. If not, see . // #include "serialmididevice.h" +#include #include CSerialMIDIDevice::CSerialMIDIDevice (CMiniSynthesizer *pSynthesizer, CInterruptSystem *pInterrupt) : CMIDIDevice (pSynthesizer), +#if RASPPI <= 3 && defined (USE_USB_FIQ) + m_Serial (pInterrupt, FALSE), +#else m_Serial (pInterrupt, TRUE), +#endif m_nSerialState (0) { }