Skip to content

Instantly share code, notes, and snippets.

@h4k1m0u
Created April 6, 2024 10:46
Show Gist options
  • Save h4k1m0u/cf78cd3df706acfe0c2041e0f3aa91e3 to your computer and use it in GitHub Desktop.
Save h4k1m0u/cf78cd3df706acfe0c2041e0f3aa91e3 to your computer and use it in GitHub Desktop.
Play a sound corresp. to a synthetic note (from sine wave) from soundcard using miniaudio
  • Modes:
    • Playback: Emit sound from speaker by writing data to output buffer (in callback).
    • Capture: Extract sound from microphone by reading data from input buffer (in callback).
  • Sample: Single unit of audio data (when format = f32, a sample is a float in [-1, 1]).
  • Frame (PCM Frame): Groups of samples having as many samples as the number of channels (stereo: 1 frame = 2 samples, mono: 1 frame = 1 sample).
  • Sample rate: Number of frames processed each second expressed in Hz (e.g 44100 Hz).
  • Device: Abstraction for a physical device.
  • Callback: Function used to deliver data to/from the device async.
  • Data source: Used for retrieving audio data from a source (e.g. decoder, waveform for generrating sine waves...).
  • Decoder: Used for reading audio files (wav, mp3, flac).
  • Sine wave:
    • Amplitude: expresses sound's loudness.
    • Frequency: expresses sound's pitch (bass or acute).
/**
* Play a sound corresp. to a synthetic note (from sine wave) from soundcard using miniaudio
* Modified version of: https://stackoverflow.com/a/72512136/2228912
* How to build: gcc -lm waveform.c -o app
*
* Other resources:
* Raylib sinewave example: https://github.com/raysan5/raylib/blob/master/examples/audio/audio_raw_stream.c
* Miniaudio sinewave example: https://github.com/mackron/miniaudio/blob/master/examples/simple_playback_sine.c
* Youtube tutorial (sound synth in cpp on windows): https://www.youtube.com/watch?v=tgamhuQnOkM
*/
#define MA_NO_DECODING
#define MA_NO_ENCODING
#define MINIAUDIO_IMPLEMENTATION
#include "miniaudio.h"
// sample_rate = # of samples / sec => t_sample = 1 / sample_rate
const float SAMPLE_RATE = 48000; // for music in movies (44100 for music on CDs)
const float DURATION_SAMPLE = 1.0f / SAMPLE_RATE;
// A4 note: determines pitch (acute or bass)
const float FREQUENCY = 440;
float t = 0;
/* Populate 'pOutput' with 'frameCount' frames. */
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount) {
// each sample is a float in [-1, 1]
float* pOutputF32 = (float*)pOutput;
// hakim: frameCount = how many frames can be written to the output buffer
// neverending synthetic data generated from sine wave (one iteration fills a single frame)
// hakim: sound is represented as a waveform (variation of amplitude as a fct of t)
for (ma_uint32 curFrame = 0; curFrame < frameCount; ++curFrame) {
// y = f(t) = sin(2pi * freq * t)
float y = sin(MA_TAU_D * FREQUENCY * t);
// next sample
t += DURATION_SAMPLE;
// force same sequence (otherwise looping causes a slight change in pitch after a while)
if (t >= 1.0) {
t = 0;
}
pOutputF32[curFrame] = y;
}
// unused
(void) pInput;
(void) pDevice;
}
int main(void) {
ma_device_config deviceConfig;
ma_device device;
deviceConfig = ma_device_config_init(ma_device_type_playback);
deviceConfig.playback.format = ma_format_f32;
deviceConfig.playback.channels = 1; // mono (i.e. 1 frame = 1 sample)
deviceConfig.sampleRate = SAMPLE_RATE;
deviceConfig.dataCallback = data_callback;
if (ma_device_init(NULL, &deviceConfig, &device) != MA_SUCCESS) {
printf("Failed to open playback device.\n");
return 1;
}
// hakim: begins playback (calls callback)
if (ma_device_start(&device) != MA_SUCCESS) {
printf("Failed to start playback device.\n");
ma_device_uninit(&device);
return 1;
}
printf("Press Enter to quit...\n");
getchar();
ma_device_uninit(&device);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment