1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ 2 3 /* 4 Rosegarden 5 A sequencer and musical notation editor. 6 Copyright 2000-2021 the Rosegarden development team. 7 See the AUTHORS file for more details. 8 9 This program is free software; you can redistribute it and/or 10 modify it under the terms of the GNU General Public License as 11 published by the Free Software Foundation; either version 2 of the 12 License, or (at your option) any later version. See the file 13 COPYING included with this distribution for more information. 14 */ 15 16 #ifndef RG_SEQUENCERDATABLOCK_H 17 #define RG_SEQUENCERDATABLOCK_H 18 19 #include "ControlBlock.h" 20 #include "base/RealTime.h" 21 #include "MappedEvent.h" 22 23 #include <QMutex> 24 25 namespace Rosegarden 26 { 27 28 /** 29 * ONLY PUT PLAIN DATA HERE - NO POINTERS EVER 30 * (and this struct mustn't have a constructor) 31 * 32 * Since we no longer use shared memory, it might be safe to lift 33 * the POD/no pointer/no ctor restrictions. 34 */ 35 struct LevelInfo 36 { 37 int level; 38 int levelRight; // if stereo audio 39 }; 40 41 class MappedEventList; 42 43 44 #define SEQUENCER_DATABLOCK_MAX_NB_INSTRUMENTS 512 // can't be a symbol 45 #define SEQUENCER_DATABLOCK_MAX_NB_SUBMASTERS 64 // can't be a symbol 46 #define SEQUENCER_DATABLOCK_RECORD_BUFFER_SIZE 1024 // MIDI events 47 48 /// Holds MIDI data going from RosegardenSequencer to RosegardenMainWindow 49 /** 50 * This class contains recorded data that is being passed from sequencer 51 * threads (RosegardenSequencer::processRecordedMidi()) to GUI threads 52 * (RosegardenMainWindow::processRecordedEvents()). It is an important 53 * link in the chain from AlsaDriver::getMappedEventList() to 54 * RosegardenDocument::insertRecordedMidi(). 55 * 56 * This class needs to be reviewed for thread safety. See the comments 57 * in addRecordedEvents(). 58 * 59 * This used to be mapped into a shared memory 60 * backed file, which had to be of fixed size and layout. The design 61 * reflects that history, though nowadays it is a simple singleton 62 * class. 63 * 64 * Examples of some of the shared memory related design decisions: 65 * Position is represented as two ints (m_positionSec and m_positionNsec) 66 * rather than a RealTime, as the RealTime default ctor 67 * initialises the space & so can't be used from the GUI's 68 * placement-new ctor (which has no write access and doesn't want 69 * it anyway). Likewise we use char[] instead of MappedEvents 70 * for m_visualEvent and m_recordBuffer. 71 * 72 * Since shared memory is no longer used, 73 * it should be possible to change this from being a fixed-layout 74 * C-style struct to something more "C++" (e.g. std::vector<MappedEvent> 75 * instead of char[sizeof(MappedEvent*CAPACITY_MAX)], RealTime instead of 76 * ints, etc...). We would need to investigate each of the users to make 77 * sure things like placement new were replaced with conventional new. 78 * From a maintenance standpoint, this should improve the code significantly. 79 * 80 * RosegardenMainWindow::slotHandleInputs() supports communication between 81 * the Sequencer and GUI threads using RosegardenSequencer::m_transportRequests. 82 * 83 * AlsaDriver::handleTransportCCs() talks across threads to 84 * RosegardenMainWindow::customEvent() using QCoreApplication::postEvent(). 85 * 86 * @see ControlBlock 87 */ 88 class SequencerDataBlock 89 { 90 public: 91 // Singleton. 92 static SequencerDataBlock *getInstance(); 93 94 /// Called by the UI. getPositionPointer()95 RealTime getPositionPointer() const { 96 return RealTime(m_positionSec, m_positionNsec); 97 } 98 /// Called by the sequencer. setPositionPointer(const RealTime & rt)99 void setPositionPointer(const RealTime &rt) { 100 m_positionSec = rt.sec; 101 m_positionNsec = rt.nsec; 102 } 103 104 /// Get the MIDI OUT event to show on the transport during playback. 105 bool getVisual(MappedEvent &ev); 106 /// Set the MIDI OUT event to show on the transport during playback. 107 void setVisual(const MappedEvent *ev); 108 109 /// Add events to the record ring buffer (m_recordBuffer). 110 /** 111 * Called by RosegardenSequencer::processRecordedMidi(). 112 */ 113 void addRecordedEvents(MappedEventList *); 114 /// Get events from the record ring buffer (m_recordBuffer). 115 /** 116 * Called by RosegardenMainWindow::processRecordedEvents(). 117 */ 118 int getRecordedEvents(MappedEventList &); 119 120 bool getTrackLevel(TrackId track, LevelInfo &) const; 121 void setTrackLevel(TrackId track, const LevelInfo &); 122 123 // Two of these to rather hamfistedly get around the fact 124 // we need to fetch this value twice - once from IPB, 125 // and again for the Mixer. 126 // 127 bool getInstrumentLevel(InstrumentId id, LevelInfo &) const; 128 bool getInstrumentLevelForMixer(InstrumentId id, LevelInfo &) const; 129 130 void setInstrumentLevel(InstrumentId id, const LevelInfo &); 131 132 bool getInstrumentRecordLevel(InstrumentId id, LevelInfo &) const; 133 bool getInstrumentRecordLevelForMixer(InstrumentId id, LevelInfo &) const; 134 135 void setInstrumentRecordLevel(InstrumentId id, const LevelInfo &); 136 137 bool getSubmasterLevel(int submaster, LevelInfo &) const; 138 void setSubmasterLevel(int submaster, const LevelInfo &); 139 140 bool getMasterLevel(LevelInfo &) const; 141 void setMasterLevel(const LevelInfo &); 142 143 // Reset this class on (for example) GUI restart 144 // rename: reset() 145 void clearTemporaries(); 146 147 protected: 148 SequencerDataBlock(); 149 150 int instrumentToIndex(InstrumentId id) const; 151 int instrumentToIndexCreating(InstrumentId id); 152 153 // ??? Thread-safe? Probably not. Seems like the worst-case is that 154 // the pointer might jump forward about one second momentarily. 155 int m_positionSec; 156 int m_positionNsec; 157 158 int m_setVisualIndex; 159 int m_getVisualIndex; 160 bool m_haveVisualEvent; 161 /// MIDI OUT event for display on the transport during playback. 162 char m_visualEvent[sizeof(MappedEvent)]; 163 164 /// Index of the next available position in m_recordBuffer. 165 /** 166 * volatile is needed here (and probably other places) since this is used 167 * across threads. volatile prevents compiler optimizations (caching) 168 * that might render the data useless. The chances that the compiler 169 * might optimize in this fashion are very slim given the code that uses 170 * this variable. However, better safe than sorry. 171 */ 172 volatile int m_recordEventIndex; 173 /// Read position in m_recordBuffer. 174 /** 175 * It's iffy as to whether "volatile" is actually needed here. The two 176 * functions that use this may or may not be on different threads. 177 */ 178 int m_readIndex; 179 /// Ring buffer of recorded MIDI events. 180 /** 181 * This should probably be volatile for thread-safety, but that means 182 * the memset() call would need to be rewritten as a loop. Or the 183 * volatile could be cast away. 184 */ 185 char m_recordBuffer[sizeof(MappedEvent) * 186 SEQUENCER_DATABLOCK_RECORD_BUFFER_SIZE]; 187 188 // ??? Thread-safe? 189 InstrumentId m_knownInstruments[SEQUENCER_DATABLOCK_MAX_NB_INSTRUMENTS]; 190 int m_knownInstrumentCount; 191 192 // ??? Thread-safe? 193 int m_levelUpdateIndices[SEQUENCER_DATABLOCK_MAX_NB_INSTRUMENTS]; 194 LevelInfo m_levels[SEQUENCER_DATABLOCK_MAX_NB_INSTRUMENTS]; 195 196 // ??? Thread-safe? 197 int m_recordLevelUpdateIndices[SEQUENCER_DATABLOCK_MAX_NB_INSTRUMENTS]; 198 LevelInfo m_recordLevels[SEQUENCER_DATABLOCK_MAX_NB_INSTRUMENTS]; 199 200 // ??? Thread-safe? 201 int m_submasterLevelUpdateIndices[SEQUENCER_DATABLOCK_MAX_NB_SUBMASTERS]; 202 LevelInfo m_submasterLevels[SEQUENCER_DATABLOCK_MAX_NB_SUBMASTERS]; 203 204 // ??? Thread-safe? 205 int m_masterLevelUpdateIndex; 206 LevelInfo m_masterLevel; 207 }; 208 209 } 210 211 #endif 212