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 );
232 seq.convert_to_seconds();
235 if (t1 > seq.get_dur()) {
237 if (t0 >= t1)
return;
239 Alg_iterator iter(
mSeq.get(),
false);
242 while (0 != (event = iter.next()) && event->time < t1) {
243 if (event->is_note() && event->time >= t0) {
244 event->set_pitch(event->get_pitch() + semitones);
249 seq.convert_to_beats();
250 Alg_time_map_ptr map = seq.get_time_map();
251 map->insert_beat(t0, map->time_to_beat(t0));
252 map->insert_beat(t1, map->time_to_beat(t1));
253 int i, len = map->length();
254 for (i = 0; i < len; i++) {
255 Alg_beat &beat = map->beats[i];
256 beat.time = warper.
Warp(beat.time + offset) - offset;
259 seq.convert_to_seconds();
264 mSeq = std::move(seq);
271 debugOutput = fopen(
"debugOutput.txt",
"wt");
272 wxFprintf(debugOutput,
"Importing MIDI...\n");
279 while(i < mSeq->length()) {
280 wxFprintf(debugOutput,
"--\n");
281 wxFprintf(debugOutput,
"type: %c\n",
282 ((Alg_event_ptr)
mSeq->track_list.tracks[i])->get_type());
283 wxFprintf(debugOutput,
"time: %f\n",
284 ((Alg_event_ptr)
mSeq->track_list.tracks[i])->time);
285 wxFprintf(debugOutput,
"channel: %li\n",
286 ((Alg_event_ptr)
mSeq->track_list.tracks[i])->chan);
288 if(((Alg_event_ptr)
mSeq->track_list.tracks[i])->get_type() ==
wxT(
'n'))
290 wxFprintf(debugOutput,
"pitch: %f\n",
291 ((Alg_note_ptr)
mSeq->track_list.tracks[i])->pitch);
292 wxFprintf(debugOutput,
"duration: %f\n",
293 ((Alg_note_ptr)
mSeq->track_list.tracks[i])->dur);
294 wxFprintf(debugOutput,
"velocity: %f\n",
295 ((Alg_note_ptr)
mSeq->track_list.tracks[i])->loud);
297 else if(((Alg_event_ptr)
mSeq->track_list.tracks[i])->get_type() ==
wxT(
'n'))
299 wxFprintf(debugOutput,
"key: %li\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->get_identifier());
300 wxFprintf(debugOutput,
"attribute type: %c\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_type());
301 wxFprintf(debugOutput,
"attribute: %s\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_name());
303 if(((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_type() ==
wxT(
'r'))
305 wxFprintf(debugOutput,
"value: %f\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.r);
307 else if(((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_type() ==
wxT(
'i')) {
308 wxFprintf(debugOutput,
"value: %li\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.i);
310 else if(((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.attr_type() ==
wxT(
's')) {
311 wxFprintf(debugOutput,
"value: %s\n", ((Alg_update_ptr)
mSeq->track_list.tracks[i])->parameter.s);
320 wxFprintf(debugOutput,
"No sequence defined!\n");
336 auto newTrack = std::make_shared<NoteTrack>();
338 newTrack->Init(*
this);
341 seq.convert_to_seconds();
342 newTrack->mSeq.reset(seq.cut(t0 -
mOrigin, len,
false));
363 auto newTrack = std::make_shared<NoteTrack>();
365 newTrack->Init(*
this);
368 seq.convert_to_seconds();
369 newTrack->mSeq.reset(seq.copy(t0 -
mOrigin, len,
false));
388 seq.convert_to_seconds();
390 seq.clear(t1 -
mOrigin, seq.get_dur() + 10000.0,
false);
392 seq.clear(0.0, t0 -
mOrigin,
false);
413 auto start = t0 - offset;
418 seq.clear(0, len + start,
false);
428 seq.clear(start, len,
false);
459 auto offset = other.mOrigin;
461 seq.convert_to_seconds();
462 seq.insert_silence(t -
mOrigin, offset);
474 seq.paste(t -
mOrigin, &other.GetSeq());
494 seq.convert_to_seconds();
498 seq.silence(t0 -
mOrigin, len,
false);
507 seq.convert_to_seconds();
508 seq.insert_silence(t -
mOrigin, len);
524 mVelocity.store(velocity, std::memory_order_relaxed);
534 seq.convert_to_beats();
536 double tempo = seq.get_tempo(0.0);
537 double beats_per_measure = seq.get_bar_len(0.0);
538 int m =
ROUND(t * tempo / beats_per_measure);
542 tempo = beats_per_measure * m / t;
543 seq.insert_silence(0.0, beats_per_measure * m);
544 seq.set_tempo(tempo * 60.0 , 0.0, beats_per_measure * m);
545 seq.write(
"afterShift.gro");
548 seq.convert_to_seconds();
549 seq.clear(0, t,
true);
559 double seq_time = time -
mOrigin;
562 seq_time = seq.nearest_beat_time(seq_time, &beat);
564 return { seq_time +
mOrigin, beat };
570 {
"note",
"midi",
XO(
"Note Track") },
true,
587 auto pNewTrack = std::make_shared<NoteTrack>();
588 pNewTrack->Init(*
this);
589 pNewTrack->Paste(0.0, *
this);
599std::shared_ptr<WideChannelGroupInterval>
604 return std::make_shared<Interval>(*
this);
613 seq.set_real_dur( seq.get_real_dur() + delta );
615 seq.convert_to_seconds();
616 seq.set_dur( seq.get_dur() + delta );
624 bool result = seq.stretch_region( t0.second, t1.second, newDur );
626 const auto oldDur = t1.first - t0.first;
634 void swap(std::unique_ptr<Alg_seq> &a, std::unique_ptr<Alg_seq> &b)
636 std::unique_ptr<Alg_seq> tmp = std::move(a);
649 double start = -offset;
650 if (start < 0) start = 0;
654 auto seq = cleanup.get();
659 auto cleanup2 =
finally( [&] {
swap( this->
mSeq, cleanup ); } );
666 double beats_per_measure = 4.0;
667 Alg_time_sig_ptr tsp = NULL;
668 if (seq->time_sig.length() > 0 && seq->time_sig[0].beat < ALG_EPS) {
670 tsp = &(seq->time_sig[0]);
671 beats_per_measure = (tsp->num * 4) / tsp->den;
674 double bps = ALG_DEFAULT_BPM / 60;
675 Alg_time_map_ptr map = seq->get_time_map();
676 Alg_beat_ptr bp = &(map->beats[0]);
677 if (bp->time < ALG_EPS) {
678 if (map->beats.len > 1) {
679 bps = (map->beats[1].beat - map->beats[0].beat) /
680 (map->beats[1].time - map->beats[0].time);
681 }
else if (seq->get_time_map()->last_tempo_flag) {
682 bps = seq->get_time_map()->last_tempo;
687 double measure_time = beats_per_measure / bps;
688 int n =
ROUND(offset / measure_time);
691 measure_time = offset / n;
692 bps = beats_per_measure / measure_time;
694 seq->convert_to_beats();
695 seq->insert_silence(0, beats_per_measure * n);
698 seq->set_time_sig(0, tsp->num, tsp->den);
701 seq->set_tempo(bps * 60.0, 0, beats_per_measure * n);
711 double beat = mySeq.get_time_map()->time_to_beat(start);
713 int i = mySeq.time_sig.find_beat(beat);
718 if (mySeq.time_sig.length() > 0 &&
719 within(beat, mySeq.time_sig[i].beat, ALG_EPS)) {
724 }
else if (i == 0 && (mySeq.time_sig.length() == 0 ||
725 mySeq.time_sig[i].beat > beat)) {
728 double measures = beat / 4.0;
729 double imeasures =
ROUND(measures);
730 if (!
within(measures, imeasures, ALG_EPS)) {
731 double bar_offset = ((int)(measures) + 1) * 4.0 - beat;
732 seq->set_time_sig(bar_offset, 4, 4);
742 Alg_time_sig_ptr tsp = &(mySeq.time_sig[i]);
743 double beats_per_measure = (tsp->num * 4) / tsp->den;
744 double measures = (beat - tsp->beat) / beats_per_measure;
745 int imeasures =
ROUND(measures);
746 if (!
within(measures, imeasures, ALG_EPS)) {
750 double bar = tsp->beat + beats_per_measure * ((int)(measures) + 1);
751 double bar_offset = bar - beat;
755 seq->set_time_sig(bar_offset, tsp->num, tsp->den);
767 std::unique_ptr<Alg_seq> cleanup;
769 bool rslt = seq->smf_write(f.mb_str());
774 wxT(
"/FileFormats/AllegroStyleChoice"),
786 wxT(
"/FileFormats/AllegroStyle"),
795 seq.convert_to_seconds();
797 seq.convert_to_beats();
799 return seq.write(f.mb_str(), offset);
806 return (nValue >= 0 && nValue < (1 << 16));
812 if (tag ==
"notetrack") {
813 for (
auto pair : attrs)
815 auto attr = pair.first;
816 auto value = pair.second;
823 return attachment.HandleAttribute(pair);
828 else if (attr ==
"offset" && value.TryGet(dblValue))
830 else if (attr ==
"visiblechannels") {
831 if (!value.TryGet(nValue) ||
836 else if (attr ==
"velocity" && value.TryGet(dblValue))
838 else if (attr ==
"data") {
839 std::string s(value.ToWString());
840 std::istringstream data(s);
841 mSeq = std::make_unique<Alg_seq>(data,
false);
857 std::ostringstream data;
863 holder = Clone(
false);
864 saveme =
static_cast<NoteTrack*
>(holder.get());
866 saveme->
GetSeq().write(data,
true);
867 xmlFile.StartTag(
wxT(
"notetrack"));
868 saveme->Track::WriteCommonXMLAttributes( xmlFile );
870 xmlFile.WriteAttr(
wxT(
"offset"), saveme->
mOrigin);
871 xmlFile.WriteAttr(
wxT(
"visiblechannels"),
874 xmlFile.WriteAttr(
wxT(
"velocity"),
876 saveme->Attachments::ForEach([&](
auto &attachment){
877 attachment.WriteXML(xmlFile);
879 xmlFile.WriteAttr(
wxT(
"data"), wxString(data.str().c_str(), wxConvUTF8));
880 xmlFile.EndTag(
wxT(
"notetrack"));
884#include <wx/sstream.h>
885#include <wx/txtstrm.h>
892 wxStringOutputStream o;
893 wxTextOutputStream s(o, wxEOL_UNIX);
896 return XO(
"Stream is active ... unable to gather information.\n")
902 int recDeviceNum = Pm_GetDefaultInputDeviceID();
903 int playDeviceNum = Pm_GetDefaultOutputDeviceID();
904 int cnt = Pm_CountDevices();
907 wxLogDebug(
wxT(
"PortMidi reports %d MIDI devices"), cnt);
909 s <<
wxT(
"==============================\n");
910 s <<
XO(
"Default recording device number: %d\n").Format( recDeviceNum );
911 s <<
XO(
"Default playback device number: %d\n").Format( playDeviceNum );
918 s <<
XO(
"No devices found\n");
919 return o.GetString();
922 for (
int i = 0; i < cnt; i++) {
923 s <<
wxT(
"==============================\n");
925 const PmDeviceInfo* info = Pm_GetDeviceInfo(i);
927 s <<
XO(
"Device info unavailable for: %d\n").Format( i );
931 wxString
name = wxSafeConvertMB2WX(info->name);
932 wxString hostName = wxSafeConvertMB2WX(info->interf);
934 s <<
XO(
"Device ID: %d\n").Format( i );
935 s <<
XO(
"Device name: %s\n").Format(
name );
936 s <<
XO(
"Host name: %s\n").Format( hostName );
938 s <<
XO(
"Supports output: %d\n").Format( info->output );
940 s <<
XO(
"Supports input: %d\n").Format( info->input );
941 s <<
XO(
"Opened: %d\n").Format( info->opened );
943 if (
name == playDevice && info->output)
946 if (
name == recDevice && info->input)
951 if (recDeviceNum < 0 && info->input){
954 if (playDeviceNum < 0 && info->output){
959 bool haveRecDevice = (recDeviceNum >= 0);
960 bool havePlayDevice = (playDeviceNum >= 0);
962 s <<
wxT(
"==============================\n");
964 s <<
XO(
"Selected MIDI recording device: %d - %s\n").Format( recDeviceNum, recDevice );
966 s <<
XO(
"No MIDI recording device found for '%s'.\n").Format( recDevice );
969 s <<
XO(
"Selected MIDI playback device: %d - %s\n").Format( playDeviceNum, playDevice );
971 s <<
XO(
"No MIDI playback device found for '%s'.\n").Format( playDevice );
977 s <<
wxT(
"==============================\n");
978#ifdef EXPERIMENTAL_MIDI_IN
979 s <<
wxT(
"EXPERIMENTAL_MIDI_IN is enabled\n");
981 s <<
wxT(
"EXPERIMENTAL_MIDI_IN is NOT enabled\n");
986 return o.GetString();
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 ShiftBy(double t0, double delta) override
Shift all intervals that starts after t0 by delta seconds.
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