AudioInputPulse.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. //
  2. // libtgvoip is free and unencumbered public domain software.
  3. // For more information, see http://unlicense.org or the UNLICENSE file
  4. // you should have received with this source code distribution.
  5. //
  6. #include <assert.h>
  7. #include <dlfcn.h>
  8. #include <unistd.h>
  9. #include "AudioInputPulse.h"
  10. #include "../../logging.h"
  11. #include "../../VoIPController.h"
  12. #include "AudioPulse.h"
  13. #include "PulseFunctions.h"
  14. #if !defined(__GLIBC__)
  15. #include <libgen.h>
  16. #endif
  17. #define BUFFER_SIZE 960
  18. #define CHECK_ERROR(res, msg) if(res!=0){LOGE(msg " failed: %s", pa_strerror(res)); failed=true; return;}
  19. using namespace tgvoip::audio;
  20. AudioInputPulse::AudioInputPulse(pa_context* context, pa_threaded_mainloop* mainloop, std::string devID){
  21. isRecording=false;
  22. isConnected=false;
  23. didStart=false;
  24. this->mainloop=mainloop;
  25. this->context=context;
  26. stream=NULL;
  27. remainingDataSize=0;
  28. pa_threaded_mainloop_lock(mainloop);
  29. stream=CreateAndInitStream();
  30. pa_threaded_mainloop_unlock(mainloop);
  31. isLocked=false;
  32. if(!stream){
  33. return;
  34. }
  35. SetCurrentDevice(devID);
  36. }
  37. AudioInputPulse::~AudioInputPulse(){
  38. if(stream){
  39. pa_stream_disconnect(stream);
  40. pa_stream_unref(stream);
  41. }
  42. }
  43. pa_stream* AudioInputPulse::CreateAndInitStream(){
  44. pa_sample_spec sampleSpec{
  45. .format=PA_SAMPLE_S16LE,
  46. .rate=48000,
  47. .channels=1
  48. };
  49. pa_proplist* proplist=pa_proplist_new();
  50. pa_proplist_sets(proplist, PA_PROP_FILTER_APPLY, ""); // according to PA sources, this disables any possible filters
  51. pa_stream* stream=pa_stream_new_with_proplist(context, "libtgvoip capture", &sampleSpec, NULL, proplist);
  52. pa_proplist_free(proplist);
  53. if(!stream){
  54. LOGE("Error initializing PulseAudio (pa_stream_new)");
  55. failed=true;
  56. return NULL;
  57. }
  58. pa_stream_set_state_callback(stream, AudioInputPulse::StreamStateCallback, this);
  59. pa_stream_set_read_callback(stream, AudioInputPulse::StreamReadCallback, this);
  60. return stream;
  61. }
  62. void AudioInputPulse::Start(){
  63. if(failed || isRecording)
  64. return;
  65. pa_threaded_mainloop_lock(mainloop);
  66. isRecording=true;
  67. pa_operation_unref(pa_stream_cork(stream, 0, NULL, NULL));
  68. pa_threaded_mainloop_unlock(mainloop);
  69. }
  70. void AudioInputPulse::Stop(){
  71. if(!isRecording)
  72. return;
  73. isRecording=false;
  74. pa_threaded_mainloop_lock(mainloop);
  75. pa_operation_unref(pa_stream_cork(stream, 1, NULL, NULL));
  76. pa_threaded_mainloop_unlock(mainloop);
  77. }
  78. bool AudioInputPulse::IsRecording(){
  79. return isRecording;
  80. }
  81. void AudioInputPulse::SetCurrentDevice(std::string devID){
  82. pa_threaded_mainloop_lock(mainloop);
  83. currentDevice=devID;
  84. if(isRecording && isConnected){
  85. pa_stream_disconnect(stream);
  86. pa_stream_unref(stream);
  87. isConnected=false;
  88. stream=CreateAndInitStream();
  89. }
  90. pa_buffer_attr bufferAttr={
  91. .maxlength=(uint32_t)-1,
  92. .tlength=(uint32_t)-1,
  93. .prebuf=(uint32_t)-1,
  94. .minreq=(uint32_t)-1,
  95. .fragsize=960*2
  96. };
  97. int streamFlags=PA_STREAM_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_ADJUST_LATENCY;
  98. int err=pa_stream_connect_record(stream, devID=="default" ? NULL : devID.c_str(), &bufferAttr, (pa_stream_flags_t)streamFlags);
  99. if(err!=0){
  100. pa_threaded_mainloop_unlock(mainloop);
  101. /*if(devID!="default"){
  102. SetCurrentDevice("default");
  103. return;
  104. }*/
  105. }
  106. CHECK_ERROR(err, "pa_stream_connect_record");
  107. while(true){
  108. pa_stream_state_t streamState=pa_stream_get_state(stream);
  109. if(!PA_STREAM_IS_GOOD(streamState)){
  110. LOGE("Error connecting to audio device '%s'", devID.c_str());
  111. pa_threaded_mainloop_unlock(mainloop);
  112. failed=true;
  113. return;
  114. }
  115. if(streamState==PA_STREAM_READY)
  116. break;
  117. pa_threaded_mainloop_wait(mainloop);
  118. }
  119. isConnected=true;
  120. if(isRecording){
  121. pa_operation_unref(pa_stream_cork(stream, 0, NULL, NULL));
  122. }
  123. pa_threaded_mainloop_unlock(mainloop);
  124. }
  125. bool AudioInputPulse::EnumerateDevices(std::vector<AudioInputDevice>& devs){
  126. return AudioPulse::DoOneOperation([&](pa_context* ctx){
  127. return pa_context_get_source_info_list(ctx, [](pa_context* ctx, const pa_source_info* info, int eol, void* userdata){
  128. if(eol>0)
  129. return;
  130. std::vector<AudioInputDevice>* devs=(std::vector<AudioInputDevice>*)userdata;
  131. AudioInputDevice dev;
  132. dev.id=std::string(info->name);
  133. dev.displayName=std::string(info->description);
  134. devs->push_back(dev);
  135. }, &devs);
  136. });
  137. }
  138. void AudioInputPulse::StreamStateCallback(pa_stream *s, void* arg) {
  139. AudioInputPulse* self=(AudioInputPulse*) arg;
  140. pa_threaded_mainloop_signal(self->mainloop, 0);
  141. }
  142. void AudioInputPulse::StreamReadCallback(pa_stream *stream, size_t requestedBytes, void *userdata){
  143. ((AudioInputPulse*)userdata)->StreamReadCallback(stream, requestedBytes);
  144. }
  145. void AudioInputPulse::StreamReadCallback(pa_stream *stream, size_t requestedBytes) {
  146. size_t bytesRemaining = requestedBytes;
  147. uint8_t *buffer = NULL;
  148. pa_usec_t latency;
  149. if(pa_stream_get_latency(stream, &latency, NULL)==0){
  150. estimatedDelay=(int32_t)(latency/100);
  151. }
  152. while (bytesRemaining > 0) {
  153. size_t bytesToFill = 102400;
  154. if (bytesToFill > bytesRemaining) bytesToFill = bytesRemaining;
  155. int err=pa_stream_peek(stream, (const void**) &buffer, &bytesToFill);
  156. CHECK_ERROR(err, "pa_stream_peek");
  157. if(isRecording){
  158. if(remainingDataSize+bytesToFill>sizeof(remainingData)){
  159. LOGE("Capture buffer is too big (%d)", (int)bytesToFill);
  160. }
  161. memcpy(remainingData+remainingDataSize, buffer, bytesToFill);
  162. remainingDataSize+=bytesToFill;
  163. while(remainingDataSize>=960*2){
  164. InvokeCallback(remainingData, 960*2);
  165. memmove(remainingData, remainingData+960*2, remainingDataSize-960*2);
  166. remainingDataSize-=960*2;
  167. }
  168. }
  169. err=pa_stream_drop(stream);
  170. CHECK_ERROR(err, "pa_stream_drop");
  171. bytesRemaining -= bytesToFill;
  172. }
  173. }