|
- /*
- * 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)
- {
- //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 && readFrame+j == handlePtr->maxFrame)
- {
- 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;
- }
-
- 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);
- 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;
- }
- else
- {
- ad_close(handlePtr->filePtr);
- handlePtr->filePtr = NULL;
- ad_clear_nfo(&handlePtr->fileNfo);
- }
- }
- }
-
- 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);
- }
-
- // ------------------------------------------------------------------------------------------
-
- 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 = true;
- handlePtr->needsRead = false;
- handlePtr->doProcess = false;
- handlePtr->doQuit = false;
-
- 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);
-
- pthread_create(&handlePtr->thread, NULL, (void*)&audiofile_thread_idle, handlePtr);
-
- return handlePtr;
-
- // unused
- (void)_this_;
- }
-
- static void audiofile_cleanup(PluginHandle handle)
- {
- AudioFileInstance* const handlePtr = (AudioFileInstance*)handle;
-
- // wait for processing to end
- handlePtr->doProcess = false;
- handlePtr->doQuit = true;
- pthread_mutex_lock(&handlePtr->mutex);
-
- 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 1;
-
- // unused
- (void)handle;
- }
-
- static const Parameter* audiofile_get_parameter_info(PluginHandle handle, uint32_t index)
- {
- 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)
- {
- 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)
- {
- AudioFileInstance* const handlePtr = (AudioFileInstance*)handle;
-
- if (index != 0)
- return;
-
- handlePtr->loopMode = (value > 0.5f);
- handlePtr->needsRead = true;
- }
-
- static void audiofile_set_program(PluginHandle handle, 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;
- }
- }
-
- 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_chunk = NULL,
- .set_chunk = 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"
-
- // -----------------------------------------------------------------------
|