| @@ -87,7 +87,11 @@ endif | |||
| @echo "\033[33m||| Internal plugins: \033[0m" | |||
| ifeq ($(HAVE_AF_DEPS),true) | |||
| @echo "AudioFile: $(ANS_YES)" | |||
| ifeq ($(HAVE_FFMPEG),true) | |||
| @echo "AudioFile: $(ANS_YES) (with ffmpeg)" | |||
| else | |||
| @echo "AudioFIle: $(ANS_YES) (without ffmpeg)" | |||
| endif | |||
| else | |||
| @echo "AudioFIle: $(ANS_NO)" | |||
| endif | |||
| @@ -0,0 +1,97 @@ | |||
| # QtCreator project file | |||
| TARGET = CarlaPlugin | |||
| TEMPLATE = lib | |||
| VERSION = 1.0 | |||
| # ------------------------------------------------------- | |||
| QT = core gui | |||
| CONFIG = debug | |||
| CONFIG += link_pkgconfig qt shared warn_on | |||
| DEFINES = DEBUG | |||
| DEFINES += HAVE_CPP11_SUPPORT | |||
| DEFINES += QTCREATOR_TEST MOC_PARSING | |||
| # Shared | |||
| DEFINES += WANT_NATIVE | |||
| DEFINES += WANT_LADSPA | |||
| DEFINES += WANT_DSSI | |||
| DEFINES += WANT_LV2 | |||
| DEFINES += WANT_VST | |||
| DEFINES += WANT_PLUGIN | |||
| DEFINES += WANT_RTAUDIO | |||
| DEFINES += WANT_FLUIDSYNTH | |||
| DEFINES += WANT_LINUXSAMPLER | |||
| DEFINES += WANT_OPENGL | |||
| DEFINES += WANT_AUDIOFILE | |||
| DEFINES += WANT_MIDIFILE | |||
| DEFINES += WANT_ZYNADDSUBFX | |||
| DEFINES += WANT_ZYNADDSUBFX_UI | |||
| # Audio file | |||
| DEFINES += HAVE_FFMPEG | |||
| PKGCONFIG += sndfile libavcodec libavformat libavutil | |||
| # MIDI file | |||
| PKGCONFIG += smf | |||
| # DISTRHO | |||
| PKGCONFIG += gl | |||
| # ZynAddSubFX | |||
| DEFINES += NTK_GUI | |||
| PKGCONFIG += fftw3 mxml zlib ntk ntk_images | |||
| # ------------------------------------------------------- | |||
| SOURCES = \ | |||
| bypass.c \ | |||
| lfo.c \ | |||
| midi-split.c \ | |||
| midi-through.c \ | |||
| midi-transpose.c \ | |||
| nekofilter.c | |||
| SOURCES += \ | |||
| audio-file.cpp \ | |||
| midi-file.cpp \ | |||
| midi-sequencer.cpp \ | |||
| sunvox-file.cpp \ | |||
| zynaddsubfx.cpp \ | |||
| zynaddsubfx-src.cpp \ | |||
| zynaddsubfx-ui.cpp | |||
| SOURCES += \ | |||
| distrho-3bandeq.cpp | |||
| HEADERS = \ | |||
| midi-base.hpp | |||
| HEADERS += \ | |||
| ../CarlaNative.h \ | |||
| ../CarlaNative.hpp | |||
| HEADERS += \ | |||
| distrho/DistrhoPluginCarla.cpp | |||
| HEADERS += \ | |||
| ../../utils/CarlaUtils.hpp \ | |||
| ../../utils/CarlaJuceUtils.hpp \ | |||
| ../../utils/CarlaLibUtils.hpp \ | |||
| ../../utils/CarlaOscUtils.hpp \ | |||
| ../../utils/CarlaStateUtils.hpp \ | |||
| ../../utils/CarlaMutex.hpp \ | |||
| ../../utils/CarlaString.hpp | |||
| INCLUDEPATH = . .. \ | |||
| 3bandeq distrho \ | |||
| ../../includes \ | |||
| ../../libs/distrho \ | |||
| ../../utils \ | |||
| ../../widgets | |||
| QMAKE_CFLAGS *= -std=c99 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -62,7 +62,10 @@ OBJS = \ | |||
| # AudioFile | |||
| ifeq ($(HAVE_AF_DEPS),true) | |||
| OBJS += \ | |||
| audiofile.c.o | |||
| audio-file.cpp.o \ | |||
| audio_decoder/ad_ffmpeg.c.o \ | |||
| audio_decoder/ad_plugin.c.o \ | |||
| audio_decoder/ad_soundfile.c.o | |||
| endif | |||
| # MidiFile | |||
| @@ -147,10 +150,13 @@ debug: | |||
| # -------------------------------------------------------------- | |||
| %.c.o: %.c ../CarlaNative.h | |||
| CDEPS = ../CarlaNative.h | |||
| CXXDEPS = ../CarlaNative.h ../CarlaNative.hpp | |||
| %.c.o: %.c $(CDEPS) | |||
| $(CC) $< $(BUILD_C_FLAGS) -c -o $@ | |||
| %.cpp.o: %.cpp ../CarlaNative.h ../CarlaNative.hpp | |||
| %.cpp.o: %.cpp $(CXXDEPS) | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| $(TARGET): $(OBJS) | |||
| @@ -158,37 +164,46 @@ $(TARGET): $(OBJS) | |||
| # -------------------------------------------------------------- | |||
| audiofile.c.o: audiofile.c | |||
| audio_decoder/%.c.o: audio_decoder/%.c | |||
| $(CC) $< $(AF_C_FLAGS) -c -o $@ | |||
| distrho-3bandeq.cpp.o: distrho-3bandeq.cpp 3bandeq/DistrhoArtwork3BandEQ.cpp 3bandeq/DistrhoPlugin3BandEQ.cpp 3bandeq/DistrhoUI3BandEQ.cpp distrho/DistrhoPluginCarla.cpp | |||
| audio-file.cpp.o: audio-file.cpp $(CXXDEPS) | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| distrho-3bandeq.cpp.o: distrho-3bandeq.cpp 3bandeq/*.cpp 3bandeq/*.h 3bandeq/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) | |||
| $(CXX) $< $(GL_CXX_FLAGS) -I3bandeq -DDISTRHO_NAMESPACE=DISTRHO_3BandEQ -c -o $@ | |||
| distrho-3bandsplitter.cpp.o: distrho-3bandsplitter.cpp 3bandsplitter/DistrhoArtwork3BandSplitter.cpp 3bandsplitter/DistrhoPlugin3BandSplitter.cpp 3bandsplitter/DistrhoUI3BandSplitter.cpp distrho/DistrhoPluginCarla.cpp | |||
| distrho-3bandsplitter.cpp.o: distrho-3bandsplitter.cpp 3bandsplitter/*.cpp 3bandsplitter/*.h 3bandsplitter/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) | |||
| $(CXX) $< $(GL_CXX_FLAGS) -I3bandsplitter -DDISTRHO_NAMESPACE=DISTRHO_3BandSplitter -c -o $@ | |||
| distrho-pingpongpan.cpp.o: distrho-pingpongpan.cpp pingpongpan/DistrhoArtworkPingPongPan.cpp pingpongpan/DistrhoPluginPingPongPan.cpp pingpongpan/DistrhoUIPingPongPan.cpp distrho/DistrhoPluginCarla.cpp | |||
| distrho-pingpongpan.cpp.o: distrho-pingpongpan.cpp pingpongpan/*.cpp pingpongpan/*.h pingpongpan/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) | |||
| $(CXX) $< $(GL_CXX_FLAGS) -Ipingpongpan -DDISTRHO_NAMESPACE=DISTRHO_PingPongPan -c -o $@ | |||
| distrho-notes.cpp.o: distrho-notes.cpp notes/DistrhoPluginNotes.cpp notes/DistrhoUINotes.cpp distrho/DistrhoPluginCarla.cpp moc_DistrhoUINotes.cpp | |||
| distrho-notes.cpp.o: distrho-notes.cpp notes/moc_DistrhoUINotes.cpp notes/*.cpp notes/*.h notes/*.hpp distrho/DistrhoPluginCarla.cpp $(CXXDEPS) | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -Inotes -DDISTRHO_NAMESPACE=DISTRHO_Notes -c -o $@ | |||
| midi-file.cpp.o: midi-file.cpp | |||
| midi-file.cpp.o: midi-file.cpp midi-base.hpp $(CXXDEPS) | |||
| $(CXX) $< $(MF_CXX_FLAGS) -c -o $@ | |||
| zynaddsubfx.cpp.o: zynaddsubfx.cpp $(ZYN_UI_FILES_H) | |||
| midi-sequencer.cpp.o: midi-sequencer.cpp midi-base.hpp $(CXXDEPS) | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| nekofilter.c.o: nekofilter.c nekofilter/*.c nekofilter/*.h $(CDEPS) | |||
| $(CC) $< $(BUILD_C_FLAGS) -c -o $@ | |||
| zynaddsubfx.cpp.o: zynaddsubfx.cpp $(CXXDEPS) $(ZYN_UI_FILES_H) | |||
| $(CXX) $< $(ZYN_CXX_FLAGS) -c -o $@ | |||
| zynaddsubfx-src.cpp.o: zynaddsubfx-src.cpp | |||
| zynaddsubfx-src.cpp.o: zynaddsubfx-src.cpp $(ZYN_UI_FILES_CPP) | |||
| $(CXX) $< $(ZYN_CXX_FLAGS) -c -o $@ | |||
| zynaddsubfx-ui.cpp.o: zynaddsubfx-ui.cpp $(ZYN_UI_FILES_CPP) | |||
| zynaddsubfx-ui.cpp.o: zynaddsubfx-ui.cpp $(ZYN_UI_FILES_H) | |||
| $(CXX) $< $(ZYN_CXX_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| moc_DistrhoUINotes.cpp: notes/DistrhoUINotes.hpp | |||
| $(MOC) $< -o $@ | |||
| moc_%.cpp: %.hpp | |||
| $(MOC) $< -DMOC_PARSING -o $@ | |||
| zynaddsubfx/UI/%.cpp: zynaddsubfx/UI/%.fl | |||
| ntk-fluid -c -o zynaddsubfx/UI/$*.cpp -h zynaddsubfx/UI/$*.h $< | |||
| @@ -0,0 +1,520 @@ | |||
| /* | |||
| * Carla Native Plugins | |||
| * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 2 of | |||
| * the License, or 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. | |||
| * | |||
| * For a full copy of the GNU General Public License see the GPL.txt file | |||
| */ | |||
| #include "CarlaNative.hpp" | |||
| #include "CarlaMutex.hpp" | |||
| #include "CarlaString.hpp" | |||
| #include <QtCore/QThread> | |||
| extern "C" { | |||
| #include "audio_decoder/ad.h" | |||
| } | |||
| #define PROGRAM_COUNT 16 | |||
| typedef struct adinfo ADInfo; | |||
| class AudioFilePlugin : public PluginDescriptorClass | |||
| { | |||
| public: | |||
| AudioFilePlugin(const HostDescriptor* const host) | |||
| : PluginDescriptorClass(host), | |||
| filePtr(nullptr), | |||
| lastFrame(0), | |||
| maxFrame(0), | |||
| loopMode(false), | |||
| doProcess(false) | |||
| { | |||
| static bool adInitiated = false; | |||
| if (! adInitiated) | |||
| { | |||
| ad_init(); | |||
| adInitiated = true; | |||
| } | |||
| ad_clear_nfo(&fileNfo); | |||
| pool.create(getSampleRate()); | |||
| thread.setPoolSize(pool.size); | |||
| } | |||
| ~AudioFilePlugin() override | |||
| { | |||
| doProcess = false; | |||
| thread.stopNow(); | |||
| if (filePtr != nullptr) | |||
| ad_close(filePtr); | |||
| pool.destroy(); | |||
| } | |||
| protected: | |||
| // ------------------------------------------------------------------- | |||
| // Plugin parameter calls | |||
| uint32_t getParameterCount() override | |||
| { | |||
| return 0; // FIXME | |||
| return 1; | |||
| } | |||
| const Parameter* getParameterInfo(const uint32_t index) override | |||
| { | |||
| return nullptr; // FIXME - loop mode needs work | |||
| if (index != 0) | |||
| return nullptr; | |||
| static Parameter param; | |||
| param.name = "Loop Mode"; | |||
| param.unit = nullptr; | |||
| param.hints = static_cast<ParameterHints>(PARAMETER_IS_ENABLED|PARAMETER_IS_BOOLEAN); | |||
| param.ranges.def = 1.0f; | |||
| param.ranges.min = 0.0f; | |||
| param.ranges.max = 1.0f; | |||
| param.ranges.step = 1.0f; | |||
| param.ranges.stepSmall = 1.0f; | |||
| param.ranges.stepLarge = 1.0f; | |||
| param.scalePointCount = 0; | |||
| param.scalePoints = nullptr; | |||
| return ¶m; | |||
| } | |||
| float getParameterValue(const uint32_t index) override | |||
| { | |||
| if (index != 0) | |||
| return 0.0f; | |||
| return loopMode ? 1.0f : 0.0f; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Plugin midi-program calls | |||
| uint32_t getMidiProgramCount() override | |||
| { | |||
| return PROGRAM_COUNT; | |||
| } | |||
| const MidiProgram* getMidiProgramInfo(const uint32_t index) override | |||
| { | |||
| if (index >= PROGRAM_COUNT) | |||
| return NULL; | |||
| static MidiProgram midiProgram; | |||
| midiProgram.bank = 0; | |||
| midiProgram.program = index; | |||
| midiProgram.name = (const char*)programs.shortNames[index]; | |||
| return &midiProgram; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Plugin state calls | |||
| void setParameterValue(const uint32_t index, const float value) override | |||
| { | |||
| if (index != 0) | |||
| return; | |||
| bool b = (value > 0.5f); | |||
| if (b == loopMode) | |||
| return; | |||
| loopMode = b; | |||
| thread.setNeedsRead(); | |||
| } | |||
| void setMidiProgram(const uint8_t, const uint32_t bank, const uint32_t program) override | |||
| { | |||
| if (bank != 0 || program >= PROGRAM_COUNT) | |||
| return; | |||
| if (programs.current != program) | |||
| { | |||
| loadFilename(programs.fullNames[program]); | |||
| programs.current = program; | |||
| } | |||
| } | |||
| void setCustomData(const char* const key, const char* const value) override | |||
| { | |||
| if (std::strlen(key) != 6) | |||
| return; | |||
| if (std::strncmp(key, "file", 4) != 0) | |||
| return; | |||
| if (key[4] < '0' || key[4] > '9') | |||
| return; | |||
| if (key[5] < '0' || key[5] > '9') | |||
| return; | |||
| uint8_t tens = key[4]-'0'; | |||
| uint8_t nums = key[5]-'0'; | |||
| uint32_t program = tens*10 + nums; | |||
| if (program >= PROGRAM_COUNT) | |||
| return; | |||
| programs.fullNames[program] = value; | |||
| if (const char* shortName = std::strrchr(value, OS_SEP)) | |||
| programs.shortNames[program] = shortName+1; | |||
| else | |||
| programs.shortNames[program] = value; | |||
| programs.shortNames[program].truncate(programs.shortNames[program].rfind('.')); | |||
| if (programs.current == program) | |||
| loadFilename(value); | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Plugin process calls | |||
| void process(float**, float** const outBuffer, const uint32_t frames, const uint32_t, const MidiEvent* const) override | |||
| { | |||
| const TimeInfo* const timePos(getTimeInfo()); | |||
| float* out1 = outBuffer[0]; | |||
| float* out2 = outBuffer[1]; | |||
| if (! doProcess) | |||
| { | |||
| lastFrame = timePos->frame; | |||
| carla_zeroFloat(out1, frames); | |||
| carla_zeroFloat(out2, frames); | |||
| return; | |||
| } | |||
| // not playing | |||
| if (! timePos->playing) | |||
| { | |||
| if (timePos->frame == 0 && lastFrame > 0) | |||
| thread.setNeedsRead(); | |||
| lastFrame = timePos->frame; | |||
| carla_zeroFloat(out1, frames); | |||
| carla_zeroFloat(out2, frames); | |||
| return; | |||
| } | |||
| const CarlaMutex::ScopedLocker sl(&mutex); | |||
| // out of reach | |||
| if (timePos->frame + frames < pool.startFrame || timePos->frame >= maxFrame) /*&& ! loopMode)*/ | |||
| { | |||
| lastFrame = timePos->frame; | |||
| thread.setNeedsRead(); | |||
| carla_zeroFloat(out1, frames); | |||
| carla_zeroFloat(out2, frames); | |||
| return; | |||
| } | |||
| int64_t poolFrame = (int64_t)timePos->frame - pool.startFrame; | |||
| int64_t poolSize = pool.size; | |||
| for (uint32_t i=0; i < frames; ++i, ++poolFrame) | |||
| { | |||
| if (poolFrame >= 0 && poolFrame < poolSize) | |||
| { | |||
| out1[i] = pool.buffer[0][poolFrame]; | |||
| out2[i] = pool.buffer[1][poolFrame]; | |||
| // reset | |||
| pool.buffer[0][poolFrame] = 0.0f; | |||
| pool.buffer[1][poolFrame] = 0.0f; | |||
| } | |||
| else | |||
| { | |||
| out1[i] = 0.0f; | |||
| out2[i] = 0.0f; | |||
| } | |||
| } | |||
| lastFrame = timePos->frame; | |||
| } | |||
| // ------------------------------------------------------------------- | |||
| // Plugin UI calls | |||
| void uiShow(const bool show) override | |||
| { | |||
| if (! show) | |||
| return; | |||
| if (const char* const filename = uiOpenFile(false, "Open Audio File", "")) | |||
| { | |||
| char fileStr[] = { 'f', 'i', 'l', 'e', '\0', '\0', '\0' }; | |||
| fileStr[4] = '0' + (programs.current / 10); | |||
| fileStr[5] = '0' + (programs.current % 10); | |||
| uiCustomDataChanged(fileStr, filename); | |||
| } | |||
| uiClosed(); | |||
| } | |||
| private: | |||
| struct Pool { | |||
| float* buffer[2]; | |||
| uint32_t startFrame; | |||
| uint32_t size; | |||
| Pool() | |||
| : buffer{nullptr}, | |||
| startFrame(0), | |||
| size(0) {} | |||
| ~Pool() | |||
| { | |||
| CARLA_ASSERT(buffer[0] == nullptr); | |||
| CARLA_ASSERT(buffer[1] == nullptr); | |||
| CARLA_ASSERT(startFrame == 0); | |||
| CARLA_ASSERT(size == 0); | |||
| } | |||
| void create(const uint32_t sampleRate) | |||
| { | |||
| CARLA_ASSERT(buffer[0] == nullptr); | |||
| CARLA_ASSERT(buffer[1] == nullptr); | |||
| CARLA_ASSERT(startFrame == 0); | |||
| CARLA_ASSERT(size == 0); | |||
| size = sampleRate * 6; | |||
| buffer[0] = new float[size]; | |||
| buffer[1] = new float[size]; | |||
| carla_zeroFloat(buffer[0], size); | |||
| carla_zeroFloat(buffer[1], size); | |||
| } | |||
| void destroy() | |||
| { | |||
| CARLA_ASSERT(buffer[0] != nullptr); | |||
| CARLA_ASSERT(buffer[1] != nullptr); | |||
| CARLA_ASSERT(size != 0); | |||
| if (buffer[0] != nullptr) | |||
| { | |||
| delete[] buffer[0]; | |||
| buffer[0] = nullptr; | |||
| } | |||
| if (buffer[1] != nullptr) | |||
| { | |||
| delete[] buffer[1]; | |||
| buffer[1] = nullptr; | |||
| } | |||
| startFrame = 0; | |||
| size = 0; | |||
| } | |||
| }; | |||
| struct Programs { | |||
| uint32_t current; | |||
| CarlaString fullNames[PROGRAM_COUNT]; | |||
| CarlaString shortNames[PROGRAM_COUNT]; | |||
| Programs() | |||
| : current(0) {} | |||
| }; | |||
| class AudioFileThread : public QThread | |||
| { | |||
| public: | |||
| AudioFileThread() | |||
| : QThread(nullptr), | |||
| fNeedsRead(false), | |||
| fQuitNow(true), | |||
| fLastFrame(0), | |||
| fPoolStartFrame(0), | |||
| fPoolSize(0) | |||
| { | |||
| } | |||
| ~AudioFileThread() | |||
| { | |||
| CARLA_ASSERT(fQuitNow == true); | |||
| CARLA_ASSERT(! isRunning()); | |||
| } | |||
| void stopNow() | |||
| { | |||
| fNeedsRead = false; | |||
| fQuitNow = true; | |||
| if (isRunning() && ! wait(1000)) | |||
| terminate(); | |||
| } | |||
| void readPoll() | |||
| { | |||
| } | |||
| void setNeedsRead() | |||
| { | |||
| fNeedsRead = true; | |||
| } | |||
| void setLastFrame(const uint32_t lastFrame) | |||
| { | |||
| fLastFrame = lastFrame; | |||
| } | |||
| void setPoolStart(const uint32_t poolStartFrame) | |||
| { | |||
| fPoolStartFrame = poolStartFrame; | |||
| } | |||
| void setPoolSize(const uint32_t poolSize) | |||
| { | |||
| fPoolSize = poolSize; | |||
| } | |||
| protected: | |||
| void run() override | |||
| { | |||
| while (! fQuitNow) | |||
| { | |||
| if (fNeedsRead || (fLastFrame >= fPoolStartFrame && fLastFrame - fPoolStartFrame >= fPoolSize*3/4)) | |||
| readPoll(); | |||
| else | |||
| carla_msleep(50); | |||
| } | |||
| } | |||
| private: | |||
| bool fNeedsRead; | |||
| bool fQuitNow; | |||
| uint32_t fLastFrame; | |||
| uint32_t fPoolStartFrame; | |||
| uint32_t fPoolSize; | |||
| }; | |||
| void* filePtr; | |||
| ADInfo fileNfo; | |||
| uint32_t lastFrame; | |||
| uint32_t maxFrame; | |||
| bool loopMode; | |||
| bool doProcess; | |||
| Pool pool; | |||
| Programs programs; | |||
| CarlaMutex mutex; | |||
| AudioFileThread thread; | |||
| void loadFilename(const char* const filename) | |||
| { | |||
| // wait for jack processing to end | |||
| doProcess = false; | |||
| { | |||
| const CarlaMutex::ScopedLocker sl(&mutex); | |||
| maxFrame = 0; | |||
| pool.startFrame = 0; | |||
| thread.stopNow(); | |||
| } | |||
| // clear old data | |||
| if (filePtr != nullptr) | |||
| { | |||
| ad_close(filePtr); | |||
| filePtr = nullptr; | |||
| } | |||
| ad_clear_nfo(&fileNfo); | |||
| if (filename == nullptr) | |||
| return; | |||
| // open new | |||
| filePtr = ad_open(filename, &fileNfo); | |||
| if (filePtr == nullptr) | |||
| return; | |||
| ad_dump_nfo(99, &fileNfo); | |||
| if (fileNfo.frames == 0) | |||
| carla_stderr("L: filename \"%s\" has 0 frames", filename); | |||
| if ((fileNfo.channels == 1 || fileNfo.channels == 2) && fileNfo.frames > 0) | |||
| { | |||
| maxFrame = fileNfo.frames; | |||
| thread.readPoll(); | |||
| doProcess = true; | |||
| thread.start(); | |||
| } | |||
| else | |||
| { | |||
| ad_clear_nfo(&fileNfo); | |||
| ad_close(filePtr); | |||
| filePtr = nullptr; | |||
| } | |||
| } | |||
| PluginDescriptorClassEND(AudioFilePlugin) | |||
| CARLA_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AudioFilePlugin) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| static const PluginDescriptor audiofileDesc = { | |||
| /* category */ PLUGIN_CATEGORY_UTILITY, | |||
| /* hints */ static_cast<PluginHints>(PLUGIN_HAS_GUI), | |||
| /* audioIns */ 0, | |||
| /* audioOuts */ 2, | |||
| /* midiIns */ 0, | |||
| /* midiOuts */ 0, | |||
| /* paramIns */ 1, | |||
| /* paramOuts */ 0, | |||
| /* name */ "Audio File", | |||
| /* label */ "audiofile", | |||
| /* maker */ "falkTX", | |||
| /* copyright */ "GNU GPL v2+", | |||
| PluginDescriptorFILL(AudioFilePlugin) | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| void carla_register_native_plugin_audiofile() | |||
| { | |||
| carla_register_native_plugin(&audiofileDesc); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| @@ -1,702 +0,0 @@ | |||
| /* | |||
| * Carla Native Plugins | |||
| * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * 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 2 of | |||
| * the License, or 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. | |||
| * | |||
| * For a full copy of the GNU General Public License see the GPL.txt file | |||
| */ | |||
| #include "CarlaNative.h" | |||
| #include "audio_decoder/ad.h" | |||
| #include <pthread.h> | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #include <string.h> | |||
| #include <unistd.h> | |||
| #define PROGRAM_COUNT 16 | |||
| #ifdef _WIN32 | |||
| # define OS_SEP '\\' | |||
| #else | |||
| # define OS_SEP '/' | |||
| #endif | |||
| typedef struct adinfo ADInfo; | |||
| typedef pthread_mutex_t Mutex; | |||
| typedef pthread_t Thread; | |||
| typedef struct _AudioFilePool { | |||
| float* buffer[2]; | |||
| uint32_t startFrame; | |||
| uint32_t size; | |||
| } AudioFilePool; | |||
| typedef struct _AudioFilePrograms { | |||
| uint32_t current; | |||
| const char* fullNames[PROGRAM_COUNT]; | |||
| const char* shortNames[PROGRAM_COUNT]; | |||
| } AudioFilePrograms; | |||
| typedef struct _AudioFileInstance { | |||
| HostDescriptor* host; | |||
| void* filePtr; | |||
| ADInfo fileNfo; | |||
| uint32_t lastFrame; | |||
| uint32_t maxFrame; | |||
| AudioFilePool pool; | |||
| AudioFilePrograms programs; | |||
| bool loopMode; | |||
| bool needsRead; | |||
| bool doProcess; | |||
| bool doQuit; | |||
| Mutex mutex; | |||
| Thread thread; | |||
| } AudioFileInstance; | |||
| // ------------------------------------------------------------------------------------------ | |||
| void zeroFloat(float* data, unsigned size) | |||
| { | |||
| for (unsigned i=0; i < size; ++i) | |||
| *data++ = 0.0f; | |||
| } | |||
| void audiofile_read_poll(AudioFileInstance* const handlePtr) | |||
| { | |||
| if (handlePtr == NULL) | |||
| { | |||
| fprintf(stderr, "R: invalid instance\n"); | |||
| return; | |||
| } | |||
| if (handlePtr->fileNfo.frames == 0 || handlePtr->maxFrame == 0) | |||
| { | |||
| //fprintf(stderr, "R: no song loaded\n"); | |||
| handlePtr->needsRead = false; | |||
| return; | |||
| } | |||
| int64_t lastFrame = handlePtr->lastFrame; | |||
| int64_t readFrame = lastFrame; | |||
| if (lastFrame >= handlePtr->maxFrame) | |||
| { | |||
| if (handlePtr->loopMode) | |||
| { | |||
| //fprintf(stderr, "R: DEBUG read loop, lastFrame:%i, maxFrame:%i\n", handlePtr->lastFrame, handlePtr->maxFrame); | |||
| if (handlePtr->maxFrame >= handlePtr->pool.size) | |||
| { | |||
| readFrame %= handlePtr->maxFrame; | |||
| } | |||
| else | |||
| { | |||
| readFrame = 0; | |||
| lastFrame -= lastFrame % handlePtr->maxFrame; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| //fprintf(stderr, "R: transport out of bounds\n"); | |||
| handlePtr->needsRead = false; | |||
| return; | |||
| } | |||
| } | |||
| // temp data buffer | |||
| const uint32_t tmpSize = handlePtr->pool.size * handlePtr->fileNfo.channels; | |||
| float tmpData[tmpSize]; | |||
| zeroFloat(tmpData, tmpSize); | |||
| { | |||
| fprintf(stderr, "R: poll data - reading at %li:%02li\n", readFrame/44100/60, (readFrame/44100) % 60); | |||
| ad_seek(handlePtr->filePtr, readFrame); | |||
| ssize_t i, j, rv = ad_read(handlePtr->filePtr, tmpData, tmpSize); | |||
| i = j = 0; | |||
| // lock, and put data asap | |||
| pthread_mutex_lock(&handlePtr->mutex); | |||
| for (; i < handlePtr->pool.size && j < rv; ++j) | |||
| { | |||
| if (handlePtr->fileNfo.channels == 1) | |||
| { | |||
| handlePtr->pool.buffer[0][i] = tmpData[j]; | |||
| handlePtr->pool.buffer[1][i] = tmpData[j]; | |||
| i++; | |||
| } | |||
| else | |||
| { | |||
| if (j % 2 == 0) | |||
| { | |||
| handlePtr->pool.buffer[0][i] = tmpData[j]; | |||
| } | |||
| else | |||
| { | |||
| handlePtr->pool.buffer[1][i] = tmpData[j]; | |||
| i++; | |||
| } | |||
| } | |||
| } | |||
| if (handlePtr->loopMode && i < handlePtr->pool.size) | |||
| { | |||
| while (i < handlePtr->pool.size) | |||
| { | |||
| for (j=0; i < handlePtr->pool.size && j < rv; ++j) | |||
| { | |||
| if (handlePtr->fileNfo.channels == 1) | |||
| { | |||
| handlePtr->pool.buffer[0][i] = tmpData[j]; | |||
| handlePtr->pool.buffer[1][i] = tmpData[j]; | |||
| i++; | |||
| } | |||
| else | |||
| { | |||
| if (j % 2 == 0) | |||
| { | |||
| handlePtr->pool.buffer[0][i] = tmpData[j]; | |||
| } | |||
| else | |||
| { | |||
| handlePtr->pool.buffer[1][i] = tmpData[j]; | |||
| i++; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for (; i < handlePtr->pool.size; ++i) | |||
| { | |||
| handlePtr->pool.buffer[0][i] = 0.0f; | |||
| handlePtr->pool.buffer[1][i] = 0.0f; | |||
| } | |||
| } | |||
| handlePtr->pool.startFrame = lastFrame; | |||
| // done | |||
| pthread_mutex_unlock(&handlePtr->mutex); | |||
| } | |||
| handlePtr->needsRead = false; | |||
| } | |||
| static void audiofile_thread_idle(void* ptr) | |||
| { | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)ptr; | |||
| while (! handlePtr->doQuit) | |||
| { | |||
| if (handlePtr->needsRead || handlePtr->lastFrame - handlePtr->pool.startFrame >= handlePtr->pool.size*3/4) | |||
| audiofile_read_poll(handlePtr); | |||
| else | |||
| usleep(50*1000); | |||
| } | |||
| pthread_exit(NULL); | |||
| } | |||
| void audiofile_load_filename(AudioFileInstance* const handlePtr, const char* const filename) | |||
| { | |||
| // wait for jack processing to end | |||
| handlePtr->doProcess = false; | |||
| pthread_mutex_lock(&handlePtr->mutex); | |||
| handlePtr->maxFrame = 0; | |||
| handlePtr->pool.startFrame = 0; | |||
| // stop thread | |||
| if (! handlePtr->doQuit) | |||
| { | |||
| handlePtr->doQuit = true; | |||
| pthread_join(handlePtr->thread, NULL); | |||
| } | |||
| pthread_mutex_unlock(&handlePtr->mutex); | |||
| // clear old data | |||
| if (handlePtr->filePtr != NULL) | |||
| { | |||
| ad_close(handlePtr->filePtr); | |||
| handlePtr->filePtr = NULL; | |||
| } | |||
| ad_clear_nfo(&handlePtr->fileNfo); | |||
| if (filename == NULL) | |||
| return; | |||
| // open new | |||
| handlePtr->filePtr = ad_open(filename, &handlePtr->fileNfo); | |||
| if (handlePtr->filePtr != NULL) | |||
| { | |||
| ad_dump_nfo(99, &handlePtr->fileNfo); | |||
| if (handlePtr->fileNfo.frames == 0) | |||
| fprintf(stderr, "L: filename \"%s\" has 0 frames\n", filename); | |||
| if ((handlePtr->fileNfo.channels == 1 || handlePtr->fileNfo.channels == 2) && handlePtr->fileNfo.frames > 0) | |||
| { | |||
| handlePtr->maxFrame = handlePtr->fileNfo.frames; | |||
| audiofile_read_poll(handlePtr); | |||
| handlePtr->doProcess = true; | |||
| // start thread | |||
| handlePtr->doQuit = false; | |||
| pthread_create(&handlePtr->thread, NULL, (void*)&audiofile_thread_idle, handlePtr); | |||
| } | |||
| else | |||
| { | |||
| ad_close(handlePtr->filePtr); | |||
| handlePtr->filePtr = NULL; | |||
| ad_clear_nfo(&handlePtr->fileNfo); | |||
| } | |||
| } | |||
| } | |||
| // ------------------------------------------------------------------------------------------ | |||
| static bool gADInitiated = false; | |||
| // ------------------------------------------------------------------------------------------ | |||
| static PluginHandle audiofile_instantiate(const PluginDescriptor* _this_, HostDescriptor* host) | |||
| { | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)malloc(sizeof(AudioFileInstance)); | |||
| if (handlePtr == NULL) | |||
| return NULL; | |||
| if (! gADInitiated) | |||
| { | |||
| ad_init(); | |||
| gADInitiated = true; | |||
| } | |||
| // init | |||
| handlePtr->host = host; | |||
| handlePtr->filePtr = NULL; | |||
| handlePtr->lastFrame = 0; | |||
| handlePtr->maxFrame = 0; | |||
| handlePtr->pool.buffer[0] = NULL; | |||
| handlePtr->pool.buffer[1] = NULL; | |||
| handlePtr->pool.startFrame = 0; | |||
| handlePtr->pool.size = 0; | |||
| handlePtr->programs.current = 0; | |||
| handlePtr->loopMode = false; | |||
| handlePtr->needsRead = false; | |||
| handlePtr->doProcess = false; | |||
| handlePtr->doQuit = true; | |||
| ad_clear_nfo(&handlePtr->fileNfo); | |||
| pthread_mutex_init(&handlePtr->mutex, NULL); | |||
| for (uint32_t i=0; i < PROGRAM_COUNT; ++i) | |||
| { | |||
| handlePtr->programs.fullNames[i] = NULL; | |||
| handlePtr->programs.shortNames[i] = NULL; | |||
| } | |||
| // create audio pool | |||
| handlePtr->pool.size = host->get_sample_rate(host->handle) * 6; // 6 secs | |||
| handlePtr->pool.buffer[0] = (float*)malloc(sizeof(float) * handlePtr->pool.size); | |||
| if (handlePtr->pool.buffer[0] == NULL) | |||
| { | |||
| free(handlePtr); | |||
| return NULL; | |||
| } | |||
| handlePtr->pool.buffer[1] = (float*)malloc(sizeof(float) * handlePtr->pool.size); | |||
| if (handlePtr->pool.buffer[1] == NULL) | |||
| { | |||
| free(handlePtr->pool.buffer[0]); | |||
| free(handlePtr); | |||
| return NULL; | |||
| } | |||
| zeroFloat(handlePtr->pool.buffer[0], handlePtr->pool.size); | |||
| zeroFloat(handlePtr->pool.buffer[1], handlePtr->pool.size); | |||
| return handlePtr; | |||
| // unused | |||
| (void)_this_; | |||
| } | |||
| static void audiofile_cleanup(PluginHandle handle) | |||
| { | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)handle; | |||
| // wait for processing to end | |||
| handlePtr->doProcess = false; | |||
| pthread_mutex_lock(&handlePtr->mutex); | |||
| // stop thread | |||
| if (! handlePtr->doQuit) | |||
| { | |||
| handlePtr->doQuit = true; | |||
| pthread_join(handlePtr->thread, NULL); | |||
| } | |||
| pthread_mutex_unlock(&handlePtr->mutex); | |||
| pthread_mutex_destroy(&handlePtr->mutex); | |||
| if (handlePtr->filePtr != NULL) | |||
| ad_close(handlePtr->filePtr); | |||
| if (handlePtr->pool.buffer[0] != NULL) | |||
| free(handlePtr->pool.buffer[0]); | |||
| if (handlePtr->pool.buffer[1] != NULL) | |||
| free(handlePtr->pool.buffer[1]); | |||
| for (uint32_t i=0; i < PROGRAM_COUNT; ++i) | |||
| { | |||
| if (handlePtr->programs.fullNames[i] != NULL) | |||
| free((void*)handlePtr->programs.fullNames[i]); | |||
| if (handlePtr->programs.shortNames[i] != NULL) | |||
| free((void*)handlePtr->programs.shortNames[i]); | |||
| } | |||
| free(handlePtr); | |||
| } | |||
| static uint32_t audiofile_get_parameter_count(PluginHandle handle) | |||
| { | |||
| return 0; // FIXME - loop mode needs work | |||
| // unused | |||
| (void)handle; | |||
| } | |||
| static const Parameter* audiofile_get_parameter_info(PluginHandle handle, uint32_t index) | |||
| { | |||
| // FIXME - loop mode needs work | |||
| return NULL; | |||
| if (index != 0) | |||
| return NULL; | |||
| static Parameter param; | |||
| param.name = "Loop Mode"; | |||
| param.unit = NULL; | |||
| param.hints = PARAMETER_IS_ENABLED|PARAMETER_IS_BOOLEAN; | |||
| param.ranges.def = 1.0f; | |||
| param.ranges.min = 0.0f; | |||
| param.ranges.max = 1.0f; | |||
| param.ranges.step = 1.0f; | |||
| param.ranges.stepSmall = 1.0f; | |||
| param.ranges.stepLarge = 1.0f; | |||
| param.scalePointCount = 0; | |||
| param.scalePoints = NULL; | |||
| return ¶m; | |||
| // unused | |||
| (void)handle; | |||
| } | |||
| static float audiofile_get_parameter_value(PluginHandle handle, uint32_t index) | |||
| { | |||
| // FIXME - loop mode needs work | |||
| return 0.0f; | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)handle; | |||
| if (index != 0) | |||
| return 0.0f; | |||
| return handlePtr->loopMode ? 1.0f : 0.0f; | |||
| // unused | |||
| (void)handle; | |||
| } | |||
| static uint32_t audiofile_get_program_count(PluginHandle handle) | |||
| { | |||
| return PROGRAM_COUNT; | |||
| // unused | |||
| (void)handle; | |||
| } | |||
| const MidiProgram* audiofile_get_program_info(PluginHandle handle, uint32_t index) | |||
| { | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)handle; | |||
| if (index >= PROGRAM_COUNT) | |||
| return NULL; | |||
| static MidiProgram midiProgram; | |||
| midiProgram.bank = 0; | |||
| midiProgram.program = index; | |||
| midiProgram.name = handlePtr->programs.shortNames[index]; | |||
| if (midiProgram.name == NULL) | |||
| midiProgram.name = ""; | |||
| return &midiProgram; | |||
| } | |||
| static void audiofile_set_parameter_value(PluginHandle handle, uint32_t index, float value) | |||
| { | |||
| // FIXME - loop mode needs work | |||
| return; | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)handle; | |||
| if (index != 0) | |||
| return; | |||
| handlePtr->loopMode = (value > 0.5f); | |||
| handlePtr->needsRead = true; | |||
| } | |||
| static void audiofile_set_program(PluginHandle handle, uint8_t channel, uint32_t bank, uint32_t program) | |||
| { | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)handle; | |||
| if (bank != 0 || program >= PROGRAM_COUNT) | |||
| return; | |||
| if (handlePtr->programs.current != program) | |||
| { | |||
| audiofile_load_filename(handlePtr, handlePtr->programs.fullNames[program]); | |||
| handlePtr->programs.current = program; | |||
| } | |||
| return; | |||
| // unused | |||
| (void)channel; | |||
| } | |||
| static void audiofile_set_custom_data(PluginHandle handle, const char* key, const char* value) | |||
| { | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)handle; | |||
| if (strncmp(key, "file", 4) != 0) | |||
| return; | |||
| if (key[4] < '0' || key[4] > '9') | |||
| return; | |||
| if (key[5] < '0' || key[5] > '9') | |||
| return; | |||
| uint8_t tens = key[4]-'0'; | |||
| uint8_t nums = key[5]-'0'; | |||
| uint32_t program = tens*10 + nums; | |||
| if (program >= PROGRAM_COUNT) | |||
| return; | |||
| if (handlePtr->programs.fullNames[program] != NULL) | |||
| free((void*)handlePtr->programs.fullNames[program]); | |||
| if (handlePtr->programs.shortNames[program] != NULL) | |||
| free((void*)handlePtr->programs.shortNames[program]); | |||
| handlePtr->programs.fullNames[program] = strdup(value); | |||
| { | |||
| const char* shortName1 = strrchr(value, OS_SEP)+1; | |||
| //const char* shortName2 = strchr(shortName1, '.'); | |||
| handlePtr->programs.shortNames[program] = strdup(shortName1); | |||
| } | |||
| if (handlePtr->programs.current == program) | |||
| audiofile_load_filename(handlePtr, value); | |||
| } | |||
| static void audiofile_ui_show(PluginHandle handle, bool show) | |||
| { | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)handle; | |||
| if (! show) | |||
| return; | |||
| const char* const filename = handlePtr->host->ui_open_file(handlePtr->host->handle, false, "Open Audio File", ""); | |||
| if (filename != NULL) | |||
| { | |||
| char fileStr[4+2+1] = { 'f', 'i', 'l', 'e', 0, 0, 0 }; | |||
| fileStr[4] = '0' + (handlePtr->programs.current / 10); | |||
| fileStr[5] = '0' + (handlePtr->programs.current % 10); | |||
| handlePtr->host->ui_custom_data_changed(handlePtr->host->handle, fileStr, filename); | |||
| } | |||
| handlePtr->host->ui_closed(handlePtr->host->handle); | |||
| } | |||
| static void audiofile_process(PluginHandle handle, float** inBuffer, float** outBuffer, uint32_t frames, uint32_t midiEventCount, const MidiEvent* midiEvents) | |||
| { | |||
| AudioFileInstance* const handlePtr = (AudioFileInstance*)handle; | |||
| const TimeInfo* const timePos = handlePtr->host->get_time_info(handlePtr->host->handle); | |||
| float* out1 = outBuffer[0]; | |||
| float* out2 = outBuffer[1]; | |||
| if (! handlePtr->doProcess) | |||
| { | |||
| //fprintf(stderr, "P: no process\n"); | |||
| handlePtr->lastFrame = timePos->frame; | |||
| zeroFloat(out1, frames); | |||
| zeroFloat(out2, frames); | |||
| return; | |||
| } | |||
| // not playing | |||
| if (! timePos->playing) | |||
| { | |||
| //fprintf(stderr, "P: not rolling\n"); | |||
| if (timePos->frame == 0 && handlePtr->lastFrame > 0) | |||
| handlePtr->needsRead = true; | |||
| handlePtr->lastFrame = timePos->frame; | |||
| zeroFloat(out1, frames); | |||
| zeroFloat(out2, frames); | |||
| return; | |||
| } | |||
| pthread_mutex_lock(&handlePtr->mutex); | |||
| // out of reach | |||
| if (timePos->frame + frames < handlePtr->pool.startFrame || (timePos->frame >= handlePtr->maxFrame && ! handlePtr->loopMode)) | |||
| { | |||
| //fprintf(stderr, "P: non-continuous playback, out of reach %u vs %u\n", timePos->frame + frames, handlePtr->maxFrame); | |||
| handlePtr->lastFrame = timePos->frame; | |||
| handlePtr->needsRead = true; | |||
| pthread_mutex_unlock(&handlePtr->mutex); | |||
| zeroFloat(out1, frames); | |||
| zeroFloat(out2, frames); | |||
| return; | |||
| } | |||
| int64_t poolFrame = (int64_t)timePos->frame - handlePtr->pool.startFrame; | |||
| int64_t poolSize = handlePtr->pool.size; | |||
| for (uint32_t i=0; i < frames; ++i, ++poolFrame) | |||
| { | |||
| if (poolFrame >= 0 && poolFrame < poolSize) | |||
| { | |||
| out1[i] = handlePtr->pool.buffer[0][poolFrame]; | |||
| out2[i] = handlePtr->pool.buffer[1][poolFrame]; | |||
| // reset | |||
| handlePtr->pool.buffer[0][poolFrame] = 0.0f; | |||
| handlePtr->pool.buffer[1][poolFrame] = 0.0f; | |||
| } | |||
| else | |||
| { | |||
| out1[i] = 0.0f; | |||
| out2[i] = 0.0f; | |||
| } | |||
| } | |||
| handlePtr->lastFrame = timePos->frame; | |||
| pthread_mutex_unlock(&handlePtr->mutex); | |||
| return; | |||
| // unused | |||
| (void)inBuffer; | |||
| (void)midiEventCount; | |||
| (void)midiEvents; | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| static const PluginDescriptor audiofileDesc = { | |||
| .category = PLUGIN_CATEGORY_UTILITY, | |||
| .hints = PLUGIN_IS_RTSAFE|PLUGIN_HAS_GUI, | |||
| .audioIns = 0, | |||
| .audioOuts = 2, | |||
| .midiIns = 0, | |||
| .midiOuts = 0, | |||
| .parameterIns = 1, | |||
| .parameterOuts = 0, | |||
| .name = "Audio File", | |||
| .label = "audiofile", | |||
| .maker = "falkTX", | |||
| .copyright = "GNU GPL v2+", | |||
| .instantiate = audiofile_instantiate, | |||
| .cleanup = audiofile_cleanup, | |||
| .get_parameter_count = audiofile_get_parameter_count, | |||
| .get_parameter_info = audiofile_get_parameter_info, | |||
| .get_parameter_value = audiofile_get_parameter_value, | |||
| .get_parameter_text = NULL, | |||
| .get_midi_program_count = audiofile_get_program_count, | |||
| .get_midi_program_info = audiofile_get_program_info, | |||
| .set_parameter_value = audiofile_set_parameter_value, | |||
| .set_midi_program = audiofile_set_program, | |||
| .set_custom_data = audiofile_set_custom_data, | |||
| .ui_show = audiofile_ui_show, | |||
| .ui_idle = NULL, | |||
| .ui_set_parameter_value = NULL, | |||
| .ui_set_midi_program = NULL, | |||
| .ui_set_custom_data = NULL, | |||
| .activate = NULL, | |||
| .deactivate = NULL, | |||
| .process = audiofile_process, | |||
| .get_state = NULL, | |||
| .set_state = NULL, | |||
| .dispatcher = NULL | |||
| }; | |||
| // ----------------------------------------------------------------------- | |||
| void carla_register_native_plugin_audiofile() | |||
| { | |||
| carla_register_native_plugin(&audiofileDesc); | |||
| } | |||
| // ----------------------------------------------------------------------- | |||
| // amalgamated build | |||
| #include "audio_decoder/ad_ffmpeg.c" | |||
| #include "audio_decoder/ad_plugin.c" | |||
| #include "audio_decoder/ad_soundfile.c" | |||
| // ----------------------------------------------------------------------- | |||
| @@ -1,53 +0,0 @@ | |||
| # QtCreator project file | |||
| QT = core gui | |||
| CONFIG = debug | |||
| CONFIG += link_pkgconfig qt shared warn_on | |||
| DEFINES = DEBUG | |||
| DEFINES += HAVE_CPP11_SUPPORT | |||
| DEFINES += QTCREATOR_TEST | |||
| # ZynAddSubFX | |||
| DEFINES += WANT_ZYNADDSUBFX | |||
| PKGCONFIG = fftw3 mxml | |||
| TARGET = carla_native | |||
| TEMPLATE = lib | |||
| VERSION = 0.5.0 | |||
| SOURCES = \ | |||
| bypass.c \ | |||
| midi-split.c \ | |||
| midi-through.c \ | |||
| zynaddsubfx.cpp \ | |||
| zynaddsubfx-src.cpp | |||
| SOURCES += \ | |||
| distrho-3bandeq.cpp | |||
| HEADERS = \ | |||
| ../carla_native.h \ | |||
| ../carla_native.hpp | |||
| HEADERS += \ | |||
| distrho/DistrhoPluginCarla.cpp | |||
| HEADERS += \ | |||
| ../../includes/carla_defines.hpp \ | |||
| ../../includes/carla_midi.h \ | |||
| ../../utils/carla_utils.hpp \ | |||
| ../../utils/carla_juce_utils.hpp | |||
| INCLUDEPATH = . .. \ | |||
| 3bandeq distrho \ | |||
| ../../includes \ | |||
| ../../utils \ | |||
| ../../libs/distrho-plugin-toolkit | |||
| LIBS = -lGL -lpthread | |||
| QMAKE_CFLAGS *= -std=c99 | |||
| QMAKE_CXXFLAGS *= -std=c++0x | |||
| @@ -30,8 +30,8 @@ START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| static const PluginDescriptor tBandEqDesc = { | |||
| /* category */ ::PLUGIN_CATEGORY_EQ, | |||
| /* hints */ static_cast<PluginHints>(::PLUGIN_IS_RTSAFE | ::PLUGIN_HAS_GUI), | |||
| /* category */ PLUGIN_CATEGORY_EQ, | |||
| /* hints */ static_cast<PluginHints>(PLUGIN_IS_RTSAFE|PLUGIN_HAS_GUI), | |||
| /* audioIns */ DISTRHO_PLUGIN_NUM_INPUTS, | |||
| /* audioOuts */ DISTRHO_PLUGIN_NUM_OUTPUTS, | |||
| /* midiIns */ 0, | |||
| @@ -30,8 +30,8 @@ START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| static const PluginDescriptor tBandSplitterDesc = { | |||
| /* category */ ::PLUGIN_CATEGORY_EQ, | |||
| /* hints */ static_cast<PluginHints>(::PLUGIN_IS_RTSAFE | ::PLUGIN_HAS_GUI), | |||
| /* category */ PLUGIN_CATEGORY_EQ, | |||
| /* hints */ static_cast<PluginHints>(PLUGIN_IS_RTSAFE|PLUGIN_HAS_GUI), | |||
| /* audioIns */ DISTRHO_PLUGIN_NUM_INPUTS, | |||
| /* audioOuts */ DISTRHO_PLUGIN_NUM_OUTPUTS, | |||
| /* midiIns */ 0, | |||
| @@ -29,8 +29,8 @@ START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| static const PluginDescriptor notesDesc = { | |||
| /* category */ ::PLUGIN_CATEGORY_UTILITY, | |||
| /* hints */ static_cast<PluginHints>(::PLUGIN_IS_RTSAFE|::PLUGIN_HAS_GUI|::PLUGIN_USES_SINGLE_THREAD), | |||
| /* category */ PLUGIN_CATEGORY_UTILITY, | |||
| /* hints */ static_cast<PluginHints>(PLUGIN_IS_RTSAFE|PLUGIN_HAS_GUI|PLUGIN_USES_SINGLE_THREAD), | |||
| /* audioIns */ DISTRHO_PLUGIN_NUM_INPUTS, | |||
| /* audioOuts */ DISTRHO_PLUGIN_NUM_OUTPUTS, | |||
| /* midiIns */ 0, | |||
| @@ -30,8 +30,8 @@ START_NAMESPACE_DISTRHO | |||
| // ----------------------------------------------------------------------- | |||
| static PluginDescriptor tBandEqDesc = { | |||
| /* category */ ::PLUGIN_CATEGORY_UTILITY, | |||
| /* hints */ static_cast<PluginHints>(::PLUGIN_IS_RTSAFE | ::PLUGIN_HAS_GUI), | |||
| /* category */ PLUGIN_CATEGORY_UTILITY, | |||
| /* hints */ static_cast<PluginHints>(PLUGIN_IS_RTSAFE|PLUGIN_HAS_GUI), | |||
| /* audioIns */ DISTRHO_PLUGIN_NUM_INPUTS, | |||
| /* audioOuts */ DISTRHO_PLUGIN_NUM_OUTPUTS, | |||
| /* midiIns */ 0, | |||
| @@ -433,7 +433,7 @@ private: | |||
| #endif | |||
| protected: | |||
| void run() | |||
| void run() override | |||
| { | |||
| while (! fQuit) | |||
| { | |||
| @@ -566,63 +566,6 @@ private: | |||
| static int sInstanceCount; | |||
| static NonRtList<ProgramInfo*> sPrograms; | |||
| public: | |||
| static PluginHandle _instantiate(const PluginDescriptor*, HostDescriptor* host) | |||
| { | |||
| if (sInstanceCount++ == 0) | |||
| { | |||
| CARLA_ASSERT(synth == nullptr); | |||
| CARLA_ASSERT(denormalkillbuf == nullptr); | |||
| synth = new SYNTH_T(); | |||
| synth->buffersize = host->get_buffer_size(host->handle); | |||
| synth->samplerate = host->get_sample_rate(host->handle); | |||
| synth->alias(); | |||
| config.init(); | |||
| config.cfg.SoundBufferSize = synth->buffersize; | |||
| config.cfg.SampleRate = synth->samplerate; | |||
| config.cfg.GzipCompression = 0; | |||
| sprng(std::time(nullptr)); | |||
| denormalkillbuf = new float[synth->buffersize]; | |||
| for (int i=0; i < synth->buffersize; ++i) | |||
| denormalkillbuf[i] = (RND - 0.5f) * 1e-16; | |||
| Master::getInstance(); | |||
| #ifdef WANT_ZYNADDSUBFX_UI | |||
| if (gPixmapPath.isEmpty()) | |||
| { | |||
| gPixmapPath = host->resource_dir; | |||
| gPixmapPath += PIXMAP_PATH; | |||
| gUiPixmapPath = gPixmapPath; | |||
| } | |||
| #endif | |||
| } | |||
| return new ZynAddSubFxPlugin(host); | |||
| } | |||
| static void _cleanup(PluginHandle handle) | |||
| { | |||
| delete (ZynAddSubFxPlugin*)handle; | |||
| if (--sInstanceCount == 0) | |||
| { | |||
| CARLA_ASSERT(synth != nullptr); | |||
| CARLA_ASSERT(denormalkillbuf != nullptr); | |||
| Master::deleteInstance(); | |||
| delete[] denormalkillbuf; | |||
| denormalkillbuf = nullptr; | |||
| delete synth; | |||
| synth = nullptr; | |||
| } | |||
| } | |||
| static void maybeInitPrograms(Master* const master) | |||
| { | |||
| static bool doSearch = true; | |||
| @@ -691,7 +634,64 @@ public: | |||
| } | |||
| } | |||
| static void clearPrograms() | |||
| public: | |||
| static PluginHandle _instantiate(const PluginDescriptor*, HostDescriptor* host) | |||
| { | |||
| if (sInstanceCount++ == 0) | |||
| { | |||
| CARLA_ASSERT(synth == nullptr); | |||
| CARLA_ASSERT(denormalkillbuf == nullptr); | |||
| synth = new SYNTH_T(); | |||
| synth->buffersize = host->get_buffer_size(host->handle); | |||
| synth->samplerate = host->get_sample_rate(host->handle); | |||
| synth->alias(); | |||
| config.init(); | |||
| config.cfg.SoundBufferSize = synth->buffersize; | |||
| config.cfg.SampleRate = synth->samplerate; | |||
| config.cfg.GzipCompression = 0; | |||
| sprng(std::time(nullptr)); | |||
| denormalkillbuf = new float[synth->buffersize]; | |||
| for (int i=0; i < synth->buffersize; ++i) | |||
| denormalkillbuf[i] = (RND - 0.5f) * 1e-16; | |||
| Master::getInstance(); | |||
| #ifdef WANT_ZYNADDSUBFX_UI | |||
| if (gPixmapPath.isEmpty()) | |||
| { | |||
| gPixmapPath = host->resource_dir; | |||
| gPixmapPath += PIXMAP_PATH; | |||
| gUiPixmapPath = gPixmapPath; | |||
| } | |||
| #endif | |||
| } | |||
| return new ZynAddSubFxPlugin(host); | |||
| } | |||
| static void _cleanup(PluginHandle handle) | |||
| { | |||
| delete (ZynAddSubFxPlugin*)handle; | |||
| if (--sInstanceCount == 0) | |||
| { | |||
| CARLA_ASSERT(synth != nullptr); | |||
| CARLA_ASSERT(denormalkillbuf != nullptr); | |||
| Master::deleteInstance(); | |||
| delete[] denormalkillbuf; | |||
| denormalkillbuf = nullptr; | |||
| delete synth; | |||
| synth = nullptr; | |||
| } | |||
| } | |||
| static void _clearPrograms() | |||
| { | |||
| for (auto it = sPrograms.begin(); it.valid(); it.next()) | |||
| { | |||
| @@ -709,11 +709,9 @@ private: | |||
| int ZynAddSubFxPlugin::sInstanceCount = 0; | |||
| NonRtList<ZynAddSubFxPlugin::ProgramInfo*> ZynAddSubFxPlugin::sPrograms; | |||
| struct ProgramsDestructor { | |||
| ProgramsDestructor() {} | |||
| ~ProgramsDestructor() | |||
| { | |||
| ZynAddSubFxPlugin::clearPrograms(); | |||
| static const struct ProgramsDestructor { | |||
| ~ProgramsDestructor() { | |||
| ZynAddSubFxPlugin::_clearPrograms(); | |||
| } | |||
| } programsDestructor; | |||
| @@ -76,7 +76,6 @@ HEADERS += \ | |||
| ../../utils/CarlaString.hpp | |||
| INCLUDEPATH = . .. \ | |||
| ../engine \ | |||
| ../../includes \ | |||
| ../../libs \ | |||
| ../../utils | |||
| @@ -4181,6 +4181,7 @@ public: | |||
| uint32_t i = 0; | |||
| while ((fDescriptor = descFn(i++))) | |||
| { | |||
| carla_debug("LV2 Init @%i => '%s' vs '%s'", i, fDescriptor->URI, uri); | |||
| if (std::strcmp(fDescriptor->URI, uri) == 0) | |||
| break; | |||
| } | |||
| @@ -195,15 +195,13 @@ public: | |||
| size_t rfind(const char c) const | |||
| { | |||
| size_t pos = 0; | |||
| for (size_t i=0; i < bufferLen; ++i) | |||
| for (size_t i=bufferLen; i > 0; --i) | |||
| { | |||
| if (buffer[i] == c) | |||
| pos = i; | |||
| if (buffer[i-1] == c) | |||
| return i-1; | |||
| } | |||
| return pos; | |||
| return 0; | |||
| } | |||
| void replace(const char before, const char after) | |||