diff --git a/client/controller.cpp b/client/controller.cpp index b21d5924..ecdf51dc 100644 --- a/client/controller.cpp +++ b/client/controller.cpp @@ -62,7 +62,7 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg:: { auto pcmChunk = make_unique(sampleFormat_, 0); pcmChunk->deserialize(baseMessage, buffer); - // LOG(DEBUG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.rate << "\n"; + // LOG(DEBUG) << "chunk: " << pcmChunk->payloadSize << ", sampleFormat: " << sampleFormat_.getFormat() << "\n"; if (decoder_->decode(pcmChunk.get())) { // TODO: do decoding in thread? @@ -119,7 +119,7 @@ void Controller::onMessageReceived(ClientConnection* /*connection*/, const msg:: throw SnapException("codec not supported: \"" + headerChunk_->codec + "\""); sampleFormat_ = decoder_->setHeader(headerChunk_.get()); - LOG(NOTICE) << TAG("state") << "sampleformat: " << sampleFormat_.rate << ":" << sampleFormat_.bits << ":" << sampleFormat_.channels << "\n"; + LOG(NOTICE) << TAG("state") << "sampleformat: " << sampleFormat_.getFormat() << "\n"; stream_ = make_shared(sampleFormat_, settings_.player.sample_format); stream_->setBufferLen(serverSettings_->getBufferMs() - settings_.player.latency); diff --git a/client/decoder/flac_decoder.cpp b/client/decoder/flac_decoder.cpp index ab6f398d..2068bbe5 100644 --- a/client/decoder/flac_decoder.cpp +++ b/client/decoder/flac_decoder.cpp @@ -91,12 +91,11 @@ bool FlacDecoder::decode(msg::PcmChunk* chunk) if ((cacheInfo_.cachedBlocks_ > 0) && (cacheInfo_.sampleRate_ != 0)) { - double diffMs = cacheInfo_.cachedBlocks_ / ((double)cacheInfo_.sampleRate_ / 1000.); - int32_t s = (diffMs / 1000); - int32_t us = (diffMs * 1000); - us %= 1000000; - LOG(DEBUG) << "Cached: " << cacheInfo_.cachedBlocks_ << ", " << diffMs << "ms, " << s << "s, " << us << "us\n"; - chunk->timestamp = chunk->timestamp - tv(s, us); + double diffMs = static_cast(cacheInfo_.cachedBlocks_) / (static_cast(cacheInfo_.sampleRate_) / 1000.); + auto us = static_cast(diffMs * 1000.); + tv diff(us / 1000000, us % 1000000); + LOG(DEBUG) << "Cached: " << cacheInfo_.cachedBlocks_ << ", " << diffMs << "ms, " << diff.sec << "s, " << diff.usec << "us\n"; + chunk->timestamp = chunk->timestamp - diff; } return true; } @@ -116,9 +115,8 @@ SampleFormat FlacDecoder::setHeader(msg::CodecHeader* chunk) if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) throw SnapException("ERROR: initializing decoder: " + string(FLAC__StreamDecoderInitStatusString[init_status])); - sampleFormat.rate = 0; FLAC__stream_decoder_process_until_end_of_metadata(decoder); - if (sampleFormat.rate == 0) + if (sampleFormat.rate() == 0) throw SnapException("Sample format not found"); return sampleFormat; @@ -158,7 +156,7 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder* /*decod { if (pcmChunk != nullptr) { - size_t bytes = frame->header.blocksize * sampleFormat.frameSize; + size_t bytes = frame->header.blocksize * sampleFormat.frameSize(); FlacDecoder* flacDecoder = static_cast(client_data); if (flacDecoder->cacheInfo_.isCachedChunk_) @@ -166,7 +164,7 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder* /*decod pcmChunk->payload = (char*)realloc(pcmChunk->payload, pcmChunk->payloadSize + bytes); - for (size_t channel = 0; channel < sampleFormat.channels; ++channel) + for (size_t channel = 0; channel < sampleFormat.channels(); ++channel) { if (buffer[channel] == nullptr) { @@ -174,23 +172,23 @@ FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder* /*decod return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; } - if (sampleFormat.sampleSize == 1) + if (sampleFormat.sampleSize() == 1) { int8_t* chunkBuffer = (int8_t*)(pcmChunk->payload + pcmChunk->payloadSize); for (size_t i = 0; i < frame->header.blocksize; i++) - chunkBuffer[sampleFormat.channels * i + channel] = (int8_t)(buffer[channel][i]); + chunkBuffer[sampleFormat.channels() * i + channel] = (int8_t)(buffer[channel][i]); } - else if (sampleFormat.sampleSize == 2) + else if (sampleFormat.sampleSize() == 2) { int16_t* chunkBuffer = (int16_t*)(pcmChunk->payload + pcmChunk->payloadSize); for (size_t i = 0; i < frame->header.blocksize; i++) - chunkBuffer[sampleFormat.channels * i + channel] = SWAP_16((int16_t)(buffer[channel][i])); + chunkBuffer[sampleFormat.channels() * i + channel] = SWAP_16((int16_t)(buffer[channel][i])); } - else if (sampleFormat.sampleSize == 4) + else if (sampleFormat.sampleSize() == 4) { int32_t* chunkBuffer = (int32_t*)(pcmChunk->payload + pcmChunk->payloadSize); for (size_t i = 0; i < frame->header.blocksize; i++) - chunkBuffer[sampleFormat.channels * i + channel] = SWAP_32((int32_t)(buffer[channel][i])); + chunkBuffer[sampleFormat.channels() * i + channel] = SWAP_32((int32_t)(buffer[channel][i])); } } pcmChunk->payloadSize += bytes; diff --git a/client/decoder/ogg_decoder.cpp b/client/decoder/ogg_decoder.cpp index 7ff7f862..b9c9da45 100644 --- a/client/decoder/ogg_decoder.cpp +++ b/client/decoder/ogg_decoder.cpp @@ -104,16 +104,16 @@ bool OggDecoder::decode(msg::PcmChunk* chunk) (-1.<=range<=1.) to whatever PCM format and write it out */ while ((samples = vorbis_synthesis_pcmout(&vd, &pcm)) > 0) { - size_t bytes = sampleFormat_.sampleSize * vi.channels * samples; + size_t bytes = sampleFormat_.sampleSize() * vi.channels * samples; chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize + bytes); for (int channel = 0; channel < vi.channels; ++channel) { - if (sampleFormat_.sampleSize == 1) + if (sampleFormat_.sampleSize() == 1) { int8_t* chunkBuffer = (int8_t*)(chunk->payload + chunk->payloadSize); for (int i = 0; i < samples; i++) { - int8_t& val = chunkBuffer[sampleFormat_.channels * i + channel]; + int8_t& val = chunkBuffer[sampleFormat_.channels() * i + channel]; #ifdef HAS_TREMOR val = clip(pcm[channel][i], -128, 127); #else @@ -121,12 +121,12 @@ bool OggDecoder::decode(msg::PcmChunk* chunk) #endif } } - else if (sampleFormat_.sampleSize == 2) + else if (sampleFormat_.sampleSize() == 2) { int16_t* chunkBuffer = (int16_t*)(chunk->payload + chunk->payloadSize); for (int i = 0; i < samples; i++) { - int16_t& val = chunkBuffer[sampleFormat_.channels * i + channel]; + int16_t& val = chunkBuffer[sampleFormat_.channels() * i + channel]; #ifdef HAS_TREMOR val = SWAP_16(clip(pcm[channel][i] >> 9, -32768, 32767)); #else @@ -134,12 +134,12 @@ bool OggDecoder::decode(msg::PcmChunk* chunk) #endif } } - else if (sampleFormat_.sampleSize == 4) + else if (sampleFormat_.sampleSize() == 4) { int32_t* chunkBuffer = (int32_t*)(chunk->payload + chunk->payloadSize); for (int i = 0; i < samples; i++) { - int32_t& val = chunkBuffer[sampleFormat_.channels * i + channel]; + int32_t& val = chunkBuffer[sampleFormat_.channels() * i + channel]; #ifdef HAS_TREMOR val = SWAP_32(clip(pcm[channel][i] << 7, -2147483648, 2147483647)); #else diff --git a/client/decoder/opus_decoder.cpp b/client/decoder/opus_decoder.cpp index 18c32694..f211f2d5 100644 --- a/client/decoder/opus_decoder.cpp +++ b/client/decoder/opus_decoder.cpp @@ -50,13 +50,13 @@ bool OpusDecoder::decode(msg::PcmChunk* chunk) { int frame_size = 0; - while ((frame_size = opus_decode(dec_, (unsigned char*)chunk->payload, chunk->payloadSize, pcm_.data(), pcm_.size() / sample_format_.channels, 0)) == + while ((frame_size = opus_decode(dec_, (unsigned char*)chunk->payload, chunk->payloadSize, pcm_.data(), pcm_.size() / sample_format_.channels(), 0)) == OPUS_BUFFER_TOO_SMALL) { - if (pcm_.size() < const_max_frame_size * sample_format_.channels) + if (pcm_.size() < const_max_frame_size * sample_format_.channels()) { pcm_.resize(pcm_.size() * 2); - LOG(INFO) << "OPUS encoding buffer too small, resizing to " << pcm_.size() / sample_format_.channels << " samples per channel\n"; + LOG(INFO) << "OPUS encoding buffer too small, resizing to " << pcm_.size() / sample_format_.channels() << " samples per channel\n"; } else break; @@ -72,7 +72,7 @@ bool OpusDecoder::decode(msg::PcmChunk* chunk) LOG(DEBUG) << "Decoded chunk: size " << chunk->payloadSize << " bytes, decoded " << frame_size << " samples" << '\n'; // copy encoded data to chunk - chunk->payloadSize = frame_size * sample_format_.channels * sizeof(opus_int16); + chunk->payloadSize = frame_size * sample_format_.channels() * sizeof(opus_int16); chunk->payload = (char*)realloc(chunk->payload, chunk->payloadSize); memcpy(chunk->payload, (char*)pcm_.data(), chunk->payloadSize); return true; @@ -105,7 +105,7 @@ SampleFormat OpusDecoder::setHeader(msg::CodecHeader* chunk) // create the decoder int error; - dec_ = opus_decoder_create(sample_format_.rate, sample_format_.channels, &error); + dec_ = opus_decoder_create(sample_format_.rate(), sample_format_.channels(), &error); if (error != 0) throw SnapException("Failed to initialize Opus decoder: " + std::string(opus_strerror(error))); diff --git a/client/player/alsa_player.cpp b/client/player/alsa_player.cpp index be9bcf82..1feae291 100644 --- a/client/player/alsa_player.cpp +++ b/client/player/alsa_player.cpp @@ -38,8 +38,8 @@ void AlsaPlayer::initAlsa() snd_pcm_hw_params_t* params; const SampleFormat& format = stream_->getFormat(); - rate = format.rate; - channels = format.channels; + rate = format.rate(); + channels = format.channels(); /* Open the PCM device in playback mode */ if ((pcm = snd_pcm_open(&handle_, pcmDevice_.name.c_str(), SND_PCM_STREAM_PLAYBACK, 0)) < 0) @@ -61,16 +61,16 @@ void AlsaPlayer::initAlsa() throw SnapException("Can't set interleaved mode: " + string(snd_strerror(pcm))); snd_pcm_format_t snd_pcm_format; - if (format.bits == 8) + if (format.bits() == 8) snd_pcm_format = SND_PCM_FORMAT_S8; - else if (format.bits == 16) + else if (format.bits() == 16) snd_pcm_format = SND_PCM_FORMAT_S16_LE; - else if ((format.bits == 24) && (format.sampleSize == 4)) + else if ((format.bits() == 24) && (format.sampleSize() == 4)) snd_pcm_format = SND_PCM_FORMAT_S24_LE; - else if (format.bits == 32) + else if (format.bits() == 32) snd_pcm_format = SND_PCM_FORMAT_S32_LE; else - throw SnapException("Unsupported sample format: " + cpt::to_string(format.bits)); + throw SnapException("Unsupported sample format: " + cpt::to_string(format.bits())); pcm = snd_pcm_hw_params_set_format(handle_, params, snd_pcm_format); if (pcm == -EINVAL) @@ -233,8 +233,8 @@ void AlsaPlayer::worker() chronos::usec delay(static_cast(1000 * (double)framesDelay / format.msRate())); // LOG(TRACE) << "delay: " << framesDelay << ", delay[ms]: " << delay.count() / 1000 << ", avail: " << framesAvail << "\n"; - if (buffer_.size() < static_cast(framesAvail * format.frameSize)) - buffer_.resize(framesAvail * format.frameSize); + if (buffer_.size() < static_cast(framesAvail * format.frameSize())) + buffer_.resize(framesAvail * format.frameSize()); if (stream_->getPlayerChunk(buffer_.data(), delay, framesAvail)) { lastChunkTick = chronos::getTickCount(); diff --git a/client/player/coreaudio_player.cpp b/client/player/coreaudio_player.cpp index 1a0a8f22..3b8a7930 100644 --- a/client/player/coreaudio_player.cpp +++ b/client/player/coreaudio_player.cpp @@ -155,12 +155,12 @@ void CoreAudioPlayer::initAudioQueue() const SampleFormat& sampleFormat = pubStream_->getFormat(); AudioStreamBasicDescription format; - format.mSampleRate = sampleFormat.rate; + format.mSampleRate = sampleFormat.rate(); format.mFormatID = kAudioFormatLinearPCM; format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger; // | kAudioFormatFlagIsPacked; format.mBitsPerChannel = sampleFormat.bits; - format.mChannelsPerFrame = sampleFormat.channels; - format.mBytesPerFrame = sampleFormat.frameSize; + format.mChannelsPerFrame = sampleFormat.channels(); + format.mBytesPerFrame = sampleFormat.frameSize(); format.mFramesPerPacket = 1; format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket; format.mReserved = 0; @@ -176,9 +176,9 @@ void CoreAudioPlayer::initAudioQueue() // // For 100ms @ 48000:16:2 we have 19.2K // frames: 4800, ms: 100, buffer size: 19200 - frames_ = (sampleFormat.rate * ms_) / 1000; - ms_ = frames_ * 1000 / sampleFormat.rate; - buff_size_ = frames_ * sampleFormat.frameSize; + frames_ = (sampleFormat.rate() * ms_) / 1000; + ms_ = frames_ * 1000 / sampleFormat.rate(); + buff_size_ = frames_ * sampleFormat.frameSize(); LOG(INFO) << "frames: " << frames_ << ", ms: " << ms_ << ", buffer size: " << buff_size_ << "\n"; AudioQueueBufferRef buffers[NUM_BUFFERS]; diff --git a/client/player/opensl_player.cpp b/client/player/opensl_player.cpp index a8bc30b2..a221fb4c 100644 --- a/client/player/opensl_player.cpp +++ b/client/player/opensl_player.cpp @@ -161,10 +161,10 @@ void OpenslPlayer::initOpensl() const SampleFormat& format = stream_->getFormat(); - frames_ = format.rate / (1000 / ms_); // * format.channels; // 1920; // 48000 * 2 / 50 // => 50ms + frames_ = format.rate() / (1000 / ms_); // * format.channels(); // 1920; // 48000 * 2 / 50 // => 50ms - buff_size = frames_ * format.frameSize /* 2 -> sample size */; - LOG(INFO, LOG_TAG) << "frames: " << frames_ << ", channels: " << format.channels << ", rate: " << format.rate << ", buff: " << buff_size << "\n"; + buff_size = frames_ * format.frameSize() /* 2 -> sample size */; + LOG(INFO, LOG_TAG) << "frames: " << frames_ << ", channels: " << format.channels() << ", rate: " << format.rate() << ", buff: " << buff_size << "\n"; SLresult result; // create engine @@ -181,7 +181,7 @@ void OpenslPlayer::initOpensl() throwUnsuccess(kPhaseInit, "OutputMixObject::Realize", result); SLuint32 samplesPerSec = SL_SAMPLINGRATE_48; - switch (format.rate) + switch (format.rate()) { case 8000: samplesPerSec = SL_SAMPLINGRATE_8; @@ -250,7 +250,7 @@ void OpenslPlayer::initOpensl() SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2}; SLDataFormat_PCM format_pcm = { - SL_DATAFORMAT_PCM, format.channels, samplesPerSec, bitsPerSample, containerSize, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, + SL_DATAFORMAT_PCM, format.channels(), samplesPerSec, bitsPerSample, containerSize, SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN}; SLDataSource audioSrc = {&loc_bufq, &format_pcm}; diff --git a/client/player/player.cpp b/client/player/player.cpp index 7829d7e0..85d42125 100644 --- a/client/player/player.cpp +++ b/client/player/player.cpp @@ -68,12 +68,12 @@ void Player::adjustVolume(char* buffer, size_t frames) if ((volume < 1.0) || (volCorrection_ != 1.)) { volume *= volCorrection_; - if (sampleFormat.sampleSize == 1) - adjustVolume(buffer, frames * sampleFormat.channels, volume); - else if (sampleFormat.sampleSize == 2) - adjustVolume(buffer, frames * sampleFormat.channels, volume); - else if (sampleFormat.sampleSize == 4) - adjustVolume(buffer, frames * sampleFormat.channels, volume); + if (sampleFormat.sampleSize() == 1) + adjustVolume(buffer, frames * sampleFormat.channels(), volume); + else if (sampleFormat.sampleSize() == 2) + adjustVolume(buffer, frames * sampleFormat.channels(), volume); + else if (sampleFormat.sampleSize() == 4) + adjustVolume(buffer, frames * sampleFormat.channels(), volume); } } diff --git a/client/stream.cpp b/client/stream.cpp index 031912c2..00bc0135 100644 --- a/client/stream.cpp +++ b/client/stream.cpp @@ -38,7 +38,7 @@ Stream::Stream(const SampleFormat& in_format, const SampleFormat& out_format) shortBuffer_.setSize(100); miniBuffer_.setSize(20); - if (out_format.rate != 0) + if (out_format.rate() != 0) format_ = out_format; else format_ = in_format_; @@ -50,30 +50,30 @@ Stream::Stream(const SampleFormat& in_format, const SampleFormat& out_format) x = 1,000016667 / (1,000016667 - 1) */ - // setRealSampleRate(format_.rate); + // setRealSampleRate(format_.rate()); - if ((format_.rate != in_format_.rate) || (format_.bits != in_format_.bits)) + if ((format_.rate() != in_format_.rate()) || (format_.bits() != in_format_.bits())) { LOG(INFO, LOG_TAG) << "Resampling from " << in_format_.getFormat() << " to " << format_.getFormat() << "\n"; soxr_error_t error; soxr_datatype_t in_type = SOXR_INT16_I; soxr_datatype_t out_type = SOXR_INT16_I; - if (in_format_.sampleSize > 2) + if (in_format_.sampleSize() > 2) in_type = SOXR_INT32_I; - if (format_.sampleSize > 2) + if (format_.sampleSize() > 2) out_type = SOXR_INT32_I; soxr_io_spec_t iospec = soxr_io_spec(in_type, out_type); // HQ should be fine: http://sox.sourceforge.net/Docs/FAQ soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, 0); - soxr_ = soxr_create(static_cast(in_format_.rate), static_cast(format_.rate), format_.channels, &error, &iospec, &q_spec, NULL); + soxr_ = soxr_create(static_cast(in_format_.rate()), static_cast(format_.rate()), format_.channels(), &error, &iospec, &q_spec, NULL); if (error) { LOG(ERROR, LOG_TAG) << "Error soxr_create: " << error << "\n"; soxr_ = nullptr; } // initialize the buffer with 20ms (~latency of the reampler) - resample_buffer_.resize(format_.frameSize * ceil(format_.msRate()) * 20); + resample_buffer_.resize(format_.frameSize() * ceil(format_.msRate()) * 20); } } @@ -87,14 +87,14 @@ Stream::~Stream() void Stream::setRealSampleRate(double sampleRate) { - if (sampleRate == format_.rate) + if (sampleRate == format_.rate()) { correctAfterXFrames_ = 0; } else { - correctAfterXFrames_ = round((format_.rate / sampleRate) / (format_.rate / sampleRate - 1.)); - // LOG(TRACE, LOG_TAG) << "Correct after X: " << correctAfterXFrames_ << " (Real rate: " << sampleRate << ", rate: " << format_.rate << ")\n"; + correctAfterXFrames_ = round((format_.rate() / sampleRate) / (format_.rate() / sampleRate - 1.)); + // LOG(TRACE, LOG_TAG) << "Correct after X: " << correctAfterXFrames_ << " (Real rate: " << sampleRate << ", rate: " << format_.rate() << ")\n"; } } @@ -135,7 +135,7 @@ void Stream::addChunk(unique_ptr chunk) } else { - if (in_format_.bits == 24) + if (in_format_.bits() == 24) { // sox expects 32 bit input, shift 8 bits left int32_t* frames = (int32_t*)chunk->payload; @@ -145,7 +145,7 @@ void Stream::addChunk(unique_ptr chunk) size_t idone; size_t odone; - auto resample_buffer_framesize = resample_buffer_.size() / format_.frameSize; + auto resample_buffer_framesize = resample_buffer_.size() / format_.frameSize(); auto error = soxr_process(soxr_, chunk->payload, chunk->getFrameCount(), &idone, resample_buffer_.data(), resample_buffer_framesize, &odone); if (error) { @@ -154,7 +154,7 @@ void Stream::addChunk(unique_ptr chunk) else { LOG(TRACE, LOG_TAG) << "Resample idone: " << idone << "/" << chunk->getFrameCount() << ", odone: " << odone << "/" - << resample_buffer_.size() / format_.frameSize << ", delay: " << soxr_delay(soxr_) << "\n"; + << resample_buffer_.size() / format_.frameSize() << ", delay: " << soxr_delay(soxr_) << "\n"; // some data has been resampled (odone frames) and some is still in the pipe (soxr_delay frames) if (odone > 0) @@ -170,11 +170,11 @@ void Stream::addChunk(unique_ptr chunk) resampled_chunk->timestamp.usec = us % 1000000; // copy from the resample_buffer to the resampled chunk - resampled_chunk->payloadSize = odone * format_.frameSize; + resampled_chunk->payloadSize = odone * format_.frameSize(); resampled_chunk->payload = (char*)realloc(resampled_chunk->payload, resampled_chunk->payloadSize); memcpy(resampled_chunk->payload, resample_buffer_.data(), resampled_chunk->payloadSize); - if (format_.bits == 24) + if (format_.bits() == 24) { // sox has quantized to 32 bit, shift 8 bits right int32_t* frames = (int32_t*)resampled_chunk->payload; @@ -192,7 +192,7 @@ void Stream::addChunk(unique_ptr chunk) if (odone == resample_buffer_framesize) { // buffer for resampled data too small, add space for 5ms - resample_buffer_.resize(resample_buffer_.size() + format_.frameSize * ceil(format_.msRate()) * 5); + resample_buffer_.resize(resample_buffer_.size() + format_.frameSize() * ceil(format_.msRate()) * 5); LOG(DEBUG, LOG_TAG) << "Resample buffer completely filled, adding space for 5ms; new buffer size: " << resample_buffer_.size() << " bytes\n"; } @@ -216,7 +216,7 @@ bool Stream::waitForChunk(const std::chrono::milliseconds& timeout) const void Stream::getSilentPlayerChunk(void* outputBuffer, uint32_t frames) const { - memset(outputBuffer, 0, frames * format_.frameSize); + memset(outputBuffer, 0, frames * format_.frameSize()); } @@ -229,7 +229,7 @@ cs::time_point_clk Stream::getNextPlayerChunk(void* outputBuffer, uint32_t frame uint32_t read = 0; while (read < frames) { - read += chunk_->readFrames(static_cast(outputBuffer) + read * format_.frameSize, frames - read); + read += chunk_->readFrames(static_cast(outputBuffer) + read * format_.frameSize(), frames - read); if (chunk_->isEndOfChunk() && !chunks_.try_pop(chunk_)) throw 0; } @@ -251,8 +251,8 @@ cs::time_point_clk Stream::getNextPlayerChunk(void* outputBuffer, uint32_t frame frame_delta_ -= framesCorrection; uint32_t toRead = frames + framesCorrection; - if (toRead * format_.frameSize > read_buffer_.size()) - read_buffer_.resize(toRead * format_.frameSize); + if (toRead * format_.frameSize() > read_buffer_.size()) + read_buffer_.resize(toRead * format_.frameSize()); cs::time_point_clk tp = getNextPlayerChunk(read_buffer_.data(), toRead); const auto max = framesCorrection < 0 ? frames : toRead; @@ -284,14 +284,16 @@ cs::time_point_clk Stream::getNextPlayerChunk(void* outputBuffer, uint32_t frame // Read one frame less per slice from the input, but write a duplicated frame per slice to the output // LOG(TRACE, LOG_TAG) << "duplicate - requested: " << frames << ", read: " << toRead << ", slice: " << n << ", size: " << size << ", out pos: " << // pos << ", source pos: " << pos - n << "\n"; - memcpy(static_cast(outputBuffer) + pos * format_.frameSize, read_buffer_.data() + (pos - n) * format_.frameSize, size * format_.frameSize); + memcpy(static_cast(outputBuffer) + pos * format_.frameSize(), read_buffer_.data() + (pos - n) * format_.frameSize(), + size * format_.frameSize()); } else { // Read all input frames, but skip a frame per slice when writing to the output. // LOG(TRACE, LOG_TAG) << "remove - requested: " << frames << ", read: " << toRead << ", slice: " << n << ", size: " << size << ", out pos: " << pos // - n << ", source pos: " << pos << "\n"; - memcpy(static_cast(outputBuffer) + (pos - n) * format_.frameSize, read_buffer_.data() + pos * format_.frameSize, size * format_.frameSize); + memcpy(static_cast(outputBuffer) + (pos - n) * format_.frameSize(), read_buffer_.data() + pos * format_.frameSize(), + size * format_.frameSize()); } pos += size; } @@ -381,7 +383,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT LOG(DEBUG, LOG_TAG) << "Silent frames: " << silent_frames << ", frames: " << frames << ", age: " << std::chrono::duration_cast(age).count() / 1000. << "\n"; getSilentPlayerChunk(outputBuffer, silent_frames); - getNextPlayerChunk((char*)outputBuffer + (chunk_->format.frameSize * silent_frames), frames - silent_frames); + getNextPlayerChunk((char*)outputBuffer + (chunk_->format.frameSize() * silent_frames), frames - silent_frames); if (result) { @@ -410,7 +412,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT cs::usec age = std::chrono::duration_cast(TimeProvider::serverNow() - getNextPlayerChunk(outputBuffer, frames, framesCorrection) - bufferMs_ + outputBufferDacTime); - setRealSampleRate(format_.rate); + setRealSampleRate(format_.rate()); // check if we need a hard sync if (buffer_.full() && (cs::usec(abs(median_)) > cs::msec(2))) { @@ -444,7 +446,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT // LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n"; // we are late (age > 0), this means we are not playing fast enough // => the real sample rate seems to be lower, we have to drop some frames - setRealSampleRate(format_.rate * rate); // 0.9999); + setRealSampleRate(format_.rate() * rate); // 0.9999); } else if ((cs::usec(shortMedian_) < -kCorrectionBegin) && (cs::usec(miniMedian) < -cs::usec(50)) && (cs::usec(age) < -cs::usec(50))) { @@ -453,7 +455,7 @@ bool Stream::getPlayerChunk(void* outputBuffer, const cs::usec& outputBufferDacT // LOG(INFO, LOG_TAG) << "Rate: " << rate << "\n"; // we are early (age > 0), this means we are playing too fast // => the real sample rate seems to be higher, we have to insert some frames - setRealSampleRate(format_.rate * rate); // 1.0001); + setRealSampleRate(format_.rate() * rate); // 1.0001); } } diff --git a/common/message/pcm_chunk.hpp b/common/message/pcm_chunk.hpp index 30b8719d..bfefdd59 100644 --- a/common/message/pcm_chunk.hpp +++ b/common/message/pcm_chunk.hpp @@ -36,7 +36,7 @@ namespace msg class PcmChunk : public WireChunk { public: - PcmChunk(const SampleFormat& sampleFormat, size_t ms) : WireChunk(sampleFormat.rate * sampleFormat.frameSize * ms / 1000), format(sampleFormat), idx_(0) + PcmChunk(const SampleFormat& sampleFormat, size_t ms) : WireChunk(sampleFormat.rate() * sampleFormat.frameSize() * ms / 1000), format(sampleFormat), idx_(0) { } @@ -59,18 +59,18 @@ public: int readFrames(void* outputBuffer, size_t frameCount) { - // logd << "read: " << frameCount << ", total: " << (wireChunk->length / format.frameSize) << ", idx: " << idx;// << std::endl; + // logd << "read: " << frameCount << ", total: " << (wireChunk->length / format.frameSize()) << ", idx: " << idx;// << std::endl; int result = frameCount; - if (idx_ + frameCount > (payloadSize / format.frameSize)) - result = (payloadSize / format.frameSize) - idx_; + if (idx_ + frameCount > (payloadSize / format.frameSize())) + result = (payloadSize / format.frameSize()) - idx_; - // logd << ", from: " << format.frameSize*idx << ", to: " << format.frameSize*idx + format.frameSize*result; + // logd << ", from: " << format.frameSize()*idx << ", to: " << format.frameSize()*idx + format.frameSize()*result; if (outputBuffer != nullptr) - memcpy((char*)outputBuffer, (char*)(payload) + format.frameSize * idx_, format.frameSize * result); + memcpy((char*)outputBuffer, (char*)(payload) + format.frameSize() * idx_, format.frameSize() * result); idx_ += result; - // logd << ", new idx: " << idx << ", result: " << result << ", wireChunk->length: " << wireChunk->length << ", format.frameSize: " << format.frameSize - // << "\n";//std::endl; + // logd << ", new idx: " << idx << ", result: " << result << ", wireChunk->length: " << wireChunk->length << ", format.frameSize(): " << + // format.frameSize() << "\n"; return result; } @@ -90,7 +90,7 @@ public: chronos::time_point_clk start() const override { return chronos::time_point_clk(chronos::sec(timestamp.sec) + chronos::usec(timestamp.usec) + - chronos::usec(static_cast(1000000. * ((double)idx_ / (double)format.rate)))); + chronos::usec(static_cast(1000000. * ((double)idx_ / (double)format.rate())))); } inline chronos::time_point_clk end() const @@ -117,12 +117,12 @@ public: inline size_t getFrameCount() const { - return (payloadSize / format.frameSize); + return (payloadSize / format.frameSize()); } inline size_t getSampleCount() const { - return (payloadSize / format.sampleSize); + return (payloadSize / format.sampleSize()); } SampleFormat format; diff --git a/common/sample_format.cpp b/common/sample_format.cpp index c86615d9..91f15519 100644 --- a/common/sample_format.cpp +++ b/common/sample_format.cpp @@ -51,7 +51,7 @@ SampleFormat::SampleFormat(uint32_t sampleRate, uint16_t bitsPerSample, uint16_t string SampleFormat::getFormat() const { stringstream ss; - ss << rate << ":" << bits << ":" << channels; + ss << rate_ << ":" << bits_ << ":" << channels_; return ss.str(); } @@ -70,12 +70,12 @@ void SampleFormat::setFormat(uint32_t rate, uint16_t bits, uint16_t channels) // needs something like: // 24_4 = 3 bytes, padded to 4 // 32 = 4 bytes - this->rate = rate; - this->bits = bits; - this->channels = channels; - sampleSize = bits / 8; - if (bits == 24) - sampleSize = 4; - frameSize = channels * sampleSize; + rate_ = rate; + bits_ = bits; + channels_ = channels; + sample_size_ = bits / 8; + if (bits_ == 24) + sample_size_ = 4; + frame_size_ = channels_ * sample_size_; // LOG(DEBUG) << "SampleFormat: " << rate << ":" << bits << ":" << channels << "\n"; } diff --git a/common/sample_format.hpp b/common/sample_format.hpp index 1de56b3e..eb7f100c 100644 --- a/common/sample_format.hpp +++ b/common/sample_format.hpp @@ -46,30 +46,54 @@ public: void setFormat(const std::string& format); void setFormat(uint32_t rate, uint16_t bits, uint16_t channels); - uint32_t rate; - uint16_t bits; - uint16_t channels; + uint32_t rate() const + { + return rate_; + } + + uint16_t bits() const + { + return bits_; + } + + uint16_t channels() const + { + return channels_; + } // size in [bytes] of a single mono sample, e.g. 2 bytes (= 16 bits) - uint16_t sampleSize; + uint16_t sampleSize() const + { + return sample_size_; + } // size in [bytes] of a frame (sum of sample sizes = #channel*sampleSize), e.g. 4 bytes (= 2 channel * 16 bit) - uint16_t frameSize; + uint16_t frameSize() const + { + return frame_size_; + } inline double msRate() const { - return (double)rate / 1000.; + return (double)rate_ / 1000.; } inline double usRate() const { - return (double)rate / 1000000.; + return (double)rate_ / 1000000.; } inline double nsRate() const { - return (double)rate / 1000000000.; + return (double)rate_ / 1000000000.; } + +private: + uint16_t sample_size_; + uint16_t frame_size_; + uint32_t rate_; + uint16_t bits_; + uint16_t channels_; }; diff --git a/server/encoder/flac_encoder.cpp b/server/encoder/flac_encoder.cpp index a20d1040..567aa88a 100644 --- a/server/encoder/flac_encoder.cpp +++ b/server/encoder/flac_encoder.cpp @@ -82,19 +82,19 @@ void FlacEncoder::encode(const msg::PcmChunk* chunk) pcmBuffer_ = (FLAC__int32*)realloc(pcmBuffer_, pcmBufferSize_ * sizeof(FLAC__int32)); } - if (sampleFormat_.sampleSize == 1) + if (sampleFormat_.sampleSize() == 1) { FLAC__int8* buffer = (FLAC__int8*)chunk->payload; for (int i = 0; i < samples; i++) pcmBuffer_[i] = (FLAC__int32)(buffer[i]); } - else if (sampleFormat_.sampleSize == 2) + else if (sampleFormat_.sampleSize() == 2) { FLAC__int16* buffer = (FLAC__int16*)chunk->payload; for (int i = 0; i < samples; i++) pcmBuffer_[i] = (FLAC__int32)(buffer[i]); } - else if (sampleFormat_.sampleSize == 4) + else if (sampleFormat_.sampleSize() == 4) { FLAC__int32* buffer = (FLAC__int32*)chunk->payload; for (int i = 0; i < samples; i++) @@ -106,7 +106,7 @@ void FlacEncoder::encode(const msg::PcmChunk* chunk) if (encodedSamples_ > 0) { - double resMs = encodedSamples_ / ((double)sampleFormat_.rate / 1000.); + double resMs = encodedSamples_ / sampleFormat_.msRate(); // LOG(INFO) << "encoded: " << chunk->payloadSize << "\tframes: " << encodedSamples_ << "\tres: " << resMs << "\n"; encodedSamples_ = 0; listener_->onChunkEncoded(this, flacChunk_, resMs); @@ -176,9 +176,9 @@ void FlacEncoder::initEncoder() // 0-2: 1152 frames, ~26.1224ms // 3-8: 4096 frames, ~92.8798ms ok &= FLAC__stream_encoder_set_compression_level(encoder_, quality); - ok &= FLAC__stream_encoder_set_channels(encoder_, sampleFormat_.channels); - ok &= FLAC__stream_encoder_set_bits_per_sample(encoder_, sampleFormat_.bits); - ok &= FLAC__stream_encoder_set_sample_rate(encoder_, sampleFormat_.rate); + ok &= FLAC__stream_encoder_set_channels(encoder_, sampleFormat_.channels()); + ok &= FLAC__stream_encoder_set_bits_per_sample(encoder_, sampleFormat_.bits()); + ok &= FLAC__stream_encoder_set_sample_rate(encoder_, sampleFormat_.rate()); if (!ok) throw SnapException("error setting up encoder"); diff --git a/server/encoder/ogg_encoder.cpp b/server/encoder/ogg_encoder.cpp index c411479d..c3e3daaa 100644 --- a/server/encoder/ogg_encoder.cpp +++ b/server/encoder/ogg_encoder.cpp @@ -73,25 +73,25 @@ void OggEncoder::encode(const msg::PcmChunk* chunk) float** buffer = vorbis_analysis_buffer(&vd_, frames); /* uninterleave samples */ - for (size_t channel = 0; channel < sampleFormat_.channels; ++channel) + for (size_t channel = 0; channel < sampleFormat_.channels(); ++channel) { - if (sampleFormat_.sampleSize == 1) + if (sampleFormat_.sampleSize() == 1) { int8_t* chunkBuffer = (int8_t*)chunk->payload; for (int i = 0; i < frames; i++) - buffer[channel][i] = chunkBuffer[sampleFormat_.channels * i + channel] / 128.f; + buffer[channel][i] = chunkBuffer[sampleFormat_.channels() * i + channel] / 128.f; } - else if (sampleFormat_.sampleSize == 2) + else if (sampleFormat_.sampleSize() == 2) { int16_t* chunkBuffer = (int16_t*)chunk->payload; for (int i = 0; i < frames; i++) - buffer[channel][i] = chunkBuffer[sampleFormat_.channels * i + channel] / 32768.f; + buffer[channel][i] = chunkBuffer[sampleFormat_.channels() * i + channel] / 32768.f; } - else if (sampleFormat_.sampleSize == 4) + else if (sampleFormat_.sampleSize() == 4) { int32_t* chunkBuffer = (int32_t*)chunk->payload; for (int i = 0; i < frames; i++) - buffer[channel][i] = chunkBuffer[sampleFormat_.channels * i + channel] / 2147483648.f; + buffer[channel][i] = chunkBuffer[sampleFormat_.channels() * i + channel] / 2147483648.f; } } @@ -141,7 +141,7 @@ void OggEncoder::encode(const msg::PcmChunk* chunk) if (res > 0) { - res /= (sampleFormat_.rate / 1000.); + res /= sampleFormat_.msRate(); // LOG(INFO) << "res: " << res << "\n"; lastGranulepos_ = os_.granulepos; // make oggChunk smaller @@ -209,7 +209,7 @@ void OggEncoder::initEncoder() *********************************************************************/ - int ret = vorbis_encode_init_vbr(&vi_, sampleFormat_.channels, sampleFormat_.rate, quality); + int ret = vorbis_encode_init_vbr(&vi_, sampleFormat_.channels(), sampleFormat_.rate(), quality); /* do not continue if setup failed; this can happen if we ask for a mode that libVorbis does not support (eg, too low a bitrate, etc, diff --git a/server/encoder/opus_encoder.cpp b/server/encoder/opus_encoder.cpp index cd1d3102..9af2a910 100644 --- a/server/encoder/opus_encoder.cpp +++ b/server/encoder/opus_encoder.cpp @@ -77,7 +77,7 @@ void OpusEncoder::initEncoder() { // Opus is quite restrictive in sample rate and bit depth // It can handle mono signals, but we will check for stereo - if ((sampleFormat_.rate != 48000) || (sampleFormat_.bits != 16) || (sampleFormat_.channels != 2)) + if ((sampleFormat_.rate() != 48000) || (sampleFormat_.bits() != 16) || (sampleFormat_.channels() != 2)) throw SnapException("Opus sampleformat must be 48000:16:2"); opus_int32 bitrate = 192000; @@ -135,7 +135,7 @@ void OpusEncoder::initEncoder() LOG(INFO) << "Opus bitrate: " << bitrate << " bps, complexity: " << complexity << "\n"; int error; - enc_ = opus_encoder_create(sampleFormat_.rate, sampleFormat_.channels, OPUS_APPLICATION_RESTRICTED_LOWDELAY, &error); + enc_ = opus_encoder_create(sampleFormat_.rate(), sampleFormat_.channels(), OPUS_APPLICATION_RESTRICTED_LOWDELAY, &error); if (error != 0) { throw SnapException("Failed to initialize Opus encoder: " + std::string(opus_strerror(error))); @@ -149,9 +149,9 @@ void OpusEncoder::initEncoder() headerChunk_->payload = (char*)realloc(headerChunk_->payload, headerChunk_->payloadSize); char* payload = headerChunk_->payload; assign(payload, SWAP_32(ID_OPUS)); - assign(payload + 4, SWAP_32(sampleFormat_.rate)); - assign(payload + 8, SWAP_16(sampleFormat_.bits)); - assign(payload + 10, SWAP_16(sampleFormat_.channels)); + assign(payload + 4, SWAP_32(sampleFormat_.rate())); + assign(payload + 8, SWAP_16(sampleFormat_.bits())); + assign(payload + 10, SWAP_16(sampleFormat_.channels())); remainder_ = std::make_unique(sampleFormat_, 10); remainder_max_size_ = remainder_->payloadSize; @@ -192,7 +192,7 @@ void OpusEncoder::encode(const msg::PcmChunk* chunk) std::vector chunk_durations{60, 40, 20, 10}; for (const auto duration : chunk_durations) { - auto ms2bytes = [this](size_t ms) { return (ms * sampleFormat_.msRate() * sampleFormat_.frameSize); }; + auto ms2bytes = [this](size_t ms) { return (ms * sampleFormat_.msRate() * sampleFormat_.frameSize()); }; uint32_t bytes = ms2bytes(duration); while (chunk->payloadSize - offset >= bytes) { @@ -217,7 +217,7 @@ void OpusEncoder::encode(const SampleFormat& format, const char* data, size_t si { // void* buffer; // LOG(INFO) << "frames: " << chunk->readFrames(buffer, std::chrono::milliseconds(10)) << "\n"; - int samples_per_channel = size / format.frameSize; + int samples_per_channel = size / format.frameSize(); if (encoded_.size() < size) encoded_.resize(size); @@ -231,7 +231,7 @@ void OpusEncoder::encode(const SampleFormat& format, const char* data, size_t si opusChunk->payloadSize = len; opusChunk->payload = (char*)realloc(opusChunk->payload, opusChunk->payloadSize); memcpy(opusChunk->payload, encoded_.data(), len); - listener_->onChunkEncoded(this, opusChunk, (double)samples_per_channel / ((double)sampleFormat_.rate / 1000.)); + listener_->onChunkEncoded(this, opusChunk, (double)samples_per_channel / sampleFormat_.msRate()); } else { diff --git a/server/encoder/pcm_encoder.cpp b/server/encoder/pcm_encoder.cpp index 203f2ac7..4553a164 100644 --- a/server/encoder/pcm_encoder.cpp +++ b/server/encoder/pcm_encoder.cpp @@ -65,11 +65,11 @@ void PcmEncoder::initEncoder() assign(payload + 12, SWAP_32(ID_FMT)); assign(payload + 16, SWAP_32(16)); assign(payload + 20, SWAP_16(1)); - assign(payload + 22, SWAP_16(sampleFormat_.channels)); - assign(payload + 24, SWAP_32(sampleFormat_.rate)); - assign(payload + 28, SWAP_32(sampleFormat_.rate * sampleFormat_.bits * sampleFormat_.channels / 8)); - assign(payload + 32, SWAP_16(sampleFormat_.channels * ((sampleFormat_.bits + 7) / 8))); - assign(payload + 34, SWAP_16(sampleFormat_.bits)); + assign(payload + 22, SWAP_16(sampleFormat_.channels())); + assign(payload + 24, SWAP_32(sampleFormat_.rate())); + assign(payload + 28, SWAP_32(sampleFormat_.rate() * sampleFormat_.bits() * sampleFormat_.channels() / 8)); + assign(payload + 32, SWAP_16(sampleFormat_.channels() * ((sampleFormat_.bits() + 7) / 8))); + assign(payload + 34, SWAP_16(sampleFormat_.bits())); assign(payload + 36, SWAP_32(ID_DATA)); assign(payload + 40, SWAP_32(0)); } diff --git a/server/streamreader/posix_stream.cpp b/server/streamreader/posix_stream.cpp index c61af7d7..8e87e217 100644 --- a/server/streamreader/posix_stream.cpp +++ b/server/streamreader/posix_stream.cpp @@ -52,7 +52,7 @@ void PosixStream::connect() return; idle_bytes_ = 0; - max_idle_bytes_ = sampleFormat_.rate * sampleFormat_.frameSize * dryout_ms_ / 1000; + max_idle_bytes_ = sampleFormat_.rate() * sampleFormat_.frameSize() * dryout_ms_ / 1000; try { @@ -89,7 +89,7 @@ void PosixStream::do_read() { // nothing to read for a longer time now, set the chunk to silent LOG(DEBUG, LOG_TAG) << "count < 0: " << errno - << " && idleBytes < maxIdleBytes, ms: " << 1000 * chunk_->payloadSize / (sampleFormat_.rate * sampleFormat_.frameSize) + << " && idleBytes < maxIdleBytes, ms: " << 1000 * chunk_->payloadSize / (sampleFormat_.rate() * sampleFormat_.frameSize()) << "\n"; memset(chunk_->payload + len, 0, toRead - len); idle_bytes_ += toRead - len;