| // 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; |
| |
| // 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_; |
| |
| DISALLOW_COPY_AND_ASSIGN(AudioSynchronizedStream); |
| }; |
| |
| } // namespace media |
| |
| #endif // MEDIA_AUDIO_MAC_AUDIO_SYNCHRONIZED_MAC_H_ |