forked from ExoticMatter/ImoSPC
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimo-impl.cpp
123 lines (102 loc) · 2.47 KB
/
imo-impl.cpp
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include "html5/emapi.h"
#include <stdint.h>
#include "snes_spc/Fir_Resampler.h"
#include "snes_spc/SNES_SPC.h"
#include "snes_spc/SPC_Filter.h"
#define SPC_SAMPLE_RATE 32000
Fir_Resampler<24> *resampler = NULL;
SNES_SPC *spc = NULL;
SPC_Filter *filter = NULL;
// note: imo_set_sample_rate is based on Spc_Emu::set_sample_rate_ from
// Game Music Emu, imo_run is partially based on Spc_Emu::play_, and imo_skip is
// based on Spc_Emu::skip_.
// http://www.slack.net/~ant/libs/audio.html#Game_Music_Emu
double _sampleRate = SPC_SAMPLE_RATE;
IMO_EXTERN void imo_set_sample_rate(double sampleRate)
{
if (sampleRate != _sampleRate)
{
if (resampler)
{
delete resampler;
resampler = NULL;
}
if (sampleRate > 0 && sampleRate != SPC_SAMPLE_RATE)
{
resampler = new Fir_Resampler<24>();
resampler->buffer_size(SPC_SAMPLE_RATE / 20 * 2);
resampler->time_ratio((double)SPC_SAMPLE_RATE / (_sampleRate = sampleRate), 0.9965);
}
}
}
IMO_EXTERN bool imo_load(void *file, int size)
{
if (!filter)
filter = new SPC_Filter();
if (!spc)
{
spc = new SNES_SPC();
spc->init();
}
else spc->reset();
if (spc->load_spc(file, size) != NULL) return false;
spc->clear_echo();
filter->clear();
if (resampler) resampler->clear();
return true;
}
IMO_EXTERN void imo_run(short *outBuf, int count)
{
// Don't bother if count is nothing.
if (count <= 0) return;
if (resampler)
{
int remain = count;
//while (remain > 0)
while (true)
{
int nRead = resampler->read(outBuf, remain);
remain -= nRead;
outBuf += nRead;
// Instead of checking remain twice every loop, just do an infinite
// loop, breaking once if it is ever less than or equal to zero.
//if (remain > 0) {
if (remain <= 0) break;
int nWrite = resampler->max_write();
short *tmpBuf = resampler->buffer();
spc->play(nWrite, tmpBuf);
filter->run(tmpBuf, nWrite);
resampler->write(nWrite);
//}
}
}
else
{
spc->play(count, outBuf);
filter->run(outBuf, count);
}
}
IMO_EXTERN void imo_skip(int count)
{
if (resampler)
{
int realCount = int(count * resampler->ratio()) & ~1;
if (resampler->avail() > 0)
realCount -= resampler->skip_input(realCount);
#define RESAMPLER_LATENCY 64
if (realCount <= 0) return;
short buf[RESAMPLER_LATENCY];
if (realCount > RESAMPLER_LATENCY)
{
spc->skip(realCount - RESAMPLER_LATENCY);
filter->clear();
realCount = RESAMPLER_LATENCY;
}
imo_run(buf, realCount);
}
else
{
spc->skip(count);
filter->clear();
}
}