kopia lustrzana https://github.com/jameshball/osci-render
				
				
				
			Use a blocking queue instead of lock free to massively reduce CPU usage
							rodzic
							
								
									32b2d2d35d
								
							
						
					
					
						commit
						a6d25a122c
					
				|  | @ -444,8 +444,11 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju | |||
|     } | ||||
|      | ||||
|     prevMidiEnabled = usingMidi; | ||||
| 
 | ||||
|     const double EPSILON = 0.00001; | ||||
|      | ||||
|     { | ||||
|      | ||||
|     if (volume > EPSILON) { | ||||
|         juce::SpinLock::ScopedLockType lock1(parsersLock); | ||||
|         juce::SpinLock::ScopedLockType lock2(effectsLock); | ||||
|         synth.renderNextBlock(buffer, midiMessages, 0, buffer.getNumSamples()); | ||||
|  | @ -460,9 +463,11 @@ void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, ju | |||
|         { | ||||
|             juce::SpinLock::ScopedLockType lock1(parsersLock); | ||||
|             juce::SpinLock::ScopedLockType lock2(effectsLock); | ||||
|             for (auto& effect : toggleableEffects) { | ||||
|                 if (effect->enabled->getValue()) { | ||||
|                     channels = effect->apply(sample, channels); | ||||
|             if (volume > EPSILON) { | ||||
|                 for (auto& effect : toggleableEffects) { | ||||
|                     if (effect->enabled->getValue()) { | ||||
|                         channels = effect->apply(sample, channels); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             for (auto& effect : permanentEffects) { | ||||
|  |  | |||
|  | @ -9,6 +9,11 @@ ShapeSound::ShapeSound(std::shared_ptr<FileParser> parser) : parser(parser) { | |||
|     producer->startThread(); | ||||
| } | ||||
| 
 | ||||
| ShapeSound::~ShapeSound() { | ||||
|     frames.kill(); | ||||
|     producer->stopThread(1000); | ||||
| } | ||||
| 
 | ||||
| bool ShapeSound::appliesToNote(int note) { | ||||
|     return true; | ||||
| } | ||||
|  | @ -18,36 +23,12 @@ bool ShapeSound::appliesToChannel(int channel) { | |||
| } | ||||
| 
 | ||||
| void ShapeSound::addFrame(std::vector<std::unique_ptr<Shape>>& frame) { | ||||
|     const auto scope = frameFifo.write(1); | ||||
| 
 | ||||
|     if (scope.blockSize1 > 0) { | ||||
|         frameBuffer[scope.startIndex1].clear(); | ||||
|         for (auto& shape : frame) { | ||||
|             frameBuffer[scope.startIndex1].push_back(std::move(shape)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (scope.blockSize2 > 0) { | ||||
|         frameBuffer[scope.startIndex2].clear(); | ||||
|         for (auto& shape : frame) { | ||||
|             frameBuffer[scope.startIndex2].push_back(std::move(shape)); | ||||
|         } | ||||
|     } | ||||
|     frames.push(std::move(frame)); | ||||
| } | ||||
| 
 | ||||
| double ShapeSound::updateFrame(std::vector<std::unique_ptr<Shape>>& frame) { | ||||
|     if (frameFifo.getNumReady() > 0) { | ||||
|         { | ||||
|             const auto scope = frameFifo.read(1); | ||||
| 
 | ||||
|             if (scope.blockSize1 > 0) { | ||||
|                 frame.swap(frameBuffer[scope.startIndex1]); | ||||
|             } else if (scope.blockSize2 > 0) { | ||||
|                 frame.swap(frameBuffer[scope.startIndex2]); | ||||
|             } | ||||
| 
 | ||||
|             frameLength = Shape::totalLength(frame); | ||||
|         } | ||||
|     if (frames.try_pop(frame)) { | ||||
|         frameLength = Shape::totalLength(frame); | ||||
|     } | ||||
| 
 | ||||
|     return frameLength; | ||||
|  |  | |||
|  | @ -3,10 +3,12 @@ | |||
| #include "../parser/FileParser.h" | ||||
| #include "../parser/FrameProducer.h" | ||||
| #include "../parser/FrameConsumer.h" | ||||
| #include "../concurrency/BlockingQueue.h" | ||||
| 
 | ||||
| class ShapeSound : public juce::SynthesiserSound, public FrameConsumer { | ||||
| public: | ||||
| 	ShapeSound(std::shared_ptr<FileParser> parser); | ||||
| 	~ShapeSound() override; | ||||
| 
 | ||||
| 	bool appliesToNote(int note) override; | ||||
| 	bool appliesToChannel(int channel) override; | ||||
|  | @ -19,8 +21,7 @@ public: | |||
| 
 | ||||
| private: | ||||
| 	 | ||||
| 	juce::AbstractFifo frameFifo{ 10 }; | ||||
| 	std::vector<std::unique_ptr<Shape>> frameBuffer[10]; | ||||
| 	BlockingQueue frames{10}; | ||||
| 	std::unique_ptr<FrameProducer> producer; | ||||
| 	double frameLength = 0.0; | ||||
| }; | ||||
|  | @ -0,0 +1,81 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <mutex> | ||||
| #include <condition_variable> | ||||
| #include <queue> | ||||
| 
 | ||||
| typedef std::vector<std::unique_ptr<Shape>> Frame; | ||||
| class BlockingQueue { | ||||
|     std::vector<Frame> content; | ||||
|     std::atomic<int> size = 0; | ||||
|     int head = 0; | ||||
|     std::atomic<bool> killed = false; | ||||
| 
 | ||||
|     std::mutex mutex; | ||||
|     std::condition_variable not_empty; | ||||
|     std::condition_variable not_full; | ||||
| 
 | ||||
|     BlockingQueue(const BlockingQueue &) = delete; | ||||
|     BlockingQueue(BlockingQueue &&) = delete; | ||||
|     BlockingQueue &operator = (const BlockingQueue &) = delete; | ||||
|     BlockingQueue &operator = (BlockingQueue &&) = delete; | ||||
| 
 | ||||
| public: | ||||
|     BlockingQueue(size_t capacity) { | ||||
|         content = std::vector<Frame>(capacity); | ||||
|     } | ||||
| 
 | ||||
|     void kill() { | ||||
|         killed = true; | ||||
|         not_empty.notify_all(); | ||||
|         not_full.notify_all(); | ||||
|     } | ||||
| 
 | ||||
|     void push(Frame &&item) { | ||||
|         { | ||||
|             std::unique_lock<std::mutex> lk(mutex); | ||||
|             not_full.wait(lk, [this]() { return size < content.size() || killed; }); | ||||
|             content[head] = std::move(item); | ||||
|             size++; | ||||
|         } | ||||
|         not_empty.notify_one(); | ||||
|     } | ||||
| 
 | ||||
|     bool try_push(Frame &&item) { | ||||
|         { | ||||
|             std::unique_lock<std::mutex> lk(mutex); | ||||
|             if (size == content.size()) { | ||||
|                 return false; | ||||
|             } | ||||
|             content[head] = std::move(item); | ||||
|             size++; | ||||
|         } | ||||
|         not_empty.notify_one(); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void pop(Frame &item) { | ||||
|         { | ||||
|             std::unique_lock<std::mutex> lk(mutex); | ||||
|             not_empty.wait(lk, [this]() { return size > 0 || killed; }); | ||||
|             content[head].swap(item); | ||||
|             head = (head + 1) % content.size(); | ||||
|             size--; | ||||
|         } | ||||
|         not_full.notify_one(); | ||||
|     } | ||||
| 
 | ||||
|     bool try_pop(Frame &item) { | ||||
|         { | ||||
|             std::unique_lock<std::mutex> lk(mutex); | ||||
|             if (size == 0) { | ||||
|                 return false; | ||||
|             } | ||||
|             content[head].swap(item); | ||||
|             head = (head + 1) % content.size(); | ||||
|             size--; | ||||
|         } | ||||
|         not_full.notify_one(); | ||||
|         return true; | ||||
|     } | ||||
| }; | ||||
|  | @ -2,10 +2,7 @@ | |||
| 
 | ||||
| FrameProducer::FrameProducer(FrameConsumer& fc, std::shared_ptr<FileParser> fs) : frameConsumer(fc), frameSource(fs), juce::Thread("producer", 0) {} | ||||
| 
 | ||||
| FrameProducer::~FrameProducer() { | ||||
| 	frameSource->disable(); | ||||
| 	stopThread(-1); | ||||
| } | ||||
| FrameProducer::~FrameProducer() {} | ||||
| 
 | ||||
| void FrameProducer::run() { | ||||
| 	while (!threadShouldExit()) { | ||||
|  |  | |||
|  | @ -135,6 +135,7 @@ | |||
|               file="Source/components/VolumeComponent.h"/> | ||||
|       </GROUP> | ||||
|       <GROUP id="{9F5970A9-8094-E7F3-7AC1-812AE5589B9F}" name="concurrency"> | ||||
|         <FILE id="F5kUMH" name="BlockingQueue.h" compile="0" resource="0" file="Source/concurrency/BlockingQueue.h"/> | ||||
|         <FILE id="WQ2W15" name="BufferConsumer.h" compile="0" resource="0" | ||||
|               file="Source/concurrency/BufferConsumer.h"/> | ||||
|       </GROUP> | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 James Ball
						James Ball