Sample accurate timing

is it possible for JUCE to create a sample accurate timing?

specifically, for use with a step sequencer/DAW, where sample accurate timing is used to synchronize events and tempo

or at least accurate enough to keep up with a professional DAW’s tempo such as FL STUDIO or ABLETON

Of course it’s possible. :slight_smile:

In each processBlock() call you get X number of samples, as well as the tempo and transport information from the host via the getPlayHead() function.

If you use this chance to also calculate how much time should pass in each sample, you can dispatch notes or audio events in exact, sample accurate positions.

hmmm are there any examples which demonstrate this?

preferably for android if possible

namespace sequencer {
	template<typename Float>
	struct Phase {
		Phase() :
			phase(0),
			inc(0)
		{}
		void setPhase(const Float ph) { phase = ph; }
		void setInc(const Float i) { inc = i; }
		bool operator()() {
			phase -= inc;
			if (phase < 0) {
				++phase;
				return true;
			}
			return false;
		}
		Float phase, inc;
	};

        template<typename Float>
	class Sequencer {
#define ppqPos hostInfo.ppqPosition
	public:
		Sequencer() :
			phaseData(),
			triggerData(),
			hostInfo(),
			phase(),
			bpm(0),
			minuteInv(0),
			rate(0)
		{
			setRateInBeatsPerBar(8);
		}
		// PREPARE TO PLAY
		void setFs(double Fs) { // sampleRate
			bpm = 0;
			minuteInv = (Float)1 / Float(60 * Fs);
		}
		void setMaximumBufferSize(int maxBufferSize) {
			data.resize(maxBufferSize, 0);
			triggerData.resize(maxBufferSize, false);
		}
		// PARAMETER
		void setRateInBeatsPerBar(const Float r) { rate = r / (Float)4; }
		// PROCESS
		void processBlock(const juce::AudioPlayHead* playHead, const juce::AudioBuffer<float>& buffer) {
			if (!playHeadValid(playHead)) return;
			
			updatePhase(playHead);
			for(auto s = 0; s < buffer.getNumSamples(); ++s) {
				triggerData[s] = phase();
				phaseData[s] = phase.phase;
			}
		}
		// GET
		std::vector<Float> phaseData;
		std::vector<bool> triggerData;
		
		private:
			juce::AudioPlayHead::CurrentPositionInfo hostInfo;
			Phase phase;
			Float bpm, minuteInv, rate;
			
			bool playHeadValid(const juce::AudioPlayHead* playHead) {
				if (playHead == nullptr) return false;
				playHead->getCurrentPosition(hostInfo);
				if (!hostInfo.isPlaying) return false;
				return true;
			}
			
			void updatePhase(const juce::AudioPlayHead* playHead){
				auto ppqResampling = (Float)ppqPos * rate;
				auto phase = ppqResampling - (int)ppqResampling;
				phase.phase = phase;
				auto hostInfoBpm = (Float)hostInfo.bpm;
				if (bpm != hostInfoBpm) {
					bpm = hostInfoBpm;
					auto speed = bpm * minuteInv;
					lfo.inc = (Float)(speed * rate);
				}
			}
		};
	}

didn’t test this code, but this should do the trick. copy it into a header file, include it into your project, setFs and MaximumBufferSize in prepareToPlay, then choose a rate either in prepareToPlay or at the beginning of processBlock, then call processBlock with the given arguments. then you can access phaseData and triggerData. phaseData will contain the phase of the sequencer, a saw-tooth wave of the frequency of your rate. triggerData contains bools that are true only when a musical unit of your chosen rate has finished in the context of your project’s tempo and position