!7 set flag DynModule. sclass Recording { File file; double length; S containsVoice; *() {} *(File *file) { containsVoice = getFileInfoField(file, "Contains voice"); } bool updateClip() { bool change = false; if (length == 0) try { double l = lengthOfWAVInSeconds(file); if (l != 0) { length = l; set change; } } catch print e { length = -1; } ret change; } } cmodule AudioRecordings2 extends DynObjectTable { transient ReliableSingleThread scan = dm_rst(this, r _scan); start { itemToMap = func(Recording r) -> Map { litorderedmap( Name := fileName(r.file), "Length" := formatDouble(r.length, 1) + " s", "Contains Voice" := or2(r.containsVoice, "?")) }; ownResource(vmBus_onMessage('newRecording, scan)); scan.trigger(); } void _scan { temp enter(); setData(map(latestFilesFirst(allRecordings()), f1_new(Recording))); thread "Get Clip Lengths" { bool change = false; for (Recording r : data) if (r.updateClip()) set change; if (change) updateTable(); } } visualize { JComponent c = super.visualize(); ret withCenteredButtons(c, tableDependentButton(table, "Clip", rThread { File f = selected().file; //autoClipRecording(f); clipWAVFromMarkers(f, fileInSubDir(f, "clipped")); selected().updateClip(); updateTable(); }), tableDependentButton(table, "Copy path", rThread { copyTextToClipboard(f2s(selected().file)) }), tableDependentButton(table, "Play", rThread { dm_playWAV(selected().file) }), tableDependentButton(table, "Has voice", rThread { final Recording r = selected(); setFileInfoField(r.file, "Contains voice", "Yes"); updateTable(); }), tableDependentButton(table, "No voice", rThread { final Recording r = selected(); setFileInfoField(r.file, "Contains voice", "No"); updateTable(); }), ); } }