From fb710b3a283015f5aabca6ed368857549c4f06b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Sat, 18 Nov 2017 00:52:25 +0100 Subject: [PATCH] qtractor: add latest MIDI import changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Andreas Müller --- ...inListDocument-to-save-load-plugin-l.patch | 2 +- ...ptions-to-create-ready-to-play-sess.patch} | 349 ++- ...ackCommand-extend-to-accept-qtractor.patch | 163 -- ...timebase-reset-on-main-time-scale-up.patch | 2203 +++++++++++++++++ .../qtractor/qtractor_0.8.4.bb | 10 +- 5 files changed, 2435 insertions(+), 292 deletions(-) rename recipes-misc/recipes-multimedia/qtractor/files/{0007-Add-MIDI-import-options-to-create-ready-to-play-sess.patch => 0006-Add-MIDI-import-options-to-create-ready-to-play-sess.patch} (88%) delete mode 100644 recipes-misc/recipes-multimedia/qtractor/files/0006-qtractorImportTrackCommand-extend-to-accept-qtractor.patch create mode 100644 recipes-misc/recipes-multimedia/qtractor/files/0007-Hold-defer-JACK-timebase-reset-on-main-time-scale-up.patch diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0005-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch b/recipes-misc/recipes-multimedia/qtractor/files/0005-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch index fbcb77c4..72b451b8 100644 --- a/recipes-misc/recipes-multimedia/qtractor/files/0005-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch +++ b/recipes-misc/recipes-multimedia/qtractor/files/0005-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch @@ -1,7 +1,7 @@ From a5275b9facecc8a77cb6c47c1c186b8135fa34f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Sat, 4 Nov 2017 20:41:50 +0100 -Subject: [PATCH 1/3] Add qtractorPluginListDocument to save/load plugin-lists +Subject: [PATCH 1/4] Add qtractorPluginListDocument to save/load plugin-lists to XML MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0007-Add-MIDI-import-options-to-create-ready-to-play-sess.patch b/recipes-misc/recipes-multimedia/qtractor/files/0006-Add-MIDI-import-options-to-create-ready-to-play-sess.patch similarity index 88% rename from recipes-misc/recipes-multimedia/qtractor/files/0007-Add-MIDI-import-options-to-create-ready-to-play-sess.patch rename to recipes-misc/recipes-multimedia/qtractor/files/0006-Add-MIDI-import-options-to-create-ready-to-play-sess.patch index bf24204c..31ea1b01 100644 --- a/recipes-misc/recipes-multimedia/qtractor/files/0007-Add-MIDI-import-options-to-create-ready-to-play-sess.patch +++ b/recipes-misc/recipes-multimedia/qtractor/files/0006-Add-MIDI-import-options-to-create-ready-to-play-sess.patch @@ -1,7 +1,7 @@ -From 19a97a228e1af0adcb2cc565920a14646215d638 Mon Sep 17 00:00:00 2001 +From bd053e36f5dbd62e4ebce2d96f724980a9c7a1ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Thu, 9 Nov 2017 22:08:14 +0100 -Subject: [PATCH 3/3] Add MIDI import options to create ready-to-play sessions +Subject: [PATCH] Add MIDI import options to create ready-to-play sessions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -22,8 +22,8 @@ following: Signed-off-by: Andreas Müller --- src/qtractorMainForm.cpp | 14 +- - src/qtractorMidiImportExtender.cpp | 398 ++++++++++++++++++++++++++++ - src/qtractorMidiImportExtender.h | 100 +++++++ + src/qtractorMidiImportExtender.cpp | 433 +++++++++++++++++++++++++++++++ + src/qtractorMidiImportExtender.h | 107 ++++++++ src/qtractorMidiImportForm.cpp | 519 +++++++++++++++++++++++++++++++++++++ src/qtractorMidiImportForm.h | 110 ++++++++ src/qtractorMidiImportForm.ui | 396 ++++++++++++++++++++++++++++ @@ -32,12 +32,14 @@ Signed-off-by: Andreas Müller src/qtractorOptionsForm.cpp | 35 +++ src/qtractorOptionsForm.h | 5 + src/qtractorOptionsForm.ui | 21 +- + src/qtractorPlugin.cpp | 15 +- + src/qtractorPlugin.h | 6 + src/qtractorSession.cpp | 2 + src/qtractorTrackList.cpp | 8 +- - src/qtractorTracks.cpp | 47 +++- + src/qtractorTracks.cpp | 42 ++- src/qtractorTracks.h | 4 +- src/src.pro | 5 + - 16 files changed, 1674 insertions(+), 15 deletions(-) + 18 files changed, 1731 insertions(+), 16 deletions(-) create mode 100644 src/qtractorMidiImportExtender.cpp create mode 100644 src/qtractorMidiImportExtender.h create mode 100644 src/qtractorMidiImportForm.cpp @@ -45,7 +47,7 @@ Signed-off-by: Andreas Müller create mode 100644 src/qtractorMidiImportForm.ui diff --git a/src/qtractorMainForm.cpp b/src/qtractorMainForm.cpp -index d7d16ba..30ed26f 100644 +index 04072d1..78905ad 100644 --- a/src/qtractorMainForm.cpp +++ b/src/qtractorMainForm.cpp @@ -76,6 +76,8 @@ @@ -78,10 +80,10 @@ index d7d16ba..30ed26f 100644 diff --git a/src/qtractorMidiImportExtender.cpp b/src/qtractorMidiImportExtender.cpp new file mode 100644 -index 0000000..431e9d7 +index 0000000..e84b779 --- /dev/null +++ b/src/qtractorMidiImportExtender.cpp -@@ -0,0 +1,398 @@ +@@ -0,0 +1,433 @@ +// qtractorMidiImportExtender.cpp +// +/**************************************************************************** @@ -112,7 +114,9 @@ index 0000000..431e9d7 +#include "qtractorMidiEngine.h" +#include "qtractorMidiManager.h" +#include "qtractorTrackCommand.h" -+#include "QDomDocument" ++#include "qtractorMidiClip.h" ++#include ++#include + +//---------------------------------------------------------------------- +// class qtractorMidiImportExtender -- MIDI import extender class. @@ -132,7 +136,8 @@ index 0000000..431e9d7 + +// Pointer to singleton plugin list for display. +qtractorPluginList *qtractorMidiImportExtender::m_pPluginList = NULL; -+ ++// Keep info that plugin list was empied. ++bool qtractorMidiImportExtender::m_bPluginListIsEmpty = true; + +// Constructor. +qtractorMidiImportExtender::qtractorMidiImportExtender() @@ -234,7 +239,7 @@ index 0000000..431e9d7 + + +// Accessor/creator for plugin list. -+qtractorPluginList *qtractorMidiImportExtender::pluginList() ++qtractorPluginList *qtractorMidiImportExtender::pluginListForGui() +{ + qtractorSession *pSession = qtractorSession::getInstance(); + if (!pSession) @@ -242,8 +247,12 @@ index 0000000..431e9d7 + + // Create plugin list. + if (!m_pPluginList) { -+ // Create plugin list. + m_pPluginList = new qtractorPluginList(0, qtractorPluginList::Midi); ++ // GUI plugin list will never be heard so do no processing. ++ m_pPluginList->forceNoProcessing(true); ++ } ++ // Were plugins removed? ++ if (m_bPluginListIsEmpty) { + // Create plugins from document. + if (m_extendedSettings.pPluginDomDocument) { + qtractorPluginListDocument pluginListDocument( @@ -274,6 +283,8 @@ index 0000000..431e9d7 + else + // Use reasonable fallback channel count. + m_pPluginList->setChannels(2, qtractorPluginList::Midi); ++ // Plugin list is filled now. ++ m_bPluginListIsEmpty = false; + } + + return m_pPluginList; @@ -287,29 +298,35 @@ index 0000000..431e9d7 + delete m_pPluginList; + m_pPluginList = NULL; + } ++ m_bPluginListIsEmpty = true; +} + + -+// Add plugins to track and return a track-edit-command for setting midi-bank. -+qtractorEditTrackCommand *qtractorMidiImportExtender::modifyBareTrackAndGetEditCommand( -+ qtractorTrack *pTrack) ++// Add plugins to track - this must be done before track is added to session. ++// There was no way found to wait for plugins e.g. loading soundfonts. ++void qtractorMidiImportExtender::prepareTrackForExtension(qtractorTrack *pTrack) +{ + // Bail out if something is wrong. + if (!pTrack) -+ return NULL; -+ qtractorPluginList *pTargetList = pTrack->pluginList(); -+ if (!pTargetList) -+ return NULL; ++ return; ++ qtractorSession *pSession = qtractorSession::getInstance(); ++ if (!pSession) ++ return; ++ qtractorPluginList *pPluginList = pTrack->pluginList(); ++ if (!pPluginList) ++ return; + -+ // Add plugins. ++ // Add plugins early. Several options were tested to add plugins later ++ // (when tracks are complete / added to session): it was not possible to ++ // set patch reproducable. + + // Take plugins for first track from plugin list. -+ if (m_pPluginList) { ++ if (m_pPluginList && !m_bPluginListIsEmpty) ++ { + // Move plugins to target. + while (m_pPluginList->count()) -+ pTargetList->movePlugin(m_pPluginList->first(), NULL); -+ // Plugin list is empty now so it can be removed. -+ clearPluginList(); ++ pPluginList->movePlugin(m_pPluginList->first(), NULL); ++ m_bPluginListIsEmpty = true; + } + // All further are cloned by our document - if there is one. + else if (m_extendedSettings.pPluginDomDocument) { @@ -318,40 +335,23 @@ index 0000000..431e9d7 + m_extendedSettings.pPluginDomDocument->documentElement(); + if (elem.tagName() == "PluginList") { + qtractorPluginListDocument pluginListDocument( -+ m_extendedSettings.pPluginDomDocument, pTargetList); ++ m_extendedSettings.pPluginDomDocument, pPluginList); + pluginListDocument.loadElement(&elem); + } + } + -+ // Set MIDI bank. -+ bool bTrackChanged = false; -+ bool bIsDrumTrack = pTrack->midiChannel() == 9; -+ int iBankNew = bIsDrumTrack ? -+ m_extendedSettings.iMidiImportDrumBank : -+ m_extendedSettings.iMidiImportInstBank; -+ qtractorTrack::Properties &properties = pTrack->properties(); -+ if (iBankNew >= 0) { -+ properties.midiBank = iBankNew; -+ // By default midiBankSelMethod is -1. If not set it here, bank change -+ // is not send to instrument. -+ properties.midiBankSelMethod = 0; -+ bTrackChanged = true; -+ // Special case for drum-tracks: Some MIDI files out in the wild do not -+ // set program for drum-tracks. Give those a reasonable default. -+ if (bIsDrumTrack && properties.midiProg < 0) -+ // Set standard drums. -+ properties.midiProg = 0; ++ if (pPluginList->count() > 0) { ++ // Deactivate processing for plugin list - we have to choose the hard way ++ // because during adding tracks to session activation state of plugins ++ // flip. It seems activated plugins must report proper activation state ++ // for next actions - otherwise output is scrambled. ++ pPluginList->forceNoProcessing(true); + } -+ -+ qtractorEditTrackCommand *pEditCommand = NULL; -+ if (bTrackChanged) -+ pEditCommand = new qtractorEditTrackCommand(pTrack, properties); -+ return pEditCommand; +} + + +// Set instrument and track-name. Track must be 'complete' (have an output bus). -+bool qtractorMidiImportExtender::modifyFinishedTrack(qtractorTrack *pTrack) ++bool qtractorMidiImportExtender::finishTrackForExtension(qtractorTrack *pTrack) +{ + // Bail out if something is wrong. + if (!pTrack) @@ -359,61 +359,93 @@ index 0000000..431e9d7 + qtractorSession *pSession = qtractorSession::getInstance(); + if (!pSession) + return false; ++ qtractorPluginList *pPluginList = pTrack->pluginList(); ++ if (!pPluginList) ++ return false; + -+ // Some helpers... -+ bool bTrackChanged = false; -+ int iMidiChannel = pTrack->midiChannel(); -+ bool bIsDrumTrack = pTrack->midiChannel() == 9; ++ // Reactivate plugin processing first. ++ if (pPluginList->count() > 0) ++ pPluginList->forceNoProcessing(false); ++ ++ // Track change must be reported as return value. ++ bool bTrackChanged = pPluginList->count() > 0; ++ ++ // Get current track properties. ++ qtractorTrack::Properties &properties = pTrack->properties(); ++ bool bIsDrumTrack = properties.midiChannel == 9; ++ ++ // Midi bank. ++ int iBankNew = properties.midiBank; ++ if (!bIsDrumTrack) { ++ // Instruments: apply bank settings only if clip does not suggest. ++ if (iBankNew < 0) ++ iBankNew = m_extendedSettings.iMidiImportInstBank; ++ } else ++ // Follow settings for drums whatever clip suggests. ++ iBankNew = m_extendedSettings.iMidiImportDrumBank; ++ // And bank action... ++ if (iBankNew >= 0) { ++ // By default midiBankSelMethod is -1. If not set it here, bank change ++ // is not send to instrument. ++ properties.midiBankSelMethod = 0; ++ properties.midiBank = iBankNew; ++ bTrackChanged = true; ++ } ++ ++ // Special case for drum-tracks program: Some MIDI files out in the wild do ++ // not set program for drum tracks. Give those a reasonable default. ++ if (bIsDrumTrack && properties.midiProg < 0) { ++ // Set standard drums. ++ properties.midiProg = 0; ++ bTrackChanged = true; ++ } ++ ++ // Make sure plugins are able to process program/bank changes send below. ++ if (pPluginList->count() > 0) ++ pSession->stabilize(100); ++ ++ // Set patch (instrument / midiprog / midibank). ++ QString strInstrumentNew = bIsDrumTrack ? ++ m_extendedSettings.sMidiImportDrumInst : ++ m_extendedSettings.sMidiImportInstInst; + -+ // Set instrument. + qtractorMidiBus *pMidiBus = + static_cast (pTrack->outputBus()); + if (pMidiBus) { -+ // Get current patch -+ const qtractorMidiBus::Patch& patch = pMidiBus->patch(iMidiChannel); -+ // set instrument / bank for tracks with program set only. -+ if (patch.prog >= 0) { -+ // Set instrument. -+ QString strInstrumentNew = bIsDrumTrack ? -+ m_extendedSettings.sMidiImportDrumInst : -+ m_extendedSettings.sMidiImportInstInst; ++ // Set instrument / bank for tracks with program set only. At least ++ // plugins have missed program change during adding tracks to session - ++ // they were not processing. ++ if (properties.midiProg >= 0) { ++ bTrackChanged = true; ++ pMidiBus->setPatch(properties.midiChannel, ++ strInstrumentNew, ++ properties.midiBankSelMethod, ++ properties.midiBank, ++ properties.midiProg, ++ pTrack); + -+ // Update patch if necessary. -+ if (!strInstrumentNew.isEmpty()) { -+ pMidiBus->setPatch(iMidiChannel, -+ strInstrumentNew, -+ patch.bankSelMethod, -+ patch.bank, -+ patch.prog, -+ pTrack); -+ bTrackChanged = true; -+ } ++ bTrackChanged = true; + } + } + -+ // Set track name. ++ // Set track name. Set names only for tracks making noise after import. ++ // This indicates from first glance that track is special or broken or... + QString sTrackName; -+ // Set names only for tracks making noise after import. This indicates -+ // immediately that such track is special or broken or... + if (pTrack->midiBank() >= 0 && pTrack->midiProg() >= 0) { + // Set track name based upon type set up. + switch (m_extendedSettings.eMidiImportTrackNameType) { + case Midifile: + // MIDI filename is default: no modification. + break; -+ case Track: { ++ case Track: + sTrackName = pSession->uniqueTrackName( -+ QString("Track %1").arg(++m_iTrackNumber)); ++ QString("Track %1").arg(++m_iTrackNumber)); ++ bTrackChanged = true; + break; -+ } + case PatchName: + // Instrument track. + if (!bIsDrumTrack) { + // Collect what's necessary to get patch name. -+ qtractorPluginList *pPluginList = pTrack->pluginList(); -+ if (!pPluginList) -+ break; -+ + qtractorMidiManager *pMidiManager = pPluginList->midiManager(); + if (!pMidiManager) + break; @@ -442,17 +474,22 @@ index 0000000..431e9d7 + } + // Let name overrides cause effect. + if (!sTrackName.isEmpty()) { ++ properties.trackName = sTrackName; + pSession->releaseTrackName(pTrack); + pTrack->setTrackName(sTrackName); + pSession->acquireTrackName(pTrack); + bTrackChanged = true; + } + ++ if (bTrackChanged) ++ // Apply track changes changes. ++ pTrack->setProperties(properties); ++ + return bTrackChanged; +} + + -+// Accessor to exteneded settings. ++// Accessor to extended settings. +qtractorMidiImportExtender::exportExtensionsData *qtractorMidiImportExtender::exportExtensions() +{ + return &m_extendedSettings; @@ -482,10 +519,10 @@ index 0000000..431e9d7 +// end of qtractorMidiImportExtender.cpp diff --git a/src/qtractorMidiImportExtender.h b/src/qtractorMidiImportExtender.h new file mode 100644 -index 0000000..b507602 +index 0000000..e202e89 --- /dev/null +++ b/src/qtractorMidiImportExtender.h -@@ -0,0 +1,100 @@ +@@ -0,0 +1,107 @@ +// qtractorMidiImportExtender.h +// +/**************************************************************************** @@ -510,13 +547,17 @@ index 0000000..b507602 +#ifndef __qtractorMidiImportExtender_h +#define __qtractorMidiImportExtender_h + -+#include "QString" ++#include ++#include + +// forward decalarations. ++class qtractorSession; ++class qtractorPlugin; +class qtractorPluginList; +class qtractorPluginListDocument; +class qtractorEditTrackCommand; +class qtractorTrack; ++class qtractorMidiClip; +class qtractorCommand; +class QDomDocument; + @@ -536,14 +577,14 @@ index 0000000..b507602 + // Restore plugin list command list to the point of creation. + void restoreCommandList(); + -+ // Accessor/creator for plugin list. -+ qtractorPluginList *pluginList(); -+ // Clear singleton plugin list - do only call on session close!!! -+ static void clearPluginList(); ++ // Accessor/creator for GUI plugin list. ++ qtractorPluginList *pluginListForGui(); ++ // Clear GUI singleton plugin list - do only call on session close!!! ++ static void clearPluginList(); + + // Methods used during import. -+ qtractorEditTrackCommand *modifyBareTrackAndGetEditCommand(qtractorTrack *pTrack); -+ bool modifyFinishedTrack(qtractorTrack *pTrack); ++ void prepareTrackForExtension(qtractorTrack *pTrack); ++ bool finishTrackForExtension(qtractorTrack *pTrack); + + // Track name set types. + typedef enum { Midifile, Track, PatchName } TrackNameType; @@ -572,6 +613,8 @@ index 0000000..b507602 + + // Pointer to singleton plugin list for display. + static qtractorPluginList *m_pPluginList; ++ // Keep info that plugin list was empied. ++ static bool m_bPluginListIsEmpty; + + // Settings member. + exportExtensionsData m_extendedSettings; @@ -581,6 +624,7 @@ index 0000000..b507602 + + // Track number for 'Track n' naming type. + int m_iTrackNumber; ++ +}; + +#endif // __qtractorMidiImportExtender_h @@ -588,7 +632,7 @@ index 0000000..b507602 +// end of qtractorMidiImportExtender.h diff --git a/src/qtractorMidiImportForm.cpp b/src/qtractorMidiImportForm.cpp new file mode 100644 -index 0000000..30a6e86 +index 0000000..e04efb5 --- /dev/null +++ b/src/qtractorMidiImportForm.cpp @@ -0,0 +1,519 @@ @@ -643,7 +687,7 @@ index 0000000..30a6e86 + return; + + // Get plugin list to display. -+ qtractorPluginList *pPluginList = m_pMidiImportExtender->pluginList(); ++ qtractorPluginList *pPluginList = m_pMidiImportExtender->pluginListForGui(); + if (!pPluginList) + return; + @@ -921,7 +965,7 @@ index 0000000..30a6e86 + + // Take care of MIDI plugin instrument names... + updateInstrumentsAdd(icon, -+ m_pMidiImportExtender->pluginList()->midiManager()); ++ m_pMidiImportExtender->pluginListForGui()->midiManager()); + + // Regular instrument names... + qtractorInstrumentList::ConstIterator iter = pInstruments->constBegin(); @@ -1009,7 +1053,7 @@ index 0000000..30a6e86 + + // Care of MIDI plugin instrument banks... + bool bMidiManager = updateBanksAdd(icon, -+ m_pMidiImportExtender->pluginList()->midiManager(), ++ m_pMidiImportExtender->pluginListForGui()->midiManager(), + sInstrumentName, iBank, iBankIndex, + pComboBox, pBankMap); + @@ -1099,7 +1143,7 @@ index 0000000..30a6e86 +void qtractorMidiImportForm::commonCleanup() +{ + // Close all plugin windows before the turn into zombies. -+ for (qtractorPlugin *pPlugin = m_pMidiImportExtender->pluginList()->first(); ++ for (qtractorPlugin *pPlugin = m_pMidiImportExtender->pluginListForGui()->first(); + pPlugin; pPlugin = pPlugin->next()) { + pPlugin->closeEditor(); + pPlugin->closeForm(); @@ -1630,7 +1674,7 @@ index 0000000..f6e5e2d + + diff --git a/src/qtractorOptions.cpp b/src/qtractorOptions.cpp -index 9be2296..2459ef8 100644 +index 8796413..6d30146 100644 --- a/src/qtractorOptions.cpp +++ b/src/qtractorOptions.cpp @@ -162,6 +162,13 @@ void qtractorOptions::loadOptions (void) @@ -1647,7 +1691,7 @@ index 9be2296..2459ef8 100644 m_settings.endGroup(); // Metronome options group. -@@ -439,7 +446,7 @@ void qtractorOptions::saveOptions (void) +@@ -442,7 +449,7 @@ void qtractorOptions::saveOptions (void) m_settings.setValue("/MetroOffset", uint(iAudioMetroOffset)); m_settings.endGroup(); @@ -1656,7 +1700,7 @@ index 9be2296..2459ef8 100644 m_settings.beginGroup("/Midi"); m_settings.setValue("/CaptureFormat", iMidiCaptureFormat); m_settings.setValue("/CaptureQuantize", iMidiCaptureQuantize); -@@ -454,6 +461,13 @@ void qtractorOptions::saveOptions (void) +@@ -457,6 +464,13 @@ void qtractorOptions::saveOptions (void) m_settings.setValue("/MmcMode", iMidiMmcMode); m_settings.setValue("/SppMode", iMidiSppMode); m_settings.setValue("/ClockMode", iMidiClockMode); @@ -1671,7 +1715,7 @@ index 9be2296..2459ef8 100644 // Metronome options group. diff --git a/src/qtractorOptions.h b/src/qtractorOptions.h -index ca8aad0..1822b63 100644 +index 7233304..1bb68c4 100644 --- a/src/qtractorOptions.h +++ b/src/qtractorOptions.h @@ -140,6 +140,15 @@ public: @@ -1691,7 +1735,7 @@ index ca8aad0..1822b63 100644 int iMetroChannel; int iMetroBarNote; diff --git a/src/qtractorOptionsForm.cpp b/src/qtractorOptionsForm.cpp -index f15a3a7..39b5f7e 100644 +index db2d0ef..f397354 100644 --- a/src/qtractorOptionsForm.cpp +++ b/src/qtractorOptionsForm.cpp @@ -36,6 +36,8 @@ @@ -1713,7 +1757,7 @@ index f15a3a7..39b5f7e 100644 qtractorSession *pSession = qtractorSession::getInstance(); if (pSession) { m_pTimeScale = new qtractorTimeScale(*pSession->timeScale()); -@@ -264,6 +269,9 @@ qtractorOptionsForm::qtractorOptionsForm ( +@@ -269,6 +274,9 @@ qtractorOptionsForm::qtractorOptionsForm ( QObject::connect(m_ui.MidiCaptureFormatComboBox, SIGNAL(activated(int)), SLOT(changed())); @@ -1723,7 +1767,7 @@ index f15a3a7..39b5f7e 100644 QObject::connect(m_ui.MidiCaptureQuantizeComboBox, SIGNAL(activated(int)), SLOT(changed())); -@@ -502,6 +510,7 @@ qtractorOptionsForm::qtractorOptionsForm ( +@@ -506,6 +514,7 @@ qtractorOptionsForm::qtractorOptionsForm ( qtractorOptionsForm::~qtractorOptionsForm (void) { if (m_pTimeScale) delete m_pTimeScale; @@ -1731,7 +1775,7 @@ index f15a3a7..39b5f7e 100644 } -@@ -801,6 +810,9 @@ void qtractorOptionsForm::accept (void) +@@ -805,6 +814,9 @@ void qtractorOptionsForm::accept (void) m_pOptions->iMetroBeatDuration = m_ui.MetroBeatDurationSpinBox->value(); m_pOptions->bMidiMetroBus = m_ui.MidiMetroBusCheckBox->isChecked(); m_pOptions->iMidiMetroOffset = m_ui.MidiMetroOffsetSpinBox->value(); @@ -1741,7 +1785,7 @@ index f15a3a7..39b5f7e 100644 // Display options... m_pOptions->bConfirmRemove = m_ui.ConfirmRemoveCheckBox->isChecked(); m_pOptions->bConfirmArchive = m_ui.ConfirmArchiveCheckBox->isChecked(); -@@ -892,6 +904,10 @@ void qtractorOptionsForm::accept (void) +@@ -896,6 +908,10 @@ void qtractorOptionsForm::accept (void) // Save/commit to disk. m_pOptions->saveOptions(); @@ -1752,7 +1796,7 @@ index f15a3a7..39b5f7e 100644 // Just go with dialog acceptance QDialog::accept(); } -@@ -917,6 +933,11 @@ void qtractorOptionsForm::reject (void) +@@ -921,6 +937,11 @@ void qtractorOptionsForm::reject (void) accept(); return; case QMessageBox::Discard: @@ -1764,7 +1808,7 @@ index f15a3a7..39b5f7e 100644 break; default: // Cancel. bReject = false; -@@ -1561,6 +1582,20 @@ void qtractorOptionsForm::chooseSessionTemplatePath (void) +@@ -1565,6 +1586,20 @@ void qtractorOptionsForm::chooseSessionTemplatePath (void) } @@ -1816,7 +1860,7 @@ index baa6a55..ed0a845 100644 diff --git a/src/qtractorOptionsForm.ui b/src/qtractorOptionsForm.ui -index ab60726..ff62036 100644 +index 42ca936..b7e33e6 100644 --- a/src/qtractorOptionsForm.ui +++ b/src/qtractorOptionsForm.ui @@ -1371,7 +1371,7 @@ @@ -1854,8 +1898,72 @@ index ab60726..ff62036 100644 +diff --git a/src/qtractorPlugin.cpp b/src/qtractorPlugin.cpp +index e547381..4d5b1ce 100644 +--- a/src/qtractorPlugin.cpp ++++ b/src/qtractorPlugin.cpp +@@ -1410,7 +1410,8 @@ qtractorPluginList::qtractorPluginList ( + : m_iChannels(iChannels), m_iFlags(iFlags), + m_iActivated(0), m_pMidiManager(NULL), + m_iMidiBank(-1), m_iMidiProg(-1), +- m_pMidiProgramSubject(NULL), m_bAutoDeactivated(false) ++ m_pMidiProgramSubject(NULL), m_bAutoDeactivated(false), ++ m_iForceNoProcessing(0) + { + setAutoDelete(true); + +@@ -1801,6 +1802,10 @@ void qtractorPluginList::removeView ( qtractorPluginListView *pView ) + // The meta-main audio-processing plugin-chain procedure. + void qtractorPluginList::process ( float **ppBuffer, unsigned int nframes ) + { ++ // Forced not to process data? ++ if (m_iForceNoProcessing) ++ return; ++ + // Sanity checks... + if (!isActivated()) + return; +@@ -2220,6 +2225,14 @@ bool qtractorPluginList::isAutoDeactivated (void) const + return m_bAutoDeactivated; + } + ++void qtractorPluginList::forceNoProcessing(bool bForce) ++{ ++ if (bForce) ++ ++m_iForceNoProcessing; ++ else if (m_iForceNoProcessing > 0) ++ --m_iForceNoProcessing; ++} ++ + + // Check/sanitize plugin file-path; + bool qtractorPluginList::checkPluginFile ( +diff --git a/src/qtractorPlugin.h b/src/qtractorPlugin.h +index f56720b..b34e04d 100644 +--- a/src/qtractorPlugin.h ++++ b/src/qtractorPlugin.h +@@ -928,6 +928,9 @@ public: + void autoDeactivatePlugins(bool bDeactivated, bool bForce = false); + bool isAutoDeactivated() const; + ++ // Force no sound processing. ++ void forceNoProcessing(bool bForce); ++ + protected: + + // Check/sanitize plugin file-path. +@@ -978,6 +981,9 @@ private: + + // Auto-plugin-deactivation + bool m_bAutoDeactivated; ++ ++ // Force no sound processing. ++ int m_iForceNoProcessing; + }; + + diff --git a/src/qtractorSession.cpp b/src/qtractorSession.cpp -index c080215..9d71fc5 100644 +index 9fac440..80be9c8 100644 --- a/src/qtractorSession.cpp +++ b/src/qtractorSession.cpp @@ -33,6 +33,7 @@ @@ -1901,7 +2009,7 @@ index 0b710eb..7a99060 100644 m_pTracks->addAudioTracks(audio_files, iClipStart, pAfterTrack); diff --git a/src/qtractorTracks.cpp b/src/qtractorTracks.cpp -index 9b9c4c4..06405ea 100644 +index 9b9c4c4..4cd0b54 100644 --- a/src/qtractorTracks.cpp +++ b/src/qtractorTracks.cpp @@ -61,9 +61,12 @@ @@ -1945,7 +2053,7 @@ index 9b9c4c4..06405ea 100644 // We'll build a composite command... qtractorImportTrackCommand *pImportTrackCommand -@@ -2877,12 +2884,16 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, +@@ -2877,12 +2884,14 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, } // Time to check whether there is actual data on track... if (pMidiClip->clipLength() > 0) { @@ -1954,16 +2062,14 @@ index 9b9c4c4..06405ea 100644 pTrack->setTrackName( pSession->uniqueTrackName(pMidiClip->clipName())); pTrack->setMidiChannel(pMidiClip->channel()); ++ if (pMidiImportExtender) ++ pMidiImportExtender->prepareTrackForExtension(pTrack); pImportTrackCommand->addTrack(pTrack); - ++iUpdate; -+ // Enhanced dialog: do modifications on bare track -+ if (pMidiImportExtender) -+ pImportTrackCommand->addTrackEditCommand( -+ pMidiImportExtender->modifyBareTrackAndGetEditCommand(pTrack)); // Don't forget to add this one to local repository. if (pMainForm) pMainForm->addMidiFile(sPath); -@@ -2894,7 +2905,7 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, +@@ -2894,7 +2903,7 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, } } // Log this successful import operation... @@ -1972,7 +2078,7 @@ index 9b9c4c4..06405ea 100644 sDescription += tr("MIDI file import \"%1\" on %2 %3.\n") .arg(QFileInfo(sPath).fileName()) .arg(QDate::currentDate().toString("MMM dd yyyy")) -@@ -2906,17 +2917,39 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, +@@ -2906,17 +2915,36 @@ bool qtractorTracks::addMidiTracks ( const QStringList& files, // Have we changed anything? bool bResult = false; @@ -1989,11 +2095,8 @@ index 9b9c4c4..06405ea 100644 + QListIterator iter(addedTracks); + while (iter.hasNext()) { + qtractorTrack *pTrack = iter.next(); -+ if (pMidiImportExtender->modifyFinishedTrack(pTrack)) { -+ // reopen to take changes effect -+ pTrack->open(); ++ if (pMidiImportExtender->finishTrackForExtension(pTrack)) + bModified = true; -+ } + } + if (bModified) { + // Mixer needs extra invitation... diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0006-qtractorImportTrackCommand-extend-to-accept-qtractor.patch b/recipes-misc/recipes-multimedia/qtractor/files/0006-qtractorImportTrackCommand-extend-to-accept-qtractor.patch deleted file mode 100644 index a7b770eb..00000000 --- a/recipes-misc/recipes-multimedia/qtractor/files/0006-qtractorImportTrackCommand-extend-to-accept-qtractor.patch +++ /dev/null @@ -1,163 +0,0 @@ -From 093a9a2af41222116dc7525dc3050842134d47c5 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andreas=20M=C3=BCller?= -Date: Sun, 5 Nov 2017 00:58:28 +0100 -Subject: [PATCH 2/3] qtractorImportTrackCommand: extend to accept - qtractorEditTrackCommand -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This is neccessary to align tracks during import the way track modifications -are performed. - -* m_trackCommands is now a list of qtractorCommand. This is necessary because - qtractorEditTrackCommand is not inheriting qtractorTrackCommand. -* qtractorImportTrackCommand::addTrackEditCommand is added -* declaration of qtractorEditTrackCommand is moved before - qtractorImportTrackCommand to avoid useless forward declarations. This makes - the diff a bit human-unreadable. - -Signed-off-by: Andreas Müller ---- - src/qtractorTrackCommand.cpp | 15 +++++++++---- - src/qtractorTrackCommand.h | 51 ++++++++++++++++++++++---------------------- - 2 files changed, 37 insertions(+), 29 deletions(-) - -diff --git a/src/qtractorTrackCommand.cpp b/src/qtractorTrackCommand.cpp -index b1a720e..959b566 100644 ---- a/src/qtractorTrackCommand.cpp -+++ b/src/qtractorTrackCommand.cpp -@@ -653,6 +653,13 @@ void qtractorImportTrackCommand::addTrack ( qtractorTrack *pTrack ) - m_pAfterTrack = pTrack; - } - -+void qtractorImportTrackCommand::addTrackEditCommand( -+ qtractorEditTrackCommand *pEditTrackCommand) -+{ -+ if (pEditTrackCommand) -+ m_trackCommands.append(pEditTrackCommand); -+} -+ - - // Track-import command methods. - bool qtractorImportTrackCommand::redo (void) -@@ -665,9 +672,9 @@ bool qtractorImportTrackCommand::redo (void) - } - ++m_iSaveCount; - -- QListIterator iter(m_trackCommands); -+ QListIterator iter(m_trackCommands); - while (iter.hasNext()) { -- qtractorAddTrackCommand *pTrackCommand = iter.next(); -+ qtractorCommand *pTrackCommand = iter.next(); - if (!pTrackCommand->redo()) - bResult = false; - } -@@ -679,10 +686,10 @@ bool qtractorImportTrackCommand::undo (void) - { - bool bResult = true; - -- QListIterator iter(m_trackCommands); -+ QListIterator iter(m_trackCommands); - iter.toBack(); - while (iter.hasPrevious()) { -- qtractorAddTrackCommand *pTrackCommand = iter.previous(); -+ qtractorCommand *pTrackCommand = iter.previous(); - if (!pTrackCommand->undo()) - bResult = false; - } -diff --git a/src/qtractorTrackCommand.h b/src/qtractorTrackCommand.h -index 0746fba..ab15218 100644 ---- a/src/qtractorTrackCommand.h -+++ b/src/qtractorTrackCommand.h -@@ -191,61 +191,62 @@ private: - - - //---------------------------------------------------------------------- --// class qtractorInportTracksCommand - declaration. -+// class qtractorEditTrackCommand - declaration. - // - --class qtractorImportTrackCommand : public qtractorCommand -+class qtractorEditTrackCommand -+ : public qtractorPropertyCommand - { - public: - - // Constructor. -- qtractorImportTrackCommand(qtractorTrack *pAfterTrack); -- -- // Destructor. -- ~qtractorImportTrackCommand(); -- -- // Add track to command list. -- void addTrack(qtractorTrack *pTrack); -+ qtractorEditTrackCommand(qtractorTrack *pTrack, -+ const qtractorTrack::Properties& props); - -- // Track-import command methods. -+ // Overridden track-edit command methods. - bool redo(); - bool undo(); - - private: - - // Instance variables. -- qtractorTrack *m_pAfterTrack; -- -- QList m_trackCommands; -- -- // Session properties backup stuff. -- qtractorSession::Properties m_sessionProps; -- qtractorPropertyCommand *m_pSaveCommand; -- int m_iSaveCount; -+ qtractorTrack *m_pTrack; - }; - - - //---------------------------------------------------------------------- --// class qtractorEditTrackCommand - declaration. -+// class qtractorInportTracksCommand - declaration. - // - --class qtractorEditTrackCommand -- : public qtractorPropertyCommand -+class qtractorImportTrackCommand : public qtractorCommand - { - public: - - // Constructor. -- qtractorEditTrackCommand(qtractorTrack *pTrack, -- const qtractorTrack::Properties& props); -+ qtractorImportTrackCommand(qtractorTrack *pAfterTrack); - -- // Overridden track-edit command methods. -+ // Destructor. -+ ~qtractorImportTrackCommand(); -+ -+ // Add track to command list. -+ void addTrack(qtractorTrack *pTrack); -+ // Add track edit command to command list. -+ void addTrackEditCommand(qtractorEditTrackCommand* pEditTrackCommand); -+ -+ // Track-import command methods. - bool redo(); - bool undo(); - - private: - - // Instance variables. -- qtractorTrack *m_pTrack; -+ qtractorTrack *m_pAfterTrack; -+ QList m_trackCommands; -+ -+ // Session properties backup stuff. -+ qtractorSession::Properties m_sessionProps; -+ qtractorPropertyCommand *m_pSaveCommand; -+ int m_iSaveCount; - }; - - --- -2.9.5 - diff --git a/recipes-misc/recipes-multimedia/qtractor/files/0007-Hold-defer-JACK-timebase-reset-on-main-time-scale-up.patch b/recipes-misc/recipes-multimedia/qtractor/files/0007-Hold-defer-JACK-timebase-reset-on-main-time-scale-up.patch new file mode 100644 index 00000000..f13d4f59 --- /dev/null +++ b/recipes-misc/recipes-multimedia/qtractor/files/0007-Hold-defer-JACK-timebase-reset-on-main-time-scale-up.patch @@ -0,0 +1,2203 @@ +From 61a00f2bb2946cd1fedbb8a06cb93f31e98c9ca2 Mon Sep 17 00:00:00 2001 +From: rncbc +Date: Fri, 17 Nov 2017 22:09:06 +0000 +Subject: [PATCH] - Hold/defer JACK-timebase reset on main time-scale updates: + fixes zero-frame MIDI imports. + +--- + src/qtractorAudioEngine.cpp | 25 +++++++++++- + src/qtractorAudioEngine.h | 7 ++++ + src/qtractorMainForm.cpp | 3 +- + src/qtractorSession.cpp | 2 +- + src/translations/qtractor_cs.ts | 84 ++++++++++++++++++++--------------------- + src/translations/qtractor_de.ts | 84 ++++++++++++++++++++--------------------- + src/translations/qtractor_es.ts | 84 ++++++++++++++++++++--------------------- + src/translations/qtractor_fr.ts | 84 ++++++++++++++++++++--------------------- + src/translations/qtractor_it.ts | 84 ++++++++++++++++++++--------------------- + src/translations/qtractor_ja.ts | 84 ++++++++++++++++++++--------------------- + src/translations/qtractor_pt.ts | 84 ++++++++++++++++++++--------------------- + src/translations/qtractor_ru.ts | 84 ++++++++++++++++++++--------------------- + 12 files changed, 370 insertions(+), 339 deletions(-) + +diff --git a/src/qtractorAudioEngine.cpp b/src/qtractorAudioEngine.cpp +index a8d8e83..093afee 100644 +--- a/src/qtractorAudioEngine.cpp ++++ b/src/qtractorAudioEngine.cpp +@@ -449,6 +449,9 @@ qtractorAudioEngine::qtractorAudioEngine ( qtractorSession *pSession ) + // JACK timebase mode control. + m_bTimebase = true; + m_iTimebase = 0; ++ ++ // JACK Timebase sync flag. ++ m_iTimebaseHold = 0; + } + + +@@ -1186,7 +1189,11 @@ void qtractorAudioEngine::timebase ( jack_position_t *pPos, int iNewPos ) + pPos->beat_type = float(1 << pNode->beatDivisor); + + // Tell that we've been here... +- if (iNewPos) ++m_iTimebase; ++ if (iNewPos) { ++ ++m_iTimebase; ++ if (m_iTimebaseHold > 0) ++ m_iTimebaseHold = 0; ++ } + } + + +@@ -2047,6 +2054,22 @@ void qtractorAudioEngine::resetTimebase (void) + } + + ++// JACK Timebase/transport sync flagging. ++void qtractorAudioEngine::setTimebaseHold ( bool bTimebaseHold ) ++{ ++ if (m_bTimebase && bTimebaseHold) ++ ++m_iTimebaseHold; ++ else ++ m_iTimebaseHold = 0; ++} ++ ++ ++bool qtractorAudioEngine::isTimebaseHold (void) const ++{ ++ return (m_iTimebaseHold > 0); ++} ++ ++ + // Absolute number of frames elapsed since engine start. + unsigned long qtractorAudioEngine::jackFrameTime (void) const + { +diff --git a/src/qtractorAudioEngine.h b/src/qtractorAudioEngine.h +index 718c256..441c6a7 100644 +--- a/src/qtractorAudioEngine.h ++++ b/src/qtractorAudioEngine.h +@@ -221,6 +221,10 @@ public: + // JACK Timebase reset method. + void resetTimebase(); + ++ // JACK Timebase sync flagging. ++ void setTimebaseHold(bool bTimebaseHold); ++ bool isTimebaseHold() const; ++ + // Absolute number of frames elapsed since engine start. + unsigned long jackFrameTime() const; + +@@ -323,6 +327,9 @@ private: + // JACK Timebase mode and control. + bool m_bTimebase; + unsigned int m_iTimebase; ++ ++ // JACK Timebase sync flag. ++ unsigned int m_iTimebaseHold; + }; + + +diff --git a/src/qtractorMainForm.cpp b/src/qtractorMainForm.cpp +index 1b845d6..be17f22 100644 +--- a/src/qtractorMainForm.cpp ++++ b/src/qtractorMainForm.cpp +@@ -7455,7 +7455,8 @@ void qtractorMainForm::slowTimerSlot (void) + m_pSession->seek(iPlayHead, true); + } + // 2. Watch for temp/time-sig changes on JACK transport... +- if (pos.valid & JackPositionBBT) { ++ if ((pos.valid & JackPositionBBT) ++ && (!pAudioEngine->isTimebaseHold())) { + qtractorTimeScale *pTimeScale = m_pSession->timeScale(); + qtractorTimeScale::Cursor& cursor = pTimeScale->cursor(); + qtractorTimeScale::Node *pNode = cursor.seekFrame(pos.frame); +diff --git a/src/qtractorSession.cpp b/src/qtractorSession.cpp +index 7853d96..c6fc4b9 100644 +--- a/src/qtractorSession.cpp ++++ b/src/qtractorSession.cpp +@@ -743,7 +743,7 @@ void qtractorSession::updateTimeScale (void) + m_iEditTail = frameFromTick(m_iEditTailTime); + + // Get timebase up to date immediately... +- m_pAudioEngine->resetTimebase(); ++ m_pAudioEngine->setTimebaseHold(true); + } + + +diff --git a/src/translations/qtractor_cs.ts b/src/translations/qtractor_cs.ts +index 3f0729b..ba1413e 100644 +--- a/src/translations/qtractor_cs.ts ++++ b/src/translations/qtractor_cs.ts +@@ -34,16 +34,16 @@ Audio: %1 channels, %2 Hz + (posunutí výšky tónu v půltónech %1) + + +- +- ++ ++ + + + %1 In + %1 Vstup + + +- +- ++ ++ + + + %1 Out +@@ -633,15 +633,15 @@ Stopa: "%1" Vstup: "%2" Výstup: "%3"%1 Hlasitost + + +- +- ++ ++ + + %1 Gain + %1 Zesílení + + +- +- ++ ++ + + + +@@ -2802,7 +2802,7 @@ Jste si jistý? + Žádný + + +- ++ + The audio engine has been shutdown. + + Make sure the JACK audio server (jackd) +@@ -2813,120 +2813,120 @@ Ujistěte se, že je zvukový server Jack (jackd) + spuštěn a běží, a potom sezení začněte znovu. + + +- +- ++ ++ + STOP + ZASTAVIT + + +- ++ + PLAY + PŘEHRÁT + + +- ++ + FFWD + RYCHLE DOPŘEDU + + +- ++ + REW + PŘETOČIT + + +- ++ + REC ON + NAHRÁVÁNÍ ZAPNUTO + + +- ++ + REC OFF + NAHRÁVÁNÍ VYPNUTO + + +- ++ + RESET + NASTAVIT ZNOVU + + +- ++ + LOCATE %1 + NAJÍT %1 + + +- ++ + SHUTTLE %1 + JEZDIT TAM A ZPĚT %1 + + +- ++ + STEP %1 + INTERVAL %1 + + +- ++ + TRACK RECORD %1 %2 + NAHRÁVAT STOPU %1 %2 + + +- ++ + TRACK MUTE %1 %2 + ZTLUMIT STOPU %1 %2 + + +- ++ + TRACK SOLO %1 %2 + SÓLO PRO STOPU %1 %2 + + +- ++ + Unknown sub-command + Neznámý pod-příkaz + + +- +- ++ ++ + Not implemented + Neprovedeno + + +- ++ + MIDI CTL: %1, Channel %2, Param %3, Value %4 + MIDI CTL %1, Kanál %2, Parametr %3, Hodnota %4 + + +- ++ + %1 BPM + %1 ÚZM + + +- ++ + (track %1, gain %2) + (stopa %1, zesílení %2) + + +- ++ + (track %1, panning %2) + (stopa %1, vyvažování %2) + + +- ++ + START + SPUSTIT + + +- ++ + CONTINUE + POKRAČOVAT + + +- ++ + SONGPOS %1 + POLOHA V PÍSNI %1 + + + +- ++ + Untitled%1 + Bez názvu %1 + +@@ -3253,32 +3253,32 @@ Důrazně se doporučuje uložit do jiného souboru se sezením. + Chyba + + +- ++ + XRUN(%1 skipped) + XRUN(%1 přeskočeno) + + +- ++ + XRUN(%1): some frames might have been lost. + XRUN(%1): některé snímky mohly být ztraceny. + + +- ++ + Audio connections change. + Změna zvukových připojení. + + +- ++ + MIDI connections change. + Změna MIDI připojení. + + +- ++ + Playing ended. + Přehrávání ukončeno. + + +- ++ + The audio engine buffer size has changed, + increased from %1 to %2 frames/period. + +@@ -3291,8 +3291,8 @@ Velmi se doporučuje nahrát nynější soubor se + sezením znovu. + + +- +- ++ ++ + Playing "%1"... + Přehrává "%1"... + +diff --git a/src/translations/qtractor_de.ts b/src/translations/qtractor_de.ts +index 9d3ae05..6b5668c 100644 +--- a/src/translations/qtractor_de.ts ++++ b/src/translations/qtractor_de.ts +@@ -34,16 +34,16 @@ Audio: %1 Kanäle, %2 Hz + + + +- +- ++ ++ + + + %1 In + %1 Ein + + +- +- ++ ++ + + + %1 Out +@@ -582,15 +582,15 @@ MIDI: + %1 Lautstärke + + +- +- ++ ++ + + %1 Gain + + + +- +- ++ ++ + + + +@@ -5493,7 +5493,7 @@ Sorry. + + + +- ++ + Untitled%1 + Unbenannt%1 + +@@ -6001,32 +6001,32 @@ Saving into another session file is highly recommended. + Fehler + + +- ++ + XRUN(%1 skipped) + XRUN(%1 übersprungen) + + +- ++ + XRUN(%1): some frames might have been lost. + + + +- ++ + Audio connections change. + Audio-Verbindung geändert. + + +- ++ + MIDI connections change. + MIDI-Verbindung geändert. + + +- ++ + Playing ended. + + + +- ++ + The audio engine has been shutdown. + + Make sure the JACK audio server (jackd) +@@ -6034,7 +6034,7 @@ is up and running and then restart session. + + + +- ++ + The audio engine buffer size has changed, + increased from %1 to %2 frames/period. + +@@ -6043,120 +6043,120 @@ is highly recommended. + + + +- +- ++ ++ + STOP + + + +- ++ + PLAY + + + +- ++ + FFWD + + + +- ++ + REW + + + +- ++ + REC ON + + + +- ++ + REC OFF + + + +- ++ + RESET + + + +- ++ + LOCATE %1 + + + +- ++ + SHUTTLE %1 + + + +- ++ + STEP %1 + + + +- ++ + TRACK RECORD %1 %2 + + + +- ++ + TRACK MUTE %1 %2 + + + +- ++ + TRACK SOLO %1 %2 + + + +- ++ + Unknown sub-command + Unbekanntes Unterkommando + + +- +- ++ ++ + Not implemented + Nicht implementiert + + +- ++ + MIDI CTL: %1, Channel %2, Param %3, Value %4 + + + +- ++ + (track %1, gain %2) + + + +- ++ + (track %1, panning %2) + + + +- ++ + START + + + +- ++ + CONTINUE + + + +- ++ + SONGPOS %1 + + + +- ++ + %1 BPM + + + +- +- ++ ++ + Playing "%1"... + Spiele "%1" ab... + +diff --git a/src/translations/qtractor_es.ts b/src/translations/qtractor_es.ts +index 0160f5b..b7868e5 100644 +--- a/src/translations/qtractor_es.ts ++++ b/src/translations/qtractor_es.ts +@@ -35,16 +35,16 @@ Audio: %1 canales, %2 Hz + (%1 tranponer semitonos) + + +- +- ++ ++ + + + %1 In + %1 Entrada + + +- +- ++ ++ + + + %1 Out +@@ -652,15 +652,15 @@ MIDI: + %1 Volumen + + +- +- ++ ++ + + %1 Gain + %1 Ganancia + + +- +- ++ ++ + + + +@@ -5509,7 +5509,7 @@ Lo siento. + + + +- ++ + Untitled%1 + Sin título%1 + +@@ -6059,32 +6059,32 @@ Guardar en otro archivo de sesión es muy recomendable. + Error + + +- ++ + XRUN(%1 skipped) + XRUN(%1 omitido) + + +- ++ + XRUN(%1): some frames might have been lost. + XRUN(%1): algunos cuadros se pueden haber perdido. + + +- ++ + Audio connections change. + Cambio en las conexiones de Audio. + + +- ++ + MIDI connections change. + Cambio en las conexiones MIDI. + + +- ++ + Playing ended. + Reproducción terminada. + + +- ++ + The audio engine has been shutdown. + + Make sure the JACK audio server (jackd) +@@ -6095,7 +6095,7 @@ Asegúrese de que el servidor de audio JACK (jackd) + este en funcionamiento y reinicie la sesión. + + +- ++ + The audio engine buffer size has changed, + increased from %1 to %2 frames/period. + +@@ -6108,120 +6108,120 @@ Se recomienda que se vuelva a cargar + el archivo de sesión actual. + + +- +- ++ ++ + STOP + STOP + + +- ++ + PLAY + PLAY + + +- ++ + FFWD + FFWD + + +- ++ + REW + REW + + +- ++ + REC ON + REC ON + + +- ++ + REC OFF + REC OFF + + +- ++ + RESET + RESET + + +- ++ + LOCATE %1 + LOCATE %1 + + +- ++ + SHUTTLE %1 + SHUTTLE %1 + + +- ++ + STEP %1 + STEP %1 + + +- ++ + TRACK RECORD %1 %2 + TRACK RECORD %1 %2 + + +- ++ + TRACK MUTE %1 %2 + TRACK MUTE %1 %2 + + +- ++ + TRACK SOLO %1 %2 + TRACK SOLO %1 %2 + + +- ++ + Unknown sub-command + Sub-comando desconocido + + +- +- ++ ++ + Not implemented + No implementado + + +- ++ + MIDI CTL: %1, Channel %2, Param %3, Value %4 + MIDI CTL: %1, Canal %2, Param %3, Valor %4 + + +- ++ + (track %1, gain %2) + (pista %1, ganancia %2) + + +- ++ + (track %1, panning %2) + (pista %1, paneo %2) + + +- ++ + START + INICIAR + + +- ++ + CONTINUE + CONTINUAR + + +- ++ + SONGPOS %1 + SONGPOS %1 + + +- ++ + %1 BPM + %1 PPM + + +- +- ++ ++ + Playing "%1"... + Reproduciendo "%1"... + +diff --git a/src/translations/qtractor_fr.ts b/src/translations/qtractor_fr.ts +index 4010de2..91f3502 100644 +--- a/src/translations/qtractor_fr.ts ++++ b/src/translations/qtractor_fr.ts +@@ -34,16 +34,16 @@ Audio : %1 canaux, %2 Hz + (%1 demi-tons de décalage de la tonalité) + + +- +- ++ ++ + + + %1 In + %1 Entrée + + +- +- ++ ++ + + + %1 Out +@@ -585,15 +585,15 @@ MIDI: + %1 Volume + + +- +- ++ ++ + + %1 Gain + %1 Gain + + +- +- ++ ++ + + + +@@ -5499,7 +5499,7 @@ Désolé. + + + +- ++ + Untitled%1 + Sans titre%1 + +@@ -5871,7 +5871,7 @@ Saving into another session file is highly recommended. + Sauvegarder dans un autre fichier session est fortement recommandé. + + +- ++ + The audio engine buffer size has changed, + increased from %1 to %2 frames/period. + +@@ -6072,32 +6072,32 @@ est près et en fonctionnement puis redémarrer la session. + Erreur + + +- ++ + XRUN(%1 skipped) + XRUN(%1 sauté) + + +- ++ + XRUN(%1): some frames might have been lost. + XRUN(%1): des trames peuvent avoir été perdues. + + +- ++ + Audio connections change. + Les connexions audio changent. + + +- ++ + MIDI connections change. + Les connexions MIDI changent. + + +- ++ + Playing ended. + Lecture terminée. + + +- ++ + The audio engine has been shutdown. + + Make sure the JACK audio server (jackd) +@@ -6108,120 +6108,120 @@ Assurez-vous que le serveur audio JACK (jackd) + est près et en fonctionnement puis redémarrez la session. + + +- +- ++ ++ + STOP + STOP + + +- ++ + PLAY + JOUER + + +- ++ + FFWD + FFWD + + +- ++ + REW + REW + + +- ++ + REC ON + ENR ON + + +- ++ + REC OFF + ENR OFF + + +- ++ + RESET + REINIT + + +- ++ + LOCATE %1 + LOCALISE %1 + + +- ++ + SHUTTLE %1 + NAVETTE %1 + + +- ++ + STEP %1 + PAS %1 + + +- ++ + TRACK RECORD %1 %2 + PISTE ENREGISTREMENT %1 %2 + + +- ++ + TRACK MUTE %1 %2 + PISTE MUET %1 %2 + + +- ++ + TRACK SOLO %1 %2 + PISTE SOLO %1 %2 + + +- ++ + Unknown sub-command + Sous-commande inconnue + + +- +- ++ ++ + Not implemented + Pas implémenté + + +- ++ + MIDI CTL: %1, Channel %2, Param %3, Value %4 + MIDI CTL: %1, Canal %2, Param %3, Valeur %4 + + +- ++ + (track %1, gain %2) + (piste %1, gain %2) + + +- ++ + (track %1, panning %2) + (piste %1, panoramique %2) + + +- ++ + START + DEMARRE + + +- ++ + CONTINUE + CONTINUE + + +- ++ + SONGPOS %1 + SONGPOS %1 + + +- ++ + %1 BPM + %1 BPM + + +- +- ++ ++ + Playing "%1"... + Joue "%1"... + +diff --git a/src/translations/qtractor_it.ts b/src/translations/qtractor_it.ts +index c6e65c5..8bb7c91 100644 +--- a/src/translations/qtractor_it.ts ++++ b/src/translations/qtractor_it.ts +@@ -35,16 +35,16 @@ Audio: %1 canali, %2 Hz + (%1 semitoni trasposti) + + +- +- ++ ++ + + + %1 In + %1 Ingressi + + +- +- ++ ++ + + + %1 Out +@@ -586,15 +586,15 @@ MIDI: + %1 Volume + + +- +- ++ ++ + + %1 Gain + %1 Guadagno + + +- +- ++ ++ + + + +@@ -5500,7 +5500,7 @@ Spiacente. + + + +- ++ + Untitled%1 + SenzaTitolo%1 + +@@ -5872,7 +5872,7 @@ Saving into another session file is highly recommended. + E' fortemente consigliato il salvataggio in un nuovo file di sessione. + + +- ++ + The audio engine buffer size has changed, + increased from %1 to %2 frames/period. + +@@ -6073,32 +6073,32 @@ siano attivi ed in esecuzione, poi riavvia la sessione. + Errore + + +- ++ + XRUN(%1 skipped) + XRUN(%1 saltato) + + +- ++ + XRUN(%1): some frames might have been lost. + XRUN(%1): alcuni campioni potrebbero essere stati persi. + + +- ++ + Audio connections change. + Cambiamento connessioni audio. + + +- ++ + MIDI connections change. + Cambiamento connessioni MIDI. + + +- ++ + Playing ended. + Riproduzione terminata. + + +- ++ + The audio engine has been shutdown. + + Make sure the JACK audio server (jackd) +@@ -6109,120 +6109,120 @@ Accertati che il server audio JACK (jackd) + sia avviato ed in esecuzione, poi riavvia la sessione. + + +- +- ++ ++ + STOP + STOP + + +- ++ + PLAY + PLAY + + +- ++ + FFWD + FFWD + + +- ++ + REW + REW + + +- ++ + REC ON + REC ON + + +- ++ + REC OFF + REC OFF + + +- ++ + RESET + RESET + + +- ++ + LOCATE %1 + TROVA %1 + + +- ++ + SHUTTLE %1 + SHUTTLE %1 + + +- ++ + STEP %1 + STEP %1 + + +- ++ + TRACK RECORD %1 %2 + REGISTRA TRACCIA %1 %2 + + +- ++ + TRACK MUTE %1 %2 + MUTE TRACCIA %1 %2 + + +- ++ + TRACK SOLO %1 %2 + SOLO TRACCIA %1 %2 + + +- ++ + Unknown sub-command + Sotto-comando sconosciuto + + +- +- ++ ++ + Not implemented + Non implementato + + +- ++ + MIDI CTL: %1, Channel %2, Param %3, Value %4 + MIDI CTL: %1, Canale %2, Parametro %3, Valore %4 + + +- ++ + (track %1, gain %2) + (traccia %1, guadagno %2) + + +- ++ + (track %1, panning %2) + (traccia %1, bilanciamento %2) + + +- ++ + START + AVVIA + + +- ++ + CONTINUE + CONTINUA + + +- ++ + SONGPOS %1 + SONGPOS %1 + + +- ++ + %1 BPM + %1 BPM + + +- +- ++ ++ + Playing "%1"... + Riproduzione "%1"... + +diff --git a/src/translations/qtractor_ja.ts b/src/translations/qtractor_ja.ts +index 5ecb80d..e3dd3c6 100644 +--- a/src/translations/qtractor_ja.ts ++++ b/src/translations/qtractor_ja.ts +@@ -33,16 +33,16 @@ Audio: %1 channels, %2 Hz + + + +- +- ++ ++ + + + %1 In + %1 入力 + + +- +- ++ ++ + + + %1 Out +@@ -584,15 +584,15 @@ MIDI: + %1 ボリューム + + +- +- ++ ++ + + %1 Gain + %1 ゲイン + + +- +- ++ ++ + + + +@@ -5498,7 +5498,7 @@ Sorry. + + + +- ++ + Untitled%1 + タイトルなし%1 + +@@ -5870,7 +5870,7 @@ Saving into another session file is highly recommended. + 別なセッションファイルに保存してください。 + + +- ++ + The audio engine buffer size has changed, + increased from %1 to %2 frames/period. + +@@ -6070,32 +6070,32 @@ ALSAシーケンサーカーネルモジュール (snd-seq-midi) が + エラー + + +- ++ + XRUN(%1 skipped) + XRUN(%1スキップ) + + +- ++ + XRUN(%1): some frames might have been lost. + XRUN(%1): いくつかのフレームが失われました。 + + +- ++ + Audio connections change. + 音声接続が変更されました。 + + +- ++ + MIDI connections change. + MIDI接続が変更されました。 + + +- ++ + Playing ended. + 再生が終了しました。 + + +- ++ + The audio engine has been shutdown. + + Make sure the JACK audio server (jackd) +@@ -6106,120 +6106,120 @@ JACK音声サーバー(jackd)が起動し実行されているなら + セッションを再始動してください。 + + +- +- ++ ++ + STOP + 停止 + + +- ++ + PLAY + 再生 + + +- ++ + FFWD + 進む + + +- ++ + REW + 戻る + + +- ++ + REC ON + 録音オン + + +- ++ + REC OFF + 録音オフ + + +- ++ + RESET + リセット + + +- ++ + LOCATE %1 + + + +- ++ + SHUTTLE %1 + + + +- ++ + STEP %1 + ステップ %1 + + +- ++ + TRACK RECORD %1 %2 + + + +- ++ + TRACK MUTE %1 %2 + トラック ミュート %1 %2 + + +- ++ + TRACK SOLO %1 %2 + トラック ソロ %1 %2 + + +- ++ + Unknown sub-command + 不明なサブコマンドです + + +- +- ++ ++ + Not implemented + 実装されていません + + +- ++ + MIDI CTL: %1, Channel %2, Param %3, Value %4 + MIDI コントロール: %1, チャンネル %2, パラメーター %3, 値 %4 + + +- ++ + (track %1, gain %2) + (トラック %1, ゲイン %2) + + +- ++ + (track %1, panning %2) + (トラック %1, パン %2) + + +- ++ + START + 開始 + + +- ++ + CONTINUE + 継続 + + +- ++ + SONGPOS %1 + ソングポジション %1 + + +- ++ + %1 BPM + + + +- +- ++ ++ + Playing "%1"... + "%1"を再生... + +diff --git a/src/translations/qtractor_pt.ts b/src/translations/qtractor_pt.ts +index 5487b0e..9c40116 100644 +--- a/src/translations/qtractor_pt.ts ++++ b/src/translations/qtractor_pt.ts +@@ -40,8 +40,8 @@ + %1 Hz + + +- +- ++ ++ + + + %1 In +@@ -125,8 +125,8 @@ Fim: %3 Tamanho: %4 + Mostrar informações da versão + + +- +- ++ ++ + + + +@@ -134,8 +134,8 @@ Fim: %3 Tamanho: %4 + %1 Panorâmico + + +- +- ++ ++ + + + %1 Out +@@ -469,8 +469,8 @@ MIDI: + %1 (%2) + + +- +- ++ ++ + + %1 Gain + %1 Ganho +@@ -2622,7 +2622,7 @@ Você quer mesmo manter as modificações? + GRAVAR + + +- ++ + REW + RETROCEDER + +@@ -2669,7 +2669,7 @@ Você quer mesmo manter as modificações? + &Recortar + + +- ++ + FFWD + ANTERIOR + +@@ -2704,7 +2704,7 @@ Você quer mesmo manter as modificações? + Loop + + +- ++ + PLAY + TOCAR + +@@ -2763,7 +2763,7 @@ Você quer mesmo manter as modificações? + Suporte nativo a X11 para Interface de Plugin LV2 desativado. + + +- ++ + The audio engine buffer size has changed, + increased from %1 to %2 frames/period. + +@@ -2775,8 +2775,8 @@ aumente de %1 para %2 amostras/período. + a sessão atual. + + +- +- ++ ++ + STOP + PARAR + +@@ -3142,7 +3142,7 @@ Deseja salvar essas modificações? + Colar + + +- ++ + RESET + RESETAR + +@@ -3162,7 +3162,7 @@ Deseja salvar essas modificações? + Limites + + +- ++ + START + COMEÇAR + +@@ -3264,7 +3264,7 @@ Deseja salvar essas modificações? + + + +- ++ + Untitled%1 + Sem nome%1 + +@@ -3300,7 +3300,7 @@ Deseja salvar essas modificações? + Barra de Ferramentas para Tempo + + +- ++ + TRACK MUTE %1 %2 + MUTAR PISTAS %1 %2 + +@@ -3561,7 +3561,7 @@ Desculpe. + Limites da sequência + + +- ++ + %1 BPM + %1 BPM + +@@ -3581,7 +3581,7 @@ Desculpe. + &Limpar + + +- ++ + XRUN(%1 skipped) + XRUN(%1 ignorado(s)) + +@@ -3888,7 +3888,7 @@ Desculpe. + Selecionar a primeira tentativa da sequência atual + + +- ++ + Audio connections change. + Conexões de áudio modificadas. + +@@ -3944,12 +3944,12 @@ Desculpe. + Diminuir eventos em uma sequência + + +- ++ + Playing ended. + Reprodução completada. + + +- ++ + CONTINUE + CONTINUAR + +@@ -4161,7 +4161,7 @@ Desculpe. + Modificar mapa de tempo da sessão / marcadores + + +- ++ + The audio engine has been shutdown. + + Make sure the JACK audio server (jackd) +@@ -4207,7 +4207,7 @@ está rodando e reinicie a sessão. + Pâ&nico + + +- ++ + REC ON + GRAVAR LIGADO + +@@ -4451,7 +4451,7 @@ está rodando e reinicie a sessão. + Quebrar sequência atual no início da reprodução + + +- ++ + Unknown sub-command + Sub-comando desconhecido + +@@ -4461,7 +4461,7 @@ está rodando e reinicie a sessão. + Suporte a VeSTige header habilitado. + + +- ++ + REC OFF + GRAVAR DESLIGADO + +@@ -4497,7 +4497,7 @@ está rodando e reinicie a sessão. + &Saídas + + +- ++ + TRACK RECORD %1 %2 + GRAVAR PISTAS %1 %2 + +@@ -4533,7 +4533,7 @@ está rodando e reinicie a sessão. + Adicionar uma nova pista à sessão + + +- ++ + TRACK SOLO %1 %2 + PISTAS SOLO %1 %2 + +@@ -4623,7 +4623,7 @@ está rodando e reinicie a sessão. + Nenhuma Pista + + +- ++ + LOCATE %1 + LOCALIZAR %1 + +@@ -4693,7 +4693,7 @@ está rodando e reinicie a sessão. + XRUN + + +- ++ + (track %1, panning %2) + (pista %1, panorâmico %2) + +@@ -4798,7 +4798,7 @@ Desculpe. + Re&troceder + + +- ++ + STEP %1 + PASSO %1 + +@@ -5044,7 +5044,7 @@ Deseja subtituí-lo? + Salvar como + + +- ++ + SHUTTLE %1 + RETRIBUIR %1 + +@@ -5249,7 +5249,7 @@ de sessão é altamente recomendado. + Mapa de Tempo / Marcadores + + +- ++ + (track %1, gain %2) + (pista %1, ganho %2) + +@@ -5482,7 +5482,7 @@ de sessão é altamente recomendado. + Marcar pista que estiver selecionada + + +- ++ + SONGPOS %1 + POSIÇÃO DA MÚSICA %1 + +@@ -5765,8 +5765,8 @@ Desculpe. + &Propriedades... + + +- +- ++ ++ + Playing "%1"... + Tocando "%1"... + +@@ -5911,7 +5911,7 @@ Posso continuar? + Importar Arquivo MIDI + + +- ++ + MIDI connections change. + Conexões MIDI modificadas. + +@@ -5931,8 +5931,8 @@ Posso continuar? + Log&arítmico + + +- +- ++ ++ + Not implemented + Não implementado + +@@ -6002,7 +6002,7 @@ Posso continuar? + Tempo atual (reprodução) + + +- ++ + MIDI CTL: %1, Channel %2, Param %3, Value %4 + MIDI CTL: %1, Canal %2, Param %3, Valor %4 + +@@ -6198,7 +6198,7 @@ Posso continuar? + &Instrumentos... + + +- ++ + XRUN(%1): some frames might have been lost. + XRUN(%1): algumas amostras podem ter sido perdidas. + +diff --git a/src/translations/qtractor_ru.ts b/src/translations/qtractor_ru.ts +index fda60fc..6f66ac4 100644 +--- a/src/translations/qtractor_ru.ts ++++ b/src/translations/qtractor_ru.ts +@@ -33,16 +33,16 @@ Audio: %1 channels, %2 Hz + + + +- +- ++ ++ + + + %1 In + Вход %1 + + +- +- ++ ++ + + + %1 Out +@@ -629,15 +629,15 @@ Track: "%1" Input: "%2" Output: "%3" + + + +- +- ++ ++ + + %1 Gain + + + +- +- ++ ++ + + + +@@ -2537,7 +2537,7 @@ Sorry. + + + +- ++ + Untitled%1 + + +@@ -2792,7 +2792,7 @@ Do you want to crash-recover from it? + Нет + + +- ++ + The audio engine has been shutdown. + + Make sure the JACK audio server (jackd) +@@ -2804,114 +2804,114 @@ JACK (jackd) запущен и функционирует, + а затем начните сеанс заново. + + +- +- ++ ++ + STOP + СТОП + + +- ++ + PLAY + + + +- ++ + FFWD + + + +- ++ + REW + + + +- ++ + REC ON + ЗАП ВКЛ + + +- ++ + REC OFF + ЗАП ВЫКЛ + + +- ++ + RESET + СБРОС + + +- ++ + LOCATE %1 + + + +- ++ + SHUTTLE %1 + + + +- ++ + STEP %1 + ШАГ %1 + + +- ++ + TRACK RECORD %1 %2 + + + +- ++ + TRACK MUTE %1 %2 + + + +- ++ + TRACK SOLO %1 %2 + + + +- ++ + Unknown sub-command + + + +- +- ++ ++ + Not implemented + Не реализовано + + +- ++ + MIDI CTL: %1, Channel %2, Param %3, Value %4 + + + +- ++ + %1 BPM + + + +- ++ + (track %1, gain %2) + (дорожка %1, усиление %2) + + +- ++ + (track %1, panning %2) + (дорожка %1, панорамирование %2) + + +- ++ + START + + + +- ++ + CONTINUE + ПРОДОЛЖИТЬ + + +- ++ + SONGPOS %1 + + +@@ -3208,32 +3208,32 @@ Saving into another session file is highly recommended. + Ошибка + + +- ++ + XRUN(%1 skipped) + + + +- ++ + XRUN(%1): some frames might have been lost. + + + +- ++ + Audio connections change. + Смена звуковых соединений + + +- ++ + MIDI connections change. + Смена соединений MIDI + + +- ++ + Playing ended. + Воспроизведение завершено + + +- ++ + The audio engine buffer size has changed, + increased from %1 to %2 frames/period. + +@@ -3242,8 +3242,8 @@ is highly recommended. + + + +- +- ++ ++ + Playing "%1"... + Воспроизводится "%1"... + +-- +2.9.5 + diff --git a/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb b/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb index 1eb13801..bce57957 100644 --- a/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb +++ b/recipes-misc/recipes-multimedia/qtractor/qtractor_0.8.4.bb @@ -24,10 +24,10 @@ SRC_URI = " \ file://0003-do-nor-try-run-for-suil-libs-detection.patch \ file://0004-Add-ARM-NEON-acceleration-for-time-stretch-not-yet-t.patch \ file://0005-Add-qtractorPluginListDocument-to-save-load-plugin-l.patch \ - file://0006-qtractorImportTrackCommand-extend-to-accept-qtractor.patch \ - file://0007-Add-MIDI-import-options-to-create-ready-to-play-sess.patch \ + file://0006-Add-MIDI-import-options-to-create-ready-to-play-sess.patch \ + file://0007-Hold-defer-JACK-timebase-reset-on-main-time-scale-up.patch \ " -SRCREV = "3de003e171a605e7fb4dab761f3943727d8cad05" +SRCREV = "9e4fe9fecbd8d8161c40b77924e834f139597a83" PV = "0.8.4+git${SRCPV}" S = "${WORKDIR}/git" @@ -40,6 +40,6 @@ EXTRA_OECONF = " \ " FILES_${PN} += " \ - ${datadir}/mime \ - ${datadir}/metainfo \ + ${datadir}/mime \ + ${datadir}/metainfo \ "