From 8ec3f8e82acd70410515550fd1790ee5827aafdb Mon Sep 17 00:00:00 2001 From: sanine Date: Fri, 26 Aug 2022 12:17:51 -0500 Subject: make sound --- .gitignore | 1 + CMakeLists.txt | 5 +++ examples/example.c | 36 ++++++++++++++-- src/mossrose-channel.c | 85 ++++++++++++++++++++++++++++++++++++++ src/mossrose-channel.h | 26 ++++++++++++ src/mossrose-mutex.c | 58 ++++++++++++++++++++++++++ src/mossrose-mutex.h | 28 +++++++++++++ src/mossrose.c | 110 +++++++++++++++++++++++++++++++++++++++++-------- src/mossrose.h | 6 +-- 9 files changed, 332 insertions(+), 23 deletions(-) create mode 100644 src/mossrose-channel.c create mode 100644 src/mossrose-channel.h create mode 100644 src/mossrose-mutex.c create mode 100644 src/mossrose-mutex.h diff --git a/.gitignore b/.gitignore index 378eac2..89efb0f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build +*.swp diff --git a/CMakeLists.txt b/CMakeLists.txt index 07d5174..d86dc60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,8 @@ add_subdirectory(${CMAKE_SOURCE_DIR}/portaudio EXCLUDE_FROM_ALL) add_library(mossrose ${CMAKE_SOURCE_DIR}/src/mossrose.c + ${CMAKE_SOURCE_DIR}/src/mossrose-mutex.c + ${CMAKE_SOURCE_DIR}/src/mossrose-channel.c ) set_target_properties(mossrose PROPERTIES C_STANDARD 99 @@ -20,6 +22,9 @@ set_target_properties(mossrose PROPERTIES PUBLIC_HEADER src/mossrose.h ) target_link_libraries(mossrose portaudio) +if (UNIX) + target_link_libraries(mossrose pthread) +endif() if (MOSSROSE_BUILD_EXAMPLES) diff --git a/examples/example.c b/examples/example.c index 960b9f2..9ae3acc 100644 --- a/examples/example.c +++ b/examples/example.c @@ -1,14 +1,44 @@ +#include +#include #include #include +#define PI 3.14159 + #define SAMPLE_RATE 44100 #define N_CHANNELS 8 int main() { - mr_init(SAMPLE_RATE, N_CHANNELS); - Pa_Sleep(3000); - mr_terminate(); + float sin1[SAMPLE_RATE]; + const int f1 = 440; + float sin2[SAMPLE_RATE]; + const int f2 = 512; + for (long i=0; i +#include +#include "mossrose-mutex.h" +#include "mossrose-channel.h" + + +void mossrose_channel_init(struct mossrose_channel_t *chan) +{ + mossrose_mutex_init(&(chan->mutex)); + chan->left = NULL; + chan->right = NULL; + chan->n_samples = 0; + chan->pos = 0; +} + + +int mossrose_channel_set(struct mossrose_channel_t *chan, float *left, float *right, size_t len, int force) +{ + int result = 0; + mossrose_mutex_lock(&(chan->mutex)); + if (chan->n_samples != 0 && !force) { + /* channel is still playing! */ + result = 1; goto unlock; + } + + chan->n_samples = len; + chan->pos = 0; + + /* left channel */ + if (chan->left != NULL) free(chan->left); + if (left == NULL) + chan->left = NULL; + else { + chan->left = malloc(len * sizeof(float)); + if (chan->left == NULL) { result = 2; goto unlock; } + memcpy(chan->left, left, len * sizeof(float)); + } + + /* right channel */ + if (chan->right != NULL) free(chan->right); + if (right == NULL) + chan->right = NULL; + else { + chan->right = malloc(len * sizeof(float)); + if (chan->right == NULL) { result = 3; goto unlock; } + memcpy(chan->right, right, len * sizeof(float)); + } + + unlock: + mossrose_mutex_unlock(&(chan->mutex)); + return result; +} + + +void mossrose_channel_reset(struct mossrose_channel_t *chan) +{ + chan->n_samples = 0; + chan->pos = 0; +} + + +int mossrose_channel_advance(float *left, float *right, struct mossrose_channel_t *chan) +{ + if (chan->pos >= chan->n_samples) return 1; + if (chan->left != NULL) + *left = chan->left[chan->pos]; + else + *left = 0; + if (chan->right != NULL) + *right = chan->right[chan->pos]; + else + *right = 0; + chan->pos += 1; + return 0; +} + + +void mossrose_channel_destroy(struct mossrose_channel_t *chan) +{ + mossrose_mutex_destroy(&(chan->mutex)); + if (chan->left != NULL) free(chan->left); + if (chan->right != NULL) free(chan->right); +} + + diff --git a/src/mossrose-channel.h b/src/mossrose-channel.h new file mode 100644 index 0000000..e1db89d --- /dev/null +++ b/src/mossrose-channel.h @@ -0,0 +1,26 @@ +#ifndef MOSSROSE_CHANNEL_H +#define MOSSROSE_CHANNEL_H + +#include +#include "mossrose-mutex.h" + +struct mossrose_channel_t { + mossrose_mutex_t mutex; + float *left; + float *right; + size_t n_samples; + size_t pos; +}; + + +void mossrose_channel_init(struct mossrose_channel_t *chan); + +int mossrose_channel_set(struct mossrose_channel_t *chan, float *left, float *right, size_t len, int force); + +void mossrose_channel_reset(struct mossrose_channel_t *chan); + +int mossrose_channel_advance(float *left, float *right, struct mossrose_channel_t *chan); + +void mossrose_channel_destroy(struct mossrose_channel_t *chan); + +#endif diff --git a/src/mossrose-mutex.c b/src/mossrose-mutex.c new file mode 100644 index 0000000..b6865f8 --- /dev/null +++ b/src/mossrose-mutex.c @@ -0,0 +1,58 @@ +#include "mossrose-mutex.h" + +#ifdef WIN32 +#include +void mossrose_mutex_init(mossrose_mutex_t *mutex) +{ + *mutex = CreateMutex(NULL, false, NULL); +} + +void mossrose_mutex_lock(mossrose_mutex_t *mutex) +{ + WaitForSingleObject(*mutex, INFINITE); +} + +int mossrose_mutex_trylock(mossrose_mutex_t *mutex) +{ + int result = WaitForSingleObject(*mutex, 0); + return result != WAIT_OBJECT_0; +} + +void mossrose_mutex_unlock(mossrose_mutex_t *mutex) +{ + ReleaseMutex(*mutex); +} + +void mossrose_mutex_destroy(mossrose_mutex_t *mutex) +{ + ReleaseMutex(*mutex); +} + + +#else +#include +void mossrose_mutex_init(mossrose_mutex_t *mutex) +{ + pthread_mutex_init(mutex, NULL); +} + +void mossrose_mutex_lock(mossrose_mutex_t *mutex) +{ + pthread_mutex_lock(mutex); +} + +int mossrose_mutex_trylock(mossrose_mutex_t *mutex) +{ + return pthread_mutex_trylock(mutex); +} + +void mossrose_mutex_unlock(mossrose_mutex_t *mutex) +{ + pthread_mutex_unlock(mutex); +} + +void mossrose_mutex_destroy(mossrose_mutex_t *mutex) +{ + pthread_mutex_destroy(mutex); +} +#endif diff --git a/src/mossrose-mutex.h b/src/mossrose-mutex.h new file mode 100644 index 0000000..84d1f63 --- /dev/null +++ b/src/mossrose-mutex.h @@ -0,0 +1,28 @@ +#ifndef MOSSROSE_MUTEX_H +#define MOSSROSE_MUTEX_H + + +#ifdef WIN32 +#include +typedef HANDLE mossrose_mutex_t; +#else +#include +typedef pthread_mutex_t mossrose_mutex_t; +#endif + +/* initialize a mutex */ +void mossrose_mutex_init(mossrose_mutex_t *mutex); + +/* lock a mutex, hanging until locked */ +void mossrose_mutex_lock(mossrose_mutex_t *mutex); + +/* attempt to lock a mutex. returns 0 on success and 1 otherwise */ +int mossrose_mutex_trylock(mossrose_mutex_t *mutex); + +/* unlock a mutex */ +void mossrose_mutex_unlock(mossrose_mutex_t *mutex); + +/* destroy a mutex */ +void mossrose_mutex_destroy(mossrose_mutex_t *mutex); + +#endif diff --git a/src/mossrose.c b/src/mossrose.c index fdaa10c..02f063a 100644 --- a/src/mossrose.c +++ b/src/mossrose.c @@ -1,17 +1,67 @@ #include +#include #include #include "mossrose.h" +#include "mossrose-mutex.h" +#include "mossrose-channel.h" -PaStream *stream; - +/* ~~~~~~~~~~~~~~~~ type definitions ~~~~~~~~~~~~~~~~ */ +/* audio output */ struct audio_output_t { float l; float r; }; + +/* ~~~~~~~~~~~~~~~~ globals ~~~~~~~~~~~~~~~~ */ + +struct mossrose_globals_t { + PaStream *stream; + struct mossrose_channel_t *channels; + int n_channels; +} mossrose_global; + + +struct audio_output_t build_sample() +{ + struct audio_output_t out; + out.l = 0; out.r = 0; + + /* loop variables */ + struct mossrose_channel_t *chan; + float chan_l, chan_r; + + for (int i=0; imutex)) != 0) { + /* can't lock the mutex, this channel is being modified */ + printf("can't lock channel %d\n", i); + continue; + } + + if (chan->n_samples == 0) { + /* channel is not currently in use, skip */ + } + else { + if (mossrose_channel_advance(&chan_l, &chan_r, chan) != 0) + /* channel is done playing, reset */ + mossrose_channel_reset(chan); + else { + out.l += chan_l; + out.r += chan_r; + } + } + + mossrose_mutex_unlock(&(chan->mutex)); + } + + return out; +} + + static int callback( const void *input, void *output, @@ -19,39 +69,49 @@ static int callback( const PaStreamCallbackTimeInfo *time_info, void *userdata) { - static float left = 0; - static float right = 0; - struct audio_output_t *out = output; + struct audio_output_t sample; + for (int i=0; i 1) left -= 2; - if (right > 1) right -= 2; + sample = build_sample(); + out[i].l = sample.l; + out[i].r = sample.r; } return 0; } -int mr_init(double sample_rate, int n_channels) +int mossrose_init(double sample_rate, int n_channels) { + /* initialize channels */ + mossrose_global.n_channels = n_channels; + mossrose_global.channels = malloc(n_channels * sizeof(struct mossrose_channel_t)); + if (mossrose_global.channels == NULL) { + fprintf(stderr, "failed to allocate memory for %d channels", n_channels); + return 1; + } + for (int i=0; i 0) { + return mossrose_channel_set(mossrose_global.channels+channel, left, right, len, 1); + } + else { + struct mossrose_channel_t *chan; + for (int i=0; i -int mr_init(double sample_rate, int n_channels); +int mossrose_init(double sample_rate, int n_channels); -int mr_terminate(); +int mossrose_terminate(); -int mr_play(float *left, float *right, size_t n_samples, int channel); +int mossrose_play(float *left, float *right, size_t n_samples, int channel); #endif -- cgit v1.2.1