mirror of
https://github.com/sjlongland/atinysynth.git
synced 2025-09-13 10:03:15 +10:00
- Wav output instead of raw via libao
- Support missing live ao support - Added support for script file
This commit is contained in:
parent
a83da25fa1
commit
6e5978d7a3
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@
|
||||
sine.c
|
||||
obj
|
||||
bin
|
||||
out.wav
|
||||
|
13
README.md
13
README.md
@ -16,7 +16,7 @@ Principle of operation
|
||||
----------------------
|
||||
|
||||
The library runs as a state machine. Synthesis is performed completely
|
||||
using only integer artihmatic operations: specifically addition,
|
||||
using only integer arithmetic operations: specifically addition,
|
||||
subtraction, left and right shifts, and occasional multiplication. This
|
||||
makes it suitable for smaller CPU cores such as Atmel's TinyAVR, ARM's
|
||||
Cortex M0+, lower-end TI MSP430 and other minimalist CPU cores that lack
|
||||
@ -51,7 +51,7 @@ waveform moves through the following states:
|
||||
state of multiple voices to be configured at some convenient point in
|
||||
the program in bulk whilst still providing flexibility on when a
|
||||
particular note is played. As there is no amplitude change, an
|
||||
"infinte" time delay may also be specified here, allowing a note to
|
||||
"infinite" time delay may also be specified here, allowing a note to
|
||||
be configured then triggered "on cue".
|
||||
|
||||
2. Attack phase: The amplitude starts at 0, and using an approximated
|
||||
@ -287,7 +287,7 @@ amplitude.
|
||||
### PC port (`pc`)
|
||||
|
||||
This uses `libao` and a command line interface to simulate the output of
|
||||
the synthesizer. It was used to debug the synthesizer.
|
||||
the synthesizer and to output a `.wav` file. It was used to debug the synthesizer.
|
||||
|
||||
The synthesizer commands are given as command-line arguments:
|
||||
|
||||
@ -321,8 +321,11 @@ The synthesizer commands are given as command-line arguments:
|
||||
`A`
|
||||
* `reset` resets the ADSR state machine for the selected channel.
|
||||
|
||||
Or, alternatively, you can pass all the above commands stored in a text file:
|
||||
* `-- NAME` loads and run the script, and skip all the remaining arguments.
|
||||
|
||||
Once the `enable` bit-mask is set, the program loops, playing sound via
|
||||
`libao` and writing the samples to `out.raw` for later analysis until
|
||||
`libao` and writing the samples to `out.wav` for later analysis until
|
||||
all bits in the `enable` bit-mask are cleared by the ADSR state machines.
|
||||
|
||||
When the program runs out of command line arguments, it exits.
|
||||
When the program runs out of command line arguments, or the script ends, it exits.
|
||||
|
@ -28,6 +28,25 @@ const uint16_t synth_freq = 32000;
|
||||
struct voice_ch_t poly_voice[16];
|
||||
struct poly_synth_t synth;
|
||||
|
||||
/* Read a script instead of command-line tokens */
|
||||
static void readScript(const char* name, int* argc, char*** argv) {
|
||||
FILE *fp = fopen(name, "r");
|
||||
char token[64];
|
||||
int n = 0;
|
||||
int size = 16;
|
||||
char** list = malloc(size * sizeof(char*));
|
||||
while (fscanf(fp, "%63s", token) == 1) {
|
||||
list[n++] = strdup(token);
|
||||
if (n >= size) {
|
||||
size += 16;
|
||||
list = realloc(list, size * sizeof(char*));
|
||||
}
|
||||
}
|
||||
|
||||
*argv = list;
|
||||
*argc = n;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int voice = 0;
|
||||
int16_t samples[8192];
|
||||
@ -37,36 +56,47 @@ int main(int argc, char** argv) {
|
||||
synth.enable = 0;
|
||||
synth.mute = 0;
|
||||
|
||||
ao_device* device;
|
||||
ao_sample_format format;
|
||||
ao_initialize();
|
||||
memset(poly_voice, 0, sizeof(poly_voice));
|
||||
FILE* out = fopen("out.raw", "wb");
|
||||
|
||||
{
|
||||
int driver = ao_default_driver_id();
|
||||
memset(&format, 0, sizeof(format));
|
||||
format.bits = 16;
|
||||
format.channels = 1;
|
||||
format.rate = synth_freq;
|
||||
format.byte_format = AO_FMT_NATIVE;
|
||||
device = ao_open_live(driver, &format, NULL);
|
||||
if (!device) {
|
||||
fprintf(stderr, "Failed to open audio device\n");
|
||||
return 1;
|
||||
}
|
||||
ao_sample_format format;
|
||||
memset(&format, 0, sizeof(format));
|
||||
format.bits = 16;
|
||||
format.channels = 1;
|
||||
format.rate = synth_freq;
|
||||
format.byte_format = AO_FMT_NATIVE;
|
||||
|
||||
ao_initialize();
|
||||
int wavDriver = ao_driver_id("wav");
|
||||
ao_device* wavDevice = ao_open_file(wavDriver, "out.wav", 1, &format, NULL);
|
||||
if (!wavDevice) {
|
||||
fprintf(stderr, "Failed to open WAV device\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ao_device* liveDevice = NULL;
|
||||
int liveDriver = ao_default_driver_id();
|
||||
liveDevice = ao_open_live(liveDriver, &format, NULL);
|
||||
if (!liveDevice) {
|
||||
printf("Live driver not available\n");
|
||||
}
|
||||
|
||||
argc--;
|
||||
argv++;
|
||||
while (argc > 0) {
|
||||
int res = 0;
|
||||
|
||||
if (!strcmp(argv[0], "end"))
|
||||
break;
|
||||
|
||||
/* Check for external script */
|
||||
if (!strcmp(argv[0], "--")) {
|
||||
const char* name = argv[1];
|
||||
_DPRINTF("reading script %s\n", name);
|
||||
readScript(name, &argc, &argv);
|
||||
// Fake item will be skipped at the end of the if
|
||||
argc++;
|
||||
argv--;
|
||||
|
||||
/* Voice selection */
|
||||
if (!strcmp(argv[0], "voice")) {
|
||||
} else if (!strcmp(argv[0], "voice")) {
|
||||
voice = atoi(argv[1]);
|
||||
_DPRINTF("select voice %d\n", voice);
|
||||
argv++;
|
||||
@ -203,7 +233,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
while (synth.enable) {
|
||||
int16_t* sample_ptr = samples;
|
||||
uint16_t samples_remain = 8192;
|
||||
uint16_t samples_remain = sizeof(samples) / sizeof(uint16_t);
|
||||
/* Fill the buffer as much as we can */
|
||||
while (synth.enable && samples_remain) {
|
||||
_DPRINTF("enable = 0x%lx\n", synth.enable);
|
||||
@ -213,15 +243,18 @@ int main(int argc, char** argv) {
|
||||
samples_sz++;
|
||||
samples_remain--;
|
||||
}
|
||||
fwrite(samples, samples_sz, 2, out);
|
||||
ao_play(device, (char*)samples, 2*samples_sz);
|
||||
ao_play(wavDevice, (char*)samples, 2*samples_sz);
|
||||
if (liveDevice) {
|
||||
ao_play(liveDevice, (char*)samples, 2*samples_sz);
|
||||
}
|
||||
samples_sz = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(out);
|
||||
|
||||
ao_close(device);
|
||||
ao_close(wavDevice);
|
||||
if (liveDevice) {
|
||||
ao_close(liveDevice);
|
||||
}
|
||||
ao_shutdown();
|
||||
return 0;
|
||||
}
|
||||
|
3
ports/pc/test
Normal file
3
ports/pc/test
Normal file
@ -0,0 +1,3 @@
|
||||
voice 0 scale 100 delay 1 attack 100 decay 100 sustain 1000 release 10 peak 63 samp 32 triangle 2000 63
|
||||
voice 1 scale 100 delay 1 attack 100 decay 100 sustain 50 release 1000 peak 63 samp 32 sawtooth 1500 63
|
||||
en 3
|
Loading…
Reference in New Issue
Block a user