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