blob: a6db48e3037d8ee53e72106cdba96adad0018047 [file] [log] [blame]
// Copyright (c) 2012 The Chromium 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 MEDIA_AUDIO_MAC_AUDIO_SYNCHRONIZED_MAC_H_
#define MEDIA_AUDIO_MAC_AUDIO_SYNCHRONIZED_MAC_H_
#include <AudioToolbox/AudioToolbox.h>
#include <AudioUnit/AudioUnit.h>
#include <CoreAudio/CoreAudio.h>
#include "base/compiler_specific.h"
#include "base/synchronization/lock.h"
#include "media/audio/audio_io.h"
#include "media/audio/audio_parameters.h"
#include "media/base/audio_bus.h"
#include "media/base/audio_fifo.h"
namespace media {
class AudioManagerMac;
class ChannelMixer;
// AudioSynchronizedStream allows arbitrary combinations of input and output
// devices running off different clocks and using different drivers, with
// potentially differing sample-rates. It implements AudioOutputStream
// and shuttles its synchronized I/O data using AudioSourceCallback.
//
// It is required to first acquire the native sample rate of the selected
// output device and then use the same rate when creating this object.
//
// ............................................................................
// Theory of Operation:
// .
// INPUT THREAD . OUTPUT THREAD
// +-----------------+ +------+ .
// | Input AudioUnit | --> | | .
// +-----------------+ | | .
// | FIFO | .
// | | +-----------+
// | | -----> | Varispeed |
// | | +-----------+
// +------+ . |
// . | +-----------+
// . OnMoreIOData() --> | Output AU |
// . +-----------+
//
// The input AudioUnit's InputProc is called on one thread which feeds the
// FIFO. The output AudioUnit's OutputProc is called on a second thread
// which pulls on the varispeed to get the current input data. The varispeed
// handles mismatches between input and output sample-rate and also clock drift
// between the input and output drivers. The varispeed consumes its data from
// the FIFO and adjusts its rate dynamically according to the amount
// of data buffered in the FIFO. If the FIFO starts getting too much data
// buffered then the varispeed will speed up slightly to compensate
// and similarly if the FIFO doesn't have enough data buffered then the
// varispeed will slow down slightly.
//
// Finally, once the input data is available then OnMoreIOData() is called
// which is given this input, and renders the output which is finally sent
// to the Output AudioUnit.
class AudioSynchronizedStream : public AudioOutputStream {
public:
// The ctor takes all the usual parameters, plus |manager| which is the
// the audio manager who is creating this object.
AudioSynchronizedStream(AudioManagerMac* manager,
const AudioParameters& params,
AudioDeviceID input_id,
AudioDeviceID output_id);
virtual ~AudioSynchronizedStream();
// Implementation of AudioOutputStream.
virtual bool Open() OVERRIDE;
virtual void Close() OVERRIDE;
virtual void Start(AudioSourceCallback* callback) OVERRIDE;
virtual void Stop() OVERRIDE;
virtual void SetVolume(double volume) OVERRIDE;
virtual void GetVolume(double* volume) OVERRIDE;
OSStatus SetInputDeviceAsCurrent(AudioDeviceID input_id);
OSStatus SetOutputDeviceAsCurrent(AudioDeviceID output_id);
AudioDeviceID GetInputDeviceID() { return input_info_.id_; }
AudioDeviceID GetOutputDeviceID() { return output_info_.id_; }
bool IsRunning();
private:
// Initialization.
OSStatus CreateAudioUnits();
OSStatus SetupInput(AudioDeviceID input_id);
OSStatus EnableIO();
OSStatus SetupOutput(AudioDeviceID output_id);
OSStatus SetupCallbacks();
OSStatus SetupStreamFormats();
void AllocateInputData();
// Handlers for the AudioUnit callbacks.
OSStatus HandleInputCallback(AudioUnitRenderActionFlags* io_action_flags,
const AudioTimeStamp* time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data);
OSStatus HandleVarispeedCallback(AudioUnitRenderActionFlags* io_action_flags,
const AudioTimeStamp* time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data);
OSStatus HandleOutputCallback(AudioUnitRenderActionFlags* io_action_flags,
const AudioTimeStamp* time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data);
// AudioUnit callbacks.
static OSStatus InputProc(void* user_data,
AudioUnitRenderActionFlags* io_action_flags,
const AudioTimeStamp* time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data);
static OSStatus VarispeedProc(void* user_data,
AudioUnitRenderActionFlags* io_action_flags,
const AudioTimeStamp* time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data);
static OSStatus OutputProc(void* user_data,
AudioUnitRenderActionFlags* io_action_flags,
const AudioTimeStamp* time_stamp,
UInt32 bus_number,
UInt32 number_of_frames,
AudioBufferList* io_data);
// Our creator.
AudioManagerMac* manager_;
// Client parameters.
AudioParameters params_;
double input_sample_rate_;
double output_sample_rate_;
// Pointer to the object that will provide the audio samples.
AudioSourceCallback* source_;
// Values used in Open().
AudioDeviceID input_id_;
AudioDeviceID output_id_;
// The input AudioUnit renders its data here.
AudioBufferList* input_buffer_list_;
// Holds the actual data for |input_buffer_list_|.
scoped_ptr<AudioBus> input_bus_;
// Used to overlay AudioBufferLists.
scoped_ptr<AudioBus> wrapper_bus_;
class AudioDeviceInfo {
public:
AudioDeviceInfo()
: id_(kAudioDeviceUnknown),
is_input_(false),
buffer_size_frames_(0) {}
void Initialize(AudioDeviceID inID, bool isInput);
bool IsInitialized() const { return id_ != kAudioDeviceUnknown; }
AudioDeviceID id_;
bool is_input_;
UInt32 buffer_size_frames_;
};
AudioDeviceInfo input_info_;
AudioDeviceInfo output_info_;
// Used for input to output buffering.
AudioFifo fifo_;
// The optimal number of frames we'd like to keep in the FIFO at all times.
int target_fifo_frames_;
// A running average of the measured delta between actual number of frames
// in the FIFO versus |target_fifo_frames_|.
double average_delta_;
// A varispeed rate scalar which is calculated based on FIFO drift.
double fifo_rate_compensation_;
// AudioUnits.
AudioUnit input_unit_;
AudioUnit varispeed_unit_;
AudioUnit output_unit_;
double first_input_time_;
bool is_running_;
int hardware_buffer_size_;
int channels_;
// Channel mixer used to transform mono to stereo data. It is only created
// if the input_hardware_channels is mono.
scoped_ptr<ChannelMixer> channel_mixer_;
scoped_ptr<AudioBus> mixer_bus_;
DISALLOW_COPY_AND_ASSIGN(AudioSynchronizedStream);
};
} // namespace media
#endif // MEDIA_AUDIO_MAC_AUDIO_SYNCHRONIZED_MAC_H_