I have been scratching my head about this all day and have run out of ideas now, so I am posting this here
I am trying to implement a feedback suppressor in Android using the NLMS algorithm
I use an AudioRecord to obtain audio samples from the MIC and AudioTrack to play it back. I read in 1 sample at a time as a short variable, convert it to double, pass it through my algorithm, convert it back to short and send it to the speaker. However, I am getting weird values in the intermediate variable and canot understand why it is happening. Here is the code:
public class MainActivity extends Activity {
AudioManager am = null;
AudioRecord record =null;
AudioTrack track =null;
final int SAMPLE_FREQUENCY = 16000; // ORIGINAL 44100
final int SIZE_OF_RECORD_ARRAY = 1; // 1024 ORIGINAL; 1000 / 40 = 25
// final int WAV_SAMPLE_MULTIPLICATION_FACTOR = 1;
final double WAV_SAMPLE_MULTIPLICATION_FACTOR = 0.5;
final int N = 4; // ORIGINAL 40
final int FEEDBACK_DELAY_IN_MSEC = 1; // use small integer values here to keep the calculation of NO_OF_DELAY_SAMPLES from becoming non-whole number
// final int NO_OF_DELAY_SAMPLES = SAMPLE_FREQUENCY / (FEEDBACK_DELAY_IN_MSEC * 1000); // NO_OF_DELAY_SAMPLES = 16
final int NO_OF_DELAY_SAMPLES = 0;
final int TOTAL_SIZE_OF_X = N + NO_OF_DELAY_SAMPLES;
int i = 0, n = 0; // n represents nth sample
boolean isPlaying = false; // represents if the Pass Through button is pressed
boolean applyDsp = false; // represents if the Apply Filter button is pressed
boolean bufferFull = false;
private volatile boolean keepThreadRunning;
double[] w = new double[N]; // w represents filter coefficients
double[] x = new double[TOTAL_SIZE_OF_X];
double e;
double d;
double send_out;
double mu;
double y = 0;
// /*
private RandomAccessFile stateFile;
String stateFileLoc = Environment.getExternalStorageDirectory().getPath();
FileDescriptor fd;
// */
class MyThread extends Thread{
private volatile boolean needsToPassThrough;
// /*
MyThread(){
super();
}
MyThread(boolean newPTV){
this.needsToPassThrough = newPTV;
}
// */
// /*
#Override
public void run(){
short[] lin = new short[SIZE_OF_RECORD_ARRAY];
short[] speaker = new short[SIZE_OF_RECORD_ARRAY];
double speaker_double;
int num = 0;
Log.d("MYLOG", "ENTERED RUN");
if(needsToPassThrough){
record.startRecording();
track.play();
Log.d("MYLOG", "COMES HERE BEFORE BTN PRESS?");
}
n = TOTAL_SIZE_OF_X -1;
while (keepThreadRunning) { // thread runs until this loop stops; this loop runs as long as the program is running
num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY);
for(i=0;i<lin.length;i++)
d = (double)lin[i]; // this line requires that lin[] has to be a single element array
if(isPlaying){
if(applyDsp){
y=0.0; // initialize every time
for(i=0; i<N; i++){
y += w[N-1-i] * x[n-i - NO_OF_DELAY_SAMPLES];
}
// Implementing step 2
e = d - y;
// /*
try {
stateFile.writeDouble(e);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// Implementing step 3
mu = 0.5 / (x[n] * x[n] + 0.01);
// Implementing step 4
for(i=0; i<N; i++){
w[N-1-i] = w[N-1-i] + 2.0*mu*e*x[n-i - NO_OF_DELAY_SAMPLES];
}
} // closing of if(applyDsp) block
// Implementing step 5
for(i=0;i<TOTAL_SIZE_OF_X-1;i++){
x[i] = x[i+1];
}
send_out = e;
speaker_double = send_out * WAV_SAMPLE_MULTIPLICATION_FACTOR;
// implementing step 6
x[TOTAL_SIZE_OF_X -1] = speaker_double;
for(i=0;i<speaker.length; i++)
speaker[i] = (short)speaker_double;
track.write(speaker, 0, num);
} // if(isPlaying) block closed; this represents if the "Pass Through" button has been clicked
} // while (keepThreadRunning) closes here; this infinite loop runs as long as the program is running
record.stop();
track.stop();
record.release();
track.release();
}
public void stopThread(){
keepThreadRunning = false;
}
} // End of MyThread class
MyThread newThread;
I have just included that part of the code that contains the Thread which performs the NLMS to keep it short. Since SIZE_OF_RECORD_ARRAY is 1, the lin variable in num = record.read(lin, 0, SIZE_OF_RECORD_ARRAY); will always be a short array with just one element. (in fact, anu double or short array that may be encountered in the above code will just have a single element). the line e = d - y is key here. d is the value I get from the mic (d is short typecasted to double). During every run of the while (keepThreadRunning) { infinite loop, a new value for e should be calculated, which is later typecasted to short and passed to the AudioTrack. The problem is that e is not getting proper values in my case, and there is no output from the speakers. If I send the variable d to the AudioTrack, whatever input is spoken in to the microphone appears at the output (with a slight delay, but that is expected). If I try to write the e variable values to file, I am getting strange values. Since using String.valueOf(e) did not work out for me (each string character is considered as a 16-bit character and so 0 appears as <space>0, -1 appears as <space>-<space>1), I directly wrote the double value to file using RandomAccessFile.write Double and viewed the file in a hex viewer. It seems that there is some random value at the beginning of the file, after which a pattern emerges, which I am not sure why it is there. A screenshot of the hex file is shown below:
The pattern shown continues till the end of file. Why is this happening? Since there was no output I assumed at least everything should have been 0, but as can be seen from this figure it's not 0 either, instead it is 7F F8 00 00 00 00 00 00 repeated again and again. Please help me determine why this is happening.
PS: The boolean values isPlaying and applyDsp represent the state of two buttons that I have on the interface. When I press the applyDsp button on the interface, the speaker makes a very short and relatively loud "pop" sound, which I assume may be the reason for the random values in the beginning of the file containing the values of e shown above, but I am not sure about this, and I am not sure abput why that popping noise is there in the first place.
Related
i want to implement an Entropy function in parallel with APARAPI.
in that function i need to count different keys in a vector but it cant execute correctly.
assume that we have just 3 different values.
here is my codes:
final int[] V = new int[1024];
// Initialization for V values
final int[] count = new int[3];
Kernel kernel = new Kernel(){
#Override
public void run(){
int gid = getGlobalId();
count[V[gid]]++;
}
};
kernel.execute(Range.create(V.length));
kernel.dispose();
after run this code segment, when i print count[] values it gives me 1,1,1.
it seems that count[V[gid]]++ execute just 1 time for each V[gid].
thanks.
So here is the problem. The ++ operator is actually three operations in one: read the current value, increment it, write the new value. In Aparapi you have potentially 1024 GPU threads running simultaneously. That means they will read the value, probably all the same time when the value is 0, then increment it to 1, then all 1024 threads will write 1. So it is acting as expected.
What you are trying to do is called a Map-reduce function. You are just skipping a lot of steps. You need to remember Aparapi is a system that has no Thread safety, so you have to write your algorithms to accommodate that. That is where Map-reduce comes in and here is how to do one. I just wrote it and added it to the Aparapi repository at its new home, details below.
int size = 1024;
final int count = 3;
final int[] V = new int[size];
//lets fill in V randomly...
for (int i = 0; i < size; i++) {
//random number either 0, 1, or 2
V[i] = (int) (Math.random() * 3);
}
//this will hold our values between the phases.
int[][] totals = new int[count][size];
///////////////
// MAP PHASE //
///////////////
final int[][] kernelTotals = totals;
Kernel mapKernel = new Kernel() {
#Override
public void run() {
int gid = getGlobalId();
int value = V[gid];
for(int index = 0; index < count; index++) {
if (value == index)
kernelTotals[index][gid] = 1;
}
}
};
mapKernel.execute(Range.create(size));
mapKernel.dispose();
totals = kernelTotals;
//////////////////
// REDUCE PHASE //
//////////////////
while (size > 1) {
int nextSize = size / 2;
final int[][] currentTotals = totals;
final int[][] nextTotals = new int[count][nextSize];
Kernel reduceKernel = new Kernel() {
#Override
public void run() {
int gid = getGlobalId();
for(int index = 0; index < count; index++) {
nextTotals[index][gid] = currentTotals[index][gid * 2] + currentTotals[index][gid * 2 + 1];
}
}
};
reduceKernel.execute(Range.create(nextSize));
reduceKernel.dispose();
totals = nextTotals;
size = nextSize;
}
assert size == 1;
/////////////////////////////
// Done, just print it out //
/////////////////////////////
int[] results = new int[3];
results[0] = totals[0][0];
results[1] = totals[1][0];
results[2] = totals[2][0];
System.out.println(Arrays.toString(results));
Keep in mind while it may seem inefficient it actually works pretty well on much larger number. This algorithm works just fine with
size = 1048576.
With the new size the following result was computed on my system in about a second.
[349602, 349698, 349276]
One final note, you might want to consider moving to the more active project at aparapi.com. It includes several fixes to bugs and a lot of extra features and performance enhancements over the older library you linked above. It is also in maven central with about a dozen releases. so it is easier to use. I just wrote the code in this answer but decided to use it in the new Aparapi repository's example section, you can find that at the following link in the new Aparapi repository.
I have converted a String value that consists solely of numbers into a Long value. Now I want to split this Long value every n digits, and add those "sub-Longs" into a List.
Long myLong = Long.valueOf(str).longValue();
I want to split myLong at every nth digits, and add those sections into a List<Long>.
How would I go about doing this?
Would have to use String.substring(...) to make this work. That and a for loop. I'm going to edit in the rest of the code. Edited in. Explanatory comments added as well.
public static void main(String[] args) {
// Just for testing.
StringGrouper grouper = new StringGrouper();
// Have tested for 2, 3, 4, and 5
List<Long> output = grouper.makeList(12892374897L, 4);
// Sends out the output. Note that this calls the List's toString method.
System.out.println(output);
}
/**
* #param l
* #param spacing
*/
private List<Long> makeList(long l, int spacing) {
List<Long> output = new ArrayList<>(Math.round(l / spacing));
String longStr = String.valueOf(l);
// System.err.println(longStr);
for (int x = 0; x < longStr.length(); x++) {
// When the remainder is 0, that's where the grouping starts and ends
if (x % spacing == 0) {
// If it does not overflow, send it in
if ((x + spacing) < longStr.length()) {
String element = longStr.substring(x, x + spacing);
// System.err.println(element);
output.add(Long.parseLong(element));
} else {
// If it does overflow, put in the remainder
output.add(Long.parseLong(longStr.substring(x, longStr.length())));
}
}
}
return output;
}
Here is the question I am trying to answer
Construct a new sorting algorithm which uses ONLY three stacks, labelled A, B, and C, a
single “double” variable called x, and any auxiliary variables such as loop counters. Your
algorithm assumes that stack A contains a collection of UNSORTED data, and by the end
of your algorithm, one of the stacks will contain the data sorted in increasing order.
I am trying to figure out the algorithm for it in Java, but I can't figure it out for the life of me! Can you help?!
If there's a bonus for doing a faster sort, with 3 stacks you can implement a bottom up merge sort O(n log(n)). As pointed out by greybeard, a poly phase bottom up merge sort (a method oriented towards tape drives or other sequential devices), should be the fastest 3 stack sort.
A simpler merge sort would move every run (initial size == 1) from A to B and C, in an alternating pattern, even runs to B, odd runs to C, then 2 way merge B and C back to A, double run size, repeat until run size >= stack size. Poly phase eliminates the move / split steps, except for an initial distribute step that moves some of the elements from A to B and C.
Setting up the initial descending / ascending state (reverses the sense of a compare), and tracking when the run size on a stack changes (+1 or -1) due to dummy elements was a bit tricky. I used a table of 47 Fibonacci integers for initial distribution setup (handles stack size up to 1/2 billion elements). Stack size is known at the start, but this could be generated by doing a single copy (copy order doesn't matter since initial run size is 1).
Initial distribution for n elements: Assume that fib(m+1) > n > fib(m). n-fib(m) elements are moved to B. fib(m+1)-n elements are moved to C. n-fib(m) elements from A and B are merged (pushed) to C. After the first merge, C ends up with n-fib(m) runs of size 2, and fib(m+1)-n runs of size 1 = fib(m-1) runs. B is emptied. A ends up with (n) - (fib(m+1)-n) - 2(n-fib(m)) = 2 fib(m) - fib(m+1) = fib(m) - fib(m-1) = fib(m-2) runs of size 1. In the case that n = fib(m), then fib(m-1) elements are moved to B, leaving fib(m-2) elements in A.
Wiki article also describes a similar situation to the 3 stack sort with tape drives written forward and read backwards, but doesn't mention the details of how to distribute dummy runs (runs of size 0) at the start, but this was probably included in that 55 year old publication mentioned by greybeard.
http://en.wikipedia.org/wiki/Polyphase_merge_sort
I wrote a C++ example, but since the question asked for Java (example code below), I'll provide a link to a zip for the C++ example. Instead of a stack class, the C++ example uses arrays with a stack pointer for each array (ppmrg3s.cpp). The zip also has a regular poly phase merge sort using arrays (ppmrg.cpp).
http://rcgldr.net/misc/ppmrg.zip
Example java code. On my system, Intel 2600K, 3.4ghz, Win 7 64 bit, it sorts 16 million doubles in about 4 seconds.
public class ppmrg3s {
static final int[] FIBTBL =
{ 0, 1, 1, 2, 3, 5,
8, 13, 21, 34, 55, 89,
144, 233, 377, 610, 987, 1597,
2584, 4181, 6765, 10946, 17711, 28657,
46368, 75025, 121393, 196418, 317811, 514229,
832040, 1346269, 2178309, 3524578, 5702887, 9227465,
14930352, 24157817, 39088169, 63245986, 102334155, 165580141,
267914296, 433494437, 701408733,1134903170,1836311903};
// return index of largest fib() <= n
static int flfib(int n)
{
int lo = 0;
int hi = 47;
while((hi - lo) > 1){
int i = (lo + hi)/2;
if(n < FIBTBL[i]){
hi = i;
continue;
}
if(n > FIBTBL[i]){
lo = i;
continue;
}
return i;
}
return lo;
}
// poly phase merge sort using 3 stacks
static void ppmrg3s(dstack a, dstack b, dstack c)
{
if(a.size() < 2)
return;
int ars = 1; // init run sizes
int brs = 1;
int asc = 0; // no size change
int bsc = 0;
int csc = 0;
int scv = 0-1; // size change value
boolean dsf; // == 1 if descending sequence
{ // block for local variable scope
int f = flfib(a.size()); // FIBTBL[f] >= size >= FIBTBL[f-1]
dsf = ((f%3) == 0); // init compare flag
if(FIBTBL[f] == a.size()){ // if exact fibonacci size,
for (int i = 0; i < FIBTBL[f - 1]; i++) { // move to b
b.push(a.pop());
}
} else { // else move to b, c
// update compare flag
dsf ^= 1 == ((a.size() - FIBTBL[f]) & 1);
// i = excess run count
int i = a.size() - FIBTBL[f];
// j = dummy run count
int j = FIBTBL[f + 1] - a.size();
// move excess elements to b
do{
b.push(a.pop());
}while(0 != --i);
// move dummy count elements to c
do{
c.push(a.pop());
}while(0 != --j);
csc = c.size();
}
} // end block scope
while(true){ // start merge pass
if(asc == a.size()){ // check for size count change
ars += scv; // (due to dummy run size == 0)
scv = 0-scv;
asc = 0;
csc = c.size();
}
if(bsc == b.size()){
brs += scv;
scv = 0-scv;
bsc = 0;
csc = c.size();
}
int arc = ars; // init run counters
int brc = brs;
while(true){ // start merge pair of runs
if(dsf ^ (a.peek() <= b.peek())){
c.push(a.pop()); // move a to c
if(--arc != 0) // if not end a
continue; // continue back to compare
do{ // else move rest of b run to c
c.push(b.pop());
}while(0 != --brc);
break; // and break
} else {
c.push(b.pop()); // move b to c
if(0 != --brc) // if not end b
continue; // continue back to compare
do{ // else move rest of a run to c
c.push(a.pop());
}while(0 != --arc);
break; // and break
}
} // end merge pair of runs
dsf ^= true; // toggle compare flag
if(b.empty()){ // if end b
if(a.empty()) // if end a, done
break;
b.swap(c); // swap b, c
brs += ars;
if (0 == asc)
bsc = csc;
} else { // else not end b
if(!a.empty()) // if not end a
continue; // continue back to merge pair
a.swap(c); // swap a, c
ars += brs;
if (0 == bsc)
asc = csc;
}
}
a.swap(c); // return sorted stack in a
}
I created a fast stack class that uses a fixed maximum size array of doubles that includes a swap function member:
class dstack{
double []ar; // array
int sz; // size
int sp; // stack pointer
public dstack(int sz){ // constructor with size
this.ar = new double[sz];
this.sz = sz;
this.sp = sz;
}
public void push(double d){
this.ar[--sp] = d;
}
public double pop(){
return this.ar[sp++];
}
public double peek(){
return this.ar[sp];
}
public boolean empty(){
return sp == sz;
}
public int size(){
return sz-sp;
}
public void swap(dstack othr){
double []tempar = othr.ar;
int tempsz = othr.sz;
int tempsp = othr.sp;
othr.ar = this.ar;
othr.sz = this.sz;
othr.sp = this.sp;
this.ar = tempar;
this.sz = tempsz;
this.sp = tempsp;
}
}
Test program. It uses random integers (nextInt), that get converted to doubles during a.push(...). This made the early debugging easier. For other platforms, or to follow with debug, use a smaller number for NUMELEM, which is the number of elements.
static final int NUMELEM = 16*1024*1024;
public static void main(String[] args) {
dstack a = new dstack(NUMELEM);
dstack b = new dstack(NUMELEM);
dstack c = new dstack(NUMELEM);
Random r = new Random();
for(int i = 0; i < NUMELEM; i++){
a.push(r.nextInt(NUMELEM));
}
long bgn, end;
bgn = System.currentTimeMillis();
ppmrg3s(a, b, c);
end = System.currentTimeMillis();
double d;
d = a.pop();
while(!a.empty()){
if(d > a.peek()){
System.out.println("error");
break;
}
d = a.pop();
}
System.out.println("milliseconds");
System.out.println(end-bgn);
}
A simple program to take values from an array and then print them to a command console would be.
import java.util.*;
public class StackSort
{
static Stack<Double> A = new Stack<Double>();
public void createStackA()
{
double[] x = {-10,5, 2, 1, 9, 0, 10};
for (int i = 0; i < x.length; i++)
{
A.push(x[i]);
}
}
public void sortStackA(Stack<Double> C)
{
Stack<Double> B = new Stack<Double>();
while(!C.isEmpty())
{
double s1 = (double) C.pop();
while(!B.isEmpty() && (B.peek() > s1))
{
C.push(B.pop());
}
B.push(s1);
}
System.out.println(B);
}
public static void main(String[] args)
{
StackSort sS = new StackSort();
sS.createStackA();
sS.sortStackA(A);
}
}
for a starting hint, check the shunting-yard algorithm it is a similar approach in that operators (i.e values) are pushed to a stack and popped to another stack (i.e output queue) depending on their relative priority (i.e value)
The algorithm has 3 stacks, a) input queue (lets say A), b) operator stack (lets say B) and c) output queue (lets say C), now try to translate this into an algorithm for sorting
This should do:
Move all items from stack A to stack B, store maximum value found in 'x'.
Move all items from stack B to stack A, except those with value 'x' determined from previous stem. Move those to C instead.
Repeat until both A and B are empty.
So I've been working on the following problem:
I buried my sapphire then started walking. I always walked in a
straight line following a compass direction (N, S, E, W). When I
stopped, I made a 90 degree turn and continued walking. I might have
crossed my path, but I don’t remember. Below are the number of meters
I travelled in each direction. I’m now lost and must abandon this
record while I search for a way out. I’m placing this note under a
rock at my final location. Perhaps some lucky adventurer will decode
my note and retrace my steps to earn the treasure. Unfortunately,
there is no record of where in the ruins the note was found. Instead,
you must write a program to find the treasure. Input The first
line contains two integers X Y, representing the number of rows and
columns in the ruins. Maximum of 20 rows and 50 columns. The next X
lines show a grid map of the space. A period “.” is an empty square. A
hash “#” is a large boulder, marking a square that cannot be entered.
The next line has an integer N, the count of the straight paths
walked. Maximum of 20 paths. The last line contains N integers
separated by spaces, showing the successive path-lengths.. 5 10
####
........#
.#...##.#
...#....#
#### 8 2 4 2 2 2 5 2 1 Output Your program must print the same map, with the location of both the Sapphire (S) and the final
location of the message (F) marked. Also, label every turning point
with successive lowercase letters (if the same point is used more
than once, print the letter for the later turn.) There is only one
route which follows the path-lengths in the list.
####
b.e.a..f#
.#...##.#
c.d#S.Fg#
#
and I have made a recursive method that checks every direction starting from every open position of the maze until it finds the solution, however the output of the problem needs to be the mazes with the turns.
The problem is, when I use a recursive solution and edit the actual char[][] map, it never knows which path will lead to the actual finish, so it will create output like this:
d...d
.....
cbabc
d...d
but instead I would like it to show only one path, like this:
....d
.....
..abc
.....
Here is my incomplete solution:
import java.util.Scanner;
public class SapphireSearch {
private static int rs; // Row Size
private static int cs; // Column Size
private static int sr; // Save row (saves solution row)
private static int sc; // Save col (saves solution col)
private static Direction sd; // Save direction (saves solution dir)
private static char[][] map; // the maze to traverse
private static int n; // number of turns
private static int[] go; // length of the turns
public static void main(String[] args) {
getInput();
for (int r = 0; r < rs; r++)
for (int c = 0; c < cs; c++)
for (Direction d : Direction.values())
solve(sr = r, sc = c, sd = d, 0, false);
}
public static void solve(int r, int c, Direction d, int start,
boolean printing) {
if (isSolid(r, c))
return;
if (printing) {
if (start == 0)
map[r][c] = 'S';
else
map[r][c] = (char) (start - 1 + 'a');
if (start == n) {
map[r][c] = 'F';
return;
}
}
if (start == n - 1 && !printing) {
solve(sr, sc, sd, 0, true);
printArray(map);
System.exit(0);
}
int count = 0;
while (start < go.length && count < go[start]) {
count++;
r += d.dr;
c += d.dc;
if (isSolid(r, c))
return;
}
for (Direction t : d.turn())
solve(r, c, t, start + 1, printing);
}
public static boolean isSolid(int r, int c) {
return map[r][c] == '#';
}
public static void printArray(char[][] o) {
for (int r = 0; r < o.length; r++) {
for (int c = 0; c < o[r].length; c++)
System.out.print(o[r][c]);
System.out.println();
}
}
private static void getInput() {
Scanner s = new Scanner(System.in);
rs = s.nextInt();
cs = s.nextInt();
s.nextLine(); // clear buffer
map = new char[rs][cs];
for (int r = 0; r < rs; r++) {
int c = 0;
char[] f = s.nextLine().trim().toCharArray();
for (char t : f)
map[r][c++] = t;
}
n = s.nextInt();
go = new int[n];
for (int i = 0; i < n; i++)
go[i] = s.nextInt();
}
}
enum Direction {
// deltaR, deltaC
up(-1, 0), down(1, 0), left(0, -1), right(0, 1);
public int dr;
public int dc;
private Direction(int dr, int dc) {
this.dr = dr;
this.dc = dc;
}
public Direction[] turn() {
Direction[] out = new Direction[2];
switch (this) {
case up:
case down:
out[0] = left;
out[1] = right;
break;
case left:
case right:
out[0] = up;
out[1] = down;
}
return out;
}
}
The question is: building upon my recursive solve algorithm, what would be the best way to print the solution path (where it doesn't print out every path it tries to take)?
You need to build up your list of turns as you do the recursive search (I'm just listing the direction here for simplicity but you could store an object with co-ordinates as well for example).
If the path is (N,E,N,W,S) and then save that as you exit.
To do that keep the partial list so far and each recursive call COPY the list so far and add to it.
i.e.:
n
ne
nw Fail
nen
nes Fail
nenw
etc.
At the end you can either return the completed solution or if you need to handle multiple solutions have a final results list of lists that you insert the completed one into.
The key step is to copy the list so far so that recursion branches cannot interfere with each other.
currently i'm working on a project regarding "delayed auditory feedback" (DAF). Basically i want to record sounds from a microphone, delay it by a specific amount of time and then play it back. Using a delay around 200ms and a person with a headset, this feedback shuts down the persons ability to speak fluently. (Pretty much fun: DAF on youtube)
Right now i am trying to make this loop with SourceDataLine and TargetDataLine using a byte[]-buffer with 256 bytes. If the buffer gets bigger, so does the delay. My problem is now: I can't tell what the delay in milliseconds is.
Is there any way to calculate the real delay in ms from the buffer size? Or is there maybe another approach to get this result?
This is what my loop looks like at the moment:
private int mBufferSize; // 256
private TargetDataLine mLineOutput;
private SourceDataLine mLineInput;
public void run() {
... creating the DataLines and getting the lines from AudioSystem ...
// byte buffer for audio
byte[] data = new byte[mBufferSize];
// start the data lines
mLineOutput.start();
mLineInput.start();
// start recording and playing back
while (running) {
mLineOutput.read(data, 0, mBufferSize);
mLineInput.write(data, 0, mBufferSize);
}
... closing the lines and exiting ...
}
You can calculate the delay easily, as it's dependent on the sample rate of the audio. Assuming this is CD-quality (mono) audio, the sample rate is 44,100 samples per second. 200 milliseconds is 0.2 seconds, so 44,100 X 0.2 = 8820.
So your audio playback needs to be delayed by 8820 samples (or 17640 bytes). If you make your recording and playback buffers exactly this size (17640 bytes) it will make your code pretty simple. As each recording buffer is filled you pass it to playback; this will achieve a playback lag of exactly one buffer's duration.
There is some delay inherent in Android that you should account for, but aside from that...
Create a circular buffer. Doesn't matter how big, as long as it is more than big enough for N 0 samples. Now write it with N '0' samples.
N in this case is (delay in seconds) * (sample rate in hertz).
Example: 200ms with 16kHz stereo:
0.2s*16000Hz*(2 channels)=3200*2 samples = 6400 samples
You will probably be working with pcm data too, which is 16-bit, so use short instead of byte.
After filling the buffer with the right amount of zeroes, start reading data for the speaker while filling with data from the microphone.
PCM Fifo:
public class PcmQueue
{
private short mBuf[] = null;
private int mWrIdx = 0;
private int mRdIdx = 0;
private int mCount = 0;
private int mBufSz = 0;
private Object mSync = new Object();
private PcmQueue(){}
public PcmQueue( int nBufSz )
{
try {
mBuf = new short[nBufSz];
} catch (Exception e) {
Log.e(this.getClass().getName(), "AudioQueue allocation failed.", e);
mBuf = null;
mBufSz = 0;
}
}
public int doWrite( final short pWrBuf[], final int nWrBufIdx, final int nLen )
{
int sampsWritten = 0;
if ( nLen > 0 ) {
int toWrite;
synchronized(mSync) {
// Write nothing if there isn't room in the buffer.
toWrite = (nLen <= (mBufSz - mCount)) ? nLen : 0;
}
// We can definitely read toWrite shorts.
while (toWrite > 0)
{
// Calculate how many contiguous shorts to the end of the buffer
final int sampsToCopy = Math.min( toWrite, (mBufSz - mWrIdx) );
// Copy that many shorts.
System.arraycopy(pWrBuf, sampsWritten + nWrBufIdx, mBuf, mWrIdx, sampsToCopy);
// Circular buffering.
mWrIdx += sampsToCopy;
if (mWrIdx >= mBufSz) {
mWrIdx -= mBufSz;
}
// Increment the number of shorts sampsWritten.
sampsWritten += sampsToCopy;
toWrite -= sampsToCopy;
}
synchronized(mSync) {
// Increment the count.
mCount = mCount + sampsWritten;
}
}
return sampsWritten;
}
public int doRead( short pcmBuffer[], final int nRdBufIdx, final int nRdBufLen )
{
int sampsRead = 0;
final int nSampsToRead = Math.min( nRdBufLen, pcmBuffer.length - nRdBufIdx );
if ( nSampsToRead > 0 ) {
int sampsToRead;
synchronized(mSync) {
// Calculate how many shorts can be read from the RdBuffer.
sampsToRead = Math.min(mCount, nSampsToRead);
}
// We can definitely read sampsToRead shorts.
while (sampsToRead > 0)
{
// Calculate how many contiguous shorts to the end of the buffer
final int sampsToCopy = Math.min( sampsToRead, (mBufSz - mRdIdx) );
// Copy that many shorts.
System.arraycopy( mBuf, mRdIdx, pcmBuffer, sampsRead + nRdBufIdx, sampsToCopy);
// Circular buffering.
mRdIdx += sampsToCopy;
if (mRdIdx >= mBufSz) {
mRdIdx -= mBufSz;
}
// Increment the number of shorts read.
sampsRead += sampsToCopy;
sampsToRead -= sampsToCopy;
}
// Decrement the count.
synchronized(mSync) {
mCount = mCount - sampsRead;
}
}
return sampsRead;
}
}
And your code, modified for the FIFO... I have no experience with TargetDataLine/SourceDataLine so if they only handle byte arrays, modify the FIFO for byte instead of short.
private int mBufferSize; // 256
private TargetDataLine mLineOutput;
private SourceDataLine mLineInput;
public void run() {
... creating the DataLines and getting the lines from AudioSystem ...
// short buffer for audio
short[] data = new short[256];
final int emptySamples = (int)(44100.0 * 0.2);
final int bufferSize = emptySamples*2;
PcmQueue pcmQueue = new PcmQueue( bufferSize );
// Create a temporary empty buffer to write to the PCM queue
{
short[] emptyBuf = new short[emptySamples];
Arrays.fill(emptyBuf, (short)emptySamples );
pcmQueue.doWrite(emptyBuf, 0, emptySamples);
}
// start recording and playing back
while (running) {
mLineOutput.read(data, 0, mBufferSize);
pcmQueue.doWrite(data, 0, mBufferSize);
pcmQueue.doRead(data, 0, mBufferSize);
mLineInput.write(data, 0, mBufferSize);
}
... closing the lines and exiting ...
}