Skip to content

Commit

Permalink
Merge pull request audacity#1637 from Paul-Licameli/Disentangle-MIDI-…
Browse files Browse the repository at this point in the history
…play-from-AudioIO

Disentangle midi play from audio io
  • Loading branch information
Paul-Licameli authored Sep 23, 2021
2 parents ec87140 + f891676 commit 819faf3
Show file tree
Hide file tree
Showing 24 changed files with 1,800 additions and 1,633 deletions.
129 changes: 16 additions & 113 deletions libraries/lib-audio-devices/AudioIOBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ Paul Licameli split from AudioIO.cpp
#include "portmixer.h"
#endif

#ifdef EXPERIMENTAL_MIDI_OUT
#include <portmidi.h>
#endif

int AudioIOBase::mCachedPlaybackIndex = -1;
std::vector<long> AudioIOBase::mCachedPlaybackRates;
int AudioIOBase::mCachedCaptureIndex = -1;
Expand Down Expand Up @@ -88,11 +84,15 @@ wxString AudioIOBase::HostName(const PaDeviceInfo* info)

std::unique_ptr<AudioIOBase> AudioIOBase::ugAudioIO;

AudioIOExtBase::~AudioIOExtBase() = default;

AudioIOBase *AudioIOBase::Get()
{
return ugAudioIO.get();
}

AudioIOBase::AudioIOBase() = default;

AudioIOBase::~AudioIOBase() = default;

void AudioIOBase::SetMixer(int inputSource)
Expand Down Expand Up @@ -358,10 +358,9 @@ bool AudioIOBase::IsStreamActive() const
if( mPortStreamV19 )
isActive = (Pa_IsStreamActive( mPortStreamV19 ) > 0);

#ifdef EXPERIMENTAL_MIDI_OUT
if( mMidiStreamActive && !mMidiOutputComplete )
isActive = true;
#endif
isActive = isActive ||
std::any_of(mAudioIOExt.begin(), mAudioIOExt.end(),
[](auto &pExt){ return pExt && pExt->IsOtherStreamActive(); });
return isActive;
}

Expand Down Expand Up @@ -705,7 +704,7 @@ int AudioIOBase::getRecordDevIndex(const wxString &devNameArg)
return deviceNum;
}

wxString AudioIOBase::GetDeviceInfo()
wxString AudioIOBase::GetDeviceInfo() const
{
wxStringOutputStream o;
wxTextOutputStream s(o, wxEOL_UNIX);
Expand Down Expand Up @@ -951,112 +950,16 @@ wxString AudioIOBase::GetDeviceInfo()
return o.GetString();
}

#ifdef EXPERIMENTAL_MIDI_OUT
// FIXME: When EXPERIMENTAL_MIDI_IN is added (eventually) this should also be enabled -- Poke
wxString AudioIOBase::GetMidiDeviceInfo()
auto AudioIOBase::GetAllDeviceInfo() -> std::vector<AudioIODiagnostics>
{
wxStringOutputStream o;
wxTextOutputStream s(o, wxEOL_UNIX);

if (IsStreamActive()) {
return XO("Stream is active ... unable to gather information.\n")
.Translation();
}


// XXX: May need to trap errors as with the normal device info
int recDeviceNum = Pm_GetDefaultInputDeviceID();
int playDeviceNum = Pm_GetDefaultOutputDeviceID();
int cnt = Pm_CountDevices();

// PRL: why only into the log?
wxLogDebug(wxT("PortMidi reports %d MIDI devices"), cnt);

s << wxT("==============================\n");
s << XO("Default recording device number: %d\n").Format( recDeviceNum );
s << XO("Default playback device number: %d\n").Format( playDeviceNum );

wxString recDevice = gPrefs->Read(wxT("/MidiIO/RecordingDevice"), wxT(""));
wxString playDevice = gPrefs->Read(wxT("/MidiIO/PlaybackDevice"), wxT(""));

// This gets info on all available audio devices (input and output)
if (cnt <= 0) {
s << XO("No devices found\n");
return o.GetString();
}

for (int i = 0; i < cnt; i++) {
s << wxT("==============================\n");

const PmDeviceInfo* info = Pm_GetDeviceInfo(i);
if (!info) {
s << XO("Device info unavailable for: %d\n").Format( i );
continue;
}

wxString name = wxSafeConvertMB2WX(info->name);
wxString hostName = wxSafeConvertMB2WX(info->interf);

s << XO("Device ID: %d\n").Format( i );
s << XO("Device name: %s\n").Format( name );
s << XO("Host name: %s\n").Format( hostName );
/* i18n-hint: Supported, meaning made available by the system */
s << XO("Supports output: %d\n").Format( info->output );
/* i18n-hint: Supported, meaning made available by the system */
s << XO("Supports input: %d\n").Format( info->input );
s << XO("Opened: %d\n").Format( info->opened );

if (name == playDevice && info->output)
playDeviceNum = i;

if (name == recDevice && info->input)
recDeviceNum = i;

// XXX: This is only done because the same was applied with PortAudio
// If PortMidi returns -1 for the default device, use the first one
if (recDeviceNum < 0 && info->input){
recDeviceNum = i;
}
if (playDeviceNum < 0 && info->output){
playDeviceNum = i;
}
}

bool haveRecDevice = (recDeviceNum >= 0);
bool havePlayDevice = (playDeviceNum >= 0);

s << wxT("==============================\n");
if (haveRecDevice)
s << XO("Selected MIDI recording device: %d - %s\n").Format( recDeviceNum, recDevice );
else
s << XO("No MIDI recording device found for '%s'.\n").Format( recDevice );

if (havePlayDevice)
s << XO("Selected MIDI playback device: %d - %s\n").Format( playDeviceNum, playDevice );
else
s << XO("No MIDI playback device found for '%s'.\n").Format( playDevice );

// Mention our conditional compilation flags for Alpha only
#ifdef IS_ALPHA

// Not internationalizing these alpha-only messages
s << wxT("==============================\n");
#ifdef EXPERIMENTAL_MIDI_OUT
s << wxT("EXPERIMENTAL_MIDI_OUT is enabled\n");
#else
s << wxT("EXPERIMENTAL_MIDI_OUT is NOT enabled\n");
#endif
#ifdef EXPERIMENTAL_MIDI_IN
s << wxT("EXPERIMENTAL_MIDI_IN is enabled\n");
#else
s << wxT("EXPERIMENTAL_MIDI_IN is NOT enabled\n");
#endif

#endif

return o.GetString();
std::vector<AudioIODiagnostics> result;
result.push_back({
wxT("audiodev.txt"), GetDeviceInfo(), wxT("Audio Device Info") });
for( auto &pExt : mAudioIOExt )
if ( pExt )
result.emplace_back(pExt->Dump());
return result;
}
#endif

StringSetting AudioIOHost{
L"/AudioIO/Host", L"" };
Expand Down
48 changes: 35 additions & 13 deletions libraries/lib-audio-devices/AudioIOBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ struct AudioIOStartStreamOptions
double * pStartTime;
double preRoll;

bool playNonWaveTracks{ true };

#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
// Non-null value indicates that scrubbing will happen
// (do not specify a time track, looping, or recording, which
Expand All @@ -111,6 +113,25 @@ struct AudioIOStartStreamOptions
std::function< unsigned long() > playbackStreamPrimer;
};

struct AudioIODiagnostics{
wxString filename; // For crash report bundle
wxString text; // One big string, may be localized
wxString description; // Non-localized short description
};

//! Abstract interface to alternative, concurrent playback with the main audio (such as MIDI events)
class AUDIO_DEVICES_API AudioIOExtBase
{
public:
virtual ~AudioIOExtBase();

// Formerly in AudioIOBase
virtual bool IsOtherStreamActive() const = 0;

//! Get diagnostic information for audio devices and also for extensions
virtual AudioIODiagnostics Dump() const = 0;
};

///\brief A singleton object supporting queries of the state of any active
/// audio streams, and audio device capabilities
class AUDIO_DEVICES_API AudioIOBase /* not final */
Expand All @@ -119,8 +140,12 @@ class AUDIO_DEVICES_API AudioIOBase /* not final */
public:
static AudioIOBase *Get();

AudioIOBase();
virtual ~AudioIOBase();

AudioIOBase(const AudioIOBase &) = delete;
AudioIOBase &operator=(const AudioIOBase &) = delete;

void SetCaptureMeter(
AudacityProject *project, const std::weak_ptr<Meter> &meter);
void SetPlaybackMeter(
Expand Down Expand Up @@ -205,12 +230,10 @@ class AUDIO_DEVICES_API AudioIOBase /* not final */
/** \brief Get diagnostic information on all the available audio I/O devices
*
*/
wxString GetDeviceInfo();
wxString GetDeviceInfo() const;

#ifdef EXPERIMENTAL_MIDI_OUT
/** \brief Get diagnostic information on all the available MIDI I/O devices */
wxString GetMidiDeviceInfo();
#endif
//! Get diagnostic information for audio devices and also for extensions
std::vector<AudioIODiagnostics> GetAllDeviceInfo();

/** \brief Find out if playback / recording is currently paused */
bool IsPaused() const;
Expand Down Expand Up @@ -261,12 +284,6 @@ class AUDIO_DEVICES_API AudioIOBase /* not final */
/// True if audio playback is paused
bool mPaused;

/// True when output reaches mT1
bool mMidiOutputComplete{ true };

/// mMidiStreamActive tells when mMidiStream is open for output
bool mMidiStreamActive;

volatile int mStreamToken;

/// Audio playback rate in samples per second
Expand Down Expand Up @@ -335,9 +352,12 @@ class AUDIO_DEVICES_API AudioIOBase /* not final */
static const int RatesToTry[];
/** \brief How many sample rates to try */
static const int NumRatesToTry;
};

#endif
/*! This class needs to iterate this array for one limited purpose but does
not populate it and does not give access to it except to subclasses
*/
std::vector<std::unique_ptr<AudioIOExtBase>> mAudioIOExt;
};

#include "Prefs.h"

Expand All @@ -349,3 +369,5 @@ extern AUDIO_DEVICES_API IntSetting AudioIORecordChannels;
extern AUDIO_DEVICES_API StringSetting AudioIORecordingDevice;
extern AUDIO_DEVICES_API StringSetting AudioIORecordingSource;
extern AUDIO_DEVICES_API IntSetting AudioIORecordingSourceIndex;

#endif
1 change: 0 additions & 1 deletion libraries/lib-audio-devices/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ set( SOURCES
)
set( LIBRARIES
PortAudio::PortAudio
PortMidi::PortMidi
portmixer
lib-preferences-interface
PRIVATE
Expand Down
12 changes: 7 additions & 5 deletions libraries/lib-utility/MemoryX.h
Original file line number Diff line number Diff line change
Expand Up @@ -363,13 +363,15 @@ ValueRestorer< T > valueRestorer( T& var, const T& newValue )
they cooperate correctly with stl algorithms and std::reverse_iterator
*/
template< typename Value, typename Category = std::forward_iterator_tag >
using ValueIterator = std::iterator<
Category, const Value, ptrdiff_t,
struct ValueIterator{
using iterator_category = Category;
using value_type = Value;
using difference_type = ptrdiff_t;
// void pointer type so that operator -> is disabled
void,
using pointer = void;
// make "reference type" really the same as the value type
const Value
>;
using reference = const Value;
};

/**
\brief A convenience for use with range-for
Expand Down
Loading

0 comments on commit 819faf3

Please sign in to comment.