mirror of
https://github.com/sjlongland/atinysynth.git
synced 2025-09-14 10:33: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
|
sine.c
|
||||||
obj
|
obj
|
||||||
bin
|
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
|
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
|
subtraction, left and right shifts, and occasional multiplication. This
|
||||||
makes it suitable for smaller CPU cores such as Atmel's TinyAVR, ARM's
|
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
|
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
|
state of multiple voices to be configured at some convenient point in
|
||||||
the program in bulk whilst still providing flexibility on when a
|
the program in bulk whilst still providing flexibility on when a
|
||||||
particular note is played. As there is no amplitude change, an
|
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".
|
be configured then triggered "on cue".
|
||||||
|
|
||||||
2. Attack phase: The amplitude starts at 0, and using an approximated
|
2. Attack phase: The amplitude starts at 0, and using an approximated
|
||||||
@ -287,7 +287,7 @@ amplitude.
|
|||||||
### PC port (`pc`)
|
### PC port (`pc`)
|
||||||
|
|
||||||
This uses `libao` and a command line interface to simulate the output of
|
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:
|
The synthesizer commands are given as command-line arguments:
|
||||||
|
|
||||||
@ -321,8 +321,11 @@ The synthesizer commands are given as command-line arguments:
|
|||||||
`A`
|
`A`
|
||||||
* `reset` resets the ADSR state machine for the selected channel.
|
* `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
|
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.
|
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 voice_ch_t poly_voice[16];
|
||||||
struct poly_synth_t synth;
|
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 main(int argc, char** argv) {
|
||||||
int voice = 0;
|
int voice = 0;
|
||||||
int16_t samples[8192];
|
int16_t samples[8192];
|
||||||
@ -37,36 +56,47 @@ int main(int argc, char** argv) {
|
|||||||
synth.enable = 0;
|
synth.enable = 0;
|
||||||
synth.mute = 0;
|
synth.mute = 0;
|
||||||
|
|
||||||
ao_device* device;
|
|
||||||
ao_sample_format format;
|
|
||||||
ao_initialize();
|
|
||||||
memset(poly_voice, 0, sizeof(poly_voice));
|
memset(poly_voice, 0, sizeof(poly_voice));
|
||||||
FILE* out = fopen("out.raw", "wb");
|
|
||||||
|
|
||||||
{
|
ao_sample_format format;
|
||||||
int driver = ao_default_driver_id();
|
memset(&format, 0, sizeof(format));
|
||||||
memset(&format, 0, sizeof(format));
|
format.bits = 16;
|
||||||
format.bits = 16;
|
format.channels = 1;
|
||||||
format.channels = 1;
|
format.rate = synth_freq;
|
||||||
format.rate = synth_freq;
|
format.byte_format = AO_FMT_NATIVE;
|
||||||
format.byte_format = AO_FMT_NATIVE;
|
|
||||||
device = ao_open_live(driver, &format, NULL);
|
ao_initialize();
|
||||||
if (!device) {
|
int wavDriver = ao_driver_id("wav");
|
||||||
fprintf(stderr, "Failed to open audio device\n");
|
ao_device* wavDevice = ao_open_file(wavDriver, "out.wav", 1, &format, NULL);
|
||||||
return 1;
|
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--;
|
argc--;
|
||||||
argv++;
|
argv++;
|
||||||
while (argc > 0) {
|
while (argc > 0) {
|
||||||
int res = 0;
|
|
||||||
|
|
||||||
if (!strcmp(argv[0], "end"))
|
if (!strcmp(argv[0], "end"))
|
||||||
break;
|
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 */
|
/* Voice selection */
|
||||||
if (!strcmp(argv[0], "voice")) {
|
} else if (!strcmp(argv[0], "voice")) {
|
||||||
voice = atoi(argv[1]);
|
voice = atoi(argv[1]);
|
||||||
_DPRINTF("select voice %d\n", voice);
|
_DPRINTF("select voice %d\n", voice);
|
||||||
argv++;
|
argv++;
|
||||||
@ -203,7 +233,7 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
while (synth.enable) {
|
while (synth.enable) {
|
||||||
int16_t* sample_ptr = samples;
|
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 */
|
/* Fill the buffer as much as we can */
|
||||||
while (synth.enable && samples_remain) {
|
while (synth.enable && samples_remain) {
|
||||||
_DPRINTF("enable = 0x%lx\n", synth.enable);
|
_DPRINTF("enable = 0x%lx\n", synth.enable);
|
||||||
@ -213,15 +243,18 @@ int main(int argc, char** argv) {
|
|||||||
samples_sz++;
|
samples_sz++;
|
||||||
samples_remain--;
|
samples_remain--;
|
||||||
}
|
}
|
||||||
fwrite(samples, samples_sz, 2, out);
|
ao_play(wavDevice, (char*)samples, 2*samples_sz);
|
||||||
ao_play(device, (char*)samples, 2*samples_sz);
|
if (liveDevice) {
|
||||||
|
ao_play(liveDevice, (char*)samples, 2*samples_sz);
|
||||||
|
}
|
||||||
samples_sz = 0;
|
samples_sz = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(out);
|
ao_close(wavDevice);
|
||||||
|
if (liveDevice) {
|
||||||
ao_close(device);
|
ao_close(liveDevice);
|
||||||
|
}
|
||||||
ao_shutdown();
|
ao_shutdown();
|
||||||
return 0;
|
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