libopenmpt General Development Question - Programmatically creating modules

Started by Danny-E 33, May 27, 2022, 01:20:48

Previous topic - Next topic

Danny-E 33

Hello.

I am interested in writing a C++ program that creates a song/module from scratch in memory in a format ready to immediately be handed to libopenmpt and played.

My question: What documentation should I be reading to figure out how to do this?

Other thoughts:

I want to start with the absolute simplest case. I would like to construct an in-memory song from nothing that just plays a single note when passed to libopenmpt (such as a square note).
I don't need this song data to be modified or preserved -- just created then played.

I'm assuming (and could be wrong) that this in-memory data would follow the spec of some tracker file format (.it, .mptm, or perhaps something even simpler may be better).
What documentation is there are on: 1) the exact spec of the format(s) and 2) any APIs for constructing/managing these data structures?

Actually, if you know of a format that is significantly more basic than .it or .mptm that is supported by libopenmpt please let me know.
The sound I need to produce is not very complex, so the more basic the better.

If I happen to be so lucky, is there a sample demo C++ program that does something even somewhat along these lines?

Thanks in advance for any advice!

(Edit: It looks like openmpt::ext::interactive::play_note might also fit my needs (or at least be a decent place to start experimenting), but as I'm looking at the documentation, my main question is; how do I configure the instruments programmatically?)

Saga Musix

There's no in-memory format that you could serialize from/to. OpenMPT internals are constantly in flux and could change in any version. So indeed you would have to create a file in memory following one of the many supported formats. For the IT format, you can take ITTECH.TXT as a starting point: https://github.com/schismtracker/schismtracker/wiki/ITTECH.TXT - for your use case, constructing such a file should be relatively simple because all you would need is a single order list item, a single pattern and a single sample slot. Optional features such as instruments can be left out completely. There may be simpler formats but depending on whether you need 16-bit or stereo samples they may or may not be usable.
» No support, bug reports, feature requests via private messages - they will not be answered. Use the forums and the issue tracker so that everyone can benefit from your post.

Danny-E 33

Thank you for that link, that was exactly what I needed.

I now have a 432-byte array (totally static for now) which should play a single note, but I'm currently struggling to get sound playback to work in my separate project. (Sound playback worked just fine from the command line with openmpt123.exe and libopenmpt_example_cxx.exe.)

I am trying to base playback off of this example for now:
https://github.com/OpenMPT/openmpt/blob/libopenmpt-0.6.3/examples/libopenmpt_example_cxx.cpp

In order to accomplish this, these are the steps I have taken:
1. I cloned git tag "libopenmpt-0.6.3" of openmpt.
2. I opened ./build/vs2022win10/libopenmpt.sln in Visual Studio 2022 and built Debug|Win32 (my separate project is 32-bit).
3. I copied the resulting .lib files from ./build/lib/vs2022win10/x86/Debug/ into my separate project's lib include directory. (libopenmpt.lib, openmpt-ogg.lib, openmpt-portaudio.lib, openmpt-portaudiocpp.lib, openmpt-vorbis.lib, and openmpt-zlib.lib)
    3a. I also had to copy over ./bin/debug/vs2022-win10-static/x86/openmpt-mpg123.lib to resolve some linker errors.
4. I copied the public interface headers from ./libopenmpt/ into my project's header include directory (just libopenmpt.hpp, libopenmpt_config.h, and libopenmpt_version.h).
5. I copied all header files from ./include/portaudio/include/ and ./include/portaudio/bindings/cpp/include/portaudiocpp/ into my project's header include directory.

Currently, I am facing 18 "unresolved external symbol" linker errors, all of which are being reported by openmpt-portaudio.lib(pa_win_wdmks.obj).
Each unresolved symbol is in the form "_KS*", like "_KSDATAFORMAT_SUBTYPE_PCM" for example.

Can you take a look at the steps I took and tell me if anything I did seems odd/incomplete/unnecessary/barking up the wrong tree?

Your advice is sincerely appreciated.

Edit: I have narrowed down the root of the linker errors to this line of code:
portaudio::AutoSystem portaudio_initializer;
(taken from ./examples/libopenmpt_example_cxx.cpp)

I also noticed that my executable still expects to find openmpt-mpg123.dll. How do I configure it to use openmpt-mpg123.lib so that I do not have to redistribute the dll along with my executable?
(Do I even really need mpg123 at all? I noticed that ./libopenmpt/dox/dependencies.md mentions that minimp3 can be used instead, which I noticed is used by libopenmpt-small, so I gave that a shot but it seemed like it was missing portaudio, so I got stuck there as well.)

Edit 2: I built portaudio from source (https://github.com/PortAudio/portaudio/tree/v19.7.0) and simply added the resulting portaudio.lib to my project and that resolved the 18 linker errors that I mentioned above.
With portaudio taken care of, I was able to switch back to libopenmpt-small instead of libopenmpt. (Except, to keep using the c++ bindings for portaudio, I kept openmpt-portaudiocpp.lib.)

Now my IT module is playing in my custom project, from a single tiny executable with no dlls :) (now to do it all over again on Linux, and then again on Mac...)

Sorry for all the rambling but I think my issues are 100% resolved. However, I'm still curious what you think about this "frankenstein" approach of mixing libopenmpt and libopenmpt-small.

Edit 3: I realized I can build portaudiocpp from portaudio's source without using openmpt-portaudiocpp. So I am no longer mixing libopenmpt and libopenmpt-small. I am solely using libopenmpt-small.
I also got everything compiling and (statically) linking on Linux :) Just Mac to go... (Edit 4: Mac is done)

Saga Musix

The bundled Portaudio is mostly for use with OpenMPT (and contains custom modifications for that). If you want to write your own cross-platform application, it's indeed better to use Portaudio as provided by the platform, your package manager or by building it yourself, as you did.
» No support, bug reports, feature requests via private messages - they will not be answered. Use the forums and the issue tracker so that everyone can benefit from your post.

Danny-E 33

Thanks for the confirmation!

One other small issue I ran into while building libopenmpt on Linux and Mac..

I had to install libportaudio.a and libportaudiocpp.a system-wide instead of keeping everything contained within my project directory.

It seems like libopenmpt's autoconf doesn't honor CPPFLAGS and LDFLAGS.

My (poor) understanding of autoconf is that I can define CPPFLAGS and LDFLAGS and then the configure script will use this to look for libraries in the specified directories.

For example, I ran:
CPPFLAGS="-I$(realpath "$PWD/../../include")" LDFLAGS="-L$(realpath "$PWD/../../lib")" ./configure --prefix="$(realpath "$PWD/../..")" \
--without-mpg123 \
--without-ogg \
--without-vorbis \
--without-vorbisfile \
--without-sndfile \
--without-flac
and it still failed with configure: error: Unable to find libportaudiocpp. even though libportaudiocpp is installed in $(realpath "$PWD/../../lib").

This isn't too big of a deal since installing system-wide fixed it, but I still thought it was odd.

Edit: Oh, I supposed I should specify that I am referring to libopenmpt-0.6.3+release.autotools.tar.gz