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