Sound.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. /******************************************************************************/
  2. #include "stdafx.h"
  3. /******************************************************************************/
  4. SoundEditor SoundEdit;
  5. /******************************************************************************/
  6. /******************************************************************************/
  7. ::SoundEditor::ImportAs SoundEditor::Import_as[]=
  8. {
  9. {"Original", SOUND_NONE, -1},
  10. {"Vorbis, auto BitRate", SOUND_SND_VORBIS, 0},
  11. {"Opus, auto BitRate", SOUND_SND_OPUS, 0},
  12. {"Uncompressed", SOUND_WAV, -1},
  13. {null, SOUND_NONE, 0},
  14. //{"Original, 32 Kbps relative BitRate", SOUND_NONE, 32}, Vorbis and Opus disabled
  15. {"Original, 48 Kbps relative BitRate", SOUND_NONE, 48},
  16. {"Original, 64 Kbps relative BitRate", SOUND_NONE, 64},
  17. {"Original, 80 Kbps relative BitRate", SOUND_NONE, 80},
  18. {"Original, 96 Kbps relative BitRate", SOUND_NONE, 96},
  19. {"Original, 112 Kbps relative BitRate", SOUND_NONE, 112},
  20. {"Original, 128 Kbps relative BitRate", SOUND_NONE, 128},
  21. {null, SOUND_NONE, 0},
  22. {"Vorbis, original BitRate", SOUND_SND_VORBIS, -1},
  23. //{"Vorbis, 32 Kbps relative BitRate", SOUND_SND_VORBIS, 32}, // only aoTuV
  24. {"Vorbis, 48 Kbps relative BitRate", SOUND_SND_VORBIS, 48},
  25. {"Vorbis, 64 Kbps relative BitRate", SOUND_SND_VORBIS, 64},
  26. {"Vorbis, 80 Kbps relative BitRate", SOUND_SND_VORBIS, 80},
  27. {"Vorbis, 96 Kbps relative BitRate", SOUND_SND_VORBIS, 96},
  28. {"Vorbis, 112 Kbps relative BitRate", SOUND_SND_VORBIS, 112},
  29. {"Vorbis, 128 Kbps relative BitRate", SOUND_SND_VORBIS, 128},
  30. {null, SOUND_NONE, 0},
  31. {"Opus, original BitRate", SOUND_SND_OPUS, -1},
  32. //{"Opus, 32 Kbps relative BitRate", SOUND_SND_OPUS, 32}, very bad quality
  33. {"Opus, 48 Kbps relative BitRate", SOUND_SND_OPUS, 48},
  34. {"Opus, 64 Kbps relative BitRate", SOUND_SND_OPUS, 64},
  35. {"Opus, 80 Kbps relative BitRate", SOUND_SND_OPUS, 80},
  36. {"Opus, 96 Kbps relative BitRate", SOUND_SND_OPUS, 96},
  37. {"Opus, 112 Kbps relative BitRate", SOUND_SND_OPUS, 112},
  38. {"Opus, 128 Kbps relative BitRate", SOUND_SND_OPUS, 128},
  39. };
  40. int SoundEditor::ImportAsElms=Elms(Import_as);
  41. /******************************************************************************/
  42. void SoundEditor::Locate(SoundEditor &editor) {Proj.elmLocate(editor.elm_id);}
  43. void SoundEditor::Seek(SoundEditor &editor) {editor.seek_pos=editor.progress(); editor.seek_set=true;}
  44. void SoundEditor::Play(SoundEditor &editor)
  45. {
  46. if(editor.sound.playing())editor.sound.stop();else
  47. {
  48. if(!editor.sound.size() && editor.elm)editor.sound.create(Proj.gamePath(*editor.elm), editor.loop, editor.volume);
  49. if(editor.seek_set){editor.seek_set=false; editor.sound.frac(editor.seek_pos);}
  50. editor.sound.play();
  51. }
  52. }
  53. void SoundEditor::SetImportAs(SoundEditor &editor, C Str &t)
  54. {
  55. int i=TextInt(t); if(InRange(i, Import_as))
  56. {
  57. C ImportAs &ia=Import_as[i];
  58. Proj.soundImportAs(editor.elm_id, ia.codec, ia.bit_rate);
  59. }
  60. }
  61. Str SoundEditor::GetImportAs(C SoundEditor &editor)
  62. {
  63. if(editor.elm)
  64. {
  65. Mems<Edit::FileParams> files=Edit::FileParams::Decode(editor.elm->srcFile());
  66. if(files.elms())
  67. {
  68. SOUND_CODEC codec=SOUND_NONE; if(C TextParam *param=files[0].findParam("codec"))codec=TextSoundCodec(param->value);
  69. int rel_bit_rate=-1 ; C TextParam *rbr=files[0].findParam("relBitRate"); if(!rbr)rbr=files[0].findParam("relativeBitRate"); if(rbr)rel_bit_rate=rbr->asInt();
  70. int best=-1, bit_rate_d=0;
  71. REPA(Import_as)
  72. {
  73. C ImportAs &ia=Import_as[i]; if(ia.name && ia.codec==codec && Sign(rel_bit_rate)==Sign(ia.bit_rate))
  74. {
  75. int d=Abs(ia.bit_rate-rel_bit_rate);
  76. if(best<0 || d<bit_rate_d){best=i; bit_rate_d=d;}
  77. }
  78. }
  79. return best;
  80. }
  81. }
  82. return S;
  83. }
  84. void SoundEditor::ApplyVol(SoundEditor &editor)
  85. {
  86. if(editor.elm && editor.vol)
  87. {
  88. flt volume=TextFlt(editor.vol->textline()); // use textline instead of editor.volume because that one is clamped to 0..1
  89. Proj.mulSoundVolume(editor.elm_id, volume);
  90. editor.vol->set(1);
  91. }
  92. }
  93. void SoundEditor::create()
  94. {
  95. import_as=&add("Import as", MemberDesc().setFunc(GetImportAs, SetImportAs)).setEnum();
  96. #if 1 // use menu to disable clicking on null strings
  97. Node<MenuElm> n; FREPA(Import_as)n.New().create(Import_as[i].name);
  98. import_as->combobox.setData(n);
  99. #else
  100. ListColumn lc[]=
  101. {
  102. ListColumn(MEMBER(ImportAs, name), LCW_MAX_DATA_PARENT, S),
  103. };
  104. import_as.combobox.setColumns(lc, Elms(lc)).setData(Import_as, Elms(Import_as));
  105. #endif
  106. length =&add();
  107. channels=&add();
  108. freq =&add();
  109. kbps =&add();
  110. size =&add();
  111. codec =&add();
  112. add("Loop" , MemberDesc(MEMBER(SoundEditor, loop)));
  113. vol =&add("Volume", MemberDesc(MEMBER(SoundEditor, volume))).range(0, 1);
  114. autoData(this);
  115. Rect r=::PropWin::create("Sound Player", Vec2(0.02f, -0.02f), 0.036f, 0.043f, 0.15f); button[2].func(HideProjAct, SCAST(GuiObj, T)).show();
  116. import_as->combobox.resize(Vec2(0.33f, 0));
  117. rect(Rect_C(0, 0, 0.67f, 0.481f));
  118. r.set(r.max.x, -clientHeight(), clientWidth(), 0); r.extend(-0.04f);
  119. T+=play .create(Rect_U(r.up()+Vec2(0, -0.05f), 0.23f, 0.055f)).func(Play, T);
  120. T+=progress .create(Rect_U(play .rect().down()-Vec2(0, 0.030f), r.w(), 0.06f)).func(Seek, T);
  121. T+=locate .create(Rect_U(progress.rect().down()-Vec2(0, 0.030f), 0.15f, 0.055f), "Locate").func(Locate, T).desc("Locate this element in the Project");
  122. T+=apply_vol.create(Rect_LU(vol->button.rect().ru()+Vec2(vol->button.rect().h(), 0), 0.11f, vol->button.rect().h()), "Apply").func(ApplyVol, T).focusable(false).desc("Apply volume scale on the sound file");
  123. }
  124. void SoundEditor::update(bool set_frac)
  125. {
  126. if(set_frac)progress.set(sound.frac(), QUIET);
  127. play.text=(sound.playing() ? "Stop" : "Play");
  128. sound.loop(loop).volume(volume);
  129. }
  130. void SoundEditor::setInfo()
  131. {
  132. toGui();
  133. if(length )length ->name.set(S+"Length: " +TextReal(sound.length(), -2)+'s');
  134. if(channels)channels->name.set(S+"Channels: " +((sound.channels()==1) ? S+"Mono" : (sound.channels()==2) ? S+"Stereo" : S+sound.channels()));
  135. if(freq )freq ->name.set(S+"Frequency: "+sound.frequency());
  136. if(kbps )kbps ->name.set(S+"Kbps: " +DivRound(sound.bitRate(), 1000));
  137. if(size )size ->name.set(S+"Size: " +FileSize(FSize(sound.name())));
  138. if(codec )codec ->name.set(S+"Codec: " +sound.codecName());
  139. }
  140. void SoundEditor::update(C GuiPC &gpc)
  141. {
  142. if(seek_set && sound.size())
  143. {
  144. REPA(MT)if(MT.b(i) && MT.guiObj(i)==&progress)goto setting; // check if there's still an input editing the progress
  145. seek_set=false; sound.frac(seek_pos);
  146. setting:;
  147. }
  148. if(gpc.visible && visible())update(!seek_set);
  149. ::EE::ClosableWindow::update(gpc); // call after 'update' so progress value will be recent once 'Seek' is called
  150. }
  151. void SoundEditor::draw(C GuiPC &gpc)
  152. {
  153. ::EE::Window::draw(gpc);
  154. if(elm && import_as->combobox.contains(Gui.ms()) && Proj.invalidSrc(elm->srcFile())) // if source was not found, then draw exclamation
  155. {
  156. D.clip(gpc.clip);
  157. Proj.exclamation->drawFit(Rect_LD(import_as->combobox.rect().min+clientRect().lu()+gpc.offset, import_as->combobox.rect().h()));
  158. }
  159. }
  160. SoundEditor& SoundEditor::hide(){set(null); ::PropWin::hide(); return T;}
  161. void SoundEditor::flush()
  162. {
  163. if(elm && changed)
  164. {
  165. if(ElmSound *data=elm->soundData())data->newVer(); // modify just before saving/sending in case we've received data from server after edit
  166. Preview.elmChanged(elm->id);
  167. }
  168. changed=false;
  169. }
  170. void SoundEditor::setChanged()
  171. {
  172. if(elm)
  173. {
  174. changed=true;
  175. if(ElmSound *data=elm->soundData())data->newVer();
  176. }
  177. }
  178. void SoundEditor::set(Elm *elm)
  179. {
  180. if(elm && elm->type!=ELM_SOUND)elm=null;
  181. if(T.elm!=elm)
  182. {
  183. flush();
  184. T.elm =elm;
  185. T.elm_id=(elm ? elm->id : UIDZero);
  186. bool play=sound.playing();
  187. sound.close(); if(elm)sound.create(Proj.gamePath(*elm), loop, volume); // always close to wait for file handle release
  188. if(play)sound.play();
  189. update(); setInfo();
  190. Proj.refresh(false, false);
  191. visible(T.elm!=null).moveToTop();
  192. }
  193. }
  194. void SoundEditor::activate(Elm *elm) {set(elm); if(T.elm)::EE::GuiObj::activate();}
  195. void SoundEditor::toggle(Elm *elm) {if(elm==T.elm)elm=null; set(elm);}
  196. void SoundEditor::closeElm(C UID &elm_id)
  197. {
  198. if(elm && elm->id==elm_id)
  199. {
  200. sound.close();
  201. }
  202. }
  203. void SoundEditor::elmChanged(C UID &elm_id)
  204. {
  205. if(elm && elm->id==elm_id)
  206. {
  207. sound.create(Proj.gamePath(*elm), loop, volume);
  208. if(play.text=="stop")sound.frac(progress()).play(); // if was playing
  209. update(); setInfo();
  210. }
  211. }
  212. void SoundEditor::erasing(C UID &elm_id) {if(elm && elm->id==elm_id)set(null);}
  213. SoundEditor::SoundEditor() : elm_id(UIDZero), elm(null), changed(false), loop(false), seek_set(false), volume(1), seek_pos(0), import_as(null), length(null), channels(null), freq(null), kbps(null), size(null), codec(null), vol(null) {}
  214. /******************************************************************************/