Result SoundSourceWV::tryOpen(const AudioSourceConfig& audioSrcCfg) { DEBUG_ASSERT(!m_wpc); char msg[80]; // hold possible error message int openFlags = OPEN_WVC | OPEN_NORMALIZE; if ((kChannelCountMono == audioSrcCfg.channelCountHint) || (kChannelCountStereo == audioSrcCfg.channelCountHint)) { openFlags |= OPEN_2CH_MAX; } m_wpc = WavpackOpenFileInput( getLocalFileNameBytes().constData(), msg, openFlags, 0); if (!m_wpc) { qDebug() << "SSWV::open: failed to open file : " << msg; return ERR; } setChannelCount(WavpackGetReducedChannels(m_wpc)); setFrameRate(WavpackGetSampleRate(m_wpc)); setFrameCount(WavpackGetNumSamples(m_wpc)); if (WavpackGetMode(m_wpc) & MODE_FLOAT) { m_sampleScaleFactor = CSAMPLE_PEAK; } else { const int bitsPerSample = WavpackGetBitsPerSample(m_wpc); const uint32_t wavpackPeakSampleValue = uint32_t(1) << (bitsPerSample - 1); m_sampleScaleFactor = CSAMPLE_PEAK / CSAMPLE(wavpackPeakSampleValue); } return OK; }
SINT SoundSourceWV::readSampleFrames( SINT numberOfFrames, CSAMPLE* sampleBuffer) { if (sampleBuffer == nullptr) { // NOTE(uklotzde): The WavPack API does not provide any // functions for skipping samples in the audio stream. Calling // API functions with a nullptr buffer does not return. Since // we don't want to read samples into a temporary buffer that // has to be allocated we are seeking to the position after // the skipped samples. SINT curFrameIndexBefore = m_curFrameIndex; SINT curFrameIndexAfter = seekSampleFrame(m_curFrameIndex + numberOfFrames); DEBUG_ASSERT(curFrameIndexBefore <= curFrameIndexAfter); DEBUG_ASSERT(m_curFrameIndex == curFrameIndexAfter); return curFrameIndexAfter - curFrameIndexBefore; } // static assert: sizeof(CSAMPLE) == sizeof(int32_t) SINT unpackCount = WavpackUnpackSamples(m_wpc, reinterpret_cast<int32_t*>(sampleBuffer), numberOfFrames); DEBUG_ASSERT(unpackCount >= 0); if (!(WavpackGetMode(m_wpc) & MODE_FLOAT)) { // signed integer -> float const SINT sampleCount = frames2samples(unpackCount); for (SINT i = 0; i < sampleCount; ++i) { const int32_t sampleValue = reinterpret_cast<int32_t*>(sampleBuffer)[i]; sampleBuffer[i] = CSAMPLE(sampleValue) * m_sampleScaleFactor; } } m_curFrameIndex += unpackCount; return unpackCount; }
SoundSource::OpenResult SoundSourceWV::tryOpen( OpenMode /*mode*/, const OpenParams& params) { DEBUG_ASSERT(!m_wpc); char msg[80]; // hold possible error message int openFlags = OPEN_WVC | OPEN_NORMALIZE; if ((params.channelCount() == 1) || (params.channelCount() == 2)) { openFlags |= OPEN_2CH_MAX; } // We use WavpackOpenFileInputEx to support Unicode paths on windows // http://www.wavpack.com/lib_use.txt QString wavPackFileName = getLocalFileName(); m_pWVFile = new QFile(wavPackFileName); m_pWVFile->open(QFile::ReadOnly); QString correctionFileName(wavPackFileName + "c"); if (QFile::exists(correctionFileName)) { // If there is a correction file, open it as well m_pWVCFile = new QFile(correctionFileName); m_pWVCFile->open(QFile::ReadOnly); } m_wpc = WavpackOpenFileInputEx(&s_streamReader, m_pWVFile, m_pWVCFile, msg, openFlags, 0); if (!m_wpc) { kLogger.warning() << "failed to open file : " << msg; return OpenResult::Failed; } setChannelCount(WavpackGetReducedChannels(m_wpc)); setSampleRate(WavpackGetSampleRate(m_wpc)); initFrameIndexRangeOnce( mixxx::IndexRange::forward( 0, WavpackGetNumSamples(m_wpc))); if (WavpackGetMode(m_wpc) & MODE_FLOAT) { m_sampleScaleFactor = CSAMPLE_PEAK; } else { const int bitsPerSample = WavpackGetBitsPerSample(m_wpc); if ((bitsPerSample >= 8) && (bitsPerSample <= 32)) { // Range of signed sample values: [-2 ^ (bitsPerSample - 1), 2 ^ (bitsPerSample - 1) - 1] const uint32_t absSamplePeak = 1u << (bitsPerSample - 1); DEBUG_ASSERT(absSamplePeak > 0); // Scaled range of sample values: [-CSAMPLE_PEAK, CSAMPLE_PEAK) m_sampleScaleFactor = CSAMPLE_PEAK / absSamplePeak; } else { kLogger.warning() << "Invalid bits per sample:" << bitsPerSample; return OpenResult::Aborted; } } m_curFrameIndex = frameIndexMin(); return OpenResult::Succeeded; }
void process_buffer(size_t num_samples) { int tsamples = num_samples * num_channels; if (!(WavpackGetMode (ctx) & MODE_FLOAT)) { float scaler = (float) (1.0 / ((unsigned int32_t) 1 << (bytes_per_sample * 8 - 1))); float *fptr = (float *) input; int32_t *lptr = input; int cnt = tsamples; while (cnt--) *fptr++ = *lptr++ * scaler; } if (play_gain != 1.0) { float *fptr = (float *) input; int cnt = tsamples; while (cnt--) *fptr++ *= play_gain; } if (tsamples) { float *fptr = (float *) input; short *sptr = (short *) output; int cnt = num_samples, ch; while (cnt--) for (ch = 0; ch < num_channels; ++ch) { int dst; *fptr -= shaping_error [ch]; if (*fptr >= 1.0) dst = 32767; else if (*fptr <= -1.0) dst = -32768; else dst = (int) floor (*fptr * 32768.0); shaping_error [ch] = (float)(dst / 32768.0 - *fptr++); *sptr++ = dst; } } if (EQ_on) iir ((char *) output, tsamples * sizeof(int16_t)); mod->add_vis_pcm(mod->output->written_time(), FMT_S16_NE, num_channels, tsamples * sizeof(int16_t), output); mod->output->write_audio(output, tsamples * sizeof(int16_t)); }
static void wav_data_init (struct wavpack_data *data) { data->sample_num = WavpackGetNumSamples (data->wpc); data->sample_rate = WavpackGetSampleRate (data->wpc); data->channels = WavpackGetReducedChannels (data->wpc); data->duration = data->sample_num / data->sample_rate; data->mode = WavpackGetMode (data->wpc); data->avg_bitrate = WavpackGetAverageBitrate (data->wpc, 1) / 1000; data->ok = 1; debug ("File opened. S_n %d. S_r %d. Time %d. Avg_Bitrate %d.", data->sample_num, data->sample_rate, data->duration, data->avg_bitrate ); }
SINT SoundSourceWV::readSampleFrames( SINT numberOfFrames, CSAMPLE* sampleBuffer) { // static assert: sizeof(CSAMPLE) == sizeof(int32_t) SINT unpackCount = WavpackUnpackSamples(m_wpc, reinterpret_cast<int32_t*>(sampleBuffer), numberOfFrames); if (!(WavpackGetMode(m_wpc) & MODE_FLOAT)) { // signed integer -> float const SINT sampleCount = frames2samples(unpackCount); for (SINT i = 0; i < sampleCount; ++i) { const int32_t sampleValue = reinterpret_cast<int32_t*>(sampleBuffer)[i]; sampleBuffer[i] = CSAMPLE(sampleValue) * m_sampleScaleFactor; } } return unpackCount; }
static int wavpack_assign_values(struct mpxplay_filehand_buffered_func_s *fbfs,void *fbds,struct wavpack_decoder_data *wpdi,struct mpxplay_infile_info_s *miis) { struct mpxplay_audio_decoder_info_s *adi=miis->audio_decoder_infos; unsigned int encmode; unsigned long pcmdatalen; adi->filechannels = adi->outchannels = WavpackGetReducedChannels(wpdi->wpc);//WavpackGetNumChannels(wpdi->wpc); if((adi->outchannels<PCM_MIN_CHANNELS) || (adi->outchannels>PCM_MAX_CHANNELS)) return 0; adi->bits = WavpackGetBitsPerSample(wpdi->wpc); if((adi->bits<PCM_MIN_BITS) || (adi->bits>PCM_MAX_BITS)) return 0; adi->freq = WavpackGetSampleRate(wpdi->wpc); wpdi->bytes_per_sample = WavpackGetBytesPerSample(wpdi->wpc); if(!adi->freq || !wpdi->bytes_per_sample) return 0; pcmdatalen=WavpackGetNumSamples(wpdi->wpc); miis->timemsec=(float)pcmdatalen*1000.0/adi->freq; encmode=WavpackGetMode(wpdi->wpc); if(encmode&MODE_FLOAT){ adi->infobits|=ADI_FLAG_FLOATOUT; adi->bits=1; wpdi->bytes_per_sample=sizeof(MPXPLAY_PCMOUT_FLOAT_T); } miis->longname="WavPack "; if(encmode&MODE_HYBRID){ adi->bitrate=(long)((float)miis->filesize*8.0/1000.0*(float)adi->freq/(float)pcmdatalen); }else{ long compr_ratio; adi->bitratetext=malloc(MPXPLAY_ADITEXTSIZE_BITRATE+8); if(!adi->bitratetext) return 0; compr_ratio=(long)(1000.0*(float)miis->filesize/(float)pcmdatalen/(float)wpdi->bytes_per_sample/(float)adi->filechannels); sprintf(adi->bitratetext,"%2d/%2.2d.%1.1d%%",adi->bits,compr_ratio/10,compr_ratio%10); } return 1; }
ReadableSampleFrames SoundSourceWV::readSampleFramesClamped( WritableSampleFrames writableSampleFrames) { const SINT firstFrameIndex = writableSampleFrames.frameIndexRange().start(); if (m_curFrameIndex != firstFrameIndex) { if (WavpackSeekSample(m_wpc, firstFrameIndex)) { m_curFrameIndex = firstFrameIndex; } else { kLogger.warning() << "Could not seek to first frame index" << firstFrameIndex; m_curFrameIndex = WavpackGetSampleIndex(m_wpc); return ReadableSampleFrames(IndexRange::between(m_curFrameIndex, m_curFrameIndex)); } } DEBUG_ASSERT(m_curFrameIndex == firstFrameIndex); const SINT numberOfFramesTotal = writableSampleFrames.frameLength(); static_assert(sizeof(CSAMPLE) == sizeof(int32_t), "CSAMPLE and int32_t must have the same size"); CSAMPLE* pOutputBuffer = writableSampleFrames.writableData(); SINT unpackCount = WavpackUnpackSamples(m_wpc, reinterpret_cast<int32_t*>(pOutputBuffer), numberOfFramesTotal); DEBUG_ASSERT(unpackCount >= 0); DEBUG_ASSERT(unpackCount <= numberOfFramesTotal); if (!(WavpackGetMode(m_wpc) & MODE_FLOAT)) { // signed integer -> float const SINT sampleCount = frames2samples(unpackCount); for (SINT i = 0; i < sampleCount; ++i) { const int32_t sampleValue = *reinterpret_cast<int32_t*>(pOutputBuffer); *pOutputBuffer++ = CSAMPLE(sampleValue) * m_sampleScaleFactor; } } const auto resultRange = IndexRange::forward(m_curFrameIndex, unpackCount); m_curFrameIndex += unpackCount; return ReadableSampleFrames( resultRange, SampleBuffer::ReadableSlice( writableSampleFrames.writableData(), frames2samples(unpackCount))); }
SoundSource::OpenResult SoundSourceWV::tryOpen(const AudioSourceConfig& audioSrcCfg) { DEBUG_ASSERT(!m_wpc); char msg[80]; // hold possible error message int openFlags = OPEN_WVC | OPEN_NORMALIZE; if ((kChannelCountMono == audioSrcCfg.getChannelCount()) || (kChannelCountStereo == audioSrcCfg.getChannelCount())) { openFlags |= OPEN_2CH_MAX; } // We use WavpackOpenFileInputEx to support Unicode paths on windows // http://www.wavpack.com/lib_use.txt QString wavPackFileName = getLocalFileName(); m_pWVFile = new QFile(wavPackFileName); m_pWVFile->open(QFile::ReadOnly); QString correctionFileName(wavPackFileName + "c"); if (QFile::exists(correctionFileName)) { // If there is a correction file, open it as well m_pWVCFile = new QFile(correctionFileName); m_pWVCFile->open(QFile::ReadOnly); } m_wpc = WavpackOpenFileInputEx(&s_streamReader, m_pWVFile, m_pWVCFile, msg, openFlags, 0); if (!m_wpc) { qDebug() << "SSWV::open: failed to open file : " << msg; return OpenResult::FAILED; } setChannelCount(WavpackGetReducedChannels(m_wpc)); setSamplingRate(WavpackGetSampleRate(m_wpc)); setFrameCount(WavpackGetNumSamples(m_wpc)); if (WavpackGetMode(m_wpc) & MODE_FLOAT) { m_sampleScaleFactor = CSAMPLE_PEAK; } else { const int bitsPerSample = WavpackGetBitsPerSample(m_wpc); const uint32_t wavpackPeakSampleValue = 1u << (bitsPerSample - 1); m_sampleScaleFactor = CSAMPLE_PEAK / wavpackPeakSampleValue; } return OpenResult::SUCCEEDED; }
bool AKSampler_Plugin::loadCompressedSampleFile(AKSampleFileDescriptor& sfd, float volBoostDb) { char errMsg[100]; WavpackContext* wpc = WavpackOpenFileInput(sfd.path, errMsg, OPEN_2CH_MAX, 0); if (wpc == 0) { printf("Wavpack error loading %s: %s\n", sfd.path, errMsg); return false; } AKSampleDataDescriptor sdd; sdd.sampleDescriptor = sfd.sampleDescriptor; sdd.sampleRate = (float)WavpackGetSampleRate(wpc); sdd.channelCount = WavpackGetReducedChannels(wpc); sdd.sampleCount = WavpackGetNumSamples(wpc); sdd.isInterleaved = sdd.channelCount > 1; sdd.data = new float[sdd.channelCount * sdd.sampleCount]; int mode = WavpackGetMode(wpc); WavpackUnpackSamples(wpc, (int32_t*)sdd.data, sdd.sampleCount); if ((mode & MODE_FLOAT) == 0) { // convert samples to floating-point int bps = WavpackGetBitsPerSample(wpc); float scale = 1.0f / (1 << (bps - 1)); float* pf = sdd.data; int32_t* pi = (int32_t*)pf; for (int i = 0; i < (sdd.sampleCount * sdd.channelCount); i++) *pf++ = scale * *pi++; } if (volBoostDb != 0.0f) { float scale = exp(volBoostDb / 20.0f); float* pf = sdd.data; for (int i = 0; i < (sdd.sampleCount * sdd.channelCount); i++) *pf++ *= scale; } loadSampleData(sdd); delete[] sdd.data; return true; }
void wavpack_buffer_format_samples(wavpack_buffer_decoder* wbd, uchar *dst, long *src, uint32_t samples, int float32_output) { long temp; int bps = WavpackGetBytesPerSample(wbd->wpc); uint32_t samcnt = samples * WavpackGetNumChannels (wbd->wpc); switch (bps) { case 1: while (samcnt--) *dst++ = (uchar)(*src++ + 128); break; case 2: while (samcnt--) { *dst++ = (uchar)(temp = *src++); *dst++ = (uchar)(temp >> 8); } break; case 3: while (samcnt--) { *dst++ = (uchar)(temp = *src++); *dst++ = (uchar)(temp >> 8); *dst++ = (uchar)(temp >> 16); } break; case 4: if (float32_output && !(WavpackGetMode (wbd->wpc) & MODE_FLOAT)) { float *fdst = (float *) dst; while (samcnt--) *fdst++ = *src++ * 4.656612873077393e-10F; } break; } }
static char * wv_get_quality(WavpackContext *ctx) { int mode = WavpackGetMode(ctx); const char *quality; if (mode & MODE_LOSSLESS) quality = _("lossless"); else if (mode & MODE_HYBRID) quality = _("lossy (hybrid)"); else quality = _("lossy"); return str_printf ("%s%s%s", quality, (mode & MODE_WVC) ? " (wvc corrected)" : "", #ifdef MODE_DNS /* WavPack 4.50 or later */ (mode & MODE_DNS) ? " (dynamic noise shaped)" : #endif ""); }
static DB_playItem_t * wv_insert (ddb_playlist_t *plt, DB_playItem_t *after, const char *fname) { DB_FILE *fp = deadbeef->fopen (fname); if (!fp) { return NULL; } char error[80]; #ifdef TINYWV WavpackContext *ctx = WavpackOpenFileInput (wv_read_stream, fp, error); #else WavpackContext *ctx = WavpackOpenFileInputEx (&wsr, fp, NULL, error, 0, 0); #endif if (!ctx) { fprintf (stderr, "wavpack error: %s\n", error); deadbeef->fclose (fp); return NULL; } int totalsamples = WavpackGetNumSamples (ctx); int samplerate = WavpackGetSampleRate (ctx); float duration = (float)totalsamples / samplerate; DB_playItem_t *it = deadbeef->pl_item_alloc_init (fname, plugin.plugin.id); deadbeef->pl_add_meta (it, ":FILETYPE", "wv"); deadbeef->plt_set_item_duration (plt, it, duration); trace ("wv: totalsamples=%d, samplerate=%d, duration=%f\n", totalsamples, samplerate, duration); #if 0 int num = WavpackGetNumTagItems (ctx); trace ("num tag items: %d\n", num); for (int i = 0; i < num; i++) { char str[1024]; WavpackGetTagItemIndexed (ctx, i, str, sizeof (str)); trace ("tag item: %s\n", str); } #endif int apeerr = deadbeef->junk_apev2_read (it, fp); if (!apeerr) { trace ("wv: ape tag found\n"); } int v1err = deadbeef->junk_id3v1_read (it, fp); if (!v1err) { trace ("wv: id3v1 tag found\n"); } deadbeef->pl_add_meta (it, "title", NULL); char s[100]; snprintf (s, sizeof (s), "%lld", deadbeef->fgetlength (fp)); deadbeef->pl_add_meta (it, ":FILE_SIZE", s); snprintf (s, sizeof (s), "%d", WavpackGetBytesPerSample (ctx) * 8); deadbeef->pl_add_meta (it, ":BPS", s); snprintf (s, sizeof (s), "%d", WavpackGetNumChannels (ctx)); deadbeef->pl_add_meta (it, ":CHANNELS", s); snprintf (s, sizeof (s), "%d", WavpackGetSampleRate (ctx)); deadbeef->pl_add_meta (it, ":SAMPLERATE", s); snprintf (s, sizeof (s), "%d", (int)(WavpackGetAverageBitrate (ctx, 1) / 1000)); deadbeef->pl_add_meta (it, ":BITRATE", s); snprintf (s, sizeof (s), "%s", (WavpackGetMode (ctx) & MODE_FLOAT) ? "FLOAT" : "INTEGER"); deadbeef->pl_add_meta (it, ":WAVPACK_MODE", s); // embedded cue deadbeef->pl_lock (); const char *cuesheet = deadbeef->pl_find_meta (it, "cuesheet"); if (cuesheet) { trace ("found cuesheet: %s\n", cuesheet); DB_playItem_t *last = deadbeef->plt_insert_cue_from_buffer (plt, after, it, cuesheet, strlen (cuesheet), totalsamples, samplerate); if (last) { deadbeef->pl_unlock (); deadbeef->fclose (fp); WavpackCloseFile (ctx); deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (last); return last; } } deadbeef->pl_unlock (); // cue file on disc DB_playItem_t *cue_after = deadbeef->plt_insert_cue (plt, after, it, totalsamples, samplerate); if (cue_after) { deadbeef->fclose (fp); WavpackCloseFile (ctx); deadbeef->pl_item_unref (it); deadbeef->pl_item_unref (cue_after); return cue_after; } after = deadbeef->plt_insert_item (plt, after, it); deadbeef->pl_item_unref (it); deadbeef->fclose (fp); WavpackCloseFile (ctx); return after; }
/* * This does the main decoding thing. * Requires an already opened WavpackContext. */ static void wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek) { GError *error = NULL; bool is_float; enum sample_format sample_format; struct audio_format audio_format; format_samples_t format_samples; float total_time; int bytes_per_sample, output_sample_size; is_float = (WavpackGetMode(wpc) & MODE_FLOAT) != 0; sample_format = wavpack_bits_to_sample_format(is_float, WavpackGetBytesPerSample(wpc)); if (!audio_format_init_checked(&audio_format, WavpackGetSampleRate(wpc), sample_format, WavpackGetNumChannels(wpc), &error)) { g_warning("%s", error->message); g_error_free(error); return; } if (is_float) { format_samples = format_samples_float; } else { format_samples = format_samples_int; } total_time = WavpackGetNumSamples(wpc); total_time /= audio_format.sample_rate; bytes_per_sample = WavpackGetBytesPerSample(wpc); output_sample_size = audio_format_frame_size(&audio_format); /* wavpack gives us all kind of samples in a 32-bit space */ int32_t chunk[1024]; const uint32_t samples_requested = G_N_ELEMENTS(chunk) / audio_format.channels; decoder_initialized(decoder, &audio_format, can_seek, total_time); enum decoder_command cmd = decoder_get_command(decoder); while (cmd != DECODE_COMMAND_STOP) { if (cmd == DECODE_COMMAND_SEEK) { if (can_seek) { unsigned where = decoder_seek_where(decoder) * audio_format.sample_rate; if (WavpackSeekSample(wpc, where)) { decoder_command_finished(decoder); } else { decoder_seek_error(decoder); } } else { decoder_seek_error(decoder); } } uint32_t samples_got = WavpackUnpackSamples(wpc, chunk, samples_requested); if (samples_got == 0) break; int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 + 0.5); format_samples(bytes_per_sample, chunk, samples_got * audio_format.channels); cmd = decoder_data(decoder, NULL, chunk, samples_got * output_sample_size, bitrate); } }
bool WavPackDecoder::Open(CFErrorRef *error) { if(IsOpen()) { LOGGER_WARNING("org.sbooth.AudioEngine.AudioDecoder.WavPack", "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; mStreamReader.read_bytes = read_bytes_callback; mStreamReader.get_pos = get_pos_callback; mStreamReader.set_pos_abs = set_pos_abs_callback; mStreamReader.set_pos_rel = set_pos_rel_callback; mStreamReader.push_back_byte = push_back_byte_callback; mStreamReader.get_length = get_length_callback; mStreamReader.can_seek = can_seek_callback; char errorBuf [80]; // Setup converter mWPC = WavpackOpenFileInputEx(&mStreamReader, this, nullptr, errorBuf, OPEN_WVC | OPEN_NORMALIZE, 0); if(nullptr == mWPC) { if(error) { CFStringRef description = CFCopyLocalizedString(CFSTR("The file “%@” is not a valid WavPack file."), ""); CFStringRef failureReason = CFCopyLocalizedString(CFSTR("Not a WavPack file"), ""); CFStringRef recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), ""); *error = CreateErrorForURL(AudioDecoderErrorDomain, AudioDecoderInputOutputError, description, mInputSource->GetURL(), failureReason, recoverySuggestion); CFRelease(description), description = nullptr; CFRelease(failureReason), failureReason = nullptr; CFRelease(recoverySuggestion), recoverySuggestion = nullptr; } return false; } // Floating-point and lossy files will be handed off in the canonical Core Audio format int mode = WavpackGetMode(mWPC); if(MODE_FLOAT & mode || !(MODE_LOSSLESS & mode)) { // Canonical Core Audio format mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; mFormat.mSampleRate = WavpackGetSampleRate(mWPC); mFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mFormat.mBitsPerChannel = 8 * sizeof(float); mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; } else { mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsNonInterleaved; // Don't set kAudioFormatFlagIsAlignedHigh for 32-bit integer files mFormat.mFormatFlags |= (32 == WavpackGetBitsPerSample(mWPC) ? kAudioFormatFlagIsPacked : kAudioFormatFlagIsAlignedHigh); mFormat.mSampleRate = WavpackGetSampleRate(mWPC); mFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mFormat.mBitsPerChannel = WavpackGetBitsPerSample(mWPC); mFormat.mBytesPerPacket = sizeof(int32_t); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; } mTotalFrames = WavpackGetNumSamples(mWPC); // Set up the source format mSourceFormat.mFormatID = 'WVPK'; mSourceFormat.mSampleRate = WavpackGetSampleRate(mWPC); mSourceFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mSourceFormat.mBitsPerChannel = WavpackGetBitsPerSample(mWPC); // Setup the channel layout switch(mFormat.mChannelsPerFrame) { case 1: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; case 4: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Quadraphonic); break; } mBuffer = static_cast<int32_t *>(calloc(BUFFER_SIZE_FRAMES * mFormat.mChannelsPerFrame, sizeof(int32_t))); if(nullptr == mBuffer) { if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, nullptr); return false; } mIsOpen = true; return true; }
UInt32 WavPackDecoder::ReadAudio(AudioBufferList *bufferList, UInt32 frameCount) { if(!IsOpen() || nullptr == bufferList || bufferList->mNumberBuffers != mFormat.mChannelsPerFrame || 0 == frameCount) return 0; // Reset output buffer data size for(UInt32 i = 0; i < bufferList->mNumberBuffers; ++i) bufferList->mBuffers[i].mDataByteSize = 0; UInt32 framesRemaining = frameCount; UInt32 totalFramesRead = 0; while(0 < framesRemaining) { UInt32 framesToRead = std::min(framesRemaining, static_cast<UInt32>(BUFFER_SIZE_FRAMES)); // Wavpack uses "complete" samples (one sample across all channels), i.e. a Core Audio frame uint32_t samplesRead = WavpackUnpackSamples(mWPC, mBuffer, framesToRead); if(0 == samplesRead) break; // The samples returned are handled differently based on the file's mode int mode = WavpackGetMode(mWPC); // Floating point files require no special handling other than deinterleaving if(MODE_FLOAT & mode) { float *inputBuffer = reinterpret_cast<float *>(mBuffer); // Deinterleave the samples for(UInt32 channel = 0; channel < mFormat.mChannelsPerFrame; ++channel) { float *floatBuffer = static_cast<float *>(bufferList->mBuffers[channel].mData); for(UInt32 sample = channel; sample < samplesRead * mFormat.mChannelsPerFrame; sample += mFormat.mChannelsPerFrame) *floatBuffer++ = inputBuffer[sample]; bufferList->mBuffers[channel].mNumberChannels = 1; bufferList->mBuffers[channel].mDataByteSize = static_cast<UInt32>(samplesRead * sizeof(float)); } } // Lossless files will be handed off as integers else if(MODE_LOSSLESS & mode) { // WavPack hands us 32-bit signed ints with the samples low-aligned; shift them to high alignment UInt32 shift = static_cast<UInt32>(8 * (sizeof(int32_t) - WavpackGetBytesPerSample(mWPC))); // Deinterleave the 32-bit samples and shift to high-alignment for(UInt32 channel = 0; channel < mFormat.mChannelsPerFrame; ++channel) { int32_t *shiftedBuffer = static_cast<int32_t *>(bufferList->mBuffers[channel].mData); for(UInt32 sample = channel; sample < samplesRead * mFormat.mChannelsPerFrame; sample += mFormat.mChannelsPerFrame) *shiftedBuffer++ = mBuffer[sample] << shift; bufferList->mBuffers[channel].mNumberChannels = 1; bufferList->mBuffers[channel].mDataByteSize = static_cast<UInt32>(samplesRead * sizeof(int32_t)); } } // Convert lossy files to float else { float scaleFactor = (1 << ((WavpackGetBytesPerSample(mWPC) * 8) - 1)); // Deinterleave the 32-bit samples and convert to float for(UInt32 channel = 0; channel < mFormat.mChannelsPerFrame; ++channel) { float *floatBuffer = static_cast<float *>(bufferList->mBuffers[channel].mData); for(UInt32 sample = channel; sample < samplesRead * mFormat.mChannelsPerFrame; sample += mFormat.mChannelsPerFrame) *floatBuffer++ = mBuffer[sample] / scaleFactor; bufferList->mBuffers[channel].mNumberChannels = 1; bufferList->mBuffers[channel].mDataByteSize = static_cast<UInt32>(samplesRead * sizeof(float)); } } totalFramesRead += samplesRead; framesRemaining -= samplesRead; } mCurrentFrame += totalFramesRead; return totalFramesRead; }
/* * This does the main decoding thing. * Requires an already opened WavpackContext. */ static void wavpack_decode(struct decoder *decoder, WavpackContext *wpc, bool can_seek, struct replay_gain_info *replay_gain_info) { struct audio_format audio_format; format_samples_t format_samples; char chunk[CHUNK_SIZE]; int samples_requested, samples_got; float total_time, current_time; int bytes_per_sample, output_sample_size; int position; audio_format.sample_rate = WavpackGetSampleRate(wpc); audio_format.channels = WavpackGetReducedChannels(wpc); audio_format.bits = WavpackGetBitsPerSample(wpc); /* round bitwidth to 8-bit units */ audio_format.bits = (audio_format.bits + 7) & (~7); /* mpd handles max 24-bit samples */ if (audio_format.bits > 24) { audio_format.bits = 24; } if (!audio_format_valid(&audio_format)) { g_warning("Invalid audio format: %u:%u:%u\n", audio_format.sample_rate, audio_format.bits, audio_format.channels); return; } if ((WavpackGetMode(wpc) & MODE_FLOAT) == MODE_FLOAT) { format_samples = format_samples_float; } else { format_samples = format_samples_int; } total_time = WavpackGetNumSamples(wpc); total_time /= audio_format.sample_rate; bytes_per_sample = WavpackGetBytesPerSample(wpc); output_sample_size = audio_format_frame_size(&audio_format); /* wavpack gives us all kind of samples in a 32-bit space */ samples_requested = sizeof(chunk) / (4 * audio_format.channels); decoder_initialized(decoder, &audio_format, can_seek, total_time); position = 0; do { if (decoder_get_command(decoder) == DECODE_COMMAND_SEEK) { if (can_seek) { int where; where = decoder_seek_where(decoder); where *= audio_format.sample_rate; if (WavpackSeekSample(wpc, where)) { position = where; decoder_command_finished(decoder); } else { decoder_seek_error(decoder); } } else { decoder_seek_error(decoder); } } if (decoder_get_command(decoder) == DECODE_COMMAND_STOP) { break; } samples_got = WavpackUnpackSamples( wpc, (int32_t *)chunk, samples_requested ); if (samples_got > 0) { int bitrate = (int)(WavpackGetInstantBitrate(wpc) / 1000 + 0.5); position += samples_got; current_time = position; current_time /= audio_format.sample_rate; format_samples( bytes_per_sample, chunk, samples_got * audio_format.channels ); decoder_data( decoder, NULL, chunk, samples_got * output_sample_size, current_time, bitrate, replay_gain_info ); } } while (samples_got > 0); }
bool WavPackDecoder::Open(CFErrorRef *error) { if(IsOpen()) { log4cxx::LoggerPtr logger = log4cxx::Logger::getLogger("org.sbooth.AudioEngine.AudioDecoder.WavPack"); LOG4CXX_WARN(logger, "Open() called on an AudioDecoder that is already open"); return true; } // Ensure the input source is open if(!mInputSource->IsOpen() && !mInputSource->Open(error)) return false; mStreamReader.read_bytes = read_bytes_callback; mStreamReader.get_pos = get_pos_callback; mStreamReader.set_pos_abs = set_pos_abs_callback; mStreamReader.set_pos_rel = set_pos_rel_callback; mStreamReader.push_back_byte = push_back_byte_callback; mStreamReader.get_length = get_length_callback; mStreamReader.can_seek = can_seek_callback; char errorBuf [80]; // Setup converter mWPC = WavpackOpenFileInputEx(&mStreamReader, this, NULL, errorBuf, OPEN_WVC | OPEN_NORMALIZE, 0); if(NULL == mWPC) { if(error) { CFMutableDictionaryRef errorDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 32, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFStringRef displayName = CreateDisplayNameForURL(mInputSource->GetURL()); CFStringRef errorString = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFCopyLocalizedString(CFSTR("The file “%@” is not a valid WavPack file."), ""), displayName); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedDescriptionKey, errorString); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedFailureReasonKey, CFCopyLocalizedString(CFSTR("Not a WavPack file"), "")); CFDictionarySetValue(errorDictionary, kCFErrorLocalizedRecoverySuggestionKey, CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "")); CFRelease(errorString), errorString = NULL; CFRelease(displayName), displayName = NULL; *error = CFErrorCreate(kCFAllocatorDefault, AudioDecoderErrorDomain, AudioDecoderInputOutputError, errorDictionary); CFRelease(errorDictionary), errorDictionary = NULL; } return false; } // Floating-point and lossy files will be handed off in the canonical Core Audio format int mode = WavpackGetMode(mWPC); if(MODE_FLOAT & mode || !(MODE_LOSSLESS & mode)) { // Canonical Core Audio format mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved; mFormat.mSampleRate = WavpackGetSampleRate(mWPC); mFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mFormat.mBitsPerChannel = 8 * sizeof(float); mFormat.mBytesPerPacket = (mFormat.mBitsPerChannel / 8); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; } else { mFormat.mFormatID = kAudioFormatLinearPCM; mFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsAlignedHigh | kAudioFormatFlagIsNonInterleaved; mFormat.mSampleRate = WavpackGetSampleRate(mWPC); mFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mFormat.mBitsPerChannel = WavpackGetBitsPerSample(mWPC); mFormat.mBytesPerPacket = sizeof(int32_t); mFormat.mFramesPerPacket = 1; mFormat.mBytesPerFrame = mFormat.mBytesPerPacket * mFormat.mFramesPerPacket; mFormat.mReserved = 0; } mTotalFrames = WavpackGetNumSamples(mWPC); // Set up the source format mSourceFormat.mFormatID = 'WVPK'; mSourceFormat.mSampleRate = WavpackGetSampleRate(mWPC); mSourceFormat.mChannelsPerFrame = WavpackGetNumChannels(mWPC); mSourceFormat.mBitsPerChannel = WavpackGetBitsPerSample(mWPC); // Setup the channel layout switch(mFormat.mChannelsPerFrame) { case 1: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Mono); break; case 2: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Stereo); break; case 4: mChannelLayout = CreateChannelLayoutWithTag(kAudioChannelLayoutTag_Quadraphonic); break; } mBuffer = static_cast<int32_t *>(calloc(BUFFER_SIZE_FRAMES * mFormat.mChannelsPerFrame, sizeof(int32_t))); if(NULL == mBuffer) { if(error) *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainPOSIX, ENOMEM, NULL); return false; } mIsOpen = true; return true; }
static int wv_read (DB_fileinfo_t *_info, char *bytes, int size) { wvctx_t *info = (wvctx_t *)_info; int currentsample = WavpackGetSampleIndex (info->ctx); int samplesize = _info->fmt.channels * _info->fmt.bps / 8; if (size / samplesize + currentsample > info->endsample) { size = (info->endsample - currentsample + 1) * samplesize; trace ("wv: size truncated to %d bytes (%d samples), cursample=%d, endsample=%d\n", size, info->endsample - currentsample + 1, currentsample, info->endsample); if (size <= 0) { return 0; } } int initsize = size; int n; if (WavpackGetMode (info->ctx) & MODE_FLOAT) { _info->fmt.is_float = 1; } if (_info->fmt.is_float || _info->fmt.bps == 32) { n = WavpackUnpackSamples (info->ctx, (int32_t *)bytes, size / samplesize); size -= n * samplesize; } else { int32_t buffer[size/(_info->fmt.bps / 8)]; n = WavpackUnpackSamples (info->ctx, (int32_t *)buffer, size / samplesize); size -= n * samplesize; n *= _info->fmt.channels; // convert from int32 to input (what???) int32_t *p = buffer; if (_info->fmt.bps == 16) { while (n > 0) { *((int16_t *)bytes) = (int16_t)(*p); bytes += sizeof (int16_t); p++; n--; } } else if (_info->fmt.bps == 8) { while (n > 0) { *bytes++ = (char)(*p); p++; n--; } } else if (_info->fmt.bps == 24) { while (n > 0) { *bytes++ = (*p)&0xff; *bytes++ = ((*p)&0xff00)>>8; *bytes++ = ((*p)&0xff0000)>>16; p++; n--; } } } _info->readpos = (float)(WavpackGetSampleIndex (info->ctx)-info->startsample)/WavpackGetSampleRate (info->ctx); #ifndef TINYWV deadbeef->streamer_set_bitrate (WavpackGetInstantBitrate (info->ctx) / 1000); #endif return initsize-size; }
static int wv_init (DB_fileinfo_t *_info, DB_playItem_t *it) { wvctx_t *info = (wvctx_t *)_info; deadbeef->pl_lock (); info->file = deadbeef->fopen (deadbeef->pl_find_meta (it, ":URI")); deadbeef->pl_unlock (); if (!info->file) { return -1; } #ifndef TINYWV deadbeef->pl_lock (); const char *uri = deadbeef->pl_find_meta (it, ":URI"); char *c_fname = alloca (strlen (uri) + 2); if (c_fname) { strcpy (c_fname, uri); strcat (c_fname, "c"); info->c_file = deadbeef->fopen (c_fname); } else { fprintf (stderr, "wavpack warning: failed to alloc memory for correction file name\n"); } deadbeef->pl_unlock (); #endif char error[80]; #ifdef TINYWV info->ctx = WavpackOpenFileInput (wv_read_stream, info->file, error); #else info->ctx = WavpackOpenFileInputEx (&wsr, info->file, info->c_file, error, OPEN_NORMALIZE, 0); #endif if (!info->ctx) { fprintf (stderr, "wavpack error: %s\n", error); return -1; } _info->plugin = &plugin; _info->fmt.bps = WavpackGetBytesPerSample (info->ctx) * 8; _info->fmt.channels = WavpackGetNumChannels (info->ctx); _info->fmt.samplerate = WavpackGetSampleRate (info->ctx); _info->fmt.is_float = (WavpackGetMode (info->ctx) & MODE_FLOAT) ? 1 : 0; // FIXME: streamer and maybe output plugins need to be fixed to support // arbitrary channelmask // _info->fmt.channelmask = WavpackGetChannelMask (info->ctx); for (int i = 0; i < _info->fmt.channels; i++) { _info->fmt.channelmask |= 1 << i; } _info->readpos = 0; if (it->endsample > 0) { info->startsample = it->startsample; info->endsample = it->endsample; if (plugin.seek_sample (_info, 0) < 0) { return -1; } } else { info->startsample = 0; info->endsample = WavpackGetNumSamples (info->ctx)-1; } return 0; }