-
-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Random crash in sf2_player #2401
Comments
I have investigated the problem a bit. To narrow down the scope I started with turning I have then added a check in SF2PluginData * currentData = static_cast<SF2PluginData *>( currentNote->m_pluginData ); If the Heres my patch if you want to experiment yourself: diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp
index 07c4a0e..8c3d75a 100644
--- a/plugins/sf2_player/sf2_player.cpp
+++ b/plugins/sf2_player/sf2_player.cpp
@@ -43,6 +43,8 @@
#include "embed.cpp"
+#include <cassert>
+
extern "C"
{
@@ -63,15 +65,32 @@ Plugin::Descriptor PLUGIN_EXPORT sf2player_plugin_descriptor =
}
-struct SF2PluginData
+class SF2PluginData
{
- int midiNote;
+public:
+ SF2PluginData(int _midiNote) : midiNote(_midiNote)
+ {
+ bool noteInCorrectRange = (midiNote >= 0) && (midiNote <= 127);
+ assert(noteInCorrectRange);
+ if (!noteInCorrectRange)
+ {
+ // Add a line so we can add a breakpoint here...
+ int i = 0;
+ }
+ }
+
+ int getMidiNote() const { return midiNote; }
+ void setMidiNote(int _midiNote) { midiNote = _midiNote; }
+
int lastPanning;
float lastVelocity;
fluid_voice_t * fluidVoice;
bool isNew;
f_cnt_t offset;
bool noteOffSent;
+
+private:
+ int midiNote;
} ;
@@ -556,8 +575,8 @@ void sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * )
}
const int baseVelocity = instrumentTrack()->midiPort()->baseVelocity();
- SF2PluginData * pluginData = new SF2PluginData;
- pluginData->midiNote = midiNote;
+ SF2PluginData * pluginData = new SF2PluginData(midiNote);
+ //pluginData->midiNote = midiNote;
pluginData->lastPanning = 0;
pluginData->lastVelocity = _n->midiVelocity( baseVelocity );
pluginData->fluidVoice = NULL;
@@ -605,7 +624,12 @@ void sf2Instrument::noteOn( SF2PluginData * n )
id[i] = fluid_voice_get_id( voices[i] );
}
- fluid_synth_noteon( m_synth, m_channel, n->midiNote, n->lastVelocity );
+ if (n->getMidiNote() < 0 || n->getMidiNote() > 127)
+ {
+ // Add a line so we can add a breakpoint here...
+ int i = 1;
+ }
+ fluid_synth_noteon( m_synth, m_channel, n->getMidiNote(), n->lastVelocity );
// get new voice and save it
fluid_synth_get_voicelist( m_synth, voices, poly, -1 );
@@ -622,7 +646,7 @@ void sf2Instrument::noteOn( SF2PluginData * n )
m_synthMutex.unlock();
m_notesRunningMutex.lock();
- ++m_notesRunning[ n->midiNote ];
+ ++m_notesRunning[ n->getMidiNote() ];
m_notesRunningMutex.unlock();
}
@@ -631,13 +655,13 @@ void sf2Instrument::noteOff( SF2PluginData * n )
{
n->noteOffSent = true;
m_notesRunningMutex.lock();
- const int notes = --m_notesRunning[n->midiNote];
+ const int notes = --m_notesRunning[n->getMidiNote()];
m_notesRunningMutex.unlock();
if( notes <= 0 )
{
m_synthMutex.lock();
- fluid_synth_noteoff( m_synth, m_channel, n->midiNote );
+ fluid_synth_noteoff( m_synth, m_channel, n->getMidiNote() );
m_synthMutex.unlock();
} |
@michaelgregorius For sure I will try your patch 😄 Thanks for your help! |
Probably not related, but worth re-testing after this issue has been resolved: #649 |
@michaelgregorius I think I found the problem but it looks so silly that I wonder if I am right or wrong... I would like a second advice on this because this seems a so basic programming flaw that I wonder if I am right... |
@tresf I don't think its related because this problem arises while playing the project, not while loading it as in # 649 (space added intentionally) |
@tresf May I suggest to escalate this issue as it breaks usability severely and tag it to 1.2? |
😉 |
What a responsiveness! 😆 |
@midi-pascal I think the pattern you have described is just used to make sure that a pointer is not deleted twice which would lead to a crash. For example executing this code would crash: Foo *bar = new Foo();
delete bar; // Everything ok here
delete bar; // This will crash because the memory that bar points to has already been deleted This code will not crash because calling Foo *bar = new Foo();
delete bar; // Everything ok here
bar = 0;
delete bar; // Ok because delete 0 is legal So I agree with @tresf that this is likely not the cause of the problem. |
@michaelgregorius I am aware of this and agree with your explanation of the crash on double delete() when the pointer is not set to NULL. In fact, the fundamental problem is: How these invalidated notes stay in m_playingNotes vector in sf2Instrument::play()? |
@midi-pascal I still believe that the crash is caused by pushing something that is not an Attached you can find another patch which seems to support this theory. It adds an The patch also adds an extra check to make sure that all
The warning is the one that is placed right after the aforementioned SF2PluginData * currentData = static_cast<SF2PluginData *>( currentNote->m_pluginData ); To me this is a strong indicator that something that is no Here is the diff that can be used for debugging (please note that it does not include the other diff above): diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp
index 07c4a0e..f00c9c5 100644
--- a/plugins/sf2_player/sf2_player.cpp
+++ b/plugins/sf2_player/sf2_player.cpp
@@ -65,6 +65,25 @@ Plugin::Descriptor PLUGIN_EXPORT sf2player_plugin_descriptor =
struct SF2PluginData
{
+public:
+ SF2PluginData() :
+ midiNote(0),
+ lastPanning(0),
+ lastVelocity(0),
+ fluidVoice(0),
+ isNew(true),
+ offset(0),
+ noteOffSent(false),
+ aboutToBeDeletedAtAKnownPoint(false)
+ {}
+ ~SF2PluginData()
+ {
+ if (!aboutToBeDeletedAtAKnownPoint)
+ {
+ qDebug() << "SF2PluginData deleted at unknown point!";
+ }
+ }
+
int midiNote;
int lastPanning;
float lastVelocity;
@@ -72,6 +91,7 @@ struct SF2PluginData
bool isNew;
f_cnt_t offset;
bool noteOffSent;
+ bool aboutToBeDeletedAtAKnownPoint;
} ;
@@ -260,6 +280,59 @@ QString sf2Instrument::nodeName() const
+void sf2Instrument::addToKnownSF2PluginDatas(SF2PluginData * sf2PluginData)
+{
+ if (!isKnownSF2PluginData(sf2PluginData))
+ {
+ QMutexLocker locker(&m_debugMutex);
+ m_knownSF2PluginDatas.insert(sf2PluginData);
+ }
+ else
+ {
+ qDebug() << "Known plugin data inserted again!";
+ dumpSF2PluginData(sf2PluginData);
+ }
+}
+
+void sf2Instrument::removeFromKnownSF2PluginDatas(SF2PluginData * sf2PluginData)
+{
+ if (isKnownSF2PluginData(sf2PluginData))
+ {
+ QMutexLocker locker(&m_debugMutex);
+ m_knownSF2PluginDatas.erase(sf2PluginData);
+ }
+ else
+ {
+ qDebug() << "Tried to remove an unknown plugin data!";
+ dumpSF2PluginData(sf2PluginData);
+ }
+}
+
+bool sf2Instrument::isKnownSF2PluginData(SF2PluginData * sf2PluginData)
+{
+ QMutexLocker locker(&m_debugMutex);
+ return m_knownSF2PluginDatas.find(sf2PluginData) != m_knownSF2PluginDatas.end();
+}
+
+void sf2Instrument::ensureKnownSF2PluginData(SF2PluginData * sf2PluginData, QString const & debugMessage)
+{
+ if (!isKnownSF2PluginData(sf2PluginData))
+ {
+ qDebug() << debugMessage;
+ dumpSF2PluginData(sf2PluginData);
+ }
+}
+
+void sf2Instrument::dumpSF2PluginData(SF2PluginData * sf2PluginData)
+{
+ qDebug() << "(isNew: " << sf2PluginData->isNew << ", lastPanning: " << sf2PluginData->lastPanning
+ << ", lastVelocity: " << sf2PluginData->lastVelocity
+ << ", midiNote: " << sf2PluginData->midiNote
+ << ", noteOffSent: " << sf2PluginData->noteOffSent
+ << ", offset: " << sf2PluginData->offset << ")";
+}
+
+
void sf2Instrument::freeFont()
{
@@ -557,6 +630,7 @@ void sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * )
const int baseVelocity = instrumentTrack()->midiPort()->baseVelocity();
SF2PluginData * pluginData = new SF2PluginData;
+ addToKnownSF2PluginDatas(pluginData);
pluginData->midiNote = midiNote;
pluginData->lastPanning = 0;
pluginData->lastVelocity = _n->midiVelocity( baseVelocity );
@@ -575,6 +649,7 @@ void sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * )
else if( _n->isReleased() ) // note is released during this period
{
SF2PluginData * pluginData = static_cast<SF2PluginData *>( _n->m_pluginData );
+ ensureKnownSF2PluginData(pluginData, "Unknown plugin data in sf2Instrument::playNote");
pluginData->offset = _n->framesBeforeRelease();
pluginData->isNew = false;
@@ -686,7 +761,11 @@ void sf2Instrument::play( sampleFrame * _working_buffer )
for( int i = 1; i < m_playingNotes.size(); ++i )
{
SF2PluginData * currentData = static_cast<SF2PluginData *>( currentNote->m_pluginData );
+ ensureKnownSF2PluginData(currentData, "Unknown plugin data in sf2Instrument::play (currentData)");
+
SF2PluginData * iData = static_cast<SF2PluginData *>( m_playingNotes[i]->m_pluginData );
+ ensureKnownSF2PluginData(iData, "Unknown plugin data in sf2Instrument::play (iData)");
+
if( currentData->offset > iData->offset )
{
currentNote = m_playingNotes[i];
@@ -696,6 +775,8 @@ void sf2Instrument::play( sampleFrame * _working_buffer )
// process the current note:
// first see if we're synced in frame count
SF2PluginData * currentData = static_cast<SF2PluginData *>( currentNote->m_pluginData );
+ ensureKnownSF2PluginData(currentData, "Unknown plugin data in sf2Instrument::play (currentData, second variable)");
+
if( currentData->offset > currentFrame )
{
renderFrames( currentData->offset - currentFrame, _working_buffer + currentFrame );
@@ -776,11 +857,13 @@ void sf2Instrument::renderFrames( f_cnt_t frames, sampleFrame * buf )
void sf2Instrument::deleteNotePluginData( NotePlayHandle * _n )
{
SF2PluginData * pluginData = static_cast<SF2PluginData *>( _n->m_pluginData );
+ removeFromKnownSF2PluginDatas(pluginData);
if( ! pluginData->noteOffSent ) // if we for some reason haven't noteoffed the note before it gets deleted,
// do it here
{
noteOff( pluginData );
}
+ pluginData->aboutToBeDeletedAtAKnownPoint = true;
delete pluginData;
}
diff --git a/plugins/sf2_player/sf2_player.h b/plugins/sf2_player/sf2_player.h
index 2a6995c..155d91f 100644
--- a/plugins/sf2_player/sf2_player.h
+++ b/plugins/sf2_player/sf2_player.h
@@ -39,6 +39,8 @@
#include "SampleBuffer.h"
#include "MemoryManager.h"
+#include <set>
+
class sf2InstrumentView;
class sf2Font;
class NotePlayHandle;
@@ -154,6 +156,14 @@ private:
QVector<NotePlayHandle *> m_playingNotes;
QMutex m_playingNotesMutex;
+ std::set<SF2PluginData*> m_knownSF2PluginDatas;
+ QMutex m_debugMutex;
+ void addToKnownSF2PluginDatas(SF2PluginData * sf2PluginData);
+ void removeFromKnownSF2PluginDatas(SF2PluginData * sf2PluginData);
+ bool isKnownSF2PluginData(SF2PluginData * sf2PluginData);
+ void ensureKnownSF2PluginData(SF2PluginData * sf2PluginData, QString const & debugMessage);
+ void dumpSF2PluginData(SF2PluginData * sf2PluginData);
+
private:
void freeFont();
void noteOn( SF2PluginData * n ); |
@michaelgregorius Thanks a lot for this. I will try it right now and let you know the results. |
@michaelgregorius I tested your patch and it works well. |
@midi-pascal The patch above checks all accesses to Another possible cause for the problem might be multithreading. When I was developing the patch I had a version that did not protect the accesses to Please also note that in |
@michaelgregorius I added
So my main hypothesis are now:
Note: By inspecting the notes that produce the crashes (or filtered out by my filter) I can see that faulty notes are always from the drums track. I believe this is not trivial: drums tracks have some simultaneous and very short notes. And faster the tempo, more faulty notes are produced for the same piece. |
@michaelgregorius I tried the piece with no drums track: issue occurs in other tracks but less often. Then I tried the drums track alone: issue occurs but less often. |
@michaelgregorius Finally I found something interesting: |
do not know if this is useful at all, but i have the same result on |
I found what causes the crash and made a patch that solves it.
However I do not understand why the problem occurs because my knowledge
of Lmms inside working is still far from good.
May I ask for a second look on what I did from a more knowledgeable
person before I submit a PR for it?
Thanks.
|
@midi-pascal I think the most straight forward way for others to inspect your changes would be to create a pull request with them. If the review would find that everything was fine then it could be merged immediately. If for some reason the problem would not not be solved by the pull request you could simply delete it. Another thing that I have thought of is that it might be helpful to extend |
@michaelgregorius I submited a pull request few minutes ago (#2422) so my changes can be reviewed by everybody. For sure this patch solves the issue but not its cause. This is why I asked for a 'master' 😄 |
Branch: lmms-master, up to date
(Linux x86_64, Qt 4.8.6, GCC 4.8.4)
Ubuntu 14.04 x64 Core i5 8Gb ram
I already reported this problem in issue #2192 but it was occurring on my old box (a P4 with Ubuntu 12.04) and I thought it was because of a lack of hardware resources.
I am pretty well equipped now and the problem is still happening at a point that I cannot play a piece completely most of the time.
Note: this never occurs in stable-1.1 (1.1.3)
Steps to reproduce:
1- Start a new project
2- Import a Midi file with more than 10 tracks
3- Play
Lmms crashes randomly in sf2Instrument::noteOff() or sf2Instrument::noteOn() where the contents of the SF2PluginData* is totally corrupted:
Notes:
A Midi file to reproduce can be found there:
http://jazzycat.tripod.com/tpspecl.mid
The backtrace I put in the other issue is still valid.
The text was updated successfully, but these errors were encountered: