1 /* 2 * Copyright 2020 The Android Open Source Project 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 package org.hyphonate.megaaudio.player.sources; 17 18 import org.hyphonate.megaaudio.player.AudioSource; 19 20 /** 21 * An AudioFiller implementation for feeding data from a PCMFLOAT wavetable. 22 * We do simple, linear interpolation for inter-table values. 23 */ 24 public class WaveTableSource extends AudioSource { 25 @SuppressWarnings("unused") private static String TAG = WaveTableSource.class.getSimpleName(); 26 27 /** The samples defining one cycle of the waveform to play */ 28 protected float[] mWaveTbl; 29 /** The number of samples in the wave table. Note that the wave table is presumed to contain 30 * an "extra" sample (a copy of the 1st sample) in order to simplify the interpolation 31 * calculation. Thus, this value will be 1 less than the length of mWaveTbl. 32 */ 33 protected int mNumWaveTblSamples; 34 /** The phase (offset within the wave table) of the next output sample. 35 * Note that this may (will) be a fractional value. Range 0.0 -> mNumWaveTblSamples. 36 */ 37 protected float mSrcPhase; 38 /** The sample rate at which playback occurs */ 39 protected float mSampleRate = 48000; // This seems likely, but can be changed 40 /** The frequency of the generated audio signal */ 41 protected float mFreq = 1000; // Some reasonable default frequency 42 /** The "Nominal" frequency of the wavetable. i.e., the frequency that would be generated if 43 * each sample in the wave table was sent in turn to the output at the specified sample rate. 44 */ 45 protected float mFN; 46 /** 1 / mFN. Calculated when mFN is set to avoid a division on each call to fill() */ 47 protected float mFNInverse; 48 49 /** 50 * Constructor. 51 */ WaveTableSource()52 public WaveTableSource() { 53 } 54 55 /** 56 * Calculates the "Nominal" frequency of the wave table. 57 */ calcFN()58 private void calcFN() { 59 mFN = mSampleRate / (float)mNumWaveTblSamples; 60 mFNInverse = 1.0f / mFN; 61 } 62 63 /** 64 * Sets up to play samples from the provided wave table. 65 * @param waveTbl Contains the samples defining a single cycle of the desired waveform. 66 * This wave table contains a redundant sample in the last slot (== first slot) 67 * to make the interpolation calculation simpler, so the logical length of 68 * the wave table is one less than the length of the array. 69 */ setWaveTable(float[] waveTbl)70 public void setWaveTable(float[] waveTbl) { 71 mWaveTbl = waveTbl; 72 mNumWaveTblSamples = waveTbl != null ? mWaveTbl.length - 1 : 0; 73 74 calcFN(); 75 } 76 77 /** 78 * Sets the playback sample rate for which samples will be generated. 79 * @param sampleRate 80 */ setSampleRate(float sampleRate)81 public void setSampleRate(float sampleRate) { 82 mSampleRate = sampleRate; 83 calcFN(); 84 } 85 86 /** 87 * Set the frequency of the output signal. 88 * @param freq Signal frequency in Hz. 89 */ setFreq(float freq)90 public void setFreq(float freq) { 91 mFreq = freq; 92 } 93 94 /** 95 * Resets the playback position to the 1st sample. 96 */ 97 @Override reset()98 public void reset() { 99 mSrcPhase = 0.0f; 100 } 101 102 /** 103 * Fills the specified buffer with values generated from the wave table which will playback 104 * at the specified frequency. 105 * 106 * @param buffer The buffer to be filled. 107 * @param numFrames The number of frames of audio to provide. 108 * @param numChans The number of channels (in the buffer) required by the player. 109 * @return The number of samples generated. Since we are generating a continuous periodic 110 * signal, this will always be <code>numFrames</code>. 111 */ 112 @Override pull(float[] buffer, int numFrames, int numChans)113 public int pull(float[] buffer, int numFrames, int numChans) { 114 final float phaseIncr = mFreq * mFNInverse; 115 int outIndex = 0; 116 for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { 117 // 'mod' back into the waveTable 118 while (mSrcPhase >= (float)mNumWaveTblSamples) { 119 mSrcPhase -= (float)mNumWaveTblSamples; 120 } 121 122 // linear-interpolate 123 int srcIndex = (int)mSrcPhase; 124 float delta0 = mSrcPhase - (float)srcIndex; 125 float delta1 = 1.0f - delta0; 126 float value = ((mWaveTbl[srcIndex] * delta0) + (mWaveTbl[srcIndex + 1] * delta1)); 127 128 // Put the same value in all channels. 129 // This is inefficient and should be pulled out of this loop 130 for (int chanIndex = 0; chanIndex < numChans; chanIndex++) { 131 buffer[outIndex++] = value; 132 } 133 134 mSrcPhase += phaseIncr; 135 } 136 137 return numFrames; 138 } 139 140 /* 141 * Standard wavetable generators 142 */ 143 144 /** 145 * Generates a sin waveform wavetable. 146 * @param buffer The buffer to receive the sample values. 147 */ genSinWave(float[] buffer)148 public static void genSinWave(float[] buffer) { 149 int size = buffer.length; 150 float incr = ((float)Math.PI * 2.0f) / (float)(size - 1); 151 for(int index = 0; index < size; index++) { 152 buffer[index] = (float)Math.sin(index * incr); 153 } 154 } 155 156 /** 157 * Generates a triangular waveform 158 * @param buffer The buffer to receive the sample values. 159 * @param maxValue The maximum value for the generated wavetable 160 * @param minValue The minimum value for the generated wavetable. 161 * @param dutyCycle The fraction of wavetable for the first 1/4 of the triangle wave. 162 */ genTriangleWave( float[] buffer, float maxValue, float minValue, float dutyCycle)163 public static void genTriangleWave( 164 float[] buffer, float maxValue, float minValue, float dutyCycle) { 165 float range = maxValue - minValue; 166 int size = buffer.length - 1; 167 168 // Make a triangle that goes 0 -> max -> min -> 0. 169 int index = 0; 170 int phase0Size = (int) (size / 2 * dutyCycle); 171 172 int breakIndex = phase0Size; 173 float val = 0; 174 // Phase 0 (0 -> max) 175 if (phase0Size != 0) { 176 float phase0Incr = maxValue / (float) phase0Size; 177 for (; index < breakIndex; ++index) { 178 buffer[index] = val; 179 val += phase0Incr; 180 } 181 } else { 182 val = maxValue; 183 } 184 185 // Phase 1 & 2 (max -> min) 186 breakIndex = size - phase0Size; 187 float incr = -range / ((float) size * (1.0f - dutyCycle)); 188 for (; index < breakIndex; ++index) { 189 buffer[index] = val; 190 val += incr; 191 } 192 193 // Phase 3 (min -> 0) 194 if (phase0Size != 0) { 195 float phase0Incr = maxValue / (float) phase0Size; 196 for (; index < size; ++index) { 197 buffer[index] = val; 198 val += phase0Incr; 199 } 200 } 201 202 buffer[size] = buffer[0]; 203 } 204 } 205