2026-01-15 00:04:42 -05:00
|
|
|
// beep.c
|
|
|
|
|
// Build (Linux/macOS): cc beep.c -lm -o beep
|
|
|
|
|
// Build (Windows MSVC): cl /O2 beep.c
|
|
|
|
|
|
|
|
|
|
#define MINIAUDIO_IMPLEMENTATION
|
|
|
|
|
#include "miniaudio.h"
|
2026-01-14 22:49:49 -05:00
|
|
|
|
|
|
|
|
#include <math.h>
|
2026-01-15 00:04:42 -05:00
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <stdio.h>
|
2026-01-14 22:49:49 -05:00
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
typedef struct {
|
|
|
|
|
double phase;
|
|
|
|
|
double phase_inc;
|
|
|
|
|
uint32_t frames_left;
|
|
|
|
|
ma_uint32 channels;
|
|
|
|
|
} beep_state;
|
|
|
|
|
|
|
|
|
|
static void data_callback(ma_device* dev, void* out, const void* in, ma_uint32 frameCount)
|
2026-01-14 22:49:49 -05:00
|
|
|
{
|
2026-01-15 00:04:42 -05:00
|
|
|
(void)in;
|
|
|
|
|
beep_state* s = (beep_state*)dev->pUserData;
|
|
|
|
|
float* output = (float*)out;
|
2026-01-14 22:49:49 -05:00
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
ma_uint32 framesToWrite = frameCount;
|
|
|
|
|
if (s->frames_left < framesToWrite) framesToWrite = s->frames_left;
|
2026-01-14 22:49:49 -05:00
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
for (ma_uint32 i = 0; i < framesToWrite; i++) {
|
|
|
|
|
float sample = (float)sin(s->phase) * 0.2f; // volume
|
|
|
|
|
s->phase += s->phase_inc;
|
2026-01-14 22:49:49 -05:00
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
// keep phase bounded (optional)
|
|
|
|
|
if (s->phase >= (2.0 * M_PI)) s->phase -= (2.0 * M_PI);
|
2026-01-14 22:49:49 -05:00
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
for (ma_uint32 ch = 0; ch < s->channels; ch++) {
|
|
|
|
|
output[i * s->channels + ch] = sample;
|
|
|
|
|
}
|
2026-01-14 22:49:49 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
// If we ran out early, zero the rest (silence).
|
|
|
|
|
for (ma_uint32 i = framesToWrite; i < frameCount; i++) {
|
|
|
|
|
for (ma_uint32 ch = 0; ch < s->channels; ch++) {
|
|
|
|
|
output[i * s->channels + ch] = 0.0f;
|
|
|
|
|
}
|
2026-01-14 22:49:49 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
s->frames_left -= framesToWrite;
|
2026-01-14 22:49:49 -05:00
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
// Stop after the beep finishes.
|
|
|
|
|
if (s->frames_left == 0) {
|
|
|
|
|
ma_device_stop(dev); // safe to request stop
|
2026-01-14 22:49:49 -05:00
|
|
|
}
|
2026-01-15 00:04:42 -05:00
|
|
|
}
|
2026-01-14 22:49:49 -05:00
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
int main(void)
|
|
|
|
|
{
|
|
|
|
|
const double freq = 440.0;
|
|
|
|
|
const ma_uint32 sampleRate = 48000;
|
|
|
|
|
const double durationSec = 1.0;
|
|
|
|
|
|
|
|
|
|
beep_state s = {0};
|
|
|
|
|
s.phase = 0.0;
|
|
|
|
|
s.phase_inc = (2.0 * M_PI * freq) / (double)sampleRate;
|
|
|
|
|
s.frames_left = (uint32_t)(durationSec * (double)sampleRate);
|
|
|
|
|
|
|
|
|
|
ma_device_config cfg = ma_device_config_init(ma_device_type_playback);
|
|
|
|
|
cfg.playback.format = ma_format_f32;
|
|
|
|
|
cfg.playback.channels = 2;
|
|
|
|
|
cfg.sampleRate = sampleRate;
|
|
|
|
|
cfg.dataCallback = data_callback;
|
|
|
|
|
cfg.pUserData = &s;
|
|
|
|
|
|
|
|
|
|
ma_device dev;
|
|
|
|
|
if (ma_device_init(NULL, &cfg, &dev) != MA_SUCCESS) {
|
|
|
|
|
fprintf(stderr, "Failed to init audio device\n");
|
2026-01-14 22:49:49 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
s.channels = dev.playback.channels; // in case backend adjusts
|
2026-01-14 22:49:49 -05:00
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
if (ma_device_start(&dev) != MA_SUCCESS) {
|
|
|
|
|
fprintf(stderr, "Failed to start audio device\n");
|
|
|
|
|
ma_device_uninit(&dev);
|
2026-01-14 22:49:49 -05:00
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
// Wait until callback stops the device.
|
|
|
|
|
while (ma_device_is_started(&dev)) {
|
|
|
|
|
ma_sleep(10);
|
2026-01-14 22:49:49 -05:00
|
|
|
}
|
|
|
|
|
|
2026-01-15 00:04:42 -05:00
|
|
|
ma_device_uninit(&dev);
|
2026-01-14 22:33:58 -05:00
|
|
|
return 0;
|
|
|
|
|
}
|
2026-01-15 00:04:42 -05:00
|
|
|
|