Commit Graph

2466 Commits

Author SHA1 Message Date
mmeeaallyynn
352ef8c25e Sample Track Recording with Jack backend (#7567)
Enable the `AudioJack` to take recorded input and push it to the `AudioEngine`. This adds functionality for `AudioJack` which already exists for `AudioSdl` and `AudioPortAudio`. Note that sample track recording in the LMMS core is not completed before #7786 .

This PR also removes the reading and saving of the channel number configuration in several places as it will default to `DEFAULT_CHANNELS` all the time anyway.

Co-authored-by: Johannes Lorenz <j.git@lorenz-ho.me>
Co-authored-by: Michael Gregorius <michael.gregorius.git@arcor.de>
2025-05-31 12:49:14 +02:00
Michael Gregorius
b6d6509068 Individual knob labels rendered using the widget's font size (#7525)
Adjust the `Knob` class so that it defaults to taking the font size of
the knob's font into account when rendering its label. This allows to
use labels of different sizes for different knobs. Previously all knob
labels throughout the whole application were rendered with the same fixed
font size. Hence it was not possible to adjust the label size for a
single knob because this would have affected all other knobs as well.

The new implementation also allows the knobs to pick up CSS rules.

To be able to control the knob behavior two new constructors have been
added to the `Knob` class. Both constructors are concerned with creating
knobs with labels and therefore they directly take the label text as a
parameter. This removes numerous explicit calls to `setLabel` in the
code.

There is only one constructor that allows to switch between the new
behavior of taking the widget's font size into account and the old legacy
behavior of always rendering with the same fixed font size of
`SMALL_FONT_SIZE`. The parameter was modelled as an enum to make it
easier to find the remaining knob instances that use the legacy behavior.
This makes it easier to find them in case they should be removed as well.
In that case the string `LegacyFixedFontSize` can be searched.

The other new constructor allows to directly set the knob's (and
therefore the label's) font size to a pixel value.

Corresponding constructors have been added to `TempoSyncKnob`. The
constructors of `KnobControl` and `CustomTextKnob` have been adjusted to
take advantage of the new constructors. An usused constructor was removed
in `CustomTextKnob`.

The method `Knob::setLabel` has been made protected because labels should
now be set through the new constructors.

A new property called `m_fixedFontSizeLabelRendering` was added to the
`Knob` class. It controls how the labels are rendered. Fixed font size
legacy rendering can be activated by calling the protected method
`setFixedFontSizeLabelRendering`. The current setting can be queried via
`fixedFontSizeLabelRendering`.

## Changes in the plugins

Some plugins have been switched to using layouts to organize their
widgets so that they can accommodate for knobs with label sizes set by
the application font.

The fixed font size (legacy) rendering mode is still used in the
following places:
* EnvelopeAndLfoView
* InstrumentSoundShapingView
* InstrumentFunctionViews
* EffectView
* InstrumentTrackView
* SampleTrackView
* Delay plugin
* Carla plugin


# individual commit messages

What follows are the individual commit messages of the commits that have been squashed into one commit. They might help in case of more detailed investigations of how things came to be.

* Knob with correct label rendering

Enable the knob to render the label correctly at arbitrary sizes if it's configured to do so. Otherwise it will render like before. The used mode is determined when a label is set for the knob because as long as the label is not set a knob does not have one anyway.

The painting code now always renders the label with the font that's set for the widget.

The are now two methods to set the label text. The new method `setLabelLegacy` renders the label as before albeit in a slightly adjusted implementation. It now sets the widget font to a fixed pixel size font and then calculates the new widget size as before, i.e. not really taking the size of the font into account. This might lead to overlaps if the font of the knob is large.

The method `setLabel` now has an additional (temporary) parameter called `legacyMode`. It is by default set to `true` so that all knobs still render like they did before. This is implemented by delegating to `setLabelLegacy` if it's set to `true`. Otherwise the method calculates the new size of the widget by taking the pixmap and the label with the current font into account.

Please note that as of now you must set the knob font before calling any of the methods that sets the label. This is because the new size is only calculated via these code paths. However, this is already much better than only being able to use one hard-coded label size for all knobs.

* Switch from `setLabel` to `setLabelLegacy`

Switch all callers of `setLabel` to `setLabelLegacy` so that it becomes
obvious in which places the old knob implementation is used.

* Remove parameter `legacyMode` from `setLabel`

Remove the parameter `legacyMode` from `setLabel`. Add the member
`m_legacyMode` as it is needed in `Knob::changeEvent` so that we do not
switch the behavior when the knob is enabled/disabled.

* Extract methods

Extract `setLegacyMode` and `updateFixedSize`. Also add the getter `legacyMode`.

* Introduce legacy knob builders

Introduce legacy knob builders and apply them to the plugins. There are three new methods which encapsulate how to create a corresponding legacy knob:
* `Knob::buildLegacyKnob`
* `CustomTextKnob::buildLegacyKnob`
* `TempoSyncKnob::buildLegacyKnob`

These methods set the knob they build to legacy mode and also set a label in legacy mode. The idea is to concentrate the relevant legacy code in these methods. They will later also be useful to quickly find all the places in the application where legacy knobs are used.

The three methods are applied to the plugins directory. Most plugins use the build methods to build their knobs which also enables the removal of the explicit calls to `setLabelLegacy` from their code.

For some plugins their implementations were adjusted so that they can deal with showing the labels in the applicaiton font, i.e. in the font size of the widget their are contained in. Most of the times this involved removing the fixed size and putting the elements in a layout (while also removing move calls). The following LMMS plugins use the application font now and are thus better readable:
* Amplifier
* BassBooster
* Dispersion
* Flanger
* Peak Controller
* ReverbSC
* StereoEnhancer Effect

The Vectorscope now shows the "Persist." label in the same size as the label of the check boxes.

Setting an empty label was removed for Lb302.

* Legacy knob builders in GUI

Apply the legacy knob builders in the GUI components. Most components use the legacy knobs until they can be redesigned:
* Effect view ("W/D", "DECAY", "GATE")
* LFO Controller
* Instrument window

Everything related to the instrument window is for now kept to use the legacy knobs and should be adjusted at a later point to be more flexible:
* Envelope and LFO
* Functions
* Sound Shaping

The Instrument and sample track both use the legacy knobs for the "VOL" and "PAN" knobs. This might be adjusted later.

The following components now render the labels of their knobs with the application font size:
* MIDI CC Rack
* The class `LadspaControlView`, which is not in used anymore

Some vertical spacing was added to the MIDI CC Rack for slightly improved separation of the elements. The knobs are center aligned in the layout so that the transition between element under and over "CC 100" is cleaner. Setting the models in an explicit loop was removed and is now done when the knobs are created.

## Technical details
Extend `Knob::buildLegacyKnob` with the option to also set the name of the knob. This is needed for some changes in this PR.

The method `KnobControl::setText` now needs to distinguish between legacy mode and non-legacy mode.

* Remove `Knob::setLabelLegacy`

Remove `Knob::setLabelLegacy`. Instead make sure that the `Knob` updates its size in the following situations:
* The label is set.
* The font changes.
* Legacy mode is set or unset (already implemented).

The handling of font changes has been added to `Knob::changeEvent`. The update in case of a changed label is added to `Knob::setLabel`.

Every client that called `setLabelLegacy` now also sets the legacy font in size `SMALL_FONT_SIZE` as this was previously done in `setLabelLegacy`. The label is set via `setLabel` now. Both actions should result in an up-to-date size.

The method `KnobControl::setText` now only sets the label via `setLabel`, assuming that the wrapped knob was already configured correctly to either be a legacy knob or not.

* Use descent to calculate base line

Use the descent of the font to calculate the distance of the base line from the bottom of the knob widget if we are not in legacy mode. In legacy mode we still assume the descent to have a value of 2, i.e. the base line will always have a distance of 2 from the bottom of the knob widget regardless of the used font.

Also refactor the code a bit to make it more manageable.

* Extract `Knob::drawLabel`

Extract the method `Knob::drawLabel` which draws the label. It is called from `paintEvent`.

* Use non-legacy knobs for instrument and sample track

Use non-legacy knobs for the "VOL" and "PAN" knobs of the instrument and sample track. This gives a bit more separation between the knob and the label but to make this work the font size had to be decreased by one pixel.

* Introduce `buildKnobWithSmallPixelFont`

Introduce the builder method `buildKnobWithSmallPixelFont` in `Knob` and `TempoSyncKnob`. It creates a non-legacy knob with a small pixel sized font, i.e. it still uses the small font but with a corrected size computation and corrected space between the knob and the label. It is mostly used in places with manual layouts where there's enough space to have the bit of extra space between the knob and the label.

The following plugins use these knobs:
* Bitcrush
* Crossover EQ
* Dual Filter
* Dynamics Processor
* Multitap Echo
* Spectrum analyzer
* Mallets
* Waveshaper
* ZynAddSubFx

The "IN" and "OUT" label of the Bitcrush plugin use the default fixed font size now because the plugin uses a pixel based layout. Using the point based application font looked off.

They are also used in the following component:
* Effect view, i.e. the "W/D", "DECAY", "GATE" knobs of an effect
* LFO Controller

* Non-legacy knobs for VSTs

Use non-legacy knobs with small pixel fonts for the parameter lists of VST instruments and effects.

This is accomplished by renaming `CustomTextKnob::buildLegacyKnob` to `buildKnobWithSmallPixelFont` and removing the call to `setLegacyMode`.

* Fix styled knobs

Fix the handling of styled knobs which are created in non-legacy mode. Styled knobs do not use pixmaps and have no labels. Their size is set from the outside and they are painted within these limits. Hence we should not compute a new size from a pixmap and/or label in `Knob::updateFixedSize`.

This fixes the following plugins:
* FreeBoy
* Kicker
* Monstro
* Nescaline
* Opulenz
* Organic
* Sf2 Player
* sfxr
* SID
* SlicerT
* Triple
* Watsyn
* Xpressive

The functionality broke with commit defa8c0180e.

An alternative would have been to check for a styled knob in the contructor or `initUI` method and to set the legacy flag for these.

The best solution would likely be to create an own class for styled knobs and to pull that functionality out of `Knob` because they somewhat clash in their handling.

* Code review changes

Parameter whitespaces in the builder methods of `Knob`.

Use `adjustedToPixelSize` in `InstrumentTrackView` and `SampleTrackView`.

* Code review changes

Make the code that computes the new fixed size in legacy more readable
even if it is just legacy code that's was not touched. Add some code
documentation.

Other cosmetic changes:
* Whitespace adjustments
* Remove unused parameter in `paintEvent`
* Rename `knob_num` to `knobNum`

* Add documentation for legacy mode

Add some documentation which explains what the effects of legacy mode
are.

* Code review

Remove unnecessary dereference.

Also remove unncessary code repetition by introducing
`currentParamModel`.

* Decrease the label size of some knobs

Decrease the size of the following knob labels to 8 pixels:
* "VOL" and "PAN" in the instrument and sample track views
* "W/D", "DECAY" and "GATE" in the effect view

Technically this is accomplished by introducing
`Knob::buildKnobWithFixedPixelFont` and
`TempoSyncKnob::buildKnobWithFixedPixelFont`.

Both versions of `buildKnobWithSmallPixelFont` now also delegate to
the new methods.

* Adjustments to CrossoverEQControlDialog

Commit the adjustments that were done to `CrossoverEQControlDialog` which I had forgotten to add after fixing the merge.

* Fix formatting of CrossoverEQControlDialog

Fix the formatting of `CrossoverEQControlDialog` which got messed up after copying the code from the current version on GitHub.

* Code review changes

Use `std::max` instead of `qMax`. Remove some unnecessary whitespace.

* Protected legacy mode methods

Make `legacyMode` and `setLegacyMode` protected to ensure that legacy knobs can only be built using the factory method `buildLegacyKnob`. In the long term legacy mode should be removed.

* Code review: remove indexed access

The original request in the code review was to use `size_t` instead of
`uint32_t` in the for-loop. However it is possible to completely remove
the indexed access and to turn it into a simple iterated for-loop.

Also remove code repetition in the calculation of the maximum knob width
of the group. Use std::max instead of manual management.

* Fix u_int16_t to uint16_t

This should hopefully fix the WIndows builds.

* Fix AudioFileProcessor knobs

Fix a problem with the `AudioFileProcessorWaveView::knob` which is caused
by the fact the this knob uses the pixmap based knob type `Bright26`
without a label. Most other knobs that inherit from `Knob` set their knob
type to `Styled` which means that no pixmap is used to render the knob.

In the specific case the knob instance is created and the contructor
runs. In the constructor the AFP knob is set to a fixed size of (37,47).
However, at a later point the method `Knob::changeEvent` is triggered by
Qt due to a font change. This in turn calls `Knob::updateFixedSize` which
then recomputes the fixed size and effectively changes the width of the
knob to the width of the pixmap which is 27. Because the knob previously
was rendered centered with a width of 37 this means that the knob is now
effectively shifted by five pixels to the left.

This commit counters this effect by moving the affected AFP knobs five
pixels to the right. A visual difference between the fixed version and
the current master showed no differences. So this should fix the problem.

Because setting the knob to a fixed size of (37,47) does not have any
lasting effect anyway the code is removed from the constructor of the AFP
knob.

* Use legacy knobs in EffectView

* Legacy knobs for instrument & sample

Use legacy knobs for the instrument and sample track view ("VOL", "PAN").

* Add documentation to Knob builder methods

Add some documentation to the `Knob` builder methods. Mark `buildLegacyKnob` as deprecated and note that it should not be used in new code.

* Ensure legancy rendering for legacy knobs

Ensure that legacy knobs are always rendered at a size of 12 pixels,
i.e. `SMALL_FONT_SIZE`.

The previous implementation used the font metrics of the knob's current
font to compute the new fixed size and to render the label. It assumed
that the knob was created using `Knob::buildLegacyKnob` and that
therefore the font is set to 12 pixels. However, this meant that legacy
knobs can still be affected by style sheet settings. The following CSS
rule for example resulted in legacy knobs with a larger font size:
```
* { font-size: 18px; }
```

The fix is to use a font with a size of `SMALL_FONT_SIZE` when
calculating the fixed size of the `Knob` widget and when rendering it if
we are in legacy mode. This ensures that a legacy knob is unaffected by
CSS rules.

The non-legacy knob still uses the widget's font size and therefore it is
affected by CSS rules. However, this is a feature and not a bug because
when for example using a rule like the one above the knob does exactly
what it's asked to do.

* Remove unused constructor

Remove an unused constructor from CustomTextKnob

* Remove Knob::buildKnobWithSmallPixelFont

Remove the builder method `Knob::buildKnobWithSmallPixelFont` and replace
it with an equivalent new contstructor which takes the same arguments as
the build method.

* Remove Knob::buildKnobWithFixedPixelFont

Remove `Knob::buildKnobWithFixedPixelFont` as it is not used anymore.
Previously it was delegated to by the now removed method
`Knob::buildKnobWithSmallPixelFont`.

* Constructor for knobs with pixel size labels

Remove `TempoSyncKnob::TempoSyncKnob` and add an equivalent constructor.

Make `buildKnobWithSmallPixelFont` use the new constructor.

* Remove TempoSyncKnob::buildKnobWithSmallPixelFont

Remove `TempoSyncKnob::buildKnobWithSmallPixelFont` and make clients use
the new constructor.

* Remove CustomTextKnob::buildKnobWithSmallPixelFont

Remove `CustomTextKnob::buildKnobWithSmallPixelFont` and extend the
constructor so that it also takes a label. Previously all constructions
went through the build method and now all constructions use the extended
constructor.

* Knob constructors whichKnob constructors which take labels

Add constructors for `Knob` and `TempoSyncKnob` which also take the label
text. Make setLabel protected as most knobs should know their labels at
construction time.

This prevents "chatty" code like the following example:
```
Knob* knob = new Knob(KnobType::Bright26, this);
knob->setLabel("My label");
```

This now becomes a simple a one-liner:
```
Knob* knob = new Knob(KnobType::Bright26, "My label", this);
```

The constructor of `KnobControl` also had to be extended with the label
text because it cannot access the setLabel of the Knob that it manages.
However, it can pass the text during construction. Its implementation of
the virtual method `Control::setText` becomes empty due to this. The
`KnobControl` is currently only used in `Lv2ViewProc::Lv2ViewProc`. Here
the `KnobControl` is created by passing the port name into the
constructor. However, the virtual method `setText` is still called in
line 91 for all other implementations.

Add documentation for the constructors.

* Remove Knob::buildLegacyKnob

Remove `Knob::buildLegacyKnob` by extending the very similar constructor
with an enum that indicates whether the constructed `Knob` should be in
legacy mode or not. The default is to build a non-legacy `Knob`.

Wherever `Knob::buildLegacyKnob` was called the constructor is now called
with `Knob::Mode::Legacy` as the parameter. In some places where the
onject name is set `Knob::Mode::NonLegacy` has to be added explicitly.

* Remove TempoSyncKnob::buildLegacyKnob

Remove `TempoSyncKnob::buildLegacyKnob` by extending the very similar
constructor with an enum that indicates whether the constructed
`TempoSyncKnob` should be in legacy mode or not. The default is to
build a non-legacy `TempoSyncKnob`.

Wherever `TempoSyncKnob::buildLegacyKnob` was called the constructor is
now called with `Knob::Mode::Legacy` as the parameter.

* Vertical spacing for Peak Controller

Add a vertical spacing of 10 pixel between the knobs of the Peak Controller.

* Peak Controller: use default margins

Remove the specific call to `setContentsMargins` from the Peak Controller so that the main layout uses Qt's default margins.

Also remove the spacing again.

* Rename the enum `Knob::Mode`

Rename the enum `Knob::Mode` and its values so that they better describe
what they influence.

`Knob::Mode` is renamed to `Knob::LabelRendering` to indicate that its
value affects the label rendering.

The value `NonLegacy` is now called `WidgetFont` to indicate that the
knob uses the font settings of the widget when rendering the label.

The value `Legacy` is now called `LegacyFixedFontSize` to indicate that
it's a legacy behavior which uses a fixed font size that does not adhere
to the font size that's set for the widget's font.

Adjust all callers accordingly.

* Add TempoSyncKnob documentation

Document the constructor of `TempoSyncKnob` that can be used to set the
label rendering to lecacy mode.

* Name adjustments and parameter removal

Rename `m_legacyMode` to `m_fixedFontSizeLabelRendering`.

Rename the method `legacyMode` to `fixedFontSizeLabelRendering`.

Rename `setLegacyMode` to `setFixedFontSizeLabelRendering`. Also remove
the boolean parameter from the method as it was only called with `true`
anyway.
2025-05-29 19:35:48 +02:00
regulus79
7e02795f47 Don't copy DetuningHelper in Note copy constructor (#7888)
Reworks how note detuning copying works so as not to perform a clip duplication and allocation by default in the constructors

---------

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-05-28 19:28:13 -04:00
cyberrumor
41093efcbe Revert "Fix PeakController attack/decay (#7566)" (#7871)
Prior to commit b5de1d50e, audible zipper artifacts were present when
using the PeakController on a bus to control the volume of another bus
for sidechain compression. The bus that had its volume reduced was the
one which produced audible artifacts.

Commit b5de1d50e introduced LERP to PeakController to smooth out its
signal, which eliminated the zipper artifacts. However, this had the
side effect of increased latency even when both attack and decay
settings were set to zero.

Until a more robust solution is implemented, reverting this change
eliminates the latency by eliminating the lerp, but reintroduces audible
zipper artifacts.
2025-05-02 11:04:47 -04:00
regulus79
679f9b848e Add splitting (and resizing) to all types of clips (#7477)
Allow for splitting and resizing all types of clips (automation, MIDI, sample, pattern, etc) using the knife tool in the Song Editor.

---------

Co-authored-by: szeli1 <143485814+szeli1@users.noreply.github.com>
Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-04-29 17:56:13 -04:00
Andrew Wiltshire
d403a54140 Add option to favorite items in the file browser (#7753)
Added the ability to favorite items. This gets added to its own tab named "My Favorites".

---------

Co-authored-by: Sotonye Atemie <sakertooth@gmail.com>
2025-04-27 21:34:37 -04:00
regulus79
510fbf6ffc Global Spacebar Play (#7762)
This PR refactors how the spacebar is handled by the editors and the MainWindow to allow it to play/stop (Shift+Space for play/pause) the last played editor, no matter if it is in focus or not.

---------

Co-authored-by: Fawn <rubiefawn@gmail.com>
Co-authored-by: Sotonye Atemie <satemiej@gmail.com>
2025-04-26 18:26:26 -04:00
regulus79
32c427eab4 Add Midi Reversing (#7606)
Adds the ability to reverse the selected midi notes in the PianoRoll.

The tool is located under the wrench icon in the PianoRoll and can also be triggered with Shift-R.

---------

Co-authored-by: szeli1 <143485814+szeli1@users.noreply.github.com>
2025-04-19 15:40:21 -04:00
regulus79
cb8badc0bb Fix Clip Creation Quantization in Song Editor (#7763)
Previously, clicking on a track or dragging in a sample would always create a clip on the closest/previous bar, no matter what the Song Editor's snap size was. This fixes that issue by using the Song Editor's quantization when placing new clips, so for example if the snap size is 1/4 bar, the clip will be added at the closest/previous 1/4 bar mark.
2025-04-19 15:08:15 -04:00
Cas Pascal
64053342d8 [Follow up] Improve performance when rendering sample waveforms (#7695)
A follow up to 786088baec to fix rendering issues and crashes.

---------

Co-authored-by: Sotonye Atemie <sakertooth@gmail.com>
2025-04-15 23:40:16 -04:00
Tres Finocchiaro
3f0044dc7f Handle SIGINT (#7698)
Handle SIGINT
Allows Ctrl + C to close LMMS for unix-like environments that 
---------

Co-authored-by: Fawn Sannar <rubiefawn@gmail.com>
2025-04-11 10:25:27 -04:00
Fawn
8afe95aeaf Fix track operations button alignment, size, and style (#7779)
Make the track ops button consistently sized and style it and the solo & mute buttons using CSS instead of SVG assets
2025-03-25 19:02:08 -06:00
Sotonye Atemie
1d5f2c0050 Add ability to change sample rate in the settings menu (#7719)
Add a slider in the audio settings menu to allow users to select a few standard sample rates, those being 44100, 48000, 88200, 96000, and 192000.
2025-03-25 16:27:00 -04:00
Rossmaxx
7367750823 [Code Cleanup] Cleaned up some header files and move a bit of debugging logic to cmake (#7677)
* Renamed lmms_basics.h to LmmsTypes.h and scoped it down for that purpose.

* Shifted the LMMS_STRINGIFY macro to it's own header.

* Removed the debug.h header file and use cmake to handle the macro logic.

* Remove some unused headers and include directives
2025-03-24 19:07:46 +05:30
regulus79
db9ccbeb56 Add Strum Tool to Piano Roll (#7725)
Adds a complex strum tool to the Piano Roll, allowing the user to take a selection of chords, and drag around the notes to shape the strum exactly how they want it.

---------

Co-authored-by: szeli1 <143485814+szeli1@users.noreply.github.com>
Co-authored-by: Sotonye Atemie <satemiej@gmail.com>
2025-03-22 05:54:40 -04:00
Fawn
953f6b7351 Rework Crossover EQ plugin GUI (#7781)
* Switches to SVG assets
* Fixes layout issues
* Refactors redundant code

---------

Co-authored-by: Tres Finocchiaro <tres.finocchiaro@gmail.com>
2025-03-20 01:30:29 -04:00
Fawn
7f2761df9b Fix incorrect graph step size behavior (#7703)
The y-axis quantization for graph values was being calculated
incorrectly. This would have gone unnoticed since the only step sizes
currently used are 0 (continuous) or 1. Any other values produce
"sloped" quantization. This fix prevents this error from affecting
any future uses of graphModel.
2025-03-11 16:53:29 -06:00
Fawn
6233c5b9e4 Update mute and solo buttons, add them to instrument windows (#7708)
* Add mute and solo buttons to instrument windows
* Change mute and solo buttons to optimized CC0 SVG assets
* Icons provided by @StakeoutPunch, button backgrounds provided by
@RebeccaDeField

---------

Co-authored-by: Sotonye Atemie <sakertooth@gmail.com>
Co-authored-by: Rebecca Noel Ati <contactme@rebeccadefield.com>
Co-authored-by: Stakeout Punch <StakeoutPunch@users.noreply.github.com>
2025-03-11 14:30:04 -04:00
Michael Gregorius
8821d88c09 InstrumentSoundShaping refactoring (#7229)
* Accessors for volume, cutoff, resonance

Add private accessors for the volume, cutoff and resonance parameters (envelope and LFO).

This makes the code less technical and more readable as it for example removes lots of static casts. The casts are now done in a special helper method that returns the parameters for a given target.

* Remove EnvelopeAndLfoParameters array

Remove the `EnvelopeAndLfoParameters` array and use explicit instances for volume, cutoff, resonance. This simplifies construction and initialization of the instances.

Besides the array this also removes the `Target` enum and the `NumTargets` value.

To simplify storage and retrieval of the parameters three private methods have been added which provide the node names of the volume, cutoff and resonance parameters.

Adjust `InstrumentSoundShapingView` to the removed `NumTargets` property.

* Use references instead of pointers

Use references to the volume, cutoff and resonance parameters instead of pointers.

* Remove friend relationship

Remove the friend relationship between `InstrumentSoundShaping` and its related view by providing the models via getters.

* Get rid of targetNames

Get rid of `InstrumentSoundShaping::targetNames` by using translations and strings directly. Move the remaining stuff into `InstrumentSoundShapingView` until it is removed there as well.

* Explicit EnvelopeAndLfoViews

Remove the array of EnvelopeAndLfoViews and use dedicated instances instead.

This also enables the final removal of the remaining `targetNames`.

* Move the code of some getters

Move the code of some getters into the header file.

* Several code review changes

Apply some code review proposals.

Co-authored-by: Sotonye Atemie <sakertooth@gmail.com>

---------

Co-authored-by: Sotonye Atemie <sakertooth@gmail.com>
2025-03-09 11:25:00 +01:00
Sotonye Atemie
f44aa3edc3 Refactor OGG export and always use VBR (#7697)
Refactors `AudioFileOgg`, a class used to export to OGG files. There were problems reported of the exported OGG file failing to be played back on some systems. To fix this issue as well as to improve code quality, the class was refactored. In addition, VBR (variable bit rate) is always used, with the quality of the export being determined by a ratio of the selected bit rate and the maximum bit rate allowed. This change naturally occurred when refactoring, though the libvorbisenc documentation recommend VBR for improved audio quality.
2025-03-07 10:23:30 -05:00
regulus79
c12fd571f5 Allow cutting multiple notes at once in Piano Roll (#7715)
Adds the ability to cut multiple notes at once in the Piano Roll. Users can select the Knife tool and create a cut line by holding the mouse and dragging it across the notes that should be cut. This also allows cutting the notes at an angle. When releasing the mouse, the Shift key can be pressed to remove the shorter end of the notes that were cut. If any notes are selected, only they will be considered for the cut, even if the cut line covers more notes.
2025-03-02 18:29:29 -05:00
szeli1
5fa01e7dbc Navigate workspace in the main window by dragging the mouse (#7595)
The scroll bars shown in the main window were removed in favor of a new navigation feature that utilizes dragging the mouse instead. Users can now hold the mouse down on an empty part of the workspace and drag the mouse around to navigate where necessary.
2025-03-02 18:03:37 -05:00
Andrew Wiltshire
9159533814 FileBrowser: "Open containing folder" automatically selects file in the OS's file manager (#7700)
Introduces a new class FileRevealer to manage file selection across different platforms (Windows, macOS, Linux, and other *nix operating systems with xdg). Includes methods to open and select files or directories using the default file manager on the respective platform.

---------

Co-authored-by: Tres Finocchiaro <tres.finocchiaro@gmail.com>
Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
Co-authored-by: Sotonye Atemie <sakertooth@gmail.com>
Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-03-01 19:38:51 -05:00
Rossmaxx
050df381b0 Fix Clang warning due to implicit conversion from int to float for the RAND_MAX macro (#7717)
* fix compiler warning due to implicit conversion

* fix warnings from plugins too
2025-03-01 16:48:37 -05:00
Michael Gregorius
3c3441bb0c Faders with a dB scale (instead of linear/percentage) (#7636)
* Use dbFS scale for the faders

Make the faders use a linear dbFS scale. They now also change position on first click and can then be dragged.

The `Fader` class now has a new property called `m_faderMinDb`. It's the minimum value before the amplification drops down fully to 0. It is needed because we cannot represent a scale from -inf to maxDB.

Rename `knobPosY` to `calculateKnobPosYFromModel` and move the implementation into the cpp file. The method now first converts the model's current amplification value to dbFS and then computes a ratio based on that values and the minimum and maximum dbFS values.

Add the method `setVolumeByLocalPixelValue` which takes a local y coordinate of the widget and sets the amplification of the model based on a dbFS scale.

Adjust `mousePressEvent` and `mouseMoveEvent` so that they mostly take the local y coordinate of the event and use that to adjust the model via `setVolumeByLocalPixelValue`.

Remove `m_moveStartPoint` and `m_startValue` and they are not needed anymore.

* Apply curve to faders

Apply a curve, i.e. the cube function and its inverse, to the fader
positions to that we have more space to work with in the "interesting"
areas around 0 dB and less space in the area where we tend to "-inf dB".

Set the minimum dB value of the fader to -120 dB to increase the potential
headroom.

* Support for dB models

Add support for models that are in dB.

There's a new member `m_modelIsLinear` which can be set via the
constructor. The default value in the constructor is `true`.

Add the method `modelIsLinear` which can be used to query the parameter.
It is used in `calculateKnobPosYFromModel` and
`setVolumeByLocalPixelValue`. Both methods got extended to deal with
models in dB. They were also refactored to extract code that is common
for both cases.

Ensure that the constructor of `Fader` is called with `false` for
`CrossoverEQControlDialog` and `EqFader` because these use models that
are in dB.

* Show current dB value of fader in tool tip

Show the current value of the fader in its tool tip. Please note that
this value is always shown in dB because the goal should be to get rid
of the linear values for the faders.

Some of the code is extracted into the new method
`Fader::getModelValueAsDbString` which is shared by
`Fader::modelValueChanged` (also new) and `Fader::updateTextFloat`.

Please note that `getModelValueAsDbString` will use "dB" instead of
"dBFS" as the unit and that it will format "-inf dB" instead of "-∞ dB".
These changes will also be visible in the text float that is used to
show the new values as the fader is being dragged.

* Let users enter values in dB

Let users enter values in dB in the dialog that opens with a double
click.

The minimum value that can be entered is the minimum value that the
fader allows to set, i.e. -120 dB. The maximum value is the maximum
value of the model converted to dB. As of now this is ~6 dB. The
current value is converted to dB. If it corresponds to "-inf dB", i.e.
if the amplification is 0 then the minimum value is used.

* Remove option "Display volume as dBFS"

Remove the option "Display volume as dBFS" from the settings dialog and
the check box option "Volume as dBFS" from the "View" menu. Volumes are
now always treated as dB, i.e. all evaluations of the property
"displaydbfs" have been removed which results in assuming that this
option is always true.

The upgrade code in `ConfigManager::upgrade_1_1_91` was left as is.
However, a note was added which informs the reader that the value of
"displaydbfs" is not evaluated anymore.

* Extend Fader::wheelEvent

Extend `Fader::wheelEvent` for the case where the model is not a dB
model (`modelIsLinear() == true`), e.g. for the mixer faders. In that
case it works as follows. Each step increments or decrements by 1 dB if
no modifier is pressed. With "Shift" pressed the increment value is 3 dB.
With "STRG" ("CTRL") pressed the increment value is reduced to 0.1 dB. If
the value goes below the minimum positive dB value that is allowed by the
fader then the fader is set to "-inf dB", i.e. zero amplification. If the
fader is set to "-inf dB" and the users want to increase the value then
it is first set to the minimum positive value that is allowed by the
fader.

If the model is a dB model then the same behavior as before is used.
Although it should be considered to adjust this case as well. These
models are used by the faders of the Crossover Equalizer, Equalizer,
Compressor and Delay and they are not very usable with the mouse wheel.

* Adjust the wheel behavior for faders with dB models

Make the faders of the Crossover Equalizer, Equalizer, Compressor and
Delay behave like the mixer faders, i.e. step in sizes of 3 dB, 1dB and
0.1 dB depending on whether a modifier key is pressed or not.

Extract some common code to do so and add some `const` keywords.

* Less "jumpy" knobs

Implement a more stable knob behavior.

Remove the jumping behavior if the users directly click on a volume
knob. By storing the offset from the knob center and taking it into
account during the changes it now also feels like the users are
dragging the knob.

Changes to the amplification are now only applied when the mouse is
moved. This makes the double click behavior much more stable, i.e. if
users click on the knob when it is at 0 dB the dialog will also show
0 dB and not something like 0.3 dB because the first click is already
registered as a change of volume.

If the users click next to the knob the amplification will still be
changed immediately to that value.

## Technical details

To make the knobs more stable a variable called `m_knobCenterOffset` was
introduced. It stores the offset of the click from the knob center so
that this value can be taken into account for in the method
`setVolumeByLocalPixelValue`.

* Make MSVC happy

Add an indication that a float value is assigned to a float variable.

* Introduce constexpr for scaling exponent

Introduce the `constexpr c_dBScalingExponent` which describes the
scaling exponent that's used to scale the dB scale in both directions.
This will simplify potential adjustments by keeping the values
consistent everywhere.

* Draw fader ticks

Draw fader ticks in case the model is a linear one. This means that for
now they should only be painted for the mixer faders but not for the
faders of the Compressor, Delay, etc.

Extract the computation of the scaled ratio between the maximum model dB
value and the minimum supported fader dB value into the new private
method `computeScaledRatio`. This is necessary because it is needed to
paint the fader knob at the correct position (using the knob bottom as
the reference) and to paint the fader ticks at the correct position
(using the knob center).

Introduce the private method `paintFaderTicks` which paints the fader
ticks.

Note: to paint some non-evenly spaced fader ticks replace the `for`
expression in `paintFaderTicks` with something like the following:
```
for (auto & i : {6.f, 0.f, -6.f, -12.f, -24.f, -36.f, -48.f, -60.f, -72.f, -84.f, -96.f, -108.f, -120.f})
```

* Fader adjustments via keyboard

Allow the adjustment of the faders via the keyboard. Using the up or
plus key will increment the fader value whereas the down or minus key
will decrement it. The same key modifiers as for the wheel event apply:
* No modifier: adjust by 1 dB
* Shift: adjust by 3 dB
* Control: adjust by 0.1 dB

Due to the very similar behavior of the mouse wheel and key press
handling some common functionality was factored out:
* Determinination of the (absolute) adjustment delta value by
  insprecting the modifier keys of an event. Factored into
  `determineAdjustmentDelta`.
* Adjustment of the model by a given dB delta value. Factored into
  `adjustModelByDBDelta`.

* Move the fader of the selected channel

Move the fader of the selected channel instead of the fader that has
focus when the up/plus or down/minus keys are pressed. Doing so also
feels more natural because users can already change the selected
channel via the left and right keys and now they can immediately adjust
the volume of the currently selected channel while doing so.

Key events are now handled in `MixerView::keyPressEvent` instead of
`Fader::keyPressEvent` and the latter is removed. `MixerChannelView`
now has a method called `fader` which provides the associated fader.
This is needed so that the event handler of `MixerView` can act upon
the currently selected fader.

## Changes in Fader
The `Fader` class provides two new public methods.

The `adjust` method takes the modifier key(s) and the adjustment
direction and then decides internally how the modifier keys are mapped
to increment values. This is done to keep the mapping between modifier
keys and increment values consistent across different clients, e.g. the
key event of the `MixerView` and the wheel event of the `Fader` itself.
The direction is provided by the client because the means to determine
the direction can differ between clients and cases, e.g. a wheel event
determines the direction differently than a key event does.

The method `adjustByDecibelDelta` simply adjusts the fader by the given
delta amount. It currently is not really used in a public way but it
still makes sense to provide this functionality in case a parent class
or client wants to manipulate the faders by its very own logic.

Because the `Fader` class does not react itself to key press events
anymore the call to `setFocusPolicy` is removed again.

* Enter fader value when space key pressed

Let the users enter the fader value via dialog when the space key is
pressed.

Extract the dialog logic into the new method `adjustByDialog` and call
it from `MixerView::keyPressEvent`.

Make `Fader::mouseDoubleClickEvent` delegate to `adjustByDialog` and
also fix the behavior by accepting the event.

* More prominent fader ticks around 0 dB

Make the fader ticks around 0 dB more prominent but drawing them
slightly wider and with a more opaque color.

* Work around a Qt bug

Work around a Qt bug in conjunction with the scroll wheel and the Alt
key. Simply return 0 for the fader delta as soon as Alt is pressed.

* Fix wheel events without any modifier

Fix the handling of wheel events without any modifier key being pressed.

Commit ff435d551b accidentally tested against Alt using a logical OR
instead of an AND.

* Code review changes

First set of code review changes:
* Use Doxygen style documentation comments
* Remove comment about `displaydbfs` from upgrade routine
* White-space changes for touched lines.

* Make minimum dB value a constexpr

Make the minimum dB value a constexpr in the implementation file because
currently it's an implementation detail that should not be of any
interest to any other client.

So `m_faderMinDb` becomes `c_faderMinDb`.

* More flexible painting of fader ticks

Paint the fader ticks in a more systematic and flexible way. This also
removes some "magic numbers", e.g. by using `c_faderMinDb` instead of
`-120.f` as the lower limit.

The upper limit, i.e. the "starting point" is now also computed using
the maximum value of the model so that the fader will still paint
correctly if it ever changes.

* Make the zero indicator bolder

Make the zero indicator tick of the fader bolder.

* Make rendering of fader ticks a preference

Make rendering of fader ticks a preference which is off by default.

Introduce the new option "Show fader ticks" to the setup dialog and save
it to the UI attribute `showfaderticks`.

The configuration value is currently evaluated in `Fader::paintEvent`.
If this leads to performance problems it might be better to introduce a
boolean member to the `Fader` class which caches that value.

* Move constexprs to anonymous namespace
2025-03-01 20:08:04 +01:00
Johannes Lorenz
3aa1a5dafa Rename AudioPort -> AudioBusHandle (#7712)
No functional changes.
2025-02-28 23:44:58 +01:00
superpaik
a4c91f8ba0 Recursively unmute channels when soloing in the mixer (#6746)
* unmute related FX channels

When soled one FX channel, unmute send and receive channels, to allow complex FX channel routing (BUS, SENDs, etc.)

* Comment change

* SEND channels

Activate also SEND channels recursively

* Remove unnecessary whitespace

* Encapsulate mixer channel index

Make the mixer channel index `m_channelIndex` private and add getters and
setters. Also add a method to determine if the channel is the master
channel.

Move `m_channelIndex` to the end of the initialization list of the
`MixerChannel` constructor because it is now the last member in the
header.

Adjust all clients to make use of the new methods.

* Replace const int& getIndex() with int index() const

* Format changes

---------

Co-authored-by: Michael Gregorius <michael.gregorius.git@arcor.de>
Co-authored-by: Sotonye Atemie <sakertooth@gmail.com>
2025-02-28 09:52:41 -05:00
Kevin Zander
9b04e29c4e Remove song import global automation (#5229)
Co-authored-by: Hyunjin Song <tteu.ingog@gmail.com>
2025-02-22 14:24:29 +05:30
Tres Finocchiaro
055e0ba576 Fix additional warnings when Carla is missing (#7722)
Fix additional warnings when Carla is missing
Adds new env var LMMS_EXCLUDE_LADSPA

---------

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-02-21 00:56:04 -05:00
Tres Finocchiaro
7725d0024e Allow plugins to be skipped at runtime (#7691)
Allows plugins (such as carla) to be skipped when not installed
2025-02-18 01:37:54 -05:00
Fawn
e328a136fc Use C++20 std::numbers, std::lerp() (#7696)
* use c++20 concepts and numbers for lmms_constants.h

* replace lmms::numbers::sqrt2 with std::numbers::sqrt2

* replace lmms::numbers::e with std::numbers::e
Also replace the only use of lmms::numbers::inv_e with a local constexpr instead

* remove lmms::numbers::pi_half and lmms::numbers::pi_sqr
They were only used in one or two places each

* replace lmms::numbers::pi with std::numbers::pi

* add #include <numbers> to every file touched so far
This is probably not needed for some of these files. I'll remove those later

* Remove lmms::numbers

Rest in peace lmms::numbers::tau, my beloved

* Add missing #include <numbers>

* replace stray use of F_EPSILON with approximatelyEqual()

* make many constants inline constexpr
A lot of the remaining constants in lmms_constants.h are specific to
SaProcessor. If they are only used there, shouldn't they be in SaProcessor.h?

* ok then, it's allowed to be signed

* remove #include "lmms_constants.h" for files that don't need it
- And also move F_EPSILON into lmms_math.h
- And also add an overload for fast_rand() to specify a higher and lower bound
- And a bunch of other nonsense

* ok then, it's allowed to be inferred

* ok then, it can accept an integral

* fix typo

* appease msvc

* appease msvc again

* Replace linearInterpolate with std::lerp()

As well as time travel to undo several foolish decisions and squash tiny
commits together

* Fix msvc constexpr warnings

* Fix msvc float to double truncation warning

* Apply two suggestions from code review

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* Apply suggestions from code review

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* fix silly mistake

* Remove SlicerT's dependence on lmms_math.h

* Allow more type inference on fastRand() and fastPow10f()

* Apply suggestions from code review

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* Clean up fastRand() a little bit more

---------

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-02-13 13:15:08 -05:00
Tres Finocchiaro
30216aac51 macOS: Change drag copy shortcut from Command to Option (#7325)
macOS: Replace Command + Drag shortcut key with Option + Drag
Add new header `KeyboardShortcuts.h` for platform-specific keyboard mappings

---------

Co-authored-by: Michael Gregorius <michael.gregorius.git@arcor.de>
2025-02-09 02:50:38 -05:00
Fawn
4a089a19dc Update math functions to C++ standard library (#7685)
* use c++ std::* math functions
This updates usages of sin, cos, tan, pow, exp, log, log10, sqrt, fmod, fabs, and fabsf,
excluding any usages that look like they might be part of a submodule or 3rd-party code.
There's probably some std math functions not listed here that haven't been updated yet.

* fix std::sqrt typo

lmao one always sneaks by

* Apply code review suggestions
- std::pow(2, x) -> std::exp2(x)
- std::pow(10, x) -> lmms::fastPow10f(x)
- std::pow(x, 2) -> x * x, std::pow(x, 3) -> x * x * x, etc.
- Resolve TODOs, fix typos, and so forth

Co-authored-by: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com>

* Fix double -> float truncation, DrumSynth fix

I mistakenly introduced a bug in my recent PR regarding template
constants, in which a -1 that was supposed to appear outside of an abs()
instead was moved inside it, screwing up the generated waveform. I fixed
that and also simplified the function by factoring out the phase domain
wrapping using the new `ediv()` function from this PR. It should behave
how it's supposed to now... assuming all my parentheses are in the right
place lol

* Annotate magic numbers with TODOs for C++20

* On second thought, why wait?

What else is lmms::numbers for?

* begone inline

Co-authored-by: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com>

* begone other inline

Co-authored-by: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com>

* Re-inline function in lmms_math.h

For functions, constexpr implies inline so this just re-adds inline to
the one that isn't constexpr yet

* Formatting fixes, readability improvements

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* Fix previously missed pow() calls, cleanup

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* Just delete ediv() entirely lmao

No ediv(), no std::fmod(), no std::remainder(), just std::floor().
It should all work for negative phase inputs as well. If I end up
needing ediv() in the future, I can add it then.

* Simplify DrumSynth triangle waveform

This reuses more work and is also a lot more easy to visualize.

It's probably a meaningless micro-optimization, but it might be worth changing it back to a switch-case and just calculating ph_tau and saw01 at the beginning of the function in all code paths, even if it goes unused for the first two cases. Guess I'll see if anybody has strong opinions about it.

* Move multiplication inside abs()

* Clean up a few more pow(x, 2) -> x * x

* Remove numbers::inv_pi, numbers::inv_tau

* delete spooky leading 0

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

---------

Co-authored-by: Rossmaxx <74815851+Rossmaxx@users.noreply.github.com>
Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-02-08 23:50:02 -05:00
Dominic Clark
cb7c6d16cb Reload stylesheet when CSS file changes (#6331)
* Reload stylesheet when CSS file changes

---------

Co-authored-by: Tres Finocchiaro <tres.finocchiaro@gmail.com>
2025-02-08 23:33:44 -05:00
Cas Pascal
786088baec Improve performance when rendering sample waveforms (#7366)
This PR attempts a number of improvements to the sample rendering (in the song editor, automation editor, AudioFileProcessor, SlicerT, etc) in LMMS:

Thumbnails: Samples are aggregated into a set of thumbnails of multiple resolutions. The smallest larger thumbnail is then chosen for rendering during sample drawing. Each set of thumbnails is stored with its sample file metadata in an unordered_map, so that duplicate samples will use the same set of thumbnails.

Partial rendering: additionally, this PR only renders the portions of the sample clips visible to the viewer to save rendering time.

* Experimental sample thumbnail

* Rename some classes and type aliases. Make some type declarations explicit

* Use a combination of audioFile name and shared_ptrs to track samples; refactor some loops

* That weird line at the end of the sample is now gone

* Small changes to the code; Add comments

* Add missing word to comment; Fix typo

* Track `SharedSampleThumbnailList`s instead

* Major refactor; implement thumbnailing for SlicerT, AFP and Automation editor

* Code clean up, renames and documenting

* Add the namespace lmms comments

* More code updates and documentation

* Fix error in comment

* Comment out `qDebug`s

* Fix formatting

* Use alternative initialization of `SampleThumbnailVisualizeParameters`

* Remove commented code

* Fix style and simplify code

* Use auto

* Draw lines using floating point

* Merge the classes into one nested class
+ Replace while loop with std::find_if

* Fix comparison of different signedness

* Include memory header

* Fix a logic error when selecting samples; Rename a const

* Fix more issues with signedness

* Fix sample drawing error in `visualizeOriginal`

* Only render regions in view of the sample

* Allow partial repaints of sample clips

* Remove unused variable

* Limit most of the painting to the visible region

* Revert back to using rect() in some places

* Partial rendering for AutomationEditor

* Don't redraw painted regions; allowHighResolution; remove `visualizeOriginal`; Remove s_sampleThumbnailCacheMap

* Add s_sampleThumbnailCacheMap back for testing convenience

* Minor change to the way `thumbnailSizeDivisor` is calculated

* Extend update region by an amount

* forgot about this

* Adapt to master; Redesign VisualizeParameters; Don't rely entirely on needsUpdate()

* Don't try to preserve painted regions

* Allow for a bit more thumbnails; Fix incorrect rendering when vertically scrolling

* Fix missing include statement

* Remove the unused variable

* Code optimization; Remove RMS member from Bit; Rename viewRect to drawRect

* More code optimizations

* Fix formatting

* Use begin instead of cbegin

* Improve generation of thumbnails

* Improve expressiveness of the draw code

* Add support for reversing

* Fix drawing code

* Fix draw code (part 2)

* Apply more fixes and simplifications

* Undo some out of scope changes

* Remove SampleWaveform

* Improve documentation

* Use size_t for some counters

* Change width parameter to be size_t

* Remove temporary aggregated peak variable

* Bump up AggregationPerZoomStep to 10

* Zoom out only requested range of thumbnail instead of clipping it separately

* Rename targetSampleWidth to targetThumbnailWidth

* Handle reversing for AFP; Iterate in reverse instead of reversing the entire thumbnail

* Change names to be more accurate

* Improve implementation of sample thumbnail cache map

* Move AggregationPerZoomStep back down to 2, do not cap smallest thumbnail width to display width
To improve performance with especially long
samples (~20 minutes)

* Simplify sample thumbnail cache handling in constructor

* Call drawLines instead of drawLine in a loop

QPainter::drawLine calls drawLines with a line count of 1. Therefore, calling drawLine in a loop means we are simply calling drawLines a lot more times than necessary.

* Bump up AggregationPerZoomStep to 10 again
Thought using 2 would help performance (when rendering). Maybe it does, but it's still quite slow when rendering a bunch of thumbnails at once.

* Fix off-by-one error when constructing Thumbnail from buffer

* Fix crash when viewport is not in bounds

* Apply performance improvements

Performance in the zoomOut function was bad because of the dynamic memory allocation. Huge chunks of memory were being allocated quite often, casuing a ton of cache misses and all around slower performance. To fix this, all the necessary downsampling is now done within the for loop and handled one pixel after another, instead of all at once.

To further avoid allocations in the draw call, the change to use drawLines has been reverted.

We now pass VisualizeParameters by value (it is only 64 bytes, which will fit quite nicely in a cache line, which is more benefical than reaching for that data by reference to some other part of the code).

After applying these changes, we are now practically only bounded by the painting done by Qt (according to profiling done by Valgrind). Proper use of OpenGL could resolve this issue, which should finally make the performance quite optimal in  variety of situations.

* Apply minor changes

Update copyright and unused functions. Move in newly created thumbnail into the cache instead of copying it.

* Use C++20's designated initializers

* Create param right before visualizing

* Fix regressions with reversing

* Fix incorrect rendering in AFP and SlicerT

* Move MaxSampleThumbnailCacheSize and AggregationPerZoomStep into implementation file

* Remove static keyword

* Remove getter and setter for peak data

---------

Co-authored-by: Sotonye Atemie <sakertooth@gmail.com>
2025-02-08 20:05:19 -05:00
Tres Finocchiaro
f38c649923 SharedMemory: Make key optional, default to shorter UID on macOS (#7681)
* SharedMemory: Make key optional, shorter on macOS
* Add getters for shared memory size
* macOS: Fix linking regression for RemoteZynAddSubFx introduced with #7252 

---------

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-02-03 14:18:02 -05:00
Fawn
9aa937d391 Use templates for common geometric constants (#7558)
* add templates for common geometric constants

* oops missed one

* LD_2PI -> LD_PI

i re-added the wrong constant ffs

* CamelCase names and also verify compilation without -DLMMS_MINIMAL

* C++20 stuff

Updated to account for `<numbers>` and C++20:
- Marked all `lmms_constants.h` constants with an exact equivalent in `<numbers>` as deprecated
- Removed all `lmms_constants.h` constants where no variant is currently in use
- Using `inline constexpr`
- Using `std::floating_point` concept instead of `typename`

* add lmms::numbers namespace

* Remove panning_constants.h

Moves the four constants in panning_constants.h into panning.h, then
removes panning.h.

* Use std::exp(n) instead of powf(numbers::e, n)

* Use C++ std math functions

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>

* Use overloaded std math functions

An attempt to fix compiler warnings on some platforms

* Remove uses of __USE_XOPEN

And also update two functions I missed from the previous commit

* Missed a few

* Fix ANOTHER std math function use

Of course there's another one

---------

Co-authored-by: Dalton Messmer <messmer.dalton@gmail.com>
2025-02-03 11:52:13 -05:00
Sotonye Atemie
77ca0f8994 Improve mono compatibility with LADSPA plugins (#7674) 2025-01-31 00:58:37 -05:00
Michael Gregorius
303215f8b1 Maximize button for resizable instruments (#7514)
* Maximize button for resizable instruments

Show the maximize button for resizable instruments.

Most other changes have the character of refactorings and code
reorganizations.

Remove the negation in the if condition for resizable instruments to
make the code better readable.

Only manipulate the system menu if the instrument is not resizable.

Add a TODO to the special code that sets a size.

* Fix rendering of maximized sub windows

In `SubWindow::paintEvent` don't paint anything if the sub window is
maximized . Otherwise some gradients are visible behind the maximized
child content.

In `SubWindow::adjustTitleBar` hide the title label and the buttons if the
sub window is maximized. Always show the title and close button if not
maximized. This is needed to reset the state correctly after
maximization.

* Add SubWindow::addTitleButton

Add the helper method `SubWindow::addTitleButton` to reduce code
repetition in the constructor.

* Only disable the minimize button

Disable the minimize button by taking the current flags and removing
the minimize button hint from them instead of giving a list which might
become incomplete in the future. So only do what we want to do.

* Remove dependency on MdiArea

Remove a dependency on the `MdiArea` when checking if the sub window is
the active one. Query its own window state to find out if it is active.

* Clear Qt::MSWindowsFixedSizeDialogHint

Clear the `Qt::MSWindowsFixedSizeDialogHint` flag for resizable
instruments (symmetric to the `else` case).

* Update the sub window title bar of exchanged instruments

Update the title bar of an instrument's sub window if the model changes, e.g. if an instrument is exchanged via drag & drop.

The main fix is to call the new method `updateSubWindowState` in `InstrumentTrackWindow::modelChanged`. It contains mostly the code that was previously executed in the constructor of `InstrumentTrackWindow`. The constructor now simply calls this method after it has put the constructed instance into a sub window.

With the current implementation the sub window needs to be explicitly triggered to update its title bar once the flags have been adjusted in `updateSubWindowState`. This is done with the new public method `SubWindow::updateTitleBar`. Please note that such an explicit update is not needed if the instrument windows are managed by a `QMdiSubWindow` instead of a `SubWindow`. This means that the implementation of `SubWindow` is still missing something that `QMdiSubWindow` does. However, debugging also showed that setting the window flags of the sub window does not seem to lead to an event that could be caught in `SubWindow::changeEvent`. This was found out by simply dumping the event types of all events that arrive in that method and exchanging an instrument.

The method `updateSubWindowState` uses the added method `findSubWindowInParents` to find the sub window it is contained in. The latter method should be considered to be moved into a templated helper class because it might be useful in other contexts as well.

## Technical details

If you want to experiment with using QMdiSubWindows then simply add the following method to `MainWindow` (right next to `addWindowedWidget`):
```
QMdiSubWindow* MainWindow::addQMdiSubWindow(QWidget *w, Qt::WindowFlags windowFlags)
{
	// wrap the widget in our own *custom* window that patches some errors in QMdiSubWindow
	auto win = new QMdiSubWindow(m_workspace->viewport(), windowFlags);
	win->setAttribute(Qt::WA_DeleteOnClose);
	win->setWidget(w);

	m_workspace->addSubWindow(win);
	return win;
}
```

Then call that method instead of `addWindowedWidget` in the constructor of `InstrumentTrackWindow`:
```
QMdiSubWindow* subWin = getGUI()->mainWindow()->addQMdiSubWindow( this );
```

You can then comment out the cast and the call of `updateTitleBar` in `updateSubWindowState` and everything will still work.

* Update the system menu

Show or hide the "Size" and "Maximize" entries in the system menu
depending on whether the instrument view is resizable or not.

* Show non-resizable instruments as normal

Show the sub windows of non-resizable instruments as normal if the sub
window is maximized because it was previously used with a resizable
instrument.

* Fix typo

* Rename updateSubWindowState

Rename `updateSubWindowState` to `updateSubWindow`.
2025-01-01 14:25:40 +01:00
Oskar Wallgren
3562bbed3c Open up some gui elements to theming (#7314)
* Theming for current step note

* Theming for EnvelopeGraph

* Theming for LfoGraph

* curStepNoteColor - don't break old themes

* EnvelopeGraph - don't break old themes

* LfoGraph - don't break old themea

* currentStepNoteColor
2024-11-30 19:24:45 +05:30
Dalton Messmer
c21a7cd487 Upgrade to C++20 (#7554)
* Compile in C++20 mode

* Fix implicit lambda captures of `this` by `[=]`

Those implicit captures were deprecated in C++20

* Silence MSVC atomic std::shared_ptr warning

Unfortunately std::atomic<std::shared_ptr> (P0718R2) is not supported by
GCC until GCC 12 and still is not supported by Clang or Apple Clang, so
it looks like we will continue using std::atomic_load for the time being

* Use C++20 in RemoteVstPlugin

* Simplification

* Add comment

* Fix bitwise operations between different enumeration types

* Revert "Fix bitwise operations between different enumeration types"

This reverts commit d45792cd72.

* Use a helper function to combine keys and modifiers

* Remove AnalyzeTemporaryDtors from .clang-tidy

AnalyzeTemporaryDtors was deprecated in clang-tidy 16 and fully removed
in clang-tidy 18

* Use C++20 in .clang-format

* Use bitwise OR

Prevents issues if any enum flags in `args` have bits in common
2024-11-22 23:11:39 -05:00
Rossmaxx
26d5241dd7 Optimise usage of pow using fast equivalent and exp2 (#7548)
* replace std::pow with better performing equivalents

* revert one instance where I swapped to fastPow10f

* Negative slope instead of multiplying -1

Co-authored-by: saker <sakertooth@gmail.com>

---------

Co-authored-by: saker <sakertooth@gmail.com>
2024-11-21 14:29:51 +05:30
Dalton Messmer
e36463ce77 Update macOS CI (#7572)
* Use macOS 13

See: https://github.com/actions/runner-images/issues/10721

* Upgrade to XCode 15.2

XCode 15.2 is the default on macOS 13

* Fix unqualified call to std::move warning

* Fix sprintf deprecated warnings

* Upgrade macOS 14 ARM64 builds to XCode 15.4

See: https://github.com/actions/runner-images/issues/10703

* Fix unused lambda capture warnings in Fader.cpp

* Fix unused variable warnings

* Fix formatting warning

Cannot format `const void*` as a string

* Force lambda conversion to function pointer
2024-11-06 17:46:12 -05:00
saker
07baf9e27a Tidy up MixerChannelView (#7527) 2024-11-03 02:13:55 -05:00
Rossmaxx
9912fd88e3 Add member variable for base sample rate and inline sample rate getter functions (#7552)
Co-authored-by: saker <sakertooth@gmail.com>
2024-10-30 19:55:11 -04:00
cyberrumor
b5de1d50e1 Fix PeakController attack/decay, use linear interpolation between samples (#7566)
Historically, the PeakController has had issues like attack/decay knobs
acting like on/off switches, and audio artifacts like buzzing or
clicking. This patch aims to address those issues.

The PeakController previously used lerp (linear interpolation) when
looping through the sample buffer, which was in updateValueBuffer. This
lerp utilized attack/decay values from control knobs. This is not the
correct place to utilize attack/decay because the only temporal data
available to the function is the frame and sample size. Therefore the
coefficient should simply be the sample rate instead.

Between each sample, processImpl would set m_lastSample to the RMS
without any sort of lerp. This resulted in m_lastSample producing
stair-like patterns over time, rather than a smooth line.

For context, m_lastSample is used to set the value of whatever is
connected to the PeakController. The basic lerp formula is:

m_lastSample = m_lastSample + ((1 - attack) * (RMS - m_lastSample))

This is useful because an attack of 0 sets m_lastSample to RMS, whereas
an attack of 1 would set m_lastSample to m_lastSample. This means our
attack/decay knobs can be used on a range from "snap to the next value
immediately" to "never stray from the last value".

* Remove attack/decay from PeakController frame lerp.
* Set frame lerp coefficient to 100.0 / sample_rate to fix buzzing.
* Add lerp between samples for PeakController to fix stairstep bug.
* The newly added lerp utilizes (1 - attack or decay) as the
  coefficient, which means the knobs actually do something now.
2024-10-28 15:01:08 -04:00
saker
1f37c9ba7c Inline TimePos and TimeSig functions to improve performance (#7549)
* Inline TimeSig functions

* Inline TimePos functions
2024-10-26 12:41:46 -04:00
Lost Robot
b8b1dae407 Optimize dBFS/amplitude conversion functions (#7535)
* Optimize dBFS <-> amplitude functions
2024-10-15 17:59:47 +05:30
Johannes Lorenz
97b61bbd9a Make PluginView::isResizable a virtual (#7541)
Remove the member `PluginView::m_isResizable` and it's associated method `setResizable`. Turn `isResizable` into a virtual method.

The reasoning is that whether or not an effect can be resized depends on its implementation. Therefore does not make sense to have a method like `setResizable`. If the underlying implementation does not support resizing then it would not make sense to call `setResizable(true)`. So `isResizable` now describes the underlying ability of a plugin to resize. It's then up to the clients of that method to decide how to treat the result of `isResizable`, i.e. if they want to make use of the ability to resize or not.
2024-10-12 10:39:42 +02:00
Michael Gregorius
fb5516cfdc Track operations widget with layout (#7537)
Put the elements of the `TrackOperationsWidget` into layouts. These are:
* The grip that can be used to move tracks
* The gear icon that opens the operations menu
* The mute button
* The solo button

The grip that can be used to move tracks around is extracted into its own class called `TrackGrip`. This has several advantages:
* It can be put into a layout.
* It can render itself at arbitrary sizes by simply repeating its pattern pixmap.
* It can be used in a much more object-oriented way because it emits signals when it is grabbed and released.
* It is responsible for locally updating its cursor state.

The default cursor of the grip now is an open hand which indicates to the users that it can be grabbed. While being grabbed the cursor now is a closed hand.

## Technical details
The class `TrackOperationsWidget` now holds an instance of `TrackGrip` and provides a getter to retrieve it. This getter is used by `TrackView` to connect to the two new signals `grabbed` and `released`. The method `TrackOperationsWidget::paintEvent` now only paints the background as it does not need to paint the grip anymore.

The `TrackView` now handles the grabbing and release of the grip in `TrackView::onTrackGripGrabbed` and `TrackView::onTrackGripReleased`. Because the events and cursor states are now handled by `TrackGrip` this code could be removed from `TrackView::mousePressEvent`.

There was a comment in `TrackView` which indicated that the `TrackOperationsWidget` had to be updated when the track is moved and released because it would hide some elements during the move. The comment and the corresponding code was removed because the operations widget does not hide its elements during moves (this was already the state before the changes made by this commit).

Adjust the style sheets of the classic and default themes with regards to the `QPushButton` that's used to show the gear menu in the `TrackOperationsWidget`. The `>` has been removed because the `QPushButton` is not a direct decendent of the `TrackOperationsWidget` anymore.

### Wrapping of `PixmapButton` in `QWidget`
The PixmapButtons that are used in `TrackOperationsWidget` current have to be wrapped into a `QWidget`. This is necessary due to some strange effect where the PixmapButtons are resized to a size that's larger than their minimum/fixed size when the method `show` is called in `TrackContainerView::realignTracks`. Specifically, with the default theme the buttons are resized from their minimum size of (16, 14) to (26, 26). This then makes them behave not as expected in layouts.

The resizing is not done for QWidgets. Therefore we wrap the PixmapButton in a QWidget which is set to a fixed size that will be able to show the active and inactive pixmap. We can then use the QWidget in layouts without any disturbances.

The resizing only seems to affect the track view hierarchy and is triggered by Qt's internal mechanisms. For example the buttons in the mixer view do not seem to be affected.

If you want to debug this simply override "PixmapButton::resizeEvent" and trigger a break point in there, e.g. whenever the new size is not (16, 14).

### More layout-friendly PixmapButton

Make the `PixmapButton` more friendly for layouts by implementing `minimumSizeHint`. It returns a size that accommodate to show the active and the inactive pixmap.

Also make `sizeHint` return the minimum size hint. The previous implementation would have made layouts jump when the pixmap is toggled with pixmaps of different sizes.
2024-10-12 10:02:56 +02:00