So, this would allow the 4 channels to work with streaming music from SPIRAM and a sound effect from FLASH?
What if I could store them both in FLASH but with the new more condensed data format?
So, this would allow the 4 channels to work with streaming music from SPIRAM and a sound effect from FLASH?
You'll have to ask D3thAdd3r about that; I haven't messed with the SPIRAM stuff at all.nicksen782 wrote: ↑Fri Apr 06, 2018 3:56 amSo, this would allow the 4 channels to work with streaming music from SPIRAM and a sound effect from FLASH?
What if I could store them both in FLASH but with the new more condensed data format?
Code: Select all
../src/code/bb_music.c: In function ‘N782_changeSong’:
../src/code/bb_music.c:136:3: ERROR: too few arguments to function ‘StartSong’
StartSong();
^
In file included from ../src/code/BubbleBobble.h:8:0,
from ../src/code/BubbleBobble.c:108:
../kernel/uzebox/kernel/uzebox.h:110:16: note: declared here
extern void StartSong(const char *song);
Code: Select all
# Music options
KERNEL_OPTIONS += -DMUSIC_ENGINE=STREAM -DSTREAM_MUSIC_DEBUG=1 -DSONG_BUFFER_SIZE=16
KERNEL_OPTIONS += -DSTREAM_MUSIC_RAM=1
KERNEL_OPTIONS += -DSOUND_CHANNEL_5_ENABLE=0 -DSOUND_MIXER=1 -DSONG_SPEED=1
So, to use the newer compressed midi but with flash, I would then NOT include the "STREAM_MUSIC_RAM=1" and instead of including the binary sound into a data file loaded into SPIRAM, I would just use a normal include in the game code like before? But with the benefits of the smaller FLASH footprint for each song?
The buffering is now optional then? I can chose not to buffer meaning whatever I want to play would need to be in FLASH. Right? This implies that you can chose between the original stock music player or the new streaming one (even if that stream is coming from FLASH and not SPIRAM/SDCARD?
"TriggerFx(19,0xff,true);" would play a sound effect (whatever 19 happens to be.) You would still need to do "InitMusicPlayer(patches);" beforehand, right? Are you saying that the new streaming music has no bearing on sound effects? Sound effects are patches consisting of some notes? What is the difference between that and a song other than the length?
Correct. It should save a good deal of space, and also a couple bytes of ram over the MIDI player due to some optimizations. It is called the same way from code, with StartSong(songName), etc. None of the juggling required like for the buffered version.nicksen782 wrote: ↑Mon Apr 09, 2018 4:17 am So, to use the newer compressed midi but with flash, I would then NOT include the "STREAM_MUSIC_RAM=1" and instead of including the binary sound into a data file loaded into SPIRAM, I would just use a normal include in the game code like before? But with the benefits of the smaller FLASH footprint for each song?
Yes, streaming music has 2 modes now and basically this will be the final state as I see it. "-DMUSIC_ENGINE=STREAMING" is for FLASH bufferless operation, and "-DMUSIC_ENGINE=STREAMING -DSTREAM_MUSIC_RAM=1 -DSONG_BUFFER_SIZE=XX" is for ram buffered version. The caveats of all the buffered stuff you already know, and none of that part has changed in a good while so should work just like before. The recent changes were just to add the flash version, which basically just replaces the circular buffer code with a pointer version similar to the stock MIDI player.nicksen782 wrote: ↑Mon Apr 09, 2018 4:17 am The buffering is now optional then? I can chose not to buffer meaning whatever I want to play would need to be in FLASH. Right? This implies that you can chose between the original stock music player or the new streaming one (even if that stream is coming from FLASH and not SPIRAM/SDCARD?
Yes, no buffer should be specified for the FLASH only version as it does not use a buffer, rather it reads from a pointer that always moves forward until it reaches a song end, or a loop end, just like the MIDI player but in a different format. songStalls is the only thing STREAM_MUSIC_DEBUG does, and it's only use might be for testing how small a buffer size you can get away with. There would be no real purpose to leave it enabled in a game release.nicksen782 wrote: ↑Mon Apr 09, 2018 4:17 am Just to be certain then: "MUSIC_ENGINE=STREAM" activates the new music player. For FLASH only I would not need "SONG_BUFFER_SIZE=16".
Furthermore, "STREAM_MUSIC_DEBUG=1" only works with "songStalls" which is optional, right?
Yes.nicksen782 wrote: ↑Mon Apr 09, 2018 4:17 am "TriggerFx(19,0xff,true);" would play a sound effect (whatever 19 happens to be.) You would still need to do "InitMusicPlayer(patches);" beforehand, right? Are you saying that the new streaming music has no bearing on sound effects?
A sound effect and a song are different ways of controlling patches, but ultimately either one of those relies on the channel playing some sort of patch. You could well make a short song inside a single patch with appropriate timings between events, but this would not be space efficient to repeat envelopes, etc for every note, nor fun to develop. It would also get messy trying to control more than one channel, as sound effects are not optimized for this, though several games do have multi-channel sound effects. You could I suppose also start a song that triggered a specific patch, in effect using StartSong(x) to trigger a sound effect in an inefficient way. The point is, in general there are things you want from a sound effect(don't clobber the music, have some basic priority system among sound effects), and things you want from a music engine(assume the patch should always play ignoring what sound effects are doing, and also pass a specific note in addition to the volume). By making these assumptions, both can be done more optimally for the types of things they are likely to be used for. But in some sense yes, they are just different ways of controlling the channels underneath, and at the end of the line it is just patches. It is just safe to assume that most of the time a song will consist of a linear series of patch triggers, where sound effects are generally not as predictable and often based on game logic dictated by human input. Thus, the assumptions you can make about either one of them is quite different thus the different implementations.nicksen782 wrote: ↑Mon Apr 09, 2018 4:17 am What is the difference between that and a song other than the length?
Code: Select all
void TriggerFx(unsigned char patch,unsigned char volume,bool retrig){
unsigned char channel;
unsigned char type=(unsigned char)pgm_read_byte(&(patchPointers[patch].type));
//find the channel to play the fx
//try to steal voice 2 then 1
//never steal voice 0, reserve it for lead melodies
if(type==1 || (type==2 && MIXER_CHAN4_TYPE == 1)){
//noise or PCM channel fx
channel=3;
}else if(type==2){
channel=4;
}else if( (tracks[1].flags&TRACK_FLAGS_PRIORITY)==0 || (tracks[1].fxPatchNo==patch && (tracks[1].flags&TRACK_FLAGS_PRIORITY)!=0 && retrig==true)){ //fx already playing
channel=1;
}else if( (tracks[2].flags&TRACK_FLAGS_PRIORITY)==0 || (tracks[2].fxPatchNo==patch && (tracks[2].flags&TRACK_FLAGS_PRIORITY)!=0 && retrig==true)){ //fx already playing
channel=2;
}else{
//both channels have fx playing, use the oldest one
if(tracks[1].patchPlayingTime>tracks[2].patchPlayingTime){
channel=1;
}else{
channel=2;
}
}
Track* track=&tracks[channel];
track->flags=TRACK_FLAGS_PRIORITY; //priority=1;
track->patchCommandStreamPos = NULL;
TriggerCommon(track,patch,volume,80);//<<<<======================
track->flags|=TRACK_FLAGS_PLAYING;
}
Code: Select all
...
switch(lastStatus&0xf0){
//note-on
case 0x90:
//c1 = note
c2=pgm_read_byte(songPos++)<<1; //get volume
if(tracks[channel].flags|TRACK_FLAGS_ALLOCATED){ //allocated==true
TriggerNote(channel,tracks[channel].patchNo,c1,c2);//<========================
}
break;
...
Code: Select all
...
}else if((c1 & 0b11100000) < 0b10100000){//0bCCCXXXXX, if (CCC>>5) is 0-4, then it is a Note On
//Note On: 0bCCCVVVVV, VNNNNNNN = CCC = channel, VVVVV V = volume, NNNNNNN = note
channel = (c1>>5);//get channel
c1 = (c1 & 0b00011111);//get volume 5 LSBits
c2 = SongBufRead();//get packed note and MSBit of volume
c1 |= (c2 & 0b10000000)>>2;//add 1 MSBit to previous LSBits for 6 bits total volume
c1 <<= 2;//convert our 6 bits to: 7 bit converted to 8 bit volume of original format
c2 &= 0b01111111;//mask out the MSbit of volume, leaving just the note
//c2 = note, c1 = volume
if(tracks[channel].flags|TRACK_FLAGS_ALLOCATED)//allocated==true
TriggerNote(channel,tracks[channel].patchNo,c2,c1);//<==============================
...
Code: Select all
void TriggerNote(unsigned char channel,unsigned char patch,unsigned char note,unsigned char volume){
Track* track=&tracks[channel];
//allow only other music notes
if((track->flags&TRACK_FLAGS_PLAYING)==0 || (track->flags&TRACK_FLAGS_PRIORITY)==0){
if(volume==0){ //note-off received
//cut note if there's no envelope & no note hold
if(track->envelopeStep==0 && !(track->flags&TRACK_FLAGS_HOLD_ENV)){
track->noteVol=0;
}
track->flags&=(~TRACK_FLAGS_HOLD_ENV);//patchEnvelopeHold=false;
}else{
track->flags=0;//&=(~TRACK_FLAGS_PRIORITY);// priority=0;
track->patchCommandStreamPos = NULL;
TriggerCommon(track,patch,volume,note);//<=============================
track->flags|=TRACK_FLAGS_PLAYING;
}
}
}