Chata is an idea for programming language that’s just for DSP, or Digital Signal Processing.
Chata gets rid of all the junk and cruft associated with DSP, letting you do awesome things easier than ever!
What is DSP anyway?
Tons of things today use DSP, or Digital Signal Processing, to make them work.
Take your wireless noise cancelling headphones for example. They’re using DSP right now to beam that wireless signal and cancel that noise.
My guitar pedal with zillions of different effects uses a special DSP processor to create them all.
But how do you even get to this point?
Enter the clown world of DSP programming.
Current DSP processors often require bespoke tools to work with them. For example, take SigmaStudio.
If you just want to type code instead, then you’re dealing with more bespoke SDKs, APIs, and other fun things.
What about running DSP on a regular computer instead? At least you don’t have to deal with closed-source, visual tools, but now you’ve got even more bespoke libraries to deal with.
Is there a way out?
Enter Chata.
The goal of Chata is to be the dedicated way to do great things with DSP. To get really creative, imagine a one-two punch of an open road where your mind can run full speed with a language that lets you express your ideas, and a powerful ecosystem to put those ideas to work.
Sound interesting? Read on.
[!Important] Chata doesn’t actually exist yet.
First, let’s write a standard that’s as polished as it can be. Then, once the standard is ready, we can start making it real.
The reason for doing this is because I think it’s sometimes better to know what you want to achieve first and then work towards that actionable goal rather than working towards something and making up the goal along the way.
Here’s some of my goals for Chata:
The language standard will live in this repository right here on GitHub. :octocat:
We’ll figure out what it should look like and see if we can write some example programs that solve common problems.
[!Tip] We’re all in this journey together!
There’s already a similar thing called SOUL, or SOUnd Language.
And there’s now a followup called Cmajor!
There’s also something else called Faust.
:
, ,
, ~
, <:
, and :>
. With only a couple exceptions, Chata’s non-text symbols, on the other hand, are standard mathematical symbols that aren’t specific to one language, neither Human nor programming.How about something called Glicol?
Introducing yet another competitor: Extempore!
Let’s finish this comparison off with Mimium:
Common to all of these options is that there is only one toolchain available for each. This is a monoculture, which means that all projects using it depend on one toolchain to work. Additionally, noncompliant behavior from a single toolchain influences the standard by becoming what projects experience. That’s not good if you’re interested in language standardization. Other languages like C, C++, Rust, Zig, and Go don’t suffer from this. An example is with C and C++’s “big three” of GCC, Clang, and MSVC. For Rust, there’s GCC and rustc. For Go, there’s also GCC and the official go toolchain. Therefore, it’s in our best interest that Chata never becomes a monoculture by making sure multiple toolchains remain available.
Other than that, there is almost nothing available that even compares to Chata. Therefore, we’ll be competing only with ourselves to make the best DSP language out there.
If you want to help out, feel free to do so! I want Chata to be something that everyone can feel good about because it listens to them.
How To Help
Check out the Discord server above :point_up:.
After that, if you see something that you want to change, fork this repo and make those changes. Then, we’ll see if they integrate well into the main codebase.
A Chata implementation is any software that follows the Chata standard.
A keyword is a symbol with an assigned meaning in a Chata implementation.
An action is a collection of symbols and keywords that contains the following in this order:
action
type name
separated by a comma (,
) character{
)}
)[!Note] An action is like a function in regular programming languages, but because functions in Chata have a slightly different way of working, we call them actions instead.
A Chata program is at least one action with name “main.”
A Chata program file is a text file that contains only text encoded in UTF-8 format, and that end in a .cha
extension, and that contains a valid Chata program.
A symbol is the name of an action, function, variable, or other component of a Chata program that the code refers to by a Human-readable string of valid characters.
A statement is a combination of symbols that fits on one line.
A scope is the part of a Chata program in which code within the part can access variables.
A variable is named container of data of a specific type.
A namespace is a way to abstract variables within a single name.
All symbols must contain only the following classes of text characters:
Variable names must start with a Latin script character.
Variable names must not be a symbol already used by a Chata implementation.
All statements must end with an ASCII line feed character or an ASCII carriage return character followed by an ASCII line feed character.
[!Note] This means that either
\n
or\r\n
can delimit lines regardless of if the platform uses either option conventionally.
Chata provides some ways to change your code before an implemention runs the main
action.
Insert a file where the inject
symbol appears. Specify the filename after inject
with or without quotes, using POSIX conventions.
The inject
symbol may appear within an action.
Example
inject foo.cha
inject foo bar.cha
inject "bar.cha"
inject "bar foo.cha"
action main {
inject baz.cha
# do stuff here
}
There are these variable types in Chata:
The real
type is a floating-point or posit number.
The signal
type is a real
with minimum value -1.0 and maximum value 1.0.
The int
type is a signed integer.
The imag
type is the same as real
, except that it has the semantics of imaginary numbers.
[!Note] For example, if you multiplied a
imag
with itself, you would get a negative result.
The complex
type combines a real
and a imag
.
The Chata implementation defines the precision that all number types use.
If adding a value to a type where the result exceeds the type’s maximum capacity in either the positive or negative direction, the type must remain at its maximum possible value.
[!Note] This means that if you add 1 to an
int
at value 2^31, it will stay at 2^31. This is also called “saturating” a value.
All actions start with the keyword action
followed by the name of the action, optionally variables, followed by an opening curly bracket, Chata code, and closing curly bracket.
Example
action main {
foobar
}
Actions may specify which variables that they can access directly in the format action action-name, 1st-variable-type 1st-variable-name {code}
. To provide multiple variables, use the format action action-name, 1st-variable-type 1st-variable-name, 2nd-variable-type 2nd-variable-name {code}
.
Example
An action that accepts a variable called in1
of type signal
and another of name count
of type int
can look like
action main, signal in1, int count {
foobar
}
Variable names must not start with a number.
You can create a variable by declaring it in the format variable-type variable-name
.
Example
Declare a signal variable of name foo
with signal foo
:
action main {
signal foo
}
All variables which do not depend on other variables contain the value 0 by default.
To create a variable that matches the value of a different variable, declare it with variable-type variable-name = target-variable-name
. Chata implementations must only copy the value held by target-variable-name
into variable-name
.
Chata supports all standard algebraic math symbols like +, -, *, /, <, >, <= (alias for ≤), => (alias for ≥), != (alias for ≠), =, | , and ^. |
Chata supports mathematical constants like π, τ, e, and i.
Chata supports mathematical functions like sqrt() (alias for √()), sin(), cos(), tan(), sec(), csc(), cot(), sinh(), cosh(), and tanh().
Chata supports standard mathematical syntax such as 5pi (alias for 5π) and 2 + 1.2sin(e)√(5), and (foovariable)sin(1)
Chata supports different mathematical order of operations, but PEMDAS is the default.
All trigonometric functions use radians for angle units.
[!Note] This means that you can effortlessly do math on variables as if you were in a math class!
Example
action main, signal in {
signal foo = 2πin
}
To use an action, use the format action-name 1st-operand, 2nd-operand, ...
.
Example
action doSomething, signal one, signal two {
signal three = one + two
}
action main, signal in, signal in2 {
doSomething in, in2
}
Chata provides several built-in actions that all implementations must include.
out target, ...
Set variables as the output signals of a Chata program.
Example
action main, real in1 {
out in1
}
[!Note] You can only use the
out
action in themain
action.
set target, value, ...
Set a variable to one or more values.
Example
int foo
set foo, 100, 69, 420, 42
// result: foo is 42
add target, value, ...
Add one or more values to a variable.
Example
int foo
add foo, 1
// result: foo is 1
add foo, 100, 50
// result: foo is 151
mul target, value, ...
Multiply one or more values with a variable.
Example
int foo
mul foo, 10000000
// result: foo is 0
add foo, 5
mul foo, 100, 1, 5
// result: foo is 2500
sub target, value, ...
Subtract one or more values from a variable.
Example
int foo = 100
sub foo, 10
// result: foo is 90
sub foo, foo - 50, 1
// result: foo is 49
Set a variable to one or more values if a condition is true.
setif condition, target, value, ...
Example
int foo = 100
sub foo, 50
setif foo = 50, foo, 10
// result: foo is 10
[!Note]
setif
exists to provide an easy way to do predicated actions on variables as is commonly done in DSPs.
Add one or more values to a variable if a condition is true.
addif condition, target, value, ...
Example
int foo = 100
sub foo, 50
addif foo = 50, foo, 10
// result: foo is 60
Subtract one or more values from a variable if a condition is true.
subif condition, target, value, ...
Example
int foo = 100
sub foo, 50
subif foo = 50, foo, 10
// result: foo is 40
Multiply one or more values with a variable if a condition is true.
mulif condition, target, value, ...
Example
int foo = 100
sub foo, 50
mulif foo = 50, foo, 10
// result: foo is 500
All Chata programs have a global scope where all actions within the program can access any variables declared in it.
Within an action, only code within the action and other actions created within it can access variables created in that action.
Only code following the creation of a variable may access that variable.
Chata implementations must not interpret code comments as code.
Code comments can start anywhere.
Code comments which span a single line start with the characters //
.
Code comments which can span multiple lines start with the character #
and end with the character #
.
[!Note] The reason for using
#
for multi-line comments was so that you don’t have to type more characters compared to a single-line comment.
A namespace may contain one or more variables and/or namespaces under a single name. For example, the foo
namespace may contain the variables bar
and baz
.
You can access variables or namespaces within a namespace in Chata in the format namespace:content
. For example, to access the bar
variable within foo
, use foo:bar
.
To create a namespace, use the format namespace namespace-name {content}
. content
is lines of variable or namespace declarations.
Example
signal someGlobalVar
namespace foo {
signal bar
int baz
}
#namespace junk {
blah var
}#
action main, signal in {
// make a copy of foo:bar
signal bat = foo:bar
set foo:baz, someGlobalVar
}
[!Note] Here’s the equivalent code but in C++:
float someGlobalVar; namespace foo { float bar; int baz; } /*namespace junk { blah var }*/ int main(float& in) { //note that you can't actually do this in C++! // make a copy of foo:bar float bat = foo::bar; foo::baz = someGlobalVar + bat }
Chata provides several global variables that are always present in all Chata programs.
This variable of type int
represents the current sample rate of the Chata implementation in Hertz. If the Chata implementation does not have a set sample rate, the value is 0.
Access the sample rate with std:SampleRate
.
Example
action main {
int foo = std:SampleRate
}
action main, signal in1 {
int hertz = 440
int samplesPerCycle = std:SampleRate / hertz
int cycles
set in1, sin(2πcycles/samplesPerCycle)
add cycles, 1
}
issues:
cycles
will eventually saturate which leads to no more signals getting madeNot implemented yet
Not implemented yet
Not implemented yet
Not implemented yet
Bachata music, especially the guitar component of it, I think is the coolest thing ever. If you don’t know what that is, check out the explanation on my profile page.
To make the “classic” bachata guitar sound, you need four audio effects: high-pass EQ, compression, 20ms-delay stereo chorus, and reverb, in that order. Unfortunately, unless you want to lug around a bunch of heavy guitar pedals, a laptop, or a vintage Ibanez PT-4 from the 1990s, a DSP “multi-effects processor” is the only practical option. Also unfortunately, researching how these DSP things work revealed how sorry of a state the professional audio industry is regarding free and open-source software.
So, I decided to just make the solution.
Sound interesting? Take a listen for yourself!
Aventura: Cuando volverás (English Version)
Note that ⏫ this one ⏫ doesn’t actually use a PT-4 due to barely predating its release in 1993. However, Antony Santos (NOT to be confused with Romeo Santos) was the first to use it to define the sound of bachata music.