TimeSoundEditor.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719
  1. /* TimeSoundEditor.cpp
  2. *
  3. * Copyright (C) 1992-2018 Paul Boersma
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "NUM2.h"
  19. #include "TimeSoundEditor.h"
  20. #include "EditorM.h"
  21. #include "../kar/UnicodeData.h"
  22. #include "enums_getText.h"
  23. #include "TimeSoundEditor_enums.h"
  24. #include "enums_getValue.h"
  25. #include "TimeSoundEditor_enums.h"
  26. Thing_implement (TimeSoundEditor, FunctionEditor, 0);
  27. #include "prefs_define.h"
  28. #include "TimeSoundEditor_prefs.h"
  29. #include "prefs_install.h"
  30. #include "TimeSoundEditor_prefs.h"
  31. #include "prefs_copyToInstance.h"
  32. #include "TimeSoundEditor_prefs.h"
  33. /********** Thing methods **********/
  34. void structTimeSoundEditor :: v_destroy () noexcept {
  35. if (our d_ownSound)
  36. forget (our d_sound.data);
  37. NUMvector_free (our d_sound.muteChannels, 1);
  38. TimeSoundEditor_Parent :: v_destroy ();
  39. }
  40. void structTimeSoundEditor :: v_info () {
  41. TimeSoundEditor_Parent :: v_info ();
  42. /* Sound flags: */
  43. MelderInfo_writeLine (U"Sound scaling strategy: ", kTimeSoundEditor_scalingStrategy_getText (our p_sound_scalingStrategy));
  44. }
  45. /***** FILE MENU *****/
  46. static void menu_cb_DrawVisibleSound (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  47. EDITOR_FORM (U"Draw visible sound", nullptr)
  48. my v_form_pictureWindow (cmd);
  49. LABEL (U"Sound:")
  50. BOOLEAN (preserveTimes, U"Preserve times", my default_picture_preserveTimes ());
  51. REAL (bottom, U"left Vertical range", my default_picture_bottom ())
  52. REAL (top, U"right Vertical range", my default_picture_top ())
  53. my v_form_pictureMargins (cmd);
  54. my v_form_pictureSelection (cmd);
  55. BOOLEAN (garnish, U"Garnish", my default_picture_garnish ());
  56. EDITOR_OK
  57. my v_ok_pictureWindow (cmd);
  58. SET_BOOLEAN (preserveTimes, my pref_picture_preserveTimes ())
  59. SET_REAL (bottom, my pref_picture_bottom ())
  60. SET_REAL (top, my pref_picture_top ())
  61. my v_ok_pictureMargins (cmd);
  62. my v_ok_pictureSelection (cmd);
  63. SET_BOOLEAN (garnish, my pref_picture_garnish ())
  64. EDITOR_DO
  65. my v_do_pictureWindow (cmd);
  66. my pref_picture_preserveTimes () = preserveTimes;
  67. my pref_picture_bottom () = bottom;
  68. my pref_picture_top () = top;
  69. my v_do_pictureMargins (cmd);
  70. my v_do_pictureSelection (cmd);
  71. my pref_picture_garnish () = garnish;
  72. if (! my d_longSound.data && ! my d_sound.data)
  73. Melder_throw (U"There is no sound to draw.");
  74. autoSound publish = my d_longSound.data ?
  75. LongSound_extractPart (my d_longSound.data, my startWindow, my endWindow, my pref_picture_preserveTimes ()) :
  76. Sound_extractPart (my d_sound.data, my startWindow, my endWindow, kSound_windowShape::RECTANGULAR, 1.0, my pref_picture_preserveTimes ());
  77. Editor_openPraatPicture (me);
  78. Sound_draw (publish.get(), my pictureGraphics, 0.0, 0.0, my pref_picture_bottom (), my pref_picture_top (),
  79. my pref_picture_garnish (), U"Curve");
  80. FunctionEditor_garnish (me);
  81. Editor_closePraatPicture (me);
  82. EDITOR_END
  83. }
  84. static void menu_cb_DrawSelectedSound (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  85. EDITOR_FORM (U"Draw selected sound", nullptr)
  86. my v_form_pictureWindow (cmd);
  87. LABEL (U"Sound:")
  88. BOOLEAN (preserveTimes, U"Preserve times", my default_picture_preserveTimes ());
  89. REAL (bottom, U"left Vertical range", my default_picture_bottom ());
  90. REAL (top, U"right Vertical range", my default_picture_top ());
  91. my v_form_pictureMargins (cmd);
  92. BOOLEAN (garnish, U"Garnish", my default_picture_garnish ());
  93. EDITOR_OK
  94. my v_ok_pictureWindow (cmd);
  95. SET_BOOLEAN (preserveTimes, my pref_picture_preserveTimes ());
  96. SET_REAL (bottom, my pref_picture_bottom ());
  97. SET_REAL (top, my pref_picture_top ());
  98. my v_ok_pictureMargins (cmd);
  99. SET_BOOLEAN (garnish, my pref_picture_garnish ());
  100. EDITOR_DO
  101. my v_do_pictureWindow (cmd);
  102. my pref_picture_preserveTimes () = preserveTimes;
  103. my pref_picture_bottom () = bottom;
  104. my pref_picture_top () = top;
  105. my v_do_pictureMargins (cmd);
  106. my pref_picture_garnish () = garnish;
  107. if (! my d_longSound.data && ! my d_sound.data)
  108. Melder_throw (U"There is no sound to draw.");
  109. autoSound publish = my d_longSound.data ?
  110. LongSound_extractPart (my d_longSound.data, my startSelection, my endSelection, my pref_picture_preserveTimes ()) :
  111. Sound_extractPart (my d_sound.data, my startSelection, my endSelection,
  112. kSound_windowShape::RECTANGULAR, 1.0, my pref_picture_preserveTimes ());
  113. Editor_openPraatPicture (me);
  114. Sound_draw (publish.get(), my pictureGraphics, 0.0, 0.0, my pref_picture_bottom (), my pref_picture_top (),
  115. my pref_picture_garnish (), U"Curve");
  116. Editor_closePraatPicture (me);
  117. EDITOR_END
  118. }
  119. static void do_ExtractSelectedSound (TimeSoundEditor me, bool preserveTimes) {
  120. autoSound extract;
  121. if (my endSelection <= my startSelection)
  122. Melder_throw (U"No selection.");
  123. if (my d_longSound.data) {
  124. extract = LongSound_extractPart (my d_longSound.data, my startSelection, my endSelection, preserveTimes);
  125. } else if (my d_sound.data) {
  126. extract = Sound_extractPart (my d_sound.data, my startSelection, my endSelection, kSound_windowShape::RECTANGULAR, 1.0, preserveTimes);
  127. }
  128. Editor_broadcastPublication (me, extract.move());
  129. }
  130. static void menu_cb_ExtractSelectedSound_timeFromZero (TimeSoundEditor me, EDITOR_ARGS_DIRECT) {
  131. do_ExtractSelectedSound (me, false);
  132. }
  133. static void menu_cb_ExtractSelectedSound_preserveTimes (TimeSoundEditor me, EDITOR_ARGS_DIRECT) {
  134. do_ExtractSelectedSound (me, true);
  135. }
  136. static void menu_cb_ExtractSelectedSound_windowed (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  137. EDITOR_FORM (U"Extract selected sound (windowed)", nullptr)
  138. WORD (name, U"Name", U"slice")
  139. OPTIONMENU_ENUM (kSound_windowShape, windowShape, U"Window shape", my default_extract_windowShape ())
  140. POSITIVE (relativeWidth, U"Relative width", my default_extract_relativeWidth ())
  141. BOOLEAN (preserveTimes, U"Preserve times", my default_extract_preserveTimes ())
  142. EDITOR_OK
  143. SET_ENUM (windowShape, kSound_windowShape, my pref_extract_windowShape ())
  144. SET_REAL (relativeWidth, my pref_extract_relativeWidth ())
  145. SET_BOOLEAN (preserveTimes, my pref_extract_preserveTimes ())
  146. EDITOR_DO
  147. Sound sound = my d_sound.data;
  148. Melder_assert (sound);
  149. my pref_extract_windowShape () = windowShape;
  150. my pref_extract_relativeWidth () = relativeWidth;
  151. my pref_extract_preserveTimes () = preserveTimes;
  152. autoSound extract = Sound_extractPart (sound, my startSelection, my endSelection, my pref_extract_windowShape (),
  153. my pref_extract_relativeWidth (), my pref_extract_preserveTimes ());
  154. Thing_setName (extract.get(), name);
  155. Editor_broadcastPublication (me, extract.move());
  156. EDITOR_END
  157. }
  158. static void menu_cb_ExtractSelectedSoundForOverlap (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  159. EDITOR_FORM (U"Extract selected sound for overlap)", nullptr)
  160. WORD (name, U"Name", U"slice")
  161. POSITIVE (overlap, U"Overlap (s)", my default_extract_overlap ())
  162. EDITOR_OK
  163. SET_REAL (overlap, my pref_extract_overlap ())
  164. EDITOR_DO
  165. Sound sound = my d_sound.data;
  166. Melder_assert (sound);
  167. my pref_extract_overlap () = overlap;
  168. autoSound extract = Sound_extractPartForOverlap (sound, my startSelection, my endSelection,
  169. my pref_extract_overlap ());
  170. Thing_setName (extract.get(), name);
  171. Editor_broadcastPublication (me, extract.move());
  172. EDITOR_END
  173. }
  174. static void do_write (TimeSoundEditor me, MelderFile file, int format, int numberOfBitsPerSamplePoint) {
  175. if (my startSelection >= my endSelection)
  176. Melder_throw (U"No samples selected.");
  177. if (my d_longSound.data) {
  178. LongSound_savePartAsAudioFile (my d_longSound.data, format, my startSelection, my endSelection, file, numberOfBitsPerSamplePoint);
  179. } else if (my d_sound.data) {
  180. Sound sound = my d_sound.data;
  181. double margin = 0.0;
  182. integer nmargin = Melder_ifloor (margin / sound -> dx);
  183. integer first, last, numberOfSamples = Sampled_getWindowSamples (sound,
  184. my startSelection, my endSelection, & first, & last) + nmargin * 2;
  185. first -= nmargin;
  186. last += nmargin;
  187. if (numberOfSamples) {
  188. autoSound save = Sound_create (sound -> ny, 0.0, numberOfSamples * sound -> dx, numberOfSamples, sound -> dx, 0.5 * sound -> dx);
  189. integer offset = first - 1;
  190. if (first < 1)
  191. first = 1;
  192. if (last > sound -> nx)
  193. last = sound -> nx;
  194. for (integer channel = 1; channel <= sound -> ny; channel ++) {
  195. for (integer i = first; i <= last; i ++)
  196. save -> z [channel] [i - offset] = sound -> z [channel] [i];
  197. }
  198. Sound_saveAsAudioFile (save.get(), file, format, numberOfBitsPerSamplePoint);
  199. }
  200. }
  201. }
  202. static void menu_cb_WriteWav (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  203. EDITOR_FORM_SAVE (U"Save selected sound as WAV file", nullptr)
  204. Melder_sprint (defaultName,300, my d_longSound.data ? my d_longSound.data -> name.get() : my d_sound.data -> name.get(), U".wav");
  205. EDITOR_DO_SAVE
  206. do_write (me, file, Melder_WAV, 16);
  207. EDITOR_END
  208. }
  209. static void menu_cb_SaveAs24BitWav (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  210. EDITOR_FORM_SAVE (U"Save selected sound as 24-bit WAV file", nullptr)
  211. Melder_assert (! my d_longSound.data && my d_sound.data);
  212. Melder_sprint (defaultName,300, my d_sound.data -> name.get(), U".wav");
  213. EDITOR_DO_SAVE
  214. do_write (me, file, Melder_WAV, 24);
  215. EDITOR_END
  216. }
  217. static void menu_cb_SaveAs32BitWav (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  218. EDITOR_FORM_SAVE (U"Save selected sound as 32-bit WAV file", nullptr)
  219. Melder_assert (! my d_longSound.data && my d_sound.data);
  220. Melder_sprint (defaultName,300, my d_sound.data -> name.get(), U".wav");
  221. EDITOR_DO_SAVE
  222. do_write (me, file, Melder_WAV, 32);
  223. EDITOR_END
  224. }
  225. static void menu_cb_WriteAiff (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  226. EDITOR_FORM_SAVE (U"Save selected sound as AIFF file", nullptr)
  227. Melder_sprint (defaultName,300, my d_longSound.data ? my d_longSound.data -> name.get() : my d_sound.data -> name.get(), U".aiff");
  228. EDITOR_DO_SAVE
  229. do_write (me, file, Melder_AIFF, 16);
  230. EDITOR_END
  231. }
  232. static void menu_cb_WriteAifc (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  233. EDITOR_FORM_SAVE (U"Save selected sound as AIFC file", nullptr)
  234. Melder_sprint (defaultName,300, my d_longSound.data ? my d_longSound.data -> name.get() : my d_sound.data -> name.get(), U".aifc");
  235. EDITOR_DO_SAVE
  236. do_write (me, file, Melder_AIFC, 16);
  237. EDITOR_END
  238. }
  239. static void menu_cb_WriteNextSun (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  240. EDITOR_FORM_SAVE (U"Save selected sound as NeXT/Sun file", nullptr)
  241. Melder_sprint (defaultName,300, my d_longSound.data ? my d_longSound.data -> name.get() : my d_sound.data -> name.get(), U".au");
  242. EDITOR_DO_SAVE
  243. do_write (me, file, Melder_NEXT_SUN, 16);
  244. EDITOR_END
  245. }
  246. static void menu_cb_WriteNist (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  247. EDITOR_FORM_SAVE (U"Save selected sound as NIST file", nullptr)
  248. Melder_sprint (defaultName,300, my d_longSound.data ? my d_longSound.data -> name.get() : my d_sound.data -> name.get(), U".nist");
  249. EDITOR_DO_SAVE
  250. do_write (me, file, Melder_NIST, 16);
  251. EDITOR_END
  252. }
  253. static void menu_cb_WriteFlac (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  254. EDITOR_FORM_SAVE (U"Save selected sound as FLAC file", nullptr)
  255. Melder_sprint (defaultName,300, my d_longSound.data ? my d_longSound.data -> name.get() : my d_sound.data -> name.get(), U".flac");
  256. EDITOR_DO_SAVE
  257. do_write (me, file, Melder_FLAC, 16);
  258. EDITOR_END
  259. }
  260. void structTimeSoundEditor :: v_createMenuItems_file_draw (EditorMenu menu) {
  261. EditorMenu_addCommand (menu, U"Draw to picture window:", GuiMenu_INSENSITIVE, menu_cb_DrawVisibleSound /* dummy */);
  262. if (our d_sound.data || our d_longSound.data) {
  263. EditorMenu_addCommand (menu, U"Draw visible sound...", 0, menu_cb_DrawVisibleSound);
  264. our drawButton = EditorMenu_addCommand (menu, U"Draw selected sound...", 0, menu_cb_DrawSelectedSound);
  265. }
  266. }
  267. void structTimeSoundEditor :: v_createMenuItems_file_extract (EditorMenu menu) {
  268. EditorMenu_addCommand (menu, U"Extract to objects window:", GuiMenu_INSENSITIVE, menu_cb_ExtractSelectedSound_preserveTimes /* dummy */);
  269. if (our d_sound.data || our d_longSound.data) {
  270. our publishPreserveButton = EditorMenu_addCommand (menu, U"Extract selected sound (preserve times)", 0, menu_cb_ExtractSelectedSound_preserveTimes);
  271. EditorMenu_addCommand (menu, U"Extract sound selection (preserve times)", Editor_HIDDEN, menu_cb_ExtractSelectedSound_preserveTimes);
  272. EditorMenu_addCommand (menu, U"Extract selection (preserve times)", Editor_HIDDEN, menu_cb_ExtractSelectedSound_preserveTimes);
  273. our publishButton = EditorMenu_addCommand (menu, U"Extract selected sound (time from 0)", 0, menu_cb_ExtractSelectedSound_timeFromZero);
  274. EditorMenu_addCommand (menu, U"Extract sound selection (time from 0)", Editor_HIDDEN, menu_cb_ExtractSelectedSound_timeFromZero);
  275. EditorMenu_addCommand (menu, U"Extract selection (time from 0)", Editor_HIDDEN, menu_cb_ExtractSelectedSound_timeFromZero);
  276. EditorMenu_addCommand (menu, U"Extract selection", Editor_HIDDEN, menu_cb_ExtractSelectedSound_timeFromZero);
  277. if (our d_sound.data) {
  278. our publishWindowButton = EditorMenu_addCommand (menu, U"Extract selected sound (windowed)...", 0, menu_cb_ExtractSelectedSound_windowed);
  279. EditorMenu_addCommand (menu, U"Extract windowed sound selection...", Editor_HIDDEN, menu_cb_ExtractSelectedSound_windowed);
  280. EditorMenu_addCommand (menu, U"Extract windowed selection...", Editor_HIDDEN, menu_cb_ExtractSelectedSound_windowed);
  281. our publishOverlapButton = EditorMenu_addCommand (menu, U"Extract selected sound for overlap...", 0, menu_cb_ExtractSelectedSoundForOverlap);
  282. }
  283. }
  284. }
  285. void structTimeSoundEditor :: v_createMenuItems_file_write (EditorMenu menu) {
  286. EditorMenu_addCommand (menu, U"Save to disk:", GuiMenu_INSENSITIVE, menu_cb_WriteWav /* dummy */);
  287. if (our d_sound.data || our d_longSound.data) {
  288. our writeWavButton = EditorMenu_addCommand (menu, U"Save selected sound as WAV file...", 0, menu_cb_WriteWav);
  289. EditorMenu_addCommand (menu, U"Write selected sound to WAV file...", Editor_HIDDEN, menu_cb_WriteWav);
  290. EditorMenu_addCommand (menu, U"Write sound selection to WAV file...", Editor_HIDDEN, menu_cb_WriteWav);
  291. EditorMenu_addCommand (menu, U"Write selection to WAV file...", Editor_HIDDEN, menu_cb_WriteWav);
  292. if (our d_sound.data) {
  293. our saveAs24BitWavButton = EditorMenu_addCommand (menu, U"Save selected sound as 24-bit WAV file...", 0, menu_cb_SaveAs24BitWav);
  294. our saveAs32BitWavButton = EditorMenu_addCommand (menu, U"Save selected sound as 32-bit WAV file...", 0, menu_cb_SaveAs32BitWav);
  295. }
  296. our writeAiffButton = EditorMenu_addCommand (menu, U"Save selected sound as AIFF file...", 0, menu_cb_WriteAiff);
  297. EditorMenu_addCommand (menu, U"Write selected sound to AIFF file...", Editor_HIDDEN, menu_cb_WriteAiff);
  298. EditorMenu_addCommand (menu, U"Write sound selection to AIFF file...", Editor_HIDDEN, menu_cb_WriteAiff);
  299. EditorMenu_addCommand (menu, U"Write selection to AIFF file...", Editor_HIDDEN, menu_cb_WriteAiff);
  300. our writeAifcButton = EditorMenu_addCommand (menu, U"Save selected sound as AIFC file...", 0, menu_cb_WriteAifc);
  301. EditorMenu_addCommand (menu, U"Write selected sound to AIFC file...", Editor_HIDDEN, menu_cb_WriteAifc);
  302. EditorMenu_addCommand (menu, U"Write sound selection to AIFC file...", Editor_HIDDEN, menu_cb_WriteAifc);
  303. EditorMenu_addCommand (menu, U"Write selection to AIFC file...", Editor_HIDDEN, menu_cb_WriteAifc);
  304. our writeNextSunButton = EditorMenu_addCommand (menu, U"Save selected sound as Next/Sun file...", 0, menu_cb_WriteNextSun);
  305. EditorMenu_addCommand (menu, U"Write selected sound to Next/Sun file...", Editor_HIDDEN, menu_cb_WriteNextSun);
  306. EditorMenu_addCommand (menu, U"Write sound selection to Next/Sun file...", Editor_HIDDEN, menu_cb_WriteNextSun);
  307. EditorMenu_addCommand (menu, U"Write selection to Next/Sun file...", Editor_HIDDEN, menu_cb_WriteNextSun);
  308. our writeNistButton = EditorMenu_addCommand (menu, U"Save selected sound as NIST file...", 0, menu_cb_WriteNist);
  309. EditorMenu_addCommand (menu, U"Write selected sound to NIST file...", Editor_HIDDEN, menu_cb_WriteNist);
  310. EditorMenu_addCommand (menu, U"Write sound selection to NIST file...", Editor_HIDDEN, menu_cb_WriteNist);
  311. EditorMenu_addCommand (menu, U"Write selection to NIST file...", Editor_HIDDEN, menu_cb_WriteNist);
  312. our writeFlacButton = EditorMenu_addCommand (menu, U"Save selected sound as FLAC file...", 0, menu_cb_WriteFlac);
  313. EditorMenu_addCommand (menu, U"Write selected sound to FLAC file...", Editor_HIDDEN, menu_cb_WriteFlac);
  314. EditorMenu_addCommand (menu, U"Write sound selection to FLAC file...", Editor_HIDDEN, menu_cb_WriteFlac);
  315. }
  316. }
  317. void structTimeSoundEditor :: v_createMenuItems_file (EditorMenu menu) {
  318. our TimeSoundEditor_Parent :: v_createMenuItems_file (menu);
  319. our v_createMenuItems_file_draw (menu);
  320. EditorMenu_addCommand (menu, U"-- after file draw --", 0, nullptr);
  321. our v_createMenuItems_file_extract (menu);
  322. EditorMenu_addCommand (menu, U"-- after file extract --", 0, nullptr);
  323. our v_createMenuItems_file_write (menu);
  324. EditorMenu_addCommand (menu, U"-- after file write --", 0, nullptr);
  325. }
  326. /********** QUERY MENU **********/
  327. static void menu_cb_SoundInfo (TimeSoundEditor me, EDITOR_ARGS_DIRECT) {
  328. Thing_info (my d_sound.data);
  329. }
  330. static void menu_cb_LongSoundInfo (TimeSoundEditor me, EDITOR_ARGS_DIRECT) {
  331. Thing_info (my d_longSound.data);
  332. }
  333. void structTimeSoundEditor :: v_createMenuItems_query_info (EditorMenu menu) {
  334. TimeSoundEditor_Parent :: v_createMenuItems_query_info (menu);
  335. if (our d_sound.data && our d_sound.data != data) {
  336. EditorMenu_addCommand (menu, U"Sound info", 0, menu_cb_SoundInfo);
  337. } else if (our d_longSound.data && our d_longSound.data != data) {
  338. EditorMenu_addCommand (menu, U"LongSound info", 0, menu_cb_LongSoundInfo);
  339. }
  340. }
  341. /********** VIEW MENU **********/
  342. static void menu_cb_soundScaling (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  343. EDITOR_FORM (U"Sound scaling", nullptr)
  344. OPTIONMENU_ENUM (kTimeSoundEditor_scalingStrategy, scalingStrategy,
  345. U"Scaling strategy", my default_sound_scalingStrategy ())
  346. LABEL (U"For \"fixed height\":")
  347. POSITIVE (height, U"Height", my default_sound_scaling_height ())
  348. LABEL (U"For \"fixed range\":")
  349. REAL (minimum, U"Minimum", my default_sound_scaling_minimum ())
  350. REAL (maximum, U"Maximum", my default_sound_scaling_maximum ())
  351. EDITOR_OK
  352. SET_ENUM (scalingStrategy, kTimeSoundEditor_scalingStrategy, my p_sound_scalingStrategy)
  353. SET_REAL (height, my p_sound_scaling_height)
  354. SET_REAL (minimum, my p_sound_scaling_minimum)
  355. SET_REAL (maximum, my p_sound_scaling_maximum)
  356. EDITOR_DO
  357. my pref_sound_scalingStrategy () = my p_sound_scalingStrategy = scalingStrategy;
  358. my pref_sound_scaling_height () = my p_sound_scaling_height = height;
  359. my pref_sound_scaling_minimum () = my p_sound_scaling_minimum = minimum;
  360. my pref_sound_scaling_maximum () = my p_sound_scaling_maximum = maximum;
  361. FunctionEditor_redraw (me);
  362. EDITOR_END
  363. }
  364. static void menu_cb_soundMuteChannels (TimeSoundEditor me, EDITOR_ARGS_FORM) {
  365. EDITOR_FORM (U"Mute channels", nullptr)
  366. TEXTFIELD (channels_string, U"Channels", U"2")
  367. EDITOR_OK
  368. EDITOR_DO
  369. integer numberOfChannels = my d_longSound.data ? my d_longSound.data -> numberOfChannels : my d_sound.data -> ny;
  370. integer numberOfElements;
  371. autoINTVEC channelNumber = NUMstring_getElementsOfRanges (channels_string, 5 * numberOfChannels, U"channel", false);
  372. bool *muteChannels = my d_sound.muteChannels;
  373. for (integer i = 1; i <= numberOfChannels; i ++)
  374. muteChannels [i] = false;
  375. for (integer i = 1; i <= numberOfElements; i++) {
  376. if (channelNumber [i] > 0 && channelNumber [i] <= numberOfChannels)
  377. muteChannels [channelNumber [i]] = true;
  378. }
  379. FunctionEditor_redraw (me);
  380. EDITOR_END
  381. }
  382. void structTimeSoundEditor :: v_createMenuItems_view (EditorMenu menu) {
  383. if (our d_sound.data || our d_longSound.data)
  384. our v_createMenuItems_view_sound (menu);
  385. TimeSoundEditor_Parent :: v_createMenuItems_view (menu);
  386. }
  387. void structTimeSoundEditor :: v_createMenuItems_view_sound (EditorMenu menu) {
  388. EditorMenu_addCommand (menu, U"Sound scaling...", 0, menu_cb_soundScaling);
  389. EditorMenu_addCommand (menu, U"Mute channels...", 0, menu_cb_soundMuteChannels);
  390. }
  391. void structTimeSoundEditor :: v_updateMenuItems_file () {
  392. Sampled sound;
  393. if (our d_sound.data) { // cannot do this with "?:", because d_sound.data and d_longSound.data have differemt types
  394. sound = our d_sound.data;
  395. } else {
  396. sound = our d_longSound.data;
  397. }
  398. if (! sound) return;
  399. integer first, last, selectedSamples = Sampled_getWindowSamples (sound, our startSelection, our endSelection, & first, & last);
  400. if (our drawButton) {
  401. GuiThing_setSensitive (our drawButton, selectedSamples != 0);
  402. GuiThing_setSensitive (our publishButton, selectedSamples != 0);
  403. GuiThing_setSensitive (our publishPreserveButton, selectedSamples != 0);
  404. if (our publishWindowButton)
  405. GuiThing_setSensitive (our publishWindowButton, selectedSamples != 0);
  406. if (our publishOverlapButton)
  407. GuiThing_setSensitive (our publishOverlapButton, selectedSamples != 0);
  408. }
  409. GuiThing_setSensitive (our writeWavButton, selectedSamples != 0);
  410. if (our saveAs24BitWavButton)
  411. GuiThing_setSensitive (our saveAs24BitWavButton, selectedSamples != 0);
  412. if (our saveAs32BitWavButton)
  413. GuiThing_setSensitive (our saveAs32BitWavButton, selectedSamples != 0);
  414. GuiThing_setSensitive (our writeAiffButton, selectedSamples != 0);
  415. GuiThing_setSensitive (our writeAifcButton, selectedSamples != 0);
  416. GuiThing_setSensitive (our writeNextSunButton, selectedSamples != 0);
  417. GuiThing_setSensitive (our writeNistButton, selectedSamples != 0);
  418. GuiThing_setSensitive (our writeFlacButton, selectedSamples != 0);
  419. }
  420. void TimeSoundEditor_drawSound (TimeSoundEditor me, double globalMinimum, double globalMaximum) {
  421. Sound sound = my d_sound.data;
  422. LongSound longSound = my d_longSound.data;
  423. Melder_assert (!! sound != !! longSound);
  424. integer numberOfChannels = ( sound ? sound -> ny : longSound -> numberOfChannels );
  425. bool cursorVisible = my startSelection == my endSelection && my startSelection >= my startWindow && my startSelection <= my endWindow;
  426. Graphics_setColour (my graphics.get(), Graphics_BLACK);
  427. bool fits;
  428. try {
  429. fits = sound ? true : LongSound_haveWindow (longSound, my startWindow, my endWindow);
  430. } catch (MelderError) {
  431. bool outOfMemory = !! str32str (Melder_getError (), U"memory");
  432. if (Melder_debug == 9) Melder_flushError (); else Melder_clearError ();
  433. Graphics_setWindow (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
  434. Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_HALF);
  435. Graphics_text (my graphics.get(), 0.5, 0.5, outOfMemory ? U"(out of memory)" : U"(cannot read sound file)");
  436. return;
  437. }
  438. if (! fits) {
  439. Graphics_setWindow (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
  440. Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_HALF);
  441. Graphics_text (my graphics.get(), 0.5, 0.5, U"(window too large; zoom in to see the data)");
  442. return;
  443. }
  444. integer first, last;
  445. if (Sampled_getWindowSamples (sound ? (Sampled) sound : (Sampled) longSound, my startWindow, my endWindow, & first, & last) <= 1) {
  446. Graphics_setWindow (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
  447. Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_HALF);
  448. Graphics_text (my graphics.get(), 0.5, 0.5, U"(zoom out to see the data)");
  449. return;
  450. }
  451. const integer numberOfVisibleChannels = ( numberOfChannels > 8 ? 8 : numberOfChannels );
  452. const integer firstVisibleChannel = my d_sound.channelOffset + 1;
  453. integer lastVisibleChannel = my d_sound.channelOffset + numberOfVisibleChannels;
  454. if (lastVisibleChannel > numberOfChannels)
  455. lastVisibleChannel = numberOfChannels;
  456. double maximumExtent = 0.0, visibleMinimum = 0.0, visibleMaximum = 0.0;
  457. if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::BY_WINDOW) {
  458. if (longSound)
  459. LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, firstVisibleChannel, & visibleMinimum, & visibleMaximum);
  460. else
  461. Matrix_getWindowExtrema (sound, first, last, firstVisibleChannel, firstVisibleChannel, & visibleMinimum, & visibleMaximum);
  462. for (integer ichan = firstVisibleChannel + 1; ichan <= lastVisibleChannel; ichan ++) {
  463. double visibleChannelMinimum, visibleChannelMaximum;
  464. if (longSound)
  465. LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, ichan, & visibleChannelMinimum, & visibleChannelMaximum);
  466. else
  467. Matrix_getWindowExtrema (sound, first, last, ichan, ichan, & visibleChannelMinimum, & visibleChannelMaximum);
  468. if (visibleChannelMinimum < visibleMinimum)
  469. visibleMinimum = visibleChannelMinimum;
  470. if (visibleChannelMaximum > visibleMaximum)
  471. visibleMaximum = visibleChannelMaximum;
  472. }
  473. maximumExtent = visibleMaximum - visibleMinimum;
  474. }
  475. for (integer ichan = firstVisibleChannel; ichan <= lastVisibleChannel; ichan ++) {
  476. double cursorFunctionValue = longSound ? 0.0 :
  477. Vector_getValueAtX (sound, 0.5 * (my startSelection + my endSelection), ichan, 70);
  478. /*
  479. * BUG: this will only work for mono or stereo, until Graphics_function16 handles quadro.
  480. */
  481. double ymin = (double) (numberOfVisibleChannels - ichan + my d_sound.channelOffset) / numberOfVisibleChannels;
  482. double ymax = (double) (numberOfVisibleChannels + 1 - ichan + my d_sound.channelOffset) / numberOfVisibleChannels;
  483. Graphics_Viewport vp = Graphics_insetViewport (my graphics.get(), 0.0, 1.0, ymin, ymax);
  484. bool horizontal = false;
  485. double minimum = sound ? globalMinimum : -1.0, maximum = sound ? globalMaximum : 1.0;
  486. if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::BY_WINDOW) {
  487. if (numberOfChannels > 2) {
  488. if (longSound) {
  489. LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, ichan, & minimum, & maximum);
  490. } else {
  491. Matrix_getWindowExtrema (sound, first, last, ichan, ichan, & minimum, & maximum);
  492. }
  493. if (maximumExtent > 0.0) {
  494. double middle = 0.5 * (minimum + maximum);
  495. minimum = middle - 0.5 * maximumExtent;
  496. maximum = middle + 0.5 * maximumExtent;
  497. }
  498. } else {
  499. minimum = visibleMinimum;
  500. maximum = visibleMaximum;
  501. }
  502. } else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::BY_WINDOW_AND_CHANNEL) {
  503. if (longSound) {
  504. LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, ichan, & minimum, & maximum);
  505. } else {
  506. Matrix_getWindowExtrema (sound, first, last, ichan, ichan, & minimum, & maximum);
  507. }
  508. } else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::FIXED_HEIGHT) {
  509. if (longSound) {
  510. LongSound_getWindowExtrema (longSound, my startWindow, my endWindow, ichan, & minimum, & maximum);
  511. } else {
  512. Matrix_getWindowExtrema (sound, first, last, ichan, ichan, & minimum, & maximum);
  513. }
  514. double channelExtent = my p_sound_scaling_height;
  515. double middle = 0.5 * (minimum + maximum);
  516. minimum = middle - 0.5 * channelExtent;
  517. maximum = middle + 0.5 * channelExtent;
  518. } else if (my p_sound_scalingStrategy == kTimeSoundEditor_scalingStrategy::FIXED_RANGE) {
  519. minimum = my p_sound_scaling_minimum;
  520. maximum = my p_sound_scaling_maximum;
  521. }
  522. if (minimum == maximum) {
  523. horizontal = true;
  524. minimum -= 1.0;
  525. maximum += 1.0;
  526. }
  527. Graphics_setWindow (my graphics.get(), my startWindow, my endWindow, minimum, maximum);
  528. if (horizontal) {
  529. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_HALF);
  530. double mid = 0.5 * (minimum + maximum);
  531. Graphics_text (my graphics.get(), my startWindow, mid, Melder_float (Melder_half (mid)));
  532. } else {
  533. if (! cursorVisible || isundef (cursorFunctionValue) || Graphics_dyWCtoMM (my graphics.get(), cursorFunctionValue - minimum) > 5.0) {
  534. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_BOTTOM);
  535. Graphics_text (my graphics.get(), my startWindow, minimum, Melder_float (Melder_half (minimum)));
  536. }
  537. if (! cursorVisible || isundef (cursorFunctionValue) || Graphics_dyWCtoMM (my graphics.get(), maximum - cursorFunctionValue) > 5.0) {
  538. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_TOP);
  539. Graphics_text (my graphics.get(), my startWindow, maximum, Melder_float (Melder_half (maximum)));
  540. }
  541. }
  542. if (minimum < 0 && maximum > 0 && ! horizontal) {
  543. Graphics_setWindow (my graphics.get(), 0.0, 1.0, minimum, maximum);
  544. if (! cursorVisible || isundef (cursorFunctionValue) || fabs (Graphics_dyWCtoMM (my graphics.get(), cursorFunctionValue - 0.0)) > 3.0) {
  545. Graphics_setTextAlignment (my graphics.get(), Graphics_RIGHT, Graphics_HALF);
  546. Graphics_text (my graphics.get(), 0.0, 0.0, U"0");
  547. }
  548. Graphics_setColour (my graphics.get(), Graphics_CYAN);
  549. Graphics_setLineType (my graphics.get(), Graphics_DOTTED);
  550. Graphics_line (my graphics.get(), 0.0, 0.0, 1.0, 0.0);
  551. Graphics_setLineType (my graphics.get(), Graphics_DRAWN);
  552. }
  553. /*
  554. * Garnish the drawing area of each channel.
  555. */
  556. Graphics_setWindow (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
  557. Graphics_setColour (my graphics.get(), Graphics_CYAN);
  558. Graphics_innerRectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
  559. Graphics_setColour (my graphics.get(), Graphics_BLACK);
  560. if (numberOfChannels > 1) {
  561. Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_HALF);
  562. Graphics_setTextAlignment (my graphics.get(), Graphics_LEFT, Graphics_HALF);
  563. conststring32 channelName = my v_getChannelName (ichan);
  564. static MelderString channelLabel { };
  565. MelderString_copy (& channelLabel, ( channelName ? U"ch" : U"Ch " ), ichan);
  566. if (channelName)
  567. MelderString_append (& channelLabel, U": ", channelName);
  568. //
  569. MelderString_append (& channelLabel, U" ",
  570. ( my d_sound.muteChannels [ichan] ? UNITEXT_SPEAKER_WITH_CANCELLATION_STROKE : UNITEXT_SPEAKER ));
  571. if (ichan > 8 && ichan - my d_sound.channelOffset == 1) {
  572. MelderString_append (& channelLabel, U" " UNITEXT_UPWARDS_ARROW);
  573. } else if (numberOfChannels >= 8 && ichan - my d_sound.channelOffset == 8 && ichan < numberOfChannels) {
  574. MelderString_append (& channelLabel, U" " UNITEXT_DOWNWARDS_ARROW);
  575. }
  576. Graphics_text (my graphics.get(), 1.0, 0.5, channelLabel.string);
  577. }
  578. /*
  579. * Draw a very thin separator line underneath.
  580. */
  581. if (ichan < numberOfChannels) {
  582. /*Graphics_setColour (my graphics.get(), Graphics_BLACK);*/
  583. Graphics_line (my graphics.get(), 0.0, 0.0, 1.0, 0.0);
  584. }
  585. /*
  586. * Draw the samples.
  587. */
  588. /*if (ichan == 1) FunctionEditor_SoundAnalysis_drawPulses (this);*/
  589. if (sound) {
  590. Graphics_setWindow (my graphics.get(), my startWindow, my endWindow, minimum, maximum);
  591. if (cursorVisible && isdefined (cursorFunctionValue))
  592. FunctionEditor_drawCursorFunctionValue (me, cursorFunctionValue, Melder_float (Melder_half (cursorFunctionValue)), U"");
  593. Graphics_setColour (my graphics.get(), Graphics_BLACK);
  594. Graphics_function (my graphics.get(), sound -> z [ichan], first, last,
  595. Sampled_indexToX (sound, first), Sampled_indexToX (sound, last));
  596. } else {
  597. Graphics_setWindow (my graphics.get(), my startWindow, my endWindow, minimum * 32768, maximum * 32768);
  598. Graphics_function16 (my graphics.get(),
  599. longSound -> buffer - longSound -> imin * numberOfChannels + (ichan - 1), numberOfChannels - 1, first, last,
  600. Sampled_indexToX (longSound, first), Sampled_indexToX (longSound, last));
  601. }
  602. Graphics_resetViewport (my graphics.get(), vp);
  603. }
  604. Graphics_setWindow (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
  605. Graphics_rectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
  606. }
  607. bool structTimeSoundEditor :: v_click (double xbegin, double ybegin, bool shiftKeyPressed) {
  608. Sound sound = our d_sound.data;
  609. LongSound longSound = our d_longSound.data;
  610. if (!! sound != !! longSound) {
  611. ybegin = (ybegin - v_getBottomOfSoundArea ()) / (1.0 - v_getBottomOfSoundArea ());
  612. integer numberOfChannels = ( sound ? sound -> ny : longSound -> numberOfChannels );
  613. if (numberOfChannels > 8) {
  614. trace (xbegin, U" ", ybegin, U" ", numberOfChannels, U" ", our d_sound.channelOffset);
  615. if (xbegin >= our endWindow && ybegin > 0.875 && ybegin <= 1.000 && our d_sound.channelOffset > 0) {
  616. our d_sound.channelOffset -= 8;
  617. return FunctionEditor_UPDATE_NEEDED;
  618. }
  619. if (xbegin >= our endWindow && ybegin > 0.000 && ybegin <= 0.125 && our d_sound.channelOffset < numberOfChannels - 8) {
  620. our d_sound.channelOffset += 8;
  621. return FunctionEditor_UPDATE_NEEDED;
  622. }
  623. }
  624. }
  625. return TimeSoundEditor_Parent :: v_click (xbegin, ybegin, shiftKeyPressed);
  626. }
  627. bool structTimeSoundEditor :: v_clickB (double xbegin, double ybegin) {
  628. Sound sound = our d_sound.data;
  629. LongSound longSound = our d_longSound.data;
  630. if (!! sound != !! longSound) {
  631. ybegin = (ybegin - v_getBottomOfSoundArea ()) / (1.0 - v_getBottomOfSoundArea ());
  632. integer numberOfChannels = ( sound ? sound -> ny : longSound -> numberOfChannels );
  633. if (numberOfChannels > 1) {
  634. integer numberOfVisibleChannels = ( numberOfChannels > 8 ? 8 : numberOfChannels );
  635. bool *muteChannels = our d_sound. muteChannels;
  636. trace (xbegin, U" ", ybegin, U" ", numberOfChannels, U" ", d_sound.channelOffset);
  637. integer box = ybegin * numberOfVisibleChannels + 1;
  638. if (box < 1)
  639. box = 1;
  640. if (box > numberOfVisibleChannels)
  641. box = numberOfVisibleChannels;
  642. integer channel = numberOfVisibleChannels - box + 1 + d_sound.channelOffset;
  643. if (Melder_debug == 24)
  644. Melder_casual (U"structTimeSoundEditor :: v_clickB ", ybegin, U" ", channel);
  645. muteChannels [channel] = ! muteChannels [channel];
  646. return FunctionEditor_UPDATE_NEEDED;
  647. }
  648. }
  649. return TimeSoundEditor_Parent :: v_clickB (xbegin, ybegin);
  650. }
  651. void TimeSoundEditor_init (TimeSoundEditor me, conststring32 title, Function data, Sampled sound, bool ownSound) {
  652. my d_ownSound = ownSound;
  653. if (sound) {
  654. integer numberOfChannels = 1;
  655. if (ownSound) {
  656. Melder_assert (Thing_isa (sound, classSound));
  657. my d_sound.data = Data_copy ((Sound) sound).releaseToAmbiguousOwner(); // deep copy; ownership transferred
  658. Matrix_getWindowExtrema (my d_sound.data, 1, my d_sound.data -> nx, 1, my d_sound.data -> ny, & my d_sound.minimum, & my d_sound.maximum);
  659. numberOfChannels = my d_sound.data -> ny;
  660. } else if (Thing_isa (sound, classSound)) {
  661. my d_sound.data = (Sound) sound; // reference copy; ownership not transferred
  662. Matrix_getWindowExtrema (my d_sound.data, 1, my d_sound.data -> nx, 1, my d_sound.data -> ny, & my d_sound.minimum, & my d_sound.maximum);
  663. numberOfChannels = my d_sound.data -> ny;
  664. } else if (Thing_isa (sound, classLongSound)) {
  665. my d_longSound.data = (LongSound) sound;
  666. my d_sound.minimum = -1.0, my d_sound.maximum = 1.0;
  667. numberOfChannels = my d_longSound.data -> numberOfChannels;
  668. } else {
  669. Melder_fatal (U"Invalid sound class in TimeSoundEditor::init.");
  670. }
  671. my d_sound.muteChannels = NUMvector<bool> (1, numberOfChannels);
  672. }
  673. FunctionEditor_init (me, title, data);
  674. }
  675. /* End of file TimeSoundEditor.cpp */