AlsaSound.cpp 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. 
  2. // Use -lasound for linking to the winmm library in GCC/G++
  3. // Install on Arch: sudo pacman -S libasound-dev
  4. // Install on Debian: sudo apt-get install libasound-dev
  5. #include "../../dfpsr/Source/DFPSR/includeFramework.h"
  6. #include "soundManagers.h"
  7. #include <alsa/asoundlib.h>
  8. using namespace dsr;
  9. snd_pcm_t *pcm = nullptr;
  10. static int bufferElements = 0;
  11. static int16_t *outputBuffer = nullptr;
  12. static float *floatBuffer = nullptr;
  13. static void allocateBuffers(int neededElements) {
  14. // TODO: Use aligned memory with Buffer
  15. outputBuffer = (int16_t *)calloc(roundUp(neededElements, 8), sizeof(int16_t));
  16. floatBuffer = (float *)calloc(roundUp(neededElements, 8), sizeof(float));
  17. bufferElements = neededElements;
  18. }
  19. static void terminateSound() {
  20. if (pcm) {
  21. snd_pcm_drop(pcm);
  22. snd_pcm_drain(pcm);
  23. snd_pcm_close(pcm);
  24. pcm = nullptr;
  25. }
  26. if (outputBuffer) { free(outputBuffer); }
  27. if (floatBuffer) { free(floatBuffer); }
  28. }
  29. bool sound_streamToSpeakers(int channels, int sampleRate, std::function<bool(float*, int)> soundOutput) {
  30. int errorCode;
  31. if ((errorCode = snd_pcm_open(&pcm, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
  32. terminateSound();
  33. throwError("Cannot open sound device. (", snd_strerror(errorCode), ")\n");
  34. }
  35. snd_pcm_hw_params_t *hardwareParameters;
  36. snd_pcm_hw_params_alloca(&hardwareParameters);
  37. snd_pcm_hw_params_any(pcm, hardwareParameters);
  38. if ((errorCode = snd_pcm_hw_params_set_access(pcm, hardwareParameters, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
  39. terminateSound();
  40. throwError("Failed to select interleaved sound. (", snd_strerror(errorCode), ")\n");
  41. }
  42. if ((errorCode = snd_pcm_hw_params_set_format(pcm, hardwareParameters, SND_PCM_FORMAT_S16_LE)) < 0) {
  43. terminateSound();
  44. throwError("Failed to select sound format. (", snd_strerror(errorCode), ")\n");
  45. }
  46. if ((errorCode = snd_pcm_hw_params_set_channels(pcm, hardwareParameters, channels)) < 0) {
  47. terminateSound();
  48. throwError("Failed to select channel count. (", snd_strerror(errorCode), ")\n");
  49. }
  50. if ((errorCode = snd_pcm_hw_params_set_buffer_size(pcm, hardwareParameters, 2048)) < 0) {
  51. terminateSound();
  52. throwError("Failed to select buffer size. (", snd_strerror(errorCode), ")\n");
  53. }
  54. uint rate = sampleRate;
  55. if ((errorCode = snd_pcm_hw_params_set_rate_near(pcm, hardwareParameters, &rate, 0)) < 0) {
  56. terminateSound();
  57. throwError("Failed to select approximate sample rate. (", snd_strerror(errorCode), ")\n");
  58. }
  59. if ((errorCode = snd_pcm_hw_params(pcm, hardwareParameters)) < 0) {
  60. terminateSound();
  61. throwError("Failed to select hardware parameters. (", snd_strerror(errorCode), ")\n");
  62. }
  63. // Allocate a buffer for sending data to speakers
  64. snd_pcm_uframes_t samplesPerChannel;
  65. snd_pcm_hw_params_get_period_size(hardwareParameters, &samplesPerChannel, 0);
  66. // Allocate target buffers
  67. int totalSamples = samplesPerChannel * channels;
  68. allocateBuffers(totalSamples);
  69. while (true) {
  70. memset(floatBuffer, 0, totalSamples * sizeof(float));
  71. bool keepRunning = soundOutput(floatBuffer, samplesPerChannel);
  72. // Convert to target format so that the sound can be played
  73. // TODO: Use SIMD
  74. for (uint32_t t = 0; t < samplesPerChannel * channels; t++) {
  75. outputBuffer[t] = sound_convertF32ToI16(floatBuffer[t]);
  76. }
  77. errorCode = snd_pcm_writei(pcm, outputBuffer, samplesPerChannel);
  78. if (errorCode == -EPIPE) {
  79. // Came too late! Not enough written samples to play.
  80. snd_pcm_prepare(pcm);
  81. } else if (errorCode < 0) {
  82. terminateSound();
  83. throwError("Failed writing data to PCM. (", snd_strerror(errorCode), ")\n");
  84. }
  85. if (!keepRunning) {
  86. break;
  87. }
  88. }
  89. terminateSound();
  90. return true;
  91. }