726 lines
14 KiB
C++
726 lines
14 KiB
C++
/*******************************************************************************
|
|
* Copyright 2009-2016 Jörg Müller
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
******************************************************************************/
|
|
|
|
#include "generator/Sawtooth.h"
|
|
#include "generator/Sine.h"
|
|
#include "generator/Silence.h"
|
|
#include "generator/Square.h"
|
|
#include "generator/Triangle.h"
|
|
#include "file/File.h"
|
|
#include "file/FileWriter.h"
|
|
#include "util/StreamBuffer.h"
|
|
#include "fx/Accumulator.h"
|
|
#include "fx/ADSR.h"
|
|
#include "fx/Delay.h"
|
|
#include "fx/Envelope.h"
|
|
#include "fx/Fader.h"
|
|
#include "fx/Highpass.h"
|
|
#include "fx/IIRFilter.h"
|
|
#include "fx/Limiter.h"
|
|
#include "fx/Loop.h"
|
|
#include "fx/Lowpass.h"
|
|
#include "fx/Modulator.h"
|
|
#include "fx/Pitch.h"
|
|
#include "fx/Reverse.h"
|
|
#include "fx/Sum.h"
|
|
#include "fx/Threshold.h"
|
|
#include "fx/Volume.h"
|
|
#include "fx/SoundList.h"
|
|
#include "fx/MutableSound.h"
|
|
#include "sequence/Double.h"
|
|
#include "sequence/Superpose.h"
|
|
#include "sequence/PingPong.h"
|
|
#include "respec/LinearResample.h"
|
|
#include "respec/JOSResample.h"
|
|
#include "respec/JOSResampleReader.h"
|
|
#include "respec/ChannelMapper.h"
|
|
#include "respec/ChannelMapperReader.h"
|
|
#include "util/Buffer.h"
|
|
#include "Exception.h"
|
|
|
|
#ifdef WITH_CONVOLUTION
|
|
#include "fx/BinauralSound.h"
|
|
#include "fx/ConvolverSound.h"
|
|
#endif
|
|
|
|
#include <cassert>
|
|
#include <cstring>
|
|
|
|
using namespace aud;
|
|
|
|
#define AUD_CAPI_IMPLEMENTATION
|
|
#include "AUD_Sound.h"
|
|
|
|
static inline AUD_Specs convSpecToC(aud::Specs specs)
|
|
{
|
|
AUD_Specs s;
|
|
s.channels = static_cast<AUD_Channels>(specs.channels);
|
|
s.rate = static_cast<AUD_SampleRate>(specs.rate);
|
|
return s;
|
|
}
|
|
|
|
static inline aud::Specs convCToSpec(AUD_Specs specs)
|
|
{
|
|
aud::Specs s;
|
|
s.channels = static_cast<Channels>(specs.channels);
|
|
s.rate = static_cast<SampleRate>(specs.rate);
|
|
return s;
|
|
}
|
|
|
|
AUD_API AUD_Specs AUD_Sound_getSpecs(AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
|
|
return convSpecToC((*sound)->createReader()->getSpecs());
|
|
}
|
|
|
|
AUD_API int AUD_Sound_getLength(AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
|
|
return (*sound)->createReader()->getLength();
|
|
}
|
|
|
|
AUD_API sample_t* AUD_Sound_data(AUD_Sound* sound, int* length, AUD_Specs* specs)
|
|
{
|
|
assert(sound);
|
|
assert(length);
|
|
assert(specs);
|
|
|
|
auto stream_buffer = std::dynamic_pointer_cast<StreamBuffer>(*sound);
|
|
if(!stream_buffer)
|
|
stream_buffer = std::make_shared<StreamBuffer>(*sound);
|
|
*specs = convSpecToC(stream_buffer->getSpecs());
|
|
auto buffer = stream_buffer->getBuffer();
|
|
|
|
*length = buffer->getSize() / AUD_SAMPLE_SIZE((*specs));
|
|
|
|
sample_t* data = new sample_t[buffer->getSize()];
|
|
|
|
std::memcpy(data, buffer->getBuffer(), buffer->getSize());
|
|
|
|
return data;
|
|
}
|
|
|
|
AUD_API void AUD_Sound_freeData(sample_t* data)
|
|
{
|
|
delete[] data;
|
|
}
|
|
|
|
AUD_API const char* AUD_Sound_write(AUD_Sound* sound, const char* filename, AUD_SampleRate rate, AUD_Channels channels, AUD_SampleFormat format, AUD_Container container, AUD_Codec codec, int bitrate, int buffersize)
|
|
{
|
|
assert(sound);
|
|
assert(filename);
|
|
|
|
try
|
|
{
|
|
std::shared_ptr<IReader> reader = (*sound)->createReader();
|
|
|
|
DeviceSpecs specs;
|
|
specs.specs = reader->getSpecs();
|
|
|
|
if((rate != RATE_INVALID) && (specs.rate != rate))
|
|
{
|
|
specs.rate = rate;
|
|
reader = std::make_shared<JOSResampleReader>(reader, rate);
|
|
}
|
|
|
|
if((channels != AUD_CHANNELS_INVALID) && (specs.channels != static_cast<Channels>(channels)))
|
|
{
|
|
specs.channels = static_cast<Channels>(channels);
|
|
reader = std::make_shared<ChannelMapperReader>(reader, specs.channels);
|
|
}
|
|
|
|
if(format == AUD_FORMAT_INVALID)
|
|
format = AUD_FORMAT_S16;
|
|
specs.format = static_cast<SampleFormat>(format);
|
|
|
|
const char* invalid_container_error = "Container could not be determined from filename.";
|
|
|
|
if(container == AUD_CONTAINER_INVALID)
|
|
{
|
|
std::string path = filename;
|
|
|
|
if(path.length() < 4)
|
|
return invalid_container_error;
|
|
|
|
std::string extension = path.substr(path.length() - 4);
|
|
|
|
if(extension == ".ac3")
|
|
container = AUD_CONTAINER_AC3;
|
|
else if(extension == "flac")
|
|
container = AUD_CONTAINER_FLAC;
|
|
else if(extension == ".mkv")
|
|
container = AUD_CONTAINER_MATROSKA;
|
|
else if(extension == ".mp2")
|
|
container = AUD_CONTAINER_MP2;
|
|
else if(extension == ".mp3")
|
|
container = AUD_CONTAINER_MP3;
|
|
else if(extension == ".ogg")
|
|
container = AUD_CONTAINER_OGG;
|
|
else if(extension == ".wav")
|
|
container = AUD_CONTAINER_WAV;
|
|
else
|
|
return invalid_container_error;
|
|
}
|
|
|
|
if(codec == AUD_CODEC_INVALID)
|
|
{
|
|
switch(container)
|
|
{
|
|
case AUD_CONTAINER_AC3:
|
|
codec = AUD_CODEC_AC3;
|
|
break;
|
|
case AUD_CONTAINER_FLAC:
|
|
codec = AUD_CODEC_FLAC;
|
|
break;
|
|
case AUD_CONTAINER_MATROSKA:
|
|
codec = AUD_CODEC_OPUS;
|
|
break;
|
|
case AUD_CONTAINER_MP2:
|
|
codec = AUD_CODEC_MP2;
|
|
break;
|
|
case AUD_CONTAINER_MP3:
|
|
codec = AUD_CODEC_MP3;
|
|
break;
|
|
case AUD_CONTAINER_OGG:
|
|
codec = AUD_CODEC_VORBIS;
|
|
break;
|
|
case AUD_CONTAINER_WAV:
|
|
codec = AUD_CODEC_PCM;
|
|
break;
|
|
default:
|
|
return "Unknown container, cannot select default codec.";
|
|
}
|
|
}
|
|
|
|
if(buffersize <= 0)
|
|
buffersize = AUD_DEFAULT_BUFFER_SIZE;
|
|
|
|
std::shared_ptr<IWriter> writer = FileWriter::createWriter(filename, specs, static_cast<Container>(container), static_cast<Codec>(codec), bitrate);
|
|
FileWriter::writeReader(reader, writer, 0, buffersize);
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return "An exception occured while writing.";
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_buffer(sample_t* data, int length, AUD_Specs specs)
|
|
{
|
|
assert(data);
|
|
|
|
if(length <= 0 || specs.rate <= 0 || specs.channels <= 0)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
int size = length * AUD_SAMPLE_SIZE(specs);
|
|
|
|
std::shared_ptr<Buffer> buffer = std::make_shared<Buffer>(size);
|
|
|
|
std::memcpy(buffer->getBuffer(), data, size);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new StreamBuffer(buffer, convCToSpec(specs)));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size)
|
|
{
|
|
assert(buffer);
|
|
return new AUD_Sound(new File(buffer, size));
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new StreamBuffer(*sound));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_file(const char* filename)
|
|
{
|
|
assert(filename);
|
|
return new AUD_Sound(new File(filename));
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_sawtooth(float frequency, AUD_SampleRate rate)
|
|
{
|
|
return new AUD_Sound(new Sawtooth(frequency, rate));
|
|
}
|
|
|
|
AUD_API AUD_Sound*AUD_Sound_silence()
|
|
{
|
|
return new AUD_Sound(new Silence());
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_sine(float frequency, AUD_SampleRate rate)
|
|
{
|
|
return new AUD_Sound(new Sine(frequency, rate));
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_square(float frequency, AUD_SampleRate rate)
|
|
{
|
|
return new AUD_Sound(new Square(frequency, rate));
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_triangle(float frequency, AUD_SampleRate rate)
|
|
{
|
|
return new AUD_Sound(new Triangle(frequency, rate));
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_accumulate(AUD_Sound* sound, int additive)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Accumulator(*sound, additive));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_ADSR(AUD_Sound* sound, float attack, float decay, float sustain, float release)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new ADSR(*sound, attack, decay, sustain, release));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_delay(AUD_Sound* sound, float delay)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Delay(*sound, delay));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_envelope(AUD_Sound* sound, float attack, float release, float threshold, float arthreshold)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Envelope(*sound, attack, release, threshold, arthreshold));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_fadein(AUD_Sound* sound, float start, float length)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Fader(*sound, FADE_IN, start, length));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_fadeout(AUD_Sound* sound, float start, float length)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Fader(*sound, FADE_OUT, start, length));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_filter(AUD_Sound* sound, float* b, int b_length, float* a, int a_length)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
std::vector<float> a_coeff, b_coeff;
|
|
|
|
if(b)
|
|
for(int i = 0; i < b_length; i++)
|
|
b_coeff.push_back(b[i]);
|
|
|
|
if(a)
|
|
{
|
|
for(int i = 0; i < a_length; i++)
|
|
a_coeff.push_back(a[i]);
|
|
|
|
if(*a == 0.0f)
|
|
a_coeff[0] = 1.0f;
|
|
}
|
|
|
|
return new AUD_Sound(new IIRFilter(*sound, b_coeff, a_coeff));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_highpass(AUD_Sound* sound, float frequency, float Q)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Highpass(*sound, frequency, Q));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_limit(AUD_Sound* sound, float start, float end)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Limiter(*sound, start, end));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_loop(AUD_Sound* sound, int count)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Loop(*sound, count));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_lowpass(AUD_Sound* sound, float frequency, float Q)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Lowpass(*sound, frequency, Q));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_modulate(AUD_Sound* first, AUD_Sound* second)
|
|
{
|
|
assert(first);
|
|
assert(second);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Modulator(*first, *second));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_pitch(AUD_Sound* sound, float factor)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Pitch(*sound, factor));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_rechannel(AUD_Sound* sound, AUD_Channels channels)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
DeviceSpecs specs;
|
|
specs.channels = static_cast<Channels>(channels);
|
|
specs.rate = RATE_INVALID;
|
|
specs.format = FORMAT_INVALID;
|
|
return new AUD_Sound(new ChannelMapper(*sound, specs));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_resample(AUD_Sound* sound, AUD_SampleRate rate, bool high_quality)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
DeviceSpecs specs;
|
|
specs.channels = CHANNELS_INVALID;
|
|
specs.rate = rate;
|
|
specs.format = FORMAT_INVALID;
|
|
if(high_quality)
|
|
return new AUD_Sound(new JOSResample(*sound, specs));
|
|
else
|
|
return new AUD_Sound(new LinearResample(*sound, specs));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_reverse(AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Reverse(*sound));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_sum(AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Sum(*sound));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_threshold(AUD_Sound* sound, float threshold)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Threshold(*sound, threshold));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_volume(AUD_Sound* sound, float volume)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Volume(*sound, volume));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_join(AUD_Sound* first, AUD_Sound* second)
|
|
{
|
|
assert(first);
|
|
assert(second);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Double(*first, *second));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_mix(AUD_Sound* first, AUD_Sound* second)
|
|
{
|
|
assert(first);
|
|
assert(second);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new Superpose(*first, *second));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_pingpong(AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new PingPong(*sound));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API void AUD_Sound_free(AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
delete sound;
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_copy(AUD_Sound* sound)
|
|
{
|
|
return new std::shared_ptr<ISound>(*sound);
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_list(int random)
|
|
{
|
|
try
|
|
{
|
|
return new AUD_Sound(new SoundList(random));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API int AUD_SoundList_addSound(AUD_Sound* list, AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
assert(list);
|
|
|
|
std::shared_ptr<SoundList> s = std::dynamic_pointer_cast<SoundList>(*list);
|
|
if(s.get())
|
|
{
|
|
s->addSound(*sound);
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_mutable(AUD_Sound* sound)
|
|
{
|
|
assert(sound);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new MutableSound(*sound));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
#ifdef WITH_CONVOLUTION
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_Convolver(AUD_Sound* sound, AUD_ImpulseResponse* filter, AUD_ThreadPool* threadPool)
|
|
{
|
|
assert(sound);
|
|
assert(filter);
|
|
assert(threadPool);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new ConvolverSound(*sound, *filter, *threadPool));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
AUD_API AUD_Sound* AUD_Sound_Binaural(AUD_Sound* sound, AUD_HRTF* hrtfs, AUD_Source* source, AUD_ThreadPool* threadPool)
|
|
{
|
|
assert(sound);
|
|
assert(hrtfs);
|
|
assert(source);
|
|
assert(threadPool);
|
|
|
|
try
|
|
{
|
|
return new AUD_Sound(new BinauralSound(*sound, *hrtfs, *source, *threadPool));
|
|
}
|
|
catch(Exception&)
|
|
{
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
#endif
|