151 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
| /*
 | |
| Copyright © 2024 filifa
 | |
| 
 | |
| This program is free software: you can redistribute it and/or modify
 | |
| it under the terms of the GNU General Public License as published by
 | |
| the Free Software Foundation, either version 3 of the License, or
 | |
| (at your option) any later version.
 | |
| 
 | |
| This program is distributed in the hope that it will be useful,
 | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| GNU General Public License for more details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License
 | |
| along with this program. If not, see <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| package cmd
 | |
| 
 | |
| /*
 | |
| #cgo LDFLAGS: -lprojectM-4
 | |
| #include "projectM-4/projectM.h"
 | |
| #include <stdlib.h>
 | |
| */
 | |
| import "C"
 | |
| 
 | |
| import (
 | |
| 	"container/ring"
 | |
| 	"encoding/binary"
 | |
| 	"encoding/json"
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"unsafe"
 | |
| 
 | |
| 	"github.com/veandco/go-sdl2/sdl"
 | |
| )
 | |
| 
 | |
| const bufSize uint = 2048
 | |
| 
 | |
| type milkDropWindow struct {
 | |
| 	window  *sdl.Window
 | |
| 	context sdl.GLContext
 | |
| 	handle  C.projectm_handle
 | |
| 	preset  *ring.Ring
 | |
| }
 | |
| 
 | |
| type script struct {
 | |
| 	Presets []string
 | |
| 	Times   []float64
 | |
| }
 | |
| 
 | |
| func readScript(scriptPath string) (*script, error) {
 | |
| 	if scriptPath == "" {
 | |
| 		return nil, errors.New("no script given")
 | |
| 	}
 | |
| 
 | |
| 	f, err := os.Open(scriptPath)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	defer f.Close()
 | |
| 
 | |
| 	data, err := io.ReadAll(f)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	var s script
 | |
| 	err = json.Unmarshal(data, &s)
 | |
| 	return &s, err
 | |
| }
 | |
| 
 | |
| func (m *milkDropWindow) nextPreset() {
 | |
| 	m.preset = m.preset.Next()
 | |
| 	m.loadPreset(m.preset.Value.(string))
 | |
| }
 | |
| 
 | |
| func (m *milkDropWindow) prevPreset() {
 | |
| 	m.preset = m.preset.Prev()
 | |
| 	m.loadPreset(m.preset.Value.(string))
 | |
| }
 | |
| 
 | |
| func newMilkDropWindow(width, height int32, scriptPath string) (*milkDropWindow, error) {
 | |
| 	var m milkDropWindow
 | |
| 	var err error
 | |
| 
 | |
| 	s, err := readScript(scriptPath)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	m.preset = ring.New(len(s.Presets))
 | |
| 	for _, preset := range s.Presets {
 | |
| 		m.preset.Value = preset
 | |
| 		m.preset = m.preset.Next()
 | |
| 	}
 | |
| 
 | |
| 	m.window, err = sdl.CreateWindow("milkbucket", sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED, width, height, sdl.WINDOW_OPENGL|sdl.WINDOW_RESIZABLE)
 | |
| 	if err != nil {
 | |
| 		return &m, err
 | |
| 	}
 | |
| 
 | |
| 	m.context, err = m.window.GLCreateContext()
 | |
| 	if err != nil {
 | |
| 		return &m, err
 | |
| 	}
 | |
| 
 | |
| 	m.handle = C.projectm_create()
 | |
| 	if m.handle == nil {
 | |
| 		return &m, errors.New("error creating projectM instance")
 | |
| 	}
 | |
| 
 | |
| 	C.projectm_set_window_size(m.handle, C.ulong(width), C.ulong(height))
 | |
| 
 | |
| 	return &m, nil
 | |
| }
 | |
| 
 | |
| func (m *milkDropWindow) destroy() {
 | |
| 	C.projectm_destroy(m.handle)
 | |
| 	sdl.GLDeleteContext(m.context)
 | |
| 	m.window.Destroy()
 | |
| }
 | |
| 
 | |
| func (m *milkDropWindow) loadPreset(preset string) {
 | |
| 	cPreset := C.CString(preset)
 | |
| 	defer C.free(unsafe.Pointer(cPreset))
 | |
| 	C.projectm_load_preset_file(m.handle, cPreset, true)
 | |
| }
 | |
| 
 | |
| func (m *milkDropWindow) render() (bool, error) {
 | |
| 	var audioData [bufSize]int16
 | |
| 
 | |
| 	err := binary.Read(os.Stdin, binary.LittleEndian, &audioData)
 | |
| 	if err == io.ErrUnexpectedEOF {
 | |
| 		return false, nil
 | |
| 	} else if err != nil {
 | |
| 		return true, err
 | |
| 	}
 | |
| 
 | |
| 	C.projectm_pcm_add_int16(m.handle, (*C.int16_t)(&audioData[0]), C.uint(bufSize), C.PROJECTM_STEREO)
 | |
| 
 | |
| 	C.projectm_opengl_render_frame(m.handle)
 | |
| 	m.window.GLSwap()
 | |
| 
 | |
| 	return true, nil
 | |
| }
 | |
| 
 | |
| func (m *milkDropWindow) resize(w, h int32) {
 | |
| 	C.projectm_set_window_size(m.handle, C.ulong(w), C.ulong(h))
 | |
| }
 |