kopia lustrzana https://github.com/jameshball/osci-render
				
				
				
			Add Blender support
							rodzic
							
								
									068459812f
								
							
						
					
					
						commit
						1bf3ba6646
					
				|  | @ -107,12 +107,13 @@ MainComponent::~MainComponent() { | |||
| } | ||||
| 
 | ||||
| void MainComponent::updateFileLabel() { | ||||
| 	if (audioProcessor.getCurrentFileIndex() == -1) { | ||||
| 	if (audioProcessor.objectServerRendering) { | ||||
| 		fileLabel.setText("Rendering from Blender", juce::dontSendNotification); | ||||
| 	} else if (audioProcessor.getCurrentFileIndex() == -1) { | ||||
| 		fileLabel.setText("No file open", juce::dontSendNotification); | ||||
| 		return; | ||||
| 	} else { | ||||
| 		fileLabel.setText(audioProcessor.getCurrentFileName(), juce::dontSendNotification); | ||||
| 	} | ||||
| 	 | ||||
| 	fileLabel.setText(audioProcessor.getCurrentFileName(), juce::dontSendNotification); | ||||
| } | ||||
| 
 | ||||
| void MainComponent::resized() { | ||||
|  |  | |||
|  | @ -42,6 +42,7 @@ OscirenderAudioProcessorEditor::OscirenderAudioProcessorEditor(OscirenderAudioPr | |||
| 
 | ||||
|     { | ||||
|         juce::MessageManagerLock lock; | ||||
|         audioProcessor.fileChangeBroadcaster.addChangeListener(this); | ||||
|         audioProcessor.broadcaster.addChangeListener(this); | ||||
|     } | ||||
| 
 | ||||
|  | @ -65,6 +66,7 @@ OscirenderAudioProcessorEditor::~OscirenderAudioProcessorEditor() { | |||
|     juce::Desktop::getInstance().setDefaultLookAndFeel(nullptr); | ||||
|     juce::MessageManagerLock lock; | ||||
|     audioProcessor.broadcaster.removeChangeListener(this); | ||||
|     audioProcessor.fileChangeBroadcaster.removeChangeListener(this); | ||||
| } | ||||
| 
 | ||||
| // parsersLock must be held
 | ||||
|  | @ -206,8 +208,13 @@ void OscirenderAudioProcessorEditor::handleAsyncUpdate() { | |||
| 
 | ||||
| void OscirenderAudioProcessorEditor::changeListenerCallback(juce::ChangeBroadcaster* source) { | ||||
|     juce::SpinLock::ScopedLockType lock(audioProcessor.parsersLock); | ||||
|     initialiseCodeEditors(); | ||||
|     settings.update(); | ||||
|     if (source == &audioProcessor.broadcaster) { | ||||
|         initialiseCodeEditors(); | ||||
|         settings.update(); | ||||
|     } else if (source == &audioProcessor.fileChangeBroadcaster) { | ||||
|         // triggered when the audioProcessor changes the current file (e.g. to Blender)
 | ||||
|         settings.fileUpdated(audioProcessor.getCurrentFileName()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OscirenderAudioProcessorEditor::editPerspectiveFunction(bool enable) { | ||||
|  |  | |||
|  | @ -388,11 +388,13 @@ void OscirenderAudioProcessor::changeCurrentFile(int index) { | |||
| } | ||||
| 
 | ||||
| void OscirenderAudioProcessor::changeSound(ShapeSound::Ptr sound) { | ||||
|     synth.clearSounds(); | ||||
|     synth.addSound(sound); | ||||
|     for (int i = 0; i < synth.getNumVoices(); i++) { | ||||
|         auto voice = dynamic_cast<ShapeVoice*>(synth.getVoice(i)); | ||||
|         voice->updateSound(sound.get()); | ||||
|     if (!objectServerRendering || sound == objectServerSound) { | ||||
|         synth.clearSounds(); | ||||
|         synth.addSound(sound); | ||||
|         for (int i = 0; i < synth.getNumVoices(); i++) { | ||||
|             auto voice = dynamic_cast<ShapeVoice*>(synth.getVoice(i)); | ||||
|             voice->updateSound(sound.get()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -405,7 +407,7 @@ std::shared_ptr<FileParser> OscirenderAudioProcessor::getCurrentFileParser() { | |||
| } | ||||
| 
 | ||||
| juce::String OscirenderAudioProcessor::getCurrentFileName() { | ||||
|     if (currentFile == -1) { | ||||
|     if (objectServerRendering || currentFile == -1) { | ||||
| 		return ""; | ||||
|     } else { | ||||
|         return fileNames[currentFile]; | ||||
|  | @ -420,6 +422,24 @@ std::shared_ptr<juce::MemoryBlock> OscirenderAudioProcessor::getFileBlock(int in | |||
|     return fileBlocks[index]; | ||||
| } | ||||
| 
 | ||||
| void OscirenderAudioProcessor::setObjectServerRendering(bool enabled) { | ||||
|     { | ||||
|         juce::SpinLock::ScopedLockType lock1(parsersLock); | ||||
| 
 | ||||
|         objectServerRendering = enabled; | ||||
|         if (enabled) { | ||||
|             changeSound(objectServerSound); | ||||
|         } else { | ||||
|             changeCurrentFile(currentFile); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         juce::MessageManagerLock lock; | ||||
|         fileChangeBroadcaster.sendChangeMessage(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void OscirenderAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) { | ||||
|     juce::ScopedNoDenormals noDenormals; | ||||
|     auto totalNumInputChannels  = getTotalNumInputChannels(); | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ | |||
| #include "audio/PitchDetector.h" | ||||
| #include "audio/WobbleEffect.h" | ||||
| #include "audio/PerspectiveEffect.h" | ||||
| #include "obj/ObjectServer.h" | ||||
| 
 | ||||
| //==============================================================================
 | ||||
| /**
 | ||||
|  | @ -187,6 +188,8 @@ public: | |||
|     std::atomic<int> currentFile = -1; | ||||
| 
 | ||||
|     juce::ChangeBroadcaster broadcaster; | ||||
|     std::atomic<bool> objectServerRendering = false; | ||||
|     juce::ChangeBroadcaster fileChangeBroadcaster; | ||||
| 
 | ||||
| private: | ||||
|     juce::SpinLock consumerLock; | ||||
|  | @ -203,6 +206,8 @@ public: | |||
|     juce::SpinLock fontLock; | ||||
|     juce::Font font = juce::Font(juce::Font::getDefaultSansSerifFontName(), 1.0f, juce::Font::plain); | ||||
| 
 | ||||
|     ShapeSound::Ptr objectServerSound = new ShapeSound(); | ||||
| 
 | ||||
|     void addLuaSlider(); | ||||
|     void updateEffectPrecedence(); | ||||
|     void updateFileBlock(int index, std::shared_ptr<juce::MemoryBlock> block); | ||||
|  | @ -218,6 +223,7 @@ public: | |||
| 	juce::String getCurrentFileName(); | ||||
|     juce::String getFileName(int index); | ||||
| 	std::shared_ptr<juce::MemoryBlock> getFileBlock(int index); | ||||
|     void setObjectServerRendering(bool enabled); | ||||
| private: | ||||
|     std::atomic<double> volume = 1.0; | ||||
|     std::atomic<double> threshold = 1.0; | ||||
|  | @ -232,6 +238,7 @@ private: | |||
|     juce::Synthesiser synth; | ||||
| 
 | ||||
|     AudioWebSocketServer softwareOscilloscopeServer{*this}; | ||||
|     ObjectServer objectServer{*this}; | ||||
| 
 | ||||
|     void updateLuaValues(); | ||||
|     void updateObjValues(); | ||||
|  |  | |||
|  | @ -47,7 +47,7 @@ void SettingsComponent::fileUpdated(juce::String fileName) { | |||
|     lua.setVisible(false); | ||||
|     obj.setVisible(false); | ||||
|     txt.setVisible(false); | ||||
|     if (fileName.isEmpty()) { | ||||
|     if (fileName.isEmpty() || audioProcessor.objectServerRendering) { | ||||
|         // do nothing
 | ||||
|     } else if (extension == ".lua") { | ||||
|         lua.setVisible(true); | ||||
|  |  | |||
|  | @ -9,9 +9,13 @@ ShapeSound::ShapeSound(std::shared_ptr<FileParser> parser) : parser(parser) { | |||
|     producer->startThread(); | ||||
| } | ||||
| 
 | ||||
| ShapeSound::ShapeSound() {} | ||||
| 
 | ||||
| ShapeSound::~ShapeSound() { | ||||
|     frames.kill(); | ||||
|     producer->stopThread(1000); | ||||
|     if (producer != nullptr) { | ||||
|         producer->stopThread(1000); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool ShapeSound::appliesToNote(int note) { | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ | |||
| class ShapeSound : public juce::SynthesiserSound, public FrameConsumer { | ||||
| public: | ||||
| 	ShapeSound(std::shared_ptr<FileParser> parser); | ||||
| 	ShapeSound(); | ||||
| 	~ShapeSound() override; | ||||
| 
 | ||||
| 	bool appliesToNote(int note) override; | ||||
|  |  | |||
|  | @ -88,10 +88,11 @@ void ShapeVoice::renderNextBlock(juce::AudioSampleBuffer& outputBuffer, int star | |||
|         bool renderingSample = true; | ||||
| 
 | ||||
|         if (sound.load() != nullptr) { | ||||
|             renderingSample = sound.load()->parser->isSample(); | ||||
|             auto parser = sound.load()->parser; | ||||
|             renderingSample = parser != nullptr && parser->isSample(); | ||||
| 
 | ||||
|             if (renderingSample) { | ||||
|                 channels = sound.load()->parser->nextSample(); | ||||
|                 channels = parser->nextSample(); | ||||
|             } else if (currentShape < frame.size()) { | ||||
|                 auto& shape = frame[currentShape]; | ||||
|                 double length = shape->length(); | ||||
|  |  | |||
|  | @ -0,0 +1,188 @@ | |||
| #include "ObjectServer.h" | ||||
| #include "../PluginProcessor.h" | ||||
| #include "../shape/Line.h" | ||||
| 
 | ||||
| ObjectServer::ObjectServer(OscirenderAudioProcessor& p) : audioProcessor(p), juce::Thread("Object Server") { | ||||
|     startThread(); | ||||
| } | ||||
| 
 | ||||
| ObjectServer::~ObjectServer() { | ||||
|     stopThread(1000); | ||||
| } | ||||
| 
 | ||||
| void ObjectServer::run() { | ||||
|     if (socket.createListener(51677, "127.0.0.1")) { | ||||
|         // preallocating a large buffer to avoid allocations in the loop
 | ||||
|         std::unique_ptr<char[]> message{ new char[10 * 1024 * 1024] }; | ||||
| 
 | ||||
|         while (!threadShouldExit()) { | ||||
|             if (socket.waitUntilReady(true, 200)) { | ||||
|                 std::unique_ptr<juce::StreamingSocket> connection(socket.waitForNextConnection()); | ||||
| 
 | ||||
|                 if (connection != nullptr) { | ||||
|                     audioProcessor.setObjectServerRendering(true); | ||||
| 
 | ||||
|                     while (!threadShouldExit()) { | ||||
|                         if (connection->waitUntilReady(true, 200) == 1) { | ||||
|                             int i = 0; | ||||
| 
 | ||||
|                             // read until we get a newline
 | ||||
|                             while (!threadShouldExit()) { | ||||
|                                 char buffer[1024]; | ||||
|                                 int bytesRead = connection->read(buffer, sizeof(buffer), false); | ||||
| 
 | ||||
|                                 if (bytesRead <= 0) { | ||||
|                                     break; | ||||
|                                 } | ||||
| 
 | ||||
|                                 std::memcpy(message.get() + i, buffer, bytesRead); | ||||
|                                 i += bytesRead; | ||||
| 
 | ||||
|                                 if (message[i] == '\n') { | ||||
|                                     message[i] = '\0'; | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
| 
 | ||||
|                             if (strncmp(message.get(), "CLOSE", 5) == 0) { | ||||
|                                 connection->close(); | ||||
|                                 audioProcessor.setObjectServerRendering(false); | ||||
|                                 break; | ||||
|                             } | ||||
| 
 | ||||
|                             // format of json is:
 | ||||
|                             // {
 | ||||
|                             //   "objects": [
 | ||||
|                             //     {
 | ||||
|                             //       "name": "Line Art",
 | ||||
|                             //       "vertices": [
 | ||||
|                             //         [
 | ||||
|                             //           {
 | ||||
|                             //             "x": double value,
 | ||||
|                             //             "y": double value,
 | ||||
|                             //             "z": double value
 | ||||
|                             //           },
 | ||||
|                             //           ...
 | ||||
|                             //         ],
 | ||||
|                             //         ...
 | ||||
|                             //       ],
 | ||||
|                             //       "matrix": [
 | ||||
|                             //         16 double values
 | ||||
|                             //       ]
 | ||||
|                             //     }
 | ||||
|                             //   ],
 | ||||
|                             //   "focalLength": double value
 | ||||
|                             // }
 | ||||
| 
 | ||||
|                             auto json = juce::JSON::parse(message.get()); | ||||
| 
 | ||||
|                             auto objects = *json.getProperty("objects", juce::Array<juce::var>()).getArray(); | ||||
|                             std::vector<std::vector<double>> allMatrices; | ||||
|                             std::vector<std::vector<std::vector<Vector3D>>> allVertices; | ||||
| 
 | ||||
|                             double focalLength = json.getProperty("focalLength", 1); | ||||
| 
 | ||||
|                             for (int i = 0; i < objects.size(); i++) { | ||||
|                                 auto verticesArray = *objects[i].getProperty("vertices", juce::Array<juce::var>()).getArray(); | ||||
|                                 std::vector<std::vector<Vector3D>> vertices; | ||||
| 
 | ||||
|                                 for (auto& vertexArrayVar : verticesArray) { | ||||
|                                     vertices.push_back(std::vector<Vector3D>()); | ||||
|                                     auto& vertexArray = *vertexArrayVar.getArray(); | ||||
|                                     for (auto& vertex : vertexArray) { | ||||
|                                         double x = vertex.getProperty("x", 0); | ||||
|                                         double y = vertex.getProperty("y", 0); | ||||
|                                         double z = vertex.getProperty("z", 0); | ||||
|                                         vertices[vertices.size() - 1].push_back(Vector3D(x, y, z)); | ||||
|                                     } | ||||
|                                 } | ||||
|                                 auto matrix = *objects[i].getProperty("matrix", juce::Array<juce::var>()).getArray(); | ||||
| 
 | ||||
|                                 allMatrices.push_back(std::vector<double>()); | ||||
|                                 for (auto& value : matrix) { | ||||
|                                     allMatrices[i].push_back(value); | ||||
|                                 } | ||||
| 
 | ||||
|                                 std::vector<std::vector<Vector3D>> reorderedVertices; | ||||
| 
 | ||||
|                                 if (vertices.size() > 0 && matrix.size() == 16) { | ||||
|                                     std::vector<bool> visited = std::vector<bool>(vertices.size(), false); | ||||
|                                     std::vector<int> order = std::vector<int>(vertices.size(), 0); | ||||
|                                     visited[0] = true; | ||||
| 
 | ||||
|                                     auto endPoint = vertices[0].back(); | ||||
| 
 | ||||
|                                     for (int i = 1; i < vertices.size(); i++) { | ||||
|                                         int minPath = 0; | ||||
|                                         double minDistance = 9999999; | ||||
|                                         for (int j = 0; j < vertices.size(); j++) { | ||||
|                                             if (!visited[j]) { | ||||
|                                                 auto startPoint = vertices[j][0]; | ||||
| 
 | ||||
|                                                 double diffX = endPoint.x - startPoint.x; | ||||
|                                                 double diffY = endPoint.y - startPoint.y; | ||||
|                                                 double diffZ = endPoint.z - startPoint.z; | ||||
| 
 | ||||
|                                                 double distance = std::sqrt(diffX * diffX + diffY * diffY + diffZ * diffZ); | ||||
|                                                 if (distance < minDistance) { | ||||
|                                                     minPath = j; | ||||
|                                                     minDistance = distance; | ||||
|                                                 } | ||||
|                                             } | ||||
|                                         } | ||||
|                                         visited[minPath] = true; | ||||
|                                         order[i] = minPath; | ||||
|                                         endPoint = vertices[minPath].back(); | ||||
|                                     } | ||||
| 
 | ||||
|                                     for (int i = 0; i < vertices.size(); i++) { | ||||
|                                         std::vector<Vector3D> reorderedVertex; | ||||
|                                         int index = order[i]; | ||||
|                                         for (int j = 0; j < vertices[index].size(); j++) { | ||||
|                                             reorderedVertex.push_back(vertices[index][j]); | ||||
|                                         } | ||||
|                                         reorderedVertices.push_back(reorderedVertex); | ||||
|                                     } | ||||
|                                 } | ||||
| 
 | ||||
|                                 allVertices.push_back(reorderedVertices); | ||||
|                             } | ||||
| 
 | ||||
|                             // generate a frame from the vertices and matrix
 | ||||
|                             std::vector<std::unique_ptr<Shape>> frame; | ||||
| 
 | ||||
|                             for (int i = 0; i < objects.size(); i++) { | ||||
|                                 for (int j = 0; j < allVertices[i].size(); j++) { | ||||
|                                     for (int k = 0; k < allVertices[i][j].size() - 1; k++) { | ||||
|                                         auto start = allVertices[i][j][k]; | ||||
|                                         auto end = allVertices[i][j][k + 1]; | ||||
| 
 | ||||
|                                         // multiply the start and end points by the matrix
 | ||||
|                                         double rotatedX = start.x * allMatrices[i][0] + start.y * allMatrices[i][1] + start.z * allMatrices[i][2] + allMatrices[i][3]; | ||||
|                                         double rotatedY = start.x * allMatrices[i][4] + start.y * allMatrices[i][5] + start.z * allMatrices[i][6] + allMatrices[i][7]; | ||||
|                                         double rotatedZ = start.x * allMatrices[i][8] + start.y * allMatrices[i][9] + start.z * allMatrices[i][10] + allMatrices[i][11]; | ||||
| 
 | ||||
|                                         double rotatedX2 = end.x * allMatrices[i][0] + end.y * allMatrices[i][1] + end.z * allMatrices[i][2] + allMatrices[i][3]; | ||||
|                                         double rotatedY2 = end.x * allMatrices[i][4] + end.y * allMatrices[i][5] + end.z * allMatrices[i][6] + allMatrices[i][7]; | ||||
|                                         double rotatedZ2 = end.x * allMatrices[i][8] + end.y * allMatrices[i][9] + end.z * allMatrices[i][10] + allMatrices[i][11]; | ||||
| 
 | ||||
|                                         double x = rotatedX * focalLength / rotatedZ; | ||||
|                                         double y = rotatedY * focalLength / rotatedZ; | ||||
| 
 | ||||
|                                         double x2 = rotatedX2 * focalLength / rotatedZ2; | ||||
|                                         double y2 = rotatedY2 * focalLength / rotatedZ2; | ||||
| 
 | ||||
|                                         frame.push_back(std::make_unique<Line>(x, y, x2, y2)); | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
| 
 | ||||
|                             audioProcessor.objectServerSound->addFrame(frame); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,17 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include <JuceHeader.h> | ||||
| 
 | ||||
| class OscirenderAudioProcessor; | ||||
| class ObjectServer : public juce::Thread { | ||||
| public: | ||||
|     ObjectServer(OscirenderAudioProcessor& p); | ||||
|     ~ObjectServer(); | ||||
| 
 | ||||
|     void run() override; | ||||
| 
 | ||||
| private: | ||||
|     OscirenderAudioProcessor& audioProcessor; | ||||
| 
 | ||||
|     juce::StreamingSocket socket; | ||||
| }; | ||||
|  | @ -390,6 +390,9 @@ | |||
|         <FILE id="ix12FT" name="Camera.h" compile="0" resource="0" file="Source/obj/Camera.h"/> | ||||
|         <FILE id="JJTNO9" name="Line3D.cpp" compile="1" resource="0" file="Source/obj/Line3D.cpp"/> | ||||
|         <FILE id="TMrur0" name="Line3D.h" compile="0" resource="0" file="Source/obj/Line3D.h"/> | ||||
|         <FILE id="Yfpzzn" name="ObjectServer.cpp" compile="1" resource="0" | ||||
|               file="Source/obj/ObjectServer.cpp"/> | ||||
|         <FILE id="CqYgqM" name="ObjectServer.h" compile="0" resource="0" file="Source/obj/ObjectServer.h"/> | ||||
|         <FILE id="a4ILpa" name="tiny_obj_loader.cpp" compile="1" resource="0" | ||||
|               file="Source/obj/tiny_obj_loader.cpp"/> | ||||
|         <FILE id="YdfYbM" name="tiny_obj_loader.h" compile="0" resource="0" | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 James Ball
						James Ball