diff --git a/.gitignore b/.gitignore index c332f108424624b6348f9774784a40aa88e2bbac..ac09f4b9dde5d2bbfb0e4bcef482eb31cd357afd 100644 --- a/.gitignore +++ b/.gitignore @@ -84,3 +84,6 @@ nbproject # Windows *.exe + +# Mac +.DS_Store diff --git a/daemon/.gitignore b/daemon/.gitignore index 9e296754491b0c8242b2924550a3ca437c9b05f1..135e9127e6a26dff6501e7c8632d9970d85f87c2 100644 --- a/daemon/.gitignore +++ b/daemon/.gitignore @@ -5,4 +5,3 @@ configure bin/dbus/*.adaptor.h bin/dbus/org.sflphone.SFLphone.service bin/sflphoned -.DS_Store diff --git a/daemon/bin/osxmain.cpp b/daemon/bin/osxmain.cpp index ef891f4cd2a3fcfc7e96cd8f321c7ee99421c38c..b88e2ae12194c3a7d1adee54f8016d552dfa2d92 100644 --- a/daemon/bin/osxmain.cpp +++ b/daemon/bin/osxmain.cpp @@ -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() diff --git a/daemon/src/audio/audiobuffer.cpp b/daemon/src/audio/audiobuffer.cpp index 26b843ec6f07c771262bb5ac4d50c357b7e563c9..9aeab1834a9e11a04697b7f28ecd165f590f094f 100644 --- a/daemon/src/audio/audiobuffer.cpp +++ b/daemon/src/audio/audiobuffer.cpp @@ -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) diff --git a/daemon/src/audio/audiobuffer.h b/daemon/src/audio/audiobuffer.h index 82e23b8243eb1129128d121bf4639114bb273ca4..ac227bbcd3508d8c1fd1494aae641abea5eb0fd9 100644 --- a/daemon/src/audio/audiobuffer.h +++ b/daemon/src/audio/audiobuffer.h @@ -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. diff --git a/daemon/src/audio/coreaudio/audiodevice.h b/daemon/src/audio/coreaudio/audiodevice.h index cae2017ae2925cf65a0732b6cc6e9dd2a357c4d2..9611e9bdd7cee8646df9b9c56991c9960dafae45 100644 --- a/daemon/src/audio/coreaudio/audiodevice.h +++ b/daemon/src/audio/coreaudio/audiodevice.h @@ -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: diff --git a/daemon/src/audio/coreaudio/corelayer.cpp b/daemon/src/audio/coreaudio/corelayer.cpp index 088200d46f78874e965db527767adf02c1164121..008ad8b71e82792f0a446457224c3a6a5476610f 100644 --- a/daemon/src/audio/coreaudio/corelayer.cpp +++ b/daemon/src/audio/coreaudio/corelayer.cpp @@ -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) { diff --git a/daemon/src/audio/coreaudio/corelayer.h b/daemon/src/audio/coreaudio/corelayer.h index c5331f84aab9a34869b183bf7beaab1e9bc7ea02..f9e0179a73c84a40ed86cfd93079d1dccba92e00 100644 --- a/daemon/src/audio/coreaudio/corelayer.h +++ b/daemon/src/audio/coreaudio/corelayer.h @@ -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 { virtual void startStream(); + void destroyAudioLayer(); + /** * Stop the playback and capture streams. * Drops the pending frames and put the capture and playback handles to PREPARED state @@ -99,9 +116,39 @@ class CoreLayer : public AudioLayer { */ virtual void stopStream(); + + private: NON_COPYABLE(CoreLayer); - friend class CoreAudioThread; + + void initAudioFormat(); + + static OSStatus outputCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); + + void write(AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); + + static OSStatus inputCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); + + void read(AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); + virtual void updatePreference(AudioPreference &pref, int index, DeviceType type); /** @@ -121,21 +168,18 @@ class CoreLayer : public AudioLayer { /** Non-interleaved audio buffers */ AudioBuffer playbackBuff_; - AudioBuffer captureBuff_; + ::AudioBufferList* captureBuff_; // CoreAudio buffer. /** Interleaved buffer */ std::vector<SFLAudioSample> playbackIBuff_; std::vector<SFLAudioSample> captureIBuff_; - bool is_playback_prepared_; - bool is_capture_prepared_; + AudioUnit outputUnit_; + AudioUnit inputUnit_; + std::shared_ptr<RingBuffer> mainRingBuffer_; + bool is_playback_running_; bool is_capture_running_; - bool is_playback_open_; - bool is_capture_open_; - - CoreAudioThread* audioThread_; - std::shared_ptr<RingBuffer> mainRingBuffer_; std::vector<AudioDevice> getDeviceList(bool getCapture) const; };