alsa.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. #include <alsa/asoundlib.h>
  2. struct AudioALSA : AudioDriver {
  3. AudioALSA& self = *this;
  4. AudioALSA(Audio& super) : AudioDriver(super) {}
  5. ~AudioALSA() { terminate(); }
  6. auto create() -> bool override {
  7. super.setDevice("default");
  8. super.setChannels(2);
  9. super.setFrequency(48000);
  10. super.setLatency(20);
  11. return initialize();
  12. }
  13. auto driver() -> string override { return "ALSA"; }
  14. auto ready() -> bool override { return _ready; }
  15. auto hasBlocking() -> bool override { return true; }
  16. auto hasDynamic() -> bool override { return true; }
  17. auto hasDevices() -> vector<string> override {
  18. vector<string> devices;
  19. char** list;
  20. if(snd_device_name_hint(-1, "pcm", (void***)&list) == 0) {
  21. uint index = 0;
  22. while(list[index]) {
  23. char* deviceName = snd_device_name_get_hint(list[index], "NAME");
  24. if(deviceName) devices.append(deviceName);
  25. free(deviceName);
  26. index++;
  27. }
  28. }
  29. snd_device_name_free_hint((void**)list);
  30. return devices;
  31. }
  32. auto hasChannels() -> vector<uint> override {
  33. return {2};
  34. }
  35. auto hasFrequencies() -> vector<uint> override {
  36. return {44100, 48000, 96000};
  37. }
  38. auto hasLatencies() -> vector<uint> override {
  39. return {20, 40, 60, 80, 100};
  40. }
  41. auto setDevice(string device) -> bool override { return initialize(); }
  42. auto setBlocking(bool blocking) -> bool override { return true; }
  43. auto setChannels(uint channels) -> bool override { return true; }
  44. auto setFrequency(uint frequency) -> bool override { return initialize(); }
  45. auto setLatency(uint latency) -> bool override { return initialize(); }
  46. auto level() -> double override {
  47. snd_pcm_sframes_t available;
  48. for(uint timeout : range(256)) {
  49. available = snd_pcm_avail_update(_interface);
  50. if(available >= 0) break;
  51. snd_pcm_recover(_interface, available, 1);
  52. }
  53. return (double)(_bufferSize - available) / _bufferSize;
  54. }
  55. auto output(const double samples[]) -> void override {
  56. _buffer[_offset] = (uint16_t)sclamp<16>(samples[0] * 32767.0) << 0;
  57. _buffer[_offset] |= (uint16_t)sclamp<16>(samples[1] * 32767.0) << 16;
  58. if(++_offset < _periodSize) return;
  59. snd_pcm_sframes_t available;
  60. do {
  61. available = snd_pcm_avail_update(_interface);
  62. if(available < 0) {
  63. snd_pcm_recover(_interface, available, 1);
  64. continue;
  65. }
  66. if(available < _offset) {
  67. if(!self.blocking) {
  68. _offset = 0;
  69. return;
  70. }
  71. int error = snd_pcm_wait(_interface, -1);
  72. if(error < 0) snd_pcm_recover(_interface, error, 1);
  73. }
  74. } while(available < _offset);
  75. uint32_t* output = _buffer;
  76. int i = 4;
  77. while(_offset > 0 && i--) {
  78. snd_pcm_sframes_t written = snd_pcm_writei(_interface, output, _offset);
  79. if(written < 0) {
  80. //no samples written
  81. snd_pcm_recover(_interface, written, 1);
  82. } else if(written <= _offset) {
  83. _offset -= written;
  84. output += written;
  85. }
  86. }
  87. if(i < 0) {
  88. if(_buffer == output) {
  89. _offset--;
  90. output++;
  91. }
  92. memory::move<uint32_t>(_buffer, output, _offset);
  93. }
  94. }
  95. private:
  96. auto initialize() -> bool {
  97. terminate();
  98. if(!hasDevices().find(self.device)) self.device = "default";
  99. if(snd_pcm_open(&_interface, self.device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0) return terminate(), false;
  100. uint rate = self.frequency;
  101. uint bufferTime = self.latency * 1000;
  102. uint periodTime = self.latency * 1000 / 8;
  103. snd_pcm_hw_params_t* hardwareParameters;
  104. snd_pcm_hw_params_alloca(&hardwareParameters);
  105. if(snd_pcm_hw_params_any(_interface, hardwareParameters) < 0) return terminate(), false;
  106. if(snd_pcm_hw_params_set_access(_interface, hardwareParameters, SND_PCM_ACCESS_RW_INTERLEAVED) < 0
  107. || snd_pcm_hw_params_set_format(_interface, hardwareParameters, SND_PCM_FORMAT_S16_LE) < 0
  108. || snd_pcm_hw_params_set_channels(_interface, hardwareParameters, 2) < 0
  109. || snd_pcm_hw_params_set_rate_near(_interface, hardwareParameters, &rate, 0) < 0
  110. || snd_pcm_hw_params_set_period_time_near(_interface, hardwareParameters, &periodTime, 0) < 0
  111. || snd_pcm_hw_params_set_buffer_time_near(_interface, hardwareParameters, &bufferTime, 0) < 0
  112. ) return terminate(), false;
  113. if(snd_pcm_hw_params(_interface, hardwareParameters) < 0) return terminate(), false;
  114. if(snd_pcm_get_params(_interface, &_bufferSize, &_periodSize) < 0) return terminate(), false;
  115. snd_pcm_sw_params_t* softwareParameters;
  116. snd_pcm_sw_params_alloca(&softwareParameters);
  117. if(snd_pcm_sw_params_current(_interface, softwareParameters) < 0) return terminate(), false;
  118. if(snd_pcm_sw_params_set_start_threshold(_interface, softwareParameters, _bufferSize / 2) < 0) return terminate(), false;
  119. if(snd_pcm_sw_params(_interface, softwareParameters) < 0) return terminate(), false;
  120. _buffer = new uint32_t[_periodSize]();
  121. _offset = 0;
  122. return _ready = true;
  123. }
  124. auto terminate() -> void {
  125. _ready = false;
  126. if(_interface) {
  127. //snd_pcm_drain(_interface); //prevents popping noise; but causes multi-second lag
  128. snd_pcm_close(_interface);
  129. _interface = nullptr;
  130. }
  131. if(_buffer) {
  132. delete[] _buffer;
  133. _buffer = nullptr;
  134. }
  135. }
  136. bool _ready = false;
  137. snd_pcm_t* _interface = nullptr;
  138. snd_pcm_uframes_t _bufferSize;
  139. snd_pcm_uframes_t _periodSize;
  140. uint32_t* _buffer = nullptr;
  141. uint _offset = 0;
  142. };