mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
289 lines
8.4 KiB
C++
289 lines
8.4 KiB
C++
// ****************************************************************************
|
|
//
|
|
// Copyright (c) 2006-2007 Moitah (moitah@yahoo.com)
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright
|
|
// notice, this list of conditions and the following disclaimer in the
|
|
// documentation and/or other materials provided with the distribution.
|
|
// * Neither the name of the author nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without
|
|
// specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// ****************************************************************************
|
|
|
|
using namespace System;
|
|
using namespace System::Runtime::InteropServices;
|
|
using namespace System::Collections::Specialized;
|
|
using namespace APETagsDotNet;
|
|
|
|
#include <stdio.h>
|
|
#include <memory.h>
|
|
#include "WavPack\wavpack.h"
|
|
#include <string.h>
|
|
//typedef char str_ansi;
|
|
//typedef wchar_t str_utf16;
|
|
//#define BOOL int
|
|
//#define TRUE 1
|
|
//#define FALSE 0
|
|
//#include "..\MAC_SDK\Shared\SmartPtr.h"
|
|
//#include "..\MAC_SDK\Shared\APETag.h"
|
|
|
|
namespace WavPackDotNet {
|
|
int write_block(void *id, void *data, int32_t length);
|
|
|
|
public ref class WavPackReader {
|
|
public:
|
|
WavPackReader(String^ path) {
|
|
IntPtr pathChars;
|
|
char errorMessage[256];
|
|
|
|
_path = path;
|
|
|
|
pathChars = Marshal::StringToHGlobalUni(path);
|
|
size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1;
|
|
wchar_t * pPath = new wchar_t[pathLen];
|
|
memcpy ((void*) pPath, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t));
|
|
Marshal::FreeHGlobal(pathChars);
|
|
|
|
_wpc = WavpackOpenFileInput(pPath, errorMessage, OPEN_WVC, 0);
|
|
if (_wpc == NULL) {
|
|
throw gcnew Exception("Unable to initialize the decoder.");
|
|
}
|
|
|
|
_bitsPerSample = WavpackGetBitsPerSample(_wpc);
|
|
_channelCount = WavpackGetNumChannels(_wpc);
|
|
_sampleRate = WavpackGetSampleRate(_wpc);
|
|
_sampleCount = WavpackGetNumSamples(_wpc);
|
|
_sampleOffset = 0;
|
|
}
|
|
|
|
~WavPackReader()
|
|
{
|
|
}
|
|
|
|
property Int32 BitsPerSample {
|
|
Int32 get() {
|
|
return _bitsPerSample;
|
|
}
|
|
}
|
|
|
|
property Int32 ChannelCount {
|
|
Int32 get() {
|
|
return _channelCount;
|
|
}
|
|
}
|
|
|
|
property Int32 SampleRate {
|
|
Int32 get() {
|
|
return _sampleRate;
|
|
}
|
|
}
|
|
|
|
property Int32 Length {
|
|
Int32 get() {
|
|
return _sampleCount;
|
|
}
|
|
}
|
|
|
|
property Int32 Position {
|
|
Int32 get() {
|
|
return _sampleOffset;
|
|
}
|
|
void set(Int32 offset) {
|
|
_sampleOffset = offset;
|
|
if (!WavpackSeekSample(_wpc, offset)) {
|
|
throw gcnew Exception("Unable to seek.");
|
|
}
|
|
}
|
|
}
|
|
|
|
property Int32 Remaining {
|
|
Int32 get() {
|
|
return _sampleCount - _sampleOffset;
|
|
}
|
|
}
|
|
|
|
property NameValueCollection^ Tags {
|
|
NameValueCollection^ get () {
|
|
if (!_tags) _tags = (gcnew APETagDotNet (_path, true))->GetStringTags (true);
|
|
return _tags;
|
|
}
|
|
void set (NameValueCollection ^tags) {
|
|
_tags = tags;
|
|
}
|
|
}
|
|
|
|
void Close() {
|
|
_wpc = WavpackCloseFile(_wpc);
|
|
}
|
|
|
|
void Read(array<Int32, 2>^ sampleBuffer, Int32 sampleCount) {
|
|
pin_ptr<Int32> pSampleBuffer = &sampleBuffer[0, 0];
|
|
int samplesRead;
|
|
|
|
samplesRead = WavpackUnpackSamples(_wpc, pSampleBuffer, sampleCount);
|
|
_sampleOffset += samplesRead;
|
|
|
|
if (samplesRead != sampleCount) {
|
|
throw gcnew Exception("Decoder returned a different number of samples than requested.");
|
|
}
|
|
}
|
|
|
|
private:
|
|
WavpackContext *_wpc;
|
|
NameValueCollection^ _tags;
|
|
Int32 _sampleCount, _sampleOffset;
|
|
Int32 _bitsPerSample, _channelCount, _sampleRate;
|
|
String^ _path;
|
|
};
|
|
|
|
public ref class WavPackWriter {
|
|
public:
|
|
WavPackWriter(String^ path, Int32 bitsPerSample, Int32 channelCount, Int32 sampleRate) {
|
|
IntPtr pathChars;
|
|
|
|
if ((channelCount != 1) && (channelCount != 2)) {
|
|
throw gcnew Exception("Only stereo and mono audio formats are allowed.");
|
|
}
|
|
|
|
_compressionMode = 1;
|
|
_extraMode = 0;
|
|
|
|
_bitsPerSample = bitsPerSample;
|
|
_channelCount = channelCount;
|
|
_sampleRate = sampleRate;
|
|
|
|
pathChars = Marshal::StringToHGlobalUni(path);
|
|
_hFile = _wfopen((const wchar_t*)pathChars.ToPointer(), L"w+b");
|
|
Marshal::FreeHGlobal(pathChars);
|
|
if (!_hFile) {
|
|
throw gcnew Exception("Unable to open file.");
|
|
}
|
|
}
|
|
|
|
void Close() {
|
|
WavpackFlushSamples(_wpc);
|
|
_wpc = WavpackCloseFile(_wpc);
|
|
fclose(_hFile);
|
|
|
|
if ((_finalSampleCount != 0) && (_samplesWritten != _finalSampleCount)) {
|
|
throw gcnew Exception("Samples written differs from the expected sample count.");
|
|
}
|
|
}
|
|
|
|
property Int32 FinalSampleCount {
|
|
Int32 get() {
|
|
return _finalSampleCount;
|
|
}
|
|
void set(Int32 value) {
|
|
if (value < 0) {
|
|
throw gcnew Exception("Invalid final sample count.");
|
|
}
|
|
if (_initialized) {
|
|
throw gcnew Exception("Final sample count cannot be changed after encoding begins.");
|
|
}
|
|
_finalSampleCount = value;
|
|
}
|
|
}
|
|
|
|
property Int32 CompressionMode {
|
|
Int32 get() {
|
|
return _compressionMode;
|
|
}
|
|
void set(Int32 value) {
|
|
if ((value < 0) || (value > 3)) {
|
|
throw gcnew Exception("Invalid compression mode.");
|
|
}
|
|
_compressionMode = value;
|
|
}
|
|
}
|
|
|
|
property Int32 ExtraMode {
|
|
Int32 get() {
|
|
return _extraMode;
|
|
}
|
|
void set(Int32 value) {
|
|
if ((value < 0) || (value > 6)) {
|
|
throw gcnew Exception("Invalid extra mode.");
|
|
}
|
|
_extraMode = value;
|
|
}
|
|
}
|
|
|
|
void Write(array<Int32, 2>^ sampleBuffer, Int32 sampleCount) {
|
|
if (!_initialized) Initialize();
|
|
|
|
pin_ptr<Int32> pSampleBuffer = &sampleBuffer[0, 0];
|
|
|
|
if (!WavpackPackSamples(_wpc, (int32_t*)pSampleBuffer, sampleCount)) {
|
|
throw gcnew Exception("An error occurred while encoding.");
|
|
}
|
|
|
|
_samplesWritten += sampleCount;
|
|
}
|
|
|
|
private:
|
|
FILE *_hFile;
|
|
bool _initialized;
|
|
WavpackContext *_wpc;
|
|
Int32 _finalSampleCount, _samplesWritten;
|
|
Int32 _bitsPerSample, _channelCount, _sampleRate;
|
|
Int32 _compressionMode, _extraMode;
|
|
|
|
void Initialize() {
|
|
WavpackConfig config;
|
|
|
|
_wpc = WavpackOpenFileOutput(write_block, _hFile, NULL);
|
|
if (!_wpc) {
|
|
throw gcnew Exception("Unable to create the encoder.");
|
|
}
|
|
|
|
memset(&config, 0, sizeof(WavpackConfig));
|
|
config.bits_per_sample = _bitsPerSample;
|
|
config.bytes_per_sample = (_bitsPerSample + 7) / 8;
|
|
config.num_channels = _channelCount;
|
|
config.channel_mask = 5 - _channelCount;
|
|
config.sample_rate = _sampleRate;
|
|
if (_compressionMode == 0) config.flags |= CONFIG_FAST_FLAG;
|
|
if (_compressionMode == 2) config.flags |= CONFIG_HIGH_FLAG;
|
|
if (_compressionMode == 3) config.flags |= CONFIG_HIGH_FLAG | CONFIG_VERY_HIGH_FLAG;
|
|
if (_extraMode != 0) {
|
|
config.flags |= CONFIG_EXTRA_MODE;
|
|
config.xmode = _extraMode;
|
|
}
|
|
|
|
if (!WavpackSetConfiguration(_wpc, &config, (_finalSampleCount == 0) ? -1 : _finalSampleCount)) {
|
|
throw gcnew Exception("Invalid configuration setting.");
|
|
}
|
|
|
|
if (!WavpackPackInit(_wpc)) {
|
|
throw gcnew Exception("Unable to initialize the encoder.");
|
|
}
|
|
|
|
_initialized = true;
|
|
}
|
|
};
|
|
|
|
#pragma unmanaged
|
|
int write_block(void *id, void *data, int32_t length) {
|
|
return (fwrite(data, 1, length, (FILE*)id) == length);
|
|
}
|
|
} |