diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | CMakeLists.txt | 5 | ||||
-rw-r--r-- | examples/example.c | 36 | ||||
-rw-r--r-- | src/mossrose-channel.c | 85 | ||||
-rw-r--r-- | src/mossrose-channel.h | 26 | ||||
-rw-r--r-- | src/mossrose-mutex.c | 58 | ||||
-rw-r--r-- | src/mossrose-mutex.h | 28 | ||||
-rw-r--r-- | src/mossrose.c | 110 | ||||
-rw-r--r-- | src/mossrose.h | 6 |
9 files changed, 332 insertions, 23 deletions
@@ -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 <stdio.h> +#include <math.h> #include <mossrose.h> #include <portaudio.h> +#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<SAMPLE_RATE; i++) { + float time = ((float)i)/SAMPLE_RATE; + sin1[i] = sin(2*PI*f1*time); + sin2[i] = sin(2*PI*f2*time); + } + mossrose_init(SAMPLE_RATE, N_CHANNELS); + + Pa_Sleep(500); + + printf("play 1\n"); + mossrose_play(sin1, NULL, SAMPLE_RATE, -1); + printf("wait\n"); + Pa_Sleep(1000); + + printf("play 2\n"); + mossrose_play(sin2, NULL, SAMPLE_RATE, -1); + printf("wait\n"); + Pa_Sleep(500); + + printf("play 3\n"); + mossrose_play(NULL, sin1, SAMPLE_RATE, -1); + printf("wait\n"); + Pa_Sleep(1000); + + mossrose_terminate(); return 0; } diff --git a/src/mossrose-channel.c b/src/mossrose-channel.c new file mode 100644 index 0000000..7232417 --- /dev/null +++ b/src/mossrose-channel.c @@ -0,0 +1,85 @@ +#include <stdlib.h> +#include <string.h> +#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 <stddef.h> +#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 <windows.h> +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 <pthread.h> +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 <windows.h> +typedef HANDLE mossrose_mutex_t; +#else +#include <pthread.h> +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 <stdio.h> +#include <stdlib.h> #include <portaudio.h> #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; i<mossrose_global.n_channels; i++) { + chan = mossrose_global.channels + i; + if (mossrose_mutex_trylock(&(chan->mutex)) != 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<frame_count; i++) { - out[i].l = left; - out[i].r = right; - left += 0.01; - right += 0.03; - if (left > 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<n_channels; i++) { + mossrose_channel_init(mossrose_global.channels + i); + } + PaError err; + /* initialize portaudio */ err = Pa_Initialize(); if (err != paNoError) { fprintf(stderr, "failed to initialize PortAudio!\n"); return 1; } - err = Pa_OpenDefaultStream(&stream, 0, 2, paFloat32, sample_rate, 0, callback, NULL); + /* open stream */ + err = Pa_OpenDefaultStream(&(mossrose_global.stream), 0, 2, paFloat32, sample_rate, 0, callback, NULL); if (err != paNoError) { fprintf(stderr, "failed to open audio stream!\n"); return 1; } - err = Pa_StartStream(stream); + /* start stream */ + err = Pa_StartStream(mossrose_global.stream); if (err != paNoError) { fprintf(stderr, "failed to start audio stream!\n"); return 1; @@ -61,10 +121,26 @@ int mr_init(double sample_rate, int n_channels) }; -int mr_terminate(double sample_rate, int n_channels) +int mossrose_play(float *left, float *right, size_t len, int channel) +{ + if (channel > 0) { + return mossrose_channel_set(mossrose_global.channels+channel, left, right, len, 1); + } + else { + struct mossrose_channel_t *chan; + for (int i=0; i<mossrose_global.n_channels; i++) { + chan = mossrose_global.channels + i; + if (mossrose_channel_set(chan, left, right, len, 0) == 0) return 0; + } + return 1; + } +} + + +int mossrose_terminate(double sample_rate, int n_channels) { - Pa_AbortStream(stream); - Pa_CloseStream(stream); + Pa_AbortStream(mossrose_global.stream); + Pa_CloseStream(mossrose_global.stream); Pa_Terminate(); return 0; } diff --git a/src/mossrose.h b/src/mossrose.h index 3d5bcd5..fb7d088 100644 --- a/src/mossrose.h +++ b/src/mossrose.h @@ -3,10 +3,10 @@ #include <stddef.h> -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 |