AudioTrack.java revision 0f76d9b1c2d93a19b436dcbfef9fc46a2712d195
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. If looping is enabled this value
290     *         represents the duration of the looped audio track, otherwise it
291     *         is the duration of the audio track (mDurationMs).
292     */
293    public long getTimelineDuration() {
294        return mTimelineDurationMs;
295    }
296
297    /**
298     * Sets the start and end marks for trimming an audio track
299     *
300     * @param beginMs start time in the audio track in milliseconds (relative to
301     *            the beginning of the audio track)
302     * @param endMs end time in the audio track in milliseconds (relative to the
303     *            beginning of the audio track)
304     */
305    public void setExtractBoundaries(long beginMs, long endMs) {
306        if (beginMs > mDurationMs) {
307            throw new IllegalArgumentException("Invalid start time");
308        }
309        if (endMs > mDurationMs) {
310            throw new IllegalArgumentException("Invalid end time");
311        }
312
313        mBeginBoundaryTimeMs = beginMs;
314        mEndBoundaryTimeMs = endMs;
315        if (mLoop) {
316            // TODO: Compute mDurationMs (from the beginning of the loop until
317            // the end of all the loops.
318            mTimelineDurationMs = mEndBoundaryTimeMs - mBeginBoundaryTimeMs;
319        } else {
320            mTimelineDurationMs = mEndBoundaryTimeMs - mBeginBoundaryTimeMs;
321        }
322    }
323
324    /**
325     * @return The boundary begin time
326     */
327    public long getBoundaryBeginTime() {
328        return mBeginBoundaryTimeMs;
329    }
330
331    /**
332     * @return The boundary end time
333     */
334    public long getBoundaryEndTime() {
335        return mEndBoundaryTimeMs;
336    }
337
338    /**
339     * Enable the loop mode for this audio track. Note that only one of the
340     * audio tracks in the timeline can have the loop mode enabled. When looping
341     * is enabled the samples between mBeginBoundaryTimeMs and
342     * mEndBoundaryTimeMs are looped.
343     */
344    public void enableLoop() {
345        mLoop = true;
346    }
347
348    /**
349     * Disable the loop mode
350     */
351    public void disableLoop() {
352        mLoop = false;
353    }
354
355    /**
356     * @return true if looping is enabled
357     */
358    public boolean isLooping() {
359        return mLoop;
360    }
361
362    /**
363     * Disable the audio duck effect
364     */
365    public void disableDucking() {
366        mIsDuckingEnabled = false;
367    }
368
369    /**
370     * Enable ducking by specifying the required parameters
371     *
372     * @param threshold Ducking will be activated when the energy in
373     *      the media items audio signal goes above this value. The valid
374     *      range of values is 0db to 90dB. 0dB is equivalent to disabling
375     *      ducking.
376     * @param duckedTrackVolume The relative volume of the audio track when ducking
377     *      is active. The valid range of values is 0 to 100.
378     */
379    public void enableDucking(int threshold, int duckedTrackVolume) {
380        if (threshold < 0 || threshold > 90) {
381            throw new IllegalArgumentException("Invalid threshold value: " + threshold);
382        }
383
384        if (duckedTrackVolume < 0 || duckedTrackVolume > 100) {
385            throw new IllegalArgumentException("Invalid duckedTrackVolume value: "
386                    + duckedTrackVolume);
387        }
388
389        mDuckingThreshold = threshold;
390        mDuckedTrackVolume = duckedTrackVolume;
391        mIsDuckingEnabled = true;
392    }
393
394    /**
395     * @return true if ducking is enabled
396     */
397    public boolean isDuckingEnabled() {
398        return mIsDuckingEnabled;
399    }
400
401    /**
402     * @return The ducking threshold
403     */
404    public int getDuckingThreshhold() {
405        return mDuckingThreshold;
406    }
407
408    /**
409     * @return The ducked track volume
410     */
411    public int getDuckedTrackVolume() {
412        return mDuckedTrackVolume;
413    }
414
415    /**
416     * This API allows to generate a file containing the sample volume levels of
417     * this audio track object. This function may take significant time and is
418     * blocking. The filename can be retrieved using getAudioWaveformFilename().
419     *
420     * @param listener The progress listener
421     *
422     * @throws IOException if the output file cannot be created
423     * @throws IllegalArgumentException if the audio file does not have a valid
424     *             audio track
425     */
426    public void extractAudioWaveform(ExtractAudioWaveformProgressListener listener)
427            throws IOException {
428        // TODO: Set mAudioWaveformFilename at the end once the extract is
429        // complete
430        mWaveformData = new SoftReference<WaveformData>(new WaveformData(mAudioWaveformFilename));
431    }
432
433    /**
434     * Get the audio waveform file name if extractAudioWaveform was successful.
435     * The file format is as following:
436     * <ul>
437     * <li>first 4 bytes provide the number of samples for each value, as
438     * big-endian signed</li>
439     * <li>4 following bytes is the total number of values in the file, as
440     * big-endian signed</li>
441     * <li>then, all values follow as bytes</li>
442     * </ul>
443     *
444     * @return the name of the file, null if the file does not exist
445     */
446    String getAudioWaveformFilename() {
447        return mAudioWaveformFilename;
448    }
449
450    /**
451     * @return The waveform data
452     */
453    public WaveformData getWaveformData() {
454        if (mWaveformData == null) {
455            return null;
456        }
457
458        WaveformData waveformData = mWaveformData.get();
459        if (waveformData != null) {
460            return waveformData;
461        } else if (mAudioWaveformFilename != null) {
462            waveformData = new WaveformData(mAudioWaveformFilename);
463            mWaveformData = new SoftReference<WaveformData>(waveformData);
464            return waveformData;
465        } else {
466            return null;
467        }
468    }
469
470    /*
471     * {@inheritDoc}
472     */
473    @Override
474    public boolean equals(Object object) {
475        if (!(object instanceof AudioTrack)) {
476            return false;
477        }
478        return mUniqueId.equals(((AudioTrack)object).mUniqueId);
479    }
480
481    /*
482     * {@inheritDoc}
483     */
484    @Override
485    public int hashCode() {
486        return mUniqueId.hashCode();
487    }
488}
489