| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
-
- #ifndef DFPSR_SOUND_API
- #define DFPSR_SOUND_API
- #include "bufferAPI.h"
- #include "stringAPI.h"
- namespace dsr {
- // Call this function from a separate thread in a sound engine to initialize the sound system, call back with sound output requests and terminate when the callback returns false.
- // The sound_streamToSpeakers function returns false if the backend could not be created, and true iff the backend completed all work and terminated safely.
- // Channels: The number of virtual speakers to send samples to.
- // How this is mapped to physical speakers depends on the system, because surround speakers may choose to play mono and stereo sound using only the front speakers.
- // sampleRate: The number of ticks per second for each channel.
- // soundOutput: A callback that requests length number of ticks generated by the sound engine and written in a packed format into the target array.
- // The soundOutput function returns true iff the audio backend should keep fetching sound samples, and false iff the engine is done and ready for the call to sound_streamToSpeakers to return.
- // target: The target array should be filled with sound samples in the -1.0f to 1.0f range, in indices from 0 to (length * channels) - 1.
- // The audio backend is responsible for converting the 32-bit float samples into a bit-depth chosen by the backend.
- // The backend is supposed to padd the SafePointer's range to at least be divisible by DSR_FLOAT_ALIGNMENT, which allow using both X vectors and F vectors.
- // The F vector can be larger than the X vector if building for a SIMD extension that only supports the widest vector length for floating-point operations.
- // Padding elements will not add to the time passed in the sound engine, for only played elements increment time.
- // length: The number of samples per channel. The total number of elements to write into target is channels * length.
- // How to use:
- // Call sound_streamToSpeakers with desired channels and sampleRate from a separate thread.
- // Handle callbacks to soundOutput by feeding the next packed sound samples and letting it return false when done.
- // Close the thread and let the sound engine clean up resources.
- bool sound_streamToSpeakers(int32_t channels, int32_t sampleRate, std::function<bool(dsr::SafePointer<float> target, int32_t length)> soundOutput);
- // Wrapper for sound_streamToSpeakers to allow working with a fixed size period for better determinism across different hardware.
- // The target elements should be filled for indices 0 to (periodSamplesPerChannel * channels) - 1
- // This allow using SIMD vectorization with a perfectly aligned period size without wasting any padding, even if the hardware's period size is an odd number.
- // A fixed period can also be used for perfect timing when playing music.
- bool sound_streamToSpeakers_fixed(int32_t channels, int32_t sampleRate, int32_t periodSamplesPerChannel, std::function<bool(dsr::SafePointer<float> target)> soundOutput);
- // A sound buffer with packed channels of 32-bit floats.
- // The duration in seconds equals samplesPerChannel / sampleRate
- struct SoundBuffer {
- Buffer impl_samples; // The packed samples.
- uint32_t impl_samplesPerChannel = 0u; // Number of samples per channel.
- uint32_t impl_channelCount = 0u; // Number of channels packed into the sound format.
- uint32_t impl_sampleRate = 0u; // How many samples each channel will play per second.
- SoundBuffer(uint32_t samplesPerChannel, uint32_t channelCount, uint32_t sampleRate);
- SoundBuffer() {}
- };
- inline SoundBuffer sound_create(uint32_t samplesPerChannel, uint32_t channelCount, uint32_t sampleRate) { return SoundBuffer(samplesPerChannel, channelCount, sampleRate); }
- inline bool sound_exists(const SoundBuffer &sound) { return sound.impl_samples.isNotNull(); }
- inline int32_t sound_getSamplesPerChannel(const SoundBuffer &sound) { return sound.impl_samplesPerChannel; }
- inline int32_t sound_getChannelCount(const SoundBuffer &sound) { return sound.impl_channelCount; }
- inline int32_t sound_getSampleRate(const SoundBuffer &sound) { return sound.impl_sampleRate; }
- inline SafePointer<float> sound_getSafePointer(const SoundBuffer &sound) { return buffer_getSafeData<float>(sound.impl_samples, "Sound buffer"); }
- SoundBuffer sound_generate_function(uint32_t samplesPerChannel, uint32_t channelCount, uint32_t sampleRate, std::function<float(double time, uint32_t channelIndex)> generator);
- enum class RiffWaveFormat {
- RawU8,
- RawI16,
- RawI24,
- RawI32
- // Floating-point sounds can currently be loaded but not saved.
- };
- // TODO: Implement random dither patterns?
- enum class RoundingMethod {
- Truncate,
- Nearest
- };
- // Encodes a Riff wave file into a file buffer.
- Buffer sound_encode_RiffWave(const SoundBuffer &sound, RiffWaveFormat format, RoundingMethod roundingMethod = RoundingMethod::Nearest);
- // Decodes a RIFF wave file from memory in file buffer and returns the sound as a packed 32-bit float sound in the -1 to +1 range.
- SoundBuffer sound_decode_RiffWave(const Buffer &fileBuffer);
- SoundBuffer sound_load(const ReadableString& filename, bool mustExist = true);
- // Save the sound buffer to the path specified by filename and return true iff the operation was successful.
- // The file extension is case insensitive after the last dot in filename.
- // Accepted file extensions:
- // *.wav
- // If mustWork is true, an exception will be raised on failure.
- // If mustWork is false, failure will return false.
- bool sound_save(const ReadableString& filename, const SoundBuffer &sound, bool mustWork = true);
- // When you want to select a spefific version of the RIFF wave format.
- bool sound_save_RiffWave(const ReadableString& filename, const SoundBuffer &sound, RiffWaveFormat format, RoundingMethod roundingMethod = RoundingMethod::Nearest, bool mustWork = true);
- // TODO:
- // * Resample sound buffers in a different speed, so that tones are generated in advance.
- // Limit sound buffers to 2^32 total samples (17 GB with 27 hours in a single sound) and try to use gather functions.
- /*
- float sampleLinear(int32_t leftIndex, int32_t rightIndex, double ratio, int32_t channel) {
- int64_t leftOffset = leftIndex * sound_getChannelCount(this->buffer) + channel;
- int64_t rightOffset = rightIndex * sound_getChannelCount(this->buffer) + channel;
- float a = 0.0, b = 0.0;
- SafePointer<float> source = sound_getSafePointer(this->buffer);
- a = source[leftOffset];
- b = source[rightOffset];
- return b * ratio + a * (1.0 - ratio);
- }
- float sampleLinear_cyclic(double location, int32_t channel) {
- int32_t truncated = (int64_t)location;
- int32_t floor = truncated % sound_getSamplesPerChannel(this->buffer);
- int32_t ceiling = floor + 1; if (ceiling == sound_getSamplesPerChannel(this->buffer)) { ceiling = 0; }
- double ratio = location - truncated;
- return this->sampleLinear(floor, ceiling, ratio, channel);
- }
- float sampleLinear_clamped(double location, int32_t channel) {
- int32_t truncated = (int64_t)location;
- int32_t floor = truncated; if (floor >= sound_getSamplesPerChannel(this->buffer)) { floor = sound_getSamplesPerChannel(this->buffer) - 1; }
- int32_t ceiling = floor + 1; if (ceiling >= sound_getSamplesPerChannel(this->buffer)) { ceiling = sound_getSamplesPerChannel(this->buffer) - 1; }
- double ratio = location - truncated;
- return this->sampleLinear(floor, ceiling, ratio, channel);
- }
- */
- // * Repeat sound buffers along time and channels for fast playback with a memory copy.
- // Balance shifts and random variations can be applied after the duplication to make use of all the memory.
- // * Convert between power ratios and decibels.
- // * Compress sounds using a bruteforce search for patterns.
- // Start with a low-pass filter separating high and low frequencies.
- // Analyze local minima and maxima to initialize an initial guess.
- // Compute error gradients to adjust frequencies, offsets and amplitudes.
- // Repeat for the higher frequencies until all tones have been matched and only some noise remains to compress or discard.
- }
- #endif
|