Browse Source

Make the audio and midi file plugins use juce; audio loops now

tags/1.9.4
falkTX 10 years ago
parent
commit
389f6b662a
6 changed files with 212 additions and 196 deletions
  1. +18
    -3
      source/backend/CarlaStandalone.cpp
  2. +2
    -16
      source/backend/engine/CarlaEngine.cpp
  3. +3
    -3
      source/modules/juce_audio_formats/AppConfig.h
  4. +2
    -7
      source/modules/native-plugins/audio-base.hpp
  5. +126
    -85
      source/modules/native-plugins/audio-file.cpp
  6. +61
    -82
      source/modules/native-plugins/midi-file.cpp

+ 18
- 3
source/backend/CarlaStandalone.cpp View File

@@ -29,6 +29,8 @@
#include "CarlaOscUtils.hpp"
#include "CarlaThread.hpp"

#include "juce_audio_formats.h"

#ifdef BUILD_BRIDGE
# undef HAVE_JUCE_UI
#endif
@@ -44,6 +46,11 @@ using juce::Thread;
namespace CB = CarlaBackend;
using CB::EngineOptions;

using juce::AudioFormat;
using juce::AudioFormatManager;
using juce::String;
using juce::StringArray;

// -------------------------------------------------------------------------------------------------------------------
// Juce Message Thread

@@ -650,9 +657,17 @@ const char* carla_get_supported_file_extensions()
retText += ";*.gig;*.sfz";
#endif

// Audio files, FIXME
retText += ";*.aiff;*.flac;*.oga;*.ogg;*.w64;*.wav";
retText += ";*.3g2;*.3gp;*.aac;*.ac3;*.amr;*.ape;*.mp2;*.mp3;*.mpc;*.wma";
// Audio files
AudioFormatManager afm;
afm.registerBasicFormats();

for (AudioFormat **it=afm.begin(), **end=afm.end(); it != end; ++it)
{
const StringArray& exts((*it)->getFileExtensions());

for (String *eit=exts.begin(), *end=exts.end(); eit != end; ++eit)
retText += String(";*" + (*eit)).toRawUTF8();
}

// MIDI files
retText += ";*.mid;*.midi";


+ 2
- 16
source/backend/engine/CarlaEngine.cpp View File

@@ -892,8 +892,7 @@ bool CarlaEngine::loadFile(const char* const filename)
CARLA_SAFE_ASSERT_RETURN_ERR(file.existsAsFile(), "Requested file does not exist or is not a readable file");

CarlaString baseName(file.getFileName().toRawUTF8());
CarlaString extension(file.getFileExtension().toRawUTF8());
extension.toLower();
CarlaString extension(file.getFileExtension().replace(".","").toLowerCase().toRawUTF8());

// -------------------------------------------------------------------

@@ -913,20 +912,7 @@ bool CarlaEngine::loadFile(const char* const filename)

// -------------------------------------------------------------------

// FIXME
if (extension == "aiff" || extension == "flac" || extension == "oga" || extension == "ogg" || extension == "w64" || extension == "wav")
{
if (addPlugin(PLUGIN_INTERNAL, nullptr, baseName, "audiofile", 0, nullptr))
{
if (CarlaPlugin* const plugin = getPlugin(pData->curPluginCount-1))
plugin->setCustomData(CUSTOM_DATA_TYPE_STRING, "file", filename, true);
return true;
}
return false;
}

if (extension == "3g2" || extension == "3gp" || extension == "aac" || extension == "ac3" || extension == "amr" || extension == "ape" ||
extension == "mp2" || extension == "mp3" || extension == "mpc" || extension == "wma")
if (extension == "aif" || extension == "aiff" || extension == "bwf" || extension == "flac" || extension == "ogg" || extension == "wav")
{
if (addPlugin(PLUGIN_INTERNAL, nullptr, baseName, "audiofile", 0, nullptr))
{


+ 3
- 3
source/modules/juce_audio_formats/AppConfig.h View File

@@ -17,14 +17,14 @@
If your app doesn't need to read FLAC files, you might want to disable this to
reduce the size of your codebase and build time.
*/
#define JUCE_USE_FLAC 0
#define JUCE_USE_FLAC 1
/** Config: JUCE_USE_OGGVORBIS
Enables the Ogg-Vorbis audio codec classes (available on all platforms).
If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to
reduce the size of your codebase and build time.
*/
#define JUCE_USE_OGGVORBIS 0
#define JUCE_USE_OGGVORBIS 1
/** Config: JUCE_USE_MP3AUDIOFORMAT
Enables the software-based MP3AudioFormat class.
@@ -43,7 +43,7 @@
/** Config: JUCE_USE_LAME_AUDIO_FORMAT
Enables the LameEncoderAudioFormat class.
*/
#define JUCE_USE_LAME_AUDIO_FORMAT 0
#define JUCE_USE_LAME_AUDIO_FORMAT 1
/** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT
Enables the Windows Media SDK codecs.


+ 2
- 7
source/modules/native-plugins/audio-base.hpp View File

@@ -20,14 +20,9 @@

#include "CarlaThread.hpp"

#include "juce_audio_basics.h"
using juce::FloatVectorOperations;

extern "C" {
#include "audio_decoder/ad.h"
}
#include "juce_audio_formats.h"

typedef struct adinfo ADInfo;
using juce::FloatVectorOperations;

struct AudioFilePool {
float* buffer[2];


+ 126
- 85
source/modules/native-plugins/audio-file.cpp View File

@@ -16,37 +16,53 @@
*/

#include "CarlaNative.hpp"

#include "CarlaMutex.hpp"
#include "CarlaString.hpp"

#include "audio-base.hpp"
#include "juce_audio_formats.h"

using juce::AudioFormat;
using juce::AudioFormatManager;
using juce::AudioFormatReader;
using juce::AudioFormatReaderSource;
using juce::AudioSampleBuffer;
using juce::BufferingAudioReader;
using juce::File;
using juce::FloatVectorOperations;
using juce::MemoryMappedAudioFormatReader;
using juce::ScopedPointer;
using juce::TimeSliceThread;

#define PROGRAM_COUNT 16
// -----------------------------------------------------------------------

class AudioFilePlugin : public NativePluginClass,
public AbstractAudioPlayer
static AudioFormatManager& getAudioFormatManagerInstance()
{
static AudioFormatManager afm;
afm.registerBasicFormats();
return afm;
}

// -----------------------------------------------------------------------

class AudioFilePlugin : public NativePluginClass
{
public:
AudioFilePlugin(const NativeHostDescriptor* const host)
: NativePluginClass(host),
AbstractAudioPlayer(),
fLoopMode(false),
fDoProcess(false),
fLastFrame(0),
fMaxFrame(0),
fThread(this, getSampleRate())
fLength(0)
//fThread("AudioFilePluginThread")
{
fPool.create((uint32_t)getSampleRate());
fReaderBuffer.setSize(2, getBufferSize());
}

~AudioFilePlugin() override
{
fPool.destroy();
fThread.stopNow();
}

uint64_t getLastFrame() const override
{
return fLastFrame;
//fThread.stopThread(-1);
fReader = nullptr;
fReaderSource = nullptr;
}

protected:
@@ -55,7 +71,7 @@ protected:

uint32_t getParameterCount() const override
{
return 0; // TODO - loopMode
return 1;
}

const NativeParameter* getParameterInfo(const uint32_t index) const override
@@ -96,21 +112,28 @@ protected:
if (index != 0)
return;

bool b = (value > 0.5f);
const bool loopMode(value > 0.5f);

if (b == fLoopMode)
if (fLoopMode == loopMode)
return;

fLoopMode = b;
fThread.setNeedsRead();
fLoopMode = loopMode;

const CarlaMutexLocker cml(fReaderMutex);

if (fReaderSource != nullptr)
fReaderSource->setLooping(loopMode);
}

void setCustomData(const char* const key, const char* const value) override
{
CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(value != nullptr && value[0] != '\0',);

if (std::strcmp(key, "file") != 0)
return;

loadFilename(value);
_loadAudioFile(value);
}

// -------------------------------------------------------------------
@@ -120,71 +143,46 @@ protected:
{
const NativeTimeInfo* const timePos(getTimeInfo());

float* out1 = outBuffer[0];
float* out2 = outBuffer[1];
float* const out1(outBuffer[0]);
float* const out2(outBuffer[1]);

if (! fDoProcess)
if (fLength == 0 || ! fDoProcess)
{
//carla_stderr("P: no process");
fLastFrame = timePos->frame;

FloatVectorOperations::clear(out1, frames);
FloatVectorOperations::clear(out2, frames);
return;
}

const int64_t nextReadPos(fLoopMode ? (timePos->frame % fLength) : timePos->frame);

// not playing
if (! timePos->playing)
{
//carla_stderr("P: not playing");
fLastFrame = timePos->frame;

if (timePos->frame == 0 && fLastFrame > 0)
fThread.setNeedsRead();

FloatVectorOperations::clear(out1, frames);
FloatVectorOperations::clear(out2, frames);
return;
}

fThread.tryPutData(fPool);

// out of reach
if (timePos->frame + frames < fPool.startFrame || timePos->frame >= fMaxFrame) /*&& ! loopMode)*/
{
//carla_stderr("P: out of reach");
fLastFrame = timePos->frame;
const CarlaMutexLocker cml(fReaderMutex);

if (timePos->frame + frames < fPool.startFrame)
fThread.setNeedsRead();
if (fReaderSource != nullptr)
fReaderSource->setNextReadPosition(nextReadPos);

FloatVectorOperations::clear(out1, frames);
FloatVectorOperations::clear(out2, frames);
return;
}

int64_t poolFrame = (int64_t)timePos->frame - fPool.startFrame;
int64_t poolSize = fPool.size;
const CarlaMutexLocker cml(fReaderMutex);

for (uint32_t i=0; i < frames; ++i, ++poolFrame)
{
if (poolFrame >= 0 && poolFrame < poolSize)
{
out1[i] = fPool.buffer[0][poolFrame];
out2[i] = fPool.buffer[1][poolFrame];

// reset
fPool.buffer[0][poolFrame] = 0.0f;
fPool.buffer[1][poolFrame] = 0.0f;
}
else
{
out1[i] = 0.0f;
out2[i] = 0.0f;
}
}
if (fReaderSource != nullptr)
fReaderSource->setNextReadPosition(nextReadPos);

if (fReader == nullptr)
return;

fLastFrame = timePos->frame;
fReader->read(&fReaderBuffer, 0, frames, nextReadPos, true, true);

FloatVectorOperations::copy(out1, fReaderBuffer.getReadPointer(0), frames);
FloatVectorOperations::copy(out2, fReaderBuffer.getReadPointer(1), frames);
}

// -------------------------------------------------------------------
@@ -201,41 +199,84 @@ protected:
uiClosed();
}

// -------------------------------------------------------------------
// Plugin dispatcher calls

void bufferSizeChanged(const uint32_t bufferSize) override
{
fReaderBuffer.setSize(2, bufferSize);
}

private:
bool fLoopMode;
bool fDoProcess;
int64_t fLength;

uint64_t fLastFrame;
uint32_t fMaxFrame;
//TimeSliceThread fThread;

AudioFilePool fPool;
AudioFileThread fThread;
AudioSampleBuffer fReaderBuffer;
CarlaMutex fReaderMutex;

void loadFilename(const char* const filename)
ScopedPointer<AudioFormatReader> fReader;
ScopedPointer<AudioFormatReaderSource> fReaderSource;

void _loadAudioFile(const char* const filename)
{
CARLA_ASSERT(filename != nullptr);
carla_debug("AudioFilePlugin::loadFilename(\"%s\")", filename);
carla_stdout("AudioFilePlugin::loadFilename(\"%s\")", filename);

fDoProcess = false;
fLength = 0;

fThread.stopNow();
//fThread.stopThread(-1);

if (filename == nullptr || *filename == '\0')
{
fDoProcess = false;
fMaxFrame = 0;
return;
fReaderMutex.lock();
AudioFormatReader* const reader(fReader.release());
AudioFormatReaderSource* const readerSource(fReaderSource.release());
fReaderMutex.unlock();

delete readerSource;
delete reader;
}

if (fThread.loadFilename(filename))
File file(filename);

if (! file.existsAsFile())
return;

AudioFormatManager& afm(getAudioFormatManagerInstance());

AudioFormat* const format(afm.findFormatForFileExtension(file.getFileExtension()));
CARLA_SAFE_ASSERT_RETURN(format != nullptr,);

if (MemoryMappedAudioFormatReader* const memReader = format->createMemoryMappedReader(file))
{
fThread.startNow();
fMaxFrame = fThread.getMaxFrame();
fDoProcess = true;
memReader->mapEntireFile();
fReader = memReader;

carla_stdout("Using memory mapped read file");
}
else
{
fDoProcess = false;
fMaxFrame = 0;
AudioFormatReader* const reader(afm.createReaderFor(file));
CARLA_SAFE_ASSERT_RETURN(reader != nullptr,);

// this code can be used for very large files
//fThread.startThread();
//BufferingAudioReader* const bufferingReader(new BufferingAudioReader(reader, fThread, getSampleRate()*2));
//bufferingReader->setReadTimeout(50);

AudioFormatReaderSource* const readerSource(new AudioFormatReaderSource(/*bufferingReader*/reader, false));
readerSource->setLooping(fLoopMode);

fReaderSource = readerSource;
fReader = reader;

carla_stdout("Using regular read file");
}

fLength = fReader->lengthInSamples;
fDoProcess = true;
}

PluginClassEND(AudioFilePlugin)
@@ -246,13 +287,13 @@ private:

static const NativePluginDescriptor audiofileDesc = {
/* category */ PLUGIN_CATEGORY_UTILITY,
/* hints */ static_cast<NativePluginHints>(PLUGIN_IS_RTSAFE|PLUGIN_HAS_UI|PLUGIN_NEEDS_UI_OPEN_SAVE),
/* hints */ static_cast<NativePluginHints>(PLUGIN_HAS_UI|PLUGIN_NEEDS_UI_OPEN_SAVE),
/* supports */ static_cast<NativePluginSupports>(0x0),
/* audioIns */ 0,
/* audioOuts */ 2,
/* midiIns */ 0,
/* midiOuts */ 0,
/* paramIns */ 0, // TODO - loopMode
/* paramIns */ 1,
/* paramOuts */ 0,
/* name */ "Audio File",
/* label */ "audiofile",


+ 61
- 82
source/modules/native-plugins/midi-file.cpp View File

@@ -18,7 +18,9 @@
#include "CarlaNative.hpp"
#include "midi-base.hpp"

#include <smf.h>
#include "juce_audio_basics.h"

// -----------------------------------------------------------------------

class MidiFilePlugin : public NativePluginClass,
public AbstractMidiPlayer
@@ -27,13 +29,7 @@ public:
MidiFilePlugin(const NativeHostDescriptor* const host)
: NativePluginClass(host),
fMidiOut(this),
fWasPlayingBefore(false)
{
}

~MidiFilePlugin() override
{
}
fWasPlayingBefore(false) {}

protected:
// -------------------------------------------------------------------
@@ -41,8 +37,8 @@ protected:

void setCustomData(const char* const key, const char* const value) override
{
CARLA_ASSERT(key != nullptr);
CARLA_ASSERT(value != nullptr);
CARLA_SAFE_ASSERT_RETURN(key != nullptr && key[0] != '\0',);
CARLA_SAFE_ASSERT_RETURN(value != nullptr && value[0] != '\0',);

if (std::strcmp(key, "file") != 0)
return;
@@ -127,88 +123,71 @@ private:
{
fMidiOut.clear();

if (smf_t* const smf = smf_load(filename))
using juce::File;
using juce::FileInputStream;
using juce::MidiFile;
using juce::MidiMessage;
using juce::MidiMessageSequence;
using juce::ScopedPointer;

File file(filename);

if (! file.existsAsFile())
return;

FileInputStream fileStream(file);
MidiFile midiFile;

if (! midiFile.readFrom(fileStream))
return;

midiFile.convertTimestampTicksToSeconds();

const double sampleRate(getSampleRate());

for (int i=0, numTracks = midiFile.getNumTracks(); i<numTracks; ++i)
{
smf_event_t* event;
const double sampleRate(getSampleRate());
const MidiMessageSequence* const track(midiFile.getTrack(i));
CARLA_SAFE_ASSERT_CONTINUE(track != nullptr);

while ((event = smf_get_next_event(smf)) != nullptr)
for (int j=0, numEvents = track->getNumEvents(); j<numEvents; ++j)
{
if (smf_event_is_valid(event) == 0)
const MidiMessageSequence::MidiEventHolder* const midiEventHolder(track->getEventPointer(j));
CARLA_SAFE_ASSERT_CONTINUE(midiEventHolder != nullptr);

const MidiMessage& midiMessage(midiEventHolder->message);
//const double time(track->getEventTime(i)*sampleRate);
const int dataSize(midiMessage.getRawDataSize());

if (dataSize <= 0 || dataSize > MAX_EVENT_DATA_SIZE)
continue;
if (midiMessage.isActiveSense())
continue;
if (midiMessage.isMetaEvent())
continue;
if (midiMessage.isMidiStart())
continue;
if (smf_event_is_metadata(event))
if (midiMessage.isMidiContinue())
continue;
if (smf_event_is_system_realtime(event))
if (midiMessage.isMidiStop())
continue;
if (smf_event_is_system_common(event))
if (midiMessage.isMidiClock())
continue;
if (event->midi_buffer_length <= 0 || event->midi_buffer_length > MAX_EVENT_DATA_SIZE)
if (midiMessage.isSongPositionPointer())
continue;
if (midiMessage.isQuarterFrame())
continue;
if (midiMessage.isFullFrame())
continue;
if (midiMessage.isMidiMachineControlMessage())
continue;
if (midiMessage.isSysEx())
continue;

const uint64_t time(uint64_t(event->time_seconds*sampleRate));

#if 1
fMidiOut.addRaw(time, event->midi_buffer, uint8_t(event->midi_buffer_length));
#else
const uint8_t status = MIDI_GET_STATUS_FROM_DATA(event->midi_buffer);
const uint8_t channel = MIDI_GET_CHANNEL_FROM_DATA(event->midi_buffer);

uint8_t nextBank[MAX_MIDI_CHANNELS] = { 0 };

if (MIDI_IS_STATUS_NOTE_OFF(status))
{
const uint8_t note = event->midi_buffer[1];
const uint8_t velo = event->midi_buffer[2];

fMidiOut.addNoteOff(time, channel, note, velo);
}
else if (MIDI_IS_STATUS_NOTE_ON(status))
{
const uint8_t note = event->midi_buffer[1];
const uint8_t velo = event->midi_buffer[2];

fMidiOut.addNoteOn(time, channel, note, velo);
}
else if (MIDI_IS_STATUS_POLYPHONIC_AFTERTOUCH(status))
{
const uint8_t note = event->midi_buffer[1];
const uint8_t pressure = event->midi_buffer[2];

fMidiOut.addNoteAftertouch(time, channel, note, pressure);
}
else if (MIDI_IS_STATUS_CONTROL_CHANGE(status))
{
const uint8_t control = event->midi_buffer[1];
const uint8_t value = (event->midi_buffer_length > 2) ? event->midi_buffer[2] : 0;

if (MIDI_IS_CONTROL_BANK_SELECT(control))
nextBank[channel] = value;
else
fMidiOut.addControl(time, channel, control, value);
}
else if (MIDI_IS_STATUS_PROGRAM_CHANGE(status))
{
const uint8_t program = event->midi_buffer[1];

fMidiOut.addProgram(time, channel, nextBank[channel], program);
}
else if (MIDI_IS_STATUS_AFTERTOUCH(status))
{
const uint8_t pressure = event->midi_buffer[1];

fMidiOut.addChannelPressure(time, channel, pressure);
}
else if (MIDI_IS_STATUS_PITCH_WHEEL_CONTROL(status))
{
const uint8_t lsb = event->midi_buffer[1];
const uint8_t msb = event->midi_buffer[2];

fMidiOut.addPitchbend(time, channel, lsb, msb);
}
#endif
}
const double time(midiMessage.getTimeStamp()*sampleRate);

smf_delete(smf);
fMidiOut.addRaw(time, midiMessage.getRawData(), static_cast<uint8_t>(dataSize));
}
}
}



Loading…
Cancel
Save