summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-08-26 12:17:51 -0500
committersanine <sanine.not@pm.me>2022-08-26 12:17:51 -0500
commit8ec3f8e82acd70410515550fd1790ee5827aafdb (patch)
tree348e32b7fe2f0e765d3acf91fa857880d8f6c3f6
parenta20cdab55ba066301da138c4df945608524e741a (diff)
make sound
-rw-r--r--.gitignore1
-rw-r--r--CMakeLists.txt5
-rw-r--r--examples/example.c36
-rw-r--r--src/mossrose-channel.c85
-rw-r--r--src/mossrose-channel.h26
-rw-r--r--src/mossrose-mutex.c58
-rw-r--r--src/mossrose-mutex.h28
-rw-r--r--src/mossrose.c110
-rw-r--r--src/mossrose.h6
9 files changed, 332 insertions, 23 deletions
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 <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