Jump to content
Sign in to follow this  
smashly

Audio CD To Wave Or MP3

Recommended Posts

smashly

Hi,

This script offers a gui that lets you rip the tracks from an Audio CD to wav.

Nothing fancy, code is difficult to read and probably contains bugs.

Only tested in WinXP x86.

Update:

Merged the 2 scripts into 1 (WAV or MP3)

Using a larger in and out buffer to speed things up at the expense of cpu time and memory use.

Rip the tail end sectors in one go (Don't know why I was ripping 1 sector at a time for the tail sectors)

Update:

Dropped the amount of sectors read per loop, makes the buffer size smaller.

Trade off is slower to rip, but less CPU time and less memory use.

Enjoy.

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <ButtonConstants.au3>
#include <ComboConstants.au3>
#include <EditConstants.au3>
#include <StaticConstants.au3>
#include <WinAPI.au3>
#include <GuiListView.au3>
#include <SliderConstants.au3>

Opt("MustDeclareVars", 1)

Global Const $tag_BE_CONFIG = _ ;   {
        "DWORD  dwConfig;" & _ ;            // BE_CONFIG_LAME
        "DWORD  dwStructVersion;" & _ ;    // LAME header version 1
        "DWORD  dwStructSize;" & _ ;       // Size of this structure (332 in autoit, should be 331)
        "DWORD  dwSampleRate;" & _ ;        // SAMPLERATE OF INPUT FILE
        "DWORD  dwReSampleRate;" & _ ;  // DOWNSAMPLERATE, 0=ENCODER DECIDES
        "LONG   nMode;" & _ ;               // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO
        "DWORD  dwBitrate;" & _ ;           // CBR bitrate, VBR min bitrate
        "DWORD  dwMaxBitrate;" & _ ;        // CBR ignored, VBR Max bitrate
        "LONG   nPreset;" & _ ;         // Quality preset, use one of the settings of the LAME_QUALITY_PRESET enum
        "DWORD  dwMpegVersion;" & _ ;       // FUTURE USE, MPEG-1 OR MPEG-2
        "DWORD  dwPsyModel;" & _ ;          // FUTURE USE, SET TO 0
        "DWORD  dwEmphasis;" & _ ;          // FUTURE USE, SET TO 0
        "BOOL   bPrivate;" & _ ;            // Set Private Bit (TRUE/FALSE)
        "BOOL   bCRC;" & _ ;                // Insert CRC (TRUE/FALSE)
        "BOOL   bCopyright;" & _ ;          // Set Copyright Bit (TRUE/FALSE)
        "BOOL   bOriginal;" & _ ;           // Set Original Bit (TRUE/FALSE)
        "BOOL   bWriteVBRHeader;" & _ ; // WRITE XING VBR HEADER (TRUE/FALSE)
        "BOOL   bEnableVBR;" & _ ;          // USE VBR ENCODING (TRUE/FALSE)
        "INT    nVBRQuality;" & _ ;     // VBR QUALITY 0..9
        "DWORD  dwVbrAbr_bps;" & _ ;        // Use ABR in stead of nVBRQuality
        "UINT   nVbrMethod;" & _ ;
        "BOOL   bNoRes; " & _ ;         // Disable Bit resorvoir (TRUE/FALSE)
        "BOOL   bStrictIso;" & _ ;          // Use strict ISO encoding rules (TRUE/FALSE)
        "WORD   nQuality;" & _ ;            // Quality Setting, HIGH BYTE should be NOT LOW byte, otherwhise quality=5
        "BYTE   btReserved[" & 255 - 4 * 4 - 2 & "]"

Global Const $tag_WAV_HEADER = _
        "BYTE RIFF[4];" & _
        "BYTE HdrDataLen[4];" & _
        "BYTE WAVEfmt[8];" & _
        "BYTE FmtChkLen[4];" & _
        "BYTE FmtType[2];" & _
        "BYTE ChanCount[2];" & _
        "BYTE SampRate[4];" & _
        "BYTE BytesPerSec[4];" & _
        "BYTE BytesPerSamp[2];" & _
        "BYTE BitsPerChan[2];" & _
        "BYTE data[4];" & _
        "BYTE DataLen[4]"

Global $h_LameEncDLL = -1
Global $sLameEncDll = "lame_enc.dll"

Global $sDrivesTmp, $aCDInfo
Global $hGui, $iCombo, $iInput, $iBrowse, $iLV, $iInv, $iAll, $iLBR, $iBitrate, $iWAV, $iMP3, $iRip
Global $iProgTrack, $iProgTrackLabel, $iProgTotal, $iProgTotalLabel, $iFlag = 0, $iFormat = 1
Global $aExt[2] = [".mp3", ".wav"]
Global $sOutPath = @ScriptDir & "\CDToWavOrMP3"
Global $aBR = StringSplit("32|40|48|56|64|80|96|112|128|160|192|224|256|320", "|", 2)
Global $iBR = 8

$hGui = GUICreate("Audio CD To Wav Or MP3", 355, 525)
GUICtrlCreateGroup("Select Drive:", 5, 5, 345, 55)
$iCombo = GUICtrlCreateCombo("", 15, 25, 325, 20, $CBS_DROPDOWNLIST)
GUICtrlCreateGroup("Save To...", 5, 65, 345, 55)
$iInput = GUICtrlCreateInput($sOutPath, 15, 85, 300, 20, BitOR($ES_READONLY, $ES_AUTOHSCROLL))
GUICtrlSetBkColor(-1, 0xffffff)
$iBrowse = GUICtrlCreateButton("...", 320, 85, 20, 20)
GUICtrlSetTip(-1, "Browse for save directory.")
GUICtrlCreateGroup("Track Selection:", 5, 125, 345, 280)
$iLV = GUICtrlCreateListView("Track|Size|Time|LBA", 15, 145, 325, 220, -1, $LVS_EX_CHECKBOXES)
$iInv = GUICtrlCreateButton("Invert Selected", 15, 375, 160, 20)
$iAll = GUICtrlCreateButton("Select All", 180, 375, 160, 20)

$iLBR = GUICtrlCreateGroup("Bitrate: " & $aBR[$iBR] & " Kbps", 5, 410, 200, 50)
$iBitrate = GUICtrlCreateSlider(15, 425, 180, 25, BitOR($TBS_BOTH, $TBS_NOTICKS))
GUICtrlSetLimit(-1, 13, 0)
GUICtrlSetData(-1, $iBR)
GUICtrlSetState(-1, $GUI_DISABLE)
GUICtrlCreateGroup("Format", 210, 410, 140, 50)
$iMP3 = GUICtrlCreateRadio("MP3", 230, 430, 50, 20)
If Not FileExists($sLameEncDll) Then GUICtrlSetState(-1, $GUI_DISABLE)
$iWAV = GUICtrlCreateRadio("WAV", 290, 430, 50, 20)
GUICtrlSetState(-1, $GUI_CHECKED)
$iRip = GUICtrlCreateButton("Rip Tracks", 300, 468, 50, 50, $BS_MULTILINE)
$iProgTrack = GUICtrlCreateProgress(5, 470, 290, 20)
$iProgTrackLabel = GUICtrlCreateLabel("Track Progress", 5, 472, 290, 20, $SS_CENTER)
GUICtrlSetBkColor(-1, -2)
GUICtrlSetFont(-1, 9, 700, 0, "Arial")
$iProgTotal = GUICtrlCreateProgress(5, 495, 290, 20)
$iProgTotalLabel = GUICtrlCreateLabel("Total Progress", 5, 497, 290, 20, $SS_CENTER)
GUICtrlSetBkColor(-1, -2)
GUICtrlSetFont(-1, 9, 700, 0, "Arial")
_GetDrives()
GUISetState(@SW_SHOW, $hGui)

GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY")
AdlibRegister("_GetDrives", 1000)

While 1
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            Exit
        Case $iCombo
            _LoadTracksToLV()
        Case $iBrowse
            _Browse()
        Case $iInv
            For $i = 0 To _GUICtrlListView_GetItemCount($iLV) - 1
                If _GUICtrlListView_GetItemChecked($iLV, $i) Then
                    _GUICtrlListView_SetItemChecked($iLV, $i, False)
                Else
                    _GUICtrlListView_SetItemChecked($iLV, $i)
                EndIf
            Next
            $iFlag = 1
        Case $iAll
            For $i = 0 To _GUICtrlListView_GetItemCount($iLV) - 1
                If Not _GUICtrlListView_GetItemChecked($iLV, $i) Then _GUICtrlListView_SetItemChecked($iLV, $i)
            Next
            $iFlag = 1
        Case $iMP3, $iWAV
            If BitAND(GUICtrlRead($iMP3), $GUI_UNCHECKED) Then
                $iFormat = 1
                GUICtrlSetState($iBitrate, $GUI_DISABLE)
            Else
                $iFormat = 0
                GUICtrlSetState($iBitrate, $GUI_ENABLE)
            EndIf
        Case $iRip
            _RipTracks()
        Case Else
            If $iFlag Then _EnableDisableRip()
            If $iBR <> GUICtrlRead($iBitrate) Then
                $iBR = GUICtrlRead($iBitrate)
                GUICtrlSetData($iLBR, "Bitrate: " & $aBR[$iBR] & " Kbps")
            EndIf
    EndSwitch
WEnd



Func _Browse()
    Local $sFSF
    $sFSF = FileSelectFolder("Select a folder to save wave files in.", "", 1, "", $hGui)
    If Not @error Then
        $sOutPath = $sFSF
        GUICtrlSetData($iInput, $sOutPath)
    EndIf
EndFunc   ;==>_Browse

Func _GetDrives()
    Local $sTmp, $aDrives, $sDrives, $aCD
    $sTmp = GUICtrlRead($iCombo)
    $aDrives = DriveGetDrive("CDROM")
    Dim $aCDInfo[$aDrives[0] + 1]
    For $i = 1 To $aDrives[0]
        $aCD = _GetAudioCDInfo($aDrives[$i])
        If IsArray($aCD) Then
            $aCDInfo[$i] = $aCD
            $sDrives &= StringUpper($aDrives[$i]) & StringFormat(' [Audio CD, %s Tracks, %.2f MB Used Disc Space]', $aCD[0][0], ($aCD[0][1] * 2352) / 1024 / 1024) & "|"
        Else
            $aCDInfo[$i] = 0
            $sDrives &= StringUpper($aDrives[$i]) & " [Audio CD Not Detected]" & "|"
        EndIf
    Next
    $sDrives = StringTrimRight($sDrives, 1)
    If $sDrivesTmp <> $sDrives Then
        GUICtrlSetData($iCombo, "")
        $sDrivesTmp = $sDrives
        If $sTmp <> "" And StringInStr($sDrivesTmp, $sTmp) Then
            GUICtrlSetData($iCombo, $sDrivesTmp, $sTmp)
        Else
            GUICtrlSetData($iCombo, $sDrivesTmp, StringLeft($sDrivesTmp, StringInStr($sDrivesTmp, "|") - 1))
        EndIf
        _LoadTracksToLV()
    EndIf
EndFunc   ;==>_GetDrives

Func _LoadTracksToLV()
    Local $sDrv, $aDrv, $aCD, $iTrkSector, $iTrkMS, $sFmt = "Track%02d|%d KB|%02d:%02d|%d"
    $sDrv = StringLeft(GUICtrlRead($iCombo), 2)
    $aDrv = StringSplit($sDrivesTmp, "|")
    For $i = 1 To $aDrv[0]
        If $sDrv = StringLeft($aDrv[$i], 2) Then $aCD = $aCDInfo[$i]
    Next
    _GUICtrlListView_DeleteAllItems($iLV)
    If Not IsArray($aCD) Then
        GUICtrlSetState($iInv, $GUI_DISABLE)
        GUICtrlSetState($iAll, $GUI_DISABLE)
        $iFlag = 1
        Return 0
    EndIf
    For $i = 1 To $aCD[0][0]
        $iTrkSector = $aCD[$i][1] - $aCD[$i][0]
        $iTrkMS = ($iTrkSector / 75) * 1000
        GUICtrlCreateListViewItem(StringFormat($sFmt, $i, Ceiling(($iTrkSector * 2352) / 1024), Mod($iTrkMS / 1000, 6000) / 60, Mod(Mod($iTrkMS / 1000, 3600), 60), $aCD[$i][0]), $iLV)
        GUICtrlSetState(-1, $GUI_CHECKED)
    Next
    For $i = 0 To 3
        _GUICtrlListView_SetColumnWidth($iLV, $i, $LVSCW_AUTOSIZE)
    Next
    GUICtrlSetState($iInv, $GUI_ENABLE)
    GUICtrlSetState($iAll, $GUI_ENABLE)
    $iFlag = 1
EndFunc   ;==>_LoadTracksToLV

Func _GetAudioCDInfo($sDrive)
    Local Const $IOCTL_CDROM_READ_TOC = 0x00024000
    Local Const $IOCTL_CDROM_DISK_TYPE = 0x00020040
    Local $hCD, $tDiscType, $iReturn, $tCDTOC, $iTrkLast, $bData, $iRead
    $hCD = _WinAPI_CreateFile("\\.\" & $sDrive, 2, 6, 6)
    If Not $hCD Then Return SetError(-1, 0, 0)
    $tDiscType = DllStructCreate("ULONG DiskData")
    $iReturn = _WinAPI_DeviceIoControl($hCD, $IOCTL_CDROM_DISK_TYPE, 0, 0, DllStructGetPtr($tDiscType), DllStructGetSize($tDiscType), $iRead)
    If Not $iReturn Or (DllStructGetData($tDiscType, "DiskData") <> 1) Then
        _WinAPI_CloseHandle($hCD)
        Return SetError(1, 0, 0)
    EndIf
    $tCDTOC = DllStructCreate("BYTE Length[2];BYTE FirstTrack;BYTE LastTrack;BYTE TrackData[800]")
    DllStructSetData($tCDTOC, "Length", DllStructGetSize($tCDTOC))
    If Not _WinAPI_DeviceIoControl($hCD, $IOCTL_CDROM_READ_TOC, 0, 0, DllStructGetPtr($tCDTOC), DllStructGetSize($tCDTOC), $iRead) Then
        _WinAPI_CloseHandle($hCD)
        Return SetError(2, 0, 0)
    EndIf
    $iTrkLast = DllStructGetData($tCDTOC, "LastTrack")
    If Not $iTrkLast Then Return SetError(2, 0, 0)
    $bData = DllStructGetData($tCDTOC, "TrackData")
    Dim $aTrk[$iTrkLast + 1][3]
    $aTrk[0][0] = $iTrkLast
    $aTrk[0][1] = BinaryMid($bData, 8 * $iTrkLast + 6, 3)
    $aTrk[0][1] = Int(BinaryMid($aTrk[0][1], 1, 1)) * 75 * 60 + Int(BinaryMid($aTrk[0][1], 2, 1)) * 75 + Int(BinaryMid($aTrk[0][1], 3, 1)) - 150
    If $aTrk[0][1] <= 300 Then ; Minimum Sectors to Audio CD 300 Sectors = 4 Seconds (the first track starts after 2 secs = 150 sectors)
        _WinAPI_CloseHandle($hCD)
        $tCDTOC = 0
        Return SetError(3, 0, 0)
    EndIf
    Dim $aMSF[4] = [$bData, 6, 7, 8]
    For $i = 1 To $aTrk[0][0]
        $aTrk[$i][0] = (Int(BinaryMid($aMSF[0], $aMSF[1], 1)) * 75 * 60) + (Int(BinaryMid($aMSF[0], $aMSF[2], 1)) * 75) + Int(BinaryMid($aMSF[0], $aMSF[3], 1)) - 150
        For $j = 1 To 3
            $aMSF[$j] += 8
        Next
        $aTrk[$i][1] = (Int(BinaryMid($aMSF[0], $aMSF[1], 1)) * 75 * 60) + (Int(BinaryMid($aMSF[0], $aMSF[2], 1)) * 75) + Int(BinaryMid($aMSF[0], $aMSF[3], 1)) - 150
        $aTrk[$i][2] = 1
    Next
    _WinAPI_CloseHandle($hCD)
    Return SetError(0, 0, $aTrk)
EndFunc   ;==>_GetAudioCDInfo

Func _RipTracks()
    Local $sDrv, $aDrv, $aCD, $iOverwrite, $hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg
    Local $sCurTrack, $hWriteOut, $hStream, $iReturn, $iSamples, $iMP3Buffer, $iOutput, $iWritten
    Local $iTS = 0, $iTSS = 0, $iTSD = 0, $aTCL[2], $iTP = 0, $iTPD = 0, $aPCL[2], $iStep = 28 ;$iStep = 288

    AdlibUnRegister("_GetDrives")
    _EnableDisableAll($GUI_DISABLE)
    GUICtrlSetData($iRip, "Abort")
    $sDrv = StringLeft(GUICtrlRead($iCombo), 2)
    $aDrv = StringSplit($sDrivesTmp, "|")
    For $i = 1 To $aDrv[0]
        If $sDrv = StringLeft($aDrv[$i], 2) Then $aCD = $aCDInfo[$i]
    Next
    For $i = 1 To _GUICtrlListView_GetItemCount($iLV)
        If _GUICtrlListView_GetItemChecked($iLV, $i - 1) Then
            $iTP += ($aCD[$i][1] - $aCD[$i][0])
            If FileExists($sOutPath & StringFormat("\Track%02d" & $aExt[$iFormat], $i)) Then $iOverwrite = 1
        EndIf
    Next
    If $iOverwrite Then
        Switch MsgBox(35, 'Overwite Warning', 'You are about to overwrite already existing tracks.' & @LF & _
                'Would you like to rip these tracks to a different directory?' & @LF & @LF & _
                'Click "Yes" to browse for a different save directory.' & @LF & _
                'Click "No" to overwrite existing tracks.' & @LF & _
                'Click "Cancel" to Abort ripping tracks.')
            Case 2
                Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, "", 15)
            Case 6
                _Browse()
        EndSwitch
    EndIf
    If Not FileExists($sOutPath) Then DirCreate($sOutPath)
    ; Open CD drive
    $hCD = _WinAPI_CreateFile("\\.\" & $sDrv, 2, 6, 6)
    If Not $hCD Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, "", -1)

    ; Structure that we use to specify track offset and sectors we want ripped.
    $tINF = DllStructCreate("int64 DiskOffset;ULONG SectorCount;ULONG TrackMode")

    ; Set $tInfo struct to CDDA mode
    DllStructSetData($tINF, "TrackMode", 2)

    ; Create In, Out buffers, wav header, encoder settings as needed.
    If $iFormat Then ; Set up for wave rip.

        ; Set the wav buffer the CD will dump it's data into.
        $tWavBuf = DllStructCreate("BYTE[" & (2352 * $iStep) & "]")

        ; Create a wave header stuct to write to wav file (header is 44 bytes in length)
        $tWavHdr = DllStructCreate($tag_WAV_HEADER)
        DllStructSetData($tWavHdr, "RIFF", "RIFF")
        DllStructSetData($tWavHdr, "WAVEfmt", "WAVEfmt ")
        DllStructSetData($tWavHdr, "FmtChkLen", 16)
        DllStructSetData($tWavHdr, "FmtType", 1)
        DllStructSetData($tWavHdr, "ChanCount", 2)
        DllStructSetData($tWavHdr, "SampRate", 44100)
        DllStructSetData($tWavHdr, "BytesPerSec", 176400)
        DllStructSetData($tWavHdr, "BytesPerSamp", 4)
        DllStructSetData($tWavHdr, "BitsPerChan", 16)
        DllStructSetData($tWavHdr, "data", "data")
    Else ; Set up for mp3 rip.

        ; Create lame settings struct and fill it with some basic settings.
        $tCfg = DllStructCreate($tag_BE_CONFIG)
        DllStructSetData($tCfg, "dwConfig", 256) ;// BE_CONFIG_LAME
        DllStructSetData($tCfg, "dwStructVersion", 1) ;// Struct version (LVH1)
        DllStructSetData($tCfg, "dwStructSize", DllStructGetSize($tCfg)); // Struct Size
        DllStructSetData($tCfg, "dwSampleRate", 44100);         // INPUT FREQUENCY
        DllStructSetData($tCfg, "nMode", 1) ;BE_MP3_MODE_JSTEREO;   // OUTPUT IN JOINT STEREO
        DllStructSetData($tCfg, "dwBitrate", $aBR[$iBR]);                   // MINIMUM BIT RATE
        DllStructSetData($tCfg, "nPreset", 12) ;LQP_CBR;        // QUALITY PRESET SETTING
        DllStructSetData($tCfg, "dwMpegVersion", 1);MPEG1;    // MPEG VERSION (I or II)
        DllStructSetData($tCfg, "bOriginal", 1);                    // SET ORIGINAL FLAG
        DllStructSetData($tCfg, "bWriteVBRHeader", 1) ;       // Write mp3 header, even on ABR or CBR mp3.

        ; Lame encoder expects signed 16bit 2 bytes so use a SHORT data type
        $tWavBuf = DllStructCreate("SHORT[" & (2352 * $iStep) / 2 & "]")

        ; Create MP3 buffer to receive the encoded mp3 data
        $tMP3Buf = DllStructCreate("BYTE[" & 1.25 * (2352 * $iStep / 2 / 2) + 7200 & "]")
    EndIf


    ; Loop through the list view, if the track is selected then process it.
    For $i = 1 To _GUICtrlListView_GetItemCount($iLV)
        If _GUICtrlListView_GetItemChecked($iLV, $i - 1) Then

            ; Current file name for output track
            $sCurTrack = $sOutPath & StringFormat("\Track%02d" & $aExt[$iFormat], $i)

            ; Create a wav or mp3 file to write the buffer to.
            $hWriteOut = _WinAPI_CreateFile($sCurTrack, 1, 4)
            If Not $hWriteOut Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, "", -2)

            ; Calculate how many sectors the track has.
            $iTS = $aCD[$i][1] - $aCD[$i][0]

            ; Make sure the track total sectors are evenly divded by $iStep, we'll catch any sector < $iStep later on.
            $iTSS = Floor($iTS / $iStep) * $iStep

            ; Tell $tINF struct how many sectors we want it to rip from the CD
            DllStructSetData($tINF, "SectorCount", $iStep)


            If $iFormat Then ; Set up for wave header.

                ;Set data in the wav header stuct.
                DllStructSetData($tWavHdr, "HdrDataLen", 36 + ($iTS * 2352))
                DllStructSetData($tWavHdr, "DataLen", $iTS * 2352)

                ; Write the header struct to the wav file
                _WinAPI_WriteFile($hWriteOut, DllStructGetPtr($tWavHdr), 44, $iWritten)

                If Not (44 = $iWritten) Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, $sCurTrack, 8)

            Else ;Set up for mp3 rip

                ; Open lame_enc.dll
                If Not _Open_LameEnc($sLameEncDll) Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, "", 1)

                ; Init the MP3 Stream
                If Not _beInitStream(DllStructGetPtr($tCfg), $iSamples, $iMP3Buffer, $hStream) Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, "", 2)
            EndIf


            ; Rip the track sectors.
            $iReturn = _RipSectors($hCD, $hWriteOut, $i, $aCD[$i][0], $aCD[$i][0] + $iTSS, $hStream, $tWavBuf, $tMP3Buf, $tINF, $iTSD, $iTS, $aTCL, $iTPD, $iTP, $aPCL)
            If $iReturn Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, $sCurTrack, $iReturn)

            ; Catch the sectors that are < $iStep and rip them.
            If $iTS > $iTSS Then

                ; Set $tINF amount of sectors to rip to the amount sectors left over.
                DllStructSetData($tINF, "SectorCount", $iTS - $iTSS)

                ; Rip the last sectors.
                $iReturn = _RipSectors($hCD, $hWriteOut, $i, $aCD[$i][0] + $iTSS, $aCD[$i][1], $hStream, $tWavBuf, $tMP3Buf, $tINF, $iTSD, $iTS, $aTCL, $iTPD, $iTP, $aPCL)
                If $iReturn Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, $sCurTrack, $iReturn)
            EndIf

            If $iFormat Then ; close wave file
                _WinAPI_CloseHandle($hWriteOut)

            Else ; finalize mp3

                ; Flush the encoder of any data that may be left.
                If Not _beDeinitStream($hStream, DllStructGetPtr($tMP3Buf), $iOutput) Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, $sCurTrack, 6)

                ; Write any data that was flushed from the encoder to the mp3 file.
                _WinAPI_WriteFile($hWriteOut, DllStructGetPtr($tMP3Buf), $iOutput, $iWritten)
                If Not ($iOutput = $iWritten) Then Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, $sCurTrack, 7)

                ; Close the stream.
                _beCloseStream($hStream)

                ; Close the mp3 file
                _WinAPI_CloseHandle($hWriteOut)

                ; Write a header to the mp3 file, can do this even for a CBR mp3
                _beWriteVBRHeader($sCurTrack)

                ; Close the encoder dll.
                _Close_LameEnc()
                $hStream = 0
            EndIf

            ; Reset the Total sectors Done for the track.
            $iTSD = 0
        EndIf
    Next
    Return _StopRip($hCD, $tINF, $tWavHdr, $tWavBuf, $tMP3Buf, $tCfg, $hWriteOut, $hStream, "", $iReturn)
EndFunc   ;==>_RipTracks

Func _RipSectors($hCD, $hWriteOut, $iTrk, $iStart, $iEnd, $hStream, $tWavBuf, $tMP3Buf, ByRef $tINF, ByRef $iTSD, ByRef $iTS, ByRef $aTCL, ByRef $iTPD, ByRef $iTP, ByRef $aPCL)
    Local Const $IOCTL_CDROM_RAW_READ = 0x0002403E
    Local $iStep, $iReturn, $iRead, $iOutput, $iWritten

    ; Get the step of how many sectors per loop we are ripping
    $iStep = DllStructGetData($tINF, "SectorCount")

    For $j = $iStart To $iEnd - 1 Step $iStep

        ;Set CD read offset in the $tINF struct to start reading sectors from.
        DllStructSetData($tINF, "DiskOffset", $j * 2048)

        ; Pass the $tINF struct and $tWavBuf struct to retieve the next buffer of data from the CD
        $iReturn = _WinAPI_DeviceIoControl($hCD, $IOCTL_CDROM_RAW_READ, DllStructGetPtr($tINF), DllStructGetSize($tINF), DllStructGetPtr($tWavBuf), DllStructGetSize($tWavBuf), $iRead)
        If Not $iReturn Or ($iRead <> (2352 * $iStep)) Then Return 3

        If $iFormat Then

            ; Write the bytes from the $tWavbuf to the wav file
            _WinAPI_WriteFile($hWriteOut, DllStructGetPtr($tWavBuf), $iRead, $iWritten)
            If Not ($iRead = $iWritten) Then Return 5

        Else
            ; Pass the Encoder the handle to the MP3 stream, how many samples to read from the $tWavBuf, $iOutput returns the amount of bytes that are in the $tMP3Buf.
            If Not _beEncodeChunk($hStream, $iRead / 2, DllStructGetPtr($tWavBuf), DllStructGetPtr($tMP3Buf), $iOutput) Then Return 4

            ; Write the bytes from the $tMP3buf to the mp3
            _WinAPI_WriteFile($hWriteOut, DllStructGetPtr($tMP3Buf), $iOutput, $iWritten)
            If Not ($iOutput = $iWritten) Then Return 5
        EndIf

        ; Update the Gui progress bars and labels.
        $iTSD += $iStep
        $aTCL[0] = Round(100 * $iTSD / $iTS)
        If $aTCL[0] <> $aTCL[1] Then
            $aTCL[1] = $aTCL[0]
            GUICtrlSetData($iProgTrack, $aTCL[1])
            GUICtrlSetData($iProgTrackLabel, StringFormat("Track%02d Progress %d%", $iTrk, $aTCL[1]))
        EndIf
        $iTPD += $iStep
        $aPCL[0] = Round(100 * $iTPD / $iTP)
        If $aPCL[0] <> $aPCL[1] Then
            $aPCL[1] = $aPCL[0]
            GUICtrlSetData($iProgTotal, $aPCL[1])
            GUICtrlSetData($iProgTotalLabel, "Total Progress " & $aPCL[1] & "%")
        EndIf

        ; Give the user a chance to bail out of encoding.
        If GUIGetMsg() = $iRip Then Return 15
    Next
    Return 0
EndFunc   ;==>_RipSectors

Func _StopRip($hCD, ByRef $t1, ByRef $t2, ByRef $t3, ByRef $t4, ByRef $t5, $hWriteOut = 0, $hStream = 0, $sTrack = "", $iError = 0)
    If $hCD Then _WinAPI_CloseHandle($hCD)
    $t1 = 0
    $t2 = 0
    $t3 = 0
    $t4 = 0
    $t5 = 0
    If $hWriteOut Then _WinAPI_CloseHandle($hWriteOut)
    If $h_LameEncDLL <> -1 Then
        If $hStream Then _beCloseStream($hStream)
        _Close_LameEnc()
    EndIf
    If $sTrack <> "" Then FileDelete($sTrack)
    Switch $iError
        Case 0
            If MsgBox(68, "Rip Completed", "Open the output directory?", 0, $hGui) = 6 Then ShellExecute($sOutPath)
        Case -1
            MsgBox(16, "Rip Failed", "Failed to open drive.", 0, $hGui)
        Case -2
            MsgBox(16, "Rip Failed", "Failed to open ouput file to write to.", 0, $hGui)
        Case 1
            MsgBox(16, "Rip Failed", "Unable to open lame_enc.dll", 0, $hGui)
        Case 2
            MsgBox(16, "Rip Failed", "Unable to create stream for MP3.", 0, $hGui)
        Case 3
            MsgBox(16, "Rip Failed", "Failed to copy sector from CD.", 0, $hGui)
        Case 4
            MsgBox(16, "Rip Failed", "Failed to encode CD buffer to MP3.", 0, $hGui)
        Case 5
            MsgBox(16, "Rip Failed", "Failed writing buffer to file.", 0, $hGui)
        Case 6
            MsgBox(16, "Rip Failed", "Failed to flush the encoder.", 0, $hGui)
        Case 7
            MsgBox(48, "Rip Aborted", "Failed to write flushed data to the mp3 file.", 0, $hGui)
        Case 8
            MsgBox(48, "Rip Aborted", "Failed to write wave header to file.", 0, $hGui)
        Case 15
            MsgBox(48, "Rip Aborted", "User Aborted Rip.", 0, $hGui)
    EndSwitch
    GUICtrlSetData($iRip, "Rip Tracks")
    GUICtrlSetData($iProgTrack, 0)
    GUICtrlSetData($iProgTrackLabel, "Track Progress")
    GUICtrlSetData($iProgTotal, 0)
    GUICtrlSetData($iProgTotalLabel, "Total Progress")
    _EnableDisableAll()
    AdlibRegister("_GetDrives", 1000)
EndFunc   ;==>_StopRip

Func _EnableDisableRip()
    Local $iTest = 0
    For $i = 0 To _GUICtrlListView_GetItemCount($iLV) - 1
        If _GUICtrlListView_GetItemChecked($iLV, $i) Then $iTest = 1
    Next
    If $iTest And BitAND(GUICtrlGetState($iRip), $GUI_DISABLE) Then
        GUICtrlSetState($iRip, $GUI_ENABLE)
    ElseIf Not $iTest And BitAND(GUICtrlGetState($iRip), $GUI_ENABLE) Then
        GUICtrlSetState($iRip, $GUI_DISABLE)
    EndIf
    $iFlag = 0
EndFunc   ;==>_EnableDisableRip

Func _EnableDisableAll($iState = $GUI_ENABLE)
    For $i = $iCombo To $iWAV
        If $i = $iBitrate And $iFormat And $iState = $GUI_ENABLE Then ContinueLoop
        If $i = $iMP3 And Not FileExists($sLameEncDll) And $iState = $GUI_ENABLE Then ContinueLoop
        GUICtrlSetState($i, $iState)
    Next
EndFunc   ;==>_EnableDisableAll

Func _WinAPI_DeviceIoControl($hFile, $iIoControlCode, $pInBuffer, $iInBufferSize, $pOutBuffer, $iOutBufferSize, ByRef $iRead, $pOverlapped = 0)
    Local $aReturn = DllCall('kernel32.dll', 'int', 'DeviceIoControl', 'ptr', $hFile, 'dword', $iIoControlCode, 'ptr', $pInBuffer, 'dword', $iInBufferSize, 'ptr', $pOutBuffer, 'dword', $iOutBufferSize, 'dword*', 0, 'ptr', $pOverlapped)
    If @error Then Return SetError(@error, @extended, 0)
    $iRead = $aReturn[7]
    Return SetError($aReturn[0] = 0, 0, $aReturn[0])
EndFunc   ;==>_WinAPI_DeviceIoControl

Func WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    #forceref $hWnd, $iMsg, $iwParam
    Local $hWndFrom, $iIDFrom, $iCode, $tNMHDR, $hListView, $aHit
    $hListView = GUICtrlGetHandle($iLV)
    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    Switch $hWndFrom
        Case $hListView
            Switch $iCode
                Case $NM_CLICK
                    $aHit = _GUICtrlListView_HitTest($hListView)
                    If $aHit[4] Then $iFlag = 1
            EndSwitch
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

; Some Lame Functions =================================================================================
Func _Open_LameEnc($sLameEncDll = "lame_enc.dll")
    $h_LameEncDLL = DllOpen($sLameEncDll)
    Return SetError($h_LameEncDLL = -1, 0, $h_LameEncDLL <> -1)
EndFunc   ;==>_Open_LameEnc

Func _beInitStream($pBeConfig, ByRef $iSamples, ByRef $iBufferSize, ByRef $hStream)
    Local $aReturn, $aResult[3]
    $aReturn = DllCall($h_LameEncDLL, "ulong:cdecl", "beInitStream", "ptr", $pBeConfig, "dword*", 0, "dword*", 0, "ptr*", 0)
    If @error Then Return SetError(-1, @error, @error = 0)
    $iSamples = $aReturn[2]
    $iBufferSize = $aReturn[3]
    $hStream = $aReturn[4]
    Return SetError($aReturn[0], 0, $aResult[0] = 0)
EndFunc   ;==>_beInitStream

Func _beEncodeChunk($hStream, $iSamples, $pWAVBuffer, $pMP3Buffer, ByRef $iOutput)
    Local $aReturn
    $aReturn = DllCall($h_LameEncDLL, "ulong:cdecl", "beEncodeChunk", "ptr", $hStream, "dword", $iSamples, "ptr", $pWAVBuffer, "ptr", $pMP3Buffer, "int*", 0)
    If @error Then Return SetError(-1, @error, @error = 0)
    $iOutput = $aReturn[5]
    Return SetError($aReturn[0], 0, $aReturn[0] = 0)
EndFunc   ;==>_beEncodeChunk

Func _beDeinitStream($hStream, $pMP3Buffer, ByRef $iOutput)
    Local $aReturn
    $aReturn = DllCall($h_LameEncDLL, "ulong:cdecl", "beDeinitStream", "ptr", $hStream, "ptr", $pMP3Buffer, "dword*", 0)
    If @error Then Return SetError(-1, 0, @error = 0)
    $iOutput = $aReturn[3]
    Return SetError($aReturn[0], $aReturn[3], $aReturn[0] = 0)
EndFunc   ;==>_beDeinitStream

Func _beCloseStream($hStream)
    Local $aReturn
    $aReturn = DllCall($h_LameEncDLL, "ulong:cdecl", "beCloseStream", "ptr", $hStream)
    If @error Then Return SetError(-1, 0, @error = 0)
    Return SetError($aReturn[0], 0, $aReturn[0] = 0)
EndFunc   ;==>_beCloseStream

Func _beWriteVBRHeader($sMp3File)
    Local $aReturn
    $aReturn = DllCall($h_LameEncDLL, "ulong:cdecl", "beWriteVBRHeader", "str", $sMp3File)
    If @error Then Return SetError(-1, 0, @error = 0)
    Return SetError($aReturn[0], 0, $aReturn[0] = 0)
EndFunc   ;==>_beWriteVBRHeader

Func _Close_LameEnc()
    DllClose($h_LameEncDLL)
    $h_LameEncDLL = -1
EndFunc   ;==>_Close_LameEnc

; =========================================================================================================

It only encodes mp3 as CBR with user selectable bitrate from 32 ~ 320 Kbps

(only cause I was to lazy to add controls for VBR/ABR/Presets/Quality/Bit Flags/Resample etc ..lol)

I've attached lame_enc.dll v3.97b to this post or you may want to grab the latest version 3.98.4 (which is larger in size) from http://www.rarewares.org/mp3.php

Either will work with this example.

Cheers

lame_enc.dll

Edited by smashly

Share this post


Link to post
Share on other sites
dmob

Impressive! Lots to learn from...

Share this post


Link to post
Share on other sites
twitchyliquid64

Very nice.

You clearly have a solid understanding of how the data is stored on file.


ongoing projects:-firestorm: Largescale P2P Social NetworkCompleted Autoit Programs/Scripts: Variable Pickler | Networked Streaming Audio (in pure autoIT) | firenet p2p web messenger | Proxy Checker | Dynamic Execute() Code Generator | P2P UDF | Graph Theory Proof of Concept - Breadth First search

Share this post


Link to post
Share on other sites
monoscout999

As i already told you in the other thread, it works in win7 64x

Great job! :)

Share this post


Link to post
Share on other sites
pierrotm777

A cool option would be to rip also to .mp3 :)

Share this post


Link to post
Share on other sites
TheSaint

Thanks for sharing! :)


AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Share this post


Link to post
Share on other sites
ChristophX086

A cool option would be to rip also to .mp3 :)

It would be possible to use LAME (http://lame.sourceforge.net/) to convert the .wav files to mp3.

I would use the Run() command and the command line "lame [options] inputfile [outputfile]".

Share this post


Link to post
Share on other sites
smashly

Hi and your welcome all.

@pierrotm777 I mainly posted the code to show how you can raw rip an audio cd by using autoit and windows.

Maybe using windows ACM you could rip the cd to mp3. (since WMP 10 and above installs l3codecp.acm which is Fraunhofer pro mp3 acm codec)

Using windows acm the user could save the tracks in any format that is listed in their window acm.

But for me to do it I'd need to learn more about windows acm, maybe at a later time I might look into it.

@ChristophX64 I would use lame_enc.dll instead of the exe. Seems tidier to use DllCall() then Run(), but that's just my opinion.

Also I can pass the cd rip buffer straight to lame_enc.dll, eliminating having to write a wave file at all.

(I've just about finished adding lame_enc.dll support to the script I posted, will update the first post when it's completed)

Cheers

Edited by smashly

Share this post


Link to post
Share on other sites
ChristophX086

@smashly I think your idea is much better than mine, but I just wanted to share my idea. Anyway great script, I really like it.

Share this post


Link to post
Share on other sites
smashly

Hi,

Updated 1st post with another example ripping from Audio CD straight to MP3 using lame_enc.dll

Cheers

Share this post


Link to post
Share on other sites
smashly

hi,

updated first post.

merged 2 scripts into 1

cheers

Share this post


Link to post
Share on other sites
ChristophX086

Could you do the same thing with movie dvds and .mpeg files?

Share this post


Link to post
Share on other sites
UEZ

Hi smashly,

I'm getting always an em Failed to copy sector from CD when trying to rip an audio CD to MP3/Wave - your Audio CD to BIN-CUE code is however working properly.

The TOC seems to read properly. Device: HP EliteBook 8440p notebook.

Looks very impressive.

Btw, I will try to implement your cue/bin converter for audio CDs into ISO Creator :)

Br,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
smashly

Hi UEZ,

Hope you had a good holiday, good to see you back :)

As a guess I'd say the error would be $iStep = 288, that makes the buffer for reading a block of sectors 288 * 2352 = 677376 /1024 = 661.5 KB.

From what I read DeviceIoControl and raw read buffer is meant to be around 64KB to 128KB, but on my PC I just pushed the buffer size up and it worked without probs on 2 different pc's I was using at the time.

Try dropping $iStep to lower value ($iStep is local var in _RipTracks() function) Lower value such as $iStep = 28 would make the raw read buffer 64KB. If that doesn't work then I have no idea as to why DeviceIoControl is failing to read the specified amount of sectors. (changed the first post to $iStep = 28, see if works or not)

Cheers

Edited by smashly

Share this post


Link to post
Share on other sites
sheeva

Hi Smashy,

First, Congratulations on this excellent script.

I had exactly the same error as huez, by changing the value $iSTEP = 28 it works

perfectly. :)

Thank's

Share this post


Link to post
Share on other sites
UEZ

Yes, with $iStep = 28 it is working properly!

Suggestion: possibility to rename track names, play tracks, online check (cddb) for track names

Anyway, 5* from me for your great work!

Br,

UEZ


Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
smashly

Hi and thank you for the bug reports,

I've stated on using windows ACM (Audio Compression Manager), but still have a bit to work out.

But atm I can rip my tracks to wma, mp3, ogg using windows ACM, I just need to tidy up the acm functions before I post it.

Cheers

Share this post


Link to post
Share on other sites
TheSaint

Once again Thanks!

Looking forward to seeing your results.

And looking forward to losing reliance on CDRTools and Lame.

:)


AutoIt.4.Life Clubrooms - Life is like a Donut (secret key)

Make sure brain is in gear before opening mouth!
Remember, what is not said, can be just as important as what is said.

Spoiler

What is the Secret Key? Life is like a Donut

If I put effort into communication, I expect you to read properly & fully, or just not comment.
Ignoring those who try to divert conversation with irrelevancies.
If I'm intent on insulting you or being rude, I will be obvious, not ambiguous about it.
I'm only big and bad, to those who have an over-active imagination.

I may have the Artistic Liesense ;) to disagree with you. TheSaint's Toolbox (be advised many downloads are not working due to ISP screwup with my storage)

userbar.png

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×