#include #include #include #include struct poly { xas_synth **synths; size_t count; }; static int16_t sample_sine(xas_synth *synth) { int16_t ret; static float tau = 2.0f * M_PI; if (synth->state == XAS_SYNTH_ACTIVE) { ret = (int16_t)roundf(synth->gain * (INT16_MAX >> 2) * sinf(synth->phase)); synth->phase += tau / (synth->format.sample_rate / synth->frequency); if (synth->phase > tau) { synth->phase -= tau; } } else { ret = 0; } return ret; } static int16_t sample_square(xas_synth *synth) { int16_t ret; static float tau = 2.0f * M_PI; float duty = synth->duty; if (synth->state == XAS_SYNTH_ACTIVE) { ret = (synth->phase - (duty * tau)) > 0.0? (int16_t)roundf(synth->gain * (INT16_MAX / 4)): (int16_t)roundf(synth->gain * (INT16_MIN / 4)); synth->phase += tau / (synth->format.sample_rate / synth->frequency); if (synth->phase > tau) { synth->phase -= tau; } } else { ret = 0; } return ret; } static int16_t sample_triangle(xas_synth *synth) { int16_t ret; static float tau = 2.0f * M_PI; if (synth->state == XAS_SYNTH_ACTIVE) { float v = 2.0f * fabs(2.0f * fabs(synth->phase - floorf(synth->phase + 0.5))) - 1.0f; ret = (int16_t)roundf(synth->gain * (INT16_MAX >> 2) * v); synth->phase += tau / (synth->format.sample_rate / synth->frequency); if (synth->phase > tau) { synth->phase -= tau; } } else { ret = 0; } return ret; } static int16_t sample_sawtooth(xas_synth *synth) { int16_t ret; static float tau = 2.0f * M_PI; if (synth->state == XAS_SYNTH_ACTIVE) { float v = 2.0f * (synth->phase / tau - 0.5f); ret = (int16_t)roundf(synth->gain * (INT16_MAX >> 2) * v); synth->phase += tau / (synth->format.sample_rate / synth->frequency); if (synth->phase > tau) { synth->phase -= tau; } } else { ret = 0; } return ret; } static ssize_t synth_fill(xas_synth *synth, int16_t *samples, size_t count) { size_t i; if (synth->frequency > 0) { for (i=0; isample(synth); } } else { for (i=0; isample = sample_sine; break; case XAS_SYNTH_SQUARE: synth->sample = sample_square; break; case XAS_SYNTH_TRIANGLE: synth->sample = sample_triangle; break; case XAS_SYNTH_SAWTOOTH: synth->sample = sample_sawtooth; break; default: errno = EINVAL; goto error_invalid_type; } synth->type = type; return 0; error_invalid_type: return -1; } static xas_object_call_table call_table = { .start = (xas_object_start_callback)synth_start, .stop = (xas_object_stop_callback)synth_stop, .set_type = (xas_object_set_type_callback)set_type, .set_freq = (xas_object_set_freq_callback)set_freq, .set_gain = (xas_object_set_gain_callback)set_gain, .stream_new = (xas_object_stream_new_callback)xas_synth_stream_new, .destroy = (xas_object_destroy_callback)xas_synth_destroy }; xas_synth *xas_synth_new(xas_audio_format format, size_t buffer_size, enum xas_synth_type type) { xas_synth *synth; if ((synth = malloc(sizeof(*synth))) == NULL) { goto error_malloc_synth; } synth->obj.table = &call_table; synth->state = XAS_SYNTH_IDLE; synth->phase = 0.0f; synth->gain = XAS_SYNTH_DEFAULT_GAIN; synth->duty = XAS_SYNTH_DEFAULT_DUTY; synth->frequency = XAS_SYNTH_DEFAULT_FREQUENCY; synth->format.channels = XAS_AUDIO_MONO; synth->format.sample_size = format.sample_size; synth->format.sample_rate = format.sample_rate; synth->buffer_size = buffer_size; if (set_type(synth, type) < 0) { goto error_set_type; } return synth; error_set_type: free(synth); error_malloc_synth: return NULL; } void xas_synth_destroy(xas_synth *synth) { free(synth); } void xas_synth_set_gain(xas_synth *synth, float gain) { synth->gain = gain; } void xas_synth_set_duty(xas_synth *synth, float duty) { synth->duty = duty; } void xas_synth_set_type(xas_synth *synth, enum xas_synth_type type) { set_type(synth, type); } void xas_synth_set_frequency(xas_synth *synth, size_t frequency) { synth->frequency = frequency; } void xas_synth_start(xas_synth *synth) { synth->state = XAS_SYNTH_ACTIVE; } void xas_synth_stop(xas_synth *synth) { synth->state = XAS_SYNTH_IDLE; synth->phase = 0.0f; } xas_audio_stream *xas_synth_stream_new(xas_synth *synth) { return xas_audio_stream_new_source((xas_audio_fill)synth_fill, NULL, synth->format, synth->buffer_size, synth); } static ssize_t poly_fill(struct poly *poly, int16_t *samples, size_t count) { size_t s; for (s=0; scount; i++) { xas_synth *synth = poly->synths[i]; samples[s] += synth->sample(synth); } } return count; } static void poly_cleanup(struct poly *poly) { free(poly->synths); free(poly); } xas_audio_stream *xas_synth_stream_new_poly(xas_synth **synths, size_t count) { struct poly *poly; size_t i; xas_audio_format format; size_t buffer_size; if (count == 0) { errno = EINVAL; goto error_invalid_count; } format = synths[0]->format; buffer_size = synths[0]->buffer_size; if ((poly = malloc(sizeof(*poly))) == NULL) { goto error_malloc_poly; } if ((poly->synths = malloc(count * sizeof(xas_synth *))) == NULL) { goto error_malloc_synths; } for (i=0; iformat, format)) { errno = EINVAL; goto error_invalid_format; } if (synths[i]->buffer_size != buffer_size) { errno = EINVAL; goto error_invalid_buffer_size; } poly->synths[i] = synths[i]; } poly->count = count; return xas_audio_stream_new_source((xas_audio_fill)poly_fill, (xas_audio_cleanup)poly_cleanup, format, buffer_size, poly); error_invalid_buffer_size: error_invalid_format: free(poly->synths); error_malloc_synths: free(poly); error_malloc_poly: error_invalid_count: return NULL; }