mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-04 03:40:07 +00:00
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.
This commit is contained in:
parent
9168728a7e
commit
d508109296
27 changed files with 1339 additions and 1376 deletions
|
|
@ -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 = "<group>"; };
|
||||
DDB94A7300C3D1F2E9E51C47 = { isa = PBXGroup; children = (
|
||||
213F0A7BF38AF6AB34414A45,
|
||||
21B2342B75097AB93CFF7E97,
|
||||
2C48BB1A286C6A63174E5798 ); name = common; sourceTree = "<group>"; };
|
||||
1004A23965A4DB0FCC441ED3 = { isa = PBXGroup; children = (
|
||||
|
|
|
|||
|
|
@ -837,6 +837,7 @@
|
|||
<File RelativePath="..\..\src\native\juce_mac_NativeCode.mm"/>
|
||||
<File RelativePath="..\..\src\native\juce_win32_NativeCode.cpp"/>
|
||||
<Filter Name="common">
|
||||
<File RelativePath="..\..\src\native\common\juce_MidiDataConcatenator.h"/>
|
||||
<File RelativePath="..\..\src\native\common\juce_posix_NamedPipe.cpp"/>
|
||||
<File RelativePath="..\..\src\native\common\juce_posix_SharedCode.h"/>
|
||||
</Filter>
|
||||
|
|
|
|||
|
|
@ -837,6 +837,7 @@
|
|||
<File RelativePath="..\..\src\native\juce_mac_NativeCode.mm"/>
|
||||
<File RelativePath="..\..\src\native\juce_win32_NativeCode.cpp"/>
|
||||
<Filter Name="common">
|
||||
<File RelativePath="..\..\src\native\common\juce_MidiDataConcatenator.h"/>
|
||||
<File RelativePath="..\..\src\native\common\juce_posix_NamedPipe.cpp"/>
|
||||
<File RelativePath="..\..\src\native\common\juce_posix_SharedCode.h"/>
|
||||
</Filter>
|
||||
|
|
|
|||
|
|
@ -839,6 +839,7 @@
|
|||
<File RelativePath="..\..\src\native\juce_mac_NativeCode.mm"/>
|
||||
<File RelativePath="..\..\src\native\juce_win32_NativeCode.cpp"/>
|
||||
<Filter Name="common">
|
||||
<File RelativePath="..\..\src\native\common\juce_MidiDataConcatenator.h"/>
|
||||
<File RelativePath="..\..\src\native\common\juce_posix_NamedPipe.cpp"/>
|
||||
<File RelativePath="..\..\src\native\common\juce_posix_SharedCode.h"/>
|
||||
</Filter>
|
||||
|
|
|
|||
|
|
@ -737,6 +737,7 @@
|
|||
<ClInclude Include="..\..\src\io\streams\juce_MemoryOutputStream.h"/>
|
||||
<ClInclude Include="..\..\src\io\streams\juce_OutputStream.h"/>
|
||||
<ClInclude Include="..\..\src\io\streams\juce_SubregionStream.h"/>
|
||||
<ClInclude Include="..\..\src\native\common\juce_MidiDataConcatenator.h"/>
|
||||
<ClInclude Include="..\..\src\native\common\juce_posix_SharedCode.h"/>
|
||||
<ClInclude Include="..\..\src\native\linux\juce_linux_NativeIncludes.h"/>
|
||||
<ClInclude Include="..\..\src\native\mac\juce_mac_CarbonViewWrapperComponent.h"/>
|
||||
|
|
|
|||
|
|
@ -2133,6 +2133,9 @@
|
|||
<ClInclude Include="..\..\src\io\streams\juce_SubregionStream.h">
|
||||
<Filter>Juce\Source\io\streams</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\native\common\juce_MidiDataConcatenator.h">
|
||||
<Filter>Juce\Source\native\common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\src\native\common\juce_posix_SharedCode.h">
|
||||
<Filter>Juce\Source\native\common</Filter>
|
||||
</ClInclude>
|
||||
|
|
|
|||
|
|
@ -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 = "<group>"; };
|
||||
DDB94A7300C3D1F2E9E51C47 = { isa = PBXGroup; children = (
|
||||
213F0A7BF38AF6AB34414A45,
|
||||
21B2342B75097AB93CFF7E97,
|
||||
2C48BB1A286C6A63174E5798 ); name = common; sourceTree = "<group>"; };
|
||||
1004A23965A4DB0FCC441ED3 = { isa = PBXGroup; children = (
|
||||
|
|
|
|||
|
|
@ -1253,6 +1253,8 @@
|
|||
<FILE id="z5G75xgix" name="juce_win32_NativeCode.cpp" compile="1" resource="0"
|
||||
file="src/native/juce_win32_NativeCode.cpp"/>
|
||||
<GROUP id="yPi7BU95s" name="common">
|
||||
<FILE id="2wc3bGX" name="juce_MidiDataConcatenator.h" compile="0" resource="0"
|
||||
file="src/native/common/juce_MidiDataConcatenator.h"/>
|
||||
<FILE id="n92kYqkP1" name="juce_posix_NamedPipe.cpp" compile="1" resource="0"
|
||||
file="src/native/common/juce_posix_NamedPipe.cpp"/>
|
||||
<FILE id="H4ShC0AEe" name="juce_posix_SharedCode.h" compile="0" resource="0"
|
||||
|
|
|
|||
|
|
@ -99,7 +99,7 @@ public:
|
|||
<< ", " << pixFormat->accumulationBufferBlueBits
|
||||
<< ", " << pixFormat->accumulationBufferAlphaBits
|
||||
<< "), full-scene AA="
|
||||
<< pixFormat->fullSceneAntiAliasingNumSamples;
|
||||
<< (int) pixFormat->fullSceneAntiAliasingNumSamples;
|
||||
|
||||
Logger::outputDebugString (formatDescription);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
1415
juce_amalgamated.cpp
1415
juce_amalgamated.cpp
File diff suppressed because it is too large
Load diff
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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;)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <AudioBufferList> 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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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<int> fullSize (0, 0, w, h);
|
||||
const Rectangle<int> 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,
|
||||
|
|
|
|||
|
|
@ -235,9 +235,7 @@ void MenuBarComponent::handleCommandMessage (int commandId)
|
|||
{
|
||||
const Point<int> mousePos (getMouseXYRelative());
|
||||
updateItemUnderMouse (mousePos.getX(), mousePos.getY());
|
||||
|
||||
if (! isCurrentlyBlockedByAnotherModalComponent())
|
||||
setOpenItem (-1);
|
||||
setOpenItem (-1);
|
||||
|
||||
if (commandId != 0 && model != 0)
|
||||
model->menuItemSelected (commandId, topLevelIndexClicked);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
148
src/native/common/juce_MidiDataConcatenator.h
Normal file
148
src/native/common/juce_MidiDataConcatenator.h
Normal file
|
|
@ -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 <const uint8*> (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<uint8*> (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__
|
||||
|
|
@ -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
|
||||
|
||||
//==============================================================================
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<MidiPortAndCallback*> 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<MidiPortAndEndpoint> portAndEndpoint;
|
||||
volatile bool active;
|
||||
|
||||
private:
|
||||
MidiInputCallback& callback;
|
||||
MidiDataConcatenator concatenator;
|
||||
};
|
||||
|
||||
static CriticalSection callbackLock;
|
||||
static Array<void*> 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 <MidiPortAndCallback*> (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 <MidiPortAndCallback> mpc (new MidiPortAndCallback (*callback));
|
||||
|
||||
ScopedPointer <MidiPortAndCallback> 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 <MidiPortAndCallback> mpc (new MidiPortAndCallback());
|
||||
ScopedPointer <MidiPortAndCallback> 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<MidiPortAndCallback*> (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<CoreMidiHelpers::MidiPortAndCallback*> (internal);
|
||||
}
|
||||
|
||||
void MidiInput::start()
|
||||
|
|
|
|||
|
|
@ -78,11 +78,7 @@ public:
|
|||
{
|
||||
const Point<int> pos (owner->relativePositionToOtherComponent (topComp, Point<int>()));
|
||||
|
||||
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];
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<Component> 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 <const WCHAR*> (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<FilePreviewComponent*> (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<File>& 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<File>& 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<WCHAR> 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<int> 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<int> 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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 <MidiInThread*> (dwInstance);
|
||||
MidiInCollector* const collector = reinterpret_cast <MidiInCollector*> (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 <void*, CriticalSection> activeMidiThreads;
|
||||
static Array <MidiInCollector*, CriticalSection> 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 <void*, CriticalSection> MidiInThread::activeMidiThreads;
|
||||
Array <MidiInCollector*, CriticalSection> MidiInCollector::activeMidiCollectors;
|
||||
|
||||
|
||||
//==============================================================================
|
||||
|
|
@ -322,18 +282,18 @@ MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const call
|
|||
}
|
||||
|
||||
ScopedPointer <MidiInput> in (new MidiInput (name));
|
||||
ScopedPointer <MidiInThread> thread (new MidiInThread (in, callback));
|
||||
ScopedPointer <MidiInCollector> 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 <MidiInThread*> (internal);
|
||||
delete static_cast <MidiInCollector*> (internal);
|
||||
}
|
||||
|
||||
void MidiInput::start()
|
||||
{
|
||||
static_cast <MidiInThread*> (internal)->start();
|
||||
static_cast <MidiInCollector*> (internal)->start();
|
||||
}
|
||||
|
||||
void MidiInput::stop()
|
||||
{
|
||||
static_cast <MidiInThread*> (internal)->stop();
|
||||
static_cast <MidiInCollector*> (internal)->stop();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue