|
| 1 | +/* |
| 2 | + AudioGeneratorTonie |
| 3 | + Audio output generator that plays Opus audio files |
| 4 | + |
| 5 | + Copyright (C) 2020 Earle F. Philhower, III |
| 6 | +
|
| 7 | + This program is free software: you can redistribute it and/or modify |
| 8 | + it under the terms of the GNU General Public License as published by |
| 9 | + the Free Software Foundation, either version 3 of the License, or |
| 10 | + (at your option) any later version. |
| 11 | +
|
| 12 | + This program is distributed in the hope that it will be useful, |
| 13 | + but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | + GNU General Public License for more details. |
| 16 | +
|
| 17 | + You should have received a copy of the GNU General Public License |
| 18 | + along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 19 | +*/ |
| 20 | + |
| 21 | +#include "AudioGeneratorTonie.h" |
| 22 | + |
| 23 | +AudioGeneratorTonie::AudioGeneratorTonie() |
| 24 | +{ |
| 25 | + of = nullptr; |
| 26 | + buff = nullptr; |
| 27 | + buffPtr = 0; |
| 28 | + buffLen = 0; |
| 29 | + running = false; |
| 30 | +} |
| 31 | + |
| 32 | +AudioGeneratorTonie::~AudioGeneratorTonie() |
| 33 | +{ |
| 34 | + if (of) op_free(of); |
| 35 | + of = nullptr; |
| 36 | + free(buff); |
| 37 | + buff = nullptr; |
| 38 | +} |
| 39 | + |
| 40 | +#define OPUS_BUFF 512 |
| 41 | + |
| 42 | +bool AudioGeneratorTonie::begin(AudioFileSource *source, AudioOutput *output) |
| 43 | +{ |
| 44 | + buff = (int16_t*)malloc(OPUS_BUFF * sizeof(int16_t)); |
| 45 | + if (!buff) return false; |
| 46 | + |
| 47 | + if (!source) return false; |
| 48 | + file = source; |
| 49 | + if (!output) return false; |
| 50 | + this->output = output; |
| 51 | + if (!file->isOpen()) return false; // Error |
| 52 | + |
| 53 | + //TODO? |
| 54 | + file->seek(4096, SEEK_CUR); //skip first sector |
| 55 | + |
| 56 | + of = op_open_callbacks((void*)this, &cb, nullptr, 0, nullptr); |
| 57 | + if (!of) return false; |
| 58 | + |
| 59 | + prev_li = -1; |
| 60 | + lastSample[0] = 0; |
| 61 | + lastSample[1] = 0; |
| 62 | + |
| 63 | + buffPtr = 0; |
| 64 | + buffLen = 0; |
| 65 | + |
| 66 | + output->begin(); |
| 67 | + |
| 68 | + // These are fixed by Opus |
| 69 | + output->SetRate(48000); |
| 70 | + output->SetBitsPerSample(16); |
| 71 | + output->SetChannels(2); |
| 72 | + |
| 73 | + running = true; |
| 74 | + return true; |
| 75 | +} |
| 76 | + |
| 77 | +bool AudioGeneratorTonie::loop() |
| 78 | +{ |
| 79 | + |
| 80 | + if (!running) goto done; |
| 81 | + |
| 82 | + if (!output->ConsumeSample(lastSample)) goto done; // Try and send last buffered sample |
| 83 | + |
| 84 | + do { |
| 85 | + if (buffPtr == buffLen) { |
| 86 | + int ret = op_read_stereo(of, (opus_int16 *)buff, OPUS_BUFF); |
| 87 | + if (ret == OP_HOLE) { |
| 88 | + // fprintf(stderr,"\nHole detected! Corrupt file segment?\n"); |
| 89 | + continue; |
| 90 | + } else if (ret < 0) { |
| 91 | + running = false; |
| 92 | + goto done; |
| 93 | + } |
| 94 | + buffPtr = 0; |
| 95 | + buffLen = ret * 2; |
| 96 | + } |
| 97 | + |
| 98 | + lastSample[AudioOutput::LEFTCHANNEL] = buff[buffPtr] & 0xffff; |
| 99 | + lastSample[AudioOutput::RIGHTCHANNEL] = buff[buffPtr+1] & 0xffff; |
| 100 | + buffPtr += 2; |
| 101 | + } while (running && output->ConsumeSample(lastSample)); |
| 102 | + |
| 103 | +done: |
| 104 | + file->loop(); |
| 105 | + output->loop(); |
| 106 | + |
| 107 | + return running; |
| 108 | +} |
| 109 | + |
| 110 | +bool AudioGeneratorTonie::stop() |
| 111 | +{ |
| 112 | + if (of) op_free(of); |
| 113 | + of = nullptr; |
| 114 | + free(buff); |
| 115 | + buff = nullptr; |
| 116 | + running = false; |
| 117 | + output->stop(); |
| 118 | + return true; |
| 119 | +} |
| 120 | + |
| 121 | +bool AudioGeneratorTonie::isRunning() |
| 122 | +{ |
| 123 | + return running; |
| 124 | +} |
| 125 | + |
| 126 | + |
| 127 | +int AudioGeneratorTonie::read_cb(unsigned char *_ptr, int _nbytes) { |
| 128 | + if (_nbytes == 0) return 0; |
| 129 | + _nbytes = file->read(_ptr, _nbytes); |
| 130 | + if (_nbytes == 0) return -1; |
| 131 | + return _nbytes; |
| 132 | +} |
| 133 | + |
| 134 | +int AudioGeneratorTonie::seek_cb(opus_int64 _offset, int _whence) { |
| 135 | + if (!file->seek((int32_t)_offset, _whence)) return -1; |
| 136 | + return 0; |
| 137 | +} |
| 138 | + |
| 139 | +opus_int64 AudioGeneratorTonie::tell_cb() { |
| 140 | + return file->getPos(); |
| 141 | +} |
| 142 | + |
| 143 | +int AudioGeneratorTonie::close_cb() { |
| 144 | + // NO OP, we close in main loop |
| 145 | + return 0; |
| 146 | +} |
0 commit comments