// -*- c-indentation-style:"stroustrup" c-basic-offset: 4 -*-

/*
  Rosegarden
  A sequencer and musical notation editor.
 
  This program is Copyright 2000-2007
  Guillaume Laurent   <glaurent@telegraph-road.org>,
  Chris Cannam        <cannam@all-day-breakfast.com>,
  Richard Bown        <bownie@bownie.com>
 
  The moral right of the authors to claim authorship of this work
  has been asserted.
 
  This program 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 2 of the
  License, or (at your option) any later version.  See the file
  COPYING included with this distribution for more information.
*/


#include <iostream>
#include "misc/Debug.h"
#include <kapplication.h>
#include <fstream>
#include <string>
#include <cstdio>
#include <algorithm>

#include "Midi.h"
#include "MidiFile.h"
#include "Segment.h"
#include "NotationTypes.h"
#include "BaseProperties.h"
#include "SegmentNotationHelper.h"
#include "SegmentPerformanceHelper.h"
#include "CompositionTimeSliceAdapter.h"
#include "AnalysisTypes.h"
#include "Track.h"
#include "Instrument.h"
#include "Quantizer.h"
#include "Studio.h"
#include "MidiTypes.h"
#include "Profiler.h"

//#define MIDI_DEBUG 1

#if (__GNUC__ < 3)
#include <strstream>
#define stringstream strstream
#else
#include <sstream>
#endif

#include <kapp.h>

namespace Rosegarden
{

using std::string;
using std::ifstream;
using std::stringstream;
using std::cerr;
using std::endl;
using std::ends;
using std::ios;

MidiFile::MidiFile(Studio *studio):
        SoundFile(std::string("unnamed.mid")),
        m_timingDivision(0),
        m_format(MIDI_FILE_NOT_LOADED),
        m_numberOfTracks(0),
        m_containsTimeChanges(false),
        m_trackByteCount(0),
        m_decrementCount(false),
        m_studio(studio)
{}

MidiFile::MidiFile(const std::string &fn,
                   Studio *studio):
        SoundFile(fn),
        m_timingDivision(0),
        m_format(MIDI_FILE_NOT_LOADED),
        m_numberOfTracks(0),
        m_containsTimeChanges(false),
        m_trackByteCount(0),
        m_decrementCount(false),
        m_studio(studio)
{}

// Make sure we clear away the m_midiComposition
//
MidiFile::~MidiFile()
{
    clearMidiComposition();
}


// A couple of convenience functions.   Watch the byte conversions out
// of the STL strings.
//
//
long
MidiFile::midiBytesToLong(const string& bytes)
{
    if (bytes.length() != 4) {
#ifdef MIDI_DEBUG
        std::cerr << "WARNING: Wrong length for long data (" << bytes.length()
        << ", should be 4)" << endl;
#endif

        throw (Exception("Wrong length for long data in MIDI stream"));
    }

    long longRet = ((long)(((MidiByte)bytes[0]) << 24)) |
                   ((long)(((MidiByte)bytes[1]) << 16)) |
                   ((long)(((MidiByte)bytes[2]) << 8)) |
                   ((long)((MidiByte)(bytes[3])));

    std::cerr << "midiBytesToLong(" << int((MidiByte)bytes[0]) << "," << int((MidiByte)bytes[1]) << "," << int((MidiByte)bytes[2]) << "," << int((MidiByte)bytes[3]) << ") -> " << longRet << std::endl;

    return longRet;
}

int
MidiFile::midiBytesToInt(const string& bytes)
{
    if (bytes.length() != 2) {
#ifdef MIDI_DEBUG
        std::cerr << "WARNING: Wrong length for int data (" << bytes.length()
        << ", should be 2)" << endl;
#endif

        throw (Exception("Wrong length for int data in MIDI stream"));
    }

    int intRet = ((int)(((MidiByte)bytes[0]) << 8)) |
                 ((int)(((MidiByte)bytes[1])));
    return (intRet);
}



// Gets a single byte from the MIDI byte stream.  For each track
// section we can read only a specified number of bytes held in
// m_trackByteCount.
//
MidiByte
MidiFile::getMidiByte(ifstream* midiFile)
{
    static int bytesGot = 0; // purely for progress reporting purposes

    if (midiFile->eof()) {
        throw(Exception("End of MIDI file encountered while reading"));
    }

    if (m_decrementCount && m_trackByteCount <= 0) {
        throw(Exception("Attempt to get more bytes than expected on Track"));
    }

    char byte;
    if (midiFile->read(&byte, 1)) {

        --m_trackByteCount;

        // update a progress dialog if we have one
        //
        ++bytesGot;
        if (bytesGot % 2000 == 0) {

            emit setProgress((int)(double(midiFile->tellg()) /
                                   double(m_fileSize) * 20.0));
            kapp->processEvents(50);
        }

        return (MidiByte)byte;
    }

    throw(Exception("Attempt to read past MIDI file end"));
}


// Gets a specified number of bytes from the MIDI byte stream.  For
// each track section we can read only a specified number of bytes
// held in m_trackByteCount.
//
string
MidiFile::getMidiBytes(ifstream* midiFile, unsigned long numberOfBytes)
{
    string stringRet;
    char fileMidiByte;
    static int bytesGot = 0; // purely for progress reporting purposes

    if (midiFile->eof()) {
#ifdef MIDI_DEBUG
        std::cerr << "MIDI file EOF - got "
        << stringRet.length() << " bytes out of "
        << numberOfBytes << endl;
#endif

        throw(Exception("End of MIDI file encountered while reading"));

    }

    if (m_decrementCount && (numberOfBytes > (unsigned long)m_trackByteCount)) {
#ifdef MIDI_DEBUG
        std::cerr << "Attempt to get more bytes than allowed on Track ("
        << numberOfBytes
        << " > "
        << m_trackByteCount << endl;
#endif

        //!!! Investigate -- I'm seeing this on new-notation-quantization
        // branch: load glazunov.rg, run Interpret on first segment, export
        // and attempt to import again

        throw(Exception("Attempt to get more bytes than expected on Track"));
    }

    while (stringRet.length() < numberOfBytes &&
            midiFile->read(&fileMidiByte, 1)) {
        stringRet += fileMidiByte;
    }

    // if we've reached the end of file without fulfilling the
    // quota then panic as our parsing has performed incorrectly
    //
    if (stringRet.length() < numberOfBytes) {
        stringRet = "";
#ifdef MIDI_DEBUG

        cerr << "Attempt to read past file end - got "
        << stringRet.length() << " bytes out of "
        << numberOfBytes << endl;
#endif

        throw(Exception("Attempt to read past MIDI file end"));

    }

    // decrement the byte count
    if (m_decrementCount)
        m_trackByteCount -= stringRet.length();

    // update a progress dialog if we have one
    //
    bytesGot += numberOfBytes;
    if (bytesGot % 2000 == 0) {
        emit setProgress((int)(double(midiFile->tellg()) /
                               double(m_fileSize) * 20.0));
        kapp->processEvents(50);
    }

    return stringRet;
}


// Get a long number of variable length from the MIDI byte stream.
//
//
long
MidiFile::getNumberFromMidiBytes(ifstream* midiFile, int firstByte)
{
    long longRet = 0;
    MidiByte midiByte;

    if (firstByte >= 0) {
        midiByte = (MidiByte)firstByte;
    } else if (midiFile->eof()) {
        return longRet;
    } else {
        midiByte = getMidiByte(midiFile);
    }

    longRet = midiByte;
    if (midiByte & 0x80 ) {
        longRet &= 0x7F;
        do {
            midiByte = getMidiByte(midiFile);
            longRet = (longRet << 7) + (midiByte & 0x7F);
        } while (!midiFile->eof() && (midiByte & 0x80));
    }

    return longRet;
}



// Seeks to the next track in the midi file and sets the number
// of bytes to be read in the counter m_trackByteCount.
//
bool
MidiFile::skipToNextTrack(ifstream *midiFile)
{
    string buffer, buffer2;
    m_trackByteCount = -1;
    m_decrementCount = false;

    while (!midiFile->eof() && (m_decrementCount == false )) {
        buffer = getMidiBytes(midiFile, 4);

#if (__GNUC__ < 3)

        if (buffer.compare(MIDI_TRACK_HEADER, 0, 4) == 0)
#else

        if (buffer.compare(0, 4, MIDI_TRACK_HEADER) == 0)
#endif

        {
            m_trackByteCount = midiBytesToLong(getMidiBytes(midiFile, 4));
            m_decrementCount = true;
        }

    }

    if ( m_trackByteCount == -1 ) // we haven't found a track
        return (false);
    else
        return (true);
}


// Read in a MIDI file.  The parsing process throws string
// exceptions back up here if we run into trouble which we
// can then pass back out to whoever called us using a nice
// bool.
//
//
bool
MidiFile::open()
{
    bool retOK = true;
    m_error = "";

#ifdef MIDI_DEBUG

    std::cerr << "MidiFile::open() : fileName = " << m_fileName.c_str() << endl;
#endif

    // Open the file
    ifstream *midiFile = new ifstream(m_fileName.c_str(), ios::in | ios::binary);

    try {
        if (*midiFile) {

            // Set file size so we can count it off
            //
            midiFile->seekg(0, std::ios::end);
            m_fileSize = midiFile->tellg();
            midiFile->seekg(0, std::ios::beg);

            // Parse the MIDI header first.  The first 14 bytes of the file.
            if (!parseHeader(getMidiBytes(midiFile, 14))) {
                m_format = MIDI_FILE_NOT_LOADED;
                m_error = "Not a MIDI file.";
                return (false);
            }

            m_containsTimeChanges = false;

            TrackId i = 0;

            for (unsigned int j = 0; j < m_numberOfTracks; ++j) {

#ifdef MIDI_DEBUG
                std::cerr << "Parsing Track " << j << endl;
#endif

                if (!skipToNextTrack(midiFile)) {
#ifdef MIDI_DEBUG
                    cerr << "Couldn't find Track " << j << endl;
#endif

                    m_error = "File corrupted or in non-standard format?";
                    m_format = MIDI_FILE_NOT_LOADED;
                    return (false);
                }

#ifdef MIDI_DEBUG
                std::cerr << "Track has " << m_trackByteCount << " bytes" << std::endl;
#endif

                // Run through the events taking them into our internal
                // representation.
                if (!parseTrack(midiFile, i)) {
#ifdef MIDI_DEBUG
                    std::cerr << "Track " << j << " parsing failed" << endl;
#endif

                    m_error = "File corrupted or in non-standard format?";
                    m_format = MIDI_FILE_NOT_LOADED;
                    return (false);
                }

                ++i; // j is the source track number, i the destination
            }

            m_numberOfTracks = i;
        } else {
            m_error = "File not found or not readable.";
            m_format = MIDI_FILE_NOT_LOADED;
            return (false);
        }

        // Close the file now
        midiFile->close();
    } catch (Exception e) {
#ifdef MIDI_DEBUG
        std::cerr << "MidiFile::open() - caught exception - "
        << e.getMessage() << endl;
#endif

        m_error = e.getMessage();
        retOK = false;
    }

    return (retOK);
}

// Parse and ensure the MIDI Header is legitimate
//
//
bool
MidiFile::parseHeader(const string &midiHeader)
{
    if (midiHeader.size() < 14) {
#ifdef MIDI_DEBUG
        std::cerr << "MidiFile::parseHeader() - file header undersized" << endl;
#endif

        return (false);
    }

#if (__GNUC__ < 3)
    if (midiHeader.compare(MIDI_FILE_HEADER, 0, 4) != 0)
#else

    if (midiHeader.compare(0, 4, MIDI_FILE_HEADER) != 0)
#endif

    {
#ifdef MIDI_DEBUG
        std::cerr << "MidiFile::parseHeader()"
        << "- file header not found or malformed"
        << endl;
#endif

        return (false);
    }

    if (midiBytesToLong(midiHeader.substr(4, 4)) != 6L) {
#ifdef MIDI_DEBUG
        std::cerr << "MidiFile::parseHeader()"
        << " - header length incorrect"
        << endl;
#endif

        return (false);
    }

    m_format = (MIDIFileFormatType) midiBytesToInt(midiHeader.substr(8, 2));
    m_numberOfTracks = midiBytesToInt(midiHeader.substr(10, 2));
    m_timingDivision = midiBytesToInt(midiHeader.substr(12, 2));

    if ( m_format == MIDI_SEQUENTIAL_TRACK_FILE ) {
#ifdef MIDI_DEBUG
        std::cerr << "MidiFile::parseHeader()"
        << "- can't load sequential track file"
        << endl;
#endif

        return (false);
    }


#ifdef MIDI_DEBUG
    if ( m_timingDivision < 0 ) {
        std::cerr << "MidiFile::parseHeader()"
        << " - file uses SMPTE timing"
        << endl;
    }
#endif

    return (true);
}



// Extract the contents from a MIDI file track and places it into
// our local map of MIDI events.
//
//
bool
MidiFile::parseTrack(ifstream* midiFile, TrackId &lastTrackNum)
{
    MidiByte midiByte, metaEventCode, data1, data2;
    MidiByte eventCode = 0x80;
    std::string metaMessage;
    unsigned int messageLength;
    unsigned long deltaTime;
    unsigned long accumulatedTime = 0;

    // The trackNum passed in to this method is the default track for
    // all events provided they're all on the same channel.  If we find
    // events on more than one channel, we increment trackNum and record
    // the mapping from channel to trackNum in this channelTrackMap.
    // We then return the new trackNum by reference so the calling
    // method knows we've got more tracks than expected.

    // This would be a vector<TrackId> but TrackId is unsigned
    // and we need -1 to indicate "not yet used"
    std::vector<int> channelTrackMap(16, -1);

    // This is used to store the last absolute time found on each track,
    // allowing us to modify delta-times correctly when separating events
    // out from one to multiple tracks
    //
    std::map<int, unsigned long> trackTimeMap;

    // Meta-events don't have a channel, so we place them in a fixed
    // track number instead
    TrackId metaTrack = lastTrackNum;

    // Remember the last non-meta status byte (-1 if we haven't seen one)
    int runningStatus = -1;

    bool firstTrack = true;

    while (!midiFile->eof() && ( m_trackByteCount > 0 ) ) {
        if (eventCode < 0x80) {
#ifdef MIDI_DEBUG
            cerr << "WARNING: Invalid event code " << eventCode
            << " in MIDI file" << endl;
#endif

            throw (Exception("Invalid event code found"));
        }

        deltaTime = getNumberFromMidiBytes(midiFile);

#ifdef MIDI_DEBUG

        cerr << "read delta time " << deltaTime << endl;
#endif

        // Get a single byte
        midiByte = getMidiByte(midiFile);

        if (!(midiByte & MIDI_STATUS_BYTE_MASK)) {
            if (runningStatus < 0) {
                throw (Exception("Running status used for first event in track"));
            }

            eventCode = (MidiByte)runningStatus;
            data1 = midiByte;

#ifdef MIDI_DEBUG

            std::cerr << "using running status (byte " << int(midiByte) << " found)" << std::endl;
#endif

        } else {
#ifdef MIDI_DEBUG
            std::cerr << "have new event code " << int(midiByte) << std::endl;
#endif

            eventCode = midiByte;
            data1 = getMidiByte(midiFile);
        }

        if (eventCode == MIDI_FILE_META_EVENT) // meta events
        {
            //            metaEventCode = getMidiByte(midiFile);
            metaEventCode = data1;
            messageLength = getNumberFromMidiBytes(midiFile);

#ifdef MIDI_DEBUG

            std::cerr << "Meta event of type " << int(metaEventCode) << " and " << messageLength << " bytes found" << std::endl;
#endif

            metaMessage = getMidiBytes(midiFile, messageLength);

            if (metaEventCode == MIDI_TIME_SIGNATURE ||
                    metaEventCode == MIDI_SET_TEMPO)
            {
                m_containsTimeChanges = true;
            }

            long gap = accumulatedTime - trackTimeMap[metaTrack];
            accumulatedTime += deltaTime;
            deltaTime += gap;
            trackTimeMap[metaTrack] = accumulatedTime;

            MidiEvent *e = new MidiEvent(deltaTime,
                                         MIDI_FILE_META_EVENT,
                                         metaEventCode,
                                         metaMessage);

            m_midiComposition[metaTrack].push_back(e);

        } else // the rest
        {
            runningStatus = eventCode;

            MidiEvent *midiEvent;

            int channel = (eventCode & MIDI_CHANNEL_NUM_MASK);
            if (channelTrackMap[channel] == -1) {
                if (!firstTrack)
                    ++lastTrackNum;
                else
                    firstTrack = false;
                channelTrackMap[channel] = lastTrackNum;
            }

            TrackId trackNum = channelTrackMap[channel];

            // accumulatedTime is abs time of last event on any track;
            // trackTimeMap[trackNum] is that of last event on this track

            long gap = accumulatedTime - trackTimeMap[trackNum];
            accumulatedTime += deltaTime;
            deltaTime += gap;
            trackTimeMap[trackNum] = accumulatedTime;

            switch (eventCode & MIDI_MESSAGE_TYPE_MASK) {
            case MIDI_NOTE_ON:
            case MIDI_NOTE_OFF:
            case MIDI_POLY_AFTERTOUCH:
            case MIDI_CTRL_CHANGE:
                data2 = getMidiByte(midiFile);

                // create and store our event
                midiEvent = new MidiEvent(deltaTime, eventCode, data1, data2);

                /*
                std::cerr << "MIDI event for channel " << channel << " (track "
                << trackNum << ")" << std::endl;
                midiEvent->print();
                          */


                m_midiComposition[trackNum].push_back(midiEvent);
                break;

            case MIDI_PITCH_BEND:
                data2 = getMidiByte(midiFile);

                // create and store our event
                midiEvent = new MidiEvent(deltaTime, eventCode, data1, data2);
                m_midiComposition[trackNum].push_back(midiEvent);
                break;

            case MIDI_PROG_CHANGE:
            case MIDI_CHNL_AFTERTOUCH:
                // create and store our event
                midiEvent = new MidiEvent(deltaTime, eventCode, data1);
                m_midiComposition[trackNum].push_back(midiEvent);
                break;

            case MIDI_SYSTEM_EXCLUSIVE:
                messageLength = getNumberFromMidiBytes(midiFile, data1);

#ifdef MIDI_DEBUG

                std::cerr << "SysEx of " << messageLength << " bytes found" << std::endl;
#endif

                metaMessage = getMidiBytes(midiFile, messageLength);

                if (MidiByte(metaMessage[metaMessage.length() - 1]) !=
                        MIDI_END_OF_EXCLUSIVE) {
#ifdef MIDI_DEBUG
                    std::cerr << "MidiFile::parseTrack() - "
                    << "malformed or unsupported SysEx type"
                    << std::endl;
#endif

                    continue;
                }

                // chop off the EOX
                // length fixed by Pedro Lopez-Cabanillas (20030523)
                //
                metaMessage = metaMessage.substr(0, metaMessage.length() - 1);

                midiEvent = new MidiEvent(deltaTime,
                                          MIDI_SYSTEM_EXCLUSIVE,
                                          metaMessage);
                m_midiComposition[trackNum].push_back(midiEvent);
                break;

            case MIDI_END_OF_EXCLUSIVE:
#ifdef MIDI_DEBUG

                std::cerr << "MidiFile::parseTrack() - "
                << "Found a stray MIDI_END_OF_EXCLUSIVE" << std::endl;
#endif

                break;

            default:
#ifdef MIDI_DEBUG

                std::cerr << "MidiFile::parseTrack()"
                << " - Unsupported MIDI Event Code:  "
                << (int)eventCode << endl;
#endif

                break;
            }
        }
    }

    return (true);
}

// borrowed from ALSA pcm_timer.c
//
static unsigned long gcd(unsigned long a, unsigned long b)
{
    unsigned long r;
    if (a < b) {
        r = a;
        a = b;
        b = r;
    }
    while ((r = a % b) != 0) {
        a = b;
        b = r;
    }
    return b;
}

// If we wanted to abstract the MidiFile class to make it more useful to
// other applications (and formats) we'd make this method and its twin
// pure virtual.
//
bool
MidiFile::convertToRosegarden(Composition &composition, ConversionType type)
{
    Profiler profiler("MidiFile::convertToRosegarden");

    MidiTrack::iterator midiEvent;
    Segment *rosegardenSegment;
    Segment *conductorSegment = 0;
    Event *rosegardenEvent;
    string trackName;

    // Time conversions
    //
    timeT rosegardenTime = 0;
    timeT rosegardenDuration = 0;
    timeT maxTime = 0;

    // To create rests
    //
    timeT endOfLastNote;

    // Event specific vars
    //
    int numerator = 4;
    int denominator = 4;
    timeT segmentTime;

    // keys
    int accidentals;
    bool isMinor;
    bool isSharp;

    if (type == CONVERT_REPLACE)
        composition.clear();

    timeT origin = 0;
    if (type == CONVERT_APPEND && composition.getDuration() > 0) {
        origin = composition.getBarEndForTime(composition.getDuration());
    }

    TrackId compTrack = 0;
    for (Composition::iterator ci = composition.begin();
            ci != composition.end(); ++ci) {
        if ((*ci)->getTrack() >= compTrack)
            compTrack = (*ci)->getTrack() + 1;
    }

    Track *track = 0;

    // precalculate the timing factor
    //
    // [cc] -- attempt to avoid floating-point rounding errors
    timeT crotchetTime = Note(Note::Crotchet).getDuration();
    int divisor = m_timingDivision ? m_timingDivision : 96;

    unsigned long multiplier = crotchetTime;
    int g = (int)gcd(crotchetTime, divisor);
    multiplier /= g;
    divisor /= g;

    timeT maxRawTime = LONG_MAX;
    if (multiplier > divisor)
        maxRawTime = (maxRawTime / multiplier) * divisor;

    bool haveTimeSignatures = false;
    InstrumentId compInstrument = MidiInstrumentBase;

    // Clear down the assigned Instruments we already have
    //
    m_studio->unassignAllInstruments();

#ifdef MIDI_DEBUG

    std::cerr << "NUMBER OF TRACKS = " << m_numberOfTracks << endl;
    std::cerr << "MIDI COMP SIZE = " << m_midiComposition.size() << endl;
#endif

    for (TrackId i = 0; i < m_numberOfTracks; i++ ) {
        segmentTime = 0;
        trackName = string("Imported MIDI");

        // progress - 20% total in file import itself and then 80%
        // split over these tracks
        emit setProgress(20 +
                         (int)((80.0 * double(i) / double(m_numberOfTracks))));
        kapp->processEvents(50);

        // Convert the deltaTime to an absolute time since
        // the start of the segment.  The addTime method
        // returns the sum of the current Midi Event delta
        // time plus the argument.
        //
        for (midiEvent = m_midiComposition[i].begin();
                midiEvent != m_midiComposition[i].end();
                ++midiEvent) {
            segmentTime = (*midiEvent)->addTime(segmentTime);
        }

        // Consolidate NOTE ON and NOTE OFF events into a NOTE ON with
        // a duration.
        //
        if (consolidateNoteOffEvents(i)) // returns true if some notes exist
        {
            // If we're true then rotate the instrument number
            compInstrument = MidiInstrumentBase + (compTrack % 16);
        } else
            compInstrument = MidiInstrumentBase;

        rosegardenSegment = new Segment;
        rosegardenSegment->setTrack(compTrack);
        rosegardenSegment->setStartTime(0);

        track = new Track(compTrack,         // id
                          compInstrument,    // instrument
                          compTrack,         // position
                          trackName,         // name
                          false);           // muted

        // rest creation token needs to be reset here
        //
        endOfLastNote = 0;

        int msb = -1, lsb = -1; // for bank selects
        Instrument *instrument = 0;

        for (midiEvent = m_midiComposition[i].begin();
                midiEvent != m_midiComposition[i].end();
                midiEvent++) {
            rosegardenEvent = 0;

            // [cc] -- avoid floating-point where possible

            timeT rawTime = (*midiEvent)->getTime();

            if (rawTime < maxRawTime) {
                rosegardenTime = origin +
                                 timeT((rawTime * multiplier) / divisor);
            } else {
                rosegardenTime = origin +
                                 timeT((double(rawTime) * multiplier) / double(divisor) + 0.01);
            }

            rosegardenDuration =
                timeT(((*midiEvent)->getDuration() * multiplier) / divisor);

#ifdef MIDI_DEBUG

            std::cerr << "MIDI file import: origin " << origin
            << ", event time " << rosegardenTime
            << ", duration " << rosegardenDuration
            << ", event type " << (int)(*midiEvent)->getMessageType()
            << ", previous max time " << maxTime
            << ", potential max time " << (rosegardenTime + rosegardenDuration)
            << ", ev raw time " << (*midiEvent)->getTime()
            << ", crotchet " << crotchetTime
            << ", multiplier " << multiplier
            << ", divisor " << divisor
            << std::endl;
#endif

            if (rosegardenTime + rosegardenDuration > maxTime) {
                maxTime = rosegardenTime + rosegardenDuration;
            }

            //	    timeT fillFromTime = rosegardenTime;
            if (rosegardenSegment->empty()) {
                //		fillFromTime = composition.getBarStartForTime(rosegardenTime);
                endOfLastNote = composition.getBarStartForTime(rosegardenTime);
            }

            if ((*midiEvent)->isMeta()) {
                switch ((*midiEvent)->getMetaEventCode()) {
                case MIDI_TEXT_EVENT: {
                        std::string text = (*midiEvent)->getMetaMessage();
                        rosegardenEvent =
                            Text(text).getAsEvent(rosegardenTime);
                    }
                    break;

                case MIDI_LYRIC: {
                        std::string text = (*midiEvent)->getMetaMessage();
                        rosegardenEvent =
                            Text(text, Text::Lyric).
                            getAsEvent(rosegardenTime);
                    }
                    break;

                case MIDI_TEXT_MARKER: {
                        std::string text = (*midiEvent)->getMetaMessage();
                        composition.addMarker(new Marker
                                              (rosegardenTime, text, ""));
                    }
                    break;

                case MIDI_COPYRIGHT_NOTICE:
                    if (type == CONVERT_REPLACE) {
                        composition.setCopyrightNote((*midiEvent)->
                                                     getMetaMessage());
                    }
                    break;

                case MIDI_TRACK_NAME:
                    track->setLabel((*midiEvent)->getMetaMessage());
                    break;

                case MIDI_END_OF_TRACK: {
                        timeT trackEndTime = rosegardenTime;
                        if (trackEndTime <= 0) {
                            trackEndTime = crotchetTime * 4 * numerator / denominator;
                        }
                        if (endOfLastNote < trackEndTime) {
                            //If there's nothing in the segment yet, then we
                            //shouldn't fill with rests because we don't want
                            //to cause the otherwise empty segment to be created
                            if (rosegardenSegment->size() > 0) {
                                rosegardenSegment->fillWithRests(trackEndTime);
                            }
                        }
                    }
                    break;

                case MIDI_SET_TEMPO: {
                        MidiByte m0 = (*midiEvent)->getMetaMessage()[0];
                        MidiByte m1 = (*midiEvent)->getMetaMessage()[1];
                        MidiByte m2 = (*midiEvent)->getMetaMessage()[2];

                        long tempo = (((m0 << 8) + m1) << 8) + m2;

                        if (tempo != 0) {
                            double qpm = 60000000.0 / double(tempo);
                            tempoT rgt(Composition::getTempoForQpm(qpm));
                            std::cout << "MidiFile: converted MIDI tempo " << tempo << " to Rosegarden tempo " << rgt << std::endl;
                            composition.addTempoAtTime(rosegardenTime, rgt);
                        }
                    }
                    break;

                case MIDI_TIME_SIGNATURE:
                    numerator = (int) (*midiEvent)->getMetaMessage()[0];
                    denominator = 1 << ((int)(*midiEvent)->getMetaMessage()[1]);

                    // NB. a MIDI time signature also has
                    // metamessage[2] and [3], containing some timing data

                    if (numerator == 0)
                        numerator = 4;
                    if (denominator == 0)
                        denominator = 4;

                    composition.addTimeSignature
                    (rosegardenTime,
                     TimeSignature(numerator, denominator));
                    haveTimeSignatures = true;
                    break;

                case MIDI_KEY_SIGNATURE:
                    // get the details
                    accidentals = (int) (*midiEvent)->getMetaMessage()[0];
                    isMinor = (int) (*midiEvent)->getMetaMessage()[1];
                    isSharp = accidentals < 0 ? false : true;
                    accidentals = accidentals < 0 ? -accidentals : accidentals;
                    // create the key event
                    //
                    try {
                        rosegardenEvent = Rosegarden::Key
                                          (accidentals, isSharp, isMinor).
                                          getAsEvent(rosegardenTime);
                    }
                    catch (...) {
#ifdef MIDI_DEBUG
                        std::cerr << "MidiFile::convertToRosegarden - "
                        << " badly formed key signature"
                        << std::endl;
#endif

                        break;
                    }
                    break;

                case MIDI_SEQUENCE_NUMBER:
                case MIDI_CHANNEL_PREFIX_OR_PORT:
                case MIDI_INSTRUMENT_NAME:
                case MIDI_CUE_POINT:
                case MIDI_CHANNEL_PREFIX:
                case MIDI_SEQUENCER_SPECIFIC:
                case MIDI_SMPTE_OFFSET:
                default:
#ifdef MIDI_DEBUG

                    std::cerr << "MidiFile::convertToRosegarden - "
                    << "unsupported META event code "
                    << (int)((*midiEvent)->getMetaEventCode()) << endl;
#endif

                    break;
                }

            } else
                switch ((*midiEvent)->getMessageType()) {
                case MIDI_NOTE_ON:

                    // A zero velocity here is a virtual "NOTE OFF"
                    // so we ignore this event
                    //
                    if ((*midiEvent)->getVelocity() == 0)
                        break;

                    endOfLastNote = rosegardenTime + rosegardenDuration;

                    //std::cerr << "MidiFile::convertToRosegarden: note at " << rosegardenTime << ", midi time " << (*midiEvent)->getTime() << std::endl;

                    // create and populate event
                    rosegardenEvent = new Event(Note::EventType,
                                                rosegardenTime,
                                                rosegardenDuration);
                    rosegardenEvent->set
                    <Int>(BaseProperties::PITCH,
                          (*midiEvent)->getPitch());
                    rosegardenEvent->set
                    <Int>(BaseProperties::VELOCITY,
                          (*midiEvent)->getVelocity());
                    break;

                    // We ignore any NOTE OFFs here as we've already
                    // converted NOTE ONs to have duration
                    //
                case MIDI_NOTE_OFF:
                    continue;
                    break;

                case MIDI_PROG_CHANGE:
                    // Attempt to turn the prog change we've found into an
                    // Instrument.  Send the program number and whether or
                    // not we're on the percussion channel.
                    //
                    // Note that we make no attempt to do the right
                    // thing with program changes during a track -- we
                    // just save them as events.  Only the first is
                    // used to select the instrument.  If it's at time
                    // zero, it's not saved as an event.
                    //
                    if (!instrument) {

                        if (msb >= 0 || lsb >= 0) {
                            instrument = m_studio->assignMidiProgramToInstrument
                                         ((*midiEvent)->getData1(),
                                          (msb >= 0 ? msb : 0),
                                          (lsb >= 0 ? lsb : 0),
                                          (*midiEvent)->getChannelNumber() ==
                                          MIDI_PERCUSSION_CHANNEL);
                            msb = -1;
                            lsb = -1;
                        } else {
                            instrument = m_studio->assignMidiProgramToInstrument
                                         ((*midiEvent)->getData1(),
                                          (*midiEvent)->getChannelNumber() ==
                                          MIDI_PERCUSSION_CHANNEL);
                        }

                        // assign it here
                        if (instrument) {

                            track->setInstrument(instrument->getId());

                            // give the Segment a name based on the the Instrument
                            //
                            rosegardenSegment->setLabel
                            (m_studio->getSegmentName(instrument->getId()));

                            if ((*midiEvent)->getTime() == 0)
                                break; // no insert
                        }
                    }

                    // did we have a bank select? if so, insert that too

                    if (msb >= 0) {
                        rosegardenSegment->insert
                        (Controller(MIDI_CONTROLLER_BANK_MSB, msb).
                         getAsEvent(rosegardenTime));
                    }
                    if (lsb >= 0) {
                        rosegardenSegment->insert
                        (Controller(MIDI_CONTROLLER_BANK_LSB, msb).
                         getAsEvent(rosegardenTime));
                    }

                    rosegardenEvent =
                        ProgramChange((*midiEvent)->getData1()).
                        getAsEvent(rosegardenTime);
                    break;

                case MIDI_CTRL_CHANGE:

                    // If it's a bank select, interpret it (or remember
                    // for later insertion) instead of just inserting it
                    // as a Rosegarden event

                    if ((*midiEvent)->getData1() == MIDI_CONTROLLER_BANK_MSB) {
                        msb = (*midiEvent)->getData2();
                        break;
                    }

                    if ((*midiEvent)->getData1() == MIDI_CONTROLLER_BANK_LSB) {
                        lsb = (*midiEvent)->getData2();
                        break;
                    }

                    // If it's something we can use as an instrument
                    // parameter, and it's at time zero, and we already
                    // have an instrument, then apply it to the instrument
                    // instead of inserting

                    if (instrument && (*midiEvent)->getTime() == 0) {
                        if ((*midiEvent)->getData1() == MIDI_CONTROLLER_VOLUME) {
                            instrument->setVolume((*midiEvent)->getData2());
                            break;
                        }
                        if ((*midiEvent)->getData1() == MIDI_CONTROLLER_PAN) {
                            instrument->setPan((*midiEvent)->getData2());
                            break;
                        }
                        if ((*midiEvent)->getData1() == MIDI_CONTROLLER_ATTACK) {
                            instrument->setControllerValue(MIDI_CONTROLLER_ATTACK, (*midiEvent)->getData2());
                            break;
                        }
                        if ((*midiEvent)->getData1() == MIDI_CONTROLLER_RELEASE) {
                            instrument->setControllerValue(MIDI_CONTROLLER_RELEASE, (*midiEvent)->getData2());
                            break;
                        }
                        if ((*midiEvent)->getData1() == MIDI_CONTROLLER_FILTER) {
                            instrument->setControllerValue(MIDI_CONTROLLER_FILTER, (*midiEvent)->getData2());
                            break;
                        }
                        if ((*midiEvent)->getData1() == MIDI_CONTROLLER_RESONANCE) {
                            instrument->setControllerValue(MIDI_CONTROLLER_RESONANCE, (*midiEvent)->getData2());
                            break;
                        }
                        if ((*midiEvent)->getData1() == MIDI_CONTROLLER_CHORUS) {
                            instrument->setControllerValue(MIDI_CONTROLLER_CHORUS, (*midiEvent)->getData2());
                            break;
                        }
                        if ((*midiEvent)->getData1() == MIDI_CONTROLLER_REVERB) {
                            instrument->setControllerValue(MIDI_CONTROLLER_REVERB, (*midiEvent)->getData2());
                            break;
                        }
                    }

                    rosegardenEvent =
                        Controller((*midiEvent)->getData1(),
                                   (*midiEvent)->getData2()).
                        getAsEvent(rosegardenTime);
                    break;

                case MIDI_PITCH_BEND:
                    rosegardenEvent =
                        PitchBend((*midiEvent)->getData2(),
                                  (*midiEvent)->getData1()).
                        getAsEvent(rosegardenTime);
                    break;

                case MIDI_SYSTEM_EXCLUSIVE:
                    rosegardenEvent =
                        SystemExclusive((*midiEvent)->getMetaMessage()).
                        getAsEvent(rosegardenTime);
                    break;

                case MIDI_POLY_AFTERTOUCH:
                    rosegardenEvent =
                        KeyPressure((*midiEvent)->getData1(),
                                    (*midiEvent)->getData2()).
                        getAsEvent(rosegardenTime);
                    break;

                case MIDI_CHNL_AFTERTOUCH:
                    rosegardenEvent =
                        ChannelPressure((*midiEvent)->getData1()).
                        getAsEvent(rosegardenTime);
                    break;

                default:
#ifdef MIDI_DEBUG

                    std::cerr << "MidiFile::convertToRosegarden - "
                    << "Unsupported event code = "
                    << (int)(*midiEvent)->getMessageType() << std::endl;
#endif

                    break;
                }

            if (rosegardenEvent) {
                //		if (fillFromTime < rosegardenTime) {
                //		    rosegardenSegment->fillWithRests(fillFromTime, rosegardenTime);
                //		}
                if (endOfLastNote < rosegardenTime) {
                    rosegardenSegment->fillWithRests(endOfLastNote, rosegardenTime);
                }
                rosegardenSegment->insert(rosegardenEvent);
            }
        }

        if (rosegardenSegment->size() > 0) {

            // if all we have is key signatures and rests, take this
            // to be a conductor segment and don't insert it
            //
            bool keySigsOnly = true;
            bool haveKeySig = false;
            for (Segment::iterator i = rosegardenSegment->begin();
                    i != rosegardenSegment->end(); ++i) {
                if (!(*i)->isa(Rosegarden::Key::EventType) &&
                        !(*i)->isa(Note::EventRestType)) {
                    keySigsOnly = false;
                    break;
                } else if ((*i)->isa(Rosegarden::Key::EventType)) {
                    haveKeySig = true;
                }
            }

            if (keySigsOnly) {
                conductorSegment = rosegardenSegment;
                continue;
            } else if (!haveKeySig && conductorSegment) {
                // copy across any key sigs from the conductor segment

                timeT segmentStartTime = rosegardenSegment->getStartTime();
                timeT earliestEventEndTime = segmentStartTime;

                for (Segment::iterator i = conductorSegment->begin();
                        i != conductorSegment->end(); ++i) {
                    if ((*i)->getAbsoluteTime() + (*i)->getDuration() <
                            earliestEventEndTime) {
                        earliestEventEndTime =
                            (*i)->getAbsoluteTime() + (*i)->getDuration();
                    }
                    rosegardenSegment->insert(new Event(**i));
                }

                if (earliestEventEndTime < segmentStartTime) {
                    rosegardenSegment->fillWithRests(earliestEventEndTime,
                                                     segmentStartTime);
                }
            }

#ifdef MIDI_DEBUG
            std::cerr << "MIDI import: adding segment with start time " << rosegardenSegment->getStartTime() << " and end time " << rosegardenSegment->getEndTime() << std::endl;
#endif

            // add the Segment to the Composition and increment the
            // Rosegarden segment number
            //
            composition.addTrack(track);
            composition.addSegment(rosegardenSegment);
            compTrack++;

        } else {
            delete rosegardenSegment;
            rosegardenSegment = 0;
            delete track;
            track = 0;
        }
    }

    if (type == CONVERT_REPLACE || maxTime > composition.getEndMarker()) {
        composition.setEndMarker(composition.getBarEndForTime(maxTime));
    }

    return true;
}

// Takes a Composition and turns it into internal MIDI representation
// that can then be written out to file.
//
// For the moment we should watch to make sure that multiple Segment
// (parts) don't equate to multiple segments in the MIDI Composition.
//
// This is a two pass operation - firstly convert the RG Composition
// into MIDI events and insert anything extra we need (i.e. NOTE OFFs)
// with absolute times before then processing all timings into delta
// times.
//
//
void
MidiFile::convertToMidi(Composition &comp)
{
    MidiEvent *midiEvent;
    int conductorTrack = 0;

    timeT midiEventAbsoluteTime;
    MidiByte midiVelocity;
    MidiByte midiChannel = 0;

    // [cc] int rather than floating point
    //
    m_timingDivision = 480; //!!! make this configurable
    timeT crotchetDuration = Note(Note::Crotchet).getDuration();

    // Export as this format only
    //
    m_format = MIDI_SIMULTANEOUS_TRACK_FILE;

    // Clear out the MidiComposition internal store
    //
    clearMidiComposition();

    // Insert the Rosegarden Signature Track here and any relevant
    // file META information - this will get written out just like
    // any other MIDI track.
    //
    midiEvent = new MidiEvent(0, MIDI_FILE_META_EVENT, MIDI_COPYRIGHT_NOTICE,
                              comp.getCopyrightNote());

    m_midiComposition[conductorTrack].push_back(midiEvent);

    midiEvent = new MidiEvent(0, MIDI_FILE_META_EVENT, MIDI_CUE_POINT,
                              "Created by Rosegarden");

    m_midiComposition[conductorTrack].push_back(midiEvent);

    midiEvent = new MidiEvent(0, MIDI_FILE_META_EVENT, MIDI_CUE_POINT,
                              "http://www.rosegardenmusic.com/");

    m_midiComposition[conductorTrack].push_back(midiEvent);

    // Insert tempo events
    //
    for (int i = 0; i < comp.getTempoChangeCount(); i++) // i=0 should be comp.getStart-something
    {
        std::pair<timeT, tempoT> tempo = comp.getTempoChange(i);

        midiEventAbsoluteTime = tempo.first * m_timingDivision
                                / crotchetDuration;

        double qpm = Composition::getTempoQpm(tempo.second);
        long tempoValue = long(60000000.0 / qpm + 0.01);

        string tempoString;
        tempoString += (MidiByte) ( tempoValue >> 16 & 0xFF );
        tempoString += (MidiByte) ( tempoValue >> 8 & 0xFF );
        tempoString += (MidiByte) ( tempoValue & 0xFF );

        midiEvent = new MidiEvent(midiEventAbsoluteTime,
                                  MIDI_FILE_META_EVENT,
                                  MIDI_SET_TEMPO,
                                  tempoString);

        m_midiComposition[conductorTrack].push_back(midiEvent);
    }

    // Insert time signatures (don't worry that the times might be out
    // of order with those of the tempo events -- we sort the track later)
    //
    for (int i = 0; i < comp.getTimeSignatureCount(); i++) {
        std::pair<timeT, TimeSignature> timeSig =
            comp.getTimeSignatureChange(i);

        midiEventAbsoluteTime = timeSig.first * m_timingDivision
                                / crotchetDuration;

        string timeSigString;
        timeSigString += (MidiByte) (timeSig.second.getNumerator());
        int denominator = timeSig.second.getDenominator();
        int denPowerOf2 = 0;

        // Work out how many powers of two are in the denominator
        //
        while (denominator >>= 1)
            denPowerOf2++;

        timeSigString += (MidiByte) denPowerOf2;

        // The third byte is the number of MIDI clocks per beat.
        // There are 24 clocks per quarter-note (the MIDI clock
        // is tempo-independent and is not related to the timebase).
        //
        int cpb = 24 * timeSig.second.getBeatDuration() / crotchetDuration;
        timeSigString += (MidiByte) cpb;

        // And the fourth byte is always 8, for us (it expresses
        // the number of notated 32nd-notes in a MIDI quarter-note,
        // for applications that may want to notate and perform
        // in different units)
        //
        timeSigString += (MidiByte) 8;

        midiEvent = new MidiEvent(midiEventAbsoluteTime,
                                  MIDI_FILE_META_EVENT,
                                  MIDI_TIME_SIGNATURE,
                                  timeSigString);

        m_midiComposition[conductorTrack].push_back(midiEvent);
    }

    // Insert markers
    // fix for bug#
    Composition::markercontainer marks = comp.getMarkers();

    for (unsigned int i = 0; i < marks.size(); i++) {
        midiEventAbsoluteTime = marks[i]->getTime() * m_timingDivision
                                / crotchetDuration;

        midiEvent = new MidiEvent( midiEventAbsoluteTime,
                                   MIDI_FILE_META_EVENT,
                                   MIDI_TEXT_MARKER,
                                   marks[i]->getName() );

        m_midiComposition[conductorTrack].push_back(midiEvent);
    }

    m_numberOfTracks = 1;
    std::map<int, int> trackPosMap; // RG track pos -> MIDI track no

    // In pass one just insert all events including new NOTE OFFs at the right
    // absolute times.
    //
    for (Composition::const_iterator segment = comp.begin();
            segment != comp.end(); ++segment) {

        // We use this later to get NOTE durations
        //
        SegmentPerformanceHelper helper(**segment);

        Track *track = comp.getTrackById((*segment)->getTrack());

        if (track->isMuted()) continue;

	// Fix #1602023, map Rosegarden tracks to MIDI tracks, instead of
	// putting each segment out on a new track

	int trackPosition = track->getPosition();
	bool firstSegmentThisTrack = false;

	if (trackPosMap.find(trackPosition) == trackPosMap.end()) {
	    firstSegmentThisTrack = true;
	    trackPosMap[trackPosition] = m_numberOfTracks++;
	}

	int trackNumber = trackPosMap[trackPosition];

	MidiTrack &mtrack = m_midiComposition[trackNumber];

	midiEvent = new MidiEvent(0,
				  MIDI_FILE_META_EVENT,
				  MIDI_TRACK_NAME,
				  track->getLabel());

	mtrack.push_back(midiEvent);

        // Get the Instrument
        //
        Instrument *instr =
            m_studio->getInstrumentById(track->getInstrument());

	if (firstSegmentThisTrack) {

	    MidiByte program = 0;
	    midiChannel = 0;

	    bool useBank = false;
	    MidiByte lsb = 0;
	    MidiByte msb = 0;

	    if (instr) {
		midiChannel = instr->getMidiChannel();
		program = instr->getProgramChange();
		if (instr->sendsBankSelect()) {
		    lsb = instr->getLSB();
		    msb = instr->getMSB();
		    useBank = true;
		}
	    }
	    
	    if (useBank) {
		
		// insert a bank select
		
		if (msb != 0) {
		    midiEvent = new MidiEvent(0,
					      MIDI_CTRL_CHANGE | midiChannel,
					      MIDI_CONTROLLER_BANK_MSB,
					      msb);
		    mtrack.push_back(midiEvent);
		}

		if (lsb != 0) {
		    midiEvent = new MidiEvent(0,
					      MIDI_CTRL_CHANGE | midiChannel,
					      MIDI_CONTROLLER_BANK_LSB,
					      lsb);
		    mtrack.push_back(midiEvent);
		}
	    }

	    // insert a program change
	    midiEvent = new MidiEvent(0,  // time
				      MIDI_PROG_CHANGE | midiChannel,
				      program);
	    mtrack.push_back(midiEvent);

	    if (instr) {

		// MidiInstrument parameters: volume, pan, attack,
		// release, filter, resonance, chorus, reverb.  Always
		// write these: the Instrument has an additional parameter
		// to record whether they should be sent, but it isn't
		// actually set anywhere so we have to ignore it.

		static int controllers[] = {
		    MIDI_CONTROLLER_ATTACK,
		    MIDI_CONTROLLER_RELEASE,
		    MIDI_CONTROLLER_FILTER,
		    MIDI_CONTROLLER_RESONANCE,
		    MIDI_CONTROLLER_CHORUS,
		    MIDI_CONTROLLER_REVERB
		};

		mtrack.push_back
		    (new MidiEvent(0, MIDI_CTRL_CHANGE | midiChannel,
				   MIDI_CONTROLLER_VOLUME, instr->getVolume()));

		mtrack.push_back
		    (new MidiEvent(0, MIDI_CTRL_CHANGE | midiChannel,
				   MIDI_CONTROLLER_PAN, instr->getPan()));

		for (int i = 0; i < sizeof(controllers)/sizeof(controllers[0]); ++i) {
		    try {
			mtrack.push_back
			    (new MidiEvent
			     (0, MIDI_CTRL_CHANGE | midiChannel, controllers[i],
			      instr->getControllerValue(controllers[i])));
		    } catch (...) {
			/* do nothing */
		    }
		}
	    } // if (instr)
	} // if (firstSegmentThisTrack)

        timeT segmentMidiDuration =
            ((*segment)->getEndMarkerTime() -
             (*segment)->getStartTime()) * m_timingDivision /
            crotchetDuration;

        for (Segment::iterator el = (*segment)->begin();
	     (*segment)->isBeforeEndMarker(el); ++el) {
            midiEventAbsoluteTime =
                (*el)->getAbsoluteTime() + (*segment)->getDelay();

            timeT absoluteTimeLimit = midiEventAbsoluteTime;
            if ((*segment)->isRepeating()) {
                absoluteTimeLimit = ((*segment)->getRepeatEndTime() - 1) +
		    (*segment)->getDelay();
            }

            if ((*segment)->getRealTimeDelay() != RealTime::zeroTime) {
                RealTime evRT = comp.getElapsedRealTime(midiEventAbsoluteTime);
                timeT timeBeforeDelay = midiEventAbsoluteTime;
                midiEventAbsoluteTime = comp.getElapsedTimeForRealTime
		    (evRT + (*segment)->getRealTimeDelay());
                absoluteTimeLimit += (midiEventAbsoluteTime - timeBeforeDelay);
            }

            midiEventAbsoluteTime =
                midiEventAbsoluteTime * m_timingDivision / crotchetDuration;
            absoluteTimeLimit =
                absoluteTimeLimit * m_timingDivision / crotchetDuration;

            while (midiEventAbsoluteTime <= absoluteTimeLimit) {

                try {

                    if ((*el)->isa(Note::EventType)) {
                        if ((*el)->has(BaseProperties::VELOCITY))
                            midiVelocity = (*el)->get
				<Int>(BaseProperties::VELOCITY);
                        else
                            midiVelocity = 127;

                        // Get the sounding time for the matching NOTE_OFF.
                        // We use SegmentPerformanceHelper::getSoundingDuration()
                        // to work out the tied duration of the NOTE.
                        timeT soundingDuration = helper.getSoundingDuration(el);
                        if (soundingDuration > 0) {

                            timeT midiEventEndTime = midiEventAbsoluteTime +
				soundingDuration * m_timingDivision /
				crotchetDuration;

                            long pitch = 60;
                            (*el)->get
				<Int>(BaseProperties::PITCH, pitch);
                            pitch += (*segment)->getTranspose();

                            // insert the NOTE_ON at the appropriate channel
                            //
                            midiEvent =
                                new MidiEvent(midiEventAbsoluteTime,
                                              MIDI_NOTE_ON | midiChannel,
                                              pitch,
                                              midiVelocity);

                            mtrack.push_back(midiEvent);

                            // insert the matching NOTE OFF
                            //
                            midiEvent =
                                new MidiEvent(midiEventEndTime,
                                              MIDI_NOTE_OFF | midiChannel,
                                              pitch,
                                              127); // full volume silence

                            mtrack.push_back(midiEvent);
                        }
                    } else if ((*el)->isa(PitchBend::EventType)) {
                        PitchBend pb(**el);
                        midiEvent =
                            new MidiEvent(midiEventAbsoluteTime,
                                          MIDI_PITCH_BEND | midiChannel,
                                          pb.getLSB(), pb.getMSB());

                        mtrack.push_back(midiEvent);
                    } else if ((*el)->isa(Rosegarden::Key::EventType)) {
                        Rosegarden::Key key(**el);

                        int accidentals = key.getAccidentalCount();
                        if (!key.isSharp())
                            accidentals = -accidentals;

                        // stack out onto the meta string
                        //
                        std::string metaMessage;
                        metaMessage += MidiByte(accidentals);
                        metaMessage += MidiByte(key.isMinor());

                        midiEvent =
                            new MidiEvent(midiEventAbsoluteTime,
                                          MIDI_FILE_META_EVENT,
                                          MIDI_KEY_SIGNATURE,
                                          metaMessage);

                        //mtrack.push_back(midiEvent);

                    } else if ((*el)->isa(Controller::EventType)) {
                        Controller c(**el);
                        midiEvent =
                            new MidiEvent(midiEventAbsoluteTime,
                                          MIDI_CTRL_CHANGE | midiChannel,
                                          c.getNumber(), c.getValue());

                        mtrack.push_back(midiEvent);
                    } else if ((*el)->isa(ProgramChange::EventType)) {
                        ProgramChange pc(**el);
                        midiEvent =
                            new MidiEvent(midiEventAbsoluteTime,
                                          MIDI_PROG_CHANGE | midiChannel,
                                          pc.getProgram());

                        mtrack.push_back(midiEvent);
                    } else if ((*el)->isa(SystemExclusive::EventType)) {
                        SystemExclusive s(**el);
                        std::string data = s.getRawData();

                        // check for closing EOX and add one if none found
                        //
                        if (MidiByte(data[data.length() - 1]) != MIDI_END_OF_EXCLUSIVE) {
                            data += MIDI_END_OF_EXCLUSIVE;
                        }

                        // construct plain SYSEX event
                        //
                        midiEvent = new MidiEvent(midiEventAbsoluteTime,
                                                  MIDI_SYSTEM_EXCLUSIVE,
                                                  data);

                        mtrack.push_back(midiEvent);

                    } else if ((*el)->isa(ChannelPressure::EventType)) {
                        ChannelPressure cp(**el);
                        midiEvent =
                            new MidiEvent(midiEventAbsoluteTime,
                                          MIDI_CHNL_AFTERTOUCH | midiChannel,
                                          cp.getPressure());

                        mtrack.push_back(midiEvent);
                    } else if ((*el)->isa(KeyPressure::EventType)) {
                        KeyPressure kp(**el);
                        midiEvent =
                            new MidiEvent(midiEventAbsoluteTime,
                                          MIDI_POLY_AFTERTOUCH | midiChannel,
                                          kp.getPitch(), kp.getPressure());

                        mtrack.push_back(midiEvent);
                    } else if ((*el)->isa(Text::EventType)) {
                        Text text(**el);
                        std::string metaMessage = text.getText();

                        MidiByte midiTextType = MIDI_TEXT_EVENT;

                        if (text.getTextType() == Text::Lyric) {
                            midiTextType = MIDI_LYRIC;
                        }

                        if (text.getTextType() != Text::Annotation) {
                            // (we don't write annotations)

                            midiEvent =
                                new MidiEvent(midiEventAbsoluteTime,
                                              MIDI_FILE_META_EVENT,
                                              midiTextType,
                                              metaMessage);

                            mtrack.push_back(midiEvent);
                        }
                    } else if ((*el)->isa(Note::EventRestType)) {
                        // skip legitimately
                    } else {
                        /*
                          cerr << "MidiFile::convertToMidi - "
                          << "unsupported MidiType \""
                          << (*el)->getType()
                          << "\" at export"
                          << std::endl;
                        */
                    }

                } catch (MIDIValueOutOfRange r) {
#ifdef MIDI_DEBUG
                    std::cerr << "MIDI value out of range at "
			      << (*el)->getAbsoluteTime() << std::endl;
#endif

                } catch (Event::NoData d) {
#ifdef MIDI_DEBUG
                    std::cerr << "Caught Event::NoData at "
			      << (*el)->getAbsoluteTime() << ", message is:"
			      << std::endl << d.getMessage() << std::endl;
#endif

                } catch (Event::BadType b) {
#ifdef MIDI_DEBUG
                    std::cerr << "Caught Event::BadType at "
			      << (*el)->getAbsoluteTime() << ", message is:"
			      << std::endl << b.getMessage() << std::endl;
#endif

                } catch (SystemExclusive::BadEncoding e) {
#ifdef MIDI_DEBUG
                    std::cerr << "Caught bad SysEx encoding at "
			      << (*el)->getAbsoluteTime() << std::endl;
#endif

                }

                if (segmentMidiDuration > 0) {
                    midiEventAbsoluteTime += segmentMidiDuration;
                } else
                    break;
            }
        }
    }

    // Now gnash through the MIDI events and turn the absolute times
    // into delta times.
    //
    //
    MidiTrack::iterator it;
    timeT deltaTime, lastMidiTime;

    for (TrackId i = 0; i < m_numberOfTracks; i++) {
        lastMidiTime = 0;

        // First sort the track with the MidiEvent comparator.  Use
        // stable_sort so that events with equal times are maintained
        // in their current order (important for e.g. bank-program
        // pairs, or the controllers at the start of the track which
        // should follow the program so we can treat them correctly
        // when re-reading).
        //
        std::stable_sort(m_midiComposition[i].begin(),
                         m_midiComposition[i].end(),
                         MidiEventCmp());

        for (it = m_midiComposition[i].begin();
                it != m_midiComposition[i].end();
                it++) {
            deltaTime = (*it)->getTime() - lastMidiTime;
            lastMidiTime = (*it)->getTime();
            (*it)->setTime(deltaTime);
        }

        // Insert end of track event (delta time = 0)
        //
        midiEvent = new MidiEvent(0, MIDI_FILE_META_EVENT,
                                  MIDI_END_OF_TRACK, "");

        m_midiComposition[i].push_back(midiEvent);

    }

    return ;
}



// Convert an integer into a two byte representation and
// write out to the MidiFile.
//
void
MidiFile::intToMidiBytes(std::ofstream* midiFile, int number)
{
    MidiByte upper;
    MidiByte lower;

    upper = (number & 0xFF00) >> 8;
    lower = (number & 0x00FF);

    *midiFile << (MidiByte) upper;
    *midiFile << (MidiByte) lower;

}

void
MidiFile::longToMidiBytes(std::ofstream* midiFile, unsigned long number)
{
    MidiByte upper1;
    MidiByte lower1;
    MidiByte upper2;
    MidiByte lower2;

    upper1 = (number & 0xff000000) >> 24;
    lower1 = (number & 0x00ff0000) >> 16;
    upper2 = (number & 0x0000ff00) >> 8;
    lower2 = (number & 0x000000ff);

    *midiFile << (MidiByte) upper1;
    *midiFile << (MidiByte) lower1;
    *midiFile << (MidiByte) upper2;
    *midiFile << (MidiByte) lower2;

}

// Turn a delta time into a MIDI time - overlapping into
// a maximum of four bytes using the MSB as the carry on
// flag.
//
std::string
MidiFile::longToVarBuffer(unsigned long number)
{
    std::string rS;

    long inNumber = number;
    long outNumber;

    // get the lowest 7 bits of the number
    outNumber = number & 0x7f;

    // Shift and test and move the numbers
    // on if we need them - setting the MSB
    // as we go.
    //
    while ((inNumber >>= 7 ) > 0) {
        outNumber <<= 8;
        outNumber |= 0x80;
        outNumber += (inNumber & 0x7f);
    }

    // Now move the converted number out onto the buffer
    //
    while (true) {
        rS += (MidiByte)(outNumber & 0xff);
        if (outNumber & 0x80)
            outNumber >>= 8;
        else
            break;
    }

    return rS;
}



// Write out the MIDI file header
//
bool
MidiFile::writeHeader(std::ofstream* midiFile)
{
    // Our identifying Header string
    //
    *midiFile << MIDI_FILE_HEADER.c_str();

    // Write number of Bytes to follow
    //
    *midiFile << (MidiByte) 0x00;
    *midiFile << (MidiByte) 0x00;
    *midiFile << (MidiByte) 0x00;
    *midiFile << (MidiByte) 0x06;

    // Write File Format
    //
    *midiFile << (MidiByte) 0x00;
    *midiFile << (MidiByte) m_format;

    // Number of Tracks we're writing out
    //
    intToMidiBytes(midiFile, m_numberOfTracks);

    // Timing Division
    //
    intToMidiBytes(midiFile, m_timingDivision);

    return (true);
}

// Write a MIDI track to file
//
bool
MidiFile::writeTrack(std::ofstream* midiFile, TrackId trackNumber)
{
    bool retOK = true;
    MidiByte eventCode = 0;
    MidiTrack::iterator midiEvent;

    // First we write into the trackBuffer, then write it out to the
    // file with it's accompanying length.
    //
    string trackBuffer;

    long progressTotal = m_midiComposition[trackNumber].size();
    long progressCount = 0;

    for (midiEvent = m_midiComposition[trackNumber].begin();
            midiEvent != m_midiComposition[trackNumber].end();
            midiEvent++) {
        // Write the time to the buffer in MIDI format
        //
        //
        trackBuffer += longToVarBuffer((*midiEvent)->getTime());

        if ((*midiEvent)->isMeta()) {
            trackBuffer += MIDI_FILE_META_EVENT;
            trackBuffer += (*midiEvent)->getMetaEventCode();

            // Variable length number field
            trackBuffer += longToVarBuffer((*midiEvent)->
                                           getMetaMessage().length());

            trackBuffer += (*midiEvent)->getMetaMessage();
        } else {
            // Send the normal event code (with encoded channel information)
            //
            // Fix for 674731 by Pedro Lopez-Cabanillas (20030531)
            if (((*midiEvent)->getEventCode() != eventCode) ||
                    ((*midiEvent)->getEventCode() == MIDI_SYSTEM_EXCLUSIVE)) {
                trackBuffer += (*midiEvent)->getEventCode();
                eventCode = (*midiEvent)->getEventCode();
            }

            // Send the relevant data
            //
            switch ((*midiEvent)->getMessageType()) {
            case MIDI_NOTE_ON:
            case MIDI_NOTE_OFF:
            case MIDI_POLY_AFTERTOUCH:
                trackBuffer += (*midiEvent)->getData1();
                trackBuffer += (*midiEvent)->getData2();
                break;

            case MIDI_CTRL_CHANGE:
                trackBuffer += (*midiEvent)->getData1();
                trackBuffer += (*midiEvent)->getData2();
                break;

            case MIDI_PROG_CHANGE:
                trackBuffer += (*midiEvent)->getData1();
                break;

            case MIDI_CHNL_AFTERTOUCH:
                trackBuffer += (*midiEvent)->getData1();
                break;

            case MIDI_PITCH_BEND:
                trackBuffer += (*midiEvent)->getData1();
                trackBuffer += (*midiEvent)->getData2();
                break;

            case MIDI_SYSTEM_EXCLUSIVE:

                // write out message length
                trackBuffer +=
                    longToVarBuffer((*midiEvent)->getMetaMessage().length());

                // now the message
                trackBuffer += (*midiEvent)->getMetaMessage();

                break;

            default:
#ifdef MIDI_DEBUG

                std::cerr << "MidiFile::writeTrack()"
                << " - cannot write unsupported MIDI event"
                << endl;
#endif

                break;
            }
        }

        // For the moment just keep the app updating until we work
        // out a good way of accounting for this write.
        //
        ++progressCount;

        if (progressCount % 500 == 0) {
            emit setProgress(progressCount * 100 / progressTotal);
            kapp->processEvents(500);
        }
    }

    // Now we write the track - First the standard header..
    //
    *midiFile << MIDI_TRACK_HEADER.c_str();

    // ..now the length of the buffer..
    //
    longToMidiBytes(midiFile, (long)trackBuffer.length());

    // ..then the buffer itself..
    //
    *midiFile << trackBuffer;

    return (retOK);
}

// Writes out a MIDI file from the internal Midi representation
//
bool
MidiFile::write()
{
    bool retOK = true;

    std::ofstream *midiFile =
        new std::ofstream(m_fileName.c_str(), ios::out | ios::binary);


    if (!(*midiFile)) {
#ifdef MIDI_DEBUG
        std::cerr << "MidiFile::write() - can't write file" << endl;
#endif

        m_format = MIDI_FILE_NOT_LOADED;
        return false;
    }

    // Write out the Header
    //
    writeHeader(midiFile);

    // And now the tracks
    //
    for (TrackId i = 0; i < m_numberOfTracks; i++ )
        if (!writeTrack(midiFile, i))
            retOK = false;

    midiFile->close();

    if (!retOK)
        m_format = MIDI_FILE_NOT_LOADED;

    return (retOK);
}

// Delete dead NOTE OFF and NOTE ON/Zero Velocty Events after
// reading them and modifying their relevant NOTE ONs
//
bool
MidiFile::consolidateNoteOffEvents(TrackId track)
{
    MidiTrack::iterator nOE, mE = m_midiComposition[track].begin();
    bool notesOnTrack = false;
    bool noteOffFound;

    for (;mE != m_midiComposition[track].end(); mE++) {
        if ((*mE)->getMessageType() == MIDI_NOTE_ON && (*mE)->getVelocity() > 0) {
            // We've found a note - flag it
            //
            if (!notesOnTrack)
                notesOnTrack = true;

            noteOffFound = false;

            for (nOE = mE; nOE != m_midiComposition[track].end(); nOE++) {
                if (((*nOE)->getChannelNumber() == (*mE)->getChannelNumber()) &&
                        ((*nOE)->getPitch() == (*mE)->getPitch()) &&
                        ((*nOE)->getMessageType() == MIDI_NOTE_OFF ||
                         ((*nOE)->getMessageType() == MIDI_NOTE_ON &&
                          (*nOE)->getVelocity() == 0x00))) {
                    (*mE)->setDuration((*nOE)->getTime() - (*mE)->getTime());

                    delete *nOE;
                    m_midiComposition[track].erase(nOE);

                    noteOffFound = true;
                    break;
                }
            }

            // If no matching NOTE OFF has been found then set
            // Event duration to length of Segment
            //
            if (noteOffFound == false) {
                --nOE; // avoid crash due to nOE == track.end()
                (*mE)->setDuration((*nOE)->getTime() - (*mE)->getTime());
            }
        }
    }

    return notesOnTrack;
}

// Clear down the MidiFile Composition
//
void
MidiFile::clearMidiComposition()
{
    for (MidiComposition::iterator ci = m_midiComposition.begin();
            ci != m_midiComposition.end(); ++ci) {

        //std::cerr << "MidiFile::clearMidiComposition: track " << ci->first << std::endl;

        for (MidiTrack::iterator ti = ci->second.begin();
                ti != ci->second.end(); ++ti) {
            delete *ti;
        }

        ci->second.clear();
    }

    m_midiComposition.clear();
}

// Doesn't do anything yet - doesn't need to.  We need to satisfy
// the pure virtual function in the base class.
//
void
MidiFile::close()
{}



}

#include "MidiFile.moc"
