diff --git a/build/macosx/Juce.xcodeproj/project.pbxproj b/build/macosx/Juce.xcodeproj/project.pbxproj index a01e34aabe..67c90fa56a 100644 --- a/build/macosx/Juce.xcodeproj/project.pbxproj +++ b/build/macosx/Juce.xcodeproj/project.pbxproj @@ -639,6 +639,7 @@ 8484E9D8103C95A6008B7C6C /* juce_posix_SharedCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 8484E9D6103C95A6008B7C6C /* juce_posix_SharedCode.h */; }; 8484E9D9103C95A6008B7C6C /* juce_posix_NamedPipe.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8484E9D7103C95A6008B7C6C /* juce_posix_NamedPipe.cpp */; }; 84A63C02107DF286000326FD /* juce_mac_ObjCSuffix.h in Headers */ = {isa = PBXBuildFile; fileRef = 84A63C01107DF286000326FD /* juce_mac_ObjCSuffix.h */; }; + 84D0F00C109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = 84D0F00B109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm */; }; 84F1E6E710403605006A1807 /* juce_Application.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F1E6DC10403605006A1807 /* juce_Application.cpp */; }; 84F1E6E810403605006A1807 /* juce_Application.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F1E6DD10403605006A1807 /* juce_Application.h */; }; 84F1E6E910403605006A1807 /* juce_ApplicationCommandID.h in Headers */ = {isa = PBXBuildFile; fileRef = 84F1E6DE10403605006A1807 /* juce_ApplicationCommandID.h */; }; @@ -1243,6 +1244,7 @@ 8484E9D6103C95A6008B7C6C /* juce_posix_SharedCode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_posix_SharedCode.h; path = ../../src/native/common/juce_posix_SharedCode.h; sourceTree = SOURCE_ROOT; }; 8484E9D7103C95A6008B7C6C /* juce_posix_NamedPipe.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = juce_posix_NamedPipe.cpp; path = ../../src/native/common/juce_posix_NamedPipe.cpp; sourceTree = SOURCE_ROOT; }; 84A63C01107DF286000326FD /* juce_mac_ObjCSuffix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = juce_mac_ObjCSuffix.h; sourceTree = ""; }; + 84D0F00B109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = juce_mac_CoreGraphicsContext.mm; sourceTree = ""; }; 84F1E6DC10403605006A1807 /* juce_Application.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Application.cpp; path = ../../src/application/juce_Application.cpp; sourceTree = SOURCE_ROOT; }; 84F1E6DD10403605006A1807 /* juce_Application.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_Application.h; path = ../../src/application/juce_Application.h; sourceTree = SOURCE_ROOT; }; 84F1E6DE10403605006A1807 /* juce_ApplicationCommandID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = juce_ApplicationCommandID.h; path = ../../src/application/juce_ApplicationCommandID.h; sourceTree = SOURCE_ROOT; }; @@ -1877,6 +1879,7 @@ 8484E9A9103C9595008B7C6C /* juce_mac_CarbonViewWrapperComponent.h */, 8484E9AA103C9595008B7C6C /* juce_mac_CoreAudio.cpp */, 8484E9AB103C9595008B7C6C /* juce_mac_CoreMidi.cpp */, + 84D0F00B109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm */, 8484E9AC103C9595008B7C6C /* juce_mac_Debugging.mm */, 8484E9AD103C9595008B7C6C /* juce_mac_FileChooser.mm */, 8484E9AE103C9595008B7C6C /* juce_mac_Files.mm */, @@ -4146,6 +4149,7 @@ 84816E5910809D07008FEC33 /* juce_iphone_MessageManager.mm in Sources */, 84816E5A10809D07008FEC33 /* juce_iphone_MiscUtilities.mm in Sources */, 84816E5C10809D07008FEC33 /* juce_iphone_UIViewComponentPeer.mm in Sources */, + 84D0F00C109B1546007F73A3 /* juce_mac_CoreGraphicsContext.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/extras/juce demo/build/linux/JuceDemo.make b/extras/juce demo/build/linux/JuceDemo.make index 81fc352705..9e4e36c1f7 100644 --- a/extras/juce demo/build/linux/JuceDemo.make +++ b/extras/juce demo/build/linux/JuceDemo.make @@ -40,10 +40,15 @@ endif OBJECTS := \ $(OBJDIR)/ApplicationStartup.o \ - $(OBJDIR)/juce_LibrarySource.o \ - $(OBJDIR)/MainDemoWindow.o \ $(OBJDIR)/BinaryData.o \ - $(OBJDIR)/AudioDemo.o \ + $(OBJDIR)/MainDemoWindow.o \ + $(OBJDIR)/juce_LibrarySource.o \ + $(OBJDIR)/AudioDemoLatencyPage.o \ + $(OBJDIR)/AudioDemoPlaybackPage.o \ + $(OBJDIR)/AudioDemoRecordPage.o \ + $(OBJDIR)/AudioDemoSetupPage.o \ + $(OBJDIR)/AudioDemoSynthPage.o \ + $(OBJDIR)/AudioDemoTabComponent.o \ $(OBJDIR)/CameraDemo.o \ $(OBJDIR)/DragAndDropDemo.o \ $(OBJDIR)/FontsAndTextDemo.o \ @@ -102,7 +107,7 @@ $(OBJDIR)/ApplicationStartup.o: ../../src/ApplicationStartup.cpp @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" -$(OBJDIR)/juce_LibrarySource.o: ../../src/juce_LibrarySource.cpp +$(OBJDIR)/BinaryData.o: ../../src/BinaryData.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" @@ -112,12 +117,37 @@ $(OBJDIR)/MainDemoWindow.o: ../../src/MainDemoWindow.cpp @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" -$(OBJDIR)/BinaryData.o: ../../src/BinaryData.cpp +$(OBJDIR)/juce_LibrarySource.o: ../../src/juce_LibrarySource.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + +$(OBJDIR)/AudioDemoLatencyPage.o: ../../src/demos/AudioDemoLatencyPage.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + +$(OBJDIR)/AudioDemoPlaybackPage.o: ../../src/demos/AudioDemoPlaybackPage.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + +$(OBJDIR)/AudioDemoRecordPage.o: ../../src/demos/AudioDemoRecordPage.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + +$(OBJDIR)/AudioDemoSetupPage.o: ../../src/demos/AudioDemoSetupPage.cpp + -@$(CMD_MKOBJDIR) + @echo $(notdir $<) + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + +$(OBJDIR)/AudioDemoSynthPage.o: ../../src/demos/AudioDemoSynthPage.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" -$(OBJDIR)/AudioDemo.o: ../../src/demos/AudioDemo.cpp +$(OBJDIR)/AudioDemoTabComponent.o: ../../src/demos/AudioDemoTabComponent.cpp -@$(CMD_MKOBJDIR) @echo $(notdir $<) @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" diff --git a/extras/juce demo/src/juce_AppConfig.h b/extras/juce demo/src/juce_AppConfig.h index dab6165235..aac15695b4 100644 --- a/extras/juce demo/src/juce_AppConfig.h +++ b/extras/juce demo/src/juce_AppConfig.h @@ -45,7 +45,8 @@ //#define JUCE_LOG_ASSERTIONS 1 #define JUCE_WASAPI 1 //#define JUCE_ASIO 1 -//#define JUCE_ALSA 1 +#define JUCE_ALSA 1 +#define JUCE_JACK 1 #if JUCE_WINDOWS || JUCE_IPHONE #define JUCE_QUICKTIME 0 // (This is disabled here by default because on windows it requires the QT SDK, diff --git a/juce_Config.h b/juce_Config.h index df63b1957d..3ee56b56b1 100644 --- a/juce_Config.h +++ b/juce_Config.h @@ -91,6 +91,12 @@ #define JUCE_ALSA 1 #endif +/** Comment out this macro to disable building of JACK device support on Linux. +*/ +#ifndef JUCE_JACK + #define JUCE_JACK 1 +#endif + //============================================================================= /** Comment out this macro if you don't want to enable QuickTime or if you don't have the SDK installed. diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index cbfbca5ff9..872269364f 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -243,6 +243,12 @@ #define JUCE_ALSA 1 #endif +/** Comment out this macro to disable building of JACK device support on Linux. +*/ +#ifndef JUCE_JACK + #define JUCE_JACK 1 +#endif + /** Comment out this macro if you don't want to enable QuickTime or if you don't have the SDK installed. @@ -678,6 +684,19 @@ public: #include #endif +#if JUCE_JACK + /* Got an include error here? If so, you've either not got jack-audio-connection-kit + installed, or you've not got your paths set up correctly to find its header files. + + The package you need to install to get JACK support is "libjack-dev". + + If you don't have the jack-audio-connection-kit library and don't want to build + Juce with low latency audio support, just disable the JUCE_JACK flag in juce_Config.h + */ + #include + //#include +#endif + #undef SIZEOF #endif // __JUCE_LINUX_NATIVEINCLUDES_JUCEHEADER__ @@ -23306,6 +23325,7 @@ AudioIODeviceType* juce_createAudioIODeviceType_WASAPI(); AudioIODeviceType* juce_createAudioIODeviceType_DirectSound(); AudioIODeviceType* juce_createAudioIODeviceType_ASIO(); AudioIODeviceType* juce_createAudioIODeviceType_ALSA(); +AudioIODeviceType* juce_createAudioIODeviceType_JACK(); void AudioDeviceManager::createAudioDeviceTypes (OwnedArray & list) { @@ -23331,6 +23351,10 @@ void AudioDeviceManager::createAudioDeviceTypes (OwnedArray & #if JUCE_LINUX && JUCE_ALSA list.add (juce_createAudioIODeviceType_ALSA()); #endif + + #if JUCE_LINUX && JUCE_JACK + list.add (juce_createAudioIODeviceType_JACK()); + #endif } const String AudioDeviceManager::initialise (const int numInputChannelsNeeded, @@ -75785,6 +75809,36 @@ const Colour ColourGradient::getColour (const int index) const throw() return Colour (colours [(index << 1) + 1]); } +const Colour ColourGradient::getColourAtPosition (const float position) const throw() +{ + jassert (colours.getUnchecked (0) == 0); // the first colour specified has to go at position 0 + + const int integerPos = jlimit (0, 65535, roundFloatToInt (position * 65536.0f)); + + if (integerPos <= 0 || colours.size() <= 2) + return getColour (0); + + int i = colours.size() - 2; + while (integerPos < colours.getUnchecked(i)) + i -= 2; + + if (i >= colours.size() - 2) + return Colour (colours.getUnchecked(i)); + + const int pos1 = colours.getUnchecked (i); + PixelARGB pix1 (colours.getUnchecked (i + 1)); + pix1.premultiply(); + + const int pos2 = colours.getUnchecked (i + 2); + PixelARGB pix2 (colours.getUnchecked (i + 3)); + pix2.premultiply(); + + pix1.tween (pix2, ((integerPos - pos1) << 8) / (pos2 - pos1)); + pix1.unpremultiply(); + + return Colour (pix1.getARGB()); +} + PixelARGB* ColourGradient::createLookupTable (int& numEntries) const throw() { #ifdef JUCE_DEBUG @@ -89641,6 +89695,43 @@ Image* Image::createCopy (int newWidth, int newHeight, return newImage; } +Image* Image::createCopyOfAlphaChannel() const +{ + jassert (format != SingleChannel); + + Image* const newImage = new Image (SingleChannel, imageWidth, imageHeight, false); + + if (! hasAlphaChannel()) + { + newImage->clear (0, 0, imageWidth, imageHeight, Colours::black); + } + else + { + int dls, dps; + uint8* dstData = newImage->lockPixelDataReadWrite (0, 0, imageWidth, imageHeight, dls, dps); + + int sls, sps; + const uint8* srcData = lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, sls, sps); + + for (int y = 0; y < imageHeight; ++y) + { + const PixelARGB* src = (const PixelARGB*) (srcData + y * sls); + uint8* dst = dstData + y * dls; + + for (int x = imageWidth; --x >= 0;) + { + *dst++ = src->getAlpha(); + ++src; + } + } + + releasePixelDataReadOnly (srcData); + newImage->releasePixelDataReadWrite (dstData); + } + + return newImage; +} + const Colour Image::getPixelAt (const int x, const int y) const { Colour c; @@ -255569,6 +255660,590 @@ AudioIODeviceType* juce_createAudioIODeviceType_ALSA() #endif /********* End of inlined file: juce_linux_Audio.cpp *********/ +/********* Start of inlined file: juce_linux_JackAudio.cpp *********/ +// (This file gets included by juce_linux_NativeCode.cpp, rather than being +// compiled on its own). +#ifdef JUCE_INCLUDED_FILE + +#if JUCE_JACK + +static void* juce_libjack_handle = 0; + +void* juce_load_jack_function (const char* const name) +{ + if (juce_libjack_handle == 0) + return 0; + + return dlsym (juce_libjack_handle, name); +} + +#define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \ + typedef return_type (*fn_name##_ptr_t)argument_types; \ + return_type fn_name argument_types { \ + static fn_name##_ptr_t fn = 0; \ + if (fn == 0) { fn = (fn_name##_ptr_t)juce_load_jack_function(#fn_name); } \ + if (fn) return (*fn)arguments; \ + else return 0; \ + } + +#define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \ + typedef void (*fn_name##_ptr_t)argument_types; \ + void fn_name argument_types { \ + static fn_name##_ptr_t fn = 0; \ + if (fn == 0) { fn = (fn_name##_ptr_t)juce_load_jack_function(#fn_name); } \ + if (fn) (*fn)arguments; \ + } + +JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status), (client_name, options, status)); +JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client)); +JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client)); +JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client)); +JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client)); +JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client)); +JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg)); +JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes)); +JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port)); +JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size)); +JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func)); +JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg)); +JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)); +JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port)); +JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port)); +JUCE_DECL_JACK_FUNCTION (int, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)); +JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)); +JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port)); +JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name)); + +#if JUCE_DEBUG + #define JACK_LOGGING_ENABLED 1 +#endif + +#if JACK_LOGGING_ENABLED +static void jack_Log (const String& s) +{ + puts (s); +} + +static void dumpJackErrorMessage (const jack_status_t status) throw() +{ + if (status & JackServerFailed || status & JackServerError) + jack_Log ("Unable to connect to JACK server"); + if (status & JackVersionError) + jack_Log ("Client's protocol version does not match"); + if (status & JackInvalidOption) + jack_Log ("The operation contained an invalid or unsupported option"); + if (status & JackNameNotUnique) + jack_Log ("The desired client name was not unique"); + if (status & JackNoSuchClient) + jack_Log ("Requested client does not exist"); + if (status & JackInitFailure) + jack_Log ("Unable to initialize client"); +} +#else + #define dumpJackErrorMessage(a) {} + #define jack_Log(...) {} +#endif + +#ifndef JUCE_JACK_CLIENT_NAME + #define JUCE_JACK_CLIENT_NAME "JuceJack" +#endif + +class JackAudioIODevice : public AudioIODevice +{ +public: + JackAudioIODevice (const String& deviceName, + const String& inputId_, + const String& outputId_) + : AudioIODevice (deviceName, T("JACK")), + inputId (inputId_), + outputId (outputId_), + isOpen_ (false), + callback (0), + inChans (0), + outChans (0), + totalNumberOfInputChannels (0), + totalNumberOfOutputChannels (0) + { + jassert (deviceName.isNotEmpty()); + + jack_status_t status; + client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status); + + if (client == 0) + { + dumpJackErrorMessage (status); + } + else + { + juce::jack_set_error_function (errorCallback); + + // open input ports + const StringArray inputChannels (getInputChannelNames()); + for (int i = 0; i < inputChannels.size(); i++) + { + String inputName; + inputName << "in_" << (++totalNumberOfInputChannels); + + inputPorts.add (juce::jack_port_register (client, (const char*) inputName, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)); + } + + // open output ports + const StringArray outputChannels (getOutputChannelNames()); + for (int i = 0; i < outputChannels.size (); i++) + { + String outputName; + outputName << "out_" << (++totalNumberOfOutputChannels); + + outputPorts.add (juce::jack_port_register (client, (const char*) outputName, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)); + } + + inChans = (float**) juce_calloc (sizeof (float*) * (totalNumberOfInputChannels + 2)); + outChans = (float**) juce_calloc (sizeof (float*) * (totalNumberOfOutputChannels + 2)); + } + } + + ~JackAudioIODevice() + { + close(); + if (client != 0) + { + juce::jack_client_close (client); + client = 0; + } + + juce_free (inChans); + juce_free (outChans); + } + + const StringArray getChannelNames (bool forInput) const + { + StringArray names; + const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ + forInput ? JackPortIsInput : JackPortIsOutput); + + if (ports != 0) + { + int j = 0; + while (ports[j] != 0) + { + const String portName (ports [j++]); + + if (portName.upToFirstOccurrenceOf (T(":"), false, false) == getName()) + names.add (portName.fromFirstOccurrenceOf (T(":"), false, false)); + } + + free (ports); + } + + return names; + } + + const StringArray getOutputChannelNames() { return getChannelNames (false); } + const StringArray getInputChannelNames() { return getChannelNames (true); } + int getNumSampleRates() { return client != 0 ? 1 : 0; } + double getSampleRate (int index) { return client != 0 ? juce::jack_get_sample_rate (client) : 0; } + int getNumBufferSizesAvailable() { return client != 0 ? 1 : 0; } + int getBufferSizeSamples (int index) { return getDefaultBufferSize(); } + int getDefaultBufferSize() { return client != 0 ? juce::jack_get_buffer_size (client) : 0; } + + const String open (const BitArray& inputChannels, const BitArray& outputChannels, + double sampleRate, int bufferSizeSamples) + { + if (client == 0) + { + lastError = T("No JACK client running"); + return lastError; + } + + lastError = String::empty; + close(); + + juce::jack_set_process_callback (client, processCallback, this); + juce::jack_on_shutdown (client, shutdownCallback, this); + juce::jack_activate (client); + isOpen_ = true; + + if (! inputChannels.isEmpty()) + { + const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsOutput); + + if (ports != 0) + { + const int numInputChannels = inputChannels.getHighestBit () + 1; + + for (int i = 0; i < numInputChannels; ++i) + { + const String portName (ports[i]); + + if (inputChannels[i] && portName.upToFirstOccurrenceOf (T(":"), false, false) == getName()) + { + int error = juce::jack_connect (client, ports[i], juce::jack_port_name ((jack_port_t*) inputPorts[i])); + if (error != 0) + jack_Log ("Cannot connect input port " + String (i) + " (" + String (ports[i]) + "), error " + String (error)); + } + } + + free (ports); + } + } + + if (! outputChannels.isEmpty()) + { + const char** const ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsInput); + + if (ports != 0) + { + const int numOutputChannels = outputChannels.getHighestBit () + 1; + + for (int i = 0; i < numOutputChannels; ++i) + { + const String portName (ports[i]); + + if (outputChannels[i] && portName.upToFirstOccurrenceOf (T(":"), false, false) == getName()) + { + int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i]), ports[i]); + if (error != 0) + jack_Log ("Cannot connect output port " + String (i) + " (" + String (ports[i]) + "), error " + String (error)); + } + } + + free (ports); + } + } + + return lastError; + } + + void close() + { + stop(); + + if (client != 0) + { + juce::jack_deactivate (client); + juce::jack_set_process_callback (client, processCallback, 0); + juce::jack_on_shutdown (client, shutdownCallback, 0); + } + + isOpen_ = false; + } + + void start (AudioIODeviceCallback* newCallback) + { + if (isOpen_ && newCallback != callback) + { + if (newCallback != 0) + newCallback->audioDeviceAboutToStart (this); + + AudioIODeviceCallback* const oldCallback = callback; + + { + const ScopedLock sl (callbackLock); + callback = newCallback; + } + + if (oldCallback != 0) + oldCallback->audioDeviceStopped(); + } + } + + void stop() + { + start (0); + } + + bool isOpen() { return isOpen_; } + bool isPlaying() { return callback != 0; } + int getCurrentBufferSizeSamples() { return getBufferSizeSamples (0); } + double getCurrentSampleRate() { return getSampleRate (0); } + int getCurrentBitDepth() { return 32; } + const String getLastError() { return lastError; } + + const BitArray getActiveOutputChannels() const + { + BitArray outputBits; + + for (int i = 0; i < outputPorts.size(); i++) + if (juce::jack_port_connected ((jack_port_t*) outputPorts [i])) + outputBits.setBit (i); + + return outputBits; + } + + const BitArray getActiveInputChannels() const + { + BitArray inputBits; + + for (int i = 0; i < inputPorts.size(); i++) + if (juce::jack_port_connected ((jack_port_t*) inputPorts [i])) + inputBits.setBit (i); + + return inputBits; + } + + int getOutputLatencyInSamples() + { + int latency = 0; + + for (int i = 0; i < outputPorts.size(); i++) + latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) outputPorts [i])); + + return latency; + } + + int getInputLatencyInSamples() + { + int latency = 0; + + for (int i = 0; i < inputPorts.size(); i++) + latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) inputPorts [i])); + + return latency; + } + + String inputId, outputId; + +private: + void process (const int numSamples) + { + int i, numActiveInChans = 0, numActiveOutChans = 0; + + for (i = 0; i < totalNumberOfInputChannels; ++i) + { + jack_default_audio_sample_t* in + = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), numSamples); + + if (in != 0) + inChans [numActiveInChans++] = (float*) in; + } + + for (i = 0; i < totalNumberOfOutputChannels; ++i) + { + jack_default_audio_sample_t* out + = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), numSamples); + + if (out != 0) + outChans [numActiveOutChans++] = (float*) out; + } + + const ScopedLock sl (callbackLock); + + if (callback != 0) + { + callback->audioDeviceIOCallback ((const float**) inChans, numActiveInChans, + outChans, numActiveOutChans, numSamples); + } + else + { + for (i = 0; i < numActiveOutChans; ++i) + zeromem (outChans[i], sizeof (float) * numSamples); + } + } + + static int processCallback (jack_nframes_t nframes, void* callbackArgument) + { + if (callbackArgument != 0) + ((JackAudioIODevice*) callbackArgument)->process (nframes); + + return 0; + } + + static void threadInitCallback (void* callbackArgument) + { + jack_Log ("JackAudioIODevice::initialise"); + } + + static void shutdownCallback (void* callbackArgument) + { + jack_Log ("JackAudioIODevice::shutdown"); + + JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument; + + if (device != 0) + { + device->client = 0; + device->close(); + } + } + + static void errorCallback (const char* msg) + { + jack_Log ("JackAudioIODevice::errorCallback " + String (msg)); + } + + bool isOpen_; + jack_client_t* client; + String lastError; + AudioIODeviceCallback* callback; + CriticalSection callbackLock; + + float** inChans; + float** outChans; + int totalNumberOfInputChannels; + int totalNumberOfOutputChannels; + VoidArray inputPorts, outputPorts; +}; + +class JackAudioIODeviceType : public AudioIODeviceType +{ +public: + + JackAudioIODeviceType() + : AudioIODeviceType (T("JACK")), + hasScanned (false) + { + } + + ~JackAudioIODeviceType() + { + } + + void scanForDevices() + { + hasScanned = true; + inputNames.clear(); + inputIds.clear(); + outputNames.clear(); + outputIds.clear(); + + if (juce_libjack_handle == 0) + { + juce_libjack_handle = dlopen ("libjack.so", RTLD_LAZY); + + if (juce_libjack_handle == 0) + return; + } + + // open a dummy client + jack_status_t status; + jack_client_t* client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status); + + if (client == 0) + { + dumpJackErrorMessage (status); + } + else + { + // scan for output devices + const char** ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsOutput); + + if (ports != 0) + { + int j = 0; + while (ports[j] != 0) + { + String clientName (ports[j]); + clientName = clientName.upToFirstOccurrenceOf (T(":"), false, false); + + if (clientName != String (JUCE_JACK_CLIENT_NAME) + && ! inputNames.contains (clientName)) + { + inputNames.add (clientName); + inputIds.add (ports [j]); + } + + ++j; + } + + free (ports); + } + + // scan for input devices + ports = juce::jack_get_ports (client, 0, 0, /* JackPortIsPhysical | */ JackPortIsInput); + + if (ports != 0) + { + int j = 0; + while (ports[j] != 0) + { + String clientName (ports[j]); + clientName = clientName.upToFirstOccurrenceOf (T(":"), false, false); + + if (clientName != String (JUCE_JACK_CLIENT_NAME) + && ! outputNames.contains (clientName)) + { + outputNames.add (clientName); + outputIds.add (ports [j]); + } + + ++j; + } + + free (ports); + } + + juce::jack_client_close (client); + } + } + + const StringArray getDeviceNames (const bool wantInputNames) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + return wantInputNames ? inputNames : outputNames; + } + + int getDefaultDeviceIndex (const bool forInput) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + return 0; + } + + bool hasSeparateInputsAndOutputs() const { return true; } + + int getIndexOfDevice (AudioIODevice* device, const bool asInput) const + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + JackAudioIODevice* const d = dynamic_cast (device); + if (d == 0) + return -1; + + return asInput ? inputIds.indexOf (d->inputId) + : outputIds.indexOf (d->outputId); + } + + AudioIODevice* createDevice (const String& outputDeviceName, + const String& inputDeviceName) + { + jassert (hasScanned); // need to call scanForDevices() before doing this + + const int inputIndex = inputNames.indexOf (inputDeviceName); + const int outputIndex = outputNames.indexOf (outputDeviceName); + + if (inputIndex >= 0 || outputIndex >= 0) + return new JackAudioIODevice (outputIndex >= 0 ? outputDeviceName + : inputDeviceName, + inputIds [inputIndex], + outputIds [outputIndex]); + + return 0; + } + + juce_UseDebuggingNewOperator + +private: + StringArray inputNames, outputNames, inputIds, outputIds; + bool hasScanned; + + JackAudioIODeviceType (const JackAudioIODeviceType&); + const JackAudioIODeviceType& operator= (const JackAudioIODeviceType&); +}; + +AudioIODeviceType* juce_createAudioIODeviceType_JACK() +{ + return new JackAudioIODeviceType(); +} + +#else // if JACK is turned off.. + +AudioIODeviceType* juce_createAudioIODeviceType_JACK() { return 0; } + +#endif +#endif +/********* End of inlined file: juce_linux_JackAudio.cpp *********/ + /********* Start of inlined file: juce_linux_Midi.cpp *********/ // (This file gets included by juce_linux_NativeCode.cpp, rather than being // compiled on its own). @@ -259054,15 +259729,19 @@ public: lineStride, pixelStride); CGDataProviderRef provider = CGDataProviderCreateWithData (0, imageData, lineStride * pixelStride, 0); + CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); - imageRef = CGImageCreate (width, height, + CGImageRef imageRef = CGImageCreate (width, height, 8, pixelStride * 8, lineStride, - CGColorSpaceCreateDeviceRGB(), + colourSpace, hasAlpha ? (kCGImageAlphaFirst | kCGBitmapByteOrder32Little) : kCGBitmapByteOrderDefault, provider, 0, true, kCGRenderingIntentDefault); + CGColorSpaceRelease (colourSpace); + CGDataProviderRelease (provider); + juceImage.releasePixelDataReadWrite (imageData); uiImage = [[UIImage imageWithCGImage: imageRef] retain]; @@ -259071,7 +259750,7 @@ public: ~JuceUIImage() { [uiImage release]; - CFRelease (imageRef); + CGImageRelease (imageRef); } Image& getJuceImage() throw() { return juceImage; } @@ -262391,11 +263070,497 @@ MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) #else +/********* Start of inlined file: juce_mac_CoreGraphicsContext.mm *********/ +// (This file gets included by juce_mac_NativeCode.mm, rather than being +// compiled on its own). +#if JUCE_INCLUDED_FILE + +class CoreGraphicsContext : public LowLevelGraphicsContext +{ +public: + CoreGraphicsContext (CGContextRef context_, const float flipHeight_) + : context (context_), + flipHeight (flipHeight_) + { + CGContextRetain (context); + } + + ~CoreGraphicsContext() + { + CGContextRelease (context); + } + + bool isVectorDevice() const { return false; } + + void setOrigin (int x, int y) + { + CGContextTranslateCTM (context, x, -y); + } + + bool reduceClipRegion (int x, int y, int w, int h) + { + CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + return ! isClipEmpty(); + } + + bool reduceClipRegion (const RectangleList& clipRegion) + { + const int numRects = clipRegion.getNumRectangles(); + CGRect* const rects = new CGRect [numRects]; + for (int i = 0; i < numRects; ++i) + { + const Rectangle& r = clipRegion.getRectangle(i); + rects[i] = CGRectMake (r.getX(), flipHeight - r.getBottom(), r.getWidth(), r.getHeight()); + } + + CGContextClipToRects (context, rects, numRects); + delete[] rects; + + return ! isClipEmpty(); + } + + void excludeClipRegion (int x, int y, int w, int h) + { + RectangleList r (getClipBounds()); + r.subtract (Rectangle (x, y, w, h)); + reduceClipRegion (r); + } + + void saveState() + { + CGContextSaveGState (context); + } + + void restoreState() + { + CGContextRestoreGState (context); + } + + bool clipRegionIntersects (int x, int y, int w, int h) + { + return getClipBounds().intersects (Rectangle (x, y, w, h)); + } + + const Rectangle getClipBounds() const + { + CGRect bounds = CGRectIntegral (CGContextGetClipBoundingBox (context)); + + return Rectangle (roundFloatToInt (bounds.origin.x), + roundFloatToInt (flipHeight - (bounds.origin.y + bounds.size.height)), + roundFloatToInt (bounds.size.width), + roundFloatToInt (bounds.size.height)); + } + + bool isClipEmpty() const + { + return CGRectIsEmpty (CGContextGetClipBoundingBox (context)); + } + + void fillRectWithColour (int x, int y, int w, int h, const Colour& colour, const bool replaceExistingContents) + { + CGContextSetBlendMode (context, replaceExistingContents ? kCGBlendModeCopy : kCGBlendModeNormal); + CGContextSetAlpha (context, 1.0f); + setColour (colour); + CGContextFillRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + } + + void fillRectWithGradient (int x, int y, int w, int h, const ColourGradient& gradient) + { + CGContextSaveGState (context); + CGContextClipToRect (context, CGRectMake (x, flipHeight - (y + h), w, h)); + flip(); + drawGradient (gradient); + CGContextRestoreGState (context); + } + + void fillPathWithColour (const Path& path, const AffineTransform& transform, const Colour& colour, EdgeTable::OversamplingLevel /*quality*/) + { + CGContextSetAlpha (context, 1.0f); + CGContextSaveGState (context); + flip(); + applyTransform (transform); + createPath (path); + setColour (colour); + CGContextSetBlendMode (context, kCGBlendModeNormal); + CGContextFillPath (context); + CGContextRestoreGState (context); + } + + void fillPathWithGradient (const Path& path, const AffineTransform& transform, const ColourGradient& gradient, EdgeTable::OversamplingLevel quality) + { + CGContextSaveGState (context); + createPath (path, transform); + CGContextClip (context); + flip(); + applyTransform (gradient.transform); + drawGradient (gradient); + CGContextRestoreGState (context); + } + + void fillPathWithImage (const Path& path, const AffineTransform& transform, + const Image& image, int imageX, int imageY, float alpha, EdgeTable::OversamplingLevel /*quality*/) + { + CGContextSaveGState (context); + createPath (path, transform); + CGContextClip (context); + blendImage (image, imageX, imageY, image.getWidth(), image.getHeight(), 0, 0, alpha); + CGContextRestoreGState (context); + } + + void fillAlphaChannelWithColour (const Image& alphaImage, int alphaImageX, int alphaImageY, const Colour& colour) + { + Image* singleChannelImage = createAlphaChannelImage (alphaImage); + CGImageRef image = createImage (*singleChannelImage, true); + + CGContextSetAlpha (context, 1.0f); + CGContextSaveGState (context); + CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), + alphaImage.getWidth(), alphaImage.getHeight()); + CGContextClipToMask (context, r, image); + setColour (colour); + CGContextSetBlendMode (context, kCGBlendModeNormal); + CGContextFillRect (context, r); + CGContextRestoreGState (context); + + CGImageRelease (image); + deleteAlphaChannelImage (alphaImage, singleChannelImage); + } + + void fillAlphaChannelWithGradient (const Image& alphaImage, int alphaImageX, int alphaImageY, const ColourGradient& gradient) + { + Image* singleChannelImage = createAlphaChannelImage (alphaImage); + CGImageRef image = createImage (*singleChannelImage, true); + + CGContextSaveGState (context); + CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), + alphaImage.getWidth(), alphaImage.getHeight()); + CGContextClipToMask (context, r, image); + flip(); + drawGradient (gradient); + CGContextRestoreGState (context); + + CGImageRelease (image); + deleteAlphaChannelImage (alphaImage, singleChannelImage); + } + + void fillAlphaChannelWithImage (const Image& alphaImage, int alphaImageX, int alphaImageY, + const Image& fillerImage, int fillerImageX, int fillerImageY, float alpha) + { + Image* singleChannelImage = createAlphaChannelImage (alphaImage); + CGImageRef image = createImage (*singleChannelImage, true); + + CGContextSaveGState (context); + CGRect r = CGRectMake (alphaImageX, flipHeight - (alphaImageY + alphaImage.getHeight()), + alphaImage.getWidth(), alphaImage.getHeight()); + CGContextClipToMask (context, r, image); + + blendImage (fillerImage, fillerImageX, fillerImageY, + fillerImage.getWidth(), fillerImage.getHeight(), + 0, 0, alpha); + + CGContextRestoreGState (context); + + CGImageRelease (image); + deleteAlphaChannelImage (alphaImage, singleChannelImage); + } + + void blendImage (const Image& sourceImage, + int destX, int destY, int destW, int destH, int sourceX, int sourceY, + float alpha) + { + CGContextSetBlendMode (context, kCGBlendModeNormal); + CGImageRef image = createImage (sourceImage, false); + + CGContextSaveGState (context); + CGContextClipToRect (context, CGRectMake (destX, flipHeight - (destY + destH), destW, destH)); + CGContextSetAlpha (context, alpha); + CGContextDrawImage (context, CGRectMake (destX - sourceX, + flipHeight - ((destY - sourceY) + sourceImage.getHeight()), + sourceImage.getWidth(), + sourceImage.getHeight()), image); + + CGContextRestoreGState (context); + CGImageRelease (image); + } + + void blendImageRescaling (const Image& sourceImage, + int dx, int dy, int dw, int dh, + int sx, int sy, int sw, int sh, + float alpha, const Graphics::ResamplingQuality quality) + { + if (sw > 0 && sh > 0) + { + if (sw == dw && sh == dh) + { + blendImage (sourceImage, + dx, dy, dw, dh, + sx, sy, alpha); + } + else + { + blendImageWarping (sourceImage, + sx, sy, sw, sh, + AffineTransform::translation ((float) -sx, + (float) -sy) + .scaled (dw / (float) sw, + dh / (float) sh) + .translated ((float) dx, + (float) dy), + alpha, + quality); + } + } + } + + void blendImageWarping (const Image& sourceImage, + int srcClipX, int srcClipY, int srcClipW, int srcClipH, + const AffineTransform& transform, + float alpha, const Graphics::ResamplingQuality quality) + { + CGContextSetBlendMode (context, kCGBlendModeNormal); + CGImageRef fullImage = createImage (sourceImage, false); + CGImageRef image = CGImageCreateWithImageInRect (fullImage, CGRectMake (srcClipX, sourceImage.getHeight() - (srcClipY + srcClipH), + srcClipW, srcClipH)); + CGImageRelease (fullImage); + + CGContextSaveGState (context); + CGContextSetAlpha (context, alpha); + flip(); + applyTransform (AffineTransform::scale (1.0f, -1.0f).translated (0, sourceImage.getHeight()).followedBy (transform)); + + CGContextSetInterpolationQuality (context, quality == Graphics::lowResamplingQuality + ? kCGInterpolationLow + : kCGInterpolationHigh); + + CGContextDrawImage (context, CGRectMake (0, 0, sourceImage.getWidth(), + sourceImage.getHeight()), image); + + CGImageRelease (image); + CGContextRestoreGState (context); + } + + void drawLine (double x1, double y1, double x2, double y2, const Colour& colour) + { + CGContextSetAlpha (context, 1.0f); + CGContextSetRGBStrokeColor (context, colour.getFloatRed(), colour.getFloatGreen(), + colour.getFloatBlue(), colour.getFloatAlpha()); + CGContextSetLineCap (context, kCGLineCapSquare); + CGContextSetLineWidth (context, 1.0f); + + CGPoint line[] = { { x1 + 0.5f, flipHeight - (y1 + 0.5f) }, + { x2 + 0.5f, flipHeight - (y2 + 0.5f) } }; + CGContextStrokeLineSegments (context, line, 1); + } + + void drawVerticalLine (const int x, double top, double bottom, const Colour& colour) + { + setColour (colour); + CGContextSetBlendMode (context, kCGBlendModeNormal); + CGContextFillRect (context, CGRectMake (x, flipHeight - bottom, 1.0f, bottom - top)); + } + + void drawHorizontalLine (const int y, double left, double right, const Colour& colour) + { + setColour (colour); + CGContextSetBlendMode (context, kCGBlendModeNormal); + CGContextFillRect (context, CGRectMake (left, y, right - left, 1.0f)); + } + +private: + CGContextRef context; + const float flipHeight; + + void setColour (const Colour& colour) const throw() + { + CGContextSetRGBFillColor (context, + colour.getFloatRed(), colour.getFloatGreen(), + colour.getFloatBlue(), colour.getFloatAlpha()); + } + + static void gradientCallback (void* info, const CGFloat* inData, CGFloat* outData) + { + const ColourGradient* const g = (const ColourGradient*) info; + const Colour c (g->getColourAtPosition (inData[0])); + outData[0] = c.getFloatRed(); + outData[1] = c.getFloatGreen(); + outData[2] = c.getFloatBlue(); + outData[3] = c.getFloatAlpha(); + } + + CGShadingRef createGradient (const ColourGradient& gradient) const throw() + { + CGShadingRef result = 0; + CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); + + CGFunctionCallbacks callbacks = { 0, gradientCallback, 0 }; + CGFunctionRef function = CGFunctionCreate ((void*) &gradient, 1, 0, 4, 0, &callbacks); + CGPoint p1 = CGPointMake (gradient.x1, gradient.y1); + + if (gradient.isRadial) + { + result = CGShadingCreateRadial (colourSpace, + p1, 0, + p1, hypotf (gradient.x1 - gradient.x2, gradient.y1 - gradient.y2), + function, true, true); + } + else + { + result = CGShadingCreateAxial (colourSpace, p1, + CGPointMake (gradient.x2, gradient.y2), + function, true, true); + } + + CGColorSpaceRelease (colourSpace); + CGFunctionRelease (function); + return result; + } + + void drawGradient (const ColourGradient& gradient) const throw() + { + CGContextSetBlendMode (context, kCGBlendModeNormal); + CGContextSetAlpha (context, 1.0f); + CGShadingRef shading = createGradient (gradient); + CGContextDrawShading (context, shading); + CGShadingRelease (shading); + } + + void createPath (const Path& path) const throw() + { + CGContextBeginPath (context); + Path::Iterator i (path); + + while (i.next()) + { + switch (i.elementType) + { + case Path::Iterator::startNewSubPath: + CGContextMoveToPoint (context, i.x1, i.y1); + break; + case Path::Iterator::lineTo: + CGContextAddLineToPoint (context, i.x1, i.y1); + break; + case Path::Iterator::quadraticTo: + CGContextAddQuadCurveToPoint (context, i.x1, i.y1, i.x2, i.y2); + break; + case Path::Iterator::cubicTo: + CGContextAddCurveToPoint (context, i.x1, i.y1, i.x2, i.y2, i.x3, i.y3); + break; + case Path::Iterator::closePath: + CGContextClosePath (context); break; + default: + jassertfalse + break; + } + } + } + + void createPath (const Path& path, const AffineTransform& transform) const throw() + { + CGContextBeginPath (context); + Path::Iterator i (path); + + while (i.next()) + { + switch (i.elementType) + { + case Path::Iterator::startNewSubPath: + transform.transformPoint (i.x1, i.y1); + CGContextMoveToPoint (context, i.x1, flipHeight - i.y1); + break; + case Path::Iterator::lineTo: + transform.transformPoint (i.x1, i.y1); + CGContextAddLineToPoint (context, i.x1, flipHeight - i.y1); + break; + case Path::Iterator::quadraticTo: + transform.transformPoint (i.x1, i.y1); + transform.transformPoint (i.x2, i.y2); + CGContextAddQuadCurveToPoint (context, i.x1, flipHeight - i.y1, i.x2, flipHeight - i.y2); + break; + case Path::Iterator::cubicTo: + transform.transformPoint (i.x1, i.y1); + transform.transformPoint (i.x2, i.y2); + transform.transformPoint (i.x3, i.y3); + CGContextAddCurveToPoint (context, i.x1, flipHeight - i.y1, i.x2, flipHeight - i.y2, i.x3, flipHeight - i.y3); + break; + case Path::Iterator::closePath: + CGContextClosePath (context); break; + default: + jassertfalse + break; + } + } + } + + CGImageRef createImage (const Image& juceImage, const bool forAlpha) const throw() + { + int lineStride = 0; + int pixelStride = 0; + const uint8* imageData = juceImage.lockPixelDataReadOnly (0, 0, juceImage.getWidth(), juceImage.getHeight(), + lineStride, pixelStride); + + CGDataProviderRef provider = CGDataProviderCreateWithData (0, imageData, lineStride * pixelStride, 0); + + CGColorSpaceRef colourSpace = forAlpha ? CGColorSpaceCreateDeviceGray() + : CGColorSpaceCreateDeviceRGB(); + + CGImageRef imageRef = CGImageCreate (juceImage.getWidth(), juceImage.getHeight(), + 8, pixelStride * 8, lineStride, + colourSpace, + (juceImage.hasAlphaChannel() && ! forAlpha) + ? (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Little) + : kCGBitmapByteOrderDefault, + provider, + 0, true, kCGRenderingIntentDefault); + + CGColorSpaceRelease (colourSpace); + CGDataProviderRelease (provider); + + juceImage.releasePixelDataReadOnly (imageData); + return imageRef; + } + + static Image* createAlphaChannelImage (const Image& im) throw() + { + if (im.getFormat() == Image::SingleChannel) + return const_cast (&im); + + return im.createCopyOfAlphaChannel(); + } + + static void deleteAlphaChannelImage (const Image& im, Image* const alphaIm) throw() + { + if (im.getFormat() != Image::SingleChannel) + delete alphaIm; + } + + void flip() const throw() + { + CGContextConcatCTM (context, CGAffineTransformMake (1, 0, 0, -1, 0, flipHeight)); + } + + void applyTransform (const AffineTransform& transform) const throw() + { + CGAffineTransform t; + t.a = transform.mat00; + t.b = transform.mat10; + t.c = transform.mat01; + t.d = transform.mat11; + t.tx = transform.mat02; + t.ty = transform.mat12; + CGContextConcatCTM (context, t); + } +}; + +#endif +/********* End of inlined file: juce_mac_CoreGraphicsContext.mm *********/ + /********* Start of inlined file: juce_mac_NSViewComponentPeer.mm *********/ // (This file gets included by juce_mac_NativeCode.mm, rather than being // compiled on its own). #if JUCE_INCLUDED_FILE +#define USE_COREGRAPHICS_RENDERING 1 + class NSViewComponentPeer; END_JUCE_NAMESPACE @@ -263780,6 +264945,11 @@ void NSViewComponentPeer::drawRect (NSRect r) if (r.size.width < 1.0f || r.size.height < 1.0f) return; +#if USE_COREGRAPHICS_RENDERING + CoreGraphicsContext context ((CGContextRef) [[NSGraphicsContext currentContext] graphicsPort], + [view frame].size.height); + handlePaint (context); +#else const float y = [view frame].size.height - (r.origin.y + r.size.height); JuceNSImage temp ((int) (r.size.width + 0.5f), @@ -263812,6 +264982,7 @@ void NSViewComponentPeer::drawRect (NSRect r) temp.draw (r.origin.x, r.origin.y, clip, originX, originY); } +#endif } bool NSViewComponentPeer::canBecomeKeyWindow() diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 17accbaac4..b12faea0eb 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -277,6 +277,12 @@ #define JUCE_ALSA 1 #endif +/** Comment out this macro to disable building of JACK device support on Linux. +*/ +#ifndef JUCE_JACK + #define JUCE_JACK 1 +#endif + /** Comment out this macro if you don't want to enable QuickTime or if you don't have the SDK installed. @@ -38755,6 +38761,11 @@ public: */ const Colour getColour (const int index) const throw(); + /** Returns the an interpolated colour at any position along the gradient. + @param position the position along the gradient, between 0 and 1 + */ + const Colour getColourAtPosition (const float position) const throw(); + /** Creates a set of interpolated premultiplied ARGB values. The caller must delete the array that is returned using juce_free(). @@ -38975,6 +38986,10 @@ public: int newHeight = -1, const Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const; + /** Returns a new single-channel image which is a copy of the alpha-channel of this image. + */ + virtual Image* createCopyOfAlphaChannel() const; + /** Returns the colour of one of the pixels in the image. If the co-ordinates given are beyond the image's boundaries, this will diff --git a/src/audio/devices/juce_AudioDeviceManager.cpp b/src/audio/devices/juce_AudioDeviceManager.cpp index 4d105a343b..d7a4fb39dc 100644 --- a/src/audio/devices/juce_AudioDeviceManager.cpp +++ b/src/audio/devices/juce_AudioDeviceManager.cpp @@ -112,6 +112,7 @@ AudioIODeviceType* juce_createAudioIODeviceType_WASAPI(); AudioIODeviceType* juce_createAudioIODeviceType_DirectSound(); AudioIODeviceType* juce_createAudioIODeviceType_ASIO(); AudioIODeviceType* juce_createAudioIODeviceType_ALSA(); +AudioIODeviceType* juce_createAudioIODeviceType_JACK(); void AudioDeviceManager::createAudioDeviceTypes (OwnedArray & list) { @@ -137,6 +138,10 @@ void AudioDeviceManager::createAudioDeviceTypes (OwnedArray & #if JUCE_LINUX && JUCE_ALSA list.add (juce_createAudioIODeviceType_ALSA()); #endif + + #if JUCE_LINUX && JUCE_JACK + list.add (juce_createAudioIODeviceType_JACK()); + #endif } //============================================================================== diff --git a/src/gui/graphics/colour/juce_ColourGradient.cpp b/src/gui/graphics/colour/juce_ColourGradient.cpp index dc16e92211..232a91931a 100644 --- a/src/gui/graphics/colour/juce_ColourGradient.cpp +++ b/src/gui/graphics/colour/juce_ColourGradient.cpp @@ -112,6 +112,36 @@ const Colour ColourGradient::getColour (const int index) const throw() return Colour (colours [(index << 1) + 1]); } +const Colour ColourGradient::getColourAtPosition (const float position) const throw() +{ + jassert (colours.getUnchecked (0) == 0); // the first colour specified has to go at position 0 + + const int integerPos = jlimit (0, 65535, roundFloatToInt (position * 65536.0f)); + + if (integerPos <= 0 || colours.size() <= 2) + return getColour (0); + + int i = colours.size() - 2; + while (integerPos < colours.getUnchecked(i)) + i -= 2; + + if (i >= colours.size() - 2) + return Colour (colours.getUnchecked(i)); + + const int pos1 = colours.getUnchecked (i); + PixelARGB pix1 (colours.getUnchecked (i + 1)); + pix1.premultiply(); + + const int pos2 = colours.getUnchecked (i + 2); + PixelARGB pix2 (colours.getUnchecked (i + 3)); + pix2.premultiply(); + + pix1.tween (pix2, ((integerPos - pos1) << 8) / (pos2 - pos1)); + pix1.unpremultiply(); + + return Colour (pix1.getARGB()); +} + //============================================================================== PixelARGB* ColourGradient::createLookupTable (int& numEntries) const throw() { diff --git a/src/gui/graphics/colour/juce_ColourGradient.h b/src/gui/graphics/colour/juce_ColourGradient.h index 348deb9f69..41d4e67643 100644 --- a/src/gui/graphics/colour/juce_ColourGradient.h +++ b/src/gui/graphics/colour/juce_ColourGradient.h @@ -116,6 +116,11 @@ public: */ const Colour getColour (const int index) const throw(); + /** Returns the an interpolated colour at any position along the gradient. + @param position the position along the gradient, between 0 and 1 + */ + const Colour getColourAtPosition (const float position) const throw(); + //============================================================================== /** Creates a set of interpolated premultiplied ARGB values. diff --git a/src/gui/graphics/imaging/juce_Image.cpp b/src/gui/graphics/imaging/juce_Image.cpp index 82ad0acfb3..32aa73df66 100644 --- a/src/gui/graphics/imaging/juce_Image.cpp +++ b/src/gui/graphics/imaging/juce_Image.cpp @@ -218,6 +218,43 @@ Image* Image::createCopy (int newWidth, int newHeight, return newImage; } +Image* Image::createCopyOfAlphaChannel() const +{ + jassert (format != SingleChannel); + + Image* const newImage = new Image (SingleChannel, imageWidth, imageHeight, false); + + if (! hasAlphaChannel()) + { + newImage->clear (0, 0, imageWidth, imageHeight, Colours::black); + } + else + { + int dls, dps; + uint8* dstData = newImage->lockPixelDataReadWrite (0, 0, imageWidth, imageHeight, dls, dps); + + int sls, sps; + const uint8* srcData = lockPixelDataReadOnly (0, 0, imageWidth, imageHeight, sls, sps); + + for (int y = 0; y < imageHeight; ++y) + { + const PixelARGB* src = (const PixelARGB*) (srcData + y * sls); + uint8* dst = dstData + y * dls; + + for (int x = imageWidth; --x >= 0;) + { + *dst++ = src->getAlpha(); + ++src; + } + } + + releasePixelDataReadOnly (srcData); + newImage->releasePixelDataReadWrite (dstData); + } + + return newImage; +} + //============================================================================== const Colour Image::getPixelAt (const int x, const int y) const { diff --git a/src/gui/graphics/imaging/juce_Image.h b/src/gui/graphics/imaging/juce_Image.h index 27bd063cf7..ee6c6162df 100644 --- a/src/gui/graphics/imaging/juce_Image.h +++ b/src/gui/graphics/imaging/juce_Image.h @@ -128,6 +128,10 @@ public: int newHeight = -1, const Graphics::ResamplingQuality quality = Graphics::mediumResamplingQuality) const; + /** Returns a new single-channel image which is a copy of the alpha-channel of this image. + */ + virtual Image* createCopyOfAlphaChannel() const; + //============================================================================== /** Returns the colour of one of the pixels in the image. diff --git a/src/native/juce_linux_NativeCode.cpp b/src/native/juce_linux_NativeCode.cpp index 082f2d4870..35d9e84ef8 100644 --- a/src/native/juce_linux_NativeCode.cpp +++ b/src/native/juce_linux_NativeCode.cpp @@ -104,6 +104,7 @@ BEGIN_JUCE_NAMESPACE #include "linux/juce_linux_Fonts.cpp" #include "linux/juce_linux_Windowing.cpp" #include "linux/juce_linux_Audio.cpp" + #include "linux/juce_linux_JackAudio.cpp" #include "linux/juce_linux_Midi.cpp" #include "linux/juce_linux_AudioCDReader.cpp" #include "linux/juce_linux_FileChooser.cpp" diff --git a/src/native/juce_mac_NativeCode.mm b/src/native/juce_mac_NativeCode.mm index 648c97a8b6..bb0fb119bc 100644 --- a/src/native/juce_mac_NativeCode.mm +++ b/src/native/juce_mac_NativeCode.mm @@ -107,6 +107,7 @@ BEGIN_JUCE_NAMESPACE #include "mac/juce_iphone_Audio.cpp" #include "mac/juce_mac_CoreMidi.cpp" #else + #include "mac/juce_mac_CoreGraphicsContext.mm" #include "mac/juce_mac_NSViewComponentPeer.mm" #include "mac/juce_mac_MouseCursor.mm" #include "mac/juce_mac_NSViewComponent.mm" diff --git a/src/native/linux/juce_linux_NativeIncludes.h b/src/native/linux/juce_linux_NativeIncludes.h index 876310a9bd..1b4ae66a18 100644 --- a/src/native/linux/juce_linux_NativeIncludes.h +++ b/src/native/linux/juce_linux_NativeIncludes.h @@ -111,6 +111,19 @@ #include #endif +#if JUCE_JACK + /* Got an include error here? If so, you've either not got jack-audio-connection-kit + installed, or you've not got your paths set up correctly to find its header files. + + The package you need to install to get JACK support is "libjack-dev". + + If you don't have the jack-audio-connection-kit library and don't want to build + Juce with low latency audio support, just disable the JUCE_JACK flag in juce_Config.h + */ + #include + //#include +#endif + #undef SIZEOF #endif // __JUCE_LINUX_NATIVEINCLUDES_JUCEHEADER__ diff --git a/src/native/mac/juce_iphone_Audio.cpp b/src/native/mac/juce_iphone_Audio.cpp index 46764a4169..8146a7177e 100644 --- a/src/native/mac/juce_iphone_Audio.cpp +++ b/src/native/mac/juce_iphone_Audio.cpp @@ -482,7 +482,6 @@ private: AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof (layout)); AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, 0, &layout, sizeof (layout)); - AURenderCallbackStruct inputProc; inputProc.inputProc = processStatic; inputProc.inputProcRefCon = this; diff --git a/src/native/mac/juce_iphone_UIViewComponentPeer.mm b/src/native/mac/juce_iphone_UIViewComponentPeer.mm index 8e30c826e1..e3f6682a66 100644 --- a/src/native/mac/juce_iphone_UIViewComponentPeer.mm +++ b/src/native/mac/juce_iphone_UIViewComponentPeer.mm @@ -312,15 +312,19 @@ public: lineStride, pixelStride); CGDataProviderRef provider = CGDataProviderCreateWithData (0, imageData, lineStride * pixelStride, 0); + CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceRGB(); - imageRef = CGImageCreate (width, height, + CGImageRef imageRef = CGImageCreate (width, height, 8, pixelStride * 8, lineStride, - CGColorSpaceCreateDeviceRGB(), + colourSpace, hasAlpha ? (kCGImageAlphaFirst | kCGBitmapByteOrder32Little) : kCGBitmapByteOrderDefault, provider, 0, true, kCGRenderingIntentDefault); + CGColorSpaceRelease (colourSpace); + CGDataProviderRelease (provider); + juceImage.releasePixelDataReadWrite (imageData); uiImage = [[UIImage imageWithCGImage: imageRef] retain]; @@ -329,7 +333,7 @@ public: ~JuceUIImage() { [uiImage release]; - CFRelease (imageRef); + CGImageRelease (imageRef); } Image& getJuceImage() throw() { return juceImage; } diff --git a/src/native/mac/juce_mac_NSViewComponentPeer.mm b/src/native/mac/juce_mac_NSViewComponentPeer.mm index 21a1e1ab64..4d67db2399 100644 --- a/src/native/mac/juce_mac_NSViewComponentPeer.mm +++ b/src/native/mac/juce_mac_NSViewComponentPeer.mm @@ -27,6 +27,8 @@ // compiled on its own). #if JUCE_INCLUDED_FILE +#define USE_COREGRAPHICS_RENDERING 1 + class NSViewComponentPeer; //============================================================================== @@ -1433,6 +1435,11 @@ void NSViewComponentPeer::drawRect (NSRect r) if (r.size.width < 1.0f || r.size.height < 1.0f) return; +#if USE_COREGRAPHICS_RENDERING + CoreGraphicsContext context ((CGContextRef) [[NSGraphicsContext currentContext] graphicsPort], + [view frame].size.height); + handlePaint (context); +#else const float y = [view frame].size.height - (r.origin.y + r.size.height); JuceNSImage temp ((int) (r.size.width + 0.5f), @@ -1465,6 +1472,7 @@ void NSViewComponentPeer::drawRect (NSRect r) temp.draw (r.origin.x, r.origin.y, clip, originX, originY); } +#endif } bool NSViewComponentPeer::canBecomeKeyWindow() @@ -1511,7 +1519,7 @@ void NSViewComponentPeer::repaint (int x, int y, int w, int h) if (insideDrawRect) { - [view performSelectorOnMainThread: @selector (asyncRepaint:) + [view performSelectorOnMainThread: @selector (asyncRepaint:) withObject: [NSData dataWithBytes: &r length: sizeof (r)] waitUntilDone: NO]; }