VorbisEncoder.cpp 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /******************************************************************************
  2. Keep this as a separate file, because if the functions are not used,
  3. then they won't be linked, and app size won't be increased.
  4. Because it's linked separately, its name can't include spaces (due to Android building toolchain).
  5. /******************************************************************************/
  6. #include "stdafx.h"
  7. #if SUPPORT_VORBIS_ENC
  8. #include "../../../ThirdPartyLibs/begin.h"
  9. #include "../../../ThirdPartyLibs/Vorbis/include/vorbis/vorbisenc.h"
  10. #include "../../../ThirdPartyLibs/end.h"
  11. /******************************************************************************/
  12. namespace EE{
  13. /******************************************************************************/
  14. // OGG VORBIS
  15. /******************************************************************************/
  16. struct _OggVorbisEncoder
  17. {
  18. ogg_stream_state os;
  19. ogg_page og;
  20. ogg_packet op;
  21. vorbis_info vi;
  22. vorbis_comment vc;
  23. vorbis_dsp_state vd;
  24. vorbis_block vb;
  25. File *f;
  26. void zero() {Zero(T);}
  27. _OggVorbisEncoder() {zero();}
  28. ~_OggVorbisEncoder() {del();}
  29. void del()
  30. {
  31. ogg_stream_clear (&os);
  32. vorbis_block_clear (&vb);
  33. vorbis_dsp_clear (&vd);
  34. vorbis_comment_clear(&vc);
  35. vorbis_info_clear (&vi);
  36. zero();
  37. }
  38. Bool init(File &f, Int frequency, Int channels, Flt quality)
  39. {
  40. // quality = -0.1->45, 0.0->64, 0.1->80, 0.2->96, 0.3->112, 0.4->128, 0.5->160, 0.6->192, 0.7->224, 0.8->256, 0.9->320, 1.0->500
  41. del();
  42. if(frequency>0 && channels>=1 && channels<=2)
  43. {
  44. vorbis_info_init (&vi);
  45. if(vorbis_encode_init_vbr(&vi, channels, frequency, Mid(quality, -0.1f, 1.0f)))goto error; // may fail if quality is out of range
  46. vorbis_comment_init (&vc);
  47. vorbis_analysis_init(&vd, &vi);
  48. vorbis_block_init (&vd, &vb);
  49. ogg_stream_init (&os, Random());
  50. ogg_packet header, header_comm, header_code;
  51. vorbis_analysis_headerout(&vd, &vc, &header, &header_comm, &header_code);
  52. ogg_stream_packetin (&os, &header);
  53. ogg_stream_packetin (&os, &header_comm);
  54. ogg_stream_packetin (&os, &header_code);
  55. for(;;)
  56. {
  57. if(!ogg_stream_flush(&os, &og))break;
  58. f.put(og.header, og.header_len);
  59. f.put(og. body, og. body_len);
  60. }
  61. if(f.ok()){T.f=&f; return true;}
  62. }
  63. error:
  64. del(); return false;
  65. }
  66. Bool encodeEx(CPtr data, Int size)
  67. {
  68. if(f)
  69. {
  70. Int samples=size/(2*vi.channels);
  71. if(Flt **buffer=vorbis_analysis_buffer(&vd, samples))
  72. {
  73. I16 *d=(I16*)data;
  74. if(vi.channels==2)REP(samples)
  75. {
  76. buffer[0][i]=d[i*2 ]/32768.0f;
  77. buffer[1][i]=d[i*2+1]/32768.0f;
  78. }else REP(samples)
  79. {
  80. buffer[0][i]=d[i]/32768.0f;
  81. }
  82. vorbis_analysis_wrote(&vd, samples);
  83. for(Int eos=0; vorbis_analysis_blockout(&vd, &vb)==1; )
  84. {
  85. vorbis_analysis(&vb, null);
  86. vorbis_bitrate_addblock(&vb);
  87. for(; vorbis_bitrate_flushpacket(&vd, &op); )
  88. {
  89. ogg_stream_packetin(&os, &op);
  90. for(; !eos; )
  91. {
  92. if(!ogg_stream_pageout(&os, &og))break;
  93. f->put(og.header, og.header_len);
  94. f->put(og. body, og. body_len);
  95. if(ogg_page_eos(&og))eos=1;
  96. }
  97. }
  98. }
  99. return true;
  100. }
  101. }
  102. return false;
  103. }
  104. Bool encode(CPtr data, Int size)
  105. {
  106. for(Byte *d=(Byte*)data; size>0; ) // encode in chunks, because: vorbis compressor will crash when passing a big size, vorbis compressor allocates a temporary buffer for every data passed
  107. {
  108. Int write=Min(size, 65536);
  109. if(!encodeEx(d, write))return false;
  110. d +=write;
  111. size-=write;
  112. }
  113. return true;
  114. }
  115. Bool finish()
  116. {
  117. if(f)
  118. {
  119. Bool ok=encodeEx(null, 0); // encode end of stream, use 'encodeEx' because 'encode' will do nothing for 0 size
  120. f=null;
  121. return ok;
  122. }
  123. return true;
  124. }
  125. };
  126. /******************************************************************************/
  127. OggVorbisEncoder& OggVorbisEncoder::del()
  128. {
  129. if(_encoder)
  130. {
  131. ((_OggVorbisEncoder*)_encoder)->finish();
  132. Delete((_OggVorbisEncoder*&)_encoder);
  133. }
  134. return T;
  135. }
  136. Bool OggVorbisEncoder::create(File &f, Int frequency, Int channels, Flt quality)
  137. {
  138. if(_encoder)
  139. {
  140. ((_OggVorbisEncoder*)_encoder)->finish();
  141. }else
  142. {
  143. New((_OggVorbisEncoder*&)_encoder);
  144. }
  145. if(((_OggVorbisEncoder*)_encoder)->init(f, frequency, channels, quality))return true;
  146. Delete((_OggVorbisEncoder*&)_encoder);
  147. return false;
  148. }
  149. Bool OggVorbisEncoder::encode(CPtr data, Int size)
  150. {
  151. if(!size)return true;
  152. if(!data || size<=0 || !_encoder)return false;
  153. return ((_OggVorbisEncoder*)_encoder)->encode(data, size);
  154. }
  155. Bool OggVorbisEncoder::finish()
  156. {
  157. return _encoder ? ((_OggVorbisEncoder*)_encoder)->finish() : true;
  158. }
  159. /******************************************************************************/
  160. // ESENTHEL SND VORBIS
  161. /******************************************************************************
  162. Vorbis generates packets with variable frame size (not constant like Opus)
  163. -therefore both indexes would need to be saved (packet source position and packet size)
  164. -when both as U16 are saved, then generated file is larger than OGG Vorbis
  165. -some other technique could be figured out, for example store cmpIntV as deltas, however that would introduce more complexity and provide results similar to OGG
  166. -because of that, SndVorbisEncoder is not finished, and OGG Vorbis is recommended instead
  167. /******************************************************************************/
  168. #if 0 // not finished yet
  169. struct _SndVorbisEncoder
  170. {
  171. ogg_packet op;
  172. vorbis_info vi;
  173. vorbis_dsp_state vd;
  174. vorbis_block vb;
  175. File *f;
  176. Long pos;
  177. void zero() {Zero(op); Zero(vi); Zero(vd); Zero(vb); f=null; pos=0;}
  178. _SndVorbisEncoder() {zero();}
  179. ~_SndVorbisEncoder() {del();}
  180. void del()
  181. {
  182. vorbis_block_clear(&vb);
  183. vorbis_dsp_clear (&vd);
  184. vorbis_info_clear (&vi);
  185. zero();
  186. }
  187. Bool init(File &f, Long samples, Int frequency, Int channels, Flt quality)
  188. {
  189. // quality = -0.1->45, 0.0->64, 0.1->80, 0.2->96, 0.3->112, 0.4->128, 0.5->160, 0.6->192, 0.7->224, 0.8->256, 0.9->320, 1.0->500
  190. del();
  191. if(frequency>0 && channels>=1 && channels<=2)
  192. {
  193. vorbis_info_init (&vi);
  194. if(vorbis_encode_init_vbr(&vi, channels, frequency, quality))goto error;
  195. vorbis_analysis_init(&vd, &vi);
  196. vorbis_block_init (&vd, &vb);
  197. if(SaveSndHeader(f, SND_VORBIS, channels, frequency, samples))
  198. {
  199. T.f=&f;
  200. return true;
  201. }
  202. }
  203. error:
  204. del(); return false;
  205. }
  206. Bool encodeEx(CPtr data, Int size)
  207. {
  208. if(f)
  209. {
  210. Int samples=size/(2*vi.channels);
  211. if(Flt **buffer=vorbis_analysis_buffer(&vd, samples))
  212. {
  213. I16 *d=(I16*)data;
  214. if(vi.channels==2)REP(samples)
  215. {
  216. buffer[0][i]=d[i*2 ]/32768.0f;
  217. buffer[1][i]=d[i*2+1]/32768.0f;
  218. }else REP(samples)
  219. {
  220. buffer[0][i]=d[i]/32768.0f;
  221. }
  222. vorbis_analysis_wrote(&vd, samples);
  223. for(; vorbis_analysis_blockout(&vd, &vb)==1; )
  224. {
  225. vorbis_analysis(&vb, null);
  226. vorbis_bitrate_addblock(&vb);
  227. for(; vorbis_bitrate_flushpacket(&vd, &op); )
  228. {
  229. UInt samples=op.granulepos-pos; pos=op.granulepos;
  230. f->putUShort(samples);
  231. f->putUShort(op.bytes);
  232. if(!f->put(op.packet, op.bytes))return false;
  233. }
  234. }
  235. return true;
  236. }
  237. }
  238. return false;
  239. }
  240. Bool encode(CPtr data, Int size)
  241. {
  242. for(Byte *d=(Byte*)data; size>0; ) // encode in chunks, because: vorbis compressor will crash when passing a big size, vorbis compressor allocates a temporary buffer for every data passed
  243. {
  244. Int write=Min(size, 65536);
  245. if(!encodeEx(d, write))return false;
  246. d +=write;
  247. size-=write;
  248. }
  249. return true;
  250. }
  251. Bool finish()
  252. {
  253. if(f)
  254. {
  255. Bool ok=encodeEx(null, 0); // encode end of stream, use 'encodeEx' because 'encode' will do nothing for 0 size
  256. f=null;
  257. return ok;
  258. }
  259. return true;
  260. }
  261. };
  262. /******************************************************************************/
  263. SndVorbisEncoder& SndVorbisEncoder::del()
  264. {
  265. if(_encoder)
  266. {
  267. ((_SndVorbisEncoder*)_encoder)->finish();
  268. Delete((_SndVorbisEncoder*&)_encoder);
  269. }
  270. return T;
  271. }
  272. Bool SndVorbisEncoder::create(File &f, Long samples, Int frequency, Int channels, Flt quality)
  273. {
  274. if(_encoder)
  275. {
  276. ((_SndVorbisEncoder*)_encoder)->finish();
  277. }else
  278. {
  279. New((_SndVorbisEncoder*&)_encoder);
  280. }
  281. if(((_SndVorbisEncoder*)_encoder)->init(f, samples, frequency, channels, quality))return true;
  282. Delete((_SndVorbisEncoder*&)_encoder);
  283. return false;
  284. }
  285. Bool SndVorbisEncoder::encode(CPtr data, Int size)
  286. {
  287. if(!size)return true;
  288. if(!data || size<=0 || !_encoder)return false;
  289. return ((_SndVorbisEncoder*)_encoder)->encode(data, size);
  290. }
  291. Bool SndVorbisEncoder::finish()
  292. {
  293. return _encoder ? ((_SndVorbisEncoder*)_encoder)->finish() : true;
  294. }
  295. #endif
  296. /******************************************************************************/
  297. }
  298. #endif
  299. /******************************************************************************/