Hi,大家好,我是编程小6,很荣幸遇见你,我把这些年在开发过程中遇到的问题或想法写出来,今天说一说实战代码项目学 Go 语言:音乐播放器[亲测有效],希望能够帮助你!!!。
设计实现一个简单的播放器。
命令行调用
全部源码在 src 文件夹下,mplayer 里是主程序,mlib 中是对播放器的控制逻辑,mp 中是对播放操作的逻辑。
在代码实现中,按照程序结构分为 3 个部分实现:
主程序
在主程序中,主要的功能点有 1 个:
func main() {
fmt.Println("Enter following commands to control the player:" +
"lib list -- View the existing music lib lib add <name><artist><source><type> -- Add a music to the music lib")
lib = mlib.NewMusicManager()
r := bufio.NewReader(os.Stdin) // 接收命令行输入
for {
fmt.Print("Enter command-> ")
rawLine, _, _ := r.ReadLine() // 接收命令行输入
line := string(rawLine)
if line == "q" || line == "e" { // 解析命令
break
}
tokens := strings.Split(line, " ")
if tokens[0] == "lib" { // 解析命令
handleLibCommands(tokens)
} else if tokens[0] == "play" { // 解析命令
handleLibCommand(tokens)
} else { // 解析命令
fmt.Println("Unrecognized command:", tokens[0])
}
}
}
当是 lib 命令时,通过解析第 1 个参数,来确定下一步的增删改查。
func handleLibCommands(tokens []string) {
switch tokens[1] {
case "list": // 列举所有列表
for i:=0; i<lib.Len(); i++ {
e, _ := lib.Get(i)
fmt.Println(i+1, ":", e.Name, e.Artist, e.Source, e.Type)
}
case "add": // 添加一条
if len(tokens) == 6 {
id++
lib.Add(&mlib.MusicEntry{strconv.Itoa(id), tokens[2], tokens[3], tokens[4], tokens[5]})
} else {
fmt.Println("USAGE: lib add <name><artist><source><type>")
}
case "remove": // 删除一条
if len(tokens) == 3 {
//lib.RemoveByName(tokens[2])
} else {
fmt.Println("USAGE: lib remove <name>")
}
default:
fmt.Println("Unrecognized lib command:", tokens[1])
}
}
当是 play 命令时,通过解析参数,来执行播放操作
func handleLibCommand(tokens []string) {
if len(tokens) != 2 {
fmt.Println("USAGE: play <name>")
return
}
e := lib.Find(tokens[1])
if e == nil {
fmt.Println("The music", tokens[1], "does not exist.")
return
}
mp.Play(e.Source, e.Type)
}
增删改查逻辑
增删改查逻辑是由 mlib.MusicManager 来实现的。它的组成内容包括 2 部分:
成员变量中包含了一个 MusicEntry 类型的变量
musics []MusicEntry
MusicEntry 中记录了歌曲的一些基本信息。
type MusicEntry struct {
Id string
Name string
Artist string
Source string
Type string
}
成员方法中定义了一系列增删改查的方法:
func (m *MusicManager) Len() int {
return len(m.musics)
}
func (m *MusicManager) Get(index int) (music *MusicEntry, err error) {
if index < 0 || index >= len(m.musics) {
return nil, errors.New("Index out of range.")
}
return &m.musics[index], nil
}
func (m *MusicManager) Find(name string) *MusicEntry {
if len(m.musics) == 0 {
return nil
}
for _, m := range m.musics {
if m.Name == name {
return &m
}
}
return nil
}
func (m *MusicManager) Add(music *MusicEntry) {
m.musics = append(m.musics, *music)
}
func (m *MusicManager) Remove(index int) *MusicEntry{
if index < 0 || index >= len(m.musics) {
return nil
}
removedMusic := &m.musics[index]
m.musics = append(m.musics[:index], m.musics[index+1:]...)
return removedMusic
}
同时定义了 MusicManager 的构造函数,在其中初始化了 MusicEntry 变量为 0。
func NewMusicManager() *MusicManager {
return &MusicManager{make([]MusicEntry, 0)}
}
操作逻辑是由 mp.Play 实现的,mp.Play 中 Player 定义了一个接口 Play(source string)
type Player interface {
Play(source string)
}
mp.mp3 和 mp.wav 分别都完整的实现了 Player 的全部接口。因此,继承了 Player 。
package mp
import (
"fmt"
"time"
)
type MP3Player struct {
stat int
progress int
}
func (p *MP3Player) Play(source string) {
fmt.Println("Playing MP3 music", source)
p.progress = 0
for p.progress < 100 {
time.Sleep(100 * time.Microsecond)
fmt.Println(".")
p.progress += 10
}
fmt.Println("\n Finished playing", source)
}
package mp
import (
"fmt"
"time"
)
type WAVPlayer struct {
stat int
progress int
}
func (w *WAVPlayer) Play(source string) {
fmt.Println("Playing WAV music", source)
w.progress = 0
for w.progress < 100 {
time.Sleep(100 * time.Microsecond)
fmt.Println(".")
w.progress += 10
}
fmt.Println("\n Finished playing", source)
}
在 mp.Play 中 Player 类型变量调用 Play 函数时,就会根据设定类型执行。
package mp
import "fmt"
type Player interface {
Play(source string)
}
func Play(source, mtype string) {
var p Player
switch mtype {
case "MP3":
p = &MP3Player{}
case "WAV":
p = &WAVPlayer{}
default:
fmt.Println("Unsupported music type", mtype)
return
}
p.Play(source)
}
构建的过程使用 go run 执行:
go run mplayer.go
期间会出现命令行交互窗口,提示按照指定的命令格式来操作播放。
经过以上过程,一个基础版的音乐播放器完整开发完毕。