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?)
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.
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)
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.
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