Skip to content

Conversation

@mmeeaallyynn
Copy link
Contributor

This adds support for the Jack backend for recording Sample Tracks.

Based on #5990

@JohannesLorenz
Copy link
Contributor

@mmeeaallyynn While the PR diff itself looks correct, the commit list (30 commits by different people) looks bad. Do you mind doing a git rebase master, followed by a git push -f? Another way would be a git reset master, followed by a git commit and a git push -f.

@mmeeaallyynn mmeeaallyynn force-pushed the sample-track-recording-jack branch from aab14d8 to f83a2f1 Compare November 24, 2024 11:11
@mmeeaallyynn
Copy link
Contributor Author

Thanks for the hint, I hope it looks good now.

Copy link
Contributor

@JohannesLorenz JohannesLorenz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I did:

  1. Functional code review
  2. Style review
  3. Testing review

Possible further reviews:

  1. Second functional review (optional)
  2. Consolidation with other developers (as other developers were active here already)

@JohannesLorenz
Copy link
Contributor

This should possibly not be merged into master, but into #5990, though other devs should consolidate this decision. I also wrote a comment: #5990 (comment). Nonetheless, the comments can be fixed here, too.

@firewall1110
Copy link
Contributor

This should possibly not be merged into master, but into #5990, though other devs should consolidate this decision.

I think, it is bad idea, that only confuse author:

This PR should be reviewed (possibly - improved) independently (and this is hard - testing is near not possible).

P.S.
It was not so bad at 2024.12.29 ...

@firewall1110
Copy link
Contributor

To author:

If You decide to integrate #5990 into this (this would be adequate to PR title), You should complete SampleRecordHandle.h , SampleRecordHandle.cpp - enabling recording to file, not only in memory.

Only in this case You will be able save recorded date to audio file (not to project file - as it is done in #5990 )

You should also make comment in #5990 , if You decide to integrate #5990 into this PR.

P.S.
My opinion is, that You should only enable audio input in Jack driver and may be change jack driver configuration gui (see #7421 ) if this is not already done. But it is only my opinion.

@JohannesLorenz
Copy link
Contributor

@mmeeaallyynn Can you please solve the comments and then close this PR and instead open a PR from your branch against the #5990 branch? Or do you not want to continue contributing to this topic and someone else needs to do it?

@michaelgregorius
Copy link
Contributor

Sample Track Recording Stage One #5990 is staled and also last PR (Sample Track Recording Stage One #5990) active developer is not more active (Farewell! #7659 , and Sample Track Recording Stage One #5990 is not in my open pull requests )

While I am indeed not actively coding on LMMS anymore I'd like to point out that #5990 was not started by me anyway and that it rather was worked on collaboratively at a certain point. So me leaving should hopefully not really stall this PR.

I have added this comment though where I describe what I think stalls that PR: #5990 (comment)

@JohannesLorenz JohannesLorenz changed the base branch from master to feature/recording-stage-one March 15, 2025 19:10
@JohannesLorenz
Copy link
Contributor

JohannesLorenz commented Mar 15, 2025

I changed the target branch to upstream (LMMS/lmms #7786 ), but this did not work too well. It looks like GH does a complete rebase and fails with that. It's probably better to open a new PR which starts at #7786 (feature/recording-stage-one) and simply adds #7786 . @mmeeaallyynn Can you please do that?

Edit: It might work just to git rebase upstream/feature/recording-stage-one (assuming the remote is named "upstream") and then git push -f.

@JohannesLorenz
Copy link
Contributor

Edit: It might work just to git rebase upstream/feature/recording-stage-one (assuming the remote is named "upstream") and then git push -f.

I'll just try it out myself now...

@JohannesLorenz JohannesLorenz force-pushed the sample-track-recording-jack branch 2 times, most recently from ebc3ad4 to 75ea709 Compare March 22, 2025 19:47
@JohannesLorenz
Copy link
Contributor

After this PR has been refactored to not contain the #7786 changes, I think it is really independent of #7786 . Actually, this PR just adds a feature for Jack that we already have for SDL.

So I think this should directly go from master to master - i.e. only add those 2 Jack files to master. This is atomic enough. Can anyone agree?

@michaelgregorius
Copy link
Contributor

@JohannesLorenz, continuing here so that it does not feel like writing on the margin of a piece of paper. 😆

Quoting your comment from the commit comment:

@michaelgregorius I updated the code with ecd6a63. It looks like this code does not make any assumptions on the channel number (at least, not more than the previous code). I guess you don't require an assert like in your last paragraph?

The way that this could break is if there are not exactly two input channels, e.g. if there were three channels. That's because SampleFrame currently represents a stereo pair.

However, for the Jack driver it does not seem to be possible to get into this situation (at least via the GUI) because the number of channels cannot be changed to something else than two anyway. So the only way to break this would be to manually edit the configuration file so that it contains the number 3. In that case the channels method would return 3 because it gets initialized to that value in the AudioJack constructor. This in turn would break the []-operator of SampleFrame here:

https://github.com/mmeeaallyynn/lmms/blob/ecd6a63a0f89da09cbb0583b4d107bbd56b0e312/src/core/audio/AudioJack.cpp#L390

To guard against this it might make sense to add an assertion which asserts that the result of channels is 2.

@JohannesLorenz
Copy link
Contributor

So the only way to break this would be to manually edit the configuration file so that it contains the number 3. In that case the channels method would return 3 because it gets initialized to that value in the AudioJack constructor.

I don't even think that would work, since the config file value is clamped in AudioJack's CTOR.

Would you still suggest to add an assert? If yes, likely in the CTOR?

@michaelgregorius
Copy link
Contributor

So the only way to break this would be to manually edit the configuration file so that it contains the number 3. In that case the channels method would return 3 because it gets initialized to that value in the AudioJack constructor.

I don't even think that would work, since the config file value is clamped in AudioJack's CTOR.

Would you still suggest to add an assert? If yes, likely in the CTOR?

Ah, you're right. It seems that for some reason I missed the clamping. Although my next question then would be: why is it even read from the configuration if it is clamped between [DEFAULT_CHANNELS, DEFAULT_CHANNELS], i.e. if it is always set to DEFAULT_CHANNELS anyway regardless of what is stored in the configuration? It seems that the configuration has no effect anyway and that the whole clamp statement can be replaced with simply DEFAULT_CHANNELS. Does the configuration value have an effect in some other place?

On a related note: what the intention of the spinbox in AudioJack::setupWidget::setupWidget if it cannot be changed and will always display the value of DEFAULT_CHANNELS? Does it add meaningful information?

IMO an assert should not be necessary as the code currently enforces a single value anyway.

@JohannesLorenz
Copy link
Contributor

So the only way to break this would be to manually edit the configuration file so that it contains the number 3. In that case the channels method would return 3 because it gets initialized to that value in the AudioJack constructor.

I don't even think that would work, since the config file value is clamped in AudioJack's CTOR.
Would you still suggest to add an assert? If yes, likely in the CTOR?

Ah, you're right. It seems that for some reason I missed the clamping. Although my next question then would be: why is it even read from the configuration if it is clamped between [DEFAULT_CHANNELS, DEFAULT_CHANNELS], i.e. if it is always set to DEFAULT_CHANNELS anyway regardless of what is stored in the configuration? It seems that the configuration has no effect anyway and that the whole clamp statement can be replaced with simply DEFAULT_CHANNELS.

It earlier clamped between DEFAULT_CHANNELS and SURROUND_CHANNELS, and I was hoping you could tell me why you changed it 😃

Does the configuration value have an effect in some other place?

No, from what I checked.

@michaelgregorius
Copy link
Contributor

Ah, you're right. It seems that for some reason I missed the clamping. Although my next question then would be: why is it even read from the configuration if it is clamped between [DEFAULT_CHANNELS, DEFAULT_CHANNELS], i.e. if it is always set to DEFAULT_CHANNELS anyway regardless of what is stored in the configuration? It seems that the configuration has no effect anyway and that the whole clamp statement can be replaced with simply DEFAULT_CHANNELS.

It earlier clamped between DEFAULT_CHANNELS and SURROUND_CHANNELS, and I was hoping you could tell me why you changed it 😃

Oops! 😅 The longer explanation can be found in #7156. The short story is that surround sound did not work/compile anyway so I removed it while introducing the SampleFrame class. Looks like I did this rather mechanically and did not realize that I could also simplify the code in the constructor while doing so.

Does the configuration value have an effect in some other place?

No, from what I checked.

In that case it could even be considered to remove the configuration for now.

@michaelgregorius
Copy link
Contributor

@JohannesLorenz, I have pushed 3ab8cc5 which removes the reading of the configuration parameter, the clamping and which also removes the LCD spinbox which cannot be changed anyway.

A next step might be to make the setup dialog similar to the SDL dialog, i.e. to enable the users to choose the first and the second input from a list. I have started to implement something in that direction:

Bildschirmaufnahme_20250501_210151.webm

This would give users control over their inputs from within LMMS, i.e. they would not have to use external tools like qpwgraph or Helvum to route the device inputs to the two inputs that LMMS currently creates. The downside of that approach would be that they'd have to restart LMMS each time they want to use a different input.

The final step (and best solution) would be to have the equivalent of this combo box available for each individual track input. That way users could create for example eight audio tracks, set them all to record, set eight different inputs for them (all from within LMMS) and then record them all at once.

@JohannesLorenz
Copy link
Contributor

Thanks. I appended a cleanup commit and triggered CI (the CI does not start automatically in this PR, probably because the OP is not from an LMMS dev).

This would give users control over their inputs from within LMMS, i.e. they would not have to use external tools like qpwgraph or Helvum to route the device inputs to the two inputs that LMMS currently creates.

Isn't the same question valid for the output? For example, I use bluetooth headphones, and when I remove them and connect them again, I have to use qjackctl to manually connect the headphones.

However, if the question is about output, too, then it might be out-of-scope for this PR.

@michaelgregorius
Copy link
Contributor

This would give users control over their inputs from within LMMS, i.e. they would not have to use external tools like qpwgraph or Helvum to route the device inputs to the two inputs that LMMS currently creates.

Isn't the same question valid for the output? For example, I use bluetooth headphones, and when I remove them and connect them again, I have to use qjackctl to manually connect the headphones.

However, if the question is about output, too, then it might be out-of-scope for this PR.

Yes, the same applies to the outputs. Here the users should be able to directly select the outputs for the master channel.

@JohannesLorenz
Copy link
Contributor

A next step might be to make the setup dialog similar to the SDL dialog, i.e. to enable the users to choose the first and the second input from a list. I have started to implement something in that direction

I was hoping you were doing this next step, because I have no idea what you have in mind 😄 Also, I still think this is independent of this PR, so I would rather prefer not to block this PR (which already has 2 approvals). Unless you think you can finish whatever you plan here relatively soon.

@michaelgregorius
Copy link
Contributor

A next step might be to make the setup dialog similar to the SDL dialog, i.e. to enable the users to choose the first and the second input from a list. I have started to implement something in that direction

I was hoping you were doing this next step, because I have no idea what you have in mind 😄 Also, I still think this is independent of this PR, so I would rather prefer not to block this PR (which already has 2 approvals). Unless you think you can finish whatever you plan here relatively soon.

It is definitively independent of this PR and therefore I have now approved it.

There is now a branch which contains some first preparation with regards to the input and output selection via combo boxes. It can be found here: https://github.com/michaelgregorius/lmms/tree/InputOutputComboBoxesForJackDriver

It already allows you to select the input and output ports and stores the selection in the configuration. However, the stored values are not used yet during the initialization of the actual Jack driver yet. In the end the stored port names must be used to search if they exist in the list of ports that are currently present and the connection must be made if that is the case. Otherwise some fallback is needed.

I am not sure if I will finish this as I now really want to wind down my involvement in the project as announced some time ago in the discussions page. If the remaining work can be done quickly I might finish it but otherwise anybody else is free to use it as a starting point to finish everything off.

@JohannesLorenz
Copy link
Contributor

Thanks for explaining.

I guess in this case, it's best to make a draft PR and copy your explanation into the PR's OP.

I will merge this branch in the next 12 hours and change the merge target from feature/recording-stage-one, to master like explained here. If anyone thinks this is wrong, scream now 😱

@michaelgregorius
Copy link
Contributor

I guess in this case, it's best to make a draft PR and copy your explanation into the PR's OP.

I think I will create an issue which coarsely describes the goal and then create a WIP PR for it.

@JohannesLorenz JohannesLorenz changed the base branch from feature/recording-stage-one to master May 31, 2025 09:48
mmeeaallyynn and others added 6 commits May 31, 2025 12:01
Also, refactor buffer resizing into one function, and avoid useless
static method.
Remove the reading and saving of the channel number configuration in several places as it will default to `DEFAULT_CHANNELS` all the time anyway.

Remove the LCD spin box which does not allow to make any changes to the channel numbers anyway.
@JohannesLorenz JohannesLorenz force-pushed the sample-track-recording-jack branch from 3ab8cc5 to 1900fc2 Compare May 31, 2025 10:03
@JohannesLorenz JohannesLorenz merged commit 352ef8c into LMMS:master May 31, 2025
11 checks passed
@michaelgregorius
Copy link
Contributor

michaelgregorius commented May 31, 2025

Edit: I have just noticed the comment in the commit message which references #7786. The question would be how to deal with the extended dialog. Should I comment out everything with regards to recording, rebase on master and make a PR for master or should I keep it based on the recording branch?

@JohannesLorenz, I am a little bit confused now. Is this PR intended as a preparation for sample recording with Jack or was it intended to merge the full feature?

I have two branches now which add a dialogue that allows users to select inputs and outputs. I think one of the branches was based on this PR's branch and it has recordings functionality and one was rebased onto the current master and there is no recording functionality.

Here's how the dialog looks like:

JackDriverWithInputOutputSelection

I was able to record from one device and playback on another.

However, letting the user choose inputs does not really make sense if the feature is not implemented/merged yet. Are there any plans with regards to that?

@JohannesLorenz
Copy link
Contributor

@michaelgregorius This was indeed just preparation, similar to SDL and PortAudio. You probably have 2 branches because I needed to force push this PR's branch due to the changed target branch - I guess you figured it all from the commit message :)

Right now, master needs to be merged into recording - feel free to do it, or I will do it in a few hours.Then, it is up to you: Either merge to master only with the outputs for now, or base on our recording branch with inputs and outputs.

I hope this helps with the confusion :)

@michaelgregorius
Copy link
Contributor

@JohannesLorenz, yes it has helped with the confusion. 😅

I have now created #7918 with the accompanying PR #7919 because selecting the outputs from a list already works for master and brings benefits to the users.

I will create another PR which will reintroduce the input selection as well. That PR would then have to be merged once the recording feature is done.

@michaelgregorius
Copy link
Contributor

The input selection branch is #7920.

@sakertooth
Copy link
Contributor

sakertooth commented Jun 1, 2025

Uhh, is m_inBuf even used? Unless we are planning to use it in the future?

@michaelgregorius
Copy link
Contributor

Uhh, is m_inBuf even used? Unless we are planning to use it in the future?

Hmm, good catch. In current master the inputs are directly read from the Jack buffers into m_inputFrameBuffer:

for (int c = 0; c < channels(); ++c)
{
jack_default_audio_sample_t* jack_input_buffer = (jack_default_audio_sample_t*) jack_port_get_buffer(m_inputPorts[c], nframes);
for (jack_nframes_t frame = 0; frame < nframes; frame++)
{
m_inputFrameBuffer[frame][c] = static_cast<sample_t>(jack_input_buffer[frame]);
}
}
audioEngine()->pushInputFrames (m_inputFrameBuffer.data(), nframes);

It it looks like m_inBuf can be removed.

@sakertooth
Copy link
Contributor

It it looks like m_inBuf can be removed.

Cool, done in #7922.

sakertooth pushed a commit to sakertooth/lmms that referenced this pull request Jun 1, 2025
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 LMMS#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 <[email protected]>
Co-authored-by: Michael Gregorius <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants