-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPROJECT-MAP
187 lines (143 loc) · 7.75 KB
/
PROJECT-MAP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
All are encouraged to modify this code as they see fit. This file is meant to
introduce the codebase and make contributing easier.
/* General info */
All lines, including comments, are at or under 80 chars in length.
All functions and types defined `static` within a .c file are intended for
local use within the context of larger functions. Their headers are located at
the top of the .c file.
All functions and types referenced by other sections of the program are defined
in a corresponding .h file. These header files are #included liberally, which
is why they must contain `#pragma once` at the top to avoided redefining
anything.
All newly-defined structs and enums are typedef'd to StructName and EnumName.
They are referred to as such throughout the program. Types defined by external
libraries are not typedef'd, and retain their original formatting.
All struct fields are PascalCase. This was an accident of history, based on the
author's former enthusiasm for and half understanding of the Go language. Maybe
they will all be refactored to camelCase at some stage. All enum and #defines
are ALL_CAPPED. Constants such as these should reveal which file they were
defined in. The default sample rate, defined in "defaults.h" is DEFAULT_RATE,
for instance.
All function names are camelCase. The return type of a function (including any
possible pointers) is expressed on its own line. The function name and
parameters follow on the next. Parameter names are camelCase. Their terseness
or verbosity depends upon how complex the function is. Ideally, an argument
is an easy to understand abbreviation of its type; most Voice type parameters
are passed as `v`, etc. The opening brace is included after the parameters,
and not on its own line.
The body of a function opens with a line break, a /* comment */ that describes
the function, and another line break. All variables are declared at the top of
the body. These variables must be initialized during declaration. If there is
no immediate value to assign to a variable, then it will take a zero-type, such
as 0, false, NULL, etc. Variables are camelCase. Another line break follows
variable declaration.
An example function:
static float
doublePhase(const Osc *o) {
/* Doubles an oscillator's current phase. This makes no sense from a synthesis
* standpoint, but is a quick and easy example, so just go with it. */
const float p = o->Phase;
return p * 2.0f;
}
Due to the realtime nature of synthesis, many functions are not referentially
transparent and do not return values that can be meticulously error-checked.
It is considered acceptable to modify playback variables in place through void
procedures. A small audio glitch should not bring the entire system to a
grinding halt.
This liberal approach to sound should be balanced by more stringent checks on
initialization and user input. Initialization functions are still often void,
but they will throw fatal errors with errx() if anything goes wrong. User
input should be checked for correctness and discarded with a warnx() message
if it is found lacking.
boar should compile according to the flags in `Makefile`. All warnings should
be considered errors, and rectified accordingly. The sole exception to this
at the moment is OpenBSD's complaint about `rand()`.
All new features are unacceptable until they've been documented with comments
and explained in the man file `boar.1`.
/* Specific files */
The files that make up the codebase are described in a specific ground-up
manner that will hopefully make sense to somebody exploring boar's source
for the first time. The descriptions here are brief, but more information is
available in the files themselves.
/* Universal reference files */
FILE: constants/defaults.h
Contains #defines used to set default values for various parameters throughout
the program. Some of these defaults are discarded in favor of user-defined
settings, while others remain constant.
FILE constants/maximums.h
Contains constants that set strict upper and lower bounds on user input.
Nothing here defers to user input.
FILE constants/errors.h
Contains the Error enum, which is returned by various procedures to signal
specifically what went wrong with them.
/* Audio initialization */
FILE audio-settings.c audio-settings.h
Defines the AudioSettings type and its related functions, which parse the
user input into playback settings. AudioSettings remains immutable after its
initialization and is accessible to other parts of the program in a read-only
manner.
FILE buffers.c buffers.h
Audio output centers around summing synthesis data into an array of floats,
then writing it to the soundcard in an array of bytes. These files describe
a monophonic buffer for internal floats and a byte buffer for stereo output to
the soundcard.
FILE audio-init.c audio-init.h
Describes the Audio type, which uses AudioSettings to establish a link to the
sndio device that listens for audio data. The functions defined here only deal
with the initialization of Audio, and not its operation.
/* Synthesis */
FILE: numerical.c numerical.h
Miscellaneous numerical functions related to more than one task. Used by
various parts of the program.
FILE: wavetable-*.h
Wavetable files are constant arrays of floats that describe one cycle of a
an arbitrary wave shape. Audio synthesis takes place by referencing values in
these tables at specific frequencies. All wavetables are bipolar.
FILE: wave.c wave.h
Describes the WaveType enum, which is used to indicate which constant wavetable
a sound component should reference while synthesizing audio data.
FILE: envelope.c envelope.h
Describes the Env type, which is used to apply dynamism to various parts of
a signal through an ADSR envelope.
FILE velocity.c velocity.h
Defines the Velocity type, which envelopes carrier/modulator amplitudes based
upon how strongly the performer hits keys.
FILE synthesis.c synthesis.h
Defines the Osc type, which generates audio data from wavetables and writes
them to a Buffer. Also defines the Operator, which couples an Osc with an Env
for a complete synthesis unit. The bulk of arithmetic functions that govern
synthesis take place here.
FILE key.c key.h
Defines the Keyboard type, which translates MIDI key numbers into internal Osc
data. Velocity, key following, and micro-tuning take are implemented using the
Keyboard and its related functions.
/* Audio playback */
FILE voice.c voice.h
Defines the Voice type, which takes high-level music theory concepts like
notes and feeds them to Operators to deal with them in synthesis terms. The
functions here are a three-way crossroads between the Audio type, the Operator
types, and user input. All management of polyphonic playback also takes place
here.
FILE amplitude.c amplitude.h
A very simple struct that governs master volume as well as the left/right
balance of the stereo channels.
FILE audio-output.c audio-output.h
After a cycle of data has been synthesized, it is written to the Audio type's
Buffer. It is the job of the functions in this file to read the Buffer into
a ByteBuffer and write it to the soundcard.
/* User input */
FILE constants/types.h
Contains bit flags for parsing argument types from user input. Also contains
TYPE_SIGNATURES, which is an array of the expected argument type for every
possible command in boar.
FILE constants/funcs.h
Contains enum values for every user-facing boar procedure, which can be used
to dispatch functions from repl input.
FILE parse.c parse.h
Defines the Cmd type, which is where user input is parsed into program data
and sent off to execute audio commands. The functions in this file center
around inferring the type of user input and ensuring it is correct.
FILE repl.c repl.h
Defines the main loop that reads lines of user input from stdin, parses them
into arguments, and dispatches them to the Audio type to perform actual sound
generating functions.