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 */ 81 @Override setSampleRate(int sampleRate)82 public void setSampleRate(int sampleRate) { 83 mSampleRate = sampleRate; 84 85 calcFN(); 86 } 87 88 /** 89 * Set the frequency of the output signal. 90 * @param freq Signal frequency in Hz. 91 */ setFreq(float freq)92 public void setFreq(float freq) { 93 mFreq = freq; 94 } 95 96 /** 97 * Resets the playback position to the 1st sample. 98 */ 99 @Override reset()100 public void reset() { 101 mSrcPhase = 0.0f; 102 } 103 104 /** 105 * Fills the specified buffer with values generated from the wave table which will playback 106 * at the specified frequency. 107 * 108 * @param buffer The buffer to be filled. 109 * @param numFrames The number of frames of audio to provide. 110 * @param numChans The number of channels (in the buffer) required by the player. 111 * @return The number of samples generated. Since we are generating a continuous periodic 112 * signal, this will always be <code>numFrames</code>. 113 */ 114 @Override pull(float[] buffer, int numFrames, int numChans)115 public int pull(float[] buffer, int numFrames, int numChans) { 116 final float phaseIncr = mFreq * mFNInverse; 117 int outIndex = 0; 118 for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) { 119 // 'mod' back into the waveTable 120 while (mSrcPhase >= (float)mNumWaveTblSamples) { 121 mSrcPhase -= (float)mNumWaveTblSamples; 122 } 123 124 // linear-interpolate 125 int srcIndex = (int)mSrcPhase; 126 float delta0 = mSrcPhase - (float)srcIndex; 127 float delta1 = 1.0f - delta0; 128 float value = ((mWaveTbl[srcIndex] * delta0) + (mWaveTbl[srcIndex + 1] * delta1)); 129 130 // Put the same value in all channels. 131 // This is inefficient and should be pulled out of this loop 132 for (int chanIndex = 0; chanIndex < numChans; chanIndex++) { 133 buffer[outIndex++] = value; 134 } 135 136 mSrcPhase += phaseIncr; 137 } 138 139 return numFrames; 140 } 141 142 /* 143 * Standard wavetable generators 144 */ 145 146 /** 147 * Generates a sin waveform wavetable. 148 * @param buffer The buffer to receive the sample values. 149 */ genSinWave(float[] buffer)150 public static void genSinWave(float[] buffer) { 151 int size = buffer.length; 152 // remember, the table has 1 extra (redundant) sample as a guard point, so the logical size 153 // is buffer.length - 1 154 float incr = ((float) Math.PI * 2.0f) / (float) (buffer.length - 1); 155 for(int index = 0; index < size; index++) { 156 buffer[index] = (float)Math.sin(index * incr); 157 } 158 } 159 160 /** 161 * Generates a triangular waveform 162 * @param buffer The buffer to receive the sample values. 163 * @param maxValue The maximum value for the generated wavetable 164 * @param minValue The minimum value for the generated wavetable. 165 * @param dutyCycle The fraction of wavetable for the first 1/4 of the triangle wave. 166 */ genTriangleWave( float[] buffer, float maxValue, float minValue, float dutyCycle)167 public static void genTriangleWave( 168 float[] buffer, float maxValue, float minValue, float dutyCycle) { 169 float range = maxValue - minValue; 170 // remember, the table has 1 extra (redundant) sample as a guard point, so the logical size 171 // is buffer.length - 1 172 int size = buffer.length - 1; 173 174 // Make a triangle that goes 0 -> max -> min -> 0. 175 int index = 0; 176 int phase0Size = (int) (size / 2 * dutyCycle); 177 178 int breakIndex = phase0Size; 179 float val = 0; 180 // Phase 0 (0 -> max) 181 if (phase0Size != 0) { 182 float phase0Incr = maxValue / (float) phase0Size; 183 for (; index < breakIndex; ++index) { 184 buffer[index] = val; 185 val += phase0Incr; 186 } 187 } else { 188 val = maxValue; 189 } 190 191 // Phase 1 & 2 (max -> min) 192 breakIndex = size - phase0Size; 193 float incr = -range / ((float) size * (1.0f - dutyCycle)); 194 for (; index < breakIndex; ++index) { 195 buffer[index] = val; 196 val += incr; 197 } 198 199 // Phase 3 (min -> 0) 200 if (phase0Size != 0) { 201 float phase0Incr = maxValue / (float) phase0Size; 202 for (; index < size; ++index) { 203 buffer[index] = val; 204 val += phase0Incr; 205 } 206 } 207 208 buffer[size] = buffer[0]; 209 } 210 } 211