changeset 714:fe268c16ae28

Make it possible to open an audio device for playback and upgrade it to I/O later on - avoiding the Mac microphone-permission dialog until we are actually wanting to record
author Chris Cannam
date Wed, 16 Oct 2019 15:26:59 +0100
parents ce698f8d0831
children ec8f21a20fa7
files framework/MainWindowBase.cpp framework/MainWindowBase.h
diffstat 2 files changed, 72 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/framework/MainWindowBase.cpp	Fri Oct 11 13:40:54 2019 +0100
+++ b/framework/MainWindowBase.cpp	Wed Oct 16 15:26:59 2019 +0100
@@ -137,13 +137,15 @@
 #undef Window
 #endif
 
-MainWindowBase::MainWindowBase(SoundOptions soundOptions,
+MainWindowBase::MainWindowBase(AudioMode audioMode,
+                               MIDIMode midiMode,
                                PaneStack::Options paneStackOptions) :
     m_document(nullptr),
     m_paneStack(nullptr),
     m_viewManager(nullptr),
     m_timeRulerLayer(nullptr),
-    m_soundOptions(soundOptions),
+    m_audioMode(audioMode),
+    m_midiMode(midiMode),
     m_playSource(nullptr),
     m_recordTarget(nullptr),
     m_resamplerWrapper(nullptr),
@@ -171,12 +173,6 @@
 
     SVDEBUG << "MainWindowBase::MainWindowBase" << endl;
 
-    if (m_soundOptions & WithAudioInput) {
-        if (!(m_soundOptions & WithAudioOutput)) {
-            SVCERR << "WARNING: MainWindowBase: WithAudioInput requires WithAudioOutput -- recording will not work" << endl;
-        }
-    }
-    
     qRegisterMetaType<sv_frame_t>("sv_frame_t");
     qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t");
     qRegisterMetaType<ModelId>("ModelId");
@@ -250,7 +246,8 @@
     m_playSource = new AudioCallbackPlaySource
         (m_viewManager, QApplication::applicationName());
 
-    if (m_soundOptions & WithAudioInput) {
+    if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
+        m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
         SVDEBUG << "MainWindowBase: Creating record target" << endl;
         m_recordTarget = new AudioCallbackRecordTarget
             (m_viewManager, QApplication::applicationName());
@@ -303,7 +300,7 @@
     m_labeller = new Labeller(labellerType);
     m_labeller->setCounterCycleSize(cycle);
 
-    if (m_soundOptions & WithMIDIInput) {
+    if (m_midiMode == MIDI_LISTEN) {
         SVDEBUG << "MainWindowBase: Creating MIDI input" << endl;
         m_midiInput = new MIDIInput(QApplication::applicationName(), this);
     }
@@ -739,12 +736,21 @@
     // system play target or I/O exists, we can record even if no
     // record source (i.e. audioIO) exists because we can record into
     // an empty session before the audio device has been
-    // opened. However, if there is no record *target* then recording
-    // was actively disabled (flag not set in m_soundOptions). And if
-    // we have a play target instead of an audioIO, then we must have
-    // tried to open the device but failed to find any capture source.
+    // opened.
+    //
+    // However, if there is no record *target* then recording was
+    // actively disabled via the audio mode setting.
+    // 
+    // If we have a play target instead of an audioIO, then if the
+    // audio mode is AUDIO_PLAYBACK_NOW_RECORD_LATER, we are still
+    // expecting to open the IO on demand, but if it is
+    // AUDIO_PLAYBACK_AND_RECORD then we must have tried to open the
+    // device and failed to find any capture source.
+    // 
     bool recordDisabled = (m_recordTarget == nullptr);
-    bool recordDeviceFailed = (m_playTarget != nullptr && m_audioIO == nullptr);
+    bool recordDeviceFailed = 
+        (m_audioMode == AUDIO_PLAYBACK_AND_RECORD &&
+         (m_playTarget != nullptr && m_audioIO == nullptr));
     emit canRecord(!recordDisabled && !recordDeviceFailed);
 }
 
@@ -2503,8 +2509,8 @@
 
     static AudioLogCallback audioLogCallback;
     breakfastquay::AudioFactory::setLogCallback(&audioLogCallback);
-    
-    if (!(m_soundOptions & WithAudioOutput)) return;
+
+    if (m_audioMode == AUDIO_NONE) return;
 
     QSettings settings;
     settings.beginGroup("Preferences");
@@ -2541,7 +2547,7 @@
 
     std::string errorString;
     
-    if (m_soundOptions & WithAudioInput) {
+    if (m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
         m_audioIO = breakfastquay::AudioFactory::
             createCallbackIO(m_recordTarget, m_resamplerWrapper,
                              preference, errorString);
@@ -2579,7 +2585,8 @@
             } else {
                 firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error);
             }
-            if (m_soundOptions & WithAudioInput) {
+            if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
+                m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
                 secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>");
             } else {
                 secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>");
@@ -2593,7 +2600,8 @@
             } else {
                 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error);
             }
-            if (m_soundOptions & WithAudioInput) {
+            if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER ||
+                m_audioMode == AUDIO_PLAYBACK_AND_RECORD) {
                 secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>");
             } else {
                 secondBit = tr("<p>Audio playback will not be available during this session.</p>");
@@ -3191,7 +3199,7 @@
 {
     QAction *action = qobject_cast<QAction *>(sender());
     
-    if (!(m_soundOptions & WithAudioInput)) {
+    if (m_audioMode == AUDIO_NONE || m_audioMode == AUDIO_PLAYBACK_ONLY) {
         if (action) action->setChecked(false);
         return;
     }
@@ -3201,6 +3209,14 @@
         return;
     }
 
+    if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER) {
+        SVDEBUG << "MainWindowBase::record: upgrading from "
+                << "AUDIO_PLAYBACK_NOW_RECORD_LATER to "
+                << "AUDIO_PLAYBACK_AND_RECORD" << endl;
+        m_audioMode = AUDIO_PLAYBACK_AND_RECORD;
+        deleteAudioIO();
+    }
+
     if (!m_audioIO) {
         SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
         createAudioIO();
@@ -4090,8 +4106,7 @@
     updateDescriptionLabel();
     auto model = ModelById::getAs<WaveFileModel>(modelId);
     if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
-    if (model && !(m_playTarget || m_audioIO) &&
-        (m_soundOptions & WithAudioOutput)) {
+    if (model && !(m_playTarget || m_audioIO) && (m_audioMode != AUDIO_NONE)) {
         createAudioIO();
     }
 }
--- a/framework/MainWindowBase.h	Fri Oct 11 13:40:54 2019 +0100
+++ b/framework/MainWindowBase.h	Wed Oct 16 15:26:59 2019 +0100
@@ -93,17 +93,39 @@
     Q_OBJECT
 
 public:
-    enum SoundOption {
-        WithAudioOutput = 0x01,
-        WithAudioInput  = 0x02,
-        WithMIDIInput   = 0x04,
-        WithEverything  = 0xff,
-        WithNothing     = 0x00
+    /**
+     * Determine what kind of audio device to open when the first
+     * model is loaded or record() is called.
+     */
+    enum AudioMode {
+
+        /// Open no audio device, ever
+        AUDIO_NONE,
+
+        /// Open for playback, never for recording
+        AUDIO_PLAYBACK_ONLY,
+
+        /// Open for playback when model loaded, switch to I/O if record called
+        AUDIO_PLAYBACK_NOW_RECORD_LATER,
+
+        /// Open for I/O as soon as model loaded or record called
+        AUDIO_PLAYBACK_AND_RECORD
     };
-    typedef int SoundOptions;
-    
-    MainWindowBase(SoundOptions soundOptions = WithEverything,
-                   PaneStack::Options paneStackOptions = 0x0);
+
+    /**
+     * Determine whether to open a MIDI input device.
+     */
+    enum MIDIMode {
+
+        /// Open no MIDI device
+        MIDI_NONE,
+        
+        /// Open a MIDI device and listen for MIDI input
+        MIDI_LISTEN
+    };
+
+    MainWindowBase(AudioMode audioMode, MIDIMode midiMode,
+                   PaneStack::Options paneStackOptions);
     virtual ~MainWindowBase();
     
     enum AudioFileOpenMode {
@@ -365,7 +387,8 @@
     ViewManager *m_viewManager;
     Layer *m_timeRulerLayer;
 
-    SoundOptions m_soundOptions;
+    AudioMode m_audioMode;
+    MIDIMode m_midiMode;
 
     AudioCallbackPlaySource *m_playSource;
     AudioCallbackRecordTarget *m_recordTarget;