All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jaudiolibs.pipes.graph.Clock Maven / Gradle / Ivy

Go to download

Support for building and running graphs of Pipes UGens, based on the audio API inside PraxisCORE.

The newest version!
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2019 Neil C Smith.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License version 3 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License version 3
 * along with this work; if not, see http://www.gnu.org/licenses/
 *
 */
package org.jaudiolibs.pipes.graph;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.IntConsumer;

/**
 * A Clock that triggers at subdivisions of a given BPM, synchronized to the
 * audio playback clock. All clock triggers happen at the beginning of an audio
 * buffer, so the actual rate may be slightly different to the requested BPM to
 * ensure a uniform clock period.
 * 

* Clock provides a {@link Linkable.Int} for subscribing to triggers, * maintaining a continuously rising count. It is also possible to add Runnable * functions to be called on each clock trigger. *

* Clock fields may be annotated with {@link Inject}. */ public final class Clock implements Graph.Dependent { private final List links; private Graph graph; private int index; private int maxIndex; private double bpm = 120; private int subdivision = 4; private int bufferCount; private int position; public Clock() { this.links = new CopyOnWriteArrayList<>(); maxIndex = Integer.MAX_VALUE; } @Override public void attach(Graph graph) { this.graph = graph; updatePulse(); } @Override public void update() { if (position++ == 0) { fire(); } position %= bufferCount; } public Clock bpm(double bpm) { this.bpm = bpm; updatePulse(); return this; } public double bpm() { return bpm; } public Clock subdivision(int subdivision) { this.subdivision = subdivision; updatePulse(); return this; } public int subdivision() { return subdivision; } /** * Clear all Linkables from this Trigger. * * @return this */ public Clock clearLinks() { links.clear(); return this; } /** * Run the provided Runnable each time this Trigger is triggered. This * method is shorthand for {@code on().link(i -> runnable.run());}. * * @param runnable function to run on trigger * @return this */ public Clock link(Runnable runnable) { Link l = new Link(); l.link(i -> runnable.run()); return this; } /** * Returns a new {@link Linkable.Int} for listening to each trigger. The int * passed to the created linkable will be the same as index, incrementing * each time, wrapping at maxIndex. * * @return new Linkable.Int for reacting to triggers */ public Linkable.Int on() { return new Link(); } /** * Set the current index. Must not be negative. * * @param idx new index * @return this */ public Clock index(int idx) { if (idx < 0) { throw new IllegalArgumentException("Index cannot be less than zero"); } index = (idx % maxIndex); return this; } /** * Set the maximum index, at which the index will wrap back to zero. * * @param max maximum index * @return this */ public Clock maxIndex(int max) { if (max < 1) { throw new IllegalArgumentException("Max index must be greater than 0"); } maxIndex = max; index %= maxIndex; return this; } /** * Get the current index. * * @return current index */ public int index() { return index; } /** * Get the current maximum index. * * @return maximum index */ public int maxIndex() { return maxIndex; } private void fire() { fireLinks(); incrementIndex(); } private void fireLinks() { links.forEach(l -> l.fire(index)); } private void incrementIndex() { index = (index + 1) % maxIndex; } void updatePulse() { if (graph == null) { return; } double secPerPulse = 1 / ((bpm * subdivision) / 60); double bufPerPulse = secPerPulse / (graph.blockSize() / graph.sampleRate()); bufferCount = (int) (bufPerPulse + 0.5); // period = bufferCount * (blockSize / sampleRate); // actualBpm = 60 / subdivision * (1 / period); } private class Link implements Linkable.Int { private IntConsumer consumer; @Override public void link(IntConsumer consumer) { if (this.consumer != null) { throw new IllegalStateException("Cannot link multiple consumers in one chain"); } this.consumer = Objects.requireNonNull(consumer); links.add(this); } private void fire(int value) { consumer.accept(value); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy