Browse Source

Tweaks to the win32 camera capture code to reduce CPU load.

tags/2021-05-28
Julian Storer 14 years ago
parent
commit
ea16741b3d
8 changed files with 308 additions and 342 deletions
  1. +62
    -0
      extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h
  2. +0
    -2
      extras/juce demo/Source/demos/WidgetsDemo.cpp
  3. +139
    -170
      juce_amalgamated.cpp
  4. +28
    -47
      juce_amalgamated.h
  5. +5
    -4
      src/core/juce_MathsFunctions.h
  6. +3
    -13
      src/gui/graphics/drawables/juce_Drawable.cpp
  7. +21
    -92
      src/native/windows/juce_win32_ASIO.cpp
  8. +50
    -14
      src/native/windows/juce_win32_CameraDevice.cpp

+ 62
- 0
extras/Jucer (experimental)/Source/Utility/jucer_MiscUtilities.h View File

@@ -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 <DrawableComposite*> (static_cast <Drawable*> (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<Drawable> drawableObject;
void updateGraphics()
{
if (drawableObject != 0)
{
const Rectangle<float> dirtyArea (drawableObject->refreshFromValueTree (drawable, 0));
repaint (dirtyArea.getSmallestIntegerContainer());
}
}
};
//==============================================================================
/**
*/


+ 0
- 2
extras/juce demo/Source/demos/WidgetsDemo.cpp View File

@@ -1110,8 +1110,6 @@ public:
#endif
//==============================================================================
const int numGroups = 4;
class WidgetsDemo : public Component,
public ButtonListener
{


+ 139
- 170
juce_amalgamated.cpp View File

@@ -18349,6 +18349,28 @@ void ValueTree::SharedObject::moveChild (int currentIndex, int newIndex, UndoMan
}
}

void ValueTree::SharedObject::reorderChildren (const ReferenceCountedArray <SharedObject>& 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<float> 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<int> 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<int>& screenPos, const int64 time, Component* const component) throw()
void registerMouseDown (const Point<int>& 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<float>& source, const Rectangle<float>& 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<float>& destArea,
const RectanglePlacement& placement,
const float opacity) const
{
if (destW > 0 && destH > 0)
{
Rectangle<float> 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<float> (vx, vy, vw, vh),
Rectangle<float> (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 ("<profile version=\"589824\" storageformat=\"1\" name=\"Quality\" description=\"Quality type for output.\"><streamconfig "
"majortype=\"{73646976-0000-0010-8000-00AA00389B71}\" streamnumber=\"1\" streamname=\"Video Stream\" inputname=\"Video409\" bitrate=\"894960\" "
"bufferwindow=\"0\" reliabletransport=\"1\" decodercomplexity=\"AU\" rfc1766langid=\"en-us\"><videomediaprops maxkeyframespacing=\"50000000\" quality=\"90\"/>"
"<wmmediatype subtype=\"{33564D57-0000-0010-8000-00AA00389B71}\" bfixedsizesamples=\"0\" btemporalcompression=\"1\" lsamplesize=\"0\"> <videoinfoheader "
"dwbitrate=\"894960\" dwbiterrorrate=\"0\" avgtimeperframe=\"100000\"><rcsource left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/> <rctarget "
"left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/> <bitmapinfoheader biwidth=\"$WIDTH\" biheight=\"$HEIGHT\" biplanes=\"1\" bibitcount=\"24\" "
"bicompression=\"WMV3\" bisizeimage=\"0\" bixpelspermeter=\"0\" biypelspermeter=\"0\" biclrused=\"0\" biclrimportant=\"0\"/> "
"</videoinfoheader></wmmediatype></streamconfig></profile>");
String prof ("<profile version=\"589824\" storageformat=\"1\" name=\"Quality\" description=\"Quality type for output.\">"
" <streamconfig majortype=\"{73646976-0000-0010-8000-00AA00389B71}\" streamnumber=\"1\""
" streamname=\"Video Stream\" inputname=\"Video409\" bitrate=\"894960\""
" bufferwindow=\"0\" reliabletransport=\"1\" decodercomplexity=\"AU\" rfc1766langid=\"en-us\">"
" <videomediaprops maxkeyframespacing=\"50000000\" quality=\"90\"/>"
" <wmmediatype subtype=\"{33564D57-0000-0010-8000-00AA00389B71}\" bfixedsizesamples=\"0\""
" btemporalcompression=\"1\" lsamplesize=\"0\">"
" <videoinfoheader dwbitrate=\"894960\" dwbiterrorrate=\"0\" avgtimeperframe=\"$AVGTIMEPERFRAME\">"
" <rcsource left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
" <rctarget left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
" <bitmapinfoheader biwidth=\"$WIDTH\" biheight=\"$HEIGHT\" biplanes=\"1\" bibitcount=\"24\""
" bicompression=\"WMV3\" bisizeimage=\"0\" bixpelspermeter=\"0\" biypelspermeter=\"0\""
" biclrused=\"0\" biclrimportant=\"0\"/>"
" </videoinfoheader>"
" </wmmediatype>"
" </streamconfig>"
"</profile>");

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 <IWMProfile> 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;


+ 28
- 47
juce_amalgamated.h View File

@@ -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 <typename Type>
inline int numElementsInArray (Type& array)
template <typename Type, int N>
inline int numElementsInArray (Type (&array)[N])
{
(void) array; // (required to avoid a spurious warning in MS compilers)
return static_cast<int> (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 <typename ElementComparator>
void sort (ElementComparator& comparator, const bool retainOrderOfEquivalentItems = false)
void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems)
{
if (object != 0)
{
ReferenceCountedArray <SharedObject> sortedList (object->children);
ComparatorAdapter <ElementComparator> 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 <SharedObject>& 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<float>& source,
const Rectangle<float>& 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<float>& destArea,
const RectanglePlacement& placement,
float opacity) const;



+ 5
- 4
src/core/juce_MathsFunctions.h View File

@@ -172,11 +172,12 @@ inline void swapVariables (Type& variable1, Type& variable2)
int numElements = numElementsInArray (myArray) // returns 3
@endcode
*/
template <typename Type>
inline int numElementsInArray (Type& array)
template <typename Type, int N>
inline int numElementsInArray (Type (&array)[N])
{
(void) array; // (required to avoid a spurious warning in MS compilers)
return static_cast<int> (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);


+ 3
- 13
src/gui/graphics/drawables/juce_Drawable.cpp View File

@@ -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<float>& destArea,
const RectanglePlacement& placement,
const float opacity) const
{
if (destW > 0 && destH > 0)
{
Rectangle<float> 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));
}
//==============================================================================


+ 21
- 92
src/native/windows/juce_win32_ASIO.cpp View File

@@ -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]);


+ 50
- 14
src/native/windows/juce_win32_CameraDevice.cpp View File

@@ -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 ("<profile version=\"589824\" storageformat=\"1\" name=\"Quality\" description=\"Quality type for output.\"><streamconfig "
"majortype=\"{73646976-0000-0010-8000-00AA00389B71}\" streamnumber=\"1\" streamname=\"Video Stream\" inputname=\"Video409\" bitrate=\"894960\" "
"bufferwindow=\"0\" reliabletransport=\"1\" decodercomplexity=\"AU\" rfc1766langid=\"en-us\"><videomediaprops maxkeyframespacing=\"50000000\" quality=\"90\"/>"
"<wmmediatype subtype=\"{33564D57-0000-0010-8000-00AA00389B71}\" bfixedsizesamples=\"0\" btemporalcompression=\"1\" lsamplesize=\"0\"> <videoinfoheader "
"dwbitrate=\"894960\" dwbiterrorrate=\"0\" avgtimeperframe=\"100000\"><rcsource left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/> <rctarget "
"left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/> <bitmapinfoheader biwidth=\"$WIDTH\" biheight=\"$HEIGHT\" biplanes=\"1\" bibitcount=\"24\" "
"bicompression=\"WMV3\" bisizeimage=\"0\" bixpelspermeter=\"0\" biypelspermeter=\"0\" biclrused=\"0\" biclrimportant=\"0\"/> "
"</videoinfoheader></wmmediatype></streamconfig></profile>");
String prof ("<profile version=\"589824\" storageformat=\"1\" name=\"Quality\" description=\"Quality type for output.\">"
" <streamconfig majortype=\"{73646976-0000-0010-8000-00AA00389B71}\" streamnumber=\"1\""
" streamname=\"Video Stream\" inputname=\"Video409\" bitrate=\"894960\""
" bufferwindow=\"0\" reliabletransport=\"1\" decodercomplexity=\"AU\" rfc1766langid=\"en-us\">"
" <videomediaprops maxkeyframespacing=\"50000000\" quality=\"90\"/>"
" <wmmediatype subtype=\"{33564D57-0000-0010-8000-00AA00389B71}\" bfixedsizesamples=\"0\""
" btemporalcompression=\"1\" lsamplesize=\"0\">"
" <videoinfoheader dwbitrate=\"894960\" dwbiterrorrate=\"0\" avgtimeperframe=\"$AVGTIMEPERFRAME\">"
" <rcsource left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
" <rctarget left=\"0\" top=\"0\" right=\"$WIDTH\" bottom=\"$HEIGHT\"/>"
" <bitmapinfoheader biwidth=\"$WIDTH\" biheight=\"$HEIGHT\" biplanes=\"1\" bibitcount=\"24\""
" bicompression=\"WMV3\" bisizeimage=\"0\" bixpelspermeter=\"0\" biypelspermeter=\"0\""
" biclrused=\"0\" biclrimportant=\"0\"/>"
" </videoinfoheader>"
" </wmmediatype>"
" </streamconfig>"
"</profile>");
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 <IWMProfile> 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


Loading…
Cancel
Save