summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTim Mayberry <mojofunk@gmail.com>2016-12-05 11:41:00 +1000
committerTim Mayberry <mojofunk@gmail.com>2016-12-06 13:44:19 +1000
commitbd52d4e3289dee403a84e734907d51d1f928520a (patch)
treeed17be4f44f463bca052c2943f3bc87d8211f476
parent568cf861f948fcf3ad1dff142c1ed6aa8af04618 (diff)
Fix issues in export with trim enabled
A complete reimplementation of AudioGrapher::SilentTrimmer::process to support trimming the beginning and end in the same processing block Fix export with trim end enabled to actually trim silent frames Only add silence to beginning or end of export data if data was written Should resolve: #6412
-rw-r--r--libs/audiographer/audiographer/general/silence_trimmer.h178
1 files changed, 90 insertions, 88 deletions
diff --git a/libs/audiographer/audiographer/general/silence_trimmer.h b/libs/audiographer/audiographer/general/silence_trimmer.h
index 8959c8674a..e7ae089028 100644
--- a/libs/audiographer/audiographer/general/silence_trimmer.h
+++ b/libs/audiographer/audiographer/general/silence_trimmer.h
@@ -74,8 +74,8 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
TypeUtils<T>::zero_fill (silence_buffer, silence_buffer_size);
}
- in_beginning = true;
- in_end = false;
+ processed_data = false;
+ processing_finished = false;
trim_beginning = false;
trim_end = false;
silence_frames = 0;
@@ -90,8 +90,8 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
*/
void add_silence_to_beginning (framecnt_t frames_per_channel)
{
- if (throw_level (ThrowObject) && !in_beginning) {
- throw Exception(*this, "Tried to add silence to beginning after already outputting data");
+ if (throw_level (ThrowObject) && processed_data) {
+ throw Exception(*this, "Tried to add silence to beginning after processing started");
}
add_to_beginning = frames_per_channel;
}
@@ -102,8 +102,8 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
*/
void add_silence_to_end (framecnt_t frames_per_channel)
{
- if (throw_level (ThrowObject) && in_end) {
- throw Exception(*this, "Tried to add silence to end after already reaching end");
+ if (throw_level (ThrowObject) && processed_data) {
+ throw Exception(*this, "Tried to add silence to end after processing started");
}
add_to_end = frames_per_channel;
}
@@ -114,8 +114,8 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
*/
void set_trim_beginning (bool yn)
{
- if (throw_level (ThrowObject) && !in_beginning) {
- throw Exception(*this, "Tried to set beginning trim after already outputting data");
+ if (throw_level (ThrowObject) && processed_data) {
+ throw Exception(*this, "Tried to set beginning trim after processing started");
}
trim_beginning = yn;
}
@@ -126,8 +126,8 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
*/
void set_trim_end (bool yn)
{
- if (throw_level (ThrowObject) && in_end) {
- throw Exception(*this, "Tried to set end trim after already reaching end");
+ if (throw_level (ThrowObject) && processed_data) {
+ throw Exception(*this, "Tried to set end trim after processing started");
}
trim_end = yn;
}
@@ -146,16 +146,14 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
check_flags (*this, c);
- if (throw_level (ThrowStrict) && in_end) {
+ if (throw_level (ThrowStrict) && processing_finished) {
throw Exception(*this, "process() after reaching end of input");
}
- // delay end of input propagation until after all output is complete
- in_end = c.has_flag (ProcessContext<T>::EndOfInput);
+ // delay end of input propagation until output/processing is complete
+ processing_finished = c.has_flag (ProcessContext<T>::EndOfInput);
c.remove_flag (ProcessContext<T>::EndOfInput);
- framecnt_t frame_index = 0;
-
/* TODO this needs a general overhaul.
*
* - decouple "required silence duration" from buffer-size.
@@ -164,103 +162,83 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
* -> allocate a buffer "hold time" worth of samples.
* check if all samples in buffer are above/below threshold,
*
- * * in_beginning, in_end may be in the same cycle.
- * * end-trim should not be on a buffersize boundary
- * * results should be consistent for all buffer-sizes and samplerates
- *
- * (currently this is mosly fine because the "Chunker"
- * produces a fixAed 8K stream, but this 8K are for interleaved
- * data all channels and it's regardless of sample-rate)
- *
* https://github.com/x42/silan/blob/master/src/main.c#L130
* may lend itself for some inspiration.
*/
- if (in_beginning) {
-
- bool has_data = true;
-
- // only check silence if doing either of these
- // This will set both has_data and frame_index
- if (add_to_beginning || trim_beginning) {
- has_data = find_first_non_silent_frame (c, frame_index);
- }
-
- // Added silence if there is silence to add
- if (add_to_beginning) {
-
- if (debug_level (DebugVerbose)) {
- debug_stream () << DebugUtils::demangled_name (*this) <<
- " adding to beginning" << std::endl;
+ framecnt_t output_start_index = 0;
+ framecnt_t output_sample_count = c.frames();
+
+ if (!processed_data) {
+ if (trim_beginning) {
+ framecnt_t first_non_silent_frame_index = 0;
+ if (find_first_non_silent_frame (c, first_non_silent_frame_index)) {
+ // output from start of non-silent data until end of buffer
+ // output_sample_count may also be altered in trim end
+ output_start_index = first_non_silent_frame_index;
+ output_sample_count = c.frames() - first_non_silent_frame_index;
+ processed_data = true;
+ } else {
+ // keep entering this block until non-silence is found to trim
+ processed_data = false;
}
+ } else {
+ processed_data = true;
+ }
- add_to_beginning *= c.channels();
+ // This block won't be called again so add silence to beginning
+ if (processed_data && add_to_beginning) {
+ add_to_beginning *= c.channels ();
output_silence_frames (c, add_to_beginning);
}
+ }
- // If we are not trimming the beginning, output everything
- // Then has_data = true and frame_index = 0
- // Otherwise these reflect the silence state
- if (has_data) {
-
- if (debug_level (DebugVerbose)) {
- debug_stream () << DebugUtils::demangled_name (*this) <<
- " outputting whole frame to beginning" << std::endl;
- }
+ if (processed_data) {
+ if (trim_end) {
+ framecnt_t first_non_silent_frame_index = 0;
+ if (find_first_non_silent_frame (c, first_non_silent_frame_index)) {
+ // context buffer contains non-silent data, flush any intermediate silence
+ output_silence_frames (c, silence_frames);
- in_beginning = false;
- ConstProcessContext<T> c_out (c, &c.data()[frame_index], c.frames() - frame_index);
- ListedSource<T>::output (c_out);
- }
+ framecnt_t silent_frame_index = 0;
+ find_last_silent_frame_reverse (c, silent_frame_index);
- } else if (trim_end) { // Only check zero samples if trimming end
+ // Count of samples at end of block that are "silent", may be zero.
+ framecnt_t silent_end_samples = c.frames () - silent_frame_index;
+ framecnt_t samples_before_silence = c.frames() - silent_end_samples;
- if (find_first_non_silent_frame (c, frame_index)) {
+ assert (samples_before_silence + silent_end_samples == c.frames ());
- if (debug_level (DebugVerbose)) {
- debug_stream () << DebugUtils::demangled_name (*this) <<
- " flushing intermediate silence and outputting frame" << std::endl;
- }
+ // output_start_index may be non-zero if start trim occurred above
+ output_sample_count = samples_before_silence - output_start_index;
- // context contains non-zero data
- output_silence_frames (c, silence_frames); // flush intermediate silence
- ListedSource<T>::output (c); // output rest of data
- } else { // whole context is zero
+ // keep track of any silent samples not output
+ silence_frames = silent_end_samples;
- if (debug_level (DebugVerbose)) {
- debug_stream () << DebugUtils::demangled_name (*this) <<
- " no, output, adding frames to silence count" << std::endl;
+ } else {
+ // whole context buffer is silent output nothing
+ silence_frames += c.frames ();
+ output_sample_count = 0;
}
-
- silence_frames += c.frames();
- }
-
- } else { // no need to do anything special
-
- if (debug_level (DebugVerbose)) {
- debug_stream () << DebugUtils::demangled_name (*this) <<
- " outputting whole frame in middle" << std::endl;
}
- ListedSource<T>::output (c);
+ // now output data if any
+ ConstProcessContext<T> c_out (c, &c.data()[output_start_index], output_sample_count);
+ ListedSource<T>::output (c_out);
}
- // Finally, if in end, add silence to end
- if (in_end && add_to_end) {
- if (debug_level (DebugVerbose)) {
- debug_stream () << DebugUtils::demangled_name (*this) <<
- " adding to end" << std::endl;
- }
-
+ // Finally, if in last process call, add silence to end
+ if (processing_finished && processed_data && add_to_end) {
add_to_end *= c.channels();
output_silence_frames (c, add_to_end);
}
- if (in_end) {
+ if (processing_finished) {
// reset flag removed previous to processing above
c.set_flag (ProcessContext<T>::EndOfInput);
// Finally mark write complete by writing nothing with EndOfInput set
+ // whether or not any data has been written
ConstProcessContext<T> c_out(c, silence_buffer, 0);
c_out().set_flag (ProcessContext<T>::EndOfInput);
ListedSource<T>::output (c_out);
@@ -270,7 +248,7 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
using Sink<T>::process;
- private:
+private:
bool find_first_non_silent_frame (ProcessContext<T> const & c, framecnt_t & result_frame)
{
@@ -285,6 +263,31 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
return false;
}
+ /**
+ * Reverse find the last silent frame index. If the last sample in the
+ * buffer is non-silent the index will be one past the end of the buffer and
+ * equal to c.frames(). e.g silent_end_samples = c.frames() - result_frame
+ *
+ * @return true if result_frame index is valid, false if there were only
+ * silent samples in the context buffer
+ */
+ bool find_last_silent_frame_reverse (ProcessContext<T> const & c, framecnt_t & result_frame)
+ {
+ framecnt_t last_sample_index = c.frames() - 1;
+
+ for (framecnt_t i = last_sample_index; i != 0; --i) {
+ if (!tester.is_silent (c.data()[i])) {
+ result_frame = i;
+ // Round down to nearest interleaved "frame" beginning
+ result_frame -= result_frame % c.channels();
+ // Round up to return the "last" silent interleaved frame
+ result_frame += c.channels();
+ return true;
+ }
+ }
+ return false;
+ }
+
void output_silence_frames (ProcessContext<T> const & c, framecnt_t & total_frames)
{
assert (!c.has_flag (ProcessContext<T>::EndOfInput));
@@ -302,9 +305,8 @@ class /*LIBAUDIOGRAPHER_API*/ SilenceTrimmer
}
}
-
- bool in_beginning;
- bool in_end;
+ bool processed_data;
+ bool processing_finished;
bool trim_beginning;
bool trim_end;