361#include "portaudio.h"
366#define ROUND(x) (int) ((x)+0.5)
377#if defined( __APPLE__ )
379#include <mach/mach_time.h>
382static double machSecondsConversionScaler_ = 0.0;
386 mach_timebase_info_data_t info;
387 kern_return_t err = mach_timebase_info( &info );
389 machSecondsConversionScaler_ = 1e-9 * (double) info.numer / (
double) info.denom;
394 return mach_absolute_time() * machSecondsConversionScaler_;
397#elif defined( __WXMSW__ )
399#include "profileapi.h"
400#include "sysinfoapi.h"
407 LARGE_INTEGER ticksPerSecond;
409 if( QueryPerformanceFrequency( &ticksPerSecond ) != 0 )
439 QueryPerformanceCounter( &time );
444 #if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
445 return GetTickCount64() * .001;
447 return timeGetTime() * .001;
452#elif defined(HAVE_CLOCK_GETTIME)
459 clock_gettime(CLOCK_REALTIME, &tp);
460 return (PaTime)(tp.tv_sec + tp.tv_nsec * 1e-9);
470 gettimeofday( &tv, NULL );
471 return (PaTime) tv.tv_usec * 1e-6 + tv.tv_sec;
492#if defined(CLOCK_MONOTONIC_RAW)
493 clock_gettime(CLOCK_MONOTONIC_RAW, &now);
495 clock_gettime(CLOCK_REALTIME, &now);
501 static_cast<void>(usingAlsa);
507bool MIDIPlay::mMidiStreamActive =
false;
508bool MIDIPlay::mMidiOutputComplete =
true;
511 [](
const auto &playbackSchedule){
512 return std::make_unique<MIDIPlay>(playbackSchedule);
517 : mPlaybackSchedule{ schedule }
519#ifdef AUDIO_IO_GB_MIDI_WORKAROUND
525 PmError pmErr = Pm_Initialize();
527 if (pmErr != pmNoError) {
529 XO(
"There was an error initializing the midi i/o layer.\n");
530 errStr +=
XO(
"You will not be able to play midi.\n\n");
531 wxString pmErrStr =
LAT1CTOWX(Pm_GetErrorText(pmErr));
532 if (!pmErrStr.empty())
533 errStr +=
XO(
"Error: %s").Format( pmErrStr );
541 .ButtonStyle(Button::Ok)
542 .IconStyle(Icon::Error));
554 const PaStreamInfo* info,
double,
double rate)
557 for (
const auto &pSequence :
tracks.otherPlayableSequences)
558 if (
const auto pNoteTrack =
559 dynamic_cast<const NoteTrack *
>(pSequence.get())
562 pNoteTrack->SharedPointer<
const NoteTrack>());
592 bool successMidi =
true;
626 double startTime,
double offset,
bool send )
627 : mPlaybackSchedule{ schedule }
628 , mMIDIPlay{ midiPlay }
633 for (
auto &t : midiPlaybackTracks) {
634 Alg_seq_ptr seq = &t->GetSeq();
638 seq->set_in_use(
true);
639 const void *cookie = t.get();
642 const_cast<void*
>(cookie), t->GetStartTime() + offset);
644 Prime(send, startTime + offset);
697 PmDeviceID playbackDevice = Pm_GetDefaultOutputDeviceID();
700 if (wxStrcmp(playbackDeviceName,
wxT(
"")) != 0) {
701 for (i = 0; i < Pm_CountDevices(); i++) {
702 const PmDeviceInfo *info = Pm_GetDeviceInfo(i);
704 if (!info->output)
continue;
705 wxString interf = wxSafeConvertMB2WX(info->interf);
706 wxString
name = wxSafeConvertMB2WX(info->name);
707 interf.Append(
wxT(
": ")).Append(
name);
708 if (wxStrcmp(interf, playbackDeviceName) == 0) {
714 if (playbackDevice < 0)
761 using namespace std::chrono;
762 std::this_thread::sleep_for(1ms);
770 for (
int i = 0; i < nTracks; i++) {
772 Alg_seq_ptr seq = &t->GetSeq();
773 seq->set_in_use(
false);
792 return time + pauseTime;
800 const bool channelIsMute = hasSolo
803 return !channelIsMute;
816 double time = eventTime + 0.0005 -
823 if (time < 0 || midiStateOnly)
866 const bool sendIt = [&]{
878 if (
mNextEvent->is_note() && !midiStateOnly) {
886 data2 = (data2 < 1 ? 1 : (data2 > 127 ? 127 : data2));
888 it.request_note_off();
890#ifdef AUDIO_IO_GB_MIDI_WORKAROUND
896#ifdef AUDIO_IO_GB_MIDI_WORKAROUND
898 auto iter = std::find(
909 Alg_update_ptr update =
static_cast<Alg_update_ptr
>(
mNextEvent);
910 const char *
name = update->get_attribute();
912 if (!strcmp(
name,
"programi")) {
914 data1 = update->parameter.i;
917 }
else if (!strncmp(
name,
"control", 7)) {
922 data1 = atoi(
name + 7);
924 data2 =
ROUND(update->parameter.r * 127);
926 }
else if (!strcmp(
name,
"bendr")) {
930 int temp =
ROUND(0x2000 * (update->parameter.r + 1));
931 if (temp > 0x3fff) temp = 0x3fff;
932 if (temp < 0) temp = 0;
936 }
else if (!strcmp(
name,
"pressurer")) {
938 data1 = (int) (update->parameter.r * 127);
939 if (update->get_identifier() < 0) {
946 data1 = update->get_identifier();
957 Pm_Message((
int) (command + channel),
958 (
long) data1, (
long) data2));
992 double rate,
unsigned long pauseFrames,
bool paused,
bool hasSolo)
1031 return pauseFrames / rate;
1055 (1000 * (now + 1.0005 -
1066 bool doDelay = !looping;
1068 bool doDelay =
false;
1069 static_cast<void>(looping);
1078#ifdef AUDIO_IO_GB_MIDI_WORKAROUND
1100 0x90 + pair.first, pair.second, 0));
1108 for (
int chan = 0; chan < 16; chan++) {
1111 Pm_Message(0xB0 + chan, 0x7B, 0));
1117 const PaStreamCallbackTimeInfo *timeInfo,
1118 unsigned long framesPerBuffer
1145 const auto increase =
1175 (timeInfo->outputBufferDacTime - timeInfo->currentTime);
1194 return std::count_if(
1196 [](
const auto &pTrack){ return pTrack->GetSolo(); } );
1205bool MIDIPlay::IsActive()
1207 return ( mMidiStreamActive && !mMidiOutputComplete );
1210bool MIDIPlay::IsOtherStreamActive()
const
1220 wxT(
"MIDI Device Info")
StringSetting AudioIOHost
Toolkit-neutral facade for basic user interface services.
std::vector< std::shared_ptr< const NoteTrack > > NoteTrackConstArray
Inject added MIDI playback capability into Audacity's audio engine.
IntSetting MIDISynthLatency_ms
wxString GetMIDIDeviceInfo()
StringSetting MIDIPlaybackDevice
Callbacks that AudioIO uses, to synchronize audio and MIDI playback.
A Track that is used for Midi notes. (Somewhat old code).
bool IsVisibleChan(int c) const
float GetVelocity() const
virtual bool Looping(const PlaybackSchedule &schedule) const
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined */
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
static double util_GetTime(void)
static double secondsPerTick_
static double SystemTime(bool usingAlsa)
static struct anonymous_namespace{MIDIPlay.cpp}::InitializeTime initializeTime
static double streamStartTime
PmTimestamp MidiTime(void *pInfo)
static int usePerformanceCounter_
@ MIDI_MINIMAL_LATENCY_MS
AudioIOExt::RegisteredFactory sMIDIPlayFactory
const char * end(const char *str) noexcept
Typically statically constructed.
MessageBoxOptions && Caption(TranslatableString caption_) &&
double mT0
Playback starts at offset of mT0, which is measured in seconds.
double mT1
Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 duri...
const BoundedEnvelope * mEnvelope
double RealDuration(double trackTime1) const
PlaybackPolicy & GetPolicy()
bool Unmuted(bool hasSolo) const
Iterator(const PlaybackSchedule &schedule, MIDIPlay &midiPlay, NoteTrackConstArray &midiPlaybackTracks, double startTime, double offset, bool send)
NoteTrack * mNextEventTrack
Track of next event.
const PlaybackSchedule & mPlaybackSchedule
double GetNextEventTime() const
double UncorrectedMidiEventTime(double pauseTime)
bool mNextIsNoteOn
Is the next event a note-on?
bool OutputEvent(double pauseTime, bool sendMidiState, bool hasSolo)
void Prime(bool send, double startTime)
Alg_event * mNextEvent
The next event to play (or null)
double mSystemMinusAudioTime
bool StartOtherStream(const TransportSequences &tracks, const PaStreamInfo *info, double startTime, double rate) override
PmTimestamp MidiTime()
Compute the current PortMidi timestamp time.
void SignalOtherCompletion() override
void AbortOtherStream() override
PmTimestamp mMaxMidiTimestamp
std::optional< Iterator > mIterator
std::vector< std::pair< int, int > > mPendingNotesOff
NoteTrackConstArray mMidiPlaybackTracks
void StopOtherStream() override
void ComputeOtherTimings(double rate, bool paused, const PaStreamCallbackTimeInfo *timeInfo, unsigned long framesPerBuffer) override
static bool mMidiStreamActive
mMidiStreamActive tells when mMidiStream is open for output
const PlaybackSchedule & mPlaybackSchedule
long mSynthLatency
Latency of MIDI synthesizer.
void PrepareMidiIterator(bool send, double startTime, double offset)
long mCallbackCount
number of callbacks since stream start
bool StartPortMidiStream(double rate)
double mSystemMinusAudioTimePlusLatency
static bool mMidiOutputComplete
True when output reaches mT1.
int mMidiLoopPasses
total of backward jumps
long mAudioFramesPerBuffer
double AudioTime(double rate) const
void AllNotesOff(bool looping=false)
long mNumFrames
Number of frames output, including pauses.
double PauseTime(double rate, unsigned long pauseFrames)
void FillOtherBuffers(double rate, unsigned long pauseFrames, bool paused, bool hasSolo) override
unsigned CountOtherSolo() const override