1 /*******************************************************************************
2  * Copyright 2009-2016 Jörg Müller
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 #include "Exception.h"
18 #include "IReader.h"
19 #include "file/File.h"
20 #include "respec/ChannelMapper.h"
21 #include "fx/Lowpass.h"
22 #include "fx/Highpass.h"
23 #include "fx/Envelope.h"
24 #include "respec/LinearResample.h"
25 #include "fx/Threshold.h"
26 #include "fx/Accumulator.h"
27 #include "fx/Sum.h"
28 #include "generator/Silence.h"
29 #include "fx/Limiter.h"
30 #include "devices/DeviceManager.h"
31 #include "sequence/Sequence.h"
32 #include "file/FileWriter.h"
33 #include "devices/ReadDevice.h"
34 #include "plugin/PluginManager.h"
35 #include "devices/DeviceManager.h"
36 #include "devices/IDeviceFactory.h"
37 #include "devices/NULLDevice.h"
38 
39 #include <cassert>
40 #include <cstring>
41 #include <cmath>
42 #include <sstream>
43 
44 using namespace aud;
45 
46 #define AUD_CAPI_IMPLEMENTATION
47 #include "AUD_Special.h"
48 
convSpecToC(aud::Specs specs)49 static inline AUD_Specs convSpecToC(aud::Specs specs)
50 {
51 	AUD_Specs s;
52 	s.channels = static_cast<AUD_Channels>(specs.channels);
53 	s.rate = static_cast<AUD_SampleRate>(specs.rate);
54 	return s;
55 }
56 
convCToSpec(AUD_Specs specs)57 static inline aud::Specs convCToSpec(AUD_Specs specs)
58 {
59 	aud::Specs s;
60 	s.channels = static_cast<Channels>(specs.channels);
61 	s.rate = static_cast<SampleRate>(specs.rate);
62 	return s;
63 }
64 
convDSpecToC(aud::DeviceSpecs specs)65 static inline AUD_DeviceSpecs convDSpecToC(aud::DeviceSpecs specs)
66 {
67 	AUD_DeviceSpecs s;
68 	s.specs = convSpecToC(specs.specs);
69 	s.format = static_cast<AUD_SampleFormat>(specs.format);
70 	return s;
71 }
72 
convCToDSpec(AUD_DeviceSpecs specs)73 static inline aud::DeviceSpecs convCToDSpec(AUD_DeviceSpecs specs)
74 {
75 	aud::DeviceSpecs s;
76 	s.specs = convCToSpec(specs.specs);
77 	s.format = static_cast<SampleFormat>(specs.format);
78 	return s;
79 }
80 
AUD_getInfo(AUD_Sound * sound)81 AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound)
82 {
83 	assert(sound);
84 
85 	AUD_SoundInfo info;
86 	info.specs.channels = AUD_CHANNELS_INVALID;
87 	info.specs.rate = AUD_RATE_INVALID;
88 	info.length = 0.0f;
89 
90 	try
91 	{
92 		std::shared_ptr<IReader> reader = (*sound)->createReader();
93 
94 		if(reader.get())
95 		{
96 			info.specs = convSpecToC(reader->getSpecs());
97 			info.length = reader->getLength() / (float) info.specs.rate;
98 		}
99 	}
100 	catch(Exception&)
101 	{
102 	}
103 
104 	return info;
105 }
106 
AUD_readSoundBuffer(const char * filename,float low,float high,float attack,float release,float threshold,int accumulate,int additive,int square,float sthreshold,double samplerate,int * length)107 AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high,
108 						   float attack, float release, float threshold,
109 						   int accumulate, int additive, int square,
110 						   float sthreshold, double samplerate, int* length)
111 {
112 	Buffer buffer;
113 	DeviceSpecs specs;
114 	specs.channels = CHANNELS_MONO;
115 	specs.rate = (SampleRate)samplerate;
116 	std::shared_ptr<ISound> sound;
117 
118 	std::shared_ptr<ISound> file = std::shared_ptr<ISound>(new File(filename));
119 
120 	int position = 0;
121 
122 	try
123 	{
124 		std::shared_ptr<IReader> reader = file->createReader();
125 
126 		SampleRate rate = reader->getSpecs().rate;
127 
128 		sound = std::shared_ptr<ISound>(new ChannelMapper(file, specs));
129 
130 		if(high < rate)
131 			sound = std::shared_ptr<ISound>(new Lowpass(sound, high));
132 		if(low > 0)
133 			sound = std::shared_ptr<ISound>(new Highpass(sound, low));
134 
135 		sound = std::shared_ptr<ISound>(new Envelope(sound, attack, release, threshold, 0.1f));
136 		sound = std::shared_ptr<ISound>(new LinearResample(sound, specs));
137 
138 		if(square)
139 			sound = std::shared_ptr<ISound>(new Threshold(sound, sthreshold));
140 
141 		if(accumulate)
142 			sound = std::shared_ptr<ISound>(new Accumulator(sound, additive));
143 		else if(additive)
144 			sound = std::shared_ptr<ISound>(new Sum(sound));
145 
146 		reader = sound->createReader();
147 
148 		if(!reader.get())
149 			return nullptr;
150 
151 		int len;
152 		bool eos;
153 		do
154 		{
155 			len = samplerate;
156 			buffer.resize((position + len) * sizeof(float), true);
157 			reader->read(len, eos, buffer.getBuffer() + position);
158 			position += len;
159 		} while(!eos);
160 	}
161 	catch(Exception&)
162 	{
163 		return nullptr;
164 	}
165 
166 	float * result = (float *)malloc(position * sizeof(float));
167 	std::memcpy(result, buffer.getBuffer(), position * sizeof(float));
168 	*length = position;
169 	return result;
170 }
171 
pauseSound(AUD_Handle * handle)172 static void pauseSound(AUD_Handle* handle)
173 {
174 	assert(handle);
175 	(*handle)->pause();
176 }
177 
AUD_pauseAfter(AUD_Handle * handle,double seconds)178 AUD_API AUD_Handle* AUD_pauseAfter(AUD_Handle* handle, double seconds)
179 {
180 	auto device = DeviceManager::getDevice();
181 
182 	std::shared_ptr<ISound> silence = std::shared_ptr<ISound>(new Silence(device->getSpecs().rate));
183 	std::shared_ptr<ISound> limiter = std::shared_ptr<ISound>(new Limiter(silence, 0, seconds));
184 
185 	std::lock_guard<ILockable> lock(*device);
186 
187 	try
188 	{
189 		AUD_Handle handle2 = device->play(limiter);
190 		if(handle2.get())
191 		{
192 			handle2->setStopCallback((stopCallback)pauseSound, handle);
193 			return new AUD_Handle(handle2);
194 		}
195 	}
196 	catch(Exception&)
197 	{
198 	}
199 
200 	return nullptr;
201 }
202 
AUD_readSound(AUD_Sound * sound,float * buffer,int length,int samples_per_second,short * interrupt)203 AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int samples_per_second, short* interrupt)
204 {
205 	DeviceSpecs specs;
206 	float* buf;
207 	Buffer aBuffer;
208 
209 	specs.rate = RATE_INVALID;
210 	specs.channels = CHANNELS_MONO;
211 	specs.format = FORMAT_INVALID;
212 
213 	std::shared_ptr<IReader> reader = ChannelMapper(*sound, specs).createReader();
214 
215 	specs.specs = reader->getSpecs();
216 	int len;
217 	float samplejump = specs.rate / samples_per_second;
218 	float min, max, power, overallmax;
219 	bool eos;
220 
221 	overallmax = 0;
222 
223 	for(int i = 0; i < length; i++)
224 	{
225 		len = floor(samplejump * (i+1)) - floor(samplejump * i);
226 
227 		if(*interrupt)
228 			return 0;
229 
230 		aBuffer.assureSize(len * AUD_SAMPLE_SIZE(specs));
231 		buf = aBuffer.getBuffer();
232 
233 		reader->read(len, eos, buf);
234 
235 		max = min = *buf;
236 		power = *buf * *buf;
237 		for(int j = 1; j < len; j++)
238 		{
239 			if(buf[j] < min)
240 				min = buf[j];
241 			if(buf[j] > max)
242 				max = buf[j];
243 			power += buf[j] * buf[j];
244 		}
245 
246 		buffer[i * 3] = min;
247 		buffer[i * 3 + 1] = max;
248 		buffer[i * 3 + 2] = sqrt(power) / len;
249 
250 		if(overallmax < max)
251 			overallmax = max;
252 		if(overallmax < -min)
253 			overallmax = -min;
254 
255 		if(eos)
256 		{
257 			length = i;
258 			break;
259 		}
260 	}
261 
262 	if(overallmax > 1.0f)
263 	{
264 		for(int i = 0; i < length * 3; i++)
265 		{
266 			buffer[i] /= overallmax;
267 		}
268 	}
269 
270 	return length;
271 }
272 
AUD_mixdown(AUD_Sound * sound,unsigned int start,unsigned int length,unsigned int buffersize,const char * filename,AUD_DeviceSpecs specs,AUD_Container format,AUD_Codec codec,unsigned int bitrate,void (* callback)(float,void *),void * data)273 AUD_API const char* AUD_mixdown(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data)
274 {
275 	try
276 	{
277 		Sequence* f = dynamic_cast<Sequence *>(sound->get());
278 
279 		f->setSpecs(convCToSpec(specs.specs));
280 		std::shared_ptr<IReader> reader = f->createQualityReader();
281 		reader->seek(start);
282 		std::shared_ptr<IWriter> writer = FileWriter::createWriter(filename, convCToDSpec(specs), static_cast<Container>(format), static_cast<Codec>(codec), bitrate);
283 		FileWriter::writeReader(reader, writer, length, buffersize, callback, data);
284 
285 		return nullptr;
286 	}
287 	catch(Exception& e)
288 	{
289 		return e.getMessage().c_str();
290 	}
291 }
292 
AUD_mixdown_per_channel(AUD_Sound * sound,unsigned int start,unsigned int length,unsigned int buffersize,const char * filename,AUD_DeviceSpecs specs,AUD_Container format,AUD_Codec codec,unsigned int bitrate,void (* callback)(float,void *),void * data)293 AUD_API const char* AUD_mixdown_per_channel(AUD_Sound* sound, unsigned int start, unsigned int length, unsigned int buffersize, const char* filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate, void(*callback)(float, void*), void* data)
294 {
295 	try
296 	{
297 		Sequence* f = dynamic_cast<Sequence *>(sound->get());
298 
299 		f->setSpecs(convCToSpec(specs.specs));
300 
301 		std::vector<std::shared_ptr<IWriter> > writers;
302 
303 		int channels = specs.channels;
304 		specs.channels = AUD_CHANNELS_MONO;
305 
306 		for(int i = 0; i < channels; i++)
307 		{
308 			std::stringstream stream;
309 			std::string fn = filename;
310 			size_t index = fn.find_last_of('.');
311 			size_t index_slash = fn.find_last_of('/');
312 			size_t index_backslash = fn.find_last_of('\\');
313 
314 			if((index == std::string::npos) ||
315 				((index < index_slash) && (index_slash != std::string::npos)) ||
316 				((index < index_backslash) && (index_backslash != std::string::npos)))
317 			{
318 				stream << filename << "_" << (i + 1);
319 			}
320 			else
321 			{
322 				stream << fn.substr(0, index) << "_" << (i + 1) << fn.substr(index);
323 			}
324 			writers.push_back(FileWriter::createWriter(stream.str(), convCToDSpec(specs), static_cast<Container>(format), static_cast<Codec>(codec), bitrate));
325 		}
326 
327 		std::shared_ptr<IReader> reader = f->createQualityReader();
328 		reader->seek(start);
329 		FileWriter::writeReader(reader, writers, length, buffersize, callback, data);
330 
331 		return nullptr;
332 	}
333 	catch(Exception& e)
334 	{
335 		return e.getMessage().c_str();
336 	}
337 }
338 
AUD_openMixdownDevice(AUD_DeviceSpecs specs,AUD_Sound * sequencer,float volume,double start)339 AUD_API AUD_Device* AUD_openMixdownDevice(AUD_DeviceSpecs specs, AUD_Sound* sequencer, float volume, double start)
340 {
341 	try
342 	{
343 		ReadDevice* device = new ReadDevice(convCToDSpec(specs));
344 		device->setQuality(true);
345 		device->setVolume(volume);
346 
347 		Sequence* f = dynamic_cast<Sequence*>(sequencer->get());
348 
349 		f->setSpecs(convCToSpec(specs.specs));
350 
351 		AUD_Handle handle = device->play(f->createQualityReader());
352 		if(handle.get())
353 		{
354 			handle->seek(start);
355 		}
356 
357 		return new AUD_Device(device);
358 	}
359 	catch(Exception&)
360 	{
361 		return nullptr;
362 	}
363 }
364 
AUD_initOnce()365 AUD_API void AUD_initOnce()
366 {
367 	PluginManager::loadPlugins();
368 	NULLDevice::registerPlugin();
369 }
370 
AUD_exitOnce()371 AUD_API void AUD_exitOnce()
372 {
373 }
374 
AUD_init(const char * device,AUD_DeviceSpecs specs,int buffersize,const char * name)375 AUD_API AUD_Device* AUD_init(const char* device, AUD_DeviceSpecs specs, int buffersize, const char* name)
376 {
377 	try
378 	{
379 		std::shared_ptr<IDeviceFactory> factory = DeviceManager::getDeviceFactory(device);
380 
381 		if(factory)
382 		{
383 			factory->setName(name);
384 			factory->setBufferSize(buffersize);
385 			factory->setSpecs(convCToDSpec(specs));
386 			auto device = factory->openDevice();
387 			DeviceManager::setDevice(device);
388 
389 			return new AUD_Device(device);
390 		}
391 	}
392 	catch(Exception&)
393 	{
394 	}
395 	return nullptr;
396 }
397 
AUD_exit(AUD_Device * device)398 AUD_API void AUD_exit(AUD_Device* device)
399 {
400 	delete device;
401 	DeviceManager::releaseDevice();
402 }
403 
404 
AUD_getDeviceNames()405 AUD_API char** AUD_getDeviceNames()
406 {
407 	std::vector<std::string> v_names = DeviceManager::getAvailableDeviceNames();
408 	char** names = (char**) malloc(sizeof(char*) * (v_names.size() + 1));
409 
410 	for(int i = 0; i < v_names.size(); i++)
411 	{
412 		std::string name = v_names[i];
413 		names[i] = (char*) malloc(sizeof(char) * (name.length() + 1));
414 		strcpy(names[i], name.c_str());
415 	}
416 
417 	names[v_names.size()] = nullptr;
418 
419 	return names;
420 }
421