#pragma once #include #include #include #include #include #include #include #include class QThread; namespace Xybrid::Data { class Project; class Node; } namespace Xybrid::Audio { template struct PointerCompare { bool operator()(T* a, T* b) const { return *a == *b; } size_t operator()(T* a) const { return std::hash()(*a); } }; struct NoteInfo { bool valid = false; uint8_t port = 0; uint16_t noteId = 0; NoteInfo() = default; NoteInfo(uint8_t p, uint16_t nId) { valid = true; port = p; noteId = nId; } }; class AudioEngine : public QIODevice { Q_OBJECT explicit AudioEngine(QObject *parent = nullptr); public: enum PlaybackMode { Stopped, // stopped Playing, // playing track Paused, // paused during playback Previewing, // instrument live preview Rendering, // rendering to file }; private: QThread* thread; std::unique_ptr output; int sampleRate = 48000; std::vector buffer[2]; size_t bufPos = 0; static const constexpr size_t tickBufSize = (1024*1024*5); // 5mb should be enough std::unique_ptr tickBuf; std::atomic tickBufPtr; size_t* tickBufEnd; PlaybackMode mode = Stopped; size_t tickId = 0; std::shared_ptr project; std::deque> queue; bool queueValid; void buildQueue(); std::array portLastNoteId; std::vector chTrack; std::vector noteEndQueue; std::unordered_map, PointerCompare> nameTrack; std::vector buf; /// preallocated buffer for building commands // playback timing and position float tempo = 140.0; int seqPos; int curRow; int curTick; double tickAcc; // accumulator for tick remainder void postInit(); void initAudio(bool startNow = false); void deinitAudio(); void nextTick(); public: static void init(); inline constexpr PlaybackMode playbackMode() const { return mode; } inline constexpr const std::shared_ptr& playingProject() const { return project; } void play(std::shared_ptr); void stop(); inline void invalidateQueue(Data::Project* p) { if (p == project.get()) queueValid = false; } void* tickAlloc(size_t size); inline size_t curTickId() const { return tickId; } inline size_t curTickSize() const { return buffer[0].size(); } inline int curSampleRate() const { return sampleRate; } // QIODevice functions qint64 readData(char* data, qint64 maxlen) override; qint64 writeData(const char*, qint64) override { return 0; } qint64 bytesAvailable() const override { return 0; } // not actually used by QAudioOutput volatile int note = 12*5; signals: void playbackModeChanged(); public slots: }; extern AudioEngine* audioEngine; }