• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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