#include <glibmm/miscutils.h>
#include "ardour/ardour.h"
+#include "ardour/audio_unit.h"
#include "ardour/audioengine.h"
#include "ardour/audio_buffer.h"
#include "ardour/debug.h"
-#include "ardour/midi_buffer.h"
#include "ardour/filesystem_paths.h"
#include "ardour/io.h"
-#include "ardour/audio_unit.h"
+#include "ardour/midi_buffer.h"
#include "ardour/route.h"
#include "ardour/session.h"
#include "ardour/tempo.h"
#define ArdourFindNext AudioComponentFindNext
#endif
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace std;
using namespace PBD;
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
- UInt32 inNumberFrames,
+ UInt32 inNumberSamples,
AudioBufferList* ioData)
{
if (userData) {
- return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
+ return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberSamples, ioData);
}
return paramErr;
}
, comp (_comp)
, unit (new CAAudioUnit)
, initialized (false)
- , _current_block_size (0)
+ , _last_nframes (0)
+ , _current_latency (UINT_MAX)
, _requires_fixed_size_buffers (false)
, buffers (0)
, variable_inputs (false)
, bus_outputs (0)
, input_maxbuf (0)
, input_offset (0)
+ , cb_offsets (0)
, input_buffers (0)
, input_map (0)
- , frames_processed (0)
+ , samples_processed (0)
, audio_input_cnt (0)
, _parameter_listener (0)
, _parameter_listener_arg (0)
- , last_transport_rolling (false)
+ , transport_sample (0)
+ , transport_speed (0)
, last_transport_speed (0.0)
{
if (!preset_search_path_initialized) {
, comp (other.get_comp())
, unit (new CAAudioUnit)
, initialized (false)
- , _current_block_size (0)
, _last_nframes (0)
+ , _current_latency (UINT_MAX)
, _requires_fixed_size_buffers (false)
, buffers (0)
, variable_inputs (false)
, bus_outputs (0)
, input_maxbuf (0)
, input_offset (0)
+ , cb_offsets (0)
, input_buffers (0)
, input_map (0)
- , frames_processed (0)
+ , samples_processed (0)
+ , audio_input_cnt (0)
, _parameter_listener (0)
, _parameter_listener_arg (0)
+ , transport_sample (0)
+ , transport_speed (0)
+ , last_transport_speed (0.0)
{
init ();
free (buffers);
free (bus_inputs);
free (bus_outputs);
+ free (cb_offsets);
}
void
DEBUG_TRACE (DEBUG::AudioUnits, "count output elements\n");
unit->GetElementCount (kAudioUnitScope_Output, output_elements);
+ cb_offsets = (samplecnt_t*) calloc (input_elements, sizeof(samplecnt_t));
bus_inputs = (uint32_t*) calloc (input_elements, sizeof(uint32_t));
bus_outputs = (uint32_t*) calloc (output_elements, sizeof(uint32_t));
for (size_t i = 0; i < output_elements; ++i) {
- AudioUnitReset (unit->AU(), kAudioUnitScope_Output, i);
+ unit->Reset (kAudioUnitScope_Output, i);
AudioStreamBasicDescription fmt;
- UInt32 sz = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(unit->AU(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, i, &fmt, &sz);
+ err = unit->GetFormat (kAudioUnitScope_Output, i, fmt);
if (err == noErr) {
bus_outputs[i] = fmt.mChannelsPerFrame;
}
CFStringRef name;
- sz = sizeof (CFStringRef);
+ UInt32 sz = sizeof (CFStringRef);
if (AudioUnitGetProperty (unit->AU(), kAudioUnitProperty_ElementName, kAudioUnitScope_Output,
i, &name, &sz) == noErr
&& sz > 0) {
}
for (size_t i = 0; i < input_elements; ++i) {
- AudioUnitReset (unit->AU(), kAudioUnitScope_Input, i);
+ unit->Reset (kAudioUnitScope_Input, i);
AudioStreamBasicDescription fmt;
- UInt32 sz = sizeof(AudioStreamBasicDescription);
- err = AudioUnitGetProperty(unit->AU(), kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, i, &fmt, &sz);
+ err = unit->GetFormat (kAudioUnitScope_Input, i, fmt);
if (err == noErr) {
bus_inputs[i] = fmt.mChannelsPerFrame;
}
CFStringRef name;
- sz = sizeof (CFStringRef);
+ UInt32 sz = sizeof (CFStringRef);
if (AudioUnitGetProperty (unit->AU(), kAudioUnitProperty_ElementName, kAudioUnitScope_Input,
i, &name, &sz) == noErr
&& sz > 0) {
break;
}
- d.min_unbound = 0; // lower is bound
- d.max_unbound = 0; // upper is bound
d.update_steps();
descriptors.push_back (d);
return 0;
}
-framecnt_t
+samplecnt_t
AUPlugin::signal_latency () const
{
- return unit->Latency() * _session.frame_rate();
+ guint lat = g_atomic_int_get (&_current_latency);;
+ if (lat == UINT_MAX) {
+ lat = unit->Latency() * _session.sample_rate();
+ g_atomic_int_set (&_current_latency, lat);
+ }
+ return lat;
}
void
if ((err = unit->Initialize()) != noErr) {
error << string_compose (_("AUPlugin: %1 cannot initialize plugin (err = %2)"), name(), err) << endmsg;
} else {
- frames_processed = 0;
+ samples_processed = 0;
initialized = true;
}
}
AUPlugin::set_block_size (pframes_t nframes)
{
bool was_initialized = initialized;
- UInt32 numFrames = nframes;
+ UInt32 numSamples = nframes;
OSErr err;
if (initialized) {
deactivate ();
}
- DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("set MaximumFramesPerSlice in global scope to %1\n", numFrames));
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("set MaximumFramesPerSlice in global scope to %1\n", numSamples));
if ((err = unit->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
- 0, &numFrames, sizeof (numFrames))) != noErr) {
- error << string_compose (_("AU: cannot set max frames (err = %1)"), err) << endmsg;
+ 0, &numSamples, sizeof (numSamples))) != noErr) {
+ error << string_compose (_("AU: cannot set max samples (err = %1)"), err) << endmsg;
return -1;
}
activate ();
}
- _current_block_size = nframes;
-
return 0;
}
}
}
- streamFormat.mSampleRate = _session.frame_rate();
+ streamFormat.mSampleRate = _session.sample_rate();
streamFormat.mFormatID = kAudioFormatLinearPCM;
streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved;
configured_output_busses = 0;
/* reset busses */
for (size_t i = 0; i < output_elements; ++i) {
- AudioUnitReset (unit->AU(), kAudioUnitScope_Output, i);
+ unit->Reset (kAudioUnitScope_Output, i);
}
for (size_t i = 0; i < input_elements; ++i) {
- AudioUnitReset (unit->AU(), kAudioUnitScope_Input, i);
+ unit->Reset (kAudioUnitScope_Input, i);
}
/* now assign the channels to available busses */
uint32_t used_in = 0;
uint32_t used_out = 0;
- if (variable_inputs) {
+ if (variable_inputs || input_elements == 1) {
// we only ever use the first bus
if (input_elements > 1) {
warning << string_compose (_("AU %1 has multiple input busses and variable port count."), name()) << endmsg;
}
}
- if (variable_outputs) {
+ if (variable_outputs || output_elements == 1) {
if (output_elements > 1) {
warning << string_compose (_("AU %1 has multiple output busses and variable port count."), name()) << endmsg;
}
output_channels = used_out;
/* reset plugin info to show currently configured state */
- _info->n_inputs = in;
- _info->n_outputs = out;
+ _info->n_inputs = ChanCount (DataType::AUDIO, used_in) + ChanCount (DataType::MIDI, _has_midi_input ? 1 : 0);
+ _info->n_outputs = ChanCount (DataType::AUDIO, used_out);
if (was_initialized) {
activate ();
return false;
}
- vector<pair<int,int> >& io_configs = pinfo->cache.io_configs;
+ vector<pair<int,int> > io_configs = pinfo->cache.io_configs;
+
+ if (input_elements > 1) {
+ const vector<pair<int,int> >& ioc (pinfo->cache.io_configs);
+ for (vector<pair<int,int> >::const_iterator i = ioc.begin(); i != ioc.end(); ++i) {
+ int32_t possible_in = i->first;
+ int32_t possible_out = i->second;
+ if (possible_in < 0) {
+ continue;
+ }
+ for (uint32_t i = 1; i < input_elements; ++i) {
+ // can't use up-to bus_inputs[]
+ // waves' SC-C6(s) for example fails to configure with only 1 input
+ // on the 2nd bus.
+ io_configs.push_back (pair<int,int> (possible_in + bus_inputs[i], possible_out));
+ }
+ /* only add additional, optional busses to first available config.
+ * AUPluginInfo::cached_io_configuration () already incrementally
+ * adds busses (for instruments w/ multiple configurations)
+ */
+ break;
+ }
+ }
+
+ if (output_elements > 1) {
+ const vector<pair<int,int> >& ioc (pinfo->cache.io_configs);
+ for (vector<pair<int,int> >::const_iterator i = ioc.begin(); i != ioc.end(); ++i) {
+ int32_t possible_in = i->first;
+ int32_t possible_out = i->second;
+ if (possible_out < 0) {
+ continue;
+ }
+ for (uint32_t i = 1; i < output_elements; ++i) {
+ int32_t c = bus_outputs[i];
+ for (uint32_t j = 1; j < i; ++j) {
+ c += bus_outputs [j];
+ }
+ io_configs.push_back (pair<int,int> (possible_in, possible_out + c));
+ }
+ /* only add additional, optional busses to first available config.
+ * AUPluginInfo::cached_io_configuration () already incrementally
+ * adds busses (for instruments w/ multiple configurations)
+ */
+ break;
+ }
+ }
DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 has %2 IO configurations, looking for %3 in, %4 out\n",
name(), io_configs.size(), in, out));
#if 0
- printf ("AU I/O Configs %s %d\n", name().c_str(), io_configs.size());
+ printf ("AU I/O Configs %s %d\n", name(), io_configs.size());
for (vector<pair<int,int> >::iterator i = io_configs.begin(); i != io_configs.end(); ++i) {
printf ("- I/O %d / %d\n", i->first, i->second);
}
_output_configs.insert (0);
#define UPTO(nch) { \
- for (int n = 1; n < nch; ++n) { \
+ for (int n = 1; n <= nch; ++n) { \
_output_configs.insert (n); \
} \
}
AUPlugin::render_callback(AudioUnitRenderActionFlags*,
const AudioTimeStamp*,
UInt32 bus,
- UInt32 inNumberFrames,
+ UInt32 inNumberSamples,
AudioBufferList* ioData)
{
/* not much to do with audio - the data is already in the buffers given to us in connect_and_run() */
- cerr << string_compose ("%1: render callback, frames %2 bus %3 bufs %4\n",
- name(), inNumberFrames, bus, ioData->mNumberBuffers);
- // DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1: render callback, frames %2 bus %3 bufs %4\n",
- // name(), inNumberFrames, bus, ioData->mNumberBuffers));
+ // DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1: render callback, samples %2 bus %3 bufs %4\n",
+ // name(), inNumberSamples, bus, ioData->mNumberBuffers));
if (input_maxbuf == 0) {
DEBUG_TRACE (DEBUG::AudioUnits, "AUPlugin: render callback called illegally!");
for (uint32_t i = 0; i < limit; ++i) {
ioData->mBuffers[i].mNumberChannels = 1;
- ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberFrames;
+ ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberSamples;
bool valid = false;
uint32_t idx = input_map->get (DataType::AUDIO, i + busoff, &valid);
if (valid) {
- ioData->mBuffers[i].mData = input_buffers->get_audio (idx).data (cb_offset + input_offset);
+ ioData->mBuffers[i].mData = input_buffers->get_audio (idx).data (cb_offsets[bus] + input_offset);
} else {
- ioData->mBuffers[i].mData = silent_bufs.get_audio(0).data (cb_offset + input_offset);
+ ioData->mBuffers[i].mData = silent_bufs.get_audio(0).data (cb_offsets[bus] + input_offset);
}
}
-#if 0 // TODO per bus
- cb_offset += inNumberFrames;
-#endif
+ cb_offsets[bus] += inNumberSamples;
return noErr;
}
int
-AUPlugin::connect_and_run (BufferSet& bufs, ChanMapping in_map, ChanMapping out_map, pframes_t nframes, framecnt_t offset)
+AUPlugin::connect_and_run (BufferSet& bufs,
+ samplepos_t start, samplepos_t end, double speed,
+ ChanMapping const& in_map, ChanMapping const& out_map,
+ pframes_t nframes, samplecnt_t offset)
{
- Plugin::connect_and_run (bufs, in_map, out_map, nframes, offset);
+ Plugin::connect_and_run(bufs, start, end, speed, in_map, out_map, nframes, offset);
+
+ transport_sample = start;
+ transport_speed = speed;
AudioUnitRenderActionFlags flags = 0;
AudioTimeStamp ts;
}
/* test if we can run in-place; only compare audio buffers */
- bool inplace = true; // configured_output_busses == 1;
+ bool inplace = true; // TODO check plugin-insert in-place ?
ChanMapping::Mappings inmap (in_map.mappings ());
ChanMapping::Mappings outmap (out_map.mappings ());
- assert (outmap[DataType::AUDIO].size () > 0);
+ if (outmap[DataType::AUDIO].size () == 0) {
+ inplace = false;
+ }
if (inmap[DataType::AUDIO].size() > 0 && inmap != outmap) {
inplace = false;
}
- DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 in %2 out %3 MIDI %4 bufs %5 (available %6) Inplace: %7\n",
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 in %2 out %3 MIDI %4 bufs %5 (available %6) InBus %7 OutBus %8 Inplace: %9 var-i/o %10 %11\n",
name(), input_channels, output_channels, _has_midi_input,
- bufs.count(), bufs.available(), inplace));
+ bufs.count(), bufs.available(),
+ configured_input_busses, configured_output_busses, inplace, variable_inputs, variable_outputs));
/* the apparent number of buffers matches our input configuration, but we know that the bufferset
* has the capacity to handle our outputs.
input_map = &in_map;
input_maxbuf = bufs.count().n_audio(); // number of input audio buffers
input_offset = offset;
- cb_offset = 0;
+ for (size_t i = 0; i < input_elements; ++i) {
+ cb_offsets[i] = 0;
+ }
ChanCount bufs_count (DataType::AUDIO, 1);
BufferSet& scratch_bufs = _session.get_scratch_buffers(bufs_count);
/* one MIDI port/buffer only */
MidiBuffer& m = bufs.get_midi (i);
for (MidiBuffer::iterator i = m.begin(); i != m.end(); ++i) {
- Evoral::MIDIEvent<framepos_t> ev (*i);
+ Evoral::Event<samplepos_t> ev (*i);
if (ev.is_channel_event()) {
const uint8_t* b = ev.buffer();
DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1: MIDI event %2\n", name(), ev));
}
}
+ assert (input_maxbuf < 512);
+ std::bitset<512> used_outputs;
+
bool ok = true;
uint32_t busoff = 0;
uint32_t remain = output_channels;
for (uint32_t bus = 0; remain > 0 && bus < configured_output_busses; ++bus) {
- uint32_t cnt = std::min (remain, bus_outputs[bus]);
+ uint32_t cnt;
+ if (variable_outputs || (output_elements == configured_output_busses && configured_output_busses == 1)) {
+ cnt = output_channels;
+ } else {
+ cnt = std::min (remain, bus_outputs[bus]);
+ }
assert (cnt > 0);
buffers->mNumberBuffers = cnt;
}
/* does this really mean anything ? */
- ts.mSampleTime = frames_processed;
+ ts.mSampleTime = samples_processed;
ts.mFlags = kAudioTimeStampSampleTimeValid;
DEBUG_TRACE (DEBUG::AudioUnits, string_compose ("%1 render flags=%2 time=%3 nframes=%4 bus=%5 buffers=%6\n",
- name(), flags, frames_processed, nframes, bus, buffers->mNumberBuffers));
+ name(), flags, samples_processed, nframes, bus, buffers->mNumberBuffers));
if ((err = unit->Render (&flags, &ts, bus, nframes, buffers)) == noErr) {
bool valid = false;
uint32_t idx = out_map.get (DataType::AUDIO, i + busoff, &valid);
if (!valid) continue;
+ used_outputs.set (i + busoff);
Sample* expected_buffer_address = bufs.get_audio (idx).data (offset);
if (expected_buffer_address != buffers->mBuffers[i].mData) {
/* plugin provided its own buffer for output so copy it back to where we want it */
}
}
} else {
- error << string_compose (_("AU: render error for %1, bus %2 status = %2"), name(), bus, err) << endmsg;
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose (_("AU: render error for %1, bus %2 status = %3\n"), name(), bus, err));
+ error << string_compose (_("AU: render error for %1, bus %2 status = %3"), name(), bus, err) << endmsg;
ok = false;
break;
}
remain -= cnt;
busoff += bus_outputs[bus];
}
-#if 0
+
/* now silence any buffers that were passed in but the that the plugin
* did not fill/touch/use.
+ *
+ * TODO: optimize, when plugin-insert is processing in-place
+ * unconnected buffers are (also) cleared there.
*/
- for (;i < output_channels; ++i) {
+ for (uint32_t i = 0; i < input_maxbuf; ++i) {
+ if (used_outputs.test (i)) { continue; }
bool valid = false;
uint32_t idx = out_map.get (DataType::AUDIO, i, &valid);
if (!valid) continue;
memset (bufs.get_audio (idx).data (offset), 0, nframes * sizeof (Sample));
}
-#endif
input_maxbuf = 0;
if (ok) {
- frames_processed += nframes;
+ samples_processed += nframes;
return 0;
}
return -1;
DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour beat&tempo callback\n");
- /* more than 1 meter or more than 1 tempo means that a simplistic computation
- (and interpretation) of a beat position will be incorrect. So refuse to
- offer the value.
- */
-
- if (tmap.n_tempos() > 1 || tmap.n_meters() > 1) {
- return kAudioUnitErr_CannotDoInCurrentContext;
- }
-
- Timecode::BBT_Time bbt;
- TempoMetric metric = tmap.metric_at (_session.transport_frame() + input_offset);
- tmap.bbt_time (_session.transport_frame() + input_offset, bbt);
-
if (outCurrentBeat) {
- float beat;
- beat = metric.meter().divisions_per_bar() * bbt.bars;
- beat += bbt.beats;
- beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
- *outCurrentBeat = beat;
+ *outCurrentBeat = tmap.quarter_note_at_sample (transport_sample + input_offset);
}
if (outCurrentTempo) {
- *outCurrentTempo = floor (metric.tempo().beats_per_minute());
+ *outCurrentTempo = tmap.tempo_at_sample (transport_sample + input_offset).quarter_notes_per_minute();
}
return noErr;
DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour music time location callback\n");
- /* more than 1 meter or more than 1 tempo means that a simplistic computation
- (and interpretation) of a beat position will be incorrect. So refuse to
- offer the value.
- */
-
- if (tmap.n_tempos() > 1 || tmap.n_meters() > 1) {
- return kAudioUnitErr_CannotDoInCurrentContext;
- }
-
- Timecode::BBT_Time bbt;
- TempoMetric metric = tmap.metric_at (_session.transport_frame() + input_offset);
- tmap.bbt_time (_session.transport_frame() + input_offset, bbt);
+ TempoMetric metric = tmap.metric_at (transport_sample + input_offset);
+ Timecode::BBT_Time bbt = _session.tempo_map().bbt_at_sample (transport_sample + input_offset);
if (outDeltaSampleOffsetToNextBeat) {
if (bbt.ticks == 0) {
/* on the beat */
*outDeltaSampleOffsetToNextBeat = 0;
} else {
- *outDeltaSampleOffsetToNextBeat = (UInt32)
- floor (((Timecode::BBT_Time::ticks_per_beat - bbt.ticks)/Timecode::BBT_Time::ticks_per_beat) * // fraction of a beat to next beat
- metric.tempo().frames_per_beat (_session.frame_rate())); // frames per beat
+ double const next_beat = ceil (tmap.quarter_note_at_sample (transport_sample + input_offset));
+ samplepos_t const next_beat_sample = tmap.sample_at_quarter_note (next_beat);
+
+ *outDeltaSampleOffsetToNextBeat = next_beat_sample - (transport_sample + input_offset);
}
}
3|1|0 -> 1 + (2 * divisions_per_bar)
etc.
*/
+ bbt.beats = 1;
+ bbt.ticks = 0;
- *outCurrentMeasureDownBeat = 1 + metric.meter().divisions_per_bar() * (bbt.bars - 1);
+ *outCurrentMeasureDownBeat = tmap.quarter_note_at_bbt (bbt);
}
return noErr;
Float64* outCycleStartBeat,
Float64* outCycleEndBeat)
{
- bool rolling;
- float speed;
+ const bool rolling = (transport_speed != 0);
+ const bool last_transport_rolling = (last_transport_speed != 0);
DEBUG_TRACE (DEBUG::AudioUnits, "AU calls ardour transport state callback\n");
- rolling = _session.transport_rolling();
- speed = _session.transport_speed ();
if (outIsPlaying) {
- *outIsPlaying = _session.transport_rolling();
+ *outIsPlaying = rolling;
}
if (outTransportStateChanged) {
if (rolling != last_transport_rolling) {
*outTransportStateChanged = true;
- } else if (speed != last_transport_speed) {
+ } else if (transport_speed != last_transport_speed) {
*outTransportStateChanged = true;
} else {
*outTransportStateChanged = false;
/* this assumes that the AU can only call this host callback from render context,
where input_offset is valid.
*/
- *outCurrentSampleInTimeLine = _session.transport_frame() + input_offset;
+ *outCurrentSampleInTimeLine = transport_sample + input_offset;
}
if (outIsCycling) {
+ // TODO check bounce-processing
Location* loc = _session.locations()->auto_loop_location();
- *outIsCycling = (loc && _session.transport_rolling() && _session.get_play_loop());
+ *outIsCycling = (loc && rolling && _session.get_play_loop());
if (*outIsCycling) {
TempoMap& tmap (_session.tempo_map());
- /* more than 1 meter means that a simplistic computation (and interpretation) of
- a beat position will be incorrect. so refuse to offer the value.
- */
-
- if (tmap.n_meters() > 1) {
- return kAudioUnitErr_CannotDoInCurrentContext;
- }
-
Timecode::BBT_Time bbt;
if (outCycleStartBeat) {
- TempoMetric metric = tmap.metric_at (loc->start() + input_offset);
- _session.tempo_map().bbt_time (loc->start(), bbt);
-
- float beat;
- beat = metric.meter().divisions_per_bar() * bbt.bars;
- beat += bbt.beats;
- beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
-
- *outCycleStartBeat = beat;
+ *outCycleStartBeat = tmap.quarter_note_at_sample (loc->start() + input_offset);
}
if (outCycleEndBeat) {
- TempoMetric metric = tmap.metric_at (loc->end() + input_offset);
- _session.tempo_map().bbt_time (loc->end(), bbt);
-
- float beat;
- beat = metric.meter().divisions_per_bar() * bbt.bars;
- beat += bbt.beats;
- beat += bbt.ticks / Timecode::BBT_Time::ticks_per_beat;
-
- *outCycleEndBeat = beat;
+ *outCycleEndBeat = tmap.quarter_note_at_sample (loc->end() + input_offset);
}
}
}
}
- last_transport_rolling = rolling;
- last_transport_speed = speed;
+ last_transport_speed = transport_speed;
return noErr;
}
break;
}
+ std::string busname;
+
if (dt == DataType::AUDIO) {
if (input) {
uint32_t pid = id;
if (pid < bus_inputs[bus]) {
id = pid;
ss << _bus_name_in[bus];
+ ss << " / Bus " << (1 + bus);
+ busname = _bus_name_in[bus];
break;
}
pid -= bus_inputs[bus];
if (pid < bus_outputs[bus]) {
id = pid;
ss << _bus_name_out[bus];
+ ss << " / Bus " << (1 + bus);
+ busname = _bus_name_out[bus];
break;
}
pid -= bus_outputs[bus];
ss << (id + 1);
Plugin::IOPortDescription iod (ss.str());
+ if (!busname.empty()) {
+ iod.group_name = busname;
+ iod.group_channel = id;
+ }
return iod;
}
}
void
-AUPlugin::print_parameter (uint32_t /*param*/, char* /*buf*/, uint32_t /*len*/) const
+AUPlugin::print_parameter (uint32_t param, char* buf, uint32_t len) const
{
// NameValue stuff here
+ if (buf && len) {
+ if (param < parameter_count()) {
+ snprintf (buf, len, "%.3f", get_parameter (param));
+ } else {
+ strcat (buf, "0");
+ }
+ }
}
bool
void
AUPlugin::add_state (XMLNode* root) const
{
- LocaleGuard lg (X_("C"));
+ LocaleGuard lg;
CFDataRef xmlData;
CFPropertyListRef propertyList;
{
int ret = -1;
CFPropertyListRef propertyList;
- LocaleGuard lg (X_("C"));
+ LocaleGuard lg;
if (node.name() != state_node_name()) {
error << _("Bad node sent to AUPlugin::set_state") << endmsg;
DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Saving Preset to %1\n", user_preset_path));
- return string ("file:///") + user_preset_path;
+ return user_preset_path;
}
//-----------------------------------------------------------------------------
/* add factory presets */
for (FactoryPresetMap::iterator i = factory_preset_map.begin(); i != factory_preset_map.end(); ++i) {
- /* XXX: dubious */
- string const uri = string_compose ("%1", _presets.size ());
+ /* XXX: dubious -- deleting & re-adding a preset -> same URI
+ * good that we don't support deleting AU presets :)
+ */
+ string const uri = PBD::to_string<uint32_t> (_presets.size ());
_presets.insert (make_pair (uri, Plugin::PresetRecord (uri, i->first, false)));
DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Adding Factory Preset: %1 > %2\n", i->first, i->second));
}
if (!Glib::file_test (au_cache_path(), Glib::FILE_TEST_EXISTS)) {
ARDOUR::BootMessage (_("Discovering AudioUnit plugins (could take some time ...)"));
+ // flush RAM cache -- after clear_cache()
+ cached_info.clear();
}
// create crash log file
au_start_crashlog ();
continue;
case kAudioUnitType_Output:
- info->category = _("AudioUnit Outputs");
+ info->category = _("Output");
break;
case kAudioUnitType_MusicDevice:
- info->category = _("AudioUnit Instruments");
+ info->category = _("Instrument");
has_midi_in = true;
break;
case kAudioUnitType_MusicEffect:
- info->category = _("AudioUnit MusicEffects");
+ info->category = _("Effect");
has_midi_in = true;
break;
case kAudioUnitType_Effect:
- info->category = _("AudioUnit Effects");
+ info->category = _("Effect");
break;
case kAudioUnitType_Mixer:
- info->category = _("AudioUnit Mixers");
+ info->category = _("Mixer");
break;
case kAudioUnitType_Generator:
- info->category = _("AudioUnit Generators");
+ info->category = _("Generator");
break;
default:
- info->category = _("AudioUnit (Unknown)");
+ info->category = _("(Unknown)");
break;
}
}
if (ret > 0) {
-
- /* no explicit info available, so default to 1in/1out */
-
- /* XXX this is wrong. we should be indicating wildcard values */
-
+ /* AU is expected to deal with same channel valance in and out */
cinfo.io_configs.push_back (pair<int,int> (-1, -1));
-
} else {
- /* store each configuration */
- if (comp.Desc().IsGenerator() || comp.Desc().IsMusicDevice()) {
- // incrementally add busses
+ /* CAAudioUnit::GetChannelInfo silently merges bus formats
+ * check if this was the case and if so, add
+ * bus configs as incremental options.
+ */
+ Boolean* isWritable = 0;
+ UInt32 dataSize = 0;
+ OSStatus result = AudioUnitGetPropertyInfo (unit.AU(),
+ kAudioUnitProperty_SupportedNumChannels,
+ kAudioUnitScope_Global, 0,
+ &dataSize, isWritable);
+ if (result != noErr && (comp.Desc().IsGenerator() || comp.Desc().IsMusicDevice())) {
+ /* incrementally add busses */
int in = 0;
int out = 0;
for (uint32_t n = 0; n < cnt; ++n) {
cinfo.io_configs.push_back (pair<int,int> (in, out));
}
} else {
+ /* store each configuration */
for (uint32_t n = 0; n < cnt; ++n) {
cinfo.io_configs.push_back (pair<int,int> (channel_info[n].inChannels,
channel_info[n].outChannels));
return 0;
}
+void
+AUPluginInfo::clear_cache ()
+{
+ const string& fn = au_cache_path();
+ if (Glib::file_test (fn, Glib::FILE_TEST_EXISTS)) {
+ ::g_unlink(fn.c_str());
+ }
+ // keep cached_info in RAM until restart or re-scan
+ cached_info.clear();
+}
+
void
AUPluginInfo::add_cached_info (const std::string& id, AUPluginCachedInfo& cinfo)
{
XMLNode* node;
node = new XMLNode (X_("AudioUnitPluginCache"));
- node->add_property( "version", AU_CACHE_VERSION );
+ node->set_property( "version", AU_CACHE_VERSION );
for (map<string,AUPluginCachedInfo>::iterator i = cached_info.begin(); i != cached_info.end(); ++i) {
XMLNode* parent = new XMLNode (X_("plugin"));
- parent->add_property ("id", i->first);
+ parent->set_property ("id", i->first);
node->add_child_nocopy (*parent);
for (vector<pair<int, int> >::iterator j = i->second.io_configs.begin(); j != i->second.io_configs.end(); ++j) {
XMLNode* child = new XMLNode (X_("io"));
- char buf[32];
- snprintf (buf, sizeof (buf), "%d", j->first);
- child->add_property (X_("in"), buf);
- snprintf (buf, sizeof (buf), "%d", j->second);
- child->add_property (X_("out"), buf);
+ child->set_property (X_("in"), j->first);
+ child->set_property (X_("out"), j->second);
parent->add_child_nocopy (*child);
}
}
//initial version has incorrectly stored i/o info, and/or garbage chars.
- const XMLProperty* version = root->property(X_("version"));
+ XMLProperty const * version = root->property(X_("version"));
if (! ((version != NULL) && (version->value() == X_(AU_CACHE_VERSION)))) {
error << "au_cache is not correct version. AU plugins will be re-scanned" << endmsg;
return -1;
const XMLNode* gchild;
const XMLNodeList gchildren = child->children();
- const XMLProperty* prop = child->property (X_("id"));
- if (!prop) {
+ string id;
+ if (!child->get_property (X_("id"), id)) {
continue;
}
- string id = prop->value();
string fixed;
string version;
if (gchild->name() == X_("io")) {
- int in;
- int out;
- const XMLProperty* iprop;
- const XMLProperty* oprop;
-
- if (((iprop = gchild->property (X_("in"))) != 0) &&
- ((oprop = gchild->property (X_("out"))) != 0)) {
- in = atoi (iprop->value());
- out = atoi (oprop->value());
+ int32_t in;
+ int32_t out;
+ if (gchild->get_property (X_("in"), in) && gchild->get_property (X_("out"), out)) {
cinfo.io_configs.push_back (pair<int,int> (in, out));
}
}
return descriptor->IsMusicDevice();
}
+bool
+AUPluginInfo::is_utility () const
+{
+ return (descriptor->IsGenerator() || descriptor->componentType == 'aumi');
+ // kAudioUnitType_MidiProcessor ..looks like we aren't even scanning for these yet?
+}
+
void
AUPlugin::set_info (PluginInfoPtr info)
{
_parameter_listener_arg = arg;
+ // listen for latency changes
+ AudioUnitEvent event;
+ event.mEventType = kAudioUnitEvent_PropertyChange;
+ event.mArgument.mProperty.mAudioUnit = unit->AU();
+ event.mArgument.mProperty.mPropertyID = kAudioUnitProperty_Latency;
+ event.mArgument.mProperty.mScope = kAudioUnitScope_Global;
+ event.mArgument.mProperty.mElement = 0;
+
+ if (AUEventListenerAddEventType (_parameter_listener, _parameter_listener_arg, &event) != noErr) {
+ PBD::error << "Failed to create latency event listener\n";
+ // TODO don't cache _current_latency
+ }
+
return 0;
}
void
AUPlugin::parameter_change_listener (void* /*arg*/, void* src, const AudioUnitEvent* event, UInt64 /*host_time*/, Float32 new_value)
{
+ if (event->mEventType == kAudioUnitEvent_PropertyChange) {
+ if (event->mArgument.mProperty.mPropertyID == kAudioUnitProperty_Latency) {
+ DEBUG_TRACE (DEBUG::AudioUnits, string_compose("AU Latency Change Event %1 <> %2\n", new_value, unit->Latency()));
+ guint lat = unit->Latency() * _session.sample_rate();
+ g_atomic_int_set (&_current_latency, lat);
+ }
+ return;
+ }
+
ParameterMap::iterator i;
if ((i = parameter_map.find (event->mArgument.mParameter.mParameterID)) == parameter_map.end()) {
/* whenever we change a parameter, we request that we are NOT notified of the change, so anytime we arrive here, it
means that something else (i.e. the plugin GUI) made the change.
*/
- ParameterChangedExternally (i->second, new_value);
+ Plugin::parameter_changed_externally (i->second, new_value);
break;
default:
break;