/* * OpenMRCP - Open Source Media Resource Control Protocol Stack * Copyright (C) 2007, Cepstral LLC * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * Author(s): * Arsen Chaloyan * * Contributor(s): * */ #include #include "audio_buffer.h" typedef enum { WRITER_STATE_NONE, WRITER_STATE_WAIT_FOR_WRITE, WRITER_STATE_WAIT_FOR_READ_COMPLETE } writer_state_t; typedef enum { READER_STATE_NONE, READER_STATE_PAUSED } reader_state_t; struct audio_buffer_t { char *data; apr_size_t max_size; apr_size_t actual_size; apr_size_t head; apr_size_t tail; apr_byte_t opened; writer_state_t writer_state; reader_state_t reader_state; apr_thread_mutex_t *guard; apr_thread_cond_t *wait_object; apr_pool_t *pool; }; static void audio_buffer_cyclic_write(audio_buffer_t *buffer, void *data, apr_size_t size); static void audio_buffer_cyclic_read(audio_buffer_t *buffer, void *data, apr_size_t size); audio_buffer_t* audio_buffer_create(apr_size_t size, apr_pool_t *pool) { audio_buffer_t *buffer = apr_palloc(pool,sizeof(audio_buffer_t)); buffer->pool = pool; buffer->max_size = size; buffer->actual_size = 0; buffer->data = apr_palloc(pool,size); buffer->head = buffer->tail = 0; buffer->opened = 0; buffer->writer_state = WRITER_STATE_NONE; buffer->reader_state = READER_STATE_NONE; apr_thread_mutex_create(&buffer->guard,APR_THREAD_MUTEX_UNNESTED,pool); apr_thread_cond_create(&buffer->wait_object,pool); return buffer; } apr_status_t audio_buffer_destroy(audio_buffer_t *buffer) { apr_thread_mutex_destroy(buffer->guard); apr_thread_cond_destroy(buffer->wait_object); return APR_SUCCESS; } apr_status_t audio_buffer_open(audio_buffer_t *buffer) { apr_thread_mutex_lock(buffer->guard); buffer->actual_size = 0; buffer->head = buffer->tail = 0; buffer->opened = 1; apr_thread_mutex_unlock(buffer->guard); return APR_SUCCESS; } apr_status_t audio_buffer_close(audio_buffer_t *buffer) { apr_thread_mutex_lock(buffer->guard); if(buffer->opened) { buffer->opened = 0; if(buffer->writer_state != WRITER_STATE_NONE) { buffer->writer_state = WRITER_STATE_NONE; apr_thread_cond_signal(buffer->wait_object); } } apr_thread_mutex_unlock(buffer->guard); return APR_SUCCESS; } apr_status_t audio_buffer_pause(audio_buffer_t *buffer) { apr_thread_mutex_lock(buffer->guard); if(buffer->opened) { buffer->reader_state = READER_STATE_PAUSED; } apr_thread_mutex_unlock(buffer->guard); return APR_SUCCESS; } apr_status_t audio_buffer_resume(audio_buffer_t *buffer) { apr_thread_mutex_lock(buffer->guard); if(buffer->opened) { buffer->reader_state = READER_STATE_NONE; } apr_thread_mutex_unlock(buffer->guard); return APR_SUCCESS; } apr_status_t audio_buffer_read(audio_buffer_t *buffer, void *data, apr_size_t size) { apr_status_t status = APR_SUCCESS; apr_thread_mutex_lock(buffer->guard); if(buffer->opened) { if(buffer->reader_state == READER_STATE_NONE) { if(buffer->actual_size >= size) { audio_buffer_cyclic_read(buffer,data,size); if(buffer->writer_state == WRITER_STATE_WAIT_FOR_WRITE) { if(buffer->actual_size <= buffer->max_size/2) { buffer->writer_state = WRITER_STATE_NONE; apr_thread_cond_signal(buffer->wait_object); } } } else { memset(data,0,size); if(buffer->writer_state == WRITER_STATE_WAIT_FOR_READ_COMPLETE) { buffer->writer_state = WRITER_STATE_NONE; apr_thread_cond_signal(buffer->wait_object); } // failed } } else { /*paused*/ memset(data,0,size); } } else { memset(data,0,size); } apr_thread_mutex_unlock(buffer->guard); return status; } apr_status_t audio_buffer_write(audio_buffer_t* buffer, void *data, apr_size_t size) { apr_size_t write_size; apr_thread_mutex_lock(buffer->guard); if(buffer->opened) { while(size) { write_size = buffer->max_size - buffer->actual_size; if(write_size < size) { if(write_size) { audio_buffer_cyclic_write(buffer,data,write_size); size -= write_size; data = (char*)data + write_size; } /* wait for next write */ buffer->writer_state = WRITER_STATE_WAIT_FOR_WRITE; apr_thread_cond_wait(buffer->wait_object,buffer->guard); if(!buffer->opened) { break; } } else { audio_buffer_cyclic_write(buffer,data,size); size = 0; } } } apr_thread_mutex_unlock(buffer->guard); return APR_SUCCESS; } apr_status_t audio_buffer_wait_for_read_complete(audio_buffer_t *buffer) { apr_thread_mutex_lock(buffer->guard); if(buffer->opened) { buffer->writer_state = WRITER_STATE_WAIT_FOR_READ_COMPLETE; apr_thread_cond_wait(buffer->wait_object,buffer->guard); } apr_thread_mutex_unlock(buffer->guard); return APR_SUCCESS; } static void audio_buffer_cyclic_read(audio_buffer_t *buffer, void *data, apr_size_t size) { if(buffer->tail + size > buffer->max_size) { apr_size_t size0 = buffer->max_size - buffer->tail; memcpy(data, buffer->data + buffer->tail, size0); memcpy((char*)data + size0, buffer->data, size - size0); } else { memcpy(data, buffer->data + buffer->tail, size); } buffer->tail = (buffer->tail + size) % buffer->max_size; buffer->actual_size -= size; #if 0 printf("AudioBuffer: Read [%lu,%lu,%lu]\n",buffer->head, buffer->tail, buffer->actual_size); #endif } static void audio_buffer_cyclic_write(audio_buffer_t *buffer, void *data, apr_size_t size) { if(buffer->head + size > buffer->max_size) { apr_size_t size0 = buffer->max_size - buffer->head; memcpy(buffer->data + buffer->head, data, size0); memcpy(buffer->data, (char*)data + size0, size - size0); } else { memcpy(buffer->data + buffer->head, data, size); } buffer->head = (buffer->head + size) % buffer->max_size; buffer->actual_size += size; #if 0 printf("AudioBuffer: Write [%lu,%lu,%lu]\n",buffer->head, buffer->tail, buffer->actual_size); #endif }