diff --git a/src/API.ts b/src/API.ts
index 6de4545..887aee6 100644
--- a/src/API.ts
+++ b/src/API.ts
@@ -148,6 +148,28 @@ export class UserAPI {
silence = this.stop;
hush = this.stop;
+ // =============================================================
+ // Time warp functions
+ // =============================================================
+
+ public warp = (n: number): void => {
+ /**
+ * Time-warp the clock by using the tick you wish to jump to.
+ */
+ this.app.clock.tick = n;
+ this.app.clock.time_position = this.app.clock.convertTicksToTimeposition(n);
+ };
+
+ public beat_warp = (beat: number): void => {
+ /**
+ * Time-warp the clock by using the tick you wish to jump to.
+ */
+ this.app.clock.tick = beat * this.app.clock.ppqn;
+ this.app.clock.time_position = this.app.clock.convertTicksToTimeposition(
+ beat * this.app.clock.ppqn
+ );
+ };
+
// =============================================================
// Mouse functions
// =============================================================
@@ -910,7 +932,7 @@ export class UserAPI {
/**
* Returns the current number of pulses elapsed since origin of time
*/
- return this.app.clock.pulses_since_origin + 1;
+ return this.app.clock.pulses_since_origin + 1;
};
nominator = (): number => {
@@ -918,7 +940,7 @@ export class UserAPI {
* Returns the current nominator of the time signature
*/
return this.app.clock.time_signature[0];
- }
+ };
meter = (): number => {
/**
@@ -935,20 +957,25 @@ export class UserAPI {
public mod = (...n: number[]): boolean => {
const results: boolean[] = n.map(
- (value) => this.app.clock.pulses_since_origin % Math.floor(value * this.ppqn()) === 0
+ (value) =>
+ this.app.clock.pulses_since_origin % Math.floor(value * this.ppqn()) ===
+ 0
);
return results.some((value) => value === true);
};
public modpulse = (...n: number[]): boolean => {
- const results: boolean[] = n.map((value) => this.app.clock.pulses_since_origin % value === 0);
+ const results: boolean[] = n.map(
+ (value) => this.app.clock.pulses_since_origin % value === 0
+ );
return results.some((value) => value === true);
};
modp = this.modpulse;
public modbar = (...n: number[]): boolean => {
const results: boolean[] = n.map(
- (value) => this.app.clock.time_position.bar % Math.floor(value * this.ppqn()) === 0
+ (value) =>
+ this.app.clock.time_position.bar % Math.floor(value * this.ppqn()) === 0
);
return results.some((value) => value === true);
};
@@ -1000,7 +1027,7 @@ export class UserAPI {
return final_pulses.some((p) => p == true);
};
- oncount = (beats: number[]|number, count: number): boolean => {
+ oncount = (beats: number[] | number, count: number): boolean => {
/**
* Returns true if the current beat is in the given list of beats.
*
@@ -1010,11 +1037,11 @@ export class UserAPI {
* @param beat - The beats to check
* @returns True if the current beat is in the given list of beats
*/
- if(typeof beats === "number") beats = [beats];
+ if (typeof beats === "number") beats = [beats];
const origin = this.app.clock.pulses_since_origin;
let final_pulses: boolean[] = [];
beats.forEach((b) => {
- b = b<1 ? 0 : b-1;
+ b = b < 1 ? 0 : b - 1;
const beatInTicks = Math.ceil(b * this.ppqn());
const meterPosition = origin % (this.ppqn() * count);
return final_pulses.push(meterPosition === beatInTicks);
diff --git a/src/Clock.ts b/src/Clock.ts
index e50029d..aca16bc 100644
--- a/src/Clock.ts
+++ b/src/Clock.ts
@@ -59,6 +59,15 @@ export class Clock {
});
}
+ convertTicksToTimeposition(ticks: number): TimePosition {
+ const beatsPerBar = this.app.clock.time_signature[0];
+ const ppqnPosition = ticks % this.app.clock.ppqn;
+ const beatNumber = Math.floor(ticks / this.app.clock.ppqn);
+ const barNumber = Math.floor(beatNumber / beatsPerBar);
+ const beatWithinBar = Math.floor(beatNumber % beatsPerBar);
+ return { bar: barNumber, beat: beatWithinBar, pulse: ppqnPosition };
+ }
+
get ticks_before_new_bar(): number {
/**
* This function returns the number of ticks separating the current moment
diff --git a/src/TransportNode.js b/src/TransportNode.js
index a515b5e..64c2fef 100644
--- a/src/TransportNode.js
+++ b/src/TransportNode.js
@@ -18,8 +18,7 @@ export class TransportNode extends AudioWorkletNode {
this.app.clock.tick++
- const futureTimeStamp = this.convertTicksToTimeposition(this.app.clock.tick);
- //console.log("BANG", this.logicalTime, futureTimeStamp);
+ const futureTimeStamp = this.app.clock.convertTicksToTimeposition(this.app.clock.tick);
this.app.clock.time_position = futureTimeStamp;
tryEvaluate(this.app, this.app.global_buffer);
@@ -27,14 +26,6 @@ export class TransportNode extends AudioWorkletNode {
}
};
- convertTicksToTimeposition(ticks) {
- const beatsPerBar = this.app.clock.time_signature[0];
- const ppqnPosition = (ticks % this.app.clock.ppqn);
- const beatNumber = Math.floor(ticks / this.app.clock.ppqn);
- const barNumber = Math.floor(beatNumber / beatsPerBar);
- const beatWithinBar = Math.floor(beatNumber % beatsPerBar);
- return {bar: barNumber, beat: beatWithinBar, pulse: ppqnPosition};
- }
start() {
this.port.postMessage("start");
diff --git a/src/documentation/time.ts b/src/documentation/time.ts
index a0c5ae4..ef5bddb 100644
--- a/src/documentation/time.ts
+++ b/src/documentation/time.ts
@@ -223,7 +223,53 @@ prob(80)::mod(.5) && sound('hh').out()
true
)}
+## Time Warping
+
+Time is cool. But it's even cooler when you can manipulate it to your liking. Think about jumping back or forward in time. Think about looping a specific part of your current pattern or song. This is all possible thanks to two simple functions: warp(n: number) and beat_warp(n: number). They are both very easy to use and very powerful. Let's see how they work.
+
+- warp(n: number): this function jumps to the _n_ tick of the clock. 1 is the first pulsation ever, and the number keeps increasing to the infinite.
+
+
+${makeExample(
+ "Jumping back and forth in time",
+ `
+// Obscure Shenanigans - Bubobubobubo
+mod([1/4,1/8,1/16].div(8)):: sound('sine')
+ .freq([100,50].div(16) + 50 * ($(1)%10))
+ .gain(0.5).room(0.9).size(0.9)
+ .sustain(0.1).out()
+mod(1) :: sound('kick').out()
+mod(2) :: sound('dr').n(5).out()
+div(3) :: mod([.25,.5].div(.5)) :: sound('dr')
+ .n([8,9].pick()).gain([.8,.5,.25,.1,.0].div(.25)).out()
+// Time is elastic now!
+mod(.25) :: warp([12, 48, 24, 1, 120, 30].pick())
+`,
+ true
+)}
+- beat_warp(beat: number): this function jumps to the _n_ beat of the clock. The first beat is 1.
+
+${makeExample(
+ "Jumping back and forth with beats",
+ `
+// Resonance bliss - Bubobubobubo
+mod(.25)::snd('arpy')
+ .note(30 + [0,3,7,10].beat())
+ .cutoff(usine(.5) * 5000).resonance(10).gain(0.3)
+ .end(0.8).room(0.9).size(0.9).n(0).out();
+mod([.25,.125].div(2))::snd('arpy')
+ .note(30 + [0,3,7,10].beat())
+ .cutoff(usine(.5) * 5000).resonance(20).gain(0.3)
+ .end(0.8).room(0.9).size(0.9).n(3).out();
+mod(.5) :: snd('arpy').note(
+ [30, 33, 35].repeatAll(4).div(1) - [12,0].div(0.5)).out()
+// Comment me to stop warping!
+mod(1) :: beat_warp([2,4,5,10,11].pick())
+`,
+ true
+)}
+
## Larger time divisions
Now you know how to play some basic rhythmic music but you are a bit stuck in a one-bar long loop. Let's see how we can think about time flowing on longer periods. The functions you are going to learn now are _very fundamental_ and all the fun comes from mastering them. **Read and experiment a lot with the following examples**.