2017-06-18 19:32:20 +02:00
|
|
|
//
|
|
|
|
// libtgvoip is free and unencumbered public domain software.
|
|
|
|
// For more information, see http://unlicense.org or the UNLICENSE file
|
|
|
|
// you should have received with this source code distribution.
|
|
|
|
//
|
|
|
|
|
2017-07-04 15:50:02 +02:00
|
|
|
#include "AudioInputModule.h"
|
2017-06-18 19:32:20 +02:00
|
|
|
#include <stdio.h>
|
2017-06-22 14:48:52 +02:00
|
|
|
#include "../libtgvoip/logging.h"
|
2017-07-11 21:11:23 +02:00
|
|
|
#include <queue>
|
2017-07-13 18:03:33 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <phpcpp.h>
|
2017-06-18 19:32:20 +02:00
|
|
|
|
|
|
|
using namespace tgvoip;
|
|
|
|
using namespace tgvoip::audio;
|
|
|
|
|
2017-07-13 18:03:33 +02:00
|
|
|
AudioInputModule::AudioInputModule(std::string deviceID, VoIPController *controller)
|
2017-07-09 21:12:01 +02:00
|
|
|
{
|
2017-07-13 18:03:33 +02:00
|
|
|
wrapper = (VoIP *)(controller->implData);
|
2017-07-10 12:30:13 +02:00
|
|
|
wrapper->in = this;
|
2017-07-13 18:03:33 +02:00
|
|
|
wrapper->inputState = AUDIO_STATE_CREATED;
|
|
|
|
init_mutex(inputMutex);
|
|
|
|
configuringInput = false;
|
2017-06-18 19:32:20 +02:00
|
|
|
}
|
|
|
|
|
2017-07-09 21:12:01 +02:00
|
|
|
AudioInputModule::~AudioInputModule()
|
|
|
|
{
|
2017-07-13 18:03:33 +02:00
|
|
|
wrapper->inputState = AUDIO_STATE_NONE;
|
2017-07-10 12:30:13 +02:00
|
|
|
wrapper->in = NULL;
|
2017-07-13 18:03:33 +02:00
|
|
|
|
2017-07-18 19:16:27 +02:00
|
|
|
if (senderThread) {
|
|
|
|
LOGD("before join senderThread");
|
|
|
|
join_thread(senderThread);
|
|
|
|
}
|
2017-07-13 18:03:33 +02:00
|
|
|
|
|
|
|
while (holdFiles.size()) {
|
|
|
|
fclose(holdFiles.front());
|
|
|
|
holdFiles.pop();
|
|
|
|
}
|
|
|
|
while (inputFiles.size()) {
|
|
|
|
fclose(inputFiles.front());
|
|
|
|
inputFiles.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
free_mutex(inputMutex);
|
2017-06-18 19:32:20 +02:00
|
|
|
}
|
2017-06-28 21:02:15 +02:00
|
|
|
|
2017-07-09 21:12:01 +02:00
|
|
|
void AudioInputModule::Configure(uint32_t sampleRate, uint32_t bitsPerSample, uint32_t channels)
|
|
|
|
{
|
2017-07-11 21:11:23 +02:00
|
|
|
inputSampleNumber = 960;
|
|
|
|
inputSampleRate = sampleRate;
|
|
|
|
inputBitsPerSample = bitsPerSample;
|
|
|
|
inputChannels = channels;
|
|
|
|
inputSamplePeriod = 1.0 / sampleRate * 1000000;
|
|
|
|
inputWritePeriod = 1.0 / sampleRate * inputSampleNumber * 1000000;
|
|
|
|
inputSamplePeriodSec = 1.0 / sampleRate;
|
|
|
|
inputWritePeriodSec = 1.0 / sampleRate * inputSampleNumber;
|
|
|
|
inputSamplesSize = inputSampleNumber * inputChannels * inputBitsPerSample / 8;
|
|
|
|
inputCSamplesSize = inputSampleNumber * inputChannels * inputBitsPerSample / 8 * sizeof(unsigned char);
|
2017-06-28 21:02:15 +02:00
|
|
|
|
2017-07-13 18:03:33 +02:00
|
|
|
wrapper->inputState = AUDIO_STATE_CONFIGURED;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AudioInputModule::play(const char *file) {
|
|
|
|
FILE *tmp = fopen(file, "rb");
|
|
|
|
|
|
|
|
if (tmp == NULL) {
|
|
|
|
throw Php::Exception("Could not open file!");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
configuringInput = true;
|
|
|
|
lock_mutex(inputMutex);
|
|
|
|
inputFiles.push(tmp);
|
|
|
|
configuringInput = false;
|
|
|
|
unlock_mutex(inputMutex);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AudioInputModule::playOnHold(Php::Parameters ¶ms) {
|
|
|
|
configuringInput = true;
|
|
|
|
FILE *tmp = NULL;
|
|
|
|
|
|
|
|
lock_mutex(inputMutex);
|
|
|
|
while (holdFiles.size()) {
|
|
|
|
fclose(holdFiles.front());
|
|
|
|
holdFiles.pop();
|
|
|
|
}
|
|
|
|
for (int i = 0; i < params[0].size(); i++) {
|
|
|
|
tmp = fopen(params[0][i], "rb");
|
|
|
|
if (tmp == NULL) {
|
|
|
|
throw Php::Exception("Could not open file!");
|
|
|
|
configuringInput = false;
|
|
|
|
unlock_mutex(inputMutex);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
holdFiles.push(tmp);
|
|
|
|
}
|
|
|
|
configuringInput = false;
|
|
|
|
unlock_mutex(inputMutex);
|
|
|
|
return true;
|
2017-06-18 19:32:20 +02:00
|
|
|
}
|
|
|
|
|
2017-07-13 18:03:33 +02:00
|
|
|
|
2017-07-09 21:12:01 +02:00
|
|
|
void AudioInputModule::Start()
|
|
|
|
{
|
2017-07-13 18:03:33 +02:00
|
|
|
if (wrapper->inputState == AUDIO_STATE_RUNNING)
|
2017-06-19 15:39:45 +02:00
|
|
|
return;
|
2017-07-13 18:03:33 +02:00
|
|
|
wrapper->inputState = AUDIO_STATE_RUNNING;
|
|
|
|
|
2017-07-11 21:11:23 +02:00
|
|
|
LOGE("STARTING SENDER THREAD");
|
|
|
|
start_thread(senderThread, StartSenderThread, this);
|
|
|
|
set_thread_priority(senderThread, get_thread_max_priority());
|
|
|
|
set_thread_name(senderThread, "voip-sender");
|
2017-07-13 18:03:33 +02:00
|
|
|
|
2017-06-18 19:32:20 +02:00
|
|
|
}
|
|
|
|
|
2017-07-09 21:12:01 +02:00
|
|
|
void AudioInputModule::Stop()
|
|
|
|
{
|
2017-07-13 18:03:33 +02:00
|
|
|
if (wrapper->inputState != AUDIO_STATE_RUNNING)
|
2017-07-10 00:06:50 +02:00
|
|
|
return;
|
2017-07-13 18:03:33 +02:00
|
|
|
wrapper->inputState = AUDIO_STATE_CONFIGURED;
|
2017-06-18 19:32:20 +02:00
|
|
|
}
|
2017-07-11 21:11:23 +02:00
|
|
|
|
2017-07-13 18:03:33 +02:00
|
|
|
|
2017-07-11 21:11:23 +02:00
|
|
|
void* AudioInputModule::StartSenderThread(void* input){
|
|
|
|
((AudioInputModule*)input)->RunSenderThread();
|
|
|
|
return NULL;
|
2017-06-19 23:38:03 +02:00
|
|
|
}
|
2017-07-04 15:50:02 +02:00
|
|
|
|
2017-07-11 21:11:23 +02:00
|
|
|
void AudioInputModule::RunSenderThread() {
|
|
|
|
unsigned char *data = (unsigned char *) malloc(inputCSamplesSize);
|
|
|
|
size_t read;
|
|
|
|
double time = VoIPController::GetCurrentTime();
|
2017-07-13 18:03:33 +02:00
|
|
|
double sleeptime;
|
|
|
|
while (wrapper->inputState == AUDIO_STATE_RUNNING) {
|
|
|
|
lock_mutex(inputMutex);
|
|
|
|
while (!configuringInput && wrapper->inputState == AUDIO_STATE_RUNNING) {
|
|
|
|
if ((sleeptime = (inputWritePeriodSec - (VoIPController::GetCurrentTime() - time))*1000000.0) < 0) {
|
|
|
|
LOGE("Sender: I'm late!");
|
|
|
|
} else {
|
|
|
|
usleep(sleeptime);
|
|
|
|
}
|
2017-07-11 21:11:23 +02:00
|
|
|
time = VoIPController::GetCurrentTime();
|
2017-07-13 18:03:33 +02:00
|
|
|
|
|
|
|
if (!inputFiles.empty()) {
|
|
|
|
if ((read = fread(data, sizeof(unsigned char), inputSamplesSize, inputFiles.front())) != inputCSamplesSize) {
|
|
|
|
fclose(inputFiles.front());
|
|
|
|
inputFiles.pop();
|
2017-07-11 21:11:23 +02:00
|
|
|
memset(data + (read % inputCSamplesSize), 0, inputCSamplesSize - (read % inputCSamplesSize));
|
|
|
|
}
|
2017-07-13 18:54:59 +02:00
|
|
|
wrapper->playing = true;
|
2017-07-11 21:11:23 +02:00
|
|
|
} else {
|
2017-07-13 18:54:59 +02:00
|
|
|
wrapper->playing = false;
|
2017-07-13 18:03:33 +02:00
|
|
|
if (holdFiles.empty()) {
|
2017-07-11 21:11:23 +02:00
|
|
|
memset(data, 0, inputCSamplesSize);
|
|
|
|
} else {
|
2017-07-13 18:03:33 +02:00
|
|
|
if ((read = fread(data, sizeof(unsigned char), inputSamplesSize, holdFiles.front())) != inputCSamplesSize) {
|
|
|
|
fseek(holdFiles.front(), 0, SEEK_SET);
|
|
|
|
holdFiles.push(holdFiles.front());
|
|
|
|
holdFiles.pop();
|
2017-07-11 21:11:23 +02:00
|
|
|
memset(data + (read % inputCSamplesSize), 0, inputCSamplesSize - (read % inputCSamplesSize));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
InvokeCallback(data, inputCSamplesSize);
|
|
|
|
}
|
2017-07-13 18:03:33 +02:00
|
|
|
unlock_mutex(inputMutex);
|
2017-07-11 21:11:23 +02:00
|
|
|
}
|
|
|
|
free(data);
|
2017-07-13 18:03:33 +02:00
|
|
|
}
|
2017-07-11 21:11:23 +02:00
|
|
|
|
2017-07-09 21:12:01 +02:00
|
|
|
void AudioInputModule::EnumerateDevices(std::vector<AudioInputDevice> &devs)
|
|
|
|
{
|
2017-07-04 15:50:02 +02:00
|
|
|
}
|