1 /* 2 Copyright (c) 2013 yvt 3 4 This file is part of OpenSpades. 5 6 OpenSpades is free software: you can redistribute it and/or modify 7 it under the terms of the GNU 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 OpenSpades 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 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with OpenSpades. If not, see <http://www.gnu.org/licenses/>. 18 19 */ 20 21 #include <cstring> 22 #include <string> 23 24 #include <opusfile.h> 25 26 #include "OpusAudioStream.h" 27 28 #include "Debug.h" 29 #include "Exception.h" 30 #include <Core/Strings.h> 31 32 namespace spades { 33 34 namespace { stringifyOpusErrorCode(int error)35 std::string stringifyOpusErrorCode(int error) { 36 const char *errorMessage = nullptr; 37 switch (error) { 38 case OP_EREAD: errorMessage = "OP_EREAD"; break; 39 case OP_EFAULT: errorMessage = "OP_EFAULT"; break; 40 case OP_EIMPL: errorMessage = "OP_EIMPL"; break; 41 case OP_EINVAL: errorMessage = "OP_EINVAL"; break; 42 case OP_ENOTFORMAT: errorMessage = "OP_ENOTFORMAT"; break; 43 case OP_EBADHEADER: errorMessage = "OP_EBADHEADER"; break; 44 case OP_EVERSION: errorMessage = "OP_EVERSION"; break; 45 case OP_EBADLINK: errorMessage = "OP_EBADLINK"; break; 46 case OP_EBADTIMESTAMP: errorMessage = "OP_EBADTIMESTAMP"; break; 47 } 48 if (errorMessage) { 49 return errorMessage; 50 } else { 51 return Format("{0}", error); 52 } 53 } 54 } 55 OpusAudioStream(IStream * s,bool ac)56 OpusAudioStream::OpusAudioStream(IStream *s, bool ac) : subsamplePosition{0} { 57 SPADES_MARK_FUNCTION(); 58 59 stream = s; 60 autoClose = ac; 61 62 opusCallback.reset(new OpusFileCallbacks()); __anonc2605ac60202(void *stream, unsigned char *ptr, int nbytes) 63 opusCallback->read = [](void *stream, unsigned char *ptr, int nbytes) -> int { 64 auto &self = *reinterpret_cast<OpusAudioStream *>(stream); 65 return static_cast<int>(self.stream->Read(ptr, static_cast<int>(nbytes))); 66 }; __anonc2605ac60302(void *stream, opus_int64 offset, int whence) 67 opusCallback->seek = [](void *stream, opus_int64 offset, int whence) -> int { 68 auto &self = *reinterpret_cast<OpusAudioStream *>(stream); 69 switch (whence) { 70 case SEEK_CUR: 71 self.stream->SetPosition( 72 static_cast<std::uint64_t>(offset + self.stream->GetPosition())); 73 break; 74 case SEEK_SET: self.stream->SetPosition(static_cast<std::uint64_t>(offset)); break; 75 case SEEK_END: 76 self.stream->SetPosition( 77 static_cast<std::uint64_t>(offset + self.stream->GetLength())); 78 break; 79 } 80 return 0; 81 }; __anonc2605ac60402(void *stream) 82 opusCallback->tell = [](void *stream) -> opus_int64 { 83 auto &self = *reinterpret_cast<OpusAudioStream *>(stream); 84 return static_cast<opus_int64>(self.stream->GetPosition()); 85 }; 86 opusCallback->close = nullptr; 87 88 int result = 0; 89 opusFile = op_open_callbacks(this, opusCallback.get(), nullptr, 0, &result); 90 91 if (result) { 92 SPRaise("op_open_callbacks failed with error code %s", 93 stringifyOpusErrorCode(result).c_str()); 94 } 95 96 channels = op_channel_count(opusFile, 0); 97 currentSample.resize(channels); 98 99 SetPosition(0); 100 } 101 ~OpusAudioStream()102 OpusAudioStream::~OpusAudioStream() { 103 SPADES_MARK_FUNCTION(); 104 105 op_free(opusFile); 106 107 if (autoClose) 108 delete stream; 109 } 110 GetLength()111 uint64_t OpusAudioStream::GetLength() { return op_pcm_total(opusFile, 0) * channels * 4; } 112 GetSamplingFrequency()113 int OpusAudioStream::GetSamplingFrequency() { return 48000; } 114 GetNumChannels()115 int OpusAudioStream::GetNumChannels() { return channels; } 116 GetSampleFormat()117 OpusAudioStream::SampleFormat OpusAudioStream::GetSampleFormat() { 118 return SampleFormat::SingleFloat; 119 } 120 ReadByte()121 int OpusAudioStream::ReadByte() { 122 SPADES_MARK_FUNCTION(); 123 124 if (subsamplePosition == 0) { 125 if (op_pcm_tell(opusFile) >= op_pcm_total(opusFile, 0)) { 126 return -1; 127 } 128 int result = op_read_float(opusFile, currentSample.data(), channels, nullptr); 129 if (result < 0) { 130 SPRaise("op_read_float failed with error code %s", 131 stringifyOpusErrorCode(result).c_str()); 132 } else if (result != 1) { 133 SPRaise("op_read_float returned %d (expected: 1)", result); 134 } 135 } 136 137 int ret = reinterpret_cast<const uint8_t *>(currentSample.data())[subsamplePosition]; 138 139 if ((++subsamplePosition) == channels * 4) { 140 subsamplePosition = 0; 141 } 142 143 return ret; 144 } 145 Read(void * data,size_t bytes)146 size_t OpusAudioStream::Read(void *data, size_t bytes) { 147 SPADES_MARK_FUNCTION(); 148 149 uint64_t maxLen = GetLength() - GetPosition(); 150 if ((uint64_t)bytes > maxLen) 151 bytes = (size_t)maxLen; 152 153 uint8_t *retBytes = reinterpret_cast<uint8_t *>(data); 154 uint64_t remainingBytes = bytes; 155 156 if (subsamplePosition) { 157 uint64_t copied = 158 std::min(remainingBytes, static_cast<uint64_t>(channels * 4 - subsamplePosition)); 159 std::memcpy(retBytes, 160 reinterpret_cast<uint8_t *>(currentSample.data()) + subsamplePosition, 161 static_cast<size_t>(copied)); 162 subsamplePosition += static_cast<int>(copied); 163 remainingBytes -= copied; 164 retBytes += copied; 165 166 if (subsamplePosition == channels * 4) { 167 subsamplePosition = 0; 168 } 169 } 170 171 while (remainingBytes >= channels * 4) { 172 uint64_t numSamples = remainingBytes / (channels * 4); 173 174 // 64-bit sample count might doesn't fit in int which op_read_float's third parameter 175 // accepts 176 numSamples = std::min<uint64_t>(numSamples, 0x1000000); 177 178 int result = op_read_float(opusFile, reinterpret_cast<float *>(retBytes), 179 static_cast<int>(numSamples * channels), nullptr); 180 181 if (result < 0) { 182 SPRaise("op_read_float failed with error code %s", 183 stringifyOpusErrorCode(result).c_str()); 184 } else if (result == 0) { 185 SPRaise("op_read_float returned 0 (expected: > 0)"); 186 } 187 188 numSamples = static_cast<uint64_t>(result); 189 190 retBytes += numSamples * (channels * 4); 191 remainingBytes -= numSamples * (channels * 4); 192 } 193 194 if (remainingBytes) { 195 int result = op_read_float(opusFile, currentSample.data(), channels, nullptr); 196 if (result < 0) { 197 SPRaise("op_read_float failed with error code %s", 198 stringifyOpusErrorCode(result).c_str()); 199 } else if (result != 1) { 200 SPRaise("op_read_float returned %d (expected: 1)", result); 201 } 202 203 std::memcpy(retBytes, reinterpret_cast<const uint8_t *>(currentSample.data()), 204 static_cast<size_t>(remainingBytes)); 205 206 subsamplePosition = static_cast<int>(remainingBytes); 207 } 208 209 return bytes; 210 } 211 GetPosition()212 uint64_t OpusAudioStream::GetPosition() { 213 if (subsamplePosition) { 214 return (op_pcm_tell(opusFile) - 1) * channels * 4 + subsamplePosition; 215 } else { 216 return op_pcm_tell(opusFile) * channels * 4; 217 } 218 } 219 SetPosition(uint64_t pos)220 void OpusAudioStream::SetPosition(uint64_t pos) { 221 pos = std::min(pos, GetLength()); 222 223 op_pcm_seek(opusFile, static_cast<opus_int64>(pos / (channels * 4))); 224 225 subsamplePosition = static_cast<int>(pos % (channels * 4)); 226 if (subsamplePosition) { 227 int result = op_read_float(opusFile, currentSample.data(), channels, nullptr); 228 if (result < 0) { 229 SPRaise("op_read_float failed with error code %s", 230 stringifyOpusErrorCode(result).c_str()); 231 } else if (result != 1) { 232 SPRaise("op_read_float returned %d (expected: 1)", result); 233 } 234 } 235 } 236 } 237