AudioTrack.java revision a0dfa2cdd8c84db4732c9ac8344c5e8d1480bc1f
1/*
2 * Copyright (C) 2010 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
17package android.media.videoeditor;
18
19import java.io.IOException;
20import java.lang.ref.SoftReference;
21
22/**
23 * This class allows to handle an audio track. This audio file is mixed with the
24 * audio samples of the media items.
25 * {@hide}
26 */
27public class AudioTrack {
28    // Instance variables
29    private final String mUniqueId;
30    private final String mFilename;
31    private long mStartTimeMs;
32    private long mTimelineDurationMs;
33    private int mVolumePercent;
34    private long mBeginBoundaryTimeMs;
35    private long mEndBoundaryTimeMs;
36    private boolean mLoop;
37    private boolean mMuted;
38
39    private final long mDurationMs;
40    private final int mAudioChannels;
41    private final int mAudioType;
42    private final int mAudioBitrate;
43    private final int mAudioSamplingFrequency;
44
45    // Ducking variables
46    private int mDuckingThreshold;
47    private int mDuckedTrackVolume;
48    private boolean mIsDuckingEnabled;
49
50    // The audio waveform filename
51    private String mAudioWaveformFilename;
52    // The audio waveform data
53    private SoftReference<WaveformData> mWaveformData;
54
55    /**
56     * An object of this type cannot be instantiated by using the default
57     * constructor
58     */
59    @SuppressWarnings("unused")
60    private AudioTrack() throws IOException {
61        this(null, null, null);
62    }
63
64    /**
65     * Constructor
66     *
67     * @param editor The video editor reference
68     * @param audioTrackId The audio track id
69     * @param filename The absolute file name
70     *
71     * @throws IOException if file is not found
72     * @throws IllegalArgumentException if file format is not supported or if
73     *             the codec is not supported
74     */
75    public AudioTrack(VideoEditor editor, String audioTrackId, String filename)
76            throws IOException {
77        mUniqueId = audioTrackId;
78        mFilename = filename;
79        mStartTimeMs = 0;
80        // TODO: This value represents to the duration of the audio file
81        mDurationMs = 300000;
82        // TODO: This value needs to be read from the audio track of the source
83        // file
84        mAudioChannels = 2;
85        mAudioType = MediaProperties.ACODEC_AAC_LC;
86        mAudioBitrate = 128000;
87        mAudioSamplingFrequency = 44100;
88
89        mTimelineDurationMs = mDurationMs;
90        mVolumePercent = 100;
91
92        // Play the entire audio track
93        mBeginBoundaryTimeMs = 0;
94        mEndBoundaryTimeMs = mDurationMs;
95
96        // By default loop is disabled
97        mLoop = false;
98
99        // By default the audio track is not muted
100        mMuted = false;
101
102        // Ducking is enabled by default
103        mDuckingThreshold = 0;
104        mDuckedTrackVolume = 0;
105        mIsDuckingEnabled = false;
106
107        // The audio waveform file is generated later
108        mAudioWaveformFilename = null;
109        mWaveformData = null;
110    }
111
112    /**
113     * Constructor
114     *
115     * @param editor The video editor reference
116     * @param audioTrackId The audio track id
117     * @param filename The audio filename
118     * @param startTimeMs the start time in milliseconds (relative to the
119     *              timeline)
120     * @param beginMs start time in the audio track in milliseconds (relative to
121     *            the beginning of the audio track)
122     * @param endMs end time in the audio track in milliseconds (relative to the
123     *            beginning of the audio track)
124     * @param loop true to loop the audio track
125     * @param volume The volume in percentage
126     * @param muted true if the audio track is muted
127     * @param threshold Ducking will be activated when the relative energy in
128     *      the media items audio signal goes above this value. The valid
129     *      range of values is 0 to 100.
130     * @param duckedTrackVolume The relative volume of the audio track when ducking
131     *      is active. The valid range of values is 0 to 100.
132     * @param audioWaveformFilename The name of the waveform file
133     *
134     * @throws IOException if file is not found
135     */
136    AudioTrack(VideoEditor editor, String audioTrackId, String filename, long startTimeMs,
137            long beginMs, long endMs, boolean loop, int volume, boolean muted,
138            boolean duckingEnabled, int duckThreshold, int duckedTrackVolume,
139            String audioWaveformFilename) throws IOException {
140        mUniqueId = audioTrackId;
141        mFilename = filename;
142        mStartTimeMs = startTimeMs;
143
144        // TODO: This value represents to the duration of the audio file
145        mDurationMs = 300000;
146
147        // TODO: This value needs to be read from the audio track of the source
148        // file
149        mAudioChannels = 2;
150        mAudioType = MediaProperties.ACODEC_AAC_LC;
151        mAudioBitrate = 128000;
152        mAudioSamplingFrequency = 44100;
153
154        mTimelineDurationMs = endMs - beginMs;
155        mVolumePercent = volume;
156
157        mBeginBoundaryTimeMs = beginMs;
158        mEndBoundaryTimeMs = endMs;
159
160        mLoop = loop;
161        mMuted = muted;
162
163        mIsDuckingEnabled = duckingEnabled;
164        mDuckingThreshold = duckThreshold;
165        mDuckedTrackVolume = duckedTrackVolume;
166
167        mAudioWaveformFilename = audioWaveformFilename;
168        if (audioWaveformFilename != null) {
169            mWaveformData =
170                new SoftReference<WaveformData>(new WaveformData(audioWaveformFilename));
171        } else {
172            mWaveformData = null;
173        }
174    }
175
176    /**
177     * @return The id of the audio track
178     */
179    public String getId() {
180        return mUniqueId;
181    }
182
183    /**
184     * Get the filename source for this audio track.
185     *
186     * @return The filename as an absolute file name
187     */
188    public String getFilename() {
189        return mFilename;
190    }
191
192    /**
193     * @return The number of audio channels in the source of this audio track
194     */
195    public int getAudioChannels() {
196        return mAudioChannels;
197    }
198
199    /**
200     * @return The audio codec of the source of this audio track
201     */
202    public int getAudioType() {
203        return mAudioType;
204    }
205
206    /**
207     * @return The audio sample frequency of the audio track
208     */
209    public int getAudioSamplingFrequency() {
210        return mAudioSamplingFrequency;
211    }
212
213    /**
214     * @return The audio bitrate of the audio track
215     */
216    public int getAudioBitrate() {
217        return mAudioBitrate;
218    }
219
220    /**
221     * Set the volume of this audio track as percentage of the volume in the
222     * original audio source file.
223     *
224     * @param volumePercent Percentage of the volume to apply. If it is set to
225     *            0, then volume becomes mute. It it is set to 100, then volume
226     *            is same as original volume. It it is set to 200, then volume
227     *            is doubled (provided that volume amplification is supported)
228     *
229     * @throws UnsupportedOperationException if volume amplification is
230     *             requested and is not supported.
231     */
232    public void setVolume(int volumePercent) {
233        mVolumePercent = volumePercent;
234    }
235
236    /**
237     * Get the volume of the audio track as percentage of the volume in the
238     * original audio source file.
239     *
240     * @return The volume in percentage
241     */
242    public int getVolume() {
243        return mVolumePercent;
244    }
245
246    /**
247     * @param muted true to mute the audio track
248     */
249    public void setMute(boolean muted) {
250        mMuted = muted;
251    }
252
253    /**
254     * @return true if the audio track is muted
255     */
256    public boolean isMuted() {
257        return mMuted;
258    }
259
260    /**
261     * Set the start time of this audio track relative to the storyboard
262     * timeline. Default value is 0.
263     *
264     * @param startTimeMs the start time in milliseconds
265     */
266    public void setStartTime(long startTimeMs) {
267        mStartTimeMs = startTimeMs;
268    }
269
270    /**
271     * Get the start time of this audio track relative to the storyboard
272     * timeline.
273     *
274     * @return The start time in milliseconds
275     */
276    public long getStartTime() {
277        return mStartTimeMs;
278    }
279
280    /**
281     * @return The duration in milliseconds. This value represents the audio
282     *         track duration (not looped)
283     */
284    public long getDuration() {
285        return mDurationMs;
286    }
287
288    /**
289     * @return The timeline duration as defined by the begin and end boundaries
290     */
291    public long getTimelineDuration() {
292        return mTimelineDurationMs;
293    }
294
295    /**
296     * Sets the start and end marks for trimming an audio track
297     *
298     * @param beginMs start time in the audio track in milliseconds (relative to
299     *            the beginning of the audio track)
300     * @param endMs end time in the audio track in milliseconds (relative to the
301     *            beginning of the audio track)
302     */
303    public void setExtractBoundaries(long beginMs, long endMs) {
304        if (beginMs > mDurationMs) {
305            throw new IllegalArgumentException("Invalid start time");
306        }
307        if (endMs > mDurationMs) {
308            throw new IllegalArgumentException("Invalid end time");
309        }
310
311        mBeginBoundaryTimeMs = beginMs;
312        mEndBoundaryTimeMs = endMs;
313
314        mTimelineDurationMs = mEndBoundaryTimeMs - mBeginBoundaryTimeMs;
315    }
316
317    /**
318     * @return The boundary begin time
319     */
320    public long getBoundaryBeginTime() {
321        return mBeginBoundaryTimeMs;
322    }
323
324    /**
325     * @return The boundary end time
326     */
327    public long getBoundaryEndTime() {
328        return mEndBoundaryTimeMs;
329    }
330
331    /**
332     * Enable the loop mode for this audio track. Note that only one of the
333     * audio tracks in the timeline can have the loop mode enabled. When looping
334     * is enabled the samples between mBeginBoundaryTimeMs and
335     * mEndBoundaryTimeMs are looped.
336     */
337    public void enableLoop() {
338        mLoop = true;
339    }
340
341    /**
342     * Disable the loop mode
343     */
344    public void disableLoop() {
345        mLoop = false;
346    }
347
348    /**
349     * @return true if looping is enabled
350     */
351    public boolean isLooping() {
352        return mLoop;
353    }
354
355    /**
356     * Disable the audio duck effect
357     */
358    public void disableDucking() {
359        mIsDuckingEnabled = false;
360    }
361
362    /**
363     * Enable ducking by specifying the required parameters
364     *
365     * @param threshold Ducking will be activated when the energy in
366     *      the media items audio signal goes above this value. The valid
367     *      range of values is 0db to 90dB. 0dB is equivalent to disabling
368     *      ducking.
369     * @param duckedTrackVolume The relative volume of the audio track when ducking
370     *      is active. The valid range of values is 0 to 100.
371     */
372    public void enableDucking(int threshold, int duckedTrackVolume) {
373        if (threshold < 0 || threshold > 90) {
374            throw new IllegalArgumentException("Invalid threshold value: " + threshold);
375        }
376
377        if (duckedTrackVolume < 0 || duckedTrackVolume > 100) {
378            throw new IllegalArgumentException("Invalid duckedTrackVolume value: "
379                    + duckedTrackVolume);
380        }
381
382        mDuckingThreshold = threshold;
383        mDuckedTrackVolume = duckedTrackVolume;
384        mIsDuckingEnabled = true;
385    }
386
387    /**
388     * @return true if ducking is enabled
389     */
390    public boolean isDuckingEnabled() {
391        return mIsDuckingEnabled;
392    }
393
394    /**
395     * @return The ducking threshold
396     */
397    public int getDuckingThreshhold() {
398        return mDuckingThreshold;
399    }
400
401    /**
402     * @return The ducked track volume
403     */
404    public int getDuckedTrackVolume() {
405        return mDuckedTrackVolume;
406    }
407
408    /**
409     * This API allows to generate a file containing the sample volume levels of
410     * this audio track object. This function may take significant time and is
411     * blocking. The filename can be retrieved using getAudioWaveformFilename().
412     *
413     * @param listener The progress listener
414     *
415     * @throws IOException if the output file cannot be created
416     * @throws IllegalArgumentException if the audio file does not have a valid
417     *             audio track
418     */
419    public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)
420            throws IOException {
421        // TODO: Set mAudioWaveformFilename at the end once the extract is
422        // complete
423        mWaveformData = new SoftReference<WaveformData>(new WaveformData(mAudioWaveformFilename));
424    }
425
426    /**
427     * Get the audio waveform file name if extractAudioWaveform was successful.
428     * The file format is as following:
429     * <ul>
430     * <li>first 4 bytes provide the number of samples for each value, as
431     * big-endian signed</li>
432     * <li>4 following bytes is the total number of values in the file, as
433     * big-endian signed</li>
434     * <li>then, all values follow as bytes</li>
435     * </ul>
436     *
437     * @return the name of the file, null if the file does not exist
438     */
439    String getAudioWaveformFilename() {
440        return mAudioWaveformFilename;
441    }
442
443    /**
444     * @return The waveform data
445     */
446    public WaveformData getWaveformData() throws IOException {
447        if (mWaveformData == null) {
448            return null;
449        }
450
451        WaveformData waveformData = mWaveformData.get();
452        if (waveformData != null) {
453            return waveformData;
454        } else if (mAudioWaveformFilename != null) {
455            waveformData = new WaveformData(mAudioWaveformFilename);
456            mWaveformData = new SoftReference<WaveformData>(waveformData);
457            return waveformData;
458        } else {
459            return null;
460        }
461    }
462
463    /*
464     * {@inheritDoc}
465     */
466    @Override
467    public boolean equals(Object object) {
468        if (!(object instanceof AudioTrack)) {
469            return false;
470        }
471        return mUniqueId.equals(((AudioTrack)object).mUniqueId);
472    }
473
474    /*
475     * {@inheritDoc}
476     */
477    @Override
478    public int hashCode() {
479        return mUniqueId.hashCode();
480    }
481}
482