latency test emits periodic pulses

See #2
This commit is contained in:
Andrew Kelley 2015-09-02 12:21:41 -07:00
parent debab8e70f
commit 59fca8cb75
4 changed files with 242 additions and 65 deletions

View file

@ -147,57 +147,51 @@ set(LIBSOUNDIO_HEADERS
${CONFIGURE_OUT_FILE}
)
set(TEST_SOURCES
"${CMAKE_SOURCE_DIR}/test/unit_tests.cpp"
"${CMAKE_SOURCE_DIR}/src/util.cpp"
"${CMAKE_SOURCE_DIR}/src/os.cpp"
"${CMAKE_SOURCE_DIR}/src/soundio.cpp"
"${CMAKE_SOURCE_DIR}/src/dummy.cpp"
"${CMAKE_SOURCE_DIR}/src/channel_layout.cpp"
"${CMAKE_SOURCE_DIR}/src/ring_buffer.cpp"
)
if(SOUNDIO_HAVE_JACK)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${CMAKE_SOURCE_DIR}/src/jack.cpp"
)
set(TEST_SOURCES ${TEST_SOURCES}
"${CMAKE_SOURCE_DIR}/src/jack.cpp"
)
endif()
if(SOUNDIO_HAVE_PULSEAUDIO)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${CMAKE_SOURCE_DIR}/src/pulseaudio.cpp"
)
set(TEST_SOURCES ${TEST_SOURCES}
"${CMAKE_SOURCE_DIR}/src/pulseaudio.cpp"
)
endif()
if(SOUNDIO_HAVE_ALSA)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${CMAKE_SOURCE_DIR}/src/alsa.cpp"
)
set(TEST_SOURCES ${TEST_SOURCES}
"${CMAKE_SOURCE_DIR}/src/alsa.cpp"
)
endif()
if(SOUNDIO_HAVE_COREAUDIO)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${CMAKE_SOURCE_DIR}/src/coreaudio.cpp"
)
set(TEST_SOURCES ${TEST_SOURCES}
"${CMAKE_SOURCE_DIR}/src/coreaudio.cpp"
)
endif()
if(SOUNDIO_HAVE_WASAPI)
set(LIBSOUNDIO_SOURCES ${LIBSOUNDIO_SOURCES}
"${CMAKE_SOURCE_DIR}/src/wasapi.cpp"
)
set(TEST_SOURCES ${TEST_SOURCES}
"${CMAKE_SOURCE_DIR}/src/wasapi.cpp"
)
endif()
include_directories(
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
"${CMAKE_SOURCE_DIR}/test"
"${CMAKE_SOURCE_DIR}/src"
)
set(LIBSOUNDIO_LIBS
${JACK_LIBRARY}
${PULSEAUDIO_LIBRARY}
${ALSA_LIBRARIES}
${COREAUDIO_LIBRARY}
${COREFOUNDATION_LIBRARY}
${AUDIOUNIT_LIBRARY}
m
${CMAKE_THREAD_LIBS_INIT}
)
# GTFO, -lstdc++ !!
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
@ -209,10 +203,8 @@ set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Werror -pedantic")
set(LIB_CFLAGS "-std=c++11 -fno-exceptions -fno-rtti -fvisibility=hidden -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes -Wno-c99-extensions")
set(EXAMPLE_CFLAGS "-std=c99 -Wall")
set(EXAMPLE_INCLUDES "${CMAKE_SOURCE_DIR}/src")
set(TEST_CFLAGS "${LIB_CFLAGS} -fprofile-arcs -ftest-coverage")
set(TEST_LDFLAGS "-fprofile-arcs -ftest-coverage")
set(TEST_INCLUDES "${CMAKE_SOURCE_DIR}/test")
include(TestBigEndian)
test_big_endian(IS_BIG_ENDIAN)
@ -241,20 +233,7 @@ set_target_properties(libsoundio_shared PROPERTIES
COMPILE_FLAGS ${LIB_CFLAGS}
LINKER_LANGUAGE C
)
include_directories(
${CMAKE_SOURCE_DIR}
${CMAKE_BINARY_DIR}
)
target_link_libraries(libsoundio_shared LINK_PUBLIC
${JACK_LIBRARY}
${PULSEAUDIO_LIBRARY}
${ALSA_LIBRARIES}
${COREAUDIO_LIBRARY}
${COREFOUNDATION_LIBRARY}
${AUDIOUNIT_LIBRARY}
m
${CMAKE_THREAD_LIBS_INIT}
)
target_link_libraries(libsoundio_shared LINK_PUBLIC ${LIBSOUNDIO_LIBS})
install(TARGETS libsoundio_shared DESTINATION ${CMAKE_INSTALL_LIBDIR})
@ -276,7 +255,6 @@ add_executable(sio_sine example/sio_sine.c)
set_target_properties(sio_sine PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(sio_sine libsoundio_shared)
install(TARGETS sio_sine DESTINATION ${CMAKE_INSTALL_BINDIR})
@ -284,7 +262,6 @@ add_executable(sio_list_devices example/sio_list_devices.c)
set_target_properties(sio_list_devices PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(sio_list_devices libsoundio_shared)
install(TARGETS sio_list_devices DESTINATION ${CMAKE_INSTALL_BINDIR})
@ -292,7 +269,6 @@ add_executable(sio_microphone example/sio_microphone.c)
set_target_properties(sio_microphone PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(sio_microphone libsoundio_shared)
install(TARGETS sio_microphone DESTINATION ${CMAKE_INSTALL_BINDIR})
@ -300,49 +276,41 @@ add_executable(sio_record example/sio_record.c)
set_target_properties(sio_record PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(sio_record libsoundio_shared)
install(TARGETS sio_record DESTINATION ${CMAKE_INSTALL_BINDIR})
add_executable(unit_tests ${TEST_SOURCES})
target_link_libraries(unit_tests LINK_PUBLIC
${CMAKE_THREAD_LIBS_INIT}
${JACK_LIBRARY}
${PULSEAUDIO_LIBRARY}
${ALSA_LIBRARIES}
${COREAUDIO_LIBRARY}
${COREFOUNDATION_LIBRARY}
${AUDIOUNIT_LIBRARY}
m
)
add_executable(unit_tests "${CMAKE_SOURCE_DIR}/test/unit_tests.cpp" ${LIBSOUNDIO_SOURCES})
target_link_libraries(unit_tests LINK_PUBLIC ${LIBSOUNDIO_LIBS})
set_target_properties(unit_tests PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${TEST_CFLAGS}
LINK_FLAGS ${TEST_LDFLAGS}
)
include_directories(${TEST_INCLUDES})
add_executable(latency "${CMAKE_SOURCE_DIR}/test/latency.cpp" ${LIBSOUNDIO_SOURCES})
target_link_libraries(latency LINK_PUBLIC ${LIBSOUNDIO_LIBS})
set_target_properties(latency PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${LIB_CFLAGS}
)
add_executable(underflow test/underflow.c)
set_target_properties(underflow PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(underflow libsoundio_shared)
add_executable(backend_disconnect_recover test/backend_disconnect_recover.c)
set_target_properties(backend_disconnect_recover PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(backend_disconnect_recover libsoundio_shared)
add_executable(overflow test/overflow.c)
set_target_properties(overflow PROPERTIES
LINKER_LANGUAGE C
COMPILE_FLAGS ${EXAMPLE_CFLAGS})
include_directories(${EXAMPLE_INCLUDES})
target_link_libraries(overflow libsoundio_shared)

View file

@ -1115,7 +1115,10 @@ SOUNDIO_EXPORT int soundio_instream_get_latency(struct SoundIoInStream *instream
double *out_latency);
// Ring Buffer
/// A ring buffer is a single-reader single-writer lock-free fixed-size queue.
/// libsoundio ring buffers use memory mapping techniques to enable a
/// contiguous buffer when reading or writing across the boundary of the ring
/// buffer's capacity.
struct SoundIoRingBuffer;
/// `requested_capacity` in bytes.
/// Returns `NULL` if and only if memory could not be allocated.
@ -1129,12 +1132,12 @@ SOUNDIO_EXPORT void soundio_ring_buffer_destroy(struct SoundIoRingBuffer *ring_b
/// capacity for alignment purposes. This function returns the actual capacity.
SOUNDIO_EXPORT int soundio_ring_buffer_capacity(struct SoundIoRingBuffer *ring_buffer);
/// don't write more than capacity
/// Do not write more than capacity.
SOUNDIO_EXPORT char *soundio_ring_buffer_write_ptr(struct SoundIoRingBuffer *ring_buffer);
/// `count` in bytes.
SOUNDIO_EXPORT void soundio_ring_buffer_advance_write_ptr(struct SoundIoRingBuffer *ring_buffer, int count);
/// don't read more than capacity
/// Do not read more than capacity.
SOUNDIO_EXPORT char *soundio_ring_buffer_read_ptr(struct SoundIoRingBuffer *ring_buffer);
/// `count` in bytes.
SOUNDIO_EXPORT void soundio_ring_buffer_advance_read_ptr(struct SoundIoRingBuffer *ring_buffer, int count);

206
test/latency.cpp Normal file
View file

@ -0,0 +1,206 @@
/*
* Copyright (c) 2015 Andrew Kelley
*
* This file is part of libsoundio, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "soundio.hpp"
#include "os.h"
#include "util.hpp"
#include "atomics.hpp"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
static int usage(char *exe) {
fprintf(stderr, "Usage: %s [--backend dummy|alsa|pulseaudio|jack|coreaudio|wasapi]\n", exe);
return 1;
}
static void write_sample_s16ne(char *ptr, double sample) {
int16_t *buf = (int16_t *)ptr;
double range = (double)INT16_MAX - (double)INT16_MIN;
double val = sample * range / 2.0;
*buf = val;
}
static void write_sample_s32ne(char *ptr, double sample) {
int32_t *buf = (int32_t *)ptr;
double range = (double)INT32_MAX - (double)INT32_MIN;
double val = sample * range / 2.0;
*buf = val;
}
static void write_sample_float32ne(char *ptr, double sample) {
float *buf = (float *)ptr;
*buf = sample;
}
static void write_sample_float64ne(char *ptr, double sample) {
double *buf = (double *)ptr;
*buf = sample;
}
static void (*write_sample)(char *ptr, double sample);
static int frames_until_pulse = 0;
static int pulse_frames_left = 0;
static const double PI = 3.14159265358979323846264338328;
static double seconds_offset = 0.0;
static SoundIoRingBuffer pulse_rb;
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min, int frame_count_max) {
double float_sample_rate = outstream->sample_rate;
double seconds_per_frame = 1.0f / float_sample_rate;
struct SoundIoChannelArea *areas;
int err;
int frames_left = frame_count_max;
while (frames_left > 0) {
int frame_count = frames_left;
if ((err = soundio_outstream_begin_write(outstream, &areas, &frame_count)))
soundio_panic("begin write: %s", soundio_strerror(err));
if (!frame_count)
break;
const struct SoundIoChannelLayout *layout = &outstream->layout;
double pitch = 440.0;
double radians_per_second = pitch * 2.0 * PI;
for (int frame = 0; frame < frame_count; frame += 1) {
double sample;
if (frames_until_pulse <= 0) {
if (pulse_frames_left <= 0) {
frames_until_pulse = (1.0 + (rand() / (double)RAND_MAX) * 3.0) * float_sample_rate;
pulse_frames_left = 0.05 * float_sample_rate;
sample = 0.0;
} else {
pulse_frames_left -= 1;
sample = sinf((seconds_offset + frame * seconds_per_frame) * radians_per_second);
}
} else {
frames_until_pulse -= 1;
sample = 0.0;
}
for (int channel = 0; channel < layout->channel_count; channel += 1) {
write_sample(areas[channel].ptr, sample);
areas[channel].ptr += areas[channel].step;
}
}
seconds_offset += seconds_per_frame * frame_count;
if ((err = soundio_outstream_end_write(outstream)))
soundio_panic("end write: %s", soundio_strerror(err));
frames_left -= frame_count;
}
}
static void underflow_callback(struct SoundIoOutStream *outstream) {
static int count = 0;
fprintf(stderr, "underflow %d\n", count++);
}
int main(int argc, char **argv) {
char *exe = argv[0];
enum SoundIoBackend backend = SoundIoBackendNone;
for (int i = 1; i < argc; i += 1) {
char *arg = argv[i];
if (arg[0] == '-' && arg[1] == '-') {
i += 1;
if (i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp("dummy", argv[i]) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("alsa", argv[i]) == 0) {
backend = SoundIoBackendAlsa;
} else if (strcmp("pulseaudio", argv[i]) == 0) {
backend = SoundIoBackendPulseAudio;
} else if (strcmp("jack", argv[i]) == 0) {
backend = SoundIoBackendJack;
} else if (strcmp("coreaudio", argv[i]) == 0) {
backend = SoundIoBackendCoreAudio;
} else if (strcmp("wasapi", argv[i]) == 0) {
backend = SoundIoBackendWasapi;
} else {
fprintf(stderr, "Invalid backend: %s\n", argv[i]);
return 1;
}
} else {
return usage(exe);
}
} else {
return usage(exe);
}
}
struct SoundIo *soundio;
if (!(soundio = soundio_create()))
soundio_panic("out of memory");
int err = (backend == SoundIoBackendNone) ?
soundio_connect(soundio) : soundio_connect_backend(soundio, backend);
if (err)
soundio_panic("error connecting: %s", soundio_strerror(err));
soundio_flush_events(soundio);
int default_out_device_index = soundio_default_output_device_index(soundio);
if (default_out_device_index < 0)
soundio_panic("no output device found");
struct SoundIoDevice *device = soundio_get_output_device(soundio, default_out_device_index);
if (!device)
soundio_panic("out of memory");
fprintf(stderr, "Output device: %s\n", device->name);
struct SoundIoOutStream *outstream = soundio_outstream_create(device);
outstream->format = SoundIoFormatFloat32NE;
outstream->write_callback = write_callback;
outstream->underflow_callback = underflow_callback;
if (soundio_device_supports_format(device, SoundIoFormatFloat32NE)) {
outstream->format = SoundIoFormatFloat32NE;
write_sample = write_sample_float32ne;
} else if (soundio_device_supports_format(device, SoundIoFormatFloat64NE)) {
outstream->format = SoundIoFormatFloat64NE;
write_sample = write_sample_float64ne;
} else if (soundio_device_supports_format(device, SoundIoFormatS32NE)) {
outstream->format = SoundIoFormatS32NE;
write_sample = write_sample_s32ne;
} else if (soundio_device_supports_format(device, SoundIoFormatS16NE)) {
outstream->format = SoundIoFormatS16NE;
write_sample = write_sample_s16ne;
} else {
soundio_panic("No suitable device format available.\n");
}
if ((err = soundio_outstream_open(outstream)))
soundio_panic("unable to open device: %s", soundio_strerror(err));
if (outstream->layout_error)
fprintf(stderr, "unable to set channel layout: %s\n", soundio_strerror(outstream->layout_error));
if ((err = soundio_outstream_start(outstream)))
soundio_panic("unable to start device: %s", soundio_strerror(err));
for (;;)
soundio_wait_events(soundio);
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_destroy(soundio);
return 0;
}

View file

@ -103,7 +103,7 @@ int main(int argc, char **argv) {
if (i >= argc) {
return usage(exe);
} else if (strcmp(arg, "--backend") == 0) {
if (strcmp("-dummy", argv[i]) == 0) {
if (strcmp("dummy", argv[i]) == 0) {
backend = SoundIoBackendDummy;
} else if (strcmp("alsa", argv[i]) == 0) {
backend = SoundIoBackendAlsa;