dorne/lib/main.c

165 lines
4.2 KiB
C

#include <math.h>
#include <spa/param/audio/format-utils.h>
#include <pipewire/pipewire.h>
#define M_PI_M2 (M_PI + M_PI)
#define DEFAULT_RATE 44100
#define DEFAULT_PERIOD 1.0 / DEFAULT_RATE
#define DEFAULT_CHANNELS 2
#define DEFAULT_VOLUME 0.7
#define DEFAULT_FREQ 500
#define DEFAULT_ACCUMULATOR -1
#define PWSTREAM_NAME "Dorne"
struct data {
struct pw_main_loop *loop;
struct pw_stream *stream;
/*
*/
double t, tcyc; // global time and cycle time
uint32_t frame, cycle;
};
#define NEW_DATA (struct data) \
{ \
.t = 0, \
.tcyc = 0, \
.frame = 0, \
.cycle = 0, \
}
typedef int16_t (*snd)(struct data*);
inline void data_progress(struct data *state) {
state->t += DEFAULT_PERIOD / DEFAULT_FREQ;
state->tcyc += DEFAULT_PERIOD / DEFAULT_FREQ;
if (++state->frame == DEFAULT_FREQ) {
state->tcyc -= DEFAULT_PERIOD;
state->frame = 0;
state->cycle++;
}
}
int16_t snd_sine(struct data *state) {
int16_t val;
/* sin() gives a value between -1.0 and 1.0, we first apply
* the volume and then scale with 32767.0 to get a 16 bits value
* between [-32767 32767].
* Another common method to convert a double to
* 16 bits is to multiple by 32768.0 and then clamp to
* [-32768 32767] to get the full 16 bits range. */
val = sin(state->tcyc * M_PI_M2 * DEFAULT_FREQ) * 32767.0 * DEFAULT_VOLUME;
return val;
}
int graph_snd(snd f, struct data *state, double t_max, FILE *fstream) {
int16_t val;
if (fstream == NULL)
return 1;
fputs("x, sin(x)\n", fstream);
while (state->t < t_max) {
val = f(state);
fprintf(fstream, "%f, %d\n", state->t, val);
data_progress(state);
}
fclose(fstream);
return 0;
}
/* [on_process] */
static void on_process(void *userdata) {
struct data *data = userdata;
struct pw_buffer *b;
struct spa_buffer *buf;
int n_frames, stride;
int16_t *dst, val;
int i, c;
if ((b = pw_stream_dequeue_buffer(data->stream)) == NULL) {
pw_log_warn("out of buffers: %m");
return;
}
buf = b->buffer;
if ((dst = buf->datas[0].data) == NULL)
return;
stride = sizeof(int16_t) * DEFAULT_CHANNELS;
n_frames = buf->datas[0].maxsize / stride;
if (b->requested)
n_frames = SPA_MIN(b->requested, n_frames);
for (i = 0; i < n_frames; i++) {
val = snd_sine(data);
for (c = 0; c < DEFAULT_CHANNELS; c++)
*dst++ = val;
}
buf->datas[0].chunk->offset = 0;
buf->datas[0].chunk->stride = stride;
buf->datas[0].chunk->size = n_frames * stride;
pw_stream_queue_buffer(data->stream, b);
}
/* [on_process] */
static const struct pw_stream_events stream_events = {
PW_VERSION_STREAM_EVENTS,
.process = on_process,
};
int main(int argc, char **argv) {
struct data data = NEW_DATA;
const struct spa_pod *params[1];
uint8_t buffer[1024];
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer));
// DEBUG: check sine wave is actually a sine wave
FILE *fstream = fopen("sine.txt", "w");
graph_snd(&snd_sine, &NEW_DATA, DEFAULT_PERIOD, fstream);
exit(69);
int pw_argc = 0;
char **pw_argv = NULL;
pw_init(&pw_argc, &pw_argv); // pw_init(&argc, &argv);
data.loop = pw_main_loop_new(NULL);
data.stream = pw_stream_new_simple(
pw_main_loop_get_loop(data.loop), PWSTREAM_NAME,
pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY,
"Playback", PW_KEY_MEDIA_ROLE, "Music", NULL),
&stream_events, &data);
params[0] = spa_format_audio_raw_build(
&b, SPA_PARAM_EnumFormat,
&SPA_AUDIO_INFO_RAW_INIT(.format = SPA_AUDIO_FORMAT_S16,
.channels = DEFAULT_CHANNELS,
.rate = DEFAULT_RATE));
pw_stream_connect(data.stream, PW_DIRECTION_OUTPUT, PW_ID_ANY,
PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
params, 1);
// TODO: use pw_thread_loop instead of pw_main_loop
pw_main_loop_run(data.loop);
printf("exitting\n");
pw_stream_destroy(data.stream);
pw_main_loop_destroy(data.loop);
return EXIT_SUCCESS;
}
/* [code] */