From d5081092967ad738feb45af100e0d3a5b0d8fa57 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Fri, 8 Oct 2010 14:52:23 +0100 Subject: [PATCH] Refactored midi input code to allow unlimited syex length + partial sysex callbacks on win32. Fixed a few problems with menu bars, Quicktime, AudioUnits. Modernised some old win32 file chooser code. Tweaked some window border rendering. --- Builds/MacOSX/Juce.xcodeproj/project.pbxproj | 2 + Builds/VisualStudio2005/Juce.vcproj | 1 + Builds/VisualStudio2008/Juce.vcproj | 1 + Builds/VisualStudio2008_DLL/Juce.vcproj | 1 + Builds/VisualStudio2010/Juce.vcxproj | 1 + Builds/VisualStudio2010/Juce.vcxproj.filters | 3 + Builds/iPhone/Juce.xcodeproj/project.pbxproj | 2 + Juce.jucer | 2 + extras/juce demo/Source/demos/OpenGLDemo.cpp | 2 +- extras/juce demo/Source/demos/WidgetsDemo.cpp | 16 +- juce_amalgamated.cpp | 1415 ++++++++--------- juce_amalgamated.h | 17 +- .../juce_QuickTimeAudioFormat.cpp | 6 +- src/audio/devices/juce_AudioDeviceManager.h | 12 + .../formats/juce_AudioUnitPluginFormat.mm | 173 +- src/core/juce_StandardHeader.h | 2 +- .../lookandfeel/juce_LookAndFeel.cpp | 32 +- .../menus/juce_MenuBarComponent.cpp | 4 +- src/gui/graphics/geometry/juce_BorderSize.h | 3 + src/native/common/juce_MidiDataConcatenator.h | 148 ++ src/native/juce_mac_NativeCode.mm | 1 + src/native/juce_win32_NativeCode.cpp | 1 + src/native/mac/juce_mac_CoreMidi.cpp | 207 +-- src/native/mac/juce_mac_NSViewComponent.mm | 6 +- .../mac/juce_mac_NSViewComponentPeer.mm | 14 +- src/native/windows/juce_win32_FileChooser.cpp | 389 ++--- src/native/windows/juce_win32_Midi.cpp | 254 ++- 27 files changed, 1339 insertions(+), 1376 deletions(-) create mode 100644 src/native/common/juce_MidiDataConcatenator.h diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj index e402b7e09a..000e6ad90a 100644 --- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj @@ -911,6 +911,7 @@ 1C4E5F07F277AE37C71EA547 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_linux_NativeCode.cpp; path = ../../src/native/juce_linux_NativeCode.cpp; sourceTree = SOURCE_ROOT; }; 3BC24CC03A2F940A615FE935 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = juce_mac_NativeCode.mm; path = ../../src/native/juce_mac_NativeCode.mm; sourceTree = SOURCE_ROOT; }; 9DFCF3F7BB734C8AABD83D8D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_win32_NativeCode.cpp; path = ../../src/native/juce_win32_NativeCode.cpp; sourceTree = SOURCE_ROOT; }; + 213F0A7BF38AF6AB34414A45 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_MidiDataConcatenator.h; path = ../../src/native/common/juce_MidiDataConcatenator.h; sourceTree = SOURCE_ROOT; }; 21B2342B75097AB93CFF7E97 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_posix_NamedPipe.cpp; path = ../../src/native/common/juce_posix_NamedPipe.cpp; sourceTree = SOURCE_ROOT; }; 2C48BB1A286C6A63174E5798 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_posix_SharedCode.h; path = ../../src/native/common/juce_posix_SharedCode.h; sourceTree = SOURCE_ROOT; }; 7A51D8B81F390A4CABF25C73 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_linux_Audio.cpp; path = ../../src/native/linux/juce_linux_Audio.cpp; sourceTree = SOURCE_ROOT; }; @@ -1686,6 +1687,7 @@ 13FBF71BD76A08C8971C6351, 177636E4EEEBBB139F934897 ); name = io; sourceTree = ""; }; DDB94A7300C3D1F2E9E51C47 = { isa = PBXGroup; children = ( + 213F0A7BF38AF6AB34414A45, 21B2342B75097AB93CFF7E97, 2C48BB1A286C6A63174E5798 ); name = common; sourceTree = ""; }; 1004A23965A4DB0FCC441ED3 = { isa = PBXGroup; children = ( diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj index 7e5cb5bb0e..21690ba386 100644 --- a/Builds/VisualStudio2005/Juce.vcproj +++ b/Builds/VisualStudio2005/Juce.vcproj @@ -837,6 +837,7 @@ + diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj index 7876fcd26a..a4f3a8bf1c 100644 --- a/Builds/VisualStudio2008/Juce.vcproj +++ b/Builds/VisualStudio2008/Juce.vcproj @@ -837,6 +837,7 @@ + diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj index a01cbaea76..e60da01048 100644 --- a/Builds/VisualStudio2008_DLL/Juce.vcproj +++ b/Builds/VisualStudio2008_DLL/Juce.vcproj @@ -839,6 +839,7 @@ + diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj index 2742358082..3f2f974fed 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj +++ b/Builds/VisualStudio2010/Juce.vcxproj @@ -737,6 +737,7 @@ + diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters index c0a7c452f4..63958eeb1c 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj.filters +++ b/Builds/VisualStudio2010/Juce.vcxproj.filters @@ -2133,6 +2133,9 @@ Juce\Source\io\streams + + Juce\Source\native\common + Juce\Source\native\common diff --git a/Builds/iPhone/Juce.xcodeproj/project.pbxproj b/Builds/iPhone/Juce.xcodeproj/project.pbxproj index 6bac4f1f8f..7ad29552f9 100644 --- a/Builds/iPhone/Juce.xcodeproj/project.pbxproj +++ b/Builds/iPhone/Juce.xcodeproj/project.pbxproj @@ -911,6 +911,7 @@ 1C4E5F07F277AE37C71EA547 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_linux_NativeCode.cpp; path = ../../src/native/juce_linux_NativeCode.cpp; sourceTree = SOURCE_ROOT; }; 3BC24CC03A2F940A615FE935 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; name = juce_mac_NativeCode.mm; path = ../../src/native/juce_mac_NativeCode.mm; sourceTree = SOURCE_ROOT; }; 9DFCF3F7BB734C8AABD83D8D = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_win32_NativeCode.cpp; path = ../../src/native/juce_win32_NativeCode.cpp; sourceTree = SOURCE_ROOT; }; + 213F0A7BF38AF6AB34414A45 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_MidiDataConcatenator.h; path = ../../src/native/common/juce_MidiDataConcatenator.h; sourceTree = SOURCE_ROOT; }; 21B2342B75097AB93CFF7E97 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_posix_NamedPipe.cpp; path = ../../src/native/common/juce_posix_NamedPipe.cpp; sourceTree = SOURCE_ROOT; }; 2C48BB1A286C6A63174E5798 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_posix_SharedCode.h; path = ../../src/native/common/juce_posix_SharedCode.h; sourceTree = SOURCE_ROOT; }; 7A51D8B81F390A4CABF25C73 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_linux_Audio.cpp; path = ../../src/native/linux/juce_linux_Audio.cpp; sourceTree = SOURCE_ROOT; }; @@ -1686,6 +1687,7 @@ 13FBF71BD76A08C8971C6351, 177636E4EEEBBB139F934897 ); name = io; sourceTree = ""; }; DDB94A7300C3D1F2E9E51C47 = { isa = PBXGroup; children = ( + 213F0A7BF38AF6AB34414A45, 21B2342B75097AB93CFF7E97, 2C48BB1A286C6A63174E5798 ); name = common; sourceTree = ""; }; 1004A23965A4DB0FCC441ED3 = { isa = PBXGroup; children = ( diff --git a/Juce.jucer b/Juce.jucer index 78be8b2aa1..0411f10826 100644 --- a/Juce.jucer +++ b/Juce.jucer @@ -1253,6 +1253,8 @@ + accumulationBufferBlueBits << ", " << pixFormat->accumulationBufferAlphaBits << "), full-scene AA=" - << pixFormat->fullSceneAntiAliasingNumSamples; + << (int) pixFormat->fullSceneAntiAliasingNumSamples; Logger::outputDebugString (formatDescription); } diff --git a/extras/juce demo/Source/demos/WidgetsDemo.cpp b/extras/juce demo/Source/demos/WidgetsDemo.cpp index 3857f5e245..8cd47d0fed 100644 --- a/extras/juce demo/Source/demos/WidgetsDemo.cpp +++ b/extras/juce demo/Source/demos/WidgetsDemo.cpp @@ -1347,13 +1347,15 @@ public: "*", useNativeVersion); - if (fc.browseForFileToOpen()) + if (fc.browseForMultipleFilesToOpen()) { - File chosenFile = fc.getResult(); + String chosen; + for (int i = 0; i < fc.getResults().size(); ++i) + chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; AlertWindow::showMessageBox (AlertWindow::InfoIcon, "File Chooser...", - "You picked: " + chosenFile.getFullPathName()); + "You picked: " + chosen); } } else if (result == 124) @@ -1366,13 +1368,15 @@ public: "*.jpg;*.jpeg;*.png;*.gif", useNativeVersion); - if (fc.browseForFileToOpen (&imagePreview)) + if (fc.browseForMultipleFilesToOpen (&imagePreview)) { - File chosenFile = fc.getResult(); + String chosen; + for (int i = 0; i < fc.getResults().size(); ++i) + chosen << fc.getResults().getReference(i).getFullPathName() << "\n"; AlertWindow::showMessageBox (AlertWindow::InfoIcon, "File Chooser...", - "You picked: " + chosenFile.getFullPathName()); + "You picked: " + chosen); } } else if (result == 122) diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 25d28e5edb..824064d577 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -22774,7 +22774,7 @@ public: } } - int framesToDo = bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame; + int framesToDo = jmin (numSamples, bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame); bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * framesToDo; UInt32 outFlags = 0; @@ -22786,8 +22786,8 @@ public: break; } - lastSampleRead = startSampleInFile + actualNumFrames * samplesPerFrame; - const int samplesReceived = actualNumFrames * samplesPerFrame; + lastSampleRead = startSampleInFile + actualNumFrames; + const int samplesReceived = actualNumFrames; for (int j = numDestChannels; --j >= 0;) { @@ -31297,6 +31297,8 @@ public: ~AudioUnitPluginInstance(); + void initialise(); + // AudioPluginInstance methods: void fillInPluginDescription (PluginDescription& desc) const @@ -31365,7 +31367,7 @@ private: String pluginName, manufacturer, version; String fileOrIdentifier; CriticalSection lock; - bool initialised, wantsMidiMessages, wasPlaying; + bool wantsMidiMessages, wasPlaying, prepared; HeapBlock outputBufferList; AudioTimeStamp timeStamp; @@ -31375,7 +31377,8 @@ private: Array parameterIds; bool getComponentDescFromFile (const String& fileOrIdentifier); - void initialise(); + void setPluginCallbacks(); + void getParameterListFromPlugin(); OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, @@ -31437,16 +31440,49 @@ private: 0, supportedChannels, &supportedChannelsSize) == noErr && supportedChannelsSize > 0) { + int explicitNumIns = 0; + int explicitNumOuts = 0; + int maximumNumIns = 0; + int maximumNumOuts = 0; + for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i) { - numIns = jmax (numIns, (int) supportedChannels[i].inChannels); - numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels); + const int inChannels = (int) supportedChannels[i].inChannels; + const int outChannels = (int) supportedChannels[i].outChannels; + + if (inChannels < 0) + maximumNumIns = jmin (maximumNumIns, inChannels); + else + explicitNumIns = jmax (explicitNumIns, inChannels); + + if (outChannels < 0) + maximumNumOuts = jmin (maximumNumOuts, outChannels); + else + explicitNumOuts = jmax (explicitNumOuts, outChannels); + } + + if ((maximumNumIns == -1 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, as long as they match) + || (maximumNumIns == -2 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, even if they don't match) + || (maximumNumIns == -1 && maximumNumOuts == -2)) + { + numIns = numOuts = 2; + } + else + { + numIns = explicitNumIns; + numOuts = explicitNumOuts; + + if (maximumNumIns == -1 || (maximumNumIns < 0 && explicitNumIns <= -maximumNumIns)) + numIns = 2; + + if (maximumNumOuts == -1 || (maximumNumOuts < 0 && explicitNumOuts <= -maximumNumOuts)) + numOuts = 2; } } else { - // (this really means the plugin will take any number of ins/outs as long - // as they are the same) + // (this really means the plugin will take any number of ins/outs as long + // as they are the same) numIns = numOuts = 2; } } @@ -31458,8 +31494,7 @@ private: AudioUnitPluginInstance::AudioUnitPluginInstance (const String& fileOrIdentifier) : fileOrIdentifier (fileOrIdentifier), - initialised (false), - wantsMidiMessages (false), + wantsMidiMessages (false), wasPlaying (false), prepared (false), audioUnit (0), currentBuffer (0) { @@ -31584,13 +31619,20 @@ bool AudioUnitPluginInstance::getComponentDescFromFile (const String& fileOrIden void AudioUnitPluginInstance::initialise() { - if (initialised || audioUnit == 0) - return; + getParameterListFromPlugin(); + setPluginCallbacks(); - log ("Initialising AU: " + pluginName); + int numIns, numOuts; + getNumChannels (numIns, numOuts); + setPlayConfigDetails (numIns, numOuts, 0, 0); + setLatencySamples (0); +} +void AudioUnitPluginInstance::getParameterListFromPlugin() +{ parameterIds.clear(); + if (audioUnit != 0) { UInt32 paramListSize = 0; AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, @@ -31604,36 +31646,34 @@ void AudioUnitPluginInstance::initialise() 0, ¶meterIds.getReference(0), ¶mListSize); } } +} +void AudioUnitPluginInstance::setPluginCallbacks() +{ + if (audioUnit != 0) { - AURenderCallbackStruct info; - zerostruct (info); - info.inputProcRefCon = this; - info.inputProc = renderGetInputCallback; + { + AURenderCallbackStruct info; + zerostruct (info); + info.inputProcRefCon = this; + info.inputProc = renderGetInputCallback; - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, - 0, &info, sizeof (info)); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, + 0, &info, sizeof (info)); + } + + { + HostCallbackInfo info; + zerostruct (info); + info.hostUserData = this; + info.beatAndTempoProc = getBeatAndTempoCallback; + info.musicalTimeLocationProc = getMusicalTimeLocationCallback; + info.transportStateProc = getTransportStateCallback; + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, + 0, &info, sizeof (info)); + } } - - { - HostCallbackInfo info; - zerostruct (info); - info.hostUserData = this; - info.beatAndTempoProc = getBeatAndTempoCallback; - info.musicalTimeLocationProc = getMusicalTimeLocationCallback; - info.transportStateProc = getTransportStateCallback; - - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, - 0, &info, sizeof (info)); - } - - int numIns, numOuts; - getNumChannels (numIns, numOuts); - setPlayConfigDetails (numIns, numOuts, 0, 0); - - initialised = AudioUnitInitialize (audioUnit) == noErr; - - setLatencySamples (0); } void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, @@ -31641,6 +31681,8 @@ void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, { if (audioUnit != 0) { + releaseResources(); + Float64 sampleRateIn = 0, sampleRateOut = 0; UInt32 sampleRateSize = sizeof (sampleRateIn); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRateIn, &sampleRateSize); @@ -31648,25 +31690,13 @@ void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, if (sampleRateIn != sampleRate_ || sampleRateOut != sampleRate_) { - if (initialised) - { - AudioUnitUninitialize (audioUnit); - initialised = false; - } - Float64 sr = sampleRate_; AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sr, sizeof (Float64)); AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sr, sizeof (Float64)); } - } - initialise(); - - if (initialised) - { int numIns, numOuts; getNumChannels (numIns, numOuts); - setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected); Float64 latencySecs = 0.0; @@ -31680,24 +31710,26 @@ void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); - AudioStreamBasicDescription stream; - zerostruct (stream); - stream.mSampleRate = sampleRate_; - stream.mFormatID = kAudioFormatLinearPCM; - stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; - stream.mFramesPerPacket = 1; - stream.mBytesPerPacket = 4; - stream.mBytesPerFrame = 4; - stream.mBitsPerChannel = 32; - stream.mChannelsPerFrame = numIns; + { + AudioStreamBasicDescription stream; + zerostruct (stream); + stream.mSampleRate = sampleRate_; + stream.mFormatID = kAudioFormatLinearPCM; + stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; + stream.mFramesPerPacket = 1; + stream.mBytesPerPacket = 4; + stream.mBytesPerFrame = 4; + stream.mBitsPerChannel = 32; + stream.mChannelsPerFrame = numIns; - OSStatus err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, - 0, &stream, sizeof (stream)); + OSStatus err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + 0, &stream, sizeof (stream)); - stream.mChannelsPerFrame = numOuts; + stream.mChannelsPerFrame = numOuts; - err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, - 0, &stream, sizeof (stream)); + err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, + 0, &stream, sizeof (stream)); + } outputBufferList.calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1), 1); outputBufferList->mNumberBuffers = numOuts; @@ -31712,19 +31744,24 @@ void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, currentBuffer = 0; wasPlaying = false; + + prepared = (AudioUnitInitialize (audioUnit) == noErr); } } void AudioUnitPluginInstance::releaseResources() { - if (initialised) + if (prepared) { + AudioUnitUninitialize (audioUnit); + AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); outputBufferList.free(); currentBuffer = 0; + prepared = false; } } @@ -31762,7 +31799,7 @@ void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, { const int numSamples = buffer.getNumSamples(); - if (initialised) + if (prepared) { AudioUnitRenderActionFlags flags = 0; @@ -31802,7 +31839,7 @@ void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, } else { - // Not initialised, so just bypass.. + // Plugin not working correctly, so just bypass.. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) buffer.clear (i, 0, buffer.getNumSamples()); } @@ -31979,6 +32016,8 @@ private: UInt32 dataSize = 0; Boolean isWritable = false; + AudioUnitInitialize (plugin.audioUnit); + if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr && dataSize != 0 @@ -66638,9 +66677,25 @@ void LookAndFeel::drawCornerResizer (Graphics& g, } } -void LookAndFeel::drawResizableFrame (Graphics&, int /*w*/, int /*h*/, - const BorderSize& /*borders*/) +void LookAndFeel::drawResizableFrame (Graphics& g, int w, int h, const BorderSize& border) { + if (! border.isEmpty()) + { + const Rectangle fullSize (0, 0, w, h); + const Rectangle centreArea (border.subtractedFrom (fullSize)); + + g.saveState(); + + g.excludeClipRegion (centreArea); + + g.setColour (Colour (0x50000000)); + g.drawRect (fullSize); + + g.setColour (Colour (0x19000000)); + g.drawRect (centreArea.expanded (1, 1)); + + g.restoreState(); + } } void LookAndFeel::fillResizableWindowBackground (Graphics& g, int /*w*/, int /*h*/, @@ -66649,17 +66704,9 @@ void LookAndFeel::fillResizableWindowBackground (Graphics& g, int /*w*/, int /*h g.fillAll (window.getBackgroundColour()); } -void LookAndFeel::drawResizableWindowBorder (Graphics& g, int w, int h, - const BorderSize& border, ResizableWindow&) +void LookAndFeel::drawResizableWindowBorder (Graphics&, int /*w*/, int /*h*/, + const BorderSize& /*border*/, ResizableWindow&) { - g.setColour (Colour (0x80000000)); - g.drawRect (0, 0, w, h); - - g.setColour (Colour (0x19000000)); - g.drawRect (border.getLeft() - 1, - border.getTop() - 1, - w + 2 - border.getLeftAndRight(), - h + 2 - border.getTopAndBottom()); } void LookAndFeel::drawDocumentWindowTitleBar (DocumentWindow& window, @@ -68752,9 +68799,7 @@ void MenuBarComponent::handleCommandMessage (int commandId) { const Point mousePos (getMouseXYRelative()); updateItemUnderMouse (mousePos.getX(), mousePos.getY()); - - if (! isCurrentlyBlockedByAnotherModalComponent()) - setOpenItem (-1); + setOpenItem (-1); if (commandId != 0 && model != 0) model->menuItemSelected (commandId, topLevelIndexClicked); @@ -237450,6 +237495,130 @@ END_JUCE_NAMESPACE BEGIN_JUCE_NAMESPACE + +/*** Start of inlined file: juce_MidiDataConcatenator.h ***/ +#ifndef __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ +#define __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ + +/** + Helper class that takes chunks of incoming midi bytes, packages them into + messages, and dispatches them to a midi callback. +*/ +class MidiDataConcatenator +{ +public: + + MidiDataConcatenator (const int initialBufferSize) + : pendingData (initialBufferSize), + pendingBytes (0), pendingDataTime (0) + { + } + + void reset() + { + pendingBytes = 0; + pendingDataTime = 0; + } + + void pushMidiData (const void* data, int numBytes, double time, + MidiInput* input, MidiInputCallback& callback) + { + const uint8* d = static_cast (data); + + while (numBytes > 0) + { + if (pendingBytes > 0 || d[0] == 0xf0) + { + processSysex (d, numBytes, time, input, callback); + } + else + { + int used = 0; + const MidiMessage m (d, numBytes, used, 0, time); + + if (used <= 0) + { + jassertfalse; // malformed midi message + break; + } + + callback.handleIncomingMidiMessage (input, m); + numBytes -= used; + d += used; + } + } + } + +private: + void processSysex (const uint8*& d, int& numBytes, double time, + MidiInput* input, MidiInputCallback& callback) + { + if (*d == 0xf0) + { + pendingBytes = 0; + pendingDataTime = time; + } + + pendingData.ensureSize (pendingBytes + numBytes, false); + uint8* totalMessage = static_cast (pendingData.getData()); + uint8* dest = totalMessage + pendingBytes; + + do + { + if (pendingBytes > 0 && *d >= 0x80) + { + if (*d >= 0xfa || *d == 0xf8) + { + callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); + ++d; + --numBytes; + } + else + { + if (*d == 0xf7) + { + *dest++ = *d++; + pendingBytes++; + --numBytes; + } + + break; + } + } + else + { + *dest++ = *d++; + pendingBytes++; + --numBytes; + } + } + while (numBytes > 0); + + if (pendingBytes > 0) + { + if (totalMessage [pendingBytes - 1] == 0xf7) + { + callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); + pendingBytes = 0; + } + else + { + callback.handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); + } + } + } + + MemoryBlock pendingData; + int pendingBytes; + double pendingDataTime; + + MidiDataConcatenator (const MidiDataConcatenator&); + MidiDataConcatenator& operator= (const MidiDataConcatenator&); +}; + +#endif // __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ +/*** End of inlined file: juce_MidiDataConcatenator.h ***/ + #define JUCE_INCLUDED_FILE 1 // Now include the actual code files.. @@ -244077,14 +244246,8 @@ bool DragAndDropContainer::performExternalDragDropOfText (const String& text) // compiled on its own). #if JUCE_INCLUDED_FILE -void juce_setWindowStyleBit (HWND h, const int styleType, const int feature, const bool bitIsSet) throw(); - namespace FileChooserHelpers { - static const void* defaultDirPath = 0; - static String returnedString; // need this to get non-existent pathnames from the directory chooser - static Component* currentExtraFileWin = 0; - static bool areThereAnyAlwaysOnTopWindows() { for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) @@ -244098,64 +244261,67 @@ namespace FileChooserHelpers return false; } - static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM /*lpData*/) + struct FileChooserCallbackInfo { + String initialPath; + String returnedString; // need this to get non-existent pathnames from the directory chooser + ScopedPointer customComponent; + }; + + static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM lpData) + { + FileChooserCallbackInfo* info = (FileChooserCallbackInfo*) lpData; + if (msg == BFFM_INITIALIZED) - SendMessage (hWnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) defaultDirPath); + SendMessage (hWnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) static_cast (info->initialPath)); else if (msg == BFFM_VALIDATEFAILEDW) - returnedString = (LPCWSTR) lParam; + info->returnedString = (LPCWSTR) lParam; else if (msg == BFFM_VALIDATEFAILEDA) - returnedString = (const char*) lParam; + info->returnedString = (const char*) lParam; return 0; } static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam) { - if (currentExtraFileWin != 0) + if (uiMsg == WM_INITDIALOG) { - if (uiMsg == WM_INITDIALOG) + Component* customComp = ((FileChooserCallbackInfo*) (((OPENFILENAMEW*) lParam)->lCustData))->customComponent; + + HWND dialogH = GetParent (hdlg); + jassert (dialogH != 0); + if (dialogH == 0) + dialogH = hdlg; + + RECT r, cr; + GetWindowRect (dialogH, &r); + GetClientRect (dialogH, &cr); + + SetWindowPos (dialogH, 0, + r.left, r.top, + customComp->getWidth() + jmax (150, (int) (r.right - r.left)), + jmax (150, (int) (r.bottom - r.top)), + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + + customComp->setBounds (cr.right, cr.top, customComp->getWidth(), cr.bottom - cr.top); + customComp->addToDesktop (0, dialogH); + } + else if (uiMsg == WM_NOTIFY) + { + LPOFNOTIFY ofn = (LPOFNOTIFY) lParam; + + if (ofn->hdr.code == CDN_SELCHANGE) { - HWND dialogH = GetParent (hdlg); - jassert (dialogH != 0); - if (dialogH == 0) - dialogH = hdlg; + FileChooserCallbackInfo* info = (FileChooserCallbackInfo*) ofn->lpOFN->lCustData; + FilePreviewComponent* comp = static_cast (info->customComponent->getChildComponent(0)); - RECT r, cr; - GetWindowRect (dialogH, &r); - GetClientRect (dialogH, &cr); - - SetWindowPos (dialogH, 0, - r.left, r.top, - currentExtraFileWin->getWidth() + jmax (150, (int) (r.right - r.left)), - jmax (150, (int) (r.bottom - r.top)), - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); - - currentExtraFileWin->setBounds (cr.right, cr.top, currentExtraFileWin->getWidth(), cr.bottom - cr.top); - currentExtraFileWin->getChildComponent(0)->setBounds (0, 0, currentExtraFileWin->getWidth(), currentExtraFileWin->getHeight()); - - SetParent ((HWND) currentExtraFileWin->getWindowHandle(), (HWND) dialogH); - juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_CHILD, (dialogH != 0)); - juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_POPUP, (dialogH == 0)); - } - else if (uiMsg == WM_NOTIFY) - { - LPOFNOTIFY ofn = (LPOFNOTIFY) lParam; - - if (ofn->hdr.code == CDN_SELCHANGE) + if (comp != 0) { - FilePreviewComponent* comp = (FilePreviewComponent*) currentExtraFileWin->getChildComponent(0); + WCHAR path [MAX_PATH * 2]; + zerostruct (path); + CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH); - if (comp != 0) - { - TCHAR path [MAX_PATH * 2]; - path[0] = 0; - CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH); - - const String fn ((const WCHAR*) path); - - comp->selectedFileChanged (File (fn)); - } + comp->selectedFileChanged (File (path)); } } } @@ -244163,17 +244329,15 @@ namespace FileChooserHelpers return 0; } - class FPComponentHolder : public Component + class CustomComponentHolder : public Component { public: - FPComponentHolder() + CustomComponentHolder (Component* customComp) { setVisible (true); setOpaque (true); - } - - ~FPComponentHolder() - { + addAndMakeVisible (customComp); + setSize (jlimit (20, 800, customComp->getWidth()), customComp->getHeight()); } void paint (Graphics& g) @@ -244181,202 +244345,156 @@ namespace FileChooserHelpers g.fillAll (Colours::lightgrey); } + void resized() + { + if (getNumChildComponents() > 0) + getChildComponent(0)->setBounds (getLocalBounds()); + } + + juce_UseDebuggingNewOperator + private: - FPComponentHolder (const FPComponentHolder&); - FPComponentHolder& operator= (const FPComponentHolder&); + CustomComponentHolder (const CustomComponentHolder&); + CustomComponentHolder& operator= (const CustomComponentHolder&); }; } -void FileChooser::showPlatformDialog (Array& results, - const String& title, - const File& currentFileOrDirectory, - const String& filter, - bool selectsDirectory, - bool /*selectsFiles*/, - bool isSaveDialogue, - bool warnAboutOverwritingExistingFiles, - bool selectMultipleFiles, - FilePreviewComponent* extraInfoComponent) +void FileChooser::showPlatformDialog (Array& results, const String& title, const File& currentFileOrDirectory, + const String& filter, bool selectsDirectory, bool /*selectsFiles*/, + bool isSaveDialogue, bool warnAboutOverwritingExistingFiles, + bool selectMultipleFiles, FilePreviewComponent* extraInfoComponent) { using namespace FileChooserHelpers; - const int numCharsAvailable = 32768; - MemoryBlock filenameSpace ((numCharsAvailable + 1) * sizeof (WCHAR), true); - WCHAR* const fname = (WCHAR*) filenameSpace.getData(); - int fnameIdx = 0; - JUCE_TRY + HeapBlock files; + const int charsAvailableForResult = 32768; + files.calloc (charsAvailableForResult + 1); + int filenameOffset = 0; + + FileChooserCallbackInfo info; + + // use a modal window as the parent for this dialog box + // to block input from other app windows + Component parentWindow (String::empty); + const Rectangle mainMon (Desktop::getInstance().getMainMonitorArea()); + parentWindow.setBounds (mainMon.getX() + mainMon.getWidth() / 4, + mainMon.getY() + mainMon.getHeight() / 4, + 0, 0); + parentWindow.setOpaque (true); + parentWindow.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows()); + parentWindow.addToDesktop (0); + + if (extraInfoComponent == 0) + parentWindow.enterModalState(); + + if (currentFileOrDirectory.isDirectory()) { - // use a modal window as the parent for this dialog box - // to block input from other app windows - const Rectangle mainMon (Desktop::getInstance().getMainMonitorArea()); + info.initialPath = currentFileOrDirectory.getFullPathName(); + } + else + { + currentFileOrDirectory.getFileName().copyToUnicode (files, charsAvailableForResult); + info.initialPath = currentFileOrDirectory.getParentDirectory().getFullPathName(); + } - Component w (String::empty); - w.setBounds (mainMon.getX() + mainMon.getWidth() / 4, - mainMon.getY() + mainMon.getHeight() / 4, - 0, 0); - w.setOpaque (true); - w.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows()); - w.addToDesktop (0); + if (selectsDirectory) + { + BROWSEINFO bi; + zerostruct (bi); - if (extraInfoComponent == 0) - w.enterModalState(); + bi.hwndOwner = (HWND) parentWindow.getWindowHandle(); + bi.pszDisplayName = files; + bi.lpszTitle = title; + bi.lParam = (LPARAM) &info; + bi.lpfn = browseCallbackProc; + #ifdef BIF_USENEWUI + bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE; + #else + bi.ulFlags = 0x50; + #endif - String initialDir; + LPITEMIDLIST list = SHBrowseForFolder (&bi); - if (currentFileOrDirectory.isDirectory()) + if (! SHGetPathFromIDListW (list, files)) { - initialDir = currentFileOrDirectory.getFullPathName(); - } - else - { - currentFileOrDirectory.getFileName().copyToUnicode (fname, numCharsAvailable); - - initialDir = currentFileOrDirectory.getParentDirectory().getFullPathName(); + files[0] = 0; + info.returnedString = String::empty; } - if (currentExtraFileWin->isValidComponent()) + LPMALLOC al; + if (list != 0 && SUCCEEDED (SHGetMalloc (&al))) + al->Free (list); + + if (info.returnedString.isNotEmpty()) { - jassertfalse; + results.add (File (String (files)).getSiblingFile (info.returnedString)); return; } - - if (selectsDirectory) - { - LPITEMIDLIST list = 0; - filenameSpace.fillWith (0); - - { - BROWSEINFO bi; - zerostruct (bi); - - bi.hwndOwner = (HWND) w.getWindowHandle(); - bi.pszDisplayName = fname; - bi.lpszTitle = title; - bi.lpfn = browseCallbackProc; -#ifdef BIF_USENEWUI - bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE; -#else - bi.ulFlags = 0x50; -#endif - defaultDirPath = (const WCHAR*) initialDir; - - list = SHBrowseForFolder (&bi); - - if (! SHGetPathFromIDListW (list, fname)) - { - fname[0] = 0; - returnedString = String::empty; - } - } - - LPMALLOC al; - if (list != 0 && SUCCEEDED (SHGetMalloc (&al))) - al->Free (list); - - defaultDirPath = 0; - - if (returnedString.isNotEmpty()) - { - const String stringFName (fname); - - results.add (File (stringFName).getSiblingFile (returnedString)); - returnedString = String::empty; - - return; - } - } - else - { - DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; - - if (warnAboutOverwritingExistingFiles) - flags |= OFN_OVERWRITEPROMPT; - - if (selectMultipleFiles) - flags |= OFN_ALLOWMULTISELECT; - - if (extraInfoComponent != 0) - { - flags |= OFN_ENABLEHOOK; - - currentExtraFileWin = new FPComponentHolder(); - currentExtraFileWin->addAndMakeVisible (extraInfoComponent); - currentExtraFileWin->setSize (jlimit (20, 800, extraInfoComponent->getWidth()), - extraInfoComponent->getHeight()); - currentExtraFileWin->addToDesktop (0); - - currentExtraFileWin->enterModalState(); - } - - { - WCHAR filters [1024]; - zeromem (filters, sizeof (filters)); - filter.copyToUnicode (filters, 1024); - filter.copyToUnicode (filters + filter.length() + 1, - 1022 - filter.length()); - - OPENFILENAMEW of; - zerostruct (of); - -#ifdef OPENFILENAME_SIZE_VERSION_400W - of.lStructSize = OPENFILENAME_SIZE_VERSION_400W; -#else - of.lStructSize = sizeof (of); -#endif - of.hwndOwner = (HWND) w.getWindowHandle(); - of.lpstrFilter = filters; - of.nFilterIndex = 1; - of.lpstrFile = fname; - of.nMaxFile = numCharsAvailable; - of.lpstrInitialDir = initialDir; - of.lpstrTitle = title; - of.Flags = flags; - - if (extraInfoComponent != 0) - of.lpfnHook = &openCallback; - - if (isSaveDialogue) - { - if (! GetSaveFileName (&of)) - fname[0] = 0; - else - fnameIdx = of.nFileOffset; - } - else - { - if (! GetOpenFileName (&of)) - fname[0] = 0; - else - fnameIdx = of.nFileOffset; - } - } - } } -#if JUCE_CATCH_UNHANDLED_EXCEPTIONS - catch (...) + else { - fname[0] = 0; + DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; + + if (warnAboutOverwritingExistingFiles) + flags |= OFN_OVERWRITEPROMPT; + + if (selectMultipleFiles) + flags |= OFN_ALLOWMULTISELECT; + + if (extraInfoComponent != 0) + { + flags |= OFN_ENABLEHOOK; + + info.customComponent = new CustomComponentHolder (extraInfoComponent); + info.customComponent->enterModalState(); + } + + WCHAR filters [1024]; + zerostruct (filters); + filter.copyToUnicode (filters, 1024); + filter.copyToUnicode (filters + filter.length() + 1, 1022 - filter.length()); + + OPENFILENAMEW of; + zerostruct (of); + + #ifdef OPENFILENAME_SIZE_VERSION_400W + of.lStructSize = OPENFILENAME_SIZE_VERSION_400W; + #else + of.lStructSize = sizeof (of); + #endif + of.hwndOwner = (HWND) parentWindow.getWindowHandle(); + of.lpstrFilter = filters; + of.nFilterIndex = 1; + of.lpstrFile = files; + of.nMaxFile = charsAvailableForResult; + of.lpstrInitialDir = info.initialPath; + of.lpstrTitle = title; + of.Flags = flags; + of.lCustData = (LPARAM) &info; + + if (extraInfoComponent != 0) + of.lpfnHook = &openCallback; + + if (! (isSaveDialogue ? GetSaveFileName (&of) + : GetOpenFileName (&of))) + return; + + filenameOffset = of.nFileOffset; } -#endif - deleteAndZero (currentExtraFileWin); - - const WCHAR* const files = fname; - - if (selectMultipleFiles && fnameIdx > 0 && files [fnameIdx - 1] == 0) + if (selectMultipleFiles && filenameOffset > 0 && files [filenameOffset - 1] == 0) { - const WCHAR* filename = files + fnameIdx; + const WCHAR* filename = files + filenameOffset; while (*filename != 0) { - const String filepath (String (files) + "\\" + String (filename)); - results.add (File (filepath)); + results.add (File (String (files) + "\\" + String (filename))); filename += CharacterFunctions::length (filename) + 1; } } else if (files[0] != 0) { - results.add (File (files)); + results.add (File (String (files))); } } @@ -248490,30 +248608,22 @@ bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) // compiled on its own). #if JUCE_INCLUDED_FILE -class MidiInThread : public Thread +class MidiInCollector { public: - MidiInThread (MidiInput* const input_, - MidiInputCallback* const callback_) - : Thread ("Juce Midi"), - deviceHandle (0), + MidiInCollector (MidiInput* const input_, + MidiInputCallback& callback_) + : deviceHandle (0), input (input_), callback (callback_), + concatenator (4096), isStarted (false), startTime (0) { - pending.ensureSize ((int) defaultBufferSize); + } - for (int i = (int) numInHeaders; --i >= 0;) - { - zeromem (&hdr[i], sizeof (MIDIHDR)); - hdr[i].lpData = inData[i]; - hdr[i].dwBufferLength = (int) inBufferSize; - } - }; - - ~MidiInThread() + ~MidiInCollector() { stop(); @@ -248530,89 +248640,21 @@ public: } } - void handle (const uint32 message, const uint32 timeStamp) + void handleMessage (const uint32 message, const uint32 timeStamp) { - const int byte = message & 0xff; - if (byte < 0x80) - return; - - const int time = timeStampToMs (timeStamp); - + if ((message & 0xff) >= 0x80 && isStarted) { - const ScopedLock sl (lock); - pending.addEvent (&message, 3, time); + concatenator.pushMidiData (&message, 3, convertTimeStamp (timeStamp), input, callback); + writeFinishedBlocks(); } - - notify(); } void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp) { - const int time = timeStampToMs (timeStamp); - const int num = hdr->dwBytesRecorded; - - if (num > 0) + if (isStarted) { - { - const ScopedLock sl (lock); - pending.addEvent (hdr->lpData, num, time); - } - - notify(); - } - } - - void writeBlock (const int i) - { - hdr[i].dwBytesRecorded = 0; - MMRESULT res = midiInPrepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)); - jassert (res == MMSYSERR_NOERROR); - res = midiInAddBuffer (deviceHandle, &hdr[i], sizeof (MIDIHDR)); - jassert (res == MMSYSERR_NOERROR); - } - - void run() - { - MidiBuffer newEvents; - newEvents.ensureSize ((int) defaultBufferSize); - - while (! threadShouldExit()) - { - for (int i = 0; i < (int) numInHeaders; ++i) - { - if ((hdr[i].dwFlags & WHDR_DONE) != 0) - { - MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)); - (void) res; - jassert (res == MMSYSERR_NOERROR); - writeBlock (i); - } - } - - newEvents.clear(); // (resets it without freeing allocated storage) - - { - const ScopedLock sl (lock); - newEvents.swapWith (pending); - } - - //xxx needs to figure out if blocks are broken up or not - - if (newEvents.isEmpty()) - { - wait (500); - } - else - { - MidiMessage message (0xf4, 0.0); - int time; - - for (MidiBuffer::Iterator i (newEvents); i.getNextEvent (message, time);) - { - message.setTimeStamp (time * 0.001); - callback->handleIncomingMidiMessage (input, message); - } - } + concatenator.pushMidiData (hdr->lpData, hdr->dwBytesRecorded, convertTimeStamp (timeStamp), input, callback); + writeFinishedBlocks(); } } @@ -248621,23 +248663,22 @@ public: jassert (deviceHandle != 0); if (deviceHandle != 0 && ! isStarted) { - stop(); + activeMidiCollectors.addIfNotAlreadyThere (this); - activeMidiThreads.addIfNotAlreadyThere (this); - - int i; - for (i = 0; i < (int) numInHeaders; ++i) - writeBlock (i); + for (int i = 0; i < (int) numHeaders; ++i) + headers[i].write (deviceHandle); startTime = Time::getMillisecondCounter(); MMRESULT res = midiInStart (deviceHandle); - jassert (res == MMSYSERR_NOERROR); if (res == MMSYSERR_NOERROR) { + concatenator.reset(); isStarted = true; - pending.clear(); - startThread (6); + } + else + { + unprepareAllHeaders(); } } } @@ -248646,42 +248687,25 @@ public: { if (isStarted) { - stopThread (5000); - + isStarted = false; midiInReset (deviceHandle); midiInStop (deviceHandle); - - activeMidiThreads.removeValue (this); - - { const ScopedLock sl (lock); } - - for (int i = (int) numInHeaders; --i >= 0;) - { - if ((hdr[i].dwFlags & WHDR_DONE) != 0) - { - int c = 10; - while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)) == MIDIERR_STILLPLAYING) - Sleep (20); - - jassert (c >= 0); - } - } - - isStarted = false; - pending.clear(); + activeMidiCollectors.removeValue (this); + unprepareAllHeaders(); + concatenator.reset(); } } static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR midiMessage, DWORD_PTR timeStamp) { - MidiInThread* const thread = reinterpret_cast (dwInstance); + MidiInCollector* const collector = reinterpret_cast (dwInstance); - if (thread != 0 && activeMidiThreads.contains (thread)) + if (activeMidiCollectors.contains (collector)) { if (uMsg == MIM_DATA) - thread->handle ((uint32) midiMessage, (uint32) timeStamp); + collector->handleMessage ((uint32) midiMessage, (uint32) timeStamp); else if (uMsg == MIM_LONGDATA) - thread->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp); + collector->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp); } } @@ -248690,24 +248714,77 @@ public: HMIDIIN deviceHandle; private: - static Array activeMidiThreads; + static Array activeMidiCollectors; MidiInput* input; - MidiInputCallback* callback; - bool isStarted; + MidiInputCallback& callback; + MidiDataConcatenator concatenator; + bool volatile isStarted; uint32 startTime; - CriticalSection lock; - enum { defaultBufferSize = 8192, - numInHeaders = 32, - inBufferSize = 256 }; + class MidiHeader + { + public: + MidiHeader() + { + zerostruct (hdr); + hdr.lpData = data; + hdr.dwBufferLength = numElementsInArray (data); + } - MIDIHDR hdr [(int) numInHeaders]; - char inData [(int) numInHeaders] [(int) inBufferSize]; + void write (HMIDIIN deviceHandle) + { + hdr.dwBytesRecorded = 0; + MMRESULT res = midiInPrepareHeader (deviceHandle, &hdr, sizeof (hdr)); + res = midiInAddBuffer (deviceHandle, &hdr, sizeof (hdr)); + } - MidiBuffer pending; + void writeIfFinished (HMIDIIN deviceHandle) + { + if ((hdr.dwFlags & WHDR_DONE) != 0) + { + MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)); + (void) res; + write (deviceHandle); + } + } - int timeStampToMs (uint32 timeStamp) + void unprepare (HMIDIIN deviceHandle) + { + if ((hdr.dwFlags & WHDR_DONE) != 0) + { + int c = 10; + while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)) == MIDIERR_STILLPLAYING) + Thread::sleep (20); + + jassert (c >= 0); + } + } + + private: + MIDIHDR hdr; + char data [256]; + + MidiHeader (const MidiHeader&); + MidiHeader& operator= (const MidiHeader&); + }; + + enum { numHeaders = 32 }; + MidiHeader headers [numHeaders]; + + void writeFinishedBlocks() + { + for (int i = 0; i < (int) numHeaders; ++i) + headers[i].writeIfFinished (deviceHandle); + } + + void unprepareAllHeaders() + { + for (int i = 0; i < (int) numHeaders; ++i) + headers[i].unprepare (deviceHandle); + } + + double convertTimeStamp (uint32 timeStamp) { timeStamp += startTime; @@ -248720,14 +248797,14 @@ private: timeStamp = now; } - return (int) timeStamp; + return timeStamp * 0.001; } - MidiInThread (const MidiInThread&); - MidiInThread& operator= (const MidiInThread&); + MidiInCollector (const MidiInCollector&); + MidiInCollector& operator= (const MidiInCollector&); }; -Array MidiInThread::activeMidiThreads; +Array MidiInCollector::activeMidiCollectors; const StringArray MidiInput::getDevices() { @@ -248781,18 +248858,18 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call } ScopedPointer in (new MidiInput (name)); - ScopedPointer thread (new MidiInThread (in, callback)); + ScopedPointer collector (new MidiInCollector (in, *callback)); HMIDIIN h; HRESULT err = midiInOpen (&h, deviceId, - (DWORD_PTR) &MidiInThread::midiInCallback, - (DWORD_PTR) (MidiInThread*) thread, + (DWORD_PTR) &MidiInCollector::midiInCallback, + (DWORD_PTR) (MidiInCollector*) collector, CALLBACK_FUNCTION); if (err == MMSYSERR_NOERROR) { - thread->deviceHandle = h; - in->internal = thread.release(); + collector->deviceHandle = h; + in->internal = collector.release(); return in.release(); } @@ -248807,17 +248884,17 @@ MidiInput::MidiInput (const String& name_) MidiInput::~MidiInput() { - delete static_cast (internal); + delete static_cast (internal); } void MidiInput::start() { - static_cast (internal)->start(); + static_cast (internal)->start(); } void MidiInput::stop() { - static_cast (internal)->stop(); + static_cast (internal)->stop(); } struct MidiOutHandle @@ -270041,129 +270118,74 @@ namespace CoreMidiHelpers MIDIEndpointDispose (endPoint); } + void send (const MIDIPacketList* const packets) + { + if (port != 0) + MIDISend (port, endPoint, packets); + else + MIDIReceived (endPoint, packets); + } + MIDIPortRef port; MIDIEndpointRef endPoint; }; + class MidiPortAndCallback; + static CriticalSection callbackLock; + static Array activeCallbacks; + class MidiPortAndCallback { public: - MidiInput* input; - MidiPortAndEndpoint* portAndEndpoint; - MidiInputCallback* callback; - MemoryBlock pendingData; - int pendingBytes; - double pendingDataTime; - bool active; - - void processSysex (const uint8*& d, int& size, const double time) + MidiPortAndCallback (MidiInputCallback& callback_) + : input (0), active (false), callback (callback_), concatenator (2048) { - if (*d == 0xf0) + } + + ~MidiPortAndCallback() + { + active = false; + { - pendingBytes = 0; - pendingDataTime = time; + const ScopedLock sl (callbackLock); + activeCallbacks.removeValue (this); } - pendingData.ensureSize (pendingBytes + size, false); - uint8* totalMessage = (uint8*) pendingData.getData(); + if (portAndEndpoint != 0 && portAndEndpoint->port != 0) + CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint)); + } - uint8* dest = totalMessage + pendingBytes; + void handlePackets (const MIDIPacketList* const pktlist) + { + const double time = Time::getMillisecondCounterHiRes() * 0.001; - while (size > 0) + const ScopedLock sl (callbackLock); + if (activeCallbacks.contains (this) && active) { - if (pendingBytes > 0 && *d >= 0x80) + const MIDIPacket* packet = &pktlist->packet[0]; + + for (unsigned int i = 0; i < pktlist->numPackets; ++i) { - if (*d >= 0xfa || *d == 0xf8) - { - callback->handleIncomingMidiMessage (input, MidiMessage (*d, time)); - ++d; - --size; - } - else - { - if (*d == 0xf7) - { - *dest++ = *d++; - pendingBytes++; - --size; - } + concatenator.pushMidiData (packet->data, (int) packet->length, time, + input, callback); - break; - } + packet = MIDIPacketNext (packet); } - else - { - *dest++ = *d++; - pendingBytes++; - --size; - } - } - - if (totalMessage [pendingBytes - 1] == 0xf7) - { - callback->handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); - pendingBytes = 0; - } - else - { - callback->handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); } } + + MidiInput* input; + ScopedPointer portAndEndpoint; + volatile bool active; + + private: + MidiInputCallback& callback; + MidiDataConcatenator concatenator; }; - static CriticalSection callbackLock; - static Array activeCallbacks; - - static void midiInputProc (const MIDIPacketList* pktlist, - void* readProcRefCon, - void* /*srcConnRefCon*/) + static void midiInputProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* /*srcConnRefCon*/) { - double time = Time::getMillisecondCounterHiRes() * 0.001; - const double originalTime = time; - - MidiPortAndCallback* const mpc = (MidiPortAndCallback*) readProcRefCon; - const ScopedLock sl (CoreMidiHelpers::callbackLock); - - if (CoreMidiHelpers::activeCallbacks.contains (mpc) && mpc->active) - { - const MIDIPacket* packet = &pktlist->packet[0]; - - for (unsigned int i = 0; i < pktlist->numPackets; ++i) - { - const uint8* d = (const uint8*) (packet->data); - int size = packet->length; - - while (size > 0) - { - time = originalTime; - - if (mpc->pendingBytes > 0 || d[0] == 0xf0) - { - mpc->processSysex (d, size, time); - } - else - { - int used = 0; - const MidiMessage m (d, size, used, 0, time); - - if (used <= 0) - { - jassertfalse; // malformed midi message - break; - } - else - { - mpc->callback->handleIncomingMidiMessage (mpc->input, m); - } - - size -= used; - d += used; - } - } - - packet = MIDIPacketNext (packet); - } - } + static_cast (readProcRefCon)->handlePackets (pktlist); } } @@ -270287,10 +270309,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) p = MIDIPacketNext (p); } - if (mpe->port != 0) - MIDISend (mpe->port, mpe->endPoint, packets); - else - MIDIReceived (mpe->endPoint, packets); + mpe->send (packets); } else { @@ -270300,10 +270319,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) packets.packet[0].length = message.getRawDataSize(); *(int*) (packets.packet[0].data) = *(const int*) message.getRawData(); - if (mpe->port != 0) - MIDISend (mpe->port, mpe->endPoint, &packets); - else - MIDIReceived (mpe->endPoint, &packets); + mpe->send (&packets); } } @@ -270341,8 +270357,10 @@ int MidiInput::getDefaultDeviceIndex() MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) { + jassert (callback != 0); + using namespace CoreMidiHelpers; - MidiInput* mi = 0; + MidiInput* newInput = 0; if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) { @@ -270350,31 +270368,26 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) if (endPoint != 0) { - CFStringRef pname; + CFStringRef name; - if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name))) { MIDIClientRef client = getGlobalMidiClient(); if (client != 0) { MIDIPortRef port; + ScopedPointer mpc (new MidiPortAndCallback (*callback)); - ScopedPointer mpc (new MidiPortAndCallback()); - mpc->active = false; - - if (CHECK_ERROR (MIDIInputPortCreate (client, pname, midiInputProc, mpc, &port))) + if (CHECK_ERROR (MIDIInputPortCreate (client, name, midiInputProc, mpc, &port))) { if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, 0))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (port, endPoint); - mpc->callback = callback; - mpc->pendingBytes = 0; - mpc->pendingData.ensureSize (128); - mi = new MidiInput (getDevices() [index]); - mpc->input = mi; - mi->internal = mpc; + newInput = new MidiInput (getDevices() [index]); + mpc->input = newInput; + newInput->internal = mpc; const ScopedLock sl (callbackLock); activeCallbacks.add (mpc.release()); @@ -270387,22 +270400,24 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) } } - CFRelease (pname); + CFRelease (name); } } - return mi; + return newInput; } MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) { + jassert (callback != 0); + using namespace CoreMidiHelpers; MidiInput* mi = 0; MIDIClientRef client = getGlobalMidiClient(); if (client != 0) { - ScopedPointer mpc (new MidiPortAndCallback()); + ScopedPointer mpc (new MidiPortAndCallback (*callback)); mpc->active = false; MIDIEndpointRef endPoint; @@ -270410,9 +270425,6 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba if (CHECK_ERROR (MIDIDestinationCreate (client, name, midiInputProc, mpc, &endPoint))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (0, endPoint); - mpc->callback = callback; - mpc->pendingBytes = 0; - mpc->pendingData.ensureSize (128); mi = new MidiInput (deviceName); mpc->input = mi; @@ -270435,21 +270447,7 @@ MidiInput::MidiInput (const String& name_) MidiInput::~MidiInput() { - using namespace CoreMidiHelpers; - - MidiPortAndCallback* const mpc = static_cast (internal); - mpc->active = false; - - { - const ScopedLock sl (callbackLock); - activeCallbacks.removeValue (mpc); - } - - if (mpc->portAndEndpoint->port != 0) - CHECK_ERROR (MIDIPortDisconnectSource (mpc->portAndEndpoint->port, mpc->portAndEndpoint->endPoint)); - - delete mpc->portAndEndpoint; - delete mpc; + delete static_cast (internal); } void MidiInput::start() @@ -272611,11 +272609,7 @@ NSViewComponentPeer::NSViewComponentPeer (Component* const component_, #endif recursiveToFrontCall (false) { - NSRect r; - r.origin.x = 0; - r.origin.y = 0; - r.size.width = (float) component->getWidth(); - r.size.height = (float) component->getHeight(); + NSRect r = NSMakeRect (0, 0, (float) component->getWidth(),(float) component->getHeight()); view = [[JuceNSView alloc] initWithOwner: this withFrame: r]; [view setPostsFrameChangedNotifications: YES]; @@ -272738,14 +272732,8 @@ void NSViewComponentPeer::setSize (int w, int h) void NSViewComponentPeer::setBounds (int x, int y, int w, int h, bool isNowFullScreen) { fullScreen = isNowFullScreen; - w = jmax (0, w); - h = jmax (0, h); - NSRect r; - r.origin.x = (float) x; - r.origin.y = (float) y; - r.size.width = (float) w; - r.size.height = (float) h; + NSRect r = NSMakeRect ((float) x, (float) y, (float) jmax (0, w), (float) jmax (0, h)); if (isSharedWindow) { @@ -273679,11 +273667,7 @@ public: { const Point pos (owner->relativePositionToOtherComponent (topComp, Point())); - NSRect r; - r.origin.x = (float) pos.getX(); - r.origin.y = (float) pos.getY(); - r.size.width = (float) owner->getWidth(); - r.size.height = (float) owner->getHeight(); + NSRect r = NSMakeRect ((float) pos.getX(), (float) pos.getY(), (float) owner->getWidth(), (float) owner->getHeight()); r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height); [view setFrame: r]; @@ -278586,129 +278570,74 @@ namespace CoreMidiHelpers MIDIEndpointDispose (endPoint); } + void send (const MIDIPacketList* const packets) + { + if (port != 0) + MIDISend (port, endPoint, packets); + else + MIDIReceived (endPoint, packets); + } + MIDIPortRef port; MIDIEndpointRef endPoint; }; + class MidiPortAndCallback; + static CriticalSection callbackLock; + static Array activeCallbacks; + class MidiPortAndCallback { public: - MidiInput* input; - MidiPortAndEndpoint* portAndEndpoint; - MidiInputCallback* callback; - MemoryBlock pendingData; - int pendingBytes; - double pendingDataTime; - bool active; - - void processSysex (const uint8*& d, int& size, const double time) + MidiPortAndCallback (MidiInputCallback& callback_) + : input (0), active (false), callback (callback_), concatenator (2048) { - if (*d == 0xf0) + } + + ~MidiPortAndCallback() + { + active = false; + { - pendingBytes = 0; - pendingDataTime = time; + const ScopedLock sl (callbackLock); + activeCallbacks.removeValue (this); } - pendingData.ensureSize (pendingBytes + size, false); - uint8* totalMessage = (uint8*) pendingData.getData(); + if (portAndEndpoint != 0 && portAndEndpoint->port != 0) + CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint)); + } - uint8* dest = totalMessage + pendingBytes; + void handlePackets (const MIDIPacketList* const pktlist) + { + const double time = Time::getMillisecondCounterHiRes() * 0.001; - while (size > 0) + const ScopedLock sl (callbackLock); + if (activeCallbacks.contains (this) && active) { - if (pendingBytes > 0 && *d >= 0x80) + const MIDIPacket* packet = &pktlist->packet[0]; + + for (unsigned int i = 0; i < pktlist->numPackets; ++i) { - if (*d >= 0xfa || *d == 0xf8) - { - callback->handleIncomingMidiMessage (input, MidiMessage (*d, time)); - ++d; - --size; - } - else - { - if (*d == 0xf7) - { - *dest++ = *d++; - pendingBytes++; - --size; - } + concatenator.pushMidiData (packet->data, (int) packet->length, time, + input, callback); - break; - } + packet = MIDIPacketNext (packet); } - else - { - *dest++ = *d++; - pendingBytes++; - --size; - } - } - - if (totalMessage [pendingBytes - 1] == 0xf7) - { - callback->handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); - pendingBytes = 0; - } - else - { - callback->handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); } } + + MidiInput* input; + ScopedPointer portAndEndpoint; + volatile bool active; + + private: + MidiInputCallback& callback; + MidiDataConcatenator concatenator; }; - static CriticalSection callbackLock; - static Array activeCallbacks; - - static void midiInputProc (const MIDIPacketList* pktlist, - void* readProcRefCon, - void* /*srcConnRefCon*/) + static void midiInputProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* /*srcConnRefCon*/) { - double time = Time::getMillisecondCounterHiRes() * 0.001; - const double originalTime = time; - - MidiPortAndCallback* const mpc = (MidiPortAndCallback*) readProcRefCon; - const ScopedLock sl (CoreMidiHelpers::callbackLock); - - if (CoreMidiHelpers::activeCallbacks.contains (mpc) && mpc->active) - { - const MIDIPacket* packet = &pktlist->packet[0]; - - for (unsigned int i = 0; i < pktlist->numPackets; ++i) - { - const uint8* d = (const uint8*) (packet->data); - int size = packet->length; - - while (size > 0) - { - time = originalTime; - - if (mpc->pendingBytes > 0 || d[0] == 0xf0) - { - mpc->processSysex (d, size, time); - } - else - { - int used = 0; - const MidiMessage m (d, size, used, 0, time); - - if (used <= 0) - { - jassertfalse; // malformed midi message - break; - } - else - { - mpc->callback->handleIncomingMidiMessage (mpc->input, m); - } - - size -= used; - d += used; - } - } - - packet = MIDIPacketNext (packet); - } - } + static_cast (readProcRefCon)->handlePackets (pktlist); } } @@ -278832,10 +278761,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) p = MIDIPacketNext (p); } - if (mpe->port != 0) - MIDISend (mpe->port, mpe->endPoint, packets); - else - MIDIReceived (mpe->endPoint, packets); + mpe->send (packets); } else { @@ -278845,10 +278771,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) packets.packet[0].length = message.getRawDataSize(); *(int*) (packets.packet[0].data) = *(const int*) message.getRawData(); - if (mpe->port != 0) - MIDISend (mpe->port, mpe->endPoint, &packets); - else - MIDIReceived (mpe->endPoint, &packets); + mpe->send (&packets); } } @@ -278886,8 +278809,10 @@ int MidiInput::getDefaultDeviceIndex() MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) { + jassert (callback != 0); + using namespace CoreMidiHelpers; - MidiInput* mi = 0; + MidiInput* newInput = 0; if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) { @@ -278895,31 +278820,26 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) if (endPoint != 0) { - CFStringRef pname; + CFStringRef name; - if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name))) { MIDIClientRef client = getGlobalMidiClient(); if (client != 0) { MIDIPortRef port; + ScopedPointer mpc (new MidiPortAndCallback (*callback)); - ScopedPointer mpc (new MidiPortAndCallback()); - mpc->active = false; - - if (CHECK_ERROR (MIDIInputPortCreate (client, pname, midiInputProc, mpc, &port))) + if (CHECK_ERROR (MIDIInputPortCreate (client, name, midiInputProc, mpc, &port))) { if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, 0))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (port, endPoint); - mpc->callback = callback; - mpc->pendingBytes = 0; - mpc->pendingData.ensureSize (128); - mi = new MidiInput (getDevices() [index]); - mpc->input = mi; - mi->internal = mpc; + newInput = new MidiInput (getDevices() [index]); + mpc->input = newInput; + newInput->internal = mpc; const ScopedLock sl (callbackLock); activeCallbacks.add (mpc.release()); @@ -278932,22 +278852,24 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) } } - CFRelease (pname); + CFRelease (name); } } - return mi; + return newInput; } MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) { + jassert (callback != 0); + using namespace CoreMidiHelpers; MidiInput* mi = 0; MIDIClientRef client = getGlobalMidiClient(); if (client != 0) { - ScopedPointer mpc (new MidiPortAndCallback()); + ScopedPointer mpc (new MidiPortAndCallback (*callback)); mpc->active = false; MIDIEndpointRef endPoint; @@ -278955,9 +278877,6 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba if (CHECK_ERROR (MIDIDestinationCreate (client, name, midiInputProc, mpc, &endPoint))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (0, endPoint); - mpc->callback = callback; - mpc->pendingBytes = 0; - mpc->pendingData.ensureSize (128); mi = new MidiInput (deviceName); mpc->input = mi; @@ -278980,21 +278899,7 @@ MidiInput::MidiInput (const String& name_) MidiInput::~MidiInput() { - using namespace CoreMidiHelpers; - - MidiPortAndCallback* const mpc = static_cast (internal); - mpc->active = false; - - { - const ScopedLock sl (callbackLock); - activeCallbacks.removeValue (mpc); - } - - if (mpc->portAndEndpoint->port != 0) - CHECK_ERROR (MIDIPortDisconnectSource (mpc->portAndEndpoint->port, mpc->portAndEndpoint->endPoint)); - - delete mpc->portAndEndpoint; - delete mpc; + delete static_cast (internal); } void MidiInput::start() diff --git a/juce_amalgamated.h b/juce_amalgamated.h index f06c217dfe..948a5116b1 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 73 +#define JUCE_BUILDNUMBER 74 /** Current Juce version number. @@ -25726,6 +25726,9 @@ public: /** Returns the sum of the left and right gaps. */ int getLeftAndRight() const throw() { return left + right; } + /** Returns true if this border has no thickness along any edge. */ + bool isEmpty() const throw() { return left + right + top + bottom == 0; } + /** Changes the top gap. */ void setTop (int newTopGap) throw(); @@ -38574,6 +38577,18 @@ public: */ double getCurrentInputLevel() const; + /** Returns the a lock that can be used to synchronise access to the audio callback. + Obviously while this is locked, you're blocking the audio thread from running, so + it must only be used for very brief periods when absolutely necessary. + */ + CriticalSection& getAudioCallbackLock() throw() { return audioCallbackLock; } + + /** Returns the a lock that can be used to synchronise access to the midi callback. + Obviously while this is locked, you're blocking the midi system from running, so + it must only be used for very brief periods when absolutely necessary. + */ + CriticalSection& getMidiCallbackLock() throw() { return midiCallbackLock; } + juce_UseDebuggingNewOperator private: diff --git a/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp b/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp index 96d8cc41b8..6247c7826d 100644 --- a/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp @@ -256,7 +256,7 @@ public: } } - int framesToDo = bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame; + int framesToDo = jmin (numSamples, bufferList->mBuffers[0].mDataByteSize / inputStreamDesc.mBytesPerFrame); bufferList->mBuffers[0].mDataByteSize = inputStreamDesc.mBytesPerFrame * framesToDo; UInt32 outFlags = 0; @@ -268,8 +268,8 @@ public: break; } - lastSampleRead = startSampleInFile + actualNumFrames * samplesPerFrame; - const int samplesReceived = actualNumFrames * samplesPerFrame; + lastSampleRead = startSampleInFile + actualNumFrames; + const int samplesReceived = actualNumFrames; for (int j = numDestChannels; --j >= 0;) { diff --git a/src/audio/devices/juce_AudioDeviceManager.h b/src/audio/devices/juce_AudioDeviceManager.h index 3e9441fbc5..44358bbb63 100644 --- a/src/audio/devices/juce_AudioDeviceManager.h +++ b/src/audio/devices/juce_AudioDeviceManager.h @@ -429,6 +429,18 @@ public: */ double getCurrentInputLevel() const; + /** Returns the a lock that can be used to synchronise access to the audio callback. + Obviously while this is locked, you're blocking the audio thread from running, so + it must only be used for very brief periods when absolutely necessary. + */ + CriticalSection& getAudioCallbackLock() throw() { return audioCallbackLock; } + + /** Returns the a lock that can be used to synchronise access to the midi callback. + Obviously while this is locked, you're blocking the midi system from running, so + it must only be used for very brief periods when absolutely necessary. + */ + CriticalSection& getMidiCallbackLock() throw() { return midiCallbackLock; } + //============================================================================== juce_UseDebuggingNewOperator diff --git a/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm b/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm index 1ce4b007ea..0c273e4206 100644 --- a/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm +++ b/src/audio/plugins/formats/juce_AudioUnitPluginFormat.mm @@ -199,6 +199,8 @@ public: //============================================================================== ~AudioUnitPluginInstance(); + void initialise(); + //============================================================================== // AudioPluginInstance methods: @@ -273,7 +275,7 @@ private: String pluginName, manufacturer, version; String fileOrIdentifier; CriticalSection lock; - bool initialised, wantsMidiMessages, wasPlaying; + bool wantsMidiMessages, wasPlaying, prepared; HeapBlock outputBufferList; AudioTimeStamp timeStamp; @@ -284,7 +286,8 @@ private: //============================================================================== bool getComponentDescFromFile (const String& fileOrIdentifier); - void initialise(); + void setPluginCallbacks(); + void getParameterListFromPlugin(); //============================================================================== OSStatus renderGetInput (AudioUnitRenderActionFlags* ioActionFlags, @@ -348,16 +351,49 @@ private: 0, supportedChannels, &supportedChannelsSize) == noErr && supportedChannelsSize > 0) { + int explicitNumIns = 0; + int explicitNumOuts = 0; + int maximumNumIns = 0; + int maximumNumOuts = 0; + for (int i = 0; i < supportedChannelsSize / sizeof (AUChannelInfo); ++i) { - numIns = jmax (numIns, (int) supportedChannels[i].inChannels); - numOuts = jmax (numOuts, (int) supportedChannels[i].outChannels); + const int inChannels = (int) supportedChannels[i].inChannels; + const int outChannels = (int) supportedChannels[i].outChannels; + + if (inChannels < 0) + maximumNumIns = jmin (maximumNumIns, inChannels); + else + explicitNumIns = jmax (explicitNumIns, inChannels); + + if (outChannels < 0) + maximumNumOuts = jmin (maximumNumOuts, outChannels); + else + explicitNumOuts = jmax (explicitNumOuts, outChannels); + } + + if ((maximumNumIns == -1 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, as long as they match) + || (maximumNumIns == -2 && maximumNumOuts == -1) // (special meaning: any number of ins/outs, even if they don't match) + || (maximumNumIns == -1 && maximumNumOuts == -2)) + { + numIns = numOuts = 2; + } + else + { + numIns = explicitNumIns; + numOuts = explicitNumOuts; + + if (maximumNumIns == -1 || (maximumNumIns < 0 && explicitNumIns <= -maximumNumIns)) + numIns = 2; + + if (maximumNumOuts == -1 || (maximumNumOuts < 0 && explicitNumOuts <= -maximumNumOuts)) + numOuts = 2; } } else { - // (this really means the plugin will take any number of ins/outs as long - // as they are the same) + // (this really means the plugin will take any number of ins/outs as long + // as they are the same) numIns = numOuts = 2; } } @@ -371,8 +407,7 @@ private: //============================================================================== AudioUnitPluginInstance::AudioUnitPluginInstance (const String& fileOrIdentifier) : fileOrIdentifier (fileOrIdentifier), - initialised (false), - wantsMidiMessages (false), + wantsMidiMessages (false), wasPlaying (false), prepared (false), audioUnit (0), currentBuffer (0) { @@ -498,13 +533,20 @@ bool AudioUnitPluginInstance::getComponentDescFromFile (const String& fileOrIden //============================================================================== void AudioUnitPluginInstance::initialise() { - if (initialised || audioUnit == 0) - return; + getParameterListFromPlugin(); + setPluginCallbacks(); - log ("Initialising AU: " + pluginName); + int numIns, numOuts; + getNumChannels (numIns, numOuts); + setPlayConfigDetails (numIns, numOuts, 0, 0); + setLatencySamples (0); +} +void AudioUnitPluginInstance::getParameterListFromPlugin() +{ parameterIds.clear(); + if (audioUnit != 0) { UInt32 paramListSize = 0; AudioUnitGetProperty (audioUnit, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, @@ -518,36 +560,34 @@ void AudioUnitPluginInstance::initialise() 0, ¶meterIds.getReference(0), ¶mListSize); } } +} +void AudioUnitPluginInstance::setPluginCallbacks() +{ + if (audioUnit != 0) { - AURenderCallbackStruct info; - zerostruct (info); - info.inputProcRefCon = this; - info.inputProc = renderGetInputCallback; + { + AURenderCallbackStruct info; + zerostruct (info); + info.inputProcRefCon = this; + info.inputProc = renderGetInputCallback; - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, - 0, &info, sizeof (info)); + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, + 0, &info, sizeof (info)); + } + + { + HostCallbackInfo info; + zerostruct (info); + info.hostUserData = this; + info.beatAndTempoProc = getBeatAndTempoCallback; + info.musicalTimeLocationProc = getMusicalTimeLocationCallback; + info.transportStateProc = getTransportStateCallback; + + AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, + 0, &info, sizeof (info)); + } } - - { - HostCallbackInfo info; - zerostruct (info); - info.hostUserData = this; - info.beatAndTempoProc = getBeatAndTempoCallback; - info.musicalTimeLocationProc = getMusicalTimeLocationCallback; - info.transportStateProc = getTransportStateCallback; - - AudioUnitSetProperty (audioUnit, kAudioUnitProperty_HostCallbacks, kAudioUnitScope_Global, - 0, &info, sizeof (info)); - } - - int numIns, numOuts; - getNumChannels (numIns, numOuts); - setPlayConfigDetails (numIns, numOuts, 0, 0); - - initialised = AudioUnitInitialize (audioUnit) == noErr; - - setLatencySamples (0); } @@ -557,6 +597,8 @@ void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, { if (audioUnit != 0) { + releaseResources(); + Float64 sampleRateIn = 0, sampleRateOut = 0; UInt32 sampleRateSize = sizeof (sampleRateIn); AudioUnitGetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sampleRateIn, &sampleRateSize); @@ -564,25 +606,13 @@ void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, if (sampleRateIn != sampleRate_ || sampleRateOut != sampleRate_) { - if (initialised) - { - AudioUnitUninitialize (audioUnit); - initialised = false; - } - Float64 sr = sampleRate_; AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Input, 0, &sr, sizeof (Float64)); AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SampleRate, kAudioUnitScope_Output, 0, &sr, sizeof (Float64)); } - } - initialise(); - - if (initialised) - { int numIns, numOuts; getNumChannels (numIns, numOuts); - setPlayConfigDetails (numIns, numOuts, sampleRate_, samplesPerBlockExpected); Float64 latencySecs = 0.0; @@ -596,24 +626,26 @@ void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); - AudioStreamBasicDescription stream; - zerostruct (stream); - stream.mSampleRate = sampleRate_; - stream.mFormatID = kAudioFormatLinearPCM; - stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; - stream.mFramesPerPacket = 1; - stream.mBytesPerPacket = 4; - stream.mBytesPerFrame = 4; - stream.mBitsPerChannel = 32; - stream.mChannelsPerFrame = numIns; + { + AudioStreamBasicDescription stream; + zerostruct (stream); + stream.mSampleRate = sampleRate_; + stream.mFormatID = kAudioFormatLinearPCM; + stream.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; + stream.mFramesPerPacket = 1; + stream.mBytesPerPacket = 4; + stream.mBytesPerFrame = 4; + stream.mBitsPerChannel = 32; + stream.mChannelsPerFrame = numIns; - OSStatus err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, - 0, &stream, sizeof (stream)); + OSStatus err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, + 0, &stream, sizeof (stream)); - stream.mChannelsPerFrame = numOuts; + stream.mChannelsPerFrame = numOuts; - err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, - 0, &stream, sizeof (stream)); + err = AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, + 0, &stream, sizeof (stream)); + } outputBufferList.calloc (sizeof (AudioBufferList) + sizeof (AudioBuffer) * (numOuts + 1), 1); outputBufferList->mNumberBuffers = numOuts; @@ -628,19 +660,24 @@ void AudioUnitPluginInstance::prepareToPlay (double sampleRate_, currentBuffer = 0; wasPlaying = false; + + prepared = (AudioUnitInitialize (audioUnit) == noErr); } } void AudioUnitPluginInstance::releaseResources() { - if (initialised) + if (prepared) { + AudioUnitUninitialize (audioUnit); + AudioUnitReset (audioUnit, kAudioUnitScope_Input, 0); AudioUnitReset (audioUnit, kAudioUnitScope_Output, 0); AudioUnitReset (audioUnit, kAudioUnitScope_Global, 0); outputBufferList.free(); currentBuffer = 0; + prepared = false; } } @@ -678,7 +715,7 @@ void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, { const int numSamples = buffer.getNumSamples(); - if (initialised) + if (prepared) { AudioUnitRenderActionFlags flags = 0; @@ -718,7 +755,7 @@ void AudioUnitPluginInstance::processBlock (AudioSampleBuffer& buffer, } else { - // Not initialised, so just bypass.. + // Plugin not working correctly, so just bypass.. for (int i = getNumInputChannels(); i < getNumOutputChannels(); ++i) buffer.clear (i, 0, buffer.getNumSamples()); } @@ -898,6 +935,8 @@ private: UInt32 dataSize = 0; Boolean isWritable = false; + AudioUnitInitialize (plugin.audioUnit); + if (AudioUnitGetPropertyInfo (plugin.audioUnit, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, &dataSize, &isWritable) == noErr && dataSize != 0 diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 530acdb68f..3d0bc8cb93 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 73 +#define JUCE_BUILDNUMBER 74 /** Current Juce version number. diff --git a/src/gui/components/lookandfeel/juce_LookAndFeel.cpp b/src/gui/components/lookandfeel/juce_LookAndFeel.cpp index 1194c896c4..12fc17abe7 100644 --- a/src/gui/components/lookandfeel/juce_LookAndFeel.cpp +++ b/src/gui/components/lookandfeel/juce_LookAndFeel.cpp @@ -1745,9 +1745,25 @@ void LookAndFeel::drawCornerResizer (Graphics& g, } } -void LookAndFeel::drawResizableFrame (Graphics&, int /*w*/, int /*h*/, - const BorderSize& /*borders*/) +void LookAndFeel::drawResizableFrame (Graphics& g, int w, int h, const BorderSize& border) { + if (! border.isEmpty()) + { + const Rectangle fullSize (0, 0, w, h); + const Rectangle centreArea (border.subtractedFrom (fullSize)); + + g.saveState(); + + g.excludeClipRegion (centreArea); + + g.setColour (Colour (0x50000000)); + g.drawRect (fullSize); + + g.setColour (Colour (0x19000000)); + g.drawRect (centreArea.expanded (1, 1)); + + g.restoreState(); + } } //============================================================================== @@ -1757,17 +1773,9 @@ void LookAndFeel::fillResizableWindowBackground (Graphics& g, int /*w*/, int /*h g.fillAll (window.getBackgroundColour()); } -void LookAndFeel::drawResizableWindowBorder (Graphics& g, int w, int h, - const BorderSize& border, ResizableWindow&) +void LookAndFeel::drawResizableWindowBorder (Graphics&, int /*w*/, int /*h*/, + const BorderSize& /*border*/, ResizableWindow&) { - g.setColour (Colour (0x80000000)); - g.drawRect (0, 0, w, h); - - g.setColour (Colour (0x19000000)); - g.drawRect (border.getLeft() - 1, - border.getTop() - 1, - w + 2 - border.getLeftAndRight(), - h + 2 - border.getTopAndBottom()); } void LookAndFeel::drawDocumentWindowTitleBar (DocumentWindow& window, diff --git a/src/gui/components/menus/juce_MenuBarComponent.cpp b/src/gui/components/menus/juce_MenuBarComponent.cpp index f04e9754d1..c216926788 100644 --- a/src/gui/components/menus/juce_MenuBarComponent.cpp +++ b/src/gui/components/menus/juce_MenuBarComponent.cpp @@ -235,9 +235,7 @@ void MenuBarComponent::handleCommandMessage (int commandId) { const Point mousePos (getMouseXYRelative()); updateItemUnderMouse (mousePos.getX(), mousePos.getY()); - - if (! isCurrentlyBlockedByAnotherModalComponent()) - setOpenItem (-1); + setOpenItem (-1); if (commandId != 0 && model != 0) model->menuItemSelected (commandId, topLevelIndexClicked); diff --git a/src/gui/graphics/geometry/juce_BorderSize.h b/src/gui/graphics/geometry/juce_BorderSize.h index 3dc676530f..20aa1c384a 100644 --- a/src/gui/graphics/geometry/juce_BorderSize.h +++ b/src/gui/graphics/geometry/juce_BorderSize.h @@ -82,6 +82,9 @@ public: /** Returns the sum of the left and right gaps. */ int getLeftAndRight() const throw() { return left + right; } + /** Returns true if this border has no thickness along any edge. */ + bool isEmpty() const throw() { return left + right + top + bottom == 0; } + //============================================================================== /** Changes the top gap. */ void setTop (int newTopGap) throw(); diff --git a/src/native/common/juce_MidiDataConcatenator.h b/src/native/common/juce_MidiDataConcatenator.h new file mode 100644 index 0000000000..6905dd022a --- /dev/null +++ b/src/native/common/juce_MidiDataConcatenator.h @@ -0,0 +1,148 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online at www.gnu.org/licenses. + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ +#define __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ + + +//============================================================================== +/** + Helper class that takes chunks of incoming midi bytes, packages them into + messages, and dispatches them to a midi callback. +*/ +class MidiDataConcatenator +{ +public: + //============================================================================== + MidiDataConcatenator (const int initialBufferSize) + : pendingData (initialBufferSize), + pendingBytes (0), pendingDataTime (0) + { + } + + void reset() + { + pendingBytes = 0; + pendingDataTime = 0; + } + + void pushMidiData (const void* data, int numBytes, double time, + MidiInput* input, MidiInputCallback& callback) + { + const uint8* d = static_cast (data); + + while (numBytes > 0) + { + if (pendingBytes > 0 || d[0] == 0xf0) + { + processSysex (d, numBytes, time, input, callback); + } + else + { + int used = 0; + const MidiMessage m (d, numBytes, used, 0, time); + + if (used <= 0) + { + jassertfalse; // malformed midi message + break; + } + + callback.handleIncomingMidiMessage (input, m); + numBytes -= used; + d += used; + } + } + } + +private: + void processSysex (const uint8*& d, int& numBytes, double time, + MidiInput* input, MidiInputCallback& callback) + { + if (*d == 0xf0) + { + pendingBytes = 0; + pendingDataTime = time; + } + + pendingData.ensureSize (pendingBytes + numBytes, false); + uint8* totalMessage = static_cast (pendingData.getData()); + uint8* dest = totalMessage + pendingBytes; + + do + { + if (pendingBytes > 0 && *d >= 0x80) + { + if (*d >= 0xfa || *d == 0xf8) + { + callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); + ++d; + --numBytes; + } + else + { + if (*d == 0xf7) + { + *dest++ = *d++; + pendingBytes++; + --numBytes; + } + + break; + } + } + else + { + *dest++ = *d++; + pendingBytes++; + --numBytes; + } + } + while (numBytes > 0); + + if (pendingBytes > 0) + { + if (totalMessage [pendingBytes - 1] == 0xf7) + { + callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); + pendingBytes = 0; + } + else + { + callback.handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); + } + } + } + + MemoryBlock pendingData; + int pendingBytes; + double pendingDataTime; + + MidiDataConcatenator (const MidiDataConcatenator&); + MidiDataConcatenator& operator= (const MidiDataConcatenator&); +}; + + +#endif // __JUCE_MIDIDATACONCATENATOR_JUCEHEADER__ diff --git a/src/native/juce_mac_NativeCode.mm b/src/native/juce_mac_NativeCode.mm index 47e6356afc..a79746d71d 100644 --- a/src/native/juce_mac_NativeCode.mm +++ b/src/native/juce_mac_NativeCode.mm @@ -85,6 +85,7 @@ BEGIN_JUCE_NAMESPACE #include "../audio/devices/juce_AudioIODeviceType.h" #include "../audio/devices/juce_MidiOutput.h" #include "../audio/devices/juce_MidiInput.h" +#include "common/juce_MidiDataConcatenator.h" #undef Point //============================================================================== diff --git a/src/native/juce_win32_NativeCode.cpp b/src/native/juce_win32_NativeCode.cpp index bbb4760559..4b59973c5e 100644 --- a/src/native/juce_win32_NativeCode.cpp +++ b/src/native/juce_win32_NativeCode.cpp @@ -83,6 +83,7 @@ BEGIN_JUCE_NAMESPACE #include "../audio/devices/juce_AudioIODeviceType.h" #include "../audio/devices/juce_MidiOutput.h" #include "../audio/devices/juce_MidiInput.h" +#include "common/juce_MidiDataConcatenator.h" //============================================================================== #define JUCE_INCLUDED_FILE 1 diff --git a/src/native/mac/juce_mac_CoreMidi.cpp b/src/native/mac/juce_mac_CoreMidi.cpp index 7d09a7a4de..7c29041ac3 100644 --- a/src/native/mac/juce_mac_CoreMidi.cpp +++ b/src/native/mac/juce_mac_CoreMidi.cpp @@ -214,130 +214,75 @@ namespace CoreMidiHelpers MIDIEndpointDispose (endPoint); } + void send (const MIDIPacketList* const packets) + { + if (port != 0) + MIDISend (port, endPoint, packets); + else + MIDIReceived (endPoint, packets); + } + MIDIPortRef port; MIDIEndpointRef endPoint; }; //============================================================================== + class MidiPortAndCallback; + static CriticalSection callbackLock; + static Array activeCallbacks; + class MidiPortAndCallback { public: - MidiInput* input; - MidiPortAndEndpoint* portAndEndpoint; - MidiInputCallback* callback; - MemoryBlock pendingData; - int pendingBytes; - double pendingDataTime; - bool active; - - void processSysex (const uint8*& d, int& size, const double time) + MidiPortAndCallback (MidiInputCallback& callback_) + : input (0), active (false), callback (callback_), concatenator (2048) { - if (*d == 0xf0) + } + + ~MidiPortAndCallback() + { + active = false; + { - pendingBytes = 0; - pendingDataTime = time; + const ScopedLock sl (callbackLock); + activeCallbacks.removeValue (this); } - pendingData.ensureSize (pendingBytes + size, false); - uint8* totalMessage = (uint8*) pendingData.getData(); + if (portAndEndpoint != 0 && portAndEndpoint->port != 0) + CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint)); + } - uint8* dest = totalMessage + pendingBytes; + void handlePackets (const MIDIPacketList* const pktlist) + { + const double time = Time::getMillisecondCounterHiRes() * 0.001; - while (size > 0) + const ScopedLock sl (callbackLock); + if (activeCallbacks.contains (this) && active) { - if (pendingBytes > 0 && *d >= 0x80) + const MIDIPacket* packet = &pktlist->packet[0]; + + for (unsigned int i = 0; i < pktlist->numPackets; ++i) { - if (*d >= 0xfa || *d == 0xf8) - { - callback->handleIncomingMidiMessage (input, MidiMessage (*d, time)); - ++d; - --size; - } - else - { - if (*d == 0xf7) - { - *dest++ = *d++; - pendingBytes++; - --size; - } + concatenator.pushMidiData (packet->data, (int) packet->length, time, + input, callback); - break; - } + packet = MIDIPacketNext (packet); } - else - { - *dest++ = *d++; - pendingBytes++; - --size; - } - } - - if (totalMessage [pendingBytes - 1] == 0xf7) - { - callback->handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); - pendingBytes = 0; - } - else - { - callback->handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); } } + + MidiInput* input; + ScopedPointer portAndEndpoint; + volatile bool active; + + private: + MidiInputCallback& callback; + MidiDataConcatenator concatenator; }; - static CriticalSection callbackLock; - static Array activeCallbacks; - - static void midiInputProc (const MIDIPacketList* pktlist, - void* readProcRefCon, - void* /*srcConnRefCon*/) + static void midiInputProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* /*srcConnRefCon*/) { - double time = Time::getMillisecondCounterHiRes() * 0.001; - const double originalTime = time; - - MidiPortAndCallback* const mpc = (MidiPortAndCallback*) readProcRefCon; - const ScopedLock sl (CoreMidiHelpers::callbackLock); - - if (CoreMidiHelpers::activeCallbacks.contains (mpc) && mpc->active) - { - const MIDIPacket* packet = &pktlist->packet[0]; - - for (unsigned int i = 0; i < pktlist->numPackets; ++i) - { - const uint8* d = (const uint8*) (packet->data); - int size = packet->length; - - while (size > 0) - { - time = originalTime; - - if (mpc->pendingBytes > 0 || d[0] == 0xf0) - { - mpc->processSysex (d, size, time); - } - else - { - int used = 0; - const MidiMessage m (d, size, used, 0, time); - - if (used <= 0) - { - jassertfalse; // malformed midi message - break; - } - else - { - mpc->callback->handleIncomingMidiMessage (mpc->input, m); - } - - size -= used; - d += used; - } - } - - packet = MIDIPacketNext (packet); - } - } + static_cast (readProcRefCon)->handlePackets (pktlist); } } @@ -462,10 +407,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) p = MIDIPacketNext (p); } - if (mpe->port != 0) - MIDISend (mpe->port, mpe->endPoint, packets); - else - MIDIReceived (mpe->endPoint, packets); + mpe->send (packets); } else { @@ -475,10 +417,7 @@ void MidiOutput::sendMessageNow (const MidiMessage& message) packets.packet[0].length = message.getRawDataSize(); *(int*) (packets.packet[0].data) = *(const int*) message.getRawData(); - if (mpe->port != 0) - MIDISend (mpe->port, mpe->endPoint, &packets); - else - MIDIReceived (mpe->endPoint, &packets); + mpe->send (&packets); } } @@ -517,8 +456,10 @@ int MidiInput::getDefaultDeviceIndex() MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) { + jassert (callback != 0); + using namespace CoreMidiHelpers; - MidiInput* mi = 0; + MidiInput* newInput = 0; if (((unsigned int) index) < (unsigned int) MIDIGetNumberOfSources()) { @@ -526,31 +467,26 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) if (endPoint != 0) { - CFStringRef pname; + CFStringRef name; - if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) + if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name))) { MIDIClientRef client = getGlobalMidiClient(); if (client != 0) { MIDIPortRef port; + ScopedPointer mpc (new MidiPortAndCallback (*callback)); - ScopedPointer mpc (new MidiPortAndCallback()); - mpc->active = false; - - if (CHECK_ERROR (MIDIInputPortCreate (client, pname, midiInputProc, mpc, &port))) + if (CHECK_ERROR (MIDIInputPortCreate (client, name, midiInputProc, mpc, &port))) { if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, 0))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (port, endPoint); - mpc->callback = callback; - mpc->pendingBytes = 0; - mpc->pendingData.ensureSize (128); - mi = new MidiInput (getDevices() [index]); - mpc->input = mi; - mi->internal = mpc; + newInput = new MidiInput (getDevices() [index]); + mpc->input = newInput; + newInput->internal = mpc; const ScopedLock sl (callbackLock); activeCallbacks.add (mpc.release()); @@ -563,22 +499,24 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) } } - CFRelease (pname); + CFRelease (name); } } - return mi; + return newInput; } MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) { + jassert (callback != 0); + using namespace CoreMidiHelpers; MidiInput* mi = 0; MIDIClientRef client = getGlobalMidiClient(); if (client != 0) { - ScopedPointer mpc (new MidiPortAndCallback()); + ScopedPointer mpc (new MidiPortAndCallback (*callback)); mpc->active = false; MIDIEndpointRef endPoint; @@ -586,9 +524,6 @@ MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallba if (CHECK_ERROR (MIDIDestinationCreate (client, name, midiInputProc, mpc, &endPoint))) { mpc->portAndEndpoint = new MidiPortAndEndpoint (0, endPoint); - mpc->callback = callback; - mpc->pendingBytes = 0; - mpc->pendingData.ensureSize (128); mi = new MidiInput (deviceName); mpc->input = mi; @@ -611,21 +546,7 @@ MidiInput::MidiInput (const String& name_) MidiInput::~MidiInput() { - using namespace CoreMidiHelpers; - - MidiPortAndCallback* const mpc = static_cast (internal); - mpc->active = false; - - { - const ScopedLock sl (callbackLock); - activeCallbacks.removeValue (mpc); - } - - if (mpc->portAndEndpoint->port != 0) - CHECK_ERROR (MIDIPortDisconnectSource (mpc->portAndEndpoint->port, mpc->portAndEndpoint->endPoint)); - - delete mpc->portAndEndpoint; - delete mpc; + delete static_cast (internal); } void MidiInput::start() diff --git a/src/native/mac/juce_mac_NSViewComponent.mm b/src/native/mac/juce_mac_NSViewComponent.mm index 61e43f64ba..c0e974dafc 100644 --- a/src/native/mac/juce_mac_NSViewComponent.mm +++ b/src/native/mac/juce_mac_NSViewComponent.mm @@ -78,11 +78,7 @@ public: { const Point pos (owner->relativePositionToOtherComponent (topComp, Point())); - NSRect r; - r.origin.x = (float) pos.getX(); - r.origin.y = (float) pos.getY(); - r.size.width = (float) owner->getWidth(); - r.size.height = (float) owner->getHeight(); + NSRect r = NSMakeRect ((float) pos.getX(), (float) pos.getY(), (float) owner->getWidth(), (float) owner->getHeight()); r.origin.y = [[view superview] frame].size.height - (r.origin.y + r.size.height); [view setFrame: r]; diff --git a/src/native/mac/juce_mac_NSViewComponentPeer.mm b/src/native/mac/juce_mac_NSViewComponentPeer.mm index c8d7b761f0..1c0fc15c1a 100644 --- a/src/native/mac/juce_mac_NSViewComponentPeer.mm +++ b/src/native/mac/juce_mac_NSViewComponentPeer.mm @@ -871,11 +871,7 @@ NSViewComponentPeer::NSViewComponentPeer (Component* const component_, #endif recursiveToFrontCall (false) { - NSRect r; - r.origin.x = 0; - r.origin.y = 0; - r.size.width = (float) component->getWidth(); - r.size.height = (float) component->getHeight(); + NSRect r = NSMakeRect (0, 0, (float) component->getWidth(),(float) component->getHeight()); view = [[JuceNSView alloc] initWithOwner: this withFrame: r]; [view setPostsFrameChangedNotifications: YES]; @@ -999,14 +995,8 @@ void NSViewComponentPeer::setSize (int w, int h) void NSViewComponentPeer::setBounds (int x, int y, int w, int h, bool isNowFullScreen) { fullScreen = isNowFullScreen; - w = jmax (0, w); - h = jmax (0, h); - NSRect r; - r.origin.x = (float) x; - r.origin.y = (float) y; - r.size.width = (float) w; - r.size.height = (float) h; + NSRect r = NSMakeRect ((float) x, (float) y, (float) jmax (0, w), (float) jmax (0, h)); if (isSharedWindow) { diff --git a/src/native/windows/juce_win32_FileChooser.cpp b/src/native/windows/juce_win32_FileChooser.cpp index ec454297fe..7ca022730d 100644 --- a/src/native/windows/juce_win32_FileChooser.cpp +++ b/src/native/windows/juce_win32_FileChooser.cpp @@ -29,14 +29,8 @@ //============================================================================== -void juce_setWindowStyleBit (HWND h, const int styleType, const int feature, const bool bitIsSet) throw(); - namespace FileChooserHelpers { - static const void* defaultDirPath = 0; - static String returnedString; // need this to get non-existent pathnames from the directory chooser - static Component* currentExtraFileWin = 0; - static bool areThereAnyAlwaysOnTopWindows() { for (int i = Desktop::getInstance().getNumComponents(); --i >= 0;) @@ -50,64 +44,67 @@ namespace FileChooserHelpers return false; } - static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM /*lpData*/) + struct FileChooserCallbackInfo { + String initialPath; + String returnedString; // need this to get non-existent pathnames from the directory chooser + ScopedPointer customComponent; + }; + + static int CALLBACK browseCallbackProc (HWND hWnd, UINT msg, LPARAM lParam, LPARAM lpData) + { + FileChooserCallbackInfo* info = (FileChooserCallbackInfo*) lpData; + if (msg == BFFM_INITIALIZED) - SendMessage (hWnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) defaultDirPath); + SendMessage (hWnd, BFFM_SETSELECTIONW, TRUE, (LPARAM) static_cast (info->initialPath)); else if (msg == BFFM_VALIDATEFAILEDW) - returnedString = (LPCWSTR) lParam; + info->returnedString = (LPCWSTR) lParam; else if (msg == BFFM_VALIDATEFAILEDA) - returnedString = (const char*) lParam; + info->returnedString = (const char*) lParam; return 0; } static UINT_PTR CALLBACK openCallback (HWND hdlg, UINT uiMsg, WPARAM /*wParam*/, LPARAM lParam) { - if (currentExtraFileWin != 0) + if (uiMsg == WM_INITDIALOG) { - if (uiMsg == WM_INITDIALOG) + Component* customComp = ((FileChooserCallbackInfo*) (((OPENFILENAMEW*) lParam)->lCustData))->customComponent; + + HWND dialogH = GetParent (hdlg); + jassert (dialogH != 0); + if (dialogH == 0) + dialogH = hdlg; + + RECT r, cr; + GetWindowRect (dialogH, &r); + GetClientRect (dialogH, &cr); + + SetWindowPos (dialogH, 0, + r.left, r.top, + customComp->getWidth() + jmax (150, (int) (r.right - r.left)), + jmax (150, (int) (r.bottom - r.top)), + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); + + customComp->setBounds (cr.right, cr.top, customComp->getWidth(), cr.bottom - cr.top); + customComp->addToDesktop (0, dialogH); + } + else if (uiMsg == WM_NOTIFY) + { + LPOFNOTIFY ofn = (LPOFNOTIFY) lParam; + + if (ofn->hdr.code == CDN_SELCHANGE) { - HWND dialogH = GetParent (hdlg); - jassert (dialogH != 0); - if (dialogH == 0) - dialogH = hdlg; + FileChooserCallbackInfo* info = (FileChooserCallbackInfo*) ofn->lpOFN->lCustData; + FilePreviewComponent* comp = static_cast (info->customComponent->getChildComponent(0)); - RECT r, cr; - GetWindowRect (dialogH, &r); - GetClientRect (dialogH, &cr); - - SetWindowPos (dialogH, 0, - r.left, r.top, - currentExtraFileWin->getWidth() + jmax (150, (int) (r.right - r.left)), - jmax (150, (int) (r.bottom - r.top)), - SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER); - - currentExtraFileWin->setBounds (cr.right, cr.top, currentExtraFileWin->getWidth(), cr.bottom - cr.top); - currentExtraFileWin->getChildComponent(0)->setBounds (0, 0, currentExtraFileWin->getWidth(), currentExtraFileWin->getHeight()); - - SetParent ((HWND) currentExtraFileWin->getWindowHandle(), (HWND) dialogH); - juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_CHILD, (dialogH != 0)); - juce_setWindowStyleBit ((HWND)currentExtraFileWin->getWindowHandle(), GWL_STYLE, WS_POPUP, (dialogH == 0)); - } - else if (uiMsg == WM_NOTIFY) - { - LPOFNOTIFY ofn = (LPOFNOTIFY) lParam; - - if (ofn->hdr.code == CDN_SELCHANGE) + if (comp != 0) { - FilePreviewComponent* comp = (FilePreviewComponent*) currentExtraFileWin->getChildComponent(0); + WCHAR path [MAX_PATH * 2]; + zerostruct (path); + CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH); - if (comp != 0) - { - TCHAR path [MAX_PATH * 2]; - path[0] = 0; - CommDlg_OpenSave_GetFilePath (GetParent (hdlg), (LPARAM) &path, MAX_PATH); - - const String fn ((const WCHAR*) path); - - comp->selectedFileChanged (File (fn)); - } + comp->selectedFileChanged (File (path)); } } } @@ -115,17 +112,15 @@ namespace FileChooserHelpers return 0; } - class FPComponentHolder : public Component + class CustomComponentHolder : public Component { public: - FPComponentHolder() + CustomComponentHolder (Component* customComp) { setVisible (true); setOpaque (true); - } - - ~FPComponentHolder() - { + addAndMakeVisible (customComp); + setSize (jlimit (20, 800, customComp->getWidth()), customComp->getHeight()); } void paint (Graphics& g) @@ -133,203 +128,157 @@ namespace FileChooserHelpers g.fillAll (Colours::lightgrey); } + void resized() + { + if (getNumChildComponents() > 0) + getChildComponent(0)->setBounds (getLocalBounds()); + } + + juce_UseDebuggingNewOperator + private: - FPComponentHolder (const FPComponentHolder&); - FPComponentHolder& operator= (const FPComponentHolder&); + CustomComponentHolder (const CustomComponentHolder&); + CustomComponentHolder& operator= (const CustomComponentHolder&); }; } //============================================================================== -void FileChooser::showPlatformDialog (Array& results, - const String& title, - const File& currentFileOrDirectory, - const String& filter, - bool selectsDirectory, - bool /*selectsFiles*/, - bool isSaveDialogue, - bool warnAboutOverwritingExistingFiles, - bool selectMultipleFiles, - FilePreviewComponent* extraInfoComponent) +void FileChooser::showPlatformDialog (Array& results, const String& title, const File& currentFileOrDirectory, + const String& filter, bool selectsDirectory, bool /*selectsFiles*/, + bool isSaveDialogue, bool warnAboutOverwritingExistingFiles, + bool selectMultipleFiles, FilePreviewComponent* extraInfoComponent) { using namespace FileChooserHelpers; - const int numCharsAvailable = 32768; - MemoryBlock filenameSpace ((numCharsAvailable + 1) * sizeof (WCHAR), true); - WCHAR* const fname = (WCHAR*) filenameSpace.getData(); - int fnameIdx = 0; - JUCE_TRY + HeapBlock files; + const int charsAvailableForResult = 32768; + files.calloc (charsAvailableForResult + 1); + int filenameOffset = 0; + + FileChooserCallbackInfo info; + + // use a modal window as the parent for this dialog box + // to block input from other app windows + Component parentWindow (String::empty); + const Rectangle mainMon (Desktop::getInstance().getMainMonitorArea()); + parentWindow.setBounds (mainMon.getX() + mainMon.getWidth() / 4, + mainMon.getY() + mainMon.getHeight() / 4, + 0, 0); + parentWindow.setOpaque (true); + parentWindow.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows()); + parentWindow.addToDesktop (0); + + if (extraInfoComponent == 0) + parentWindow.enterModalState(); + + if (currentFileOrDirectory.isDirectory()) { - // use a modal window as the parent for this dialog box - // to block input from other app windows - const Rectangle mainMon (Desktop::getInstance().getMainMonitorArea()); + info.initialPath = currentFileOrDirectory.getFullPathName(); + } + else + { + currentFileOrDirectory.getFileName().copyToUnicode (files, charsAvailableForResult); + info.initialPath = currentFileOrDirectory.getParentDirectory().getFullPathName(); + } - Component w (String::empty); - w.setBounds (mainMon.getX() + mainMon.getWidth() / 4, - mainMon.getY() + mainMon.getHeight() / 4, - 0, 0); - w.setOpaque (true); - w.setAlwaysOnTop (areThereAnyAlwaysOnTopWindows()); - w.addToDesktop (0); + if (selectsDirectory) + { + BROWSEINFO bi; + zerostruct (bi); - if (extraInfoComponent == 0) - w.enterModalState(); + bi.hwndOwner = (HWND) parentWindow.getWindowHandle(); + bi.pszDisplayName = files; + bi.lpszTitle = title; + bi.lParam = (LPARAM) &info; + bi.lpfn = browseCallbackProc; + #ifdef BIF_USENEWUI + bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE; + #else + bi.ulFlags = 0x50; + #endif - String initialDir; + LPITEMIDLIST list = SHBrowseForFolder (&bi); - if (currentFileOrDirectory.isDirectory()) + if (! SHGetPathFromIDListW (list, files)) { - initialDir = currentFileOrDirectory.getFullPathName(); - } - else - { - currentFileOrDirectory.getFileName().copyToUnicode (fname, numCharsAvailable); - - initialDir = currentFileOrDirectory.getParentDirectory().getFullPathName(); + files[0] = 0; + info.returnedString = String::empty; } - if (currentExtraFileWin->isValidComponent()) + LPMALLOC al; + if (list != 0 && SUCCEEDED (SHGetMalloc (&al))) + al->Free (list); + + if (info.returnedString.isNotEmpty()) { - jassertfalse; + results.add (File (String (files)).getSiblingFile (info.returnedString)); return; } - - if (selectsDirectory) - { - LPITEMIDLIST list = 0; - filenameSpace.fillWith (0); - - { - BROWSEINFO bi; - zerostruct (bi); - - bi.hwndOwner = (HWND) w.getWindowHandle(); - bi.pszDisplayName = fname; - bi.lpszTitle = title; - bi.lpfn = browseCallbackProc; -#ifdef BIF_USENEWUI - bi.ulFlags = BIF_USENEWUI | BIF_VALIDATE; -#else - bi.ulFlags = 0x50; -#endif - defaultDirPath = (const WCHAR*) initialDir; - - list = SHBrowseForFolder (&bi); - - if (! SHGetPathFromIDListW (list, fname)) - { - fname[0] = 0; - returnedString = String::empty; - } - } - - LPMALLOC al; - if (list != 0 && SUCCEEDED (SHGetMalloc (&al))) - al->Free (list); - - defaultDirPath = 0; - - if (returnedString.isNotEmpty()) - { - const String stringFName (fname); - - results.add (File (stringFName).getSiblingFile (returnedString)); - returnedString = String::empty; - - return; - } - } - else - { - DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; - - if (warnAboutOverwritingExistingFiles) - flags |= OFN_OVERWRITEPROMPT; - - if (selectMultipleFiles) - flags |= OFN_ALLOWMULTISELECT; - - if (extraInfoComponent != 0) - { - flags |= OFN_ENABLEHOOK; - - currentExtraFileWin = new FPComponentHolder(); - currentExtraFileWin->addAndMakeVisible (extraInfoComponent); - currentExtraFileWin->setSize (jlimit (20, 800, extraInfoComponent->getWidth()), - extraInfoComponent->getHeight()); - currentExtraFileWin->addToDesktop (0); - - currentExtraFileWin->enterModalState(); - } - - { - WCHAR filters [1024]; - zeromem (filters, sizeof (filters)); - filter.copyToUnicode (filters, 1024); - filter.copyToUnicode (filters + filter.length() + 1, - 1022 - filter.length()); - - OPENFILENAMEW of; - zerostruct (of); - -#ifdef OPENFILENAME_SIZE_VERSION_400W - of.lStructSize = OPENFILENAME_SIZE_VERSION_400W; -#else - of.lStructSize = sizeof (of); -#endif - of.hwndOwner = (HWND) w.getWindowHandle(); - of.lpstrFilter = filters; - of.nFilterIndex = 1; - of.lpstrFile = fname; - of.nMaxFile = numCharsAvailable; - of.lpstrInitialDir = initialDir; - of.lpstrTitle = title; - of.Flags = flags; - - if (extraInfoComponent != 0) - of.lpfnHook = &openCallback; - - if (isSaveDialogue) - { - if (! GetSaveFileName (&of)) - fname[0] = 0; - else - fnameIdx = of.nFileOffset; - } - else - { - if (! GetOpenFileName (&of)) - fname[0] = 0; - else - fnameIdx = of.nFileOffset; - } - } - } } -#if JUCE_CATCH_UNHANDLED_EXCEPTIONS - catch (...) + else { - fname[0] = 0; + DWORD flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY; + + if (warnAboutOverwritingExistingFiles) + flags |= OFN_OVERWRITEPROMPT; + + if (selectMultipleFiles) + flags |= OFN_ALLOWMULTISELECT; + + if (extraInfoComponent != 0) + { + flags |= OFN_ENABLEHOOK; + + info.customComponent = new CustomComponentHolder (extraInfoComponent); + info.customComponent->enterModalState(); + } + + WCHAR filters [1024]; + zerostruct (filters); + filter.copyToUnicode (filters, 1024); + filter.copyToUnicode (filters + filter.length() + 1, 1022 - filter.length()); + + OPENFILENAMEW of; + zerostruct (of); + + #ifdef OPENFILENAME_SIZE_VERSION_400W + of.lStructSize = OPENFILENAME_SIZE_VERSION_400W; + #else + of.lStructSize = sizeof (of); + #endif + of.hwndOwner = (HWND) parentWindow.getWindowHandle(); + of.lpstrFilter = filters; + of.nFilterIndex = 1; + of.lpstrFile = files; + of.nMaxFile = charsAvailableForResult; + of.lpstrInitialDir = info.initialPath; + of.lpstrTitle = title; + of.Flags = flags; + of.lCustData = (LPARAM) &info; + + if (extraInfoComponent != 0) + of.lpfnHook = &openCallback; + + if (! (isSaveDialogue ? GetSaveFileName (&of) + : GetOpenFileName (&of))) + return; + + filenameOffset = of.nFileOffset; } -#endif - deleteAndZero (currentExtraFileWin); - - const WCHAR* const files = fname; - - if (selectMultipleFiles && fnameIdx > 0 && files [fnameIdx - 1] == 0) + if (selectMultipleFiles && filenameOffset > 0 && files [filenameOffset - 1] == 0) { - const WCHAR* filename = files + fnameIdx; + const WCHAR* filename = files + filenameOffset; while (*filename != 0) { - const String filepath (String (files) + "\\" + String (filename)); - results.add (File (filepath)); + results.add (File (String (files) + "\\" + String (filename))); filename += CharacterFunctions::length (filename) + 1; } } else if (files[0] != 0) { - results.add (File (files)); + results.add (File (String (files))); } } diff --git a/src/native/windows/juce_win32_Midi.cpp b/src/native/windows/juce_win32_Midi.cpp index feee1c3a0c..2a136ee9ca 100644 --- a/src/native/windows/juce_win32_Midi.cpp +++ b/src/native/windows/juce_win32_Midi.cpp @@ -27,31 +27,24 @@ // compiled on its own). #if JUCE_INCLUDED_FILE + //============================================================================== -class MidiInThread : public Thread +class MidiInCollector { public: //============================================================================== - MidiInThread (MidiInput* const input_, - MidiInputCallback* const callback_) - : Thread ("Juce Midi"), - deviceHandle (0), + MidiInCollector (MidiInput* const input_, + MidiInputCallback& callback_) + : deviceHandle (0), input (input_), callback (callback_), + concatenator (4096), isStarted (false), startTime (0) { - pending.ensureSize ((int) defaultBufferSize); + } - for (int i = (int) numInHeaders; --i >= 0;) - { - zeromem (&hdr[i], sizeof (MIDIHDR)); - hdr[i].lpData = inData[i]; - hdr[i].dwBufferLength = (int) inBufferSize; - } - }; - - ~MidiInThread() + ~MidiInCollector() { stop(); @@ -69,89 +62,21 @@ public: } //============================================================================== - void handle (const uint32 message, const uint32 timeStamp) + void handleMessage (const uint32 message, const uint32 timeStamp) { - const int byte = message & 0xff; - if (byte < 0x80) - return; - - const int time = timeStampToMs (timeStamp); - + if ((message & 0xff) >= 0x80 && isStarted) { - const ScopedLock sl (lock); - pending.addEvent (&message, 3, time); + concatenator.pushMidiData (&message, 3, convertTimeStamp (timeStamp), input, callback); + writeFinishedBlocks(); } - - notify(); } void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp) { - const int time = timeStampToMs (timeStamp); - const int num = hdr->dwBytesRecorded; - - if (num > 0) + if (isStarted) { - { - const ScopedLock sl (lock); - pending.addEvent (hdr->lpData, num, time); - } - - notify(); - } - } - - void writeBlock (const int i) - { - hdr[i].dwBytesRecorded = 0; - MMRESULT res = midiInPrepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)); - jassert (res == MMSYSERR_NOERROR); - res = midiInAddBuffer (deviceHandle, &hdr[i], sizeof (MIDIHDR)); - jassert (res == MMSYSERR_NOERROR); - } - - void run() - { - MidiBuffer newEvents; - newEvents.ensureSize ((int) defaultBufferSize); - - while (! threadShouldExit()) - { - for (int i = 0; i < (int) numInHeaders; ++i) - { - if ((hdr[i].dwFlags & WHDR_DONE) != 0) - { - MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)); - (void) res; - jassert (res == MMSYSERR_NOERROR); - writeBlock (i); - } - } - - newEvents.clear(); // (resets it without freeing allocated storage) - - { - const ScopedLock sl (lock); - newEvents.swapWith (pending); - } - - //xxx needs to figure out if blocks are broken up or not - - if (newEvents.isEmpty()) - { - wait (500); - } - else - { - MidiMessage message (0xf4, 0.0); - int time; - - for (MidiBuffer::Iterator i (newEvents); i.getNextEvent (message, time);) - { - message.setTimeStamp (time * 0.001); - callback->handleIncomingMidiMessage (input, message); - } - } + concatenator.pushMidiData (hdr->lpData, hdr->dwBytesRecorded, convertTimeStamp (timeStamp), input, callback); + writeFinishedBlocks(); } } @@ -160,23 +85,22 @@ public: jassert (deviceHandle != 0); if (deviceHandle != 0 && ! isStarted) { - stop(); + activeMidiCollectors.addIfNotAlreadyThere (this); - activeMidiThreads.addIfNotAlreadyThere (this); - - int i; - for (i = 0; i < (int) numInHeaders; ++i) - writeBlock (i); + for (int i = 0; i < (int) numHeaders; ++i) + headers[i].write (deviceHandle); startTime = Time::getMillisecondCounter(); MMRESULT res = midiInStart (deviceHandle); - jassert (res == MMSYSERR_NOERROR); if (res == MMSYSERR_NOERROR) { + concatenator.reset(); isStarted = true; - pending.clear(); - startThread (6); + } + else + { + unprepareAllHeaders(); } } } @@ -185,42 +109,25 @@ public: { if (isStarted) { - stopThread (5000); - + isStarted = false; midiInReset (deviceHandle); midiInStop (deviceHandle); - - activeMidiThreads.removeValue (this); - - { const ScopedLock sl (lock); } - - for (int i = (int) numInHeaders; --i >= 0;) - { - if ((hdr[i].dwFlags & WHDR_DONE) != 0) - { - int c = 10; - while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr[i], sizeof (MIDIHDR)) == MIDIERR_STILLPLAYING) - Sleep (20); - - jassert (c >= 0); - } - } - - isStarted = false; - pending.clear(); + activeMidiCollectors.removeValue (this); + unprepareAllHeaders(); + concatenator.reset(); } } static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR midiMessage, DWORD_PTR timeStamp) { - MidiInThread* const thread = reinterpret_cast (dwInstance); + MidiInCollector* const collector = reinterpret_cast (dwInstance); - if (thread != 0 && activeMidiThreads.contains (thread)) + if (activeMidiCollectors.contains (collector)) { if (uMsg == MIM_DATA) - thread->handle ((uint32) midiMessage, (uint32) timeStamp); + collector->handleMessage ((uint32) midiMessage, (uint32) timeStamp); else if (uMsg == MIM_LONGDATA) - thread->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp); + collector->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp); } } @@ -229,24 +136,77 @@ public: HMIDIIN deviceHandle; private: - static Array activeMidiThreads; + static Array activeMidiCollectors; MidiInput* input; - MidiInputCallback* callback; - bool isStarted; + MidiInputCallback& callback; + MidiDataConcatenator concatenator; + bool volatile isStarted; uint32 startTime; - CriticalSection lock; - enum { defaultBufferSize = 8192, - numInHeaders = 32, - inBufferSize = 256 }; + class MidiHeader + { + public: + MidiHeader() + { + zerostruct (hdr); + hdr.lpData = data; + hdr.dwBufferLength = numElementsInArray (data); + } - MIDIHDR hdr [(int) numInHeaders]; - char inData [(int) numInHeaders] [(int) inBufferSize]; + void write (HMIDIIN deviceHandle) + { + hdr.dwBytesRecorded = 0; + MMRESULT res = midiInPrepareHeader (deviceHandle, &hdr, sizeof (hdr)); + res = midiInAddBuffer (deviceHandle, &hdr, sizeof (hdr)); + } - MidiBuffer pending; + void writeIfFinished (HMIDIIN deviceHandle) + { + if ((hdr.dwFlags & WHDR_DONE) != 0) + { + MMRESULT res = midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)); + (void) res; + write (deviceHandle); + } + } - int timeStampToMs (uint32 timeStamp) + void unprepare (HMIDIIN deviceHandle) + { + if ((hdr.dwFlags & WHDR_DONE) != 0) + { + int c = 10; + while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)) == MIDIERR_STILLPLAYING) + Thread::sleep (20); + + jassert (c >= 0); + } + } + + private: + MIDIHDR hdr; + char data [256]; + + MidiHeader (const MidiHeader&); + MidiHeader& operator= (const MidiHeader&); + }; + + enum { numHeaders = 32 }; + MidiHeader headers [numHeaders]; + + void writeFinishedBlocks() + { + for (int i = 0; i < (int) numHeaders; ++i) + headers[i].writeIfFinished (deviceHandle); + } + + void unprepareAllHeaders() + { + for (int i = 0; i < (int) numHeaders; ++i) + headers[i].unprepare (deviceHandle); + } + + double convertTimeStamp (uint32 timeStamp) { timeStamp += startTime; @@ -259,14 +219,14 @@ private: timeStamp = now; } - return (int) timeStamp; + return timeStamp * 0.001; } - MidiInThread (const MidiInThread&); - MidiInThread& operator= (const MidiInThread&); + MidiInCollector (const MidiInCollector&); + MidiInCollector& operator= (const MidiInCollector&); }; -Array MidiInThread::activeMidiThreads; +Array MidiInCollector::activeMidiCollectors; //============================================================================== @@ -322,18 +282,18 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call } ScopedPointer in (new MidiInput (name)); - ScopedPointer thread (new MidiInThread (in, callback)); + ScopedPointer collector (new MidiInCollector (in, *callback)); HMIDIIN h; HRESULT err = midiInOpen (&h, deviceId, - (DWORD_PTR) &MidiInThread::midiInCallback, - (DWORD_PTR) (MidiInThread*) thread, + (DWORD_PTR) &MidiInCollector::midiInCallback, + (DWORD_PTR) (MidiInCollector*) collector, CALLBACK_FUNCTION); if (err == MMSYSERR_NOERROR) { - thread->deviceHandle = h; - in->internal = thread.release(); + collector->deviceHandle = h; + in->internal = collector.release(); return in.release(); } @@ -348,17 +308,17 @@ MidiInput::MidiInput (const String& name_) MidiInput::~MidiInput() { - delete static_cast (internal); + delete static_cast (internal); } void MidiInput::start() { - static_cast (internal)->start(); + static_cast (internal)->start(); } void MidiInput::stop() { - static_cast (internal)->stop(); + static_cast (internal)->stop(); }