mirror of
https://github.com/claunia/cuetools.net.git
synced 2025-12-16 18:14:25 +00:00
This commit is contained in:
@@ -29,8 +29,8 @@ namespace ArCueDotNet
|
||||
try
|
||||
{
|
||||
CUESheet cueSheet = new CUESheet(config);
|
||||
cueSheet.Open(pathIn, false);
|
||||
cueSheet.GenerateFilenames(OutputAudioFormat.NoAudio, pathIn);
|
||||
cueSheet.Open(pathIn);
|
||||
cueSheet.GenerateFilenames(OutputAudioFormat.NoAudio, false, pathIn);
|
||||
cueSheet.AccurateRip = AccurateRipMode.Verify;
|
||||
cueSheet.WriteAudioFiles(Path.GetDirectoryName(pathIn), CUEStyle.SingleFile);
|
||||
cueSheet.GenerateAccurateRipLog(sw);
|
||||
|
||||
@@ -125,6 +125,10 @@
|
||||
<Project>{6458A13A-30EF-45A9-9D58-E5031B17BEE2}</Project>
|
||||
<Name>CUETools.Codecs</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\CUETools.Processor\CUETools.Processor.csproj">
|
||||
<Project>{4911BD82-49EF-4858-8B51-5394F86739A4}</Project>
|
||||
<Name>CUETools.Processor</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\CUETools.Ripper.SCSI\CUETools.Ripper.SCSI.csproj">
|
||||
<Project>{8CF07381-BEA2-4AFC-B3DD-9B2F21C65A3A}</Project>
|
||||
<Name>CUETools.Ripper.SCSI</Name>
|
||||
|
||||
6
CUERipper/frmCUERipper.Designer.cs
generated
6
CUERipper/frmCUERipper.Designer.cs
generated
@@ -136,34 +136,34 @@ namespace CUERipper
|
||||
// comboLossless
|
||||
//
|
||||
this.comboLossless.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
resources.ApplyResources(this.comboLossless, "comboLossless");
|
||||
this.comboLossless.FormattingEnabled = true;
|
||||
this.comboLossless.Items.AddRange(new object[] {
|
||||
resources.GetString("comboLossless.Items"),
|
||||
resources.GetString("comboLossless.Items1"),
|
||||
resources.GetString("comboLossless.Items2")});
|
||||
resources.ApplyResources(this.comboLossless, "comboLossless");
|
||||
this.comboLossless.Name = "comboLossless";
|
||||
//
|
||||
// comboCodec
|
||||
//
|
||||
this.comboCodec.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
resources.ApplyResources(this.comboCodec, "comboCodec");
|
||||
this.comboCodec.FormattingEnabled = true;
|
||||
this.comboCodec.Items.AddRange(new object[] {
|
||||
resources.GetString("comboCodec.Items"),
|
||||
resources.GetString("comboCodec.Items1"),
|
||||
resources.GetString("comboCodec.Items2"),
|
||||
resources.GetString("comboCodec.Items3")});
|
||||
resources.ApplyResources(this.comboCodec, "comboCodec");
|
||||
this.comboCodec.Name = "comboCodec";
|
||||
//
|
||||
// comboImage
|
||||
//
|
||||
this.comboImage.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
resources.ApplyResources(this.comboImage, "comboImage");
|
||||
this.comboImage.FormattingEnabled = true;
|
||||
this.comboImage.Items.AddRange(new object[] {
|
||||
resources.GetString("comboImage.Items"),
|
||||
resources.GetString("comboImage.Items1")});
|
||||
resources.ApplyResources(this.comboImage, "comboImage");
|
||||
this.comboImage.Name = "comboImage";
|
||||
//
|
||||
// buttonAbort
|
||||
|
||||
@@ -7,21 +7,29 @@ using System.Drawing;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using CUETools.Ripper.SCSI;
|
||||
using CUETools.AccurateRip;
|
||||
using CUETools.CDImage;
|
||||
using CUETools.Codecs;
|
||||
using CUETools.Processor;
|
||||
using CUETools.Ripper.SCSI;
|
||||
using MusicBrainz;
|
||||
|
||||
namespace CUERipper
|
||||
{
|
||||
public partial class frmCUERipper : Form
|
||||
{
|
||||
private CDDriveReader _reader = null;
|
||||
private Thread _workThread = null;
|
||||
private CDDriveReader _reader = null;
|
||||
private StartStop _startStop;
|
||||
private CUEConfig _config;
|
||||
private OutputAudioFormat _format;
|
||||
private CUEStyle _style;
|
||||
private CUESheet _cueSheet;
|
||||
|
||||
public frmCUERipper()
|
||||
{
|
||||
InitializeComponent();
|
||||
_config = new CUEConfig();
|
||||
_startStop = new StartStop();
|
||||
}
|
||||
|
||||
@@ -44,9 +52,12 @@ namespace CUERipper
|
||||
private void SetupControls ()
|
||||
{
|
||||
bool running = _workThread != null;
|
||||
listTracks.Enabled = !running;
|
||||
comboDrives.Enabled = !running;
|
||||
comboRelease.Enabled = !running;
|
||||
listTracks.Enabled =
|
||||
comboDrives.Enabled =
|
||||
comboRelease.Enabled =
|
||||
comboCodec.Enabled =
|
||||
comboImage.Enabled =
|
||||
comboLossless.Enabled = !running;
|
||||
buttonPause.Visible = buttonPause.Enabled = buttonAbort.Visible = buttonAbort.Enabled = running;
|
||||
buttonGo.Visible = buttonGo.Enabled = !running;
|
||||
toolStripStatusLabel1.Text = String.Empty;
|
||||
@@ -95,26 +106,57 @@ namespace CUERipper
|
||||
CDDriveReader audioSource = (CDDriveReader)o;
|
||||
audioSource.ReadProgress += new EventHandler<ReadProgressArgs>(CDReadProgress);
|
||||
int[,] buff = new int[audioSource.BestBlockSize, audioSource.ChannelCount];
|
||||
AccurateRipVerify arVerify = new AccurateRipVerify(audioSource.TOC);
|
||||
string ArId = AccurateRipVerify.CalculateAccurateRipId(audioSource.TOC);
|
||||
|
||||
arVerify.ContactAccurateRip(ArId);
|
||||
|
||||
IAudioDest audioDest = null;
|
||||
|
||||
try
|
||||
{
|
||||
audioSource.Position = 0;
|
||||
do
|
||||
if (_style == CUEStyle.SingleFile || _style == CUEStyle.SingleFileWithCUE)
|
||||
audioDest = AudioReadWrite.GetAudioDest(_cueSheet.SingleFilename, (long)audioSource.Length, _config);
|
||||
|
||||
for (int iTrack = 0; iTrack <= audioSource.TOC.AudioTracks; iTrack++)
|
||||
{
|
||||
uint toRead = Math.Min((uint)buff.GetLength(0), (uint)audioSource.Remaining);
|
||||
uint samplesRead = audioSource.Read(buff, toRead);
|
||||
if (samplesRead == 0) break;
|
||||
if (samplesRead != toRead)
|
||||
throw new Exception("samples read != samples requested");
|
||||
//arVerify.Write(buff, samplesRead);
|
||||
//audioDest.Write(buff, samplesRead);
|
||||
} while (true);
|
||||
uint samplesRemTrack = (iTrack > 0 ? audioSource.TOC[iTrack].Length : audioSource.TOC.Pregap) * 588;
|
||||
if (_style == CUEStyle.GapsAppended)
|
||||
{
|
||||
if (audioDest != null)
|
||||
audioDest.Close();
|
||||
audioDest = AudioReadWrite.GetAudioDest(iTrack > 0 ? _cueSheet.TrackFilenames[iTrack - 1] : _cueSheet.HTOAFilename, (long)samplesRemTrack, _config);
|
||||
}
|
||||
while (samplesRemTrack > 0)
|
||||
{
|
||||
uint toRead = Math.Min((uint)buff.GetLength(0), (uint)samplesRemTrack);
|
||||
uint samplesRead = audioSource.Read(buff, toRead);
|
||||
if (samplesRead != toRead)
|
||||
throw new Exception("samples read != samples requested");
|
||||
arVerify.Write(buff, samplesRead);
|
||||
audioDest.Write(buff, samplesRead);
|
||||
samplesRemTrack -= samplesRead;
|
||||
}
|
||||
}
|
||||
|
||||
if (audioDest != null)
|
||||
audioDest.Close();
|
||||
audioDest = null;
|
||||
}
|
||||
catch (StopException)
|
||||
{
|
||||
if (audioDest != null)
|
||||
try { audioDest.Close(); }
|
||||
catch { };
|
||||
audioDest = null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (audioDest != null)
|
||||
try { audioDest.Close(); }
|
||||
catch { };
|
||||
audioDest = null;
|
||||
this.Invoke((MethodInvoker)delegate()
|
||||
{
|
||||
string message = "Exception";
|
||||
@@ -134,6 +176,18 @@ namespace CUERipper
|
||||
{
|
||||
if (_reader == null)
|
||||
return;
|
||||
|
||||
_config.lossyWAVHybrid = comboLossless.SelectedIndex == 1; // _cueSheet.Config?
|
||||
_config.singleFilenameFormat = "%D - %C.cue";
|
||||
_format = (string)comboCodec.SelectedItem == "wav" ? OutputAudioFormat.WAV :
|
||||
(string)comboCodec.SelectedItem == "flac" ? OutputAudioFormat.FLAC :
|
||||
(string)comboCodec.SelectedItem == "wv" ? OutputAudioFormat.WavPack :
|
||||
(string)comboCodec.SelectedItem == "ape" ? OutputAudioFormat.APE :
|
||||
OutputAudioFormat.NoAudio;
|
||||
_style = comboImage.SelectedIndex == 0 ? CUEStyle.SingleFileWithCUE :
|
||||
CUEStyle.GapsAppended;
|
||||
_cueSheet.GenerateFilenames(_format, comboLossless.SelectedIndex != 0, ".");
|
||||
|
||||
_workThread = new Thread(Rip);
|
||||
_workThread.Priority = ThreadPriority.BelowNormal;
|
||||
_workThread.IsBackground = true;
|
||||
@@ -174,26 +228,22 @@ namespace CUERipper
|
||||
{
|
||||
if (e.ListItem is string)
|
||||
return;
|
||||
ReadOnlyCollection<Event> events = ((Release)e.ListItem).GetEvents();
|
||||
string year = events.Count > 0 ? events[0].Date.Substring(0, 4) + ": " : "";
|
||||
e.Value = string.Format("{0}{1} - {2}", year, ((Release)e.ListItem).GetArtist(), ((Release)e.ListItem).GetTitle());
|
||||
CUELine date = General.FindCUELine(((CUESheet)e.ListItem).Attributes, "REM", "DATE");
|
||||
e.Value = string.Format("{0}{1} - {2}", date != null ? date.Params[2] + ": " : "", ((CUESheet)e.ListItem).Artist, ((CUESheet)e.ListItem).Title);
|
||||
}
|
||||
|
||||
private void comboRelease_SelectedIndexChanged(object sender, EventArgs e)
|
||||
{
|
||||
listTracks.Items.Clear();
|
||||
if (comboRelease.SelectedItem == null || comboRelease.SelectedItem is string)
|
||||
{
|
||||
for (int i = 1; i <= _reader.TOC.AudioTracks; i++)
|
||||
listTracks.Items.Add(new ListViewItem(new string[] { "Track " + _reader.TOC[i].Number.ToString(), _reader.TOC[i].Number.ToString(), _reader.TOC[i].StartMSF, _reader.TOC[i].LengthMSF }));
|
||||
return;
|
||||
}
|
||||
Release release = (Release) comboRelease.SelectedItem;
|
||||
_cueSheet = (CUESheet)comboRelease.SelectedItem;
|
||||
for (int i = 1; i <= _reader.TOC.AudioTracks; i++)
|
||||
{
|
||||
Track track = release.GetTracks()[(int)_reader.TOC[i].Number - 1];
|
||||
listTracks.Items.Add(new ListViewItem(new string[] { track.GetTitle(), _reader.TOC[i].Number.ToString(), _reader.TOC[i].StartMSF, _reader.TOC[i].LengthMSF }));
|
||||
}
|
||||
listTracks.Items.Add(new ListViewItem(new string[] {
|
||||
_cueSheet.Tracks[i-1].Title,
|
||||
_reader.TOC[i].Number.ToString(),
|
||||
_reader.TOC[i].StartMSF,
|
||||
_reader.TOC[i].LengthMSF }));
|
||||
}
|
||||
|
||||
private void MusicBrainz_LookupProgress(object sender, XmlRequestEventArgs e)
|
||||
@@ -230,23 +280,41 @@ namespace CUERipper
|
||||
CDDriveReader audioSource = (CDDriveReader)o;
|
||||
|
||||
ReleaseQueryParameters p = new ReleaseQueryParameters();
|
||||
p.DiscId = _reader.TOC.MusicBrainzId;
|
||||
p.DiscId = audioSource.TOC.MusicBrainzId;
|
||||
Query<Release> results = Release.Query(p);
|
||||
MusicBrainzService.XmlRequest += new EventHandler<XmlRequestEventArgs>(MusicBrainz_LookupProgress);
|
||||
foreach (Release release in results)
|
||||
{
|
||||
release.GetEvents();
|
||||
release.GetTracks();
|
||||
|
||||
this.BeginInvoke((MethodInvoker)delegate()
|
||||
{
|
||||
comboRelease.Items.Add(release);
|
||||
CUESheet cueSheet = new CUESheet(_config);
|
||||
cueSheet.OpenTOC(audioSource.TOC);
|
||||
cueSheet.Artist = release.GetArtist();
|
||||
cueSheet.Title = release.GetTitle();
|
||||
if (release.GetEvents().Count > 0)
|
||||
General.SetCUELine(cueSheet.Attributes, "REM", "DATE", release.GetEvents()[0].Date.Substring(0, 4), false);
|
||||
for (int i = 1; i <= audioSource.TOC.AudioTracks; i++)
|
||||
{
|
||||
Track track = release.GetTracks()[(int)audioSource.TOC[i].Number - 1];
|
||||
cueSheet.Tracks[i - 1].Title = track.GetTitle();
|
||||
cueSheet.Tracks[i - 1].Artist = track.GetArtist();
|
||||
}
|
||||
comboRelease.Items.Add(cueSheet);
|
||||
});
|
||||
}
|
||||
MusicBrainzService.XmlRequest -= new EventHandler<XmlRequestEventArgs>(MusicBrainz_LookupProgress);
|
||||
this.BeginInvoke((MethodInvoker)delegate()
|
||||
{
|
||||
if (comboRelease.Items.Count == 0)
|
||||
comboRelease.Items.Add("MusicBrainz: not found");
|
||||
{
|
||||
CUESheet cueSheet = new CUESheet(_config);
|
||||
cueSheet.OpenTOC(audioSource.TOC);
|
||||
comboRelease.Items.Add(cueSheet);
|
||||
// cueSheet.Tracks[i - 1].Title = "Track " + _reader.TOC[i].Number.ToString();
|
||||
}
|
||||
});
|
||||
_workThread = null;
|
||||
this.BeginInvoke((MethodInvoker)delegate()
|
||||
|
||||
@@ -270,9 +270,6 @@
|
||||
<data name=">>buttonGo.ZOrder" xml:space="preserve">
|
||||
<value>6</value>
|
||||
</data>
|
||||
<data name="comboLossless.Enabled" type="System.Boolean, mscorlib">
|
||||
<value>False</value>
|
||||
</data>
|
||||
<data name="comboLossless.Items" xml:space="preserve">
|
||||
<value>lossless</value>
|
||||
</data>
|
||||
@@ -303,14 +300,11 @@
|
||||
<data name=">>comboLossless.ZOrder" xml:space="preserve">
|
||||
<value>5</value>
|
||||
</data>
|
||||
<data name="comboCodec.Enabled" type="System.Boolean, mscorlib">
|
||||
<value>False</value>
|
||||
</data>
|
||||
<data name="comboCodec.Items" xml:space="preserve">
|
||||
<value>flac</value>
|
||||
</data>
|
||||
<data name="comboCodec.Items1" xml:space="preserve">
|
||||
<value>WavPack</value>
|
||||
<value>wv</value>
|
||||
</data>
|
||||
<data name="comboCodec.Items2" xml:space="preserve">
|
||||
<value>wav</value>
|
||||
@@ -339,9 +333,6 @@
|
||||
<data name=">>comboCodec.ZOrder" xml:space="preserve">
|
||||
<value>4</value>
|
||||
</data>
|
||||
<data name="comboImage.Enabled" type="System.Boolean, mscorlib">
|
||||
<value>False</value>
|
||||
</data>
|
||||
<data name="comboImage.Items" xml:space="preserve">
|
||||
<value>image</value>
|
||||
</data>
|
||||
|
||||
558
CUETools.Codecs.APE/CUETools.Codecs.APE.cpp
Normal file
558
CUETools.Codecs.APE/CUETools.Codecs.APE.cpp
Normal file
@@ -0,0 +1,558 @@
|
||||
// This is the main DLL file.
|
||||
|
||||
using namespace System;
|
||||
using namespace System::Text;
|
||||
using namespace System::Collections::Generic;
|
||||
using namespace System::Collections::Specialized;
|
||||
using namespace System::Runtime::InteropServices;
|
||||
using namespace System::IO;
|
||||
using namespace APETagsDotNet;
|
||||
using namespace CUETools::Codecs;
|
||||
|
||||
#ifndef _WAVEFORMATEX_
|
||||
#define _WAVEFORMATEX_
|
||||
|
||||
#define BOOL int
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define HWND long
|
||||
|
||||
/*
|
||||
* extended waveform format structure used for all non-PCM formats. this
|
||||
* structure is common to all non-PCM formats.
|
||||
*/
|
||||
typedef struct tWAVEFORMATEX
|
||||
{
|
||||
Int16 wFormatTag; /* format type */
|
||||
Int16 nChannels; /* number of channels (i.e. mono, stereo...) */
|
||||
Int32 nSamplesPerSec; /* sample rate */
|
||||
Int32 nAvgBytesPerSec; /* for buffer estimation */
|
||||
Int16 nBlockAlign; /* block size of data */
|
||||
Int16 wBitsPerSample; /* number of bits per sample of mono data */
|
||||
Int16 cbSize; /* the count in bytes of the size of */
|
||||
/* extra information (after cbSize) */
|
||||
} WAVEFORMATEX, *PWAVEFORMATEX, *NPWAVEFORMATEX, *LPWAVEFORMATEX;
|
||||
|
||||
#endif /* _WAVEFORMATEX_ */
|
||||
|
||||
#include "All.h"
|
||||
#include "MACLib.h"
|
||||
#include "IO.h"
|
||||
|
||||
namespace CUETools { namespace Codecs { namespace APE {
|
||||
|
||||
class CWinFileIO : public CIO
|
||||
{
|
||||
public:
|
||||
|
||||
// construction / destruction
|
||||
CWinFileIO(GCHandle gchIO, GCHandle gchBuffer)
|
||||
{
|
||||
_gchIO = gchIO;
|
||||
_gchBuffer = gchBuffer;
|
||||
}
|
||||
~CWinFileIO()
|
||||
{
|
||||
}
|
||||
|
||||
// open / close
|
||||
int Open(const wchar_t * pName)
|
||||
{
|
||||
throw gcnew Exception("CIO::Open Unsupported.");
|
||||
}
|
||||
int Close()
|
||||
{
|
||||
throw gcnew Exception("CIO::Close Unsupported.");
|
||||
}
|
||||
|
||||
// read / write
|
||||
int Read(void * pBuffer, unsigned int nBytesToRead, unsigned int * pBytesRead);
|
||||
int Write(const void * pBuffer, unsigned int nBytesToWrite, unsigned int * pBytesWritten);
|
||||
|
||||
// seek
|
||||
int Seek(int nDistance, unsigned int nMoveMode);
|
||||
|
||||
// other functions
|
||||
int SetEOF()
|
||||
{
|
||||
throw gcnew Exception("CIO::SetEOF unsupported.");
|
||||
}
|
||||
|
||||
// creation / destruction
|
||||
int Create(const wchar_t * pName)
|
||||
{
|
||||
throw gcnew Exception("CIO::Create unsupported.");
|
||||
}
|
||||
|
||||
int Delete()
|
||||
{
|
||||
throw gcnew Exception("CIO::Delete unsupported.");
|
||||
}
|
||||
|
||||
// attributes
|
||||
int GetPosition();
|
||||
int GetSize();
|
||||
|
||||
int GetName(wchar_t * pBuffer)
|
||||
{
|
||||
throw gcnew Exception("CIO::GetName unsupported.");
|
||||
}
|
||||
|
||||
private:
|
||||
GCHandle _gchIO;
|
||||
GCHandle _gchBuffer;
|
||||
};
|
||||
|
||||
public ref class APEReader : public IAudioSource
|
||||
{
|
||||
public:
|
||||
APEReader(String^ path, Stream^ IO)
|
||||
{
|
||||
pAPEDecompress = NULL;
|
||||
_sampleOffset = 0;
|
||||
_bufferOffset = 0;
|
||||
_bufferLength = 0;
|
||||
_path = path;
|
||||
|
||||
int nRetVal = 0;
|
||||
|
||||
_IO = (IO != nullptr) ? IO : gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read);
|
||||
_readBuffer = gcnew array<unsigned char>(0x4000);
|
||||
_gchIO = GCHandle::Alloc(_IO);
|
||||
_gchReadBuffer = GCHandle::Alloc(_readBuffer);
|
||||
_winFileIO = new CWinFileIO(_gchIO, _gchReadBuffer);
|
||||
pAPEDecompress = CreateIAPEDecompressEx (_winFileIO, &nRetVal);
|
||||
if (!pAPEDecompress) {
|
||||
throw gcnew Exception("Unable to open file.");
|
||||
}
|
||||
|
||||
_sampleRate = pAPEDecompress->GetInfo (APE_INFO_SAMPLE_RATE, 0, 0);
|
||||
_bitsPerSample = pAPEDecompress->GetInfo (APE_INFO_BITS_PER_SAMPLE, 0, 0);
|
||||
_channelCount = pAPEDecompress->GetInfo (APE_INFO_CHANNELS, 0, 0);
|
||||
|
||||
// make a buffer to hold 16384 blocks of audio data
|
||||
nBlockAlign = pAPEDecompress->GetInfo (APE_INFO_BLOCK_ALIGN, 0, 0);
|
||||
_samplesBuffer = gcnew array<unsigned char> (16384 * nBlockAlign);
|
||||
|
||||
// loop through the whole file
|
||||
_sampleCount = pAPEDecompress->GetInfo (APE_DECOMPRESS_TOTAL_BLOCKS, 0, 0); // * ?
|
||||
}
|
||||
|
||||
~APEReader ()
|
||||
{
|
||||
if (_winFileIO)
|
||||
delete _winFileIO;
|
||||
if (_gchIO.IsAllocated)
|
||||
_gchIO.Free();
|
||||
if (_gchReadBuffer.IsAllocated)
|
||||
_gchReadBuffer.Free();
|
||||
}
|
||||
|
||||
virtual property Int32 BitsPerSample {
|
||||
Int32 get() {
|
||||
return _bitsPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property Int32 ChannelCount {
|
||||
Int32 get() {
|
||||
return _channelCount;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property Int32 SampleRate {
|
||||
Int32 get() {
|
||||
return _sampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property UInt64 Length {
|
||||
UInt64 get() {
|
||||
return _sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property UInt64 Position
|
||||
{
|
||||
UInt64 get() {
|
||||
return _sampleOffset - SamplesInBuffer;
|
||||
}
|
||||
void set(UInt64 offset) {
|
||||
_sampleOffset = offset;
|
||||
_bufferOffset = 0;
|
||||
_bufferLength = 0;
|
||||
if (pAPEDecompress->Seek ((int) offset /*? */))
|
||||
throw gcnew Exception("Unable to seek.");
|
||||
}
|
||||
}
|
||||
|
||||
virtual property UInt64 Remaining {
|
||||
UInt64 get() {
|
||||
return _sampleCount - _sampleOffset + SamplesInBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Close()
|
||||
{
|
||||
if (pAPEDecompress)
|
||||
{
|
||||
delete pAPEDecompress;
|
||||
pAPEDecompress = NULL;
|
||||
}
|
||||
if (_IO != nullptr)
|
||||
{
|
||||
_IO->Close ();
|
||||
_IO = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property String^ Path {
|
||||
String^ get() {
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property NameValueCollection^ Tags {
|
||||
NameValueCollection^ get ()
|
||||
{
|
||||
if (!_tags)
|
||||
{
|
||||
APETagDotNet^ apeTag = gcnew APETagDotNet (_IO, true);
|
||||
_tags = apeTag->GetStringTags (true);
|
||||
apeTag->Close ();
|
||||
}
|
||||
return _tags;
|
||||
}
|
||||
void set (NameValueCollection ^tags)
|
||||
{
|
||||
_tags = tags;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool UpdateTags(bool preserveTime)
|
||||
{
|
||||
Close ();
|
||||
APETagDotNet^ apeTag = gcnew APETagDotNet (_path, true, false);
|
||||
apeTag->SetStringTags (_tags, true);
|
||||
apeTag->Save();
|
||||
apeTag->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual UInt32 Read([Out] array<Int32, 2>^ buff, UInt32 sampleCount)
|
||||
{
|
||||
UInt32 buffOffset = 0;
|
||||
UInt32 samplesNeeded = sampleCount;
|
||||
|
||||
while (samplesNeeded != 0)
|
||||
{
|
||||
if (SamplesInBuffer == 0)
|
||||
{
|
||||
int nBlocksRetrieved;
|
||||
pin_ptr<unsigned char> pSampleBuffer = &_samplesBuffer[0];
|
||||
if (pAPEDecompress->GetData ((char *) pSampleBuffer, 16384, &nBlocksRetrieved))
|
||||
throw gcnew Exception("An error occurred while decoding.");
|
||||
_bufferOffset = 0;
|
||||
_bufferLength = nBlocksRetrieved;
|
||||
_sampleOffset += nBlocksRetrieved;
|
||||
}
|
||||
UInt32 copyCount = Math::Min(samplesNeeded, SamplesInBuffer);
|
||||
AudioSamples::BytesToFLACSamples_16(_samplesBuffer, _bufferOffset*nBlockAlign, buff, buffOffset, copyCount, _channelCount);
|
||||
samplesNeeded -= copyCount;
|
||||
buffOffset += copyCount;
|
||||
_bufferOffset += copyCount;
|
||||
}
|
||||
return sampleCount;
|
||||
}
|
||||
|
||||
private:
|
||||
IAPEDecompress * pAPEDecompress;
|
||||
|
||||
NameValueCollection^ _tags;
|
||||
Int64 _sampleCount, _sampleOffset;
|
||||
Int32 _bitsPerSample, _channelCount, _sampleRate;
|
||||
UInt32 _bufferOffset, _bufferLength;
|
||||
int nBlockAlign;
|
||||
array<unsigned char>^ _samplesBuffer;
|
||||
String^ _path;
|
||||
Stream^ _IO;
|
||||
array<unsigned char>^ _readBuffer;
|
||||
CWinFileIO* _winFileIO;
|
||||
GCHandle _gchIO, _gchReadBuffer;
|
||||
|
||||
property UInt32 SamplesInBuffer
|
||||
{
|
||||
UInt32 get ()
|
||||
{
|
||||
return (UInt32) (_bufferLength - _bufferOffset);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public ref class APEWriter : IAudioDest
|
||||
{
|
||||
public:
|
||||
APEWriter(String^ path, Int32 bitsPerSample, Int32 channelCount, Int32 sampleRate)
|
||||
{
|
||||
if (channelCount != 1 && channelCount != 2)
|
||||
throw gcnew Exception("Only stereo and mono audio formats are allowed.");
|
||||
if (bitsPerSample != 16 && bitsPerSample != 24)
|
||||
throw gcnew Exception("Monkey's Audio doesn't support selected bits per sample value.");
|
||||
|
||||
_path = path;
|
||||
_tags = gcnew NameValueCollection();
|
||||
_winFileIO = NULL;
|
||||
|
||||
_compressionLevel = COMPRESSION_LEVEL_NORMAL;
|
||||
|
||||
_bitsPerSample = bitsPerSample;
|
||||
_channelCount = channelCount;
|
||||
_sampleRate = sampleRate;
|
||||
_blockAlign = _channelCount * ((_bitsPerSample + 7) / 8);
|
||||
|
||||
int nRetVal;
|
||||
pAPECompress = CreateIAPECompress (&nRetVal);
|
||||
if (!pAPECompress)
|
||||
throw gcnew Exception("Unable to open APE compressor.");
|
||||
}
|
||||
|
||||
~APEWriter()
|
||||
{
|
||||
if (_winFileIO)
|
||||
delete _winFileIO;
|
||||
if (_gchIO.IsAllocated)
|
||||
_gchIO.Free();
|
||||
if (_gchBuffer.IsAllocated)
|
||||
_gchBuffer.Free();
|
||||
}
|
||||
|
||||
virtual void Close()
|
||||
{
|
||||
if (pAPECompress)
|
||||
{
|
||||
pAPECompress->Finish (NULL, 0, 0);
|
||||
delete pAPECompress;
|
||||
pAPECompress = NULL;
|
||||
}
|
||||
|
||||
if ((_finalSampleCount != 0) && (_samplesWritten != _finalSampleCount)) {
|
||||
throw gcnew Exception("Samples written differs from the expected sample count.");
|
||||
}
|
||||
|
||||
if (_tags->Count > 0)
|
||||
{
|
||||
APETagDotNet^ apeTag = gcnew APETagDotNet (_IO, true);
|
||||
apeTag->SetStringTags (_tags, true);
|
||||
apeTag->Save();
|
||||
apeTag->Close();
|
||||
_tags->Clear ();
|
||||
}
|
||||
|
||||
if (_IO != nullptr)
|
||||
{
|
||||
_IO->Close ();
|
||||
_IO = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Delete()
|
||||
{
|
||||
try { Close (); } catch (Exception^) {}
|
||||
File::Delete(_path);
|
||||
}
|
||||
|
||||
virtual property Int64 FinalSampleCount
|
||||
{
|
||||
Int64 get()
|
||||
{
|
||||
return _finalSampleCount;
|
||||
}
|
||||
void set(Int64 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;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property Int64 BlockSize
|
||||
{
|
||||
void set(Int64 value)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
virtual property int BitsPerSample
|
||||
{
|
||||
int get() { return _bitsPerSample; }
|
||||
}
|
||||
|
||||
virtual void Write(array<Int32,2>^ buff, UInt32 sampleCount)
|
||||
{
|
||||
if (_sampleBuffer == nullptr || _sampleBuffer.Length < sampleCount * _blockAlign)
|
||||
_sampleBuffer = gcnew array<unsigned char>(sampleCount * _blockAlign);
|
||||
AudioSamples::FLACSamplesToBytes(buff, 0, _sampleBuffer, 0, sampleCount, _channelCount, _bitsPerSample);
|
||||
if (!_initialized) Initialize();
|
||||
pin_ptr<unsigned char> pSampleBuffer = &_sampleBuffer[0];
|
||||
if (pAPECompress->AddData (pSampleBuffer, sampleCount * _blockAlign))
|
||||
throw gcnew Exception("An error occurred while encoding.");
|
||||
_samplesWritten += sampleCount;
|
||||
}
|
||||
|
||||
virtual property String^ Path
|
||||
{
|
||||
String^ get() {
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool SetTags (NameValueCollection^ tags)
|
||||
{
|
||||
_tags = tags;
|
||||
return true;
|
||||
}
|
||||
|
||||
property Int32 CompressionLevel {
|
||||
Int32 get() {
|
||||
return _compressionLevel;
|
||||
}
|
||||
void set(Int32 value) {
|
||||
if ((value < 1) || (value > 5)) {
|
||||
throw gcnew Exception("Invalid compression mode.");
|
||||
}
|
||||
_compressionLevel = value * 1000;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
IAPECompress * pAPECompress;
|
||||
bool _initialized;
|
||||
Int32 _finalSampleCount, _samplesWritten;
|
||||
Int32 _bitsPerSample, _channelCount, _sampleRate, _blockAlign;
|
||||
Int32 _compressionLevel;
|
||||
NameValueCollection^ _tags;
|
||||
String^ _path;
|
||||
Stream^ _IO;
|
||||
GCHandle _gchIO, _gchBuffer;
|
||||
CWinFileIO* _winFileIO;
|
||||
array<unsigned char>^ _writeBuffer;
|
||||
array<unsigned char>^ _sampleBuffer;
|
||||
|
||||
void Initialize() {
|
||||
_IO = gcnew FileStream (_path, FileMode::Create, FileAccess::ReadWrite, FileShare::Read);
|
||||
_writeBuffer = gcnew array<unsigned char>(0x4000);
|
||||
|
||||
_gchIO = GCHandle::Alloc(_IO);
|
||||
_gchBuffer = GCHandle::Alloc(_writeBuffer);
|
||||
_winFileIO = new CWinFileIO(_gchIO, _gchBuffer);
|
||||
|
||||
WAVEFORMATEX waveFormat;
|
||||
FillWaveFormatEx (&waveFormat, _sampleRate, _bitsPerSample, _channelCount);
|
||||
|
||||
int res = pAPECompress->StartEx (_winFileIO,
|
||||
&waveFormat,
|
||||
(_finalSampleCount == 0) ? MAX_AUDIO_BYTES_UNKNOWN : _finalSampleCount * _blockAlign,
|
||||
_compressionLevel,
|
||||
NULL,
|
||||
CREATE_WAV_HEADER_ON_DECOMPRESSION);
|
||||
if (res)
|
||||
throw gcnew Exception("Unable to create the encoder.");
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
};
|
||||
|
||||
int CWinFileIO::Read(void * pBuffer, unsigned int nBytesToRead, unsigned int * pBytesRead)
|
||||
{
|
||||
array<unsigned char>^ buff = (array<unsigned char>^) _gchBuffer.Target;
|
||||
if (buff->Length < nBytesToRead)
|
||||
{
|
||||
Array::Resize (buff, nBytesToRead);
|
||||
_gchBuffer.Target = buff;
|
||||
}
|
||||
int len = ((Stream^)_gchIO.Target)->Read (buff, 0, nBytesToRead);
|
||||
if (len) Marshal::Copy (buff, 0, (IntPtr)pBuffer, len);
|
||||
*pBytesRead = len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CWinFileIO::Write(const void * pBuffer, unsigned int nBytesToWrite, unsigned int * pBytesWritten)
|
||||
{
|
||||
array<unsigned char>^ buff = (array<unsigned char>^) _gchBuffer.Target;
|
||||
if (buff->Length < nBytesToWrite)
|
||||
{
|
||||
Array::Resize (buff, nBytesToWrite);
|
||||
_gchBuffer.Target = buff;
|
||||
}
|
||||
Marshal::Copy ((IntPtr)(void*)pBuffer, buff, 0, nBytesToWrite);
|
||||
((Stream^)_gchIO.Target)->Write (buff, 0, nBytesToWrite);
|
||||
*pBytesWritten = nBytesToWrite;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int CWinFileIO::GetPosition()
|
||||
{
|
||||
return ((Stream^)_gchIO.Target)->Position;
|
||||
}
|
||||
|
||||
int CWinFileIO::GetSize()
|
||||
{
|
||||
return ((Stream^)_gchIO.Target)->Length;
|
||||
}
|
||||
|
||||
int CWinFileIO::Seek(int delta, unsigned int mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case FILE_BEGIN:
|
||||
((Stream^)_gchIO.Target)->Seek (delta, System::IO::SeekOrigin::Begin);
|
||||
break;
|
||||
case FILE_END:
|
||||
((Stream^)_gchIO.Target)->Seek (delta, System::IO::SeekOrigin::End);
|
||||
break;
|
||||
case FILE_CURRENT:
|
||||
((Stream^)_gchIO.Target)->Seek (delta, System::IO::SeekOrigin::Current);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
extern "C"
|
||||
{
|
||||
BOOL GetMMXAvailable();
|
||||
int CalculateDotProduct8(const short* pA, const short* pB, int nOrder);
|
||||
};
|
||||
|
||||
public ref class SSE2Functions
|
||||
{
|
||||
public:
|
||||
SSE2Functions ()
|
||||
{
|
||||
_haveSSE2 = GetMMXAvailable();
|
||||
}
|
||||
int SumInts (short*a, short*b, int count)
|
||||
{
|
||||
if (_haveSSE2 && count == 8)
|
||||
return CalculateDotProduct8(a, b, count);
|
||||
int sum = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
sum += a[j] * b[j];
|
||||
return sum;
|
||||
}
|
||||
int SumInts (int*a, short*b, int count)
|
||||
{
|
||||
int sum = 0;
|
||||
for (int j = 0; j < count; j++)
|
||||
sum += a[j] * b[j];
|
||||
return sum;
|
||||
}
|
||||
private:
|
||||
bool _haveSSE2;
|
||||
};
|
||||
#endif
|
||||
}}}
|
||||
@@ -10,7 +10,7 @@ using namespace System::Security::Permissions;
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
//
|
||||
[assembly:AssemblyTitleAttribute("FLACDotNet")];
|
||||
[assembly:AssemblyTitleAttribute("CUETools.Codecs.FLAC")];
|
||||
[assembly:AssemblyDescriptionAttribute("")];
|
||||
[assembly:AssemblyConfigurationAttribute("")];
|
||||
[assembly:AssemblyCompanyAttribute("")];
|
||||
|
||||
@@ -85,8 +85,10 @@ namespace CUETools.Codecs.LossyWAV
|
||||
process_this_codec_block();
|
||||
}
|
||||
}
|
||||
if (_lwcdfDest != null)
|
||||
try { _lwcdfDest.Close(); }
|
||||
catch { }
|
||||
if (_audioDest != null) _audioDest.Close();
|
||||
if (_lwcdfDest != null) _lwcdfDest.Close();
|
||||
}
|
||||
|
||||
public void Write(int[,] buff, uint sampleCount)
|
||||
|
||||
561
CUETools.Codecs.WavPack/CUETools.Codecs.WavPack.cpp
Normal file
561
CUETools.Codecs.WavPack/CUETools.Codecs.WavPack.cpp
Normal file
@@ -0,0 +1,561 @@
|
||||
// ****************************************************************************
|
||||
//
|
||||
// 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 System::Security::Cryptography;
|
||||
using namespace System::IO;
|
||||
using namespace APETagsDotNet;
|
||||
using namespace CUETools::Codecs;
|
||||
|
||||
#include <stdio.h>
|
||||
#include <memory.h>
|
||||
#include "wavpack.h"
|
||||
#include <string.h>
|
||||
|
||||
namespace CUETools { namespace Codecs { namespace WavPack {
|
||||
int write_block(void *id, void *data, int32_t length);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
|
||||
public delegate int32_t DecoderReadDelegate(void *id, void *data, int32_t bcount);
|
||||
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
|
||||
public delegate uint32_t DecoderTellDelegate(void *id);
|
||||
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
|
||||
public delegate int DecoderSeekDelegate(void *id, uint32_t pos);
|
||||
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
|
||||
public delegate int DecoderSeekRelativeDelegate(void *id, int32_t delta, int mode);
|
||||
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
|
||||
public delegate int DecoderPushBackDelegate(void *id, int c);
|
||||
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
|
||||
public delegate uint32_t DecoderLengthDelegate(void *id);
|
||||
[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
|
||||
public delegate int DecoderCanSeekDelegate(void *id);
|
||||
|
||||
public ref class WavPackReader : public IAudioSource
|
||||
{
|
||||
public:
|
||||
WavPackReader(String^ path, Stream^ IO, Stream^ IO_WVC)
|
||||
{
|
||||
char errorMessage[256];
|
||||
|
||||
_readDel = gcnew DecoderReadDelegate (this, &WavPackReader::ReadCallback);
|
||||
_tellDel = gcnew DecoderTellDelegate (this, &WavPackReader::TellCallback);
|
||||
_seekDel = gcnew DecoderSeekDelegate (this, &WavPackReader::SeekCallback);
|
||||
_seekRelDel = gcnew DecoderSeekRelativeDelegate (this, &WavPackReader::SeekRelCallback);
|
||||
_pushBackDel = gcnew DecoderPushBackDelegate (this, &WavPackReader::PushBackCallback);
|
||||
_lengthDel = gcnew DecoderLengthDelegate (this, &WavPackReader::LengthCallback);
|
||||
_canSeekDel = gcnew DecoderCanSeekDelegate (this, &WavPackReader::CanSeekCallback);
|
||||
|
||||
ioReader = new WavpackStreamReader;
|
||||
ioReader->read_bytes = (int32_t (*)(void *, void *, int32_t)) Marshal::GetFunctionPointerForDelegate(_readDel).ToPointer();
|
||||
ioReader->get_pos = (uint32_t (*)(void *)) Marshal::GetFunctionPointerForDelegate(_tellDel).ToPointer();
|
||||
ioReader->set_pos_abs = (int (*)(void *, uint32_t)) Marshal::GetFunctionPointerForDelegate(_seekDel).ToPointer();
|
||||
ioReader->set_pos_rel = (int (*)(void *, int32_t, int)) Marshal::GetFunctionPointerForDelegate(_seekRelDel).ToPointer();
|
||||
ioReader->push_back_byte = (int (*)(void *, int)) Marshal::GetFunctionPointerForDelegate(_pushBackDel).ToPointer();
|
||||
ioReader->get_length = (uint32_t (*)(void *)) Marshal::GetFunctionPointerForDelegate(_lengthDel).ToPointer();
|
||||
ioReader->can_seek = (int (*)(void *)) Marshal::GetFunctionPointerForDelegate(_canSeekDel).ToPointer();
|
||||
ioReader->write_bytes = NULL;
|
||||
|
||||
_IO_ungetc = _IO_WVC_ungetc = -1;
|
||||
|
||||
_path = path;
|
||||
|
||||
_IO = (IO != nullptr) ? IO : gcnew FileStream (path, FileMode::Open, FileAccess::Read, FileShare::Read);
|
||||
_IO_WVC = (IO != nullptr) ? IO_WVC : System::IO::File::Exists (path+"c") ? gcnew FileStream (path+"c", FileMode::Open, FileAccess::Read, FileShare::Read) : nullptr;
|
||||
|
||||
_wpc = WavpackOpenFileInputEx (ioReader, "v", _IO_WVC != nullptr ? "c" : NULL, 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()
|
||||
{
|
||||
delete ioReader;
|
||||
}
|
||||
|
||||
virtual property Int32 BitsPerSample {
|
||||
Int32 get() {
|
||||
return _bitsPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property Int32 ChannelCount {
|
||||
Int32 get() {
|
||||
return _channelCount;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property Int32 SampleRate {
|
||||
Int32 get() {
|
||||
return _sampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property UInt64 Length {
|
||||
UInt64 get() {
|
||||
return _sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property UInt64 Position {
|
||||
UInt64 get() {
|
||||
return _sampleOffset;
|
||||
}
|
||||
void set(UInt64 offset) {
|
||||
_sampleOffset = offset;
|
||||
if (!WavpackSeekSample(_wpc, offset)) {
|
||||
throw gcnew Exception("Unable to seek.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual property UInt64 Remaining {
|
||||
UInt64 get() {
|
||||
return _sampleCount - _sampleOffset;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property String^ Path {
|
||||
String^ get() {
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property NameValueCollection^ Tags {
|
||||
NameValueCollection^ get () {
|
||||
if (!_tags)
|
||||
{
|
||||
APETagDotNet^ apeTag = gcnew APETagDotNet (_IO, true);
|
||||
_tags = apeTag->GetStringTags (true);
|
||||
apeTag->Close ();
|
||||
}
|
||||
return _tags;
|
||||
}
|
||||
void set (NameValueCollection ^tags) {
|
||||
_tags = tags;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool UpdateTags(bool preserveTime)
|
||||
{
|
||||
Close ();
|
||||
APETagDotNet^ apeTag = gcnew APETagDotNet (_path, true, false);
|
||||
apeTag->SetStringTags (_tags, true);
|
||||
apeTag->Save();
|
||||
apeTag->Close();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void Close()
|
||||
{
|
||||
if (_wpc != NULL)
|
||||
_wpc = WavpackCloseFile(_wpc);
|
||||
if (_IO != nullptr)
|
||||
{
|
||||
_IO->Close ();
|
||||
_IO = nullptr;
|
||||
}
|
||||
if (_IO_WVC != nullptr)
|
||||
{
|
||||
_IO_WVC->Close ();
|
||||
_IO_WVC = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
virtual UInt32 Read(array<Int32, 2>^ sampleBuffer, UInt32 sampleCount)
|
||||
{
|
||||
pin_ptr<Int32> pSampleBuffer = &sampleBuffer[0, 0];
|
||||
int samplesRead = WavpackUnpackSamples(_wpc, pSampleBuffer, sampleCount);
|
||||
_sampleOffset += samplesRead;
|
||||
if (samplesRead != sampleCount)
|
||||
throw gcnew Exception("Decoder returned a different number of samples than requested.");
|
||||
return sampleCount;
|
||||
}
|
||||
|
||||
private:
|
||||
WavpackContext *_wpc;
|
||||
NameValueCollection^ _tags;
|
||||
Int32 _sampleCount, _sampleOffset;
|
||||
Int32 _bitsPerSample, _channelCount, _sampleRate;
|
||||
String^ _path;
|
||||
Stream^ _IO;
|
||||
Stream^ _IO_WVC;
|
||||
DecoderReadDelegate^ _readDel;
|
||||
DecoderTellDelegate^ _tellDel;
|
||||
DecoderSeekDelegate^ _seekDel;
|
||||
DecoderSeekRelativeDelegate^ _seekRelDel;
|
||||
DecoderPushBackDelegate^ _pushBackDel;
|
||||
DecoderLengthDelegate^ _lengthDel;
|
||||
DecoderCanSeekDelegate^ _canSeekDel;
|
||||
array<unsigned char>^ _readBuffer;
|
||||
int _IO_ungetc, _IO_WVC_ungetc;
|
||||
WavpackStreamReader* ioReader;
|
||||
|
||||
int32_t ReadCallback (void *id, void *data, int32_t bcount)
|
||||
{
|
||||
Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
|
||||
int IO_ungetc = (*(char*)id=='c') ? _IO_WVC_ungetc : _IO_ungetc;
|
||||
int unget_len = 0;
|
||||
|
||||
if (IO_ungetc != -1)
|
||||
{
|
||||
*(unsigned char*)data = (unsigned char) IO_ungetc;
|
||||
if (IO == _IO)
|
||||
_IO_ungetc = -1;
|
||||
else
|
||||
_IO_WVC_ungetc = -1;
|
||||
bcount --;
|
||||
if (!bcount)
|
||||
return 1;
|
||||
data = 1 + (unsigned char*)data;
|
||||
unget_len = 1;
|
||||
}
|
||||
|
||||
if (_readBuffer == nullptr || _readBuffer->Length < bcount)
|
||||
_readBuffer = gcnew array<unsigned char>(bcount < 0x4000 ? 0x4000 : bcount);
|
||||
int len = IO->Read (_readBuffer, 0, bcount);
|
||||
if (len) Marshal::Copy (_readBuffer, 0, (IntPtr)data, len);
|
||||
return len + unget_len;
|
||||
}
|
||||
|
||||
uint32_t TellCallback(void *id)
|
||||
{
|
||||
Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
|
||||
return IO->Position;
|
||||
}
|
||||
|
||||
int SeekCallback (void *id, uint32_t pos)
|
||||
{
|
||||
Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
|
||||
IO->Position = pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SeekRelCallback (void *id, int32_t delta, int mode)
|
||||
{
|
||||
Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
|
||||
switch (mode)
|
||||
{
|
||||
case SEEK_SET:
|
||||
IO->Seek (delta, System::IO::SeekOrigin::Begin);
|
||||
break;
|
||||
case SEEK_END:
|
||||
IO->Seek (delta, System::IO::SeekOrigin::End);
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
IO->Seek (delta, System::IO::SeekOrigin::Current);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PushBackCallback (void *id, int c)
|
||||
{
|
||||
Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
|
||||
if (IO == _IO)
|
||||
{
|
||||
if (_IO_ungetc != -1)
|
||||
throw gcnew Exception("Double PushBackCallback unsupported.");
|
||||
_IO_ungetc = c;
|
||||
} else
|
||||
{
|
||||
if (_IO_WVC_ungetc != -1)
|
||||
throw gcnew Exception("Double PushBackCallback unsupported.");
|
||||
_IO_WVC_ungetc = c;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t LengthCallback (void *id)
|
||||
{
|
||||
Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
|
||||
return IO->Length;
|
||||
}
|
||||
|
||||
int CanSeekCallback(void *id)
|
||||
{
|
||||
Stream^ IO = (*(char*)id=='c') ? _IO_WVC : _IO;
|
||||
return IO->CanSeek;
|
||||
}
|
||||
};
|
||||
|
||||
public ref class WavPackWriter : IAudioDest
|
||||
{
|
||||
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.");
|
||||
if (bitsPerSample < 16 || bitsPerSample > 24)
|
||||
throw gcnew Exception("Bits per sample must be 16..24.");
|
||||
|
||||
_path = path;
|
||||
_tags = gcnew NameValueCollection();
|
||||
|
||||
_compressionMode = 1;
|
||||
_extraMode = 0;
|
||||
_blockSize = 0;
|
||||
|
||||
_bitsPerSample = bitsPerSample;
|
||||
_channelCount = channelCount;
|
||||
_sampleRate = sampleRate;
|
||||
_blockAlign = _channelCount * ((_bitsPerSample + 7) / 8);
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Close()
|
||||
{
|
||||
if (_md5Sum)
|
||||
{
|
||||
_md5hasher->TransformFinalBlock (gcnew array<unsigned char>(1), 0, 0);
|
||||
pin_ptr<unsigned char> md5_digest = &_md5hasher->Hash[0];
|
||||
WavpackStoreMD5Sum (_wpc, md5_digest);
|
||||
}
|
||||
|
||||
WavpackFlushSamples(_wpc);
|
||||
_wpc = WavpackCloseFile(_wpc);
|
||||
fclose(_hFile);
|
||||
|
||||
if ((_finalSampleCount != 0) && (_samplesWritten != _finalSampleCount)) {
|
||||
throw gcnew Exception("Samples written differs from the expected sample count.");
|
||||
}
|
||||
|
||||
if (_tags->Count > 0)
|
||||
{
|
||||
APETagDotNet^ apeTag = gcnew APETagDotNet (_path, true, false);
|
||||
apeTag->SetStringTags (_tags, true);
|
||||
apeTag->Save();
|
||||
apeTag->Close();
|
||||
_tags->Clear ();
|
||||
}
|
||||
}
|
||||
|
||||
virtual void Delete()
|
||||
{
|
||||
try { Close (); } catch (Exception^) {}
|
||||
File::Delete(_path);
|
||||
}
|
||||
|
||||
virtual property Int64 FinalSampleCount
|
||||
{
|
||||
Int64 get()
|
||||
{
|
||||
return _finalSampleCount;
|
||||
}
|
||||
void set(Int64 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;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property Int64 BlockSize
|
||||
{
|
||||
void set(Int64 value)
|
||||
{
|
||||
_blockSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
virtual property int BitsPerSample
|
||||
{
|
||||
int get() { return _bitsPerSample; }
|
||||
}
|
||||
|
||||
virtual void Write(array<Int32, 2>^ sampleBuffer, UInt32 sampleCount)
|
||||
{
|
||||
if (!_initialized)
|
||||
Initialize();
|
||||
|
||||
if (MD5Sum)
|
||||
{
|
||||
if (_sampleBuffer == nullptr || _sampleBuffer.Length < sampleCount * _blockAlign)
|
||||
_sampleBuffer = gcnew array<unsigned char>(sampleCount * _blockAlign);
|
||||
AudioSamples::FLACSamplesToBytes(sampleBuffer, 0, _sampleBuffer, 0, sampleCount, _channelCount, _bitsPerSample);
|
||||
UpdateHash(_sampleBuffer, (int) sampleCount * _blockAlign);
|
||||
}
|
||||
|
||||
if ((_bitsPerSample & 7) != 0)
|
||||
{
|
||||
if (_shiftedSampleBuffer == nullptr || _shiftedSampleBuffer.GetLength(0) < sampleCount)
|
||||
_shiftedSampleBuffer = gcnew array<int,2>(sampleCount, _channelCount);
|
||||
for (int i = 0; i < sampleCount; i++)
|
||||
for (int c = 0; c < _channelCount; c++)
|
||||
_shiftedSampleBuffer[i,c] = sampleBuffer[i,c] << 8 - (_bitsPerSample & 7);
|
||||
pin_ptr<Int32> pSampleBuffer = &_shiftedSampleBuffer[0, 0];
|
||||
if (!WavpackPackSamples(_wpc, (int32_t*)pSampleBuffer, sampleCount))
|
||||
throw gcnew Exception("An error occurred while encoding.");
|
||||
} else
|
||||
{
|
||||
pin_ptr<Int32> pSampleBuffer = &sampleBuffer[0, 0];
|
||||
if (!WavpackPackSamples(_wpc, (int32_t*)pSampleBuffer, sampleCount))
|
||||
throw gcnew Exception("An error occurred while encoding.");
|
||||
}
|
||||
|
||||
_samplesWritten += sampleCount;
|
||||
}
|
||||
|
||||
virtual property String^ Path
|
||||
{
|
||||
String^ get() {
|
||||
return _path;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool SetTags (NameValueCollection^ tags)
|
||||
{
|
||||
_tags = tags;
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
property bool MD5Sum {
|
||||
bool get() {
|
||||
return _md5Sum;
|
||||
}
|
||||
void set(bool value) {
|
||||
_md5Sum = value;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateHash(array<unsigned char>^ buff, Int32 len)
|
||||
{
|
||||
if (!_initialized) Initialize();
|
||||
|
||||
if (!_md5Sum || !_md5hasher)
|
||||
throw gcnew Exception("MD5 not enabled.");
|
||||
_md5hasher->TransformBlock (buff, 0, len, buff, 0);
|
||||
}
|
||||
|
||||
private:
|
||||
FILE *_hFile;
|
||||
bool _initialized;
|
||||
WavpackContext *_wpc;
|
||||
Int32 _finalSampleCount, _samplesWritten;
|
||||
Int32 _bitsPerSample, _channelCount, _sampleRate, _blockAlign;
|
||||
Int32 _compressionMode, _extraMode, _blockSize;
|
||||
NameValueCollection^ _tags;
|
||||
String^ _path;
|
||||
bool _md5Sum;
|
||||
MD5^ _md5hasher;
|
||||
array<unsigned char>^ _sampleBuffer;
|
||||
array<int,2>^ _shiftedSampleBuffer;
|
||||
|
||||
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 (_md5Sum)
|
||||
{
|
||||
_md5hasher = gcnew MD5CryptoServiceProvider ();
|
||||
config.flags |= CONFIG_MD5_CHECKSUM;
|
||||
}
|
||||
config.block_samples = (int)_blockSize;
|
||||
if (_blockSize > 0 && _blockSize < 2048)
|
||||
config.flags |= CONFIG_MERGE_BLOCKS;
|
||||
|
||||
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);
|
||||
}
|
||||
}}}
|
||||
@@ -1,10 +1,10 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using FLACDotNet;
|
||||
using WavPackDotNet;
|
||||
using APEDotNet;
|
||||
using CUETools.Codecs;
|
||||
using CUETools.Codecs.ALAC;
|
||||
using CUETools.Codecs.FLAC;
|
||||
using CUETools.Codecs.WavPack;
|
||||
using CUETools.Codecs.APE;
|
||||
using CUETools.Codecs.LossyWAV;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
@@ -95,7 +95,7 @@ namespace CUETools.Processor
|
||||
int destBitsPerSample = (config.detectHDCD && config.decodeHDCD) ? ((!config.decodeHDCDtoLW16 && config.decodeHDCDto24bit) ? 24 : 20) : 16;
|
||||
int lossyBitsPerSample = (config.detectHDCD && config.decodeHDCD && !config.decodeHDCDtoLW16) ? 24 : 16;
|
||||
IAudioDest lossyDest = GetAudioDest(path, lossyBitsPerSample, 2, 44100, finalSampleCount, extension, config);
|
||||
IAudioDest lwcdfDest = GetAudioDest(lwcdfPath, destBitsPerSample, 2, 44100, finalSampleCount, extension, config);
|
||||
IAudioDest lwcdfDest = config.lossyWAVHybrid ? GetAudioDest(lwcdfPath, destBitsPerSample, 2, 44100, finalSampleCount, extension, config) : null;
|
||||
return new LossyWAVWriter(lossyDest, lwcdfDest, destBitsPerSample, 2, 44100, config.lossyWAVQuality);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AudioReadWrite.cs" />
|
||||
<Compile Include="Main.cs" />
|
||||
<Compile Include="Processor.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Settings.cs" />
|
||||
</ItemGroup>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -806,6 +806,8 @@ namespace CUETools.Ripper.SCSI
|
||||
}
|
||||
set
|
||||
{
|
||||
_currentTrack = -1;
|
||||
_currentIndex = -1;
|
||||
_sampleOffset = (int) value + _driveOffset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace JDP
|
||||
cueName = Path.GetFileNameWithoutExtension(pathIn) + ".cue";
|
||||
|
||||
bool outputAudio = _accurateRip != AccurateRipMode.Verify;
|
||||
cueSheet.Open(pathIn, _lossyWAV);
|
||||
cueSheet.Open(pathIn);
|
||||
if (outputAudio)
|
||||
{
|
||||
bool pathFound = false;
|
||||
@@ -153,7 +153,7 @@ namespace JDP
|
||||
}
|
||||
else
|
||||
pathOut = Path.Combine(Path.GetDirectoryName(pathIn) ?? pathIn, cueName);
|
||||
cueSheet.GenerateFilenames(_audioFormat, pathOut);
|
||||
cueSheet.GenerateFilenames(_audioFormat, _lossyWAV, pathOut);
|
||||
if (outputAudio)
|
||||
{
|
||||
if (_cueStyle == CUEStyle.SingleFileWithCUE)
|
||||
|
||||
@@ -304,7 +304,7 @@ namespace JDP {
|
||||
bool outputCUE = cueStyle != CUEStyle.SingleFileWithCUE && accurateRip != AccurateRipMode.Verify;
|
||||
string pathOut = null;
|
||||
|
||||
cueSheet.Open(pathIn, lossyWAV);
|
||||
cueSheet.Open(pathIn);
|
||||
|
||||
this.Invoke((MethodInvoker)delegate()
|
||||
{
|
||||
@@ -312,7 +312,7 @@ namespace JDP {
|
||||
pathOut = txtOutputPath.Text;
|
||||
});
|
||||
|
||||
cueSheet.GenerateFilenames(outputFormat, pathOut);
|
||||
cueSheet.GenerateFilenames(outputFormat, lossyWAV, pathOut);
|
||||
string outDir = Path.GetDirectoryName(pathOut);
|
||||
if (cueStyle == CUEStyle.SingleFileWithCUE)
|
||||
cueSheet.SingleFilename = Path.GetFileName(pathOut);
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user