1. 程式人生 > >go學習筆記(6)——音樂播放器實現

go學習筆記(6)——音樂播放器實現

宣告:首先說明這個專案來自於許式偉的《Go語言程式設計》,書中也給出了詳盡的原始碼描述,不過程式碼中還是存在一些問題,首先說明一下所存在的問題

問題一:音樂的播放結構體中定義了五個屬性欄位,在後面賦值的時候又變成了六個欄位的賦值

問題二:Play函式在呼叫的時候多傳遞了兩個引數,在函式原型的時候只有兩個引數

問題三:RemoveByName方法並沒有實現

這兩個問題應該都是為了後期更好的進行專案擴充套件,不過作為小白的我現在還是以學習和調通程式碼為主,再此基礎上面在進行一步步的功能擴充套件,下面給出響應的專案說明和可以執行的實現基本功能的程式碼實現,所有的實現細節已經給出了詳細的註釋:

一:整體的邏輯框架


二:音樂庫管理模組

》Len    求歌曲名長度

》Get    通過索引查詢歌曲

》Find    通過名字查詢歌曲

》Add    新增歌曲

》Remove    通過索引刪除歌曲

》RemoveByName    通過歌名刪除歌曲

package library

import (
	"errors"
	"fmt"
)

//定義音樂結構體
type MusicEntry struct {
	Id 	string		//音樂的唯一Id
	Name 	string		//音樂名
	Artist	string		//藝術家名
	Source	string		//音樂位置
	Type 	string		//音樂型別(MP3和WAV)
}

type MusicManager struct {
	musics	[]MusicEntry
}

func NewMusicManager() *MusicManager {
	return &MusicManager{make([]MusicEntry, 0)}
}

//獲取歌曲的歌名長度
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], err
}

//通過對比名字檢視要找的歌曲是否存在
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]
	//從陣列切片中刪除元素
	if index < len(m.musics) - 1 {	//中間元素
		m.musics = append(m.musics[:index - 1], m.musics[index + 1:]...)
	}
	return &removedMusic
}

//通過歌名刪除歌曲
func (m *MusicManager) RemoveByName(name string) *MusicEntry {
	removedMusic := m.Find(name)
	if removedMusic == nil{
		fmt.Println("Want to delete the song does not exist")
		return nil
	}
	return removedMusic
}

二:音樂管理單元測試模組(go單元測試測試將在後面的學習過程在詳細說明,現在只要知道測試的重要性即可)

package library

import "testing"

func TestOps(t *testing.T) {
	mm := NewMusicManager()    //新建管理測試
	if mm == nil {
		t.Error("NewManagerMusic faild")
	}

	if mm.Len() != 0 {    //len測試
		t.Error("NewManagerMusic faild, not empty")
	}

	m0 := &MusicEntry{
		"1",
		"My Heart Will Go On",
		"Celion Dion",
		//"Pop",
		"http://qbox.me/24501234",
		"Mp3",
	}
	mm.Add(m0)    //新增測試
	if mm.Len() != 1 {
		t.Error("MusicManager.Add() failed")
	}

	m := mm.Find(m0.Name)    //find測試
	if m == nil {
		t.Error("MusicManager.Find() failed")
	}

	 if m.Id != m0.Id || m.Name != m0.Name || m.Artist != m0.Artist ||
		 m.Source != m0.Source || m.Type != m0.Type {
			t.Error("MusicManager.Find() failed. Find item mismatch")
	}

	m, err := mm.Get(0)    //get測試
	if m == nil {
		t.Error("MusicManager.Get() failed", err)
	}

	//空的時候直接進行測試會出問題
	m = mm.Remove(0)       //Remove測試
	if m == nil || mm.Len() != 0 {
		t.Error("MusicManager.Remove() failed", err)
	}
}

三:播放實現(播放包括的格式有MP3和WAV兩種格式,當然再次也可以擴張使其支援其他格式的播放)

package mp

//音樂播放模組
import "fmt"

//設計一個簡單的介面避免將MusicEntry中多餘的資訊傳入
type Player interface {
	Play(Source string)
}

//播放實現,再此也可以在新增其他格式
func Play(Source, mtype string)  {
	var p Player

	switch mtype {
	case "MP3":		//MP3格式播放
		p = &MP3Player{}
	case "WAV":		//WAV格式播放
		p = &WAVPlayer{}
	default:
		fmt.Println("Unsupported music type", mtype)
		return
	}
	p.Play(Source)
}
package mp

import (
	"fmt"
	"time"
)

type MP3Player struct {
	stat 		int
	progress 	int
}

//MP3格式播放具體實現
func (p *MP3Player) Play(Source string) {
	fmt.Println("Playing MP3 music", Source)

	p.progress = 0

	for p.progress < 100 {
		time.Sleep(100 * time.Millisecond)		//假裝正在播放
		fmt.Print(".")
		p.progress += 10
	}
	fmt.Println("\nFinished playing", Source)
}
package mp

import (
	"fmt"
	"time"
)

type MP3Player struct {
	stat 		int
	progress 	int
}

//MP3格式播放具體實現
func (p *MP3Player) Play(Source string) {
	fmt.Println("Playing MP3 music", Source)

	p.progress = 0

	for p.progress < 100 {
		time.Sleep(100 * time.Millisecond)		//假裝正在播放
		fmt.Print(".")
		p.progress += 10
	}
	fmt.Println("\nFinished playing", Source)
}

四:主程式呼叫實現

package main

import (
	"goCode/src/music/library"
	"fmt"
	"strconv"
	"goCode/src/music/mp"
	"bufio"
	"os"
	"strings"
)

var lib *library.MusicManager
var id int = 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(&library.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("Unrecognied lib commond:", tokens[1])
	}
}

//播放模組控制代碼實現
func handlePlayCommand(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)
}

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 remove <name> -- Remove the specified music from the lib
		play <name> -- Play the specified music
	`)
	lib = library.NewMusicManager()

	r := bufio.NewReader(os.Stdin)

	for{
		fmt.Print("Enter Command-> ")

		rawLine, _, _ := r.ReadLine()
		line := string(rawLine)

		//輸入q或者e時退出播放器
		if line == "q" || line == "e" {
			break
		}

		tokens := strings.Split(line, " ")
		if tokens[0] == "lib" {
			handleLibCommands(tokens)
		}else if tokens[0] == "play"{
			handlePlayCommand(tokens)
		}else {
			fmt.Println("Unrecognized command:", tokens[0])
		}
	}
}

五:演示