From ea16741b3d3b91c85013adc3d7b64cf8f3350512 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Mon, 18 Oct 2010 21:03:17 +0100 Subject: [PATCH] Tweaks to the win32 camera capture code to reduce CPU load. --- .../Source/Utility/jucer_MiscUtilities.h | 62 ++++ extras/juce demo/Source/demos/WidgetsDemo.cpp | 2 - juce_amalgamated.cpp | 309 ++++++++---------- juce_amalgamated.h | 75 ++--- src/core/juce_MathsFunctions.h | 9 +- src/gui/graphics/drawables/juce_Drawable.cpp | 16 +- src/native/windows/juce_win32_ASIO.cpp | 113 ++----- .../windows/juce_win32_CameraDevice.cpp | 64 +++- 8 files changed, 308 insertions(+), 342 deletions(-) diff --git a/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h b/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h index 4f8a9b460b..d039fed14a 100644 --- a/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h +++ b/extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h @@ -142,6 +142,68 @@ private: }; +//============================================================================== +class DrawableComponent : public Component, + public ValueTree::Listener +{ +public: + DrawableComponent (const ValueTree& drawable_) + { + setDrawable (drawable_); + } + + ~DrawableComponent() + { + } + + void setDrawable (const ValueTree& newDrawable) + { + drawable.removeListener (this); + drawable = newDrawable; + drawable.addListener (this); + drawableObject = Drawable::createFromValueTree (drawable, 0); // xxx image provider missing + resized(); + repaint(); + } + + void paint (Graphics& g) + { + if (drawableObject != 0) + drawableObject->drawAt (g, 0, 0, 1.0f); + } + + void resized() + { + DrawableComposite* dc = dynamic_cast (static_cast (drawableObject)); + + if (dc != 0) + { + dc->setMarker (DrawableComposite::contentLeftMarkerName, true, RelativeCoordinate (0)); + dc->setMarker (DrawableComposite::contentTopMarkerName, false, RelativeCoordinate (0)); + dc->setMarker (DrawableComposite::contentRightMarkerName, true, RelativeCoordinate (getWidth())); + dc->setMarker (DrawableComposite::contentBottomMarkerName, false, RelativeCoordinate (getHeight())); + } + } + + void valueTreePropertyChanged (ValueTree&, const Identifier&) { updateGraphics(); } + void valueTreeChildrenChanged (ValueTree&) { updateGraphics(); } + void valueTreeParentChanged (ValueTree&) { updateGraphics(); } + +private: + ValueTree drawable; + ScopedPointer drawableObject; + + void updateGraphics() + { + if (drawableObject != 0) + { + const Rectangle dirtyArea (drawableObject->refreshFromValueTree (drawable, 0)); + repaint (dirtyArea.getSmallestIntegerContainer()); + } + } +}; + + //============================================================================== /** */ diff --git a/extras/juce demo/Source/demos/WidgetsDemo.cpp b/extras/juce demo/Source/demos/WidgetsDemo.cpp index eb510aa06c..a73e35ee22 100644 --- a/extras/juce demo/Source/demos/WidgetsDemo.cpp +++ b/extras/juce demo/Source/demos/WidgetsDemo.cpp @@ -1110,8 +1110,6 @@ public: #endif //============================================================================== -const int numGroups = 4; - class WidgetsDemo : public Component, public ButtonListener { diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 1e39210216..3cecd5cbcd 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -18349,6 +18349,28 @@ void ValueTree::SharedObject::moveChild (int currentIndex, int newIndex, UndoMan } } +void ValueTree::SharedObject::reorderChildren (const ReferenceCountedArray & newOrder, UndoManager* undoManager) +{ + jassert (newOrder.size() == children.size()); + + if (undoManager == 0) + { + children = newOrder; + sendChildChangeMessage(); + } + else + { + for (int i = 0; i < children.size(); ++i) + { + if (children.getUnchecked(i) != newOrder.getUnchecked(i)) + { + jassert (children.contains (newOrder.getUnchecked(i))); + moveChild (children.indexOf (newOrder.getUnchecked(i)), i, undoManager); + } + } + } +} + bool ValueTree::SharedObject::isEquivalentTo (const SharedObject& other) const { if (type != other.type @@ -44257,19 +44279,9 @@ void DrawableButton::paintButton (Graphics& g, if (imageToDraw != 0) { if (style == ImageRaw) - { imageToDraw->draw (g, 1.0f); - } else - { - imageToDraw->drawWithin (g, - imageSpace.getX(), - imageSpace.getY(), - imageSpace.getWidth(), - imageSpace.getHeight(), - RectanglePlacement::centred, - 1.0f); - } + imageToDraw->drawWithin (g, imageSpace.toFloat(), RectanglePlacement::centred, 1.0f); } } @@ -44831,13 +44843,15 @@ void ToolbarButton::paintButtonArea (Graphics& g, if (getToggleState() && toggledOnImage != 0) d = toggledOnImage; + const Rectangle area (0.0f, 0.0f, (float) width, (float) height); + if (! isEnabled()) { Image im (Image::ARGB, width, height, true); { Graphics g2 (im); - d->drawWithin (g2, 0, 0, width, height, RectanglePlacement::centred, 1.0f); + d->drawWithin (g2, area, RectanglePlacement::centred, 1.0f); } im.desaturate(); @@ -44845,7 +44859,7 @@ void ToolbarButton::paintButtonArea (Graphics& g, } else { - d->drawWithin (g, 0, 0, width, height, RectanglePlacement::centred, 1.0f); + d->drawWithin (g, area, RectanglePlacement::centred, 1.0f); } } @@ -71698,7 +71712,7 @@ public: if (current != 0) { - registerMouseDown (screenPos, time, current); + registerMouseDown (screenPos, time, current, buttonState); sendMouseDown (current, screenPos, time); } } @@ -71845,16 +71859,10 @@ public: for (int i = 1; i < numElementsInArray (mouseDowns); ++i) { - if (mouseDowns[0].time - mouseDowns[i].time < (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1))) - && abs (mouseDowns[0].position.getX() - mouseDowns[i].position.getX()) < 8 - && abs (mouseDowns[0].position.getY() - mouseDowns[i].position.getY()) < 8) - { + if (mouseDowns[0].canBePartOfMultipleClickWith (mouseDowns[1], (int) (MouseEvent::getDoubleClickTimeout() * (1.0 + 0.25 * (i - 1))))) ++numClicks; - } else - { break; - } } } @@ -71970,13 +71978,23 @@ private: Point position; int64 time; Component* component; + ModifierKeys buttons; + + bool canBePartOfMultipleClickWith (const RecentMouseDown& other, int maxTimeBetween) const + { + return time - other.time < maxTimeBetween + && abs (position.getX() - other.position.getX()) < 8 + && abs (position.getY() - other.position.getY()) < 8 + && buttons == other.buttons;; + } }; RecentMouseDown mouseDowns[4]; bool mouseMovedSignificantlySincePressed; int64 lastTime; - void registerMouseDown (const Point& screenPos, const int64 time, Component* const component) throw() + void registerMouseDown (const Point& screenPos, const int64 time, + Component* const component, const ModifierKeys& modifiers) throw() { for (int i = numElementsInArray (mouseDowns); --i > 0;) mouseDowns[i] = mouseDowns[i - 1]; @@ -71984,6 +72002,7 @@ private: mouseDowns[0].position = screenPos; mouseDowns[0].time = time; mouseDowns[0].component = component; + mouseDowns[0].buttons = modifiers.withOnlyMouseButtons(); mouseMovedSignificantlySincePressed = false; } @@ -85932,21 +85951,21 @@ void RectanglePlacement::applyTo (double& x, double& y, } } -const AffineTransform RectanglePlacement::getTransformToFit (float x, float y, - float w, float h, - const float dx, const float dy, - const float dw, const float dh) const throw() +const AffineTransform RectanglePlacement::getTransformToFit (const Rectangle& source, const Rectangle& destination) const throw() { - if (w == 0 || h == 0) + if (source.isEmpty()) return AffineTransform::identity; - const float scaleX = dw / w; - const float scaleY = dh / h; + float w = source.getWidth(); + float h = source.getHeight(); + + const float scaleX = destination.getWidth() / w; + const float scaleY = destination.getHeight() / h; if ((flags & stretchToFit) != 0) - return AffineTransform::translation (-x, -y) + return AffineTransform::translation (-source.getX(), -source.getY()) .scaled (scaleX, scaleY) - .translated (dx, dy); + .translated (destination.getX(), destination.getY()); float scale = (flags & fillDestination) != 0 ? jmax (scaleX, scaleY) : jmin (scaleX, scaleY); @@ -85960,21 +85979,20 @@ const AffineTransform RectanglePlacement::getTransformToFit (float x, float y, w *= scale; h *= scale; - float newX = dx; + float newX = destination.getX(); + float newY = destination.getY(); if ((flags & xRight) != 0) - newX += dw - w; // right + newX += destination.getWidth() - w; // right else if ((flags & xLeft) == 0) - newX += (dw - w) / 2.0f; // centre - - float newY = dy; + newX += (destination.getWidth() - w) / 2.0f; // centre if ((flags & yBottom) != 0) - newY += dh - h; // bottom + newY += destination.getHeight() - h; // bottom else if ((flags & yTop) == 0) - newY += (dh - h) / 2.0f; // centre + newY += (destination.getHeight() - h) / 2.0f; // centre - return AffineTransform::translation (-x, -y) + return AffineTransform::translation (-source.getX(), -source.getY()) .scaled (scale, scale) .translated (newX, newY); } @@ -86015,22 +86033,12 @@ void Drawable::drawAt (Graphics& g, const float x, const float y, const float op } void Drawable::drawWithin (Graphics& g, - const int destX, - const int destY, - const int destW, - const int destH, + const Rectangle& destArea, const RectanglePlacement& placement, const float opacity) const { - if (destW > 0 && destH > 0) - { - Rectangle bounds (getBounds()); - - draw (g, opacity, - placement.getTransformToFit (bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), - (float) destX, (float) destY, - (float) destW, (float) destH)); - } + if (! destArea.isEmpty()) + draw (g, opacity, placement.getTransformToFit (getBounds(), destArea)); } Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes) @@ -88190,8 +88198,8 @@ public: const RectanglePlacement placement (placementFlags); newState.transform - = placement.getTransformToFit (vx, vy, vw, vh, - 0.0f, 0.0f, newState.width, newState.height) + = placement.getTransformToFit (Rectangle (vx, vy, vw, vh), + Rectangle (0.0f, 0.0f, newState.width, newState.height)) .followedBy (newState.transform); } } @@ -249243,8 +249251,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) #define log(a) {} #endif -#define JUCE_ASIOCALLBACK // should probably use this to define the callback type, but - // the asio header doesn't actually specify a calling convention for the functions.. +#define JUCE_ASIOCALLBACK __cdecl #if ASIO_DEBUGGING static void logError (const String& context, long error) @@ -249347,40 +249354,15 @@ public: } } - const StringArray getOutputChannelNames() - { - return outputChannelNames; - } - - const StringArray getInputChannelNames() - { - return inputChannelNames; - } - - int getNumSampleRates() - { - return sampleRates.size(); - } - - double getSampleRate (int index) - { - return sampleRates [index]; - } - - int getNumBufferSizesAvailable() - { - return bufferSizes.size(); - } + const StringArray getOutputChannelNames() { return outputChannelNames; } + const StringArray getInputChannelNames() { return inputChannelNames; } - int getBufferSizeSamples (int index) - { - return bufferSizes [index]; - } + int getNumSampleRates() { return sampleRates.size(); } + double getSampleRate (int index) { return sampleRates [index]; } - int getDefaultBufferSize() - { - return preferredSize; - } + int getNumBufferSizesAvailable() { return bufferSizes.size(); } + int getBufferSizeSamples (int index) { return bufferSizes [index]; } + int getDefaultBufferSize() { return preferredSize; } const String open (const BigInteger& inputChannels, const BigInteger& outputChannels, @@ -249834,45 +249816,18 @@ public: } } - bool isOpen() - { - return isOpen_ || insideControlPanelModalLoop; - } + bool isOpen() { return isOpen_ || insideControlPanelModalLoop; } + bool isPlaying() { return isASIOOpen && (currentCallback != 0); } - int getCurrentBufferSizeSamples() - { - return currentBlockSizeSamples; - } + int getCurrentBufferSizeSamples() { return currentBlockSizeSamples; } + double getCurrentSampleRate() { return currentSampleRate; } + int getCurrentBitDepth() { return currentBitDepth; } - double getCurrentSampleRate() - { - return currentSampleRate; - } + const BigInteger getActiveOutputChannels() const { return currentChansOut; } + const BigInteger getActiveInputChannels() const { return currentChansIn; } - const BigInteger getActiveOutputChannels() const - { - return currentChansOut; - } - - const BigInteger getActiveInputChannels() const - { - return currentChansIn; - } - - int getCurrentBitDepth() - { - return currentBitDepth; - } - - int getOutputLatencyInSamples() - { - return outputLatency + currentBlockSizeSamples / 4; - } - - int getInputLatencyInSamples() - { - return inputLatency + currentBlockSizeSamples / 4; - } + int getOutputLatencyInSamples() { return outputLatency + currentBlockSizeSamples / 4; } + int getInputLatencyInSamples() { return inputLatency + currentBlockSizeSamples / 4; } void start (AudioIODeviceCallback* callback) { @@ -249898,20 +249853,8 @@ public: lastCallback->audioDeviceStopped(); } - bool isPlaying() - { - return isASIOOpen && (currentCallback != 0); - } - - const String getLastError() - { - return error; - } - - bool hasControlPanel() const - { - return true; - } + const String getLastError() { return error; } + bool hasControlPanel() const { return true; } bool showControlPanel() { @@ -250438,7 +250381,6 @@ private: for (i = 0; i < numActiveInputChans; ++i) { float* const dst = inBuffers[i]; - jassert (dst != 0); const char* const src = (const char*) (infos[i].buffers[bi]); @@ -250475,16 +250417,12 @@ private: } } - currentCallback->audioDeviceIOCallback ((const float**) inBuffers, - numActiveInputChans, - outBuffers, - numActiveOutputChans, - samps); + currentCallback->audioDeviceIOCallback ((const float**) inBuffers, numActiveInputChans, + outBuffers, numActiveOutputChans, samps); for (i = 0; i < numActiveOutputChans; ++i) { float* const src = outBuffers[i]; - jassert (src != 0); char* const dst = (char*) (infos [numActiveInputChans + i].buffers[bi]); @@ -253757,7 +253695,8 @@ public: width (0), height (0), activeUsers (0), - recordNextFrameTime (false) + recordNextFrameTime (false), + previewMaxFPS (60) { HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph); if (FAILED (hr)) @@ -253888,6 +253827,11 @@ public: mediaControl->Stop(); } + int getPreviewMaxFPS() const + { + return previewMaxFPS; + } + void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/) { if (recordNextFrameTime) @@ -253958,13 +253902,14 @@ public: g.drawImage (activeImage, rx, ry, rw, rh, 0, 0, width, height); } - bool createFileCaptureFilter (const File& file) + bool createFileCaptureFilter (const File& file, int quality) { removeFileCaptureFilter(); file.deleteFile(); mediaControl->Stop(); firstRecordedTime = Time(); recordNextFrameTime = true; + previewMaxFPS = 60; HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter); @@ -253990,17 +253935,30 @@ public: hr = WMCreateProfileManager (profileManager.resetAndGetPointerAddress()); // This gibberish is the DirectShow profile for a video-only wmv file. - String prof ("" - " " - ""); + String prof ("" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""); + + const int fps[] = { 10, 15, 30 }; + const int maxFramesPerSecond = fps [quality % numElementsInArray (fps)]; prof = prof.replace ("$WIDTH", String (width)) - .replace ("$HEIGHT", String (height)); + .replace ("$HEIGHT", String (height)) + .replace ("$AVGTIMEPERFRAME", String (10000000 / maxFramesPerSecond)); ComSmartPtr currentProfile; hr = profileManager->LoadProfileByData ((const WCHAR*) prof, currentProfile.resetAndGetPointerAddress()); @@ -254018,6 +253976,7 @@ public: && ok && activeUsers > 0 && SUCCEEDED (mediaControl->Run())) { + previewMaxFPS = (quality < 2) ? 15 : 25; // throttle back the preview comps to try to leave the cpu free for encoding return true; } } @@ -254047,6 +254006,8 @@ public: if (ok && activeUsers > 0) mediaControl->Run(); + + previewMaxFPS = 60; } void addListener (CameraDevice::Listener* listenerToAdd) @@ -254086,7 +254047,7 @@ public: { public: DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_) - : owner (owner_) + : owner (owner_), maxFPS (15), lastRepaintTime (0) { setOpaque (true); owner->addChangeListener (this); @@ -254123,11 +254084,22 @@ public: void changeListenerCallback (void*) { - repaint(); + const int64 now = Time::currentTimeMillis(); + + if (now >= lastRepaintTime + (1000 / maxFPS)) + { + lastRepaintTime = now; + repaint(); + + if (owner != 0) + maxFPS = owner->getPreviewMaxFPS(); + } } private: DShowCameraDeviceInteral* owner; + int maxFPS; + int64 lastRepaintTime; }; bool ok; @@ -254157,6 +254129,7 @@ private: Image activeImage; bool recordNextFrameTime; + int previewMaxFPS; void getVideoSizes (IAMStreamConfig* const streamConfig) { @@ -254384,11 +254357,12 @@ const String CameraDevice::getFileExtension() void CameraDevice::startRecordingToFile (const File& file, int quality) { + jassert (quality >= 0 && quality <= 2); stopRecording(); DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal; d->addUser(); - isRecording = d->createFileCaptureFilter (file); + isRecording = d->createFileCaptureFilter (file, quality); } const Time CameraDevice::getTimeOfFirstRecordedFrame() const @@ -265301,7 +265275,7 @@ bool File::isHidden() const static const String getIOSSystemLocation (NSSearchPathDirectory type) { return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES) - objectAtIndex:0]); + objectAtIndex: 0]); } #endif @@ -276241,17 +276215,12 @@ static NSArray* findDiskBurnerDevices() NSMutableArray* results = [NSMutableArray array]; NSArray* devs = [DRDevice devices]; - if (devs != 0) + for (int i = 0; i < [devs count]; ++i) { - int num = [devs count]; - int i; - for (i = 0; i < num; ++i) - { - NSDictionary* dic = [[devs objectAtIndex: i] info]; - NSString* name = [dic valueForKey: DRDeviceProductNameKey]; - if (name != nil) - [results addObject: name]; - } + NSDictionary* dic = [[devs objectAtIndex: i] info]; + NSString* name = [dic valueForKey: DRDeviceProductNameKey]; + if (name != nil) + [results addObject: name]; } return results; diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 43c8f2544e..a077296519 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -64,7 +64,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 77 +#define JUCE_BUILDNUMBER 78 /** Current Juce version number. @@ -1123,18 +1123,19 @@ inline void swapVariables (Type& variable1, Type& variable2) int numElements = numElementsInArray (myArray) // returns 3 @endcode */ -template -inline int numElementsInArray (Type& array) +template +inline int numElementsInArray (Type (&array)[N]) { (void) array; // (required to avoid a spurious warning in MS compilers) - return static_cast (sizeof (array) / sizeof (0[array])); + sizeof (0[array]); // This line should cause an error if you pass an object with a user-defined subscript operator + return N; } // Some useful maths functions that aren't always present with all compilers and build settings. /** Using juce_hypot and juce_hypotf is easier than dealing with all the different versions of these functions of various platforms and compilers. */ -inline double juce_hypot (double a, double b) +inline double juce_hypot (double a, double b) throw() { #if JUCE_WINDOWS return _hypot (a, b); @@ -3665,9 +3666,7 @@ static void sortArray (ElementComparator& comparator, { if (comparator.compareElements (array[i], array [i + 1]) > 0) { - const ElementType temp = array [i]; - array [i] = array[i + 1]; - array [i + 1] = temp; + swapVariables (array[i], array[i + 1]); if (i > firstElement) i -= 2; @@ -3695,19 +3694,14 @@ static void sortArray (ElementComparator& comparator, if (comparator.compareElements (array[k], array [maxIndex]) > 0) maxIndex = k; - const ElementType temp = array [maxIndex]; - array [maxIndex] = array[j]; - array [j] = temp; - + swapVariables (array[j], array[maxIndex]); --j; } } else { const int mid = firstElement + (size >> 1); - ElementType temp = array [mid]; - array [mid] = array [firstElement]; - array [firstElement] = temp; + swapVariables (array[mid], array[firstElement]); int i = firstElement; int j = lastElement + 1; @@ -3725,14 +3719,10 @@ static void sortArray (ElementComparator& comparator, if (j < i) break; - temp = array[i]; - array[i] = array[j]; - array[j] = temp; + swapVariables (array[i], array[j]); } - temp = array [firstElement]; - array [firstElement] = array[j]; - array [j] = temp; + swapVariables (array[j], array[firstElement]); if (j - 1 - firstElement >= lastElement - i) { @@ -13897,22 +13887,21 @@ public: To improve performance, the compareElements() method can be declared as static or const. @param comparator the comparator to use for comparing elements. - @param retainOrderOfEquivalentItems if this is true, then items - which the comparator says are equivalent will be - kept in the order in which they currently appear - in the array. This is slower to perform, but may - be important in some cases. If it's false, a faster - algorithm is used, but equivalent elements may be - rearranged. + @param undoManager optional UndoManager for storing the changes + @param retainOrderOfEquivalentItems if this is true, then items which the comparator says are + equivalent will be kept in the order in which they currently appear in the array. + This is slower to perform, but may be important in some cases. If it's false, a + faster algorithm is used, but equivalent elements may be rearranged. */ template - void sort (ElementComparator& comparator, const bool retainOrderOfEquivalentItems = false) + void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems) { if (object != 0) { + ReferenceCountedArray sortedList (object->children); ComparatorAdapter adapter (comparator); - object->children.sort (adapter, retainOrderOfEquivalentItems); - object->sendChildChangeMessage(); + sortedList.sort (adapter, retainOrderOfEquivalentItems); + object->reorderChildren (sortedList, undoManager); } } @@ -13964,6 +13953,7 @@ private: void removeChild (int childIndex, UndoManager*); void removeAllChildren (UndoManager*); void moveChild (int currentIndex, int newIndex, UndoManager*); + void reorderChildren (const ReferenceCountedArray & newOrder, UndoManager*); bool isEquivalentTo (const SharedObject& other) const; XmlElement* createXml() const; @@ -24392,14 +24382,8 @@ public: /** Returns the transform that should be applied to these source co-ordinates to fit them into the destination rectangle using the current flags. */ - const AffineTransform getTransformToFit (float sourceX, - float sourceY, - float sourceW, - float sourceH, - float destinationX, - float destinationY, - float destinationW, - float destinationH) const throw(); + const AffineTransform getTransformToFit (const Rectangle& source, + const Rectangle& destination) const throw(); private: @@ -43224,6 +43208,9 @@ private: just call the post() method to send them, and when they arrive, your messageCallback() method will automatically be invoked. + Always create an instance of a CallbackMessage on the heap, as it will be + deleted automatically after the message has been delivered. + @see MessageListener, MessageManager, ActionListener, ChangeListener */ class JUCE_API CallbackMessage : public Message @@ -44527,19 +44514,13 @@ public: and can either be made as big as possible, or just reduced to fit. @param g the graphics context to render onto - @param destX top-left of the target rectangle to fit it into - @param destY top-left of the target rectangle to fit it into - @param destWidth size of the target rectangle to fit the image into - @param destHeight size of the target rectangle to fit the image into + @param destArea the target rectangle to fit the drawable into @param placement defines the alignment and rescaling to use to fit this object within the target rectangle. @param opacity the opacity to use, in the range 0 to 1.0 */ void drawWithin (Graphics& g, - int destX, - int destY, - int destWidth, - int destHeight, + const Rectangle& destArea, const RectanglePlacement& placement, float opacity) const; diff --git a/src/core/juce_MathsFunctions.h b/src/core/juce_MathsFunctions.h index 3b79634492..198cd40387 100644 --- a/src/core/juce_MathsFunctions.h +++ b/src/core/juce_MathsFunctions.h @@ -172,11 +172,12 @@ inline void swapVariables (Type& variable1, Type& variable2) int numElements = numElementsInArray (myArray) // returns 3 @endcode */ -template -inline int numElementsInArray (Type& array) +template +inline int numElementsInArray (Type (&array)[N]) { (void) array; // (required to avoid a spurious warning in MS compilers) - return static_cast (sizeof (array) / sizeof (0[array])); + sizeof (0[array]); // This line should cause an error if you pass an object with a user-defined subscript operator + return N; } //============================================================================== @@ -184,7 +185,7 @@ inline int numElementsInArray (Type& array) /** Using juce_hypot and juce_hypotf is easier than dealing with all the different versions of these functions of various platforms and compilers. */ -inline double juce_hypot (double a, double b) +inline double juce_hypot (double a, double b) throw() { #if JUCE_WINDOWS return _hypot (a, b); diff --git a/src/gui/graphics/drawables/juce_Drawable.cpp b/src/gui/graphics/drawables/juce_Drawable.cpp index fc0b670c87..3a4307e475 100644 --- a/src/gui/graphics/drawables/juce_Drawable.cpp +++ b/src/gui/graphics/drawables/juce_Drawable.cpp @@ -69,22 +69,12 @@ void Drawable::drawAt (Graphics& g, const float x, const float y, const float op } void Drawable::drawWithin (Graphics& g, - const int destX, - const int destY, - const int destW, - const int destH, + const Rectangle& destArea, const RectanglePlacement& placement, const float opacity) const { - if (destW > 0 && destH > 0) - { - Rectangle bounds (getBounds()); - - draw (g, opacity, - placement.getTransformToFit (bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight(), - (float) destX, (float) destY, - (float) destW, (float) destH)); - } + if (! destArea.isEmpty()) + draw (g, opacity, placement.getTransformToFit (getBounds(), destArea)); } //============================================================================== diff --git a/src/native/windows/juce_win32_ASIO.cpp b/src/native/windows/juce_win32_ASIO.cpp index 309f023df3..80c80a1939 100644 --- a/src/native/windows/juce_win32_ASIO.cpp +++ b/src/native/windows/juce_win32_ASIO.cpp @@ -38,8 +38,7 @@ #define log(a) {} #endif -#define JUCE_ASIOCALLBACK // should probably use this to define the callback type, but - // the asio header doesn't actually specify a calling convention for the functions.. +#define JUCE_ASIOCALLBACK __cdecl //============================================================================== #if ASIO_DEBUGGING @@ -146,40 +145,15 @@ public: } } - const StringArray getOutputChannelNames() - { - return outputChannelNames; - } + const StringArray getOutputChannelNames() { return outputChannelNames; } + const StringArray getInputChannelNames() { return inputChannelNames; } - const StringArray getInputChannelNames() - { - return inputChannelNames; - } + int getNumSampleRates() { return sampleRates.size(); } + double getSampleRate (int index) { return sampleRates [index]; } - int getNumSampleRates() - { - return sampleRates.size(); - } - - double getSampleRate (int index) - { - return sampleRates [index]; - } - - int getNumBufferSizesAvailable() - { - return bufferSizes.size(); - } - - int getBufferSizeSamples (int index) - { - return bufferSizes [index]; - } - - int getDefaultBufferSize() - { - return preferredSize; - } + int getNumBufferSizesAvailable() { return bufferSizes.size(); } + int getBufferSizeSamples (int index) { return bufferSizes [index]; } + int getDefaultBufferSize() { return preferredSize; } const String open (const BigInteger& inputChannels, const BigInteger& outputChannels, @@ -633,45 +607,18 @@ public: } } - bool isOpen() - { - return isOpen_ || insideControlPanelModalLoop; - } - - int getCurrentBufferSizeSamples() - { - return currentBlockSizeSamples; - } - - double getCurrentSampleRate() - { - return currentSampleRate; - } - - const BigInteger getActiveOutputChannels() const - { - return currentChansOut; - } + bool isOpen() { return isOpen_ || insideControlPanelModalLoop; } + bool isPlaying() { return isASIOOpen && (currentCallback != 0); } - const BigInteger getActiveInputChannels() const - { - return currentChansIn; - } + int getCurrentBufferSizeSamples() { return currentBlockSizeSamples; } + double getCurrentSampleRate() { return currentSampleRate; } + int getCurrentBitDepth() { return currentBitDepth; } - int getCurrentBitDepth() - { - return currentBitDepth; - } - - int getOutputLatencyInSamples() - { - return outputLatency + currentBlockSizeSamples / 4; - } + const BigInteger getActiveOutputChannels() const { return currentChansOut; } + const BigInteger getActiveInputChannels() const { return currentChansIn; } - int getInputLatencyInSamples() - { - return inputLatency + currentBlockSizeSamples / 4; - } + int getOutputLatencyInSamples() { return outputLatency + currentBlockSizeSamples / 4; } + int getInputLatencyInSamples() { return inputLatency + currentBlockSizeSamples / 4; } void start (AudioIODeviceCallback* callback) { @@ -697,20 +644,8 @@ public: lastCallback->audioDeviceStopped(); } - bool isPlaying() - { - return isASIOOpen && (currentCallback != 0); - } - - const String getLastError() - { - return error; - } - - bool hasControlPanel() const - { - return true; - } + const String getLastError() { return error; } + bool hasControlPanel() const { return true; } bool showControlPanel() { @@ -840,7 +775,6 @@ private: bool volatile insideControlPanelModalLoop; bool volatile shouldUsePreferredSize; - //============================================================================== void removeCurrentDriver() { @@ -1242,7 +1176,6 @@ private: for (i = 0; i < numActiveInputChans; ++i) { float* const dst = inBuffers[i]; - jassert (dst != 0); const char* const src = (const char*) (infos[i].buffers[bi]); @@ -1279,16 +1212,12 @@ private: } } - currentCallback->audioDeviceIOCallback ((const float**) inBuffers, - numActiveInputChans, - outBuffers, - numActiveOutputChans, - samps); + currentCallback->audioDeviceIOCallback ((const float**) inBuffers, numActiveInputChans, + outBuffers, numActiveOutputChans, samps); for (i = 0; i < numActiveOutputChans; ++i) { float* const src = outBuffers[i]; - jassert (src != 0); char* const dst = (char*) (infos [numActiveInputChans + i].buffers[bi]); diff --git a/src/native/windows/juce_win32_CameraDevice.cpp b/src/native/windows/juce_win32_CameraDevice.cpp index 6139643f52..09ec49a499 100644 --- a/src/native/windows/juce_win32_CameraDevice.cpp +++ b/src/native/windows/juce_win32_CameraDevice.cpp @@ -45,7 +45,8 @@ public: width (0), height (0), activeUsers (0), - recordNextFrameTime (false) + recordNextFrameTime (false), + previewMaxFPS (60) { HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph); if (FAILED (hr)) @@ -176,6 +177,11 @@ public: mediaControl->Stop(); } + int getPreviewMaxFPS() const + { + return previewMaxFPS; + } + void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/) { if (recordNextFrameTime) @@ -246,13 +252,14 @@ public: g.drawImage (activeImage, rx, ry, rw, rh, 0, 0, width, height); } - bool createFileCaptureFilter (const File& file) + bool createFileCaptureFilter (const File& file, int quality) { removeFileCaptureFilter(); file.deleteFile(); mediaControl->Stop(); firstRecordedTime = Time(); recordNextFrameTime = true; + previewMaxFPS = 60; HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter); @@ -278,17 +285,30 @@ public: hr = WMCreateProfileManager (profileManager.resetAndGetPointerAddress()); // This gibberish is the DirectShow profile for a video-only wmv file. - String prof ("" - " " - ""); + String prof ("" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""); + + const int fps[] = { 10, 15, 30 }; + const int maxFramesPerSecond = fps [quality % numElementsInArray (fps)]; prof = prof.replace ("$WIDTH", String (width)) - .replace ("$HEIGHT", String (height)); + .replace ("$HEIGHT", String (height)) + .replace ("$AVGTIMEPERFRAME", String (10000000 / maxFramesPerSecond)); ComSmartPtr currentProfile; hr = profileManager->LoadProfileByData ((const WCHAR*) prof, currentProfile.resetAndGetPointerAddress()); @@ -306,6 +326,7 @@ public: && ok && activeUsers > 0 && SUCCEEDED (mediaControl->Run())) { + previewMaxFPS = (quality < 2) ? 15 : 25; // throttle back the preview comps to try to leave the cpu free for encoding return true; } } @@ -335,6 +356,8 @@ public: if (ok && activeUsers > 0) mediaControl->Run(); + + previewMaxFPS = 60; } //============================================================================== @@ -377,7 +400,7 @@ public: { public: DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_) - : owner (owner_) + : owner (owner_), maxFPS (15), lastRepaintTime (0) { setOpaque (true); owner->addChangeListener (this); @@ -414,11 +437,22 @@ public: void changeListenerCallback (void*) { - repaint(); + const int64 now = Time::currentTimeMillis(); + + if (now >= lastRepaintTime + (1000 / maxFPS)) + { + lastRepaintTime = now; + repaint(); + + if (owner != 0) + maxFPS = owner->getPreviewMaxFPS(); + } } private: DShowCameraDeviceInteral* owner; + int maxFPS; + int64 lastRepaintTime; }; //============================================================================== @@ -449,6 +483,7 @@ private: Image activeImage; bool recordNextFrameTime; + int previewMaxFPS; void getVideoSizes (IAMStreamConfig* const streamConfig) { @@ -681,11 +716,12 @@ const String CameraDevice::getFileExtension() void CameraDevice::startRecordingToFile (const File& file, int quality) { + jassert (quality >= 0 && quality <= 2); stopRecording(); DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal; d->addUser(); - isRecording = d->createFileCaptureFilter (file); + isRecording = d->createFileCaptureFilter (file, quality); } const Time CameraDevice::getTimeOfFirstRecordedFrame() const