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