Extra "ports" of juce-based plugins using the distrho build system
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

147 lines
5.6KB

  1. --- Class to read audio files.
  2. -- Example usage: @{soundfile-test.lua}.
  3. --
  4. -- Reads the formats that JUCE supports, namely: WAV, AIFF, Flac, Ogg-Vorbis, Windows Media codecs,
  5. -- CoreAudio codecs, MP3.
  6. --
  7. -- Is a pointer to a [JUCE AudioFormatReader](http://www.juce.com/api/classAudioFormatReader.html),
  8. -- and wraps some [AudioFormatManager](http://www.juce.com/api/classAudioFormatManager.html)
  9. -- functionality.
  10. -- @classmod juce.AudioFormatReader
  11. local plugin = require"include/core/plugin"
  12. local LagrangeInterpolator= require"include/protojuce/lagrangeinterpolator"
  13. ffi.cdef [[
  14. pAudioFormatReader AudioFormatReader_new(const char *filename);
  15. bool AudioFormatReader_read (pAudioFormatReader a,
  16. int *const * destSamples,
  17. int numDestChannels,
  18. int64_t startSampleInSource,
  19. int numSamplesToRead,
  20. bool fillLeftoverChannelsWithCopies);
  21. void AudioFormatReader_delete(pAudioFormatReader a);
  22. ]]
  23. local AudioFormatReader = setmetatable ({}, {
  24. --- Load a sound file as an AudioFormatReader.
  25. -- The path can be absolute or relative to the protoplug directory.
  26. -- Returns `nil` if unsuccessful. The file will remain open until the
  27. -- AudioFormatReader is unset or otherwise garbage-collected.
  28. -- @param filename
  29. -- @within Constructors
  30. -- @constructor
  31. -- @function AudioFormatReader
  32. __call = function(self, filename)
  33. local afr = protolib.AudioFormatReader_new(filename)
  34. if afr.pointer == nil then return end
  35. return ffi.gc(afr, protolib.AudioFormatReader_delete)
  36. end;
  37. })
  38. local AudioFormatReader_mt = {
  39. -- methods
  40. __index = {
  41. --- Read samples.
  42. -- Copies a number of samples from the file into the provided array.
  43. -- @param destSamples a cdata array of pointers to buffers for each channel (`int * const *`)
  44. -- @param numDestChannels the number of elements in `destSamples`
  45. -- @param startSampleInSource
  46. -- @param numSamplesToRead
  47. -- @tparam[opt=true] boolean fillLeftoverChannelsWithCopies used if `destSamples` has more channels than the source.
  48. -- @treturn boolean success
  49. -- @function read
  50. read = function (self, destSamples, numDestChannels, startSampleInSource, numSamplesToRead, fillLeftoverChannelsWithCopies)
  51. if fillLeftoverChannelsWithCopies==nil then
  52. fillLeftoverChannelsWithCopies = true
  53. end
  54. return protolib.AudioFormatReader_read(self, destSamples, numDestChannels, startSampleInSource, numSamplesToRead, fillLeftoverChannelsWithCopies)
  55. end;
  56. --- Read entire wave to float array.
  57. -- A simplified wrapper function for `read`
  58. -- @param[opt=2] nChannels number of channels to be returned
  59. -- @param[opt=true] resample whether to perform samplerate conversion to match the host's sample rate.
  60. -- If `true`, the length of the returned array may not be the wave's original `lengthInSamples`.
  61. -- It will be given by the second returned value.
  62. -- @return a two-dimensional cdata array of channels containing samples (`float [nChannels][nSamples]`)
  63. -- @return the number of samples in each channel of the returned array
  64. -- @function readToFloat
  65. readToFloat = function (self, nChannels, resample)
  66. nChannels = nChannels or 2
  67. resample = resample or true
  68. local floty = ffi.new("float["..nChannels.."]["..tonumber(self.lengthInSamples).."]")
  69. local inty_arg = ffi.new("int*["..nChannels.."]")
  70. for ch=0,nChannels-1 do
  71. inty_arg[ch] = ffi.cast("int*", floty)+self.lengthInSamples*ch
  72. end
  73. local gotWave = self:read(inty_arg, nChannels, 0, tonumber(self.lengthInSamples))
  74. if not gotWave then error "can't read wave" end
  75. if not self.usesFloatingPointData then
  76. for i=0,tonumber(self.lengthInSamples)-1 do
  77. for ch=0,nChannels-1 do
  78. floty[ch][i] = inty_arg[ch][i]/0x80000000
  79. end
  80. end
  81. end
  82. if resample then
  83. local plugSampleRate = plugin.getSampleRate()
  84. if self.sampleRate ~= plugSampleRate then
  85. local len2 = math.floor((plugSampleRate*tonumber(self.lengthInSamples))/self.sampleRate)
  86. local flot2 = ffi.new("float["..nChannels.."]["..len2.."]")
  87. local li = LagrangeInterpolator()
  88. for ch=0,nChannels-1 do
  89. li:process(self.sampleRate/plugSampleRate, floty[ch], flot2[ch], len2)
  90. end
  91. return flot2, len2
  92. end
  93. end
  94. return floty, self.lengthInSamples
  95. end;
  96. --- Read entire wave to double array.
  97. -- This wraps `readToFloat` and returns an array containing `double`-precision numbers.
  98. -- This takes twice as much space, but it may be faster to use, as this is the native Lua type.
  99. -- @param[opt=2] nChannels number of channels to be returned
  100. -- @param[opt=true] resample whether to perform samplerate conversion to match the host's sample rate.
  101. -- If `true`, the length of the returned array may not be the wave's original `lengthInSamples`.
  102. -- It will be given by the second returned value.
  103. -- @return a two-dimensional cdata array of channels containing samples (`double [nChannels][nSamples]`)
  104. -- @return the number of samples in each channel of the returned array
  105. -- @function readToDouble
  106. readToDouble = function (self, nChannels, resample)
  107. nChannels = nChannels or 2
  108. resample = resample or true
  109. local floty, len = self:readToFloat(nChannels)
  110. local douby = ffi.new("double["..nChannels.."]["..tonumber(len).."]")
  111. for i=0,tonumber(len)-1 do
  112. for ch=0,nChannels-1 do
  113. douby[ch][i] = floty[ch][i]
  114. end
  115. end
  116. return douby, len
  117. end;
  118. }
  119. }
  120. ffi.metatype("pAudioFormatReader", AudioFormatReader_mt);
  121. --- Sample rate
  122. -- @simplefield sampleRate
  123. --- Bits per sample
  124. -- @simplefield bitsPerSample
  125. --- Length in samples
  126. -- @simplefield lengthInSamples
  127. --- Number of channels
  128. -- @simplefield numChannels
  129. --- Uses floating point data (boolean)
  130. -- @simplefield usesFloatingPointData
  131. return AudioFormatReader