From 2a58188ee062ec12a515756bbbc54e8fde4aec43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miloslav=20=C4=8C=C3=AD=C5=BE?= Date: Thu, 16 Apr 2020 16:59:06 +0200 Subject: [PATCH] Continue music --- main.c | 95 ++++++++++++++++++++++++++++++++++++++------------ platform_sdl.h | 12 ++++++- sounds.h | 19 ++++++++-- 3 files changed, 101 insertions(+), 25 deletions(-) diff --git a/main.c b/main.c index 89f744e..3c8736e 100755 --- a/main.c +++ b/main.c @@ -101,6 +101,14 @@ static inline void SFG_setPixel(uint16_t x, uint16_t y, uint8_t colorIndex); */ void SFG_playSound(uint8_t soundIndex, uint8_t volume); + +/** + Informs the frontend whether music should get enabled/disabled. Playing music + is optional and the frontend can ignore it. If a frontend wants to implement + music, it can use the one provided in sounds.h or use its own. +*/ +void SFG_enableMusic(uint8_t enable); + /* ========================================================================= */ /** @@ -285,6 +293,9 @@ struct uint8_t selectedMenuItem; uint8_t selectedLevel; ///< Level to play selected in the main menu. uint8_t antiSpam; ///< Prevents log message spamming. + + uint8_t soundSettings; /**< Sound settings: LSB says whether SFX is on, + second LSB says whether music is on. */ } SFG_game; /** @@ -442,8 +453,11 @@ uint8_t SFG_random() return SFG_game.currentRandom; } -void SFG_playSoundSafe(uint8_t soundIndex, uint8_t volume) +void SFG_playGameSound(uint8_t soundIndex, uint8_t volume) { + if (!(SFG_game.soundSettings & 0x01)) + return; + uint8_t mask = 0x01 << soundIndex; if (!(SFG_game.soundsPlayedThisFrame & mask)) @@ -1418,6 +1432,9 @@ void SFG_init() SFG_game.antiSpam = 0; + SFG_game.soundSettings = 0xff; + SFG_enableMusic(1); + SFG_LOG("computing average texture colors") for (uint8_t i = 0; i < SFG_WALL_TEXTURE_COUNT; ++i) @@ -1693,10 +1710,10 @@ void SFG_monsterChangeHealth(SFG_MonsterRecord *monster, int8_t healthAdd) SFG_MONSTER_COORD_TO_SQUARES(monster->coords[0]), SFG_MONSTER_COORD_TO_SQUARES(monster->coords[1]))); - SFG_playSoundSafe(5,volume); + SFG_playGameSound(5,volume); if (monster->health == 0) - SFG_playSoundSafe(2,volume); + SFG_playGameSound(2,volume); } } @@ -1748,7 +1765,7 @@ void SFG_createExplosion(RCL_Unit x, RCL_Unit y, RCL_Unit z) { SFG_ProjectileRecord explosion; - SFG_playSound(2,SFG_distantSoundVolume(x,y,z)); + SFG_playGameSound(2,SFG_distantSoundVolume(x,y,z)); explosion.type = SFG_PROJECTILE_EXPLOSION; @@ -1926,7 +1943,7 @@ void SFG_monsterPerformAI(SFG_MonsterRecord *monster) } if (projectile == SFG_PROJECTILE_BULLET) - SFG_playSoundSafe(0, + SFG_playGameSound(0, SFG_distantSoundVolume( SFG_MONSTER_COORD_TO_RCL_UNITS(monster->coords[0]), SFG_MONSTER_COORD_TO_RCL_UNITS(monster->coords[1]), @@ -2014,7 +2031,7 @@ void SFG_monsterPerformAI(SFG_MonsterRecord *monster) SFG_playerChangeHealth( -1 * SFG_getDamageValue(SFG_WEAPON_FIRE_TYPE_MELEE)); - SFG_playSoundSafe(3,255); + SFG_playGameSound(3,255); } else // SFG_MONSTER_ATTACK_EXPLODE { @@ -2085,7 +2102,7 @@ void SFG_monsterPerformAI(SFG_MonsterRecord *monster) if ((coordAdd[0] != 0 || coordAdd[1] != 0) && SFG_random() < SFG_MONSTER_SOUND_PROBABILITY) - SFG_playSoundSafe(5, + SFG_playGameSound(5, SFG_distantSoundVolume( SFG_MONSTER_COORD_TO_RCL_UNITS(monster->coords[0]), SFG_MONSTER_COORD_TO_RCL_UNITS(monster->coords[1]), @@ -2309,7 +2326,7 @@ void SFG_updateLevel() else if (p->type == SFG_PROJECTILE_BULLET) SFG_createDust(p->position[0],p->position[1],p->position[2]); else if (p->type == SFG_PROJECTILE_PLASMA) - SFG_playSoundSafe(4,SFG_distantSoundVolume(pos[0],pos[1],pos[2])); + SFG_playGameSound(4,SFG_distantSoundVolume(pos[0],pos[1],pos[2])); // remove the projectile @@ -2360,7 +2377,7 @@ void SFG_updateLevel() ) ? SFG_DOOR_UP_DOWN_MASK : 0x00; if (upDownState != newUpDownState) - SFG_playSoundSafe(1,255); + SFG_playGameSound(1,255); door->state = (door->state & ~SFG_DOOR_UP_DOWN_MASK) | newUpDownState; @@ -2567,7 +2584,7 @@ void SFG_gameStepPlaying() } if (sound != 255) - SFG_playSoundSafe(sound,255); + SFG_playGameSound(sound,255); if (ammo != SFG_AMMO_NONE) SFG_player.ammo[ammo] -= projectileCount; @@ -2970,7 +2987,7 @@ void SFG_gameStepPlaying() SFG_removeItem(i); SFG_player.lastItemTakenFrame = SFG_game.frame; i--; - SFG_playSoundSafe(3,255); + SFG_playGameSound(3,255); #endif } else if ( @@ -3011,7 +3028,7 @@ void SFG_gameStepPlaying() SFG_player.justTeleported = 1; - SFG_playSoundSafe(4,255); + SFG_playGameSound(4,255); break; } @@ -3104,12 +3121,12 @@ void SFG_gameStepMenu() (SFG_game.selectedMenuItem < menuItems - 1)) { SFG_game.selectedMenuItem++; - SFG_playSoundSafe(3,64); + SFG_playGameSound(3,64); } else if (SFG_keyRegisters(SFG_KEY_UP) && (SFG_game.selectedMenuItem > 0)) { SFG_game.selectedMenuItem--; - SFG_playSoundSafe(3,64); + SFG_playGameSound(3,64); } else if (SFG_keyJustPressed(SFG_KEY_A)) { @@ -3135,6 +3152,23 @@ void SFG_gameStepMenu() SFG_setGameState(SFG_GAME_STATE_MAP); break; + case SFG_MENU_ITEM_SOUND: + SFG_LOG("sound changed"); + + SFG_game.soundSettings++; + SFG_playGameSound(3,64); + + if ((SFG_game.soundSettings & 0x02) != + ((SFG_game.soundSettings - 1) & 0x02)) + { + if (SFG_game.soundSettings & 0x02) + SFG_enableMusic(1); + else + SFG_enableMusic(0); + } + + break; + default: break; } @@ -3145,12 +3179,12 @@ void SFG_gameStepMenu() (SFG_game.selectedLevel < SFG_NUMBER_OF_LEVELS - 1)) { SFG_game.selectedLevel++; - SFG_playSoundSafe(3,64); + SFG_playGameSound(3,64); } else if (SFG_keyRegisters(SFG_KEY_LEFT) && SFG_game.selectedLevel > 0) { SFG_game.selectedLevel--; - SFG_playSoundSafe(3,64); + SFG_playGameSound(3,64); } } } @@ -3625,12 +3659,29 @@ void SFG_drawMenu() SFG_drawText(text,drawX,y,SFG_FONT_SIZE_MEDIUM,textColor,0,0); - if (item == SFG_MENU_ITEM_PLAY && - (((i != SFG_game.selectedMenuItem) || - (SFG_game.frame / SFG_BLINK_PERIOD_FRAMES) % 2))) - SFG_drawNumber((SFG_game.selectedLevel + 1), - drawX + SFG_characterSize(SFG_FONT_SIZE_MEDIUM) * (textLen + 1), - y,SFG_FONT_SIZE_MEDIUM,93); + if ((item == SFG_MENU_ITEM_PLAY || item == SFG_MENU_ITEM_SOUND) && + ((i != SFG_game.selectedMenuItem) || + ((SFG_game.frame / SFG_BLINK_PERIOD_FRAMES) % 2))) + { + //uint8_t blink = (SFG_game.frame / SFG_BLINK_PERIOD_FRAMES) % 2; + + uint32_t x = + drawX + SFG_characterSize(SFG_FONT_SIZE_MEDIUM) * (textLen + 1); + + uint8_t c = 93; + + if (item == SFG_MENU_ITEM_PLAY) + SFG_drawNumber((SFG_game.selectedLevel + 1),x,y,SFG_FONT_SIZE_MEDIUM,c); + else + { + char settingText[3] = " "; + + settingText[0] = (SFG_game.soundSettings & 0x01) ? 'S' : ' '; + settingText[1] = (SFG_game.soundSettings & 0x02) ? 'M' : ' '; + + SFG_drawText(settingText,x,y,SFG_FONT_SIZE_MEDIUM,c,0,0); + } + } y += SFG_characterSize(SFG_FONT_SIZE_MEDIUM) + SFG_FONT_SIZE_MEDIUM; i++; diff --git a/platform_sdl.h b/platform_sdl.h index c60521d..56b8da0 100644 --- a/platform_sdl.h +++ b/platform_sdl.h @@ -188,16 +188,26 @@ static inline uint8_t addSamples(uint8_t sample1, uint8_t sample2) return mixed; } +uint8_t musicOn = 1; + void audioFillCallback(void *userdata, uint8_t *s, int l) { for (int i = 0; i < l; ++i) { - s[i] = addSamples(audioBuff[audioPos],SFG_getNextMusicSample()); + s[i] = musicOn ? + addSamples(audioBuff[audioPos],SFG_getNextMusicSample()) : + audioBuff[audioPos]; + audioBuff[audioPos] = 127; audioPos = (audioPos < SFG_SFX_SAMPLE_COUNT - 1) ? (audioPos + 1) : 0; } } +void SFG_enableMusic(uint8_t enable) +{ + musicOn = enable; +} + void SFG_playSound(uint8_t soundIndex, uint8_t volume) { uint8_t volumeStep = volume / 16; diff --git a/sounds.h b/sounds.h index de14750..0ecea03 100644 --- a/sounds.h +++ b/sounds.h @@ -10,6 +10,9 @@ by Miloslav Ciz (drummyfish), 2019 + Music is based on bytebeat (procedural waveforms generated by short bitwise + operation formulas). + Released under CC0 1.0 (https://creativecommons.org/publicdomain/zero/1.0/) plus a waiver of all other intellectual property. The goal of this work is be and remain completely in the public domain forever, available for any use @@ -34,6 +37,7 @@ struct { // all should be initialized to 0 + // TODO: leave only those needed here uint8_t track; uint32_t t; uint32_t t2; @@ -42,13 +46,17 @@ struct uint32_t n7t; } SFG_MusicState; +/** + Gets the next 8bit 8KHz music sample for the bytebeat soundtrack. This + function is to be used by the frontend that plays music. +*/ uint8_t SFG_getNextMusicSample() { if (SFG_MusicState.t >= SFG_TRACK_SAMPLES) { SFG_MusicState.track++; - if (SFG_MusicState.track >= 3) + if (SFG_MusicState.track >= 4) SFG_MusicState.track = 0; SFG_MusicState.t = 0; @@ -64,7 +72,7 @@ uint8_t SFG_getNextMusicSample() #define t2 SFG_MusicState.t2 #define n3t SFG_MusicState.n3t - switch (SFG_MusicState.track) + switch (SFG_MusicState.track) // individual music tracks { case 0: { @@ -82,6 +90,13 @@ uint8_t SFG_getNextMusicSample() } case 2: + { + result = + ~((((t >> (t >> 2)) | (t >> (t >> 5))) & 0x12) << 1) | (t >> 11); + break; + } + + case 3: { result = ((((t >> (t >> 2)) + (t >> (t >> 7)))) & 0x3f | (t >> 5) | (t >> 11))