New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Various fixes and additions related to recording the mix to a file #1035
Conversation
@@ -308,6 +308,10 @@ bool EngineRecord::openFile() { | |||
#endif | |||
if (m_pSndfile) { | |||
sf_command(m_pSndfile, SFC_SET_NORM_FLOAT, NULL, SF_TRUE); | |||
// Warning! Depending on how libsndfile is compiled autoclip may not work. | |||
// Ensure CPU_CLIPS_NEGATIVE and CPU_CLIPS_NEGATIVE is setup properly in the build. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like CPU_CLIPS_NEGATIVE shouldn't be mentioned twice here.
Thank you for this PR. It is a bad idea to reuse the quality slider to set the sample format. We may also consider to replace the quality slider, with a dynamic region that allows to set the recording parameter in established options for each format. |
The other parts are looking good. Would you mind to split the quality related changes out into a new PR? |
Ok, fine. I'll do the split now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added some findings to improve the code a bit more.
@@ -117,21 +117,30 @@ DlgPrefRecord::DlgPrefRecord(QWidget* parent, UserSettingsPointer pConfig) | |||
|
|||
slotApply(); | |||
// Make sure a corrupt config file won't cause us to record constantly. | |||
// TODO: What does this do exactly? I thougth it stopped the recording when showing | |||
// the preferences page but it does not (and i would preffer that keeps not stopping it). | |||
m_pRecordControl->set(RECORD_OFF); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is only passed when Mixxx is initialized. This line should be removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if I understand you correctly. Do you mean that I have to remove the comments I added, or that I have to remove the "set", because it is useless?
In fact, I am not sure if the initial value is set anywhere. Probably recordingmanager should do it in its constructor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All control objects are 0.0 = RECORD_OFF by default so the whole block with comments can be removed.
@@ -100,17 +112,25 @@ void RecordingManager::startRecording(bool generateFileName) { | |||
m_recordingLocation = m_recording_base_file + "."+ encodingType.toLower(); | |||
m_pConfig->set(ConfigKey(RECORDING_PREF_KEY, "Path"), m_recordingLocation); | |||
m_pConfig->set(ConfigKey(RECORDING_PREF_KEY, "CuePath"), m_recording_base_file +".cue"); | |||
|
|||
m_recReady->set(RECORD_READY); | |||
|
|||
} else { | |||
// This is only executed if filesplit occurs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about putting the else branch into a new function like
splitContinueRecording()?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I kept the structure there, but yes, my initial intention was to separate it into its own method. I could do it for clarity
|
||
// Only called when recording is active. | ||
void RecordingManager::slotBytesRecorded(int bytes) | ||
{ | ||
// auto conversion to long | ||
m_iNumberOfBytesRecorded += bytes; | ||
if(m_iNumberOfBytesRecorded >= m_split_size) | ||
m_iNumberOfBytesRecordedSplit += bytes; | ||
if(m_iNumberOfBytesRecordedSplit >= m_split_size) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It will likely split the file > m_split_size. How about adding here the a fixed offset that allows to process one buffer + closing frames, instead of tweaking the m_split_size value in the preferences?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least a comment about the issue should be placed here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand that sentence. I will explain what I do just in case:
enginerecord emits a signal with the number of bytes processed (i.e. the size of the buffer that the process call worked on).
on recordingmanager, we collect that size and increment a variable, so that we can compare how big the file is (in order to split it), but also in order to emit a signal to the UI (recordings, in the library), where it shows the total amount recorded.
The implementation as it was, needs the amount to be reset once we reach split_size, because we create a new file and start from 0, but then, we were telling to the UI that we started again too.
With these separated variables, we keep the current file size in one variable, and the total size recorded in the other.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I understand it in general. Here we have the issue, that we have to split the file before it is bigger than the limit. It is done by actually using smaller limits than the user has selected.
This was hard to understand for me in the first place.
A comment like:
// The m_split_size limit is tweaked that the user value is not exceeded in any case
will help. But using an offset here might be better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The split size would probably never be exactly the size requested. Reasoning is that the size is incremented by process' buffer size (In my tests, this was around 12K frames, so 48KB for a 16bit stereo file). As such, I don't see any problem in having the constants rounded down already, and not try to infer an offset that is mostly by convenience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, than just add a comment here.
else if(fileSizeStr == SPLIT_120MIN) | ||
return 120*60; | ||
else // Do not limit by time for the rest. | ||
return 999999999; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use -1 for unlimited
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes and no. Maybe for the method, but not for the comparison. If I were to use -1 (or 0, which is what I initially wanted to use), then the if ( x > split_time ) would need to be if ( split_time != -1 && x > split_time).
Sure, this way it is not really unlimited, and the method is only called once each second, but I would prefer the simpler if.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah OK, 999999999 should than be a const variable or just use INT_MAX
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
INT_MAX sounds good. Will do all these changes tomorrow.
else | ||
return SIZE_650MB; | ||
} | ||
long RecordingManager::getFileSplitTime() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"long" has different size on different architectures. int should be big enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My fault, I copied it from the other method (previous to changing the other method's size)
else | ||
return SIZE_650MB; | ||
} | ||
long RecordingManager::getFileSplitTime() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getFileSplitSeconds()
@@ -145,23 +167,37 @@ QString& RecordingManager::getRecordingDir() { | |||
} | |||
|
|||
// Only called when recording is active. | |||
void RecordingManager::slotDurationRecorded(QString durationStr) | |||
void RecordingManager::slotDurationRecorded(quint64 duration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a nice class mixxx::Duration with 64 bit nano seconds resolution that includes some convenient helpers for printing and debugging. Maybe it is worth to use this here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just adapting the code to what we had. Duration represents seconds, so it really doesn't need that much precision. It's just that it already was quint64 in the enginerecord class.
updateFromPreferences(); // Update file location from preferences. | ||
if (openFile()) { | ||
qDebug() << "Splitting to a new file: "<< m_fileName; | ||
m_pRecReady->set(RECORD_ON); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be an (unlikely) race condition if the user has stopped the recording in between.
It can be solved by using connectValueChangeRequest() and write the value finally by setAndConfirm()
This is done for broadcasting as well:
m_pStatusCO->connectValueChangeRequest( |
In addition, status and enable is separated into two COs
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am unsure if I follow your reasoning fully here, but anyway, you seem to suggest a conceptual change.
If your scenario is:
mixxx is recording
process is called
split is required
new mode set to split
(time passes)
process is called
the split part is entered
user stops recording. This should be calling the encodingmanager to stop, and so set the mode to off
since we had already entered into the if, but not yet called the set on (we were opening the file), the stop request is ignored.
If you want to ensure thread synchronization, the only bet is synchronizing the whole method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since it was that way before anyway and the case is unlikely, it is not required to do this in this PR. But a bullet prove solution would be nice on the long run.
What do you mean with "synchronizing the whole method"?
For my feeling using two separate COs will works, but we have to verify this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have just tested this PR and it works like a charm.
I only see two compiler warnings.
I think it is a known issue that I get
INDEX 01 00:00:44
in a split cue file
Do we have a bug for it?
We have discussed to Improve the PlayerInfo class and make it real-time along with a OSC client here:
http://www.mixxx.org/forums/viewtopic.php?f=6&t=8003&start=20
Maybe you have some Ideas and fun to help Jakob with building on Windows.
Please give a hint when it is ready to merge.
{ | ||
if(m_durationRecorded != durationStr) | ||
if(m_secondsRecordedSplit != duration) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This produces a warning:
warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
m_durationRecorded = durationStr; | ||
emit(durationRecorded(m_durationRecorded)); | ||
m_secondsRecordedSplit = duration; | ||
if(duration >= m_split_time) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here as well:
warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I didn't see on launchpad the bug about the imprecisions with the track start positions. It was on my plan to fix that too, but in the end I couldn't get how to work it out.
Later on I thought that maybe we don't need to be precise. It is enough with being ahead instead of too late.
That would fix the 00:00:44 that you say, as well as the first song start, which always starts too late. In fact, for the first song, I was thinking on avoiding the counter in metaDataHasChanged until we get a track from getCurrentPlayingTrack for the first time.
So, with that, and assuming that the change took place between the previous call and now, we coud set the time to that of the previous call.
An improved PlayerInfo could help to determine this better, but we also want to avoid filling the cue with several changes forth and back when moving the faders/crossfader as an effect. I guess that's part of the reason why the counter was added initially, although it's not a perfect solution.
As for the warniings, I've just changed that and will commit it later on. Compiling on windows it's quite difficult to get anything out of the console output (except when it directly fails).
… on new splits, and improves the time precision of first track of the initial cue.
With the latest commit (small improvement on start time of cue files) I would consider the changes ready. Further improvement of start time needs improvement in the PlayerInfo class and changing the decision of what is considered a new track (so that the lifecounter is no longer needed). |
Thank you! I have just tested this branch an it works nicely. However have two finding:
|
The new bugs are here: Before merge, you have to become a Mixxx contributor. https://docs.google.com/a/mixxx.org/spreadsheet/viewform?formkey=dEpYN2NkVEFnWWQzbkFfM0ZYYUZ5X2c6MQ and comment here when done. |
Hi. And I've accepted and submitted the contributor agreement. |
Thank you, LGTM! I think this PR is big enough for a first merge. |
You are right. I saw this on recent builds but I haven't checked why yet.
It is as if there are extra radios that shouldn't be there. They are
grnerated dynamically in code.
El dia 27/09/2017 17:11, "Be" <notifications@github.com> va escriure:
… I'm not sure what's going on with the preferences pane. Maybe some widgets
aren't getting hidden when selecting different codecs?
[image: image]
<https://user-images.githubusercontent.com/9455094/30921486-40170d0c-a36c-11e7-9d1c-f68df7be52ec.png>
—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
<#1035 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AJdFf3NUIFvccXmofw0XOXpabwjnEciXks5smmWzgaJpZM4Klq0p>
.
|
Weird, I did a full rebuild again (I already needed to do it some weeks
ago for other weird crashes and waveform misbehaviour) and the bug has
gone. Try deleting lin64_build, .sconf_temp and cache dirs and well as
sconsign.dblite and rebuild it to see if that also fixes it for you.
I also checked out the most recent changes, but I don't see any change that
could have fixed this.
|
launchpad bugs:
#1637786 : Note: this one requires fixing the compilation of libsndfile, at least on windows (config.h, CPU_CLIPS_NEGATIVE)
#1415758
#1605921
#1605923
#1638115
#1638120
#1605922
#1415720