BsOAOggVorbisWriter.cpp 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. #include "BsOAOggVorbisWriter.h"
  2. #include "BsDataStream.h"
  3. #include "BsAudioUtility.h"
  4. namespace BansheeEngine
  5. {
  6. // Writes to the internal cached buffer and flushes it if needed
  7. #define WRITE_TO_BUFFER(data, length) \
  8. if ((mBufferOffset + length) > BUFFER_SIZE) \
  9. flush(); \
  10. \
  11. if(length > BUFFER_SIZE) \
  12. mWriteCallback(data, length); \
  13. else \
  14. { \
  15. memcpy(mBuffer + mBufferOffset, data, length); \
  16. mBufferOffset += length; \
  17. }
  18. OAOggVorbisWriter::OAOggVorbisWriter()
  19. :mBufferOffset(0), mNumChannels(0), mBitDepth(0)
  20. { }
  21. OAOggVorbisWriter::~OAOggVorbisWriter()
  22. {
  23. close();
  24. }
  25. bool OAOggVorbisWriter::open(std::function<void(UINT8*, UINT32)> writeCallback, UINT32 sampleRate, UINT32 bitDepth,
  26. UINT32 numChannels)
  27. {
  28. mNumChannels = numChannels;
  29. mBitDepth = bitDepth;
  30. mWriteCallback = writeCallback;
  31. ogg_stream_init(&mOggState, std::rand());
  32. vorbis_info_init(&mVorbisInfo);
  33. // Automatic bitrate management with quality 0.4 (~128 kbps for 44 KHz stereo sound)
  34. INT32 status = vorbis_encode_init_vbr(&mVorbisInfo, numChannels, sampleRate, 0.4f);
  35. if (status < 0)
  36. {
  37. LOGERR("Failed to write Ogg Vorbis file.");
  38. close();
  39. return false;
  40. }
  41. vorbis_analysis_init(&mVorbisState, &mVorbisInfo);
  42. // Generate header
  43. vorbis_comment comment;
  44. vorbis_comment_init(&comment);
  45. ogg_packet headerPacket, commentPacket, codePacket;
  46. status = vorbis_analysis_headerout(&mVorbisState, &comment, &headerPacket, &commentPacket, &codePacket);
  47. vorbis_comment_clear(&comment);
  48. if (status < 0)
  49. {
  50. LOGERR("Failed to write Ogg Vorbis file.");
  51. close();
  52. return false;
  53. }
  54. // Write header
  55. ogg_stream_packetin(&mOggState, &headerPacket);
  56. ogg_stream_packetin(&mOggState, &commentPacket);
  57. ogg_stream_packetin(&mOggState, &codePacket);
  58. ogg_page page;
  59. while (ogg_stream_flush(&mOggState, &page) > 0)
  60. {
  61. WRITE_TO_BUFFER(page.header, page.header_len);
  62. WRITE_TO_BUFFER(page.body, page.body_len);
  63. }
  64. return true;
  65. }
  66. void OAOggVorbisWriter::write(UINT8* samples, UINT32 numSamples)
  67. {
  68. // Allocate new buffer and write PCM data to it
  69. UINT32 numFrames = numSamples / mNumChannels;
  70. float** buffer = vorbis_analysis_buffer(&mVorbisState, numFrames);
  71. if (mBitDepth == 8)
  72. {
  73. for (UINT32 i = 0; i < numFrames; i++)
  74. {
  75. for (UINT32 j = 0; j < mNumChannels; j++)
  76. {
  77. UINT8 sample = *(UINT8*)samples;
  78. float encodedSample = sample / 255.0f;
  79. buffer[j][i] = encodedSample;
  80. samples++;
  81. }
  82. }
  83. }
  84. else if (mBitDepth == 16)
  85. {
  86. for (UINT32 i = 0; i < numFrames; i++)
  87. {
  88. for (UINT32 j = 0; j < mNumChannels; j++)
  89. {
  90. INT16 sample = *(INT16*)samples;
  91. float encodedSample = sample / 32767.0f;
  92. buffer[j][i] = encodedSample;
  93. samples += 2;
  94. }
  95. }
  96. }
  97. else if (mBitDepth == 24)
  98. {
  99. for (UINT32 i = 0; i < numFrames; i++)
  100. {
  101. for (UINT32 j = 0; j < mNumChannels; j++)
  102. {
  103. INT32 sample = AudioUtility::convert24To32Bits(samples);
  104. float encodedSample = sample / 8388607.0f;
  105. buffer[j][i] = encodedSample;
  106. samples += 3;
  107. }
  108. }
  109. }
  110. else if (mBitDepth == 32)
  111. {
  112. for (UINT32 i = 0; i < numFrames; i++)
  113. {
  114. for (UINT32 j = 0; j < mNumChannels; j++)
  115. {
  116. INT32 sample = *(INT32*)samples;
  117. float encodedSample = sample / 2147483647.0f;
  118. buffer[j][i] = encodedSample;
  119. samples += 4;
  120. }
  121. }
  122. }
  123. else
  124. assert(false);
  125. // Signal how many frames were written
  126. vorbis_analysis_wrote(&mVorbisState, numFrames);
  127. writeBlocks();
  128. }
  129. void OAOggVorbisWriter::writeBlocks()
  130. {
  131. // Create a new block
  132. vorbis_block block;
  133. vorbis_block_init(&mVorbisState, &block);
  134. while (vorbis_analysis_blockout(&mVorbisState, &block) == 1)
  135. {
  136. // Analyze and determine optimal bitrate
  137. vorbis_analysis(&block, nullptr);
  138. vorbis_bitrate_addblock(&block);
  139. // Write block into ogg packets
  140. ogg_packet packet;
  141. while (vorbis_bitrate_flushpacket(&mVorbisState, &packet))
  142. {
  143. ogg_stream_packetin(&mOggState, &packet);
  144. // If new page, write it to the internal buffer
  145. ogg_page page;
  146. while (ogg_stream_flush(&mOggState, &page) > 0)
  147. {
  148. WRITE_TO_BUFFER(page.header, page.header_len);
  149. WRITE_TO_BUFFER(page.body, page.body_len);
  150. }
  151. }
  152. }
  153. // Clear the allocated block
  154. vorbis_block_clear(&block);
  155. }
  156. void OAOggVorbisWriter::flush()
  157. {
  158. if (mBufferOffset > 0 && mWriteCallback != nullptr)
  159. mWriteCallback(mBuffer, mBufferOffset);
  160. mBufferOffset = 0;
  161. }
  162. void OAOggVorbisWriter::close()
  163. {
  164. // Mark end of data and flush any remaining data in the buffers
  165. vorbis_analysis_wrote(&mVorbisState, 0);
  166. writeBlocks();
  167. flush();
  168. ogg_stream_clear(&mOggState);
  169. vorbis_dsp_clear(&mVorbisState);
  170. vorbis_info_clear(&mVorbisInfo);
  171. }
  172. UINT8* OAOggVorbisWriter::PCMToOggVorbis(UINT8* samples, const AudioFileInfo& info, UINT32& size)
  173. {
  174. struct EncodedBlock
  175. {
  176. UINT8* data;
  177. UINT32 size;
  178. };
  179. Vector<EncodedBlock> blocks;
  180. UINT32 totalEncodedSize = 0;
  181. auto writeCallback = [&](UINT8* buffer, UINT32 size)
  182. {
  183. EncodedBlock newBlock;
  184. newBlock.data = bs_frame_alloc(size);
  185. newBlock.size = size;
  186. memcpy(newBlock.data, buffer, size);
  187. blocks.push_back(newBlock);
  188. totalEncodedSize += size;
  189. };
  190. bs_frame_mark();
  191. OAOggVorbisWriter writer;
  192. writer.open(writeCallback, info.sampleRate, info.bitDepth, info.numChannels);
  193. writer.write(samples, info.numSamples);
  194. writer.close();
  195. UINT8* outSampleBuffer = (UINT8*)bs_alloc(totalEncodedSize);
  196. UINT32 offset = 0;
  197. for (auto& block : blocks)
  198. {
  199. memcpy(outSampleBuffer + offset, block.data, block.size);
  200. offset += block.size;
  201. bs_frame_free(block.data);
  202. }
  203. bs_frame_clear();
  204. size = totalEncodedSize;
  205. return outSampleBuffer;
  206. }
  207. #undef WRITE_TO_BUFFER
  208. }