mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
initial checkin
This commit is contained in:
314
WavPackDotNet/WavPackDotNet.cpp
Normal file
314
WavPackDotNet/WavPackDotNet.cpp
Normal file
@@ -0,0 +1,314 @@
|
||||
// ****************************************************************************
|
||||
//
|
||||
// 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;
|
||||
|
||||
#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 = NULL;
|
||||
|
||||
pathChars = Marshal::StringToHGlobalUni(path);
|
||||
size_t pathLen = wcslen ((const wchar_t*)pathChars.ToPointer())+1;
|
||||
_path = new wchar_t[pathLen];
|
||||
memcpy ((void*) _path, (const wchar_t*)pathChars.ToPointer(), pathLen*sizeof(wchar_t));
|
||||
Marshal::FreeHGlobal(pathChars);
|
||||
|
||||
_wpc = WavpackOpenFileInput(_path, 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()
|
||||
{
|
||||
if (_path) delete [] _path;
|
||||
}
|
||||
|
||||
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) GetTags ();
|
||||
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;
|
||||
const wchar_t * _path;
|
||||
|
||||
void GetTags (void)
|
||||
{
|
||||
_tags = gcnew NameValueCollection();
|
||||
CAPETag apeTag (_path, TRUE);
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
CAPETagField * field = apeTag.GetTagField (i);
|
||||
if (!field)
|
||||
break;
|
||||
if (field->GetIsUTF8Text())
|
||||
{
|
||||
int valueSize = field->GetFieldValueSize();
|
||||
while (valueSize && field->GetFieldValue()[valueSize-1]=='\0')
|
||||
valueSize --;
|
||||
const wchar_t * fieldName = field->GetFieldName ();
|
||||
if (!_wcsicmp (fieldName, L"YEAR"))
|
||||
fieldName = L"DATE";
|
||||
if (!_wcsicmp (fieldName, L"TRACK"))
|
||||
fieldName = L"TRACKNUMBER";
|
||||
_tags->Add (gcnew String (fieldName),
|
||||
gcnew String (field->GetFieldValue(), 0, valueSize, System::Text::Encoding::UTF8));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user