Compare commits
No commits in common. "9ed583e4e4b66ad674553e17ef3dcbb64eacc927" and "72f5bdfd3dbd4e6876ad24b37e25406067bfc109" have entirely different histories.
9ed583e4e4
...
72f5bdfd3d
6 changed files with 9 additions and 193 deletions
|
@ -4,15 +4,11 @@ version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
amplify = "4.7.0"
|
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
derived-deref = "2.1.0"
|
derived-deref = "2.1.0"
|
||||||
fasteval = "0.2.4"
|
fasteval = "0.2.4"
|
||||||
nom = "7.1.3"
|
|
||||||
serde = "1.0.209"
|
|
||||||
serde_yml = "0.0.12"
|
|
||||||
splines = "4.3.1"
|
splines = "4.3.1"
|
||||||
tinyaudio = { version = "0.1", optional = true }
|
tinyaudio = { version = "0.1", optional = true }
|
||||||
|
|
||||||
|
|
44
poc/poc.yml
44
poc/poc.yml
|
@ -1,44 +0,0 @@
|
||||||
# fixed
|
|
||||||
instruments:
|
|
||||||
# instrument name
|
|
||||||
sine:
|
|
||||||
# fixed
|
|
||||||
expr: sin(2*PI*f*t) # instrument formula (f is the frequency in Hertz, t is the time in seconds)
|
|
||||||
square:
|
|
||||||
expr: v*abs(sin(2*PI*f*t))
|
|
||||||
# fixed
|
|
||||||
vars:
|
|
||||||
# name of the variable
|
|
||||||
v: 1 # initial value of the variable
|
|
||||||
channels:
|
|
||||||
melody:
|
|
||||||
instr: sine
|
|
||||||
score:
|
|
||||||
aabc.
|
|
||||||
'rt°y
|
|
||||||
+d+d+d---
|
|
||||||
/ff/f\\
|
|
||||||
ab>c<ba
|
|
||||||
;this is a comment (or lyrics whatever),
|
|
||||||
s:df
|
|
||||||
(3deff)
|
|
||||||
(deff)
|
|
||||||
[ffe]
|
|
||||||
{1-cos((PI*x)/2),acced}
|
|
||||||
abbc!o5cc!v15feed!l4fedd!t60hdd
|
|
||||||
# rest: .
|
|
||||||
# pizz.: '°
|
|
||||||
# volume: +-
|
|
||||||
# length: /\
|
|
||||||
# octave: ><
|
|
||||||
# comment?: ;,
|
|
||||||
# start here: ':'
|
|
||||||
# glissando: {EXPR, score}
|
|
||||||
# loop: ()
|
|
||||||
# loop with count: (COUNT, score)
|
|
||||||
# tuple: []
|
|
||||||
# modifier: !
|
|
||||||
# volume modifier prefix: v
|
|
||||||
# octave modifier prefix: o
|
|
||||||
# length modifier prefix: l
|
|
||||||
# tempo modifier prefix: t
|
|
|
@ -1,42 +0,0 @@
|
||||||
use fasteval::Instruction;
|
|
||||||
|
|
||||||
mod lex;
|
|
||||||
|
|
||||||
pub(super) enum Atom {
|
|
||||||
Note(u8),
|
|
||||||
Rest,
|
|
||||||
StartHere,
|
|
||||||
Modifier(Modifier),
|
|
||||||
QuickModifier(QuickModifier),
|
|
||||||
Wrapper(WrapperKind, Vec<Atom>),
|
|
||||||
EmptyWrapper(WrapperKind),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) enum Modifier {
|
|
||||||
Volume(u8),
|
|
||||||
Octave(u8),
|
|
||||||
Length(u8),
|
|
||||||
Tempo(u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) enum QuickModifier {
|
|
||||||
Volume(bool),
|
|
||||||
Octave(bool),
|
|
||||||
Length(bool),
|
|
||||||
Pizz(bool),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) enum WrapperKind {
|
|
||||||
Loop(u8),
|
|
||||||
Tuple,
|
|
||||||
Slope(SlopeModifier, Instruction),
|
|
||||||
Comment,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) enum SlopeModifier {
|
|
||||||
Note,
|
|
||||||
Volume,
|
|
||||||
Octave,
|
|
||||||
Length,
|
|
||||||
Tempo,
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
use super::{Atom, Modifier, WrapperKind};
|
|
||||||
const MORE: bool = true;
|
|
||||||
const LESS: bool = false;
|
|
||||||
const ON: bool = true;
|
|
||||||
const OFF: bool = false;
|
|
||||||
|
|
||||||
struct WrappingTokens;
|
|
||||||
impl WrappingTokens {
|
|
||||||
const PARENTHESES: (char, char) = ('(', ')');
|
|
||||||
const SQUARE_BRACKETS: (char, char) = ('[', ']');
|
|
||||||
const BRACKETS: (char, char) = ('{', '}');
|
|
||||||
const SEMICOLON_COMMA: (char, char) = (';', ',');
|
|
||||||
}
|
|
||||||
|
|
||||||
trait Token<T> {
|
|
||||||
fn token(self) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Token<(char, char)> for WrapperKind {
|
|
||||||
fn token(self) -> (char, char) {
|
|
||||||
match self {
|
|
||||||
Self::Loop(_) => WrappingTokens::PARENTHESES,
|
|
||||||
Self::Tuple => WrappingTokens::SQUARE_BRACKETS,
|
|
||||||
Self::Slope(_, _) => WrappingTokens::BRACKETS,
|
|
||||||
WrapperKind::Comment => WrappingTokens::SEMICOLON_COMMA,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Token<char> for Modifier {
|
|
||||||
fn token(self) -> char {
|
|
||||||
match self {
|
|
||||||
Self::Volume(_) => 'v',
|
|
||||||
Self::Octave(_) => 'o',
|
|
||||||
Self::Length(_) => 'l',
|
|
||||||
Self::Tempo(_) => 't',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Token<char> for Atom {
|
|
||||||
fn token(self) -> char {
|
|
||||||
match self {
|
|
||||||
Atom::Rest => '.',
|
|
||||||
Atom::StartHere => ':',
|
|
||||||
Atom::Modifier(_) => '!',
|
|
||||||
_ => unimplemented!("not a singleton"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
60
src/cli.rs
60
src/cli.rs
|
@ -1,6 +1,5 @@
|
||||||
use std::{fmt::Display, fs::read_to_string, io, str::FromStr};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use amplify::{From, Wrapper};
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
/// Cli entry point
|
/// Cli entry point
|
||||||
|
@ -9,9 +8,9 @@ use clap::Parser;
|
||||||
pub enum BngCli {
|
pub enum BngCli {
|
||||||
/// Play the song through default sink
|
/// Play the song through default sink
|
||||||
Play(PlayOpts),
|
Play(PlayOpts),
|
||||||
/// Export the song to a sound FileContents
|
/// Export the song to a sound file
|
||||||
Export(ExportOpts),
|
Export(ExportOpts),
|
||||||
/// List supported sound FileContents extensions and instrument / song available expressions
|
/// List supported sound file extensions and instrument / song available expressions
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
List(ListOpts),
|
List(ListOpts),
|
||||||
}
|
}
|
||||||
|
@ -20,27 +19,24 @@ pub enum BngCli {
|
||||||
#[derive(Clone, Parser)]
|
#[derive(Clone, Parser)]
|
||||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
#[cfg_attr(debug_assertions, derive(Debug))]
|
||||||
pub struct PlayOpts {
|
pub struct PlayOpts {
|
||||||
#[arg(value_parser = FileContents::from_str)]
|
input: PathBuf,
|
||||||
input: FileContents,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`BngCli`] "export" command options
|
/// [`BngCli`] "export" command options
|
||||||
#[derive(Clone, Parser)]
|
#[derive(Clone, Parser)]
|
||||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
#[cfg_attr(debug_assertions, derive(Debug))]
|
||||||
pub struct ExportOpts {
|
pub struct ExportOpts {
|
||||||
/// Input FileContents (written song FileContents)
|
/// Input file (written song file)
|
||||||
#[arg(value_parser = FileContents::from_str)]
|
input: PathBuf,
|
||||||
input: FileContents,
|
/// Output file (sound file)
|
||||||
/// Output FileContents (sound FileContents)
|
output: PathBuf,
|
||||||
#[arg(value_parser = AudioFileName::from_str)]
|
|
||||||
output: AudioFileName,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// [`BngCli`] "list" command sub-commands
|
/// [`BngCli`] "list" command sub-commands
|
||||||
#[derive(Clone, Parser)]
|
#[derive(Clone, Parser)]
|
||||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
#[cfg_attr(debug_assertions, derive(Debug))]
|
||||||
pub enum ListOpts {
|
pub enum ListOpts {
|
||||||
/// List supported sound FileContents extensions to export songs
|
/// List supported sound file extensions to export songs
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Extensions,
|
Extensions,
|
||||||
/// List available math expressions for instrument definition
|
/// List available math expressions for instrument definition
|
||||||
|
@ -50,41 +46,3 @@ pub enum ListOpts {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
Glyphs,
|
Glyphs,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Wrapper, From)]
|
|
||||||
#[wrapper(Deref)]
|
|
||||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
|
||||||
pub struct FileContents(String);
|
|
||||||
|
|
||||||
impl FromStr for FileContents {
|
|
||||||
type Err = io::Error;
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
read_to_string(s).map(Into::into)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Wrapper, From)]
|
|
||||||
#[wrapper(Deref)]
|
|
||||||
#[cfg_attr(debug_assertions, derive(Debug))]
|
|
||||||
pub struct AudioFileName(String);
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct UnsupportedFileExtensionError;
|
|
||||||
|
|
||||||
impl std::error::Error for UnsupportedFileExtensionError {}
|
|
||||||
|
|
||||||
impl Display for UnsupportedFileExtensionError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"The extension of the selected output sound file is not supported."
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for AudioFileName {
|
|
||||||
type Err = UnsupportedFileExtensionError;
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
||||||
Ok(s.to_owned().into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,11 +1,9 @@
|
||||||
#![feature(unsize)]
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
mod bng;
|
mod bng;
|
||||||
mod cli;
|
mod cli;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// println!("{}", option_env!("TEST").unwrap_or("ok"));
|
|
||||||
let args = cli::BngCli::parse();
|
let args = cli::BngCli::parse();
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
println!("{:?}", args);
|
println!("{:?}", args);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue