Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
tree: 084a84ae02
Fetching contributors…

Cannot retrieve contributors at this time

2059 lines (1749 sloc) 61.101 kB
// This file is distributed under a BSD license. See LICENSE.txt for details.
#include "_viruz2.hpp"
#pragma lekktor(off)
#if sLINK_VIRUZ2
extern "C"
{
extern void __stdcall synthInit(const void *patchmap, int samplerate=44100);
extern void __stdcall synthRender(void *buf, int smp, void *buf2=0, int add=0);
extern void __stdcall synthProcessMIDI(const void *ptr);
extern void __stdcall synthSetGlobals(const void *ptr);
extern void __stdcall synthGetPoly(void *dest);
extern void __stdcall synthGetPgm(void *dest);
extern void __stdcall synthGetLD(float *l, float *r);
// only if VUMETER define is set in synth source
// vu output values are floats, 1.0 == 0dB
// you might want to clip or logarithmize the values for yourself
extern void __stdcall synthSetVUMode(int mode); // 0: peak, 1: rms
extern void __stdcall synthGetChannelVU(int ch, float *l, float *r); // ch: 0..15
extern void __stdcall synthGetMainVU(float *l, float *r);
extern long __stdcall synthGetFrameSize();
}
#ifdef RONAN
extern void __stdcall synthSetLyrics(const char **ptr);
#endif
// file automatically generated by kram.exe - do not ask
#pragma pack (push, 1)
struct Phoneme
{
sF32 f1f, f1b;
sF32 f2f, f2b;
sF32 f3f, f3b;
sF32 fnf;
sF32 a_voicing, a_aspiration, a_frication, a_bypass;
sF32 a_1, a_2, a_3, a_4, a_n, a_56;
sF32 duration, rank;
};
#pragma pack (pop)
// { 490, 60, 1480, 90, 2500, 150, 270, 0, 0, 0,-16,-16,-16,-16,-16,-16,-16, 0, 5, 31}, // 18: end
// { 280, 60, 1720, 90, 2560, 150, 270, 62, 0, 0,-16, 43, 38, 38, 50,-16,-16, 0, 4, 20}, // 68: zz
// f1f: /10
// f1b: /10
// f2f: /20
// f2b: /10
// f3f: /20
// f3b: /10
// fnf: /10
#define NPHONEMES 69
#define PTABSIZE 1311
static const sU8 multipliers[PTABSIZE/NPHONEMES] = { 10,10,20,10,20,10,10,1,1,1,1,1,1,1,1,1,1,1,1};
// CHANGES: a_f56 + 15 (-> duration -15)
// a_bypass -3 (-> a_f1 +3)
static const sU8 rawphonemes[PTABSIZE] =
{
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
// f1f (Hz*1/10)
49, 30,241, 0, 15,226,226, 0, 0, 0, 21,235, 9, 0,247, 0, 45,217, 24, 9,238,235, 0, 0, 30,247,247, 18, 30,196, 9,247, 0, 0, 27, 0, 2, 0, 0, 13,244, 0,244, 0, 12, 30,214,238, 0, 0, 30,235, 21, 0,247, 0,235, 21,235, 0, 51,211, 3,247, 0, 6, 3, 0, 0,
// f1b (Hz*1/10)
234, 7,249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,254, 0, 12,246, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14,242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// f2f (Hz*1/20)
68, 15,247, 21,199,253,253, 0, 0, 51, 12,244,247, 0, 9, 0, 12, 15,214,253, 0, 3, 0, 0, 0, 30, 6,220,226, 45, 12,229, 0, 0, 0,229, 3, 39,208, 3, 30,223, 9, 0,247, 24,241,244, 0, 0, 36,253,244, 0, 27, 24,235, 0, 0, 0,235,232, 27,223, 36, 51,217, 15,241,
// f2b (Hz*1/10)
179, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 13,241,250, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// f3f (Hz*1/20)
116, 0, 0, 0, 0, 0, 0, 0, 0, 9,250, 6,250, 0, 6, 0,247, 35,221, 0, 3, 3, 0, 0,250, 3, 18,235, 0, 9,250, 3, 0, 0,250, 0,241, 21, 9,241, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3,208, 0, 51,253, 6, 0, 0, 0,247,241, 18,229, 30, 18,235, 0, 0,
// f3b (Hz*1/10)
143, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,253, 14,240, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,248, 15,249, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// fnf (Hz*1/10)
12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 9,247,247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// a_voicing (dB)
35, 0, 0, 0, 0, 0, 0, 0, 0,194, 0, 62,230, 26, 0, 0, 0, 0,194, 62,194, 62, 0, 0,194, 62, 0, 0, 0, 0, 0,194, 0, 0, 62, 0, 0, 0,246, 10, 0, 0, 0, 0, 0, 0, 0,194, 0, 0, 0, 62, 0,244,206, 0, 0, 0, 0, 0, 62, 0, 0, 0,194, 62,234, 22, 0,
// a_aspiration (dB)
194, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,196, 0, 0, 0, 0, 0, 0, 0, 0, 32,224, 0, 0, 60,196, 0, 0, 0, 0, 0, 60, 0, 0,196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0,196, 0, 0, 0, 32, 28, 0, 0, 0, 0,196, 0, 0, 0, 60,196, 20,236, 0,
// a_frication (dB)
0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0,196, 60,196, 0, 0, 0, 0, 0, 0, 54,202, 0, 0, 60,196, 0, 0, 0, 0, 0, 60, 0, 0,196, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0,196, 0, 0, 0, 60, 0, 0, 0, 0, 0,196, 0, 0, 0, 60,196, 60,196, 0,
// a_bypass (dB)
237, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 70,186, 0, 0, 0, 0, 0, 0, 70,186, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
// a_f1 (dB)
83, 0, 0, 0, 0, 0,230, 0,202, 0, 0, 61,254, 0, 9, 0, 12, 0,176, 80,176, 65, 0, 0, 0, 15, 0, 0, 0,237,254,197, 0, 0, 66, 0,246, 9,241, 30, 0, 0, 0, 0, 0, 0, 0,176, 54, 0,202, 59, 13, 0,184, 0, 0, 0, 0, 0, 80, 0,235, 14,183, 80,235, 0, 0,
// a_f2 (dB)
21,253,254,253, 7,252,181, 79,177, 0, 61,195, 61, 0, 7,246, 14,247,193, 75,211,226, 75,246, 1, 0,255, 15,255,177, 56,200, 80,246,242, 0, 4, 5,251, 17, 3,251,253, 0, 3, 2,251,184, 79,245,188, 70,251, 0,249, 3,195, 56,200, 0, 73,251, 2,244, 12,249,247, 2,254,
// a_f3 (dB)
9, 5,253, 3,247,249,204, 73,183, 0, 72,184, 56, 0, 9,245, 14,254,190, 63,223,226, 70,245,253, 9, 1,253,252,197, 66,190, 80,246,242, 0, 7, 2, 0,243, 11,245, 6, 0,250, 13,249,198, 73,246,193, 66,255, 0,249, 14,184, 58, 10,246, 3,242, 19,241, 19,254,242, 12,244,
// a_f4 (dB)
2, 5,254, 2,247,251,209, 68,188, 0, 61,195, 58, 0, 17,246,252, 0,195, 56,200, 0, 54,246, 8, 7, 2,251,252,204, 56,200, 59,246, 2, 0,205, 50,236, 15, 11,247, 5, 0,251, 11,250,204, 68,246,198, 63,193, 0, 70,247,195, 52, 28,246,240,242, 23,193, 49, 12, 5,246, 10,
// a_fn (dB)
190, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 0,184, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56,200, 0, 66,246, 10,246, 0,246,210, 0, 56,200, 46,210, 66,236, 0,
// a_f56 (dB)
225, 0, 0, 0, 0, 0, 0, 0, 0, 46,246, 0, 10,236,246,240, 0, 0, 0, 0, 46,210, 0, 0, 26,230, 0, 0, 0, 26,246, 10, 0,246,240, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46,236, 10,246,230, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0,
// duration (frames)
235, 1, 1, 0, 9,251, 2,245,255, 4, 4, 0,252, 0,253, 0, 3, 3,254, 11,252, 0,245, 1, 3, 1, 0, 0, 0,254,255, 5,249, 3, 4, 0, 0, 0, 0,254, 0, 0,254, 2, 0, 0, 0, 2,249, 1, 4,250, 11,255, 2, 0,250, 9,242, 1, 4, 3,251, 4, 4,251,253, 0, 0,
// rank
254, 0, 0, 0, 0, 0, 24, 3,253,253,251, 8,250, 0, 9,253,232, 0, 29,227, 16, 8, 3,253,239,249, 0, 0, 0, 24,250, 3, 6,250,244, 0, 0, 0, 0,247, 0, 0, 0, 0, 0, 0, 0, 21, 6,250, 6, 1,236, 0, 8, 0, 5,251, 11,250,235, 0, 18,246, 8,248, 10, 0, 0,
};
/*
static Phoneme orgphonemes[] =
{
{ 490, 60, 1480, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 64, 47, 40,-16,-16, 4, 2}, // 0: a
{ 790, 130, 1780, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 61, 52, 45,-16,-16, 5, 2}, // 1: aa
{ 640, 60, 1600, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 59, 49, 43,-16,-16, 6, 2}, // 2: ai
{ 640, 60, 2020, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 56, 52, 45,-16,-16, 6, 2}, // 3: air
{ 790, 60, 880, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 63, 43, 36,-16,-16, 15, 2}, // 4: ar
{ 490, 60, 820, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 59, 36, 31,-16,-16, 10, 2}, // 5: aw
{ 190, 60, 760, 90, 2500, 150, 270, 62, 0, 0,-16, 38,-16,-16,-16,-16,-16, 12, 26}, // 6: b
{ 190, 60, 760, 90, 2500, 150, 270, 62, 0, 0,-16, 38, 63, 57, 52,-16,-16, 1, 29}, // 7: by
{ 190, 60, 760, 90, 2500, 150, 270, 62, 0, 0,-16,-16,-16,-16,-16,-16,-16, 0, 26}, // 8: bz
{ 190, 60, 1780, 90, 2680, 150, 270, 0, 60, 60,-16,-16,-16,-16,-16,-16, 30, 4, 23}, // 9: ch
{ 400, 60, 2020, 90, 2560, 150, 270, 0, 60, 60,-16,-16, 45, 56, 45,-16, 20, 8, 18}, // 10: ci
{ 190, 60, 1780, 90, 2680, 150, 270, 62, 0, 0,-16, 45,-16,-16,-16,-16, 20, 8, 26}, // 11: d
{ 280, 60, 1600, 90, 2560, 150, 270, 36, 0, 60, 54, 43, 45, 40, 42,-16, 30, 4, 20}, // 12: dh
{ 280, 60, 1600, 90, 2560, 150, 270, 62, 0, 0,-16, 43, 45, 40, 42,-16, 10, 4, 20}, // 13: di
{ 190, 60, 1780, 90, 2680, 150, 270, 62, 0, 0,-16, 52, 52, 49, 59,-16, 0, 1, 29}, // 14: dy
{ 190, 60, 1780, 90, 2680, 150, 270, 62, 0, 0,-16, 52, 42, 38, 49,-16,-16, 1, 26}, // 15: dz
{ 640, 60, 2020, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 56, 52, 45,-16,-16, 4, 2}, // 16: e
{ 250, 60, 2320, 90, 3200, 150, 270, 62, 0, 0,-16, 64, 47, 50, 45,-16,-16, 7, 2}, // 17: ee
{ 490, 60, 1480, 90, 2500, 150, 270, 0, 0, 0,-16,-16,-16,-16,-16,-16,-16, 5, 31}, // 18: end
{ 580, 60, 1420, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 59, 47, 40,-16,-16, 16, 2}, // 19: er
{ 400, 60, 1420, 90, 2560, 150, 270, 0, 32, 54, 54,-16, 14, 14,-16,-16, 30, 12, 18}, // 20: f
{ 190, 60, 1480, 90, 2620, 150, 270, 62, 0, 0,-16, 49,-16,-16,-16,-16,-16, 12, 26}, // 21: g
{ 190, 60, 1480, 90, 2620, 150, 270, 62, 0, 0,-16, 49, 59, 54, 38,-16,-16, 1, 29}, // 22: gy
{ 190, 60, 1480, 90, 2620, 150, 270, 62, 0, 0,-16, 49, 49, 43, 28,-16,-16, 2, 26}, // 23: gz
{ 490, 60, 1480, 90, 2500, 150, 270, 0, 60, 60,-16, 49, 50, 40, 36,-16, 10, 5, 9}, // 24: h
{ 400, 60, 2080, 90, 2560, 150, 270, 62, 0, 0,-16, 64, 50, 49, 43,-16,-16, 6, 2}, // 25: i
{ 310, 60, 2200, 90, 2920, 150, 270, 62, 0, 0,-16, 64, 49, 50, 45,-16,-16, 6, 2}, // 26: ia
{ 490, 60, 1480, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 64, 47, 40,-16,-16, 6, 2}, // 27: ib
{ 790, 60, 880, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 63, 43, 36,-16,-16, 6, 2}, // 28: ie
{ 190, 60, 1780, 90, 2680, 150, 270, 62, 0, 0,-16, 45,-16,-16,-16,-16, 10, 4, 26}, // 29: j
{ 280, 60, 2020, 90, 2560, 150, 270, 62, 0, 0,-16, 43, 40, 50, 40,-16, 0, 3, 20}, // 30: jy
{ 190, 60, 1480, 90, 2620, 150, 270, 0, 60, 60,-16,-16,-16,-16,-16,-16, 10, 8, 23}, // 31: k
{ 190, 60, 1480, 90, 2620, 150, 270, 0, 60, 60,-16,-16, 64, 64, 43,-16, 10, 1, 29}, // 32: ky
{ 190, 60, 1480, 90, 2620, 150, 270, 0, 60, 60,-16,-16, 54, 54, 33,-16, 0, 4, 23}, // 33: kz
{ 460, 60, 1480, 90, 2500, 150, 270, 62, 0, 0,-16, 50, 40, 40, 35,-16,-16, 8, 11}, // 34: l
{ 460, 60, 940, 90, 2500, 150, 270, 62, 0, 0,-16, 50, 40, 40, 35,-16,-16, 8, 11}, // 35: ll
{ 480, 40, 1000, 170, 2200, 120, 360, 62, 0, 0,-16, 40, 44, 47,-16, 56,-16, 8, 11}, // 36: m
{ 480, 40, 1780, 300, 2620, 260, 450, 62, 0, 0,-16, 49, 49, 49, 34, 56,-16, 8, 11}, // 37: n
{ 480, 160, 820, 150, 2800, 100, 360, 52, 0, 0,-16, 34, 44, 49, 14, 56,-16, 8, 11}, // 38: ng
{ 610, 60, 880, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 61, 36, 29,-16,-16, 6, 2}, // 39: o
{ 490, 60, 1480, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 64, 47, 40,-16,-16, 6, 2}, // 40: oa
{ 490, 60, 820, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 59, 36, 31,-16,-16, 6, 2}, // 41: oi
{ 370, 60, 1000, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 56, 42, 36,-16,-16, 4, 2}, // 42: oo
{ 370, 60, 1000, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 56, 42, 36,-16,-16, 6, 2}, // 43: oor
{ 490, 60, 820, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 59, 36, 31,-16,-16, 6, 2}, // 44: or
{ 790, 60, 1300, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 61, 49, 42,-16,-16, 6, 2}, // 45: ou
{ 370, 60, 1000, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 56, 42, 36,-16,-16, 6, 2}, // 46: ov
{ 190, 60, 760, 90, 2500, 150, 270, 0, 60, 60,-16,-16,-16,-16,-16,-16,-16, 8, 23}, // 47: p
{ 190, 60, 760, 90, 2500, 150, 270, 0, 60, 60,-16, 38, 63, 57, 52,-16,-16, 1, 29}, // 48: py
{ 190, 60, 760, 90, 2500, 150, 270, 0, 60, 60,-16, 38, 52, 47, 42,-16,-16, 2, 23}, // 49: pz
{ 490, 60, 1480, 90, 2500, 150, 270, 0, 0, 0,-16,-16,-16,-16,-16,-16,-16, 6, 29}, // 50: q
{ 280, 60, 1420, 90, 2560, 150, 270, 62, 0, 0,-16, 43, 54, 50, 47, 40,-16, 0, 30}, // 51: qq
{ 490, 60, 1180, 90, 1600, 150, 270, 62, 0, 0,-16, 56, 49, 49,-16,-16,-16, 11, 10}, // 52: r
{ 490, 60, 1180, 90, 1600, 70, 270, 50, 0, 0,-16, 56, 49, 49,-16,-16,-16, 10, 10}, // 53: rx
{ 400, 200, 1720, 90, 2620, 220, 270, 0, 32, 60,-16,-16, 42, 42, 54, 50, 30, 12, 18}, // 54: s
{ 400, 60, 2200, 90, 2560, 150, 270, 0, 60, 60,-16,-16, 45, 56, 45, 40, 10, 12, 18}, // 55: sh
{ 190, 60, 1780, 90, 2680, 150, 270, 0, 60, 60,-16,-16,-16,-16,-16, 50, 20, 6, 23}, // 56: t
{ 400, 60, 1780, 90, 2680, 150, 270, 0, 60, 60,-16,-16, 40, 42, 36, 40, 10, 15, 18}, // 57: th
{ 190, 60, 1780, 90, 2680, 150, 270, 0, 60, 60,-16,-16,-16, 52, 64, 40,-16, 1, 29}, // 58: ty
{ 190, 60, 1780, 90, 2680, 150, 270, 0, 60, 60,-16,-16,-16, 42, 54, 30,-16, 2, 23}, // 59: tz
{ 700, 60, 1360, 90, 2500, 150, 270, 62, 0, 0,-16, 64, 57, 45, 38,-16,-16, 6, 2}, // 60: u
{ 250, 60, 880, 90, 2200, 150, 270, 62, 0, 0,-16, 64, 52, 31, 24,-16,-16, 9, 2}, // 61: uu
{ 280, 60, 1420, 90, 2560, 150, 270, 62, 0, 0,-16, 43, 54, 50, 47, 40,-16, 4, 20}, // 62: v
{ 190, 60, 760, 90, 2020, 150, 270, 62, 0, 0,-16, 57, 42, 35,-16,-16,-16, 8, 10}, // 63: w
{ 190, 60, 1480, 90, 2620, 150, 270, 0, 60, 60,-16,-16, 54, 54, 33, 30,-16, 12, 18}, // 64: x
{ 250, 60, 2500, 90, 2980, 150, 270, 62, 0, 0,-16, 64, 47, 52, 45,-16,-16, 7, 10}, // 65: y
{ 280, 60, 1720, 90, 2560, 150, 270, 40, 20, 60,-16, 43, 38, 38, 50, 50,-16, 4, 20}, // 66: z
{ 280, 60, 2020, 90, 2560, 150, 270, 62, 0, 0,-16, 43, 40, 50, 40, 30, 10, 4, 20}, // 67: zh
{ 280, 60, 1720, 90, 2560, 150, 270, 62, 0, 0,-16, 43, 38, 38, 50, 30, 10, 4, 20}, // 68: zz
};
*/
// ronan heißt in wirklichkeit lisa. ich war nur zu faul zum renamen.
// !DHAX_ !kwIH_k !br4AH_UHn !fAA_ks !jAH_mps !OW!vE_R !DHAX_ !lEY!zIY_ !dAA_g
#ifdef RONAN
#pragma warning (disable: 4244)
namespace Ronan
{
int mystrnicmp1(const char *a, const char *b)
{
sInt l=0;
while (*a && *b)
if ((*a++ | 0x20)!=(*b++ | 0x20))
return 0;
else
l++;
return *a?0:l;
}
#define PI (4.0f*(sF32)atan(1.0f))
static Phoneme phonemes[NPHONEMES];
static struct
{
sU32 samplerate;
sF32 fcminuspi_sr;
sF32 fc2pi_sr;
} g;
static const char *texts[64]={0};
static const char *nix="";
static sF32 pitch;
static sInt framerate;
static const struct syldef
{
char syl[4];
sS8 ptab[4];
} syls[] = {
{"sil",{50,-1,-1,-1}},
{"ng",{38,-1,-1,-1}},
{"th",{57,-1,-1,-1}},
{"sh",{55,-1,-1,-1}},
{"dh",{12,51,13,-1}},
{"zh",{67,51,67,-1}},
{"ch",{ 9,10,-1,-1}},
{"ih",{25,-1,-1,-1}},
{"eh",{16,-1,-1,-1}},
{"ae",{ 1,-1,-1,-1}},
{"ah",{60,-1,-1,-1}},
{"oh",{39,-1,-1,-1}},
{"uh",{42,-1,-1,-1}},
{"ax",{ 0,-1,-1,-1}},
{"iy",{17,-1,-1,-1}},
{"er",{19,-1,-1,-1}},
{"aa",{ 4,-1,-1,-1}},
{"ao",{ 5,-1,-1,-1}},
{"uw",{61,-1,-1,-1}},
{"ey",{ 2,25,-1,-1}},
{"ay",{28,25,-1,-1}},
{"oy",{41,25,-1,-1}},
{"aw",{45,46,-1,-1}},
{"ow",{40,46,-1,-1}},
{"ia",{26,27,-1,-1}},
{"ea",{ 3,27,-1,-1}},
{"ua",{43,27,-1,-1}},
{"ll",{35,-1,-1,-1}},
{"wh",{63,-1,-1,-1}},
{"ix",{ 0,-1,-1,-1}},
{"el",{34,-1,-1,-1}},
{"rx",{53,-1,-1,-1}},
{"h",{24,-1,-1,-1}},
{"p",{47,48,49,-1}},
{"t",{56,58,59,-1}},
{"k",{31,32,33,-1}},
{"b",{ 6, 7, 8,-1}},
{"d",{11,14,15,-1}},
{"g",{21,22,23,-1}},
{"m",{36,-1,-1,-1}},
{"n",{37,-1,-1,-1}},
{"f",{20,-1,-1,-1}},
{"s",{54,-1,-1,-1}},
{"v",{62,51,62,-1}},
{"z",{66,51,68,-1}},
{"l",{34,-1,-1,-1}},
{"r",{52,-1,-1,-1}},
{"w",{63,-1,-1,-1}},
{"q",{51,-1,-1,-1}},
{"y",{65,-1,-1,-1}},
{"j",{29,30,51,30}},
{" ",{18,-1,-1,-1}},
};
#define NSYLS (sizeof(syls)/sizeof(syldef))
// filter type 1: 2-pole resonator
struct ResDef
{
sF32 a,b,c; // coefficients
void set(sF32 f, sF32 bw, sF32 gain)
{
sF32 r=(sF32)sFExp(g.fcminuspi_sr*bw);
c=-(r*r);
b=r*(sF32)cos(g.fc2pi_sr*f)*2.0f;
a=gain*(1.0f-b-c);
}
};
struct Resonator
{
ResDef *def;
sF32 p1, p2; // delay buffers
inline void setdef(ResDef &a_def) { def=&a_def; }
sF32 tick(sF32 in)
{
sF32 x=def->a*in+def->b*p1+def->c*p2;
p2=p1;
p1=x;
return x;
}
};
static ResDef d_peq1;
struct syVRonan
{
ResDef rdef[7]; // f1,f2,f3,f4,nas;
// ResDef f5,f6;
sF32 a_voicing;
sF32 a_aspiration;
sF32 a_frication;
sF32 a_bypass;
};
static struct syWRonan : syVRonan
{
syVRonan newframe;
// Resonator rf1,rf2,rf3,rf4,rnas,rf5,rf6;
Resonator res[7]; // 0:nas, 1..6: 1..6
sF32 lastin2;
// noise
sU32 nseed;
sF32 nout;
// phonem seq
sInt framecount; // frame rate divider
sInt spos; // pos within syl definition (0..3)
sInt scounter; // syl duration divider
sInt cursyl; // current syl
sInt durfactor; // duration modifier
sF32 invdur; // 1.0 / current duration
const char *baseptr; // pointer to start of text
const char *ptr; // pointer to text
sInt curp1, curp2; // current/last phonemes
// sync
sInt wait4on;
sInt wait4off;
// post EQ
sF32 hpb1, hpb2;
Resonator peq1;
} workspace;
static syWRonan *wsptr;
static sF32 flerp(const sF32 a,const sF32 b,const sF32 x) { return a+x*(b-a); }
static sF32 db2lin(sF32 db1, sF32 db2, sF32 x) { return (sF32)sFPow(2.0,(flerp(db1,db2,x)-70)/6.0); }
static const sF32 f4=3200;
static const sF32 f5=4000;
static const sF32 f6=6000;
static const sF32 bn=100;
static const sF32 b4=200;
static const sF32 b5=500;
static const sF32 b6=800;
static void SetFrame(const Phoneme &p1s, const Phoneme &p2s, const sF32 x, syVRonan &dest)
{
static Phoneme p1,p2;
register syWRonan &w=*wsptr;
static const sF32 * const p1f[]={&p1.fnf,&p1.f1f,&p1.f2f,&p1.f3f,&f4 ,&f5 ,&f6};
static const sF32 * const p1b[]={&bn ,&p1.f1b,&p1.f2b,&p1.f3b,&b4 ,&b5 ,&b6};
static const sF32 * const p1a[]={&p1.a_n,&p1.a_1,&p1.a_2,&p1.a_3,&p1.a_4,&p1.a_56,&p1.a_56};
static const sF32 * const p2f[]={&p2.fnf,&p2.f1f,&p2.f2f,&p2.f3f,&f4 ,&f5 ,&f6};
static const sF32 * const p2b[]={&bn ,&p2.f1b,&p2.f2b,&p2.f3b,&b4 ,&b5 ,&b6};
static const sF32 * const p2a[]={&p2.a_n,&p2.a_1,&p2.a_2,&p2.a_3,&p2.a_4,&p2.a_56,&p2.a_56};
p1=p1s;
p2=p2s;
for (sInt i=0; i<7; i++)
dest.rdef[i].set(flerp(*p1f[i],*p2f[i],x)*pitch,flerp(*p1b[i],*p2b[i],x),db2lin(*p1a[i],*p2a[i],x));
dest.a_voicing=db2lin(p1.a_voicing,p2.a_voicing,x);
dest.a_aspiration=db2lin(p1.a_aspiration,p2.a_aspiration,x);
dest.a_frication=db2lin(p1.a_frication,p2.a_frication,x);
dest.a_bypass=db2lin(p1.a_bypass,p2.a_bypass,x);
}
//-----------------------------------------------------------------------
#define NOISEGAIN 0.25f
static sF32 noise()
{
register syWRonan &w=*wsptr;
union { sU32 i; sF32 f; } val;
// random...
w.nseed=(w.nseed*196314165)+907633515;
// convert to float between 2.0 and 4.0
val.i=(w.nseed>>9)|0x40000000;
// slight low pass filter...
w.nout=(val.f-3.0f)+0.75f*w.nout;
return w.nout*NOISEGAIN;
}
// -----------------------------------------------------------------------
static void reset()
{
register syWRonan &w=*wsptr;
memset(wsptr,0,sizeof(syWRonan));
for (sInt i=0; i<7; i++) w.res[i].setdef(w.rdef[i]);
w.peq1.setdef(d_peq1);
SetFrame(phonemes[18],phonemes[18],0,w); // off
SetFrame(phonemes[18],phonemes[18],0,w.newframe); // off
w.curp1=w.curp2=18;
w.spos=4;
}
};
using namespace Ronan;
extern "C" void __stdcall ronanCBSetSR(sU32 sr)
{
g.samplerate=sr;
g.fc2pi_sr=2.0f*PI/(sF32)sr;
g.fcminuspi_sr=-g.fc2pi_sr*0.5f;
}
extern "C" void __stdcall ronanCBInit()
{
// convert phoneme table to a usable format
register const sS8 *ptr=(const sS8*)rawphonemes;
register sS32 val=0;
for (sInt f=0; f<(PTABSIZE/NPHONEMES); f++)
{
sF32 *dest=((sF32*)phonemes)+f;
for (sInt p=0; p<NPHONEMES; p++)
{
*dest=multipliers[f]*(sF32)(val+=*ptr++);
dest+=PTABSIZE/NPHONEMES;
}
}
wsptr=&workspace;
register syWRonan &w=*wsptr;
reset();
framerate=3;
pitch=1.0f;
if (!texts[0])
w.baseptr=w.ptr=nix;
else
w.baseptr=w.ptr=texts[0];
/*w.lastin=*/w.lastin2=/*w.nval=*/0;
d_peq1.set(12000,4000,2.0f);
}
extern "C" void __stdcall ronanCBTick()
{
register syWRonan &w=*wsptr;
if (w.wait4off || w.wait4on) return;
if (w.framecount<=0)
{
w.framecount=framerate;
// let current phoneme expire
if (w.scounter<=0)
{
// set to next phoneme
w.spos++;
if (w.spos >=4 || syls[w.cursyl].ptab[w.spos]==-1)
{
// go to next syllable
if (!(*w.ptr)) // empty text: silence!
{
w.durfactor=1;
w.framecount=1;
w.cursyl=NSYLS-1;
w.spos=0;
w.ptr=w.baseptr;
}
else if (*w.ptr=='!') // wait for noteon
{
w.framecount=0;
w.scounter=0;
w.wait4on=1;
w.ptr++;
return;
}
else if (*w.ptr=='_') // noteoff
{
w.framecount=0;
w.scounter=0;
w.wait4off=1;
w.ptr++;
return;
}
if (*w.ptr && *w.ptr!='!' && *w.ptr!='_')
{
w.durfactor=0;
while (*w.ptr>='0' && *w.ptr<='9') w.durfactor=10*w.durfactor+(*w.ptr++ - '0');
if (!w.durfactor) w.durfactor=1;
// printf2("'%s' -> ",w.ptr);
sInt fs,len=1,len2;
for (fs=0; fs<NSYLS-1; fs++)
{
const syldef &s=syls[fs];
if (len2=mystrnicmp1(s.syl,w.ptr))
{
len=len2;
// printf2("got %s\n",s.syl);
break;
}
}
w.cursyl=fs;
w.spos=0;
w.ptr+=len;
}
}
w.curp1=w.curp2;
w.curp2=syls[w.cursyl].ptab[w.spos];
w.scounter=sFtol(phonemes[w.curp2].duration*w.durfactor);
if (!w.scounter) w.scounter=1;
w.invdur=1.0f/((sF32)w.scounter*framerate);
}
w.scounter--;
}
w.framecount--;
sF32 x=(sF32)(w.scounter*framerate+w.framecount)*w.invdur;
const Phoneme &p1=phonemes[w.curp1];
const Phoneme &p2=phonemes[w.curp2];
x=(sF32)sFPow(x,(sF32)p1.rank/(sF32)p2.rank);
SetFrame(p2,(fabs(p2.rank-p1.rank)>8.0f)?p2:p1,x,w.newframe);
}
extern "C" void __stdcall ronanCBNoteOn()
{
workspace.wait4on=0;
}
extern "C" void __stdcall ronanCBNoteOff()
{
workspace.wait4off=0;
}
extern "C" void __stdcall ronanCBSetCtl(sU32 ctl, sU32 val)
{
// controller 4, 0-63 : set text #
// controller 4, 64-127 : set frame rate
// controller 5 : set pitch
register syWRonan &w=*wsptr;
switch (ctl)
{
case 4:
if (val<63)
{
reset();
if (texts[val])
w.ptr=w.baseptr=texts[val];
else
w.ptr=w.baseptr=nix;
}
else
framerate=val-63;
break;
case 5:
pitch=(sF32)sFPow(2.0f,(val-64.0f)/128.0f);
break;
}
}
extern "C" void __stdcall ronanCBProcess(sF32 *buf, sU32 len)
{
register syWRonan &w=*wsptr;
static syVRonan deltaframe;
// prepare interpolation
{
sF32 *src1=(sF32*)&w;
sF32 *src2=(sF32*)&w.newframe;
sF32 *dest=(sF32*)&deltaframe;
sF32 mul =1.0f/(sF32)len;
for (sU32 i=0; i<(sizeof(syVRonan)/sizeof(sF32)); i++)
dest[i]=(src2[i]-src1[i])*mul;
}
for (sU32 i=0; i<len; i++)
{
// interpolate all values
{
sF32 *src=(sF32*)&deltaframe;
sF32 *dest=(sF32*)&w;
for (sU32 i=0; i<(sizeof(syVRonan)/sizeof(sF32)); i++)
dest[i]+=src[i];
}
sF32 in=buf[2*i];
// add aspiration noise
in=in*w.a_voicing+noise()*w.a_aspiration;
// process complete input signal with f1/nasal filters
sF32 out=w.res[0].tick(in)+w.res[1].tick(in);
// differentiate input signal, add frication noise
sF32 lin=in;
in=(noise()*w.a_frication)+in-w.lastin2;
w.lastin2=lin;
// process diff/fric input signal with f2..f6 and bypass (phase inverted)
for (sInt r=2; r<7; r++)
out=w.res[r].tick(in)-out;
out=in*w.a_bypass-out;
// high pass filter
w.hpb1+=0.012f*(out=out-w.hpb1);
w.hpb2+=0.012f*(out=out-w.hpb2);
// EQ
out=w.peq1.tick(out)-out;
buf[2*i]=buf[2*i+1]=out;
}
}
void __stdcall synthSetLyrics(const char **a_ptr)
{
register syWRonan &w=*wsptr;
for (sInt i=0; i<64; i++) texts[i]=a_ptr[i];
// memcpy(texts,a_ptr,64*sizeof(char *));
w.baseptr=w.ptr=texts[0];
}
#endif
#define GETDELTA(p, w) ((p)[0]+((p)[w]<<8)+((p)[2*w]<<16))
#define UPDATENT(n, v, p, w) if ((n)<(w)) { (v)=m_state.time+GETDELTA((p), (w)); if ((v)<m_state.nexttime) m_state.nexttime=(v); }
#define UPDATENT2(n, v, p, w) if ((n)<(w) && GETDELTA((p), (w))) { (v)=m_state.time+GETDELTA((p), (w)); }
#define UPDATENT3(n, v, p, w) if ((n)<(w) && (v)<m_state.nexttime) m_state.nexttime=(v);
#define PUTSTAT(s) { sU8 bla=(s); if (laststat!=bla) { laststat=bla; *mptr++=(sU8)laststat; }};
namespace
{
void UpdateSampleDelta(sU32 nexttime, sU32 time, sU32 usecs, sU32 td2, sU32 *smplrem, sU32 *smpldelta)
//////////////////////////////////////////////////////////////////////////////////////////////////////
{
// performs 64bit (nexttime-time)*usecs/td2 and a 32.32bit addition to smpldelta:smplrem
__asm {
mov eax, [nexttime]
sub eax, [time]
mov ebx, [usecs]
mul ebx
mov ebx, [td2]
div ebx
mov ecx, [smplrem]
add [ecx], edx
adc eax, 0
mov ecx, [smpldelta]
mov [ecx], eax
}
}
}
CV2MPlayer::CV2MPlayer() : m_tpc(1000)
////////////////////////
{
m_base.valid=0;
}
CV2MPlayer::CV2MPlayer(sU32 a_tickspersec) : m_tpc(a_tickspersec)
////////////////////////
{
m_base.valid=0;
}
CV2MPlayer::~CV2MPlayer()
/////////////////////////
{
Close();
}
sBool CV2MPlayer::InitBase(const void *a_v2m)
///////////////////////////////////////
{
const sU8 *d=(const sU8*)a_v2m;
m_base.timediv=(*((sU32*)(d)));
m_base.timediv2=10000*m_base.timediv;
m_base.maxtime=*((sU32*)(d+4));
m_base.gdnum=*((sU32*)(d+8));
d+=12;
m_base.gptr=d;
d+=10*m_base.gdnum;
for (sInt ch=0; ch<16; ch++)
{
V2MBase::Channel &c=m_base.chan[ch];
c.notenum=*((sU32*)d);
d+=4;
if (c.notenum)
{
c.noteptr=d;
d+=5*c.notenum;
c.pcnum=*((sU32*)d);
d+=4;
c.pcptr=d;
d+=4*c.pcnum;
c.pbnum=*((sU32*)d);
d+=4;
c.pbptr=d;
d+=5*c.pbnum;
for (sInt cn=0; cn<7; cn++)
{
V2MBase::Channel::CC &cc=c.ctl[cn];
cc.ccnum=*((sU32*)d);
d+=4;
cc.ccptr=d;
d+=4*cc.ccnum;
}
}
}
sInt size=*((sU32*)d);
if (size>16384 || size<0) return sFALSE;
d+=4;
m_base.globals=d;
d+=size;
size=*((sU32*)d);
if (size>1048576 || size<0) return sFALSE;
d+=4;
m_base.patchmap=d;
d+=size;
#ifdef RONAN
sU32 spsize=*((sU32*)d);
d+=4;
if (!spsize || spsize>=8192)
{
for (sU32 i=0; i<256; i++)
m_base.speechptrs[i]=" ";
}
else
{
m_base.speechdata=(const char *)d;
d+=spsize;
const sU32 *p32=(const sU32*)m_base.speechdata;
sU32 n=*(p32++);
for (sU32 i=0; i<n; i++)
{
m_base.speechptrs[i]=m_base.speechdata+*(p32++);
}
}
#endif
return sTRUE;
}
void CV2MPlayer::Reset()
////////////////////////
{
m_state.time=0;
m_state.nexttime=(sU32)-1;
m_state.gptr=m_base.gptr;
m_state.gnr=0;
UPDATENT(m_state.gnr, m_state.gnt, m_state.gptr, m_base.gdnum);
for (sInt ch=0; ch<16; ch++)
{
V2MBase::Channel &bc=m_base.chan[ch];
PlayerState::Channel &sc=m_state.chan[ch];
if (!bc.notenum) continue;
sc.noteptr=bc.noteptr;
sc.notenr=sc.lastnte=sc.lastvel=0;
UPDATENT(sc.notenr,sc.notent, sc.noteptr, bc.notenum);
sc.pcptr=bc.pcptr;
sc.pcnr=sc.lastpc=0;
UPDATENT(sc.pcnr,sc.pcnt, sc.pcptr, bc.pcnum);
sc.pbptr=bc.pbptr;
sc.pbnr=sc.lastpb0=sc.lastpb1=0;
UPDATENT(sc.pbnr,sc.pbnt, sc.pbptr, bc.pcnum);
for (sInt cn=0; cn<7; cn++)
{
V2MBase::Channel::CC &bcc=bc.ctl[cn];
PlayerState::Channel::CC &scc=sc.ctl[cn];
scc.ccptr=bcc.ccptr;
scc.ccnr=scc.lastcc=0;
UPDATENT(scc.ccnr,scc.ccnt,scc.ccptr,bcc.ccnum);
}
}
m_state.usecs=5000*m_samplerate;
m_state.num=4;
m_state.den=4;
m_state.tpq=8;
m_state.bar=0;
m_state.beat=0;
m_state.tick=0;
m_state.smplrem=0;
if (m_samplerate)
{
synthInit((void*)m_base.patchmap,m_samplerate);
synthSetGlobals((void*)m_base.globals);
#ifdef RONAN
synthSetLyrics(m_base.speechptrs);
#endif
}
}
void CV2MPlayer::Tick()
///////////////////////
{
if (m_state.state != PlayerState::PLAYING)
return;
m_state.tick+=m_state.nexttime-m_state.time;
while (m_state.tick>=m_base.timediv)
{
m_state.tick-=m_base.timediv;
m_state.beat++;
}
sU32 qpb=(m_state.num*4/m_state.den);
while (m_state.beat>=qpb)
{
m_state.beat-=qpb;
m_state.bar++;
}
m_state.time=m_state.nexttime;
m_state.nexttime=(sU32)-1;
sU8 *mptr=m_midibuf;
sU32 laststat=-1;
if (m_state.gnr<m_base.gdnum && m_state.time==m_state.gnt) // neues global-event?
{
m_state.usecs=(*(sU32 *)(m_state.gptr+3*m_base.gdnum+4*m_state.gnr))*(m_samplerate/100);
m_state.num=m_state.gptr[7*m_base.gdnum+m_state.gnr];
m_state.den=m_state.gptr[8*m_base.gdnum+m_state.gnr];
m_state.tpq=m_state.gptr[9*m_base.gdnum+m_state.gnr];
m_state.gnr++;
UPDATENT2(m_state.gnr, m_state.gnt, m_state.gptr+m_state.gnr, m_base.gdnum);
}
UPDATENT3(m_state.gnr, m_state.gnt, m_state.gptr+m_state.gnr, m_base.gdnum);
for (sInt ch=0; ch<16; ch++)
{
V2MBase::Channel &bc=m_base.chan[ch];
PlayerState::Channel &sc=m_state.chan[ch];
if (!bc.notenum)
continue;
// 1. process pgm change events
if (sc.pcnr<bc.pcnum && m_state.time==sc.pcnt)
{
PUTSTAT(0xc0|ch)
*mptr++=(sc.lastpc+=sc.pcptr[3*bc.pcnum]);
sc.pcnr++;
sc.pcptr++;
UPDATENT2(sc.pcnr,sc.pcnt,sc.pcptr,bc.pcnum);
}
UPDATENT3(sc.pcnr,sc.pcnt,sc.pcptr,bc.pcnum);
// 2. process control changes
for (sInt cn=0; cn<7; cn++)
{
V2MBase::Channel::CC &bcc=bc.ctl[cn];
PlayerState::Channel::CC &scc=sc.ctl[cn];
if (scc.ccnr<bcc.ccnum && m_state.time==scc.ccnt)
{
PUTSTAT(0xb0|ch)
*mptr++=cn+1;
*mptr++=(scc.lastcc+=scc.ccptr[3*bcc.ccnum]);
scc.ccnr++;
scc.ccptr++;
UPDATENT2(scc.ccnr,scc.ccnt,scc.ccptr,bcc.ccnum);
}
UPDATENT3(scc.ccnr,scc.ccnt,scc.ccptr,bcc.ccnum);
}
// 3. process pitch bends
if (sc.pbnr<bc.pbnum && m_state.time==sc.pbnt)
{
PUTSTAT(0xe0|ch)
*mptr++=(sc.lastpb0+=sc.pbptr[3*bc.pcnum]);
*mptr++=(sc.lastpb1+=sc.pbptr[4*bc.pcnum]);
sc.pbnr++;
sc.pbptr++;
UPDATENT2(sc.pbnr,sc.pbnt,sc.pbptr,bc.pbnum);
}
UPDATENT3(sc.pbnr,sc.pbnt,sc.pbptr,bc.pbnum);
// 4. process notes
while (sc.notenr<bc.notenum && m_state.time==sc.notent)
{
PUTSTAT(0x90|ch)
*mptr++=(sc.lastnte+=sc.noteptr[3*bc.notenum]);
*mptr++=(sc.lastvel+=sc.noteptr[4*bc.notenum]);
sc.notenr++;
sc.noteptr++;
UPDATENT2(sc.notenr,sc.notent,sc.noteptr,bc.notenum);
}
UPDATENT3(sc.notenr,sc.notent,sc.noteptr,bc.notenum);
}
*mptr++=0xfd;
synthProcessMIDI(m_midibuf);
if (m_state.nexttime==(sU32)-1) m_state.state=PlayerState::STOPPED;
}
sBool CV2MPlayer::Open(const void *a_v2mptr, sU32 a_samplerate)
///////////////////////////////////////////////////////////////
{
if (m_base.valid) Close();
m_samplerate=a_samplerate;
if (!InitBase(a_v2mptr)) return sFALSE;
Reset();
return m_base.valid=sTRUE;
}
void CV2MPlayer::Close()
////////////////////////
{
if (!m_base.valid) return;
if (m_state.state!=PlayerState::OFF) Stop();
m_base.valid=0;
}
void CV2MPlayer::Play(sU32 a_time)
//////////////////////////////////
{
if (!m_base.valid || !m_samplerate) return;
Stop();
Reset();
m_base.valid=sFALSE;
sU32 destsmpl, cursmpl=0;
__asm
{
mov ecx, this
mov eax, [a_time]
mov ebx, [ecx + m_samplerate]
imul ebx
mov ebx, [ecx + m_tpc]
idiv ebx
mov [destsmpl], eax
}
m_state.state=PlayerState::PLAYING;
m_state.smpldelta=0;
m_state.smplrem=0;
while ((cursmpl+m_state.smpldelta)<destsmpl && m_state.state==PlayerState::PLAYING)
{
cursmpl+=m_state.smpldelta;
Tick();
if (m_state.state==PlayerState::PLAYING)
{
UpdateSampleDelta(m_state.nexttime,m_state.time,m_state.usecs,m_base.timediv2,&m_state.smplrem,&m_state.smpldelta);
}
else
m_state.smpldelta=-1;
}
m_state.smpldelta-=(destsmpl-cursmpl);
m_timeoffset=cursmpl-m_state.cursmpl;
m_fadeval=1.0f;
m_fadedelta=0.0f;
m_base.valid=sTRUE;
}
void CV2MPlayer::Stop(sU32 a_fadetime)
//////////////////////////////////////
{
if (!m_base.valid) return;
if (a_fadetime)
{
sU32 ftsmpls;
__asm
{
mov ecx, this
mov eax, [a_fadetime]
mov ebx, [ecx + m_samplerate]
imul ebx
mov ebx, [ecx + m_tpc]
idiv ebx
mov [ftsmpls], eax
}
m_fadedelta=m_fadeval/ftsmpls;
}
else
m_state.state=PlayerState::OFF;
}
sBool CV2MPlayer::Render(sF32 *a_buffer, sU32 a_len)
////////////////////////////////////////////////////
{
if (!a_buffer) return sFALSE;
if (m_base.valid && m_state.state==PlayerState::PLAYING)
{
sU32 todo=a_len;
while (todo)
{
sInt torender=(todo>m_state.smpldelta)?m_state.smpldelta:todo;
if (torender)
{
synthRender(a_buffer,torender);
a_buffer+=2*torender;
todo-=torender;
m_state.smpldelta-=torender;
m_state.cursmpl+=torender;
}
if (!m_state.smpldelta)
{
Tick();
if (m_state.state==PlayerState::PLAYING)
UpdateSampleDelta(m_state.nexttime,m_state.time,m_state.usecs,m_base.timediv2,&m_state.smplrem,&m_state.smpldelta);
else
m_state.smpldelta=-1;
}
}
}
else if (m_state.state==PlayerState::OFF || !m_base.valid)
{
__asm {
mov edi, [a_buffer]
mov ecx, [a_len]
shl ecx, 1
xor eax, eax
rep stosd
}
}
else
{
synthRender(a_buffer,a_len);
m_state.cursmpl+=a_len;
}
if (m_fadedelta)
{
for (sU32 i=0; i<a_len; i++)
{
a_buffer[2*i]*=m_fadeval;
a_buffer[2*i+1]*=m_fadeval;
m_fadeval-=m_fadedelta; if (m_fadeval<0) m_fadeval=0;
}
if (!m_fadeval) Stop();
}
return m_base.valid && m_state.state==PlayerState::PLAYING;
}
sU32 CV2MPlayer::GetTime()
//////////////////////////
{
if (!m_base.valid) return 0;
return 0;
}
#ifdef V2MPLAYER_SYNC_FUNCTIONS
sU32 CV2MPlayer::CalcPositions(sS32 **a_dest)
/////////////////////////////////////////////
{
if (!a_dest) return 0;
if (!m_base.valid)
{
*a_dest=0;
return 0;
}
// step 1: ende finden
sS32 *&dp=*a_dest;
sU32 gnr=0;
const sU8* gp=m_base.gptr;
sU32 curbar=0;
sU32 cur32th=0;
sU32 lastevtime=0;
sU32 pb32=32;
sU32 usecs=500000;
sU32 posnum=0;
sU32 ttime, td, this32;
sF64 curtimer=0;
while (gnr<m_base.gdnum)
{
ttime=lastevtime+(gp[2*m_base.gdnum]<<16)+(gp[m_base.gdnum]<<8)+gp[0];
td=ttime-lastevtime;
this32=(td*8/m_base.timediv);
posnum+=this32;
lastevtime=ttime;
pb32=gp[7*m_base.gdnum]*32/gp[8*m_base.gdnum];
gnr++;
gp++;
}
td=m_base.maxtime-lastevtime;
this32=(td*8/m_base.timediv);
posnum+=this32+1;
dp=new sS32[2*posnum];
gnr=0;
gp=m_base.gptr;
lastevtime=0;
pb32=32;
for (sU32 pn=0; pn<posnum; pn++)
{
sU32 curtime=pn*m_base.timediv/8;
if (gnr<m_base.gdnum)
{
ttime=lastevtime+(gp[2*m_base.gdnum+gnr]<<16)+(gp[m_base.gdnum+gnr]<<8)+gp[gnr];
if (curtime>=ttime)
{
pb32=gp[7*m_base.gdnum+gnr]*32/gp[8*m_base.gdnum+gnr];
usecs=*(sU32 *)(gp+3*m_base.gdnum+4*gnr);
gnr++;
lastevtime=ttime;
}
}
dp[2*pn]=(sU32)curtimer;
dp[2*pn+1]=(curbar<<16)|(cur32th<<8)|(pb32);
cur32th++;
if (cur32th==pb32)
{
cur32th=0;
curbar++;
}
curtimer+=m_tpc*usecs/8000000.0;
}
return pn;
}
#endif
/****************************************************************************/
/****************************************************************************/
#endif
// no problem if this header is included multiple times
// in case you get any linker collisions, prepend
// __declspec(selectany) to the problematic declaration
enum V2CTLTYPES { VCTL_SKIP, VCTL_SLIDER, VCTL_MB, };
typedef struct {
int no;
char *name;
char *name2;
} V2TOPIC;
typedef struct {
int version;
char *name;
V2CTLTYPES ctltype;
int offset, min, max;
int isdest;
char *ctlstr;
} V2PARAM;
////////////////////////////////////////////
//
// V2 Patch Topics
//
////////////////////////////////////////////
static const V2TOPIC v2topics[] = {
{ 2, "Voice","Vo" },
{ 6, "Osc 1","O1" },
{ 6, "Osc 2","O2" },
{ 6, "Osc 3","O3" },
{ 3, "VCF 1","F1" },
{ 3, "VCF 2","F2" },
{ 2, "Filters","Fi" },
{ 4, "Voice Dist","VD" },
{ 6, "Amp EG","E1" },
{ 6, "EG 2","E2" },
{ 7, "LFO 1","L1" },
{ 7, "LFO 2","L2" },
{ 10, "Global","Gl" },
{ 4, "Channel Dist","CD" },
{ 7, "Chorus/Flanger","CF" },
{ 9, "Compressor","CC" },
{ 1, "Polyphony","Po" },
};
static const int v2ntopics = sizeof(v2topics)/sizeof(V2TOPIC);
////////////////////////////////////////////
//
// V2 Modulation Sources
//
////////////////////////////////////////////
static const char *v2sources[] = {
"Velocity",
"Modulation",
"Breath",
"Ctl #3",
"Ctl #4",
"Ctl #5",
"Ctl #6",
"Volume",
"Amp EG",
"EG 2",
"LFO 1",
"LFO 2",
"Note",
};
static const int v2nsources = sizeof(v2sources)/sizeof(char *);
////////////////////////////////////////////
//
// V2 Patch Parameters
//
////////////////////////////////////////////
static const V2PARAM v2parms[] = {
// Voice (2)
{ 0, "Panning", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 2, "Txpose", VCTL_SLIDER, 64, 0, 127, 1, 0 },
// Osc 1 (6)
{ 0, "Mode" , VCTL_MB , 0, 0, 7, 0, "Off|Saw/Tri|Pulse|Sin|Noise|XX|AuxA|AuxB" },
{ 2, "Ringmod", VCTL_SKIP , 0, 0, 1, 0, "" },
{ 0, "Txpose", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Detune", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Color", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Volume", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// Osc 2 (6)
{ 0, "Mode" , VCTL_MB , 0, 0, 7, 0, "!Off|Tri|Pul|Sin|Noi|FM|AuxA|AuxB" },
{ 2, "RingMod", VCTL_MB , 0, 0, 1, 0, "Off|On" },
{ 0, "Txpose" , VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Detune", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Color", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Volume", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// Osc 3 (6)
{ 0, "Mode" , VCTL_MB , 0, 0, 7, 0, "!Off|Tri|Pul|Sin|Noi|FM|AuxA|AuxB" },
{ 2, "RingMod", VCTL_MB , 0, 0, 1, 0, "Off|On" },
{ 0, "Txpose", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Detune", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Color", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Volume", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// VCF 1 (3)
{ 0, "Mode", VCTL_MB , 0, 0, 5, 0, "Off|Low|Band|High|Notch|All" },
{ 0, "Cutoff", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Reso", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// VCF 2 (3)
{ 0, "Mode", VCTL_MB , 0, 0, 5, 0, "Off|Low|Band|High|Notch|All" },
{ 0, "Cutoff", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Reso", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// Routing (2)
{ 0, "Routing", VCTL_MB , 0, 0, 2, 0, "!single|serial|parallel" },
{ 3, "Balance", VCTL_SLIDER, 64, 0, 127, 1, 0 },
// Distortion (4)
{ 0, "Mode", VCTL_MB , 0, 0, 8, 0, "Off|OD|Clip|Crush|Dec|LPF|BPF|HPF|NoF|APF" },
{ 0, "InGain", VCTL_SLIDER, 32, 0, 127, 1, 0 },
{ 0, "Param 1", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Param 2", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// Amp Envelope (6)
{ 0, "Attack", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Decay", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Sustain", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "SusTime", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Release", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Amplify", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// Envelope 2 (6)
{ 0, "Attack", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Decay", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Sustain", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "SusTime", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Release", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Amplify", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// LFO 1 (7)
{ 0, "Mode" , VCTL_MB , 0, 0, 4, 0, "Saw|Tri|Pulse|Sin|S+H" },
{ 0, "KeySync", VCTL_MB , 0, 0, 2, 0, "!Off|On" },
{ 0, "EnvMode", VCTL_MB , 0, 0, 2, 0, "!Off|On" },
{ 0, "Rate", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Phase", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 5, "Polarity",VCTL_MB, 0, 0, 2, 0, "!+|-|±" },
{ 0, "Amplify", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// LFO 2 (7)
{ 0, "Mode" , VCTL_MB , 0, 0, 4, 0, "Saw|Tri|Pulse|Sin|S+H" },
{ 0, "KeySync", VCTL_MB , 0, 0, 2, 0, "!Off|On" },
{ 0, "EnvMode", VCTL_MB , 0, 0, 2, 0, "!Off|On" },
{ 0, "Rate", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Phase", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 5, "Polarity",VCTL_MB, 0, 0, 2, 0, "!+|-|±" },
{ 0, "Amplify", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// Globals (10)
{ 0, "OscSync", VCTL_MB , 0, 0, 1, 0, "!Off|On" },
{ 0, "ChanVol", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 6, "AuxA Recv",VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 6, "AuxB Recv",VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 6, "AuxA Send",VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 6, "AuxB Send",VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Reverb", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Delay", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "FXRoute", VCTL_MB , 0, 0, 1, 0, "!Dis -> Cho|Cho -> Dis" },
{ 1, "Boost", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// Channel Dist (4)
{ 0, "Mode", VCTL_MB , 0, 0, 8, 0, "Off|OD|Clip|Crush|Dec|LPF|BPF|HPF|NoF|APF" },
{ 0, "InGain", VCTL_SLIDER, 32, 0, 127, 1, 0 },
{ 0, "Param 1", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Param 2", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// Chorus/Flanger (7)
{ 0, "Amount", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "FeedBk", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Delay L", VCTL_SLIDER, 0, 1, 127, 1, 0 },
{ 0, "Delay R", VCTL_SLIDER, 0, 1, 127, 1, 0 },
{ 0, "M. Rate", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "M.Depth", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "M.Phase", VCTL_SLIDER, 64, 0, 127, 1, 0 },
// Compressor (9)
{ 1, "Mode", VCTL_MB , 0, 0, 2, 0, "!Off|Peak|RMS" },
{ 1, "Couple", VCTL_MB , 0, 0, 1, 0, "!Mono|Stereo" },
{ 1, "AutoGain",VCTL_MB , 0, 0, 1, 0, "!Off|On" },
{ 1, "LkAhead", VCTL_SLIDER, 0, 0, 10, 1, 0 },
{ 1, "Threshd", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "Ratio", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "Attack", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "Release", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "OutGain", VCTL_SLIDER, 64, 0, 127, 1, 0 },
// Polyphony (1)
{ 0, "MaxPoly", VCTL_SLIDER, 0, 1, 16, 0, 0 },
};
static const int v2nparms = sizeof(v2parms)/sizeof(V2PARAM);
// patch size
static const int v2soundsize=v2nparms+1+255*3;
////////////////////////////////////////////
//
// V2 Initial Patch Parameter Setup
//
////////////////////////////////////////////
static const unsigned char v2initsnd[v2soundsize] =
{
64, // Panning
64, // Transpose
1, 0, 64, 64, 0, 127, // osc1 : simple sawtooth
0, 0, 64, 64, 32, 127, // osc2 : off
0, 0, 64, 64, 32, 127, // osc3 : off
1, 127, 0, // vcf1 : low pass, open
0, 64, 0, // vcf2 : off
0, // routing : single
64, // filter balance: mid
0, 32, 0, 64, // VoiceDis: off
0, 64, 127, 64, 80, 0, // env1 : pling
0, 64, 127, 64, 80, 64, // env2 : pling
1, 1, 0, 64, 2, 0, 0, // lfo1 : defaultbla
1, 1, 0, 64, 2, 0, 127, // lfo2 : defaultbla
0, // oscsync
0, // chanvol
0, 0, // aux a/b recv
0, 0, // aux a/b send
0, 0, // aux 1/2 sends
0, // FXRoute
0, // Boost
0, 32, 100, 64, // ChanDist: off
64, 64, 32, 32, 0, 0, 64, // Chorus/Flanger: off
0, 0, 1, 2, 90, 32, 20, 64, 64, // Compressor: off
1, // maxpoly : 1
4, // mods : 4
0, 127, 37, // velocity -> aenv ampl
1, 127, 50, // modulation -> lfo1 ampl
10, 65, 1, // lfo1 -> txpose
7, 127, 59, // ctl7 (volume) -> chanvol
0, // rest of mods
};
////////////////////////////////////////////
//
// V2 Global Topics
//
////////////////////////////////////////////
static const V2TOPIC v2gtopics[] = {
{ 4, "Reverb","Rv" },
{ 7, "Stereo Delay","SD" },
//FICKEN { 9, "Parametric EQ","EQ" },
{ 2, "Post Filters","Fi" },
{ 9, "Sum Compressor","SC" },
{ 1, "Gui Features","GF" },
};
static const int v2ngtopics = sizeof(v2gtopics)/sizeof(V2TOPIC);
////////////////////////////////////////////
//
// V2 Global Parameters
//
////////////////////////////////////////////
static const V2PARAM v2gparms[] = {
// 00: Reverb
{ 0, "Time" , VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "HighCut", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 4, "LowCut", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "Volume", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// 03: Delay
{ 0, "Volume", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "FeedBk", VCTL_SLIDER, 64, 0, 127, 1, 0 },
{ 0, "Delay L", VCTL_SLIDER, 0, 1, 127, 1, 0 },
{ 0, "Delay R", VCTL_SLIDER, 0, 1, 127, 1, 0 },
{ 0, "M. Rate", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "M.Depth", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "M.Phase", VCTL_SLIDER, 64, 0, 127, 1, 0 },
// EQ
//FICKEN{ 6, "Gain 1", VCTL_SLIDER, 64, 0, 127, 1, 0 },
//FICKEN{ 6, "Freq 1", VCTL_SLIDER, 0, 0, 127, 1, 0 },
//FICKEN{ 6, "Q 1", VCTL_SLIDER, 0, 1, 127, 129, 0 },
//FICKEN{ 6, "Gain 2", VCTL_SLIDER, 64, 0, 127, 1, 0 },
//FICKEN{ 6, "Freq 2", VCTL_SLIDER, 0, 0, 127, 1, 0 },
//FICKEN{ 6, "Q 2", VCTL_SLIDER, 0, 1, 127, 129, 0 },
//FICKEN{ 6, "Gain 3", VCTL_SLIDER, 64, 0, 127, 1, 0 },
//FICKEN{ 6, "Freq 3", VCTL_SLIDER, 0, 0, 127, 1, 0 },
//FICKEN{ 6, "Q 3", VCTL_SLIDER, 0, 1, 127, 129, 0 },
// 10: PostProcessing
{ 0, "Low Cut", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 0, "HighCut", VCTL_SLIDER, 0, 0, 127, 1, 0 },
// 12: Compressor
{ 1, "Mode", VCTL_MB , 0, 0, 2, 0, "!Off|Peak|RMS" },
{ 1, "Couple", VCTL_MB , 0, 0, 1, 0, "!Mono|Stereo" },
{ 1, "AutoGain",VCTL_MB , 0, 0, 1, 0, "!Off|On" },
{ 1, "LkAhead", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "Threshd", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "Ratio", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "Attack", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "Release", VCTL_SLIDER, 0, 0, 127, 1, 0 },
{ 1, "OutGain", VCTL_SLIDER, 64, 0, 127, 1, 0 },
// gui features (deprecated)
{ 6, "",VCTL_SKIP, 0, 0, 3, 0, "" }, // mystery parameter: we dont go to ravenholm
};
static const int v2ngparms = sizeof(v2gparms)/sizeof(V2PARAM);
////////////////////////////////////////////
//
// V2 Initial Global Parameter Setup
//
////////////////////////////////////////////
static const unsigned char v2initglobs[v2ngparms] =
{
64, 64, 32, 127, // Reverb
100, 80, 64, 64, 0, 0, 64, // Delay
//FICKEN 64, 64,64,64,64,64,64,64,64, // EQ
0, 127, // lc/hc
0, 0, 1, 2, 90, 32, 20, 64, 64, // Compressor
0 // gui color
};
// total sound memory size
static const int smsize=128*sizeof(void*) + 128*v2soundsize;
static int v2version;
static int *v2vsizes;
static int *v2gsizes;
static void sdInit()
{
/*soundmem = new unsigned char [smsize+v2soundsize];
patchoffsets = (long *)soundmem;
unsigned char *sptr=soundmem+128*4;
printf("sound size: %d\n",v2nparms);
char s[256];
for (int i=0; i<129; i++)
{
if (i<128)
{
patchoffsets[i]=(long)(sptr-soundmem);
sprintf(s,"Init Patch #%03d",i);
strcpy(patchnames[i],s);
}
else
editmem=sptr;
memcpy(sptr,v2initsnd,v2soundsize);
sptr+=v2soundsize;
}
for (int i=0; i<v2ngparms; i++)
globals[i]=v2initglobs[i];
memcpy(v2clipboard,v2initsnd,v2soundsize);
sprintf(v2clipname,"Init Patch");*/
// init version control
v2version=0;
for (int i=0; i<v2nparms; i++)
if (v2parms[i].version>v2version) v2version=v2parms[i].version;
for (int i=0; i<v2ngparms; i++)
if (v2gparms[i].version>v2version) v2version=v2gparms[i].version;
v2vsizes = new int[v2version+1];
v2gsizes = new int[v2version+1];
memset(v2vsizes,0,(v2version+1)*sizeof(int));
memset(v2gsizes,0,(v2version+1)*sizeof(int));
for (int i=0; i<v2nparms; i++)
{
const V2PARAM &p=v2parms[i];
// ATLASSERT(p.version<=v2version);
for (int j=v2version; j>=p.version; j--)
v2vsizes[j]++;
}
for (int i=0; i<v2ngparms; i++)
{
const V2PARAM &p=v2gparms[i];
//ATLASSERT(p.version<=v2version);
for (int j=v2version; j>=p.version; j--)
v2gsizes[j]++;
}
//ATLASSERT(v2vsizes[v2version]==v2nparms);
for (int i=0; i<=v2version; i++)
{
//printf("size of version %d sound bank: %d params, %d globals\n",i,v2vsizes[i],v2gsizes[i]);
v2vsizes[i]+=1+255*3;
}
}
static void sdClose()
{
//delete soundmem;
delete[] v2vsizes;
delete[] v2gsizes;
}
#define printf2
static const char * const v2mconv_errors[] =
{
"no error",
"V2M file contains no patch data! (forgot program changes?)",
"V2M file was made with unknown synth version!",
};
// nicht drüber nachdenken.
static struct _ssbase {
const sU8 *patchmap;
const sU8 *globals;
sU32 timediv;
sU32 timediv2;
sU32 maxtime;
const sU8 *gptr;
sU32 gdnum;
struct _basech {
sU32 notenum;
const sU8 *noteptr;
sU32 pcnum;
const sU8 *pcptr;
sU32 pbnum;
const sU8 *pbptr;
struct _bcctl {
sU32 ccnum;
const sU8 *ccptr;
} ctl[7];
} chan[16];
const sU8 *mididata;
sInt midisize;
sInt patchsize;
sInt globsize;
sInt maxp;
const sU8 *newpatchmap;
const sU8 *speechdata;
sInt spsize;
} base;
static void readfile(const unsigned char *inptr, const int inlen)
{
const sU8 *d=inptr;
memset(&base,0,sizeof(base));
base.timediv=(*((sU32*)(d)));
base.timediv2=10000*base.timediv;
base.maxtime=*((sU32*)(d+4));
base.gdnum=*((sU32*)(d+8));
d+=12;
base.gptr=d;
d+=10*base.gdnum;
for (sInt ch=0; ch<16; ch++)
{
_ssbase::_basech &c=base.chan[ch];
c.notenum=*((sU32*)d);
d+=4;
if (c.notenum)
{
c.noteptr=d;
d+=5*c.notenum;
c.pcnum=*((sU32*)d);
d+=4;
c.pcptr=d;
d+=4*c.pcnum;
c.pbnum=*((sU32*)d);
d+=4;
c.pbptr=d;
d+=5*c.pbnum;
for (sInt cn=0; cn<7; cn++)
{
_ssbase::_basech::_bcctl &cc=c.ctl[cn];
cc.ccnum=*((sU32*)d);
d+=4;
cc.ccptr=d;
d+=4*cc.ccnum;
}
}
}
base.midisize=d-inptr;
base.globsize=*((sU32*)d);
if (base.globsize<0 || base.globsize>131072) return;
d+=4;
base.globals=d;
d+=base.globsize;
base.patchsize=*((sU32*)d);
if (base.patchsize<0 || base.patchsize>1048576) return;
d+=4;
base.patchmap=d;
d+=base.patchsize;
if (d-inptr < inlen)
{
base.spsize=*((sU32*)d);
d+=4;
base.speechdata=d;
d+=base.spsize;
// small sanity check
if (base.spsize<0 || base.spsize > 8192)
{
base.spsize=0;
base.speechdata=0;
}
}
else
{
base.spsize=0;
base.speechdata=0;
}
printf2("after read: est %d, is %d\n",inlen,d-inptr);
printf2("midisize: %d, globs: %d, patches: %d\n",base.midisize,base.globsize,base.patchsize);
}
// gives version deltas
static int CheckV2MVersion(const unsigned char *inptr, const int inlen)
{
int i;
readfile(inptr,inlen);
if (!base.patchsize)
return -1;
// determine highest used patch from progchange commands
// (midiconv remaps the patches so that they're a
// continuous block from 0 to x)
base.maxp=0;
for (int ch=0; ch<16; ch++)
{
for (int i=0; i<(int)base.chan[ch].pcnum; i++)
{
sInt p=base.chan[ch].pcptr[3*base.chan[ch].pcnum+i];
// WORKAROUND: omit illegal patch numbers
// (bug in midiconv? bug in logic's .mid export?)
printf2("ch%d pc%d : %2x\n",ch+1,i,p);
if (p<0x80)
if (p>=base.maxp) base.maxp=p+1;
}
}
printf2("patches used: %d\n",base.maxp);
if (!base.maxp)
return -1;
// offset table to ptaches
sInt *poffsets=(sInt *)base.patchmap;
sInt matches=0, best=-1;
// for each version check...
for (i=0; i<=v2version; i++)
{
// ... if globals size is correct...
if (base.globsize==v2gsizes[i])
{
printf2("globsize match: %d\n",i);
// ... and if the size of all patches makes sense
int p;
for (p=0; p<base.maxp-1; p++)
{
sInt d=(poffsets[p+1]-poffsets[p])-(v2vsizes[i]-3*255);
if (d%3) break;
d/=3;
if ( d != base.patchmap[poffsets[p]+v2vsizes[i]-3*255-1])
break;
}
if (p==base.maxp-1)
{
printf2("... ok!\n");
best=i;
matches++;
}
else
printf2("no match!\n");
}
}
// if we've got exactly one match, we're quite sure
sInt ret=(matches>=1)?v2version-best:-2;
printf2("found version delta: %d\n",ret);
return ret;
}
//Klicke den Narren über Dir und das Schauspiel wird beginnen...
static sInt transEnv(sInt enVal)
{
sF32 dv=(sF32)(1.0f-pow(2.0f, -enVal/128.0f*11.0f));
dv=(sF32)sqrt(dv); // square root to correct value
sInt nEnVal=(sInt)(-log(1.0f-dv)/log(2.0f)*128.0f/11.0f);
if (nEnVal<0)
{
printf2("!!clamping enval lower bound!\n");
nEnVal=0;
}
else if (nEnVal>127)
{
printf2("!!clamping enval upper bound!\n");
nEnVal=127;
}
return nEnVal;
}
static void ConvertV2M(const unsigned char *inptr, const int inlen, unsigned char **outptr, int *outlen)
{
int i,p;
// check version
sInt vdelta=CheckV2MVersion(inptr,inlen);
if (!vdelta) // if same, simply clone
{
*outptr=new sU8[inlen+4];
memset(*outptr,0,inlen+4);
*outlen=inlen+4;
memcpy(*outptr,inptr,inlen);
return;
}
else if (vdelta<0) // if invalid...
{
*outptr=0;
*outlen=0;
return;
}
vdelta=v2version-vdelta;
// fake base.maxp
int maxp2=((sInt*)base.patchmap)[0]/4;
if (maxp2!=base.maxp)
{
printf2("warning: patch count inconsistency: we:%d, they:%d\n",base.maxp,maxp2);
base.maxp=maxp2;
}
// calc new size
int gdiff=v2gsizes[v2version]-v2gsizes[vdelta];
int pdiff=v2vsizes[v2version]-v2vsizes[vdelta];
int newsize=inlen+gdiff+base.maxp*pdiff;
printf2("old size: %d, new size: %d\n",inlen,newsize);
// init new v2m
*outlen=newsize+4;
sU8 *newptr=*outptr=new sU8[newsize+4];
memset(newptr,0,newsize+4);
// copy midi data
memcpy(newptr,inptr,base.midisize);
// new globals length...
newptr+=base.midisize;
*(sInt *)newptr=v2ngparms;
printf2("glob size: old %d, new %d\n",base.globsize,*(sInt *)newptr);
newptr+=4;
// copy/remap globals
memcpy(newptr,v2initglobs,v2ngparms);
const sU8 *oldgptr=base.globals;
for (i=0; i<v2ngparms; i++)
{
if (v2gparms[i].version<=vdelta)
newptr[i]=*oldgptr++;
}
newptr+=v2ngparms;
// new patch data length
*(sInt *)newptr=base.patchsize+base.maxp*pdiff;
printf2("patch size: old %d, new %d\n",base.patchsize,*(sInt *)newptr);
newptr+=4;
base.newpatchmap=newptr;
sInt *poffsets=(sInt *)base.patchmap;
sInt *noffsets=(sInt *)newptr;
const int ros=v2vsizes[vdelta]-255*3-1;
sU8 *nptr2=newptr;
// copy patch table...
for (p=0; p<base.maxp; p++)
noffsets[p]=poffsets[p]+p*pdiff;
newptr+=4*base.maxp;
// for each patch...
for (p=0; p<base.maxp; p++)
{
const sU8 *src=base.patchmap+poffsets[p];
const sU8 *dest_soll=nptr2+noffsets[p];
printf2("p%d ist:%08x soll:%08x\n",p,newptr,dest_soll);
// fill patch with default values
memcpy(newptr,v2initsnd,v2nparms);
// copy/remap patch data
for (i=0; i<v2nparms; i++)
{
if (v2parms[i].version<=vdelta)
{
*newptr=*src++;
/*
if (vdelta<2 && (i==33 || i==36 || i==39 || i==42)) // fix envelopes
*newptr=transEnv(*newptr);
*/
}
newptr++;
}
// copy mod number
const sInt modnum=*newptr++=*src++;
// printf2("patch %d: %d mods\n",p,modnum);
// copy/remap modulations
for (i=0; i<modnum; i++)
{
newptr[0]=src[0];
newptr[1]=src[1];
newptr[2]=src[2];
for (int k=0; k<=newptr[2]; k++)
if (v2parms[k].version>vdelta) newptr[2]++;
newptr+=3;
src+=3;
}
}
// copy speech
*(sU32*)newptr=base.spsize;
newptr+=4;
memcpy(newptr,base.speechdata,base.spsize);
newptr+=base.spsize;
printf2("est size: %d, real size: %d\n",newsize,newptr-*outptr);
}
static unsigned long GetV2MPatchData(const unsigned char *inptr, const int inlen,
unsigned char **outptr, const unsigned char **patchmap)
{
int outlen;
ConvertV2M(inptr,inlen,outptr,&outlen);
sInt *poffsets=(sInt*)base.newpatchmap;
for (sInt i=0; i<base.maxp; i++)
{
patchmap[i]=base.newpatchmap+poffsets[i];
}
return base.maxp;
}
/****************************************************************************/
/*** ***/
/*** Interface ***/
/*** ***/
/****************************************************************************/
#if !sINTRO
sViruz2::sViruz2()
{
Buffer = new sF32[16384];
}
sViruz2::~sViruz2()
{
if(Buffer)
delete[] Buffer;
}
sU8 *sViruz2::ConvertV2M(const sU8 *in,sInt inlen,sInt &outlen)
{
sU8 *out=0;
sdInit();
::ConvertV2M(in,inlen,&out,&outlen);
sdClose();
return out;
}
sBool sViruz2::Init(sInt songnr)
{
Buffer = new sF32[16384];
Viruz.Open(Stream);
Viruz.Play(0);
// ssInit(Stream,0);
// ssPlay();
return sTRUE;
}
sInt sViruz2::Render(sS16 *d,sInt samples)
{
sInt result;
sInt count;
sInt i;
result = 0;
while(samples>0)
{
count = sMin(samples,4096);
Viruz.Render(Buffer,count);
for(i=0;i<count*2;i++)
{
*d++ = sFtol(sRange<sF32>(Buffer[i],1,-1)*0x7fff);
// *d++ = sFtol(sRange<sF32>(Buffer[i*2+1],1,-1)*0x7fff);
}
samples-=count;
result += count;
}
return result;
}
sInt sViruz2::GetTuneLength()
{
sS32 *timeline;
sInt nevents,len_ms;
nevents = Viruz.CalcPositions(&timeline);
len_ms = timeline[2*(nevents-1)] + 2000;
delete[] timeline;
return (len_ms*441)/10;
}
#endif
/****************************************************************************/
/****************************************************************************/
Jump to Line
Something went wrong with that request. Please try again.