Mercurial > hg > svapp
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 ¬eOffs = 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 |