Example #1
0
func main() {
	// Parse flags
	flag.Parse()

	// Move all logging output to stderr, as output image will occupy
	// the stdout stream
	log.SetOutput(os.Stderr)
	log.SetPrefix(app + ": ")

	// Create image background color from input hex color string, or default
	// to black if invalid
	colorR, colorG, colorB := hexToRGB(*strBGColor)
	bgColor := color.RGBA{colorR, colorG, colorB, 255}

	// Create image foreground color from input hex color string, or default
	// to black if invalid
	colorR, colorG, colorB = hexToRGB(*strFGColor)
	fgColor := color.RGBA{colorR, colorG, colorB, 255}

	// Create image alternate color from input hex color string, or default
	// to foreground color if empty
	altColor := fgColor
	if *strAltColor != "" {
		colorR, colorG, colorB = hexToRGB(*strAltColor)
		altColor = color.RGBA{colorR, colorG, colorB, 255}
	}

	// Set of available functions
	fnSet := map[string]waveform.ColorFunc{
		fnChecker:  waveform.CheckerColor(fgColor, altColor, 10),
		fnFuzz:     waveform.FuzzColor(fgColor, altColor),
		fnGradient: waveform.GradientColor(fgColor, altColor),
		fnSolid:    waveform.SolidColor(fgColor),
		fnStripe:   waveform.StripeColor(fgColor, altColor),
	}

	// Validate user-selected function
	colorFn, ok := fnSet[*strFn]
	if !ok {
		log.Fatalf("unknown function: %q %s", *strFn, fnOptions)
	}

	// Generate a waveform image from stdin, using values passed from
	// flags as options
	img, err := waveform.Generate(os.Stdin,
		waveform.BGColorFunction(waveform.SolidColor(bgColor)),
		waveform.FGColorFunction(colorFn),
		waveform.Resolution(*resolution),
		waveform.Scale(*scaleX, *scaleY),
		waveform.ScaleClipping(),
		waveform.Sharpness(*sharpness),
	)
	if err != nil {
		// Set of known errors
		knownErr := map[error]struct{}{
			waveform.ErrFormat:        struct{}{},
			waveform.ErrInvalidData:   struct{}{},
			waveform.ErrUnexpectedEOS: struct{}{},
		}

		// On known error, fatal log
		if _, ok := knownErr[err]; ok {
			log.Fatal(err)
		}

		// Unknown errors, panic
		panic(err)
	}

	// Encode results as PNG to stdout
	if err := png.Encode(os.Stdout, img); err != nil {
		panic(err)
	}
}
Example #2
0
// GetWaveform generates and returns a waveform image from wavepipe.  On success, this API will
// return a binary stream. On failure, it will return a JSON error.
func GetWaveform(w http.ResponseWriter, r *http.Request) {
	// Retrieve render
	ren := context.Get(r, CtxRender).(*render.Render)

	// Check API version
	if version, ok := mux.Vars(r)["version"]; ok {
		// Check if this API call is supported in the advertised version
		if !apiVersionSet.Has(version) {
			ren.JSON(w, 400, errRes(400, "unsupported API version: "+version))
			return
		}
	}

	// Check for an ID parameter
	pID, ok := mux.Vars(r)["id"]
	if !ok {
		ren.JSON(w, 400, errRes(400, "no integer song ID provided"))
		return
	}

	// Verify valid integer ID
	id, err := strconv.Atoi(pID)
	if err != nil {
		ren.JSON(w, 400, errRes(400, "invalid integer song ID"))
		return
	}

	// Check for a pre-existing set of waveform values
	var values []float64
	if tmpValues, ok := waveformLRU.Get(id); ok {
		// Use existing values
		values = tmpValues.([]float64)
	} else {
		// No existing waveform
		// Attempt to load the song with matching ID
		song := &data.Song{ID: id}
		if err := song.Load(); err != nil {
			// Check for invalid ID
			if err == sql.ErrNoRows {
				ren.JSON(w, 404, errRes(404, "song ID not found"))
				return
			}

			// All other errors
			log.Println(err)
			ren.JSON(w, 500, serverErr)
			return
		}

		// Open song's backing stream
		stream, err := song.Stream()
		if err != nil {
			log.Println(err)
			ren.JSON(w, 500, serverErr)
			return
		}

		// Generate a waveform object
		wave, err := waveform.New(stream, waveform.Resolution(4))
		if err != nil {
			log.Println(err)
			ren.JSON(w, 500, serverErr)
			return
		}

		// Compute waveform values from input stream
		tmpValues, err := wave.Compute()
		if err != nil {
			// If unknown format, return JSON error
			if err == waveform.ErrFormat {
				ren.JSON(w, 501, errRes(501, "unsupported audio format"))
				return
			}

			log.Println(err)
			ren.JSON(w, 500, serverErr)
			return
		}

		// Cache values, and use for generating current and future images
		waveformLRU.Add(id, tmpValues)
		values = tmpValues
	}

	// Check for optional color parameters
	var cR, cG, cB uint8

	// Background color
	var bgColor color.Color = color.White
	if bgColorStr := r.URL.Query().Get("bg"); bgColorStr != "" {
		// Convert %23 to #
		bgColorStr, err := url.QueryUnescape(bgColorStr)
		if err == nil {
			cR, cG, cB = hexToRGB(bgColorStr)
			bgColor = color.RGBA{cR, cG, cB, 255}
		}
	}

	// Foreground color
	var fgColor color.Color = color.Black
	if fgColorStr := r.URL.Query().Get("fg"); fgColorStr != "" {
		// Convert %23 to #
		fgColorStr, err := url.QueryUnescape(fgColorStr)
		if err == nil {
			cR, cG, cB = hexToRGB(fgColorStr)
			fgColor = color.RGBA{cR, cG, cB, 255}
		}
	}

	// Alternate color; follow foreground color by default
	var altColor color.Color = fgColor
	if altColorStr := r.URL.Query().Get("alt"); altColorStr != "" {
		// Convert %23 to #
		altColorStr, err := url.QueryUnescape(altColorStr)
		if err == nil {
			cR, cG, cB = hexToRGB(altColorStr)
			altColor = color.RGBA{cR, cG, cB, 255}
		}
	}

	// If requested, resize the image to the specified width
	var sizeX, sizeY int
	if strSize := r.URL.Query().Get("size"); strSize != "" {
		// Check for dimensions in two integers
		if _, err := fmt.Sscanf(strSize, "%dx%d", &sizeX, &sizeY); err != nil {
			ren.JSON(w, 400, errRes(400, "invalid x-separated integer pair for size"))
			return
		}
	}

	// Generate waveform object with sane defaults and user settings
	wave, err := waveform.New(nil,
		waveform.BGColorFunction(waveform.SolidColor(bgColor)),
		waveform.FGColorFunction(waveform.StripeColor(fgColor, altColor)),
		waveform.Scale(5, 4),
		waveform.Sharpness(1),
		waveform.ScaleClipping(),
	)
	if err != nil {
		log.Println(err)
		ren.JSON(w, 500, serverErr)
		return
	}

	// Generate waveform image from computed values, with specified options
	img := wave.Draw(values)

	// If a resize option was set, perform it now
	if sizeX > 0 {
		// Perform image resize
		img = resize.Resize(uint(sizeX), uint(sizeY), img, resize.NearestNeighbor)
	}

	// Encode as PNG to HTTP writer
	if err := png.Encode(w, img); err != nil {
		log.Println(err)
	}
}