// 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_