Commit 9548423f authored by Philippe Groarke's avatar Philippe Groarke Committed by Tristan Matthews

OSX: Audio output works using default output

Refs #57721

Change-Id: Ib038b25ad82047092d6dfb466495e6c3275934b2
parent 9e26a1b4
......@@ -84,3 +84,6 @@ nbproject
# Windows
*.exe
# Mac
.DS_Store
......@@ -5,4 +5,3 @@ configure
bin/dbus/*.adaptor.h
bin/dbus/org.sflphone.SFLphone.service
bin/sflphoned
.DS_Store
......@@ -34,6 +34,7 @@
#include <cstring>
#include <signal.h>
#include <getopt.h>
#include <string>
#include "sflphone.h"
#include "fileutils.h"
......@@ -129,10 +130,24 @@ static bool parse_args(int argc, char *argv[], bool &persistent)
return quit;
}
void myOnIncomingCall(const std::string& acc_id, const std::string& call_id, const std::string& from)
{
std::cout << std::endl << "INCOMING CALL!" << std::endl <<
"Account: " << acc_id <<
", Id: " << call_id <<
", From: " << from << std::endl << std::endl;
sflph_call_accept(call_id);
sflph_call_set_recording(call_id);
//sflph_call_join_participant(call_id, "patate");
}
static int osxTests()
{
sflph_ev_handlers evHandlers = {
.call_ev_handlers = {},
.call_ev_handlers = {
.on_incoming_call = myOnIncomingCall
},
.config_ev_handlers = {},
#ifdef SFL_PRESENCE
.pres_ev_handlers = {}
......@@ -145,15 +160,25 @@ static int osxTests()
sflph_init(&evHandlers, static_cast<sflph_init_flag>(sflphFlags));
sflph_call_play_dtmf("0");
for (auto x : sflph_config_get_audio_output_device_list())
std::cout << x << std::endl;
sleep(1);
sflph_call_play_dtmf("1");
sleep(1);
//sflph_call_place("IP2IP", "patate", "127.0.0.1");
//sflph_call_set_recording("patate");
while (true) {
sflph_poll_events();
sleep(1);
}
sflph_fini();
}
static int run()
{
osxTests();
return 1;
return 0;
}
static void interrupt()
......
......@@ -154,6 +154,15 @@ void AudioBuffer::applyGain(double gain)
sample *= g;
}
size_t AudioBuffer::channelToFloat(float* out, const int& channel) const
{
for (int i=0, f=frames(); i < f; i++)
*out++ = (float) samples_[channel][i] * .000030517578125f;
return frames() * samples_.size();
}
size_t AudioBuffer::interleave(SFLAudioSample* out) const
{
for (unsigned i=0, f=frames(), c=channels(); i < f; ++i)
......
......@@ -239,6 +239,14 @@ class AudioBuffer {
return raw_data;
}
/**
* Convert fixed-point channel to float and write in the out buffer (Float 32-bits).
* The out buffer must be at least of size capacity()*sizeof(SFLAudioSample) bytes.
*
* @returns Number of samples writen.
*/
size_t channelToFloat(float* out, const int& channel) const;
/**
* Write interleaved multichannel data to the out buffer (fixed-point 16-bits).
* The out buffer must be at least of size capacity()*sizeof(SFLAudioSample) bytes.
......
......@@ -42,11 +42,8 @@ class AudioDevice {
public:
AudioDevice() : id_(kAudioDeviceUnknown) { }
AudioDevice(AudioDeviceID devid, bool isInput);
void init(AudioDeviceID devid, bool isInput);
bool valid() const;
void setBufferSize(UInt32 size);
public:
......
......@@ -31,6 +31,7 @@
#include "corelayer.h"
#include "manager.h"
#include "noncopyable.h"
#include "audio/resampler.h"
#include "audio/ringbufferpool.h"
#include "audio/ringbuffer.h"
#include "audiodevice.h"
......@@ -40,46 +41,6 @@
namespace sfl {
// Actual audio thread.
class CoreAudioThread {
public:
CoreAudioThread(CoreLayer* coreAudio);
~CoreAudioThread();
void start();
private:
NON_COPYABLE(CoreAudioThread);
void run();
std::thread thread_;
CoreLayer* coreAudio_;
std::atomic_bool running_;
};
CoreAudioThread::CoreAudioThread(CoreLayer* coreAudio)
: thread_(), coreAudio_(coreAudio), running_(false)
{}
CoreAudioThread::~CoreAudioThread()
{
running_ = false;
if (thread_.joinable())
thread_.join();
}
void CoreAudioThread::start()
{
running_ = true;
thread_ = std::thread(&CoreAudioThread::run, this);
}
void CoreAudioThread::run()
{
// Actual playback is here.
while (running_) {
}
}
// AudioLayer implementation.
CoreLayer::CoreLayer(const AudioPreference &pref)
: AudioLayer(pref)
......@@ -87,27 +48,32 @@ CoreLayer::CoreLayer(const AudioPreference &pref)
, indexOut_(pref.getAlsaCardout())
, indexRing_(pref.getAlsaCardring())
, playbackBuff_(0, audioFormat_)
, captureBuff_(0, audioFormat_)
, playbackIBuff_(1024)
, captureIBuff_(1024)
, is_playback_prepared_(false)
, is_capture_prepared_(false)
, captureBuff_(0)
, is_playback_running_(false)
, is_capture_running_(false)
, is_playback_open_(false)
, is_capture_open_(false)
, audioThread_(nullptr)
, mainRingBuffer_(Manager::instance().getRingBufferPool().getRingBuffer(RingBufferPool::DEFAULT_ID))
{}
CoreLayer::~CoreLayer()
{
isStarted_ = false;
if (captureBuff_) {
for (UInt32 i = 0; i < captureBuff_->mNumberBuffers; ++i)
free(captureBuff_->mBuffers[i].mData);
free(captureBuff_);
captureBuff_ = 0;
}
}
std::vector<std::string> CoreLayer::getCaptureDeviceList() const
{
return {};
std::vector<std::string> ret;
for (auto x : getDeviceList(true))
ret.push_back(x.name_);
return ret;
}
std::vector<std::string> CoreLayer::getPlaybackDeviceList() const
......@@ -122,34 +88,350 @@ std::vector<std::string> CoreLayer::getPlaybackDeviceList() const
return ret;
}
void CoreLayer::startStream()
int CoreLayer::getAudioDeviceIndex(const std::string& name, DeviceType type) const
{
dcblocker_.reset();
return 0;
}
if (is_playback_running_ and is_capture_running_)
std::string CoreLayer::getAudioDeviceName(int index, DeviceType type) const
{
return "";
}
void CoreLayer::initAudioLayerPlayback()
{
// OS X uses Audio Units for output. Steps:
// 1) Create a description.
// 2) Find the audio unit that fits that.
// 3) Set the audio unit callback.
// 4) Initialize everything.
// 5) Profit...
SFL_DBG("INIT AUDIO PLAYBACK");
AudioComponentDescription outputDesc = {0};
outputDesc.componentType = kAudioUnitType_Output;
outputDesc.componentSubType = kAudioUnitSubType_DefaultOutput;
outputDesc.componentManufacturer = kAudioUnitManufacturer_Apple;
AudioComponent outComp = AudioComponentFindNext(NULL, &outputDesc);
if (outComp == NULL) {
SFL_ERR("Can't find default output audio component.");
return;
}
checkErr(AudioComponentInstanceNew(outComp, &outputUnit_));
AURenderCallbackStruct callback;
callback.inputProc = outputCallback;
callback.inputProcRefCon = this;
checkErr(AudioUnitSetProperty(outputUnit_,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&callback,
sizeof(callback)));
checkErr(AudioUnitInitialize(outputUnit_));
checkErr(AudioOutputUnitStart(outputUnit_));
is_playback_running_ = true;
is_capture_running_ = true;
initAudioFormat();
}
void CoreLayer::initAudioLayerCapture()
{
// HALUnit description.
AudioComponentDescription desc;
desc = {0};
desc.componentType = kAudioUnitType_Output;
desc.componentSubType = kAudioUnitSubType_HALOutput;
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
AudioComponent comp = AudioComponentFindNext(NULL, &desc);
if (comp == NULL)
SFL_ERR("Can't find an input HAL unit that matches description.");
checkErr(AudioComponentInstanceNew(comp, &inputUnit_));
// HALUnit settings.
AudioUnitScope outputBus = 0;
AudioUnitScope inputBus = 1;
UInt32 enableIO = 1;
UInt32 disableIO = 0;
UInt32 size = 0;
checkErr(AudioUnitSetProperty(inputUnit_,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
inputBus,
&enableIO,
sizeof(enableIO)));
checkErr(AudioUnitSetProperty(inputUnit_,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
outputBus,
&disableIO,
sizeof(disableIO)));
AudioDeviceID defaultDevice = kAudioObjectUnknown;
size = sizeof(defaultDevice);
AudioObjectPropertyAddress defaultDeviceProperty;
defaultDeviceProperty.mSelector = kAudioHardwarePropertyDefaultInputDevice;
defaultDeviceProperty.mScope = kAudioObjectPropertyScopeGlobal;
defaultDeviceProperty.mElement = kAudioObjectPropertyElementMaster;
checkErr(AudioObjectGetPropertyData(kAudioObjectSystemObject,
&defaultDeviceProperty,
outputBus,
NULL,
&size,
&defaultDevice));
checkErr(AudioUnitSetProperty(inputUnit_,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
outputBus,
&defaultDevice,
sizeof(defaultDevice)));
// Set format on output *SCOPE* in input *BUS*.
AudioStreamBasicDescription info;
size = sizeof(AudioStreamBasicDescription);
checkErr(AudioUnitGetProperty(inputUnit_,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inputBus,
&info,
&size));
const AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
audioFormat_ = {(unsigned int)info.mSampleRate, (unsigned int)info.mChannelsPerFrame};
//hardwareFormatAvailable(audioFormat_);
SFL_DBG("Input format : %d, %d\n\n", audioFormat_.sample_rate, audioFormat_.nb_channels);
// Input buffer setup. Note that ioData is empty and we have to store data
// in another buffer.
UInt32 bufferSizeFrames = 0;
size = sizeof(UInt32);
checkErr(AudioUnitGetProperty(inputUnit_,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global,
outputBus,
&bufferSizeFrames,
&size));
UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);
size = offsetof(AudioBufferList, mBuffers[0]) +
(sizeof(AudioBuffer) * info.mChannelsPerFrame);
captureBuff_ = (AudioBufferList *)malloc(size);
captureBuff_->mNumberBuffers = info.mChannelsPerFrame;
for (UInt32 i = 0; i < captureBuff_->mNumberBuffers; ++i) {
captureBuff_->mBuffers[i].mNumberChannels = 1;
captureBuff_->mBuffers[i].mDataByteSize = bufferSizeBytes;
captureBuff_->mBuffers[i].mData = malloc(bufferSizeBytes);
}
int CoreLayer::getAudioDeviceIndex(const std::string& name, DeviceType type) const
// Input callback setup.
AURenderCallbackStruct inputCall;
inputCall.inputProc = inputCallback;
inputCall.inputProcRefCon = this;
checkErr(AudioUnitSetProperty(inputUnit_,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
outputBus,
&inputCall,
sizeof(inputCall)));
// Start it up.
checkErr(AudioUnitInitialize(inputUnit_));
checkErr(AudioOutputUnitStart(inputUnit_));
}
void CoreLayer::startStream()
{
return 0;
SFL_DBG("START STREAM");
dcblocker_.reset();
if (is_playback_running_ and is_capture_running_)
return;
initAudioLayerPlayback();
initAudioLayerCapture();
}
std::string CoreLayer::getAudioDeviceName(int index, DeviceType type) const
void CoreLayer::destroyAudioLayer()
{
return "";
AudioOutputUnitStop(outputUnit_);
AudioUnitUninitialize(outputUnit_);
AudioComponentInstanceDispose(outputUnit_);
AudioOutputUnitStop(inputUnit_);
AudioUnitUninitialize(inputUnit_);
AudioComponentInstanceDispose(inputUnit_);
}
void CoreLayer::stopStream()
{
isStarted_ = false;
SFL_DBG("STOP STREAM");
isStarted_ = is_playback_running_ = is_capture_running_ = false;
destroyAudioLayer();
/* Flush the ring buffers */
flushUrgent();
flushMain();
}
//// PRIVATE /////
void CoreLayer::initAudioFormat()
{
AudioStreamBasicDescription info;
UInt32 size = sizeof(info);
checkErr(AudioUnitGetProperty(outputUnit_,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&info,
&size));
audioFormat_ = {(unsigned int)info.mSampleRate, (unsigned int)info.mChannelsPerFrame};
hardwareFormatAvailable(audioFormat_);
}
OSStatus CoreLayer::outputCallback(void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
static_cast<CoreLayer*>(inRefCon)->write(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
return kAudioServicesNoError;
}
void CoreLayer::write(AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
unsigned framesToGet = urgentRingBuffer_.availableForGet(RingBufferPool::DEFAULT_ID);
if (framesToGet <= 0) {
for (int i = 0; i < inNumberFrames; ++i) {
Float32* outDataL = (Float32*)ioData->mBuffers[0].mData;
outDataL[i] = 0.0;
Float32* outDataR = (Float32*)ioData->mBuffers[1].mData;
outDataR[i] = 0.0;
}
return;
}
size_t totSample = std::min(inNumberFrames, framesToGet);
playbackBuff_.setFormat(audioFormat_);
playbackBuff_.resize(totSample);
urgentRingBuffer_.get(playbackBuff_, RingBufferPool::DEFAULT_ID);
playbackBuff_.applyGain(isPlaybackMuted_ ? 0.0 : playbackGain_);
for (int i = 0; i < audioFormat_.nb_channels; ++i)
playbackBuff_.channelToFloat((Float32*)ioData->mBuffers[i].mData, i); // Write
Manager::instance().getRingBufferPool().discard(totSample, RingBufferPool::DEFAULT_ID);
}
OSStatus CoreLayer::inputCallback(void* inRefCon,
AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
static_cast<CoreLayer*>(inRefCon)->read(ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
return kAudioServicesNoError;
}
void CoreLayer::read(AudioUnitRenderActionFlags* ioActionFlags,
const AudioTimeStamp* inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList* ioData)
{
//SFL_DBG("INPUT CALLBACK");
if (inNumberFrames <= 0) {
SFL_WARN("No frames for input.");
return;
}
// Write the mic samples in our buffer
checkErr(AudioUnitRender(inputUnit_,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
captureBuff_));
AudioStreamBasicDescription info;
UInt32 size = sizeof(AudioStreamBasicDescription);
checkErr(AudioUnitGetProperty(inputUnit_,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inBusNumber,
&info,
&size));
// Add them to sflphone ringbuffer.
const AudioFormat mainBufferFormat = Manager::instance().getRingBufferPool().getInternalAudioFormat();
bool resample = info.mSampleRate != mainBufferFormat.sample_rate;
// FIXME: Performance! There *must* be a better way. This is testing only.
AudioBuffer inBuff(inNumberFrames, audioFormat_);
for (int i = 0; i < info.mChannelsPerFrame; ++i) {
Float32* data = (Float32*)captureBuff_->mBuffers[i].mData;
for (int j = 0; j < inNumberFrames; ++j) {
(*inBuff.getChannel(i))[j] = (SFLAudioSample)((data)[j] / .000030517578125f);
}
}
if (resample) {
//SFL_WARN("Resampling Input.");
//FIXME: May be a multiplication, check alsa vs pulse implementation.
int outSamples = inNumberFrames / (static_cast<double>(audioFormat_.sample_rate) / mainBufferFormat.sample_rate);
AudioBuffer out(outSamples, mainBufferFormat);
resampler_->resample(inBuff, out);
dcblocker_.process(out);
mainRingBuffer_->put(out);
} else {
dcblocker_.process(inBuff);
mainRingBuffer_->put(inBuff);
}
}
void CoreLayer::updatePreference(AudioPreference &preference, int index, DeviceType type)
{
switch (type) {
......
......@@ -33,6 +33,19 @@
#include "audio/audiolayer.h"
#include "noncopyable.h"
#include <CoreFoundation/CoreFoundation.h>
#include <AudioToolbox/AudioToolbox.h>
#include <CoreAudio/AudioHardware.h>
#define checkErr( err) \
if(err) {\
OSStatus error = static_cast<OSStatus>(err);\
fprintf(stdout, "CoreAudio Error: %ld -> %s: %d\n", (long)error,\
__FILE__, \
__LINE__\
);\
fflush(stdout);\
}
/**
* @file CoreLayer.h
......@@ -41,7 +54,6 @@
namespace sfl {
class CoreAudioThread;
class RingBuffer;
class AudioDevice;
......@@ -84,6 +96,9 @@ class CoreLayer : public AudioLayer {
return indexRing_;
}
void initAudioLayerPlayback();
void initAudioLayerCapture();
/**
* Start the capture stream and prepare the playback stream.
* The playback starts accordingly to its threshold
......@@ -92,6 +107,8 @@ class CoreLayer : public AudioLayer {