Skip to content

Fix some encoding issues (mostly on Windows)#4401

Merged
PhysSong merged 6 commits into
LMMS:stable-1.2from
PhysSong:encoding
Jul 6, 2018
Merged

Fix some encoding issues (mostly on Windows)#4401
PhysSong merged 6 commits into
LMMS:stable-1.2from
PhysSong:encoding

Conversation

@PhysSong

@PhysSong PhysSong commented Jun 4, 2018

Copy link
Copy Markdown
Member

This pull request fixes several encoding issue. Every issue except STK one are Windows-specific.

  • ZynAddSubFX setting loading/saving
  • VST DLL loading
  • VST setting loading/saving
  • Sample file decoding
  • MIDI import
  • STK rawwave path (incomplete on Windows due to upstream issue)

Issues remaining on Windows:

  • .pat file loading
  • .gig file loading
  • STK rawwave path (which toLocal8Bit fails to handle)

I'm planning to track remaining issues in #1191.

Fixes #3855, Fixes #2662, and almost #1191.

@zonkmachine

Copy link
Copy Markdown
Contributor

Every issue except STK one are Windows-specific

I replicated issue #2662 on LinuxMint/Qt4 . It's indeed fixed by this PR.

Ready to merge?

@lukas-w lukas-w self-requested a review June 10, 2018 17:10
Comment thread plugins/MidiImport/MidiImport.cpp Outdated
if (!file.open(QIODevice::ReadOnly))
{
return false;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks like you're closing and then reopening the file. Is it possible to just not close the file to begin with, and then replace QByteArray arr = file.readAll(); with QByteArray arr = file().readAll(); further down?

@PhysSong PhysSong Jun 10, 2018

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I wrote like this because of the const-ness of file(). I'll add a method to work around.

Comment thread plugins/vst_base/RemoteVstPlugin.cpp Outdated
static std::wstring toWString(std::string s)
{
/*
QString qstr = QString::fromUtf8(s.data(), int(s.size()));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see this is commented out, so I'm guessing it doesn't work. Did you try

return qstr.toStdWString();

?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Beacause it requires including Qt headers(on Windows). If that don't matter (we already do that), I can switch to QString is needed.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ah, I see. @lukas-w has offered me advice on where it's okay to use Qt stuff in the past. I think things like QString, at least, are fine everywhere, but hopefully he'll chime in here to confirm that this is OK if he's not too busy.

@PhysSong PhysSong Jun 10, 2018

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

To be more clear, RemoteVstPlugin use Qt libraries for IPC on Windows. So I think that would be fine.

Edit: Linux build also needs the function, too. That's why I didn't use Qt function here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@PhysSong is right, this is not a good place to use Qt because that would add MinGW Qt as a dependency on Linux builds. We can however use C++11 functions to convert to UTF-16:

#include <locale>
#include <codecvt>
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(s);

This does the same thing, just in fewer lines. I think we already use C++11 for RemoteVstPlugin on stable-1.2, but I didn't check.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

because that would add MinGW Qt as a dependency on Linux builds.

It seems like we can use host version Qt, but I don't think it's a good idea because it adds 32bit Qt as a dependency on 64bit Linux.

We can however use C++11 functions to convert to UTF-16:

That doesn't work with our old MinGW because of missing header files(it should work with newest g++-mingw-w64). Those functions are deprecated in C++17, too.

Comment thread plugins/vst_base/RemoteVstPlugin.cpp Outdated
#define F_OPEN(a, b) _wfopen(toWString(a).c_str(), L##b)
#else
#define F_OPEN(a, b) fopen((a).c_str(), b)
#endif

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see you added these F_OPEN defines, but they don't seem to be used anywhere (?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Forgot to commit that. Thank you!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed.

Comment thread plugins/vst_base/RemoteVstPlugin.cpp Outdated

// constructor automatically will process messages until it receives
// a IdVstLoadPlugin message and processes it
// a IdVstLoadPlugin message and processes it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

^ No need to change the indentation here. It was fine before :)

#else
int fd = dup(f.handle());
#endif
f.close();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I see you're using this to open files by their descriptor instead of the filename. Is it gzopen that's giving you problems, fopen, or both? Because if it's only gzopen that's broken, it might be possible to just always fopen, and then call gzdopen(fileno(file), options) if compression is enabled, rather than doing this trickery with duplicating the file handles and such.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Both are broken, because C library only supports characters in local code page.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oh, oops. I had typed out that comment and after digging deeper to better understand the problem I meant to remove it before posting the review >.<

Yeah, I agree with your conclusion here. It's not pretty, but I don't see any better solution.

#else
int fd = dup(f.handle());
#endif
f.close();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if you could offload this logic to a helper function,

static int fdopenFromQString(QString filename, QIODevice::OpenMode mode)

to avoid duplicating the body in multiple functins, and then leave a comment near that function explaining why the function is necessary (to most readers, this code will look silly and they may be tempted to simplify it back to the way it was before).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good suggestion!

Comment thread src/core/DrumSynth.cpp
QFile f(file);
f.open(QIODevice::ReadOnly);
QByteArray dat = f.readAll().constData();
is.str(string(dat.constData(), dat.size()));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The casual reader will see this and think "we're reading the entire file into a string, and then operating on it only as a stream? Why not directly open the file with ifstream?" and will be tempted to change this back to how it was previously. I think there should be some [short] comment explaining why we must open the file with QFile, possibly linking back to this github PR.

Comment thread src/core/SampleBuffer.cpp

if( ( snd_file = sf_open( _f, SFM_READ, &sf_info ) ) != NULL )

QFile f(_f);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm thinking these should all be commented with something along the lines of

// Open the file with QFile and pass the descriptor to sf_open_fd instead of directly calling sf_open
// for better filename encoding support. See https://github.com/LMMS/lmms/pull/4401

so that people know not to revert this change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good point.

@PhysSong PhysSong force-pushed the encoding branch 4 times, most recently from 6cd2d26 to 1f09c05 Compare June 11, 2018 03:59
@Wallacoloo

Copy link
Copy Markdown
Member

@PhysSong Thanks for the improvements!

I'm marking this as "approved" now, since you've addressed any concerns I had. But it looks like Lukas wanted to review this too, so you should probably wait until that before merging 🎉

Comment thread plugins/stk/mallets/mallets.cpp Outdated
#else
.toUtf8().constData() );
#endif

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we can (should?) use toLocal8Bit on Unix as well. It should be equivalent to toUtf8 on most Linux and macOS systems. Correct me if I'm wrong.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, I think that would be simpler. AFAIK that wouldn't break anything, too.

Comment thread plugins/vst_base/RemoteVstPlugin.cpp Outdated
static std::wstring toWString(std::string s)
{
/*
QString qstr = QString::fromUtf8(s.data(), int(s.size()));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@PhysSong is right, this is not a good place to use Qt because that would add MinGW Qt as a dependency on Linux builds. We can however use C++11 functions to convert to UTF-16:

#include <locale>
#include <codecvt>
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(s);

This does the same thing, just in fewer lines. I think we already use C++11 for RemoteVstPlugin on stable-1.2, but I didn't check.

int compression,
const char *xmldata) const
{
int fd = fdopenFromUtf8(filename, QIODevice::WriteOnly);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why not use the same method here that's used in RemoteVstPlugin.cpp? We can move the toWString method and the F_OPEN defines to a common header and include that in both files.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think F_OPEN macro is an ugly workaround for fopen. I can do that if needed, but I don't like the macro approach.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@PhysSong You can make it a function instead.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Something tricky about that: "r" vs L"r" or something like that. That's why I chose macro approach.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@PhysSong Sounds like you need to convert the mode string as well. So something like this:

std::FILE* openFile(std::string filename, std::string mode)
{
#ifdef LMMS_BUILD_WIN32
	return _wfopen(toWString(filename), toWString(mode));
#else
	return fopen(filename.data(), mode.data());
#endif
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

You could, but I don't think there's a need for it, because they're just a couple lines each and operate on an UTF-16 QString instead of an UTF-8 std::string.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think either lmms_io.h or IoHelper.h will be fine. I'll address this point soon.

@PhysSong PhysSong Jun 25, 2018

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Or how about putting them in RemotePlugin.h? I can't use-cases of UTF-8 string filename except in remote plugins.

Edit: It doesn't look like good because Zyn core needs it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Is it fine for our Zyn to include LMMS' headers at all? I think this will be fine in stable-1.2, but I'm not sure about master's submodule. It would be the last concern for me...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I know it doesn't make sense to build https://github.com/LMMS/zynaddsubfx as a standalone, of course. However, I think it's possible to check if it's building with LMMS or not, though not really needed.

@PhysSong

Copy link
Copy Markdown
Member Author

@lukas-w I addressed these issues and pushed to a separate branch. I'll push this change to the original branch when/if you says okay.

@PhysSong

Copy link
Copy Markdown
Member Author

Oops, accidentally pushed them. I can roll back to original commits whenever needed.

@lukas-w

lukas-w commented Jun 27, 2018

Copy link
Copy Markdown
Member

@PhysSong Please roll back to the original commits, all the comment/review history on GitHub is lost otherwise. It's okay to have one extra commit on top of the original ones, there's no need to rewrite history.

@PhysSong

Copy link
Copy Markdown
Member Author

all the comment/review history on GitHub is lost otherwise.

What do you mean? Review comments are still there unless you commented on specific commits, the are just collapsed.

Anyway, I agree that it's not necessary to rewrite history. I just wanted to keep things cleaner, but that was all. If you're still unhappy with this force-push, I can roll back.

@lukas-w

lukas-w commented Jun 27, 2018

Copy link
Copy Markdown
Member

What do you mean?

Sorry, I was being imprecise. The comments are still there, but the context is lost. Review comments are always on specific commits and you can't view that commits any more, so you can't see the changes the review refers to. Try clicking on the "View changes" button next to a review, this is what you'll get now:

image

If you're still unhappy with this force-push, I can roll back.

Yes, please do 👍

@PhysSong

Copy link
Copy Markdown
Member Author

I just noticed even a rebasing can cause such a thing. I just accidentally rebased commits because I didn't know that... I'll revert to the revision before rebasing if needed.

@PhysSong

PhysSong commented Jul 1, 2018

Copy link
Copy Markdown
Member Author

BTW @lukas-w, do you want any more changes in this pull request?

@lukas-w

lukas-w commented Jul 1, 2018

Copy link
Copy Markdown
Member

@PhysSong Apart from restoring the pre-force-push commits, nope.

@PhysSong

PhysSong commented Jul 4, 2018

Copy link
Copy Markdown
Member Author

Then do you mind if I merge this(probably with cleaner history)?

@lukas-w

lukas-w commented Jul 5, 2018

Copy link
Copy Markdown
Member

I don't mind, go ahead ❤️

@PhysSong

PhysSong commented Jul 5, 2018

Copy link
Copy Markdown
Member Author

Just found a very rare encoding issues when using temporary directory whose name contains some non-ASCII characters on Linux. In that case VST plugin can't be loaded.
I can fix it, but it requires some dangerous changes(main->wmain and some more) and is easy to work around. So I'll open an issue instead and merge this soon.

@PhysSong PhysSong merged commit 62d505b into LMMS:stable-1.2 Jul 6, 2018
@PhysSong PhysSong deleted the encoding branch July 6, 2018 02:07
int fd = open( _file.c_str(), O_WRONLY | O_BINARY );
if ( ::write( fd, chunk, len ) != len )
FILE* fp = F_OPEN_UTF8( _file, "wb" );
if ( fwrite( chunk, len, 1, fp ) != len )

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Only spotted this now: fwritereturns the count of elements successfully written, so in this case it will return 1 on success, not len. Therefore this condition is always leading to a false error message on success (except when coincidentally len == 1).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Thank you! Changed to fwrite( chunk, 1, len, fp ) in 0f3b41f.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Also synced into master via 170a46e.

PhysSong added a commit that referenced this pull request Jul 27, 2018
Fix a regression in 3e538d5 (#4401) that
readAllData doesn't read the file from the beginning
sdasda7777 pushed a commit to sdasda7777/lmms that referenced this pull request Jun 28, 2022
Fix a regression in 2dadb15 (LMMS#4401) that
readAllData doesn't read the file from the beginning
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.

ZynAddSubFX resets its sound to default VST plugins fail to load from folders containing diacritical marks

4 participants