zoukankan      html  css  js  c++  java
  • 在GNU/Linux下将CD音乐转为mp3

    以前我欣赏古典音乐都是听的CD,因而珍藏了不少光盘以及下载到电脑上的ape与flac格式的音乐文件。随着手机硬件性能(如电池续航能力、处理器速度、音质、存储容量等)和软件功能(音乐播放器对于曲目的管理)的提升,便需要考虑如何将这些资源转换成高质量的mp3文件放到手机上聆听。本文介绍如何基于GNU/Linux下的Audacity、k3b、easytag软件,以及自己写的Bash脚本程序来实现此功能。

    从光盘抓取音乐并转为mp3

    k3b是KDE环境下默认的光盘刻录与抓取软件。其界面如下图所示。

    选择菜单Tools中的Rip Audio CD,我们可用它直接从CD光盘上抓取出wav文件。接下来,打开Audacity音乐编辑软件,将抓取的多个wav文件导入(而非打开)为多个音轨。再选择菜单Files中的Export Multiple,将导出格式选为mp3,导出选项中的采样频率设为320 kbps,然后点击导出即可完成转换。如下图所示。

    最后,打开easytag软件,填写mp3文件的曲目信息(ID3 Tag)。注意,点击信息文本框旁边的小圆点,可以一次性地将设置应用到所有选中的文件。如下图所示。

    将ape或flac文件转为mp3文件

    一般下载的CD Rip文件是ape或flac格式的,具有无损音质。可若直接将其拷到手机上,一是某些软件可能不支持这两种格式,二是它们所占用的存储空间太大了,三是由于它们也不包含类似于mp3文件的ID3 tag信息,从而无法被音乐播放软件自动根据专辑名或作曲家名而自动分类管理。因此,有必要将ape和flac文件按曲目拆分并转换为mp3格式,填写曲目信息后再导入手机。

    可以看出,这里首先需要解决的是ape与flac文件的曲目拆分问题。一般来说,与它们一同下载的还会有个扩展名为cue的文件。此文件保存了CD的曲目信息,包括音乐风格(GENRE)、制作年份(DATE)、表演者(PERFORMER)、专辑/CD名称(TITLE)、ape或flac文件的名称(FILE),以及每个音轨(TRACK)的标题(TITLE)、表演者(PERFORMER)和开始时间(INDEX 01)。如下所示:

    REM GENRE Classical
    REM DATE 1991
    REM DISCID 9B0E701C
    REM COMMENT "ExactAudioCopy v0.95b4"
    PERFORMER "Wächter, Sutherland, Schwarzkopf, Taddei, Alva, Sciutti, Frick"
    TITLE "Don Giovanni - Giulini 1/3"
    FILE "CDImage.ape" WAVE
      TRACK 01 AUDIO
        TITLE "Sinfonia"
        PERFORMER "Wächter, Sutherland, Schwarzkopf, Taddei, Alva, Sciutti, Frick"
        INDEX 01 00:00:00
      TRACK 02 AUDIO
        TITLE "Notte e giorno faticar"
        PERFORMER "Wächter, Sutherland, Schwarzkopf, Taddei, Alva, Sciutti, Frick"
        INDEX 01 06:18:07
      TRACK 03 AUDIO
        TITLE "Non sperar, se non m'uccidi"
        PERFORMER "Wächter, Sutherland, Schwarzkopf, Taddei, Alva, Sciutti, Frick"
        INDEX 01 07:58:31

    需要说明的是,某个音轨的信息中可能同时含有INDEX 00与INDEX 01,那么该曲目实际的开始时间是由INDEX 01来指定的。INDEX 01的值比INDEX 00要大一些,目的是为了跳过该曲目前端的空白部分。由INDEX 01指定的时间分为三部分:分钟:秒:1/75秒。

    在搞清楚cue文件的格式后,我们再来看音乐编辑软件Audacity所拥有的一个功能:我们可以通过导入包含各音轨信息的标签(label)文件,再选择Export Multiple,则可以将整个的ape或flac文件作拆分并转换为mp3文件。该标签文件的格式如下:

    0.000000    0.000000    Sinfonia
    378.093333    378.093333    Notte e giorno faticar
    478.413333    478.413333    Non sperar, se non m'uccidi

    其中,每一行代表一个曲目。每一行中的第一列与第二列的内容相同,都是该音轨的开始时间,单位是秒。第三列则是该曲目的标题。

    从cue到label文件的转换可以在这里在线完成,但毕竟不太方便。所以,我写了一个脚本程序cue2lbl.sh,来实现同样的功能。源代码如下:

    #!/bin/bash
    
    script_name="cue2lbl.sh"
    script_usage=$(cat <<EOF
    $script_name [OPTIONS] cue_file
    EOF
    )
    script_function=$(cat <<EOF
    Convert a cue CD index file to Audacity label file.
    EOF
    )
    script_doc=$(cat <<EOF
    Script documentation.
    -h     Display this help.
    -o     Specify output lable file.
    EOF
    )
    script_examples=$(cat <<EOF
    $script_name -o music.txt music.cue
    EOF
    )
    state_prefix="==="
    warning_prefix="***"
    error_prefix="!!!"
    
    function display_help() {
        if [ -n "$script_usage" ]; then
            echo -e "Usage: $script_usage"
        fi
    
        if [ -n "$script_function" ]; then
            echo -e "$script_function"
        fi
    
        if [ -n "$script_doc" ] ; then
            echo -e "
    $script_doc"
        fi
    
        if [ -n "$script_examples" ]; then
            echo -e "
    Examples"
            echo -e "$script_examples"
        fi
    }
    
    # Output label file
    lbl_file=""
    
    # Process command options
    while getopts ":ho:" opt; do
        case $opt in
            h  )  display_help
                exit 0 ;;
            o  )  lbl_file=$OPTARG ;;
            ? )  display_help
                exit 1 ;;
        esac
    done
    shift $(($OPTIND - 1))
    
    # Start execute the command
    if [ -z "$lbl_file" ]; then
        lbl_file="${1%cue}txt"
    fi
    
    # Label file first time writing
    lbl_file_1st_write=1
    
    # Searched state: 0 for start; 1 for track; 2 for title; 3 for index.
    searched_state=0
    
    declare -i track_time_minute=0 track_time_second=0
    
    IFS=$'
    '
    for line in `grep -i "(INDEX)|(TITLE)|(TRACK)" "$1"`; do
        case $searched_state in
            0 )
                if [ -n "`echo "$line" | grep -i TRACK`" ]; then
                    searched_state=1
                fi ;;
            1 )
                if [ -n "`echo "$line" | grep -i "TITLE"`" ]; then
                    # Remove blanks at the beginning and end of a line
                    line=`echo "$line" | gawk -f /usr/local/bin/scripts/rmblank.awk`
                    # Get title
                    track_title=`echo "$line" | gawk -f /usr/local/bin/scripts/cue2lbl_get_title.awk`
                    track_title=`echo "$track_title" | gawk -f /usr/local/bin/scripts/rmblank.awk | tr -d "`
                    searched_state=2
                fi ;;
            2 )
                if [ -n "`echo "$line" | grep -i "INDEX"`" ]; then
                    # Remove blanks at the beginning and end of a line
                    line=`echo "$line" | gawk -f /usr/local/bin/scripts/rmblank.awk`
                    # Get track index
                    line=`echo "$line" | gawk -f /usr/local/bin/scripts/cue2lbl_get_index.awk`
                    track_index=`echo "$line" | gawk -f /usr/local/bin/scripts/rmblank.awk | cut -f 1 -d " " | tr -d " "`
                    if [ "$track_index" = "01" ] || [ "$track_index" = "1" ]; then
                        # Get track time
                        track_time=`echo "$line" | gawk -f /usr/local/bin/scripts/rmblank.awk | cut -f 2 -d " " | tr -d " "`
                        track_time_minute=`echo "$track_time" | cut -f 1 -d ":" | gawk '{if (match($0, "^0+$") != 0) {print "0";} else {gsub("^0+", ""); print}}'`
                        track_time_second=`echo "$track_time" | cut -f 2 -d ":" | gawk '{if (match($0, "^0+$") != 0) {print "0";} else {gsub("^0+", ""); print}}'`
                        track_time_sub_second=`echo "$track_time" | cut -f 3 -d ":" | gawk '{if (match($0, "^0+$") != 0) {print "0";} else {gsub("^0+", ""); print}}'`
                        track_time_total_seconds=$(($track_time_minute * 60 + $track_time_second))
                        track_time_sub_second=`echo "scale=6; $track_time_sub_second / 75" | bc`
                        if [ "$track_time_sub_second" = "0" ]; then
                            track_time_sub_second=".000000"
                        fi
                        track_time_total_seconds=`echo "$track_time_total_seconds$track_time_sub_second"`
                    fi
                fi
    
                if [ -n "`echo "$line" | grep -i "TRACK"`" ]; then
                    # Print collected track information
                    if [ "$lbl_file_1st_write" = "1" ]; then
                        echo -e "$track_time_total_seconds	$track_time_total_seconds	$track_title" > "$lbl_file"
                        lbl_file_1st_write=0
                    else
                        echo -e "$track_time_total_seconds	$track_time_total_seconds	$track_title" >> "$lbl_file"
                    fi
    
                    searched_state=1
                fi ;;
        esac
    done
    
    # Print the last collected track information
    if [ "$lbl_file_1st_write" = "1" ]; then
        echo -e "$track_time_total_seconds	$track_time_total_seconds	$track_title" > "$lbl_file"
    else
        echo -e "$track_time_total_seconds	$track_time_total_seconds	$track_title" >> "$lbl_file"
    fi
    

    为了完成字符串的匹配,该脚本程序中用到了几个gawk程序,分别如下:

    1. 移除行首与行尾的空白字符:

    {
        gsub("(^[[:blank:]]+)|([[:blank:]]+$)", "");
        print;
    }
    

    2. 删除INDEX:

    /^(INDEX)|(index)/ {print gensub("^(INDEX)|(index)[[:blank:]]+", "", 1);}
    

    3. 删除TITLE:

    /^(TITLE)|(title)/ {print gensub("^(TITLE)|(title)[[:blank:]]+", "", 1);}
    

    至此,有了label文件,便可用Audacity将ape和flac转换生成多个mp3文件了。

    将mp3文件导入手机

    目前我的手机上使用的是“酷我音乐”播放器,可以直接通过Wifi由电脑向手机传歌。如下图所示,打开“酷我音乐”后,点击页面底部的“电脑-手机传歌”:

    然后在电脑上打开浏览器,输入提示的地址,添加文件即可。

  • 相关阅读:
    分享一个Fluent风格的邮件发送封装类
    写一个ActionFilter检测WebApi接口请求和响应
    一道有趣的面试题,小鸟和火车的问题
    Centos7 查看Mysql配置文件
    Centos7 grep命令简介
    Centos7 网络配置
    django之python3.4及以上连接mysql的一些问题记录
    NetCore log4net 集成以及配置日志信息不重复显示或者记录
    ionic3中关于Ionic ui component使用的一些总结
    ionic2升级到ionic3并打包APK
  • 原文地址:https://www.cnblogs.com/quantumman/p/4678168.html
Copyright © 2011-2022 走看看