Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 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 | |
| 17 | // Test program for audio_utils FIFO library. |
| 18 | // This only tests the single-threaded aspects, not the barriers. |
| 19 | |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 20 | #include <errno.h> |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 21 | #include <limits.h> |
| 22 | #include <stdlib.h> |
| 23 | #include <string.h> |
Jim Miller | 328eb02 | 2015-06-05 17:34:31 -0700 | [diff] [blame] | 24 | #include <audio_utils/fifo.h> |
| 25 | #include <audio_utils/sndfile.h> |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 26 | |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 27 | #ifndef min |
| 28 | #define min(x, y) (((x) < (y)) ? (x) : (y)) |
| 29 | #endif |
| 30 | |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 31 | int main(int argc, char **argv) |
| 32 | { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 33 | size_t frameCount = 0; |
| 34 | size_t maxFramesPerRead = 0; |
| 35 | size_t maxFramesPerWrite = 0; |
| 36 | bool readerThrottlesWriter = true; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 37 | bool verbose = false; |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 38 | int i; |
| 39 | for (i = 1; i < argc; i++) { |
| 40 | char *arg = argv[i]; |
| 41 | if (arg[0] != '-') |
| 42 | break; |
| 43 | switch (arg[1]) { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 44 | case 'f': // FIFO frame count |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 45 | frameCount = atoi(&arg[2]); |
| 46 | break; |
| 47 | case 'r': // maximum frame count per read from FIFO |
| 48 | maxFramesPerRead = atoi(&arg[2]); |
| 49 | break; |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 50 | case 't': // disable throttling of writer by reader |
| 51 | readerThrottlesWriter = false; |
| 52 | break; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 53 | case 'v': // enable verbose logging |
| 54 | verbose = true; |
| 55 | break; |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 56 | case 'w': // maximum frame count per write to FIFO |
| 57 | maxFramesPerWrite = atoi(&arg[2]); |
| 58 | break; |
| 59 | default: |
| 60 | fprintf(stderr, "%s: unknown option %s\n", argv[0], arg); |
| 61 | goto usage; |
| 62 | } |
| 63 | } |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 64 | if (frameCount == 0) { |
| 65 | frameCount = 256; |
| 66 | } |
| 67 | if (maxFramesPerRead == 0) { |
| 68 | maxFramesPerRead = frameCount; |
| 69 | } |
| 70 | if (maxFramesPerWrite == 0) { |
| 71 | maxFramesPerWrite = frameCount; |
| 72 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 73 | |
| 74 | if (argc - i != 2) { |
| 75 | usage: |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 76 | fprintf(stderr, "usage: %s [-f#] [-r#] [-t] [-v] [-w#] in.wav out.wav\n", argv[0]); |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 77 | return EXIT_FAILURE; |
| 78 | } |
| 79 | char *inputFile = argv[i]; |
| 80 | char *outputFile = argv[i+1]; |
| 81 | |
| 82 | SF_INFO sfinfoin; |
| 83 | memset(&sfinfoin, 0, sizeof(sfinfoin)); |
| 84 | SNDFILE *sfin = sf_open(inputFile, SFM_READ, &sfinfoin); |
| 85 | if (sfin == NULL) { |
| 86 | perror(inputFile); |
| 87 | return EXIT_FAILURE; |
| 88 | } |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 89 | switch (sfinfoin.format & (SF_FORMAT_TYPEMASK | SF_FORMAT_SUBMASK)) { |
| 90 | case SF_FORMAT_WAV | SF_FORMAT_PCM_16: |
| 91 | case SF_FORMAT_WAV | SF_FORMAT_PCM_U8: |
| 92 | break; |
| 93 | default: |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 94 | fprintf(stderr, "%s: unsupported format\n", inputFile); |
| 95 | sf_close(sfin); |
| 96 | return EXIT_FAILURE; |
| 97 | } |
Glenn Kasten | 35d10a7 | 2016-11-18 15:28:42 -0800 | [diff] [blame] | 98 | size_t frameSize = sizeof(int16_t) * sfinfoin.channels; |
| 99 | int16_t *inputBuffer = new int16_t[sfinfoin.frames * sfinfoin.channels]; |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 100 | sf_count_t actualRead = sf_readf_short(sfin, inputBuffer, sfinfoin.frames); |
| 101 | if (actualRead != sfinfoin.frames) { |
| 102 | fprintf(stderr, "%s: unexpected EOF or error\n", inputFile); |
| 103 | sf_close(sfin); |
| 104 | return EXIT_FAILURE; |
| 105 | } |
| 106 | sf_close(sfin); |
| 107 | |
Glenn Kasten | 35d10a7 | 2016-11-18 15:28:42 -0800 | [diff] [blame] | 108 | int16_t *outputBuffer = new int16_t[sfinfoin.frames * sfinfoin.channels]; |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 109 | size_t framesWritten = 0; |
| 110 | size_t framesRead = 0; |
Glenn Kasten | 35d10a7 | 2016-11-18 15:28:42 -0800 | [diff] [blame] | 111 | int16_t *fifoBuffer = new int16_t[frameCount * sfinfoin.channels]; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 112 | audio_utils_fifo fifo(frameCount, frameSize, fifoBuffer, readerThrottlesWriter); |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 113 | audio_utils_fifo_writer fifoWriter(fifo); |
| 114 | audio_utils_fifo_reader fifoReader(fifo, readerThrottlesWriter); |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 115 | int fifoWriteCount = 0, fifoReadCount = 0; |
| 116 | int fifoFillLevel = 0, minFillLevel = INT_MAX, maxFillLevel = INT_MIN; |
| 117 | for (;;) { |
| 118 | size_t framesToWrite = sfinfoin.frames - framesWritten; |
| 119 | size_t framesToRead = sfinfoin.frames - framesRead; |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 120 | if (framesToWrite == 0 && (framesToRead == 0 || !readerThrottlesWriter)) { |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 121 | break; |
| 122 | } |
| 123 | |
| 124 | if (framesToWrite > maxFramesPerWrite) { |
| 125 | framesToWrite = maxFramesPerWrite; |
| 126 | } |
| 127 | framesToWrite = rand() % (framesToWrite + 1); |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 128 | ssize_t actualWritten = fifoWriter.write( |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 129 | &inputBuffer[framesWritten * sfinfoin.channels], framesToWrite); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 130 | if (verbose) { |
| 131 | printf("wrote %d out of %d\n", (int) actualWritten, (int) framesToWrite); |
| 132 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 133 | if (actualWritten < 0 || (size_t) actualWritten > framesToWrite) { |
| 134 | fprintf(stderr, "write to FIFO failed\n"); |
| 135 | break; |
| 136 | } |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 137 | if (actualWritten < min((int) frameCount - fifoFillLevel, (int) framesToWrite)) { |
| 138 | fprintf(stderr, "only wrote %d when should have written min(%d, %d)\n", |
| 139 | (int) actualWritten, (int) frameCount - fifoFillLevel, (int) framesToWrite); |
| 140 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 141 | framesWritten += actualWritten; |
| 142 | if (actualWritten > 0) { |
| 143 | fifoWriteCount++; |
| 144 | } |
| 145 | fifoFillLevel += actualWritten; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 146 | if (verbose) { |
| 147 | printf("fill level after write %d\n", fifoFillLevel); |
| 148 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 149 | if (fifoFillLevel > maxFillLevel) { |
| 150 | maxFillLevel = fifoFillLevel; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 151 | if (maxFillLevel > (int) frameCount) { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 152 | if (readerThrottlesWriter) { |
| 153 | printf("maxFillLevel=%d > frameCount=%d\n", maxFillLevel, (int) frameCount); |
| 154 | abort(); |
| 155 | } |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 156 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 157 | } |
| 158 | |
| 159 | if (framesToRead > maxFramesPerRead) { |
| 160 | framesToRead = maxFramesPerRead; |
| 161 | } |
| 162 | framesToRead = rand() % (framesToRead + 1); |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 163 | ssize_t actualRead = fifoReader.read( |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 164 | &outputBuffer[framesRead * sfinfoin.channels], framesToRead); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 165 | if (verbose) { |
| 166 | printf("read %d out of %d\n", (int) actualRead, (int) framesToRead); |
| 167 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 168 | if (actualRead < 0 || (size_t) actualRead > framesToRead) { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 169 | switch (actualRead) { |
| 170 | case -EIO: |
| 171 | fprintf(stderr, "read from FIFO failed: corrupted indices\n"); |
| 172 | abort(); |
| 173 | break; |
| 174 | case -EOVERFLOW: |
| 175 | if (readerThrottlesWriter) { |
| 176 | fprintf(stderr, "read from FIFO failed: unexpected overflow\n"); |
| 177 | abort(); |
| 178 | } |
| 179 | printf("warning: reader lost frames\n"); |
| 180 | actualRead = 0; |
| 181 | break; |
| 182 | default: |
| 183 | if (actualRead < 0) { |
| 184 | fprintf(stderr, "read from FIFO failed: unexpected error code %d\n", |
| 185 | (int) actualRead); |
| 186 | } else { |
| 187 | fprintf(stderr, "read from FIFO failed: actualRead=%d > framesToRead=%d\n", |
| 188 | (int) actualRead, (int) framesToRead); |
| 189 | } |
| 190 | abort(); |
| 191 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 192 | } |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 193 | if (actualRead < min(fifoFillLevel, (int) framesToRead)) { |
Glenn Kasten | 6d7ad76 | 2016-06-15 17:05:54 -0700 | [diff] [blame] | 194 | //fprintf(stderr, "only read %d when should have read min(%d, %d)\n", |
| 195 | // (int) actualRead, fifoFillLevel, (int) framesToRead); |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 196 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 197 | framesRead += actualRead; |
| 198 | if (actualRead > 0) { |
| 199 | fifoReadCount++; |
| 200 | } |
| 201 | fifoFillLevel -= actualRead; |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 202 | if (verbose) { |
| 203 | printf("fill level after read %d\n", fifoFillLevel); |
| 204 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 205 | if (fifoFillLevel < minFillLevel) { |
| 206 | minFillLevel = fifoFillLevel; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 207 | if (minFillLevel < 0) { |
| 208 | printf("minFillLevel=%d < 0\n", minFillLevel); |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 209 | abort(); |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 210 | } |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 211 | } |
| 212 | } |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 213 | delete[] inputBuffer; |
| 214 | inputBuffer = NULL; |
| 215 | delete[] fifoBuffer; |
| 216 | fifoBuffer = NULL; |
| 217 | |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 218 | printf("FIFO non-empty writes: %d, non-empty reads: %d\n", fifoWriteCount, fifoReadCount); |
| 219 | printf("fill=%d, min=%d, max=%d\n", fifoFillLevel, minFillLevel, maxFillLevel); |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 220 | |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 221 | printf("writing output\n"); |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 222 | SF_INFO sfinfoout; |
| 223 | memset(&sfinfoout, 0, sizeof(sfinfoout)); |
| 224 | sfinfoout.samplerate = sfinfoin.samplerate; |
| 225 | sfinfoout.channels = sfinfoin.channels; |
| 226 | sfinfoout.format = sfinfoin.format; |
| 227 | SNDFILE *sfout = sf_open(outputFile, SFM_WRITE, &sfinfoout); |
| 228 | if (sfout == NULL) { |
| 229 | perror(outputFile); |
| 230 | return EXIT_FAILURE; |
| 231 | } |
| 232 | sf_count_t actualWritten = sf_writef_short(sfout, outputBuffer, framesRead); |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 233 | delete[] outputBuffer; |
Glenn Kasten | 9b4fe47 | 2016-06-13 09:34:57 -0700 | [diff] [blame] | 234 | outputBuffer = NULL; |
| 235 | |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 236 | if (actualWritten != (sf_count_t) framesRead) { |
| 237 | fprintf(stderr, "%s: unexpected error\n", outputFile); |
| 238 | sf_close(sfout); |
| 239 | return EXIT_FAILURE; |
| 240 | } |
| 241 | sf_close(sfout); |
Glenn Kasten | c0924bc | 2016-10-02 13:00:19 -0700 | [diff] [blame] | 242 | printf("done\n"); |
| 243 | |
Glenn Kasten | b598a73 | 2015-01-13 16:25:39 -0800 | [diff] [blame] | 244 | return EXIT_SUCCESS; |
| 245 | } |