AudioChunkList.h (5194B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #ifndef DOM_MEDIA_DRIFTCONTROL_AUDIOCHUNKLIST_H_ 7 #define DOM_MEDIA_DRIFTCONTROL_AUDIOCHUNKLIST_H_ 8 9 #include "AudioSegment.h" 10 #include "TimeUnits.h" 11 12 namespace mozilla { 13 14 /** 15 * AudioChunkList provides a way to have preallocated audio buffers in 16 * AudioSegment. The idea is that the amount of AudioChunks is created in 17 * advance. Each AudioChunk is able to hold a specific amount of audio 18 * (capacity). The total capacity of AudioChunkList is specified by the number 19 * of AudioChunks. The important aspect of the AudioChunkList is that 20 * preallocates everything and reuse the same chunks similar to a ring buffer. 21 * 22 * Why the whole AudioChunk is preallocated and not some raw memory buffer? This 23 * is due to the limitations of MediaTrackGraph. The way that MTG works depends 24 * on `AudioSegment`s to convey the actual audio data. An AudioSegment consists 25 * of AudioChunks. The AudioChunk is built in a way, that owns and allocates the 26 * audio buffers. Thus, since the use of AudioSegment is mandatory if the audio 27 * data was in a different form, the only way to use it from the audio thread 28 * would be to create the AudioChunk there. That would result in a copy 29 * operation (not very important) and most of all an allocation of the audio 30 * buffer in the audio thread. This happens in many places inside MTG it's a bad 31 * practice, though, and it has been avoided due to the AudioChunkList. 32 * 33 * After construction the sample format must be set, when it is available. It 34 * can be set in the audio thread. Before setting the sample format is not 35 * possible to use any method of AudioChunkList. 36 * 37 * Every AudioChunk in the AudioChunkList is preallocated with a capacity of 128 38 * frames of float audio. Nevertheless, the sample format is not available at 39 * that point. Thus if the sample format is set to short, the capacity of each 40 * chunk changes to 256 number of frames, and the total duration becomes twice 41 * big. There are methods to get the chunk capacity and total capacity in frames 42 * and must always be used. 43 * 44 * Two things to note. First, when the channel count changes everything is 45 * recreated which means reallocations. Second, the total capacity might differs 46 * from the requested total capacity for two reasons. First, if the sample 47 * format is set to short and second because the number of chunks in the list 48 * divides exactly the final total capacity. The corresponding method must 49 * always be used to query the total capacity. 50 */ 51 class AudioChunkList { 52 public: 53 /** 54 * Constructor, the final total duration might be different from the requested 55 * `aTotalDuration`. Memory allocation takes place. 56 */ 57 AudioChunkList(uint32_t aTotalDuration, uint32_t aChannels, 58 const PrincipalHandle& aPrincipalHandle); 59 AudioChunkList(const AudioChunkList&) = delete; 60 AudioChunkList(AudioChunkList&&) = delete; 61 ~AudioChunkList() = default; 62 63 /** 64 * Set sample format. It must be done before any other method being used. 65 */ 66 void SetSampleFormat(AudioSampleFormat aFormat); 67 /** 68 * Get the next available AudioChunk. The duration of the chunk will be zero 69 * and the volume 1.0. However, the buffers will be there ready to be written. 70 * Please note, that a reference of the preallocated chunk is returned. Thus 71 * it _must not be consumed_ directly. If the chunk needs to be consumed it 72 * must be copied to a temporary chunk first. For example: 73 * ``` 74 * AudioChunk& chunk = audioChunklist.GetNext(); 75 * // Set up the chunk 76 * AudioChunk tmp = chunk; 77 * audioSegment.AppendAndConsumeChunk(std::move(tmp)); 78 * ``` 79 * This way no memory allocation or copy, takes place. 80 */ 81 AudioChunk& GetNext(); 82 83 /** 84 * Get the capacity of each individual AudioChunk in the list. 85 */ 86 uint32_t ChunkCapacity() const { 87 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_S16 || 88 mSampleFormat == AUDIO_FORMAT_FLOAT32); 89 return mChunkCapacity; 90 } 91 /** 92 * Get the total capacity of AudioChunkList. 93 */ 94 uint32_t TotalCapacity() const { 95 MOZ_ASSERT(mSampleFormat == AUDIO_FORMAT_S16 || 96 mSampleFormat == AUDIO_FORMAT_FLOAT32); 97 return CheckedInt<uint32_t>(mChunkCapacity * mChunks.Length()).value(); 98 } 99 100 /** 101 * Update the channel count of the AudioChunkList. Memory allocation is 102 * taking place. 103 */ 104 void Update(uint32_t aChannels); 105 106 private: 107 void IncrementIndex() { 108 ++mIndex; 109 mIndex = CheckedInt<uint32_t>(mIndex % mChunks.Length()).value(); 110 } 111 void CreateChunks(uint32_t aNumOfChunks, uint32_t aChannels); 112 void UpdateToMonoOrStereo(uint32_t aChannels); 113 114 private: 115 const PrincipalHandle mPrincipalHandle; 116 nsTArray<AudioChunk> mChunks; 117 uint32_t mIndex = 0; 118 uint32_t mChunkCapacity = WEBAUDIO_BLOCK_SIZE; 119 AudioSampleFormat mSampleFormat = AUDIO_FORMAT_SILENCE; 120 }; 121 122 } // namespace mozilla 123 124 #endif // DOM_MEDIA_DRIFTCONTROL_AUDIOCHUNKLIST_H_