All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tianscar.soundtouch.SoundTouch Maven / Gradle / Ivy

/*
 * MIT License
 *
 * Copyright (c) 2021-2022 Tianscar
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package com.tianscar.soundtouch;

import static com.tianscar.soundtouch.Util.checkUnsignedInt;
import static com.tianscar.soundtouch.Util.loadLibrary;
import static java.util.Objects.requireNonNull;

/**
 * The SoundTouch class that invokes native SoundTouch routines through the JNI
 * interface.
 * 

* * SoundTouch - main class for tempo/pitch/rate adjusting routines. *
* Notes: *
  • * Initialize the SoundTouch object instance by setting up the sound stream * parameters with functions 'setSampleRate' and 'setChannels', then set * desired tempo/pitch/rate settings with the corresponding functions.
  • *
  • * The SoundTouch class behaves like a first-in-first-out pipeline: The * samples that are to be processed are fed into one of the pipe by calling * function 'putSamples', while the ready processed samples can be read * from the other end of the pipeline with function 'receiveSamples'.
  • *
  • * The SoundTouch processing classes require certain sized 'batches' of * samples in order to process the sound. For this reason the classes buffer * incoming samples until there are enough of samples available for * processing, then they carry out the processing step and consequently * make the processed samples available for outputting.
  • *
  • * For the above reason, the processing routines introduce a certain * 'latency' between the input and output, so that the samples input to * SoundTouch may not be immediately available in the output, and neither * the amount of outputtable samples may not immediately be in direct * relationship with the amount of previously input samples.
  • *
  • * The tempo/pitch/rate control parameters can be altered during processing. * Please notice though that they aren't currently protected by semaphores, * so in multi-thread application external semaphore protection may be * required.
  • *
  • * This class utilizes classes 'TDStretch' for tempo change (without modifying * pitch) and 'RateTransposer' for changing the playback rate (that is, both * tempo and pitch in the same ratio) of the sound. The third available control * 'pitch' (change pitch but maintain tempo) is produced by a combination of * combining the two other controls.
* SoundTouch WWW * * @author Tianscar */ public final class SoundTouch { /** Soundtouch library version string */ public static final String SOUNDTOUCH_VERSION = "2.3.1"; /** SoundTouch library version id */ public static final long SOUNDTOUCH_VERSION_ID = 20301L; // Available setting IDs for the 'setSetting' & 'getSetting' functions: /** Enable/disable anti-alias filter in pitch transposer (0 = disable) */ public static final int SETTING_USE_AA_FILTER = 0; /** Pitch transposer anti-alias filter length (8 .. 128 taps, default = 32) */ public static final int SETTING_AA_FILTER_LENGTH = 1; /** Enable/disable quick seeking algorithm in tempo changer routine * (enabling quick seeking lowers CPU utilization but causes a minor sound * quality compromising) */ public static final int SETTING_USE_QUICKSEEK = 2; /** Time-stretch algorithm single processing sequence length in milliseconds. This determines * to how long sequences the original sound is chopped in the time-stretch algorithm. */ public static final int SETTING_SEQUENCE_MS = 3; /** Time-stretch algorithm seeking window length in milliseconds for algorithm that finds the * best possible overlapping location. This determines from how wide window the algorithm * may look for an optimal joining location when mixing the sound sequences back together. */ public static final int SETTING_SEEKWINDOW_MS = 4; /** Time-stretch algorithm overlap length in milliseconds. When the chopped sound sequences * are mixed back together, to form a continuous sound stream, this parameter defines over * how long period the two consecutive sequences are let to overlap each other. */ public static final int SETTING_OVERLAP_MS = 5; /** Call "getSetting" with this ID to query processing sequence size in samples. * This value gives approximate value of how many input samples you'll need to * feed into SoundTouch after initial buffering to get out a new batch of * output samples. *

* This value does not include initial buffering at beginning of a new processing * stream, use SETTING_INITIAL_LATENCY to get the initial buffering size. *

* Notices: * - This is read-only parameter, i.e. setSetting ignores this parameter * - This parameter value is not constant but change depending on * tempo/pitch/rate/samplerate settings. */ public static final int SETTING_NOMINAL_INPUT_SEQUENCE = 6; /** Call "getSetting" with this ID to query nominal average processing output * size in samples. This value tells approcimate value how many output samples * SoundTouch outputs once it does DSP processing run for a batch of input samples. *

* Notices: * - This is read-only parameter, i.e. setSetting ignores this parameter * - This parameter value is not constant but change depending on * tempo/pitch/rate/samplerate settings. */ public static final int SETTING_NOMINAL_OUTPUT_SEQUENCE = 7; /** Call "getSetting" with this ID to query initial processing latency, i.e. * approx. how many samples you'll need to enter to SoundTouch pipeline before * you can expect to get first batch of ready output samples out. *

* After the first output batch, you can then expect to get approx. * SETTING_NOMINAL_OUTPUT_SEQUENCE ready samples out for every * SETTING_NOMINAL_INPUT_SEQUENCE samples that you enter into SoundTouch. *

* Example:
* processing with parameter -tempo=5
* => initial latency = 5509 samples
* input sequence = 4167 samples
* output sequence = 3969 samples *

* Accordingly, you can expect to feed in approx. 5509 samples at beginning of * the stream, and then you'll get out the first 3969 samples. After that, for * every approx. 4167 samples that you'll put in, you'll receive again approx. * 3969 samples out. *

* This also means that average latency during stream processing is * INITIAL_LATENCY-OUTPUT_SEQUENCE/2, in the above example case 5509-3969/2 * = 3524 samples *

* Notices: * - This is read-only parameter, i.e. setSetting ignores this parameter * - This parameter value is not constant but change depending on * tempo/pitch/rate/samplerate settings. */ public static final int SETTING_INITIAL_LATENCY = 8; static { loadLibrary(); } private volatile long handle; private volatile boolean disposed; private void checkDisposed() { if (disposed) throw new IllegalStateException("The native instance has been disposed. " + "Please create a new SoundTouch object for use."); } /** * Return true if the native instance has been destroyed. * * @return whether the native instance has been destroyed */ public boolean isDisposed() { return disposed; } /** Create a new instance of SoundTouch processor. */ public SoundTouch() { handle = createInstance(); disposed = false; } private static native long createInstance(); /** Destroys a SoundTouch processor instance. */ public void dispose() { checkDisposed(); disposed = true; destroyInstance(handle); handle = 0; } private static native void destroyInstance(long h); /** Get SoundTouch library version string. * * @return the SoundTouch library version string */ public static String getVersionString() { return SOUNDTOUCH_VERSION; } /** Get SoundTouch library version id. * * @return the SoundTouch library version id */ public static long getVersionId() { return SOUNDTOUCH_VERSION_ID; } /** Sets new rate control value. Normal rate = 1.0, smaller values * represent slower rate, larger faster rates. * * @param newRate the new rate */ public void setRate(float newRate) { setRate(handle, newRate); } private static native void setRate(long h, float newRate); /** Sets new tempo control value. Normal tempo = 1.0, smaller values * represent slower tempo, larger faster tempo. * * @param newTempo the new tempo */ public void setTempo(float newTempo) { setTempo(handle, newTempo); } private static native void setTempo(long h, float newTempo); /** Sets new rate control value as a difference in percents compared * to the original rate (-50 .. +100 %); * * @param newRate the new rate change radio */ public void setRateChange(float newRate) { setRateChange(handle, newRate); } private static native void setRateChange(long h, float newRate); /** Sets new tempo control value as a difference in percents compared * to the original tempo (-50 .. +100 %); * * @param newTempo the new tempo change radio */ public void setTempoChange(float newTempo) { setTempoChange(handle, newTempo); } private static native void setTempoChange(long h, float newTempo); /** Sets new pitch control value. Original pitch = 1.0, smaller values * represent lower pitches, larger values higher pitch. * * @param newPitch the new pitch */ public void setPitch(float newPitch) { setPitch(handle, newPitch); } private static native void setPitch(long h, float newPitch); /** Sets pitch change in octaves compared to the original pitch * (-1.00 .. +1.00); * * @param newPitch the new pitch octaves */ public void setPitchOctaves(float newPitch) { setPitchOctaves(handle, newPitch); } private static native void setPitchOctaves(long h, float newPitch); /** Sets pitch change in semi-tones compared to the original pitch * (-12 .. +12); * * @param newPitch the new pitch semi-tones */ public void setPitchSemiTones(float newPitch) { setPitchSemiTones(handle, newPitch); } private static native void setPitchSemiTones(long h, float newPitch); /** Sets the number of channels, 1 = mono, 2 = stereo, n = multichannel * * @param numChannels Number of channels. */ public void setChannels(long numChannels) { checkUnsignedInt(numChannels); setChannels(handle, numChannels); } private static native void setChannels(long h, long numChannels); /** Sets sample rate. * * @param srate Sample rate in Hz. */ public void setSampleRate(long srate) { checkUnsignedInt(srate); setSampleRate(handle, srate); } private static native void setSampleRate(long h, long srate); /** Flushes the last samples from the processing pipeline to the output. * Clears also the internal processing buffers. *

* Note: This function is meant for extracting the last samples of a sound * stream. This function may introduce additional blank samples in the end * of the sound stream, and thus it's not recommended to call this function * in the middle of a sound stream. */ public void flush() { flush(handle); } private static native void flush(long h); /** Adds 'numSamples' pcs of samples from the 'samples' memory position into * the input of the object. Notice that sample rate _has_to_ be set before * calling this function, otherwise throws a runtime_error exception. * * @param samples Sample buffer array. * @param numSamples Number of sample frames in buffer. Notice that in case of multi-channel sound a single * sample frame contains data for all channels. */ public void putSamples(final float[] samples, int numSamples) { requireNonNull(samples, "samples cannot be null."); checkUnsignedInt(numSamples); putSamples(handle, samples, numSamples); } private static native void putSamples(long h, final float[] samples, int numSamples); /** int16 version of putSamples(): This accept int16 (short) sample data * and internally converts it to float format before processing. * * @param samples Sample buffer array. * @param numSamples Number of sample frames in buffer. Notice that in case of multi-channel sound a single * sample frame contains data for all channels. */ public void putSamplesI16(final short[] samples, int numSamples) { requireNonNull(samples, "samples cannot be null."); checkUnsignedInt(numSamples); putSamples_i16(handle, samples, numSamples); } private static native void putSamples_i16(long h, final short[] samples, int numSamples); /** Clears all the samples in the object's output and internal processing * buffers. */ public void clear() { clear(handle); } private static native void clear(long h); /** Changes a setting controlling the processing system behaviour. See the * 'SETTING_...' constants for available setting ID's. * * @param settingId Setting ID number. see SETTING_... defines. * @param value New setting value. * * @return true if the setting was successfully changed, otherwise false */ public boolean setSetting(int settingId, int value) { return setSetting(handle, settingId, value) != 0; } private static native int setSetting(long h, int settingId, int value); /** Reads a setting controlling the processing system behaviour. See the * 'SETTING_...' constants for available setting ID's. * * @param settingId Setting ID number. see SETTING_... constants. * * @return the setting value. */ public int getSetting(int settingId) { return getSetting(handle, settingId); } private static native int getSetting(long h, int settingId); /** Returns number of samples currently unprocessed. * * @return number of samples currently unprocessed */ public long numUncompressedSamples() { return numUnprocessedSamples(handle); } private static native long numUnprocessedSamples(long h); /** Adjusts book-keeping so that given number of samples are removed from beginning of the * sample buffer without copying them anywhere. *

* Used to reduce the number of samples in the buffer when accessing the sample buffer directly * with 'ptrBegin' function. * * @param outBuffer Buffer where to copy output samples. * @param maxSamples How many samples to receive at max. * * @return the number of samples returned */ public int receiveSamples(float[] outBuffer, int maxSamples) { requireNonNull(outBuffer, "outBuffer cannot be null."); checkUnsignedInt(maxSamples); return receiveSamples(handle, outBuffer, maxSamples); } private static native int receiveSamples(long h, float[] outBuffer, int maxSamples); /** int16 version of receiveSamples(): This converts internal float samples * into int16 (short) return data type. * * @param outBuffer Buffer where to copy output samples. * @param maxSamples How many samples to receive at max. * * @return the number of samples returned */ public int receiveSamplesI16(short[] outBuffer, int maxSamples) { requireNonNull(outBuffer, "outBuffer cannot be null."); checkUnsignedInt(maxSamples); return receiveSamples_i16(handle, outBuffer, maxSamples); } private static native int receiveSamples_i16(long h, short[] outBuffer, int maxSamples); /** Returns number of samples currently available. * * @return number of samples currently available */ public long numSamples() { return numSamples(handle); } private static native long numSamples(long h); /** Returns true if there aren't any samples available for outputting. * * @return whether there aren't any samples available for outputting */ public boolean isEmpty() { return isEmpty(handle) != 0; } private static native int isEmpty(long h); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy