1 /* clunk - cross-platform 3D audio API built on top SDL library
2 * Copyright (C) 2007-2014 Netive Media Group
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19
20 #include <clunk/source.h>
21 #include <clunk/clunk_ex.h>
22 #include <clunk/buffer.h>
23 #include <clunk/sample.h>
24 #include <assert.h>
25 #include <clunk/clunk_assert.h>
26 #include <clunk/mixer.h>
27
28 #if defined _MSC_VER || __APPLE__ || __FreeBSD__ || __DragonFly__
29 # define pow10f(x) powf(10.0f, (x))
30 # define log2f(x) (logf(x) / M_LN2)
31 #endif
32
33 using namespace clunk;
34
Source(const Sample * sample,const bool loop,const v3f & delta,float gain,float pitch,float panning)35 Source::Source(const Sample * sample, const bool loop, const v3f &delta, float gain, float pitch, float panning):
36 sample(sample), loop(loop), delta_position(delta), gain(gain), pitch(pitch), panning(panning),
37 position(0), fadeout(0), fadeout_total(0)
38 {
39 if (sample == NULL)
40 throw_ex(("sample for source cannot be NULL"));
41 }
42
playing() const43 bool Source::playing() const {
44 if (fadeout_total > 0 && fadeout <= 0)
45 return false;
46
47 if (loop)
48 return true;
49
50 //if (!sample3d[0].empty() || !sample3d[1].empty())
51 // return true;
52
53 return position < (int)(sample->get_data().get_size() / sample->get_spec().channels / 2);
54 }
55
_process(clunk::Buffer & dst_buf,unsigned dst_ch,const v3f & delta_position,float fx_volume,float pitch)56 float Source::_process(clunk::Buffer &dst_buf, unsigned dst_ch, const v3f &delta_position, float fx_volume, float pitch) {
57
58 const s16 * src = static_cast<const s16 *>(sample->get_data().get_ptr());
59 if (src == NULL)
60 throw_ex(("uninitialized sample used (%p)", (void *)sample));
61
62 pitch *= this->pitch * sample->pitch;
63 if (pitch <= 0)
64 throw_ex(("pitch %g could not be negative or zero", pitch));
65
66 unsigned src_ch = sample->get_spec().channels;
67 unsigned src_n = (unsigned)sample->get_data().get_size() / src_ch / 2;
68 unsigned dst_n = (unsigned)dst_buf.get_size() / dst_ch / 2;
69
70 float vol = fx_volume * gain * sample->gain;
71
72 if (vol > 1)
73 vol = 1;
74
75 Buffer src_buf;
76 unsigned dst_n_plus_overlap = dst_n + Hrtf::WINDOW_SIZE;
77 src_buf.set_size(dst_ch * dst_n_plus_overlap * 2);
78 s16 * src_buf_ptr = static_cast<s16 *>(src_buf.get_ptr());
79 for(unsigned i = 0; i < dst_n_plus_overlap; ++i) {
80 for(unsigned c = 0; c < dst_ch; ++c) {
81 int p = position + (int)(i * pitch);
82
83 s16 v = 0;
84 if (loop || (p >= 0 && p < (int)src_n)) {
85 p %= src_n;
86 if (p < 0)
87 p += src_n;
88
89 if (c < src_ch) {
90 v = src[p * src_ch + c];
91 } else {
92 v = src[p * src_ch];//expand mono channel if needed
93 }
94
95 if (panning != 0 && c < 2) {
96 bool left = c == 0;
97 int v0 = (int)((1.0f + panning * (left? -1: 1)) * v);
98 if (v0 > 32767) {
99 v = 32767;
100 } else if (v0 < -32767) {
101 v = -32767;
102 } else {
103 v = (s16)v0;
104 }
105 }
106 if (fadeout_total > 0 && fadeout - i <= 0) {
107 v = 0;
108 }
109 if (fadeout_total > 0 && fadeout - i > 0) {
110 //LOG_DEBUG(("fadeout %d: %d -> %d", fadeout - i, v, v * (fadeout - i) / fadeout_total));
111 v *= (fadeout - i) / fadeout_total;
112 }
113 }
114 src_buf_ptr[i * dst_ch + c] = v;
115 }
116 }
117
118 if (vol < 0 || (int)floor(MaxMixVolume * vol + 0.5f) <= 0) {
119 _update_position((int)(dst_n * pitch));
120 return 0;
121 }
122
123 unsigned used_samples = _hrtf.process(sample->get_spec().sample_rate, dst_buf, dst_ch, src_buf, dst_ch, delta_position, vol);
124 _update_position((int)(used_samples * pitch));
125
126 //LOG_DEBUG(("size2: %u, %u, needed: %u", (unsigned)sample3d[0].get_size(), (unsigned)sample3d[1].get_size(), dst_n));
127 return vol;
128 }
129
_update_position(const int dp)130 void Source::_update_position(const int dp) {
131 //LOG_DEBUG(("update_position(%d)", dp));
132 position += dp;
133
134 int src_n = (int)sample->get_data().get_size() / sample->get_spec().channels / 2;
135 if (loop) {
136 position %= src_n;
137 //LOG_DEBUG(("position %d", position));
138 if (position < 0)
139 position += src_n;
140 }
141 if (fadeout_total > 0) {
142 fadeout -= dp;
143 if (fadeout <= 0) {
144 fadeout = 0;
145 loop = false;
146 }
147 }
148 }
149
~Source()150 Source::~Source() {}
151
fade_out(const float sec)152 void Source::fade_out(const float sec) {
153 fadeout = fadeout_total = (int)(sample->get_spec().sample_rate * sec);
154 }
155