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"
|
||||
path = "src/cli/main.rs"
|
||||
required-features = ["bin"]
|
||||
include = ["doc/language-design", "doc/fasteval/*"]
|
||||
|
||||
[[example]]
|
||||
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,
|
||||
};
|
||||
use nom_locate::{LocatedSpan, position};
|
||||
use strum::{EnumDiscriminants, EnumString, IntoDiscriminant, IntoStaticStr};
|
||||
use strum::{EnumDiscriminants, EnumIter, EnumString, IntoDiscriminant, IntoStaticStr};
|
||||
use thiserror::Error;
|
||||
|
||||
const DEFAULT_INSTRUMENT: &str = "sin(2*pi()*(442*2^((n+1)/N))*t)";
|
||||
|
@ -43,7 +43,7 @@ pub(super) enum Cli {
|
|||
Export(ExportOpts),
|
||||
/// Memo menu for examples and general help about syntax and supported audio formats
|
||||
#[command(subcommand)]
|
||||
Memo(Memo),
|
||||
Memo(MemoKind),
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone, Getters)]
|
||||
|
@ -275,7 +275,10 @@ pub(super) struct ExportOpts {
|
|||
}
|
||||
|
||||
#[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 {
|
||||
Mp3 {
|
||||
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>>>(
|
||||
AudioFormatDiscriminants::Wav.into(),
|
||||
),
|
||||
u16.or(success(320))
|
||||
.and(value(SampleFormat::Float, char('f')).or(success(SampleFormat::Int)))
|
||||
u16.or(success(16))
|
||||
.and(value(SampleFormat::Float, char('f')).or(success(SampleFormat::Float)))
|
||||
.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:?}"))
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone, EnumString, Default)]
|
||||
#[derive(Debug, Parser, Clone, EnumString, Default, EnumIter, IntoStaticStr)]
|
||||
#[strum(ascii_case_insensitive)]
|
||||
pub(super) enum RawAudioFormat {
|
||||
ALaw,
|
||||
|
@ -438,15 +441,29 @@ pub(super) enum RawAudioFormat {
|
|||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone)]
|
||||
pub(super) enum Memo {
|
||||
Syntax,
|
||||
#[command(subcommand)]
|
||||
Example(Example),
|
||||
pub(super) enum MemoKind {
|
||||
#[command(flatten)]
|
||||
Syntax(SyntaxTarget),
|
||||
/// Show a list of examples or a specific example from the list
|
||||
Examples { n: Option<u8> },
|
||||
/// Print all available formats
|
||||
Formats,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser, Clone)]
|
||||
pub(super) enum Example {
|
||||
List,
|
||||
N { id: u8 },
|
||||
pub(super) enum SyntaxTarget {
|
||||
/// Print BLIP's grammar
|
||||
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;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::{HashMap, HashSet},
|
||||
fs::File,
|
||||
io::{Cursor, Write, read_to_string, stdout},
|
||||
|
@ -17,8 +20,12 @@ use dasp_sample::Sample;
|
|||
use hound::{SampleFormat, WavSpec, WavWriter};
|
||||
use log::{debug, error, info, warn};
|
||||
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<()> {
|
||||
env_logger::init();
|
||||
|
@ -82,7 +89,59 @@ fn main() -> anyhow::Result<()> {
|
|||
info!("writing samples to output");
|
||||
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(())
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue