1 /*
2 Copyright (C) 2005-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 <QTextStream>
23 #include <QString>
24 #include <QFile>
25 #include <QFileInfo>
26 #include <QList>
27 #include <QMap>
28 #include <QRegExp>
29 #include <QDebug>
30 
31 #include <commands.h>
32 
33 #include "AbstractAudioReader.h"
34 #include <AudioDevice.h>
35 #include <AudioBus.h>
36 #include <Client.h>
37 #include "ProjectManager.h"
38 #include "ContextPointer.h"
39 #include "Information.h"
40 #include "Sheet.h"
41 #include "Project.h"
42 #include "Track.h"
43 #include "Mixer.h"
44 #include "AudioSource.h"
45 #include "AudioClip.h"
46 #include "Peak.h"
47 #include "Export.h"
48 #include "DiskIO.h"
49 #include "WriteSource.h"
50 #include "AudioClipManager.h"
51 #include "Tsar.h"
52 #include "SnapList.h"
53 #include "Config.h"
54 #include "Utils.h"
55 #include "ContextItem.h"
56 #include "TimeLine.h"
57 #include "Marker.h"
58 #include "InputEngine.h"
59 
60 #include <Plugin.h>
61 #include <PluginChain.h>
62 
63 // Always put me below _all_ includes, this is needed
64 // in case we run with memory leak detection enabled!
65 #include "Debugger.h"
66 
67 
68 /**	\class Sheet
69 	\brief The 'work space' (as in WorkSheet) holding the Track 's and the Master Out AudioBus
70 
71 	A Sheet processes each Track, and mixes the result into it's Master Out AudioBus.
72 	Sheet connects it's Client to the AudioDevice, to become part of audio processing
73 	chain. The connection is instantiated by Project, who owns the Sheet objects.
74 
75 
76  */
77 
Sheet(Project * project)78 Sheet::Sheet(Project* project)
79 	: ContextItem()
80 	, m_project(project)
81 {
82 	PENTERCONS;
83 	title = tr("Untitled");
84 	m_id = create_id();
85 	artists = tr("No artists name set");
86 
87 	init();
88 }
89 
Sheet(Project * project,int numtracks)90 Sheet::Sheet(Project* project, int numtracks)
91 	: ContextItem()
92 	, m_project(project)
93 {
94 	PENTERCONS;
95 	title = tr("Untitled");
96 	m_id = create_id();
97 	artists = tr("No artists name set");
98 	m_hzoom = config().get_property("Sheet", "hzoomLevel", 8192).toInt();
99 
100 	init();
101 
102 	for (int i=1; i <= numtracks; i++) {
103 		Track* track = create_track();
104 		private_add_track(track);
105 	}
106 }
107 
Sheet(Project * project,const QDomNode node)108 Sheet::Sheet(Project* project, const QDomNode node)
109 		: ContextItem(), m_project(project)
110 {
111 	PENTERCONS;
112 
113 	QDomNode propertiesNode = node.firstChildElement("Properties");
114 	m_id = node.toElement().attribute("id", "0").toLongLong();
115 
116 	if (m_id == 0) {
117 		m_id = create_id();
118 	}
119 
120 	init();
121 	set_state( node );
122 }
123 
~Sheet()124 Sheet::~Sheet()
125 {
126 	PENTERDES;
127 
128 	delete [] mixdown;
129 	delete [] gainbuffer;
130 
131 	delete m_diskio;
132 	delete m_masterOut;
133 	delete m_renderBus;
134 	delete m_clipRenderBus;
135 	delete m_hs;
136 	delete m_audiodeviceClient;
137 	delete snaplist;
138 	delete workSnap;
139 }
140 
init()141 void Sheet::init()
142 {
143 	PENTER2;
144 #if defined (THREAD_CHECK)
145 	threadId = QThread::currentThreadId ();
146 #endif
147 
148 	QObject::tr("Sheet");
149 
150 	m_diskio = new DiskIO(this);
151 	m_currentSampleRate = audiodevice().get_sample_rate();
152 	m_diskio->output_rate_changed(m_currentSampleRate);
153 	int converter_type = config().get_property("Conversion", "RTResamplingConverterType", DEFAULT_RESAMPLE_QUALITY).toInt();
154 	m_diskio->set_resample_quality(converter_type);
155 
156 
157 	connect(this, SIGNAL(seekStart()), m_diskio, SLOT(seek()), Qt::QueuedConnection);
158 	connect(this, SIGNAL(prepareRecording()), this, SLOT(prepare_recording()));
159 	connect(&audiodevice(), SIGNAL(clientRemoved(TAudioDeviceClient*)), this, SLOT (audiodevice_client_removed(TAudioDeviceClient*)));
160 	connect(&audiodevice(), SIGNAL(started()), this, SLOT(audiodevice_started()));
161 	connect(&audiodevice(), SIGNAL(driverParamsChanged()), this, SLOT(audiodevice_params_changed()), Qt::DirectConnection);
162 	connect(m_diskio, SIGNAL(seekFinished()), this, SLOT(seek_finished()), Qt::QueuedConnection);
163 	connect (m_diskio, SIGNAL(readSourceBufferUnderRun()), this, SLOT(handle_diskio_readbuffer_underrun()));
164 	connect (m_diskio, SIGNAL(writeSourceBufferOverRun()), this, SLOT(handle_diskio_writebuffer_overrun()));
165 	connect(&config(), SIGNAL(configChanged()), this, SLOT(config_changed()));
166 	connect(this, SIGNAL(transportStarted()), m_diskio, SLOT(start_io()));
167 	connect(this, SIGNAL(transportStopped()), m_diskio, SLOT(stop_io()));
168 
169 	mixdown = gainbuffer = 0;
170 	m_masterOut = new AudioBus("Master Out", 2);
171 	m_renderBus = new AudioBus("Render Bus", 2);
172 	m_clipRenderBus = new AudioBus("Clip Render Bus", 2);
173 
174 	resize_buffer(false, audiodevice().get_buffer_size());
175 	m_hs = new QUndoStack(pm().get_undogroup());
176 	set_history_stack(m_hs);
177 	m_acmanager = new AudioClipManager(this);
178 
179 	set_context_item( m_acmanager );
180 
181 	m_playBackBus = audiodevice().get_playback_bus("Playback 1");
182 
183 	m_transport = m_stopTransport = m_resumeTransport = m_readyToRecord = false;
184 	snaplist = new SnapList(this);
185 	workSnap = new Snappable();
186 	workSnap->set_snap_list(snaplist);
187 
188 	m_realtimepath = false;
189 	m_scheduledForDeletion = false;
190 	m_isSnapOn=true;
191 	changed = m_rendering = m_recording = m_prepareRecording = false;
192 	firstVisibleFrame=0;
193 	m_workLocation = TimeRef();
194 	m_seeking = m_startSeek = 0;
195 	// TODO seek to old position on project exit ?
196 	m_transportLocation = TimeRef();
197 	m_mode = EDIT;
198 	m_sbx = m_sby = 0;
199 	m_hzoom = config().get_property("Sheet", "hzoomLevel", 8192).toInt();
200 
201 	m_pluginChain = new PluginChain(this, this);
202 	m_fader = m_pluginChain->get_fader();
203 	m_fader->set_gain(0.5);
204 	m_timeline = new TimeLine(this);
205 
206 	m_skipTimer.setSingleShot(true);
207 
208 	m_audiodeviceClient = new TAudioDeviceClient("sheet_" + QByteArray::number(get_id()));
209 	m_audiodeviceClient->set_process_callback( MakeDelegate(this, &Sheet::process) );
210 	m_audiodeviceClient->set_transport_control_callback( MakeDelegate(this, &Sheet::transport_control) );
211 }
212 
set_state(const QDomNode & node)213 int Sheet::set_state( const QDomNode & node )
214 {
215 	PENTER;
216 
217 	QDomNode propertiesNode = node.firstChildElement("Properties");
218 	QDomElement e = propertiesNode.toElement();
219 
220 	title = e.attribute( "title", "" );
221 	artists = e.attribute( "artists", "" );
222 	set_gain(e.attribute( "mastergain", "1.0").toFloat() );
223 	qreal zoom = e.attribute("hzoom", "4096").toDouble();
224 	set_hzoom(zoom);
225 	m_sbx = e.attribute("sbx", "0").toInt();
226 	m_sby = e.attribute("sby", "0").toInt();
227 	set_first_visible_frame(e.attribute( "firstVisibleFrame", "0" ).toUInt());
228 
229 	bool ok;
230 	TimeRef location(e.attribute( "m_workLocation", "0").toLongLong(&ok));
231 	set_work_at(location);
232 	m_transportLocation = TimeRef(e.attribute( "transportlocation", "0").toLongLong(&ok));
233 
234 	// Start seeking to the 'old' transport pos
235 	set_transport_pos(m_transportLocation);
236 	set_snapping(e.attribute("snapping", "0").toInt());
237 	m_mode = e.attribute("mode", "0").toInt();
238 
239 	m_timeline->set_state(node.firstChildElement("TimeLine"));
240 
241 	QDomNode tracksNode = node.firstChildElement("Tracks");
242 	QDomNode trackNode = tracksNode.firstChild();
243 
244 	while(!trackNode.isNull()) {
245 		Track* track = new Track(this, trackNode);
246 		private_add_track(track);
247 		track->set_state(trackNode);
248 
249 		trackNode = trackNode.nextSibling();
250 	}
251 
252 	m_acmanager->set_state(node.firstChildElement("ClipManager"));
253 
254 	QDomNode pluginChainNode = node.firstChildElement("PluginChain");
255 	m_pluginChain->set_state(pluginChainNode);
256 
257 	return 1;
258 }
259 
get_state(QDomDocument doc,bool istemplate)260 QDomNode Sheet::get_state(QDomDocument doc, bool istemplate)
261 {
262 	QDomElement sheetNode = doc.createElement("Sheet");
263 
264 	if (! istemplate) {
265 		sheetNode.setAttribute("id", m_id);
266 	}
267 
268 	QDomElement properties = doc.createElement("Properties");
269 	properties.setAttribute("title", title);
270 	properties.setAttribute("artists", artists);
271 	properties.setAttribute("firstVisibleFrame", firstVisibleFrame);
272 	properties.setAttribute("m_workLocation", m_workLocation.universal_frame());
273 	properties.setAttribute("transportlocation", m_transportLocation.universal_frame());
274 	properties.setAttribute("hzoom", m_hzoom);
275 	properties.setAttribute("sbx", m_sbx);
276 	properties.setAttribute("sby", m_sby);
277 	properties.setAttribute("snapping", m_isSnapOn);
278 	properties.setAttribute("mode", m_mode);
279 	sheetNode.appendChild(properties);
280 
281 	doc.appendChild(sheetNode);
282 
283 	sheetNode.appendChild(m_acmanager->get_state(doc));
284 
285 	sheetNode.appendChild(m_timeline->get_state(doc));
286 
287 	QDomNode tracksNode = doc.createElement("Tracks");
288 
289 	apill_foreach(Track* track, Track, m_tracks) {
290 		tracksNode.appendChild(track->get_state(doc, istemplate));
291 	}
292 
293 	sheetNode.appendChild(tracksNode);
294 
295 	QDomNode pluginChainNode = doc.createElement("PluginChain");
296 	pluginChainNode.appendChild(m_pluginChain->get_state(doc));
297 	sheetNode.appendChild(pluginChainNode);
298 
299 
300 	return sheetNode;
301 }
302 
connect_to_audiodevice()303 void Sheet::connect_to_audiodevice( )
304 {
305 	PENTER;
306 	audiodevice().add_client(m_audiodeviceClient);
307 }
308 
disconnect_from_audiodevice()309 void Sheet::disconnect_from_audiodevice()
310 {
311 	PENTER;
312 	if (is_transport_rolling()) {
313 		m_transport = false;
314 		emit transportStopped();
315 	}
316 	audiodevice().remove_client(m_audiodeviceClient);
317 }
318 
schedule_for_deletion()319 void Sheet::schedule_for_deletion()
320 {
321 	m_scheduledForDeletion = true;
322 	pm().scheduled_for_deletion(this);
323 }
324 
audiodevice_client_removed(TAudioDeviceClient * client)325 void Sheet::audiodevice_client_removed(TAudioDeviceClient* client )
326 {
327 	PENTER;
328 	if (m_audiodeviceClient == client) {
329 		if (m_scheduledForDeletion) {
330 			pm().delete_sheet(this);
331 		}
332 	}
333 }
334 
add_track(Track * track,bool historable)335 Command* Sheet::add_track(Track* track, bool historable)
336 {
337 	apill_foreach(Track* existing, Track, m_tracks) {
338 		if (existing->is_solo()) {
339 			track->set_muted_by_solo( true );
340 			break;
341 		}
342 	}
343 
344 	return new AddRemove(this, track, historable, this,
345 		"private_add_track(Track*)", "trackAdded(Track*)",
346 		"private_remove_track(Track*)", "trackRemoved(Track*)",
347    		tr("Add Track"));
348 }
349 
350 
remove_track(Track * track,bool historable)351 Command* Sheet::remove_track(Track* track, bool historable)
352 {
353 	return new AddRemove(this, track, historable, this,
354 		"private_remove_track(Track*)", "trackRemoved(Track*)",
355 		"private_add_track(Track*)", "trackAdded(Track*)",
356    		tr("Remove Track"));
357 }
358 
any_track_armed()359 bool Sheet::any_track_armed()
360 {
361 	apill_foreach(Track* track, Track, m_tracks) {
362 		if (track->armed()) {
363 			return true;
364 		}
365 	}
366 	return false;
367 }
368 
369 
prepare_export(ExportSpecification * spec)370 int Sheet::prepare_export(ExportSpecification* spec)
371 {
372 	PENTER;
373 
374 	if ( ! (spec->renderpass == ExportSpecification::CREATE_CDRDAO_TOC) ) {
375 		if (is_transport_rolling()) {
376 			spec->resumeTransport = true;
377 			// When transport is rolling, this equals stopping the transport!
378 			// prepare_export() is called from another thread, so use a queued connection
379 			// to call the function in the correct thread!
380 			if (!QMetaObject::invokeMethod(this, "start_transport",  Qt::QueuedConnection)) {
381 				printf("Invoking Sheet::start_transport() failed\n");
382 				return -1;
383 			}
384 			int count = 0;
385 			uint msecs = (audiodevice().get_buffer_size() * 1000) / audiodevice().get_sample_rate();
386 			// wait a number (max 10) of process() cycles to be sure we really stopped transport
387 			while (m_transport) {
388 				spec->thread->sleep_for(msecs);
389 				count++;
390 				if (count > 10) {
391 					break;
392 				}
393 			}
394 			printf("Sheet::prepare_export: had to wait %d process cycles before the transport was stopped\n", count);
395 		}
396 
397 		m_rendering = true;
398 	}
399 
400 	spec->startLocation = LLONG_MAX;
401 	spec->endLocation = TimeRef();
402 
403 	TimeRef endlocation, startlocation;
404 
405 	apill_foreach(Track* track, Track, m_tracks) {
406 		track->get_render_range(startlocation, endlocation);
407 
408 		if (track->is_solo()) {
409 			spec->endLocation = endlocation;
410 			spec->startLocation = startlocation;
411 			break;
412 		}
413 
414 		if (endlocation > spec->endLocation) {
415 			spec->endLocation = endlocation;
416 		}
417 
418 		if (startlocation < spec->startLocation) {
419 			spec->startLocation = startlocation;
420 		}
421 	}
422 
423 	if (spec->isCdExport) {
424 		QList<Marker*> markers = m_timeline->get_markers();
425 
426 		if (m_timeline->get_start_location(startlocation)) {
427 			PMESG2("  Start marker found at %s", QS_C(timeref_to_ms(startlocation)));
428 			// round down to the start of the CD frame (75th of a sec)
429 			startlocation = cd_to_timeref(timeref_to_cd(startlocation));
430 			spec->startLocation = startlocation;
431 		} else {
432 			PMESG2("  No start marker found");
433 		}
434 
435 		if (m_timeline->get_end_location(endlocation)) {
436 			PMESG2("  End marker found at %s", QS_C(timeref_to_ms(endlocation)));
437 			spec->endLocation = endlocation;
438 		} else {
439 			PMESG2("  No end marker found");
440 		}
441 	}
442 
443 	spec->totalTime = spec->endLocation - spec->startLocation;
444 
445 // 	PWARN("Render length is: %s",timeref_to_ms_3(spec->totalTime).toLatin1().data() );
446 
447 	spec->pos = spec->startLocation;
448 	spec->progress = 0;
449 
450 	spec->basename = "Sheet_" + QString::number(m_project->get_sheet_index(m_id)) +"-" + title;
451 	spec->name = spec->basename;
452 
453 	if (spec->startLocation == spec->endLocation) {
454 		info().warning(tr("No audio to export! (Is everything muted?)"));
455 		return -1;
456 	}
457 	else if (spec->startLocation > spec->endLocation) {
458 		info().warning(tr("Export start frame starts beyond export end frame!!"));
459 		return -1;
460 	}
461 
462 	if (spec->channels == 0) {
463 		info().warning(tr("Export tries to render to 0 channels wav file??"));
464 		return -1;
465 	}
466 
467 	if (spec->renderpass == ExportSpecification::CREATE_CDRDAO_TOC) {
468 		return 1;
469 	}
470 
471 	if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK) {
472 		m_exportSource = new WriteSource(spec);
473 		if (m_exportSource->prepare_export() == -1) {
474 			delete m_exportSource;
475 			return -1;
476 		}
477 	}
478 
479 	m_transportLocation = spec->startLocation;
480 
481 	resize_buffer(false, spec->blocksize);
482 
483 	renderDecodeBuffer = new DecodeBuffer;
484 
485 	return 1;
486 }
487 
finish_audio_export()488 int Sheet::finish_audio_export()
489 {
490 	m_exportSource->finish_export();
491 	delete m_exportSource;
492 	delete renderDecodeBuffer;
493 	resize_buffer(false, audiodevice().get_buffer_size());
494 	return 0;
495 }
496 
render(ExportSpecification * spec)497 int Sheet::render(ExportSpecification* spec)
498 {
499 	int chn;
500 	uint32_t x;
501 	int ret = -1;
502 	int progress;
503 
504 	nframes_t diff = (spec->endLocation - spec->pos).to_frame(audiodevice().get_sample_rate());
505 	nframes_t nframes = spec->blocksize;
506 	nframes_t this_nframes = std::min(diff, nframes);
507 
508 	if (!spec->running || spec->stop || this_nframes == 0) {
509 		process_export (nframes);
510 		/*		PWARN("Finished Rendering for this sheet");
511 				PWARN("running is %d", spec->running);
512 				PWARN("stop is %d", spec->stop);
513 				PWARN("this_nframes is %d", this_nframes);*/
514 		if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK) {
515 			return finish_audio_export();
516 		} else {
517 			return 0;
518 		}
519 	}
520 
521 	/* do the usual stuff */
522 
523 	process_export(nframes);
524 
525 	/* and now export the results */
526 
527 	nframes = this_nframes;
528 
529 	memset (spec->dataF, 0, sizeof (spec->dataF[0]) * nframes * spec->channels);
530 
531 	/* foreach output channel ... */
532 
533 	float* buf;
534 
535 	for (chn = 0; chn < spec->channels; ++chn) {
536 		buf = m_masterOut->get_buffer(chn, nframes);
537 
538 		if (!buf) {
539 			// Seem we are exporting at least to Stereo from an AudioBus with only one channel...
540 			// Use the first channel..
541 			buf = m_masterOut->get_buffer(0, nframes);
542 		}
543 
544 		for (x = 0; x < nframes; ++x) {
545 			spec->dataF[chn+(x*spec->channels)] = buf[x];
546 		}
547 	}
548 
549 
550 	int bufsize = spec->blocksize * spec->channels;
551 	if (spec->normalize) {
552 		if (spec->renderpass == ExportSpecification::CALC_NORM_FACTOR) {
553 			spec->peakvalue = Mixer::compute_peak(spec->dataF, bufsize, spec->peakvalue);
554 		}
555 	}
556 
557 	if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK) {
558 		if (spec->normalize) {
559 			Mixer::apply_gain_to_buffer(spec->dataF, bufsize, spec->normvalue);
560 		}
561 		if (m_exportSource->process (nframes)) {
562 			goto out;
563 		}
564 	}
565 
566 
567 	spec->pos.add_frames(nframes, audiodevice().get_sample_rate());
568 
569 	if (! spec->normalize ) {
570 		progress =  int((double((spec->pos - spec->startLocation).universal_frame()) / spec->totalTime.universal_frame()) * 100);
571 	} else {
572 		progress = (int) (double( 100 * (spec->pos - spec->startLocation).universal_frame()) / (spec->totalTime.universal_frame() * 2));
573 		if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK) {
574 			progress += 50;
575 		}
576 	}
577 
578 	if (progress > spec->progress) {
579 		spec->progress = progress;
580 		m_project->set_sheet_export_progress(progress);
581 	}
582 
583 
584 	/* and we're good to go */
585 
586 	ret = 1;
587 
588 out:
589 	if (!ret) {
590 		spec->running = false;
591 		spec->status = ret;
592 		m_rendering = false;
593 	}
594 
595 	return ret;
596 }
597 
598 
get_snap_list() const599 SnapList* Sheet::get_snap_list() const
600 {
601 	return snaplist;
602 }
603 
604 
set_artists(const QString & pArtists)605 void Sheet::set_artists(const QString& pArtists)
606 {
607 	artists = pArtists;
608 }
609 
set_gain(float gain)610 void Sheet::set_gain(float gain)
611 {
612 	if (gain < 0.0)
613 		gain = 0.0;
614 	if (gain > 2.0)
615 		gain = 2.0;
616 
617 	m_fader->set_gain(gain);
618 
619 	emit masterGainChanged();
620 }
621 
set_title(const QString & sTitle)622 void Sheet::set_title(const QString& sTitle)
623 {
624 	title=sTitle;
625 	emit propertyChanged();
626 }
627 
set_first_visible_frame(nframes_t pos)628 void Sheet::set_first_visible_frame(nframes_t pos)
629 {
630 	PENTER;
631 	firstVisibleFrame = pos;
632 	emit firstVisibleFrameChanged();
633 }
634 
set_work_at(const TimeRef & location)635 void Sheet::set_work_at(const TimeRef& location)
636 {
637 	m_workLocation = location;
638 	if (workSnap->is_snappable()) {
639 		snaplist->mark_dirty();
640 	}
641 	emit workingPosChanged();
642 }
643 
toggle_snap()644 Command* Sheet::toggle_snap()
645 {
646 	set_snapping( ! m_isSnapOn );
647 	return 0;
648 }
649 
650 
set_snapping(bool snapping)651 void Sheet::set_snapping(bool snapping)
652 {
653 	m_isSnapOn = snapping;
654 	emit snapChanged();
655 }
656 
657 /******************************** SLOTS *****************************/
658 
create_track()659 Track* Sheet::create_track()
660 {
661 	int height = Track::INITIAL_HEIGHT;
662 
663 	Track* track = new Track(this, "Unnamed", height);
664 
665 	return track;
666 }
667 
solo_track(Track * t)668 void Sheet::solo_track(Track* t)
669 {
670 	bool wasSolo = t->is_solo();
671 
672 	t->set_muted_by_solo(!wasSolo);
673 	t->set_solo(!wasSolo);
674 
675 	bool hasSolo = false;
676 	apill_foreach(Track* track, Track, m_tracks) {
677 		track->set_muted_by_solo(!track->is_solo());
678 		if (track->is_solo()) hasSolo = true;
679 	}
680 
681 	if (!hasSolo) {
682 		apill_foreach(Track* track, Track, m_tracks) {
683 			track->set_muted_by_solo(false);
684 		}
685 	}
686 }
687 
toggle_solo()688 Command* Sheet::toggle_solo()
689 {
690 	bool hasSolo = false;
691 	apill_foreach(Track* track, Track, m_tracks) {
692 		if (track->is_solo()) hasSolo = true;
693 	}
694 
695 	apill_foreach(Track* track, Track, m_tracks) {
696 		track->set_solo(!hasSolo);
697 		track->set_muted_by_solo(false);
698 	}
699 
700 	return (Command*) 0;
701 }
702 
toggle_mute()703 Command *Sheet::toggle_mute()
704 {
705 	bool hasMute = false;
706 	apill_foreach(Track* track, Track, m_tracks) {
707 		if (track->is_muted()) hasMute = true;
708 	}
709 
710 	apill_foreach(Track* track, Track, m_tracks) {
711 		track->set_muted(!hasMute);
712 	}
713 
714 	return (Command*) 0;
715 }
716 
toggle_arm()717 Command *Sheet::toggle_arm()
718 {
719 	bool hasArmed = false;
720 	apill_foreach(Track* track, Track, m_tracks) {
721 		if (track->armed()) hasArmed = true;
722 	}
723 
724 	apill_foreach(Track* track, Track, m_tracks) {
725 		if (hasArmed) {
726 			track->disarm();
727 		} else {
728 			track->arm();
729 		}
730 	}
731 
732 	return (Command*) 0;
733 }
734 
work_next_edge()735 Command* Sheet::work_next_edge()
736 {
737 /*	nframes_t w = m_acmanager->get_last_frame();
738 
739 	foreach(Track* track, m_tracks) {
740 		AudioClip* c=track->get_clip_after(m_workLocation);
741 
742 		if ((c) && (c->get_track_start_location() < w && c->get_track_start_location() > m_workLocation))
743 			w = c->get_track_start_location();
744 	}
745 
746 	set_work_at(w);
747 
748 	emit setCursorAtEdge();
749 */
750 	return (Command*) 0;
751 }
752 
work_previous_edge()753 Command* Sheet::work_previous_edge()
754 {
755 /*	TimeRef w(0);
756 	foreach(Track* track, m_tracks) {
757 		AudioClip* c = track->get_clip_before(m_workLocation);
758 		if ((c) && (c->get_track_end_location() >= w && c->get_track_end_location() < m_workLocation) )
759 			w=c->get_track_end_location();
760 	}
761 
762 	set_work_at(w);
763 
764 	emit setCursorAtEdge();
765 */
766 	return (Command*) 0;
767 }
768 
set_hzoom(qreal hzoom)769 void Sheet::set_hzoom( qreal hzoom )
770 {
771 	// Traverso <= 0.42.0 doesn't store the real zoom factor, but an
772 	// index. This currently causes problems as there is no real support
773 	// (yet) for zoomlevels other then powers of 2, so we force that for now.
774 	// NOTE: Remove those 2 lines when floating point zoomlevel is implemented!
775 	int highbit;
776 	hzoom = nearest_power_of_two(hzoom, highbit);
777 
778 
779 	if (hzoom > Peak::max_zoom_value()) {
780 		hzoom = Peak::max_zoom_value();
781 	}
782 
783 	if (hzoom < 1.0) {
784 		hzoom = 1.0;
785 	}
786 
787 	if (m_hzoom == hzoom) {
788 		return;
789 	}
790 
791 	m_hzoom = hzoom;
792 
793 	emit hzoomChanged();
794 }
795 
796 //
797 //  Function called in RealTime AudioThread processing path
798 //
process(nframes_t nframes)799 int Sheet::process( nframes_t nframes )
800 {
801 	if (m_startSeek) {
802 		start_seek();
803 		return 0;
804 	}
805 
806 	// If no need for playback/record, return.
807 	if (!is_transport_rolling()) {
808 		return 0;
809 	}
810 
811 	if (m_stopTransport) {
812 		m_transport = false;
813 		m_realtimepath = false;
814 		m_stopTransport = false;
815 
816 		RT_THREAD_EMIT(this, 0, transportStopped());
817 
818 		return 0;
819 	}
820 
821 	// zero the m_masterOut buffers
822 	m_masterOut->silence_buffers(nframes);
823 
824 	int processResult = 0;
825 
826 
827 	// Process all Tracks.
828 	apill_foreach(Track* track, Track, m_tracks) {
829 		processResult |= track->process(nframes);
830 	}
831 
832 	// update the transport location
833 	m_transportLocation.add_frames(nframes, audiodevice().get_sample_rate());
834 
835 	if (!processResult) {
836 		return 0;
837 	}
838 
839 	// Mix the result into the AudioDevice "physical" buffers
840 	if (m_playBackBus) {
841 		Mixer::mix_buffers_with_gain(m_playBackBus->get_buffer(0, nframes), m_masterOut->get_buffer(0, nframes), nframes, get_gain());
842 		Mixer::mix_buffers_with_gain(m_playBackBus->get_buffer(1, nframes), m_masterOut->get_buffer(1, nframes), nframes, get_gain());
843 
844 		m_pluginChain->process_post_fader(m_masterOut, nframes);
845 	}
846 
847 
848 	return 1;
849 }
850 
process_export(nframes_t nframes)851 int Sheet::process_export( nframes_t nframes )
852 {
853 	// Get the masterout buffers, and fill with zero's
854 	m_masterOut->silence_buffers(nframes);
855 	memset (mixdown, 0, sizeof (audio_sample_t) * nframes);
856 
857 	// Process all Tracks.
858 	apill_foreach(Track* track, Track, m_tracks) {
859 		track->process(nframes);
860 	}
861 
862 	Mixer::apply_gain_to_buffer(m_masterOut->get_buffer(0, nframes), nframes, get_gain());
863 	Mixer::apply_gain_to_buffer(m_masterOut->get_buffer(1, nframes), nframes, get_gain());
864 
865 	// update the m_transportFrame
866 // 	m_transportFrame += nframes;
867 	m_transportLocation.add_frames(nframes, audiodevice().get_sample_rate());
868 
869 	return 1;
870 }
871 
get_cdrdao_tracklist(ExportSpecification * spec,bool pregap)872 QString Sheet::get_cdrdao_tracklist(ExportSpecification* spec, bool pregap)
873 {
874 	QString output;
875 
876 	QList<Marker*> mlist = m_timeline->get_markers();
877 	QList<Marker*> tempmarkers;
878 
879 	// Here we make the marker-stuff idiot-proof ;-). Traverso doesn't insist on having any
880 	// marker at all, so we need to handle cases like:
881 	// - no markers at all
882 	// - one marker (doesn't make sense)
883 	// - enough markers, but no end marker
884 
885 	Marker* temp;
886 
887 	if (mlist.size() < 2) {
888 		switch (mlist.size()) {
889 			case 0:
890 				// no markers present. We add one at the beginning and one at the
891 				// end of the render area.
892 				temp = new Marker(m_timeline, spec->startLocation, Marker::CDTRACK);
893 				tempmarkers.append(temp);
894 				mlist.prepend(temp);
895 				temp = new Marker(m_timeline, spec->endLocation, Marker::ENDMARKER);
896 				tempmarkers.append(temp);
897 				mlist.append(temp);
898 				break;
899 			case 1:
900 				// one marker is present. We add two more, one at the beginning
901 				// and one at the end of the render area. If the present marker
902 				// happened to be at either the start or the end position,
903 				// it will now be overwritten, so we will never end up with
904 				// two markers at the same position.
905 
906 				// deactivate the next if-condition (only the first one) if you want the
907 				// stuff before the first marker to go into the pre-gap
908 				if (mlist.at(0)->get_when() != (spec->startLocation)) {
909 					temp = new Marker(m_timeline, spec->startLocation, Marker::CDTRACK);
910 					tempmarkers.append(temp);
911 					mlist.prepend(temp);
912 				}
913 				if (mlist.at(0)->get_when() != (spec->startLocation)) {
914 					temp = new Marker(m_timeline, spec->endLocation, Marker::ENDMARKER);
915 					tempmarkers.append(temp);
916 					mlist.append(temp);
917 				}
918 				break;
919 		}
920 	} else {
921 		 // would be ok, but let's check if there is an end marker present. If not,
922 		// add one to spec->end_frame
923 		if (!m_timeline->has_end_marker()) {
924 			temp = new Marker(m_timeline, spec->endLocation, Marker::ENDMARKER);
925 			tempmarkers.append(temp);
926 			mlist.append(temp);
927 		}
928 	}
929 
930 	TimeRef start;
931 
932 	for(int i = 0; i < mlist.size()-1; ++i) {
933 
934 		Marker* startmarker = mlist.at(i);
935 		Marker* endmarker = mlist.at(i+1);
936 
937 		output += "TRACK AUDIO\n";
938 
939 		if (startmarker->get_copyprotect()) {
940 			output += "  NO COPY\n";
941 		} else {
942 			output += "  COPY\n";
943 		}
944 
945 		if (startmarker->get_preemphasis()) {
946 			output += "  PRE_EMPHASIS\n";
947 		}
948 
949 		output += "  CD_TEXT {\n    LANGUAGE 0 {\n";
950 		output += "      TITLE \"" + startmarker->get_description() + "\"\n";
951 		output += "      PERFORMER \"" + startmarker->get_performer() + "\"\n";
952 		output += "      ISRC \"" + startmarker->get_isrc() + "\"\n";
953 		output += "      ARRANGER \"" + startmarker->get_arranger() + "\"\n";
954 		output += "      SONGWRITER \"" + startmarker->get_songwriter() + "\"\n";
955 		output += "      MESSAGE \"" + startmarker->get_message() + "\"\n    }\n  }\n";
956 
957 		// add some stuff only required for the first track (e.g. pre-gap)
958 		if ((i == 0) && pregap) {
959 			//if (start == 0) {
960 				// standard pregap, because we have a track marker at the beginning
961 				output += "  PREGAP 00:02:00\n";
962 			//} else {
963 			//	// no track marker at the beginning, thus use the part from 0 to the first
964 			//	// track marker for the pregap
965 			//	output += "  START " + frame_to_cd(start, m_project->get_rate()) + "\n";
966 			//	start = 0;
967 			//}
968 		}
969 
970 		TimeRef length = cd_to_timeref(timeref_to_cd(endmarker->get_when())) - cd_to_timeref(timeref_to_cd(startmarker->get_when()));
971 
972 		QString s_start = timeref_to_cd(start);
973 		QString s_length = timeref_to_cd(length);
974 
975 		output += "  FILE \"" + spec->name + "." + spec->extraFormat["filetype"] + "\" " + s_start + " " + s_length + "\n\n";
976 		start += length;
977 
978 		// check if the second marker is of type "Endmarker"
979 		if (endmarker->get_type() == Marker::ENDMARKER) {
980 			break;
981 		}
982 	}
983 
984 	// delete all temporary markers
985 	foreach(Marker* marker, tempmarkers) {
986 		delete marker;
987 	}
988 
989 	return output;
990 }
991 
resize_buffer(bool updateArmStatus,nframes_t size)992 void Sheet::resize_buffer(bool updateArmStatus, nframes_t size)
993 {
994 	if (mixdown)
995 		delete [] mixdown;
996 	if (gainbuffer)
997 		delete [] gainbuffer;
998 	mixdown = new audio_sample_t[size];
999 	gainbuffer = new audio_sample_t[size];
1000 	m_masterOut->set_buffer_size(size);
1001 	m_renderBus->set_buffer_size(size);
1002 	m_clipRenderBus->set_buffer_size(size);
1003 
1004 	if (updateArmStatus) {
1005 		apill_foreach(Track* track, Track, m_tracks) {
1006 			AudioBus* bus = audiodevice().get_capture_bus(track->get_bus_in().toLatin1());
1007 			if (bus && track->armed()) {
1008 				bus->set_monitor_peaks(true);
1009 			}
1010 		}
1011 	}
1012 }
1013 
audiodevice_params_changed()1014 void Sheet::audiodevice_params_changed()
1015 {
1016 	resize_buffer(true, audiodevice().get_buffer_size());
1017 
1018 	// The samplerate possibly has been changed, this initiates
1019 	// a seek in DiskIO, which clears the buffers and refills them
1020 	// with the correct resampled audio data!
1021 	// We need to seek to a different position then the current one,
1022 	// else the seek won't happen at all :)
1023 	if (m_currentSampleRate != audiodevice().get_sample_rate()) {
1024 		m_currentSampleRate = audiodevice().get_sample_rate();
1025 
1026 		m_diskio->output_rate_changed(m_currentSampleRate);
1027 
1028 		TimeRef location = m_transportLocation;
1029 		location.add_frames(1, audiodevice().get_sample_rate());
1030 
1031 		set_transport_pos(location);
1032 	}
1033 }
1034 
get_bitdepth()1035 int Sheet::get_bitdepth( )
1036 {
1037 	return m_project->get_bitdepth();
1038 }
1039 
get_rate()1040 int Sheet::get_rate( )
1041 {
1042 	return m_project->get_rate();
1043 }
1044 
get_first_visible_frame() const1045 nframes_t Sheet::get_first_visible_frame( ) const
1046 {
1047 	return firstVisibleFrame;
1048 }
1049 
get_diskio() const1050 DiskIO * Sheet::get_diskio( ) const
1051 {
1052 	return m_diskio;
1053 }
1054 
get_audioclip_manager() const1055 AudioClipManager * Sheet::get_audioclip_manager( ) const
1056 {
1057 	return m_acmanager;
1058 }
1059 
get_plugin_chain() const1060 PluginChain* Sheet::get_plugin_chain() const
1061 {
1062 	return m_pluginChain;
1063 }
1064 
get_track_index(qint64 id)1065 int Sheet::get_track_index(qint64 id)
1066 {
1067 	int i=0;
1068 	apill_foreach(Track* track, Track, m_tracks) {
1069 		if (track->get_id() == id) {
1070 			return i + 1;
1071 		}
1072 		++i;
1073 	}
1074 	return 0;
1075 }
1076 
handle_diskio_readbuffer_underrun()1077 void Sheet::handle_diskio_readbuffer_underrun( )
1078 {
1079 	if (is_transport_rolling()) {
1080 		printf("Sheet:: DiskIO ReadBuffer UnderRun signal received!\n");
1081 		info().critical(tr("Hard Disk overload detected!"));
1082 		info().critical(tr("Failed to fill ReadBuffer in time"));
1083 	}
1084 }
1085 
handle_diskio_writebuffer_overrun()1086 void Sheet::handle_diskio_writebuffer_overrun( )
1087 {
1088 	if (is_transport_rolling()) {
1089 		printf("Sheet:: DiskIO WriteBuffer OverRun signal received!\n");
1090 		info().critical(tr("Hard Disk overload detected!"));
1091 		info().critical(tr("Failed to empty WriteBuffer in time"));
1092 	}
1093 }
1094 
audiodevice_started()1095 void Sheet::audiodevice_started( )
1096 {
1097 	m_playBackBus = audiodevice().get_playback_bus("Playback 1");
1098 }
1099 
get_last_location() const1100 TimeRef Sheet::get_last_location() const
1101 {
1102 	TimeRef lastAudio = m_acmanager->get_last_location();
1103 
1104 	if (m_timeline->get_markers().size()) {
1105 		TimeRef lastMarker = m_timeline->get_markers().last()->get_when();
1106 		return (lastAudio > lastMarker) ? lastAudio : lastMarker;
1107 	}
1108 
1109 	return lastAudio;
1110 }
1111 
private_add_track(Track * track)1112 void Sheet::private_add_track(Track* track)
1113 {
1114 	m_tracks.append(track);
1115 }
1116 
private_remove_track(Track * track)1117 void Sheet::private_remove_track(Track* track)
1118 {
1119 	m_tracks.remove(track);
1120 }
1121 
get_track(qint64 id)1122 Track* Sheet::get_track(qint64 id)
1123 {
1124 	apill_foreach(Track* track, Track, m_tracks) {
1125 		if (track->get_id() == id) {
1126 			return track;
1127 		}
1128 	}
1129 	return 0;
1130 }
1131 
move_clip(Track * from,Track * too,AudioClip * clip,TimeRef location)1132 void Sheet::move_clip(Track * from, Track * too, AudioClip * clip, TimeRef location)
1133 {
1134 	PENTER2;
1135 
1136 	if (from == too) {
1137 		clip->set_track_start_location(location);
1138 		return;
1139 	}
1140 
1141 	// Remove has to be done BEFORE adding, else the APILinkedList logic
1142 	// gets messed up for the Tracks AudioClipList, which is an APILinkedList :(
1143 	Command::process_command(from->remove_clip(clip, false, true));
1144 	Command::process_command(too->add_clip(clip, false, true));
1145 
1146 	if (clip->get_track_start_location() != location) {
1147 		clip->set_track_start_location(location);
1148 	}
1149 }
1150 
set_editing_mode()1151 Command* Sheet::set_editing_mode( )
1152 {
1153 	m_mode = EDIT;
1154 	emit modeChanged();
1155 	return 0;
1156 }
1157 
set_effects_mode()1158 Command* Sheet::set_effects_mode( )
1159 {
1160 	m_mode = EFFECTS;
1161 	emit modeChanged();
1162 	return 0;
1163 }
1164 
set_temp_follow_state(bool state)1165 void Sheet::set_temp_follow_state(bool state)
1166 {
1167 	emit tempFollowChanged(state);
1168 }
1169 
1170 // Function is only to be called from GUI thread.
set_recordable()1171 Command * Sheet::set_recordable()
1172 {
1173 #if defined (THREAD_CHECK)
1174 	Q_ASSERT(QThread::currentThreadId() == threadId);
1175 #endif
1176 
1177 	// Do nothing if transport is rolling!
1178 	if (is_transport_rolling()) {
1179 		return 0;
1180 	}
1181 
1182 	// Transport is not rolling, it's save now to switch
1183 	// recording state to on /off
1184 	if (is_recording()) {
1185 		set_recording(false, false);
1186 	} else {
1187 		if (!any_track_armed()) {
1188 			info().critical(tr("No Tracks armed for recording!"));
1189 			return 0;
1190 		}
1191 
1192 		set_recording(true, false);
1193 	}
1194 
1195 	return 0;
1196 }
1197 
1198 // Function is only to be called from GUI thread.
set_recordable_and_start_transport()1199 Command* Sheet::set_recordable_and_start_transport()
1200 {
1201 	if (!is_recording()) {
1202 		set_recordable();
1203 	}
1204 
1205 	start_transport();
1206 
1207 	return 0;
1208 }
1209 
1210 // Function is only to be called from GUI thread.
start_transport()1211 Command* Sheet::start_transport()
1212 {
1213 #if defined (THREAD_CHECK)
1214 	Q_ASSERT(QThread::currentThreadId() == threadId);
1215 #endif
1216 	// Delegate the transport start (or if we are rolling stop)
1217 	// request to the audiodevice. Depending on the driver in use
1218 	// this call will return directly to us (by a call to transport_control),
1219 	// or handled by the driver
1220 	if (is_transport_rolling()) {
1221 		audiodevice().transport_stop(m_audiodeviceClient);
1222 	} else {
1223 		audiodevice().transport_start(m_audiodeviceClient);
1224 	}
1225 
1226 	return ie().succes();
1227 }
1228 
1229 // Function can be called either from the GUI or RT thread.
1230 // So ALL functions called here need to be RT thread save!!
transport_control(transport_state_t state)1231 int Sheet::transport_control(transport_state_t state)
1232 {
1233 	if (m_scheduledForDeletion) {
1234 		return true;
1235 	}
1236 
1237 	switch(state.tranport) {
1238 	case TransportStopped:
1239 		if (is_transport_rolling()) {
1240 			stop_transport_rolling();
1241 			if (is_recording()) {
1242 				set_recording(false, state.realtime);
1243 			}
1244 		}
1245 		return true;
1246 
1247 	case TransportStarting:
1248 		if (state.location != m_transportLocation) {
1249 			if ( ! m_seeking ) {
1250 				m_newTransportLocation = state.location;
1251 				m_startSeek = 1;
1252 				m_seeking = 1;
1253 
1254 				PMESG("tranport starting: initiating seek");
1255 				return false;
1256 			}
1257 		}
1258 		if (! m_seeking) {
1259 			if (is_recording()) {
1260 				if (!m_prepareRecording) {
1261 					m_prepareRecording = true;
1262 					// prepare_recording() is only to be called from the GUI thread
1263 					// so we delegate the prepare_recording() function call via a
1264 					// RT thread save signal!
1265 					Q_ASSERT(state.realtime);
1266 					RT_THREAD_EMIT(this, 0, prepareRecording());
1267 					PMESG("transport starting: initiating prepare for record");
1268 					return false;
1269 				}
1270 				if (!m_readyToRecord) {
1271 					PMESG("transport starting: still preparing for record");
1272 					return false;
1273 				}
1274 			}
1275 
1276 
1277 			PMESG("tranport starting: seek finished");
1278 			return true;
1279 		} else {
1280 			PMESG("tranport starting: still seeking");
1281 			return false;
1282 		}
1283 
1284 	case TransportRolling:
1285 		if (!is_transport_rolling()) {
1286 			// When the transport rolling request came from a non slave
1287 			// driver, we currently can assume it's comming from the GUI
1288 			// thread, and TransportStarting never was called before!
1289 			// So in case we are recording we have to prepare for recording now!
1290 			if ( ! state.isSlave && is_recording() ) {
1291 				Q_ASSERT(!state.realtime);
1292 				prepare_recording();
1293 			}
1294 			start_transport_rolling(state.realtime);
1295 		}
1296 		return true;
1297 	}
1298 
1299 	return false;
1300 }
1301 
1302 // RT thread save function
start_transport_rolling(bool realtime)1303 void Sheet::start_transport_rolling(bool realtime)
1304 {
1305 	m_realtimepath = true;
1306 	m_transport = 1;
1307 
1308 	if (realtime) {
1309 		RT_THREAD_EMIT(this, 0, transportStarted());
1310 	} else {
1311 		emit transportStarted();
1312 	}
1313 
1314 	PMESG("transport rolling");
1315 }
1316 
1317 // RT thread save function
stop_transport_rolling()1318 void Sheet::stop_transport_rolling()
1319 {
1320 	m_stopTransport = 1;
1321 	PMESG("transport stopped");
1322 }
1323 
1324 // RT thread save function
set_recording(bool recording,bool realtime)1325 void Sheet::set_recording(bool recording, bool realtime)
1326 {
1327 	m_recording = recording;
1328 
1329 	if (!m_recording) {
1330 		m_readyToRecord = false;
1331 		m_prepareRecording = false;
1332 	}
1333 
1334 	if (realtime) {
1335 		RT_THREAD_EMIT(this, 0, recordingStateChanged());
1336 	} else {
1337 		emit recordingStateChanged();
1338 	}
1339 }
1340 
1341 
1342 // NON RT thread save function, should only be called from GUI thread!!
prepare_recording()1343 void Sheet::prepare_recording()
1344 {
1345 #if defined (THREAD_CHECK)
1346 	Q_ASSERT(QThread::currentThreadId() == threadId);
1347 #endif
1348 
1349 	if (m_recording && any_track_armed()) {
1350 		CommandGroup* group = new CommandGroup(this, "");
1351 		int clipcount = 0;
1352 		apill_foreach(Track* track, Track, m_tracks) {
1353 			if (track->armed()) {
1354 				AudioClip* clip = track->init_recording();
1355 				if (clip) {
1356 					// For autosave purposes, we connect the recordingfinished
1357 					// signal to the clip_finished_recording() slot, and add this
1358 					// clip to our recording clip list.
1359 					// At the time the cliplist is empty, we're sure the recording
1360 					// session is finished, at which time an autosave makes sense.
1361 					connect(clip, SIGNAL(recordingFinished(AudioClip*)),
1362 						this, SLOT(clip_finished_recording(AudioClip*)));
1363 					m_recordingClips.append(clip);
1364 
1365 					group->add_command(new AddRemoveClip(clip, AddRemoveClip::ADD));
1366 					clipcount++;
1367 				}
1368 			}
1369 		}
1370 		group->setText(tr("Recording to %n Clip(s)", "", clipcount));
1371 		Command::process_command(group);
1372 	}
1373 
1374 	m_readyToRecord = true;
1375 }
1376 
clip_finished_recording(AudioClip * clip)1377 void Sheet::clip_finished_recording(AudioClip * clip)
1378 {
1379 	if (!m_recordingClips.removeAll(clip)) {
1380 		PERROR("clip %s was not in recording clip list, cannot remove it!", QS_C(clip->get_name()));
1381 	}
1382 
1383 	if (m_recordingClips.isEmpty()) {
1384 		// seems we finished recording completely now
1385 		// all clips have set their resulting ReadSource
1386 		// length and whatsoever, let's do an autosave:
1387 		m_project->save(true);
1388 	}
1389 }
1390 
1391 
set_transport_pos(TimeRef location)1392 void Sheet::set_transport_pos(TimeRef location)
1393 {
1394 #if defined (THREAD_CHECK)
1395 	Q_ASSERT(QThread::currentThreadId() ==  threadId);
1396 #endif
1397 	audiodevice().transport_seek_to(m_audiodeviceClient, location);
1398 }
1399 
1400 
1401 //
1402 //  Function is ALWAYS called in RealTime AudioThread processing path
1403 //  Be EXTREMELY carefull to not call functions() that have blocking behavior!!
1404 //
start_seek()1405 void Sheet::start_seek()
1406 {
1407 #if defined (THREAD_CHECK)
1408 	Q_ASSERT(threadId != QThread::currentThreadId ());
1409 #endif
1410 
1411 	if (is_transport_rolling()) {
1412 		m_realtimepath = false;
1413 		m_resumeTransport = true;
1414 	}
1415 
1416 	m_transport = false;
1417 	m_startSeek = 0;
1418 
1419 	// only sets a boolean flag, save to call.
1420 	m_diskio->prepare_for_seek();
1421 
1422 	// 'Tell' the diskio it should start a seek action.
1423 	RT_THREAD_EMIT(this, NULL, seekStart());
1424 }
1425 
seek_finished()1426 void Sheet::seek_finished()
1427 {
1428 #if defined (THREAD_CHECK)
1429 	Q_ASSERT_X(threadId == QThread::currentThreadId (), "Sheet::seek_finished", "Called from other Thread!");
1430 #endif
1431 	PMESG2("Sheet :: entering seek_finished");
1432 	m_transportLocation  = m_newTransportLocation;
1433 	m_seeking = 0;
1434 
1435 	if (m_resumeTransport) {
1436 		start_transport_rolling(false);
1437 		m_resumeTransport = false;
1438 	}
1439 
1440 	emit transportPosSet();
1441 	PMESG2("Sheet :: leaving seek_finished");
1442 }
1443 
config_changed()1444 void Sheet::config_changed()
1445 {
1446 	int quality = config().get_property("Conversion", "RTResamplingConverterType", DEFAULT_RESAMPLE_QUALITY).toInt();
1447 	if (m_diskio->get_resample_quality() != quality) {
1448 		m_diskio->set_resample_quality(quality);
1449 	}
1450 }
1451 
1452 
1453 
get_tracks() const1454 QList< Track * > Sheet::get_tracks() const
1455 {
1456 	QList<Track*> list;
1457 	apill_foreach(Track* track, Track, m_tracks) {
1458 		list.append(track);
1459 	}
1460 	return list;
1461 }
1462 
get_track_for_index(int index)1463 Track * Sheet::get_track_for_index(int index)
1464 {
1465 	apill_foreach(Track* track, Track, m_tracks) {
1466 		if (track->get_sort_index() == index) {
1467 			return track;
1468 		}
1469 	}
1470 
1471 	return 0;
1472 }
1473 
1474 
1475 // the timer is used to allow 'hopping' to the left from snap position to snap position
1476 // even during playback.
prev_skip_pos()1477 Command* Sheet::prev_skip_pos()
1478 {
1479 	if (snaplist->was_dirty()) {
1480 		update_skip_positions();
1481 	}
1482 
1483 	TimeRef p = get_transport_location();
1484 
1485 	if (p < TimeRef()) {
1486 		PERROR("pos < 0");
1487 		set_transport_pos(TimeRef());
1488 		return ie().failure();
1489 	}
1490 
1491 	QListIterator<TimeRef> it(m_xposList);
1492 
1493 	it.toBack();
1494 
1495 	int steps = 1;
1496 
1497 	if (m_skipTimer.isActive())
1498 	{
1499 		++steps;
1500 	}
1501 
1502 	int i = 0;
1503 	while (it.hasPrevious()) {
1504 		TimeRef pos = it.previous();
1505 		if (pos < p) {
1506 			p = pos;
1507 			++i;
1508 		}
1509 		if (i >= steps) {
1510 			break;
1511 		}
1512 	}
1513 
1514 	set_transport_pos(p);
1515 
1516 	m_skipTimer.start(500);
1517 
1518 	return ie().succes();
1519 }
1520 
next_skip_pos()1521 Command* Sheet::next_skip_pos()
1522 {
1523 	if (snaplist->was_dirty()) {
1524 		update_skip_positions();
1525 	}
1526 
1527 	TimeRef p = get_transport_location();
1528 
1529 	if (p > m_xposList.last()) {
1530 		PERROR("pos > last snap position");
1531 		return ie().failure();
1532 	}
1533 
1534 	QListIterator<TimeRef> it(m_xposList);
1535 
1536 	int i = 0;
1537 	int steps = 1;
1538 
1539 	while (it.hasNext()) {
1540 		TimeRef pos = it.next();
1541 		if (pos > p) {
1542 			p = pos;
1543 			++i;
1544 		}
1545 		if (i >= steps) {
1546 			break;
1547 		}
1548 	}
1549 
1550 	set_transport_pos(p);
1551 
1552 	return ie().succes();
1553 }
1554 
update_skip_positions()1555 void Sheet::update_skip_positions()
1556 {
1557 	m_xposList.clear();
1558 
1559 	// store the beginning of the sheet and the work cursor
1560 	m_xposList << TimeRef();
1561 	m_xposList << get_work_location();
1562 
1563 	// store all clip borders
1564 	QList<AudioClip* > acList = get_audioclip_manager()->get_clip_list();
1565 	for (int i = 0; i < acList.size(); ++i) {
1566 		m_xposList << acList.at(i)->get_track_start_location();
1567 		m_xposList << acList.at(i)->get_track_end_location();
1568 	}
1569 
1570 	// store all marker positions
1571 	QList<Marker*> markerList = get_timeline()->get_markers();
1572 	for (int i = 0; i < markerList.size(); ++i) {
1573 		m_xposList << markerList.at(i)->get_when();
1574 	}
1575 
1576 	// remove duplicates
1577 	QMutableListIterator<TimeRef> it(m_xposList);
1578 	while (it.hasNext()) {
1579 		TimeRef val = it.next();
1580 		if (m_xposList.count(val) > 1) {
1581 			it.remove();
1582 		}
1583 	}
1584 
1585 	qSort(m_xposList);
1586 }
1587 
skip_to_start()1588 void Sheet::skip_to_start()
1589 {
1590 	set_transport_pos((TimeRef()));
1591 	set_work_at((TimeRef()));
1592 }
1593 
skip_to_end()1594 void Sheet::skip_to_end()
1595 {
1596 	// stop the transport, no need to play any further than the end of the sheet
1597 	if (is_transport_rolling())
1598 	{
1599 		start_transport();
1600 	}
1601 	set_transport_pos(get_last_location());
1602 }
1603 
1604 //eof
1605