241 lines
6.6 KiB
C++
241 lines
6.6 KiB
C++
// Copyright 2024 Sean Luke
|
|
// (sean@cs.gmu.edu)
|
|
//
|
|
// Released under the Apache 2.0 License
|
|
|
|
|
|
/// BYTE
|
|
///
|
|
/// Byte is a bytebeat synthesizer similar to ALGODRONE or ByteBeat-Xfade. You can provide
|
|
/// up to 16 bytebeat expressions (defined as BYTEBEAT_1 through BYTEBEAT_16 below), and
|
|
/// then select and play them. Feel free to change them! But they must be valid C/C++
|
|
/// expressions or they won't compile. You can change the pitch and tuning, as well as the
|
|
/// volume. Additionally you can reset the bytebeat algorithm at any time by sending a
|
|
/// trigger or gate to DIGITAL OUT.
|
|
///
|
|
/// BUILDING BYTEBEATS
|
|
/// A bytebeat is any C expression using the 32-bit variable 't'. It's called 16384 times a second
|
|
/// and the result, mod 255, is output as an audio sample.
|
|
///
|
|
/// Byte has a special feature: a special variable called x which ranges from 0 to 255. It's
|
|
/// controlled by CV coming in to IN 3, and is by default 0. You could stick x in your expressions
|
|
/// to control them in real-time via CV.
|
|
///
|
|
/// Byte is fragile: if in your expression you modify Byte's own variables (like triggered or pitchCV
|
|
/// or even t!) you can seriously break Byte. So don't do that.
|
|
///
|
|
///
|
|
/// TUNING
|
|
///
|
|
/// You can't really tune BYTE: to do so would require changing the sampling rate, and Mozzi doesn't
|
|
/// make it easy to do that without modifying Mozzi's config in its library. Maybe later.
|
|
///
|
|
/// On Pot 1 I have set things up so you can change the pitch to some degree. There are 16
|
|
/// settings. Setting 8 (typically a bit to the left of due center) is the default setting.
|
|
/// Since Mozzi's sampling rate is 16384 Hz, this is about one octave above and twice as fast
|
|
/// as the traditional pitch, which normally corresponds to 8000 Hz. If you turn the knob
|
|
/// to the LEFT of this, Byte will get slower and lower in pitch. It does this by just playing
|
|
/// fewer times when Mozzi asks it to. It's pretty crude but it works. If you turn the knob to
|
|
/// the RIGHT, Byte will get higher in pitch but also change how it plays. This is because it
|
|
/// is incrementing the "t" variable by more and more (by default it's incremented by 1).
|
|
///
|
|
///
|
|
/// SET GRAINS TO MOZZI MODE. Sorry, no Grains mode.
|
|
///
|
|
/// You will need to install the Mozzi Library. You can do this from the Library Manager
|
|
/// in your Arduino IDE.
|
|
|
|
|
|
/// CONFIGURATION
|
|
///
|
|
/// IN 1 Pitch CV
|
|
/// IN 2 Bytebeat CV
|
|
/// IN 3 Auxilliary Variable 1 CV (x)
|
|
/// AUDIO IN (A) UNUSED
|
|
/// AUDIO OUT Out
|
|
/// DIGITAL OUT (D) Reset
|
|
///
|
|
/// POT 1 Pitch Scaling [Set the switch to MAN probably]
|
|
///
|
|
/// POT 2 Bytebeat [Set the switch to MAN probably]
|
|
///
|
|
/// POT 3 Volume
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
// FOR MORE BYTEBEAT EXAMPLES SEE
|
|
// http://viznut.fi/demos/unix/bytebeat_formulas.txt
|
|
//
|
|
// Test your bytebeats out first in HTML!
|
|
// https://greggman.com/downloads/examples/html5bytebeat/html5bytebeat.html
|
|
|
|
#define BYTEBEAT_1 \
|
|
t*(42&t>>10)
|
|
|
|
#define BYTEBEAT_2 \
|
|
t|t%255|t%257
|
|
|
|
#define BYTEBEAT_3 \
|
|
t*(((t>>11)&(t>>8))&(123&(t>>3)))
|
|
|
|
#define BYTEBEAT_4 \
|
|
t*(t>>((t>>9)|(t>>8))&(63&(t>>4)))
|
|
|
|
#define BYTEBEAT_5 \
|
|
(t>>6|t|t>>(t>>16))*10+((t>>11)&7)
|
|
|
|
#define BYTEBEAT_6 \
|
|
(t|(t>>9|t>>7))*t&(t>>11|t>>9)
|
|
|
|
#define BYTEBEAT_7 \
|
|
t*5&(t>>7)|t*3&(t*4>>10)
|
|
|
|
#define BYTEBEAT_8 \
|
|
(t>>7|t|t>>6)*10+4*(t&t>>13|t>>6)
|
|
|
|
#define BYTEBEAT_9 \
|
|
((t&4096)?((t*(t^t%255)|(t>>4))>>1):(t>>3)|((t&8192)?t<<2:t))
|
|
|
|
#define BYTEBEAT_10 \
|
|
((t*(t>>8|t>>9)&46&t>>8))^(t&t>>13|t>>6)
|
|
|
|
#define BYTEBEAT_11 \
|
|
(t*5&t>>7)|(t*3&t>>10)
|
|
|
|
#define BYTEBEAT_12 \
|
|
(int)(t/1e7*t*t+t)%127|t>>4|t>>5|t%127+(t>>16)|t
|
|
|
|
#define BYTEBEAT_13 \
|
|
((t/2*(15&(0x234568a0>>(t>>8&28))))|t/2>>(t>>11)^t>>12)+(t/16&t&24)
|
|
|
|
#define BYTEBEAT_14 \
|
|
(t&t%255)-(t*3&t>>13&t>>6)
|
|
|
|
#define BYTEBEAT_15 \
|
|
t>>4|t&((t>>5)/(t>>7-(t>>15)&-t>>7-(t>>15)))
|
|
|
|
#define BYTEBEAT_16 \
|
|
t*(((t>>9)&10)|((t>>11)&24)^((t>>10)&15&(t>>15)))
|
|
|
|
|
|
|
|
|
|
|
|
#define CV_POT_IN1 A7 // Note In, Pitch Scaling // Filter Cutoff
|
|
#define CV_POT_IN2 A6 // Filter Cutoff
|
|
#define CV_POT3 A4 // Resonance
|
|
#define CV_IN3 A2 // Amplitude CV
|
|
#define CV_AUDIO_IN A0 // Pitch Tune // Resonance CV
|
|
#define CV_AUDIO_OUT 9 // Out
|
|
#define CV_GATE_OUT 10 // [Unused]
|
|
#define RANDOM_PIN A5
|
|
|
|
#define CONTROL_RATE 64
|
|
|
|
|
|
#include <MozziGuts.h>
|
|
|
|
void setup()
|
|
{
|
|
pinMode(CV_GATE_OUT, INPUT);
|
|
startMozzi();
|
|
}
|
|
|
|
uint8_t triggered = false;
|
|
uint8_t x;
|
|
uint8_t frequency = 1;
|
|
uint8_t gain;
|
|
uint8_t expression;
|
|
uint32_t t = 0;
|
|
uint8_t oldExpression = 0;
|
|
uint8_t maxCount = 1;
|
|
uint8_t increment = 1;
|
|
|
|
void updateControl()
|
|
{
|
|
frequency = (mozziAnalogRead(CV_POT_IN1) >> 6) + 1;
|
|
if (frequency >= 8)
|
|
{
|
|
maxCount = 1;
|
|
increment = frequency - 7;
|
|
}
|
|
else
|
|
{
|
|
maxCount = 9 - frequency;
|
|
increment = 1;
|
|
}
|
|
|
|
gain = mozziAnalogRead(CV_POT3) >> 5; // 0...31
|
|
x = mozziAnalogRead(CV_IN3) >> 2; // 0...255
|
|
expression = mozziAnalogRead(CV_POT_IN2) >> 6; // 0...15
|
|
if (digitalRead(CV_GATE_OUT))
|
|
{
|
|
if (!triggered)
|
|
{
|
|
triggered = true;
|
|
t = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
triggered = false;
|
|
}
|
|
|
|
if (expression != oldExpression)
|
|
{
|
|
t = 0;
|
|
oldExpression = expression;
|
|
}
|
|
}
|
|
|
|
|
|
uint8_t count = 1;
|
|
int16_t last = 0;
|
|
|
|
int updateAudio()
|
|
{
|
|
count--;
|
|
if (count == 0)
|
|
{
|
|
count = maxCount;
|
|
|
|
// shift pitch
|
|
t += increment;
|
|
|
|
// select expression
|
|
uint8_t result;
|
|
switch(expression)
|
|
{
|
|
case 0: result = (uint8_t)(255 & (BYTEBEAT_1)); break;
|
|
case 1: result = (uint8_t)(255 & (BYTEBEAT_2)); break;
|
|
case 2: result = (uint8_t)(255 & (BYTEBEAT_3)); break;
|
|
case 3: result = (uint8_t)(255 & (BYTEBEAT_4)); break;
|
|
case 4: result = (uint8_t)(255 & (BYTEBEAT_5)); break;
|
|
case 5: result = (uint8_t)(255 & (BYTEBEAT_6)); break;
|
|
case 6: result = (uint8_t)(255 & (BYTEBEAT_7)); break;
|
|
case 7: result = (uint8_t)(255 & (BYTEBEAT_8)); break;
|
|
case 8: result = (uint8_t)(255 & (BYTEBEAT_9)); break;
|
|
case 9: result = (uint8_t)(255 & (BYTEBEAT_10)); break;
|
|
case 10: result = (uint8_t)(255 & (BYTEBEAT_11)); break;
|
|
case 11: result = (uint8_t)(255 & (BYTEBEAT_12)); break;
|
|
case 12: result = (uint8_t)(255 & (BYTEBEAT_13)); break;
|
|
case 13: result = (uint8_t)(255 & (BYTEBEAT_14)); break;
|
|
case 14: result = (uint8_t)(255 & (BYTEBEAT_15)); break;
|
|
case 15: result = (uint8_t)(255 & (BYTEBEAT_16)); break;
|
|
}
|
|
|
|
// convert to signed and change amplitude
|
|
last = ((result - (int16_t)128) * gain) >> 4; // gain should go 0...31
|
|
}
|
|
return last;
|
|
}
|
|
|
|
|
|
void loop()
|
|
{
|
|
audioHook();
|
|
}
|
|
|