Repository navigation
Add Wave Game audio cartridge support - #2126
MBilderbeek wants to merge 1 commit into
Conversation
|
@m9710797 is this along the lines we discussed? It seems to work well, but I guess more testing would be helpful. I did not yet implement the |
d8efd09 to
767a940
Compare
m9710797
left a comment
There was a problem hiding this comment.
Quick initial review comments. Many comments are about details, only a few bigger issues.
Later I'd like to do a more detailed review (e.g. really try to understand the details of song loading).
|
@m9710797 I was thinking about another simplification: replace the |
Good idea. The alternative I had in mind was using |
m9710797
left a comment
There was a problem hiding this comment.
I did a 2nd partial review pass. Partial because I ran out of time, I will continue later (I reached line 350).
m9710797
left a comment
There was a problem hiding this comment.
Finished the 2nd review pass.
| parseCfg(cfgPath, loopOff, startOff); | ||
| } | ||
| loopOff = std::min(loopOff, sz); | ||
| startOff = std::min(startOff, sz); |
There was a problem hiding this comment.
I think this should be
loopOff = std::min(loopOff, sz - 1);
Because we have the invariant:
startSample <= loopSample < endSample
We have half-open intervals (good): start and loop are inclusive, end is exclusive. The original expression could set start/loop equal to end.
And this is the reason I suggested to reject empty wav files (wav.getSize == 0, around line 353). For an empty wav we cannot set start/loop strictly smaller than end.
You could say that start==end means an empty song, but the current implementation in generateChannels() (and with looping == true) assumes songs with at least 1 sample.
Also should we silently correct invalid loop/start values (via this std::min() correction)? Or should we give a warning and reject those invalidly configured songs?
Similar problem on line 449.
There was a problem hiding this comment.
@jeroentaverne what does the Pico do with such invalid loop/start values?
| size_t end = (i + 1 < multiStarts.size()) | ||
| ? std::min(multiStarts[i + 1], mSize) | ||
| : mSize; | ||
| if (start >= end) continue; |
There was a problem hiding this comment.
Because of this condition, I think the start offsets in multi.cfg must be (strictly?) ascending.
Should we silently reject songs for which this isn't true, or also give a warning?
There was a problem hiding this comment.
OK, so @jeroentaverne says:
For multi.wav the offsets are not required to be ascending.
So, I'm not sure what this means for the code. (I haven't fully understood this AI generated code yet...)
| .wavIdx = *multiIdx, | ||
| .startSample = start, | ||
| .endSample = end, | ||
| .loopSample = std::min(multiLoop, mSize), |
There was a problem hiding this comment.
Because multiLoop is the same for all songs, for all but one of the songs, loopSample will be outside the range [startSample, endSample) is that OK?
There was a problem hiding this comment.
@jeroentaverne and @MauricioBraga - can you please comment?
There was a problem hiding this comment.
The idea of multi.wav is to have one "big song", that has multiple start points (that are also loop offsets), one to each stage of the game. Imagine you are playing King's Valley. The 1st stage "song" starts, but the player is taking too long to finish the stage. So the music will be done playing the first stage song and it will "invade" the region of the 2nd stage music, and if necessary, the region of the 3rd stage song, 4th stage song... Until the user finishes the stage or the wave file ends (which will restart the music at the 1st stage start (loop) offset. When you reach the 2nd stage, it will now start from the 2nd stage start (loop offset) point and so on.
So, you have multiple start points in multi.cfg, that are also the offset points of each song in the big song (multi.wav). In another words, there's no single loop offset that is valid to all music, I guess the document is not very clear about this.
There was a problem hiding this comment.
If it helps to clarify, this is the multi.cfg file of the (still unreleased) Boulder Dash wave patch.
0
2648832
5313792
7940864
10588288
13235328
15883360
18525440
21174048
23816896
26466688
29109504
There was a problem hiding this comment.
Yes, so the loopsample is the same for all and the end is just the size. I'll adjust it in my next push.
There was a problem hiding this comment.
If by loop sample you mean the loop offset, each line in that multi.cfg file I posted it is both a start point but also a loop offset (if the music is supposed to be played in loop). if the game tells that a particular region of the multi.wav should be played in loop, it will comeback to to loop offset used from the point (line) where the music started. it will comeback to that point when the file ends. But if the game tells to play a particular index (line) without loop, music playing will end when the file ends.
There was a problem hiding this comment.
But that means there cannot be a real loop offset per song in a multi.wav, only for the first song? Isn't that a bit weird? Also, it means that if you have song 1, 2, 3, 4, start playing at 3, it will then play 4 and then continue with 3. Is that really the intention?
| ar.serialize("currentDir", currentDir); | ||
| if constexpr (Archive::IS_LOADER) { | ||
| if (!currentDir.empty()) { | ||
| loadSongs(); |
There was a problem hiding this comment.
This works when the saving/loading the savestate on the same system. We also have (limited) support for saving on one system and loading an another with a slightly different directory structure. We should try to do the same here.
There was a problem hiding this comment.
How do you propose we could do that? I don't immediately see how.
There was a problem hiding this comment.
OK, I made a start with it, see my next push.
There was a problem hiding this comment.
I think we should also serialize autoSamplesDir, and then on loadState restore either "auto" or "dir".
But the handling of currentDir itself seems fine.
There was a problem hiding this comment.
Should be done in the lastest push.
|
Hi. Just found out about the work on WaveGame. Let me know if you have any questions. I can share some code pieces if needed. |
Please review the code of this PR and check whether the behaviour will be the same as on the Pico.... |
|
Comments for now:
I will have another look later. Thanks for adding this feature! |
And what about the other properties? Does it require 48kHz, 16-bit? Or will it play anything you throw at it? We now have strict checks on this, but I'm not sure if the actual Pico checks that as well. |
34b6943 to
518da4e
Compare
The samplingrate is actually not checked as well. But should be 48kHz, else the PSG and SCC will run at incorrect frequency. 16 bits signed is a must. Currently on holiday. I will do some more review next week. |
b945545 to
6d36703
Compare
| { | ||
| auto& waveGame = OUTER(WaveGame, command); | ||
| if (tokens.size() == 1) { | ||
| result = waveGame.autoSamplesDir ? "auto" : std::string_view{waveGame.currentDir.getResolved()}; |
There was a problem hiding this comment.
Maybe instead of getResolved() we should return getOriginal(). You want to return the string as was originally passed? Or do you want to see the resolved version? You could argue either way:
- Setting followed by getting should give the save result. (E.g. that allows to get the current value, temporarily change it, and afterwards restore the original).
- Showing the resolved path could also be useful.
This is an extension cartridge that implements the wave-game protocol that is currently in use in the MSX-Pico. Use the new wavegame command to manually tweak the folder with the wav files. By default it tries to auto-load these from the same folder as the currently inserted media. It is also possible to let it use a specific media slot's media folder.
Is now done, so this is obsolete. |
|
Find attached the updated readme file. Hope it's clear now. :-) |
|
As reference also the updated C code used on MSX Pico. (Function file_load_number returns the number on a certain line in a file. The top line is line 0. When file or line doesn't exist, 0 is returned.) |
|
My comments:
|
|
|
@jeroentaverne OK, but what about my other questions? Basically: what to do in case of "illegal" situations. I think when using openMSX one should get warned if things will not work on a "real" WaveGame hardware (like Pico currently). So it's important to know what happens there and then we can replicate it. |
|
I thought I answered all questions. But a proper warning when the wav file isn't 48kHz 16 bits signed should be enough. |
|
@jeroentaverne what about illegal offsets or start positions? |
Illegal loop offset: wav file will play only once |
|
@jeroentaverne @m9710797 asked on IRC:
Can you comment on this? |
|
The fadeout can be 63 seconds, so there actually is an error in the manual. |
Can you please fix it and paste/upload the update, for completeness sake? |
I'm asking, because I'd like to establish a "definitive spec" which we can put in the openMSX implementation. Also including the updated version info byte and all other things discussed here. |
|
@m9710797 any comments and/or questions you still have on that spec in https://github.com/user-attachments/files/28197978/WaveGameReadme_updated.txt? |
|
@jeroentaverne did you see my questions from last week? |
|
@jeroentaverne reminder.... I can't really proceed with this without clarity on what the exact specification is. |
|
@jeroentaverne do you still care about this? |
Closes #1975