complete cli memo menu
This commit is contained in:
parent
2f5d9aed32
commit
9a5e237733
7 changed files with 152 additions and 15 deletions
|
@ -47,6 +47,7 @@ raw = ["raw_audio"]
|
||||||
name = "blip"
|
name = "blip"
|
||||||
path = "src/cli/main.rs"
|
path = "src/cli/main.rs"
|
||||||
required-features = ["bin"]
|
required-features = ["bin"]
|
||||||
|
include = ["doc/language-design", "doc/fasteval/*"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "blip"
|
name = "blip"
|
||||||
|
|
2
doc/examples.txt
Normal file
2
doc/examples.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
sine wave: sin(2*pi()*(442*2^((n+1)/N))*t)
|
||||||
|
classic BeepComp length: 2^(2-log(2, l))*(60/T)
|
30
doc/fasteval/functions.txt
Normal file
30
doc/fasteval/functions.txt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
* print(...strings and values...) -- Prints to stderr. Very useful to 'probe' an expression.
|
||||||
|
Evaluates to the last value.
|
||||||
|
Example: `print("x is", x, "and y is", y)`
|
||||||
|
Example: `x + print("y:", y) + z == x+y+z`
|
||||||
|
|
||||||
|
* log(base=10, val) -- Logarithm with optional 'base' as first argument.
|
||||||
|
If not provided, 'base' defaults to '10'.
|
||||||
|
Example: `log(100) + log(e(), 100)`
|
||||||
|
|
||||||
|
* e() -- Euler's number (2.718281828459045)
|
||||||
|
* pi() -- π (3.141592653589793)
|
||||||
|
|
||||||
|
* int(val)
|
||||||
|
* ceil(val)
|
||||||
|
* floor(val)
|
||||||
|
* round(modulus=1, val) -- Round with optional 'modulus' as first argument.
|
||||||
|
Example: `round(1.23456) == 1 && round(0.001, 1.23456) == 1.235`
|
||||||
|
|
||||||
|
* abs(val)
|
||||||
|
* sign(val)
|
||||||
|
|
||||||
|
* min(val, ...) -- Example: `min(1, -2, 3, -4) == -4`
|
||||||
|
* max(val, ...) -- Example: `max(1, -2, 3, -4) == 3`
|
||||||
|
|
||||||
|
* sin(radians) * asin(val)
|
||||||
|
* cos(radians) * acos(val)
|
||||||
|
* tan(radians) * atan(val)
|
||||||
|
* sinh(val) * asinh(val)
|
||||||
|
* cosh(val) * acosh(val)
|
||||||
|
* tanh(val) * atanh(val)
|
17
doc/fasteval/literals.txt
Normal file
17
doc/fasteval/literals.txt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
Several numeric formats are supported:
|
||||||
|
|
||||||
|
Integers: 1, 2, 10, 100, 1001
|
||||||
|
|
||||||
|
Decimals: 1.0, 1.23456, 0.000001
|
||||||
|
|
||||||
|
Exponents: 1e3, 1E3, 1e-3, 1E-3, 1.2345e100
|
||||||
|
|
||||||
|
Suffix:
|
||||||
|
1.23p = 0.00000000000123
|
||||||
|
1.23n = 0.00000000123
|
||||||
|
1.23µ, 1.23u = 0.00000123
|
||||||
|
1.23m = 0.00123
|
||||||
|
1.23K, 1.23k = 1230
|
||||||
|
1.23M = 1230000
|
||||||
|
1.23G = 1230000000
|
||||||
|
1.23T = 1230000000000
|
11
doc/fasteval/operators.txt
Normal file
11
doc/fasteval/operators.txt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
Listed in order of precedence:
|
||||||
|
|
||||||
|
(Highest Precedence) ^ Exponentiation
|
||||||
|
% Modulo
|
||||||
|
/ Division
|
||||||
|
* Multiplication
|
||||||
|
- Subtraction
|
||||||
|
+ Addition
|
||||||
|
== != < <= >= > Comparisons (all have equal precedence)
|
||||||
|
&& and Logical AND with short-circuit
|
||||||
|
(Lowest Precedence) || or Logical OR with short-circuit
|
|
@ -26,7 +26,7 @@ use nom::{
|
||||||
sequence::preceded,
|
sequence::preceded,
|
||||||
};
|
};
|
||||||
use nom_locate::{LocatedSpan, position};
|
use nom_locate::{LocatedSpan, position};
|
||||||
use strum::{EnumDiscriminants, EnumString, IntoDiscriminant, IntoStaticStr};
|
use strum::{EnumDiscriminants, EnumIter, EnumString, IntoDiscriminant, IntoStaticStr};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
const DEFAULT_INSTRUMENT: &str = "sin(2*pi()*(442*2^((n+1)/N))*t)";
|
const DEFAULT_INSTRUMENT: &str = "sin(2*pi()*(442*2^((n+1)/N))*t)";
|
||||||
|
@ -43,7 +43,7 @@ pub(super) enum Cli {
|
||||||
Export(ExportOpts),
|
Export(ExportOpts),
|
||||||
/// Memo menu for examples and general help about syntax and supported audio formats
|
/// Memo menu for examples and general help about syntax and supported audio formats
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Memo(Memo),
|
Memo(MemoKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser, Clone, Getters)]
|
#[derive(Debug, Parser, Clone, Getters)]
|
||||||
|
@ -275,7 +275,10 @@ pub(super) struct ExportOpts {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, EnumDiscriminants)]
|
#[derive(Clone, EnumDiscriminants)]
|
||||||
#[strum_discriminants(derive(EnumString, IntoStaticStr), strum(serialize_all = "lowercase"))]
|
#[strum_discriminants(
|
||||||
|
derive(EnumString, IntoStaticStr, EnumIter),
|
||||||
|
strum(serialize_all = "lowercase")
|
||||||
|
)]
|
||||||
pub(super) enum AudioFormat {
|
pub(super) enum AudioFormat {
|
||||||
Mp3 {
|
Mp3 {
|
||||||
bitrate: Bitrate,
|
bitrate: Bitrate,
|
||||||
|
@ -368,8 +371,8 @@ fn audio_format_parser(input: &str) -> Result<AudioFormat, anyhow::Error> {
|
||||||
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
tag::<&'a str, LocatedSpan<&'a str>, nom::error::Error<LocatedSpan<&'a str>>>(
|
||||||
AudioFormatDiscriminants::Wav.into(),
|
AudioFormatDiscriminants::Wav.into(),
|
||||||
),
|
),
|
||||||
u16.or(success(320))
|
u16.or(success(16))
|
||||||
.and(value(SampleFormat::Float, char('f')).or(success(SampleFormat::Int)))
|
.and(value(SampleFormat::Float, char('f')).or(success(SampleFormat::Float)))
|
||||||
.map(|(bps, sample_format)| AudioFormat::Wav { bps, sample_format }),
|
.map(|(bps, sample_format)| AudioFormat::Wav { bps, sample_format }),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -411,7 +414,7 @@ fn audio_format_parser(input: &str) -> Result<AudioFormat, anyhow::Error> {
|
||||||
.map_err(|e| anyhow!("{e:?}"))
|
.map_err(|e| anyhow!("{e:?}"))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser, Clone, EnumString, Default)]
|
#[derive(Debug, Parser, Clone, EnumString, Default, EnumIter, IntoStaticStr)]
|
||||||
#[strum(ascii_case_insensitive)]
|
#[strum(ascii_case_insensitive)]
|
||||||
pub(super) enum RawAudioFormat {
|
pub(super) enum RawAudioFormat {
|
||||||
ALaw,
|
ALaw,
|
||||||
|
@ -438,15 +441,29 @@ pub(super) enum RawAudioFormat {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser, Clone)]
|
#[derive(Debug, Parser, Clone)]
|
||||||
pub(super) enum Memo {
|
pub(super) enum MemoKind {
|
||||||
Syntax,
|
#[command(flatten)]
|
||||||
#[command(subcommand)]
|
Syntax(SyntaxTarget),
|
||||||
Example(Example),
|
/// Show a list of examples or a specific example from the list
|
||||||
|
Examples { n: Option<u8> },
|
||||||
|
/// Print all available formats
|
||||||
Formats,
|
Formats,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Parser, Clone)]
|
#[derive(Debug, Parser, Clone)]
|
||||||
pub(super) enum Example {
|
pub(super) enum SyntaxTarget {
|
||||||
List,
|
/// Print BLIP's grammar
|
||||||
N { id: u8 },
|
Blip,
|
||||||
|
#[command(flatten)]
|
||||||
|
Expressions(FastEvalSyntaxSection),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Parser, Clone)]
|
||||||
|
pub(super) enum FastEvalSyntaxSection {
|
||||||
|
/// Print available functions and constants for expressions
|
||||||
|
Functions,
|
||||||
|
/// Print available operators for expressions
|
||||||
|
Ops,
|
||||||
|
/// Print available literals for expressions
|
||||||
|
Literals,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
|
//! See [the lib docs](https://docs.rs/bliplib)
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
collections::{HashMap, HashSet},
|
collections::{HashMap, HashSet},
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{Cursor, Write, read_to_string, stdout},
|
io::{Cursor, Write, read_to_string, stdout},
|
||||||
|
@ -17,8 +20,12 @@ use dasp_sample::Sample;
|
||||||
use hound::{SampleFormat, WavSpec, WavWriter};
|
use hound::{SampleFormat, WavSpec, WavWriter};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
use rodio::{OutputStream, Sink, buffer::SamplesBuffer};
|
use rodio::{OutputStream, Sink, buffer::SamplesBuffer};
|
||||||
|
use strum::IntoEnumIterator;
|
||||||
|
|
||||||
use crate::cli::{ExportOpts, PlayOpts};
|
use crate::cli::{
|
||||||
|
AudioFormatDiscriminants, ExportOpts, FastEvalSyntaxSection, MemoKind, PlayOpts,
|
||||||
|
RawAudioFormat, SyntaxTarget,
|
||||||
|
};
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
@ -82,7 +89,59 @@ fn main() -> anyhow::Result<()> {
|
||||||
info!("writing samples to output");
|
info!("writing samples to output");
|
||||||
writer.write_all(buff.get_ref())?;
|
writer.write_all(buff.get_ref())?;
|
||||||
}
|
}
|
||||||
Memo(_opts) => todo!(),
|
Memo(MemoKind::Syntax(s)) => println!(
|
||||||
|
"{}",
|
||||||
|
match s {
|
||||||
|
SyntaxTarget::Blip => include_str!("../../doc/language-design.txt"),
|
||||||
|
SyntaxTarget::Expressions(FastEvalSyntaxSection::Functions) =>
|
||||||
|
include_str!("../../doc/fasteval/functions.txt"),
|
||||||
|
SyntaxTarget::Expressions(FastEvalSyntaxSection::Literals) =>
|
||||||
|
include_str!("../../doc/fasteval/literals.txt"),
|
||||||
|
SyntaxTarget::Expressions(FastEvalSyntaxSection::Ops) =>
|
||||||
|
include_str!("../../doc/fasteval/operators.txt"),
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Memo(MemoKind::Examples { n }) => {
|
||||||
|
let mut examples = include_str!("../../doc/examples.txt")
|
||||||
|
.lines()
|
||||||
|
.filter_map(|l| l.split_once(':'));
|
||||||
|
match n {
|
||||||
|
None => {
|
||||||
|
for (id, (name, _)) in examples.enumerate() {
|
||||||
|
println!("{id}\t{name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(id) => println!(
|
||||||
|
"{}",
|
||||||
|
examples
|
||||||
|
.nth(id.into())
|
||||||
|
.context("example not found")?
|
||||||
|
.1
|
||||||
|
.trim_start()
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Memo(MemoKind::Formats) => {
|
||||||
|
for discriminant in AudioFormatDiscriminants::iter() {
|
||||||
|
use AudioFormatDiscriminants::*;
|
||||||
|
let ext: Cow<'static, str> = match discriminant {
|
||||||
|
Mp3 => {
|
||||||
|
"\t[int, bitrate]\t\t[str, quality from 'w' (\"worst\") to 'b' (\"best\")]\t(default: mp3320g)".into()
|
||||||
|
}
|
||||||
|
Wav => {
|
||||||
|
"\t[int, bytes per sample]\t['f', set sample format to float instead of int]\t(default: wav16f)".into()
|
||||||
|
}
|
||||||
|
Flac => "\t[int, bits per sample]\t\t\t\t\t\t\t\t(default: flac320000)".into(),
|
||||||
|
Raw => format!(
|
||||||
|
"\t[str, subformat]\t\t\t\t\t\t\t\t(default: rawmulaw)\nraw subformats include: {:#?}",
|
||||||
|
RawAudioFormat::iter()
|
||||||
|
.map(Into::into)
|
||||||
|
.collect::<Vec<&'static str>>()
|
||||||
|
).into(),
|
||||||
|
};
|
||||||
|
println!("* {}{}", Into::<&'static str>::into(discriminant), ext)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue