AudioInputALSA.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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 <stdlib.h>
  7. #include <stdio.h>
  8. #include <assert.h>
  9. #include <dlfcn.h>
  10. #include "AudioInputALSA.h"
  11. #include "../../logging.h"
  12. #include "../../VoIPController.h"
  13. using namespace tgvoip::audio;
  14. #define BUFFER_SIZE 960
  15. #define CHECK_ERROR(res, msg) if(res<0){LOGE(msg ": %s", _snd_strerror(res)); failed=true; return;}
  16. #define CHECK_DL_ERROR(res, msg) if(!res){LOGE(msg ": %s", dlerror()); failed=true; return;}
  17. #define LOAD_FUNCTION(lib, name, ref) {ref=(typeof(ref))dlsym(lib, name); CHECK_DL_ERROR(ref, "Error getting entry point for " name);}
  18. AudioInputALSA::AudioInputALSA(std::string devID){
  19. isRecording=false;
  20. handle=NULL;
  21. lib=dlopen("libasound.so.2", RTLD_LAZY);
  22. if(!lib)
  23. lib=dlopen("libasound.so", RTLD_LAZY);
  24. if(!lib){
  25. LOGE("Error loading libasound: %s", dlerror());
  26. failed=true;
  27. return;
  28. }
  29. LOAD_FUNCTION(lib, "snd_pcm_open", _snd_pcm_open);
  30. LOAD_FUNCTION(lib, "snd_pcm_set_params", _snd_pcm_set_params);
  31. LOAD_FUNCTION(lib, "snd_pcm_close", _snd_pcm_close);
  32. LOAD_FUNCTION(lib, "snd_pcm_readi", _snd_pcm_readi);
  33. LOAD_FUNCTION(lib, "snd_pcm_recover", _snd_pcm_recover);
  34. LOAD_FUNCTION(lib, "snd_strerror", _snd_strerror);
  35. SetCurrentDevice(devID);
  36. }
  37. AudioInputALSA::~AudioInputALSA(){
  38. if(handle)
  39. _snd_pcm_close(handle);
  40. if(lib)
  41. dlclose(lib);
  42. }
  43. void AudioInputALSA::Start(){
  44. if(failed || isRecording)
  45. return;
  46. isRecording=true;
  47. thread=new Thread(std::bind(&AudioInputALSA::RunThread, this));
  48. thread->SetName("AudioInputALSA");
  49. thread->Start();
  50. }
  51. void AudioInputALSA::Stop(){
  52. if(!isRecording)
  53. return;
  54. isRecording=false;
  55. thread->Join();
  56. delete thread;
  57. thread=NULL;
  58. }
  59. void AudioInputALSA::RunThread(){
  60. unsigned char buffer[BUFFER_SIZE*2];
  61. snd_pcm_sframes_t frames;
  62. while(isRecording){
  63. frames=_snd_pcm_readi(handle, buffer, BUFFER_SIZE);
  64. if (frames < 0){
  65. frames = _snd_pcm_recover(handle, frames, 0);
  66. }
  67. if (frames < 0) {
  68. LOGE("snd_pcm_readi failed: %s\n", _snd_strerror(frames));
  69. break;
  70. }
  71. InvokeCallback(buffer, sizeof(buffer));
  72. }
  73. }
  74. void AudioInputALSA::SetCurrentDevice(std::string devID){
  75. bool wasRecording=isRecording;
  76. isRecording=false;
  77. if(handle){
  78. thread->Join();
  79. _snd_pcm_close(handle);
  80. }
  81. currentDevice=devID;
  82. int res=_snd_pcm_open(&handle, devID.c_str(), SND_PCM_STREAM_CAPTURE, 0);
  83. if(res<0)
  84. res=_snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE, 0);
  85. CHECK_ERROR(res, "snd_pcm_open failed");
  86. res=_snd_pcm_set_params(handle, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 48000, 1, 100000);
  87. CHECK_ERROR(res, "snd_pcm_set_params failed");
  88. if(wasRecording){
  89. isRecording=true;
  90. thread->Start();
  91. }
  92. }
  93. void AudioInputALSA::EnumerateDevices(std::vector<AudioInputDevice>& devs){
  94. int (*_snd_device_name_hint)(int card, const char* iface, void*** hints);
  95. char* (*_snd_device_name_get_hint)(const void* hint, const char* id);
  96. int (*_snd_device_name_free_hint)(void** hinst);
  97. void* lib=dlopen("libasound.so.2", RTLD_LAZY);
  98. if(!lib)
  99. lib=dlopen("libasound.so", RTLD_LAZY);
  100. if(!lib)
  101. return;
  102. _snd_device_name_hint=(typeof(_snd_device_name_hint))dlsym(lib, "snd_device_name_hint");
  103. _snd_device_name_get_hint=(typeof(_snd_device_name_get_hint))dlsym(lib, "snd_device_name_get_hint");
  104. _snd_device_name_free_hint=(typeof(_snd_device_name_free_hint))dlsym(lib, "snd_device_name_free_hint");
  105. if(!_snd_device_name_hint || !_snd_device_name_get_hint || !_snd_device_name_free_hint){
  106. dlclose(lib);
  107. return;
  108. }
  109. char** hints;
  110. int err=_snd_device_name_hint(-1, "pcm", (void***)&hints);
  111. if(err!=0){
  112. dlclose(lib);
  113. return;
  114. }
  115. char** n=hints;
  116. while(*n){
  117. char* name=_snd_device_name_get_hint(*n, "NAME");
  118. if(strncmp(name, "surround", 8)==0 || strcmp(name, "null")==0){
  119. free(name);
  120. n++;
  121. continue;
  122. }
  123. char* desc=_snd_device_name_get_hint(*n, "DESC");
  124. char* ioid=_snd_device_name_get_hint(*n, "IOID");
  125. if(!ioid || strcmp(ioid, "Input")==0){
  126. char* l1=strtok(desc, "\n");
  127. char* l2=strtok(NULL, "\n");
  128. char* tmp=strtok(l1, ",");
  129. char* actualName=tmp;
  130. while((tmp=strtok(NULL, ","))){
  131. actualName=tmp;
  132. }
  133. if(actualName[0]==' ')
  134. actualName++;
  135. AudioInputDevice dev;
  136. dev.id=std::string(name);
  137. if(l2){
  138. char buf[256];
  139. snprintf(buf, sizeof(buf), "%s (%s)", actualName, l2);
  140. dev.displayName=std::string(buf);
  141. }else{
  142. dev.displayName=std::string(actualName);
  143. }
  144. devs.push_back(dev);
  145. }
  146. free(name);
  147. free(desc);
  148. free(ioid);
  149. n++;
  150. }
  151. _snd_device_name_free_hint((void**)hints);
  152. dlclose(lib);
  153. }