comparison audioio/AudioGenerator.cpp @ 0:db6fcbd4405c

initial import
author Chris Cannam
date Tue, 10 Jan 2006 16:33:16 +0000
parents
children df5923e33d01
comparison
equal deleted inserted replaced
-1:000000000000 0:db6fcbd4405c
1 /* -*- c-basic-offset: 4 -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 A waveform viewer and audio annotation editor.
5 Chris Cannam, Queen Mary University of London, 2005
6
7 This is experimental software. Not for distribution.
8 */
9
10 #include "AudioGenerator.h"
11
12 #include "base/ViewManager.h"
13 #include "base/PlayParameters.h"
14
15 #include "model/DenseTimeValueModel.h"
16 #include "model/SparseOneDimensionalModel.h"
17
18 #include "plugin/RealTimePluginFactory.h"
19 #include "plugin/RealTimePluginInstance.h"
20 #include "plugin/PluginIdentifier.h"
21 #include "plugin/api/alsa/seq_event.h"
22
23 #include <iostream>
24
25 const size_t
26 AudioGenerator::m_pluginBlockSize = 2048;
27
28 // #define DEBUG_AUDIO_GENERATOR 1
29
30 AudioGenerator::AudioGenerator(ViewManager *manager) :
31 m_viewManager(manager),
32 m_sourceSampleRate(0),
33 m_targetChannelCount(1)
34 {
35 }
36
37 AudioGenerator::~AudioGenerator()
38 {
39 }
40
41 void
42 AudioGenerator::addModel(Model *model)
43 {
44 if (m_sourceSampleRate == 0) {
45
46 m_sourceSampleRate = model->getSampleRate();
47
48 } else {
49
50 DenseTimeValueModel *dtvm =
51 dynamic_cast<DenseTimeValueModel *>(model);
52
53 if (dtvm) {
54 m_sourceSampleRate = model->getSampleRate();
55 }
56 }
57
58 SparseOneDimensionalModel *sodm =
59 dynamic_cast<SparseOneDimensionalModel *>(model);
60 if (!sodm) return; // nothing else to initialise
61
62 // QString pluginId = "dssi:/usr/lib/dssi/dssi-vst.so:FEARkILLERrev1.dll";
63 // QString pluginId = "dssi:/usr/lib/dssi/hexter.so:hexter";
64 // QString pluginId = "dssi:/usr/lib/dssi/sineshaper.so:sineshaper";
65 // QString pluginId = "dssi:/usr/local/lib/dssi/xsynth-dssi.so:Xsynth";
66 // QString pluginId = "dssi:/usr/local/lib/dssi/trivial_synth.so:TS";
67 QString pluginId = QString("dssi:%1:sample_player").
68 arg(PluginIdentifier::BUILTIN_PLUGIN_SONAME);
69 RealTimePluginFactory *factory =
70 RealTimePluginFactory::instanceFor(pluginId);
71
72 if (!factory) {
73 std::cerr << "Failed to get plugin factory" << std::endl;
74 return;
75 }
76
77 RealTimePluginInstance *instance =
78 factory->instantiatePlugin
79 (pluginId, 0, 0, m_sourceSampleRate, m_pluginBlockSize, m_targetChannelCount);
80
81 if (instance) {
82 m_synthMap[sodm] = instance;
83 for (unsigned int i = 0; i < instance->getParameterCount(); ++i) {
84 instance->setParameterValue(i, instance->getParameterDefault(i));
85 }
86 QString program = instance->getProgram(0, 0);
87 if (program != "") {
88 std::cerr << "selecting program " << program.toLocal8Bit().data() << std::endl;
89 instance->selectProgram(program);
90 }
91 instance->selectProgram("cowbell"); //!!!
92 instance->setIdealChannelCount(m_targetChannelCount); // reset!
93 } else {
94 std::cerr << "Failed to instantiate plugin" << std::endl;
95 }
96 }
97
98 void
99 AudioGenerator::removeModel(Model *model)
100 {
101 SparseOneDimensionalModel *sodm =
102 dynamic_cast<SparseOneDimensionalModel *>(model);
103 if (!sodm) return; // nothing to do
104
105 if (m_synthMap.find(sodm) == m_synthMap.end()) return;
106
107 RealTimePluginInstance *instance = m_synthMap[sodm];
108 m_synthMap.erase(sodm);
109 delete instance;
110 }
111
112 void
113 AudioGenerator::clearModels()
114 {
115 while (!m_synthMap.empty()) {
116 RealTimePluginInstance *instance = m_synthMap.begin()->second;
117 m_synthMap.erase(m_synthMap.begin());
118 delete instance;
119 }
120 }
121
122 void
123 AudioGenerator::reset()
124 {
125 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
126 if (i->second) {
127 i->second->silence();
128 i->second->discardEvents();
129 }
130 }
131
132 m_noteOffs.clear();
133 }
134
135 void
136 AudioGenerator::setTargetChannelCount(size_t targetChannelCount)
137 {
138 m_targetChannelCount = targetChannelCount;
139
140 for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
141 if (i->second) i->second->setIdealChannelCount(targetChannelCount);
142 }
143 }
144
145 size_t
146 AudioGenerator::getBlockSize() const
147 {
148 return m_pluginBlockSize;
149 }
150
151 size_t
152 AudioGenerator::mixModel(Model *model, size_t startFrame, size_t frameCount,
153 float **buffer)
154 {
155 if (m_sourceSampleRate == 0) {
156 std::cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << std::endl;
157 return frameCount;
158 }
159
160 PlayParameters *parameters = m_viewManager->getPlayParameters(model);
161 if (!parameters) return frameCount;
162
163 bool playing = !parameters->isPlayMuted();
164 if (!playing) return frameCount;
165
166 float gain = parameters->getPlayGain();
167 float pan = parameters->getPlayPan();
168
169 DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
170 if (dtvm) {
171 return mixDenseTimeValueModel(dtvm, startFrame, frameCount,
172 buffer, gain, pan);
173 }
174
175 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
176 (model);
177 if (sodm) {
178 return mixSparseOneDimensionalModel(sodm, startFrame, frameCount,
179 buffer, gain, pan);
180 }
181
182 return frameCount;
183 }
184
185 size_t
186 AudioGenerator::mixDenseTimeValueModel(DenseTimeValueModel *dtvm,
187 size_t startFrame, size_t frames,
188 float **buffer, float gain, float pan)
189 {
190 static float *channelBuffer = 0;
191 static size_t channelBufSiz = 0;
192
193 if (channelBufSiz < frames) {
194 delete[] channelBuffer;
195 channelBuffer = new float[frames];
196 channelBufSiz = frames;
197 }
198
199 size_t got = 0;
200
201 for (size_t c = 0; c < m_targetChannelCount && c < dtvm->getChannelCount(); ++c) {
202 got = dtvm->getValues(c, startFrame, startFrame + frames, channelBuffer);
203 for (size_t i = 0; i < frames; ++i) {
204 buffer[c][i] += gain * channelBuffer[i];
205 }
206 }
207
208 return got;
209 }
210
211 size_t
212 AudioGenerator::mixSparseOneDimensionalModel(SparseOneDimensionalModel *sodm,
213 size_t startFrame, size_t frames,
214 float **buffer, float gain, float pan)
215 {
216 RealTimePluginInstance *plugin = m_synthMap[sodm];
217 if (!plugin) return 0;
218
219 size_t latency = plugin->getLatency();
220 size_t blocks = frames / m_pluginBlockSize;
221
222 //!!! hang on -- the fact that the audio callback play source's
223 //buffer is a multiple of the plugin's buffer size doesn't mean
224 //that we always get called for a multiple of it here (because it
225 //also depends on the JACK block size). how should we ensure that
226 //all models write the same amount in to the mix, and that we
227 //always have a multiple of the plugin buffer size? I guess this
228 //class has to be queryable for the plugin buffer size & the
229 //callback play source has to use that as a multiple for all the
230 //calls to mixModel
231
232 size_t got = blocks * m_pluginBlockSize;
233
234 #ifdef DEBUG_AUDIO_GENERATOR
235 std::cout << "mixModel [sparse]: frames " << frames
236 << ", blocks " << blocks << std::endl;
237 #endif
238
239 snd_seq_event_t onEv;
240 onEv.type = SND_SEQ_EVENT_NOTEON;
241 onEv.data.note.channel = 0;
242 onEv.data.note.note = 64;
243 onEv.data.note.velocity = 127;
244
245 snd_seq_event_t offEv;
246 offEv.type = SND_SEQ_EVENT_NOTEOFF;
247 offEv.data.note.channel = 0;
248 offEv.data.note.velocity = 0;
249
250 NoteOffSet &noteOffs = m_noteOffs[sodm];
251
252 for (size_t i = 0; i < blocks; ++i) {
253
254 size_t reqStart = startFrame + i * m_pluginBlockSize;
255
256 SparseOneDimensionalModel::PointList points =
257 sodm->getPoints(reqStart > 0 ? reqStart + latency : reqStart,
258 reqStart + latency + m_pluginBlockSize);
259
260 RealTime blockTime = RealTime::frame2RealTime
261 (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
262
263 for (SparseOneDimensionalModel::PointList::iterator pli =
264 points.begin(); pli != points.end(); ++pli) {
265
266 size_t pliFrame = pli->frame;
267 if (pliFrame >= latency) pliFrame -= latency;
268
269 while (noteOffs.begin() != noteOffs.end() &&
270 noteOffs.begin()->frame <= pliFrame) {
271
272 RealTime eventTime = RealTime::frame2RealTime
273 (noteOffs.begin()->frame, m_sourceSampleRate);
274
275 offEv.data.note.note = noteOffs.begin()->pitch;
276 plugin->sendEvent(eventTime, &offEv);
277 noteOffs.erase(noteOffs.begin());
278 }
279
280 RealTime eventTime = RealTime::frame2RealTime
281 (pliFrame, m_sourceSampleRate);
282
283 plugin->sendEvent(eventTime, &onEv);
284
285 #ifdef DEBUG_AUDIO_GENERATOR
286 std::cout << "mixModel [sparse]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl;
287 #endif
288
289 size_t duration = 7000; // frames [for now]
290 NoteOff noff;
291 noff.pitch = onEv.data.note.note;
292 noff.frame = pliFrame + duration;
293 noteOffs.insert(noff);
294 }
295
296 while (noteOffs.begin() != noteOffs.end() &&
297 noteOffs.begin()->frame <=
298 startFrame + i * m_pluginBlockSize + m_pluginBlockSize) {
299
300 RealTime eventTime = RealTime::frame2RealTime
301 (noteOffs.begin()->frame, m_sourceSampleRate);
302
303 offEv.data.note.note = noteOffs.begin()->pitch;
304 plugin->sendEvent(eventTime, &offEv);
305 noteOffs.erase(noteOffs.begin());
306 }
307
308 plugin->run(blockTime);
309 float **outs = plugin->getAudioOutputBuffers();
310
311 for (size_t c = 0; c < m_targetChannelCount && c < plugin->getAudioOutputCount(); ++c) {
312 #ifdef DEBUG_AUDIO_GENERATOR
313 std::cout << "mixModel [sparse]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl;
314 #endif
315
316 for (size_t j = 0; j < m_pluginBlockSize; ++j) {
317 buffer[c][i * m_pluginBlockSize + j] += gain * outs[c][j];
318 }
319 }
320 }
321
322 return got;
323 }
324