Skip to content

Commit

Permalink
:V
Browse files Browse the repository at this point in the history
  • Loading branch information
Themaister committed Apr 28, 2010
1 parent 3186272 commit 0708b34
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 17 deletions.
52 changes: 35 additions & 17 deletions roar/pcm_roar.c
Expand Up @@ -58,6 +58,9 @@ static int roar_pcm_start (snd_pcm_ioplug_t * io) {
self->stream_opened = 1;
self->writec = 0;

self->bufptr = 0;
pthread_create(self->thread, NULL, roar_thread, self);

return 0;
}

Expand All @@ -77,6 +80,9 @@ static int roar_pcm_stop (snd_pcm_ioplug_t *io) {

ROAR_DBG("roar_pcm_stop(*) = 0");

pthread_cancel(self->thread);
pthread_join(self->thread, NULL);

return 0;
}

Expand Down Expand Up @@ -148,22 +154,24 @@ static int roar_hw_constraint(struct roar_alsa_pcm * self) {
return 0;
}


///////////////////////////////
/// TODO: Needs to be implemented
///////////////////////////////
// This hacky hack should not be used, since apparently, using this, ALSA will think the audio buffer is always empty (?),
// leading to completely broken blocking audio which will only work with audio players.
// Will need a method to determine how much buffer data is available to write to the roar buffer without blocking.
// We therefore also need to know the buffersize that ALSA uses.

// Referring to alsa-lib/src/pcm/pcm_ioplug.c : snd_pcm_ioplug_hw_ptr_update
static snd_pcm_sframes_t roar_pcm_pointer(snd_pcm_ioplug_t *io) {
struct roar_alsa_pcm * self = io->private_data;

ROAR_DBG("roar_pcm_pointer(*) = ?");

return snd_pcm_bytes_to_frames(io->pcm, self->writec);
int ptr;
if ( io->appl_ptr < self->last_ptr )
{
roar_pcm_stop(io);
roar_pcm_start(io);
}

ptr = snd_pcm_bytes_to_frames(io->pcm, self->bufptr);
ptr = io->appl_ptr - ptr;
self->last_ptr = io->appl_ptr;

return ptr;
}

// TODO: FIXME: add support for reading data!
Expand Down Expand Up @@ -198,16 +206,13 @@ static snd_pcm_sframes_t roar_pcm_transfer(snd_pcm_ioplug_t *io,
}


///////////////////////////////
/// TODO: Needs to be implemented
///////////////////////////////
static int roar_pcm_delay(snd_pcm_ioplug_t *io, snd_pcm_sframes_t *delayp) {
(void)io;
struct roar_alsa_pcm * self = io->private_data;

ROAR_DBG("roar_pcm_delay(*) = ?");

// TODO: We need to set *delayp the latency in frames.
*delayp = 0;
*delayp = snd_pcm_bytes_to_frames(io->pcm, self->bufptr);

return 0;
}
Expand All @@ -221,8 +226,6 @@ static int roar_pcm_prepare(snd_pcm_ioplug_t *io) {
static int roar_pcm_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params) {
struct roar_alsa_pcm * self = io->private_data;

(void) params;

ROAR_DBG("roar_pcm_hw_params(*) = ?");

self->info.channels = io->channels;
Expand Down Expand Up @@ -297,6 +300,16 @@ static int roar_pcm_hw_params(snd_pcm_ioplug_t *io, snd_pcm_hw_params_t *params)
return-EINVAL;
}

snd_pcm_uframes_t buffersize;
int err;

if ((err = snd_pcm_hw_params_get_buffer_size(params, &buffersize) < 0))
return err;

self->bufsize = snd_pcm_frames_to_bytes(io->pcm, buffersize);
self->buffer = malloc(self->bufsize);
self->bufptr = 0;

ROAR_DBG("roar_pcm_hw_params(*) = 0");
return 0;
}
Expand All @@ -308,6 +321,7 @@ static int roar_pcm_close (snd_pcm_ioplug_t * io) {

roar_disconnect(&(self->roar.con));

free(self->buffer);
free(self);

return 0;
Expand Down Expand Up @@ -389,6 +403,10 @@ SND_PCM_PLUGIN_DEFINE_FUNC(roar) {
return ret;
}

pthread_mutex_init(&(self->lock), NULL);
pthread_mutex_init(&(self->cond_lock), NULL);
pthread_cond_init(&(self->cond), NULL);

if ( (ret = roar_hw_constraint(self)) < 0 ) {
snd_pcm_ioplug_delete(&(self->io));
roar_disconnect(&(self->roar.con));
Expand Down
8 changes: 8 additions & 0 deletions roar/roar.h
Expand Up @@ -42,6 +42,7 @@
#include <alsa/global.h>
#include <alsa/pcm_external.h>
#include <alsa/control_external.h>
#include <pthread.h>

#define _as(x) (sizeof((x))/sizeof(*(x)))

Expand All @@ -57,6 +58,13 @@ struct roar_alsa_pcm {
struct roar_vio_calls stream_vio;
int stream_opened;
size_t writec;
size_t last_ptr;
char* buffer;
size_t bufsize;
size_t bufptr;
pthread_mutex_t lock;
pthread_mutex_t cond_lock;
pthread_cond_t cond;
};

#endif
Expand Down
93 changes: 93 additions & 0 deletions roar/thread.c
@@ -0,0 +1,93 @@

#define CHUNK_SIZE 256

static void* roar_thread ( void * thread_data )
{
/* We share data between thread and callable functions */
struct roar_alsa *self = thread_data;
int rc;

/* Plays back data as long as there is data in the buffer. Else, sleep until it can. */
/* Two (;;) for loops! :3 Beware! */
for (;;)
{

for(;;)
{

// We ask the server to send its latest backend data. Do not really care about errors atm.
// We only bother to check after 1 sec of audio has been played, as it might be quite inaccurate in the start of the stream.

/* If the buffer is empty or we've stopped the stream. Jump out of this for loop */
pthread_mutex_lock(&rd->thread.mutex);
if ( rd->buffer_pointer < (int)rd->backend_info.chunk_size || !rd->thread_active )
{
pthread_mutex_unlock(&rd->thread.mutex);
break;
}
pthread_mutex_unlock(&rd->thread.mutex);

pthread_testcancel();
rc = rsnd_send_chunk(rd->conn.socket, rd->buffer, rd->backend_info.chunk_size);

/* If this happens, we should make sure that subsequent and current calls to rsd_write() will fail. */
if ( rc <= 0 )
{
pthread_testcancel();
rsnd_reset(rd);

/* Wakes up a potentially sleeping fill_buffer() */
pthread_cond_signal(&rd->thread.cond);

/* This thread will not be joined, so detach. */
pthread_detach(pthread_self());
pthread_exit(NULL);
}

/* If this was the first write, set the start point for the timer. */
if ( !rd->has_written )
{
pthread_mutex_lock(&rd->thread.mutex);
#ifdef _POSIX_MONOTONIC_CLOCK
clock_gettime(CLOCK_MONOTONIC, &rd->start_tv_nsec);
#else
gettimeofday(&rd->start_tv_usec, NULL);
#endif
rd->has_written = 1;
pthread_mutex_unlock(&rd->thread.mutex);
}

/* Increase the total_written counter. Used in rsnd_drain() */
pthread_mutex_lock(&rd->thread.mutex);
rd->total_written += rc;
pthread_mutex_unlock(&rd->thread.mutex);

/* "Drains" the buffer. This operation looks kinda expensive with large buffers, but hey. D: */
pthread_mutex_lock(&rd->thread.mutex);
memmove(rd->buffer, rd->buffer + rd->backend_info.chunk_size, rd->buffer_size - rd->backend_info.chunk_size);
rd->buffer_pointer -= (int)rd->backend_info.chunk_size;
pthread_mutex_unlock(&rd->thread.mutex);

/* Buffer has decreased, signal fill_buffer() */
pthread_cond_signal(&rd->thread.cond);

}

/* If we're still good to go, sleep. We are waiting for fill_buffer() to fill up some data. */
pthread_testcancel();
if ( rd->thread_active )
{
pthread_mutex_lock(&rd->thread.cond_mutex);
pthread_cond_wait(&rd->thread.cond, &rd->thread.cond_mutex);
pthread_mutex_unlock(&rd->thread.cond_mutex);
}
/* Abort request, chap. */
else
{
pthread_cond_signal(&rd->thread.cond);
pthread_exit(NULL);
}

}
}

0 comments on commit 0708b34

Please sign in to comment.