Subversion Repositories eduke32

Rev

Rev 8580 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
5927 hendricks2 1
 
6074 hendricks2 2
#include "compat.h"
5927 hendricks2 3
 
4
#ifdef HAVE_XMP
5
 
8213 terminx 6
#include "_multivc.h"
7
#include "multivoc.h"
5927 hendricks2 8
#include "pitch.h"
8213 terminx 9
#include "pragmas.h"
5927 hendricks2 10
 
11
#define BUILDING_STATIC
12
#include "libxmp-lite/xmp.h"
13
 
14
typedef struct {
15
    void * ptr;
16
    VoiceNode *owner;
17
    size_t length;
18
    xmp_context context;
19
    int time;
20
} xmp_data;
21
 
8216 terminx 22
int MV_GetXMPPosition(VoiceNode *voice)
5927 hendricks2 23
{
7283 terminx 24
    auto xmpd = (xmp_data *)voice->rawdataptr;
5927 hendricks2 25
    return xmpd->time;
26
}
27
 
8216 terminx 28
void MV_SetXMPPosition(VoiceNode *voice, int position)
5927 hendricks2 29
{
7283 terminx 30
    auto xmpd = (xmp_data *)voice->rawdataptr;
5927 hendricks2 31
    xmp_seek_time(xmpd->context, position);
32
}
33
 
34
static playbackstatus MV_GetNextXMPBlock(VoiceNode *voice)
35
{
7283 terminx 36
    auto xmpd = (xmp_data *)voice->rawdataptr;
5927 hendricks2 37
    struct xmp_frame_info mi;
38
 
39
    if (xmp_play_frame(xmpd->context) != 0)
40
    {
41
        if (voice->LoopSize > 0)
42
        {
43
            xmp_restart_module(xmpd->context);
44
            if (xmp_play_frame(xmpd->context) != 0)
45
                return NoMoreData;
46
        }
47
        else
48
            return NoMoreData;
49
    }
50
 
51
    xmp_get_frame_info(xmpd->context, &mi);
52
 
53
    xmpd->time = mi.time;
54
 
7683 hendricks2 55
    uint32_t const samples = mi.buffer_size / (2 * (16/8)); // since 2-channel, 16-bit is hardcoded
56
    // uint32_t const samples = mi.buffer_size / (voice->channels * (voice->bits / 8));
57
 
5927 hendricks2 58
    voice->sound        = (char const *)mi.buffer;
7683 hendricks2 59
    voice->length       = samples << 16;
5927 hendricks2 60
    voice->position     = 0;
61
    voice->BlockLength  = 0;
62
 
63
    MV_SetVoiceMixMode(voice);
64
 
65
    return KeepPlaying;
66
}
67
 
8427 hendricks2 68
int MV_PlayXMP3D(char *ptr, uint32_t length, int loophow, int pitchoffset, int angle, int distance, int priority, fix16_t volume, intptr_t callbackval)
5927 hendricks2 69
{
70
    if (!MV_Installed)
8213 terminx 71
        return MV_SetErrorCode(MV_NotInstalled);
5927 hendricks2 72
 
73
    if (distance < 0)
74
    {
75
        distance  = -distance;
76
        angle    += MV_NUMPANPOSITIONS / 2;
77
    }
78
 
8783 terminx 79
    int vol = MIX_VOLUME(distance);
5927 hendricks2 80
 
81
    // Ensure angle is within 0 - 127
82
    angle &= MV_MAXPANPOSITION;
83
 
8783 terminx 84
    int left  = MV_PanTable[angle][vol].left;
85
    int right = MV_PanTable[angle][vol].right;
86
    int mid   = max( 0, 255 - distance );
5927 hendricks2 87
 
8783 terminx 88
    return MV_PlayXMP(ptr, length, loophow, -1, pitchoffset, mid, left, right, priority, volume, callbackval);
5927 hendricks2 89
}
90
 
8427 hendricks2 91
int MV_PlayXMP(char *ptr, uint32_t length, int loopstart, int loopend, int pitchoffset, int vol, int left, int right, int priority, fix16_t volume, intptr_t callbackval)
5927 hendricks2 92
{
93
    UNREFERENCED_PARAMETER(loopend);
94
 
95
    if (!MV_Installed)
8213 terminx 96
        return MV_SetErrorCode(MV_NotInstalled);
5927 hendricks2 97
 
8783 terminx 98
    auto xmpd = (xmp_data *)Xcalloc(1, sizeof(xmp_data));
5927 hendricks2 99
    if (!xmpd)
8213 terminx 100
        return MV_SetErrorCode(MV_InvalidFile);
5927 hendricks2 101
 
102
    xmpd->ptr = ptr;
7117 terminx 103
    xmpd->length = length;
5927 hendricks2 104
 
8213 terminx 105
    if ((xmpd->context = xmp_create_context()) == nullptr)
5927 hendricks2 106
    {
7705 terminx 107
        Xfree(xmpd);
8213 terminx 108
        return MV_SetErrorCode(MV_InvalidFile);
5927 hendricks2 109
    }
110
 
8783 terminx 111
    int const xmp_status = xmp_load_module_from_memory(xmpd->context, ptr, length);
112
 
113
    if (xmp_status)
5927 hendricks2 114
    {
8783 terminx 115
        xmp_free_context(xmpd->context);
7705 terminx 116
        Xfree(xmpd);
8783 terminx 117
        MV_Printf("MV_PlayXMP: xmp_load_module_from_memory failed (%i)\n", xmp_status);
8213 terminx 118
        return MV_SetErrorCode(MV_InvalidFile);
5927 hendricks2 119
    }
120
 
121
    // Request a voice from the voice pool
8783 terminx 122
    auto voice = MV_AllocVoice(priority);
8213 terminx 123
    if (voice == nullptr)
5927 hendricks2 124
    {
125
        xmp_release_module(xmpd->context);
126
        xmp_free_context(xmpd->context);
7705 terminx 127
        Xfree(xmpd);
8213 terminx 128
        return MV_SetErrorCode(MV_NoVoices);
5927 hendricks2 129
    }
130
 
131
    xmpd->owner = voice;
132
 
6564 pogokeen 133
    voice->length      = 0;
134
    voice->sound       = 0;
135
 
5927 hendricks2 136
    voice->wavetype    = FMT_XMP;
137
    voice->rawdataptr  = (void*)xmpd;
138
    voice->GetSound    = MV_GetNextXMPBlock;
139
    voice->LoopCount   = 0;
140
    voice->BlockLength = 0;
141
    voice->PitchScale  = PITCH_GetScale(pitchoffset);
8213 terminx 142
    voice->next        = nullptr;
143
    voice->prev        = nullptr;
5927 hendricks2 144
    voice->priority    = priority;
145
    voice->callbackval = callbackval;
146
 
147
    voice->bits        = 16;
148
    voice->channels    = 2;
149
    voice->SamplingRate = MV_MixRate;
150
 
151
    voice->Paused      = FALSE;
152
 
153
    voice->LoopStart   = 0;
154
    voice->LoopEnd     = 0;
155
    voice->LoopSize    = loopstart >= 0 ? 1 : 0;
156
 
157
    xmp_start_player(xmpd->context, MV_MixRate, 0);
8383 terminx 158
    xmp_set_player(xmpd->context, XMP_PLAYER_INTERP, MV_XMPInterpolation);
5927 hendricks2 159
 
160
    // CODEDUP multivoc.c MV_SetVoicePitch
8213 terminx 161
    voice->RateScale = divideu32(voice->SamplingRate * voice->PitchScale, MV_MixRate);
5927 hendricks2 162
    voice->FixedPointBufferSize = (voice->RateScale * MV_MIXBUFFERSIZE) - voice->RateScale;
163
    MV_SetVoiceMixMode(voice);
164
 
7122 terminx 165
    MV_SetVoiceVolume(voice, vol, left, right, volume);
5927 hendricks2 166
    MV_PlayVoice(voice);
167
 
168
    return voice->handle;
169
}
170
 
171
void MV_ReleaseXMPVoice(VoiceNode * voice)
172
{
7283 terminx 173
    auto xmpd = (xmp_data *) voice->rawdataptr;
5927 hendricks2 174
 
175
    if (voice->wavetype != FMT_XMP)
176
        return;
177
 
6564 pogokeen 178
    voice->rawdataptr = 0;
7684 hendricks2 179
    voice->length = 0;
180
    voice->sound = nullptr;
6564 pogokeen 181
 
5927 hendricks2 182
    xmp_end_player(xmpd->context);
183
    xmp_release_module(xmpd->context);
184
    xmp_free_context(xmpd->context);
7705 terminx 185
    Xfree(xmpd);
5927 hendricks2 186
}
187
 
8580 hendricks2 188
void MV_SetXMPInterpolation(void)
189
{
190
    for (VoiceNode *voice = VoiceList.next; voice != &VoiceList; voice = voice->next)
191
        if (voice->wavetype == FMT_XMP)
192
            xmp_set_player((xmp_context)voice->rawdataptr, XMP_PLAYER_INTERP, MV_XMPInterpolation);
193
}
194
 
5927 hendricks2 195
#else
196
 
197
#include "_multivc.h"
198
 
199
static char const NoXMP[] = "MV_PlayXMP: libxmp-lite support not included in this binary.\n";
200
 
8216 terminx 201
int MV_PlayXMP(char *ptr, uint32_t ptrlength, int loopstart, int loopend, int pitchoffset, int vol,
8427 hendricks2 202
                   int left, int right, int priority, fix16_t volume, intptr_t callbackval)
5927 hendricks2 203
{
204
    UNREFERENCED_PARAMETER(ptr);
205
    UNREFERENCED_PARAMETER(ptrlength);
206
    UNREFERENCED_PARAMETER(loopstart);
207
    UNREFERENCED_PARAMETER(loopend);
208
    UNREFERENCED_PARAMETER(pitchoffset);
209
    UNREFERENCED_PARAMETER(vol);
210
    UNREFERENCED_PARAMETER(left);
211
    UNREFERENCED_PARAMETER(right);
212
    UNREFERENCED_PARAMETER(priority);
7117 terminx 213
    UNREFERENCED_PARAMETER(volume);
5927 hendricks2 214
    UNREFERENCED_PARAMETER(callbackval);
215
 
216
    MV_Printf(NoXMP);
217
    return -1;
218
}
219
 
8216 terminx 220
int MV_PlayXMP3D(char *ptr, uint32_t ptrlength, int loophow, int pitchoffset, int angle,
8427 hendricks2 221
                     int distance, int priority, fix16_t volume, intptr_t callbackval)
5927 hendricks2 222
{
223
    UNREFERENCED_PARAMETER(ptr);
224
    UNREFERENCED_PARAMETER(ptrlength);
225
    UNREFERENCED_PARAMETER(loophow);
226
    UNREFERENCED_PARAMETER(pitchoffset);
227
    UNREFERENCED_PARAMETER(angle);
228
    UNREFERENCED_PARAMETER(distance);
229
    UNREFERENCED_PARAMETER(priority);
7117 terminx 230
    UNREFERENCED_PARAMETER(volume);
5927 hendricks2 231
    UNREFERENCED_PARAMETER(callbackval);
232
 
233
    MV_Printf(NoXMP);
234
    return -1;
235
}
236
 
237
#endif
238
 
6475 hendricks2 239
// KEEPINSYNC libxmp-lite/src/*_load.c
5927 hendricks2 240
 
241
static int it_test_memory(char const *ptr, uint32_t ptrlength)
242
{
243
    static char const it_magic[] = "IMPM";
8783 terminx 244
    return !!(ptrlength < sizeof(it_magic) - 1 || Bmemcmp(ptr, it_magic, sizeof(it_magic) - 1));
5927 hendricks2 245
}
246
 
247
static int mod_test_memory(char const *ptr, uint32_t ptrlength)
248
{
249
    if (ptrlength < 1084)
250
        return -1;
251
 
252
    char const * const buf = ptr + 1080;
253
 
8783 terminx 254
    if (!Bstrncmp(buf + 2, "CH", 2) && isdigit((int)buf[0]) && isdigit((int)buf[1]))
5927 hendricks2 255
    {
256
        int i = (buf[0] - '0') * 10 + buf[1] - '0';
257
        if (i > 0 && i <= 32)
258
            return 0;
259
    }
260
 
8783 terminx 261
    if (!Bstrncmp(buf + 1, "CHN", 3) && isdigit((int)*buf))
5927 hendricks2 262
    {
263
        if (*buf >= '0' && *buf <= '9')
264
            return 0;
265
    }
266
 
8783 terminx 267
    if (!Bmemcmp(buf, "M.K.", 4))
5927 hendricks2 268
        return 0;
269
 
270
    return -1;
271
}
272
 
273
static int s3m_test_memory(char const *ptr, uint32_t ptrlength)
274
{
275
    static char const s3m_magic[] = "SCRM";
276
    #define s3m_magic_offset 44
277
 
8783 terminx 278
    return !!(ptrlength < s3m_magic_offset + sizeof(s3m_magic)-1 ||
279
        Bmemcmp(ptr + s3m_magic_offset, s3m_magic, sizeof(s3m_magic)-1) ||
280
        ptr[29] != 0x10);
5927 hendricks2 281
}
282
 
283
static int xm_test_memory(char const *ptr, uint32_t ptrlength)
284
{
285
    static char const xm_magic[] = "Extended Module: ";
8783 terminx 286
    return !!(ptrlength < sizeof(xm_magic) - 1 || Bmemcmp(ptr, xm_magic, sizeof(xm_magic) - 1));
5927 hendricks2 287
}
288
 
6475 hendricks2 289
static int mtm_test_memory(char const *ptr, uint32_t ptrlength)
290
{
291
    static char const mtm_magic[] = "MTM\x10";
8783 terminx 292
    return !!(ptrlength < sizeof(mtm_magic) - 1 || Bmemcmp(ptr, mtm_magic, sizeof(mtm_magic) - 1));
6475 hendricks2 293
}
294
 
5927 hendricks2 295
int MV_IdentifyXMP(char const *ptr, uint32_t ptrlength)
296
{
6475 hendricks2 297
    static decltype(mod_test_memory) * const module_test_functions[] =
298
    {
299
        it_test_memory,
300
        mod_test_memory,
301
        s3m_test_memory,
302
        xm_test_memory,
303
        mtm_test_memory,
304
    };
305
 
306
    for (auto const test_module : module_test_functions)
307
    {
308
        if (test_module(ptr, ptrlength) == 0)
309
            return 1;
310
    }
311
 
312
    return 0;
5927 hendricks2 313
}