[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Traverso-commit] traverso/src/core Sheet.cpp Sheet.h Song.cpp So...
From: |
Remon Sijrier |
Subject: |
[Traverso-commit] traverso/src/core Sheet.cpp Sheet.h Song.cpp So... |
Date: |
Mon, 21 Jan 2008 16:22:49 +0000 |
CVSROOT: /sources/traverso
Module name: traverso
Changes by: Remon Sijrier <r_sijrier> 08/01/21 16:22:49
Added files:
src/core : Sheet.cpp Sheet.h
Removed files:
src/core : Song.cpp Song.h
Log message:
* Song->Sheet renaming
CVSWeb URLs:
http://cvs.savannah.gnu.org/viewcvs/traverso/src/core/Sheet.cpp?cvsroot=traverso&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/traverso/src/core/Sheet.h?cvsroot=traverso&rev=1.1
http://cvs.savannah.gnu.org/viewcvs/traverso/src/core/Song.cpp?cvsroot=traverso&r1=1.169&r2=0
http://cvs.savannah.gnu.org/viewcvs/traverso/src/core/Song.h?cvsroot=traverso&r1=1.79&r2=0
Patches:
Index: Sheet.cpp
===================================================================
RCS file: Sheet.cpp
diff -N Sheet.cpp
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Sheet.cpp 21 Jan 2008 16:22:48 -0000 1.1
@@ -0,0 +1,1424 @@
+/*
+Copyright (C) 2005-2007 Remon Sijrier
+
+This file is part of Traverso
+
+Traverso is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+*/
+
+#include <QTextStream>
+#include <QString>
+#include <QFile>
+#include <QFileInfo>
+#include <QList>
+#include <QMap>
+#include <QRegExp>
+#include <QDebug>
+
+#include <commands.h>
+
+#include "AbstractAudioReader.h"
+#include <AudioDevice.h>
+#include <AudioBus.h>
+#include <Client.h>
+#include "ProjectManager.h"
+#include "ContextPointer.h"
+#include "Information.h"
+#include "Sheet.h"
+#include "Project.h"
+#include "Track.h"
+#include "Mixer.h"
+#include "AudioSource.h"
+#include "AudioClip.h"
+#include "Peak.h"
+#include "Export.h"
+#include "DiskIO.h"
+#include "WriteSource.h"
+#include "AudioClipManager.h"
+#include "Tsar.h"
+#include "SnapList.h"
+#include "Config.h"
+#include "Utils.h"
+#include "ContextItem.h"
+#include "TimeLine.h"
+#include "Marker.h"
+#include "InputEngine.h"
+
+#include <Plugin.h>
+#include <PluginChain.h>
+
+// Always put me below _all_ includes, this is needed
+// in case we run with memory leak detection enabled!
+#include "Debugger.h"
+
+
+Sheet::Sheet(Project* project)
+ : ContextItem()
+ , m_project(project)
+{
+ PENTERCONS;
+ title = tr("Untitled");
+ m_id = create_id();
+ artists = tr("No artists name set");
+ m_hzoom = config().get_property("Sheet", "hzoomLevel", 8192).toInt();
+
+ init();
+}
+
+Sheet::Sheet(Project* project, int numtracks)
+ : ContextItem()
+ , m_project(project)
+{
+ PENTERCONS;
+ title = tr("Untitled");
+ m_id = create_id();
+ artists = tr("No artists name set");
+ m_hzoom = config().get_property("Sheet", "hzoomLevel", 8192).toInt();
+
+ init();
+
+ for (int i=1; i <= numtracks; i++) {
+ Track* track = create_track();
+ private_add_track(track);
+ }
+}
+
+Sheet::Sheet(Project* project, const QDomNode node)
+ : ContextItem(), m_project(project)
+{
+ PENTERCONS;
+ init();
+ set_state( node );
+}
+
+Sheet::~Sheet()
+{
+ PENTERDES;
+
+ delete [] mixdown;
+ delete [] gainbuffer;
+
+ delete m_diskio;
+ delete m_masterOut;
+ delete m_renderBus;
+ delete m_clipRenderBus;
+ delete m_hs;
+ delete m_audiodeviceClient;
+ delete snaplist;
+ delete workSnap;
+}
+
+void Sheet::init()
+{
+ PENTER2;
+#if defined (THREAD_CHECK)
+ threadId = QThread::currentThreadId ();
+#endif
+
+ m_diskio = new DiskIO(this);
+ m_currentSampleRate = audiodevice().get_sample_rate();
+ m_diskio->output_rate_changed(m_currentSampleRate);
+
+ connect(this, SIGNAL(seekStart()), m_diskio, SLOT(seek()),
Qt::QueuedConnection);
+ connect(this, SIGNAL(prepareRecording()), this,
SLOT(prepare_recording()));
+ connect(&audiodevice(), SIGNAL(clientRemoved(Client*)), this, SLOT
(audiodevice_client_removed(Client*)));
+ connect(&audiodevice(), SIGNAL(started()), this,
SLOT(audiodevice_started()));
+ connect(&audiodevice(), SIGNAL(driverParamsChanged()), this,
SLOT(audiodevice_params_changed()), Qt::DirectConnection);
+ connect(m_diskio, SIGNAL(seekFinished()), this, SLOT(seek_finished()),
Qt::QueuedConnection);
+ connect (m_diskio, SIGNAL(readSourceBufferUnderRun()), this,
SLOT(handle_diskio_readbuffer_underrun()));
+ connect (m_diskio, SIGNAL(writeSourceBufferOverRun()), this,
SLOT(handle_diskio_writebuffer_overrun()));
+ connect(&config(), SIGNAL(configChanged()), this,
SLOT(config_changed()));
+ connect(this, SIGNAL(transferStarted()), m_diskio, SLOT(start_io()));
+ connect(this, SIGNAL(transferStopped()), m_diskio, SLOT(stop_io()));
+
+ mixdown = gainbuffer = 0;
+ m_masterOut = new AudioBus("Master Out", 2);
+ m_renderBus = new AudioBus("Render Bus", 2);
+ m_clipRenderBus = new AudioBus("Clip Render Bus", 2);
+
+ resize_buffer(false, audiodevice().get_buffer_size());
+ m_hs = new QUndoStack(pm().get_undogroup());
+ set_history_stack(m_hs);
+ m_acmanager = new AudioClipManager(this);
+
+ set_context_item( m_acmanager );
+
+ m_playBackBus = audiodevice().get_playback_bus("Playback 1");
+
+ m_transport = m_stopTransport = m_resumeTransport = m_readyToRecord =
false;
+ snaplist = new SnapList(this);
+ workSnap = new Snappable();
+ workSnap->set_snap_list(snaplist);
+
+ m_realtimepath = false;
+ m_scheduledForDeletion = false;
+ m_isSnapOn=true;
+ changed = m_rendering = m_recording = m_prepareRecording = false;
+ firstVisibleFrame=0;
+ m_workLocation = TimeRef();
+ m_seeking = m_startSeek = 0;
+ // TODO seek to old position on project exit ?
+ m_transportLocation = TimeRef();
+ m_mode = EDIT;
+ m_sbx = m_sby = 0;
+
+ m_pluginChain = new PluginChain(this, this);
+ m_fader = m_pluginChain->get_fader();
+ m_fader->set_gain(0.5);
+ m_timeline = new TimeLine(this);
+
+ m_audiodeviceClient = new Client("sheet_" +
QByteArray::number(get_id()));
+ m_audiodeviceClient->set_process_callback( MakeDelegate(this,
&Sheet::process) );
+ m_audiodeviceClient->set_transport_control_callback( MakeDelegate(this,
&Sheet::transport_control) );
+}
+
+int Sheet::set_state( const QDomNode & node )
+{
+ PENTER;
+ QDomNode propertiesNode = node.firstChildElement("Properties");
+ m_id = node.toElement().attribute("id", "0").toLongLong();
+ if (m_id == 0) {
+ m_id = create_id();
+ }
+
+ QDomElement e = propertiesNode.toElement();
+
+ title = e.attribute( "title", "" );
+ artists = e.attribute( "artists", "" );
+ set_gain(e.attribute( "mastergain", "1.0").toFloat() );
+ qreal zoom = e.attribute("hzoom", "4096").toDouble();
+ set_hzoom(zoom);
+ m_sbx = e.attribute("sbx", "0").toInt();
+ m_sby = e.attribute("sby", "0").toInt();
+ set_first_visible_frame(e.attribute( "firstVisibleFrame", "0"
).toUInt());
+
+ bool ok;
+ TimeRef location(e.attribute( "m_workLocation", "0").toLongLong(&ok));
+ set_work_at(location);
+ m_transportLocation = TimeRef(e.attribute( "transportlocation",
"0").toLongLong(&ok));
+
+ // Start seeking to the 'old' transport pos
+ set_transport_pos(m_transportLocation);
+ set_snapping(e.attribute("snapping", "0").toInt());
+ m_mode = e.attribute("mode", "0").toInt();
+
+ m_timeline->set_state(node.firstChildElement("TimeLine"));
+
+ QDomNode tracksNode = node.firstChildElement("Tracks");
+ QDomNode trackNode = tracksNode.firstChild();
+
+ while(!trackNode.isNull()) {
+ Track* track = new Track(this, trackNode);
+ private_add_track(track);
+ track->set_state(trackNode);
+
+ trackNode = trackNode.nextSibling();
+ }
+
+ QDomNode pluginChainNode = node.firstChildElement("PluginChain");
+ m_pluginChain->set_state(pluginChainNode);
+
+ return 1;
+}
+
+QDomNode Sheet::get_state(QDomDocument doc, bool istemplate)
+{
+ QDomElement sheetNode = doc.createElement("Sheet");
+
+ if (! istemplate) {
+ sheetNode.setAttribute("id", m_id);
+ }
+
+ QDomElement properties = doc.createElement("Properties");
+ properties.setAttribute("title", title);
+ properties.setAttribute("artists", artists);
+ properties.setAttribute("firstVisibleFrame", firstVisibleFrame);
+ properties.setAttribute("m_workLocation",
m_workLocation.universal_frame());
+ properties.setAttribute("transportlocation",
m_transportLocation.universal_frame());
+ properties.setAttribute("hzoom", m_hzoom);
+ properties.setAttribute("sbx", m_sbx);
+ properties.setAttribute("sby", m_sby);
+ properties.setAttribute("snapping", m_isSnapOn);
+ properties.setAttribute("mode", m_mode);
+ sheetNode.appendChild(properties);
+
+ doc.appendChild(sheetNode);
+
+ sheetNode.appendChild(m_timeline->get_state(doc));
+
+ QDomNode tracksNode = doc.createElement("Tracks");
+
+ apill_foreach(Track* track, Track, m_tracks) {
+ tracksNode.appendChild(track->get_state(doc, istemplate));
+ }
+
+ sheetNode.appendChild(tracksNode);
+
+ QDomNode pluginChainNode = doc.createElement("PluginChain");
+ pluginChainNode.appendChild(m_pluginChain->get_state(doc));
+ sheetNode.appendChild(pluginChainNode);
+
+
+ return sheetNode;
+}
+
+void Sheet::connect_to_audiodevice( )
+{
+ PENTER;
+ audiodevice().add_client(m_audiodeviceClient);
+}
+
+void Sheet::disconnect_from_audiodevice()
+{
+ PENTER;
+ if (is_transport_rolling()) {
+ m_transport = false;
+ }
+ audiodevice().remove_client(m_audiodeviceClient);
+}
+
+void Sheet::schedule_for_deletion()
+{
+ m_scheduledForDeletion = true;
+ pm().scheduled_for_deletion(this);
+}
+
+void Sheet::audiodevice_client_removed(Client* client )
+{
+ PENTER;
+ if (m_audiodeviceClient == client) {
+ if (m_scheduledForDeletion) {
+ pm().delete_sheet(this);
+ }
+ }
+}
+
+Command* Sheet::add_track(Track* track, bool historable)
+{
+ apill_foreach(Track* existing, Track, m_tracks) {
+ if (existing->is_solo()) {
+ track->set_muted_by_solo( true );
+ break;
+ }
+ }
+
+ return new AddRemove(this, track, historable, this,
+ "private_add_track(Track*)", "trackAdded(Track*)",
+ "private_remove_track(Track*)", "trackRemoved(Track*)",
+ tr("Add Track"));
+}
+
+
+Command* Sheet::remove_track(Track* track, bool historable)
+{
+ return new AddRemove(this, track, historable, this,
+ "private_remove_track(Track*)", "trackRemoved(Track*)",
+ "private_add_track(Track*)", "trackAdded(Track*)",
+ tr("Remove Track"));
+}
+
+bool Sheet::any_track_armed()
+{
+ apill_foreach(Track* track, Track, m_tracks) {
+ if (track->armed()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+int Sheet::prepare_export(ExportSpecification* spec)
+{
+ PENTER;
+
+ if ( ! (spec->renderpass == ExportSpecification::CREATE_CDRDAO_TOC) ) {
+ if (is_transport_rolling()) {
+ spec->resumeTransport = true;
+ // When transport is rolling, this equals stopping the
transport!
+ // prepare_export() is called from another thread, so
use a queued connection
+ // to call the function in the correct thread!
+ if (!QMetaObject::invokeMethod(this, "start_transport",
Qt::QueuedConnection)) {
+ printf("Invoking Sheet::start_transport()
failed\n");
+ return -1;
+ }
+ int count = 0;
+ uint msecs = (audiodevice().get_buffer_size() * 1000) /
audiodevice().get_sample_rate();
+ // wait a number (max 10) of process() cycles to be
sure we really stopped transport
+ while (m_transport) {
+ spec->thread->sleep_for(msecs);
+ count++;
+ if (count > 10) {
+ break;
+ }
+ }
+ printf("Sheet::prepare_export: had to wait %d process
cycles before the transport was stopped\n", count);
+ }
+
+ m_rendering = true;
+ }
+
+ spec->startLocation = LONG_LONG_MAX;
+ spec->endLocation = TimeRef();
+
+ TimeRef endlocation, startlocation;
+
+ apill_foreach(Track* track, Track, m_tracks) {
+ track->get_render_range(startlocation, endlocation);
+
+ if (track->is_solo()) {
+ spec->endLocation = endlocation;
+ spec->startLocation = startlocation;
+ break;
+ }
+
+ if (endlocation > spec->endLocation) {
+ spec->endLocation = endlocation;
+ }
+
+ if (startlocation < spec->startLocation) {
+ spec->startLocation = startlocation;
+ }
+ }
+
+ if (spec->isCdExport) {
+ QList<Marker*> markers = m_timeline->get_markers();
+
+ if (m_timeline->get_start_location(startlocation)) {
+ PMESG2(" Start marker found at %s",
QS_C(timeref_to_ms(startlocation)));
+ // round down to the start of the CD frame (75th of a
sec)
+ startlocation =
cd_to_timeref(timeref_to_cd(startlocation));
+ spec->startLocation = startlocation;
+ } else {
+ PMESG2(" No start marker found");
+ }
+
+ if (m_timeline->get_end_location(endlocation)) {
+ PMESG2(" End marker found at %s",
QS_C(timeref_to_ms(endlocation)));
+ spec->endLocation = endlocation;
+ } else {
+ PMESG2(" No end marker found");
+ }
+ }
+
+ spec->totalTime = spec->endLocation - spec->startLocation;
+
+// PWARN("Render length is:
%s",timeref_to_ms_3(spec->totalTime).toAscii().data() );
+
+ spec->pos = spec->startLocation;
+ spec->progress = 0;
+
+ spec->basename = "Sheet_" +
QString::number(m_project->get_sheet_index(m_id)) +"-" + title;
+ spec->name = spec->basename;
+
+ if (spec->startLocation == spec->endLocation) {
+ info().warning(tr("No audio to export! (Is everything
muted?)"));
+ return -1;
+ }
+ else if (spec->startLocation > spec->endLocation) {
+ info().warning(tr("Export start frame starts beyond export end
frame!!"));
+ return -1;
+ }
+
+ if (spec->channels == 0) {
+ info().warning(tr("Export tries to render to 0 channels wav
file??"));
+ return -1;
+ }
+
+ if (spec->renderpass == ExportSpecification::CREATE_CDRDAO_TOC) {
+ return 1;
+ }
+
+ if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK) {
+ m_exportSource = new WriteSource(spec);
+ if (m_exportSource->prepare_export() == -1) {
+ delete m_exportSource;
+ return -1;
+ }
+ }
+
+ m_transportLocation = spec->startLocation;
+
+ resize_buffer(false, spec->blocksize);
+
+ renderDecodeBuffer = new DecodeBuffer;
+// renderDecodeBuffer->use_custom_destination_buffer(true);
+
+ return 1;
+}
+
+int Sheet::finish_audio_export()
+{
+ m_exportSource->finish_export();
+ delete m_exportSource;
+ delete renderDecodeBuffer;
+ resize_buffer(false, audiodevice().get_buffer_size());
+ return 0;
+}
+
+int Sheet::render(ExportSpecification* spec)
+{
+ int chn;
+ uint32_t x;
+ int ret = -1;
+ int progress;
+
+ nframes_t diff = (spec->endLocation -
spec->pos).to_frame(audiodevice().get_sample_rate());
+ nframes_t nframes = spec->blocksize;
+ nframes_t this_nframes = std::min(diff, nframes);
+
+ if (!spec->running || spec->stop || this_nframes == 0) {
+ process_export (nframes);
+ /* PWARN("Finished Rendering for this sheet");
+ PWARN("running is %d", spec->running);
+ PWARN("stop is %d", spec->stop);
+ PWARN("this_nframes is %d", this_nframes);*/
+ if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK)
{
+ return finish_audio_export();
+ } else {
+ return 0;
+ }
+ }
+
+ /* do the usual stuff */
+
+ process_export(nframes);
+
+ /* and now export the results */
+
+ nframes = this_nframes;
+
+ memset (spec->dataF, 0, sizeof (spec->dataF[0]) * nframes *
spec->channels);
+
+ /* foreach output channel ... */
+
+ float* buf;
+
+ for (chn = 0; chn < spec->channels; ++chn) {
+ buf = m_masterOut->get_buffer(chn, nframes);
+
+ if (!buf) {
+ // Seem we are exporting at least to Stereo from an
AudioBus with only one channel...
+ // Use the first channel..
+ buf = m_masterOut->get_buffer(0, nframes);
+ }
+
+ for (x = 0; x < nframes; ++x) {
+ spec->dataF[chn+(x*spec->channels)] = buf[x];
+ }
+ }
+
+
+ int bufsize = spec->blocksize * spec->channels;
+ if (spec->normalize) {
+ if (spec->renderpass == ExportSpecification::CALC_NORM_FACTOR) {
+ spec->peakvalue = Mixer::compute_peak(spec->dataF,
bufsize, spec->peakvalue);
+ }
+ }
+
+ if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK) {
+ if (spec->normalize) {
+ Mixer::apply_gain_to_buffer(spec->dataF, bufsize,
spec->normvalue);
+ }
+ if (m_exportSource->process (nframes)) {
+ goto out;
+ }
+ }
+
+
+ spec->pos.add_frames(nframes, audiodevice().get_sample_rate());
+
+ if (! spec->normalize ) {
+ progress = int((double((spec->pos -
spec->startLocation).universal_frame()) / spec->totalTime.universal_frame()) *
100);
+ } else {
+ progress = (int) (double( 100 * (spec->pos -
spec->startLocation).universal_frame()) / (spec->totalTime.universal_frame() *
2));
+ if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK)
{
+ progress += 50;
+ }
+ }
+
+ if (progress > spec->progress) {
+ spec->progress = progress;
+ m_project->set_sheet_export_progress(progress);
+ }
+
+
+ /* and we're good to go */
+
+ ret = 1;
+
+out:
+ if (!ret) {
+ spec->running = false;
+ spec->status = ret;
+ m_rendering = false;
+ }
+
+ return ret;
+}
+
+
+SnapList* Sheet::get_snap_list() const
+{
+ return snaplist;
+}
+
+
+void Sheet::set_artists(const QString& pArtists)
+{
+ artists = pArtists;
+}
+
+void Sheet::set_gain(float gain)
+{
+ if (gain < 0.0)
+ gain = 0.0;
+ if (gain > 2.0)
+ gain = 2.0;
+
+ m_fader->set_gain(gain);
+
+ emit masterGainChanged();
+}
+
+void Sheet::set_title(const QString& sTitle)
+{
+ title=sTitle;
+ emit propertyChanged();
+}
+
+void Sheet::set_first_visible_frame(nframes_t pos)
+{
+ PENTER;
+ firstVisibleFrame = pos;
+ emit firstVisibleFrameChanged();
+}
+
+void Sheet::set_work_at(const TimeRef& location)
+{
+ m_workLocation = location;
+ snaplist->mark_dirty(workSnap);
+ emit workingPosChanged();
+}
+
+Command* Sheet::toggle_snap()
+{
+ set_snapping( ! m_isSnapOn );
+ return 0;
+}
+
+
+void Sheet::set_snapping(bool snapping)
+{
+ m_isSnapOn = snapping;
+ emit snapChanged();
+}
+
+/******************************** SLOTS *****************************/
+
+Track* Sheet::create_track()
+{
+ int height = Track::INITIAL_HEIGHT;
+
+ Track* track = new Track(this, "Unnamed", height);
+
+ return track;
+}
+
+void Sheet::solo_track(Track* t)
+{
+ bool wasSolo = t->is_solo();
+
+ t->set_muted_by_solo(!wasSolo);
+ t->set_solo(!wasSolo);
+
+ bool hasSolo = false;
+ apill_foreach(Track* track, Track, m_tracks) {
+ track->set_muted_by_solo(!track->is_solo());
+ if (track->is_solo()) hasSolo = true;
+ }
+
+ if (!hasSolo) {
+ apill_foreach(Track* track, Track, m_tracks) {
+ track->set_muted_by_solo(false);
+ }
+ }
+}
+
+Command* Sheet::toggle_solo()
+{
+ bool hasSolo = false;
+ apill_foreach(Track* track, Track, m_tracks) {
+ if (track->is_solo()) hasSolo = true;
+ }
+
+ apill_foreach(Track* track, Track, m_tracks) {
+ track->set_solo(!hasSolo);
+ track->set_muted_by_solo(false);
+ }
+
+ return (Command*) 0;
+}
+
+Command *Sheet::toggle_mute()
+{
+ bool hasMute = false;
+ apill_foreach(Track* track, Track, m_tracks) {
+ if (track->is_muted()) hasMute = true;
+ }
+
+ apill_foreach(Track* track, Track, m_tracks) {
+ track->set_muted(!hasMute);
+ }
+
+ return (Command*) 0;
+}
+
+Command *Sheet::toggle_arm()
+{
+ bool hasArmed = false;
+ apill_foreach(Track* track, Track, m_tracks) {
+ if (track->armed()) hasArmed = true;
+ }
+
+ apill_foreach(Track* track, Track, m_tracks) {
+ if (hasArmed) {
+ track->disarm();
+ } else {
+ track->arm();
+ }
+ }
+
+ return (Command*) 0;
+}
+
+Command* Sheet::work_next_edge()
+{
+/* nframes_t w = m_acmanager->get_last_frame();
+
+ foreach(Track* track, m_tracks) {
+ AudioClip* c=track->get_clip_after(m_workLocation);
+
+ if ((c) && (c->get_track_start_location() < w &&
c->get_track_start_location() > m_workLocation))
+ w = c->get_track_start_location();
+ }
+
+ set_work_at(w);
+
+ emit setCursorAtEdge();
+*/
+ return (Command*) 0;
+}
+
+Command* Sheet::work_previous_edge()
+{
+/* TimeRef w(0);
+ foreach(Track* track, m_tracks) {
+ AudioClip* c = track->get_clip_before(m_workLocation);
+ if ((c) && (c->get_track_end_location() >= w &&
c->get_track_end_location() < m_workLocation) )
+ w=c->get_track_end_location();
+ }
+
+ set_work_at(w);
+
+ emit setCursorAtEdge();
+*/
+ return (Command*) 0;
+}
+
+void Sheet::set_hzoom( qreal hzoom )
+{
+ if (hzoom > Peak::max_zoom_value()) {
+ hzoom = Peak::max_zoom_value();
+ }
+
+ if (hzoom < 0.1) {
+ hzoom = 0.1;
+ }
+
+ if (m_hzoom == hzoom) {
+ return;
+ }
+
+ m_hzoom = hzoom;
+
+ emit hzoomChanged();
+}
+
+//
+// Function called in RealTime AudioThread processing path
+//
+int Sheet::process( nframes_t nframes )
+{
+ if (m_startSeek) {
+ start_seek();
+ return 0;
+ }
+
+ // If no need for playback/record, return.
+ if (!is_transport_rolling()) {
+ return 0;
+ }
+
+ if (m_stopTransport) {
+ RT_THREAD_EMIT(this, 0, transferStopped());
+ m_transport = false;
+ m_realtimepath = false;
+ m_stopTransport = false;
+
+ return 0;
+ }
+
+ // zero the m_masterOut buffers
+ m_masterOut->silence_buffers(nframes);
+
+ int processResult = 0;
+
+
+ // Process all Tracks.
+ apill_foreach(Track* track, Track, m_tracks) {
+ processResult |= track->process(nframes);
+ }
+
+ // update the transport location
+ m_transportLocation.add_frames(nframes,
audiodevice().get_sample_rate());
+
+ if (!processResult) {
+ return 0;
+ }
+
+ // Mix the result into the AudioDevice "physical" buffers
+ if (m_playBackBus) {
+ Mixer::mix_buffers_with_gain(m_playBackBus->get_buffer(0,
nframes), m_masterOut->get_buffer(0, nframes), nframes, get_gain());
+ Mixer::mix_buffers_with_gain(m_playBackBus->get_buffer(1,
nframes), m_masterOut->get_buffer(1, nframes), nframes, get_gain());
+
+ m_pluginChain->process_post_fader(m_masterOut, nframes);
+ }
+
+
+ return 1;
+}
+
+int Sheet::process_export( nframes_t nframes )
+{
+ // Get the masterout buffers, and fill with zero's
+ m_masterOut->silence_buffers(nframes);
+ memset (mixdown, 0, sizeof (audio_sample_t) * nframes);
+
+ // Process all Tracks.
+ apill_foreach(Track* track, Track, m_tracks) {
+ track->process(nframes);
+ }
+
+ Mixer::apply_gain_to_buffer(m_masterOut->get_buffer(0, nframes),
nframes, get_gain());
+ Mixer::apply_gain_to_buffer(m_masterOut->get_buffer(1, nframes),
nframes, get_gain());
+
+ // update the m_transportFrame
+// m_transportFrame += nframes;
+ m_transportLocation.add_frames(nframes,
audiodevice().get_sample_rate());
+
+ return 1;
+}
+
+QString Sheet::get_cdrdao_tracklist(ExportSpecification* spec, bool pregap)
+{
+ QString output;
+
+ QList<Marker*> mlist = m_timeline->get_markers();
+ QList<Marker*> tempmarkers;
+
+ //Â Here we make the marker-stuff idiot-proof ;-). Traverso doesn't
insist on having any
+ //Â marker at all, so we need to handle cases like:
+ // - no markers at all
+ // - one marker (doesn't make sense)
+ // - enough markers, but no end marker
+
+ Marker* temp;
+
+ if (mlist.size() < 2) {
+ switch (mlist.size()) {
+ case 0:
+ // no markers present. We add one at the
beginning and one at the
+ // end of the render area.
+ temp = new Marker(m_timeline,
spec->startLocation, Marker::CDTRACK);
+ tempmarkers.append(temp);
+ mlist.prepend(temp);
+ temp = new Marker(m_timeline,
spec->endLocation, Marker::ENDMARKER);
+ tempmarkers.append(temp);
+ mlist.append(temp);
+ break;
+ case 1:
+ // one marker is present. We add two more, one
at the beginning
+ // and one at the end of the render area. If
the present marker
+ // happened to be at either the start or the
end position,
+ // it will now be overwritten, so we will never
end up with
+ // two markers at the same position.
+
+ //Â deactivate the next if-condition (only the
first one) if you want the
+ // stuff before the first marker to go into the
pre-gap
+ if (mlist.at(0)->get_when() !=
(spec->startLocation)) {
+ temp = new Marker(m_timeline,
spec->startLocation, Marker::CDTRACK);
+ tempmarkers.append(temp);
+ mlist.prepend(temp);
+ }
+ if (mlist.at(0)->get_when() !=
(spec->startLocation)) {
+ temp = new Marker(m_timeline,
spec->endLocation, Marker::ENDMARKER);
+ tempmarkers.append(temp);
+ mlist.append(temp);
+ }
+ break;
+ }
+ } else {
+ // would be ok, but let's check if there is an end marker
present. If not,
+ // add one to spec->end_frame
+ if (!m_timeline->has_end_marker()) {
+ temp = new Marker(m_timeline, spec->endLocation,
Marker::ENDMARKER);
+ tempmarkers.append(temp);
+ mlist.append(temp);
+ }
+ }
+
+ TimeRef start;
+
+ for(int i = 0; i < mlist.size()-1; ++i) {
+
+ Marker* startmarker = mlist.at(i);
+ Marker* endmarker = mlist.at(i+1);
+
+ output += "TRACK AUDIO\n";
+
+ if (startmarker->get_copyprotect()) {
+ output += " NO COPY\n";
+ } else {
+ output += " COPY\n";
+ }
+
+ if (startmarker->get_preemphasis()) {
+ output += " PRE_EMPHASIS\n";
+ }
+
+ output += " CD_TEXT {\n LANGUAGE 0 {\n";
+ output += " TITLE \"" + startmarker->get_description() +
"\"\n";
+ output += " PERFORMER \"" + startmarker->get_performer() +
"\"\n";
+ output += " ISRC \"" + startmarker->get_isrc() + "\"\n";
+ output += " ARRANGER \"" + startmarker->get_arranger() +
"\"\n";
+ output += " SONGWRITER \"" +
startmarker->get_sheetwriter() + "\"\n";
+ output += " MESSAGE \"" + startmarker->get_message() +
"\"\n }\n }\n";
+
+ //Â add some stuff only required for the first track (e.g.
pre-gap)
+ if ((i == 0) && pregap) {
+ //if (start == 0) {
+ // standard pregap, because we have a track
marker at the beginning
+ output += " PREGAP 00:02:00\n";
+ //} else {
+ // // no track marker at the beginning, thus use
the part from 0 to the first
+ // //Â track marker for the pregap
+ // output += " START " + frame_to_cd(start,
m_project->get_rate()) + "\n";
+ // start = 0;
+ //}
+ }
+
+ TimeRef length =
cd_to_timeref(timeref_to_cd(endmarker->get_when())) -
cd_to_timeref(timeref_to_cd(startmarker->get_when()));
+
+ QString s_start = timeref_to_cd(start);
+ QString s_length = timeref_to_cd(length);
+
+ output += " FILE \"" + spec->name + "." +
spec->extraFormat["filetype"] + "\" " + s_start + " " + s_length + "\n\n";
+ start += length;
+
+ // check if the second marker is of type "Endmarker"
+ if (endmarker->get_type() == Marker::ENDMARKER) {
+ break;
+ }
+ }
+
+ //Â delete all temporary markers
+ foreach(Marker* marker, tempmarkers) {
+ delete marker;
+ }
+
+ return output;
+}
+
+void Sheet::resize_buffer(bool updateArmStatus, nframes_t size)
+{
+ if (mixdown)
+ delete [] mixdown;
+ if (gainbuffer)
+ delete [] gainbuffer;
+ mixdown = new audio_sample_t[size];
+ gainbuffer = new audio_sample_t[size];
+ m_masterOut->set_buffer_size(size);
+ m_renderBus->set_buffer_size(size);
+ m_clipRenderBus->set_buffer_size(size);
+
+ if (updateArmStatus) {
+ apill_foreach(Track* track, Track, m_tracks) {
+ AudioBus* bus =
audiodevice().get_capture_bus(track->get_bus_in().toAscii());
+ if (bus && track->armed()) {
+ bus->set_monitor_peaks(true);
+ }
+ }
+ }
+}
+
+void Sheet::audiodevice_params_changed()
+{
+ resize_buffer(true, audiodevice().get_buffer_size());
+
+ // The samplerate possibly has been changed, this initiates
+ // a seek in DiskIO, which clears the buffers and refills them
+ // with the correct resampled audio data!
+ // We need to seek to a different position then the current one,
+ // else the seek won't happen at all :)
+ if (m_currentSampleRate != audiodevice().get_sample_rate()) {
+ m_currentSampleRate = audiodevice().get_sample_rate();
+
+ m_diskio->output_rate_changed(m_currentSampleRate);
+
+ TimeRef location = m_transportLocation;
+ location.add_frames(1, audiodevice().get_sample_rate());
+
+ set_transport_pos(location);
+ }
+}
+
+int Sheet::get_bitdepth( )
+{
+ return m_project->get_bitdepth();
+}
+
+int Sheet::get_rate( )
+{
+ return m_project->get_rate();
+}
+
+nframes_t Sheet::get_first_visible_frame( ) const
+{
+ return firstVisibleFrame;
+}
+
+DiskIO * Sheet::get_diskio( ) const
+{
+ return m_diskio;
+}
+
+AudioClipManager * Sheet::get_audioclip_manager( ) const
+{
+ return m_acmanager;
+}
+
+PluginChain* Sheet::get_plugin_chain() const
+{
+ return m_pluginChain;
+}
+
+int Sheet::get_track_index(qint64 id)
+{
+ int i=0;
+ apill_foreach(Track* track, Track, m_tracks) {
+ if (track->get_id() == id) {
+ return i + 1;
+ }
+ ++i;
+ }
+ return 0;
+}
+
+void Sheet::handle_diskio_readbuffer_underrun( )
+{
+ if (is_transport_rolling()) {
+ printf("Sheet:: DiskIO ReadBuffer UnderRun signal received!\n");
+ info().critical(tr("Hard Disk overload detected!"));
+ info().critical(tr("Failed to fill ReadBuffer in time"));
+ }
+}
+
+void Sheet::handle_diskio_writebuffer_overrun( )
+{
+ if (is_transport_rolling()) {
+ printf("Sheet:: DiskIO WriteBuffer OverRun signal received!\n");
+ info().critical(tr("Hard Disk overload detected!"));
+ info().critical(tr("Failed to empty WriteBuffer in time"));
+ }
+}
+
+void Sheet::audiodevice_started( )
+{
+ m_playBackBus = audiodevice().get_playback_bus("Playback 1");
+}
+
+const TimeRef& Sheet::get_last_location() const
+{
+ TimeRef lastAudio = m_acmanager->get_last_location();
+
+ if (m_timeline->get_markers().size()) {
+ TimeRef lastMarker =
m_timeline->get_markers().last()->get_when();
+ return (lastAudio > lastMarker) ? lastAudio : lastMarker;
+ }
+
+ return lastAudio;
+}
+
+void Sheet::private_add_track(Track* track)
+{
+ m_tracks.append(track);
+}
+
+void Sheet::private_remove_track(Track* track)
+{
+ m_tracks.remove(track);
+}
+
+Track* Sheet::get_track(qint64 id)
+{
+ apill_foreach(Track* track, Track, m_tracks) {
+ if (track->get_id() == id) {
+ return track;
+ }
+ }
+ return 0;
+}
+
+void Sheet::move_clip(Track * from, Track * too, AudioClip * clip, TimeRef
location)
+{
+ if (from == too) {
+ clip->set_track_start_location(location);
+ return;
+ }
+
+ Command::process_command(from->remove_clip(clip, false, true));
+ Command::process_command(too->add_clip(clip, false, true));
+
+ clip->set_track_start_location(location);
+}
+
+Command* Sheet::set_editing_mode( )
+{
+ m_mode = EDIT;
+ emit modeChanged();
+ return 0;
+}
+
+Command* Sheet::set_effects_mode( )
+{
+ m_mode = EFFECTS;
+ emit modeChanged();
+ return 0;
+}
+
+void Sheet::set_temp_follow_state(bool state)
+{
+ emit tempFollowChanged(state);
+}
+
+// Function is only to be called from GUI thread.
+Command * Sheet::set_recordable()
+{
+#if defined (THREAD_CHECK)
+ Q_ASSERT(QThread::currentThreadId() == threadId);
+#endif
+
+ // Do nothing if transport is rolling!
+ if (is_transport_rolling()) {
+ return 0;
+ }
+
+ // Transport is not rolling, it's save now to switch
+ // recording state to on /off
+ if (is_recording()) {
+ set_recording(false, false);
+ } else {
+ if (!any_track_armed()) {
+ info().critical(tr("No Tracks armed for recording!"));
+ return 0;
+ }
+
+ set_recording(true, false);
+ }
+
+ return 0;
+}
+
+// Function is only to be called from GUI thread.
+Command* Sheet::set_recordable_and_start_transport()
+{
+ if (!is_recording()) {
+ set_recordable();
+ }
+
+ start_transport();
+
+ return 0;
+}
+
+// Function is only to be called from GUI thread.
+Command* Sheet::start_transport()
+{
+#if defined (THREAD_CHECK)
+ Q_ASSERT(QThread::currentThreadId() == threadId);
+#endif
+ // Delegate the transport start (or if we are rolling stop)
+ // request to the audiodevice. Depending on the driver in use
+ // this call will return directly to us (by a call to
transport_control),
+ // or handled by the driver
+ if (is_transport_rolling()) {
+ audiodevice().transport_stop(m_audiodeviceClient);
+ } else {
+ audiodevice().transport_start(m_audiodeviceClient);
+ }
+
+ return ie().succes();
+}
+
+// Function can be called either from the GUI or RT thread.
+// So ALL functions called here need to be RT thread save!!
+int Sheet::transport_control(transport_state_t state)
+{
+ if (m_scheduledForDeletion) {
+ return true;
+ }
+
+ switch(state.tranport) {
+ case TransportStopped:
+ if (is_transport_rolling()) {
+ stop_transport_rolling();
+ if (is_recording()) {
+ set_recording(false, state.realtime);
+ }
+ }
+ return true;
+
+ case TransportStarting:
+ if (state.location != m_transportLocation) {
+ if ( ! m_seeking ) {
+ m_newTransportLocation = state.location;
+ m_startSeek = 1;
+ m_seeking = 1;
+
+ PMESG("tranport starting: initiating seek");
+ return false;
+ }
+ }
+ if (! m_seeking) {
+ if (is_recording()) {
+ if (!m_prepareRecording) {
+ m_prepareRecording = true;
+ // prepare_recording() is only to be
called from the GUI thread
+ // so we delegate the
prepare_recording() function call via a
+ // RT thread save signal!
+ Q_ASSERT(state.realtime);
+ RT_THREAD_EMIT(this, 0,
prepareRecording());
+ PMESG("transport starting: initiating
prepare for record");
+ return false;
+ }
+ if (!m_readyToRecord) {
+ PMESG("transport starting: still
preparing for record");
+ return false;
+ }
+ }
+
+
+ PMESG("tranport starting: seek finished");
+ return true;
+ } else {
+ PMESG("tranport starting: still seeking");
+ return false;
+ }
+
+ case TransportRolling:
+ if (!is_transport_rolling()) {
+ // When the transport rolling request came from a non
slave
+ // driver, we currently can assume it's comming from
the GUI
+ // thread, and TransportStarting never was called
before!
+ // So in case we are recording we have to prepare for
recording now!
+ if ( ! state.isSlave && is_recording() ) {
+ Q_ASSERT(!state.realtime);
+ prepare_recording();
+ }
+ start_transport_rolling(state.realtime);
+ }
+ return true;
+ }
+
+ return false;
+}
+
+// RT thread save function
+void Sheet::start_transport_rolling(bool realtime)
+{
+ m_realtimepath = true;
+ m_transport = 1;
+
+ if (realtime) {
+ RT_THREAD_EMIT(this, 0, transferStarted());
+ } else {
+ emit transferStarted();
+ }
+
+ PMESG("tranport rolling");
+}
+
+// RT thread save function
+void Sheet::stop_transport_rolling()
+{
+ m_stopTransport = 1;
+ PMESG("tranport stopped");
+}
+
+// RT thread save function
+void Sheet::set_recording(bool recording, bool realtime)
+{
+ m_recording = recording;
+
+ if (!m_recording) {
+ m_readyToRecord = false;
+ m_prepareRecording = false;
+ }
+
+ if (realtime) {
+ RT_THREAD_EMIT(this, 0, recordingStateChanged());
+ } else {
+ emit recordingStateChanged();
+ }
+}
+
+
+// NON RT thread save function, should only be called from GUI thread!!
+void Sheet::prepare_recording()
+{
+#if defined (THREAD_CHECK)
+ Q_ASSERT(QThread::currentThreadId() == threadId);
+#endif
+
+ if (m_recording && any_track_armed()) {
+ CommandGroup* group = new CommandGroup(this, "");
+ int clipcount = 0;
+ apill_foreach(Track* track, Track, m_tracks) {
+ if (track->armed()) {
+ AudioClip* clip = track->init_recording();
+ if (clip) {
+ // For autosave purposes, we connect
the recordingfinished
+ // signal to the
clip_finished_recording() slot, and add this
+ // clip to our recording clip list.
+ // At the time the cliplist is empty,
we're sure the recording
+ // session is finished, at which time
an autosave makes sense.
+ connect(clip,
SIGNAL(recordingFinished(AudioClip*)),
+ this,
SLOT(clip_finished_recording(AudioClip*)));
+ m_recordingClips.append(clip);
+
+ group->add_command(new
AddRemoveClip(clip, AddRemoveClip::ADD));
+ clipcount++;
+ }
+ }
+ }
+ group->setText(tr("Recording to %n Clip(s)", "", clipcount));
+ Command::process_command(group);
+ }
+
+ m_readyToRecord = true;
+}
+
+void Sheet::clip_finished_recording(AudioClip * clip)
+{
+ if (!m_recordingClips.removeAll(clip)) {
+ PERROR("clip %s was not in recording clip list, cannot remove
it!", QS_C(clip->get_name()));
+ }
+
+ if (m_recordingClips.isEmpty()) {
+ // seems we finished recording completely now
+ // all clips have set their resulting ReadSource
+ // length and whatsoever, let's do an autosave:
+ m_project->save(true);
+ }
+}
+
+
+void Sheet::set_transport_pos(TimeRef location)
+{
+#if defined (THREAD_CHECK)
+ Q_ASSERT(QThread::currentThreadId() == threadId);
+#endif
+ audiodevice().transport_seek_to(m_audiodeviceClient, location);
+}
+
+
+//
+// Function is ALWAYS called in RealTime AudioThread processing path
+// Be EXTREMELY carefull to not call functions() that have blocking behavior!!
+//
+void Sheet::start_seek()
+{
+#if defined (THREAD_CHECK)
+ Q_ASSERT(threadId != QThread::currentThreadId ());
+#endif
+
+ if (is_transport_rolling()) {
+ m_realtimepath = false;
+ m_resumeTransport = true;
+ }
+
+ m_transport = false;
+ m_startSeek = 0;
+
+ // only sets a boolean flag, save to call.
+ m_diskio->prepare_for_seek();
+
+ // 'Tell' the diskio it should start a seek action.
+ RT_THREAD_EMIT(this, NULL, seekStart());
+}
+
+void Sheet::seek_finished()
+{
+#if defined (THREAD_CHECK)
+ Q_ASSERT_X(threadId == QThread::currentThreadId (),
"Sheet::seek_finished", "Called from other Thread!");
+#endif
+ PMESG2("Sheet :: entering seek_finished");
+ m_transportLocation = m_newTransportLocation;
+ m_seeking = 0;
+
+ if (m_resumeTransport) {
+ start_transport_rolling(false);
+ m_resumeTransport = false;
+ }
+
+ emit transportPosSet();
+ PMESG2("Sheet :: leaving seek_finished");
+}
+
+void Sheet::config_changed()
+{
+ PENTER;
+
+ int quality = config().get_property("Conversion",
"RTResamplingConverterType", 2).toInt();
+ if (m_diskio->get_resample_quality() != quality) {
+ TimeRef location = m_transportLocation;
+ location.add_frames(1, audiodevice().get_sample_rate());
+
+ set_transport_pos(location);
+ }
+}
+
+
+
+QList< Track * > Sheet::get_tracks() const
+{
+ QList<Track*> list;
+ apill_foreach(Track* track, Track, m_tracks) {
+ list.append(track);
+ }
+ return list;
+}
Index: Sheet.h
===================================================================
RCS file: Sheet.h
diff -N Sheet.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ Sheet.h 21 Jan 2008 16:22:48 -0000 1.1
@@ -0,0 +1,288 @@
+/*
+Copyright (C) 2005-2007 Remon Sijrier
+
+This file is part of Traverso
+
+Traverso is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+
+*/
+
+#ifndef SONG_H
+#define SONG_H
+
+#include "ContextItem.h"
+#include "AudioProcessingItem.h"
+#include <QDomNode>
+#include "defines.h"
+#include "APILinkedList.h"
+#include "GainEnvelope.h"
+
+class Project;
+class Track;
+class AudioSource;
+class WriteSource;
+class Track;
+class AudioClip;
+class DiskIO;
+class AudioClipManager;
+class Client;
+class AudioBus;
+class PluginChain;
+class SnapList;
+class Plugin;
+class TimeLine;
+class Snappable;
+class DecodeBuffer;
+
+struct ExportSpecification;
+
+class Sheet : public ContextItem, public AudioProcessingItem
+{
+ Q_OBJECT
+ Q_CLASSINFO("start_transport", tr("Play"))
+ Q_CLASSINFO("set_recordable_and_start_transport", tr("Record"));
+ Q_CLASSINFO("work_next_edge", tr("Workcursor: To next edge"))
+ Q_CLASSINFO("work_previous_edge", tr("Workcursor: To previous edge"))
+ Q_CLASSINFO("undo", tr("Undo"))
+ Q_CLASSINFO("redo", tr("Redo"))
+ Q_CLASSINFO("toggle_snap", tr("Snap: On/Off"))
+ Q_CLASSINFO("toggle_solo", tr("Solo: On/Off"))
+ Q_CLASSINFO("toggle_mute", tr("Mute: On/Off"))
+ Q_CLASSINFO("toggle_arm", tr("Arm: On/Off"))
+ Q_CLASSINFO("set_editing_mode", tr("Mode: Edit"))
+ Q_CLASSINFO("set_effects_mode", tr("Mode: Curve"))
+
+public:
+
+ Sheet(Project* project);
+ Sheet(Project* project, int numtracks);
+ Sheet(Project* project, const QDomNode node);
+ ~Sheet();
+
+ enum Mode {
+ EDIT = 1,
+ EFFECTS = 2
+ };
+
+ // Get functions
+ qreal get_hzoom() const {return m_hzoom;}
+ int get_rate();
+ int get_bitdepth();
+ int get_numtracks() const {return m_tracks.size();}
+ int get_track_index(qint64 id);
+ int get_mode() const {return m_mode;}
+ int is_transport_rolling() const {return m_transport;}
+ void get_scrollbar_xy(int& x, int& y) {x = m_sbx; y = m_sby;}
+
+ const TimeRef& get_work_location() const {return m_workLocation;}
+ nframes_t get_first_visible_frame() const;
+ const TimeRef& get_last_location() const;
+ const TimeRef& get_transport_location() const {return
m_transportLocation;}
+
+ const TimeRef& get_new_transport_location() const {return
m_newTransportLocation;}
+
+ QString get_title() const {return title;}
+ QString get_artists() const {return artists;}
+ QDomNode get_state(QDomDocument doc, bool istemplate=false);
+ QList<Track*> get_tracks() const;
+
+ DiskIO* get_diskio() const;
+ AudioClipManager* get_audioclip_manager() const;
+ AudioBus* get_master_out() const {return m_masterOut;}
+ AudioBus* get_render_bus() const {return m_renderBus;}
+ AudioBus* get_clip_render_bus() const {return m_clipRenderBus;}
+ SnapList* get_snap_list() const;
+ PluginChain* get_plugin_chain() const;
+ TimeLine* get_timeline() const {return m_timeline;}
+ Snappable* get_work_snap() {return workSnap;}
+ Track* get_track(qint64 id);
+
+ // Set functions
+ void set_artists(const QString& pArtistis);
+ void set_first_visible_frame(nframes_t pos);
+ void set_title(const QString& sTitle);
+ void set_work_at(const TimeRef& location);
+ void set_hzoom(qreal hzoom);
+ void set_snapping(bool snap);
+ void set_scrollbar_xy(int x, int y) {m_sbx = x; m_sby = y;}
+ int set_state( const QDomNode & node );
+ void set_recording(bool recording, bool realtime);
+
+
+ int process(nframes_t nframes);
+ // jackd only feature
+ int transport_control(transport_state_t state);
+ int process_export(nframes_t nframes);
+ int prepare_export(ExportSpecification* spec);
+ int render(ExportSpecification* spec);
+
+ void solo_track(Track* track);
+ void create(int tracksToCreate);
+ void move_clip(Track* from, Track* too, AudioClip* clip, TimeRef
location);
+ Command* add_track(Track* track, bool historable=true);
+ Command* remove_track(Track* track, bool historable=true);
+
+ bool any_track_armed();
+ bool realtime_path() const {return m_realtimepath;}
+ bool is_changed() const {return changed;}
+ bool is_snap_on() const {return m_isSnapOn;}
+ bool is_recording() const {return m_recording;}
+ bool is_smaller_then(APILinkedListNode* node) {Q_UNUSED(node); return
false;}
+
+ void disconnect_from_audiodevice();
+ void connect_to_audiodevice();
+ void schedule_for_deletion();
+ QString get_cdrdao_tracklist(ExportSpecification* spec, bool pregap =
false);
+
+ audio_sample_t* mixdown;
+ audio_sample_t* readbuffer;
+ audio_sample_t* gainbuffer;
+ DecodeBuffer* renderDecodeBuffer;
+
+#if defined (THREAD_CHECK)
+ unsigned long threadId;
+#endif
+
+private:
+ APILinkedList m_tracks;
+ QList<AudioClip*> m_recordingClips;
+ Project* m_project;
+ WriteSource* m_exportSource;
+ AudioBus* m_playBackBus;
+ Client* m_audiodeviceClient;
+ AudioBus* m_masterOut;
+ AudioBus* m_renderBus;
+ AudioBus* m_clipRenderBus;
+ DiskIO* m_diskio;
+ AudioClipManager* m_acmanager;
+ TimeLine* m_timeline;
+
+ // The following data could be read/written by multiple threads
+ // (gui, audio and m_diskio thread). Therefore they should have
+ // atomic behaviour, still not sure if volatile size_t declaration
+ // would suffice, or should we use t_atomic_int_set/get() to make
+ // it 100% portable and working on all platforms...?
+ volatile size_t m_transportFrame;
+ volatile size_t m_newTransportFramePos;
+ volatile size_t m_transport;
+ volatile size_t m_seeking;
+ volatile size_t m_startSeek;
+
+ TimeRef m_transportLocation;
+ TimeRef m_workLocation;
+ TimeRef m_newTransportLocation;
+
+
+ nframes_t firstVisibleFrame;
+ QString artists;
+ QString title;
+ int m_mode;
+ qreal m_hzoom;
+ int m_sbx;
+ int m_sby;
+ uint m_currentSampleRate;
+ bool m_rendering;
+ bool changed;
+ bool m_isSnapOn;
+ bool m_resumeTransport;
+ bool m_stopTransport;
+ bool m_realtimepath;
+ bool m_scheduledForDeletion;
+ bool m_recording;
+ bool m_prepareRecording;
+ bool m_readyToRecord;
+ SnapList* snaplist;
+ Snappable* workSnap;
+
+ void init();
+
+ int finish_audio_export();
+ void start_seek();
+ void start_transport_rolling(bool realtime);
+ void stop_transport_rolling();
+
+ void resize_buffer(bool updateArmStatus, nframes_t size);
+
+ Track* create_track();
+
+ friend class AudioClipManager;
+ friend class TimeLine;
+
+public slots :
+ void seek_finished();
+ void audiodevice_client_removed(Client* );
+ void audiodevice_started();
+ void audiodevice_params_changed();
+ void set_gain(float gain);
+ void set_transport_pos(TimeRef location);
+
+ float get_gain() const;
+
+ void set_temp_follow_state(bool state);
+
+ Command* start_transport();
+ Command* set_recordable();
+ Command* set_recordable_and_start_transport();
+ Command* work_next_edge();
+ Command* work_previous_edge();
+ Command* toggle_snap();
+ Command* toggle_solo();
+ Command* toggle_mute();
+ Command* toggle_arm();
+ Command* set_editing_mode();
+ Command* set_effects_mode();
+
+signals:
+ void trackRemoved(Track* );
+ void trackAdded(Track* );
+ void hzoomChanged();
+ void transferStarted();
+ void transferStopped();
+ void workingPosChanged();
+ void transportPosSet();
+ void firstVisibleFrameChanged();
+ void lastFramePositionChanged();
+ void seekStart();
+ void snapChanged();
+ void tempFollowChanged(bool state);
+ void propertyChanged();
+ void setCursorAtEdge();
+ void masterGainChanged();
+ void modeChanged();
+ void recordingStateChanged();
+ void prepareRecording();
+
+private slots:
+ void private_add_track(Track* track);
+ void private_remove_track(Track* track);
+ void handle_diskio_writebuffer_overrun();
+ void handle_diskio_readbuffer_underrun();
+ void prepare_recording();
+ void clip_finished_recording(AudioClip* clip);
+ void config_changed();
+};
+
+inline float Sheet::get_gain() const
+{
+ return m_fader->get_gain();
+}
+
+
+#endif
+
+
+
+
+//eof
Index: Song.cpp
===================================================================
RCS file: Song.cpp
diff -N Song.cpp
--- Song.cpp 7 Jan 2008 18:11:30 -0000 1.169
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,1414 +0,0 @@
-/*
-Copyright (C) 2005-2007 Remon Sijrier
-
-This file is part of Traverso
-
-Traverso is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-
-*/
-
-#include <QTextStream>
-#include <QString>
-#include <QFile>
-#include <QFileInfo>
-#include <QList>
-#include <QMap>
-#include <QRegExp>
-#include <QDebug>
-
-#include <commands.h>
-
-#include "AbstractAudioReader.h"
-#include <AudioDevice.h>
-#include <AudioBus.h>
-#include <Client.h>
-#include "ProjectManager.h"
-#include "ContextPointer.h"
-#include "Information.h"
-#include "Song.h"
-#include "Project.h"
-#include "Track.h"
-#include "Mixer.h"
-#include "AudioSource.h"
-#include "AudioClip.h"
-#include "Peak.h"
-#include "Export.h"
-#include "DiskIO.h"
-#include "WriteSource.h"
-#include "AudioClipManager.h"
-#include "Tsar.h"
-#include "SnapList.h"
-#include "Config.h"
-#include "Utils.h"
-#include "ContextItem.h"
-#include "TimeLine.h"
-#include "Marker.h"
-#include "InputEngine.h"
-
-#include <Plugin.h>
-#include <PluginChain.h>
-
-// Always put me below _all_ includes, this is needed
-// in case we run with memory leak detection enabled!
-#include "Debugger.h"
-
-
-Song::Song(Project* project)
- : ContextItem()
- , m_project(project)
-{
- PENTERCONS;
- title = tr("Untitled");
- m_id = create_id();
- artists = tr("No artists name set");
- m_hzoom = config().get_property("Song", "hzoomLevel", 14).toInt();
-
- init();
-}
-
-Song::Song(Project* project, int numtracks)
- : ContextItem()
- , m_project(project)
-{
- PENTERCONS;
- title = tr("Untitled");
- m_id = create_id();
- artists = tr("No artists name set");
- m_hzoom = config().get_property("Song", "hzoomLevel", 14).toInt();
-
- init();
-
- for (int i=1; i <= numtracks; i++) {
- Track* track = create_track();
- private_add_track(track);
- }
-}
-
-Song::Song(Project* project, const QDomNode node)
- : ContextItem(), m_project(project)
-{
- PENTERCONS;
- init();
- set_state( node );
-}
-
-Song::~Song()
-{
- PENTERDES;
-
- delete [] mixdown;
- delete [] gainbuffer;
-
- delete m_diskio;
- delete m_masterOut;
- delete m_renderBus;
- delete m_clipRenderBus;
- delete m_hs;
- delete m_audiodeviceClient;
- delete snaplist;
- delete workSnap;
-}
-
-void Song::init()
-{
- PENTER2;
-#if defined (THREAD_CHECK)
- threadId = QThread::currentThreadId ();
-#endif
-
- m_diskio = new DiskIO(this);
- m_currentSampleRate = audiodevice().get_sample_rate();
- m_diskio->output_rate_changed(m_currentSampleRate);
-
- connect(this, SIGNAL(seekStart()), m_diskio, SLOT(seek()),
Qt::QueuedConnection);
- connect(this, SIGNAL(prepareRecording()), this,
SLOT(prepare_recording()));
- connect(&audiodevice(), SIGNAL(clientRemoved(Client*)), this, SLOT
(audiodevice_client_removed(Client*)));
- connect(&audiodevice(), SIGNAL(started()), this,
SLOT(audiodevice_started()));
- connect(&audiodevice(), SIGNAL(driverParamsChanged()), this,
SLOT(audiodevice_params_changed()), Qt::DirectConnection);
- connect(m_diskio, SIGNAL(seekFinished()), this, SLOT(seek_finished()),
Qt::QueuedConnection);
- connect (m_diskio, SIGNAL(readSourceBufferUnderRun()), this,
SLOT(handle_diskio_readbuffer_underrun()));
- connect (m_diskio, SIGNAL(writeSourceBufferOverRun()), this,
SLOT(handle_diskio_writebuffer_overrun()));
- connect(&config(), SIGNAL(configChanged()), this,
SLOT(config_changed()));
- connect(this, SIGNAL(transferStarted()), m_diskio, SLOT(start_io()));
- connect(this, SIGNAL(transferStopped()), m_diskio, SLOT(stop_io()));
-
- mixdown = gainbuffer = 0;
- m_masterOut = new AudioBus("Master Out", 2);
- m_renderBus = new AudioBus("Render Bus", 2);
- m_clipRenderBus = new AudioBus("Clip Render Bus", 2);
-
- resize_buffer(false, audiodevice().get_buffer_size());
- m_hs = new QUndoStack(pm().get_undogroup());
- set_history_stack(m_hs);
- m_acmanager = new AudioClipManager(this);
-
- set_context_item( m_acmanager );
-
- m_playBackBus = audiodevice().get_playback_bus("Playback 1");
-
- m_transport = m_stopTransport = m_resumeTransport = m_readyToRecord =
false;
- snaplist = new SnapList(this);
- workSnap = new Snappable();
- workSnap->set_snap_list(snaplist);
-
- m_realtimepath = false;
- m_scheduledForDeletion = false;
- m_isSnapOn=true;
- changed = m_rendering = m_recording = m_prepareRecording = false;
- firstVisibleFrame=0;
- m_workLocation = TimeRef();
- m_seeking = m_startSeek = 0;
- // TODO seek to old position on project exit ?
- m_transportLocation = TimeRef();
- m_mode = EDIT;
- m_sbx = m_sby = 0;
-
- m_pluginChain = new PluginChain(this, this);
- m_fader = m_pluginChain->get_fader();
- m_fader->set_gain(0.5);
- m_timeline = new TimeLine(this);
-
- m_audiodeviceClient = new Client("song_" +
QByteArray::number(get_id()));
- m_audiodeviceClient->set_process_callback( MakeDelegate(this,
&Song::process) );
- m_audiodeviceClient->set_transport_control_callback( MakeDelegate(this,
&Song::transport_control) );
-}
-
-int Song::set_state( const QDomNode & node )
-{
- PENTER;
- QDomNode propertiesNode = node.firstChildElement("Properties");
- m_id = node.toElement().attribute("id", "0").toLongLong();
- if (m_id == 0) {
- m_id = create_id();
- }
-
- QDomElement e = propertiesNode.toElement();
-
- title = e.attribute( "title", "" );
- artists = e.attribute( "artists", "" );
- set_gain(e.attribute( "mastergain", "1.0").toFloat() );
- set_hzoom(e.attribute("hzoom", "" ).toInt());
- m_sbx = e.attribute("sbx", "0").toInt();
- m_sby = e.attribute("sby", "0").toInt();
- set_first_visible_frame(e.attribute( "firstVisibleFrame", "0"
).toUInt());
-
- bool ok;
- TimeRef location(e.attribute( "m_workLocation", "0").toLongLong(&ok));
- set_work_at(location);
- m_transportLocation = TimeRef(e.attribute( "transportlocation",
"0").toLongLong(&ok));
-
- // Start seeking to the 'old' transport pos
- set_transport_pos(m_transportLocation);
- set_snapping(e.attribute("snapping", "0").toInt());
- m_mode = e.attribute("mode", "0").toInt();
-
- m_timeline->set_state(node.firstChildElement("TimeLine"));
-
- QDomNode tracksNode = node.firstChildElement("Tracks");
- QDomNode trackNode = tracksNode.firstChild();
-
- while(!trackNode.isNull()) {
- Track* track = new Track(this, trackNode);
- private_add_track(track);
- track->set_state(trackNode);
-
- trackNode = trackNode.nextSibling();
- }
-
- QDomNode pluginChainNode = node.firstChildElement("PluginChain");
- m_pluginChain->set_state(pluginChainNode);
-
- return 1;
-}
-
-QDomNode Song::get_state(QDomDocument doc, bool istemplate)
-{
- QDomElement songNode = doc.createElement("Sheet");
-
- if (! istemplate) {
- songNode.setAttribute("id", m_id);
- }
-
- QDomElement properties = doc.createElement("Properties");
- properties.setAttribute("title", title);
- properties.setAttribute("artists", artists);
- properties.setAttribute("firstVisibleFrame", firstVisibleFrame);
- properties.setAttribute("m_workLocation",
m_workLocation.universal_frame());
- properties.setAttribute("transportlocation",
m_transportLocation.universal_frame());
- properties.setAttribute("hzoom", m_hzoom);
- properties.setAttribute("sbx", m_sbx);
- properties.setAttribute("sby", m_sby);
- properties.setAttribute("snapping", m_isSnapOn);
- properties.setAttribute("mode", m_mode);
- songNode.appendChild(properties);
-
- doc.appendChild(songNode);
-
- songNode.appendChild(m_timeline->get_state(doc));
-
- QDomNode tracksNode = doc.createElement("Tracks");
-
- apill_foreach(Track* track, Track, m_tracks) {
- tracksNode.appendChild(track->get_state(doc, istemplate));
- }
-
- songNode.appendChild(tracksNode);
-
- QDomNode pluginChainNode = doc.createElement("PluginChain");
- pluginChainNode.appendChild(m_pluginChain->get_state(doc));
- songNode.appendChild(pluginChainNode);
-
-
- return songNode;
-}
-
-void Song::connect_to_audiodevice( )
-{
- PENTER;
- audiodevice().add_client(m_audiodeviceClient);
-}
-
-void Song::disconnect_from_audiodevice()
-{
- PENTER;
- if (is_transport_rolling()) {
- m_transport = false;
- }
- audiodevice().remove_client(m_audiodeviceClient);
-}
-
-void Song::schedule_for_deletion()
-{
- m_scheduledForDeletion = true;
- pm().scheduled_for_deletion(this);
-}
-
-void Song::audiodevice_client_removed(Client* client )
-{
- PENTER;
- if (m_audiodeviceClient == client) {
- if (m_scheduledForDeletion) {
- pm().delete_song(this);
- }
- }
-}
-
-Command* Song::add_track(Track* track, bool historable)
-{
- apill_foreach(Track* existing, Track, m_tracks) {
- if (existing->is_solo()) {
- track->set_muted_by_solo( true );
- break;
- }
- }
-
- return new AddRemove(this, track, historable, this,
- "private_add_track(Track*)", "trackAdded(Track*)",
- "private_remove_track(Track*)", "trackRemoved(Track*)",
- tr("Add Track"));
-}
-
-
-Command* Song::remove_track(Track* track, bool historable)
-{
- return new AddRemove(this, track, historable, this,
- "private_remove_track(Track*)", "trackRemoved(Track*)",
- "private_add_track(Track*)", "trackAdded(Track*)",
- tr("Remove Track"));
-}
-
-bool Song::any_track_armed()
-{
- apill_foreach(Track* track, Track, m_tracks) {
- if (track->armed()) {
- return true;
- }
- }
- return false;
-}
-
-
-int Song::prepare_export(ExportSpecification* spec)
-{
- PENTER;
-
- if ( ! (spec->renderpass == ExportSpecification::CREATE_CDRDAO_TOC) ) {
- if (is_transport_rolling()) {
- spec->resumeTransport = true;
- // When transport is rolling, this equals stopping the
transport!
- // prepare_export() is called from another thread, so
use a queued connection
- // to call the function in the correct thread!
- if (!QMetaObject::invokeMethod(this, "start_transport",
Qt::QueuedConnection)) {
- printf("Invoking Song::start_transport()
failed\n");
- return -1;
- }
- int count = 0;
- uint msecs = (audiodevice().get_buffer_size() * 1000) /
audiodevice().get_sample_rate();
- // wait a number (max 10) of process() cycles to be
sure we really stopped transport
- while (m_transport) {
- spec->thread->sleep_for(msecs);
- count++;
- if (count > 10) {
- break;
- }
- }
- printf("Song::prepare_export: had to wait %d process
cycles before the transport was stopped\n", count);
- }
-
- m_rendering = true;
- }
-
- spec->startLocation = LONG_LONG_MAX;
- spec->endLocation = TimeRef();
-
- TimeRef endlocation, startlocation;
-
- apill_foreach(Track* track, Track, m_tracks) {
- track->get_render_range(startlocation, endlocation);
-
- if (track->is_solo()) {
- spec->endLocation = endlocation;
- spec->startLocation = startlocation;
- break;
- }
-
- if (endlocation > spec->endLocation) {
- spec->endLocation = endlocation;
- }
-
- if (startlocation < spec->startLocation) {
- spec->startLocation = startlocation;
- }
- }
-
- if (spec->isCdExport) {
- QList<Marker*> markers = m_timeline->get_markers();
-
- if (m_timeline->get_start_location(startlocation)) {
- PMESG2(" Start marker found at %s",
QS_C(timeref_to_ms(startlocation)));
- // round down to the start of the CD frame (75th of a
sec)
- startlocation =
cd_to_timeref(timeref_to_cd(startlocation));
- spec->startLocation = startlocation;
- } else {
- PMESG2(" No start marker found");
- }
-
- if (m_timeline->get_end_location(endlocation)) {
- PMESG2(" End marker found at %s",
QS_C(timeref_to_ms(endlocation)));
- spec->endLocation = endlocation;
- } else {
- PMESG2(" No end marker found");
- }
- }
-
- spec->totalTime = spec->endLocation - spec->startLocation;
-
-// PWARN("Render length is:
%s",timeref_to_ms_3(spec->totalTime).toAscii().data() );
-
- spec->pos = spec->startLocation;
- spec->progress = 0;
-
- spec->basename = "Sheet_" +
QString::number(m_project->get_song_index(m_id)) +"-" + title;
- spec->name = spec->basename;
-
- if (spec->startLocation == spec->endLocation) {
- info().warning(tr("No audio to export! (Is everything
muted?)"));
- return -1;
- }
- else if (spec->startLocation > spec->endLocation) {
- info().warning(tr("Export start frame starts beyond export end
frame!!"));
- return -1;
- }
-
- if (spec->channels == 0) {
- info().warning(tr("Export tries to render to 0 channels wav
file??"));
- return -1;
- }
-
- if (spec->renderpass == ExportSpecification::CREATE_CDRDAO_TOC) {
- return 1;
- }
-
- if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK) {
- m_exportSource = new WriteSource(spec);
- if (m_exportSource->prepare_export() == -1) {
- delete m_exportSource;
- return -1;
- }
- }
-
- m_transportLocation = spec->startLocation;
-
- resize_buffer(false, spec->blocksize);
-
- renderDecodeBuffer = new DecodeBuffer;
-// renderDecodeBuffer->use_custom_destination_buffer(true);
-
- return 1;
-}
-
-int Song::finish_audio_export()
-{
- m_exportSource->finish_export();
- delete m_exportSource;
- delete renderDecodeBuffer;
- resize_buffer(false, audiodevice().get_buffer_size());
- return 0;
-}
-
-int Song::render(ExportSpecification* spec)
-{
- int chn;
- uint32_t x;
- int ret = -1;
- int progress;
-
- nframes_t diff = (spec->endLocation -
spec->pos).to_frame(audiodevice().get_sample_rate());
- nframes_t nframes = spec->blocksize;
- nframes_t this_nframes = std::min(diff, nframes);
-
- if (!spec->running || spec->stop || this_nframes == 0) {
- process_export (nframes);
- /* PWARN("Finished Rendering for this song");
- PWARN("running is %d", spec->running);
- PWARN("stop is %d", spec->stop);
- PWARN("this_nframes is %d", this_nframes);*/
- if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK)
{
- return finish_audio_export();
- } else {
- return 0;
- }
- }
-
- /* do the usual stuff */
-
- process_export(nframes);
-
- /* and now export the results */
-
- nframes = this_nframes;
-
- memset (spec->dataF, 0, sizeof (spec->dataF[0]) * nframes *
spec->channels);
-
- /* foreach output channel ... */
-
- float* buf;
-
- for (chn = 0; chn < spec->channels; ++chn) {
- buf = m_masterOut->get_buffer(chn, nframes);
-
- if (!buf) {
- // Seem we are exporting at least to Stereo from an
AudioBus with only one channel...
- // Use the first channel..
- buf = m_masterOut->get_buffer(0, nframes);
- }
-
- for (x = 0; x < nframes; ++x) {
- spec->dataF[chn+(x*spec->channels)] = buf[x];
- }
- }
-
-
- int bufsize = spec->blocksize * spec->channels;
- if (spec->normalize) {
- if (spec->renderpass == ExportSpecification::CALC_NORM_FACTOR) {
- spec->peakvalue = Mixer::compute_peak(spec->dataF,
bufsize, spec->peakvalue);
- }
- }
-
- if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK) {
- if (spec->normalize) {
- Mixer::apply_gain_to_buffer(spec->dataF, bufsize,
spec->normvalue);
- }
- if (m_exportSource->process (nframes)) {
- goto out;
- }
- }
-
-
- spec->pos.add_frames(nframes, audiodevice().get_sample_rate());
-
- if (! spec->normalize ) {
- progress = int((double((spec->pos -
spec->startLocation).universal_frame()) / spec->totalTime.universal_frame()) *
100);
- } else {
- progress = (int) (double( 100 * (spec->pos -
spec->startLocation).universal_frame()) / (spec->totalTime.universal_frame() *
2));
- if (spec->renderpass == ExportSpecification::WRITE_TO_HARDDISK)
{
- progress += 50;
- }
- }
-
- if (progress > spec->progress) {
- spec->progress = progress;
- m_project->set_song_export_progress(progress);
- }
-
-
- /* and we're good to go */
-
- ret = 1;
-
-out:
- if (!ret) {
- spec->running = false;
- spec->status = ret;
- m_rendering = false;
- }
-
- return ret;
-}
-
-
-SnapList* Song::get_snap_list() const
-{
- return snaplist;
-}
-
-
-void Song::set_artists(const QString& pArtists)
-{
- artists = pArtists;
-}
-
-void Song::set_gain(float gain)
-{
- if (gain < 0.0)
- gain = 0.0;
- if (gain > 2.0)
- gain = 2.0;
-
- m_fader->set_gain(gain);
-
- emit masterGainChanged();
-}
-
-void Song::set_title(const QString& sTitle)
-{
- title=sTitle;
- emit propertyChanged();
-}
-
-void Song::set_first_visible_frame(nframes_t pos)
-{
- PENTER;
- firstVisibleFrame = pos;
- emit firstVisibleFrameChanged();
-}
-
-void Song::set_work_at(const TimeRef& location)
-{
- m_workLocation = location;
- snaplist->mark_dirty(workSnap);
- emit workingPosChanged();
-}
-
-Command* Song::toggle_snap()
-{
- set_snapping( ! m_isSnapOn );
- return 0;
-}
-
-
-void Song::set_snapping(bool snapping)
-{
- m_isSnapOn = snapping;
- emit snapChanged();
-}
-
-/******************************** SLOTS *****************************/
-
-Track* Song::create_track()
-{
- int height = Track::INITIAL_HEIGHT;
-
- Track* track = new Track(this, "Unnamed", height);
-
- return track;
-}
-
-void Song::solo_track(Track* t)
-{
- bool wasSolo = t->is_solo();
-
- t->set_muted_by_solo(!wasSolo);
- t->set_solo(!wasSolo);
-
- bool hasSolo = false;
- apill_foreach(Track* track, Track, m_tracks) {
- track->set_muted_by_solo(!track->is_solo());
- if (track->is_solo()) hasSolo = true;
- }
-
- if (!hasSolo) {
- apill_foreach(Track* track, Track, m_tracks) {
- track->set_muted_by_solo(false);
- }
- }
-}
-
-Command* Song::toggle_solo()
-{
- bool hasSolo = false;
- apill_foreach(Track* track, Track, m_tracks) {
- if (track->is_solo()) hasSolo = true;
- }
-
- apill_foreach(Track* track, Track, m_tracks) {
- track->set_solo(!hasSolo);
- track->set_muted_by_solo(false);
- }
-
- return (Command*) 0;
-}
-
-Command *Song::toggle_mute()
-{
- bool hasMute = false;
- apill_foreach(Track* track, Track, m_tracks) {
- if (track->is_muted()) hasMute = true;
- }
-
- apill_foreach(Track* track, Track, m_tracks) {
- track->set_muted(!hasMute);
- }
-
- return (Command*) 0;
-}
-
-Command *Song::toggle_arm()
-{
- bool hasArmed = false;
- apill_foreach(Track* track, Track, m_tracks) {
- if (track->armed()) hasArmed = true;
- }
-
- apill_foreach(Track* track, Track, m_tracks) {
- if (hasArmed) {
- track->disarm();
- } else {
- track->arm();
- }
- }
-
- return (Command*) 0;
-}
-
-Command* Song::work_next_edge()
-{
-/* nframes_t w = m_acmanager->get_last_frame();
-
- foreach(Track* track, m_tracks) {
- AudioClip* c=track->get_clip_after(m_workLocation);
-
- if ((c) && (c->get_track_start_location() < w &&
c->get_track_start_location() > m_workLocation))
- w = c->get_track_start_location();
- }
-
- set_work_at(w);
-
- emit setCursorAtEdge();
-*/
- return (Command*) 0;
-}
-
-Command* Song::work_previous_edge()
-{
-/* TimeRef w(0);
- foreach(Track* track, m_tracks) {
- AudioClip* c = track->get_clip_before(m_workLocation);
- if ((c) && (c->get_track_end_location() >= w &&
c->get_track_end_location() < m_workLocation) )
- w=c->get_track_end_location();
- }
-
- set_work_at(w);
-
- emit setCursorAtEdge();
-*/
- return (Command*) 0;
-}
-
-void Song::set_hzoom( int hzoom )
-{
- if (hzoom > (Peak::ZOOM_LEVELS - 1))
- hzoom = (Peak::ZOOM_LEVELS - 1);
- if (hzoom < 0)
- hzoom = 0;
- m_hzoom = hzoom;
- emit hzoomChanged();
-}
-
-//
-// Function called in RealTime AudioThread processing path
-//
-int Song::process( nframes_t nframes )
-{
- if (m_startSeek) {
- start_seek();
- return 0;
- }
-
- // If no need for playback/record, return.
- if (!is_transport_rolling()) {
- return 0;
- }
-
- if (m_stopTransport) {
- RT_THREAD_EMIT(this, 0, transferStopped());
- m_transport = false;
- m_realtimepath = false;
- m_stopTransport = false;
-
- return 0;
- }
-
- // zero the m_masterOut buffers
- m_masterOut->silence_buffers(nframes);
-
- int processResult = 0;
-
-
- // Process all Tracks.
- apill_foreach(Track* track, Track, m_tracks) {
- processResult |= track->process(nframes);
- }
-
- // update the transport location
- m_transportLocation.add_frames(nframes,
audiodevice().get_sample_rate());
-
- if (!processResult) {
- return 0;
- }
-
- // Mix the result into the AudioDevice "physical" buffers
- if (m_playBackBus) {
- Mixer::mix_buffers_with_gain(m_playBackBus->get_buffer(0,
nframes), m_masterOut->get_buffer(0, nframes), nframes, get_gain());
- Mixer::mix_buffers_with_gain(m_playBackBus->get_buffer(1,
nframes), m_masterOut->get_buffer(1, nframes), nframes, get_gain());
-
- m_pluginChain->process_post_fader(m_masterOut, nframes);
- }
-
-
- return 1;
-}
-
-int Song::process_export( nframes_t nframes )
-{
- // Get the masterout buffers, and fill with zero's
- m_masterOut->silence_buffers(nframes);
- memset (mixdown, 0, sizeof (audio_sample_t) * nframes);
-
- // Process all Tracks.
- apill_foreach(Track* track, Track, m_tracks) {
- track->process(nframes);
- }
-
- Mixer::apply_gain_to_buffer(m_masterOut->get_buffer(0, nframes),
nframes, get_gain());
- Mixer::apply_gain_to_buffer(m_masterOut->get_buffer(1, nframes),
nframes, get_gain());
-
- // update the m_transportFrame
-// m_transportFrame += nframes;
- m_transportLocation.add_frames(nframes,
audiodevice().get_sample_rate());
-
- return 1;
-}
-
-QString Song::get_cdrdao_tracklist(ExportSpecification* spec, bool pregap)
-{
- QString output;
-
- QList<Marker*> mlist = m_timeline->get_markers();
- QList<Marker*> tempmarkers;
-
- //Â Here we make the marker-stuff idiot-proof ;-). Traverso doesn't
insist on having any
- //Â marker at all, so we need to handle cases like:
- // - no markers at all
- // - one marker (doesn't make sense)
- // - enough markers, but no end marker
-
- Marker* temp;
-
- if (mlist.size() < 2) {
- switch (mlist.size()) {
- case 0:
- // no markers present. We add one at the
beginning and one at the
- // end of the render area.
- temp = new Marker(m_timeline,
spec->startLocation, Marker::CDTRACK);
- tempmarkers.append(temp);
- mlist.prepend(temp);
- temp = new Marker(m_timeline,
spec->endLocation, Marker::ENDMARKER);
- tempmarkers.append(temp);
- mlist.append(temp);
- break;
- case 1:
- // one marker is present. We add two more, one
at the beginning
- // and one at the end of the render area. If
the present marker
- // happened to be at either the start or the
end position,
- // it will now be overwritten, so we will never
end up with
- // two markers at the same position.
-
- //Â deactivate the next if-condition (only the
first one) if you want the
- // stuff before the first marker to go into the
pre-gap
- if (mlist.at(0)->get_when() !=
(spec->startLocation)) {
- temp = new Marker(m_timeline,
spec->startLocation, Marker::CDTRACK);
- tempmarkers.append(temp);
- mlist.prepend(temp);
- }
- if (mlist.at(0)->get_when() !=
(spec->startLocation)) {
- temp = new Marker(m_timeline,
spec->endLocation, Marker::ENDMARKER);
- tempmarkers.append(temp);
- mlist.append(temp);
- }
- break;
- }
- } else {
- // would be ok, but let's check if there is an end marker
present. If not,
- // add one to spec->end_frame
- if (!m_timeline->has_end_marker()) {
- temp = new Marker(m_timeline, spec->endLocation,
Marker::ENDMARKER);
- tempmarkers.append(temp);
- mlist.append(temp);
- }
- }
-
- TimeRef start;
-
- for(int i = 0; i < mlist.size()-1; ++i) {
-
- Marker* startmarker = mlist.at(i);
- Marker* endmarker = mlist.at(i+1);
-
- output += "TRACK AUDIO\n";
-
- if (startmarker->get_copyprotect()) {
- output += " NO COPY\n";
- } else {
- output += " COPY\n";
- }
-
- if (startmarker->get_preemphasis()) {
- output += " PRE_EMPHASIS\n";
- }
-
- output += " CD_TEXT {\n LANGUAGE 0 {\n";
- output += " TITLE \"" + startmarker->get_description() +
"\"\n";
- output += " PERFORMER \"" + startmarker->get_performer() +
"\"\n";
- output += " ISRC \"" + startmarker->get_isrc() + "\"\n";
- output += " ARRANGER \"" + startmarker->get_arranger() +
"\"\n";
- output += " SONGWRITER \"" + startmarker->get_songwriter()
+ "\"\n";
- output += " MESSAGE \"" + startmarker->get_message() +
"\"\n }\n }\n";
-
- //Â add some stuff only required for the first track (e.g.
pre-gap)
- if ((i == 0) && pregap) {
- //if (start == 0) {
- // standard pregap, because we have a track
marker at the beginning
- output += " PREGAP 00:02:00\n";
- //} else {
- // // no track marker at the beginning, thus use
the part from 0 to the first
- // //Â track marker for the pregap
- // output += " START " + frame_to_cd(start,
m_project->get_rate()) + "\n";
- // start = 0;
- //}
- }
-
- TimeRef length =
cd_to_timeref(timeref_to_cd(endmarker->get_when())) -
cd_to_timeref(timeref_to_cd(startmarker->get_when()));
-
- QString s_start = timeref_to_cd(start);
- QString s_length = timeref_to_cd(length);
-
- output += " FILE \"" + spec->name + "." +
spec->extraFormat["filetype"] + "\" " + s_start + " " + s_length + "\n\n";
- start += length;
-
- // check if the second marker is of type "Endmarker"
- if (endmarker->get_type() == Marker::ENDMARKER) {
- break;
- }
- }
-
- //Â delete all temporary markers
- foreach(Marker* marker, tempmarkers) {
- delete marker;
- }
-
- return output;
-}
-
-void Song::resize_buffer(bool updateArmStatus, nframes_t size)
-{
- if (mixdown)
- delete [] mixdown;
- if (gainbuffer)
- delete [] gainbuffer;
- mixdown = new audio_sample_t[size];
- gainbuffer = new audio_sample_t[size];
- m_masterOut->set_buffer_size(size);
- m_renderBus->set_buffer_size(size);
- m_clipRenderBus->set_buffer_size(size);
-
- if (updateArmStatus) {
- apill_foreach(Track* track, Track, m_tracks) {
- AudioBus* bus =
audiodevice().get_capture_bus(track->get_bus_in().toAscii());
- if (bus && track->armed()) {
- bus->set_monitor_peaks(true);
- }
- }
- }
-}
-
-void Song::audiodevice_params_changed()
-{
- resize_buffer(true, audiodevice().get_buffer_size());
-
- // The samplerate possibly has been changed, this initiates
- // a seek in DiskIO, which clears the buffers and refills them
- // with the correct resampled audio data!
- // We need to seek to a different position then the current one,
- // else the seek won't happen at all :)
- if (m_currentSampleRate != audiodevice().get_sample_rate()) {
- m_currentSampleRate = audiodevice().get_sample_rate();
-
- m_diskio->output_rate_changed(m_currentSampleRate);
-
- TimeRef location = m_transportLocation;
- location.add_frames(1, audiodevice().get_sample_rate());
-
- set_transport_pos(location);
- }
-}
-
-int Song::get_bitdepth( )
-{
- return m_project->get_bitdepth();
-}
-
-int Song::get_rate( )
-{
- return m_project->get_rate();
-}
-
-nframes_t Song::get_first_visible_frame( ) const
-{
- return firstVisibleFrame;
-}
-
-DiskIO * Song::get_diskio( ) const
-{
- return m_diskio;
-}
-
-AudioClipManager * Song::get_audioclip_manager( ) const
-{
- return m_acmanager;
-}
-
-PluginChain* Song::get_plugin_chain() const
-{
- return m_pluginChain;
-}
-
-int Song::get_track_index(qint64 id)
-{
- int i=0;
- apill_foreach(Track* track, Track, m_tracks) {
- if (track->get_id() == id) {
- return i + 1;
- }
- ++i;
- }
- return 0;
-}
-
-void Song::handle_diskio_readbuffer_underrun( )
-{
- if (is_transport_rolling()) {
- printf("Song:: DiskIO ReadBuffer UnderRun signal received!\n");
- info().critical(tr("Hard Disk overload detected!"));
- info().critical(tr("Failed to fill ReadBuffer in time"));
- }
-}
-
-void Song::handle_diskio_writebuffer_overrun( )
-{
- if (is_transport_rolling()) {
- printf("Song:: DiskIO WriteBuffer OverRun signal received!\n");
- info().critical(tr("Hard Disk overload detected!"));
- info().critical(tr("Failed to empty WriteBuffer in time"));
- }
-}
-
-void Song::audiodevice_started( )
-{
- m_playBackBus = audiodevice().get_playback_bus("Playback 1");
-}
-
-const TimeRef& Song::get_last_location() const
-{
- TimeRef lastAudio = m_acmanager->get_last_location();
-
- if (m_timeline->get_markers().size()) {
- TimeRef lastMarker =
m_timeline->get_markers().last()->get_when();
- return (lastAudio > lastMarker) ? lastAudio : lastMarker;
- }
-
- return lastAudio;
-}
-
-void Song::private_add_track(Track* track)
-{
- m_tracks.append(track);
-}
-
-void Song::private_remove_track(Track* track)
-{
- m_tracks.remove(track);
-}
-
-Track* Song::get_track(qint64 id)
-{
- apill_foreach(Track* track, Track, m_tracks) {
- if (track->get_id() == id) {
- return track;
- }
- }
- return 0;
-}
-
-void Song::move_clip(Track * from, Track * too, AudioClip * clip, TimeRef
location)
-{
- if (from == too) {
- clip->set_track_start_location(location);
- return;
- }
-
- Command::process_command(from->remove_clip(clip, false, true));
- Command::process_command(too->add_clip(clip, false, true));
-
- clip->set_track_start_location(location);
-}
-
-Command* Song::set_editing_mode( )
-{
- m_mode = EDIT;
- emit modeChanged();
- return 0;
-}
-
-Command* Song::set_effects_mode( )
-{
- m_mode = EFFECTS;
- emit modeChanged();
- return 0;
-}
-
-void Song::set_temp_follow_state(bool state)
-{
- emit tempFollowChanged(state);
-}
-
-// Function is only to be called from GUI thread.
-Command * Song::set_recordable()
-{
-#if defined (THREAD_CHECK)
- Q_ASSERT(QThread::currentThreadId() == threadId);
-#endif
-
- // Do nothing if transport is rolling!
- if (is_transport_rolling()) {
- return 0;
- }
-
- // Transport is not rolling, it's save now to switch
- // recording state to on /off
- if (is_recording()) {
- set_recording(false, false);
- } else {
- if (!any_track_armed()) {
- info().critical(tr("No Tracks armed for recording!"));
- return 0;
- }
-
- set_recording(true, false);
- }
-
- return 0;
-}
-
-// Function is only to be called from GUI thread.
-Command* Song::set_recordable_and_start_transport()
-{
- if (!is_recording()) {
- set_recordable();
- }
-
- start_transport();
-
- return 0;
-}
-
-// Function is only to be called from GUI thread.
-Command* Song::start_transport()
-{
-#if defined (THREAD_CHECK)
- Q_ASSERT(QThread::currentThreadId() == threadId);
-#endif
- // Delegate the transport start (or if we are rolling stop)
- // request to the audiodevice. Depending on the driver in use
- // this call will return directly to us (by a call to
transport_control),
- // or handled by the driver
- if (is_transport_rolling()) {
- audiodevice().transport_stop(m_audiodeviceClient);
- } else {
- audiodevice().transport_start(m_audiodeviceClient);
- }
-
- return ie().succes();
-}
-
-// Function can be called either from the GUI or RT thread.
-// So ALL functions called here need to be RT thread save!!
-int Song::transport_control(transport_state_t state)
-{
- if (m_scheduledForDeletion) {
- return true;
- }
-
- switch(state.tranport) {
- case TransportStopped:
- if (is_transport_rolling()) {
- stop_transport_rolling();
- if (is_recording()) {
- set_recording(false, state.realtime);
- }
- }
- return true;
-
- case TransportStarting:
- if (state.location != m_transportLocation) {
- if ( ! m_seeking ) {
- m_newTransportLocation = state.location;
- m_startSeek = 1;
- m_seeking = 1;
-
- PMESG("tranport starting: initiating seek");
- return false;
- }
- }
- if (! m_seeking) {
- if (is_recording()) {
- if (!m_prepareRecording) {
- m_prepareRecording = true;
- // prepare_recording() is only to be
called from the GUI thread
- // so we delegate the
prepare_recording() function call via a
- // RT thread save signal!
- Q_ASSERT(state.realtime);
- RT_THREAD_EMIT(this, 0,
prepareRecording());
- PMESG("transport starting: initiating
prepare for record");
- return false;
- }
- if (!m_readyToRecord) {
- PMESG("transport starting: still
preparing for record");
- return false;
- }
- }
-
-
- PMESG("tranport starting: seek finished");
- return true;
- } else {
- PMESG("tranport starting: still seeking");
- return false;
- }
-
- case TransportRolling:
- if (!is_transport_rolling()) {
- // When the transport rolling request came from a non
slave
- // driver, we currently can assume it's comming from
the GUI
- // thread, and TransportStarting never was called
before!
- // So in case we are recording we have to prepare for
recording now!
- if ( ! state.isSlave && is_recording() ) {
- Q_ASSERT(!state.realtime);
- prepare_recording();
- }
- start_transport_rolling(state.realtime);
- }
- return true;
- }
-
- return false;
-}
-
-// RT thread save function
-void Song::start_transport_rolling(bool realtime)
-{
- m_realtimepath = true;
- m_transport = 1;
-
- if (realtime) {
- RT_THREAD_EMIT(this, 0, transferStarted());
- } else {
- emit transferStarted();
- }
-
- PMESG("tranport rolling");
-}
-
-// RT thread save function
-void Song::stop_transport_rolling()
-{
- m_stopTransport = 1;
- PMESG("tranport stopped");
-}
-
-// RT thread save function
-void Song::set_recording(bool recording, bool realtime)
-{
- m_recording = recording;
-
- if (!m_recording) {
- m_readyToRecord = false;
- m_prepareRecording = false;
- }
-
- if (realtime) {
- RT_THREAD_EMIT(this, 0, recordingStateChanged());
- } else {
- emit recordingStateChanged();
- }
-}
-
-
-// NON RT thread save function, should only be called from GUI thread!!
-void Song::prepare_recording()
-{
-#if defined (THREAD_CHECK)
- Q_ASSERT(QThread::currentThreadId() == threadId);
-#endif
-
- if (m_recording && any_track_armed()) {
- CommandGroup* group = new CommandGroup(this, "");
- int clipcount = 0;
- apill_foreach(Track* track, Track, m_tracks) {
- if (track->armed()) {
- AudioClip* clip = track->init_recording();
- if (clip) {
- // For autosave purposes, we connect
the recordingfinished
- // signal to the
clip_finished_recording() slot, and add this
- // clip to our recording clip list.
- // At the time the cliplist is empty,
we're sure the recording
- // session is finished, at which time
an autosave makes sense.
- connect(clip,
SIGNAL(recordingFinished(AudioClip*)),
- this,
SLOT(clip_finished_recording(AudioClip*)));
- m_recordingClips.append(clip);
-
- group->add_command(new
AddRemoveClip(clip, AddRemoveClip::ADD));
- clipcount++;
- }
- }
- }
- group->setText(tr("Recording to %n Clip(s)", "", clipcount));
- Command::process_command(group);
- }
-
- m_readyToRecord = true;
-}
-
-void Song::clip_finished_recording(AudioClip * clip)
-{
- if (!m_recordingClips.removeAll(clip)) {
- PERROR("clip %s was not in recording clip list, cannot remove
it!", QS_C(clip->get_name()));
- }
-
- if (m_recordingClips.isEmpty()) {
- // seems we finished recording completely now
- // all clips have set their resulting ReadSource
- // length and whatsoever, let's do an autosave:
- m_project->save(true);
- }
-}
-
-
-void Song::set_transport_pos(TimeRef location)
-{
-#if defined (THREAD_CHECK)
- Q_ASSERT(QThread::currentThreadId() == threadId);
-#endif
- audiodevice().transport_seek_to(m_audiodeviceClient, location);
-}
-
-
-//
-// Function is ALWAYS called in RealTime AudioThread processing path
-// Be EXTREMELY carefull to not call functions() that have blocking behavior!!
-//
-void Song::start_seek()
-{
-#if defined (THREAD_CHECK)
- Q_ASSERT(threadId != QThread::currentThreadId ());
-#endif
-
- if (is_transport_rolling()) {
- m_realtimepath = false;
- m_resumeTransport = true;
- }
-
- m_transport = false;
- m_startSeek = 0;
-
- // only sets a boolean flag, save to call.
- m_diskio->prepare_for_seek();
-
- // 'Tell' the diskio it should start a seek action.
- RT_THREAD_EMIT(this, NULL, seekStart());
-}
-
-void Song::seek_finished()
-{
-#if defined (THREAD_CHECK)
- Q_ASSERT_X(threadId == QThread::currentThreadId (),
"Song::seek_finished", "Called from other Thread!");
-#endif
- PMESG2("Song :: entering seek_finished");
- m_transportLocation = m_newTransportLocation;
- m_seeking = 0;
-
- if (m_resumeTransport) {
- start_transport_rolling(false);
- m_resumeTransport = false;
- }
-
- emit transportPosSet();
- PMESG2("Song :: leaving seek_finished");
-}
-
-void Song::config_changed()
-{
- PENTER;
-
- int quality = config().get_property("Conversion",
"RTResamplingConverterType", 2).toInt();
- if (m_diskio->get_resample_quality() != quality) {
- TimeRef location = m_transportLocation;
- location.add_frames(1, audiodevice().get_sample_rate());
-
- set_transport_pos(location);
- }
-}
-
-
-
-QList< Track * > Song::get_tracks() const
-{
- QList<Track*> list;
- apill_foreach(Track* track, Track, m_tracks) {
- list.append(track);
- }
- return list;
-}
Index: Song.h
===================================================================
RCS file: Song.h
diff -N Song.h
--- Song.h 2 Jan 2008 19:23:59 -0000 1.79
+++ /dev/null 1 Jan 1970 00:00:00 -0000
@@ -1,288 +0,0 @@
-/*
-Copyright (C) 2005-2007 Remon Sijrier
-
-This file is part of Traverso
-
-Traverso is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-
-*/
-
-#ifndef SONG_H
-#define SONG_H
-
-#include "ContextItem.h"
-#include "AudioProcessingItem.h"
-#include <QDomNode>
-#include "defines.h"
-#include "APILinkedList.h"
-#include "GainEnvelope.h"
-
-class Project;
-class Track;
-class AudioSource;
-class WriteSource;
-class Track;
-class AudioClip;
-class DiskIO;
-class AudioClipManager;
-class Client;
-class AudioBus;
-class PluginChain;
-class SnapList;
-class Plugin;
-class TimeLine;
-class Snappable;
-class DecodeBuffer;
-
-struct ExportSpecification;
-
-class Song : public ContextItem, public AudioProcessingItem
-{
- Q_OBJECT
- Q_CLASSINFO("start_transport", tr("Play"))
- Q_CLASSINFO("set_recordable_and_start_transport", tr("Record"));
- Q_CLASSINFO("work_next_edge", tr("Workcursor: To next edge"))
- Q_CLASSINFO("work_previous_edge", tr("Workcursor: To previous edge"))
- Q_CLASSINFO("undo", tr("Undo"))
- Q_CLASSINFO("redo", tr("Redo"))
- Q_CLASSINFO("toggle_snap", tr("Snap: On/Off"))
- Q_CLASSINFO("toggle_solo", tr("Solo: On/Off"))
- Q_CLASSINFO("toggle_mute", tr("Mute: On/Off"))
- Q_CLASSINFO("toggle_arm", tr("Arm: On/Off"))
- Q_CLASSINFO("set_editing_mode", tr("Mode: Edit"))
- Q_CLASSINFO("set_effects_mode", tr("Mode: Curve"))
-
-public:
-
- Song(Project* project);
- Song(Project* project, int numtracks);
- Song(Project* project, const QDomNode node);
- ~Song();
-
- enum Mode {
- EDIT = 1,
- EFFECTS = 2
- };
-
- // Get functions
- int get_hzoom() const { return m_hzoom;}
- int get_rate();
- int get_bitdepth();
- int get_numtracks() const {return m_tracks.size();}
- int get_track_index(qint64 id);
- int get_mode() const {return m_mode;}
- int is_transport_rolling() const {return m_transport;}
- void get_scrollbar_xy(int& x, int& y) {x = m_sbx; y = m_sby;}
-
- const TimeRef& get_work_location() const {return m_workLocation;}
- nframes_t get_first_visible_frame() const;
- const TimeRef& get_last_location() const;
- const TimeRef& get_transport_location() const {return
m_transportLocation;}
-
- const TimeRef& get_new_transport_location() const {return
m_newTransportLocation;}
-
- QString get_title() const {return title;}
- QString get_artists() const {return artists;}
- QDomNode get_state(QDomDocument doc, bool istemplate=false);
- QList<Track*> get_tracks() const;
-
- DiskIO* get_diskio() const;
- AudioClipManager* get_audioclip_manager() const;
- AudioBus* get_master_out() const {return m_masterOut;}
- AudioBus* get_render_bus() const {return m_renderBus;}
- AudioBus* get_clip_render_bus() const {return m_clipRenderBus;}
- SnapList* get_snap_list() const;
- PluginChain* get_plugin_chain() const;
- TimeLine* get_timeline() const {return m_timeline;}
- Snappable* get_work_snap() {return workSnap;}
- Track* get_track(qint64 id);
-
- // Set functions
- void set_artists(const QString& pArtistis);
- void set_first_visible_frame(nframes_t pos);
- void set_title(const QString& sTitle);
- void set_work_at(const TimeRef& location);
- void set_hzoom(int hzoom);
- void set_snapping(bool snap);
- void set_scrollbar_xy(int x, int y) {m_sbx = x; m_sby = y;}
- int set_state( const QDomNode & node );
- void set_recording(bool recording, bool realtime);
-
-
- int process(nframes_t nframes);
- // jackd only feature
- int transport_control(transport_state_t state);
- int process_export(nframes_t nframes);
- int prepare_export(ExportSpecification* spec);
- int render(ExportSpecification* spec);
-
- void solo_track(Track* track);
- void create(int tracksToCreate);
- void move_clip(Track* from, Track* too, AudioClip* clip, TimeRef
location);
- Command* add_track(Track* track, bool historable=true);
- Command* remove_track(Track* track, bool historable=true);
-
- bool any_track_armed();
- bool realtime_path() const {return m_realtimepath;}
- bool is_changed() const {return changed;}
- bool is_snap_on() const {return m_isSnapOn;}
- bool is_recording() const {return m_recording;}
- bool is_smaller_then(APILinkedListNode* node) {Q_UNUSED(node); return
false;}
-
- void disconnect_from_audiodevice();
- void connect_to_audiodevice();
- void schedule_for_deletion();
- QString get_cdrdao_tracklist(ExportSpecification* spec, bool pregap =
false);
-
- audio_sample_t* mixdown;
- audio_sample_t* readbuffer;
- audio_sample_t* gainbuffer;
- DecodeBuffer* renderDecodeBuffer;
-
-#if defined (THREAD_CHECK)
- unsigned long threadId;
-#endif
-
-private:
- APILinkedList m_tracks;
- QList<AudioClip*> m_recordingClips;
- Project* m_project;
- WriteSource* m_exportSource;
- AudioBus* m_playBackBus;
- Client* m_audiodeviceClient;
- AudioBus* m_masterOut;
- AudioBus* m_renderBus;
- AudioBus* m_clipRenderBus;
- DiskIO* m_diskio;
- AudioClipManager* m_acmanager;
- TimeLine* m_timeline;
-
- // The following data could be read/written by multiple threads
- // (gui, audio and m_diskio thread). Therefore they should have
- // atomic behaviour, still not sure if volatile size_t declaration
- // would suffice, or should we use t_atomic_int_set/get() to make
- // it 100% portable and working on all platforms...?
- volatile size_t m_transportFrame;
- volatile size_t m_newTransportFramePos;
- volatile size_t m_transport;
- volatile size_t m_seeking;
- volatile size_t m_startSeek;
-
- TimeRef m_transportLocation;
- TimeRef m_workLocation;
- TimeRef m_newTransportLocation;
-
-
- nframes_t firstVisibleFrame;
- QString artists;
- QString title;
- int m_mode;
- int m_hzoom;
- int m_sbx;
- int m_sby;
- uint m_currentSampleRate;
- bool m_rendering;
- bool changed;
- bool m_isSnapOn;
- bool m_resumeTransport;
- bool m_stopTransport;
- bool m_realtimepath;
- bool m_scheduledForDeletion;
- bool m_recording;
- bool m_prepareRecording;
- bool m_readyToRecord;
- SnapList* snaplist;
- Snappable* workSnap;
-
- void init();
-
- int finish_audio_export();
- void start_seek();
- void start_transport_rolling(bool realtime);
- void stop_transport_rolling();
-
- void resize_buffer(bool updateArmStatus, nframes_t size);
-
- Track* create_track();
-
- friend class AudioClipManager;
- friend class TimeLine;
-
-public slots :
- void seek_finished();
- void audiodevice_client_removed(Client* );
- void audiodevice_started();
- void audiodevice_params_changed();
- void set_gain(float gain);
- void set_transport_pos(TimeRef location);
-
- float get_gain() const;
-
- void set_temp_follow_state(bool state);
-
- Command* start_transport();
- Command* set_recordable();
- Command* set_recordable_and_start_transport();
- Command* work_next_edge();
- Command* work_previous_edge();
- Command* toggle_snap();
- Command* toggle_solo();
- Command* toggle_mute();
- Command* toggle_arm();
- Command* set_editing_mode();
- Command* set_effects_mode();
-
-signals:
- void trackRemoved(Track* );
- void trackAdded(Track* );
- void hzoomChanged();
- void transferStarted();
- void transferStopped();
- void workingPosChanged();
- void transportPosSet();
- void firstVisibleFrameChanged();
- void lastFramePositionChanged();
- void seekStart();
- void snapChanged();
- void tempFollowChanged(bool state);
- void propertyChanged();
- void setCursorAtEdge();
- void masterGainChanged();
- void modeChanged();
- void recordingStateChanged();
- void prepareRecording();
-
-private slots:
- void private_add_track(Track* track);
- void private_remove_track(Track* track);
- void handle_diskio_writebuffer_overrun();
- void handle_diskio_readbuffer_underrun();
- void prepare_recording();
- void clip_finished_recording(AudioClip* clip);
- void config_changed();
-};
-
-inline float Song::get_gain() const
-{
- return m_fader->get_gain();
-}
-
-
-#endif
-
-
-
-
-//eof
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Traverso-commit] traverso/src/core Sheet.cpp Sheet.h Song.cpp So...,
Remon Sijrier <=