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