1 /*
2 Copyright (C) 2006-2007 Remon Sijrier
3
4 This file is part of Traverso
5
6 Traverso is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 */
21
22 #include "ResourcesManager.h"
23 #include "WriteSource.h"
24 #include "ReadSource.h"
25 #include "Information.h"
26 #include "AudioClip.h"
27 #include "Project.h"
28 #include "Sheet.h"
29 #include "Utils.h"
30 #include "AudioDevice.h"
31
32 // Always put me below _all_ includes, this is needed
33 // in case we run with memory leak detection enabled!
34 #include "Debugger.h"
35
36 /** \class ResourcesManager
37 \brief A class used to load / save the state of, create and delete ReadSources and AudioClips
38
39 */
40
ResourcesManager(Project * project)41 ResourcesManager::ResourcesManager(Project* project)
42 : QObject(project)
43 , m_project(project)
44 {
45 PENTERCONS;
46 m_silentReadSource = 0;
47 }
48
49
~ResourcesManager()50 ResourcesManager::~ResourcesManager()
51 {
52 PENTERDES;
53 foreach(SourceData* data, m_sources) {
54 if (! data->source->ref()) {
55 delete data->source;
56 }
57 delete data;
58 }
59
60 foreach(ClipData* data, m_clips) {
61 delete data->clip;
62 delete data;
63 }
64
65 }
66
67
get_state(QDomDocument doc)68 QDomNode ResourcesManager::get_state( QDomDocument doc )
69 {
70 PENTER;
71
72 QDomElement rsmNode = doc.createElement("ResourcesManager");
73
74 QDomElement audioSourcesElement = doc.createElement("AudioSources");
75
76 foreach(SourceData* data, m_sources) {
77 ReadSource* source = data->source;
78 audioSourcesElement.appendChild(source->get_state(doc));
79 }
80
81 rsmNode.appendChild(audioSourcesElement);
82
83
84 QDomElement audioClipsElement = doc.createElement("AudioClips");
85
86 foreach(ClipData* data, m_clips) {
87 AudioClip* clip = data->clip;
88
89 if (data->isCopy && data->removed) {
90 continue;
91 }
92
93 // If the clip was 'getted', then it's state has been fully set
94 // and likely changed, so we can get the 'new' state from it.
95 if (data->inUse) {
96 audioClipsElement.appendChild(clip->get_state(doc));
97 } else {
98 // In case it wasn't we should use the 'old' domNode which
99 // was set during set_state();
100 audioClipsElement.appendChild(clip->get_dom_node());
101 }
102 }
103
104 rsmNode.appendChild(audioClipsElement);
105
106 return rsmNode;
107 }
108
109
set_state(const QDomNode & node)110 int ResourcesManager::set_state( const QDomNode & node )
111 {
112 QDomNode sourcesNode = node.firstChildElement("AudioSources").firstChild();
113
114 while(!sourcesNode.isNull()) {
115 ReadSource* source = new ReadSource(sourcesNode);
116 SourceData* data = new SourceData();
117 data->source = source;
118 m_sources.insert(source->get_id(), data);
119 sourcesNode = sourcesNode.nextSibling();
120 if (source->get_channel_count() == 0) {
121 m_silentReadSource = source;
122 }
123 }
124
125
126 QDomNode clipsNode = node.firstChildElement("AudioClips").firstChild();
127
128 while(!clipsNode.isNull()) {
129 AudioClip* clip = new AudioClip(clipsNode);
130 ClipData* data = new ClipData();
131 data->clip = clip;
132 m_clips.insert(clip->get_id(), data);
133 clipsNode = clipsNode.nextSibling();
134 }
135
136
137 emit stateRestored();
138
139 return 1;
140 }
141
142
import_source(const QString & dir,const QString & name)143 ReadSource* ResourcesManager::import_source(const QString& dir, const QString& name)
144 {
145 QString fileName = dir + name;
146 foreach(SourceData* data, m_sources) {
147 if (data->source->get_filename() == fileName) {
148 printf("id is %lld\n", data->source->get_id());
149 return get_readsource(data->source->get_id());
150 }
151 }
152
153 ReadSource* source = new ReadSource(dir, name);
154 SourceData* data = new SourceData();
155 data->source = source;
156 source->set_created_by_sheet(m_project->get_current_sheet()->get_id());
157
158 m_sources.insert(source->get_id(), data);
159
160 source = get_readsource(source->get_id());
161
162 if (source->get_error() < 0) {
163 m_sources.remove(source->get_id());
164 delete source;
165 return 0;
166 }
167
168 emit sourceAdded(source);
169
170 return source;
171 }
172
173
create_recording_source(const QString & dir,const QString & name,int channelCount,qint64 sheetId)174 ReadSource* ResourcesManager::create_recording_source(
175 const QString& dir,
176 const QString& name,
177 int channelCount,
178 qint64 sheetId)
179 {
180 PENTER;
181
182 ReadSource* source = new ReadSource(dir, name, channelCount);
183 SourceData* data = new SourceData();
184 data->source = source;
185
186 source->set_original_bit_depth(audiodevice().get_bit_depth());
187 source->set_created_by_sheet(sheetId);
188 source->ref();
189
190 m_sources.insert(source->get_id(), data);
191
192 emit sourceAdded(source);
193
194 return source;
195 }
196
197
get_silent_readsource()198 ReadSource* ResourcesManager::get_silent_readsource()
199 {
200 if (!m_silentReadSource) {
201 m_silentReadSource = new ReadSource();
202 SourceData* data = new SourceData();
203 data->source = m_silentReadSource;
204 m_sources.insert(m_silentReadSource->get_id(), data);
205 m_silentReadSource->set_created_by_sheet( -1 );
206 }
207
208 m_silentReadSource = get_readsource(m_silentReadSource->get_id());
209
210 return m_silentReadSource;
211 }
212
213
get_readsource(qint64 id)214 ReadSource * ResourcesManager::get_readsource(qint64 id)
215 {
216 SourceData* data = m_sources.value(id);
217
218 if (!data) {
219 PWARN("ResourcesManager::get_readsource(): ReadSource with id %lld is not in my database!", id);
220 return 0;
221 }
222
223 ReadSource* source = data->source;
224
225 // When the AudioSource is "get", do a ref counting.
226 // If the source allready was ref counted, create a deep copy
227 if (source->ref()) {
228 PMESG("Creating deep copy of ReadSource: %s", QS_C(source->get_name()));
229 source = source->deep_copy();
230 source->ref();
231 }
232
233 if ( source->init() < 0) {
234 info().warning( tr("ResourcesManager:: Failed to initialize ReadSource %1 (Reason: %2)")
235 .arg(source->get_filename()).arg(source->get_error_string()));
236 }
237
238 return source;
239 }
240
241
242 /**
243 * Get the AudioClip with id \a id
244
245 This function will return 0 if no AudioClip was found with id \a id.
246
247 Only ONE AudioClip instance with this id can be retrieved via this function.
248 Using this function multiple times with the same id will implicitely create
249 a new AudioClip with a new unique id!!
250
251 * @param id The unique id of the AudioClip to get
252 * @return The AudioClip with id \a id, 0 if no AudioClip was found, and a
253 'deep copy' of the AudioClip with id \a id if the AudioClip was allready
254 getted before via this function.
255 */
get_clip(qint64 id)256 AudioClip* ResourcesManager::get_clip(qint64 id)
257 {
258 ClipData* data = m_clips.value(id);
259
260 if (!data) {
261 return 0;
262 }
263
264 AudioClip* clip = data->clip;
265
266 if (data->inUse && !data->removed) {
267 PMESG("Creating deep copy of Clip %s", QS_C(clip->get_name()));
268 clip = clip->create_copy();
269
270 // It is NOT possible for 2 clips to have the same ID
271 // check for this, since it's absolutely crucial, and
272 // indicates a design error somewhere ! (most likely in
273 // AudioClip::create_copy();
274 Q_ASSERT( ! m_clips.contains(clip->get_id()) );
275 ClipData* copy = new ClipData();
276 copy->clip = clip;
277 copy->isCopy = true;
278
279 m_clips.insert(clip->get_id(), copy);
280
281 // Now that we have created a copy of the audioclip, start
282 // the usual get_clip routine again to properly init the clip
283 // and other stuff.
284 return get_clip(clip->get_id());
285 }
286
287 ReadSource* source = get_readsource(data->clip->get_readsource_id());
288 clip->set_audio_source(source);
289
290 data->inUse = true;
291
292 return clip;
293 }
294
new_audio_clip(const QString & name)295 AudioClip* ResourcesManager::new_audio_clip(const QString& name)
296 {
297 PENTER;
298 AudioClip* clip = new AudioClip(name);
299 ClipData* data = new ClipData();
300 data->clip = clip;
301 m_clips.insert(clip->get_id(), data);
302 return get_clip(clip->get_id());
303 }
304
get_all_audio_sources() const305 QList<ReadSource*> ResourcesManager::get_all_audio_sources( ) const
306 {
307 QList< ReadSource * > list;
308 foreach(SourceData* data, m_sources) {
309 list.append(data->source);
310 }
311 if (m_silentReadSource) {
312 list.removeAll(m_silentReadSource);
313 }
314 return list;
315 }
316
get_all_clips() const317 QList< AudioClip * > ResourcesManager::get_all_clips() const
318 {
319 QList<AudioClip* > list;
320 foreach(ClipData* data, m_clips) {
321 list.append(data->clip);
322 }
323 return list;
324 }
325
326
mark_clip_removed(AudioClip * clip)327 void ResourcesManager::mark_clip_removed(AudioClip * clip)
328 {
329 ClipData* data = m_clips.value(clip->get_id());
330 if (!data) {
331 PERROR("Clip with id %lld is not in my database!", clip->get_id());
332 return;
333 }
334 data->removed = true;
335
336 SourceData* sourcedata = m_sources.value(clip->get_readsource_id());
337 if (!sourcedata) {
338 PERROR("Source %lld not in database", clip->get_readsource_id());
339 } else {
340 sourcedata->clipCount--;
341 }
342
343 emit clipRemoved(clip);
344 }
345
mark_clip_added(AudioClip * clip)346 void ResourcesManager::mark_clip_added(AudioClip * clip)
347 {
348 ClipData* clipdata = m_clips.value(clip->get_id());
349 if (!clipdata) {
350 PERROR("Clip with id %lld is not in my database!", clip->get_id());
351 return;
352 }
353 clipdata->removed = false;
354
355 SourceData* sourcedata = m_sources.value(clip->get_readsource_id());
356 if (!sourcedata) {
357 PERROR("Source %lld not in database", clip->get_readsource_id());
358 } else {
359 sourcedata->clipCount++;
360 }
361
362 emit clipAdded(clip);
363 }
364
365
is_clip_in_use(qint64 id) const366 bool ResourcesManager::is_clip_in_use(qint64 id) const
367 {
368 ClipData* data = m_clips.value(id);
369 if (!data) {
370 PERROR("Clip with id %lld is not in my database!", id);
371 return false;
372 }
373 return data->inUse && !data->removed;
374 }
375
is_source_in_use(qint64 id) const376 bool ResourcesManager::is_source_in_use(qint64 id) const
377 {
378 SourceData* data = m_sources.value(id);
379 if (!data) {
380 PERROR("Source with id %lld is not in my database!", id);
381 return false;
382 }
383
384 return data->clipCount > 0 ? true : false;
385 }
386
set_source_for_clip(AudioClip * clip,ReadSource * source)387 void ResourcesManager::set_source_for_clip(AudioClip * clip, ReadSource * source)
388 {
389 clip->set_audio_source(source);
390 }
391
SourceData()392 ResourcesManager::SourceData::SourceData()
393 {
394 source = 0;
395 clipCount = 0;
396 }
397
ClipData()398 ResourcesManager::ClipData::ClipData()
399 {
400 clip = 0;
401 inUse = false;
402 isCopy = false;
403 removed = false;
404 }
405
destroy_clip(AudioClip * clip)406 void ResourcesManager::destroy_clip(AudioClip * clip)
407 {
408 ClipData* data = m_clips.value(clip->get_id());
409 if (!data) {
410 PERROR("Clip with id %lld not in database", clip->get_id());
411 } else {
412 m_clips.remove(clip->get_id());
413 delete data;
414 delete clip;
415 }
416
417 }
418
remove_source(ReadSource * source)419 void ResourcesManager::remove_source(ReadSource * source)
420 {
421 SourceData* data = m_sources.value(source->get_id());
422 if (!data) {
423 } else {
424 if (data->clipCount > 0) {
425 info().critical(tr("ResourcesManager: Received request to remove Audio Source %1"
426 "but it is still in use by %2 AudioClips!!. NOT removing it!").
427 arg(source->get_name()).arg(data->clipCount));
428 return;
429 }
430
431 m_sources.remove(source->get_id());
432
433 emit sourceRemoved(source);
434
435 delete data;
436 delete source;
437 }
438 }
439
440