mirror of
https://github.com/cxong/cdogs-sdl.git
synced 2025-07-23 07:23:01 +02:00
Play wolf music
This commit is contained in:
32
doc/license-mame.txt
Normal file
32
doc/license-mame.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
Copyright (c) 1997-2005, Nicola Salmoria and the MAME team
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this code or any derivative works are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
* Redistributions may not be sold, nor may they be used in a commercial
|
||||
product or activity.
|
||||
|
||||
* Redistributions that are modified from the original source must include the
|
||||
complete source code, including the source code for all components used by a
|
||||
binary built from the modified sources. However, as a special exception, the
|
||||
source code distributed need not include anything that is normally distributed
|
||||
(in either source or binary form) with the major components (compiler, kernel,
|
||||
and so on) of the operating system on which the executable runs, unless that
|
||||
component itself accompanies the executable.
|
||||
|
||||
* Redistributions must reproduce the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
@@ -74,7 +74,7 @@ static void CampaignIntroTerminate(GameLoopData *data)
|
||||
static void CampaignIntroOnEnter(GameLoopData *data)
|
||||
{
|
||||
UNUSED(data);
|
||||
MusicPlay(&gSoundDevice, MUSIC_BRIEFING, NULL, NULL);
|
||||
MusicPlayGeneral(&gSoundDevice.music, MUSIC_BRIEFING);
|
||||
}
|
||||
static void CampaignIntroOnExit(GameLoopData *data)
|
||||
{
|
||||
@@ -435,7 +435,7 @@ static void MissionSummaryOnEnter(GameLoopData *data)
|
||||
{
|
||||
MissionSummaryData *mData = data->Data;
|
||||
|
||||
MusicPlay(&gSoundDevice, MUSIC_BRIEFING, NULL, NULL);
|
||||
MusicPlayGeneral(&gSoundDevice.music, MUSIC_BRIEFING);
|
||||
|
||||
if (mData->completed && CanLevelSelect(mData->c->Entry.Mode))
|
||||
{
|
||||
|
@@ -1,4 +1,7 @@
|
||||
set(CW_SOURCES audio.c cwolfmap.c expand.c vswap.c)
|
||||
set(CWHEADERS AUDIOWL1.H audiowl6.h audio.h cwolfmap.h expand.h vswap.h)
|
||||
set(CW_SOURCES audio.c cwolfmap.c expand.c vswap.c mame/fmopl.c)
|
||||
set(CWHEADERS AUDIOWL1.H audiowl6.h audio.h cwolfmap.h expand.h vswap.h mame/fmopl.h)
|
||||
|
||||
add_library(cwolfmap STATIC ${CW_SOURCES} ${CWHEADERS})
|
||||
if(NOT WIN32)
|
||||
target_link_libraries(cwolfmap m)
|
||||
endif()
|
||||
|
@@ -4,8 +4,71 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "audiowl6.h"
|
||||
#include "mame/fmopl.h"
|
||||
|
||||
#define PATH_MAX 4096
|
||||
static int volume = 20;
|
||||
const int oplChip = 0;
|
||||
#define OPL_CHANNELS 9
|
||||
#define MUSIC_RATE 700
|
||||
#define SAMPLES_PER_MUSIC_TICK (MUSIC_SAMPLE_RATE / MUSIC_RATE)
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef struct
|
||||
{
|
||||
uint8_t mChar, cChar, mScale, cScale, mAttack, cAttack, mSus, cSus, mWave,
|
||||
cWave, nConn,
|
||||
|
||||
// These are only for Muse - these bytes are really unused
|
||||
voice, mode;
|
||||
uint8_t unused[3];
|
||||
} AlInstrument;
|
||||
#pragma pack(pop)
|
||||
|
||||
static const AlInstrument ChannelRelease = {
|
||||
0, 0, 0x3F, 0x3F, 0xFF, 0xFF, 0xF, 0xF, 0, 0, 0,
|
||||
|
||||
0, 0, {0, 0, 0}};
|
||||
|
||||
#define alOut(n, b) YM3812Write(oplChip, n, b, &volume)
|
||||
|
||||
// Register addresses
|
||||
// Operator stuff
|
||||
#define alChar 0x20
|
||||
#define alScale 0x40
|
||||
#define alAttack 0x60
|
||||
#define alSus 0x80
|
||||
#define alWave 0xe0
|
||||
// Channel stuff
|
||||
#define alFreqL 0xa0
|
||||
#define alFreqH 0xb0
|
||||
#define alFeedCon 0xc0
|
||||
// Global stuff
|
||||
#define alEffects 0xbd
|
||||
|
||||
static void AlSetChanInst(const AlInstrument *inst, unsigned int chan)
|
||||
{
|
||||
static const uint8_t chanOps[OPL_CHANNELS] = {0, 1, 2, 8, 9,
|
||||
0xA, 0x10, 0x11, 0x12};
|
||||
uint8_t c, m;
|
||||
|
||||
m = chanOps[chan]; // modulator cell for channel
|
||||
c = m + 3; // carrier cell for channel
|
||||
alOut(m + alChar, inst->mChar);
|
||||
alOut(m + alScale, inst->mScale);
|
||||
alOut(m + alAttack, inst->mAttack);
|
||||
alOut(m + alSus, inst->mSus);
|
||||
alOut(m + alWave, inst->mWave);
|
||||
alOut(c + alChar, inst->cChar);
|
||||
alOut(c + alScale, inst->cScale);
|
||||
alOut(c + alAttack, inst->cAttack);
|
||||
alOut(c + alSus, inst->cSus);
|
||||
alOut(c + alWave, inst->cWave);
|
||||
|
||||
alOut(chan + alFreqL, 0);
|
||||
alOut(chan + alFreqH, 0);
|
||||
alOut(chan + alFeedCon, 0);
|
||||
}
|
||||
|
||||
int CWAudioLoadHead(CWAudioHead *head, const char *path)
|
||||
{
|
||||
@@ -26,10 +89,22 @@ int CWAudioLoadHead(CWAudioHead *head, const char *path)
|
||||
head->nOffsets)
|
||||
{
|
||||
err = -1;
|
||||
fprintf(stderr, "Failed to read audio head");
|
||||
fprintf(stderr, "Failed to read audio head\n");
|
||||
goto bail;
|
||||
}
|
||||
|
||||
// Init adlib
|
||||
if (YM3812Init(1, 3579545, MUSIC_SAMPLE_RATE))
|
||||
{
|
||||
fprintf(stderr, "Unable to create virtual OPL\n");
|
||||
goto bail;
|
||||
}
|
||||
for (int i = 1; i < 0xf6; i++)
|
||||
{
|
||||
YM3812Write(oplChip, i, 0, &volume);
|
||||
}
|
||||
YM3812Write(oplChip, 1, 0x20, &volume); // Set WSE=1
|
||||
|
||||
bail:
|
||||
if (f)
|
||||
{
|
||||
@@ -75,6 +150,7 @@ bail:
|
||||
void CWAudioFree(CWAudio *audio)
|
||||
{
|
||||
CWAudioHeadFree(&audio->head);
|
||||
YM3812Shutdown();
|
||||
free(audio->data);
|
||||
}
|
||||
|
||||
@@ -96,7 +172,7 @@ bail:
|
||||
return err;
|
||||
}
|
||||
|
||||
int CWAudioGetMusic(
|
||||
int CWAudioGetMusicRaw(
|
||||
const CWAudio *audio, const int i, const char **data, size_t *len)
|
||||
{
|
||||
int err = 0;
|
||||
@@ -108,8 +184,112 @@ int CWAudioGetMusic(
|
||||
err = -1;
|
||||
goto bail;
|
||||
}
|
||||
if (*len == 88)
|
||||
{
|
||||
*len = 0;
|
||||
goto bail;
|
||||
}
|
||||
*data = &audio->data[off];
|
||||
|
||||
bail:
|
||||
return err;
|
||||
}
|
||||
|
||||
int CWAudioGetMusic(
|
||||
const CWAudio *audio, const int idx, char **data, size_t *len)
|
||||
{
|
||||
*data = NULL;
|
||||
*len = 0;
|
||||
const char *rawData;
|
||||
size_t rawLen;
|
||||
int err = CWAudioGetMusicRaw(audio, idx, &rawData, &rawLen);
|
||||
if (err != 0)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
if (rawLen == 0)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
|
||||
for (int i = 0; i < OPL_CHANNELS; i++)
|
||||
{
|
||||
AlSetChanInst(&ChannelRelease, i);
|
||||
}
|
||||
|
||||
// Measure length of music
|
||||
const uint16_t *sqHack = (const uint16_t *)rawData;
|
||||
int sqHackLen;
|
||||
if (*sqHack == 0)
|
||||
{
|
||||
// LumpLength?
|
||||
sqHackLen = (int)rawLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqHackLen = *sqHack++;
|
||||
}
|
||||
const uint16_t *sqHackPtr = sqHack;
|
||||
|
||||
int sqHackTime = 0;
|
||||
int alTimeCount;
|
||||
for (alTimeCount = 0; sqHackLen > 0; alTimeCount++)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (sqHackTime > alTimeCount)
|
||||
break;
|
||||
sqHackTime = alTimeCount + *(sqHackPtr + 1);
|
||||
sqHackPtr += 2;
|
||||
sqHackLen -= 4;
|
||||
} while (sqHackLen > 0);
|
||||
}
|
||||
|
||||
// Decode music
|
||||
// 2 bytes per sample (16-bit audio fmt)
|
||||
*len = alTimeCount * SAMPLES_PER_MUSIC_TICK * MUSIC_AUDIO_CHANNELS * 2;
|
||||
*data = malloc(*len);
|
||||
int16_t *stream16 = (int16_t *)*data;
|
||||
|
||||
sqHack = (const uint16_t *)rawData;
|
||||
if (*sqHack == 0)
|
||||
{
|
||||
// LumpLength?
|
||||
sqHackLen = (int)rawLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqHackLen = *sqHack++;
|
||||
}
|
||||
sqHackPtr = sqHack;
|
||||
sqHackTime = 0;
|
||||
for (alTimeCount = 0; sqHackLen > 0; alTimeCount++)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (sqHackTime > alTimeCount)
|
||||
break;
|
||||
sqHackTime = alTimeCount + *(sqHackPtr + 1);
|
||||
alOut(
|
||||
*(const uint8_t *)sqHackPtr,
|
||||
*(((const uint8_t *)sqHackPtr) + 1));
|
||||
sqHackPtr += 2;
|
||||
sqHackLen -= 4;
|
||||
} while (sqHackLen > 0);
|
||||
|
||||
const int numreadysamples = SAMPLES_PER_MUSIC_TICK;
|
||||
YM3812UpdateOne(oplChip, stream16, numreadysamples);
|
||||
|
||||
stream16 += numreadysamples * MUSIC_AUDIO_CHANNELS;
|
||||
}
|
||||
|
||||
return err;
|
||||
|
||||
bail:
|
||||
if (err != 0)
|
||||
{
|
||||
free(*data);
|
||||
*data = NULL;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@@ -1,6 +1,8 @@
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL_audio.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t *offsets;
|
||||
@@ -15,6 +17,10 @@ typedef struct
|
||||
char *data;
|
||||
} CWAudio;
|
||||
|
||||
#define MUSIC_SAMPLE_RATE 44100
|
||||
#define MUSIC_AUDIO_FMT AUDIO_S16
|
||||
#define MUSIC_AUDIO_CHANNELS 2
|
||||
|
||||
int CWAudioLoadHead(CWAudioHead *head, const char *path);
|
||||
void CWAudioHeadFree(CWAudioHead *head);
|
||||
|
||||
@@ -25,5 +31,7 @@ void CWAudioFree(CWAudio *audio);
|
||||
// http://www.vgmpf.com/Wiki/index.php?title=IMF
|
||||
int CWAudioGetAdlibSound(
|
||||
const CWAudio *audio, const int i, const char **data, size_t *len);
|
||||
int CWAudioGetMusic(
|
||||
int CWAudioGetMusicRaw(
|
||||
const CWAudio *audio, const int i, const char **data, size_t *len);
|
||||
int CWAudioGetMusic(
|
||||
const CWAudio *audio, const int i, char **data, size_t *len);
|
||||
|
1960
src/cdogs/cwolfmap/mame/fmopl.c
Normal file
1960
src/cdogs/cwolfmap/mame/fmopl.c
Normal file
File diff suppressed because it is too large
Load Diff
47
src/cdogs/cwolfmap/mame/fmopl.h
Normal file
47
src/cdogs/cwolfmap/mame/fmopl.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#ifndef __FMOPL_H_
|
||||
#define __FMOPL_H_
|
||||
|
||||
/* select output bits size of output : 8 or 16 */
|
||||
#define OPL_SAMPLE_BITS 16
|
||||
|
||||
/* compiler dependence */
|
||||
#ifndef OSD_CPU_H
|
||||
#define OSD_CPU_H
|
||||
typedef unsigned char UINT8; /* unsigned 8bit */
|
||||
typedef unsigned short UINT16; /* unsigned 16bit */
|
||||
typedef unsigned int UINT32; /* unsigned 32bit */
|
||||
typedef signed char INT8; /* signed 8bit */
|
||||
typedef signed short INT16; /* signed 16bit */
|
||||
typedef signed int INT32; /* signed 32bit */
|
||||
|
||||
typedef int BOOL;
|
||||
#endif
|
||||
|
||||
#if (OPL_SAMPLE_BITS==16)
|
||||
typedef INT16 OPLSAMPLE;
|
||||
#endif
|
||||
#if (OPL_SAMPLE_BITS==8)
|
||||
typedef INT8 OPLSAMPLE;
|
||||
#endif
|
||||
|
||||
|
||||
typedef void (*OPL_TIMERHANDLER)(int channel,double interval_Sec);
|
||||
typedef void (*OPL_IRQHANDLER)(int param,int irq);
|
||||
typedef void (*OPL_UPDATEHANDLER)(int param,int min_interval_us);
|
||||
typedef void (*OPL_PORTHANDLER_W)(int param,unsigned char data);
|
||||
typedef unsigned char (*OPL_PORTHANDLER_R)(int param);
|
||||
|
||||
int YM3812Init(int num, int clock, int rate);
|
||||
void YM3812Shutdown(void);
|
||||
void YM3812ResetChip(int which);
|
||||
int YM3812Write(int which, int a, int v, const int *volume);
|
||||
unsigned char YM3812Read(int which, int a);
|
||||
void YM3812Mute(int which,int channel,BOOL mute);
|
||||
int YM3812TimerOver(int which, int c);
|
||||
void YM3812UpdateOne(int which, INT16 *buffer, int length);
|
||||
|
||||
void YM3812SetTimerHandler(int which, OPL_TIMERHANDLER TimerHandler, int channelOffset);
|
||||
void YM3812SetIRQHandler(int which, OPL_IRQHANDLER IRQHandler, int param);
|
||||
void YM3812SetUpdateHandler(int which, OPL_UPDATEHANDLER UpdateHandler, int param);
|
||||
|
||||
#endif /* __FMOPL_H_ */
|
32
src/cdogs/cwolfmap/mame/license-mame.txt
Normal file
32
src/cdogs/cwolfmap/mame/license-mame.txt
Normal file
@@ -0,0 +1,32 @@
|
||||
Copyright (c) 1997-2005, Nicola Salmoria and the MAME team
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this code or any derivative works are permitted
|
||||
provided that the following conditions are met:
|
||||
|
||||
* Redistributions may not be sold, nor may they be used in a commercial
|
||||
product or activity.
|
||||
|
||||
* Redistributions that are modified from the original source must include the
|
||||
complete source code, including the source code for all components used by a
|
||||
binary built from the modified sources. However, as a special exception, the
|
||||
source code distributed need not include anything that is normally distributed
|
||||
(in either source or binary form) with the major components (compiler, kernel,
|
||||
and so on) of the operating system on which the executable runs, unless that
|
||||
component itself accompanies the executable.
|
||||
|
||||
* Redistributions must reproduce the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
@@ -171,13 +171,13 @@ void EventPoll(
|
||||
{
|
||||
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||
regainedFocus = true;
|
||||
MusicSetPlaying(&gSoundDevice, true);
|
||||
MusicSetPlaying(&gSoundDevice.music, true);
|
||||
break;
|
||||
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||
if (!gCampaign.IsClient &&
|
||||
!ConfigGetBool(&gConfig, "StartServer"))
|
||||
{
|
||||
MusicSetPlaying(&gSoundDevice, false);
|
||||
MusicSetPlaying(&gSoundDevice.music, false);
|
||||
handlers->HasLostFocus = true;
|
||||
}
|
||||
// Reset input handlers
|
||||
|
@@ -427,7 +427,11 @@ static void ConvertMission(
|
||||
CArrayPushBack(&dest->Weapons, &wc);
|
||||
}
|
||||
}
|
||||
strcpy(dest->Song, src->song);
|
||||
if (strlen(src->song) > 0)
|
||||
{
|
||||
dest->Music.Type = MUSIC_SRC_DYNAMIC;
|
||||
CSTRDUP(dest->Music.Data.Filename, src->song);
|
||||
}
|
||||
const color_t maskAlt =
|
||||
RangeToColor(abs(src->altRange) % COLORRANGE_COUNT);
|
||||
|
||||
|
@@ -135,12 +135,13 @@ int MapNewLoadArchive(const char *filename, CampaignSetting *c)
|
||||
}
|
||||
if (hasCustomAmmo)
|
||||
{
|
||||
PickupClassesLoadAmmo(&gPickupClasses.CustomClasses, &gAmmo.CustomAmmo);
|
||||
PickupClassesLoadAmmo(
|
||||
&gPickupClasses.CustomClasses, &gAmmo.CustomAmmo);
|
||||
}
|
||||
if (hasCustomGuns)
|
||||
{
|
||||
PickupClassesLoadGuns(
|
||||
&gPickupClasses.CustomClasses, &gWeaponClasses.CustomGuns);
|
||||
&gPickupClasses.CustomClasses, &gWeaponClasses.CustomGuns);
|
||||
}
|
||||
PickupClassesLoadKeys(&gPickupClasses.KeyClasses);
|
||||
|
||||
@@ -361,8 +362,12 @@ static json_t *SaveMissions(CArray *a)
|
||||
json_insert_pair_into_object(
|
||||
node, "Weapons", SaveWeapons(&mission->Weapons));
|
||||
|
||||
json_insert_pair_into_object(
|
||||
node, "Song", json_new_string(mission->Song));
|
||||
if (mission->Music.Type == MUSIC_SRC_DYNAMIC &&
|
||||
strlen(mission->Music.Data.Filename) > 0)
|
||||
{
|
||||
json_insert_pair_into_object(
|
||||
node, "Song", json_new_string(mission->Music.Data.Filename));
|
||||
}
|
||||
|
||||
switch (mission->Type)
|
||||
{
|
||||
@@ -411,8 +416,8 @@ static json_t *SaveMissions(CArray *a)
|
||||
AddBoolPair(node, "ExitEnabled", mission->u.Interior.ExitEnabled);
|
||||
json_insert_pair_into_object(
|
||||
node, "Doors", SaveDoors(mission->u.Interior.Doors));
|
||||
json_insert_pair_into_object(
|
||||
node, "Pillars", SavePillars(mission->u.Interior.Pillars));
|
||||
json_insert_pair_into_object(
|
||||
node, "Pillars", SavePillars(mission->u.Interior.Pillars));
|
||||
break;
|
||||
default:
|
||||
CASSERT(false, "unknown map type");
|
||||
|
@@ -292,7 +292,8 @@ void LoadMissions(CArray *missions, json_t *missionsNode, int version)
|
||||
LoadInt(&m.EnemyDensity, child, "EnemyDensity");
|
||||
LoadWeapons(
|
||||
&m.Weapons, json_find_first_label(child, "Weapons")->child);
|
||||
strcpy(m.Song, json_find_first_label(child, "Song")->child->text);
|
||||
m.Music.Type = MUSIC_SRC_DYNAMIC;
|
||||
LoadStr(&m.Music.Data.Filename, child, "Song");
|
||||
switch (m.Type)
|
||||
{
|
||||
case MAPTYPE_CLASSIC:
|
||||
@@ -309,7 +310,9 @@ void LoadMissions(CArray *missions, json_t *missionsNode, int version)
|
||||
LoadDoors(
|
||||
&m.u.Classic.Doors,
|
||||
json_find_first_label(child, "Doors")->child);
|
||||
LoadPillars(&m.u.Classic.Pillars, json_find_first_label(child, "Pillars")->child);
|
||||
LoadPillars(
|
||||
&m.u.Classic.Pillars,
|
||||
json_find_first_label(child, "Pillars")->child);
|
||||
break;
|
||||
case MAPTYPE_STATIC:
|
||||
if (!MissionStaticTryLoadJSON(
|
||||
@@ -353,7 +356,9 @@ void LoadMissions(CArray *missions, json_t *missionsNode, int version)
|
||||
LoadDoors(
|
||||
&m.u.Interior.Doors,
|
||||
json_find_first_label(child, "Doors")->child);
|
||||
LoadPillars(&m.u.Interior.Pillars, json_find_first_label(child, "Pillars")->child);
|
||||
LoadPillars(
|
||||
&m.u.Interior.Pillars,
|
||||
json_find_first_label(child, "Pillars")->child);
|
||||
break;
|
||||
default:
|
||||
assert(0 && "unknown map type");
|
||||
|
@@ -125,35 +125,107 @@ static const char *GetSound(const CWMapType type, const int i)
|
||||
}
|
||||
}
|
||||
|
||||
static const char *musicW[] = {
|
||||
"corner", // 0
|
||||
"dungeon", // 1
|
||||
"warmarch", // 2
|
||||
"getthem", // 3
|
||||
"headache", // 4
|
||||
"hitlerwaltz", // 5
|
||||
"introcw3", // 6
|
||||
"nazi_nor", // 7
|
||||
"nazi_omi", // 8
|
||||
"pow", // 9
|
||||
"salute", // 10
|
||||
"searchn", // 11
|
||||
"suspense", // 12
|
||||
"victors", // 13
|
||||
"wonderin", // 14
|
||||
"funkyou", // 15
|
||||
"endlevel", // 16
|
||||
"goingaft", // 17
|
||||
"pregnant", // 18
|
||||
"ultimate", // 19
|
||||
"nazi_rap", // 20
|
||||
"zerohour", // 21
|
||||
"twelfth", // 22
|
||||
"roster", // 23
|
||||
"urahero", // 24
|
||||
"vicmarch", // 25
|
||||
"pacman", // 26
|
||||
typedef enum
|
||||
{
|
||||
CORNER_MUS, // 0
|
||||
DUNGEON_MUS, // 1
|
||||
WARMARCH_MUS, // 2
|
||||
GETTHEM_MUS, // 3
|
||||
HEADACHE_MUS, // 4
|
||||
HITLWLTZ_MUS, // 5
|
||||
INTROCW3_MUS, // 6
|
||||
NAZI_NOR_MUS, // 7
|
||||
NAZI_OMI_MUS, // 8
|
||||
POW_MUS, // 9
|
||||
SALUTE_MUS, // 10
|
||||
SEARCHN_MUS, // 11
|
||||
SUSPENSE_MUS, // 12
|
||||
VICTORS_MUS, // 13
|
||||
WONDERIN_MUS, // 14
|
||||
FUNKYOU_MUS, // 15
|
||||
ENDLEVEL_MUS, // 16
|
||||
GOINGAFT_MUS, // 17
|
||||
PREGNANT_MUS, // 18
|
||||
ULTIMATE_MUS, // 19
|
||||
NAZI_RAP_MUS, // 20
|
||||
ZEROHOUR_MUS, // 21
|
||||
TWELFTH_MUS, // 22
|
||||
ROSTER_MUS, // 23
|
||||
URAHERO_MUS, // 24
|
||||
VICMARCH_MUS, // 25
|
||||
PACMAN_MUS, // 26
|
||||
LASTMUSIC
|
||||
} MusicWolf;
|
||||
static const int songsWolf[] = {
|
||||
//
|
||||
// Episode One
|
||||
//
|
||||
GETTHEM_MUS, SEARCHN_MUS, POW_MUS, SUSPENSE_MUS, GETTHEM_MUS, SEARCHN_MUS,
|
||||
POW_MUS, SUSPENSE_MUS,
|
||||
|
||||
WARMARCH_MUS, // Boss level
|
||||
CORNER_MUS, // Secret level
|
||||
|
||||
//
|
||||
// Episode Two
|
||||
//
|
||||
NAZI_OMI_MUS, PREGNANT_MUS, GOINGAFT_MUS, HEADACHE_MUS, NAZI_OMI_MUS,
|
||||
PREGNANT_MUS, HEADACHE_MUS, GOINGAFT_MUS,
|
||||
|
||||
WARMARCH_MUS, // Boss level
|
||||
DUNGEON_MUS, // Secret level
|
||||
|
||||
//
|
||||
// Episode Three
|
||||
//
|
||||
INTROCW3_MUS, NAZI_RAP_MUS, TWELFTH_MUS, ZEROHOUR_MUS, INTROCW3_MUS,
|
||||
NAZI_RAP_MUS, TWELFTH_MUS, ZEROHOUR_MUS,
|
||||
|
||||
ULTIMATE_MUS, // Boss level
|
||||
PACMAN_MUS, // Secret level
|
||||
|
||||
//
|
||||
// Episode Four
|
||||
//
|
||||
GETTHEM_MUS, SEARCHN_MUS, POW_MUS, SUSPENSE_MUS, GETTHEM_MUS, SEARCHN_MUS,
|
||||
POW_MUS, SUSPENSE_MUS,
|
||||
|
||||
WARMARCH_MUS, // Boss level
|
||||
CORNER_MUS, // Secret level
|
||||
|
||||
//
|
||||
// Episode Five
|
||||
//
|
||||
NAZI_OMI_MUS, PREGNANT_MUS, GOINGAFT_MUS, HEADACHE_MUS, NAZI_OMI_MUS,
|
||||
PREGNANT_MUS, HEADACHE_MUS, GOINGAFT_MUS,
|
||||
|
||||
WARMARCH_MUS, // Boss level
|
||||
DUNGEON_MUS, // Secret level
|
||||
|
||||
//
|
||||
// Episode Six
|
||||
//
|
||||
INTROCW3_MUS, NAZI_RAP_MUS, TWELFTH_MUS, ZEROHOUR_MUS, INTROCW3_MUS,
|
||||
NAZI_RAP_MUS, TWELFTH_MUS, ZEROHOUR_MUS,
|
||||
|
||||
ULTIMATE_MUS, // Boss level
|
||||
FUNKYOU_MUS // Secret level
|
||||
};
|
||||
static Mix_Chunk *LoadMusic(const CWolfMap *map, const int i)
|
||||
{
|
||||
// TODO: spear music
|
||||
char *data;
|
||||
size_t len;
|
||||
const int err = CWAudioGetMusic(&map->audio, songsWolf[i], &data, &len);
|
||||
if (err != 0)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
return Mix_QuickLoad_RAW((Uint8 *)data, (Uint32)len);
|
||||
|
||||
bail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int MapWolfScan(const char *filename, char **title, int *numMissions)
|
||||
{
|
||||
@@ -384,7 +456,8 @@ static void LoadMission(
|
||||
CArrayPushBack(&m.Weapons, &wc);
|
||||
wc = StrWeaponClass("Knife");
|
||||
CArrayPushBack(&m.Weapons, &wc);
|
||||
// TODO: song
|
||||
m.Music.Type = MUSIC_SRC_CHUNK;
|
||||
m.Music.Data.Chunk = LoadMusic(map, missionIndex);
|
||||
MissionStaticInit(&m.u.Static);
|
||||
|
||||
m.u.Static.TileClasses = hashmap_copy(tileClasses, TileClassCopyHashMap);
|
||||
|
@@ -187,7 +187,20 @@ void MissionCopy(Mission *dst, const Mission *src)
|
||||
dst->EnemyDensity = src->EnemyDensity;
|
||||
CArrayCopy(&dst->Weapons, &src->Weapons);
|
||||
|
||||
memcpy(dst->Song, src->Song, sizeof dst->Song);
|
||||
dst->Music = src->Music;
|
||||
switch (src->Music.Type)
|
||||
{
|
||||
case MUSIC_SRC_DYNAMIC:
|
||||
CSTRDUP(dst->Music.Data.Filename, src->Music.Data.Filename);
|
||||
break;
|
||||
case MUSIC_SRC_CHUNK:
|
||||
// TODO: can't copy music chunks, only used by editor anyway
|
||||
dst->Music.Data.Chunk = NULL;
|
||||
break;
|
||||
default:
|
||||
CASSERT(false, "unsupported music type");
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(&dst->u, &src->u, sizeof dst->u);
|
||||
switch (src->Type)
|
||||
@@ -247,6 +260,17 @@ void MissionTerminate(Mission *m)
|
||||
CASSERT(false, "unknown map type");
|
||||
break;
|
||||
}
|
||||
switch (m->Music.Type)
|
||||
{
|
||||
case MUSIC_SRC_DYNAMIC:
|
||||
CFREE(m->Music.Data.Filename);
|
||||
break;
|
||||
case MUSIC_SRC_CHUNK:
|
||||
Mix_FreeChunk(m->Music.Data.Chunk);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
memset(m, 0, sizeof *m);
|
||||
}
|
||||
|
||||
@@ -462,9 +486,22 @@ void MissionBegin(struct MissionOptions *m, const NGameBegin gb)
|
||||
{
|
||||
m->HasBegun = true;
|
||||
m->state = MISSION_STATE_PLAY;
|
||||
MusicPlay(
|
||||
&gSoundDevice, MUSIC_GAME, gCampaign.Entry.Path, m->missionData->Song);
|
||||
const char *musicErrorMsg = MusicGetErrorMessage(&gSoundDevice);
|
||||
switch (m->missionData->Music.Type)
|
||||
{
|
||||
case MUSIC_SRC_DYNAMIC:
|
||||
MusicPlayFile(
|
||||
&gSoundDevice.music, MUSIC_GAME, gCampaign.Entry.Path,
|
||||
m->missionData->Music.Data.Filename);
|
||||
break;
|
||||
case MUSIC_SRC_CHUNK:
|
||||
MusicPlayChunk(
|
||||
&gSoundDevice.music, MUSIC_GAME, m->missionData->Music.Data.Chunk);
|
||||
break;
|
||||
default:
|
||||
CASSERT(false, "unsupported music type");
|
||||
break;
|
||||
}
|
||||
const char *musicErrorMsg = MusicGetErrorMessage(&gSoundDevice.music);
|
||||
if (strlen(musicErrorMsg) > 0)
|
||||
{
|
||||
// Display music error message for 2 seconds
|
||||
|
@@ -137,7 +137,14 @@ typedef struct
|
||||
int EnemyDensity;
|
||||
CArray Weapons; // of WeaponClass *
|
||||
|
||||
char Song[CDOGS_PATH_MAX];
|
||||
struct
|
||||
{
|
||||
MusicSourceType Type;
|
||||
union {
|
||||
char *Filename;
|
||||
Mix_Chunk *Chunk;
|
||||
} Data;
|
||||
} Music;
|
||||
|
||||
union {
|
||||
// Classic
|
||||
|
@@ -1,58 +1,115 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
|
||||
Copyright (c) 2013-2016, 2019, 2021 Cong Xu
|
||||
All rights reserved.
|
||||
Copyright (c) 2013-2016, 2019, 2021 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include "music.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL.h>
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
#else
|
||||
#include <SDL_mixer.h>
|
||||
#endif
|
||||
#include <tinydir/tinydir.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "gamedata.h"
|
||||
#include "log.h"
|
||||
#include "sounds.h"
|
||||
|
||||
static void LoadMusic(CArray *tracks, const char *path);
|
||||
void MusicPlayerInit(MusicPlayer *mp)
|
||||
{
|
||||
memset(mp, 0, sizeof *mp);
|
||||
|
||||
// Load music
|
||||
LoadMusic(&mp->generalTracks[MUSIC_MENU], "music/menu");
|
||||
LoadMusic(&mp->generalTracks[MUSIC_BRIEFING], "music/briefing");
|
||||
LoadMusic(&mp->generalTracks[MUSIC_GAME], "music/game");
|
||||
}
|
||||
static void LoadMusic(CArray *tracks, const char *path)
|
||||
{
|
||||
CArrayInit(tracks, sizeof(Mix_Music *));
|
||||
tinydir_dir dir;
|
||||
char buf[CDOGS_PATH_MAX];
|
||||
GetDataFilePath(buf, path);
|
||||
if (tinydir_open(&dir, buf) == -1)
|
||||
{
|
||||
LOG(LM_MAIN, LL_ERROR, "Cannot open music dir %s: %s", buf,
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
for (; dir.has_next; tinydir_next(&dir))
|
||||
{
|
||||
Mix_Music *m;
|
||||
tinydir_file file;
|
||||
if (tinydir_readfile(&dir, &file) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
if (!file.is_reg)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m = MusicLoad(file.path);
|
||||
if (m == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CArrayPushBack(tracks, &m);
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
}
|
||||
|
||||
static void UnloadMusic(CArray *tracks);
|
||||
void MusicPlayerTerminate(MusicPlayer *mp)
|
||||
{
|
||||
for (MusicType type = MUSIC_MENU; type < MUSIC_COUNT; type++)
|
||||
{
|
||||
UnloadMusic(&mp->generalTracks[type]);
|
||||
}
|
||||
}
|
||||
static void UnloadMusic(CArray *tracks)
|
||||
{
|
||||
CA_FOREACH(Mix_Music *, m, *tracks)
|
||||
Mix_FreeMusic(*m);
|
||||
CA_FOREACH_END()
|
||||
CArrayTerminate(tracks);
|
||||
}
|
||||
|
||||
Mix_Music *MusicLoad(const char *path)
|
||||
{
|
||||
// Only load music from known extensions
|
||||
const char *ext = strrchr(path, '.');
|
||||
if (ext == NULL || !(
|
||||
strcmp(ext, ".it") == 0 || strcmp(ext, ".IT") == 0 ||
|
||||
strcmp(ext, ".mod") == 0 || strcmp(ext, ".MOD") == 0 ||
|
||||
strcmp(ext, ".ogg") == 0 || strcmp(ext, ".OGG") == 0 ||
|
||||
strcmp(ext, ".s3m") == 0 || strcmp(ext, ".S3M") == 0 ||
|
||||
strcmp(ext, ".xm") == 0 || strcmp(ext, ".XM") == 0))
|
||||
if (ext == NULL ||
|
||||
!(strcmp(ext, ".it") == 0 || strcmp(ext, ".IT") == 0 ||
|
||||
strcmp(ext, ".mod") == 0 || strcmp(ext, ".MOD") == 0 ||
|
||||
strcmp(ext, ".ogg") == 0 || strcmp(ext, ".OGG") == 0 ||
|
||||
strcmp(ext, ".s3m") == 0 || strcmp(ext, ".S3M") == 0 ||
|
||||
strcmp(ext, ".xm") == 0 || strcmp(ext, ".XM") == 0))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
@@ -60,29 +117,19 @@ Mix_Music *MusicLoad(const char *path)
|
||||
return Mix_LoadMUS(path);
|
||||
}
|
||||
|
||||
static bool PlayMusic(SoundDevice *device)
|
||||
static void PlayMusic(MusicPlayer *mp)
|
||||
{
|
||||
if (device->music == NULL)
|
||||
{
|
||||
strcpy(device->musicErrorMessage, SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
MusicResume(device);
|
||||
MusicResume(mp);
|
||||
|
||||
if (ConfigGetInt(&gConfig, "Sound.MusicVolume") == 0)
|
||||
{
|
||||
MusicPause(device);
|
||||
MusicPause(mp);
|
||||
}
|
||||
|
||||
device->musicErrorMessage[0] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Play(SoundDevice *device, const char *path)
|
||||
static bool Play(MusicPlayer *mp, const char *path)
|
||||
{
|
||||
if (!device->isInitialised)
|
||||
if (!mp->isInitialised)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -93,19 +140,42 @@ static bool Play(SoundDevice *device, const char *path)
|
||||
return false;
|
||||
}
|
||||
|
||||
device->music = MusicLoad(path);
|
||||
return PlayMusic(device);
|
||||
mp->type = MUSIC_SRC_DYNAMIC;
|
||||
mp->u.dynamic = MusicLoad(path);
|
||||
if (mp->u.dynamic == NULL)
|
||||
{
|
||||
strcpy(mp->errorMessage, SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
mp->errorMessage[0] = '\0';
|
||||
PlayMusic(mp);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MusicPlay(
|
||||
SoundDevice *device, const MusicType type,
|
||||
const char *missionPath, const char *music)
|
||||
void MusicPlayGeneral(MusicPlayer *mp, const MusicType type)
|
||||
{
|
||||
// Play a tune
|
||||
// Start by trying to play a mission specific song,
|
||||
// otherwise pick one from the general collection...
|
||||
MusicStop(device);
|
||||
device->musicIsDynamic = false;
|
||||
mp->type = MUSIC_SRC_GENERAL;
|
||||
CArray *tracks = &mp->generalTracks[type];
|
||||
if (tracks->size == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
mp->u.general = *(Mix_Music **)CArrayGet(tracks, 0);
|
||||
// Shuffle tracks
|
||||
if (tracks->size > 1)
|
||||
{
|
||||
while (mp->u.general == *(Mix_Music **)CArrayGet(tracks, 0))
|
||||
{
|
||||
CArrayShuffle(tracks);
|
||||
}
|
||||
}
|
||||
PlayMusic(mp);
|
||||
}
|
||||
void MusicPlayFile(
|
||||
MusicPlayer *mp, const MusicType type, const char *missionPath,
|
||||
const char *music)
|
||||
{
|
||||
MusicStop(mp);
|
||||
bool played = false;
|
||||
if (music != NULL && strlen(music) != 0)
|
||||
{
|
||||
@@ -115,91 +185,133 @@ void MusicPlay(
|
||||
GetDataFilePath(buf, missionPath);
|
||||
strcat(buf, "/");
|
||||
strcat(buf, music);
|
||||
played = Play(device, buf);
|
||||
played = Play(mp, buf);
|
||||
if (!played)
|
||||
{
|
||||
char buf2[CDOGS_PATH_MAX];
|
||||
GetDataFilePath(buf2, missionPath);
|
||||
PathGetDirname(buf, buf2);
|
||||
strcat(buf, music);
|
||||
played = Play(device, buf);
|
||||
}
|
||||
if (played)
|
||||
{
|
||||
device->musicIsDynamic = true;
|
||||
played = Play(mp, buf);
|
||||
}
|
||||
}
|
||||
if (!played)
|
||||
{
|
||||
CArray *tracks = &device->musicTracks[type];
|
||||
if (tracks->size == 0)
|
||||
MusicPlayGeneral(mp, type);
|
||||
}
|
||||
}
|
||||
void MusicPlayChunk(MusicPlayer *mp, const MusicType type, Mix_Chunk *chunk)
|
||||
{
|
||||
MusicStop(mp);
|
||||
bool played = false;
|
||||
if (chunk != NULL)
|
||||
{
|
||||
mp->type = MUSIC_SRC_CHUNK;
|
||||
mp->u.chunk.chunk = chunk;
|
||||
mp->u.chunk.channel = Mix_PlayChannel(-1, chunk, -1);
|
||||
played = true;
|
||||
}
|
||||
if (!played)
|
||||
{
|
||||
MusicPlayGeneral(mp, type);
|
||||
}
|
||||
}
|
||||
|
||||
void MusicStop(MusicPlayer *mp)
|
||||
{
|
||||
switch (mp->type)
|
||||
{
|
||||
case MUSIC_SRC_GENERAL:
|
||||
Mix_HaltMusic();
|
||||
mp->u.general = NULL;
|
||||
break;
|
||||
case MUSIC_SRC_DYNAMIC:
|
||||
Mix_HaltMusic();
|
||||
if (mp->u.dynamic != NULL)
|
||||
{
|
||||
Mix_FreeMusic(mp->u.dynamic);
|
||||
}
|
||||
mp->u.dynamic = NULL;
|
||||
break;
|
||||
case MUSIC_SRC_CHUNK:
|
||||
if (mp->u.chunk.chunk != NULL)
|
||||
{
|
||||
Mix_HaltChannel(mp->u.chunk.channel);
|
||||
}
|
||||
mp->u.chunk.chunk = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MusicPause(MusicPlayer *mp)
|
||||
{
|
||||
switch (mp->type)
|
||||
{
|
||||
case MUSIC_SRC_GENERAL:
|
||||
case MUSIC_SRC_DYNAMIC:
|
||||
if (Mix_PlayingMusic())
|
||||
{
|
||||
Mix_PauseMusic();
|
||||
}
|
||||
break;
|
||||
case MUSIC_SRC_CHUNK:
|
||||
Mix_Pause(mp->u.chunk.channel);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void MusicResume(MusicPlayer *mp)
|
||||
{
|
||||
switch (mp->type)
|
||||
{
|
||||
case MUSIC_SRC_GENERAL:
|
||||
if (mp->u.general == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
device->music = *(Mix_Music **)CArrayGet(tracks, 0);
|
||||
// Shuffle tracks
|
||||
if (tracks->size > 1)
|
||||
if (Mix_PausedMusic())
|
||||
{
|
||||
while (device->music == *(Mix_Music **)CArrayGet(tracks, 0))
|
||||
{
|
||||
CArrayShuffle(tracks);
|
||||
}
|
||||
Mix_ResumeMusic();
|
||||
}
|
||||
PlayMusic(device);
|
||||
}
|
||||
}
|
||||
|
||||
void MusicStop(SoundDevice *device)
|
||||
{
|
||||
if (device->music != NULL)
|
||||
{
|
||||
Mix_HaltMusic();
|
||||
if (device->musicIsDynamic)
|
||||
else if (!Mix_PlayingMusic())
|
||||
{
|
||||
Mix_FreeMusic(device->music);
|
||||
Mix_PlayMusic(mp->u.general, -1);
|
||||
}
|
||||
device->music = NULL;
|
||||
break;
|
||||
case MUSIC_SRC_DYNAMIC:
|
||||
if (mp->u.dynamic == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Mix_PausedMusic())
|
||||
{
|
||||
Mix_ResumeMusic();
|
||||
}
|
||||
else if (!Mix_PlayingMusic())
|
||||
{
|
||||
Mix_PlayMusic(mp->u.dynamic, -1);
|
||||
}
|
||||
break;
|
||||
case MUSIC_SRC_CHUNK:
|
||||
Mix_Resume(mp->u.chunk.channel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MusicPause(SoundDevice *s)
|
||||
{
|
||||
UNUSED(s);
|
||||
if (Mix_PlayingMusic())
|
||||
{
|
||||
Mix_PauseMusic();
|
||||
}
|
||||
}
|
||||
|
||||
void MusicResume(SoundDevice *device)
|
||||
{
|
||||
if (device->music == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (Mix_PausedMusic())
|
||||
{
|
||||
Mix_ResumeMusic();
|
||||
}
|
||||
else if (!Mix_PlayingMusic())
|
||||
{
|
||||
Mix_PlayMusic(device->music, -1);
|
||||
}
|
||||
}
|
||||
|
||||
void MusicSetPlaying(SoundDevice *device, int isPlaying)
|
||||
void MusicSetPlaying(MusicPlayer *mp, const bool isPlaying)
|
||||
{
|
||||
if (isPlaying)
|
||||
{
|
||||
MusicResume(device);
|
||||
MusicResume(mp);
|
||||
}
|
||||
else
|
||||
{
|
||||
MusicPause(device);
|
||||
MusicPause(mp);
|
||||
}
|
||||
}
|
||||
|
||||
const char *MusicGetErrorMessage(SoundDevice *device)
|
||||
const char *MusicGetErrorMessage(const MusicPlayer *mp)
|
||||
{
|
||||
return device->musicErrorMessage;
|
||||
return mp->errorMessage;
|
||||
}
|
||||
|
@@ -1,62 +1,104 @@
|
||||
/*
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (C) 1995 Ronny Wester
|
||||
Copyright (C) 2003 Jeremy Chin
|
||||
Copyright (C) 2003-2007 Lucas Martin-King
|
||||
C-Dogs SDL
|
||||
A port of the legendary (and fun) action/arcade cdogs.
|
||||
Copyright (C) 1995 Ronny Wester
|
||||
Copyright (C) 2003 Jeremy Chin
|
||||
Copyright (C) 2003-2007 Lucas Martin-King
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright (c) 2013, 2016-2017, 2019 Cong Xu
|
||||
All rights reserved.
|
||||
Copyright (c) 2013, 2016-2017, 2019, 2021 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sounds.h"
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
#else
|
||||
#include <SDL_mixer.h>
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "c_array.h"
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MUSIC_MENU,
|
||||
MUSIC_BRIEFING,
|
||||
MUSIC_GAME,
|
||||
MUSIC_COUNT
|
||||
} MusicType;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MUSIC_SRC_GENERAL,
|
||||
MUSIC_SRC_DYNAMIC,
|
||||
MUSIC_SRC_CHUNK
|
||||
} MusicSourceType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool isInitialised;
|
||||
MusicSourceType type;
|
||||
union {
|
||||
Mix_Music *general;
|
||||
Mix_Music *dynamic;
|
||||
struct
|
||||
{
|
||||
Mix_Chunk *chunk;
|
||||
int channel;
|
||||
} chunk;
|
||||
} u;
|
||||
CArray generalTracks[MUSIC_COUNT]; // of Mix_Music *
|
||||
char errorMessage[128];
|
||||
} MusicPlayer;
|
||||
|
||||
void MusicPlayerInit(MusicPlayer *mp);
|
||||
void MusicPlayerTerminate(MusicPlayer *mp);
|
||||
Mix_Music *MusicLoad(const char *path);
|
||||
void MusicPlay(
|
||||
SoundDevice *device, const MusicType type,
|
||||
const char *missionPath, const char *music);
|
||||
void MusicStop(SoundDevice *device);
|
||||
void MusicPause(SoundDevice *s);
|
||||
void MusicResume(SoundDevice *device);
|
||||
void MusicSetPlaying(SoundDevice *device, int isPlaying);
|
||||
const char *MusicGetErrorMessage(SoundDevice *device);
|
||||
void MusicPlayGeneral(MusicPlayer *mp, const MusicType type);
|
||||
void MusicPlayFile(
|
||||
MusicPlayer *mp, const MusicType type, const char *missionPath,
|
||||
const char *music);
|
||||
void MusicPlayChunk(MusicPlayer *mp, const MusicType type, Mix_Chunk *chunk);
|
||||
void MusicStop(MusicPlayer *mp);
|
||||
void MusicPause(MusicPlayer *mp);
|
||||
void MusicResume(MusicPlayer *mp);
|
||||
void MusicSetPlaying(MusicPlayer *mp, const bool isPlaying);
|
||||
const char *MusicGetErrorMessage(const MusicPlayer *mp);
|
||||
|
@@ -182,7 +182,6 @@ void SoundAdd(map_t sounds, const char *name, SoundData *sound)
|
||||
}
|
||||
}
|
||||
|
||||
static void SoundLoadMusic(CArray *tracks, const char *path);
|
||||
void SoundInitialize(SoundDevice *device, const char *path)
|
||||
{
|
||||
memset(device, 0, sizeof *device);
|
||||
@@ -193,11 +192,7 @@ void SoundInitialize(SoundDevice *device, const char *path)
|
||||
char buf[CDOGS_PATH_MAX];
|
||||
GetDataFilePath(buf, path);
|
||||
SoundLoadDir(device->sounds, buf, NULL);
|
||||
|
||||
// Load music
|
||||
SoundLoadMusic(&device->musicTracks[MUSIC_MENU], "music/menu");
|
||||
SoundLoadMusic(&device->musicTracks[MUSIC_BRIEFING], "music/briefing");
|
||||
SoundLoadMusic(&device->musicTracks[MUSIC_GAME], "music/game");
|
||||
MusicPlayerInit(&device->music);
|
||||
}
|
||||
void SoundLoadDir(map_t sounds, const char *path, const char *prefix)
|
||||
{
|
||||
@@ -242,43 +237,6 @@ void SoundLoadDir(map_t sounds, const char *path, const char *prefix)
|
||||
}
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
}
|
||||
static void SoundLoadMusic(CArray *tracks, const char *path)
|
||||
{
|
||||
CArrayInit(tracks, sizeof(Mix_Music *));
|
||||
tinydir_dir dir;
|
||||
char buf[CDOGS_PATH_MAX];
|
||||
GetDataFilePath(buf, path);
|
||||
if (tinydir_open(&dir, buf) == -1)
|
||||
{
|
||||
LOG(LM_MAIN, LL_ERROR, "Cannot open music dir %s: %s", buf,
|
||||
strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
for (; dir.has_next; tinydir_next(&dir))
|
||||
{
|
||||
Mix_Music *m;
|
||||
tinydir_file file;
|
||||
if (tinydir_readfile(&dir, &file) == -1)
|
||||
{
|
||||
goto bail;
|
||||
}
|
||||
if (!file.is_reg)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
m = MusicLoad(file.path);
|
||||
if (m == NULL)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
CArrayPushBack(tracks, &m);
|
||||
}
|
||||
|
||||
bail:
|
||||
tinydir_close(&dir);
|
||||
}
|
||||
@@ -296,7 +254,7 @@ static void SoundClose(SoundDevice *s, const bool waitForSoundsComplete)
|
||||
while (Mix_Playing(-1) > 0 && SDL_GetTicks() - waitStart < 1000)
|
||||
;
|
||||
// Don't stop the music unless we're reopening
|
||||
MusicStop(s);
|
||||
MusicStop(&s->music);
|
||||
}
|
||||
while (Mix_Init(0))
|
||||
{
|
||||
@@ -308,6 +266,7 @@ static void SoundClose(SoundDevice *s, const bool waitForSoundsComplete)
|
||||
void SoundReconfigure(SoundDevice *s)
|
||||
{
|
||||
s->isInitialised = false;
|
||||
s->music.isInitialised = false;
|
||||
|
||||
if (Mix_AllocateChannels(s->channels) != s->channels)
|
||||
{
|
||||
@@ -319,16 +278,10 @@ void SoundReconfigure(SoundDevice *s)
|
||||
Mix_Volume(-1, sVol);
|
||||
const int mVol = ConfigGetInt(&gConfig, "Sound.MusicVolume");
|
||||
Mix_VolumeMusic(mVol);
|
||||
if (mVol > 0)
|
||||
{
|
||||
MusicResume(s);
|
||||
}
|
||||
else
|
||||
{
|
||||
MusicPause(s);
|
||||
}
|
||||
MusicSetPlaying(&s->music, mVol > 0);
|
||||
|
||||
s->isInitialised = true;
|
||||
s->music.isInitialised = true;
|
||||
}
|
||||
|
||||
void SoundReopen(SoundDevice *s)
|
||||
@@ -348,7 +301,6 @@ void SoundClear(map_t sounds)
|
||||
{
|
||||
hashmap_clear(sounds, SoundDataTerminate);
|
||||
}
|
||||
static void SoundUnloadMusic(CArray *tracks);
|
||||
void SoundTerminate(SoundDevice *device, const bool waitForSoundsComplete)
|
||||
{
|
||||
SoundClose(device, waitForSoundsComplete);
|
||||
@@ -356,10 +308,7 @@ void SoundTerminate(SoundDevice *device, const bool waitForSoundsComplete)
|
||||
hashmap_destroy(device->sounds, SoundDataTerminate);
|
||||
hashmap_destroy(device->customSounds, SoundDataTerminate);
|
||||
|
||||
for (MusicType type = MUSIC_MENU; type < MUSIC_COUNT; type++)
|
||||
{
|
||||
SoundUnloadMusic(&device->musicTracks[type]);
|
||||
}
|
||||
MusicPlayerTerminate(&device->music);
|
||||
}
|
||||
static void SoundDataTerminate(any_t data)
|
||||
{
|
||||
@@ -381,13 +330,6 @@ static void SoundDataTerminate(any_t data)
|
||||
}
|
||||
CFREE(s);
|
||||
}
|
||||
static void SoundUnloadMusic(CArray *tracks)
|
||||
{
|
||||
CA_FOREACH(Mix_Music *, m, *tracks)
|
||||
Mix_FreeMusic(*m);
|
||||
CA_FOREACH_END()
|
||||
CArrayTerminate(tracks);
|
||||
}
|
||||
|
||||
#define OUT_OF_SIGHT_DISTANCE_PLUS 100
|
||||
static int GetChannel(SoundDevice *s, Mix_Chunk *data);
|
||||
|
@@ -22,7 +22,7 @@
|
||||
This file incorporates work covered by the following copyright and
|
||||
permission notice:
|
||||
|
||||
Copyright (c) 2013-2017, 2019-2020 Cong Xu
|
||||
Copyright (c) 2013-2017, 2019-2021 Cong Xu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@@ -61,6 +61,7 @@
|
||||
#include "c_hashmap/hashmap.h"
|
||||
#include "defs.h"
|
||||
#include "mathc/mathc.h"
|
||||
#include "music.h"
|
||||
#include "sys_config.h"
|
||||
#include "utils.h"
|
||||
#include "vector.h"
|
||||
@@ -88,21 +89,10 @@ typedef struct
|
||||
} u;
|
||||
} SoundData;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MUSIC_MENU,
|
||||
MUSIC_BRIEFING,
|
||||
MUSIC_GAME,
|
||||
MUSIC_COUNT
|
||||
} MusicType;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int isInitialised;
|
||||
Mix_Music *music;
|
||||
bool musicIsDynamic;
|
||||
CArray musicTracks[MUSIC_COUNT]; // of Mix_Music *
|
||||
char musicErrorMessage[128];
|
||||
MusicPlayer music;
|
||||
bool isInitialised;
|
||||
int channels;
|
||||
|
||||
// Two sets of ears for 4-player split screen
|
||||
|
@@ -153,11 +153,22 @@ static char *MissionGetSong(UIObject *o, void *data)
|
||||
{
|
||||
UNUSED(o);
|
||||
Campaign *co = data;
|
||||
if (!CampaignGetCurrentMission(co))
|
||||
const Mission *m = CampaignGetCurrentMission(co);
|
||||
if (!m || m->Music.Type != MUSIC_SRC_DYNAMIC)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return CampaignGetCurrentMission(co)->Song;
|
||||
return m->Music.Data.Filename;
|
||||
}
|
||||
static char **MissionGetSongSrc(void *data)
|
||||
{
|
||||
Campaign *co = data;
|
||||
const Mission *m = CampaignGetCurrentMission(co);
|
||||
if (!m || m->Music.Type != MUSIC_SRC_DYNAMIC)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return &m->Music.Data.Filename;
|
||||
}
|
||||
static const char *MissionGetWidthStr(UIObject *o, void *data)
|
||||
{
|
||||
@@ -1037,9 +1048,9 @@ static UIObject *CreateMissionObjs(Campaign *co)
|
||||
o = UIObjectCreate(
|
||||
UITYPE_TEXTBOX, YC_MISSIONTITLE, svec2i(0, Y_ABS), svec2i(319, th));
|
||||
o->u.Textbox.TextLinkFunc = MissionGetSong;
|
||||
o->u.Textbox.MaxLen = sizeof((Mission *)0)->Song - 1;
|
||||
o->u.Textbox.TextSourceFunc = MissionGetSongSrc;
|
||||
o->Data = co;
|
||||
CSTRDUP(o->u.Textbox.Hint, "(Mission song)");
|
||||
CSTRDUP(o->u.Textbox.Hint, "(Mission song - enter filename without extension)");
|
||||
o->Id2 = XC_MUSICFILE;
|
||||
o->Flags = UI_SELECT_ONLY;
|
||||
o->CheckVisible = MissionCheckVisible;
|
||||
|
@@ -148,7 +148,7 @@ static void MainMenuOnEnter(GameLoopData *data)
|
||||
return;
|
||||
}
|
||||
|
||||
MusicPlay(&gSoundDevice, MUSIC_MENU, NULL, NULL);
|
||||
MusicPlayGeneral(&gSoundDevice.music, MUSIC_MENU);
|
||||
// Reset config - could have been set to other values by server
|
||||
ConfigResetChanged(&gConfig);
|
||||
CampaignSettingTerminateAll(&gCampaign.Setting);
|
||||
|
Reference in New Issue
Block a user