~munt/munt/trunk

« back to all changes in this revision

Viewing changes to mt32emu/src/Synth.cpp

  • Committer: sergm
  • Date: 2019-08-25 13:31:57 UTC
  • Revision ID: git-v1:1ea0469877a5a30e146ec9141bb50649901073d6
Refactored MidiEventQueue

Introduced helper interface SysexDataStorage that facilitates
various ways the SysEx data is stored. Specifically,
along with the dynamic memory allocation, a storage buffer
can now be preallocated and used to keep the SysEx data
of the MIDI event stored on the queue. This makes it possible
to push MIDI events to the queue even from a realtime thread.

Show diffs side-by-side

added added

removed removed

Lines of Context:
894
894
void Synth::flushMIDIQueue() {
895
895
        if (midiQueue != NULL) {
896
896
                for (;;) {
897
 
                        const MidiEvent *midiEvent = midiQueue->peekMidiEvent();
 
897
                        const volatile MidiEventQueue::MidiEvent *midiEvent = midiQueue->peekMidiEvent();
898
898
                        if (midiEvent == NULL) break;
899
899
                        if (midiEvent->sysexData == NULL) {
900
900
                                playMsgNow(midiEvent->shortMessageData);
1797
1797
        return extensions.masterTunePitchDelta;
1798
1798
}
1799
1799
 
1800
 
MidiEvent::~MidiEvent() {
1801
 
        if (sysexData != NULL) {
1802
 
                delete[] sysexData;
1803
 
        }
1804
 
}
1805
 
 
1806
 
void MidiEvent::setShortMessage(Bit32u useShortMessageData, Bit32u useTimestamp) {
1807
 
        if (sysexData != NULL) {
1808
 
                delete[] sysexData;
1809
 
        }
1810
 
        shortMessageData = useShortMessageData;
1811
 
        timestamp = useTimestamp;
1812
 
        sysexData = NULL;
1813
 
        sysexLength = 0;
1814
 
}
1815
 
 
1816
 
void MidiEvent::setSysex(const Bit8u *useSysexData, Bit32u useSysexLength, Bit32u useTimestamp) {
1817
 
        if (sysexData != NULL) {
1818
 
                delete[] sysexData;
1819
 
        }
1820
 
        shortMessageData = 0;
1821
 
        timestamp = useTimestamp;
1822
 
        sysexLength = useSysexLength;
1823
 
        Bit8u *dstSysexData = new Bit8u[sysexLength];
1824
 
        sysexData = dstSysexData;
1825
 
        memcpy(dstSysexData, useSysexData, sysexLength);
1826
 
}
1827
 
 
1828
 
MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize) : ringBuffer(new MidiEvent[useRingBufferSize]), ringBufferMask(useRingBufferSize - 1) {
1829
 
        memset(ringBuffer, 0, useRingBufferSize * sizeof(MidiEvent));
 
1800
/** Defines an interface of a class that maintains storage of variable-sized data of SysEx messages. */
 
1801
class MidiEventQueue::SysexDataStorage {
 
1802
public:
 
1803
        static MidiEventQueue::SysexDataStorage &create(Bit32u storageBufferSize);
 
1804
 
 
1805
        virtual ~SysexDataStorage() {}
 
1806
        virtual Bit8u *allocate(Bit32u sysexLength) = 0;
 
1807
        virtual void reclaimUnused(const Bit8u *sysexData, Bit32u sysexLength) = 0;
 
1808
        virtual void dispose(const Bit8u *sysexData, Bit32u sysexLength) = 0;
 
1809
};
 
1810
 
 
1811
/** Storage space for SysEx data is allocated dynamically on demand and is disposed lazily. */
 
1812
class DynamicSysexDataStorage : public MidiEventQueue::SysexDataStorage {
 
1813
public:
 
1814
        Bit8u *allocate(Bit32u sysexLength) {
 
1815
                return new Bit8u[sysexLength];
 
1816
        }
 
1817
 
 
1818
        void reclaimUnused(const Bit8u *, Bit32u) {}
 
1819
 
 
1820
        void dispose(const Bit8u *sysexData, Bit32u) {
 
1821
                delete[] sysexData;
 
1822
        }
 
1823
};
 
1824
 
 
1825
/**
 
1826
 * SysEx data is stored in a preallocated buffer, that makes this kind of storage safe
 
1827
 * for use in a realtime thread. Additionally, the space retained by a SysEx event,
 
1828
 * that has been processed and thus is no longer necessary, is disposed instantly.
 
1829
 */
 
1830
class BufferedSysexDataStorage : public MidiEventQueue::SysexDataStorage {
 
1831
public:
 
1832
        explicit BufferedSysexDataStorage(Bit32u useStorageBufferSize) :
 
1833
                storageBuffer(new Bit8u[useStorageBufferSize]),
 
1834
                storageBufferSize(useStorageBufferSize),
 
1835
                startPosition(),
 
1836
                endPosition()
 
1837
        {}
 
1838
 
 
1839
        ~BufferedSysexDataStorage() {
 
1840
                delete[] storageBuffer;
 
1841
        }
 
1842
 
 
1843
        Bit8u *allocate(Bit32u sysexLength) {
 
1844
                Bit32u myStartPosition = startPosition;
 
1845
                Bit32u myEndPosition = endPosition;
 
1846
 
 
1847
                // When the free space isn't contiguous, the data is allocated either right after the end position
 
1848
                // or at the buffer beginning, wherever it fits.
 
1849
                if (myStartPosition > myEndPosition) {
 
1850
                        if (myStartPosition - myEndPosition <= sysexLength) return NULL;
 
1851
                } else if (storageBufferSize - myEndPosition < sysexLength) {
 
1852
                        // There's not enough free space at the end to place the data block.
 
1853
                        if (myStartPosition == myEndPosition) {
 
1854
                                // The buffer is empty -> reset positions to the buffer beginning.
 
1855
                                if (storageBufferSize <= sysexLength) return NULL;
 
1856
                                if (myStartPosition != 0) {
 
1857
                                        myStartPosition = 0;
 
1858
                                        startPosition = myStartPosition;
 
1859
                                }
 
1860
                        } else if (myStartPosition <= sysexLength) return NULL;
 
1861
                        myEndPosition = 0;
 
1862
                }
 
1863
                endPosition = myEndPosition + sysexLength;
 
1864
                return storageBuffer + myEndPosition;
 
1865
        }
 
1866
 
 
1867
        void reclaimUnused(const Bit8u *sysexData, Bit32u sysexLength) {
 
1868
                if (sysexData == NULL) return;
 
1869
                Bit32u allocatedPosition = startPosition;
 
1870
                if (storageBuffer + allocatedPosition == sysexData) {
 
1871
                        startPosition = allocatedPosition + sysexLength;
 
1872
                } else if (storageBuffer == sysexData) {
 
1873
                        // Buffer wrapped around.
 
1874
                        startPosition = sysexLength;
 
1875
                }
 
1876
        }
 
1877
 
 
1878
        void dispose(const Bit8u *, Bit32u) {}
 
1879
 
 
1880
private:
 
1881
        Bit8u * const storageBuffer;
 
1882
        const Bit32u storageBufferSize;
 
1883
 
 
1884
        volatile Bit32u startPosition;
 
1885
        volatile Bit32u endPosition;
 
1886
};
 
1887
 
 
1888
MidiEventQueue::SysexDataStorage &MidiEventQueue::SysexDataStorage::create(Bit32u storageBufferSize) {
 
1889
        if (storageBufferSize > 0) {
 
1890
                return *new BufferedSysexDataStorage(storageBufferSize);
 
1891
        } else {
 
1892
                return *new DynamicSysexDataStorage;
 
1893
        }
 
1894
}
 
1895
 
 
1896
MidiEventQueue::MidiEventQueue(Bit32u useRingBufferSize, Bit32u storageBufferSize) :
 
1897
        sysexDataStorage(SysexDataStorage::create(storageBufferSize)),
 
1898
        ringBuffer(new MidiEvent[useRingBufferSize]), ringBufferMask(useRingBufferSize - 1)
 
1899
{
 
1900
        for (Bit32u i = 0; i <= ringBufferMask; i++) {
 
1901
                ringBuffer[i].sysexData = NULL;
 
1902
        }
1830
1903
        reset();
1831
1904
}
1832
1905
 
1833
1906
MidiEventQueue::~MidiEventQueue() {
 
1907
        for (Bit32u i = 0; i <= ringBufferMask; i++) {
 
1908
                volatile MidiEvent &currentEvent = ringBuffer[endPosition];
 
1909
                sysexDataStorage.dispose(currentEvent.sysexData, currentEvent.sysexLength);
 
1910
        }
1834
1911
        delete[] ringBuffer;
1835
1912
}
1836
1913
 
1841
1918
 
1842
1919
bool MidiEventQueue::pushShortMessage(Bit32u shortMessageData, Bit32u timestamp) {
1843
1920
        Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
1844
 
        // Is ring buffer full?
 
1921
        // If ring buffer is full, bail out.
1845
1922
        if (startPosition == newEndPosition) return false;
1846
 
        ringBuffer[endPosition].setShortMessage(shortMessageData, timestamp);
 
1923
        volatile MidiEvent &newEvent = ringBuffer[endPosition];
 
1924
        sysexDataStorage.dispose(newEvent.sysexData, newEvent.sysexLength);
 
1925
        newEvent.sysexData = NULL;
 
1926
        newEvent.shortMessageData = shortMessageData;
 
1927
        newEvent.timestamp = timestamp;
1847
1928
        endPosition = newEndPosition;
1848
1929
        return true;
1849
1930
}
1850
1931
 
1851
1932
bool MidiEventQueue::pushSysex(const Bit8u *sysexData, Bit32u sysexLength, Bit32u timestamp) {
1852
1933
        Bit32u newEndPosition = (endPosition + 1) & ringBufferMask;
1853
 
        // Is ring buffer full?
 
1934
        // If ring buffer is full, bail out.
1854
1935
        if (startPosition == newEndPosition) return false;
1855
 
        ringBuffer[endPosition].setSysex(sysexData, sysexLength, timestamp);
 
1936
        volatile MidiEvent &newEvent = ringBuffer[endPosition];
 
1937
        sysexDataStorage.dispose(newEvent.sysexData, newEvent.sysexLength);
 
1938
        Bit8u *dstSysexData = sysexDataStorage.allocate(sysexLength);
 
1939
        if (dstSysexData == NULL) return false;
 
1940
        memcpy(dstSysexData, sysexData, sysexLength);
 
1941
        newEvent.sysexData = dstSysexData;
 
1942
        newEvent.sysexLength = sysexLength;
 
1943
        newEvent.timestamp = timestamp;
1856
1944
        endPosition = newEndPosition;
1857
1945
        return true;
1858
1946
}
1859
1947
 
1860
 
const MidiEvent *MidiEventQueue::peekMidiEvent() {
 
1948
const volatile MidiEventQueue::MidiEvent *MidiEventQueue::peekMidiEvent() {
1861
1949
        return isEmpty() ? NULL : &ringBuffer[startPosition];
1862
1950
}
1863
1951
 
1864
1952
void MidiEventQueue::dropMidiEvent() {
1865
 
        // Is ring buffer empty?
1866
 
        if (startPosition != endPosition) {
1867
 
                startPosition = (startPosition + 1) & ringBufferMask;
1868
 
        }
1869
 
}
1870
 
 
1871
 
bool MidiEventQueue::isFull() const {
1872
 
        return startPosition == ((endPosition + 1) & ringBufferMask);
 
1953
        if (isEmpty()) return;
 
1954
        volatile MidiEvent &unusedEvent = ringBuffer[startPosition];
 
1955
        sysexDataStorage.reclaimUnused(unusedEvent.sysexData, unusedEvent.sysexLength);
 
1956
        startPosition = (startPosition + 1) & ringBufferMask;
1873
1957
}
1874
1958
 
1875
1959
bool MidiEventQueue::isEmpty() const {
2008
2092
                // We need to ensure zero-duration notes will play so add minimum 1-sample delay.
2009
2093
                Bit32u thisLen = 1;
2010
2094
                if (!isAbortingPoly()) {
2011
 
                        const MidiEvent *nextEvent = getMidiQueue().peekMidiEvent();
 
2095
                        const volatile MidiEventQueue::MidiEvent *nextEvent = getMidiQueue().peekMidiEvent();
2012
2096
                        Bit32s samplesToNextEvent = (nextEvent != NULL) ? Bit32s(nextEvent->timestamp - getRenderedSampleCount()) : MAX_SAMPLES_PER_RUN;
2013
2097
                        if (samplesToNextEvent > 0) {
2014
2098
                                thisLen = len > MAX_SAMPLES_PER_RUN ? MAX_SAMPLES_PER_RUN : len;