diff --git a/api.go b/api.go
index 50a130b4c7de512e1baf4edc1816e425d27556ca..63f5c777f902dc90d0ada262d9af980814922ca9 100644
--- a/api.go
+++ b/api.go
@@ -50,22 +50,51 @@ type EncodingParams struct {
 	BitRate    int
 	SampleRate int
 	Channels   int
+	StereoMode string
 	Quality    float64
 }
 
+// NewEncodingParams sets some default values.
+func NewEncodingParams() *EncodingParams {
+	return &EncodingParams{
+		SampleRate: 44100,
+		Channels:   2,
+		Quality:    -1,
+	}
+}
+
 func (p *EncodingParams) Valid() error {
-	if p.Format == "" {
+	switch p.Format {
+	case "mp3", "mp3.cbr", "mp3.abr", "vorbis.cbr", "vorbis.abr":
+		if p.BitRate == 0 {
+			return errors.New("bitrate not specified")
+		}
+	case "mp3.vbr":
+		if p.Quality < 0 || p.Quality > 9 {
+			return errors.New("quality must be in range [0, 9]")
+		}
+	case "vorbis":
+		if p.Quality < -0.2 || p.Quality > 1 {
+			return errors.New("quality must be in range [-0.2, 1]")
+		}
+	case "":
 		return errors.New("format not specified")
+	default:
+		return fmt.Errorf("unknown format \"%s\"", p.Format)
 	}
 	if p.SampleRate == 0 {
 		return errors.New("sample rate not specified")
 	}
-	if p.BitRate == 0 && p.Quality == 0 {
-		return errors.New("either bitrate or quality must be specified")
-	}
-	if p.Channels < 1 {
+	if p.Channels < 1 || p.Channels > 2 {
 		return errors.New("bad number of channels")
 	}
+	if p.Channels > 1 {
+		switch p.StereoMode {
+		case "", "stereo", "joint_stereo", "default":
+		default:
+			return fmt.Errorf("unknown stereo mode \"%s\"", p.StereoMode)
+		}
+	}
 	return nil
 }
 
diff --git a/cmd/radioctl/radioctl.go b/cmd/radioctl/radioctl.go
index 2325cdb48c89deba18c4b86fa3ee711bb28e5ca0..f0209ec28ba38e73a232b329dfd585bac194dc43 100644
--- a/cmd/radioctl/radioctl.go
+++ b/cmd/radioctl/radioctl.go
@@ -8,6 +8,7 @@ import (
 	"log"
 	"net/url"
 	"os"
+	"strconv"
 	"strings"
 
 	"git.autistici.org/ale/autoradio"
@@ -18,6 +19,97 @@ import (
 // Format for output of structured data.
 var outputFormat = flag.String("format", "txt", "Output format for structured data (json, txt)")
 
+type optionalValue struct {
+	isSet bool
+}
+
+func (v *optionalValue) IsSet() bool {
+	return v.isSet
+}
+
+type stringOptionalValue struct {
+	*optionalValue
+	value string
+}
+
+func newStringOptionalValue() *stringOptionalValue {
+	return &stringOptionalValue{}
+}
+
+func (v *stringOptionalValue) Set(val string) error {
+	v.isSet = true
+	v.value = val
+	return nil
+}
+
+func (v *stringOptionalValue) Value() string {
+	return v.value
+}
+
+func (v *stringOptionalValue) String() string {
+	return v.value
+}
+
+func (v *stringOptionalValue) Get() interface{} {
+	return v.value
+}
+
+type intOptionalValue struct {
+	*optionalValue
+	value int
+}
+
+func newIntOptionalValue() *intOptionalValue {
+	return &intOptionalValue{}
+}
+
+func (v *intOptionalValue) Set(val string) error {
+	ival, err := strconv.Atoi(val)
+	v.value = ival
+	v.isSet = true
+	return err
+}
+
+func (v *intOptionalValue) Value() int {
+	return v.value
+}
+
+func (v *intOptionalValue) String() string {
+	return fmt.Sprintf("%v", v.value)
+}
+
+func (v *intOptionalValue) Get() interface{} {
+	return v.value
+}
+
+type floatOptionalValue struct {
+	*optionalValue
+	value float64
+}
+
+func newFloatOptionalValue() *floatOptionalValue {
+	return &floatOptionalValue{}
+}
+
+func (v *floatOptionalValue) Set(val string) error {
+	fval, err := strconv.ParseFloat(val, 64)
+	v.value = fval
+	v.isSet = true
+	return err
+}
+
+func (v *floatOptionalValue) Value() float64 {
+	return v.value
+}
+
+func (v *floatOptionalValue) String() string {
+	return fmt.Sprintf("%v", v.value)
+}
+
+func (v *floatOptionalValue) Get() interface{} {
+	return v.value
+}
+
 type HasAddFlags interface {
 	AddFlags(*gonutsflag.FlagSet)
 }
@@ -99,15 +191,6 @@ func printMount(m *autoradio.Mount) {
 	}
 }
 
-func addEncodingFlags(f *gonutsflag.FlagSet, p *autoradio.EncodingParams) {
-	f.StringVar(&p.SourceName, "source", "", "Source mountpoint")
-	f.StringVar(&p.Format, "codec", "", "Encoding format")
-	f.IntVar(&p.BitRate, "bitrate", 0, "Bitrate (kbps)")
-	f.IntVar(&p.SampleRate, "samplerate", 0, "Sample rate (Hz)")
-	f.IntVar(&p.Channels, "channels", 2, "Number of channels")
-	f.Float64Var(&p.Quality, "quality", 0, "Quality (alternatively to bitrate for some encoders)")
-}
-
 func mountExists(name string, client *autoradio.Client) bool {
 	m, _ := client.GetMount(name)
 	return m != nil
@@ -184,16 +267,26 @@ func newCreateTranscodingMountCommand() *createTranscodingMountCommand {
 	return &createTranscodingMountCommand{
 		BaseCommand: BaseCommand{
 			UsageLine: "create-transcoding-mount <path>",
-			Short:     "Create a transcoded mount",
+			Short:     "Create a transcoding mount",
 			Long: `
 Create a new stream that will transcode the parent stream with
 different encoding parameters.
 `,
 		},
-		params: &autoradio.EncodingParams{},
+		params: autoradio.NewEncodingParams(),
 	}
 }
 
+func addEncodingFlags(f *gonutsflag.FlagSet, p *autoradio.EncodingParams) {
+	f.StringVar(&p.SourceName, "source", "", "Source mountpoint")
+	f.StringVar(&p.Format, "codec", p.Format, "Encoding format")
+	f.Float64Var(&p.Quality, "quality", p.Quality, "Quality (for VBR encoders)")
+	f.IntVar(&p.BitRate, "bitrate", p.BitRate, "Bitrate (kbps)")
+	f.IntVar(&p.SampleRate, "samplerate", p.SampleRate, "Sample rate (Hz)")
+	f.IntVar(&p.Channels, "channels", p.Channels, "Number of channels")
+	f.StringVar(&p.StereoMode, "stereo-mode", p.StereoMode, "Stereo mode for mp3 codec (stereo, joint_stereo)")
+}
+
 func (cmd *createTranscodingMountCommand) AddFlags(f *gonutsflag.FlagSet) {
 	addEncodingFlags(f, cmd.params)
 	f.StringVar(&cmd.fallback, "fallback", "", "Fallback stream URL")
@@ -242,9 +335,15 @@ func (cmd *createTranscodingMountCommand) Run(args []string) {
 // Edit a mountpoint.
 type editMountCommand struct {
 	BaseCommand
-	params   *autoradio.EncodingParams
-	relay    string
-	fallback string
+	relay           *stringOptionalValue
+	fallback        *stringOptionalValue
+	transFormat     *stringOptionalValue
+	transSource     *stringOptionalValue
+	transBitRate    *intOptionalValue
+	transSampleRate *intOptionalValue
+	transChannels   *intOptionalValue
+	transStereoMode *stringOptionalValue
+	transQuality    *floatOptionalValue
 }
 
 var UNSET = "UNSET"
@@ -262,18 +361,45 @@ default, non-relay behavior, set the relay to the empty string
 (with --relay="").
 `,
 		},
-		params: &autoradio.EncodingParams{},
+		relay:           newStringOptionalValue(),
+		fallback:        newStringOptionalValue(),
+		transFormat:     newStringOptionalValue(),
+		transSource:     newStringOptionalValue(),
+		transBitRate:    newIntOptionalValue(),
+		transSampleRate: newIntOptionalValue(),
+		transChannels:   newIntOptionalValue(),
+		transStereoMode: newStringOptionalValue(),
+		transQuality:    newFloatOptionalValue(),
 	}
 }
 
 func (cmd *editMountCommand) AddFlags(f *gonutsflag.FlagSet) {
-	// Note that we use a magic value to figure out whether a flag
-	// has been specified or not, to make it possible to clear a
-	// field (by setting it to the empty string). There might be
-	// better way to do this.
-	f.StringVar(&cmd.relay, "relay", UNSET, "Upstream URL to relay")
-	f.StringVar(&cmd.fallback, "fallback", UNSET, "Fallback stream URL")
-	addEncodingFlags(f, cmd.params)
+	f.Var(cmd.relay, "relay", "Upstream URL to relay")
+	f.Var(cmd.fallback, "fallback", "Fallback stream URL")
+
+	f.Var(cmd.transSource, "source", "[transcoding] Source mountpoint")
+	f.Var(cmd.transFormat, "codec", "[transcoding] Encoding format")
+	f.Var(cmd.transQuality, "quality", "[transcoding] Quality (for VBR encoders)")
+	f.Var(cmd.transBitRate, "bitrate", "[transcoding] Bitrate (kbps)")
+	f.Var(cmd.transSampleRate, "samplerate", "[transcoding] Sample rate (Hz)")
+	f.Var(cmd.transChannels, "channels", "[transcoding] Number of channels")
+	f.Var(cmd.transStereoMode, "stereo-mode", "[transcoding] Stereo mode for mp3 encoding (stereo, joint_stereo)")
+}
+
+func (cmd *editMountCommand) transcodingOptionsSet() bool {
+	opts := []interface {
+		IsSet() bool
+	}{
+		cmd.transSource, cmd.transFormat, cmd.transQuality,
+		cmd.transBitRate, cmd.transSampleRate, cmd.transChannels,
+		cmd.transStereoMode,
+	}
+	for _, o := range opts {
+		if o.IsSet() {
+			return true
+		}
+	}
+	return false
 }
 
 func (cmd *editMountCommand) Run(args []string) {
@@ -290,11 +416,38 @@ func (cmd *editMountCommand) Run(args []string) {
 		log.Fatal("ERROR: mount not found")
 	}
 
-	if cmd.fallback != UNSET {
-		m.Fallback = cmd.fallback
+	// Only set those fields that were passed on the command line.
+	if cmd.fallback.IsSet() {
+		m.Fallback = cmd.fallback.Value()
+	}
+	if cmd.relay.IsSet() {
+		setRelay(m, cmd.relay.Value())
+	}
+
+	if cmd.transcodingOptionsSet() && m.Transcoding == nil {
+		log.Fatal("ERROR: can't set transcoding options on a non-transcoding mount (delete and re-create)")
+	}
+
+	if cmd.transFormat.IsSet() {
+		m.Transcoding.Format = cmd.transFormat.Value()
+	}
+	if cmd.transSource.IsSet() {
+		m.Transcoding.SourceName = cmd.transSource.Value()
+	}
+	if cmd.transBitRate.IsSet() {
+		m.Transcoding.BitRate = cmd.transBitRate.Value()
+	}
+	if cmd.transSampleRate.IsSet() {
+		m.Transcoding.SampleRate = cmd.transSampleRate.Value()
+	}
+	if cmd.transQuality.IsSet() {
+		m.Transcoding.Quality = cmd.transQuality.Value()
+	}
+	if cmd.transChannels.IsSet() {
+		m.Transcoding.Channels = cmd.transChannels.Value()
 	}
-	if cmd.relay != UNSET {
-		setRelay(m, cmd.relay)
+	if cmd.transStereoMode.IsSet() {
+		m.Transcoding.StereoMode = cmd.transStereoMode.Value()
 	}
 
 	if err := m.Valid(); err != nil {
diff --git a/node/liquidsoap.go b/node/liquidsoap.go
index 6298c06b6aafa4881d25e3f55b3408d1295b4557..bb6a03f044018e501f8fee61c14067ddca6f4d8f 100644
--- a/node/liquidsoap.go
+++ b/node/liquidsoap.go
@@ -24,7 +24,7 @@ set("log.file", false)
 set("log.stdout", true)
 
 upstream = mksafe(input.http("{{.SourceURL}}", buffer=5.0))
-output.icecast(%{{.Format}}(samplerate={{.SampleRate}}, {{if gt .BitRate 0}}bitrate={{.BitRate}}, {{end}}{{if gt .Quality 0.0}}quality={{.Quality}}, {{end}}{{if eq .Channels 2}}stereo{{else}}mono{{end}}),
+output.icecast(%{{.Format}}(samplerate={{.SampleRate}}, {{if gt .BitRate 0}}bitrate={{.BitRate}}, {{end}}{{if gt .Quality -1.0}}quality={{.Quality}}, {{end}}{{if .StereoMode}}stereo_mode={{.StereoMode}}, {{end}}{{if eq .Channels 2}}stereo{{else}}mono{{end}}),
   mount="{{.TargetMount}}", host="{{.TargetIP}}", port={{.TargetPort}}, user="{{.TargetUsername}}", password="{{.TargetPassword}}",
   upstream)
 `
@@ -51,6 +51,7 @@ type liquidsoapParams struct {
 	BitRate    int
 	SampleRate int
 	Channels   int
+	StereoMode string
 	Quality    float32
 }
 
@@ -69,6 +70,7 @@ func newLiquidsoapParams(mount *autoradio.Mount) *liquidsoapParams {
 		BitRate:        mount.Transcoding.BitRate,
 		SampleRate:     mount.Transcoding.SampleRate,
 		Channels:       mount.Transcoding.Channels,
+		StereoMode:     mount.Transcoding.StereoMode,
 		Quality:        float32(mount.Transcoding.Quality),
 	}
 }