17#include <wx/wxcrtvararg.h>
24#define ROUND(x) ((int) ((x) + 0.5))
35#define SON_AutoSave 67
36#define SON_ModifyState 60
37#define SON_NoteBackground 72
38#define SON_NoteForeground 74
39#define SON_Measures 76
40#define SON_Serialize 77
41#define SON_Unserialize 79
46bool sonificationStarted =
false;
50 PmError err = Pm_OpenOutput(&sonMidiStream, Pm_GetDefaultOutputDeviceID(),
51 NULL, 0, NULL, NULL, 0);
52 if (err) sonMidiStream = NULL;
54 Pm_WriteShort(sonMidiStream, 0, Pm_Message(0xC0, SON_PROGRAM, 0));
55 sonificationStarted =
true;
61 if (sonMidiStream) Pm_Close(sonMidiStream);
62 sonificationStarted =
false;
68void SonifyNoteOnOff(
int p,
int v)
70 if (!sonificationStarted)
73 Pm_WriteShort(sonMidiStream, 0, Pm_Message(0x90, p, v));
77 void SonifyBegin ## name() { SonifyNoteOnOff(SON_ ## name, SON_VEL); } \
78 void SonifyEnd ## name() { SonifyNoteOnOff(SON_ ## name, 0); }
94 : mpTrack{ track.SharedPointer<const
NoteTrack>() }
101 return mpTrack->mOrigin;
106 return Start() + mpTrack->GetSeq().get_real_dur();
114std::shared_ptr<ChannelInterval>
118 return std::make_shared<ChannelInterval>();
140 auto result =
tracks.Add( std::make_shared<NoteTrack>());
141 result->AttachedTrackObjects::BuildAll();
162 mSeq = std::make_unique<Alg_seq>();
164 std::unique_ptr<Alg_track> alg_track
165 { Alg_seq::unserialize
167 wxASSERT(alg_track->get_type() ==
's');
168 mSeq.reset(
static_cast<Alg_seq*
>(alg_track.release()) );
182 auto duplicate = std::make_shared<NoteTrack>();
183 duplicate->Init(*
this);
192 mSeq->serialize(&buffer,
193 &duplicate->mSerializationLength);
194 duplicate->mSerializationBuffer.reset( (
char*)buffer );
201 duplicate->mSerializationBuffer.reset
203 memcpy( duplicate->mSerializationBuffer.get(),
204 this->mSerializationBuffer.get(), this->mSerializationLength );
226 seq.convert_to_seconds();
229 if (t1 > seq.get_dur()) {
231 if (t0 >= t1)
return;
233 Alg_iterator iter(
mSeq.get(),
false);
236 while (0 != (event = iter.next()) && event->time < t1) {
237 if (event->is_note() && event->time >= t0) {
238 event->set_pitch(event->get_pitch() + semitones);
243 seq.convert_to_beats();
244 Alg_time_map_ptr map = seq.get_time_map();
245 map->insert_beat(t0, map->time_to_beat(t0));
246 map->insert_beat(t1, map->time_to_beat(t1));
247 int i, len = map->length();
248 for (i = 0; i < len; i++) {
249 Alg_beat &beat = map->beats[i];
250 beat.time = warper.
Warp(beat.time + offset) - offset;
253 seq.convert_to_seconds();
258 mSeq = std::move(seq);
265 debugOutput = fopen(
"debugOutput.txt",
"wt");
266 wxFprintf(debugOutput,
"Importing MIDI...\n");
273 while(i < mSeq->length()) {
274 wxFprintf(debugOutput,
"--\n");
275 wxFprintf(debugOutput,
"type: %c\n",
276 ((Alg_event_ptr)
mSeq->track_list.tracks[i])->get_type());
277 wxFprintf(debugOutput,
"time: %f\n",
278 ((Alg_event_ptr)
mSeq->track_list.tracks[i])->time);
279 wxFprintf(debugOutput,
"channel: %li\n",
280 ((Alg_event_ptr)
mSeq->track_list.tracks[i])->chan);
282 if(((Alg_event_ptr)
mSeq->track_list.tracks[i])->get_type() ==
wxT(
'n'))
284 wxFprintf(debugOutput,
"pitch: %f\n",
285 ((Alg_note_ptr)
mSeq->track_list.tracks[i])->pitch);
286 wxFprintf(debugOutput,
"duration: %f\n",
287 ((Alg_note_ptr)
mSeq->track_list.tracks[i])->dur);
288 wxFprintf(debugOutput,
"velocity: %f\n",
289 ((Alg_note_ptr)
mSeq->track_list.tracks[i])->loud);
291 else if(((Alg_event_ptr)
mSeq->track_list.tracks[i])->get_type() ==
wxT(
'n'))
293 wxFprintf(debugOutput,
"key: %li\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->get_identifier());
294 wxFprintf(debugOutput,
"attribute type: %c\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_type());
295 wxFprintf(debugOutput,
"attribute: %s\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_name());
297 if(((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_type() ==
wxT(
'r'))
299 wxFprintf(debugOutput,
"value: %f\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.r);
301 else if(((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_type() ==
wxT(
'i')) {
302 wxFprintf(debugOutput,
"value: %li\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.i);
304 else if(((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_type() ==
wxT(
's')) {
305 wxFprintf(debugOutput,
"value: %s\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.s);
314 wxFprintf(debugOutput,
"No sequence defined!\n");
330 auto newTrack = std::make_shared<NoteTrack>();
332 newTrack->Init(*
this);
335 seq.convert_to_seconds();
336 newTrack->mSeq.reset(seq.cut(t0 -
mOrigin, len,
false));
357 auto newTrack = std::make_shared<NoteTrack>();
359 newTrack->Init(*
this);
362 seq.convert_to_seconds();
363 newTrack->mSeq.reset(seq.copy(t0 -
mOrigin, len,
false));
382 seq.convert_to_seconds();
384 seq.clear(t1 -
mOrigin, seq.get_dur() + 10000.0,
false);
386 seq.clear(0.0, t0 -
mOrigin,
false);
407 auto start = t0 - offset;
412 seq.clear(0, len + start,
false);
422 seq.clear(start, len,
false);
453 auto offset = other.mOrigin;
455 seq.convert_to_seconds();
456 seq.insert_silence(t -
mOrigin, offset);
468 seq.paste(t -
mOrigin, &other.GetSeq());
488 seq.convert_to_seconds();
492 seq.silence(t0 -
mOrigin, len,
false);
501 seq.convert_to_seconds();
502 seq.insert_silence(t -
mOrigin, len);
518 mVelocity.store(velocity, std::memory_order_relaxed);
528 seq.convert_to_beats();
530 double tempo = seq.get_tempo(0.0);
531 double beats_per_measure = seq.get_bar_len(0.0);
532 int m =
ROUND(t * tempo / beats_per_measure);
536 tempo = beats_per_measure * m / t;
537 seq.insert_silence(0.0, beats_per_measure * m);
538 seq.set_tempo(tempo * 60.0 , 0.0, beats_per_measure * m);
539 seq.write(
"afterShift.gro");
542 seq.convert_to_seconds();
543 seq.clear(0, t,
true);
553 double seq_time = time -
mOrigin;
556 seq_time = seq.nearest_beat_time(seq_time, &beat);
558 return { seq_time +
mOrigin, beat };
564 {
"note",
"midi",
XO(
"Note Track") },
true,
581 auto pNewTrack = std::make_shared<NoteTrack>();
582 pNewTrack->Init(*
this);
583 pNewTrack->Paste(0.0, *
this);
593std::shared_ptr<WideChannelGroupInterval>
598 return std::make_shared<Interval>(*
this);
607 seq.set_real_dur( seq.get_real_dur() + delta );
609 seq.convert_to_seconds();
610 seq.set_dur( seq.get_dur() + delta );
618 bool result = seq.stretch_region( t0.second, t1.second, newDur );
620 const auto oldDur = t1.first - t0.first;
628 void swap(std::unique_ptr<Alg_seq> &a, std::unique_ptr<Alg_seq> &b)
630 std::unique_ptr<Alg_seq> tmp = std::move(a);
643 double start = -offset;
644 if (start < 0) start = 0;
648 auto seq = cleanup.get();
653 auto cleanup2 =
finally( [&] {
swap( this->
mSeq, cleanup ); } );
660 double beats_per_measure = 4.0;
661 Alg_time_sig_ptr tsp = NULL;
662 if (seq->time_sig.length() > 0 && seq->time_sig[0].beat < ALG_EPS) {
664 tsp = &(seq->time_sig[0]);
665 beats_per_measure = (tsp->num * 4) / tsp->den;
668 double bps = ALG_DEFAULT_BPM / 60;
669 Alg_time_map_ptr map = seq->get_time_map();
670 Alg_beat_ptr bp = &(map->beats[0]);
671 if (bp->time < ALG_EPS) {
672 if (map->beats.len > 1) {
673 bps = (map->beats[1].beat - map->beats[0].beat) /
674 (map->beats[1].time - map->beats[0].time);
675 }
else if (seq->get_time_map()->last_tempo_flag) {
676 bps = seq->get_time_map()->last_tempo;
681 double measure_time = beats_per_measure / bps;
682 int n =
ROUND(offset / measure_time);
685 measure_time = offset / n;
686 bps = beats_per_measure / measure_time;
688 seq->convert_to_beats();
689 seq->insert_silence(0, beats_per_measure * n);
692 seq->set_time_sig(0, tsp->num, tsp->den);
695 seq->set_tempo(bps * 60.0, 0, beats_per_measure * n);
705 double beat = mySeq.get_time_map()->time_to_beat(start);
707 int i = mySeq.time_sig.find_beat(beat);
712 if (mySeq.time_sig.length() > 0 &&
713 within(beat, mySeq.time_sig[i].beat, ALG_EPS)) {
718 }
else if (i == 0 && (mySeq.time_sig.length() == 0 ||
719 mySeq.time_sig[i].beat > beat)) {
722 double measures = beat / 4.0;
723 double imeasures =
ROUND(measures);
724 if (!
within(measures, imeasures, ALG_EPS)) {
725 double bar_offset = ((int)(measures) + 1) * 4.0 - beat;
726 seq->set_time_sig(bar_offset, 4, 4);
736 Alg_time_sig_ptr tsp = &(mySeq.time_sig[i]);
737 double beats_per_measure = (tsp->num * 4) / tsp->den;
738 double measures = (beat - tsp->beat) / beats_per_measure;
739 int imeasures =
ROUND(measures);
740 if (!
within(measures, imeasures, ALG_EPS)) {
744 double bar = tsp->beat + beats_per_measure * ((int)(measures) + 1);
745 double bar_offset = bar - beat;
749 seq->set_time_sig(bar_offset, tsp->num, tsp->den);
761 std::unique_ptr<Alg_seq> cleanup;
763 bool rslt = seq->smf_write(f.mb_str());
768 wxT(
"/FileFormats/AllegroStyleChoice"),
780 wxT(
"/FileFormats/AllegroStyle"),
789 seq.convert_to_seconds();
791 seq.convert_to_beats();
793 return seq.write(f.mb_str(), offset);
800 return (nValue >= 0 && nValue < (1 << 16));
806 if (tag ==
"notetrack") {
807 for (
auto pair : attrs)
809 auto attr = pair.first;
810 auto value = pair.second;
817 return attachment.HandleAttribute(pair);
822 else if (attr ==
"offset" && value.TryGet(dblValue))
824 else if (attr ==
"visiblechannels") {
825 if (!value.TryGet(nValue) ||
830 else if (attr ==
"velocity" && value.TryGet(dblValue))
832 else if (attr ==
"data") {
833 std::string s(value.ToWString());
834 std::istringstream data(s);
835 mSeq = std::make_unique<Alg_seq>(data,
false);
851 std::ostringstream data;
857 holder = Clone(
false);
858 saveme =
static_cast<NoteTrack*
>(holder.get());
860 saveme->
GetSeq().write(data,
true);
861 xmlFile.StartTag(
wxT(
"notetrack"));
862 saveme->Track::WriteCommonXMLAttributes( xmlFile );
864 xmlFile.WriteAttr(
wxT(
"offset"), saveme->
mOrigin);
865 xmlFile.WriteAttr(
wxT(
"visiblechannels"),
868 xmlFile.WriteAttr(
wxT(
"velocity"),
870 saveme->Attachments::ForEach([&](
auto &attachment){
871 attachment.WriteXML(xmlFile);
873 xmlFile.WriteAttr(
wxT(
"data"), wxString(data.str().c_str(), wxConvUTF8));
874 xmlFile.EndTag(
wxT(
"notetrack"));
878#include <wx/sstream.h>
879#include <wx/txtstrm.h>
886 wxStringOutputStream o;
887 wxTextOutputStream s(o, wxEOL_UNIX);
890 return XO(
"Stream is active ... unable to gather information.\n")
896 int recDeviceNum = Pm_GetDefaultInputDeviceID();
897 int playDeviceNum = Pm_GetDefaultOutputDeviceID();
898 int cnt = Pm_CountDevices();
901 wxLogDebug(
wxT(
"PortMidi reports %d MIDI devices"), cnt);
903 s <<
wxT(
"==============================\n");
904 s <<
XO(
"Default recording device number: %d\n").Format( recDeviceNum );
905 s <<
XO(
"Default playback device number: %d\n").Format( playDeviceNum );
912 s <<
XO(
"No devices found\n");
913 return o.GetString();
916 for (
int i = 0; i < cnt; i++) {
917 s <<
wxT(
"==============================\n");
919 const PmDeviceInfo* info = Pm_GetDeviceInfo(i);
921 s <<
XO(
"Device info unavailable for: %d\n").Format( i );
925 wxString
name = wxSafeConvertMB2WX(info->name);
926 wxString hostName = wxSafeConvertMB2WX(info->interf);
928 s <<
XO(
"Device ID: %d\n").Format( i );
929 s <<
XO(
"Device name: %s\n").Format(
name );
930 s <<
XO(
"Host name: %s\n").Format( hostName );
932 s <<
XO(
"Supports output: %d\n").Format( info->output );
934 s <<
XO(
"Supports input: %d\n").Format( info->input );
935 s <<
XO(
"Opened: %d\n").Format( info->opened );
937 if (
name == playDevice && info->output)
940 if (
name == recDevice && info->input)
945 if (recDeviceNum < 0 && info->input){
948 if (playDeviceNum < 0 && info->output){
953 bool haveRecDevice = (recDeviceNum >= 0);
954 bool havePlayDevice = (playDeviceNum >= 0);
956 s <<
wxT(
"==============================\n");
958 s <<
XO(
"Selected MIDI recording device: %d - %s\n").Format( recDeviceNum, recDevice );
960 s <<
XO(
"No MIDI recording device found for '%s'.\n").Format( recDevice );
963 s <<
XO(
"Selected MIDI playback device: %d - %s\n").Format( playDeviceNum, playDevice );
965 s <<
XO(
"No MIDI playback device found for '%s'.\n").Format( playDevice );
971 s <<
wxT(
"==============================\n");
972#ifdef EXPERIMENTAL_MIDI_IN
973 s <<
wxT(
"EXPERIMENTAL_MIDI_IN is enabled\n");
975 s <<
wxT(
"EXPERIMENTAL_MIDI_IN is NOT enabled\n");
980 return o.GetString();
const TranslatableString name
XXO("&Cut/Copy/Paste Toolbar")
MessageBoxException for violation of preconditions or assertions.
#define THROW_INCONSISTENCY_EXCEPTION
Throw InconsistencyException, using C++ preprocessor to identify the source code location.
IntSetting MIDISynthLatency_ms
StringSetting MIDIRecordingDevice
static ProjectFileIORegistry::ObjectReaderEntry readerEntry
wxString GetMIDIDeviceInfo()
StringSetting MIDIPlaybackDevice
static const Track::TypeInfo & typeInfo()
#define SonifyEndSerialize()
#define SonifyBeginSerialize()
#define SonifyEndSonification()
#define SonifyBeginSonification()
std::pair< double, double > QuantizedTimeAndBeat
Contains declarations for TimeWarper, IdentityTimeWarper, ShiftTimeWarper, LinearTimeWarper,...
std::function< void(double)> ProgressReporter
bool within(A a, B b, DIST d)
std::pair< std::string_view, XMLAttributeValueView > Attribute
std::vector< Attribute > AttributesList
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
static AudioIOBase * Get()
double GetEndTime() const
Get the maximum of End() values of intervals, or 0 when none.
Utility to register hooks into a host class that attach client data.
ClientData * FindIf(const Function &function)
Return pointer to first attachment in this that is not null and satisfies a predicate,...
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Specialization of Setting for int.
A Track that is used for Midi notes. (Somewhat old code).
void SetSequence(std::unique_ptr< Alg_seq > &&seq)
const TypeInfo & GetTypeInfo() const override
static const TypeInfo & ClassTypeInfo()
bool ExportAllegro(const wxString &f) const
Track::Holder Cut(double t0, double t1) override
Create tracks and modify this track.
void AddToDuration(double delta)
size_t NIntervals() const override
Report the number of intervals.
std::shared_ptr< WideChannelGroupInterval > DoGetInterval(size_t iInterval) override
Retrieve an interval.
std::unique_ptr< char[]> mSerializationBuffer
Track::Holder PasteInto(AudacityProject &project, TrackList &list) const override
bool ExportMIDI(const wxString &f) const
void MoveTo(double origin) override
Change start time to given time point.
std::atomic< float > mVelocity
Atomic because it may be read by worker threads in playback.
void SetVisibleChannels(unsigned value)
void WriteXML(XMLWriter &xmlFile) const override
void Paste(double t, const Track &src) override
Weak precondition allows overrides to replicate one channel into many.
float GetVelocity() const
void Silence(double t0, double t1, ProgressReporter reportProgress={}) override
std::unique_ptr< Alg_seq > mSeq
void InsertSilence(double t, double len) override
XMLTagHandler * HandleXMLChild(const std::string_view &tag) override
bool StretchRegion(QuantizedTimeAndBeat t0, QuantizedTimeAndBeat t1, double newDur)
Alg_seq * MakeExportableSeq(std::unique_ptr< Alg_seq > &cleanup) const
static EnumSetting< bool > AllegroStyleSetting
static NoteTrack * New(AudacityProject &project)
unsigned GetVisibleChannels() const
QuantizedTimeAndBeat NearestBeatTime(double time) const
void WarpAndTransposeNotes(double t0, double t1, const TimeWarper &warper, double semitones)
void SetVelocity(float velocity)
bool Trim(double t0, double t1)
Track::Holder Clone(bool backup) const override
bool HandleXMLTag(const std::string_view &tag, const AttributesList &attrs) override
void Clear(double t0, double t1) override
long mSerializationLength
void DoSetVelocity(float velocity)
Track::Holder Copy(double t0, double t1, bool forClipboard=true) const override
Create new tracks and don't modify this track.
bool HandleXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &value)
static const TypeInfo & ClassTypeInfo()
void WriteXMLAttributes(XMLWriter &xmlFile) const
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined */
Specialization of Setting for strings.
Transforms one point in time to another point. For example, a time stretching effect might use one to...
virtual double Warp(double originalTime) const =0
Abstract base class for an object holding data associated with points on a time axis.
void Notify(bool allChannels, int code=-1)
R TypeSwitch(const Functions &...functions)
std::shared_ptr< Track > Holder
bool HandleCommonXMLAttribute(const std::string_view &attr, const XMLAttributeValueView &valueView)
void SetName(const wxString &n)
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
static TrackList & Get(AudacityProject &project)
TrackKind * Add(const std::shared_ptr< TrackKind > &t, bool assignIds=true)
Generates overrides of channel-related functions.
This class is an interface which should be implemented by classes which wish to be able to load and s...
Base class for XMLFileWriter and XMLStringWriter that provides the general functionality for creating...
bool IsValidVisibleChannels(const int nValue)
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
std::string Serialize(const ProjectForm &form)
void copy(const T *src, T *dst, int32_t n)
Interval(const NoteTrack &track)
std::shared_ptr< ChannelInterval > DoGetChannel(size_t iChannel) override
Retrieve a channel.
double End() const override
double Start() const override
size_t NChannels() const override
Report the number of channels.
virtual void WriteXML(XMLWriter &xmlFile) const
Default implementation does nothing.
virtual bool HandleAttribute(const Attribute &attribute)
Return whether the attribute was used; default returns false.
~NoteTrackAttachment() override