Rainer, would you mind waiting for a while, particularly so we can get some
testing in place? In the long run I think this would enable us to confidently
get results faster.


No, I don't mind waiting at all. -

But please allow me to make a little experiment: I have attached four include
files to this email which are part of the code which I have developed for
manipulating midi files.  Please read at first /item.h/, then /text_element.h/, then
/midi_element.h/, and finally /midi_track.h/.

The following should be the case: First, it should take you only 5 minutes to understand the first three include files and the resulting implications for all
classes derived from class /Midi::Element/.  Second, it should take you only
about 15 minutes to understand /Midi::Track/ to such a degree that you would
be able to confidently use its functions, without even knowing the details
of the implementation in /midi_track.cpp/ (deliberately not supplied).

Please let me know if that is the case or not.

Sincerely


Rainer

#ifndef ITEM_H
#define ITEM_H
/*******************************************************************************

Class Support::Item

Copyright (C)  2020    Rainer Hans Liffers, Carnarvon, Western Australia
                       Email: [email protected]

********************************************************************************
This code is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
details at: https://www.gnu.org/licenses.
********************************************************************************

The purpose of Class Support::Item is to enforce the existence of function
"bool valid () const" in any derived class, and to provide public
"operator const void * () const" and "bool operator ! () const" in terms of
that function.

*******************************************************************************/


namespace Support

  { class Item

      { private:

        // determine if item is valid
        virtual bool valid () const = 0;


        protected:

        // create item
        Item ()  { }

        // delete item
        virtual ~Item ()  { }


        public:

        // determine if item is valid
        operator const void * () const  { return valid () ? this : 0; }

        // determine if item is invalid
        bool operator ! () const  { return ! valid ();  }    };   }

#endif
#ifndef TEXT_ELEMENT_H
#define TEXT_ELEMENT_H
/*******************************************************************************

Class Text::Element

Copyright (C)  2020    Rainer Hans Liffers, Carnarvon, Western Australia
                       Email: [email protected]

********************************************************************************
This code is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
details at: https://www.gnu.org/licenses.
********************************************************************************

Class Text::Element is a Support::Item enforcing the existence of functions
"void extract (Text::istream &)" and "void insert (Text::ostream &) const" in
any derived class, and providing public friend functions
"Text::istream & operator >> (Text::istream &, Text::Element &)" and
"Text::ostream & operator << (Text::ostream &, const Text::Element &)"
in terms of "extract" and "insert", respectively.

No assumptions are made regarding classes Text::istream and Text::ostream.

*******************************************************************************/

#include "Support/item.h"


namespace Text

  { class istream;
    class ostream;


    class Element : public Support::Item

      { private:

        // extract element from text stream
        virtual void extract (istream &) = 0;

        // insert element into text stream
        virtual void insert (ostream &) const = 0;


        protected:

        // create element
        Element (): Item ()  { }


        public:

        // extract element from text stream
        inline friend istream & operator >> (istream & stream,
                                             Element & element)
          { element.extract (stream); return stream; }

        // insert element into text stream
        inline friend ostream & operator << (ostream & stream,
                                             const Element & element)
          { element.insert (stream); return stream; }   };   }

#endif
#ifndef MIDI_ELEMENT_H
#define MIDI_ELEMENT_H
/*******************************************************************************

Class Midi::Element

Copyright (C)  2020    Rainer Hans Liffers, Carnarvon, Western Australia
                       Email: [email protected]

********************************************************************************
This code is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
details at: https://www.gnu.org/licenses.
********************************************************************************

Class Midi::Element is a Text::Element, if compile time value TEXT_PROCESSING
is defined.  Otherwise, it is a Support::Item.

Class Midi::Element enforces the existence of functions
"void decode (Midi::istream &)" and "void encode (Midi::ostream &) const"
in any derived class, and provides public friend functions
"Midi::istream & operator >> (Midi::istream &, Midi::Element &)" and
"Midi::ostream & operator << (Midi::ostream &, const Midi::Element &)"
in terms of "decode" and "encode", respectively.

No assumptions are made regarding classes Midi::istream and Midi::ostream.

*******************************************************************************/

#ifdef TEXT_PROCESSING
    #include "Text/text_element.h"
#else
    #include "Support/item.h"
#endif

namespace Midi

  { class istream;
    class ostream;


#ifdef TEXT_PROCESSING
    class Element : public Text::Element
#else
    class Element : public Support::Item
#endif

      { private:

        // extract element from midi stream
        virtual void decode (istream &) = 0;

        // insert element into midi stream
        virtual void encode (ostream &) const = 0;


        protected:

        // create element
#ifdef TEXT_PROCESSING
        Element (): Text::Element ()  { }
#else
        Element (): Item ()  { }
#endif


        public:

        // extract element from midi stream
        inline friend istream & operator >> (istream & stream,
                                             Element & element)
          { element.decode (stream); return stream; }

        // insert element into midi stream
        inline friend ostream & operator << (ostream & stream,
                                             const Element & element)
          { element.encode (stream); return stream; }    };   }

#endif
#ifndef MIDI_TRACK_H
#define MIDI_TRACK_H
/*******************************************************************************

Class Midi::Track

Copyright (C)  2020    Rainer Hans Liffers, Carnarvon, Western Australia
                       Email: [email protected]

********************************************************************************
This code is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This code is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
details at: https://www.gnu.org/licenses.
********************************************************************************

Class Midi::Track is a Midi::Element (refer to midi_element.h) reflecting the
track event data - ie the midi events - of a midi file's track chunk (refer eg
to https://www.recordingblogs.com/wiki/track-chunk-of-a-midi-file).

All track event data are contained in data member "event", which is a list of
midi events, ie elements of type Midi::Event (refer to midi_event.h).

The other three data members, namely "title", "channelSet", and "timeCode",
specify redundant information obtained from the list of midi events.

If the list of midi events includes at least one midi track name meta message
(refer eg to https://www.recordingblogs.com/wiki/midi-track-name-meta-message),
then the contents of the first of these messages are also stored in data member
"title". In the context of this software, a midi track name meta message is a
text message (refer to midi_text_message.h). The class provides function "name"
in order to obtain a copy of the track's name.

Data member "channelSet" is a Midi::ChannelMask (refer to midi_stream_base.h)
specifying which channels are affected by the list of midi events. Function
"uniqueChannel" is provided in order to determine if all events with a channel
number have the same number, and if that is the case, it returns that number
incremented by one. Otherwise, it returns value zero.

Data member "timeCode" is a list of all time code specifications (refer to
midi_type_timecode.h) contained in the list of midi events. There are three
different midi messages defining time codes, namely the midi full frame message
(refer eg to https://www.recordingblogs.com/wiki/midi-full-frame-message),
which is a special kind of midi system exclusive message (refer to
midi_sysex_message.h), the SMPTE offset message (refer eg to
https://www.recordingblogs.com/wiki/midi-smpte-offset-meta-message and to
midi_offset_message.h), and the midi quarter frame message (refer eg to
https://www.recordingblogs.com/wiki/midi-quarter-frame-message). The latter
specifies a time code only partially (refer to midi_qframe_message.h). For
this reason, class Midi::TimeCode (refer to midi_type_timecode.h) permits
construction of an undefined time code object and provides function "add" in
order to keep track of partial specifications and to add them to that object.

The class provides functions "decode", "encode", "extract", and "insert" in
order to extract the track event data from or to insert it into a midi stream
or a text stream, respectively. It also provides function "valid" which
determines whether all track event data are valid or not. All these functions
are private, but may be used indirectly via the extraction and insertion
operators of Midi::Element (refer to midi_element.h) and Text::Element (refer
to text_element.h), or "operator const void *" and "operator !" of
Support::Item (refer to item.h), respectively. Regarding the implementation of
"decode", "encode", "extract", and "insert", also refer to midi_stream.h,
text_stream.h, and text_manip.h.

Moreover, the class provides complete functionality for scaling a Midi::Track
object, for merging multiple Midi::Track objects into a single one, and for
splitting a single Midi::Track object into multiple ones in accordance with
given criteria.

Function "scale" scales and rounds the delta times of all midi events of a
Midi::Track object in accordance with a given scale factor. The rounding is
done on an event-by-event basis, and thus guarantees maximum accuracy. The
function makes use of functions "deltaTime" and "setDeltaTime" of class
Midi::Event (refer to midi_event.h). Function "scale" has no effect on data
members "title", "channelSet", and "timeCode", all three of which therefore
remain valid after scaling.

Function "merge" accepts a vector of tracks and a boolean value, indicating
whether the name of the first element of the vector is to be retained as
the resulting track's name or not. This function completely destroys and
replaces the contents of the current Midi::Track object. For implementation
details, refer to midi_track.cpp. Note that none of the tracks in the given
vector must contain port and/or channel prefix messages. If at least one
of the tracks contains at least one of these midi messages, the result of
function "merge" will be an empty track object. Otherwise, the current
Midi::Track object will contain the track event data of all given tracks in
data member "event", with suitably amended delta times; its new name in data
member "title", if the first vector element contains at least one midi track
name meta message and the boolean value given to function "merge" is "true"
(otherwise, "title" will be empty); the set of all channel numbers found in
the vector's tracks in data member "channelSet"; and an empty list of time
code specifications, because the specifications in the tracks of the given
vector do not make sense any more for the result of function "merge".

Function "split" is actually two functions combined into one, because the
functionality of both purposes is almost identical. If the current track
shall be split into tracks with unique channel numbers, then it is sufficient
to supply a reference to a vector of tracks as a single argument where the
resulting tracks shall be stored. If the current track is supposed to be a
drum instrument track which shall be split into tracks for various sub-groups
of drum instruments, then a channel number and a pointer to an index map (refer
to index_map.h) must be provided in addition to the reference to a vector of
tracks. Function "split" has no effect on the current Midi::Track object.
The resulting track vector will be empty if the current Midi::Track object
includes at least one port and/or channel prefix message. Otherwise, the vector
will consist of one or more tracks, each one of which contains track event data
with suitably amended delta times in data member "event"; an empty "title" data
member; a suitably defined set of channel numbers in data member "channelSet";
and an empty list of time code specifications, because the specifications of
the current Midi::Track object do not make sense any more for any of the
resulting tracks. For implementation details, refer to midi_track.cpp.

*******************************************************************************/

#include <list>
#include <string>
#include <vector>
#include "Midi/midi_stream_base.h"
#include "Midi/midi_element.h"
#include "Midi/midi_event.h"
#include "Midi/type/midi_type_timecode.h"
#include "Support/index_map.h"


namespace Midi

  { class Track final : public Element

      { private:

#ifdef TEXT_PROCESSING
        static const char sequenceTitle [];
        static const char channelTitle [];
#endif

        std::string title;
        ChannelMask channelSet;
        std::list <Event> event;
        std::list <TimeCode> timeCode;

        // determine if track is valid
        bool valid () const override;

        // extract track from MIDI stream
        void decode (istream &) override;

        // insert track into MIDI stream
        void encode (ostream &) const override;

#ifdef TEXT_PROCESSING
        // extract track from text stream
        void extract (Text::istream &) override;

        // insert track into text stream
        void insert (Text::ostream &) const override;
#endif


        public:

#ifdef TEXT_PROCESSING
        static const char chunkTitle [];
#endif

        // create empty track
        Track (): Midi::Element (), title (), channelSet (),
                  event (), timeCode ()  { }

        // determine if track contains messages of specified type
        bool includes (Event::MessageType) const;

        // determine if track uniquely belongs to a specific channel
        // 1 <= return value <= 16: unique channel number
        // return value == 0 --> track contains messages for multiple channels
        unsigned int uniqueChannel () const;

        // determine track name
        std::string name () const  { return title; }

        // determine total duration of track (pulses per quarter note)
        long int duration () const;

        // determine all channel numbers of track
        const ChannelMask & allChannels () const
          { return channelSet; }

        // determine number of midi time code messages in track
        unsigned int timeCodes () const
          { return timeCode.size (); }

        // initialise track
        void clear ();

        /* merge tracks

           given tracks will be merged into current track if all tracks are
           valid and no track contains prefix messages; otherwise, current
           track will be empty                                                */
        void merge (const std::vector <Track> &, const bool = true);

        /* split current track into given vector of tracks

           if there is no index map, then split track into tracks with
           unique channel numbers, provided that it is valid and does not
           include prefix messages; otherwise, resulting vector of tracks
           will be empty

           if there is an index map, then split track into tracks for various
           drum instrument groups as specified by the index map, provided
           that it is valid and does not include prefix messages; otherwise,
           resulting vector of tracks will be empty                           */
        void split (std::vector <Track> &, const unsigned int = 0,
                    const Support::IndexMap <values> * = 0) const;

        // linearly increase or decrease track event delta times
        void scale (const double); };   }

#endif
_______________________________________________
Yoshimi-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/yoshimi-devel

Reply via email to