static int filter_frame(AVFilterLink *inlink, AVFrame *in) { AVFilterContext *ctx = inlink->dst; AVFilterLink *outlink = ctx->outputs[0]; SilenceRemoveContext *s = ctx->priv; int i, j, threshold, ret = 0; int nbs, nb_samples_read, nb_samples_written; double *obuf, *ibuf = (double *)in->data[0]; AVFrame *out; nb_samples_read = nb_samples_written = 0; switch (s->mode) { case SILENCE_TRIM: silence_trim: nbs = in->nb_samples - nb_samples_read / inlink->channels; if (!nbs) break; for (i = 0; i < nbs; i++) { threshold = 0; for (j = 0; j < inlink->channels; j++) { threshold |= compute_rms(s, ibuf[j]) > s->start_threshold; } if (threshold) { for (j = 0; j < inlink->channels; j++) { update_rms(s, *ibuf); s->start_holdoff[s->start_holdoff_end++] = *ibuf++; nb_samples_read++; } if (s->start_holdoff_end >= s->start_duration * inlink->channels) { if (++s->start_found_periods >= s->start_periods) { s->mode = SILENCE_TRIM_FLUSH; goto silence_trim_flush; } s->start_holdoff_offset = 0; s->start_holdoff_end = 0; } } else { s->start_holdoff_end = 0; for (j = 0; j < inlink->channels; j++) update_rms(s, ibuf[j]); ibuf += inlink->channels; nb_samples_read += inlink->channels; } } break; case SILENCE_TRIM_FLUSH: silence_trim_flush: nbs = s->start_holdoff_end - s->start_holdoff_offset; nbs -= nbs % inlink->channels; if (!nbs) break; out = ff_get_audio_buffer(inlink, nbs / inlink->channels); if (!out) { av_frame_free(&in); return AVERROR(ENOMEM); } memcpy(out->data[0], &s->start_holdoff[s->start_holdoff_offset], nbs * sizeof(double)); s->start_holdoff_offset += nbs; ret = ff_filter_frame(outlink, out); if (s->start_holdoff_offset == s->start_holdoff_end) { s->start_holdoff_offset = 0; s->start_holdoff_end = 0; s->mode = SILENCE_COPY; goto silence_copy; } break; case SILENCE_COPY: silence_copy: nbs = in->nb_samples - nb_samples_read / inlink->channels; if (!nbs) break; out = ff_get_audio_buffer(inlink, nbs); if (!out) { av_frame_free(&in); return AVERROR(ENOMEM); } obuf = (double *)out->data[0]; if (s->stop_periods) { for (i = 0; i < nbs; i++) { threshold = 1; for (j = 0; j < inlink->channels; j++) threshold &= compute_rms(s, ibuf[j]) > s->stop_threshold; if (threshold && s->stop_holdoff_end && !s->leave_silence) { s->mode = SILENCE_COPY_FLUSH; flush(out, outlink, &nb_samples_written, &ret); goto silence_copy_flush; } else if (threshold) { for (j = 0; j < inlink->channels; j++) { update_rms(s, *ibuf); *obuf++ = *ibuf++; nb_samples_read++; nb_samples_written++; } } else if (!threshold) { for (j = 0; j < inlink->channels; j++) { update_rms(s, *ibuf); if (s->leave_silence) { *obuf++ = *ibuf; nb_samples_written++; } s->stop_holdoff[s->stop_holdoff_end++] = *ibuf++; nb_samples_read++; } if (s->stop_holdoff_end >= s->stop_duration * inlink->channels) { if (++s->stop_found_periods >= s->stop_periods) { s->stop_holdoff_offset = 0; s->stop_holdoff_end = 0; if (!s->restart) { s->mode = SILENCE_STOP; flush(out, outlink, &nb_samples_written, &ret); goto silence_stop; } else { s->stop_found_periods = 0; s->start_found_periods = 0; s->start_holdoff_offset = 0; s->start_holdoff_end = 0; clear_rms(s); s->mode = SILENCE_TRIM; flush(out, outlink, &nb_samples_written, &ret); goto silence_trim; } } s->mode = SILENCE_COPY_FLUSH; flush(out, outlink, &nb_samples_written, &ret); goto silence_copy_flush; } } } flush(out, outlink, &nb_samples_written, &ret); } else { memcpy(obuf, ibuf, sizeof(double) * nbs * inlink->channels); ret = ff_filter_frame(outlink, out); } break; case SILENCE_COPY_FLUSH: silence_copy_flush: nbs = s->stop_holdoff_end - s->stop_holdoff_offset; nbs -= nbs % inlink->channels; if (!nbs) break; out = ff_get_audio_buffer(inlink, nbs / inlink->channels); if (!out) { av_frame_free(&in); return AVERROR(ENOMEM); } memcpy(out->data[0], &s->stop_holdoff[s->stop_holdoff_offset], nbs * sizeof(double)); s->stop_holdoff_offset += nbs; ret = ff_filter_frame(outlink, out); if (s->stop_holdoff_offset == s->stop_holdoff_end) { s->stop_holdoff_offset = 0; s->stop_holdoff_end = 0; s->mode = SILENCE_COPY; goto silence_copy; } break; case SILENCE_STOP: silence_stop: break; } av_frame_free(&in); return ret; }
/* Return number of samples processed in isamp and osamp. */ static int sox_silence_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sample_t *obuf, size_t *isamp, size_t *osamp) { priv_t * silence = (priv_t *) effp->priv; int threshold; size_t i, j; size_t nrOfTicks, /* sometimes wide, sometimes non-wide samples */ nrOfInSamplesRead, nrOfOutSamplesWritten; /* non-wide samples */ nrOfInSamplesRead = 0; nrOfOutSamplesWritten = 0; switch (silence->mode) { case SILENCE_TRIM: /* Reads and discards all input data until it detects a * sample that is above the specified threshold. Turns on * copy mode when detected. * Need to make sure and copy input in groups of "channels" to * prevent getting buffers out of sync. * nrOfTicks counts wide samples here. */ silence_trim: nrOfTicks = min((*isamp-nrOfInSamplesRead), (*osamp-nrOfOutSamplesWritten)) / effp->in_signal.channels; for(i = 0; i < nrOfTicks; i++) { threshold = 0; for (j = 0; j < effp->in_signal.channels; j++) { threshold |= aboveThreshold(effp, compute_rms(effp, ibuf[j]), silence->start_threshold, silence->start_unit); } if (threshold) { /* Add to holdoff buffer */ for (j = 0; j < effp->in_signal.channels; j++) { update_rms(effp, *ibuf); silence->start_holdoff[ silence->start_holdoff_end++] = *ibuf++; nrOfInSamplesRead++; } if (silence->start_holdoff_end >= silence->start_duration) { if (++silence->start_found_periods >= silence->start_periods) { silence->mode = SILENCE_TRIM_FLUSH; goto silence_trim_flush; } /* Trash holdoff buffer since its not * needed. Start looking again. */ silence->start_holdoff_offset = 0; silence->start_holdoff_end = 0; } } else /* !above Threshold */ { silence->start_holdoff_end = 0; for (j = 0; j < effp->in_signal.channels; j++) { update_rms(effp, ibuf[j]); } ibuf += effp->in_signal.channels; nrOfInSamplesRead += effp->in_signal.channels; } } /* for nrOfTicks */ break; case SILENCE_TRIM_FLUSH: /* nrOfTicks counts non-wide samples here. */ silence_trim_flush: nrOfTicks = min((silence->start_holdoff_end - silence->start_holdoff_offset), (*osamp-nrOfOutSamplesWritten)); nrOfTicks -= nrOfTicks % effp->in_signal.channels; for(i = 0; i < nrOfTicks; i++) { *obuf++ = silence->start_holdoff[silence->start_holdoff_offset++]; nrOfOutSamplesWritten++; } /* If fully drained holdoff then switch to copy mode */ if (silence->start_holdoff_offset == silence->start_holdoff_end) { silence->start_holdoff_offset = 0; silence->start_holdoff_end = 0; silence->mode = SILENCE_COPY; goto silence_copy; } break; case SILENCE_COPY: /* Attempts to copy samples into output buffer. * * Case B: * If not looking for silence to terminate copy then * blindly copy data into output buffer. * * Case A: * * Case 1a: * If previous silence was detect then see if input sample is * above threshold. If found then flush out hold off buffer * and copy over to output buffer. * * Case 1b: * If no previous silence detect then see if input sample * is above threshold. If found then copy directly * to output buffer. * * Case 2: * If not above threshold then silence is detect so * store in hold off buffer and do not write to output * buffer. Even though it wasn't put in output * buffer, inform user that input was consumed. * * If hold off buffer is full after this then stop * copying data and discard data in hold off buffer. * * Special leave_silence logic: * * During this mode, go ahead and copy input * samples to output buffer instead of holdoff buffer * Then also short ciruit any flushes that would occur * when non-silence is detect since samples were already * copied. This has the effect of always leaving * holdoff[] amount of silence but deleting any * beyond that amount. * * nrOfTicks counts wide samples here. */ silence_copy: nrOfTicks = min((*isamp-nrOfInSamplesRead), (*osamp-nrOfOutSamplesWritten)) / effp->in_signal.channels; if (silence->stop) { /* Case A */ for(i = 0; i < nrOfTicks; i++) { threshold = 1; for (j = 0; j < effp->in_signal.channels; j++) { threshold &= aboveThreshold(effp, compute_rms(effp, ibuf[j]), silence->stop_threshold, silence->stop_unit); } /* Case 1a * If above threshold, check to see if we where holding * off previously. If so then flush this buffer. * We haven't incremented any pointers yet so nothing * is lost. * * If user wants to leave_silence, then we * were already copying the data and so no * need to flush the old data. Just resume * copying as if we were not holding off. */ if (threshold && silence->stop_holdoff_end && !silence->leave_silence) { silence->mode = SILENCE_COPY_FLUSH; goto silence_copy_flush; } /* Case 1b */ else if (threshold) { /* Not holding off so copy into output buffer */ for (j = 0; j < effp->in_signal.channels; j++) { update_rms(effp, *ibuf); *obuf++ = *ibuf++; nrOfInSamplesRead++; nrOfOutSamplesWritten++; } } /* Case 2 */ else if (!threshold) { /* Add to holdoff buffer */ for (j = 0; j < effp->in_signal.channels; j++) { update_rms(effp, *ibuf); if (silence->leave_silence) { *obuf++ = *ibuf; nrOfOutSamplesWritten++; } silence->stop_holdoff[ silence->stop_holdoff_end++] = *ibuf++; nrOfInSamplesRead++; } /* Check if holdoff buffer is greater than duration */ if (silence->stop_holdoff_end >= silence->stop_duration) { /* Increment found counter and see if this * is the last period. If so then exit. */ if (++silence->stop_found_periods >= silence->stop_periods) { silence->stop_holdoff_offset = 0; silence->stop_holdoff_end = 0; if (!silence->restart) { *isamp = nrOfInSamplesRead; *osamp = nrOfOutSamplesWritten; silence->mode = SILENCE_STOP; /* Return SOX_EOF since no more processing */ return (SOX_EOF); } else { silence->stop_found_periods = 0; silence->start_found_periods = 0; silence->start_holdoff_offset = 0; silence->start_holdoff_end = 0; clear_rms(effp); silence->mode = SILENCE_TRIM; goto silence_trim; } } else { /* Flush this buffer and start * looking again. */ silence->mode = SILENCE_COPY_FLUSH; goto silence_copy_flush; } break; } /* Filled holdoff buffer */ } /* Detected silence */ } /* For # of samples */ } /* Trimming off backend */ else /* !(silence->stop) */ { /* Case B */ memcpy(obuf, ibuf, sizeof(sox_sample_t)*nrOfTicks* effp->in_signal.channels); nrOfInSamplesRead += (nrOfTicks*effp->in_signal.channels); nrOfOutSamplesWritten += (nrOfTicks*effp->in_signal.channels); } break; case SILENCE_COPY_FLUSH: /* nrOfTicks counts non-wide samples here. */ silence_copy_flush: nrOfTicks = min((silence->stop_holdoff_end - silence->stop_holdoff_offset), (*osamp-nrOfOutSamplesWritten)); nrOfTicks -= nrOfTicks % effp->in_signal.channels; for(i = 0; i < nrOfTicks; i++) { *obuf++ = silence->stop_holdoff[silence->stop_holdoff_offset++]; nrOfOutSamplesWritten++; } /* If fully drained holdoff then return to copy mode */ if (silence->stop_holdoff_offset == silence->stop_holdoff_end) { silence->stop_holdoff_offset = 0; silence->stop_holdoff_end = 0; silence->mode = SILENCE_COPY; goto silence_copy; } break; case SILENCE_STOP: /* This code can't be reached. */ nrOfInSamplesRead = *isamp; break; } *isamp = nrOfInSamplesRead; *osamp = nrOfOutSamplesWritten; return (SOX_SUCCESS); }