diff options
57 files changed, 2551 insertions, 249 deletions
diff --git a/SConstruct b/SConstruct index 33cb8b7feb..cb070e2ec5 100644 --- a/SConstruct +++ b/SConstruct @@ -31,6 +31,7 @@ opts.AddOptions( BoolOption('COREAUDIO', 'Compile with Apple\'s CoreAudio library', 0), BoolOption('GTKOSX', 'Compile for use with GTK-OSX, not GTK-X11', 0), BoolOption('NATIVE_OSX_KEYS', 'Build key bindings file that matches OS X conventions', 0), + BoolOption('OLDFONTS', 'Old school font sizes', 0), BoolOption('DEBUG', 'Set to build with debugging information and no optimizations', 0), PathOption('DESTDIR', 'Set the intermediate install "prefix"', '/'), EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'leopard', 'none' ), ignorecase=2), @@ -538,7 +539,7 @@ if env['LV2']: else: print 'Building Ardour with LV2 support requires SLV2 >= 0.6.0' print 'WARNING: SLV2 not found, or too old. Ardour will be built without LV2 support.' - print 'Until the 2.3 release, Ardour requires SLV2 out of SVN.' + print 'Until the 2.4 release, Ardour requires SLV2 out of SVN.' print 'Testing would be very much appreciated! svn co http://svn.drobilla.net/lad/slv2' env['LV2'] = 0 conf.Finish() diff --git a/ardour.rc.in b/ardour.rc.in index ed40ac465a..5c35fd45aa 100644 --- a/ardour.rc.in +++ b/ardour.rc.in @@ -21,6 +21,7 @@ <Option name="plugins-stop-with-transport" value="no"/> <Option name="no-sw-monitoring" value="no"/> <Option name="stop-recording-on-xrun" value="no"/> + <Option name="create-xrun-marker" value="yes"/> <Option name="stop-at-session-end" value="no"/> <Option name="auto-xfade" value="yes"/> <Option name="crossfades-active" value="1"/> diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index 901b52ec1d..2b4b4bbe3f 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -809,6 +809,8 @@ AudioRegionView::create_waves () ChanCount nchans = atv.get_diskstream()->n_channels(); + cerr << "creating waves for " << _region->name() << " with wfd = " << wait_for_data << " and channels = " << nchans << endl; + /* in tmp_waves, set up null pointers for each channel so the vector is allocated */ for (uint32_t n = 0; n < nchans.n_audio(); ++n) { tmp_waves.push_back (0); @@ -822,15 +824,20 @@ AudioRegionView::create_waves () wave_caches.push_back (WaveView::create_cache ()); + cerr << "\tchannel " << n << endl; + if (wait_for_data) { if (audio_region()->audio_source(n)->peaks_ready (bind (mem_fun(*this, &AudioRegionView::peaks_ready_handler), n), data_ready_connection)) { + cerr << "\tData is ready\n"; create_one_wave (n, true); } else { + cerr << "\tdata is not ready\n"; // we'll get a PeaksReady signal from the source in the future // and will call create_one_wave(n) then. } } else { + cerr << "\tdon't delay, display today!\n"; create_one_wave (n, true); } diff --git a/libs/appleutility/AUParamInfo.cpp b/libs/appleutility/AUParamInfo.cpp new file mode 100644 index 0000000000..9b0046166f --- /dev/null +++ b/libs/appleutility/AUParamInfo.cpp @@ -0,0 +1,134 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + AUParamInfo.cpp + +=============================================================================*/ +#include "AUParamInfo.h" +#include "CAXException.h" + +AUParamInfo::AUParamInfo (AudioUnit inAU, + bool inIncludeExpert, + bool inIncludeReadOnly, + AudioUnitScope inScope, + AudioUnitElement inElement) + : mAU (inAU), + mNumParams (0), + mParamListID(NULL), + mScope (inScope), + mElement (inElement) +{ + UInt32 size; + OSStatus result = AudioUnitGetPropertyInfo(mAU, kAudioUnitProperty_ParameterList, inScope, mElement, &size, NULL); + if (size == 0 || result) return; + + int nparams = size / sizeof(AudioUnitPropertyID); + mParamListID = new AudioUnitParameterID[nparams]; + + memset (mParamListID, 0xFF, size); + + AudioUnitParameterID *paramList = new AudioUnitParameterID[nparams]; + + result = AudioUnitGetProperty(mAU, kAudioUnitProperty_ParameterList, mScope, mElement, paramList, &size); + if (result) { + delete [] mParamListID; + delete [] paramList; + mParamListID = NULL; + return; + } + + ParameterMap params; + for (int i = 0; i < nparams; ++i) + { + CAAUParameter auvp (mAU, paramList[i], mScope, mElement); // took out only using global scope in CAAUParameter creation + const AudioUnitParameterInfo ¶mInfo = auvp.ParamInfo(); + + // don't include if parameter can't be read or written + if (!(paramInfo.flags & kAudioUnitParameterFlag_IsWritable) + && !(paramInfo.flags & kAudioUnitParameterFlag_IsReadable)) + continue; + + // only include if expert params wanted + if (!inIncludeExpert && auvp.IsExpert()) + continue; + + // only include if read only params are wanted + if (!(paramInfo.flags & kAudioUnitParameterFlag_IsWritable) + && (paramInfo.flags & kAudioUnitParameterFlag_IsReadable)) + { + if (!inIncludeReadOnly) + continue; + } + + mParamListID[mNumParams] = paramList[i]; + mNumParams++; + + // ok - if we're here, then we have a parameter we are going to display. + UInt32 clump = 0; + auvp.GetClumpID (clump); + mParams[clump].push_back (auvp); + } + + delete [] paramList; +} + +AUParamInfo::~AUParamInfo() +{ + delete [] mParamListID; +} + +UInt32 AUParamInfo::NumParamsForClump (UInt32 inClump) const +{ + ParameterMap::const_iterator it = mParams.find(inClump); + if (it != mParams.end()) + return (*it).second.size(); + return 0; +} + +const CAAUParameter* AUParamInfo::GetParamInfo (AudioUnitParameterID inParamID) const +{ + for (ParameterMap::const_iterator it = mParams.begin(); it != mParams.end(); ++it) { + const ParameterList &list = (*it).second; + for (ParameterList::const_iterator iter = list.begin(); iter != list.end(); ++iter) { + if (inParamID == (*iter).mParameterID) { + return &(*iter); + } + } + } + return NULL; +} diff --git a/libs/appleutility/AUParamInfo.h b/libs/appleutility/AUParamInfo.h new file mode 100644 index 0000000000..9d342080b1 --- /dev/null +++ b/libs/appleutility/AUParamInfo.h @@ -0,0 +1,107 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + AUParamInfo.h + +=============================================================================*/ +#include <map> +#include <vector> +#include <AudioUnit/AudioUnit.h> +#include "CAAUParameter.h" + +/* + The ParameterMap returned by the Map() method is a map where + - the key is the clumpID + - the value is a ParameterList (vector<CAAUParameter>) + + If you have parameters on multiple scopes (or elements within a scope), then you should create one of these + for each scope-element pair +*/ + +class AUParamInfo { + +public: + typedef std::vector <CAAUParameter> ParameterList; + typedef std::map <UInt32, ParameterList, std::less<UInt32> > ParameterMap; + + + + AUParamInfo (AudioUnit inAU, + bool inIncludeExpert, + bool inIncludeReadOnly, + AudioUnitScope inScope = kAudioUnitScope_Global, + AudioUnitElement inElement = 0); + + ~AUParamInfo(); + + const ParameterMap& Map () const { return mParams; } + + // some convenience methods + UInt32 NumParams () const { return mNumParams; } + + AudioUnitParameterID ParamID (UInt32 inIndex) const + { + if (inIndex < mNumParams) return mParamListID[inIndex]; + return 0xFFFFFFFF; + } + + UInt32 NumClumps () const { return mParams.size(); } + + UInt32 NumParamsForClump (UInt32 inClump) const; + + // returns NULL if there's no info for the parameter + const CAAUParameter* GetParamInfo (AudioUnitParameterID inParamID) const; + + AudioUnitScope GetScope () const { return mScope; } + AudioUnitElement GetElement () const { return mElement; } + +private: + + AudioUnit mAU; + UInt32 mNumParams; + AudioUnitParameterID * mParamListID; + + ParameterMap mParams; + AudioUnitScope mScope; + AudioUnitElement mElement; + + // disallow + AUParamInfo () {} + AUParamInfo (const AUParamInfo &c) {} + AUParamInfo& operator= (const AUParamInfo& c) { return *this; } +}; diff --git a/libs/appleutility/CAAUParameter.cpp b/libs/appleutility/CAAUParameter.cpp new file mode 100644 index 0000000000..b99b6ab749 --- /dev/null +++ b/libs/appleutility/CAAUParameter.cpp @@ -0,0 +1,316 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAAUParameter.cpp + +=============================================================================*/ + +#include "CAAUParameter.h" + +CAAUParameter::CAAUParameter() +{ + memset(this, 0, sizeof(CAAUParameter)); +} + +CAAUParameter::CAAUParameter(AudioUnit au, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement element) +{ + memset(this, 0, sizeof(CAAUParameter)); + Init (au, param, scope, element); +} + +CAAUParameter::CAAUParameter (AudioUnitParameter &inParam) +{ + memset(this, 0, sizeof(CAAUParameter)); + Init (inParam.mAudioUnit, inParam.mParameterID, inParam.mScope, inParam.mElement); +} + +CAAUParameter::CAAUParameter(const CAAUParameter &a) +{ + memset(this, 0, sizeof(CAAUParameter)); + *this = a; +} + +CAAUParameter & CAAUParameter::operator = (const CAAUParameter &a) +{ + if (mParamName) CFRelease(mParamName); + if (mParamTag) CFRelease(mParamTag); + if (mNamedParams) CFRelease(mNamedParams); + + memcpy(this, &a, sizeof(CAAUParameter)); + + if (mParamName) CFRetain(mParamName); + if (mParamTag) CFRetain(mParamTag); + if (mNamedParams) CFRetain(mNamedParams); + + return *this; +} + +CAAUParameter::~CAAUParameter() +{ + if (mParamName) CFRelease(mParamName); + if (mParamTag) CFRelease(mParamTag); + if (mNamedParams) CFRelease (mNamedParams); +} + +void CAAUParameter::Init (AudioUnit au, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement element) +{ + mAudioUnit = au; + mParameterID = param; + mScope = scope; + mElement = element; + + UInt32 propertySize = sizeof(mParamInfo); + OSStatus err = AudioUnitGetProperty(au, kAudioUnitProperty_ParameterInfo, + scope, param, &mParamInfo, &propertySize); + if (err) + memset(&mParamInfo, 0, sizeof(mParamInfo)); + if (mParamInfo.flags & kAudioUnitParameterFlag_HasCFNameString) { + mParamName = mParamInfo.cfNameString; + if (!(mParamInfo.flags & kAudioUnitParameterFlag_CFNameRelease)) + CFRetain (mParamName); + } else + mParamName = CFStringCreateWithCString(NULL, mParamInfo.name, kCFStringEncodingUTF8); + + char* str = 0; + switch (mParamInfo.unit) + { + case kAudioUnitParameterUnit_Boolean: + str = "T/F"; + break; + case kAudioUnitParameterUnit_Percent: + case kAudioUnitParameterUnit_EqualPowerCrossfade: + str = "%"; + break; + case kAudioUnitParameterUnit_Seconds: + str = "Secs"; + break; + case kAudioUnitParameterUnit_SampleFrames: + str = "Samps"; + break; + case kAudioUnitParameterUnit_Phase: + case kAudioUnitParameterUnit_Degrees: + str = "Degr."; + break; + case kAudioUnitParameterUnit_Hertz: + str = "Hz"; + break; + case kAudioUnitParameterUnit_Cents: + case kAudioUnitParameterUnit_AbsoluteCents: + str = "Cents"; + break; + case kAudioUnitParameterUnit_RelativeSemiTones: + str = "S-T"; + break; + case kAudioUnitParameterUnit_MIDINoteNumber: + case kAudioUnitParameterUnit_MIDIController: + str = "MIDI"; + //these are inclusive, so add one value here + mNumIndexedParams = short(mParamInfo.maxValue+1 - mParamInfo.minValue); + break; + case kAudioUnitParameterUnit_Decibels: + str = "dB"; + break; + case kAudioUnitParameterUnit_MixerFaderCurve1: + case kAudioUnitParameterUnit_LinearGain: + str = "Gain"; + break; + case kAudioUnitParameterUnit_Pan: + str = "L/R"; + break; + case kAudioUnitParameterUnit_Meters: + str = "Mtrs"; + break; + case kAudioUnitParameterUnit_Octaves: + str = "8ve"; + break; + case kAudioUnitParameterUnit_BPM: + str = "BPM"; + break; + case kAudioUnitParameterUnit_Beats: + str = "Beats"; + break; + case kAudioUnitParameterUnit_Milliseconds: + str = "msecs"; + break; + case kAudioUnitParameterUnit_Ratio: + str = "ratio"; + break; + case kAudioUnitParameterUnit_Indexed: + { + propertySize = sizeof(mNamedParams); + err = AudioUnitGetProperty (au, + kAudioUnitProperty_ParameterValueStrings, + scope, + param, + &mNamedParams, + &propertySize); + if (!err && mNamedParams) { + mNumIndexedParams = CFArrayGetCount(mNamedParams); + } else { + //these are inclusive, so add one value here + mNumIndexedParams = short(mParamInfo.maxValue+1 - mParamInfo.minValue); + } + str = NULL; + } + break; + case kAudioUnitParameterUnit_CustomUnit: + { + CFStringRef unitName = mParamInfo.unitName; + static char paramStr[256]; + CFStringGetCString (unitName, paramStr, 256, kCFStringEncodingUTF8); + if (mParamInfo.flags & kAudioUnitParameterFlag_CFNameRelease) + CFRelease (unitName); + str = paramStr; + break; + } + case kAudioUnitParameterUnit_Generic: + case kAudioUnitParameterUnit_Rate: + default: + str = NULL; + break; + } + + if (str) + mParamTag = CFStringCreateWithCString(NULL, str, kCFStringEncodingUTF8); + else + mParamTag = NULL; +} + + +Float32 CAAUParameter::GetValue() const +{ + Float32 value = 0.; + //OSStatus err = + AudioUnitGetParameter(mAudioUnit, mParameterID, mScope, mElement, &value); + return value; +} + +CFStringRef CAAUParameter::GetStringFromValueCopy(const Float32 *value) const +{ + if (HasNamedParams()) + { + Float32 val = (value == NULL ? GetValue() : *value); + int index = int(mParamInfo.minValue) + int(val); + CFStringRef str = GetParamName (index); + if (str) { + CFRetain (str); + return str; + } + } + else if (ValuesHaveStrings()) + { + AudioUnitParameterStringFromValue stringValue; + stringValue.inParamID = mParameterID; + stringValue.inValue = value; + stringValue.outString = NULL; + UInt32 propertySize = sizeof(stringValue); + + OSStatus err = AudioUnitGetProperty (mAudioUnit, + kAudioUnitProperty_ParameterStringFromValue, + mScope, + mParameterID, + &stringValue, + &propertySize); + + if (err == noErr && stringValue.outString != NULL) + return stringValue.outString; + } + + Float32 val = (value == NULL ? GetValue() : *value); + char valstr[32]; + AUParameterFormatValue (val, this, valstr, 4); + return CFStringCreateWithCString(NULL, valstr, kCFStringEncodingUTF8); +} + +Float32 CAAUParameter::GetValueFromString(CFStringRef str) const +{ + if (ValuesHaveStrings()) + { + AudioUnitParameterValueFromString valueString; + valueString.inParamID = mParameterID; + valueString.inString = str; + UInt32 propertySize = sizeof(valueString); + + OSStatus err = AudioUnitGetProperty (mAudioUnit, + kAudioUnitProperty_ParameterValueFromString, + mScope, + mParameterID, + &valueString, + &propertySize); + + if (err == noErr) { + return valueString.outValue; + } + } + + Float32 paramValue = mParamInfo.defaultValue; + char valstr[32]; + CFStringGetCString(str, valstr, sizeof(valstr), kCFStringEncodingUTF8); + sscanf(valstr, "%f", ¶mValue); + return paramValue; +} + +void CAAUParameter::SetValue( AUParameterListenerRef inListener, + void * inObject, + Float32 inValue) const +{ + // clip inValue as: maxValue >= inValue >= minValue before setting + Float32 valueToSet = inValue; + if (valueToSet > mParamInfo.maxValue) + valueToSet = mParamInfo.maxValue; + if (valueToSet < mParamInfo.minValue) + valueToSet = mParamInfo.minValue; + + AUParameterSet(inListener, inObject, this, valueToSet, 0); +} + +#if DEBUG +void CAAUParameter::Print() const +{ + UInt32 clump = 0; + GetClumpID (clump); + + UInt32 len = CFStringGetLength(mParamName); + char* chars = (char*)malloc (len * 2); // give us plenty of room for unichar chars + if (!CFStringGetCString (mParamName, chars, len * 2, kCFStringEncodingUTF8)) + chars[0] = 0; + + printf ("ID: %ld, Clump: %ld, Name: %s\n", mParameterID, clump, chars); + free (chars); +} +#endif diff --git a/libs/appleutility/CAAUParameter.h b/libs/appleutility/CAAUParameter.h new file mode 100644 index 0000000000..4f35b26353 --- /dev/null +++ b/libs/appleutility/CAAUParameter.h @@ -0,0 +1,187 @@ +/* Copyright: © Copyright 2005 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAAUParameter.h + +=============================================================================*/ + +#ifndef __CAAUParameter_h__ +#define __CAAUParameter_h__ + +#include <AudioToolbox/AudioUnitUtilities.h> + +// ____________________________________________________________________________ +// CAAUParameter +// complete parameter specification + /*! @class CAAUParameter */ +class CAAUParameter : public AudioUnitParameter { +public: + /*! @ctor CAAUParameter.0 */ + CAAUParameter(); + /*! @ctor CAAUParameter.1 */ + CAAUParameter(AudioUnit au, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement element); + /*! @ctor CAAUParameter.2 */ + CAAUParameter(AudioUnitParameter &inParam); + /*! @ctor CAAUParameter.3 */ + CAAUParameter(const CAAUParameter &a); + /*! @dtor ~CAAUParameter */ + ~CAAUParameter(); + + /*! @method operator <@ */ + bool operator < (const CAAUParameter &a) const + { + return memcmp(this, &a, sizeof(AudioUnitParameter)) < 0; + } + + /*! @method operator ==@ */ + bool operator == (const CAAUParameter &a) const + { + return !memcmp(this, &a, sizeof(AudioUnitParameter)); + } + + /*! @method operator =@ */ + CAAUParameter & operator = (const CAAUParameter &a); + + /*! @method GetValue */ + Float32 GetValue() const; + /*! @method SetValue */ + void SetValue( AUParameterListenerRef inListener, + void * inObject, + Float32 inValue) const; + + /*! @method GetName */ + CFStringRef GetName() const { return mParamName; } + // borrowed reference! + + /*! @method GetStringFromValueCopy */ + CFStringRef GetStringFromValueCopy(const Float32 *value = NULL) const; + // returns a copy of the name of the current parameter value + // or null if there is no name associated + // caller must release + /*! @method ValuesHaveStrings */ + bool ValuesHaveStrings () const + { + return (mParamInfo.flags & kAudioUnitParameterFlag_ValuesHaveStrings) != 0; + } + + /*! @method GetValueFromString */ + Float32 GetValueFromString (CFStringRef str) const; + // caller must release + + /*! @method ParamInfo */ + const AudioUnitParameterInfo & + ParamInfo() const { return mParamInfo; } + + /*! @method GetParamTag */ + CFStringRef GetParamTag() const { return mParamTag; } + // this may return null! - + // in which case there is no descriptive tag for the parameter + + /*! @method GetParamName */ + CFStringRef GetParamName (int inIndex) const + // this can return null if there is no name for the parameter + { + return (mNamedParams && inIndex < mNumIndexedParams) + ? (CFStringRef) CFArrayGetValueAtIndex(mNamedParams, inIndex) + : 0; + } + + /*! @method GetNumIndexedParams */ + int GetNumIndexedParams () const { return mNumIndexedParams; } + + /*! @method IsIndexedParam */ + bool IsIndexedParam () const { return mNumIndexedParams != 0; } + + /*! @method HasNamedParams */ + bool HasNamedParams () const { return IsIndexedParam() && mNamedParams; } + + /*! @method GetClumpID */ + bool GetClumpID (UInt32 &outClumpID) const + { + if (mParamInfo.flags & kAudioUnitParameterFlag_HasClump) { + outClumpID = mParamInfo.clumpID; + return true; + } + return false; + } + + /*! @method HasDisplayTransformation */ + bool HasDisplayTransformation () const + { + return GetAudioUnitParameterDisplayType (mParamInfo.flags); + } + + /*! @method IsExpert */ + bool IsExpert () const + { + return mParamInfo.flags & kAudioUnitParameterFlag_ExpertMode; + } +#if DEBUG + void Print () const; +#endif + + // these methods are defined in CAPersistence.cpp + // they will persist and restore only the scope, element and param ID's of the AudioUnitParameter + // however, this is sufficient to be able to save/restore a CAAUParameter object + void Save (CFPropertyListRef &outData) const; + + static void Save (const AudioUnitParameter &inParam, CFPropertyListRef &outData); + + static OSStatus Restore (const CFPropertyListRef inData, AudioUnitParameter &outParam); + +protected: + // cached parameter info + /*! @var mParamInfo */ + AudioUnitParameterInfo mParamInfo; + /*! @var mParamName */ + CFStringRef mParamName; + /*! @var mParamTag */ + CFStringRef mParamTag; + /*! @var mNumIndexedParams */ + short mNumIndexedParams; + /*! @var mNamedParams */ + CFArrayRef mNamedParams; + +private: + void Init (AudioUnit au, AudioUnitParameterID param, AudioUnitScope scope, AudioUnitElement element); + +}; + + + +#endif // __CAAUParameter_h__ diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 529a778942..7521bd728a 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -20,6 +20,9 @@ ardour.Append(CXXFLAGS=["-DLIBSIGC_DISABLE_DEPRECATED", "-DGLIBMM_EXCEPTIONS_ENA ardour.Append(PACKAGE = domain) ardour.Append(POTFILE = domain + '.pot') +if ardour['IS_OSX']: + ardour.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048") + # # explicitly reference the control protocol LGPL library for includes # @@ -312,7 +315,7 @@ ardour.Merge ([ libraries['sndfile-ardour'], libraries['vamp'], libraries['vamphost'], - libraries['xml'], + libraries['xml'] ]) if ardour['RUBBERBAND']: @@ -380,5 +383,10 @@ env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'i18n.h', 'gettext.h' ] + [ 'sse_functions_xmm.cc', 'sse_functions.s', 'sse_functions_64bit.s' ] + [ 'rb_effect.cc', 'st_stretch.cc', 'st_pitch.cc' ] + - ardour_files + osc_files + vst_files + coreaudio_files + audiounit_files + + ardour_files + + osc_files + + vst_files + + coreaudio_files + + audiounit_files + + lv2_files + glob.glob('po/*.po') + glob.glob('ardour/*.h'))) diff --git a/libs/ardour/analyser.cc b/libs/ardour/analyser.cc index 7ddb5428e9..2e14c74b86 100644 --- a/libs/ardour/analyser.cc +++ b/libs/ardour/analyser.cc @@ -95,7 +95,7 @@ Analyser::work () boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src); - if (afs) { + if (afs && afs->length()) { analyse_audio_file_source (afs); } } diff --git a/libs/ardour/ardour/ardour.h b/libs/ardour/ardour/ardour.h index fcec83394f..6f653f10bf 100644 --- a/libs/ardour/ardour/ardour.h +++ b/libs/ardour/ardour/ardour.h @@ -20,8 +20,10 @@ #ifndef __ardour_ardour_h__ #define __ardour_ardour_h__ -#include <limits.h> +#include <map> #include <string> + +#include <limits.h> #include <signal.h> #include <pbd/error.h> @@ -43,12 +45,15 @@ namespace ARDOUR { extern OSC* osc; static const nframes_t max_frames = JACK_MAX_FRAMES; + extern sigc::signal<void,std::string> BootMessage; int init (bool with_vst, bool try_optimization); int cleanup (); std::string get_ardour_revision (); + void find_bindings_files (std::map<std::string,std::string>&); + const layer_t max_layer = UCHAR_MAX; microseconds_t get_microseconds (); diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h index c781e8200d..bdeac0477b 100644 --- a/libs/ardour/ardour/audio_unit.h +++ b/libs/ardour/ardour/audio_unit.h @@ -32,6 +32,7 @@ #include <ardour/plugin.h> #include <AudioUnit/AudioUnit.h> +#include <appleutility/AUParamInfo.h> #include <boost/shared_ptr.hpp> @@ -45,6 +46,16 @@ namespace ARDOUR { class AudioEngine; class Session; +struct AUParameterDescriptor : public Plugin::ParameterDescriptor { + // additional fields to make operations more efficient + AudioUnitParameterID id; + AudioUnitScope scope; + AudioUnitElement element; + float default_value; + bool automatable; + AudioUnitParameterUnit unit; +}; + class AUPlugin : public ARDOUR::Plugin { public: @@ -105,7 +116,7 @@ class AUPlugin : public ARDOUR::Plugin private: boost::shared_ptr<CAComponent> comp; boost::shared_ptr<CAAudioUnit> unit; - + AudioStreamBasicDescription streamFormat; bool initialized; int format_set; @@ -119,6 +130,7 @@ class AUPlugin : public ARDOUR::Plugin int set_input_format (); int set_stream_format (int scope, uint32_t cnt); int _set_block_size (nframes_t nframes); + void discover_parameters (); std::vector<std::pair<uint32_t, uint32_t> > parameter_map; uint32_t current_maxbuf; @@ -126,6 +138,8 @@ class AUPlugin : public ARDOUR::Plugin nframes_t cb_offset; vector<Sample*>* current_buffers; nframes_t frames_processed; + + std::vector<AUParameterDescriptor> descriptors; }; typedef boost::shared_ptr<AUPlugin> AUPluginPtr; diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 1589841baa..d11b829694 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -116,7 +116,6 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR: static bool _build_peakfiles; bool _peaks_built; - bool _analysed; mutable Glib::Mutex _lock; mutable Glib::Mutex _peaks_ready_lock; Glib::ustring peakpath; diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index f96ecc0bd1..a2c1d98ae7 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -57,7 +57,10 @@ public: virtual void add_control(boost::shared_ptr<AutomationControl>); - virtual void automation_snapshot(nframes_t now); + virtual void automation_snapshot(nframes_t now, bool force); + bool should_snapshot (nframes_t now) { + return (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval); + } virtual void transport_stopped(nframes_t now); virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const; diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h index 134557daeb..015c617425 100644 --- a/libs/ardour/ardour/configuration_vars.h +++ b/libs/ardour/ardour/configuration_vars.h @@ -105,6 +105,7 @@ CONFIG_VARIABLE (bool, punch_out, "punch-out", false) CONFIG_VARIABLE (bool, plugins_stop_with_transport, "plugins-stop-with-transport", false) CONFIG_VARIABLE (bool, do_not_record_plugins, "do-not-record-plugins", false) CONFIG_VARIABLE (bool, stop_recording_on_xrun, "stop-recording-on-xrun", false) +CONFIG_VARIABLE (bool, create_xrun_marker, "create-xrun-marker", true) CONFIG_VARIABLE (bool, stop_at_session_end, "stop-at-session-end", true) CONFIG_VARIABLE (bool, seamless_loop, "seamless-loop", false) CONFIG_VARIABLE (nframes_t, preroll, "preroll", 0) diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 0b82844f61..83b6378dae 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -251,8 +251,8 @@ class IO : public Automatable, public Latent void set_parameter_automation_state (Parameter, AutoState); - virtual void transport_stopped (nframes_t now); // interface: matches Insert - void automation_snapshot (nframes_t now); // interface: matches Automatable + virtual void transport_stopped (nframes_t now); + virtual void automation_snapshot (nframes_t now, bool force); void start_pan_touch (uint32_t which); void end_pan_touch (uint32_t which); diff --git a/libs/ardour/ardour/io_processor.h b/libs/ardour/ardour/io_processor.h index 409ad91b15..a535ce3bb4 100644 --- a/libs/ardour/ardour/io_processor.h +++ b/libs/ardour/ardour/io_processor.h @@ -65,7 +65,7 @@ class IOProcessor : public Processor boost::shared_ptr<IO> io() { return _io; } boost::shared_ptr<const IO> io() const { return _io; } - virtual void automation_snapshot (nframes_t now) { _io->automation_snapshot(now); } + virtual void automation_snapshot (nframes_t now, bool force) { _io->automation_snapshot(now, force); } virtual void run_in_place (BufferSet& in, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) = 0; diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index d8a38841f8..4640a8e32f 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -126,7 +126,6 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this<Pla sigc::signal<void> LengthChanged; static string bump_name (string old_name, Session&); - static string bump_name_once (string old_name); void freeze (); void thaw (); diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 2466f37996..c7c0b77102 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -75,6 +75,8 @@ class Route : public IO Route (Session&, const XMLNode&, DataType default_type = DataType::AUDIO); virtual ~Route(); + static std::string ensure_track_or_route_name(std::string, Session &); + std::string comment() { return _comment; } void set_comment (std::string str, void *src); @@ -247,7 +249,7 @@ class Route : public IO return _mute_control; } - void automation_snapshot (nframes_t now); + void automation_snapshot (nframes_t now, bool force=false); void protect_automation (); void set_remote_control_id (uint32_t id); diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index c0c178eb02..10167ae5b3 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -353,7 +353,7 @@ class Session : public PBD::StatefulDestructible sigc::signal<void> TransportStateChange; /* generic */ sigc::signal<void,nframes_t> PositionChanged; /* sent after any non-sequential motion */ sigc::signal<void> DurationChanged; - sigc::signal<void> HaltOnXrun; + sigc::signal<void,nframes_t> Xrun; sigc::signal<void> TransportLooped; sigc::signal<void,RouteList&> RouteAdded; @@ -563,8 +563,11 @@ class Session : public PBD::StatefulDestructible /* region info */ - sigc::signal<void,boost::shared_ptr<Region> > RegionAdded; - sigc::signal<void,boost::shared_ptr<Region> > RegionRemoved; + void add_regions (std::vector<boost::shared_ptr<Region> >&); + + sigc::signal<void,boost::weak_ptr<Region> > RegionAdded; + sigc::signal<void,std::vector<boost::weak_ptr<Region> >& > RegionsAdded; + sigc::signal<void,boost::weak_ptr<Region> > RegionRemoved; int region_name (string& result, string base = string(""), bool newlevel = false) const; string new_region_name (string); @@ -586,10 +589,10 @@ class Session : public PBD::StatefulDestructible string doing_what; /* control info */ - bool sample_convert; SrcQuality quality; volatile bool freeze; std::vector<Glib::ustring> paths; + bool replace_existing_source; /* result */ SourceList sources; @@ -1480,6 +1483,12 @@ class Session : public PBD::StatefulDestructible SourceMap sources; + public: + SourceMap get_sources() { return sources; } + + private: + + int load_sources (const XMLNode& node); XMLNode& get_sources_as_xml (); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index fdf8d0b439..efc2e35ecc 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -257,7 +257,8 @@ namespace ARDOUR { enum EditMode { Slide, - Splice + Splice, + Lock }; enum RegionPoint { diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h index e52274eb1f..eecde2e85f 100644 --- a/libs/ardour/ardour/utils.h +++ b/libs/ardour/ardour/utils.h @@ -45,6 +45,8 @@ static inline float f_max(float x, float a) { return (x); } +std::string bump_name_once(std::string s); + int cmp_nocase (const std::string& s, const std::string& s2); int touch_file(Glib::ustring path); diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc index bc2f0d9feb..7d55cc343a 100644 --- a/libs/ardour/audio_track.cc +++ b/libs/ardour/audio_track.cc @@ -519,7 +519,7 @@ AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, if (lm.locked()) { // automation snapshot can also be called from the non-rt context // and it uses the redirect list, so we take the lock out here - automation_snapshot (start_frame); + automation_snapshot (start_frame, false); } } diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc index 387c7f1238..9132957743 100644 --- a/libs/ardour/audio_unit.cc +++ b/libs/ardour/audio_unit.cc @@ -32,6 +32,7 @@ #include <ardour/utils.h> #include <appleutility/CAAudioUnit.h> +#include <appleutility/CAAUParameter.h> #include <CoreServices/CoreServices.h> #include <AudioUnit/AudioUnit.h> @@ -83,6 +84,7 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAC throw failed_constructor(); } + unit->GetElementCount (kAudioUnitScope_Global, global_elements); unit->GetElementCount (kAudioUnitScope_Input, input_elements); unit->GetElementCount (kAudioUnitScope_Output, output_elements); @@ -106,6 +108,10 @@ AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAC error << _("AUPlugin: cannot set processing block size") << endmsg; throw failed_constructor(); } + + discover_parameters (); + + Plugin::setup_controls (); } AUPlugin::~AUPlugin () @@ -119,6 +125,124 @@ AUPlugin::~AUPlugin () } } +void +AUPlugin::discover_parameters () +{ + /* discover writable parameters */ + + cerr << "get param info, there are " << global_elements << " global elements\n"; + + AudioUnitScope scopes[] = { + kAudioUnitScope_Global, + kAudioUnitScope_Output, + kAudioUnitScope_Input + }; + + descriptors.clear (); + + for (uint32_t i = 0; i < sizeof (scopes) / sizeof (scopes[0]); ++i) { + + AUParamInfo param_info (unit->AU(), false, false, scopes[i]); + + cerr << "discovered " << param_info.NumParams() << " parameters in scope " << i << endl; + + for (uint32_t i = 0; i < param_info.NumParams(); ++i) { + + AUParameterDescriptor d; + + d.id = param_info.ParamID (i); + + const CAAUParameter* param = param_info.GetParamInfo (d.id); + const AudioUnitParameterInfo& info (param->ParamInfo()); + + const int len = CFStringGetLength (param->GetName());; + char local_buffer[len*2]; + Boolean good = CFStringGetCString(param->GetName(),local_buffer,len*2,kCFStringEncodingMacRoman); + if (!good) { + d.label = "???"; + } else { + d.label = local_buffer; + } + + d.scope = param_info.GetScope (); + d.element = param_info.GetElement (); + + /* info.units to consider */ + /* + kAudioUnitParameterUnit_Generic = 0 + kAudioUnitParameterUnit_Indexed = 1 + kAudioUnitParameterUnit_Boolean = 2 + kAudioUnitParameterUnit_Percent = 3 + kAudioUnitParameterUnit_Seconds = 4 + kAudioUnitParameterUnit_SampleFrames = 5 + kAudioUnitParameterUnit_Phase = 6 + kAudioUnitParameterUnit_Rate = 7 + kAudioUnitParameterUnit_Hertz = 8 + kAudioUnitParameterUnit_Cents = 9 + kAudioUnitParameterUnit_RelativeSemiTones = 10 + kAudioUnitParameterUnit_MIDINoteNumber = 11 + kAudioUnitParameterUnit_MIDIController = 12 + kAudioUnitParameterUnit_Decibels = 13 + kAudioUnitParameterUnit_LinearGain = 14 + kAudioUnitParameterUnit_Degrees = 15 + kAudioUnitParameterUnit_EqualPowerCrossfade = 16 + kAudioUnitParameterUnit_MixerFaderCurve1 = 17 + kAudioUnitParameterUnit_Pan = 18 + kAudioUnitParameterUnit_Meters = 19 + kAudioUnitParameterUnit_AbsoluteCents = 20 + kAudioUnitParameterUnit_Octaves = 21 + kAudioUnitParameterUnit_BPM = 22 + kAudioUnitParameterUnit_Beats = 23 + kAudioUnitParameterUnit_Milliseconds = 24 + kAudioUnitParameterUnit_Ratio = 25 + */ + + /* info.flags to consider */ + + /* + + kAudioUnitParameterFlag_CFNameRelease = (1L << 4) + kAudioUnitParameterFlag_HasClump = (1L << 20) + kAudioUnitParameterFlag_HasName = (1L << 21) + kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22) + kAudioUnitParameterFlag_IsHighResolution = (1L << 23) + kAudioUnitParameterFlag_NonRealTime = (1L << 24) + kAudioUnitParameterFlag_CanRamp = (1L << 25) + kAudioUnitParameterFlag_ExpertMode = (1L << 26) + kAudioUnitParameterFlag_HasCFNameString = (1L << 27) + kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28) + kAudioUnitParameterFlag_IsElementMeta = (1L << 29) + kAudioUnitParameterFlag_IsReadable = (1L << 30) + kAudioUnitParameterFlag_IsWritable = (1L << 31) + */ + + d.lower = info.minValue; + d.upper = info.maxValue; + d.default_value = info.defaultValue; + + d.integer_step = (info.unit & kAudioUnitParameterUnit_Indexed); + d.toggled = (info.unit & kAudioUnitParameterUnit_Boolean) || + (d.integer_step && ((d.upper - d.lower) == 1.0)); + d.sr_dependent = (info.unit & kAudioUnitParameterUnit_SampleFrames); + d.automatable = !d.toggled && + !(info.flags & kAudioUnitParameterFlag_NonRealTime) && + (info.flags & kAudioUnitParameterFlag_IsWritable); + + d.logarithmic = (info.flags & kAudioUnitParameterFlag_DisplayLogarithmic); + d.unit = info.unit; + + d.step = 1.0; + d.smallstep = 0.1; + d.largestep = 10.0; + d.min_unbound = 0; // lower is bound + d.max_unbound = 0; // upper is bound + + descriptors.push_back (d); + } + } +} + + string AUPlugin::unique_id () const { @@ -134,13 +258,16 @@ AUPlugin::label () const uint32_t AUPlugin::parameter_count () const { - return 0; + return descriptors.size(); } float AUPlugin::default_value (uint32_t port) { - // AudioUnits don't have default values. Maybe presets though? + if (port < descriptors.size()) { + return descriptors[port].default_value; + } + return 0; } @@ -157,28 +284,41 @@ AUPlugin::signal_latency () const void AUPlugin::set_parameter (uint32_t which, float val) { - // unit->SetParameter (parameter_map[which].first, parameter_map[which].second, 0, val); + if (which < descriptors.size()) { + const AUParameterDescriptor& d (descriptors[which]); + unit->SetParameter (d.id, d.scope, d.element, val); + } } float AUPlugin::get_parameter (uint32_t which) const { - float outValue = 0.0; - - // unit->GetParameter(parameter_map[which].first, parameter_map[which].second, 0, outValue); - - return outValue; + float val = 0.0; + if (which < descriptors.size()) { + const AUParameterDescriptor& d (descriptors[which]); + unit->GetParameter(d.id, d.scope, d.element, val); + } + return val; } int -AUPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const +AUPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& pd) const { - return 0; + if (which < descriptors.size()) { + pd = descriptors[which]; + return 0; + } + return -1; } uint32_t AUPlugin::nth_parameter (uint32_t which, bool& ok) const { + if (which < descriptors.size()) { + ok = true; + return which; + } + ok = false; return 0; } @@ -397,20 +537,26 @@ set<uint32_t> AUPlugin::automatable() const { set<uint32_t> automates; - + + for (uint32_t i = 0; i < descriptors.size(); ++i) { + if (descriptors[i].automatable) { + automates.insert (i); + } + } + return automates; } string -AUPlugin::describe_parameter (uint32_t) +AUPlugin::describe_parameter (uint32_t param) { - return ""; + return descriptors[param].label; } void -AUPlugin::print_parameter (uint32_t, char*, uint32_t len) const +AUPlugin::print_parameter (uint32_t param, char* buf, uint32_t len) const { - + // NameValue stuff here } bool @@ -422,7 +568,7 @@ AUPlugin::parameter_is_audio (uint32_t) const bool AUPlugin::parameter_is_control (uint32_t) const { - return false; + return true; } bool diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index da4f8a642c..8d2589db0e 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -126,6 +126,8 @@ AudioEngine::start () if (session) { nframes_t blocksize = jack_get_buffer_size (_jack); + BootMessage (_("Connect session to engine")); + session->set_block_size (blocksize); session->set_frame_rate (jack_get_sample_rate (_jack)); diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 81ad45e08a..00e0f925df 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -677,20 +677,18 @@ bool AudioFileSource::safe_file_extension(ustring file) { return !(file.rfind(".wav") == ustring::npos && - file.rfind(".aiff")== ustring::npos && - file.rfind(".aif") == ustring::npos && - file.rfind(".snd") == ustring::npos && - file.rfind(".au") == ustring::npos && - file.rfind(".raw") == ustring::npos && - file.rfind(".sf") == ustring::npos && - file.rfind(".cdr") == ustring::npos && - file.rfind(".smp") == ustring::npos && - file.rfind(".maud")== ustring::npos && - file.rfind(".vwe") == ustring::npos && - file.rfind(".paf") == ustring::npos && - /* protools convention */ - file.rfind(".L") == ustring::npos && - file.rfind(".R") == ustring::npos && + file.rfind(".aiff")== ustring::npos && + file.rfind(".aif") == ustring::npos && + file.rfind(".amb") == ustring::npos && + file.rfind(".snd") == ustring::npos && + file.rfind(".au") == ustring::npos && + file.rfind(".raw") == ustring::npos && + file.rfind(".sf") == ustring::npos && + file.rfind(".cdr") == ustring::npos && + file.rfind(".smp") == ustring::npos && + file.rfind(".maud")== ustring::npos && + file.rfind(".vwe") == ustring::npos && + file.rfind(".paf") == ustring::npos && #ifdef HAVE_FLAC file.rfind(".flac")== ustring::npos && #endif // HAVE_FLAC diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc index 20115ff944..7e2b709abd 100644 --- a/libs/ardour/audioregion.cc +++ b/libs/ardour/audioregion.cc @@ -1316,6 +1316,8 @@ AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new) return 0; } + cerr << "startup analysis of " << _name << endl; + TransientDetector t (pl->session().frame_rate()); bool existing_results = !results.empty(); @@ -1328,10 +1330,14 @@ AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new) t.reset (); + cerr << "working on channel " << i << endl; + if (t.run ("", this, i, these_results)) { return -1; } + cerr << "done\n"; + /* translate all transients to give absolute position */ for (AnalysisFeatureList::iterator i = these_results.begin(); i != these_results.end(); ++i) { @@ -1357,6 +1363,11 @@ AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new) /* make sure ours are clean too */ TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0); + + } else { + + TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0); + results = _transients; } _valid_transients = true; diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index cd71dd21e4..01dea08d3e 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -70,6 +70,7 @@ AudioSource::AudioSource (Session& s, ustring name) AudioSource::AudioSource (Session& s, const XMLNode& node) : Source (s, node) { + _peaks_built = false; _peak_byte_max = 0; peakfile = -1; @@ -213,7 +214,7 @@ AudioSource::initialize_peakfile (bool newfile, ustring audio_path) /* we found it in the peaks dir, so check it out */ - if (statbuf.st_size == 0) { + if (statbuf.st_size == 0 || (statbuf.st_size < ((length() / _FPP) * sizeof (PeakData)))) { // empty _peaks_built = false; } else { @@ -221,12 +222,22 @@ AudioSource::initialize_peakfile (bool newfile, ustring audio_path) struct stat stat_file; int err = stat (audio_path.c_str(), &stat_file); - if (!err && stat_file.st_mtime > statbuf.st_mtime){ + if (err) { _peaks_built = false; _peak_byte_max = 0; } else { - _peaks_built = true; - _peak_byte_max = statbuf.st_size; + + /* allow 6 seconds slop on checking peak vs. file times because of various + disk action "races" + */ + + if (stat_file.st_mtime > statbuf.st_mtime && (stat_file.st_mtime - statbuf.st_mtime > 6)) { + _peaks_built = false; + _peak_byte_max = 0; + } else { + _peaks_built = true; + _peak_byte_max = statbuf.st_size; + } } } } diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index 45b19d1997..8b8b843384 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -422,9 +422,9 @@ Automatable::protect_automation () } void -Automatable::automation_snapshot (nframes_t now) +Automatable::automation_snapshot (nframes_t now, bool force) { - if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { + if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { for (Controls::iterator i = _controls.begin(); i != _controls.end(); ++i) { if (i->second->list()->automation_write()) { diff --git a/libs/ardour/filter.cc b/libs/ardour/filter.cc index dc8185db95..be382f72da 100644 --- a/libs/ardour/filter.cc +++ b/libs/ardour/filter.cc @@ -28,6 +28,7 @@ #include <ardour/filter.h> #include <ardour/region_factory.h> #include <ardour/source_factory.h> +#include <ardour/analyser.h> #include "i18n.h" @@ -101,6 +102,10 @@ Filter::finish (boost::shared_ptr<Region> region, SourceList& nsrcs, string regi smfs->set_timeline_position (region->position()); smfs->flush_footer (); } + + /* now that there is data there, requeue the file for analysis */ + + Analyser::queue_source_for_analysis (*si, false); } /* create a new region */ diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 6aba688cae..02c4a5ced6 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -19,9 +19,12 @@ #include <cstdio> // Needed so that libraptor (included in lrdf) won't complain #include <sys/stat.h> #include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> #include <unistd.h> #include <fcntl.h> #include <locale.h> +#include <errno.h> #ifdef VST_SUPPORT #include <fst.h> @@ -31,12 +34,16 @@ #include <xmmintrin.h> #endif +#include <glibmm/fileutils.h> +#include <glibmm/miscutils.h> + #include <lrdf.h> #include <pbd/error.h> #include <pbd/id.h> #include <pbd/strsplit.h> #include <pbd/fpu.h> +#include <pbd/file_utils.h> #include <midi++/port.h> #include <midi++/manager.h> @@ -54,6 +61,7 @@ #include <ardour/source_factory.h> #include <ardour/control_protocol_manager.h> #include <ardour/audioengine.h> +#include <ardour/filesystem_paths.h> #ifdef HAVE_LIBLO #include <ardour/osc.h> @@ -96,6 +104,8 @@ apply_gain_to_buffer_t ARDOUR::apply_gain_to_buffer = 0; mix_buffers_with_gain_t ARDOUR::mix_buffers_with_gain = 0; mix_buffers_no_gain_t ARDOUR::mix_buffers_no_gain = 0; +sigc::signal<void,std::string> ARDOUR::BootMessage; + #ifdef HAVE_LIBLO static int setup_osc () @@ -107,6 +117,7 @@ setup_osc () osc = new OSC (Config->get_osc_port()); if (Config->get_use_osc ()) { + BootMessage (_("Starting OSC")); return osc->start (); } else { return 0; @@ -122,6 +133,8 @@ setup_midi () return 0; } + BootMessage (_("Configuring MIDI ports")); + for (std::map<string,XMLNode>::iterator i = Config->midi_ports.begin(); i != Config->midi_ports.end(); ++i) { MIDI::Manager::instance()->add_port (i->second); } @@ -253,6 +266,33 @@ setup_hardware_optimization (bool try_optimization) } } +static void +lotsa_files_please () +{ + struct rlimit rl; + + if (getrlimit (RLIMIT_NOFILE, &rl) == 0) { + + rl.rlim_cur = rl.rlim_max; + + if (setrlimit (RLIMIT_NOFILE, &rl) != 0) { + if (rl.rlim_cur == RLIM_INFINITY) { + error << _("Could not set system open files limit to \"unlimited\"") << endmsg; + } else { + error << string_compose (_("Could not set system open files limit to %1"), rl.rlim_cur) << endmsg; + } + } else { + if (rl.rlim_cur == RLIM_INFINITY) { + info << _("Removed open file count limit. Excellent!") << endmsg; + } else { + info << string_compose (_("Ardour will be limited to %1 open files"), rl.rlim_cur) << endmsg; + } + } + } else { + error << string_compose (_("Could not get system open files limit (%1)"), strerror (errno)) << endmsg; + } +} + int ARDOUR::init (bool use_vst, bool try_optimization) { @@ -262,9 +302,14 @@ ARDOUR::init (bool use_vst, bool try_optimization) setup_enum_writer (); + // allow ardour the absolute maximum number of open files + lotsa_files_please (); + lrdf_init(); Library = new AudioLibrary; + BootMessage (_("Loading configuration")); + Config = new Configuration; if (Config->load_state ()) { @@ -367,6 +412,34 @@ ARDOUR::get_ardour_revision () return "$Rev$"; } +void +ARDOUR::find_bindings_files (map<string,string>& files) +{ + vector<sys::path> found; + + SearchPath spath = ardour_search_path() + user_config_directory() + system_config_search_path(); + + if (getenv ("ARDOUR_SAE")) { + Glib::PatternSpec pattern("*SAE-*.bindings"); + find_matching_files_in_search_path (spath, pattern, found); + } else { + Glib::PatternSpec pattern("*.bindings"); + find_matching_files_in_search_path (spath, pattern, found); + } + + if (found.empty()) { + return; + } + + for (vector<sys::path>::iterator x = found.begin(); x != found.end(); ++x) { + sys::path path = *x; + pair<string,string> namepath; + namepath.second = path.to_string(); + namepath.first = path.leaf().substr (0, path.leaf().find_first_of ('.')); + files.insert (namepath); + } +} + ARDOUR::LocaleGuard::LocaleGuard (const char* str) { old = strdup (setlocale (LC_NUMERIC, NULL)); diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index 91e5b9850c..2bc3a00a7a 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -46,6 +46,7 @@ #include <ardour/region_factory.h> #include <ardour/source_factory.h> #include <ardour/resampled_source.h> +#include <ardour/analyser.h> #include "i18n.h" @@ -65,7 +66,7 @@ open_importable_source (const string& path, nframes_t samplerate, ARDOUR::SrcQua } static std::string -get_non_existent_filename (const std::string& basename, uint channel, uint channels) +get_non_existent_filename (const bool allow_replacing, const std::string destdir, const std::string& basename, uint channel, uint channels) { char buf[PATH_MAX+1]; bool goodfile = false; @@ -84,7 +85,9 @@ get_non_existent_filename (const std::string& basename, uint channel, uint chann snprintf (buf, sizeof(buf), "%s.wav", base.c_str()); } - if (Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { + + string tempname = destdir + "/" + buf; + if (!allow_replacing && Glib::file_test (tempname, Glib::FILE_TEST_EXISTS)) { /* if the file already exists, we must come up with * a new name for it. for now we just keep appending @@ -104,7 +107,7 @@ get_non_existent_filename (const std::string& basename, uint channel, uint chann } static vector<string> -get_paths_for_new_sources (const string& import_file_path, const string& session_dir, uint channels) +get_paths_for_new_sources (const bool allow_replacing, const string& import_file_path, const string& session_dir, uint channels) { vector<string> new_paths; const string basename = basename_nosuffix (import_file_path); @@ -115,7 +118,7 @@ get_paths_for_new_sources (const string& import_file_path, const string& session filepath = session_dir; filepath += '/'; - filepath += get_non_existent_filename (basename, n, channels); + filepath += get_non_existent_filename (allow_replacing, session_dir, basename, n, channels); new_paths.push_back (filepath); } @@ -124,6 +127,25 @@ get_paths_for_new_sources (const string& import_file_path, const string& session } static bool +map_existing_mono_sources (const vector<string>& new_paths, Session& sess, + uint samplerate, vector<boost::shared_ptr<AudioFileSource> >& newfiles, Session *session) +{ + for (vector<string>::const_iterator i = new_paths.begin(); + i != new_paths.end(); ++i) + { + boost::shared_ptr<Source> source = session->source_by_path_and_channel(*i, 0); + + if (source == 0) { + error << string_compose(_("Could not find a source for %1 even though we are updating this file!"), (*i)) << endl; + return false; + } + + newfiles.push_back(boost::dynamic_pointer_cast<AudioFileSource>(source)); + } + return true; +} + +static bool create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess, uint samplerate, vector<boost::shared_ptr<AudioFileSource> >& newfiles) { @@ -228,6 +250,10 @@ remove_file_source (boost::shared_ptr<AudioFileSource> file_source) ::unlink (file_source->path().c_str()); } +// This function is still unable to cleanly update an existing source, even though +// it is possible to set the import_status flag accordingly. The functinality +// is disabled at the GUI until the Source implementations are able to provide +// the necessary API. void Session::import_audiofiles (import_status& status) { @@ -254,13 +280,19 @@ Session::import_audiofiles (import_status& status) return; } - vector<string> new_paths = get_paths_for_new_sources (*p, + vector<string> new_paths = get_paths_for_new_sources (status.replace_existing_source, + *p, get_best_session_directory_for_new_source (), source->channels()); AudioSources newfiles; - status.cancel = !create_mono_sources_for_writing (new_paths, *this, frame_rate(), newfiles); + if (status.replace_existing_source) { + fatal << "THIS IS NOT IMPLEMENTED YET, IT SHOULD NEVER GET CALLED!!! DYING!" << endl; + status.cancel = !map_existing_mono_sources (new_paths, *this, frame_rate(), newfiles, this); + } else { + status.cancel = !create_mono_sources_for_writing (new_paths, *this, frame_rate(), newfiles); + } // copy on cancel/failure so that any files that were created will be removed below std::copy (newfiles.begin(), newfiles.end(), std::back_inserter(all_new_sources)); @@ -286,11 +318,14 @@ Session::import_audiofiles (import_status& status) /* flush the final length(s) to the header(s) */ - for (AudioSources::iterator x = all_new_sources.begin(); - x != all_new_sources.end(); ++x) + for (AudioSources::iterator x = all_new_sources.begin(); x != all_new_sources.end(); ++x) { (*x)->update_header(0, *now, xnow); (*x)->done_with_peakfile_writes (); + + /* now that there is data there, requeue the file for analysis */ + + Analyser::queue_source_for_analysis (boost::static_pointer_cast<Source>(*x), false); } /* save state so that we don't lose these new Sources */ diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index babf1e451f..f57d5b39de 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -32,6 +32,7 @@ #include <ardour/audioengine.h> #include <ardour/io.h> +#include <ardour/route.h> #include <ardour/port.h> #include <ardour/audio_port.h> #include <ardour/midi_port.h> @@ -1800,14 +1801,22 @@ IO::parse_gain_string (const string& str, vector<string>& ports) } bool -IO::set_name (const string& str) +IO::set_name (const string& requested_name) { - if (str == _name) { + if (requested_name == _name) { return true; } + string name; + Route *rt; + if ( (rt = dynamic_cast<Route *>(this))) { + name = Route::ensure_track_or_route_name(requested_name, _session); + } else { + name = requested_name; + } + + /* replace all colons in the name. i wish we didn't have to do this */ - string name = str; if (replace_all (name, ":", "-")) { warning << _("you cannot use colons to name objects with I/O connections") << endmsg; @@ -2224,13 +2233,16 @@ IO::end_pan_touch (uint32_t which) } void -IO::automation_snapshot (nframes_t now) +IO::automation_snapshot (nframes_t now, bool force) { - Automatable::automation_snapshot (now); + Automatable::automation_snapshot (now, force); if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { _panner->snapshot (now); } + + _panner->snapshot (now); + _last_automation_snapshot = now; } void diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index da3a812123..65279499ec 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -1814,50 +1814,12 @@ Playlist::bump_name (string name, Session &session) string newname = name; do { - newname = Playlist::bump_name_once (newname); + newname = bump_name_once (newname); } while (session.playlist_by_name (newname)!=NULL); return newname; } -string -Playlist::bump_name_once (string name) -{ - string::size_type period; - string newname; - - if ((period = name.find_last_of ('.')) == string::npos) { - newname = name; - newname += ".1"; - } else { - int isnumber = 1; - const char *last_element = name.c_str() + period + 1; - for (size_t i = 0; i < strlen(last_element); i++) { - if (!isdigit(last_element[i])) { - isnumber = 0; - break; - } - } - - errno = 0; - long int version = strtol (name.c_str()+period+1, (char **)NULL, 10); - - if (isnumber == 0 || errno != 0) { - // last_element is not a number, or is too large - newname = name; - newname += ".1"; - } else { - char buf[32]; - - snprintf (buf, sizeof(buf), "%ld", version+1); - - newname = name.substr (0, period+1); - newname += buf; - } - } - - return newname; -} layer_t Playlist::top_layer() const diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc index 95629f2561..3d218448dd 100644 --- a/libs/ardour/plugin.cc +++ b/libs/ardour/plugin.cc @@ -224,6 +224,19 @@ ARDOUR::find_plugin(Session& session, string identifier, PluginType type) return (*i)->load (session); } } + +#ifdef VST_SUPPORT + /* hmm, we didn't find it. could be because in older versions of Ardour. + we used to store the name of a VST plugin, not its unique ID. so try + again. + */ + + for (i = plugs.begin(); i != plugs.end(); ++i) { + if (identifier == (*i)->name){ + return (*i)->load (session); + } + } +#endif return PluginPtr ((Plugin*) 0); } diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc index 2193a69908..508f09a480 100644 --- a/libs/ardour/region.cc +++ b/libs/ardour/region.cc @@ -957,14 +957,14 @@ Region::adjust_to_sync (nframes_t pos) // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl; if (sync_dir > 0) { - if (max_frames - pos > offset) { + if (pos > offset) { pos -= offset; + } else { + pos = 0; } } else { - if (pos > offset) { + if (max_frames - pos > offset) { pos += offset; - } else { - pos = 0; } } diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index fd0d99e87a..560946d9da 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -46,6 +46,9 @@ #include <ardour/amp.h> #include <ardour/meter.h> #include <ardour/buffer_set.h> +#include <ardour/mix.h> +#include <ardour/profile.h> + #include "i18n.h" using namespace std; @@ -190,6 +193,20 @@ Route::sync_order_keys () } } +string +Route::ensure_track_or_route_name(string name, Session &session) +{ + string newname = name; + + while (session.route_by_name (newname)!=NULL) + { + newname = bump_name_once (newname); + } + + return newname; +} + + void Route::inc_gain (gain_t fraction, void *src) { @@ -843,8 +860,8 @@ Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* reset_panner (); } - processors_changed (); /* EMIT SIGNAL */ + return 0; } @@ -2291,7 +2308,7 @@ Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_f Glib::RWLock::ReaderLock lm (_processor_lock); if (!did_locate) { - automation_snapshot (now); + automation_snapshot (now, true); } for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { @@ -2398,7 +2415,7 @@ Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nfra if (lm.locked()) { // automation snapshot can also be called from the non-rt context // and it uses the processor list, so we take the lock out here - automation_snapshot (_session.transport_frame()); + automation_snapshot (_session.transport_frame(), false); } } @@ -2569,12 +2586,15 @@ Route::set_latency_delay (nframes_t longest_session_latency) } void -Route::automation_snapshot (nframes_t now) +Route::automation_snapshot (nframes_t now, bool force) { - IO::automation_snapshot (now); + if (!force && !should_snapshot(now)) { + return; + } for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) { - (*i)->automation_snapshot (now); + // IO::automation_snapshot (now, force); ? + (*i)->automation_snapshot (now, force); } } diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 5a3144b828..4cfcda992e 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -504,9 +504,13 @@ Session::when_engine_running () /* we don't want to run execute this again */ + BootMessage (_("Set block size and sample rate")); + set_block_size (_engine.frames_per_cycle()); set_frame_rate (_engine.frame_rate()); + BootMessage (_("Using configuration")); + Config->map_parameters (mem_fun (*this, &Session::config_changed)); /* every time we reconnect, recompute worst case output latencies */ @@ -562,6 +566,8 @@ Session::when_engine_running () error << _("cannot setup Click I/O") << endmsg; } + BootMessage (_("Compute I/O Latencies")); + set_worst_io_latencies (); if (_clicking) { @@ -572,6 +578,8 @@ Session::when_engine_running () to the physical outputs currently available */ + BootMessage (_("Set up standard connections")); + /* ONE: MONO */ for (uint32_t np = 0; np < n_physical_outputs; ++np) { @@ -672,11 +680,15 @@ Session::when_engine_running () } add_bundle (c); } + + BootMessage (_("Connect ports")); hookup_io (); /* catch up on send+insert cnts */ + BootMessage (_("Catch up with send/insert state")); + insert_cnt = 0; for (list<PortInsert*>::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) { @@ -704,14 +716,17 @@ Session::when_engine_running () _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty)); - /* hook us up to the engine */ + BootMessage (_("Connect to engine")); + _engine.set_session (this); #ifdef HAVE_LIBLO /* and to OSC */ + BootMessage (_("OSC startup")); + osc->set_session (*this); #endif @@ -2546,41 +2561,54 @@ Session::region_name (string& result, string base, bool newlevel) const void Session::add_region (boost::shared_ptr<Region> region) { + vector<boost::shared_ptr<Region> > v; + v.push_back (region); + add_regions (v); +} + +void +Session::add_regions (vector<boost::shared_ptr<Region> >& new_regions) +{ bool added = false; { Glib::Mutex::Lock lm (region_lock); - if (region == 0) { - error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; - } else { - - RegionList::iterator x; - - for (x = regions.begin(); x != regions.end(); ++x) { - - if (region->region_list_equivalent (x->second)) { - break; - } - } - - if (x == regions.end()) { - - pair<RegionList::key_type,RegionList::mapped_type> entry; - - entry.first = region->id(); - entry.second = region; + for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { + + boost::shared_ptr<Region> region = *ii; + + if (region == 0) { - pair<RegionList::iterator,bool> x = regions.insert (entry); + error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; + } else { - if (!x.second) { - return; + RegionList::iterator x; + + for (x = regions.begin(); x != regions.end(); ++x) { + + if (region->region_list_equivalent (x->second)) { + break; + } } - - added = true; - } - + + if (x == regions.end()) { + + pair<RegionList::key_type,RegionList::mapped_type> entry; + + entry.first = region->id(); + entry.second = region; + + pair<RegionList::iterator,bool> x = regions.insert (entry); + + if (!x.second) { + return; + } + + added = true; + } + } } } @@ -2591,9 +2619,33 @@ Session::add_region (boost::shared_ptr<Region> region) set_dirty(); if (added) { - region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr<Region>(region))); - region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr<Region>(region))); - RegionAdded (region); /* EMIT SIGNAL */ + + vector<boost::weak_ptr<Region> > v; + boost::shared_ptr<Region> first_r; + + for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) { + + boost::shared_ptr<Region> region = *ii; + + if (region == 0) { + + error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg; + + } else { + v.push_back (region); + + if (!first_r) { + first_r = region; + } + } + + region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr<Region>(region))); + region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr<Region>(region))); + } + + if (!v.empty()) { + RegionsAdded (v); /* EMIT SIGNAL */ + } } } @@ -2680,18 +2732,12 @@ Session::destroy_region (boost::shared_ptr<Region> region) vector<boost::shared_ptr<Source> > srcs; { - boost::shared_ptr<AudioRegion> aregion; - - if ((aregion = boost::dynamic_pointer_cast<AudioRegion> (region)) == 0) { - return 0; - } - - if (aregion->playlist()) { - aregion->playlist()->destroy_region (region); + if (region->playlist()) { + region->playlist()->destroy_region (region); } - for (uint32_t n = 0; n < aregion->n_channels(); ++n) { - srcs.push_back (aregion->source (n)); + for (uint32_t n = 0; n < region->n_channels(); ++n) { + srcs.push_back (region->source (n)); } } @@ -2699,17 +2745,10 @@ Session::destroy_region (boost::shared_ptr<Region> region) for (vector<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) { - if (!(*i)->used()) { - boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*i); - - if (afs) { - (afs)->mark_for_remove (); - } - + (*i)->mark_for_remove (); (*i)->drop_references (); cerr << "source was not used by any playlist\n"; - } } return 0; diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index af4be07dc4..c4448640fd 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -350,11 +350,15 @@ Session::second_stage_init (bool new_session) return -1; } + BootMessage (_("Reset Remote Controls")); + //send_full_time_code (); _engine.transport_locate (0); deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0); deliver_mmc (MIDI::MachineControl::cmdLocate, 0); + BootMessage (_("Reset Control Protocols")); + ControlProtocolManager::instance().set_session (*this); if (new_session) { @@ -364,7 +368,6 @@ Session::second_stage_init (bool new_session) } _state_of_the_state = Clean; - DirtyChanged (); /* EMIT SIGNAL */ @@ -374,6 +377,8 @@ Session::second_stage_init (bool new_session) state_was_pending = false; } + BootMessage (_("Session loading complete")); + return 0; } @@ -1373,6 +1378,8 @@ Session::load_routes (const XMLNode& node) return -1; } + BootMessage (string_compose (_("Loaded track/bus %1"), route->name())); + new_routes.push_back (route); } diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc index d422698f30..e6318f5503 100644 --- a/libs/ardour/session_transport.cc +++ b/libs/ardour/session_transport.cc @@ -1267,9 +1267,9 @@ Session::engine_halted () void Session::xrun_recovery () { - if (Config->get_stop_recording_on_xrun() && actively_recording()) { + Xrun (transport_frame()); //EMIT SIGNAL - HaltOnXrun (); /* EMIT SIGNAL */ + if (Config->get_stop_recording_on_xrun() && actively_recording()) { /* it didn't actually halt, but we need to handle things in the same way. diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc index 7941eb693b..f08d08b86e 100644 --- a/libs/ardour/source.cc +++ b/libs/ardour/source.cc @@ -195,6 +195,7 @@ Source::set_been_analysed (bool yn) } if (yn) { + load_transients (get_transients_path()); AnalysisChanged(); // EMIT SIGNAL } } @@ -265,3 +266,4 @@ Source::check_for_analysis_data_on_disk () set_been_analysed (ok); return ok; } + diff --git a/libs/ardour/transient_detector.cc b/libs/ardour/transient_detector.cc index b92bf5fb2d..d24c4c9442 100644 --- a/libs/ardour/transient_detector.cc +++ b/libs/ardour/transient_detector.cc @@ -6,18 +6,20 @@ using namespace Vamp; using namespace ARDOUR; using namespace std; -string TransientDetector::_op_id; +/* need a static initializer function for this */ + +string TransientDetector::_op_id = X_("libardourvampplugins:percussiononsets:2"); TransientDetector::TransientDetector (float sr) : AudioAnalyser (sr, X_("libardourvampplugins:percussiononsets")) { - if (_op_id.empty()) { - _op_id = X_("libardourvampplugins:percussiononsets"); - - // XXX this should load the above-named plugin and get the current version + /* update the op_id */ - _op_id += ":2"; - } + _op_id = X_("libardourvampplugins:percussiononsets"); + + // XXX this should load the above-named plugin and get the current version + + _op_id += ":2"; } TransientDetector::~TransientDetector() diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index cfd38c5099..99a6dbbc88 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -86,6 +86,45 @@ legalize_for_path (string str) } #endif +string bump_name_once(std::string name) +{ + string::size_type period; + string newname; + + if ((period = name.find_last_of ('.')) == string::npos) { + newname = name; + newname += ".1"; + } else { + int isnumber = 1; + const char *last_element = name.c_str() + period + 1; + for (size_t i = 0; i < strlen(last_element); i++) { + if (!isdigit(last_element[i])) { + isnumber = 0; + break; + } + } + + errno = 0; + long int version = strtol (name.c_str()+period+1, (char **)NULL, 10); + + if (isnumber == 0 || errno != 0) { + // last_element is not a number, or is too large + newname = name; + newname += ".1"; + } else { + char buf[32]; + + snprintf (buf, sizeof(buf), "%ld", version+1); + + newname = name.substr (0, period+1); + newname += buf; + } + } + + return newname; + +} + ostream& operator<< (ostream& o, const BBT_Time& bbt) { @@ -289,6 +328,8 @@ string_to_edit_mode (string str) return Splice; } else if (str == _("Slide Edit")) { return Slide; + } else if (str == _("Lock Edit")) { + return Lock; } fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg; /*NOTREACHED*/ @@ -302,6 +343,9 @@ edit_mode_to_string (EditMode mode) case Slide: return _("Slide Edit"); + case Lock: + return _("Lock Edit"); + default: case Splice: return _("Splice Edit"); diff --git a/libs/gtkmm2/pango/SConscript b/libs/gtkmm2/pango/SConscript index 1caa21376c..b08b8b2145 100644 --- a/libs/gtkmm2/pango/SConscript +++ b/libs/gtkmm2/pango/SConscript @@ -10,6 +10,9 @@ Import('env libraries install_prefix') pangomm = env.Copy() pangomm.Merge([libraries['glibmm2'], libraries['pango'], libraries['sigc2'], libraries['cairomm'], libraries['cairo']]) +if pangomm['IS_OSX']: + pangomm.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048") + libpangomm = pangomm.SharedLibrary('pangomm', pangomm_files) Default(libpangomm) diff --git a/libs/gtkmm2ext/SConscript b/libs/gtkmm2ext/SConscript index 829182c71e..fad538a40e 100644 --- a/libs/gtkmm2ext/SConscript +++ b/libs/gtkmm2ext/SConscript @@ -32,6 +32,8 @@ gtkmm2ext.Append(CXXFLAGS=["-DLIBSIGC_DISABLE_DEPRECATED", "-DGLIBMM_DEFAULT_SIG gtkmm2ext.Append(PACKAGE=domain) gtkmm2ext.Append(POTFILE=domain + '.pot') +extra_sources = [] + gtkmm2ext_files = Split(""" auto_spin.cc barcontroller.cc @@ -61,12 +63,21 @@ version.cc window_title.cc """) +gtkosx_files=Split(""" +sync-menu.c +""") + +if gtkmm2ext['GTKOSX']: + extra_sources += gtkosx_files + gtkmm2ext.Append (CCFLAGS="-DTOP_MENUBAR -DGTKOSX") + gtkmm2ext.Append (LINKFLAGS="-framework Carbon") + gtkmm2ext.VersionBuild(['version.cc','gtkmm2ext/version.h'], []) gtkmm2ext.Append(CCFLAGS="-D_REENTRANT") gtkmm2ext.Append(CCFLAGS="-DLOCALEDIR=\\\""+final_prefix+"/share/locale\\\"") -libgtkmm2ext = gtkmm2ext.SharedLibrary('gtkmm2ext', gtkmm2ext_files) +libgtkmm2ext = gtkmm2ext.SharedLibrary('gtkmm2ext', gtkmm2ext_files + extra_sources) Default(libgtkmm2ext) @@ -78,5 +89,6 @@ env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ar env.Alias('tarball', env.Distribute (env['DISTTREE'], [ 'SConscript', 'i18n.h', 'gettext.h'] + gtkmm2ext_files + + gtkosx_files + glob.glob('po/*.po') + glob.glob('gtkmm2ext/*.h'))) diff --git a/libs/gtkmm2ext/gtk_ui.cc b/libs/gtkmm2ext/gtk_ui.cc index 660ea32ad1..f00c6cd1e1 100644 --- a/libs/gtkmm2ext/gtk_ui.cc +++ b/libs/gtkmm2ext/gtk_ui.cc @@ -560,10 +560,31 @@ UI::popup_error (const char *text) pup->touch (); } +#ifdef GTKOSX +extern "C" { + int gdk_quartz_in_carbon_menu_event_handler (); +} +#endif void UI::flush_pending () { +#ifdef GTKOSX + /* as of february 11th 2008, gtk/osx has a problem in that mac menu events + are handled using Carbon with an "internal" event handling system that + doesn't pass things back to the glib/gtk main loop. this makes + gtk_main_iteration() block if we call it while in a menu event handler + because glib gets confused and thinks there are two threads running + g_main_poll_func(). + + this hack (relies on code in gtk2_ardour/sync-menu.c) works + around that. + */ + + if (gdk_quartz_in_carbon_menu_event_handler()) { + return; + } +#endif if (!caller_is_ui_thread()) { error << "non-UI threads cannot call UI::flush_pending()" << endmsg; diff --git a/libs/gtkmm2ext/gtkmm2ext/pixfader.h b/libs/gtkmm2ext/gtkmm2ext/pixfader.h index d974f5d5bc..519e1c9e1e 100644 --- a/libs/gtkmm2ext/gtkmm2ext/pixfader.h +++ b/libs/gtkmm2ext/gtkmm2ext/pixfader.h @@ -30,7 +30,7 @@ namespace Gtkmm2ext { class PixFader : public Gtk::DrawingArea { public: - PixFader (Glib::RefPtr<Gdk::Pixbuf> belt_image, Gtk::Adjustment& adjustment); + PixFader (Glib::RefPtr<Gdk::Pixbuf> belt_image, Gtk::Adjustment& adjustment, int orientation); virtual ~PixFader (); protected: @@ -44,23 +44,32 @@ class PixFader : public Gtk::DrawingArea { bool on_motion_notify_event (GdkEventMotion*); bool on_scroll_event (GdkEventScroll* ev); - private: + enum Orientation { + VERT=1, + HORIZ=2, + }; + + private: Glib::RefPtr<Gdk::Pixbuf> pixbuf; - gint pixheight; + int span, girth; + int _orien; GdkRectangle view; GdkWindow* grab_window; - double grab_y; + double grab_loc; double grab_start; int last_drawn; bool dragging; float default_value; - int unity_y; + int unity_loc; void adjustment_changed (); - int display_height (); + int display_span (); + + static int fine_scale_modifier; + static int extra_fine_scale_modifier; }; diff --git a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h index 74ff36816b..60c8eef660 100644 --- a/libs/gtkmm2ext/gtkmm2ext/slider_controller.h +++ b/libs/gtkmm2ext/gtkmm2ext/slider_controller.h @@ -38,7 +38,7 @@ class SliderController : public Gtkmm2ext::PixFader { public: SliderController (Glib::RefPtr<Gdk::Pixbuf> image, - Gtk::Adjustment* adj, + Gtk::Adjustment* adj, int orientation, PBD::Controllable&, bool with_numeric = true); diff --git a/libs/gtkmm2ext/gtkmm2ext/sync-menu.h b/libs/gtkmm2ext/gtkmm2ext/sync-menu.h new file mode 100644 index 0000000000..2be5e71d02 --- /dev/null +++ b/libs/gtkmm2ext/gtkmm2ext/sync-menu.h @@ -0,0 +1,44 @@ +/* GTK+ Integration for the Mac OS X Menubar. + * + * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * Copyright (C) 2007 Imendio AB + * + * For further information, see: + * http://developer.imendio.com/projects/gtk-macosx/menubar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 + * of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __IGE_MAC_MENU_H__ +#define __IGE_MAC_MENU_H__ + +#include <gtk/gtk.h> + +G_BEGIN_DECLS + +typedef struct _IgeMacMenuGroup IgeMacMenuGroup; + +void ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell); +void ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item); + +IgeMacMenuGroup * ige_mac_menu_add_app_menu_group (void); +void ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group, + GtkMenuItem *menu_item, + const gchar *label); + +G_END_DECLS + +#endif /* __IGE_MAC_MENU_H__ */ diff --git a/libs/gtkmm2ext/pixfader.cc b/libs/gtkmm2ext/pixfader.cc index f3a40ffc69..fe464d0fac 100644 --- a/libs/gtkmm2ext/pixfader.cc +++ b/libs/gtkmm2ext/pixfader.cc @@ -27,21 +27,36 @@ using namespace Gtk; using namespace Gdk; using namespace std; -PixFader::PixFader (Glib::RefPtr<Pixbuf> belt, Gtk::Adjustment& adj) +#ifdef GTKOSX +int PixFader::fine_scale_modifier = GDK_META_MASK; +#else +int PixFader::fine_scale_modifier = GDK_CONTROL_MASK; +#endif + +int PixFader::extra_fine_scale_modifier = GDK_MOD1_MASK; + +PixFader::PixFader (Glib::RefPtr<Pixbuf> belt, Gtk::Adjustment& adj, int orientation) + : adjustment (adj), - pixbuf (belt) + pixbuf (belt), + _orien(orientation) { dragging = false; default_value = adjustment.get_value(); last_drawn = -1; - pixheight = pixbuf->get_height(); view.x = 0; view.y = 0; - view.width = pixbuf->get_width(); - view.height = pixheight / 2; - unity_y = (int) rint (view.height - (default_value * view.height)) - 1; + if (orientation == VERT) { + view.width = girth = pixbuf->get_width(); + view.height = span = pixbuf->get_height() / 2; + unity_loc = (int) rint (view.height - (default_value * view.height)) - 1; + } else { + view.width = span = pixbuf->get_width () / 2; + view.height = girth = pixbuf->get_height(); + unity_loc = (int) rint (default_value * view.width) - 1; + } add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK|Gdk::SCROLL_MASK); @@ -57,13 +72,20 @@ bool PixFader::on_expose_event (GdkEventExpose* ev) { GdkRectangle intersection; - int dh = display_height (); - int offset_into_pixbuf = (int) floor (view.height / ((float) view.height / dh)); + int srcx, srcy, ds = display_span (); + int offset_into_pixbuf = (int) floor (span / ((float) span / ds)); Glib::RefPtr<Gdk::GC> fg_gc (get_style()->get_fg_gc(get_state())); if (gdk_rectangle_intersect (&view, &ev->area, &intersection)) { + if (_orien == VERT) { + srcx = intersection.x; + srcy = offset_into_pixbuf + intersection.y; + } else { + srcx = offset_into_pixbuf + intersection.x; + srcy = intersection.y; + } get_window()->draw_pixbuf (fg_gc, pixbuf, - intersection.x, offset_into_pixbuf + intersection.y, + srcx, srcy, intersection.x, intersection.y, intersection.width, intersection.height, Gdk::RGB_DITHER_NONE, 0, 0); @@ -75,10 +97,12 @@ PixFader::on_expose_event (GdkEventExpose* ev) } /* always draw the line */ - - get_window()->draw_line (fg_gc, 1, unity_y, view.width - 2, unity_y); - - last_drawn = dh; + if (_orien == VERT) { + get_window()->draw_line (fg_gc, 1, unity_loc, girth - 2, unity_loc); + } else { + get_window()->draw_line (fg_gc, unity_loc, 1, unity_loc, girth - 2); + } + last_drawn = ds; return true; } @@ -96,8 +120,8 @@ PixFader::on_button_press_event (GdkEventButton* ev) case 1: case 2: add_modal_grab(); - grab_y = ev->y; - grab_start = ev->y; + grab_loc = (_orien == VERT) ? ev->y : ev->x; + grab_start = (_orien == VERT) ? ev->y : ev->x; grab_window = ev->window; dragging = true; break; @@ -112,7 +136,9 @@ PixFader::on_button_press_event (GdkEventButton* ev) bool PixFader::on_button_release_event (GdkEventButton* ev) { - double fract; + double fract, ev_pos; + + ev_pos = (_orien == VERT) ? ev->y : 0; // Don't step if we are horizontal switch (ev->button) { case 1: @@ -120,15 +146,15 @@ PixFader::on_button_release_event (GdkEventButton* ev) remove_modal_grab(); dragging = false; - if (ev->y == grab_start) { + if (ev_pos == grab_start) { /* no motion - just a click */ if (ev->state & Gdk::SHIFT_MASK) { adjustment.set_value (default_value); - } else if (ev->state & GDK_CONTROL_MASK) { + } else if (ev->state & fine_scale_modifier) { adjustment.set_value (adjustment.get_lower()); - } else if (ev->y < view.height - display_height()) { + } else if (ev_pos < span - display_span()) { /* above the current display height, remember X Window coords */ adjustment.set_value (adjustment.get_value() + adjustment.get_step_increment()); } else { @@ -144,7 +170,7 @@ PixFader::on_button_release_event (GdkEventButton* ev) remove_modal_grab(); dragging = false; - fract = 1.0 - (ev->y / view.height); // inverted X Window coordinates, grrr + fract = 1.0 - (ev_pos / span); // inverted X Window coordinates, grrr fract = min (1.0, fract); fract = max (0.0, fract); @@ -164,29 +190,45 @@ bool PixFader::on_scroll_event (GdkEventScroll* ev) { double scale; - - if (ev->state & GDK_CONTROL_MASK) { - if (ev->state & GDK_MOD1_MASK) { - scale = 0.05; + + if (ev->state & fine_scale_modifier) { + if (ev->state & extra_fine_scale_modifier) { + scale = 0.01; } else { - scale = 0.1; + scale = 0.05; } } else { - scale = 0.5; + scale = 0.25; } - switch (ev->direction) { - - case GDK_SCROLL_UP: - /* wheel up */ - adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale)); - break; - case GDK_SCROLL_DOWN: - /* wheel down */ - adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale)); - break; - default: - break; + if (_orien == VERT) { + switch (ev->direction) { + + case GDK_SCROLL_UP: + /* wheel up */ + adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale)); + break; + case GDK_SCROLL_DOWN: + /* wheel down */ + adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale)); + break; + default: + break; + } + } else { + switch (ev->direction) { + + case GDK_SCROLL_RIGHT: + /* wheel right */ + adjustment.set_value (adjustment.get_value() + (adjustment.get_page_increment() * scale)); + break; + case GDK_SCROLL_LEFT: + /* wheel left */ + adjustment.set_value (adjustment.get_value() - (adjustment.get_page_increment() * scale)); + break; + default: + break; + } } return false; } @@ -195,18 +237,17 @@ bool PixFader::on_motion_notify_event (GdkEventMotion* ev) { if (dragging) { - double fract; - double delta; - double scale; - + double fract, delta, scale, ev_pos; + ev_pos = (_orien == VERT) ? ev->y : ev->x; + //cerr << "PixFader::on_motion_notify_event() called x:y = " << ev->x << ":" << ev->y; if (ev->window != grab_window) { - grab_y = ev->y; + grab_loc = ev_pos; grab_window = ev->window; return true; } - if (ev->state & GDK_CONTROL_MASK) { - if (ev->state & GDK_MOD1_MASK) { + if (ev->state & fine_scale_modifier) { + if (ev->state & extra_fine_scale_modifier) { scale = 0.05; } else { scale = 0.1; @@ -214,20 +255,23 @@ PixFader::on_motion_notify_event (GdkEventMotion* ev) } else { scale = 1.0; } + //cerr << " ev_pos=" << ev_pos << " grab_loc=" << grab_loc; + delta = ev_pos - grab_loc; + grab_loc = ev_pos; - delta = ev->y - grab_y; - grab_y = ev->y; - - fract = (delta / view.height); + fract = (delta / span); fract = min (1.0, fract); fract = max (-1.0, fract); // X Window is top->bottom for 0..Y - fract = -fract; + if (_orien == VERT) { + fract = -fract; + } adjustment.set_value (adjustment.get_value() + scale * fract * (adjustment.get_upper() - adjustment.get_lower())); + //cerr << " adj=" << adjustment.get_value() << " fract=" << fract << " delta=" << delta << " scale=" << scale << endl; } return true; @@ -236,14 +280,15 @@ PixFader::on_motion_notify_event (GdkEventMotion* ev) void PixFader::adjustment_changed () { - if (display_height() != last_drawn) { + if (display_span() != last_drawn) { queue_draw (); } } int -PixFader::display_height () +PixFader::display_span () { float fract = (adjustment.get_upper() - adjustment.get_value ()) / ((adjustment.get_upper() - adjustment.get_lower())); - return (int) floor (view.height * (1.0 - fract)); + return (_orien == VERT) ? (int)floor (span * (1.0 - fract)) : (int)floor (span * fract); } + diff --git a/libs/gtkmm2ext/slider_controller.cc b/libs/gtkmm2ext/slider_controller.cc index 3e2b42f409..93dfb27ae2 100644 --- a/libs/gtkmm2ext/slider_controller.cc +++ b/libs/gtkmm2ext/slider_controller.cc @@ -29,11 +29,11 @@ using namespace Gtkmm2ext; using namespace PBD; SliderController::SliderController (Glib::RefPtr<Gdk::Pixbuf> image, - Gtk::Adjustment *adj, + Gtk::Adjustment *adj, int orientation, Controllable& c, bool with_numeric) - : PixFader (image, *adj), + : PixFader (image, *adj, orientation), binding_proxy (c), spin (*adj, 0, 2) { @@ -63,7 +63,7 @@ VSliderController::VSliderController (Glib::RefPtr<Gdk::Pixbuf> image, Controllable& control, bool with_numeric) - : SliderController (image, adj, control, with_numeric) + : SliderController (image, adj, VERT, control, with_numeric) { if (with_numeric) { spin_frame.add (spin); @@ -79,7 +79,7 @@ HSliderController::HSliderController (Glib::RefPtr<Gdk::Pixbuf> image, Controllable& control, bool with_numeric) - : SliderController (image, adj, control, with_numeric) + : SliderController (image, adj, HORIZ, control, with_numeric) { if (with_numeric) { spin_frame.add (spin); diff --git a/libs/gtkmm2ext/sync-menu.c b/libs/gtkmm2ext/sync-menu.c new file mode 100644 index 0000000000..894446c424 --- /dev/null +++ b/libs/gtkmm2ext/sync-menu.c @@ -0,0 +1,977 @@ +/* GTK+ Integration for the Mac OS X Menubar. + * + * Copyright (C) 2007 Pioneer Research Center USA, Inc. + * Copyright (C) 2007 Imendio AB + * + * For further information, see: + * http://developer.imendio.com/projects/gtk-macosx/menubar + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; version 2.1 + * of the License. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <gtk/gtk.h> +#include <gdk/gdkkeysyms.h> + +#include <Carbon/Carbon.h> + +#include <gtkmm2ext/sync-menu.h> + + +/* TODO + * + * - Sync adding/removing/reordering items + * - Create on demand? (can this be done with gtk+? ie fill in menu + items when the menu is opened) + * - Figure out what to do per app/window... + * + */ + +#define IGE_QUARTZ_MENU_CREATOR 'IGEC' +#define IGE_QUARTZ_ITEM_WIDGET 'IWID' + + +static void sync_menu_shell (GtkMenuShell *menu_shell, + MenuRef carbon_menu, + gboolean toplevel, + gboolean debug); + + +/* + * utility functions + */ + +static GtkWidget * +find_menu_label (GtkWidget *widget) +{ + GtkWidget *label = NULL; + + if (GTK_IS_LABEL (widget)) + return widget; + + if (GTK_IS_CONTAINER (widget)) + { + GList *children; + GList *l; + + children = gtk_container_get_children (GTK_CONTAINER (widget)); + + for (l = children; l; l = l->next) + { + label = find_menu_label (l->data); + if (label) + break; + } + + g_list_free (children); + } + + return label; +} + +static const gchar * +get_menu_label_text (GtkWidget *menu_item, + GtkWidget **label) +{ + GtkWidget *my_label; + + my_label = find_menu_label (menu_item); + if (label) + *label = my_label; + + if (my_label) + return gtk_label_get_text (GTK_LABEL (my_label)); + + return NULL; +} + +static gboolean +accel_find_func (GtkAccelKey *key, + GClosure *closure, + gpointer data) +{ + return (GClosure *) data == closure; +} + + +/* + * CarbonMenu functions + */ + +typedef struct +{ + MenuRef menu; +} CarbonMenu; + +static GQuark carbon_menu_quark = 0; + +static CarbonMenu * +carbon_menu_new (void) +{ + return g_slice_new0 (CarbonMenu); +} + +static void +carbon_menu_free (CarbonMenu *menu) +{ + g_slice_free (CarbonMenu, menu); +} + +static CarbonMenu * +carbon_menu_get (GtkWidget *widget) +{ + return g_object_get_qdata (G_OBJECT (widget), carbon_menu_quark); +} + +static void +carbon_menu_connect (GtkWidget *menu, + MenuRef menuRef) +{ + CarbonMenu *carbon_menu = carbon_menu_get (menu); + + if (!carbon_menu) + { + carbon_menu = carbon_menu_new (); + + g_object_set_qdata_full (G_OBJECT (menu), carbon_menu_quark, + carbon_menu, + (GDestroyNotify) carbon_menu_free); + } + + carbon_menu->menu = menuRef; +} + + +/* + * CarbonMenuItem functions + */ + +typedef struct +{ + MenuRef menu; + MenuItemIndex index; + MenuRef submenu; + GClosure *accel_closure; +} CarbonMenuItem; + +static GQuark carbon_menu_item_quark = 0; + +static CarbonMenuItem * +carbon_menu_item_new (void) +{ + return g_slice_new0 (CarbonMenuItem); +} + +static void +carbon_menu_item_free (CarbonMenuItem *menu_item) +{ + if (menu_item->accel_closure) + g_closure_unref (menu_item->accel_closure); + + g_slice_free (CarbonMenuItem, menu_item); +} + +static CarbonMenuItem * +carbon_menu_item_get (GtkWidget *widget) +{ + return g_object_get_qdata (G_OBJECT (widget), carbon_menu_item_quark); +} + +static void +carbon_menu_item_update_state (CarbonMenuItem *carbon_item, + GtkWidget *widget) +{ + gboolean sensitive; + gboolean visible; + UInt32 set_attrs = 0; + UInt32 clear_attrs = 0; + + g_object_get (widget, + "sensitive", &sensitive, + "visible", &visible, + NULL); + + if (!sensitive) + set_attrs |= kMenuItemAttrDisabled; + else + clear_attrs |= kMenuItemAttrDisabled; + + if (!visible) + set_attrs |= kMenuItemAttrHidden; + else + clear_attrs |= kMenuItemAttrHidden; + + ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index, + set_attrs, clear_attrs); +} + +static void +carbon_menu_item_update_active (CarbonMenuItem *carbon_item, + GtkWidget *widget) +{ + gboolean active; + + g_object_get (widget, + "active", &active, + NULL); + + CheckMenuItem (carbon_item->menu, carbon_item->index, + active); +} + +static void +carbon_menu_item_update_submenu (CarbonMenuItem *carbon_item, + GtkWidget *widget) +{ + GtkWidget *submenu; + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + + if (submenu) + { + const gchar *label_text; + CFStringRef cfstr = NULL; + + label_text = get_menu_label_text (widget, NULL); + if (label_text) + cfstr = CFStringCreateWithCString (NULL, label_text, + kCFStringEncodingUTF8); + + CreateNewMenu (0, 0, &carbon_item->submenu); + SetMenuTitleWithCFString (carbon_item->submenu, cfstr); + SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index, + carbon_item->submenu); + + sync_menu_shell (GTK_MENU_SHELL (submenu), carbon_item->submenu, FALSE, FALSE); + + if (cfstr) + CFRelease (cfstr); + } + else + { + SetMenuItemHierarchicalMenu (carbon_item->menu, carbon_item->index, + NULL); + carbon_item->submenu = NULL; + } +} + +static void +carbon_menu_item_update_label (CarbonMenuItem *carbon_item, + GtkWidget *widget) +{ + const gchar *label_text; + CFStringRef cfstr = NULL; + + label_text = get_menu_label_text (widget, NULL); + if (label_text) + cfstr = CFStringCreateWithCString (NULL, label_text, + kCFStringEncodingUTF8); + + SetMenuItemTextWithCFString (carbon_item->menu, carbon_item->index, + cfstr); + + if (cfstr) + CFRelease (cfstr); +} + +static void +carbon_menu_item_update_accelerator (CarbonMenuItem *carbon_item, + GtkWidget *widget) +{ + GtkWidget *label; + + get_menu_label_text (widget, &label); + + if (GTK_IS_ACCEL_LABEL (label) && + GTK_ACCEL_LABEL (label)->accel_closure) + { + GtkAccelKey *key; + + key = gtk_accel_group_find (GTK_ACCEL_LABEL (label)->accel_group, + accel_find_func, + GTK_ACCEL_LABEL (label)->accel_closure); + + if (key && + key->accel_key && + key->accel_flags & GTK_ACCEL_VISIBLE) + { + GdkDisplay *display = gtk_widget_get_display (widget); + GdkKeymap *keymap = gdk_keymap_get_for_display (display); + GdkKeymapKey *keys; + gint n_keys; + gint use_command; + gboolean add_modifiers = FALSE; + + if (gdk_keymap_get_entries_for_keyval (keymap, key->accel_key, + &keys, &n_keys) == 0) + { + gint realkey = -1; + + switch (key->accel_key) { + case GDK_rightarrow: + case GDK_Right: + realkey = kRightArrowCharCode; + break; + case GDK_leftarrow: + case GDK_Left: + realkey = kLeftArrowCharCode; + break; + case GDK_uparrow: + case GDK_Up: + realkey = kUpArrowCharCode; + break; + case GDK_downarrow: + case GDK_Down: + realkey = kDownArrowCharCode; + break; + default: + break; + } + + if (realkey != -1) { + SetMenuItemCommandKey (carbon_item->menu, carbon_item->index, + false, realkey); + add_modifiers = TRUE; + } + + } else { + SetMenuItemCommandKey (carbon_item->menu, carbon_item->index, + true, keys[0].keycode); + g_free (keys); + add_modifiers = TRUE; + } + + if (add_modifiers) + { + UInt8 modifiers = 0; /* implies Command key */ + + use_command = 0; + + if (key->accel_mods) + { + if (key->accel_mods & GDK_SHIFT_MASK) { + modifiers |= kMenuShiftModifier; + } + + /* gdk/quartz maps Alt/Option to Mod1 */ + + if (key->accel_mods & (GDK_MOD1_MASK)) { + modifiers |= kMenuOptionModifier; + } + + if (key->accel_mods & GDK_CONTROL_MASK) { + modifiers |= kMenuControlModifier; + } + + /* gdk/quartz maps Command to Meta */ + + if (key->accel_mods & GDK_META_MASK) { + use_command = 1; + } + } + + if (!use_command) + modifiers |= kMenuNoCommandModifier; + + SetMenuItemModifiers (carbon_item->menu, carbon_item->index, + modifiers); + + return; + } + } + } + + /* otherwise, clear the menu shortcut */ + SetMenuItemModifiers (carbon_item->menu, carbon_item->index, + kMenuNoModifiers | kMenuNoCommandModifier); + ChangeMenuItemAttributes (carbon_item->menu, carbon_item->index, + 0, kMenuItemAttrUseVirtualKey); + SetMenuItemCommandKey (carbon_item->menu, carbon_item->index, + false, 0); +} + +static void +carbon_menu_item_accel_changed (GtkAccelGroup *accel_group, + guint keyval, + GdkModifierType modifier, + GClosure *accel_closure, + GtkWidget *widget) +{ + CarbonMenuItem *carbon_item = carbon_menu_item_get (widget); + GtkWidget *label; + + get_menu_label_text (widget, &label); + + if (GTK_IS_ACCEL_LABEL (label) && + GTK_ACCEL_LABEL (label)->accel_closure == accel_closure) + carbon_menu_item_update_accelerator (carbon_item, widget); +} + +static void +carbon_menu_item_update_accel_closure (CarbonMenuItem *carbon_item, + GtkWidget *widget) +{ + GtkAccelGroup *group; + GtkWidget *label; + + get_menu_label_text (widget, &label); + + if (carbon_item->accel_closure) + { + group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure); + + g_signal_handlers_disconnect_by_func (group, + carbon_menu_item_accel_changed, + widget); + + g_closure_unref (carbon_item->accel_closure); + carbon_item->accel_closure = NULL; + } + + if (GTK_IS_ACCEL_LABEL (label)) + carbon_item->accel_closure = GTK_ACCEL_LABEL (label)->accel_closure; + + if (carbon_item->accel_closure) + { + g_closure_ref (carbon_item->accel_closure); + + group = gtk_accel_group_from_accel_closure (carbon_item->accel_closure); + + g_signal_connect_object (group, "accel-changed", + G_CALLBACK (carbon_menu_item_accel_changed), + widget, 0); + } + + carbon_menu_item_update_accelerator (carbon_item, widget); +} + +static void +carbon_menu_item_notify (GObject *object, + GParamSpec *pspec, + CarbonMenuItem *carbon_item) +{ + if (!strcmp (pspec->name, "sensitive") || + !strcmp (pspec->name, "visible")) + { + carbon_menu_item_update_state (carbon_item, GTK_WIDGET (object)); + } + else if (!strcmp (pspec->name, "active")) + { + carbon_menu_item_update_active (carbon_item, GTK_WIDGET (object)); + } + else if (!strcmp (pspec->name, "submenu")) + { + carbon_menu_item_update_submenu (carbon_item, GTK_WIDGET (object)); + } +} + +static void +carbon_menu_item_notify_label (GObject *object, + GParamSpec *pspec, + gpointer data) +{ + CarbonMenuItem *carbon_item = carbon_menu_item_get (GTK_WIDGET (object)); + + if (!strcmp (pspec->name, "label")) + { + carbon_menu_item_update_label (carbon_item, + GTK_WIDGET (object)); + } + else if (!strcmp (pspec->name, "accel-closure")) + { + carbon_menu_item_update_accel_closure (carbon_item, + GTK_WIDGET (object)); + } +} + +static CarbonMenuItem * +carbon_menu_item_connect (GtkWidget *menu_item, + GtkWidget *label, + MenuRef menu, + MenuItemIndex index) +{ + CarbonMenuItem *carbon_item = carbon_menu_item_get (menu_item); + + if (!carbon_item) + { + carbon_item = carbon_menu_item_new (); + + g_object_set_qdata_full (G_OBJECT (menu_item), carbon_menu_item_quark, + carbon_item, + (GDestroyNotify) carbon_menu_item_free); + + g_signal_connect (menu_item, "notify", + G_CALLBACK (carbon_menu_item_notify), + carbon_item); + + if (label) + g_signal_connect_swapped (label, "notify::label", + G_CALLBACK (carbon_menu_item_notify_label), + menu_item); + } + + carbon_item->menu = menu; + carbon_item->index = index; + + return carbon_item; +} + + +/* + * carbon event handler + */ + +static int _in_carbon_menu_event_handler = 0; + +int +gdk_quartz_in_carbon_menu_event_handler () +{ + return _in_carbon_menu_event_handler; +} + +static gboolean +dummy_gtk_menu_item_activate (gpointer *arg) +{ + gtk_menu_item_activate (GTK_MENU_ITEM(arg)); + return FALSE; +} + +static OSStatus +menu_event_handler_func (EventHandlerCallRef event_handler_call_ref, + EventRef event_ref, + void *data) +{ + UInt32 event_class = GetEventClass (event_ref); + UInt32 event_kind = GetEventKind (event_ref); + MenuRef menu_ref; + OSStatus ret; + + _in_carbon_menu_event_handler = 1; + + switch (event_class) + { + case kEventClassCommand: + /* This is called when activating (is that the right GTK+ term?) + * a menu item. + */ + if (event_kind == kEventCommandProcess) + { + HICommand command; + OSStatus err; + + /*g_printerr ("Menu: kEventClassCommand/kEventCommandProcess\n");*/ + + err = GetEventParameter (event_ref, kEventParamDirectObject, + typeHICommand, 0, + sizeof (command), 0, &command); + + if (err == noErr) + { + GtkWidget *widget = NULL; + + /* Get any GtkWidget associated with the item. */ + err = GetMenuItemProperty (command.menu.menuRef, + command.menu.menuItemIndex, + IGE_QUARTZ_MENU_CREATOR, + IGE_QUARTZ_ITEM_WIDGET, + sizeof (widget), 0, &widget); + if (err == noErr && GTK_IS_WIDGET (widget)) + { + g_idle_add (dummy_gtk_menu_item_activate, widget); + // gtk_menu_item_activate (GTK_MENU_ITEM (widget)); + _in_carbon_menu_event_handler = 0; + return noErr; + } + } + } + break; + + case kEventClassMenu: + GetEventParameter (event_ref, + kEventParamDirectObject, + typeMenuRef, + NULL, + sizeof (menu_ref), + NULL, + &menu_ref); + + switch (event_kind) + { + case kEventMenuTargetItem: + /* This is called when an item is selected (what is the + * GTK+ term? prelight?) + */ + /*g_printerr ("kEventClassMenu/kEventMenuTargetItem\n");*/ + break; + + case kEventMenuOpening: + /* Is it possible to dynamically build the menu here? We + * can at least set visibility/sensitivity. + */ + /*g_printerr ("kEventClassMenu/kEventMenuOpening\n");*/ + break; + + case kEventMenuClosed: + /*g_printerr ("kEventClassMenu/kEventMenuClosed\n");*/ + break; + + default: + break; + } + + break; + + default: + break; + } + + ret = CallNextEventHandler (event_handler_call_ref, event_ref); + _in_carbon_menu_event_handler = 0; + return ret; +} + +static void +setup_menu_event_handler (void) +{ + EventHandlerUPP menu_event_handler_upp; + EventHandlerRef menu_event_handler_ref; + const EventTypeSpec menu_events[] = { + { kEventClassCommand, kEventCommandProcess }, + { kEventClassMenu, kEventMenuTargetItem }, + { kEventClassMenu, kEventMenuOpening }, + { kEventClassMenu, kEventMenuClosed } + }; + + /* FIXME: We might have to install one per window? */ + + menu_event_handler_upp = NewEventHandlerUPP (menu_event_handler_func); + InstallEventHandler (GetApplicationEventTarget (), menu_event_handler_upp, + GetEventTypeCount (menu_events), menu_events, 0, + &menu_event_handler_ref); + +#if 0 + /* FIXME: Remove the handler with: */ + RemoveEventHandler(menu_event_handler_ref); + DisposeEventHandlerUPP(menu_event_handler_upp); +#endif +} + +static void +sync_menu_shell (GtkMenuShell *menu_shell, + MenuRef carbon_menu, + gboolean toplevel, + gboolean debug) +{ + GList *children; + GList *l; + MenuItemIndex carbon_index = 1; + + if (debug) + g_printerr ("%s: syncing shell %p\n", G_STRFUNC, menu_shell); + + carbon_menu_connect (GTK_WIDGET (menu_shell), carbon_menu); + + children = gtk_container_get_children (GTK_CONTAINER (menu_shell)); + + for (l = children; l; l = l->next) + { + GtkWidget *menu_item = l->data; + CarbonMenuItem *carbon_item; + + if (GTK_IS_TEAROFF_MENU_ITEM (menu_item)) + continue; + + if (toplevel && g_object_get_data (G_OBJECT (menu_item), + "gtk-empty-menu-item")) + continue; + + carbon_item = carbon_menu_item_get (menu_item); + + if (debug) + g_printerr ("%s: carbon_item %d for menu_item %d (%s, %s)\n", + G_STRFUNC, carbon_item ? carbon_item->index : -1, + carbon_index, get_menu_label_text (menu_item, NULL), + g_type_name (G_TYPE_FROM_INSTANCE (menu_item))); + + if (carbon_item && carbon_item->index != carbon_index) + { + if (debug) + g_printerr ("%s: -> not matching, deleting\n", G_STRFUNC); + + DeleteMenuItem (carbon_item->menu, carbon_index); + carbon_item = NULL; + } + + if (!carbon_item) + { + GtkWidget *label = NULL; + const gchar *label_text; + CFStringRef cfstr = NULL; + MenuItemAttributes attributes = 0; + + if (debug) + g_printerr ("%s: -> creating new\n", G_STRFUNC); + + label_text = get_menu_label_text (menu_item, &label); + if (label_text) + cfstr = CFStringCreateWithCString (NULL, label_text, + kCFStringEncodingUTF8); + + if (GTK_IS_SEPARATOR_MENU_ITEM (menu_item)) + attributes |= kMenuItemAttrSeparator; + + if (!GTK_WIDGET_IS_SENSITIVE (menu_item)) + attributes |= kMenuItemAttrDisabled; + + if (!GTK_WIDGET_VISIBLE (menu_item)) + attributes |= kMenuItemAttrHidden; + + InsertMenuItemTextWithCFString (carbon_menu, cfstr, + carbon_index - 1, + attributes, 0); + SetMenuItemProperty (carbon_menu, carbon_index, + IGE_QUARTZ_MENU_CREATOR, + IGE_QUARTZ_ITEM_WIDGET, + sizeof (menu_item), &menu_item); + + if (cfstr) + CFRelease (cfstr); + + carbon_item = carbon_menu_item_connect (menu_item, label, + carbon_menu, + carbon_index); + + if (GTK_IS_CHECK_MENU_ITEM (menu_item)) + carbon_menu_item_update_active (carbon_item, menu_item); + + carbon_menu_item_update_accel_closure (carbon_item, menu_item); + + if (gtk_menu_item_get_submenu (GTK_MENU_ITEM (menu_item))) + carbon_menu_item_update_submenu (carbon_item, menu_item); + } + + carbon_index++; + } + + g_list_free (children); +} + + +static gulong emission_hook_id = 0; + +static gboolean +parent_set_emission_hook (GSignalInvocationHint *ihint, + guint n_param_values, + const GValue *param_values, + gpointer data) +{ + GtkWidget *instance = g_value_get_object (param_values); + + if (GTK_IS_MENU_ITEM (instance)) + { + GtkWidget *previous_parent = g_value_get_object (param_values + 1); + GtkWidget *menu_shell = NULL; + + if (GTK_IS_MENU_SHELL (previous_parent)) + { + menu_shell = previous_parent; + } + else if (GTK_IS_MENU_SHELL (instance->parent)) + { + menu_shell = instance->parent; + } + + if (menu_shell) + { + CarbonMenu *carbon_menu = carbon_menu_get (menu_shell); + + if (carbon_menu) + { +#if 0 + g_printerr ("%s: item %s %p (%s, %s)\n", G_STRFUNC, + previous_parent ? "removed from" : "added to", + menu_shell, + get_menu_label_text (instance, NULL), + g_type_name (G_TYPE_FROM_INSTANCE (instance))); +#endif + + sync_menu_shell (GTK_MENU_SHELL (menu_shell), + carbon_menu->menu, + carbon_menu->menu == (MenuRef) data, + FALSE); + } + } + } + + return TRUE; +} + +static void +parent_set_emission_hook_remove (GtkWidget *widget, + gpointer data) +{ + g_signal_remove_emission_hook (g_signal_lookup ("parent-set", + GTK_TYPE_WIDGET), + emission_hook_id); +} + + +/* + * public functions + */ + +void +ige_mac_menu_set_menu_bar (GtkMenuShell *menu_shell) +{ + MenuRef carbon_menubar; + + g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell)); + + if (carbon_menu_quark == 0) + carbon_menu_quark = g_quark_from_static_string ("CarbonMenu"); + + if (carbon_menu_item_quark == 0) + carbon_menu_item_quark = g_quark_from_static_string ("CarbonMenuItem"); + + CreateNewMenu (0 /*id*/, 0 /*options*/, &carbon_menubar); + SetRootMenu (carbon_menubar); + + setup_menu_event_handler (); + + emission_hook_id = + g_signal_add_emission_hook (g_signal_lookup ("parent-set", + GTK_TYPE_WIDGET), + 0, + parent_set_emission_hook, + carbon_menubar, NULL); + + g_signal_connect (menu_shell, "destroy", + G_CALLBACK (parent_set_emission_hook_remove), + NULL); + + sync_menu_shell (menu_shell, carbon_menubar, TRUE, FALSE); +} + +void +ige_mac_menu_set_quit_menu_item (GtkMenuItem *menu_item) +{ + MenuRef appmenu; + MenuItemIndex index; + + g_return_if_fail (GTK_IS_MENU_ITEM (menu_item)); + + if (GetIndMenuItemWithCommandID (NULL, kHICommandQuit, 1, + &appmenu, &index) == noErr) + { + SetMenuItemCommandID (appmenu, index, 0); + SetMenuItemProperty (appmenu, index, + IGE_QUARTZ_MENU_CREATOR, + IGE_QUARTZ_ITEM_WIDGET, + sizeof (menu_item), &menu_item); + + gtk_widget_hide (GTK_WIDGET (menu_item)); + } +} + + +struct _IgeMacMenuGroup +{ + GList *items; +}; + +static GList *app_menu_groups = NULL; + +IgeMacMenuGroup * +ige_mac_menu_add_app_menu_group (void) +{ + IgeMacMenuGroup *group = g_slice_new0 (IgeMacMenuGroup); + + app_menu_groups = g_list_append (app_menu_groups, group); + + return group; +} + +void +ige_mac_menu_add_app_menu_item (IgeMacMenuGroup *group, + GtkMenuItem *menu_item, + const gchar *label) +{ + MenuRef appmenu; + GList *list; + gint index = 0; + + g_return_if_fail (group != NULL); + g_return_if_fail (GTK_IS_MENU_ITEM (menu_item)); + + if (GetIndMenuItemWithCommandID (NULL, kHICommandHide, 1, + &appmenu, NULL) != noErr) + { + g_warning ("%s: retrieving app menu failed", + G_STRFUNC); + return; + } + + for (list = app_menu_groups; list; list = g_list_next (list)) + { + IgeMacMenuGroup *list_group = list->data; + + index += g_list_length (list_group->items); + + /* adjust index for the separator between groups, but not + * before the first group + */ + if (list_group->items && list->prev) + index++; + + if (group == list_group) + { + CFStringRef cfstr; + + /* add a separator before adding the first item, but not + * for the first group + */ + if (!group->items && list->prev) + { + InsertMenuItemTextWithCFString (appmenu, NULL, index, + kMenuItemAttrSeparator, 0); + index++; + } + + if (!label) + label = get_menu_label_text (GTK_WIDGET (menu_item), NULL); + + cfstr = CFStringCreateWithCString (NULL, label, + kCFStringEncodingUTF8); + + InsertMenuItemTextWithCFString (appmenu, cfstr, index, 0, 0); + SetMenuItemProperty (appmenu, index + 1, + IGE_QUARTZ_MENU_CREATOR, + IGE_QUARTZ_ITEM_WIDGET, + sizeof (menu_item), &menu_item); + + CFRelease (cfstr); + + gtk_widget_hide (GTK_WIDGET (menu_item)); + + group->items = g_list_append (group->items, menu_item); + + return; + } + } + + if (!list) + g_warning ("%s: app menu group %p does not exist", + G_STRFUNC, group); +} diff --git a/libs/pbd/misc.c b/libs/pbd/misc.c index 797be5de45..34e5888687 100644 --- a/libs/pbd/misc.c +++ b/libs/pbd/misc.c @@ -8,7 +8,7 @@ void disable_screen_updates () { #ifdef GTKOSX - NSDisableScreenUpdates (); + // NSDisableScreenUpdates (); #endif } @@ -16,6 +16,6 @@ void enable_screen_updates () { #ifdef GTKOSX - NSEnableScreenUpdates(); + // NSEnableScreenUpdates(); #endif } diff --git a/libs/vamp-plugins/SConscript b/libs/vamp-plugins/SConscript index 592fca3768..a35b789ef3 100644 --- a/libs/vamp-plugins/SConscript +++ b/libs/vamp-plugins/SConscript @@ -21,6 +21,6 @@ Default(libvampplugins) env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2', 'vamp'), libvampplugins)) env.Alias('tarball', env.Distribute (env['DISTTREE'], - [ 'SConscript', 'COPYING', 'README' ] + + [ 'SConscript' ] + plugin_files + glob.glob('*.h'))) diff --git a/svn_revision.h b/svn_revision.h index 5000e5a2a4..592c4ae9a8 100644 --- a/svn_revision.h +++ b/svn_revision.h @@ -1,4 +1,4 @@ #ifndef __ardour_svn_revision_h__ #define __ardour_svn_revision_h__ -static const char* ardour_svn_revision = "2985"; +static const char* ardour_svn_revision = "3046"; #endif diff --git a/tools/osx_packaging/app_build.rb b/tools/osx_packaging/app_build.rb index 48d3837368..18f6e6a65e 100755 --- a/tools/osx_packaging/app_build.rb +++ b/tools/osx_packaging/app_build.rb @@ -53,6 +53,10 @@ if not File.exist?(libdir+"/surfaces") then Dir.mkdir(libdir + "/surfaces") end +if not File.exist?(libdir+"/vamp-plugins") then + Dir.mkdir(libdir + "/vamp-plugins") +end + odir = Dir.getwd Dir.chdir("../..") @@ -67,6 +71,9 @@ results = results + result.split("\n").slice(1,result.size-1) result = `otool -L libs/surfaces/*/*.dylib` results = results + result.split("\n").slice(1,result.size-1) +result = `otool -L libs/vamp-plugins/*.dylib` +results = results + result.split("\n").slice(1,result.size-1) + results.uniq! $stdout.print("Copying libs to #{libdir} ...\n"); @@ -117,6 +124,9 @@ begin rescue end +# vamp plugins +`cp ../../libs/vamp-plugins/*.dylib #{libdir}/vamp-plugins` + # copy gtk and pango lib stuff `cp -R /opt/local/lib/pango #{libdir}/` diff --git a/tools/osx_packaging/bin/exporter b/tools/osx_packaging/bin/exporter index 41a2904ff9..0ac0370c82 100755 --- a/tools/osx_packaging/bin/exporter +++ b/tools/osx_packaging/bin/exporter @@ -24,10 +24,13 @@ export ARDOUR_MODULE_PATH="$TOP/lib/" export ARDOUR_DATA_PATH="$TOP/share" export ARDOUR_GLADE_PATH="$TOP/share/ardour2/glade" -export LADSPA_RDF_PATH="$HOME/Library/Audio/Plug-Ins/LADSPA/rdf:/Library/Audio/Plug-Ins/LADSPA/rdf:$TOP/s -hare/ladspa/rdf:/usr/local/share/ladspa/rdf:/usr/share/ladspa/rdf" -export LADSPA_PATH="$HOME/Library/Audio/Plug-Ins/LADSPA:/Library/Audio/Plug-Ins/LADSPA:/usr/local/lib/lad -spa:/usr/lib/ladspa" +export ARDOUR_PATH="$TOP/share/ardour/icons:$TOP/share/ardour/pixmaps" + + +export LADSPA_RDF_PATH="$HOME/Library/Audio/Plug-Ins/LADSPA/rdf:/Library/Audio/Plug-Ins/LADSPA/rdf:$TOP/share/ladspa/rdf:/usr/local/share/ladspa/rdf:/usr/share/ladspa/rdf" +export LADSPA_PATH="$HOME/Library/Audio/Plug-Ins/LADSPA:/Library/Audio/Plug-Ins/LADSPA:/usr/local/lib/ladspa:/usr/lib/ladspa" + +export VAMP_PATH="$TOP/lib/vamp-plugins" mkdir -p "$ETC" sed 's|${HOME}|'"$HOME|g" "$TOP/etc/pango/pangorc" > "$ETC/pangorc" diff --git a/vst/SConscript b/vst/SConscript index eb4dd6683b..4a130fed80 100644 --- a/vst/SConscript +++ b/vst/SConscript @@ -68,7 +68,7 @@ wine_executable = ardour_vst.SubstInFile ('ardourvst', 'ardourvst.in', SUBST_DIC if ardour_vst['VST']: ardour_vst.AddPostAction (wine_executable, ardour_vst.Action (Chmod ('vst/ardevst', 0755))) - ardour_vst.AddPostAction (wine_executable, ardour_vst.Action (Chmod (wine_executable, 0755))) + ardour_vst.AddPostAction (wine_executable, ardour_vst.Action (Chmod ("vst/" + str(wine_executable[0]), 0755))) Default([wine_generated_executable, wine_executable]) # the wine script - into the bin dir |