| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <signal.h> |
| #include <unistd.h> |
| #include <pthread.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| #include <tinyalsa/asoundlib.h> |
| |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <sys/ioctl.h> |
| |
| #include "accessory.h" |
| |
| #define BUFFER_COUNT 2 |
| #define BUFFER_SIZE 16384 |
| |
| #define BUFFER_EMPTY 0 |
| #define BUFFER_BUSY 1 |
| #define BUFFER_FULL 2 |
| |
| static char* buffers[BUFFER_COUNT]; |
| static int buffer_states[BUFFER_COUNT]; |
| static int empty_index = 0; |
| static int full_index = -1; |
| |
| static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; |
| static pthread_cond_t empty_cond = PTHREAD_COND_INITIALIZER; |
| static pthread_cond_t full_cond = PTHREAD_COND_INITIALIZER; |
| |
| static unsigned int input_card; |
| static unsigned int input_device; |
| |
| static int get_empty() |
| { |
| int index, other; |
| |
| pthread_mutex_lock(&mutex); |
| |
| while (empty_index == -1) |
| pthread_cond_wait(&empty_cond, &mutex); |
| |
| index = empty_index; |
| other = (index == 0 ? 1 : 0); |
| buffer_states[index] = BUFFER_BUSY; |
| if (buffer_states[other] == BUFFER_EMPTY) |
| empty_index = other; |
| else |
| empty_index = -1; |
| |
| pthread_mutex_unlock(&mutex); |
| return index; |
| } |
| |
| static void put_empty(int index) |
| { |
| pthread_mutex_lock(&mutex); |
| |
| buffer_states[index] = BUFFER_EMPTY; |
| if (empty_index == -1) { |
| empty_index = index; |
| pthread_cond_signal(&empty_cond); |
| } |
| |
| pthread_mutex_unlock(&mutex); |
| } |
| |
| static int get_full() |
| { |
| int index, other; |
| |
| pthread_mutex_lock(&mutex); |
| |
| while (full_index == -1) |
| pthread_cond_wait(&full_cond, &mutex); |
| |
| index = full_index; |
| other = (index == 0 ? 1 : 0); |
| buffer_states[index] = BUFFER_BUSY; |
| if (buffer_states[other] == BUFFER_FULL) |
| full_index = other; |
| else |
| full_index = -1; |
| |
| pthread_mutex_unlock(&mutex); |
| return index; |
| } |
| |
| static void put_full(int index) |
| { |
| pthread_mutex_lock(&mutex); |
| |
| buffer_states[index] = BUFFER_FULL; |
| if (full_index == -1) { |
| full_index = index; |
| pthread_cond_signal(&full_cond); |
| } |
| |
| pthread_mutex_unlock(&mutex); |
| } |
| |
| static void* capture_thread(void* arg) |
| { |
| struct pcm_config config; |
| struct pcm *pcm = NULL; |
| |
| fprintf(stderr, "capture_thread start\n"); |
| |
| memset(&config, 0, sizeof(config)); |
| |
| config.channels = 2; |
| config.rate = 44100; |
| config.period_size = 1024; |
| config.period_count = 4; |
| config.format = PCM_FORMAT_S16_LE; |
| |
| while (1) { |
| while (!pcm) { |
| pcm = pcm_open(input_card, input_device, PCM_IN, &config); |
| if (pcm && !pcm_is_ready(pcm)) { |
| pcm_close(pcm); |
| pcm = NULL; |
| } |
| if (!pcm) |
| sleep(1); |
| } |
| |
| while (pcm) { |
| int index = get_empty(); |
| if (pcm_read(pcm, buffers[index], BUFFER_SIZE)) { |
| put_empty(index); |
| pcm_close(pcm); |
| pcm = NULL; |
| } else { |
| put_full(index); |
| } |
| } |
| } |
| |
| fprintf(stderr, "capture_thread done\n"); |
| return NULL; |
| } |
| |
| static void* play_thread(void* arg) |
| { |
| struct pcm *pcm = arg; |
| int index, err; |
| |
| fprintf(stderr, "play_thread start\n"); |
| |
| while (1) { |
| index = get_full(); |
| |
| err = pcm_write(pcm, buffers[index], BUFFER_SIZE); |
| if (err) |
| fprintf(stderr, "pcm_write err: %d\n", err); |
| |
| put_empty(index); |
| } |
| |
| fprintf(stderr, "play_thread done\n"); |
| pcm_close(pcm); |
| |
| return NULL; |
| } |
| |
| int init_audio(unsigned int ic, unsigned int id, unsigned int oc, unsigned int od) |
| { |
| pthread_t tid; |
| struct pcm_config config; |
| struct pcm *pcm; |
| int i; |
| |
| input_card = ic; |
| input_device = id; |
| |
| for (i = 0; i < BUFFER_COUNT; i++) { |
| buffers[i] = malloc(BUFFER_SIZE); |
| buffer_states[i] = BUFFER_EMPTY; |
| } |
| |
| memset(&config, 0, sizeof(config)); |
| config.channels = 2; |
| config.rate = 44100; |
| config.period_size = 1024; |
| config.period_count = 4; |
| config.format = PCM_FORMAT_S16_LE; |
| |
| pcm = pcm_open(oc, od, PCM_OUT, &config); |
| if (!pcm || !pcm_is_ready(pcm)) { |
| fprintf(stderr, "Unable to open PCM device %d/%d for output (%s)\n", |
| oc, od, pcm_get_error(pcm)); |
| return -1; |
| } |
| |
| pthread_create(&tid, NULL, capture_thread, NULL); |
| pthread_create(&tid, NULL, play_thread, pcm); |
| return 0; |
| } |