mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-06 11:18:30 +00:00
Productionize polished cosmoaudio library
This change introduces comsoaudio v1. We're using a new strategy when it comes to dynamic linking of dso files and building miniaudio device code which I think will be fast and stable in the long run. You now have your choice of reading/writing to the internal ring buffer abstraction or you can specify a device-driven callback function instead. It's now possible to not open the microphone when you don't need it since touching the mic causes security popups to happen. The DLL is now built statically, so it only needs to depend on kernel32. Our NES terminal emulator now uses the cosmoaudio library and is confirmed to be working on Windows, Mac, Linux
This commit is contained in:
parent
c66abd7260
commit
dc579b79cd
13 changed files with 640 additions and 470 deletions
|
@ -19,7 +19,7 @@ TEST_LIBS=OneCore.lib
|
|||
# Compiler flags
|
||||
CFLAGS_COMMON=/nologo /W4 /Gy /EHsc
|
||||
CFLAGS_DEBUG=/Od /Zi /MDd /D_DEBUG
|
||||
CFLAGS_RELEASE=/O2 /MD /DNDEBUG
|
||||
CFLAGS_RELEASE=/O2 /MT /DNDEBUG
|
||||
|
||||
!IF "$(MODE)"=="debug"
|
||||
CFLAGS=$(CFLAGS_COMMON) $(CFLAGS_DEBUG)
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Copyright 2024 Justine Alexandra Roberts Tunney
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for
|
||||
// any purpose with or without fee is hereby granted, provided that the
|
||||
// above copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
// WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
// WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
// AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
// DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
// PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
// TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
#define COSMOAUDIO_BUILD
|
||||
#include "cosmoaudio.h"
|
||||
#include <stdio.h>
|
||||
|
@ -7,6 +22,10 @@
|
|||
#define MA_STATIC
|
||||
#define MA_NO_DECODING
|
||||
#define MA_NO_ENCODING
|
||||
#define MA_NO_ENGINE
|
||||
#define MA_NO_GENERATION
|
||||
#define MA_NO_NODE_GRAPH
|
||||
#define MA_NO_RESOURCE_MANAGER
|
||||
#ifdef NDEBUG
|
||||
#define MA_DR_MP3_NO_STDIO
|
||||
#endif
|
||||
|
@ -25,6 +44,10 @@ struct CosmoAudio {
|
|||
ma_pcm_rb output;
|
||||
ma_uint32 sampleRate;
|
||||
ma_uint32 channels;
|
||||
ma_uint32 periods;
|
||||
enum CosmoAudioDeviceType deviceType;
|
||||
cosmoaudio_data_callback_f* dataCallback;
|
||||
void* argument;
|
||||
};
|
||||
|
||||
static int read_ring_buffer(ma_pcm_rb* rb, float* pOutput, ma_uint32 frameCount,
|
||||
|
@ -88,8 +111,15 @@ static int write_ring_buffer(ma_pcm_rb* rb, const float* pInput,
|
|||
static void data_callback_f32(ma_device* pDevice, float* pOutput,
|
||||
const float* pInput, ma_uint32 frameCount) {
|
||||
struct CosmoAudio* ca = (struct CosmoAudio*)pDevice->pUserData;
|
||||
read_ring_buffer(&ca->output, pOutput, frameCount, ca->channels);
|
||||
write_ring_buffer(&ca->input, pInput, frameCount, ca->channels);
|
||||
if (ca->dataCallback) {
|
||||
ca->dataCallback(ca, pOutput, pInput, frameCount, ca->channels,
|
||||
ca->argument);
|
||||
} else {
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypePlayback)
|
||||
read_ring_buffer(&ca->output, pOutput, frameCount, ca->channels);
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypeCapture)
|
||||
write_ring_buffer(&ca->input, pInput, frameCount, ca->channels);
|
||||
}
|
||||
}
|
||||
|
||||
static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
|
||||
|
@ -97,34 +127,72 @@ static void data_callback(ma_device* pDevice, void* pOutput, const void* pInput,
|
|||
data_callback_f32(pDevice, (float*)pOutput, (const float*)pInput, frameCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current version of cosmo audio library.
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_version(void) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens access to speaker and microphone.
|
||||
*
|
||||
* @param cap will receive pointer to allocated CosmoAudio object on success,
|
||||
* which must be freed by caller with cosmoaudio_close()
|
||||
* @param sampleRate is sample rate in Hz, e.g. 44100
|
||||
* @param channels is number of channels (1 for mono, 2 for stereo)
|
||||
* @param out_ca will receive pointer to allocated CosmoAudio object,
|
||||
* which must be freed by caller with cosmoaudio_close(); if this
|
||||
* function fails, then this will receive a NULL pointer value so
|
||||
* that cosmoaudio_close(), cosmoaudio_write() etc. can be called
|
||||
* without crashing if no error checking is performed
|
||||
* @return 0 on success, or negative error code on failure
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_open(struct CosmoAudio** cap, int sampleRate,
|
||||
int channels) {
|
||||
COSMOAUDIO_ABI int cosmoaudio_open( //
|
||||
struct CosmoAudio** out_ca, //
|
||||
const struct CosmoAudioOpenOptions* options) {
|
||||
|
||||
// Validate arguments.
|
||||
if (!out_ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
*out_ca = NULL;
|
||||
if (!options)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->sizeofThis < (int)sizeof(struct CosmoAudioOpenOptions))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->periods < 0)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->sampleRate < 8000)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->channels < 1)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!options->deviceType)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (options->deviceType &
|
||||
~(kCosmoAudioDeviceTypePlayback | kCosmoAudioDeviceTypeCapture))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
|
||||
// Allocate cosmo audio object.
|
||||
struct CosmoAudio* ca;
|
||||
if (!(ca = (struct CosmoAudio*)malloc(sizeof(struct CosmoAudio))))
|
||||
if (!(ca = (struct CosmoAudio*)calloc(1, sizeof(struct CosmoAudio))))
|
||||
return COSMOAUDIO_ERROR;
|
||||
ca->channels = channels;
|
||||
ca->sampleRate = sampleRate;
|
||||
ca->channels = options->channels;
|
||||
ca->sampleRate = options->sampleRate;
|
||||
ca->deviceType = options->deviceType;
|
||||
ca->periods = options->periods ? options->periods : 10;
|
||||
ca->dataCallback = options->dataCallback;
|
||||
ca->argument = options->argument;
|
||||
|
||||
// Initialize device.
|
||||
ma_result result;
|
||||
ma_device_config deviceConfig = ma_device_config_init(ma_device_type_duplex);
|
||||
deviceConfig.sampleRate = sampleRate;
|
||||
deviceConfig.capture.channels = channels;
|
||||
deviceConfig.capture.format = ma_format_f32;
|
||||
deviceConfig.capture.shareMode = ma_share_mode_shared;
|
||||
deviceConfig.playback.channels = channels;
|
||||
deviceConfig.playback.format = ma_format_f32;
|
||||
ma_device_config deviceConfig;
|
||||
deviceConfig = ma_device_config_init(ca->deviceType);
|
||||
deviceConfig.sampleRate = ca->sampleRate;
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypeCapture) {
|
||||
deviceConfig.capture.channels = ca->channels;
|
||||
deviceConfig.capture.format = ma_format_f32;
|
||||
deviceConfig.capture.shareMode = ma_share_mode_shared;
|
||||
}
|
||||
if (ca->deviceType & kCosmoAudioDeviceTypePlayback) {
|
||||
deviceConfig.playback.channels = ca->channels;
|
||||
deviceConfig.playback.format = ma_format_f32;
|
||||
}
|
||||
deviceConfig.dataCallback = data_callback;
|
||||
deviceConfig.pUserData = ca;
|
||||
result = ma_device_init(NULL, &deviceConfig, &ca->device);
|
||||
|
@ -134,51 +202,67 @@ COSMOAUDIO_ABI int cosmoaudio_open(struct CosmoAudio** cap, int sampleRate,
|
|||
}
|
||||
|
||||
// Initialize the speaker ring buffer.
|
||||
result = ma_pcm_rb_init(ma_format_f32, channels,
|
||||
ca->device.playback.internalPeriodSizeInFrames * 10,
|
||||
NULL, NULL, &ca->output);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_device_uninit(&ca->device);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
if (!ca->dataCallback && (ca->deviceType & kCosmoAudioDeviceTypePlayback)) {
|
||||
result = ma_pcm_rb_init(
|
||||
ma_format_f32, ca->channels,
|
||||
ca->device.playback.internalPeriodSizeInFrames * ca->periods, NULL,
|
||||
NULL, &ca->output);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_device_uninit(&ca->device);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
ma_pcm_rb_set_sample_rate(&ca->output, ca->sampleRate);
|
||||
}
|
||||
ma_pcm_rb_set_sample_rate(&ca->output, sampleRate);
|
||||
|
||||
// Initialize the microphone ring buffer.
|
||||
result = ma_pcm_rb_init(ma_format_f32, channels,
|
||||
ca->device.capture.internalPeriodSizeInFrames * 10,
|
||||
NULL, NULL, &ca->input);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
ma_device_uninit(&ca->device);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
if (!ca->dataCallback && (ca->deviceType & kCosmoAudioDeviceTypeCapture)) {
|
||||
result = ma_pcm_rb_init(
|
||||
ma_format_f32, ca->channels,
|
||||
ca->device.capture.internalPeriodSizeInFrames * ca->periods, NULL, NULL,
|
||||
&ca->input);
|
||||
if (result != MA_SUCCESS) {
|
||||
if (!ca->dataCallback && (ca->deviceType & kCosmoAudioDeviceTypePlayback))
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
ma_device_uninit(&ca->device);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
ma_pcm_rb_set_sample_rate(&ca->output, ca->sampleRate);
|
||||
}
|
||||
ma_pcm_rb_set_sample_rate(&ca->output, sampleRate);
|
||||
|
||||
// Start audio playback.
|
||||
if (ma_device_start(&ca->device) != MA_SUCCESS) {
|
||||
ma_pcm_rb_uninit(&ca->input);
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
if (!ca->dataCallback && (ca->deviceType & kCosmoAudioDeviceTypeCapture))
|
||||
ma_pcm_rb_uninit(&ca->input);
|
||||
if (!ca->dataCallback && (ca->deviceType & kCosmoAudioDeviceTypePlayback))
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
ma_device_uninit(&ca->device);
|
||||
free(ca);
|
||||
return COSMOAUDIO_ERROR;
|
||||
}
|
||||
|
||||
*cap = ca;
|
||||
*out_ca = ca;
|
||||
return COSMOAUDIO_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes audio device and frees all associated resources.
|
||||
*
|
||||
* Calling this function twice on the same object will result in
|
||||
* undefined behavior.
|
||||
*
|
||||
* @param ca is CosmoAudio object returned earlier by cosmoaudio_open()
|
||||
* @return 0 on success, or negative error code on failure
|
||||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_close(struct CosmoAudio* ca) {
|
||||
if (!ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
ma_device_uninit(&ca->device);
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
ma_pcm_rb_uninit(&ca->input);
|
||||
if (!ca->dataCallback && (ca->deviceType & kCosmoAudioDeviceTypePlayback))
|
||||
ma_pcm_rb_uninit(&ca->output);
|
||||
if (!ca->dataCallback && (ca->deviceType & kCosmoAudioDeviceTypeCapture))
|
||||
ma_pcm_rb_uninit(&ca->input);
|
||||
free(ca);
|
||||
return COSMOAUDIO_SUCCESS;
|
||||
}
|
||||
|
@ -201,6 +285,18 @@ COSMOAUDIO_ABI int cosmoaudio_close(struct CosmoAudio* ca) {
|
|||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_write(struct CosmoAudio* ca, const float* data,
|
||||
int frames) {
|
||||
if (!ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (frames < 0)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (ca->dataCallback)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!(ca->deviceType & kCosmoAudioDeviceTypePlayback))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!frames)
|
||||
return 0;
|
||||
if (!data)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
return write_ring_buffer(&ca->output, data, frames, ca->channels);
|
||||
}
|
||||
|
||||
|
@ -222,6 +318,18 @@ COSMOAUDIO_ABI int cosmoaudio_write(struct CosmoAudio* ca, const float* data,
|
|||
*/
|
||||
COSMOAUDIO_ABI int cosmoaudio_read(struct CosmoAudio* ca, float* data,
|
||||
int frames) {
|
||||
if (!ca)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (frames < 0)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (ca->dataCallback)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!(ca->deviceType & kCosmoAudioDeviceTypeCapture))
|
||||
return COSMOAUDIO_EINVAL;
|
||||
if (!frames)
|
||||
return 0;
|
||||
if (!data)
|
||||
return COSMOAUDIO_EINVAL;
|
||||
return read_ring_buffer(&ca->input, data, frames, ca->channels);
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -11,14 +11,16 @@
|
|||
#else
|
||||
#define COSMOAUDIO_API
|
||||
#ifdef __x86_64__
|
||||
#define COSMOAUDIO_ABI __attribute__((__ms_abi__))
|
||||
#define COSMOAUDIO_ABI __attribute__((__ms_abi__, __visibility__("default")))
|
||||
#else
|
||||
#define COSMOAUDIO_ABI
|
||||
#define COSMOAUDIO_ABI __attribute__((__visibility__("default")))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define COSMOAUDIO_SUCCESS 0
|
||||
#define COSMOAUDIO_ERROR -1
|
||||
#define COSMOAUDIO_SUCCESS -0 // no error or nothing written
|
||||
#define COSMOAUDIO_ERROR -1 // unspecified error
|
||||
#define COSMOAUDIO_EINVAL -2 // invalid parameters passed to api
|
||||
#define COSMOAUDIO_ELINK -3 // loading cosmoaudio dso failed
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -26,13 +28,78 @@ extern "C" {
|
|||
|
||||
struct CosmoAudio;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_open(struct CosmoAudio **, int,
|
||||
int) COSMOAUDIO_ABI;
|
||||
COSMOAUDIO_API int cosmoaudio_close(struct CosmoAudio *) COSMOAUDIO_ABI;
|
||||
COSMOAUDIO_API int cosmoaudio_write(struct CosmoAudio *, const float *,
|
||||
int) COSMOAUDIO_ABI;
|
||||
COSMOAUDIO_API int cosmoaudio_read(struct CosmoAudio *, float *,
|
||||
int) COSMOAUDIO_ABI;
|
||||
typedef void cosmoaudio_data_callback_f( //
|
||||
struct CosmoAudio *ca, //
|
||||
float *outputSamples, //
|
||||
const float *inputSamples, //
|
||||
int frameCount, //
|
||||
int channels, //
|
||||
void *argument);
|
||||
|
||||
enum CosmoAudioDeviceType {
|
||||
kCosmoAudioDeviceTypePlayback = 1,
|
||||
kCosmoAudioDeviceTypeCapture = 2,
|
||||
kCosmoAudioDeviceTypeDuplex =
|
||||
kCosmoAudioDeviceTypePlayback | kCosmoAudioDeviceTypeCapture,
|
||||
};
|
||||
|
||||
struct CosmoAudioOpenOptions {
|
||||
|
||||
// This field must be set to sizeof(struct CosmoAudioOpenOptions) or
|
||||
// cosmoaudio_open() will return COSMOAUDIO_EINVAL.
|
||||
int sizeofThis;
|
||||
|
||||
// Whether you want this object to open the speaker or microphone.
|
||||
// Please note that asking for microphone access may cause some OSes
|
||||
// like MacOS to show a popup asking the user for permission.
|
||||
enum CosmoAudioDeviceType deviceType;
|
||||
|
||||
// The sample rate can be 44100 for CD quality, 8000 for telephone
|
||||
// quality, etc. Values below 8000 are currently not supported.
|
||||
int sampleRate;
|
||||
|
||||
// The number of audio channels in each interleaved frame. Should be 1
|
||||
// for mono or 2 for stereo.
|
||||
int channels;
|
||||
|
||||
// Number of periods in ring buffer. Set to 0 for default. Higher
|
||||
// numbers (e.g. 20) means more buffering. Lower numbers (e.g. 2)
|
||||
// means less buffering. This is ignored if callback is specified.
|
||||
int periods;
|
||||
|
||||
// If callback is NULL, then cosmoaudio_write() and cosmoaudio_read()
|
||||
// should be used, which ring buffer audio to the default internal
|
||||
// routine. Setting this callback to non-NULL puts CosmoAudio in
|
||||
// manual mode, where the callback is responsible for copying PCM
|
||||
// samples each time the device calls this.
|
||||
cosmoaudio_data_callback_f *dataCallback;
|
||||
|
||||
// This is an arbitrary value passed to the callback.
|
||||
void *argument;
|
||||
};
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_version(void) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_open( //
|
||||
struct CosmoAudio **out_ca, //
|
||||
const struct CosmoAudioOpenOptions *options //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_close( //
|
||||
struct CosmoAudio *ca //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_write( //
|
||||
struct CosmoAudio *ca, //
|
||||
const float *samples, //
|
||||
int frameCount //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
COSMOAUDIO_API int cosmoaudio_read( //
|
||||
struct CosmoAudio *ca, //
|
||||
float *out_samples, //
|
||||
int frameCount //
|
||||
) COSMOAUDIO_ABI;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -1,5 +1,12 @@
|
|||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#if 0
|
||||
/*─────────────────────────────────────────────────────────────────╗
|
||||
│ To the extent possible under law, Justine Tunney has waived │
|
||||
│ all copyright and related or neighboring rights to this file, │
|
||||
│ as it is written in the following disclaimers: │
|
||||
│ • http://unlicense.org/ │
|
||||
│ • http://creativecommons.org/publicdomain/zero/1.0/ │
|
||||
╚─────────────────────────────────────────────────────────────────*/
|
||||
#endif
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
@ -9,28 +16,43 @@
|
|||
#define M_PIf 3.14159265358979323846f
|
||||
#endif
|
||||
|
||||
int g_hz = 44100;
|
||||
int g_channels = 2;
|
||||
int g_generation = 0;
|
||||
int g_freq = 440;
|
||||
|
||||
void data_callback(struct CosmoAudio *ca, float *outputSamples,
|
||||
const float *inputSamples, int frameCount, int channels,
|
||||
void *argument) {
|
||||
for (int i = 0; i < frameCount; i++) {
|
||||
float t = (float)g_generation++ / g_hz;
|
||||
if (g_generation == g_hz)
|
||||
g_generation = 0;
|
||||
float s = sinf(2 * M_PIf * g_freq * t);
|
||||
for (int j = 0; j < channels; j++)
|
||||
outputSamples[i * channels + j] = s;
|
||||
}
|
||||
(void)inputSamples;
|
||||
(void)argument;
|
||||
(void)ca;
|
||||
}
|
||||
|
||||
int main() {
|
||||
|
||||
int hz = 44100;
|
||||
int channels = 2;
|
||||
struct CosmoAudioOpenOptions cao = {};
|
||||
cao.sizeofThis = sizeof(struct CosmoAudioOpenOptions);
|
||||
cao.deviceType = kCosmoAudioDeviceTypePlayback;
|
||||
cao.sampleRate = g_hz;
|
||||
cao.channels = g_channels;
|
||||
cao.dataCallback = data_callback;
|
||||
|
||||
struct CosmoAudio *ca;
|
||||
if (cosmoaudio_open(&ca, hz, channels) != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "%s: failed to open audio\n", argv[0]);
|
||||
if (cosmoaudio_open(&ca, &cao) != COSMOAUDIO_SUCCESS) {
|
||||
fprintf(stderr, "failed to open audio\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int n = 1000;
|
||||
int sample = 0;
|
||||
float *buf = (float *)malloc(sizeof(float) * channels * n);
|
||||
for (;;) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
float freq = 440;
|
||||
float t = (float)sample++ / hz;
|
||||
if (sample == hz)
|
||||
sample = 0;
|
||||
buf[i * channels] = sinf(freq * 2.f * M_PIf * t);
|
||||
buf[i * channels + 1] = sinf(freq * 2.f * M_PIf * t);
|
||||
}
|
||||
cosmoaudio_write(ca, buf, 128);
|
||||
}
|
||||
fgetc(stdin);
|
||||
|
||||
cosmoaudio_close(ca);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue