Code Monkey home page Code Monkey logo

psgplay's Introduction

compilation workflow

Atari ST loading screen Atari ST main menu

PSG play is a music player and emulator for the Atari ST Programmable Sound Generator (PSG) YM2149 and files in the SNDH archive.

How to build

This repository has Git submodules so clone it with the --recurse-submodules option, or do git submodule update --init --recursive.

For Linux and Mac OS, do make psgplay to compile psgplay. To use Advanced Linux Sound Architecture (ALSA) and interactive text mode, do make ALSA=1 psgplay.

For Atari ST, do make TARGET_COMPILE=m68k-elf- PSGPLAY.TOS.

For Javascript, Webassembly, and the Emscripten compiler, do make HOST_CC=emcc web. The PSG play library is available with Cowbell, having a particular focus on demoscene music.

The BUILD_CC, HOST_AR, HOST_CC, and TARGET_CC with TARGET_LD Makefile variables can be configured for various compilation settings. The BUILD_CFLAGS, HOST_CFLAGS, TARGET_CFLAGS, and TARGET_LDFLAGS variables are available as well.

Review the file INSTALL for installation instructions.

The package media-sound/psgplay is available for Gentoo Linux.

Github actions automatically compile and publish archives with PSG play for the Atari ST, as well as Linux and the architectures ppc64le, aarch64 and x86-64. These are built with .github/workflows/compilation.yml.

How to use

PSG play options for Linux and Mac OS:

Usage: psgplay [options]... <sndh-file>

General options:

    -h, --help             display this help and exit
    --version              display version and exit
    -v, --verbose          increase verbosity

    -i, --info             display SNDH file info and exit

Play options:

    -o, --output=<file>    write audio output to the given file in WAVE format
                           or to an ALSA handle if prefixed with "alsa:"

    --start=<[mm:]ss.ss>   start playing at the given time
    --stop=<[mm:]ss.ss|auto|never>
                           stop playing at the given time, or automatically
                           if the track has a known duration, or never
    --length=<[mm:]ss.ss>  play for the given duration

    -m, --mode=<command|text>
                           command or interactive text mode

    -t, --track=<num>      set track number
    -f, --frequency=<num>  set audio frequency in Hz (default 44100)

    --psg-mix=<empiric|linear>
                           empiric (default) mixes the three PSG channels as
                           measured on Atari ST hardware; linear sums the
                           channels to produce a cleaner sound

Disassembly options:

    --disassemble          disassemble SNDH file and exit; may be combined
                           with the --trace=cpu option for self-modifying code,
                           disassembly of interrupt code, etc.
    --disassemble-header   disassemble SNDH file header and exit
    --disassemble-address  display address column in disassembly
    --remake-header        remake SNDH file header in disassembly

Tracing options:

    --trace=<device>,...   trace device operations of SNDH file and exit:
                           all cpu reg

Interactive text mode

PSG play defaults to interactive text mode if it is compiled with ALSA for Linux, or is compiled for Atari ST. Mac OS does not support interactive text mode, only command mode with WAVE format output, as described in issue #8.

For Linux, TTY mode and ECMA-48 are used, including support for job control such as process suspension.

For Atari ST, text mode and VT52 are used. See issues #5 and #6 for ideas about additional serial port and GEM user interfaces.

The currently playing tune is indicated in reverse video. A cursor is shown with >. Keyboard controls:

  • escape or q to quit;
  • s to stop;
  • p or spacebar to pause;
  • 1, 2, ..., 9 to play tunes 1, 2, ..., 9;
  • - to decrease volume;
  • + to increase volume;
  • < to play the previous tune;
  • > to play the next tune;
  • k or up arrow to move the cursor up;
  • j or down arrow to move the cursor down;
  • return to play the tune at the cursor.

Command mode

PSG play runs in command mode if it is not compiled with ALSA for Linux, or is compiled for Mac OS, or the options -o, --output, --start, --stop, --length, --disassemble or --trace are given. Atari ST does not support command mode.

Library form

PSG play is compiled into the static library lib/psgplay/psgplay.a and the shared library lib/psgplay/psgplay.so. The application programming interface (API) is documented in include/psgplay/psgplay.h, include/psgplay/stereo.h, include/psgplay/sndh.h and include/psgplay/version.h.

The library also supplies an unaltered 250 kHz digital form for custom analogue filters and mixers. This digital interface is documented in include/psgplay/digital.h.

There are two simple examples on how to use the PSG play library:

Disassembly

PSG play can disassemble SNDH files. This can be used to debug, update metadata and reassemble SNDH files. The --disassemble option can be used for code inspection. The --disassemble-header option is the safest choice when updating SNDH metadata, because most of the code is retained with dc.b data bytes for exact reassembly.

The disassembly is guided by instruction reachability from the init, play, and exit entry points, to separate executable instructions from data. To deal with interrupt code and self-modifying code, use both the --disassemble and the --trace=cpu options. The disassembly will then print what the processor actually executed in memory, which may have been modified by the program itself, rather than the contents of the SNDH file. The tracing execution length can be set with the --length option.

The --remake-header option can be used to repair broken SNDH metadata such as missing tags, excessive whitespace, etc. It can also be used to update or add new metadata, by editing the produced assembly source code in an editor.

Excerpt of disassembly with the --disassemble option:

init:
	bra.w	_init				; init
exit:
	bra.w	_exit				; exit
play:
	bra.w	_play				; play
sndh:
	dc.b	$53,$4e,$44,$48,$43,$4f,$4d,$4d	; SNDHCOMM
	dc.b	$4d,$61,$64,$20,$4d,$61,$78,$00	; Mad Max.
	dc.b	$52,$49,$50,$50,$47,$72,$61,$7a	; RIPPGraz
	dc.b	$65,$79,$20,$2f,$20,$50,$48,$46	; ey / PHF
	dc.b	$00,$43,$4f,$4e,$56,$47,$72,$61	; .CONVGra
	dc.b	$7a,$65,$79,$20,$2f,$20,$50,$48	; zey / PH
	dc.b	$46,$00,$54,$49,$54,$4c,$57,$61	; F.TITLWa
	dc.b	$72,$70,$00,$23,$23,$30,$38,$00	; rp.##08.
	dc.b	$54,$43,$35,$30,$00,$00        	; TC50..
_init:
	lea	_9a(pc),a0			; init
	lea	_9e(pc),a1			; init
	move.l	a0,(a1)				; init
	subi.w	#1,d0				; init
	lea	_b4a(pc),a0			; init
	lea	_ac(pc),a1			; init
	move.l	d0,d1				; init
	asl.w	#3,d1				; init
	movea.l	(a1,d1.w),a2			; init
	addq.w	#6,d1				; init
	move.w	(a1,d1.w),d0			; init
	lea	_b4a(pc),a0			; init
	adda.l	a2,a0				; init
	lea	_ec(pc),a1			; init
	move.l	a0,56(a1)			; init
	move.l	a0,108(a1)			; init
	clr.w	2104(a1)			; init
	bsr.w	_ec				; init
	lea	_f4(pc),a0			; init
	lea	_9e(pc),a1			; init
	move.l	a0,(a1)				; init
_9a:
	rts					; init
_exit:
	rts					; exit
_9e:
	dc.b	$00,$00,$00,$00
_play:
	movea.l	_9e(pc),a0			; play
	jsr	(a0)				; play
	rts					; play
	...

Excerpt of disassembly with the --disassemble-header option:

init:
	bra.w	_init
exit:
	bra.w	_exit
play:
	bra.w	_play
sndh:
	dc.b	$53,$4e,$44,$48,$43,$4f,$4d,$4d	; SNDHCOMM
	dc.b	$4d,$61,$64,$20,$4d,$61,$78,$00	; Mad Max.
	dc.b	$52,$49,$50,$50,$47,$72,$61,$7a	; RIPPGraz
	dc.b	$65,$79,$20,$2f,$20,$50,$48,$46	; ey / PHF
	dc.b	$00,$43,$4f,$4e,$56,$47,$72,$61	; .CONVGra
	dc.b	$7a,$65,$79,$20,$2f,$20,$50,$48	; zey / PH
	dc.b	$46,$00,$54,$49,$54,$4c,$57,$61	; F.TITLWa
	dc.b	$72,$70,$00,$23,$23,$30,$38,$00	; rp.##08.
	dc.b	$54,$43,$35,$30,$00,$00        	; TC50..
_init:
	dc.b	$41,$fa,$00,$46,$43,$fa,$00,$46
	dc.b	$22,$88,$04,$40,$00,$01,$41,$fa
	dc.b	$0a,$e8,$43,$fa,$00,$46,$22,$00
	dc.b	$e7,$41,$24,$71,$10,$00,$5c,$41
	dc.b	$30,$31,$10,$00,$41,$fa,$0a,$d2
	dc.b	$d1,$ca,$43,$fa,$00,$6e,$23,$48
	dc.b	$00,$38,$23,$48,$00,$6c,$42,$69
	dc.b	$08,$38,$61,$00,$00,$5e,$41,$fa
	dc.b	$00,$62,$43,$fa,$00,$08,$22,$88
	dc.b	$4e,$75
_exit:
	dc.b	$4e,$75,$00,$00,$00,$00
_play:
	dc.b	$20,$7a,$ff,$fa,$4e,$90,$4e,$75
	...

Excerpt of disassembly with the --disassemble-header and --remake-header options (having the missing HDNS tag automatically repaired from the previous excerpt):

init:
	bra.w	_init
exit:
	bra.w	_exit
play:
	bra.w	_play
sndh:
	dc.b	'SNDH'
	dc.b	'COMMMad Max',0
	dc.b	'RIPPGrazey / PHF',0
	dc.b	'CONVGrazey / PHF',0
	dc.b	'TITLWarp',0
	dc.b	'##08',0
	dc.b	'TC50',0
	even
	dc.b	'HDNS'
_init:
	dc.b	$41,$fa,$00,$46,$43,$fa,$00,$46
	dc.b	$22,$88,$04,$40,$00,$01,$41,$fa
	dc.b	$0a,$e8,$43,$fa,$00,$46,$22,$00
	dc.b	$e7,$41,$24,$71,$10,$00,$5c,$41
	dc.b	$30,$31,$10,$00,$41,$fa,$0a,$d2
	dc.b	$d1,$ca,$43,$fa,$00,$6e,$23,$48
	dc.b	$00,$38,$23,$48,$00,$6c,$42,$69
	dc.b	$08,$38,$61,$00,$00,$5e,$41,$fa
	dc.b	$00,$62,$43,$fa,$00,$08,$22,$88
	dc.b	$4e,$75
_exit:
	dc.b	$4e,$75,$00,$00,$00,$00
_play:
	dc.b	$20,$7a,$ff,$fa,$4e,$90,$4e,$75
	...

Disassembly makes it possible to supply bug fixes and metadata updates in source patch form, for quick and easy review, application and SNDH file reassembly:

--- sndh/Mad_Max/Games/Lethal_Xcess_(ST).S.orig	2020-05-22 14:56:20.495508523 +0200
+++ sndh/Mad_Max/Games/Lethal_Xcess_(ST).S.new	2020-05-22 15:23:55.260509689 +0200
@@ -6,13 +6,31 @@
 	bra.w	_play
 sndh:
 	dc.b	'SNDH'
-	dc.b	'TITLLethal Xcess (ST/Falc)',0
-	dc.b	'COMMMad Max',0
+	dc.b	'TITLLethal Xcess (ST)',0
+	dc.b	'COMMJochen Hippel',0
 	dc.b	'RIPPGrazey / PHF',0
 	dc.b	'CONVGrazey / PHF',0
+	dc.b	'YEAR1991',0
 	dc.b	'TC50',0
 	dc.b	'##07',0
 	even
+.subtitles:
+	dc.b	'!#SN'
+	dc.w	.st1-.subtitles
+	dc.w	.st2-.subtitles
+	dc.w	.st3-.subtitles
+	dc.w	.st4-.subtitles
+	dc.w	.st5-.subtitles
+	dc.w	.st6-.subtitles
+	dc.w	.st7-.subtitles
+.st1:	dc.b	'Main Menu',0
+.st2:	dc.b	'Level 1: Ruins of Methallycha 1',0
+.st3:	dc.b	'Level 1: Ruins of Methallycha 2',0
+.st4:	dc.b	'Level 2: Desert of No Return',0
+.st5:	dc.b	'Level 3: The Evil Garden',0
+.st6:	dc.b	'Level 4: Volcanic Plateaus',0
+.st7:	dc.b	'Level 5: Fortress of Methallycha',0
+	even
 	dc.b	'HDNS'
 _init:
 	dc.b	$2f,$00,$41,$fa,$00,$6e,$4a,$50

How it works

The SNDH file format is an Atari ST machine code executable form of music. A substantial part of Atari ST hardware must be emulated to play such files using other kinds of computers. The five most complex parts emulated in software by PSG play are:

The digital emulation is currently fairly accurate, aiming to be within the variation of the compatible models of original Atari hardware. The analogue emulation is currently simpler, aiming to be accurate but also avoid unwanted artifacts such as the high level of noise produced with original Atari hardware.

The YM2149 PSG signal is unipolar, and has to be transformed to a bipolar signal for mixing with stereo samples. To avoid sharp and audible noise when starting and stopping playback, stereo samples fade in and out with a 10 ms logistic sigmoid at start and stop.

As described in issues #9 and #10, DMA sound and LMC1992 for tone control specific to Atari STE and related hardware are not yet fully emulated by PSG play.

psgplay's People

Contributors

frno7 avatar gasman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

psgplay's Issues

SNDH tracks don't stop playing

In the SNDH archive, file Daglish_Ben/Footballer_of_the_Year_2.sndh plays a couple of seconds then the rest is silent.
In interactive mode it shows that it is still playing track 1 although there is no sound.
Is it possible to get psgplay to move onto the next track when the first one has finished playing? --stop=auto isn't working for this particular file.
Maybe silence detection is required?

Originally mentioned in #13 (comment)

Atari ST GEM user interface

As an option, use GEM for a graphical user interface for Atari ST. See also #5. Plan:

  • AES for TOS/libc;
  • VDI for TOS/libc;
  • design an application user interface;
  • implement GEM user interface.

PSG play for Atari ST currently displays a VT52 video screen:
Atari ST main menu

Does not compile on macOS

Error message:

psgplay % make V=1 all
lib/internal/Makefile:19: WARNING: Disassembler disabled: The C compiler does not support __attribute__((__scalar_storage_order__("big-endian")))
cc -shared -Wl,-soname,libpsgplay.so.0 -g -o lib/psgplay/libpsgplay.so lib/psgplay/ice.o lib/psgplay/version.o lib/psgplay/psgplay.o lib/psgplay/sndh.o lib/psgplay/bit.o lib/psgplay/fifo.o lib/psgplay/print.o lib/psgplay/string.o lib/psgplay/bus.o lib/psgplay/cpu.o lib/psgplay/device.o lib/psgplay/exception-vector.o lib/psgplay/fdc.o lib/psgplay/glue.o lib/psgplay/machine.o lib/psgplay/mfp.o lib/psgplay/mixer.o lib/psgplay/mmu.o lib/psgplay/mmu-trace.o lib/psgplay/psg.o lib/psgplay/ram.o lib/psgplay/rom.o lib/psgplay/shifter.o lib/psgplay/sound.o lib/psgplay/system-variable.o lib/psgplay/m68kops.o lib/psgplay/m68kcpu.o
ld: unknown options: -soname 
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [lib/psgplay/libpsgplay.so] Error 1

Create a new repository for TOS/libc

TOS/libc in lib/toslibc is a variant of a C standard library that can be used to build other C programs for Atari ST, and so it makes sense to create a new TOS/libc repository for it. Its main feature is that a normal unmodified m68k compiler can be used without applying any patches. Unlike traditional Atari ST compilers, C int type integers are 32-bit rather than 16-bit.

The FreeMiNT libcmini package is similar, but it requires a special m68k-atari-mint compiler.

Illegal option pipefail

I hit a little bump building psgplay. Here's the end of the output:

$ make ALSA=1
...
  CC      lib/m68k/m68kdasm.o
  CC      lib/m68k/softfloat.o
  CC      lib/psgplay/psgplay.o
  CC      lib/psgplay/sndh.o
script/version: 4: set: Illegal option -o pipefail
lib/psgplay/Makefile:15: recipe for target 'lib/psgplay/version.c' failed
make: *** [lib/psgplay/version.c] Error 2

I discovered that set -o pipefail is a bashism, and that bash is not the default shell on my machine.

I was able to fix the problem by changing the shebang of script/version from #!/bin/sh to #!/bin/bash

Happy to submit a PR if you like.

Atari ST MIDI interface

As an option, have PSG play on Atari ST accept MIDI commands in two forms:

  • traditional MIDI instrument control for the three channels of the YM2149;
  • raw YM2149 register settings via MIDI, as a technical means of PSG register remote control.

As a variant, the standard MIDI file (SMF) file format could be supported.

Emulate Atari STE DMA sound hardware

Some SNDH files made for the Atari STE make use of its sound DMA hardware. Let PSG play emulate this piece of hardware. Plan:

  • define DMA sound device in lib/atari/sound.c;
  • 6258, 12517, 25033 and 50066 Hz sampling frequencies;
  • mono and stereo sound;
  • 8-byte FIFO DMA prefetch buffer;
  • Double buffer DMA sound registers;
  • assert MFP GPIP I7 DMA interrupt;
  • assert MFP Timer A DMA interrupt.

ALSA issue "broken pipe"

Starts playing nice, then after 23 seconds it exits violently with:

psgplay: error: (null): ALSA snd_pcm_writei failed: -32 Broken pipe

Also leaving the terminal in a bad state.

PS: Wondering how I can make it play without alsa. Maybe adding a feature that an output file of "-" means to dump the wav to stdout, then I can pipe it into any player. Asking psgplay to write to a named pipe does unfortunately not work "Illegal seek".

Mac OS Core Audio

Implement a driver for Core Audio in lib/out/core-audio.c. PSG play for Mac OS is currently limited to saving WAVE format files. Linux, on the other hand, also has a driver for ALSA in lib/out/alsa.c, which permits an interactive text mode user interface:
Atari ST main menu

Emulate Atari STE LMC1992 tone and volume hardware

Some SNDH files made for the Atari STE make use of the LMC1992 circuit for tone and volume control. Let PSG play emulate this piece of hardware:

The LMC1992 is a monolithic integrated circuit that providesfour stereo inputs, bass and treble tone controls, and volume, balance, and front-rear fader controls. These functions are digitally controlled through a three-wire communication interface.

Plan:

File is just a tone

This file is just playing a tone. I updated to the latest as I was a bit of of date, but no change:
Tao/TSD_STe/Intensity.sndh

libpsgplay as PSG play in a linkable library

Building PSG play as a library allows other music players to link with it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.