001/******************************************************************************* 002 * Copyright (c) 2024, 2026, Olivier Ayache. All rights reserved. 003 * 004 * This file is part of AVPKit. 005 * 006 * AVPKit is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU Lesser General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * AVPKit is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with AVPKit. If not, see <http://www.gnu.org/licenses/>. 018 *******************************************************************************/ 019 020package com.avpkit.core; 021 022import java.nio.ByteBuffer; 023 024import org.slf4j.Logger; 025import org.slf4j.LoggerFactory; 026 027 028import com.avpkit.ferry.IBuffer; 029import com.avpkit.core.IAudioSamples; 030import com.avpkit.core.ITimeValue; 031import com.avpkit.core.Utils; 032 033/** 034 * This class generates fake audio data that is an A-note (a sine-wave at 440hz). 035 * 036 * @author aclarke 037 * 038 */ 039public class TestAudioSamplesGenerator 040{ 041 private final Logger log = LoggerFactory.getLogger(this.getClass()); 042 043 private int mNumChannels = 0; 044 private int mSampleRate = 0; 045 private int mDesiredNoteFrequency = 440; // default to the note: 'A' 046 047 private long mSamplesGenerated = 0; 048 049 private long mStartingPts = 0; 050 051 public TestAudioSamplesGenerator() 052 { 053 log.trace("firing up"); 054 } 055 public void fillNextSamples(IAudioSamples samples, long samplesRequested) 056 { 057 long samplesToGen = Math.min(samples.getMaxSamples(), samplesRequested); 058 059 if (samples.getChannels() != mNumChannels) 060 { 061 throw new IllegalArgumentException("unmatched channels "); 062 } 063 064 if (samplesToGen <= 0) 065 { 066 throw new IllegalArgumentException("incorrect number of samples to generate"); 067 } 068 069 // we're going to access the raw data.. 070 IBuffer buf = samples.getData(); 071 if (buf == null) 072 throw new RuntimeException("could not get IBuffer from samples"); 073 074 ByteBuffer data = buf.getByteBuffer(0, buf.getBufferSize()); 075 if (data == null) 076 throw new RuntimeException("could not get raw data from samples"); 077 // clear out all the old data 078 data.clear(); 079 080 long startingSample = mSamplesGenerated; 081 long startingPts = Utils.samplesToTimeValue(mSamplesGenerated, mSampleRate).get(ITimeValue.Unit.MICROSECONDS) 082 + mStartingPts; 083 084 for(long i = 0; i < samplesToGen; i++) 085 { 086 // figure out where I am in time.... 087 double time = (double)mSamplesGenerated / (double) mSampleRate; 088 089 double croppedTime = time % ((double)1/(double)mDesiredNoteFrequency); 090 091 // where intra the desired sample frequency we are 092 double percentOfRange = croppedTime / ((double)1 /(double) mDesiredNoteFrequency); 093 094 double cycleValue = percentOfRange*Math.PI*2; 095 096 double samp = Math.sin(cycleValue); 097 098 // keep the amplitude low; at 50% 099 short aNoteSample = (short) ((samp * 256*256*.5)); 100 101 /* 102 log.trace("generated: {}, {}, {}, {}, {}, {}, {}", 103 new Object[]{ 104 mSamplesGenerated, 105 aNoteSample, 106 samp, 107 time, 108 croppedTime, 109 percentOfRange, 110 cycleValue 111 }); 112 */ 113 114 for (int j = 0; j < mNumChannels; j++) 115 { 116 /** 117 * We don't ByteBuffer.putShort here because 118 * we seem to need to flip the bytes into a 119 * low byte first format. 120 */ 121 data.put((byte)(aNoteSample&0xFF)); 122 data.put((byte)((aNoteSample>>8)&0xFF)); 123 } 124 ++mSamplesGenerated; 125 } 126 data.flip(); 127 128 log.trace("completed: {}, {}, {}, {}, {}, {}", 129 new Object[]{ 130 samplesToGen, 131 mSampleRate, 132 mNumChannels, 133 startingPts, 134 startingSample, 135 IAudioSamples.Format.FMT_S16 136 }); 137 138 samples.setComplete(true, (int)samplesToGen, mSampleRate, mNumChannels, 139 IAudioSamples.Format.FMT_S16, 140 startingPts); 141 } 142 143 public void prepare(int numChannels, int sampleRate) 144 { 145 prepare(numChannels, sampleRate, 0); 146 } 147 148 public void prepare(int numChannels, int sampleRate, long startingPts) 149 { 150 mNumChannels = numChannels; 151 mSampleRate = sampleRate; 152 mStartingPts = startingPts; 153 log.debug("prepare - channels: {}, sampleRate: {}", numChannels, sampleRate); 154 } 155 156 public void setDesiredNoteFrequency(int note) 157 { 158 mDesiredNoteFrequency = note; 159 } 160 161 public int getDesiredNoteFrequency() 162 { 163 return mDesiredNoteFrequency; 164 } 165}