mirror of
https://github.com/Ardour/ardour.git
synced 2026-01-09 15:15:41 +01:00
add TriggerBox processor (extremely prototypical)
This takes some barely working ideas from the old ableton branch and turns it into a processor instead of a separate track object
This commit is contained in:
parent
b6f290add2
commit
b8a3b25dee
3 changed files with 343 additions and 0 deletions
99
libs/ardour/ardour/triggerbox.h
Normal file
99
libs/ardour/ardour/triggerbox.h
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Paul Davis <paul@linuxaudiosystems.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
|
||||
* (at your option) 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef __ardour_triggerbox_h__
|
||||
#define __ardour_triggerbox_h__
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <exception>
|
||||
|
||||
#include <glibmm/threads.h>
|
||||
|
||||
#include "pbd/ringbuffer.h"
|
||||
|
||||
#include "temporal/beats.h"
|
||||
|
||||
#include "ardour/processor.h"
|
||||
#include "ardour/libardour_visibility.h"
|
||||
|
||||
class XMLNode;
|
||||
|
||||
namespace ARDOUR {
|
||||
|
||||
class Session;
|
||||
class AudioRegion;
|
||||
class TriggerBox;
|
||||
|
||||
class LIBARDOUR_API Trigger {
|
||||
public:
|
||||
Trigger() {}
|
||||
virtual ~Trigger() {}
|
||||
|
||||
virtual void bang (TriggerBox&, Temporal::Beats const &, samplepos_t) = 0;
|
||||
};
|
||||
|
||||
class LIBARDOUR_API AudioTrigger : public Trigger {
|
||||
public:
|
||||
AudioTrigger (boost::shared_ptr<AudioRegion>);
|
||||
~AudioTrigger ();
|
||||
|
||||
void bang (TriggerBox&, Temporal::Beats const & , samplepos_t);
|
||||
Sample* run (uint32_t channel, pframes_t& nframes, samplepos_t start_frame, samplepos_t end_frame, bool& need_butler);
|
||||
|
||||
private:
|
||||
boost::shared_ptr<AudioRegion> region;
|
||||
bool running;
|
||||
std::vector<Sample*> data;
|
||||
samplecnt_t read_index;
|
||||
samplecnt_t length;
|
||||
};
|
||||
|
||||
class LIBARDOUR_API TriggerBox : public Processor
|
||||
{
|
||||
public:
|
||||
TriggerBox (Session&, std::string const & name);
|
||||
~TriggerBox ();
|
||||
|
||||
void run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required);
|
||||
bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
|
||||
|
||||
bool queue_trigger (Trigger*);
|
||||
void add_trigger (Trigger*);
|
||||
|
||||
private:
|
||||
PBD::RingBuffer<Trigger*> _trigger_queue;
|
||||
|
||||
typedef std::vector<Trigger*> Triggers;
|
||||
Triggers active_triggers;
|
||||
Glib::Threads::Mutex trigger_lock;
|
||||
Triggers all_triggers;
|
||||
|
||||
void note_on (int note_number, int velocity);
|
||||
void note_off (int note_number, int velocity);
|
||||
|
||||
/* XXX for initial testing only */
|
||||
|
||||
boost::shared_ptr<Source> the_source;
|
||||
boost::shared_ptr<AudioRegion> the_region;
|
||||
AudioTrigger* the_trigger;
|
||||
};
|
||||
|
||||
} // namespace ARDOUR
|
||||
|
||||
#endif /* __ardour_triggerbox_h__ */
|
||||
243
libs/ardour/triggerbox.cc
Normal file
243
libs/ardour/triggerbox.cc
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
#include <iostream>
|
||||
|
||||
#include "ardour/audioregion.h"
|
||||
#include "ardour/audio_buffer.h"
|
||||
#include "ardour/midi_buffer.h"
|
||||
#include "ardour/region_factory.h"
|
||||
#include "ardour/sndfilesource.h"
|
||||
#include "ardour/triggerbox.h"
|
||||
|
||||
using namespace PBD;
|
||||
using namespace ARDOUR;
|
||||
using std::string;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
TriggerBox::TriggerBox (Session& s, std::string const & name)
|
||||
: Processor (s, name)
|
||||
, _trigger_queue (1024)
|
||||
{
|
||||
PropertyList plist;
|
||||
|
||||
the_source.reset (new SndFileSource (_session, "/usr/share/sounds/alsa/Front_Center.wav", 0, Source::Flag (0)));
|
||||
|
||||
plist.add (Properties::start, 0);
|
||||
plist.add (Properties::length, the_source->length (0));
|
||||
plist.add (Properties::name, string ("bang"));
|
||||
plist.add (Properties::layer, 0);
|
||||
plist.add (Properties::layering_index, 0);
|
||||
|
||||
boost::shared_ptr<Region> r = RegionFactory::create (the_source, plist, false);
|
||||
the_region = boost::dynamic_pointer_cast<AudioRegion> (r);
|
||||
|
||||
cerr << "Trigger track has region " << the_region->name() << " length = " << the_region->length() << endl;
|
||||
|
||||
/* XXX the_region/trigger will be looked up in a
|
||||
std::map<MIDI::byte,Trigger>
|
||||
*/
|
||||
the_trigger = new AudioTrigger (the_region);
|
||||
add_trigger (the_trigger);
|
||||
}
|
||||
|
||||
TriggerBox::~TriggerBox ()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
TriggerBox::can_support_io_configuration (const ChanCount& in, ChanCount& out)
|
||||
{
|
||||
if (in.get(DataType::MIDI) < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TriggerBox::note_on (int note_number, int velocity)
|
||||
{
|
||||
if (velocity == 0) {
|
||||
note_off (note_number, velocity);
|
||||
return;
|
||||
}
|
||||
|
||||
queue_trigger (the_trigger);
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::note_off (int note_number, int velocity)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::add_trigger (Trigger* trigger)
|
||||
{
|
||||
Glib::Threads::Mutex::Lock lm (trigger_lock);
|
||||
all_triggers.push_back (trigger);
|
||||
cerr << "Now have " << all_triggers.size() << " of all possible triggers\n";
|
||||
}
|
||||
|
||||
bool
|
||||
TriggerBox::queue_trigger (Trigger* trigger)
|
||||
{
|
||||
return _trigger_queue.write (&trigger, 1) == 1;
|
||||
}
|
||||
|
||||
void
|
||||
TriggerBox::run (BufferSet& bufs, samplepos_t start_sample, samplepos_t end_sample, double speed, pframes_t nframes, bool result_required)
|
||||
{
|
||||
/* check MIDI port input buffers for triggers */
|
||||
|
||||
for (BufferSet::midi_iterator mi = bufs.midi_begin(); mi != bufs.midi_end(); ++mi) {
|
||||
MidiBuffer& mb (*mi);
|
||||
|
||||
for (MidiBuffer::iterator ev = mb.begin(); ev != mb.end(); ++ev) {
|
||||
if ((*ev).is_note_on()) {
|
||||
cerr << "Trigger => NOTE ON!\n";
|
||||
note_on ((*ev).note(), (*ev).velocity());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* get tempo map */
|
||||
|
||||
/* find offset to next bar * and beat start
|
||||
*/
|
||||
|
||||
samplepos_t next_beat = 0;
|
||||
Temporal::Beats beats_now;
|
||||
|
||||
/* if next beat occurs in this process cycle, see if we have any triggers waiting
|
||||
*/
|
||||
|
||||
// bool run_beats = false;
|
||||
// bool run_bars = false;
|
||||
|
||||
//if (next_beat >= start_frame && next_beat < end_sample) {
|
||||
//run_beats = true;
|
||||
//}
|
||||
|
||||
/* if there are any triggers queued, run them.
|
||||
*/
|
||||
|
||||
RingBuffer<Trigger*>::rw_vector vec;
|
||||
_trigger_queue.get_read_vector (&vec);
|
||||
|
||||
for (uint32_t n = 0; n < vec.len[0]; ++n) {
|
||||
Trigger* t = vec.buf[0][n];
|
||||
t->bang (*this, beats_now, start_sample);
|
||||
active_triggers.push_back (t);
|
||||
cerr << "Trigger goes bang at " << start_sample << endl;
|
||||
}
|
||||
|
||||
for (uint32_t n = 0; n < vec.len[1]; ++n) {
|
||||
Trigger* t = vec.buf[1][n];
|
||||
t->bang (*this, beats_now, start_sample);
|
||||
active_triggers.push_back (t);
|
||||
cerr << "Trigger goes bang at " << start_sample << endl;
|
||||
}
|
||||
|
||||
_trigger_queue.increment_read_idx (vec.len[0] + vec.len[1]);
|
||||
|
||||
bool err = false;
|
||||
size_t chan = 0;
|
||||
const size_t nchans = bufs.count().get (DataType::AUDIO);
|
||||
bool need_butler = false;
|
||||
|
||||
for (Triggers::iterator t = active_triggers.begin(); !err && t != active_triggers.end(); ) {
|
||||
|
||||
|
||||
AudioTrigger* at = dynamic_cast<AudioTrigger*> (*t);
|
||||
|
||||
if (!at) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (uint32_t chan = 0; !err && chan < nchans; ++chan) {
|
||||
|
||||
AudioBuffer& buf = bufs.get_audio (chan);
|
||||
|
||||
pframes_t to_copy = nframes;
|
||||
|
||||
Sample* data = at->run (chan, to_copy, start_sample, end_sample, need_butler);
|
||||
|
||||
if (!data) {
|
||||
/* XXX need to delete the trigger/put it back in the pool */
|
||||
t = active_triggers.erase (t);
|
||||
err = true;
|
||||
} else {
|
||||
if (t == active_triggers.begin()) {
|
||||
buf.read_from (data, to_copy);
|
||||
} else {
|
||||
buf.accumulate_from (data, to_copy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------*/
|
||||
|
||||
AudioTrigger::AudioTrigger (boost::shared_ptr<AudioRegion> r)
|
||||
: region (r)
|
||||
, running (false)
|
||||
, data (0)
|
||||
, read_index (0)
|
||||
, length (0)
|
||||
{
|
||||
/* XXX catch region going away */
|
||||
|
||||
const uint32_t nchans = region->n_channels();
|
||||
|
||||
cerr << "Trigger needs " << nchans << " channels of " << region->length() << " each\n";
|
||||
|
||||
for (uint32_t n = 0; n < nchans; ++n) {
|
||||
data.push_back (new Sample (region->length()));;
|
||||
// region->read (data[n], 0, region->length(), n);
|
||||
}
|
||||
|
||||
length = region->length();
|
||||
}
|
||||
|
||||
AudioTrigger::~AudioTrigger ()
|
||||
{
|
||||
for (std::vector<Sample*>::iterator d = data.begin(); d != data.end(); ++d) {
|
||||
delete *d;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
AudioTrigger::bang (TriggerBox& /*proc*/, Temporal::Beats const &, samplepos_t)
|
||||
{
|
||||
/* user triggered this, and we need to get things set up for calls to
|
||||
* run()
|
||||
*/
|
||||
|
||||
read_index = 0;
|
||||
running = true;
|
||||
}
|
||||
|
||||
Sample*
|
||||
AudioTrigger::run (uint32_t channel, pframes_t& nframes, samplepos_t start_sample, samplepos_t end_sample, bool& need_butler)
|
||||
{
|
||||
if (!running) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (read_index >= length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (channel >= data.size()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nframes = (pframes_t) std::min ((samplecnt_t) nframes, (length - read_index));
|
||||
|
||||
Sample* ret = data[channel] + read_index;
|
||||
|
||||
read_index += nframes;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -264,6 +264,7 @@ libardour_sources = [
|
|||
'transport_master.cc',
|
||||
'transport_master_manager.cc',
|
||||
'transpose.cc',
|
||||
'triggerbox.cc',
|
||||
'unknown_processor.cc',
|
||||
'user_bundle.cc',
|
||||
'utils.cc',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue