@@ -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()); | |||||
} | |||||
} | |||||
}; | |||||
//============================================================================== | //============================================================================== | ||||
/** | /** | ||||
*/ | */ | ||||
@@ -1110,8 +1110,6 @@ public: | |||||
#endif | #endif | ||||
//============================================================================== | //============================================================================== | ||||
const int numGroups = 4; | |||||
class WidgetsDemo : public Component, | class WidgetsDemo : public Component, | ||||
public ButtonListener | public ButtonListener | ||||
{ | { | ||||
@@ -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 | bool ValueTree::SharedObject::isEquivalentTo (const SharedObject& other) const | ||||
{ | { | ||||
if (type != other.type | if (type != other.type | ||||
@@ -44257,19 +44279,9 @@ void DrawableButton::paintButton (Graphics& g, | |||||
if (imageToDraw != 0) | if (imageToDraw != 0) | ||||
{ | { | ||||
if (style == ImageRaw) | if (style == ImageRaw) | ||||
{ | |||||
imageToDraw->draw (g, 1.0f); | imageToDraw->draw (g, 1.0f); | ||||
} | |||||
else | 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) | if (getToggleState() && toggledOnImage != 0) | ||||
d = toggledOnImage; | d = toggledOnImage; | ||||
const Rectangle<float> area (0.0f, 0.0f, (float) width, (float) height); | |||||
if (! isEnabled()) | if (! isEnabled()) | ||||
{ | { | ||||
Image im (Image::ARGB, width, height, true); | Image im (Image::ARGB, width, height, true); | ||||
{ | { | ||||
Graphics g2 (im); | Graphics g2 (im); | ||||
d->drawWithin (g2, 0, 0, width, height, RectanglePlacement::centred, 1.0f); | |||||
d->drawWithin (g2, area, RectanglePlacement::centred, 1.0f); | |||||
} | } | ||||
im.desaturate(); | im.desaturate(); | ||||
@@ -44845,7 +44859,7 @@ void ToolbarButton::paintButtonArea (Graphics& g, | |||||
} | } | ||||
else | 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) | if (current != 0) | ||||
{ | { | ||||
registerMouseDown (screenPos, time, current); | |||||
registerMouseDown (screenPos, time, current, buttonState); | |||||
sendMouseDown (current, screenPos, time); | sendMouseDown (current, screenPos, time); | ||||
} | } | ||||
} | } | ||||
@@ -71845,16 +71859,10 @@ public: | |||||
for (int i = 1; i < numElementsInArray (mouseDowns); ++i) | 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; | ++numClicks; | ||||
} | |||||
else | else | ||||
{ | |||||
break; | break; | ||||
} | |||||
} | } | ||||
} | } | ||||
@@ -71970,13 +71978,23 @@ private: | |||||
Point<int> position; | Point<int> position; | ||||
int64 time; | int64 time; | ||||
Component* component; | 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]; | RecentMouseDown mouseDowns[4]; | ||||
bool mouseMovedSignificantlySincePressed; | bool mouseMovedSignificantlySincePressed; | ||||
int64 lastTime; | 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;) | for (int i = numElementsInArray (mouseDowns); --i > 0;) | ||||
mouseDowns[i] = mouseDowns[i - 1]; | mouseDowns[i] = mouseDowns[i - 1]; | ||||
@@ -71984,6 +72002,7 @@ private: | |||||
mouseDowns[0].position = screenPos; | mouseDowns[0].position = screenPos; | ||||
mouseDowns[0].time = time; | mouseDowns[0].time = time; | ||||
mouseDowns[0].component = component; | mouseDowns[0].component = component; | ||||
mouseDowns[0].buttons = modifiers.withOnlyMouseButtons(); | |||||
mouseMovedSignificantlySincePressed = false; | 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; | 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) | if ((flags & stretchToFit) != 0) | ||||
return AffineTransform::translation (-x, -y) | |||||
return AffineTransform::translation (-source.getX(), -source.getY()) | |||||
.scaled (scaleX, scaleY) | .scaled (scaleX, scaleY) | ||||
.translated (dx, dy); | |||||
.translated (destination.getX(), destination.getY()); | |||||
float scale = (flags & fillDestination) != 0 ? jmax (scaleX, scaleY) | float scale = (flags & fillDestination) != 0 ? jmax (scaleX, scaleY) | ||||
: jmin (scaleX, scaleY); | : jmin (scaleX, scaleY); | ||||
@@ -85960,21 +85979,20 @@ const AffineTransform RectanglePlacement::getTransformToFit (float x, float y, | |||||
w *= scale; | w *= scale; | ||||
h *= scale; | h *= scale; | ||||
float newX = dx; | |||||
float newX = destination.getX(); | |||||
float newY = destination.getY(); | |||||
if ((flags & xRight) != 0) | if ((flags & xRight) != 0) | ||||
newX += dw - w; // right | |||||
newX += destination.getWidth() - w; // right | |||||
else if ((flags & xLeft) == 0) | 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) | if ((flags & yBottom) != 0) | ||||
newY += dh - h; // bottom | |||||
newY += destination.getHeight() - h; // bottom | |||||
else if ((flags & yTop) == 0) | 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) | .scaled (scale, scale) | ||||
.translated (newX, newY); | .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, | 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 RectanglePlacement& placement, | ||||
const float opacity) const | 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) | Drawable* Drawable::createFromImageData (const void* data, const size_t numBytes) | ||||
@@ -88190,8 +88198,8 @@ public: | |||||
const RectanglePlacement placement (placementFlags); | const RectanglePlacement placement (placementFlags); | ||||
newState.transform | 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); | .followedBy (newState.transform); | ||||
} | } | ||||
} | } | ||||
@@ -249243,8 +249251,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) | |||||
#define log(a) {} | #define log(a) {} | ||||
#endif | #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 | #if ASIO_DEBUGGING | ||||
static void logError (const String& context, long error) | 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 String open (const BigInteger& inputChannels, | ||||
const BigInteger& outputChannels, | 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) | void start (AudioIODeviceCallback* callback) | ||||
{ | { | ||||
@@ -249898,20 +249853,8 @@ public: | |||||
lastCallback->audioDeviceStopped(); | 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() | bool showControlPanel() | ||||
{ | { | ||||
@@ -250438,7 +250381,6 @@ private: | |||||
for (i = 0; i < numActiveInputChans; ++i) | for (i = 0; i < numActiveInputChans; ++i) | ||||
{ | { | ||||
float* const dst = inBuffers[i]; | float* const dst = inBuffers[i]; | ||||
jassert (dst != 0); | jassert (dst != 0); | ||||
const char* const src = (const char*) (infos[i].buffers[bi]); | 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) | for (i = 0; i < numActiveOutputChans; ++i) | ||||
{ | { | ||||
float* const src = outBuffers[i]; | float* const src = outBuffers[i]; | ||||
jassert (src != 0); | jassert (src != 0); | ||||
char* const dst = (char*) (infos [numActiveInputChans + i].buffers[bi]); | char* const dst = (char*) (infos [numActiveInputChans + i].buffers[bi]); | ||||
@@ -253757,7 +253695,8 @@ public: | |||||
width (0), | width (0), | ||||
height (0), | height (0), | ||||
activeUsers (0), | activeUsers (0), | ||||
recordNextFrameTime (false) | |||||
recordNextFrameTime (false), | |||||
previewMaxFPS (60) | |||||
{ | { | ||||
HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph); | HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph); | ||||
if (FAILED (hr)) | if (FAILED (hr)) | ||||
@@ -253888,6 +253827,11 @@ public: | |||||
mediaControl->Stop(); | mediaControl->Stop(); | ||||
} | } | ||||
int getPreviewMaxFPS() const | |||||
{ | |||||
return previewMaxFPS; | |||||
} | |||||
void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/) | void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/) | ||||
{ | { | ||||
if (recordNextFrameTime) | if (recordNextFrameTime) | ||||
@@ -253958,13 +253902,14 @@ public: | |||||
g.drawImage (activeImage, rx, ry, rw, rh, 0, 0, width, height); | g.drawImage (activeImage, rx, ry, rw, rh, 0, 0, width, height); | ||||
} | } | ||||
bool createFileCaptureFilter (const File& file) | |||||
bool createFileCaptureFilter (const File& file, int quality) | |||||
{ | { | ||||
removeFileCaptureFilter(); | removeFileCaptureFilter(); | ||||
file.deleteFile(); | file.deleteFile(); | ||||
mediaControl->Stop(); | mediaControl->Stop(); | ||||
firstRecordedTime = Time(); | firstRecordedTime = Time(); | ||||
recordNextFrameTime = true; | recordNextFrameTime = true; | ||||
previewMaxFPS = 60; | |||||
HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter); | HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter); | ||||
@@ -253990,17 +253935,30 @@ public: | |||||
hr = WMCreateProfileManager (profileManager.resetAndGetPointerAddress()); | hr = WMCreateProfileManager (profileManager.resetAndGetPointerAddress()); | ||||
// This gibberish is the DirectShow profile for a video-only wmv file. | // 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)) | prof = prof.replace ("$WIDTH", String (width)) | ||||
.replace ("$HEIGHT", String (height)); | |||||
.replace ("$HEIGHT", String (height)) | |||||
.replace ("$AVGTIMEPERFRAME", String (10000000 / maxFramesPerSecond)); | |||||
ComSmartPtr <IWMProfile> currentProfile; | ComSmartPtr <IWMProfile> currentProfile; | ||||
hr = profileManager->LoadProfileByData ((const WCHAR*) prof, currentProfile.resetAndGetPointerAddress()); | hr = profileManager->LoadProfileByData ((const WCHAR*) prof, currentProfile.resetAndGetPointerAddress()); | ||||
@@ -254018,6 +253976,7 @@ public: | |||||
&& ok && activeUsers > 0 | && ok && activeUsers > 0 | ||||
&& SUCCEEDED (mediaControl->Run())) | && SUCCEEDED (mediaControl->Run())) | ||||
{ | { | ||||
previewMaxFPS = (quality < 2) ? 15 : 25; // throttle back the preview comps to try to leave the cpu free for encoding | |||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
@@ -254047,6 +254006,8 @@ public: | |||||
if (ok && activeUsers > 0) | if (ok && activeUsers > 0) | ||||
mediaControl->Run(); | mediaControl->Run(); | ||||
previewMaxFPS = 60; | |||||
} | } | ||||
void addListener (CameraDevice::Listener* listenerToAdd) | void addListener (CameraDevice::Listener* listenerToAdd) | ||||
@@ -254086,7 +254047,7 @@ public: | |||||
{ | { | ||||
public: | public: | ||||
DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_) | DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_) | ||||
: owner (owner_) | |||||
: owner (owner_), maxFPS (15), lastRepaintTime (0) | |||||
{ | { | ||||
setOpaque (true); | setOpaque (true); | ||||
owner->addChangeListener (this); | owner->addChangeListener (this); | ||||
@@ -254123,11 +254084,22 @@ public: | |||||
void changeListenerCallback (void*) | void changeListenerCallback (void*) | ||||
{ | { | ||||
repaint(); | |||||
const int64 now = Time::currentTimeMillis(); | |||||
if (now >= lastRepaintTime + (1000 / maxFPS)) | |||||
{ | |||||
lastRepaintTime = now; | |||||
repaint(); | |||||
if (owner != 0) | |||||
maxFPS = owner->getPreviewMaxFPS(); | |||||
} | |||||
} | } | ||||
private: | private: | ||||
DShowCameraDeviceInteral* owner; | DShowCameraDeviceInteral* owner; | ||||
int maxFPS; | |||||
int64 lastRepaintTime; | |||||
}; | }; | ||||
bool ok; | bool ok; | ||||
@@ -254157,6 +254129,7 @@ private: | |||||
Image activeImage; | Image activeImage; | ||||
bool recordNextFrameTime; | bool recordNextFrameTime; | ||||
int previewMaxFPS; | |||||
void getVideoSizes (IAMStreamConfig* const streamConfig) | void getVideoSizes (IAMStreamConfig* const streamConfig) | ||||
{ | { | ||||
@@ -254384,11 +254357,12 @@ const String CameraDevice::getFileExtension() | |||||
void CameraDevice::startRecordingToFile (const File& file, int quality) | void CameraDevice::startRecordingToFile (const File& file, int quality) | ||||
{ | { | ||||
jassert (quality >= 0 && quality <= 2); | |||||
stopRecording(); | stopRecording(); | ||||
DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal; | DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal; | ||||
d->addUser(); | d->addUser(); | ||||
isRecording = d->createFileCaptureFilter (file); | |||||
isRecording = d->createFileCaptureFilter (file, quality); | |||||
} | } | ||||
const Time CameraDevice::getTimeOfFirstRecordedFrame() const | const Time CameraDevice::getTimeOfFirstRecordedFrame() const | ||||
@@ -265301,7 +265275,7 @@ bool File::isHidden() const | |||||
static const String getIOSSystemLocation (NSSearchPathDirectory type) | static const String getIOSSystemLocation (NSSearchPathDirectory type) | ||||
{ | { | ||||
return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES) | return nsStringToJuce ([NSSearchPathForDirectoriesInDomains (type, NSUserDomainMask, YES) | ||||
objectAtIndex:0]); | |||||
objectAtIndex: 0]); | |||||
} | } | ||||
#endif | #endif | ||||
@@ -276241,17 +276215,12 @@ static NSArray* findDiskBurnerDevices() | |||||
NSMutableArray* results = [NSMutableArray array]; | NSMutableArray* results = [NSMutableArray array]; | ||||
NSArray* devs = [DRDevice devices]; | 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; | return results; | ||||
@@ -64,7 +64,7 @@ | |||||
*/ | */ | ||||
#define JUCE_MAJOR_VERSION 1 | #define JUCE_MAJOR_VERSION 1 | ||||
#define JUCE_MINOR_VERSION 52 | #define JUCE_MINOR_VERSION 52 | ||||
#define JUCE_BUILDNUMBER 77 | |||||
#define JUCE_BUILDNUMBER 78 | |||||
/** Current Juce version number. | /** Current Juce version number. | ||||
@@ -1123,18 +1123,19 @@ inline void swapVariables (Type& variable1, Type& variable2) | |||||
int numElements = numElementsInArray (myArray) // returns 3 | int numElements = numElementsInArray (myArray) // returns 3 | ||||
@endcode | @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) | (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. | // 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 | /** Using juce_hypot and juce_hypotf is easier than dealing with all the different | ||||
versions of these functions of various platforms and compilers. */ | 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 | #if JUCE_WINDOWS | ||||
return _hypot (a, b); | return _hypot (a, b); | ||||
@@ -3665,9 +3666,7 @@ static void sortArray (ElementComparator& comparator, | |||||
{ | { | ||||
if (comparator.compareElements (array[i], array [i + 1]) > 0) | 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) | if (i > firstElement) | ||||
i -= 2; | i -= 2; | ||||
@@ -3695,19 +3694,14 @@ static void sortArray (ElementComparator& comparator, | |||||
if (comparator.compareElements (array[k], array [maxIndex]) > 0) | if (comparator.compareElements (array[k], array [maxIndex]) > 0) | ||||
maxIndex = k; | maxIndex = k; | ||||
const ElementType temp = array [maxIndex]; | |||||
array [maxIndex] = array[j]; | |||||
array [j] = temp; | |||||
swapVariables (array[j], array[maxIndex]); | |||||
--j; | --j; | ||||
} | } | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
const int mid = firstElement + (size >> 1); | 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 i = firstElement; | ||||
int j = lastElement + 1; | int j = lastElement + 1; | ||||
@@ -3725,14 +3719,10 @@ static void sortArray (ElementComparator& comparator, | |||||
if (j < i) | if (j < i) | ||||
break; | 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) | if (j - 1 - firstElement >= lastElement - i) | ||||
{ | { | ||||
@@ -13897,22 +13887,21 @@ public: | |||||
To improve performance, the compareElements() method can be declared as static or const. | To improve performance, the compareElements() method can be declared as static or const. | ||||
@param comparator the comparator to use for comparing elements. | @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> | template <typename ElementComparator> | ||||
void sort (ElementComparator& comparator, const bool retainOrderOfEquivalentItems = false) | |||||
void sort (ElementComparator& comparator, UndoManager* undoManager, bool retainOrderOfEquivalentItems) | |||||
{ | { | ||||
if (object != 0) | if (object != 0) | ||||
{ | { | ||||
ReferenceCountedArray <SharedObject> sortedList (object->children); | |||||
ComparatorAdapter <ElementComparator> adapter (comparator); | 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 removeChild (int childIndex, UndoManager*); | ||||
void removeAllChildren (UndoManager*); | void removeAllChildren (UndoManager*); | ||||
void moveChild (int currentIndex, int newIndex, UndoManager*); | void moveChild (int currentIndex, int newIndex, UndoManager*); | ||||
void reorderChildren (const ReferenceCountedArray <SharedObject>& newOrder, UndoManager*); | |||||
bool isEquivalentTo (const SharedObject& other) const; | bool isEquivalentTo (const SharedObject& other) const; | ||||
XmlElement* createXml() const; | XmlElement* createXml() const; | ||||
@@ -24392,14 +24382,8 @@ public: | |||||
/** Returns the transform that should be applied to these source co-ordinates to fit them | /** Returns the transform that should be applied to these source co-ordinates to fit them | ||||
into the destination rectangle using the current flags. | 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: | private: | ||||
@@ -43224,6 +43208,9 @@ private: | |||||
just call the post() method to send them, and when they arrive, your | just call the post() method to send them, and when they arrive, your | ||||
messageCallback() method will automatically be invoked. | 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 | @see MessageListener, MessageManager, ActionListener, ChangeListener | ||||
*/ | */ | ||||
class JUCE_API CallbackMessage : public Message | 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. | and can either be made as big as possible, or just reduced to fit. | ||||
@param g the graphics context to render onto | @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 | @param placement defines the alignment and rescaling to use to fit | ||||
this object within the target rectangle. | this object within the target rectangle. | ||||
@param opacity the opacity to use, in the range 0 to 1.0 | @param opacity the opacity to use, in the range 0 to 1.0 | ||||
*/ | */ | ||||
void drawWithin (Graphics& g, | void drawWithin (Graphics& g, | ||||
int destX, | |||||
int destY, | |||||
int destWidth, | |||||
int destHeight, | |||||
const Rectangle<float>& destArea, | |||||
const RectanglePlacement& placement, | const RectanglePlacement& placement, | ||||
float opacity) const; | float opacity) const; | ||||
@@ -172,11 +172,12 @@ inline void swapVariables (Type& variable1, Type& variable2) | |||||
int numElements = numElementsInArray (myArray) // returns 3 | int numElements = numElementsInArray (myArray) // returns 3 | ||||
@endcode | @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) | (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 | /** Using juce_hypot and juce_hypotf is easier than dealing with all the different | ||||
versions of these functions of various platforms and compilers. */ | 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 | #if JUCE_WINDOWS | ||||
return _hypot (a, b); | return _hypot (a, b); | ||||
@@ -69,22 +69,12 @@ void Drawable::drawAt (Graphics& g, const float x, const float y, const float op | |||||
} | } | ||||
void Drawable::drawWithin (Graphics& g, | 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 RectanglePlacement& placement, | ||||
const float opacity) const | 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)); | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
@@ -38,8 +38,7 @@ | |||||
#define log(a) {} | #define log(a) {} | ||||
#endif | #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 | #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 String open (const BigInteger& inputChannels, | ||||
const BigInteger& outputChannels, | 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) | void start (AudioIODeviceCallback* callback) | ||||
{ | { | ||||
@@ -697,20 +644,8 @@ public: | |||||
lastCallback->audioDeviceStopped(); | 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() | bool showControlPanel() | ||||
{ | { | ||||
@@ -840,7 +775,6 @@ private: | |||||
bool volatile insideControlPanelModalLoop; | bool volatile insideControlPanelModalLoop; | ||||
bool volatile shouldUsePreferredSize; | bool volatile shouldUsePreferredSize; | ||||
//============================================================================== | //============================================================================== | ||||
void removeCurrentDriver() | void removeCurrentDriver() | ||||
{ | { | ||||
@@ -1242,7 +1176,6 @@ private: | |||||
for (i = 0; i < numActiveInputChans; ++i) | for (i = 0; i < numActiveInputChans; ++i) | ||||
{ | { | ||||
float* const dst = inBuffers[i]; | float* const dst = inBuffers[i]; | ||||
jassert (dst != 0); | jassert (dst != 0); | ||||
const char* const src = (const char*) (infos[i].buffers[bi]); | 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) | for (i = 0; i < numActiveOutputChans; ++i) | ||||
{ | { | ||||
float* const src = outBuffers[i]; | float* const src = outBuffers[i]; | ||||
jassert (src != 0); | jassert (src != 0); | ||||
char* const dst = (char*) (infos [numActiveInputChans + i].buffers[bi]); | char* const dst = (char*) (infos [numActiveInputChans + i].buffers[bi]); | ||||
@@ -45,7 +45,8 @@ public: | |||||
width (0), | width (0), | ||||
height (0), | height (0), | ||||
activeUsers (0), | activeUsers (0), | ||||
recordNextFrameTime (false) | |||||
recordNextFrameTime (false), | |||||
previewMaxFPS (60) | |||||
{ | { | ||||
HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph); | HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph); | ||||
if (FAILED (hr)) | if (FAILED (hr)) | ||||
@@ -176,6 +177,11 @@ public: | |||||
mediaControl->Stop(); | mediaControl->Stop(); | ||||
} | } | ||||
int getPreviewMaxFPS() const | |||||
{ | |||||
return previewMaxFPS; | |||||
} | |||||
void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/) | void handleFrame (double /*time*/, BYTE* buffer, long /*bufferSize*/) | ||||
{ | { | ||||
if (recordNextFrameTime) | if (recordNextFrameTime) | ||||
@@ -246,13 +252,14 @@ public: | |||||
g.drawImage (activeImage, rx, ry, rw, rh, 0, 0, width, height); | g.drawImage (activeImage, rx, ry, rw, rh, 0, 0, width, height); | ||||
} | } | ||||
bool createFileCaptureFilter (const File& file) | |||||
bool createFileCaptureFilter (const File& file, int quality) | |||||
{ | { | ||||
removeFileCaptureFilter(); | removeFileCaptureFilter(); | ||||
file.deleteFile(); | file.deleteFile(); | ||||
mediaControl->Stop(); | mediaControl->Stop(); | ||||
firstRecordedTime = Time(); | firstRecordedTime = Time(); | ||||
recordNextFrameTime = true; | recordNextFrameTime = true; | ||||
previewMaxFPS = 60; | |||||
HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter); | HRESULT hr = asfWriter.CoCreateInstance (CLSID_WMAsfWriter); | ||||
@@ -278,17 +285,30 @@ public: | |||||
hr = WMCreateProfileManager (profileManager.resetAndGetPointerAddress()); | hr = WMCreateProfileManager (profileManager.resetAndGetPointerAddress()); | ||||
// This gibberish is the DirectShow profile for a video-only wmv file. | // 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)) | prof = prof.replace ("$WIDTH", String (width)) | ||||
.replace ("$HEIGHT", String (height)); | |||||
.replace ("$HEIGHT", String (height)) | |||||
.replace ("$AVGTIMEPERFRAME", String (10000000 / maxFramesPerSecond)); | |||||
ComSmartPtr <IWMProfile> currentProfile; | ComSmartPtr <IWMProfile> currentProfile; | ||||
hr = profileManager->LoadProfileByData ((const WCHAR*) prof, currentProfile.resetAndGetPointerAddress()); | hr = profileManager->LoadProfileByData ((const WCHAR*) prof, currentProfile.resetAndGetPointerAddress()); | ||||
@@ -306,6 +326,7 @@ public: | |||||
&& ok && activeUsers > 0 | && ok && activeUsers > 0 | ||||
&& SUCCEEDED (mediaControl->Run())) | && SUCCEEDED (mediaControl->Run())) | ||||
{ | { | ||||
previewMaxFPS = (quality < 2) ? 15 : 25; // throttle back the preview comps to try to leave the cpu free for encoding | |||||
return true; | return true; | ||||
} | } | ||||
} | } | ||||
@@ -335,6 +356,8 @@ public: | |||||
if (ok && activeUsers > 0) | if (ok && activeUsers > 0) | ||||
mediaControl->Run(); | mediaControl->Run(); | ||||
previewMaxFPS = 60; | |||||
} | } | ||||
//============================================================================== | //============================================================================== | ||||
@@ -377,7 +400,7 @@ public: | |||||
{ | { | ||||
public: | public: | ||||
DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_) | DShowCaptureViewerComp (DShowCameraDeviceInteral* const owner_) | ||||
: owner (owner_) | |||||
: owner (owner_), maxFPS (15), lastRepaintTime (0) | |||||
{ | { | ||||
setOpaque (true); | setOpaque (true); | ||||
owner->addChangeListener (this); | owner->addChangeListener (this); | ||||
@@ -414,11 +437,22 @@ public: | |||||
void changeListenerCallback (void*) | void changeListenerCallback (void*) | ||||
{ | { | ||||
repaint(); | |||||
const int64 now = Time::currentTimeMillis(); | |||||
if (now >= lastRepaintTime + (1000 / maxFPS)) | |||||
{ | |||||
lastRepaintTime = now; | |||||
repaint(); | |||||
if (owner != 0) | |||||
maxFPS = owner->getPreviewMaxFPS(); | |||||
} | |||||
} | } | ||||
private: | private: | ||||
DShowCameraDeviceInteral* owner; | DShowCameraDeviceInteral* owner; | ||||
int maxFPS; | |||||
int64 lastRepaintTime; | |||||
}; | }; | ||||
//============================================================================== | //============================================================================== | ||||
@@ -449,6 +483,7 @@ private: | |||||
Image activeImage; | Image activeImage; | ||||
bool recordNextFrameTime; | bool recordNextFrameTime; | ||||
int previewMaxFPS; | |||||
void getVideoSizes (IAMStreamConfig* const streamConfig) | void getVideoSizes (IAMStreamConfig* const streamConfig) | ||||
{ | { | ||||
@@ -681,11 +716,12 @@ const String CameraDevice::getFileExtension() | |||||
void CameraDevice::startRecordingToFile (const File& file, int quality) | void CameraDevice::startRecordingToFile (const File& file, int quality) | ||||
{ | { | ||||
jassert (quality >= 0 && quality <= 2); | |||||
stopRecording(); | stopRecording(); | ||||
DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal; | DShowCameraDeviceInteral* const d = (DShowCameraDeviceInteral*) internal; | ||||
d->addUser(); | d->addUser(); | ||||
isRecording = d->createFileCaptureFilter (file); | |||||
isRecording = d->createFileCaptureFilter (file, quality); | |||||
} | } | ||||
const Time CameraDevice::getTimeOfFirstRecordedFrame() const | const Time CameraDevice::getTimeOfFirstRecordedFrame() const | ||||