I'm trying to design and develop a software (part of a bigger application) that should compute and render a spectrogram.

The spectrogram can be rendered either from live microphone or a file (assuming its content has been turned into a float array) and using two different algorithms, i.e. FFT or DCT.

In case the spectrogram has been created using an FFT I want to be able to change its scale to mel and in case it was computed from file, also optionally apply filters like Band-Pass, Low-Pass, High-Pass, maybe something more in the future, with a given roll-off per octave (input parameter to the filter).

I started to sketch an UML (don't kill me if I didn't use the aggregation/composition notations, still have to learn), that looks like this (please ignore the notes, it's in my native language and are just addressing thoughts that the UML can't represent)

UML model of my thought flow

Basic idea: I have a Spectrogram class that I use as an interface to the client. When they want to compute a spectrogram they instantiate an object of this class, configure it and call computeSpectrogram(timeBuffer: Float*): UIImage? on it.

Under the hood the Spectrogram delegates the computation of the spectrogram to its spectrogramCalculator strategy. Each strategy reimplements computeSpectrogram(...) and since different strategies from the same family (FFT or DCT) share some code, I will create a single point of change by collocating it in the DCTStrategy and FFTStrategy as a protected method with a default implementation.

At this level, I need to be able to dynamically add/remove behaviors to the FFT computation (filters, scale change) and therefore I go for Decorator Pattern, that according to Design Patterns, Elements of Reusable Object-Oriented Software, can be used to

Attach additional responsibilities to an object dynamically [...]

But only for the FFT strategy. Also the MelScaleDecorator can be written in a way that suits both MelScaleFileDecorator and MelScaleMicrophoneDecorator so that they share the same implementation, so I may create a static utility MelScaleUtils class that exposes a static method that both the decorators use to give their implementation of performFFT().

Now my question is the following: Is this a suitable use case for the Decorator Pattern? Cause if the user wants to undo some operation (eg Band Pass filtering) I'd have to recreate the whole decorator chain and use the new chain to recompute the spectrogram (takes less than one second for a 2min long .wav file, which is fine for my requirement). That or either create an ActionsQueue stack that stores Commands (see Command pattern) to support an undo mechanism to compensate the lack of flexibility in dynamically removing the additional behaviors.

Am I overengineering it?

New contributor
Baffo rasta is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
  • You are describing step-wise processing of a data pipeline with multiple stages. A portion of your "undo" requirement could be handled by simply having each stage persist its output (and perhaps its metadata such as input args). Then when you propose a new pipeline, you may find that some stages have already been pre-computed so you enjoy some cache hits. Reasoning about such a caching strategy is very simple, so you are unlikely to be surprised by an incorrect end result.
    – J_H
    Mar 18 at 1:28
  • @J_H: I feel that it would definitely be possible for the microphone capture case, but input from files can be huge and saving a processed copy for each intermediate step could be an issue. A single copy already can take tens of MB of memory (I'm designing this for a mobile environment specifically) Mar 18 at 9:48


Your Answer

Baffo rasta is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy