AVPKit
AudioSamples.cpp
1 /*******************************************************************************
2  * Copyright (c) 2024, 2026, Olivier Ayache. All rights reserved.
3  *
4  * This file is part of AVPKit.
5  *
6  * AVPKit is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * AVPKit is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with AVPKit. If not, see <http://www.gnu.org/licenses/>.
18  *******************************************************************************/
19 
20 #include <com/avpkit/ferry/Logger.h>
21 #include <com/avpkit/core/AudioSamples.h>
22 #include <com/avpkit/core/Global.h>
23 
24 // for memset
25 #include <com/avpkit/core/FfmpegIncludes.h>
26 #include <cstring>
27 #include <stdexcept>
28 
29 VS_LOG_SETUP(VS_CPP_PACKAGE);
30 
31 namespace com { namespace avpkit { namespace core
32 {
33  using namespace com::avpkit::ferry;
34 
35  AudioSamples :: AudioSamples()
36  {
37  mSamples = 0;
38  mNumSamples = 0;
39  mSampleRate = 0;
40  mChannels = 1;
41  mChannelLayout = IAudioSamples::ChannelLayout::CH_NONE;
42  mIsComplete = false;
43  mSampleFmt = FMT_S16;
44  mPts = Global::NO_PTS;
45  mRequestedSamples = 0;
46  mTimeBase = IRational::make(1, 1000000);
47  }
48 
49  AudioSamples :: ~AudioSamples()
50  {
51  mSamples = 0;
52  mTimeBase = 0;
53  mNumSamples = 0;
54  mSampleRate = 0;
55  mChannels = 1;
56  mIsComplete = false;
57  mSampleFmt = FMT_S16;
58  mPts = Global::NO_PTS;
59  mRequestedSamples = 0;
60  }
61 
62 #define VS_AUDIOSAMPLES_BUFFER_PADDING 64
63  void
64  AudioSamples :: allocInternalSamples()
65  {
66  if (!mSamples)
67  {
68  int32_t bufSize = mRequestedSamples * getSampleSize() +
69  VS_AUDIOSAMPLES_BUFFER_PADDING;
70 
71  mSamples = IBuffer::make(this, bufSize);
72  if (!mSamples)
73  throw std::bad_alloc();
74  setBufferType(mSampleFmt, mSamples.value());
75 
76  if (mSamples)
77  {
78  //void * buf = retval->mSamples->getBytes(0, bufSize);
79  //don't set to zero; this means the memory is uninitialized
80  //which helps find bugs with valgrind
81  //memset(buf, 0, bufSize);
82  mNumSamples = 0;
83  VS_LOG_TRACE("Created AudioSamples(%d bytes)", bufSize);
84  }
85 
86  }
87  }
88 
89  int32_t
91  {
92  if (mSamples &&
93  mSamples->getBufferSize() < (capacity + VS_AUDIOSAMPLES_BUFFER_PADDING))
94  {
95  // crap; need to ditch and re-recreate
96  VS_LOG_WARN("Internal buffer not big enough need to recreate %d",capacity);
97  mSamples=0;
98  }
99  int32_t sampleSize = getSampleSize();
100  int32_t requiredSamples;
101  if (sampleSize > 0)
102  requiredSamples = capacity / getSampleSize();
103  else
104  requiredSamples = 192000;// AVCODEC_MAX_AUDIO_FRAME_SIZE disappear
105  mRequestedSamples = requiredSamples;
106 
107  return 0;
108  }
109  short*
110  AudioSamples :: getRawSamples(uint32_t startingSample)
111  {
112  short *retval = 0;
113  allocInternalSamples();
114  if (mSamples)
115  {
116  uint32_t startingOffset = startingSample*getSampleSize();
117  uint32_t bufLen = (mNumSamples*getSampleSize())-startingOffset;
118  retval = (short*)mSamples->getBytes(startingOffset, bufLen);
119  }
120  return retval;
121  }
122 
123  AudioSamples*
124  AudioSamples :: make(int32_t numSamples,
125  int32_t numChannels)
126  {
127  return make(numSamples, numChannels, IAudioSamples::FMT_S16);
128  }
129 
130  AudioSamples*
131  AudioSamples :: make(int32_t numSamples,
132  int32_t numChannels,
133  IAudioSamples::Format format)
134  {
135  AudioSamples *retval=0;
136  if (numSamples > 0 && numChannels > 0)
137  {
138  retval = AudioSamples::make();
139  if (retval)
140  {
141  // FFMPEG actually requires a minimum buffer size, so we
142  // make sure we're always at least that large.
143  retval->mChannels = numChannels;
144  retval->mSampleFmt = format;
145  retval->mRequestedSamples = numSamples;
146  }
147  }
148  return retval;
149  }
150 
151  AudioSamples*
152  AudioSamples :: make(IBuffer* buffer, int32_t channels,
153  IAudioSamples::Format format)
154  {
155  if (!buffer)
156  return 0;
157  if (format == IAudioSamples::FMT_NONE)
158  return 0;
159  if (channels < 0)
160  return 0;
161  if (buffer->getBufferSize()<= 0)
162  return 0;
163 
164  int bytesPerSample = IAudioSamples::findSampleBitDepth(format)/8*channels;
165  int samplesRequested = buffer->getBufferSize()/bytesPerSample;
166  AudioSamples* retval = 0;
167  try
168  {
169  retval = make(samplesRequested, channels, format);
170  if (!retval)
171  return 0;
172  retval->setData(buffer);
173  }
174  catch (std::bad_alloc &e)
175  {
176  VS_REF_RELEASE(retval);
177  throw e;
178  }
179  catch (std::exception& e)
180  {
181  VS_LOG_DEBUG("error: %s", e.what());
182  VS_REF_RELEASE(retval);
183  }
184 
185  return retval;
186  }
187 
188  void
190  {
191  if (!buffer) return;
192  mSamples.reset(buffer, true);
193  setBufferType(mSampleFmt, buffer);
194  }
195 
196  bool
198  {
199  return mIsComplete;
200  }
201 
204  {
205  return mSampleFmt;
206  }
207 
208  int32_t
210  {
211  return mSampleRate;
212  }
213 
214  int32_t
216  {
217  return mChannels;
218  }
219 
221  AudioSamples::getChannelLayout()
222  {
223  return mChannelLayout;
224  }
225 
226 
227  int32_t
229  {
230  return mNumSamples;
231  }
232 
233  int32_t
235  {
236  allocInternalSamples();
237  return mSamples->getBufferSize()-VS_AUDIOSAMPLES_BUFFER_PADDING;
238  }
239 
240  int32_t
242  {
243  return IAudioSamples::findSampleBitDepth(mSampleFmt);
244  }
245 
246  int32_t
248  {
249  int32_t bits = getSampleBitDepth();
250  if (bits < 8)
251  bits = 8;
252 
253  return bits/8 * getChannels();
254  }
255 
258  {
259  allocInternalSamples();
260  IBuffer* retval = mSamples.get();
261  if (!retval)
262  throw std::bad_alloc();
263  return retval;
264  }
265 
266  int32_t
268  {
269  return getMaxBufferSize() / getSampleSize();
270  }
271 
272  void
273  AudioSamples :: setComplete(bool complete, int32_t numSamples,
274  int32_t sampleRate, int32_t channels, Format format,
275  int64_t pts)
276  {
277  mIsComplete = complete;
278  if (channels <= 0)
279  channels = 1;
280 
281  mChannels = channels;
282  mChannelLayout = (ChannelLayout)av_get_default_channel_layout(mChannels);
283  mSampleRate = sampleRate;
284  mSampleFmt = format;
285  if (mSamples)
286  // if we've allocated a buffer, reset the type
287  setBufferType(mSampleFmt, mSamples.value());
288 
289  if (mIsComplete)
290  {
291  mNumSamples = FFMIN(numSamples,
292  getMaxBufferSize()/(getSampleSize()));
293 #if 0
294  {
295  short* samps = this->getRawSamples(0);
296  for(int32_t i = 0; i < mNumSamples;i++)
297  {
298  int32_t samp = samps[i];
299  VS_LOG_DEBUG("i: %d; samp: %d", i, samp);
300  }
301  }
302 #endif // VS_DEBUG
303  } else {
304  mNumSamples = 0;
305  }
306  setPts(pts);
307  }
308 
309  void
310  AudioSamples::setComplete(bool complete, int32_t numSamples, int32_t sampleRate, int32_t channels, ChannelLayout channelLayout, Format format, int64_t pts)
311  {
312  setComplete(complete, numSamples, sampleRate, channels, format, pts);
313  mChannelLayout = channelLayout;
314  }
315 
316  int64_t
318  {
319  return mPts;
320  }
321 
322  void
323  AudioSamples :: setPts(int64_t aValue)
324  {
325  mPts = aValue;
326  }
327 
328  int64_t
330  {
331  int64_t retval = Global::NO_PTS;
332  if (mPts != Global::NO_PTS)
333  retval = mPts + IAudioSamples::samplesToDefaultPts(this->getNumSamples(), this->getSampleRate());
334 
335  return retval;
336  }
337 
338  int32_t
339  AudioSamples :: setSample(int32_t sampleIndex, int32_t channel, Format format, int32_t sample)
340  {
341  int32_t retval = -1;
342  try {
343  if (channel < 0 || channel >= mChannels)
344  throw std::invalid_argument("cannot setSample for given channel");
345  if (format != FMT_S16)
346  throw std::invalid_argument("only support format: FMT_S16");
347  if (sampleIndex >= this->getMaxSamples())
348  throw std::invalid_argument("sampleIndex out of bounds");
349 
350  short *rawSamples = this->getRawSamples(0);
351  if (!rawSamples)
352  throw std::runtime_error("no samples buffer set in AudioSamples");
353 
354  rawSamples[sampleIndex*mChannels + channel] = (short)sample;
355  retval = 0;
356  }
357  catch (std::exception & e)
358  {
359  VS_LOG_DEBUG("Error: %s", e.what());
360  retval = -1;
361  }
362  return retval;
363  }
364 
365  int32_t
366  AudioSamples :: getSample(int32_t sampleIndex, int32_t channel, Format format)
367  {
368  int32_t retval = 0;
369  try
370  {
371  if (channel < 0 || channel >= mChannels)
372  throw std::invalid_argument("cannot getSample for given channel");
373  if (format != FMT_S16)
374  throw std::invalid_argument("only support format: FMT_S16");
375  if (sampleIndex >= this->getNumSamples())
376  throw std::invalid_argument("sampleIndex out of bounds");
377 
378  short *rawSamples = this->getRawSamples(0);
379  if (!rawSamples)
380  throw std::runtime_error("no samples buffer set in AudioSamples");
381 
382  retval = rawSamples[sampleIndex*mChannels + channel];
383  }
384  catch(std::exception & e)
385  {
386  VS_LOG_DEBUG("Error: %s", e.what());
387  retval = 0;
388  }
389  return retval;
390  }
391 
392  void
393  AudioSamples :: setBufferType(IAudioSamples::Format format,
394  IBuffer* buffer)
395  {
396  if (!buffer)
397  return;
398  switch(format)
399  {
400  case FMT_DBL:
401  case FMT_DBLP:
402  buffer->setType(IBuffer::IBUFFER_DBL64);
403  break;
404  case FMT_FLT:
405  case FMT_FLTP:
406  buffer->setType(IBuffer::IBUFFER_FLT32);
407  break;
408  case FMT_S16:
409  case FMT_S16P:
410  buffer->setType(IBuffer::IBUFFER_SINT16);
411  break;
412  case FMT_S32:
413  case FMT_S32P:
414  buffer->setType(IBuffer::IBUFFER_SINT32);
415  break;
416  case FMT_U8:
417  case FMT_U8P:
418  buffer->setType(IBuffer::IBUFFER_UINT8);
419  break;
420  default:
421  break;
422  }
423  }
424 }}}
virtual int32_t getSampleRate()
Find the sample rate of the samples in this audio buffer.
virtual Format getFormat()
Find the Format of the samples in this buffer.
virtual int32_t getChannels()
Return the number of channels of the samples in this buffer.
virtual com::avpkit::ferry::IBuffer * getData()
Get any underlying raw data available for this object.
virtual int32_t ensureCapacity(int32_t capacityInBytes)
Called by decoder before decoding to ensure sufficient space.
virtual void setComplete(bool complete, int32_t numSamples, int32_t sampleRate, int32_t channels, Format sampleFmt, int64_t pts)
Call this if you modify the samples and are now done.
virtual int64_t getPts()
What is the Presentation Time Stamp of this set of audio samples.
virtual int32_t getMaxBufferSize()
virtual void setPts(int64_t aValue)
Set the Presentation Time Stamp for this set of samples.
virtual int32_t setSample(int32_t sampleIndex, int32_t channel, Format format, int32_t sample)
Sets the sample at the given index and channel to the sample.
virtual int32_t getSample(int32_t sampleIndex, int32_t channel, Format format)
Get the sample at the given sampleIndex and channel, and return it in the asked for format.
virtual int32_t getNumSamples()
Get the number of samples in this video.
virtual int32_t getSampleBitDepth()
Find out the bit-depth of the samples in this buffer.
virtual void setData(com::avpkit::ferry::IBuffer *buffer)
Sets the underlying buffer used by this object.
virtual bool isComplete()
Returns whether or not we think this buffer has been filled with data.
virtual int64_t getNextPts()
What would be the next Presentation Time Stamp after all the samples in this buffer were played?
static const int64_t NO_PTS
A value that means no time stamp is set for a given object.
Definition: Global.h:50
static int32_t findSampleBitDepth(Format format)
A convenience method that returns the # of bits in a given format.
Format
The format we use to represent audio.
Definition: IAudioSamples.h:46
static int64_t samplesToDefaultPts(int64_t samples, int sampleRate)
Converts a number of samples at a given sampleRate into Microseconds.
static IRational * make()
Get a new rational that will be set to 0/0.
Definition: IRational.cpp:79
Allows Java code to get data from a native buffers, and optionally modify native memory directly.
Definition: IBuffer.h:54
virtual void setType(Type type)=0
Reset the buffer type to a new type.
static IBuffer * make(RefCounted *requestor, void *bufToWrap, int32_t bufferSize, FreeFunc freeFunc, void *closure)
Allocate a new buffer by wrapping a native buffer.
Definition: IBuffer.cpp:48
virtual int32_t getBufferSize()=0
Get the current maximum number of bytes that can be safely placed in this buffer.
This library contains routines used by AVPKit libraries for "ferry"ing Java objects to and from nativ...
WARNING: Do not use logging in this class, and do not set any static file variables to values other t...