播放器歌词LRC

   最近在做一款android手机上的音乐播放器,学习到了很多东西,像是Fragment,ActionBar的使用等等,这里就先介绍一下歌词同步的实现问题。

     歌词同步的实现思路很简单:获取歌词文件LRC中的时间和歌词内容,然后在指定的时间内播放相应的内容。获取不难,难就在于如何在手机屏幕上实现歌词的滚动。

     先上效果图:

    

     先从最基本的读取歌词文件开始:

复制代码
public class LrcHandle {
    private List<String> mWords = new ArrayList<String>();
    private List<Integer> mTimeList = new ArrayList<Integer>();

    //处理歌词文件
    public void readLRC(String path) {
        File file = new File(path);

        try {
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStreamReader inputStreamReader = new InputStreamReader(
                    fileInputStream, "utf-8");
            BufferedReader bufferedReader = new BufferedReader(
                    inputStreamReader);
            String s = "";
            while ((s = bufferedReader.readLine()) != null) {
                addTimeToList(s);
                if ((s.indexOf("[ar:") != -1) || (s.indexOf("[ti:") != -1)
                        || (s.indexOf("[by:") != -1)) {
                    s = s.substring(s.indexOf(":") + 1, s.indexOf("]"));
                } else {
                    String ss = s.substring(s.indexOf("["), s.indexOf("]") + 1);
                    s = s.replace(ss, "");
                }
                mWords.add(s);
            }

            bufferedReader.close();
            inputStreamReader.close();
            fileInputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            mWords.add("没有歌词文件,赶紧去下载");
        } catch (IOException e) {
            e.printStackTrace();
            mWords.add("没有读取到歌词");
        }
    }
   public List<String> getWords() {
        return mWords;
   }

    public List<Integer> getTime() {
        return mTimeList;
    }

    // 分离出时间
    private int timeHandler(String string) {
       string = string.replace(".", ":");
       String timeData[] = string.split(":");
// 分离出分、秒并转换为整型
        int minute = Integer.parseInt(timeData[0]);
        int second = Integer.parseInt(timeData[1]);
        int millisecond = Integer.parseInt(timeData[2]);

        // 计算上一行与下一行的时间转换为毫秒数
        int currentTime = (minute * 60 + second) * 1000 + millisecond * 10;

        return currentTime;
    }

   private void addTimeToList(String string) {
        Matcher matcher = Pattern.compile(
                "\\[\\d{1,2}:\\d{1,2}([\\.:]\\d{1,2})?\\]").matcher(string);
        if (matcher.find()) {
            String str = matcher.group();
            mTimeList.add(new LrcHandle().timeHandler(str.substring(1,
                    str.length() - 1)));
        }

    }
}
复制代码

     一般歌词文件的格式大概如下:

[ar:艺人名]
[ti:曲名]
[al:专辑名]
[by:编者(指编辑LRC歌词的人)]
[offset:时间补偿值] 其单位是毫秒,正值表示整体提前,负值相反。这是用于总体调整显示快慢的。
     但也不一定,有时候并没有前面那些ar:等标识符,所以我们这里也提供了另一种解析方式。
     歌词文件中的时间格式则比较统一:[00:00.50]等等,00:表示分钟,00.表示秒数,.50表示毫秒数,当然,我们最后是要将它们转化为毫秒数处理才比较方便。
     处理完歌词文件并得到我们想要的数据后,我们就要考虑如何在手机上滚动显示我们的歌词并且与我们得到的时间同步了。
     先是布局文件:
复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <Button
        android:id="@+id/button"
        android:layout_width="60dip"
        android:layout_height="60dip"
        android:text="@string/停止" />

    <com.example.slidechange.WordView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/button" />

</RelativeLayout>
复制代码

       WordView是自定义的TextView,它继承自TextView:

复制代码
public class WordView extends TextView {
    private List<String> mWordsList = new ArrayList<String>();
    private Paint mLoseFocusPaint;
    private Paint mOnFocusePaint;
    private float mX = 0;
    private float mMiddleY = 0;
    private float mY = 0;
    private static final int DY = 50;
    private int mIndex = 0;

    public WordView(Context context) throws IOException {
        super(context);
        init();
    }

    public WordView(Context context, AttributeSet attrs) throws IOException {
        super(context, attrs);
        init();
    }

    public WordView(Context context, AttributeSet attrs, int defStyle)
            throws IOException {
        super(context, attrs, defStyle);
        init();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        canvas.drawColor(Color.BLACK);
        Paint p = mLoseFocusPaint;
        p.setTextAlign(Paint.Align.CENTER);
        Paint p2 = mOnFocusePaint;
        p2.setTextAlign(Paint.Align.CENTER);

        canvas.drawText(mWordsList.get(mIndex), mX, mMiddleY, p2);

        int alphaValue = 25;
        float tempY = mMiddleY;
        for (int i = mIndex - 1; i >= 0; i--) {
            tempY -= DY;
            if (tempY < 0) {
                break;
            }
            p.setColor(Color.argb(255 - alphaValue, 245, 245, 245));
            canvas.drawText(mWordsList.get(i), mX, tempY, p);
            alphaValue += 25;
        }
        alphaValue = 25;
        tempY = mMiddleY;
        for (int i = mIndex + 1, len = mWordsList.size(); i < len; i++) {
            tempY += DY;
            if (tempY > mY) {
                break;
            }
            p.setColor(Color.argb(255 - alphaValue, 245, 245, 245));
            canvas.drawText(mWordsList.get(i), mX, tempY, p);
            alphaValue += 25;
        }
        mIndex++;
    }

    @Override
    protected void onSizeChanged(int w, int h, int ow, int oh) {
        super.onSizeChanged(w, h, ow, oh);

        mX = w * 0.5f;
        mY = h;
        mMiddleY = h * 0.3f;
    }

    @SuppressLint("SdCardPath")
    private void init() throws IOException {
        setFocusable(true);

        LrcHandle lrcHandler = new LrcHandle();
        lrcHandler.readLRC("/sdcard/陪我去流浪.lrc");
        mWordsList = lrcHandler.getWords();

        mLoseFocusPaint = new Paint();
        mLoseFocusPaint.setAntiAlias(true);
        mLoseFocusPaint.setTextSize(22);
        mLoseFocusPaint.setColor(Color.WHITE);
        mLoseFocusPaint.setTypeface(Typeface.SERIF);

        mOnFocusePaint = new Paint();
        mOnFocusePaint.setAntiAlias(true);
        mOnFocusePaint.setColor(Color.YELLOW);
        mOnFocusePaint.setTextSize(30);
        mOnFocusePaint.setTypeface(Typeface.SANS_SERIF);
    }
}
复制代码

     最主要的是覆盖TextView的onDraw()和onSizeChanged()。
     在onDraw()中我们重新绘制TextView,这就是实现歌词滚动实现的关键。歌词滚动的实现思路并不复杂:将上一句歌词向上移动,当前歌词字体变大,颜色变黄突出显示。我们需要设置位移量DY = 50。颜色和字体大小我们可以通过设置Paint来实现。

      我们注意到,在我实现的效果中,距离当前歌词越远的歌词,就会变透明,这个可以通过p.setColor(Color.argb(255 - alphaValue, 245, 245, 245))来实现。

      接着就是主代码:

复制代码
public class MainActivity extends Activity {
    private WordView mWordView;
    private List<Integer> mTimeList;
    private MediaPlayer mPlayer;

    @SuppressLint("SdCardPath")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                mPlayer.stop();
                finish();
            }
        });

        mWordView = (WordView) findViewById(R.id.text);

        mPlayer = new MediaPlayer();
        mPlayer.reset();
        LrcHandle lrcHandler = new LrcHandle();
        try {
            lrcHandler.readLRC("/sdcard/陪我去流浪.lrc");
            mTimeList = lrcHandler.getTime();
            mPlayer.setDataSource("/sdcard/陪我去流浪.mp3");
            mPlayer.prepare();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
        final Handler handler = new Handler();
        mPlayer.start();
        new Thread(new Runnable() {
            int i = 0;

            @Override
            public void run() {
                while (mPlayer.isPlaying()) {
                    handler.post(new Runnable() {

                        @Override
                        public void run() {
                            mWordView.invalidate();
                        }
                    });
                    try {
                        Thread.sleep(mTimeList.get(i + 1) - mTimeList.get(i));
                    } catch (InterruptedException e) {
                    }
                    i++;
                    if (i == mTimeList.size() - 1) {
                        mPlayer.stop();
                        break;
                    }
                }
            }
        }).start();
    }
}



转载自:http://www.cnblogs.com/wenjiang/archive/2013/05/06/3063259.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Unity音乐播放器是一款常用的多媒体播放器,可以播放各种音频文件。然而,要在Unity中实现歌词显示并不是一件容易的事情,需要一些额外的努力和技巧。 在Unity中,要实现歌词显示,首先需要获取音频文件的播放进度。可以通过获取音频文件的当前播放时间来实现。然后,需要将歌词文件与音频文件进行匹配,确定每句歌词的起始时间和结束时间。 接下来,可以使用Unity中的UI组件,如Text组件,来显示歌词。可以将歌词分成多行,并逐行显示。可以根据当前的播放时间,判断应该显示哪一句歌词,然后将其显示在文本框中。 为了使歌词显示更加美观和易于阅读,可以调整文本框的样式,如字体、字号和颜色。可以根据实际需要,自定义文本框的外观和动画效果,以增加视觉上的吸引力。 为了实现歌词的同步显示,可以使用协程或者定时器来控制文本的显示和隐藏。可以根据歌词的起始时间和结束时间,设置显示时间的延迟和持续时间,以确保歌词与音频的播放同步。 需要注意的是,由于每首歌曲的歌词文件格式可能不同,需要根据实际情况进行解析和处理。可以使用正则表达式或者其他字符串处理方法,将歌词文件中的时间信息和歌词文本提取出来。 总之,要在Unity中实现歌词显示,需要获取音频播放进度,与歌词文件进行匹配,使用UI组件来显示歌词,并通过协程或定时器来控制歌词的同步显示。此外,还可以根据实际需要进行文本样式和动画效果的调整,以提高整体的用户体验。 ### 回答2: Unity音乐播放器可以通过编程实现歌词的显示功能。首先,我们需要将音乐播放器歌词文件进行关联。可以选择将歌词文件格式为txt或者lrc文件,因为这两种格式比较常见且易于处理。 在Unity中,我们可以创建一个UI界面来显示歌词。可以使用Text组件作为歌词的显示框,通过设置RectTransform的位置、大小和对齐方式来调整歌词在界面中的位置。 在代码中,我们可以使用StreamReader来读取歌词文件,然后将歌词文字逐行显示在文本框中。可以使用适当的算法来实现歌词的滚动效果,例如根据歌曲进度和每行歌词的时间确定当前应该显示的歌词。 另外,我们还可以对歌词进行美化处理。可以使用自定义字体、颜色、大小和动画效果等,使歌词显示更加炫酷。 为了更好地展示歌词,我们还可以添加一些交互功能。例如,可以通过点击界面上的某个位置来快速跳转到对应的歌词位置。还可以添加拖动、缩放和旋转等操作来调整歌词的显示效果。 总的来说,Unity音乐播放器可以实现歌词的显示功能,通过读取歌词文件并在UI界面上动态显示歌词。通过合适的算法和美化处理,可以使歌词显示更加生动和丰富。同时,添加交互功能可以提升用户体验。 ### 回答3: Unity是一款非常流行的游戏开发引擎,但它也可以用于创建其他类型的应用程序,比如音乐播放器。在Unity中实现歌词显示功能并不难,可以通过以下几个步骤来完成。 首先,需要将音乐文件导入到Unity工程中。可以将音乐文件拖拽到Unity的资源面板中,并设置其正确的导入设置,确保音乐文件能够在游戏中正常播放。 接下来,需要创建一个UI界面用于显示歌词。可以使用Unity的UGUI系统来创建一个Text元素,用于显示当前歌词的文本。可以设置合适的字体、大小和位置,使其在游戏中能够清晰可见。 然后,需要编写一个脚本来控制歌词的显示。可以创建一个C#脚本,将其附加到一个空的游戏对象上。在脚本中,可以使用Unity的Audio类来控制音乐的播放,并通过监听音乐的进度来实时更新歌词的显示。 在脚本中,可以使用一个字符串数组来保存歌词的内容,并使用一个整型来记录当前正在显示的歌词的索引。在每次音乐播放的时候,可以根据音乐的当前进度和歌词的时间点来决定显示哪一句歌词。可以使用Text元素的text属性来更新歌词的显示。 除了简单地显示歌词文本,还可以根据需要进行一些额外的处理。比如,可以在显示的歌词上添加特效,设置渐变的颜色或者字体的动画效果,以增加视觉上的吸引力。 总而言之,Unity音乐播放器歌词显示可以通过创建UI界面、编写控制脚本以及实时更新文本内容等步骤来实现。这样,用户就可以在使用Unity开发的应用程序中欣赏音乐的同时,看到歌词的演唱。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值