From e76bfa39ef21d5e6d38f3aebda0864064241c4fd Mon Sep 17 00:00:00 2001 From: rbdannenberg Date: Mon, 27 Sep 2010 05:51:41 +0000 Subject: [PATCH] Fix compiler warnings (string constant to char *) from allegro.h. Also implements play at speed for MIDI, but some MIDI playback problems remain. --- lib-src/portsmf/allegro.cpp | 35 ++++++++++++------- lib-src/portsmf/allegro.h | 48 +++++++++++++++++--------- lib-src/portsmf/allegrosmfwr.cpp | 14 ++++---- mac/Audacity.xcodeproj/project.pbxproj | 9 ++++- src/AudioIO.cpp | 42 ++++++++++++++-------- src/AudioIO.h | 9 +++-- src/TrackArtist.cpp | 16 ++++----- 7 files changed, 110 insertions(+), 63 deletions(-) diff --git a/lib-src/portsmf/allegro.cpp b/lib-src/portsmf/allegro.cpp index 9238cfa88..6912e0536 100644 --- a/lib-src/portsmf/allegro.cpp +++ b/lib-src/portsmf/allegro.cpp @@ -54,7 +54,7 @@ void Alg_atoms::expand() { maxlen = (maxlen + 5); // extra growth for small sizes maxlen += (maxlen >> 2); // add 25% - char **new_atoms = new Alg_attribute[maxlen]; + Alg_attribute *new_atoms = new Alg_attribute[maxlen]; // now do copy memcpy(new_atoms, atoms, len * sizeof(Alg_attribute)); if (atoms) delete[] atoms; @@ -81,6 +81,7 @@ Alg_attribute Alg_atoms::insert_new(const char *name, char attr_type) Alg_attribute Alg_atoms::insert_attribute(Alg_attribute attr) { + // should use hash algorithm for (int i = 0; i < len; i++) { if (STREQL(attr, atoms[i])) { return atoms[i]; @@ -421,7 +422,7 @@ char Alg_event::get_attribute_type(char *a) } -char *Alg_event::get_string_value(char *a, char *value) +const char *Alg_event::get_string_value(char *a, char *value) { assert(is_note()); assert(a); // must be non-null @@ -473,7 +474,7 @@ long Alg_event::get_integer_value(char *a, long value) } -char *Alg_event::get_atom_value(char *a, char *value) +const char *Alg_event::get_atom_value(char *a, char *value) { assert(is_note()); assert(a); @@ -514,12 +515,12 @@ char Alg_event::get_update_type() } -char *Alg_event::get_string_value() +const char *Alg_event::get_string_value() { assert(is_update()); Alg_update* update = (Alg_update *) this; assert(get_update_type() == 's'); - return update->parameter.attr_name(); + return update->parameter.s; } @@ -550,7 +551,7 @@ long Alg_event::get_integer_value() } -char *Alg_event::get_atom_value() +const char *Alg_event::get_atom_value() { assert(is_update()); Alg_update* update = (Alg_update *) this; @@ -1640,7 +1641,7 @@ void Alg_track::unserialize_track() void Alg_track::unserialize_parameter(Alg_parameter_ptr parm_ptr) { - char *attr = ser_read_buf.get_string(); + Alg_attribute attr = ser_read_buf.get_string(); parm_ptr->attr = symbol_table.insert_string(attr); switch (parm_ptr->attr_type()) { case 'r': @@ -2675,13 +2676,19 @@ void Alg_iterator::show() bool Alg_iterator::earlier(int i, int j) // see if event i is earlier than event j { + // note-offs are scheduled ALG_EPS early so that if a note-off is + // followed immediately with the same timestamp by a note-on (common + // in MIDI files), the note-off will be scheduled first + Alg_pending_event_ptr p_i = &(pending_events[i]); Alg_event_ptr e_i = (*(p_i->events))[p_i->index]; - double t_i = (p_i->note_on ? e_i->time : e_i->get_end_time()) + p_i->offset; + double t_i = (p_i->note_on ? e_i->time : + e_i->get_end_time() - ALG_EPS) + p_i->offset; Alg_pending_event_ptr p_j = &(pending_events[j]); Alg_event_ptr e_j = (*(p_j->events))[p_j->index]; - double t_j = (p_j->note_on ? e_j->time : e_j->get_end_time()) + p_j->offset; + double t_j = (p_j->note_on ? e_j->time : + e_j->get_end_time() - ALG_EPS) + p_j->offset; if (t_i < t_j) return true; // not sure if this case really exists or this is the best rule, but @@ -3413,11 +3420,7 @@ Alg_event_ptr Alg_iterator::next(bool *note_on, void **cookie_ptr, double *offset_ptr, double end_time) // return the next event in time from any track { - Alg_events_ptr events_ptr; - long index; bool on; - void *cookie; - double offset; if (!remove_next(events_ptr, index, on, cookie, offset)) { return NULL; } @@ -3445,6 +3448,12 @@ Alg_event_ptr Alg_iterator::next(bool *note_on, void **cookie_ptr, } +void Alg_iterator::request_note_off() +{ + insert(events_ptr, index, false, cookie, offset); +} + + void Alg_iterator::end() { } diff --git a/lib-src/portsmf/allegro.h b/lib-src/portsmf/allegro.h index f99006373..9a7da871b 100644 --- a/lib-src/portsmf/allegro.h +++ b/lib-src/portsmf/allegro.h @@ -67,7 +67,7 @@ char *heapify(const char *s); // put a string on the heap // the attribute 'tempor' (a real) is stored // as 'rtempor'. To get the string name, just // use attribute+1. -typedef char *Alg_attribute; +typedef const char *Alg_attribute; #define alg_attr_name(a) ((a) + 1) #define alg_attr_type(a) (*(a)) @@ -99,7 +99,7 @@ public: private: long maxlen; long len; - char **atoms; + Alg_attribute *atoms; // insert an Attriubute not in table after moving attr to heap Alg_attribute insert_new(const char *name, char attr_type); @@ -118,19 +118,20 @@ public: // deleted safely without further initialization. It does not // do anything useful, so it is expected that the creator will // set attr and store a value in the appropriate union field. - Alg_parameter() { attr = "i"; } - ~Alg_parameter(); Alg_attribute attr; union { double r;// real - char *s; // string + const char *s; // string long i; // integer bool l; // logical - char *a; // symbol (atom) + const char *a; // symbol (atom) }; // anonymous union + + Alg_parameter() { attr = "i"; } + ~Alg_parameter(); void copy(Alg_parameter *); // copy from another parameter - char attr_type() { return alg_attr_type(attr); } - char *attr_name() { return alg_attr_name(attr); } + const char attr_type() { return alg_attr_type(attr); } + const char *attr_name() { return alg_attr_name(attr); } void set_attr(Alg_attribute a) { attr = a; } void show(); } *Alg_parameter_ptr; @@ -245,11 +246,11 @@ public: // 's' = string, // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), // 'a' = atom (char *), a unique string stored in Alg_seq - char *get_string_value(char *attr, char *value = NULL); // get the string value + const char *get_string_value(char *attr, char *value = NULL); // get the string value double get_real_value(char *attr, double value = 0.0); // get the real value bool get_logical_value(char *attr, bool value = false); // get the logical value long get_integer_value(char *attr, long value = 0); // get the integer value - char *get_atom_value(char *attr, char *value = NULL); // get the atom value + const char *get_atom_value(char *attr, char *value = NULL); // get the atom value void delete_attribute(char *attr); // delete an attribute/value pair // (ignore if no matching attribute/value pair exists) @@ -261,13 +262,13 @@ public: char get_update_type(); // get the update's type: 's' = string, // 'r' = real (double), 'l' = logical (bool), 'i' = integer (long), // 'a' = atom (char *), a unique string stored in Alg_seq - char *get_string_value(); // get the update's string value + const char *get_string_value(); // get the update's string value // Notes: Caller does not own the return value. Do not modify. // Do not use after underlying Alg_seq is modified. double get_real_value(); // get the update's real value bool get_logical_value(); // get the update's logical value long get_integer_value(); // get the update's integer value - char *get_atom_value(); // get the update's atom value + const char *get_atom_value(); // get the update's atom value // Notes: Caller does not own the return value. Do not modify. // The return value's lifetime is forever. @@ -558,7 +559,7 @@ public: float get_float() { float f = *((float *) ptr); ptr += 4; return f; } double get_double() { double d = *((double *) ptr); ptr += sizeof(double); return d; } - char *get_string() { char *s = ptr; char *fence = buffer + len; + const char *get_string() { char *s = ptr; char *fence = buffer + len; assert(ptr < fence); while (*ptr++) assert(ptr < fence); get_pad(); @@ -586,7 +587,7 @@ typedef class Serial_write_buffer: public Serial_buffer { *loc = value; } void check_buffer(long needed); - void set_string(char *s) { + void set_string(const char *s) { char *fence = buffer + len; assert(ptr < fence); // two brackets surpress a g++ warning, because this is an @@ -922,7 +923,11 @@ private: long len; Alg_seq_ptr seq; Alg_pending_event *pending_events; - + // the next four fields are mainly for request_note_off() + Alg_events_ptr events_ptr; // remembers events containing current event + long index; // remembers index of current event + void *cookie; // remembers the cookie associated with next event + double offset; void show(); bool earlier(int i, int j); void insert(Alg_events_ptr events, long index, bool note_on, @@ -945,11 +950,14 @@ public: // can add more sequences to the iteration. Events are returned in // time order, so effectively sequence events are merged. // The optional offset is added to each event time of sequence s - // before merging/sorting. + // before merging/sorting. You should call begin_seq() for each + // sequence to be included in the iteration unless you call begin() + // (see below). void begin_seq(Alg_seq_ptr s, void *cookie = NULL, double offset = 0.0); ~Alg_iterator(); // Prepare to enumerate events in order. If note_off_flag is true, then - // iteration_next will merge note-off events into the sequence. + // iteration_next will merge note-off events into the sequence. If you + // call begin(), you should not normally call begin_seq(). See above. void begin(void *cookie = NULL) { begin_seq(seq, cookie); } // return next event (or NULL). If iteration_begin was called with // note_off_flag = true, and if note_on is not NULL, then *note_on @@ -962,6 +970,12 @@ public: // end_time is non_zero, stop iterating at the last event before end_time Alg_event_ptr next(bool *note_on = NULL, void **cookie_ptr = NULL, double *offset_ptr = NULL, double end_time = 0); + // Sometimes, the caller wants to receive note-off events for a subset + // of the notes, typically the notes that are played and need to be + // turned off. In this case, when a note is turned on, the client + // should call request_note_off(). This will insert a note-off into + // the queue for the most recent note returned by next(). + void request_note_off(); void end(); // clean up after enumerating events } *Alg_iterator_ptr; diff --git a/lib-src/portsmf/allegrosmfwr.cpp b/lib-src/portsmf/allegrosmfwr.cpp index 4bf1edc6e..cd06251a4 100644 --- a/lib-src/portsmf/allegrosmfwr.cpp +++ b/lib-src/portsmf/allegrosmfwr.cpp @@ -46,7 +46,7 @@ private: void write_note(Alg_note_ptr note, bool on); void write_update(Alg_update_ptr update); void write_text(Alg_update_ptr update, char type); - void write_binary(int type_byte, char *msg); + void write_binary(int type_byte, const char *msg); void write_midi_channel_prefix(Alg_update_ptr update); void write_smpteoffset(Alg_update_ptr update, char *s); void write_data(int data); @@ -255,13 +255,13 @@ static char hex_to_nibble(char c) } -static char hex_to_char(char *s) +static char hex_to_char(const char *s) { return (hex_to_nibble(s[0]) << 4) + hex_to_nibble(s[1]); } -void Alg_smf_write::write_binary(int type_byte, char *msg) +void Alg_smf_write::write_binary(int type_byte, const char *msg) { int len = strlen(msg) / 2; out_file->put(type_byte); @@ -275,7 +275,7 @@ void Alg_smf_write::write_binary(int type_byte, char *msg) void Alg_smf_write::write_update(Alg_update_ptr update) { - char *name = update->parameter.attr_name(); + const char *name = update->parameter.attr_name(); /****Non-Meta Events****/ if (!strcmp(name, "pressurer")) { @@ -312,7 +312,7 @@ void Alg_smf_write::write_update(Alg_update_ptr update) write_data(val); } else if (!strcmp(name, "sysexs") && update->parameter.attr_type() == 's') { - char *s = update->parameter.s; + const char *s = update->parameter.s; if (s[0] && s[1] && toupper(s[0]) == 'F' && s[1] == '0') { s += 2; // skip the initial "F0" byte in message: it is implied } @@ -320,7 +320,7 @@ void Alg_smf_write::write_update(Alg_update_ptr update) write_binary(0xF0, s); } else if (!strcmp(name, "sqspecifics") && update->parameter.attr_type() == 's') { - char *s = update->parameter.s; + const char *s = update->parameter.s; write_delta(update->time); out_file->put('\xFF'); write_binary(0x7F, s); @@ -349,7 +349,7 @@ void Alg_smf_write::write_update(Alg_update_ptr update) // smpteoffset is specified as "24fps:00h:10m:00s:11.00f" // the following simple parser does not reject all badly // formatted strings, but it should parse good strings ok - char *s = update->parameter.s; + const char *s = update->parameter.s; int len = strlen(s); char smpteoffset[5]; if (len < 24) return; // not long enough, must be bad format diff --git a/mac/Audacity.xcodeproj/project.pbxproj b/mac/Audacity.xcodeproj/project.pbxproj index 3730ebcf0..b3579dbdd 100644 --- a/mac/Audacity.xcodeproj/project.pbxproj +++ b/mac/Audacity.xcodeproj/project.pbxproj @@ -6918,7 +6918,14 @@ isa = PBXProject; buildConfigurationList = 1790ABE409883346008A330A /* Build configuration list for PBXProject "Audacity" */; compatibilityVersion = "Xcode 2.4"; + developmentRegion = English; hasScannedForEncodings = 0; + knownRegions = ( + English, + Japanese, + French, + German, + ); mainGroup = 1790ABE109883346008A330A; productRefGroup = 1790AFB109883B6D008A330A /* Products */; projectDirPath = ""; @@ -8412,7 +8419,7 @@ INFOPLIST_PREPROCESS = YES; KEEP_PRIVATE_EXTERNS = YES; ONLY_LINK_ESSENTIAL_SYMBOLS = YES; - SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + SDKROOT = /Developer/SDKs/MacOSX10.5.sdk; TOPLEVEL = ..; WX_CFLAGS = "-I$(WX_PREFIX)/lib/wx/include/mac-unicode-debug-static-$(WX_VER) -I$(WX_PREFIX)/include/wx-$(WX_VER) -D__WXDEBUG__ -D__WXMAC__ -D_FILE_OFFSET_BITS=64 -D_LARGE_FILES -DNO_GCC_PRAGMA"; WX_CONFIG = "$(WX_PREFIX)/bin/wx-config"; diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index b832d42bb..3cbe0a46c 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -914,6 +914,7 @@ bool AudioIO::StartPortAudioStream(double sampleRate, #ifdef EXPERIMENTAL_MIDI_OUT mNumFrames = 0; mNumPauseFrames = 0; + mPauseTime = 0; #endif mLastPaError = paNoError; // pick a rate to do the audio I/O at, from those available. The project @@ -1322,7 +1323,7 @@ void AudioIO::PrepareMidiIterator(bool send, double offset) int nTracks = mMidiPlaybackTracks.GetCount(); // Set up to play only one track: mSeq = mMidiPlaybackTracks[0]->GetSequence(); - mIterator = new Alg_iterator(mSeq, true); + mIterator = new Alg_iterator(mSeq, false); // Iterator not yet intialized, must add each track... for (i = 0; i < nTracks; i++) { NoteTrack *t = mMidiPlaybackTracks[i]; @@ -1390,6 +1391,7 @@ bool AudioIO::StartPortMidiStream() mMidiStreamActive = true; mPauseTime = 0; + mMidiPaused = false; mMidiLoopOffset = 0; mMidiOutputComplete = false; // mCnt = 0; @@ -1512,9 +1514,7 @@ void AudioIO::StopStream() } // now we can assume "ownership" of the mMidiStream // if output in progress, send all off, etc. - for (int i = 0; i < 16; i++) { - Pm_WriteShort(mMidiStream, 0, Pm_Message(0xB0 + i, 0x7B, 0)); - } + AllNotesOff(); // Note: this code is here for future consideration. It's // possible sometimes to abort some messages waiting in the // output buffers, but if you do that, you might leave notes @@ -2035,7 +2035,6 @@ AudioThread::ExitCode AudioThread::Entry() #ifdef EXPERIMENTAL_MIDI_OUT MidiThread::ExitCode MidiThread::Entry() { - bool paused = false; long pauseStart = 0; while( !TestDestroy() ) { @@ -2047,13 +2046,14 @@ MidiThread::ExitCode MidiThread::Entry() { // Keep track of time paused. If not paused, fill buffers. if (gAudioIO->mNumPlaybackChannels == 0 && gAudioIO->IsPaused()) { - if (!paused) { - paused = true; + if (!gAudioIO->mMidiPaused) { + gAudioIO->mMidiPaused = true; pauseStart = MidiTime(NULL); + gAudioIO->AllNotesOff(); // to avoid hanging notes during pause } } else { - if (paused) { - paused = false; + if (gAudioIO->mMidiPaused) { + gAudioIO->mMidiPaused = false; gAudioIO->mPauseTime += (MidiTime(NULL) - pauseStart); } gAudioIO->FillMidiBuffers(); @@ -2622,10 +2622,7 @@ void AudioIO::OutputEvent() // The special event gAllNotesOffEvent means "end of playback, send // all notes off on all channels" if (mNextEvent == &gAllNotesOff) { - for (channel = 0; channel < 16; channel++) { - Pm_WriteShort(mMidiStream, timestamp, - Pm_Message(0xB0 + channel, 0x7B, 0)); - } + AllNotesOff(); if (mPlayLooped) { // jump back to beginning of loop mMidiLoopOffset += (mT1 - mT0); @@ -2644,9 +2641,10 @@ void AudioIO::OutputEvent() if (((mNextEventTrack->GetVisibleChannels() & (1 << channel)) && // only play if note is not muted: !((mHasSolo || mNextEventTrack->GetMute()) && - !mNextEventTrack->GetSolo())) || + !mNextEventTrack->GetSolo()))) { + // || // the following allows note-offs even when muted or not selected - (mNextEvent->is_note() && !mNextIsNoteOn)) { + // (mNextEvent->is_note() && !mNextIsNoteOn)) { // Note event if (mNextEvent->is_note() && !mSendMidiState) { // Pitch and velocity @@ -2657,6 +2655,8 @@ void AudioIO::OutputEvent() data2 += offset; // offset comes from per-track slider // clip velocity to insure a legal note-on value data2 = (data2 < 0 ? 1 : (data2 > 127 ? 127 : data2)); + // since we are going to play this note, we need to get a note_off + mIterator->request_note_off(); } else data2 = 0; // 0 velocity means "note off" command = 0x90; // MIDI NOTE ON (or OFF when velocity == 0) // Update event @@ -2709,6 +2709,9 @@ void AudioIO::OutputEvent() Pm_WriteShort(mMidiStream, timestamp, Pm_Message((int) (command + channel), (long) data1, (long) data2)); + printf("Pm_WriteShort %x @ %d\n", + Pm_Message((int) (command + channel), + (long) data1, (long) data2), timestamp); } } } @@ -2774,6 +2777,8 @@ void AudioIO::FillMidiBuffers() if (mNumCaptureChannels <= 0) { // no audio callback, so move the time cursor here: double track_time = time - mMidiLoopOffset; + printf("mTime set. mT0 %g Pt_Time() %gs PauseTime %g\n", + mT0, Pt_Time() * 0.001, PauseTime()); // Since loop offset is incremented when we fill the // buffer, the cursor tends to jump back to mT0 early. // Therefore, if we are in loop mode, and if mTime < mT0, @@ -2823,6 +2828,13 @@ PmTimestamp AudioIO::MidiTime() } } +void AudioIO::AllNotesOff() +{ + for (int chan = 0; chan < 16; chan++) { + Pm_WriteShort(mMidiStream, 0, Pm_Message(0xB0 + chan, 0x7B, 0)); + } +} + #endif // Automated Input Level Adjustment - Automatically tries to find an acceptable input volume diff --git a/src/AudioIO.h b/src/AudioIO.h index 2c587d670..fb8e1e1bf 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -350,8 +350,9 @@ private: void AudacityMidiCallback(); double AudioTime() { return mT0 + mNumFrames / mRate; } double PauseTime(); - double getCurrentTrackTime(); - long calculateMidiTimeStamp(double time); + // double getCurrentTrackTime(); + // long CalculateMidiTimeStamp(double time); + void AllNotesOff(); #endif /** \brief Get the number of audio samples free in all of the playback @@ -411,6 +412,10 @@ private: volatile long mPauseTime; // pause in ms if no audio playback volatile double mMidiLoopOffset; // total of backward jumps volatile long mAudioFramesPerBuffer; + volatile bool mMidiPaused; // used by Midi process to record + // that pause has begun. Pause time is accumulated in mPauseTime. + // This variable is shared so that it can be cleared when playback + // begins. Alg_seq_ptr mSeq; Alg_iterator_ptr mIterator; diff --git a/src/TrackArtist.cpp b/src/TrackArtist.cpp index 567adf52a..96fb3897f 100644 --- a/src/TrackArtist.cpp +++ b/src/TrackArtist.cpp @@ -2050,18 +2050,18 @@ and optional attributes as follows: */ /* Declare Static functions */ -static char *IsShape(Alg_note_ptr note); +static const char *IsShape(Alg_note_ptr note); static double LookupRealAttribute(Alg_note_ptr note, Alg_attribute attr, double def); static long LookupIntAttribute(Alg_note_ptr note, Alg_attribute attr, long def); static bool LookupLogicalAttribute(Alg_note_ptr note, Alg_attribute attr, bool def); static const char *LookupStringAttribute(Alg_note_ptr note, Alg_attribute attr, const char *def); -static char *LookupAtomAttribute(Alg_note_ptr note, Alg_attribute attr, char *def); +static const char *LookupAtomAttribute(Alg_note_ptr note, Alg_attribute attr, char *def); static int PITCH_TO_Y(double p, int bottom); -static char *LookupAtomAttribute(Alg_note_ptr note, Alg_attribute attr, char *def); + // returns NULL if note is not a shape, // returns atom (string) value of note if note is a shape -char *IsShape(Alg_note_ptr note) +const char *IsShape(Alg_note_ptr note) { Alg_parameters_ptr parameters = note->parameters; while (parameters) { @@ -2130,7 +2130,7 @@ const char *LookupStringAttribute(Alg_note_ptr note, Alg_attribute attr, const c } // returns value of attr, or default if not found -char *LookupAtomAttribute(Alg_note_ptr note, Alg_attribute attr, char *def) +const char *LookupAtomAttribute(Alg_note_ptr note, Alg_attribute attr, char *def) { Alg_parameters_ptr parameters = note->parameters; while (parameters) { @@ -2417,7 +2417,7 @@ void TrackArtist::DrawNoteTrack(NoteTrack *track, double x = note->time + track->GetOffset(); double x1 = x + note->dur; if (x < h1 && x1 > h) { // omit if outside box - char *shape = NULL; + const char *shape = NULL; if (note->loud > 0.0 || !(shape = IsShape(note))) { wxRect nr; // "note rectangle" nr.y = track->PitchToY(note->pitch); @@ -2589,8 +2589,8 @@ void TrackArtist::DrawNoteTrack(NoteTrack *track, //// if no color specified, copy color from brush //else dc.SetTextBackground(dc.GetPen().GetColour()); - char *font = LookupAtomAttribute(note, fonta, NULL); - char *weight = LookupAtomAttribute(note, weighta, NULL); + const char *font = LookupAtomAttribute(note, fonta, NULL); + const char *weight = LookupAtomAttribute(note, weighta, NULL); int size = LookupIntAttribute(note, sizei, 8); const char *justify = LookupStringAttribute(note, justifys, "ld"); wxFont wxfont;