From 2263cb8abb3c79491f6dc6768d20736287bd456d Mon Sep 17 00:00:00 2001 From: elgiano Date: Tue, 1 Dec 2020 11:41:28 +0000 Subject: [PATCH] scsynth: adding BelaScopeUGen This is a squash and reformatting of several commits that originally made up https://github.com/BelaPlatform/supercollider/pull/75/ BelaScope: create in SC_World, setup in SC_Bela ServerOptions: -O belaMaxScopeChannels BelaScope: single instance and valid inputs checks BelaScopeUGen: single instance guard BelaScope: add .belaScope to Server, Bus and Function BelaScope: maxScopeChannels defaults to 0 --- SCClassLibrary/Common/Audio/bela/BELAUGens.sc | 12 ++ SCClassLibrary/Common/Audio/bela/BelaScope.sc | 181 ++++++++++++++++++ SCClassLibrary/Common/Control/Server.sc | 5 + include/plugin_interface/SC_World.h | 4 +- include/server/SC_WorldOptions.h | 2 +- server/plugins/BELAUGens.cpp | 62 ++++++ server/plugins/CMakeLists.txt | 1 + server/scsynth/CMakeLists.txt | 4 +- server/scsynth/SC_Bela.cpp | 13 +- server/scsynth/SC_World.cpp | 7 + server/scsynth/scsynth_main.cpp | 9 +- 11 files changed, 292 insertions(+), 8 deletions(-) create mode 100644 SCClassLibrary/Common/Audio/bela/BelaScope.sc diff --git a/SCClassLibrary/Common/Audio/bela/BELAUGens.sc b/SCClassLibrary/Common/Audio/bela/BELAUGens.sc index 840abaec9de..bf68ecb817f 100644 --- a/SCClassLibrary/Common/Audio/bela/BELAUGens.sc +++ b/SCClassLibrary/Common/Audio/bela/BELAUGens.sc @@ -96,3 +96,15 @@ DigitalIO : UGen { ^this.multiNew('control', digitalPin, output, pinMode ).madd(mul,add) } } + +/* input 1: bus + * input 2: number of channels to scope + */ +BelaScopeUGen : AbstractOut { + *ar { arg busnum, numChannels; + super.performList('new1', 'audio', In.ar(busnum, numChannels)); + ^0.0; // BelaScopeUGen has no outputs + } + *numFixedArgs { ^0 } + writesToBus { ^false } +} diff --git a/SCClassLibrary/Common/Audio/bela/BelaScope.sc b/SCClassLibrary/Common/Audio/bela/BelaScope.sc new file mode 100644 index 00000000000..b7920d537a7 --- /dev/null +++ b/SCClassLibrary/Common/Audio/bela/BelaScope.sc @@ -0,0 +1,181 @@ +BelaScope { + + classvar this.maxChannels){ + warn( + "BelaScope: can't scope this signal to scope channel (%), max number of channels (%) exceeded.\nSignal: %" + .format(channelOffset, this.maxChannels, signals) + ); + ^false; + }; + ^true; + } +} + ++ UGen { + belaScope { |scopeChannel, server| + ^BelaScope.scope(scopeChannel, this, server) + } +} + ++ Array { + belaScope { |scopeChannel, server| + ^BelaScope.scope(scopeChannel, this, server) + } +} + ++ Bus { + belaScope { |scopeChannel| + ^BelaScope.monitorBus(scopeChannel, index, numChannels); + } +} + ++ Function { + belaScope { |scopeChannel, numChannels = 1, target, outbus = 0, fadeTime = 0.02, addAction = \addToHead, args| + var synth = this.play(target, outbus, fadeTime, addAction, args); + var monitor = BelaScope.monitorBus(scopeChannel, outbus, numChannels, target); + ^synth.onFree { if(monitor.notNil) { monitor.free } }; + } +} + ++ Server { + belaScope { |scopeChannel, numChannels, index = 0| + numChannels = numChannels ?? { if (index == 0) { options.numOutputBusChannels } { 2 } }; + ^Bus(\audio, index, numChannels, this).belaScope(scopeChannel); + } +} diff --git a/SCClassLibrary/Common/Control/Server.sc b/SCClassLibrary/Common/Control/Server.sc index 27aae0311ee..32bd065f2ca 100644 --- a/SCClassLibrary/Common/Control/Server.sc +++ b/SCClassLibrary/Common/Control/Server.sc @@ -68,6 +68,7 @@ ServerOptions { var <>adcLevel; var <>numMultiplexChannels; var <>belaPRU; + var <>belaMaxScopeChannels; *initClass { defaultValues = IdentityDictionary.newFrom( @@ -122,6 +123,7 @@ ServerOptions { adcLevel: 0, numMultiplexChannels: 0, belaPRU: 1, + belaMaxScopeChannels: 0, ) ) } @@ -283,6 +285,9 @@ ServerOptions { if (belaPRU.notNil, { o = o ++ " -T " ++ belaPRU; }); + if (belaMaxScopeChannels.notNil, { + o = o ++ " -O " ++ belaMaxScopeChannels; + }); ^o } diff --git a/include/plugin_interface/SC_World.h b/include/plugin_interface/SC_World.h index 9d68c3d0096..db190ba45f3 100644 --- a/include/plugin_interface/SC_World.h +++ b/include/plugin_interface/SC_World.h @@ -23,6 +23,7 @@ #ifdef BELA # include "Bela.h" +# include "libraries/Scope/Scope.h" #endif #include "SC_Types.h" @@ -110,7 +111,8 @@ struct World { #ifdef BELA BelaContext* mBelaContext; - // uint32 mBelaAnalogChannels; + Scope* mBelaScope; + uint32 mBelaMaxScopeChannels; uint32 mBelaAnalogInputChannels; uint32 mBelaAnalogOutputChannels; uint32 mBelaDigitalChannels; diff --git a/include/server/SC_WorldOptions.h b/include/server/SC_WorldOptions.h index a5d5a116db7..263ad867e13 100644 --- a/include/server/SC_WorldOptions.h +++ b/include/server/SC_WorldOptions.h @@ -81,7 +81,6 @@ struct WorldOptions { int mSharedMemoryID = 0; #ifdef BELA - // uint32 mBelaAnalogChannels; uint32 mBelaAnalogInputChannels; uint32 mBelaAnalogOutputChannels; uint32 mBelaDigitalChannels; @@ -93,6 +92,7 @@ struct WorldOptions { float mBelaADCLevel; uint32 mBelaNumMuxChannels; uint32 mBelaPRU; + uint32 mBelaMaxScopeChannels; #endif }; diff --git a/server/plugins/BELAUGens.cpp b/server/plugins/BELAUGens.cpp index 90ec160f906..ded4db2c0a3 100644 --- a/server/plugins/BELAUGens.cpp +++ b/server/plugins/BELAUGens.cpp @@ -1197,6 +1197,67 @@ void DigitalIO_Ctor(DigitalIO* unit) { ////////////////////////////////////////////////////////////////////////////////////////////////// +struct BelaScopeUGen : public Unit { + static unsigned int instanceCount; + + Scope* belaScope; + float* frameData; + unsigned int noScopeChannels; + unsigned int maxScopeChannels; +}; + +unsigned int BelaScopeUGen::instanceCount = 0; + +void BelaScopeUGen_next(BelaScopeUGen* unit, unsigned int numSamples) { + unsigned int numChannels = unit->noScopeChannels; + unsigned int maxChannels = unit->maxScopeChannels; + float* frameData = unit->frameData; + float* inputPointers[maxChannels]; + for (unsigned int ch = 0; ch < numChannels; ++ch) + inputPointers[ch] = ZIN(ch); + + LOOP1(numSamples, for (unsigned int ch = 0; ch < numChannels; ++ch) frameData[ch] = ZXP(inputPointers[ch]); + for (unsigned int ch = numChannels; ch < maxChannels; ++ch) frameData[ch] = 0.0; + unit->belaScope->log(frameData);) +} + +void BelaScopeUGen_noop(unsigned int numFrames) { /* no-op */ +} + +void BelaScopeUGen_Ctor(BelaScopeUGen* unit) { + uint32 numInputs = unit->mNumInputs; + uint32 maxScopeChannels = unit->mWorld->mBelaMaxScopeChannels; + if (numInputs > maxScopeChannels) { + rt_fprintf(stderr, + "BelaScopeUGen warning: can't initialise scope %i channels, maxBelaScopeChannels is set to %i\n", + numInputs, maxScopeChannels); + } + BelaScopeUGen::instanceCount++; + if (BelaScopeUGen::instanceCount > 1) { + rt_fprintf( + stderr, + "BelaScopeUGen warning: creating a new instance when one is already active. This one will do nothing.\n"); + SETCALC(BelaScopeUGen_noop); + return; + }; + unit->noScopeChannels = sc_min(numInputs, maxScopeChannels); + unit->maxScopeChannels = maxScopeChannels; + unit->frameData = (float*)RTAlloc(unit->mWorld, sizeof(float) * unit->noScopeChannels); + unit->belaScope = unit->mWorld->mBelaScope; + // initiate first sample + BelaScopeUGen_next(unit, 1); + // set calculation method + SETCALC(BelaScopeUGen_next); +} + +void BelaScopeUGen_Dtor(BelaScopeUGen* unit) { + if (unit->frameData) + RTFree(unit->mWorld, unit->frameData); + BelaScopeUGen::instanceCount--; +} + +////////////////////////////////////////////////////////////////////////////////////////////////// + PluginLoad(BELA) { ft = inTable; @@ -1206,6 +1267,7 @@ PluginLoad(BELA) { DefineSimpleUnit(DigitalIn); DefineSimpleUnit(DigitalOut); DefineSimpleUnit(DigitalIO); + DefineDtorUnit(BelaScopeUGen); } diff --git a/server/plugins/CMakeLists.txt b/server/plugins/CMakeLists.txt index 548107b267e..0a44ea4d557 100644 --- a/server/plugins/CMakeLists.txt +++ b/server/plugins/CMakeLists.txt @@ -99,6 +99,7 @@ if (BELA_FOUND) add_definitions("-DBELA" ${XENOMAI_DEFINITIONS} ${BELA_DEFINITIONS}) include_directories(${XENOMAI_INCLUDE_DIRS}) include_directories(${BELA_INCLUDE_DIRS}) + include_directories(${BELA_INCLUDE_DIRS}/../) # set(CMAKE_EXECUTABLE_RUNTIME_C_FLAG "-Wl,-wrap,clock_gettime,-rpath,") # set(CMAKE_EXECUTABLE_RUNTIME_CXX_FLAG "-Wl,-wrap,clock_gettime,-rpath,") diff --git a/server/scsynth/CMakeLists.txt b/server/scsynth/CMakeLists.txt index 596b2845cbe..7fbcc8562cc 100644 --- a/server/scsynth/CMakeLists.txt +++ b/server/scsynth/CMakeLists.txt @@ -222,7 +222,7 @@ elseif(AUDIOAPI STREQUAL portaudio) target_link_libraries(libscsynth ${PORTAUDIO_LIBRARIES}) endif() elseif(AUDIOAPI STREQUAL bela) - target_link_libraries(libscsynth ${XENOMAI_LIBRARIES} ${BELA_LIBRARIES}) + target_link_libraries(libscsynth ${XENOMAI_LIBRARIES} ${BELA_LIBRARIES} belaextra) elseif(AUDIOAPI STREQUAL coreaudio) target_link_libraries(libscsynth "-framework CoreAudio") endif() @@ -268,7 +268,7 @@ add_executable(scsynth target_link_libraries(scsynth libscsynth) if(AUDIOAPI STREQUAL bela) - target_link_libraries(scsynth ${XENOMAI_LIBRARIES} ${BELA_LIBRARIES}) + target_link_libraries(scsynth ${XENOMAI_LIBRARIES} ${BELA_LIBRARIES} belaextra) endif() if (PTHREADS_FOUND) diff --git a/server/scsynth/SC_Bela.cpp b/server/scsynth/SC_Bela.cpp index 5c5db1296e8..b96677c9cf9 100644 --- a/server/scsynth/SC_Bela.cpp +++ b/server/scsynth/SC_Bela.cpp @@ -77,6 +77,8 @@ class SC_BelaDriver : public SC_AudioDriver { void BelaAudioCallback(BelaContext* belaContext); void SignalReceived(int); static int countInstances; + Scope* mBelaScope; + int mBelaMaxScopeChannels; private: uint32 mSCBufLength; @@ -104,6 +106,8 @@ SC_BelaDriver::SC_BelaDriver(struct World* inWorld): SC_AudioDriver(inWorld) { countInstances); exit(1); } + mBelaScope = inWorld->mBelaScope; + mBelaMaxScopeChannels = inWorld->mBelaMaxScopeChannels; } SC_BelaDriver::~SC_BelaDriver() { @@ -118,8 +122,10 @@ static float gBelaSampleRate; // Return true on success; returning false halts the program. bool sc_belaSetup(BelaContext* belaContext, void* userData) { // cast void pointer - // SC_BelaDriver *belaDriver = (SC_BelaDriver*) userData; + SC_BelaDriver* belaDriver = (SC_BelaDriver*)userData; gBelaSampleRate = belaContext->audioSampleRate; + if (belaDriver->mBelaScope) + belaDriver->mBelaScope->setup(belaDriver->mBelaMaxScopeChannels, gBelaSampleRate); return true; } @@ -453,10 +459,11 @@ bool SC_BelaDriver::DriverSetup(int* outNumSamples, double* outSampleRate) { scprintf("SC_BelaDriver: >>DriverSetup - Running on PRU (%i)\nConfigured with \n (%i) analog input and (%i) analog " "output channels, (%i) digital channels, and (%i) multiplexer channels.\n HeadphoneLevel (%f dB), " - "pga_gain_left (%f dB) and pga_gain_right (%f dB)\n DAC Level (%f dB), ADC Level (%f dB)\n", + "pga_gain_left (%f dB) and pga_gain_right (%f dB)\n DAC Level (%f dB), ADC Level (%f dB) " + "oscilloscope channels (%i)\n", settings->pruNumber, settings->numAnalogInChannels, settings->numAnalogOutChannels, settings->numDigitalChannels, settings->numMuxChannels, settings->headphoneLevel, settings->pgaGain[0], - settings->pgaGain[1], settings->dacLevel, settings->adcLevel); + settings->pgaGain[1], settings->dacLevel, settings->adcLevel, mWorld->mBelaMaxScopeChannels); if (settings->beginMuted == 1) { scprintf("Speakers are muted.\n"); } else { diff --git a/server/scsynth/SC_World.cpp b/server/scsynth/SC_World.cpp index 513e2fb443f..0206bd280ef 100644 --- a/server/scsynth/SC_World.cpp +++ b/server/scsynth/SC_World.cpp @@ -422,6 +422,9 @@ World* World_New(WorldOptions* inOptions) { world->mBelaADCLevel = inOptions->mBelaADCLevel; world->mBelaNumMuxChannels = inOptions->mBelaNumMuxChannels; world->mBelaPRU = inOptions->mBelaPRU; + world->mBelaMaxScopeChannels = inOptions->mBelaMaxScopeChannels; + if (inOptions->mBelaMaxScopeChannels > 0) + world->mBelaScope = new Scope(); #endif #ifdef __APPLE__ @@ -982,6 +985,10 @@ void World_Cleanup(World* world, bool unload_plugins) { free_alig(world->mControlBus); free_alig(world->mAudioBus); delete[] world->mRGen; +#ifdef BELA + if (world->mBelaScope) + delete world->mBelaScope; +#endif if (hw) { #ifndef NO_LIBSNDFILE if (hw->mNRTInputFile) diff --git a/server/scsynth/scsynth_main.cpp b/server/scsynth/scsynth_main.cpp index eebb5bb4cbd..5d2c981c2d3 100644 --- a/server/scsynth/scsynth_main.cpp +++ b/server/scsynth/scsynth_main.cpp @@ -100,6 +100,7 @@ void Usage() { " -y \n" " -g \n" " -T \n" + " -O \n" #endif #if (_POSIX_MEMLOCK - 0) >= 200112L " -L enable memory locking\n" @@ -169,11 +170,13 @@ int scsynth_main(int argc, char** argv) { options.mBelaDACLevel = 0; options.mBelaNumMuxChannels = 0; options.mBelaPRU = 1; + options.mBelaMaxScopeChannels = 0; #endif for (int i = 1; i < argc;) { #ifdef BELA - if (argv[i][0] != '-' || argv[i][1] == 0 || strchr("utBaioczblndpmwZrCNSDIOMHvVRUhPLJKGXYQsxygT", argv[i][1]) == nullptr) { + if (argv[i][0] != '-' || argv[i][1] == 0 + || strchr("utBaioczblndpmwZrCNSDIOMHvVRUhPLJKGXYQsxygT", argv[i][1]) == nullptr) { #else // BELA if (argv[i][0] != '-' || argv[i][1] == 0 || strchr("utBaioczblndpmwZrCNSDIOMHvVRUhPL", argv[i][1]) == nullptr) { #endif // BELA @@ -348,6 +351,10 @@ int scsynth_main(int argc, char** argv) { checkNumArgs(2); options.mBelaPRU = atoi(argv[j + 1]); break; + case 'O': + checkNumArgs(2); + options.mBelaMaxScopeChannels = atoi(argv[j + 1]); + break; #endif case 'V': checkNumArgs(2);