Ejemplo n.º 1
0
TouchArray TouchTracker::process(const SensorFrame& in, int maxTouches)
{
	setMaxTouches(maxTouches);
	
	mTouches = TouchArray{};
	
	if(mMaxTouchesPerFrame > 0)
	{
		mTouches = findTouches(in);
		
		// match -> position filter -> feedback
		mTouches = matchTouches(mTouches, mTouchesMatch1);
		mTouches = filterTouchesXYAdaptive(mTouches, mTouchesMatch1);
		mTouchesMatch1 = mTouches;
		
		// asymmetrical z filter from user setting. Ages are created here.
		mTouches = filterTouchesZ(mTouches, mTouches2, mLopassZ*2.f, mLopassZ*0.25f);
		mTouches2 = mTouches;
		
		// after variable filter, exile decayed touches so they are not matched. Note this affects match feedback!
		mTouchesMatch1 = exileUnusedTouches(mTouchesMatch1, mTouches);
		
		// TODO hysteresis after matching to prevent glitching when there are more
		// physical touches than mMaxTouchesPerFrame and touches are stolen
		
		if(mRotate)
		{
			mTouches = rotateTouches(mTouches);
		}
		
		mTouches = clampAndScaleTouches(mTouches);
	}
	clearAndSendNextFrameIfNeeded();
	return mTouches;
}
Ejemplo n.º 2
0
void TouchTracker::process(int)
{	
	if (!mpIn) return;
	const MLSignal& in(*mpIn);
	
	if (mNeedsClear)
	{
		mBackground.copy(in);
		mBackgroundFilter.clear();
		mNeedsClear = false;
		return;
	}
		
	mFilteredInput.copy(in);
	
	if (mCalibrator.isCalibrating())
	{		
		int done = mCalibrator.addSample(mFilteredInput);
		
		if(done == 1)
		{
			// Tell the listener we have a new calibration. We still do the calibration here in the Tracker, 
			// but the Model will be responsible for saving and restoring the calibration maps.
			if(mpListener)
			{
				mpListener->hasNewCalibration(mCalibrator.mCalibrateSignal, mCalibrator.mNormalizeMap, mCalibrator.mAvgDistance);
			}
		}
	}
	else
	{
		if(mDoNormalize)
		{
			mCalibrator.normalizeInput(mFilteredInput);
		}
		
		if(mMaxTouchesPerFrame > 0)
		{
			// smooth input	
			float kc, ke, kk;
			kc = 4./16.; ke = 2./16.; kk=1./16.;
			mFilteredInput.convolve3x3r(kc, ke, kk);

			// build sum of currently tracked touches	
			//
			mSumOfTouches.clear();
			int numActiveTouches = 0;
			for(int i = 0; i < mMaxTouchesPerFrame; ++i)
			{
				const Touch& t(mTouches[i]);
				if(t.isActive())
				{
					Vec2 touchPos(t.x, t.y);
					mTemplateScaled.clear();
					mTemplateScaled.add2D(mCalibrator.getTemplate(touchPos), 0, 0);
					mTemplateScaled.scale(t.z*mCalibrator.getZAdjust(touchPos));
					mSumOfTouches.add2D(mTemplateScaled, touchPos - Vec2(kTemplateRadius, kTemplateRadius));
					numActiveTouches++;
				}
			}	
			
			// to make sum of touches a bit bigger 
			//mSumOfTouches.scale(1.5f);
			//mSumOfTouches.convolve3x3r(kc, ke, kk);

			//
			// TODO lots of optimization here in onepole, 2D filter
			//
			// TODO the mean of lowpass background can be its own control source that will 
			// act like an accelerometer!  tilt controls even. 
			
			mBackgroundFilterFrequency.fill(mBackgroundFilterFreq);
			
			// build background: lowpass filter rest state.  Filter freq.
			// is nonzero where there are no touches, 0 where there are touches.
			mTemp.copy(mSumOfTouches);
			mTemp.scale(100.f); 
			mBackgroundFilterFrequency.subtract(mTemp);
			mBackgroundFilterFrequency.sigMax(0.);		
			
			// TODO allow filter to move a little if touch template distance is near threshold
			// this will fix most stuck touches

			// filter background in up direction 
			mBackgroundFilterFrequency2.fill(mBackgroundFilterFreq);
			mBackgroundFilter.setInputSignal(&mFilteredInput);
			mBackgroundFilter.setOutputSignal(&mBackground);

			// set asymmetric filter coeffs and get background
			mBackgroundFilter.setCoeffs(mBackgroundFilterFrequency, mBackgroundFilterFrequency2);
			mBackgroundFilter.process(1);	

 		}

		// subtract background from input 
		//
		mInputMinusBackground.copy(mFilteredInput);
		mInputMinusBackground.subtract(mBackground);
								
		// move or remove and filter existing touches
		//
		updateTouches(mInputMinusBackground);	
		
		// TODO can negative values be used to inhibit nearby touches?  
		// this might prevent sticking touches when a lot of force is
		// applied then quickly released.
		
		// TODO look for retriggers here, touches not fallen to 0 but where 
		// dz warrants a new note-on. The way to do this is keep a second,
		// separate set of key states that do not get cleared by current
		// touches.  These can be used to get the dz values and using the exact
		// same math, velocities will match other note-ons.
        //
        // we can also look for a nearby release just beforehand when
        // retriggering. this will increase confidence in a retrigger as
        // opposed to simply moving the touch.

		// after update Touches, subtract sum of touches to get residual R
		// R = input - T.
		// This represents any pressure data not currently part of a touch.
		if(mMaxTouchesPerFrame > 0)
		{
			mInputMinusBackground.sigMax(0.);	
			mResidual.copy(mInputMinusBackground);
			mResidual.subtract(mSumOfTouches);
			mResidual.sigMax(0.);
		}

		// get signals for viewer
		// TODO optimize: we only have to copy these each time a view is needed
		mCalibratedSignal.copy(mInputMinusBackground);
		mCookedSignal.copy(mSumOfTouches);		
		mTestSignal.copy(mResidual);		
		
		// get subpixel xyz peak from residual
		addPeakToKeyState(mResidual);
		
		// update key states 
		for(int i=0; i<mNumKeys; ++i)
		{
			mKeyStates[i].tick();
		}
		
		findTouches();

		// filter touches
		// filter x and y for output
		// filter touches and write touch data to one frame of output signal.
		//
		MLSignal& out = *mpOut;
		for(int i = 0; i < mMaxTouchesPerFrame; ++i)
		{
			Touch& t = mTouches[i];			
			if(t.age > 1)
			{
				float xyc = 1.0f - powf(2.71828f, -kMLTwoPi * mLopass*0.1f / (float)mSampleRate);
				t.xf += (t.x - t.xf)*xyc;
				t.yf += (t.y - t.yf)*xyc;
			}
			else if(t.age == 1)
			{
				t.xf = t.x;
				t.yf = t.y;
			}
			out(xColumn, i) = t.xf;
			out(yColumn, i) = t.yf;
			out(zColumn, i) = (t.age > 0) ? t.zf : 0.;			
			out(dzColumn, i) = t.dz;
			out(ageColumn, i) = t.age;
			out(dtColumn, i) = t.tDist;
		}
	}
	
#if DEBUG	
	// TEMP	
	if (mCount++ > 1000) 
	{
		mCount = 0;
//		dumpTouches();
	}
#endif	
}