3

I'm trying to make a .wav file generator in Emacs Lisp. I have some binary data, as a sequence of bytes:

(setq binary-data (list 62 236 60))

I would like to insert that data into a file. I expect that file to be three bytes long.

If I try to insert it into a buffer, then write that buffer to a file, I get a file that consists of four bytes:

(with-temp-buffer
  (seq-doseq (char binary-data)
    (insert char))
  (write-region nil nil "~/char-inserted.wav"))

$ hd ~/char-inserted.wav
00000000  3e c3 ac 3c                                       |>..<|
00000004

So it seems like I'm having problems with inserting that single byte. I've also tried converting the data into a unibyte string, but that still gives me a four-byte file:

(with-temp-buffer
  (seq-doseq (char (apply #'unibyte-string binary-data))
    (insert char))
  (write-region nil nil "~/unibyte-string.wav"))

$ hd ~/unibyte-string.wav
00000000  3e c3 ac 3c                                       |>..<|
00000004

I've also converted the unibyte string to a multibyte string:

(with-temp-buffer
  (seq-doseq (char (string-as-multibyte (apply #'unibyte-string binary-data)))
    (insert char))
  (write-region nil nil "~/multibyte-string.wav"))

But this gives me an error:

> These default coding systems were tried to encode text
in the buffer ‘ *temp*-110101’:
  (utf-8-unix (2 . 4194284))
However, each of them encountered characters it couldn’t encode:
utf-8-unix cannot encode these: 

It suggests I select one of the coding systems "raw-text" or "no-conversion", but I'm unsure how to do that, or if it will even help.

How do I save these three bytes into a file that ends up as a three-byte file? What's going on here?

1 Answer 1

3

At a minimum, if by default your buffers are multibyte, you will need to toggle this specific buffer to unibyte; you also need to set the file coding system of the buffer to raw-text:

  (with-temp-buffer
    (toggle-enable-multibyte-characters)
    (set-buffer-file-coding-system 'raw-text)
    (seq-doseq (char binary-data)
      (insert char))
    (write-region nil nil "~/char-inserted.wav"))

You might also have to specify no-conversion for the write, but I'm not sure. Something like this perhaps:

(let ((coding-system-for-write 'no-conversion))
  (with-temp-buffer
    (toggle-enable-multibyte-characters)
    (set-buffer-file-coding-system 'raw-text)
    (seq-doseq (char binary-data)
      (insert char))
    (write-region nil nil "~/char-inserted.wav")))

But there may be other gotcha's as well: it would be good to have an answer by an expert.

4
  • Thanks! With either of these, I still get a four-byte file. Do they result in a three-byte file for you? I'll play around with options for coding-system-for-write.
    – zck
    Jul 5, 2020 at 3:39
  • Missed something - edited.
    – NickD
    Jul 5, 2020 at 13:06
  • Ah, it looks like toggle-enable-multibyte-characters was the secret! With that, I don't need either coding-system-for-write nor set-buffer-file-coding-system, at least to get this specific three-byte file. Thanks so much!
    – zck
    Jul 5, 2020 at 16:05
  • I suspect that in general, you will need all three: what I'm not sure about is whether you'll need more.
    – NickD
    Jul 5, 2020 at 16:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.