blob: 1878ebaead51cb5123b035674f9253d44d7d950d [file] [log] [blame]
// Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef AUTOTEST_CLIENT_SITE_TESTS_AUDIO_ALSA_CLIENT_H_
#define AUTOTEST_CLIENT_SITE_TESTS_AUDIO_ALSA_CLIENT_H_
#include <set>
#include <string>
#include <cstdio>
#include <tr1/memory>
#include <alsa/asoundlib.h>
#include "common.h"
using std::tr1::shared_ptr;
// Alsa API forward declares.
struct _snd_pcm;
namespace autotest_client {
namespace audio {
class FrameGenerator;
_snd_pcm_format SampleFormatToAlsaFormat(SampleFormat format);
/* Calculate number of bytes per frame given format and channels */
int SampleFormatToFrameBytes(SampleFormat format, int channels);
/*
* Convert a sample cell (size = num_frames) into double cell.
* If there are two channels, the sample will be averaged
*/
void SampleCellToDoubleCell(void *sample_cell, double *double_cell,
int num_frames, SampleFormat format,
int num_channels);
/*
* Store captured sample data
* Store converted double data for FFT
* cell_[][] is a 2-D array (buffer_count_ * buffer_size_)
*/
template<typename T>
struct CircularBuffer {
public:
CircularBuffer(int count, int size)
: buffer_count_(count), buffer_size_(size), write_ptr_(0), read_ptr_(0) {
cell_ = new T*[count];
cell_[0] = new T[count * size];
for (int i = 1; i < count; ++i) {
cell_[i] = cell_[i - 1] + size;
}
mutexes = new pthread_mutex_t[count];
has_data = new pthread_cond_t[count];
for (int i = 0; i < count; ++i) {
pthread_mutex_init(&mutexes[i], NULL);
pthread_cond_init(&has_data[i], NULL);
}
}
~CircularBuffer() {
delete [] cell_[0];
delete [] cell_;
delete [] mutexes;
delete [] has_data;
}
/* Lock mutex, return cell pointer
* MUST call UnlockCellToWrite(); after work is done.
*/
T* LockCellToWrite(int *index = NULL) {
if (index) *index = write_ptr_;
pthread_mutex_lock(&mutexes[write_ptr_]);
return cell_[write_ptr_];
}
void UnlockCellToWrite() {
int last = write_ptr_;
write_ptr_ = (write_ptr_ + 1) % buffer_count_;
pthread_cond_signal(&has_data[last]);
pthread_mutex_unlock(&mutexes[last]);
}
/* Lock mutex and increment read_ptr_, return cell pointer
* MUST call UnlockCellToRead(); after work is done.
*/
T* LockCellToRead(int *index = NULL) {
if (index) *index = read_ptr_;
pthread_mutex_lock(&mutexes[read_ptr_]);
while (read_ptr_ == write_ptr_) {
pthread_cond_wait(&has_data[read_ptr_], &mutexes[read_ptr_]);
}
return cell_[read_ptr_];
}
void UnlockCellToRead() {
pthread_mutex_unlock(&mutexes[read_ptr_]);
read_ptr_ = (read_ptr_ + 1) % buffer_count_;
}
void SyncReadPtrToWrite() { read_ptr_ = write_ptr_; }
bool MoreToRead() { return read_ptr_ != write_ptr_; }
void Print(FILE *fp) {
fprintf(fp, " buffer_count_ = %d\n", buffer_count_);
fprintf(fp, " buffer_size_ = %d\n", buffer_size_);
fprintf(fp, " write_ptr_ = %d\n", write_ptr_);
fprintf(fp, " read_ptr_ = %d\n", read_ptr_);
}
int Count() { return buffer_count_; }
int Size() { return buffer_size_; }
private:
int buffer_count_;
int buffer_size_;
int write_ptr_, read_ptr_;
T** cell_;
pthread_mutex_t *mutexes;
pthread_cond_t *has_data;
};
inline size_t NumFrames(CircularBuffer<char> &buffers,
SampleFormat format,
int num_channels) {
return buffers.Size() / SampleFormatToFrameBytes(format, num_channels);
}
class AlsaPlaybackClient {
public:
enum State {
kCreated,
kFailed,
kTerminated,
kReady,
kComplete,
};
class PlaybackParam {
friend class AlsaPlaybackClient;
PlaybackParam() : chunk_(NULL), num_frames_(0), frame_bytes_(0) {}
void FreeMemory() {
if (chunk_) delete [] chunk_;
chunk_ = NULL;
}
~PlaybackParam() { FreeMemory(); }
int Init(_snd_pcm* handle, SampleFormat format, int num_channels);
void Print(FILE *fp);
char *chunk_;
size_t num_frames_;
int frame_bytes_;
};
AlsaPlaybackClient();
AlsaPlaybackClient(const std::string &playback_device);
virtual ~AlsaPlaybackClient();
virtual void Print(FILE *fp);
void SetPlayObj(FrameGenerator* gen) { generator_ = gen; }
FrameGenerator* PlayObj() { return generator_; }
virtual bool Init(int sample_rate,
SampleFormat format,
int num_channels,
std::set<int>* act_chs,
int period_size = 0);
virtual void PlayTones();
virtual void Play(shared_ptr<CircularBuffer<char> > buffers);
// Trivial accessors/mutators.
virtual void set_state(State state) { state_ = state; }
virtual State state() const { return state_; }
virtual int last_error() const { return last_error_; }
virtual int SampRate() const { return sample_rate_; }
virtual int NumChannel() const { return num_channels_; }
virtual SampleFormat Format() const { return format_; }
virtual std::set<int>* ActiveChannels() const { return active_channels_; }
private:
static const unsigned kDefaultLatencyMs = 50;
// Callback signaling completion of flushing of a stream.
//static void StreamFlushed(int success, void* userdata);
_snd_pcm* pcm_out_handle_;
int sample_rate_;
int num_channels_;
SampleFormat format_;
unsigned int latency_ms_;
PlaybackParam pb_param_;
std::set<int>* active_channels_;
// Our abstracted version of the connection state.
State state_;
// The last error reported by Alsa. Useful for debugging.
int last_error_;
// The playback device to open.
std::string playback_device_;
// snd_pcm_set_params() argument when PlayThreadEntry() calls PlayTones()
FrameGenerator* generator_;
};
class AlsaCaptureClient {
public:
enum State {
kCreated,
kFailed,
kTerminated,
kReady,
kComplete,
};
AlsaCaptureClient();
AlsaCaptureClient(const std::string &capture_device);
virtual ~AlsaCaptureClient();
virtual bool Init(int sample_rate, SampleFormat format, int num_channels,
int buffer_count, int period_size = 0);
virtual void Print(FILE *fp);
virtual int Capture();
// Trivial accessors/mutators.
virtual snd_pcm_hw_params_t *get_hw_params() const { return hwparams_; }
virtual void set_state(State state) { state_ = state; }
virtual State state() const { return state_; }
virtual int last_error() const { return last_error_; }
virtual int SampRate() const { return sample_rate_; }
virtual int NumChannel() const { return num_channels_; }
virtual SampleFormat Format() const { return format_; }
virtual shared_ptr<CircularBuffer<char> > Buffer() const {
return circular_buffer_;
}
private:
static const unsigned kDefaultLatencyMs = 50;
// Callback signaling completion of flushing of a stream.
//static void StreamFlushed(int success, void* userdata);
_snd_pcm* pcm_capture_handle_;
snd_pcm_hw_params_t *hwparams_;
unsigned int sample_rate_;
int num_channels_;
SampleFormat format_;
unsigned int latency_ms_;
// Our abstracted version of the connection state.
State state_;
// The last error reported by Alsa. Useful for debugging.
int last_error_;
// The playback device to open.
std::string capture_device_;
// Circular buffer to write captured data
shared_ptr<CircularBuffer<char> > circular_buffer_;
};
} // namespace audio
} // namespace autotest_client
#endif // AUTOTEST_CLIENT_SITE_TESTS_AUDIO_ALSA_CLIENT_H_