Streaming Audio from Windows to PulseAudio Server

PulseAudio is a great network audio server for Linux. It allows me to stream audio between machines. However, the biggest problem with PulseAudio is that it doesn’t have a descent client implementation for Windows. There are a couple known workarounds such as using WinESD, but they are all pretty experimental. Also, the Win32 binary build didn’t work for me, perhaps because it’s somewhat outdated and buggy.

My first try was to use LineInCode and Netcat for Windows on the client side and to use module-simple-protocol-tcp on the server side. After adding -L "module-simple-protocol-tcp port=4712 rate=44100 format=s16le channels=2" option to the PulseAudio startup option, I was able to stream audio from Windows to PulseAudio server using the following command:

linco.exe -B 16 -C 2 -R 44100 | nc.exe <host> 4712

However, PulseAudio dies whenever the connection is closed because of a known issue of module-simple-protocol-tcp. I wrote some script that restarts PulseAudio whenever it dies, but I realized there’s a better solution – OpenSSH.

I have installed a Cygwin build of OpenSSH, and replaced Netcat with it:

linco.exe -B 16 -C 2 -R 44100 | ssh.exe <[email protected]> "cat - | pacat --playback"

This solution works without module-simple-protocol-tcp, and therefore there’s no need to restart the PulseAudio server on disconnection. Better security is another bonus. Instead, it requires a running SSH server. If SSHD is not running in the PulseAudio server, you can give it a little twist:

linco.exe -B 16 -C 2 -R 44100 | ssh.exe <[email protected]> "cat - | pacat --server <host> --playback"

In case your Windows machine doesn’t have a sound card or it doesn’t allow you to capture the audio stream, you might want to use Virtual Audio Cable. It’s a commercial software, but it worths a purchase.

You might also want to set up a passwordless login so you don’t have to enter the password every time you stream the audio.

영한 혼용과 똘레랑스

영어는 우리 생활 곳곳에 침투해 있다. 어떤 사람은 그것에 더 많이 익숙하고 다른 이는 그렇지 못하다. 나는 그 중간 즈음에 위치했다고 생각하고 있다. 그래서일까, 외국 생활을 오래 했다거나 외국계 회사에서 오래 일했다는 이유로 한국어 문장 속에서 영어를 혼용하는 것이 아직은 거북하게 들린다. 대표적으로 다음과 같은 것들 말이다:

  • Boss – 상사
  • Claim – 불만 사항
  • Expense – 비용
  • Industry – 업계
  • Meeting – 회의
  • Office – 사무소
  • Payroll – 월급 명세
  • Position – 직위
  • Post – 글
  • Report – 보고

수시로 문장마다 한자를 섞어 쓰는 마당에 무엇이 문제냐고 묻는다면, 그것은 아마도 익숙함의 차이 아니겠냐고 대답하겠다. 내 모든 글에서 가능한 글자를 전부 한자로 바꿔버린다면 거북해 할 사람들이 영어로 바꿀 경우보다 훨씬 많으리라고 예상할 수 있듯 말이다.

하지만 중간 즈음에 위치한 사람인 나는 혼란스럽다. 나도 모르게 섞어 말하고, 적절한 한국어 단어가 떠오르지 않는 일이 생긴다. 그러면서도 상대방에게서 그런 경우를 보면 왠지 거북하고, 그래서 내게서 그런 말이 나오면 부끄러울 때도 있다.

그런데 이것을 조금만 더 확장해 보면, 수학자가 각종 수학 용어를 섞어 일반인과 소통한다면 그것이 비록 영어가 털끝만치도 섞이지 않은 순수한 한국어라 할 지라도 거북하기는 마찬가지일 것이라고 쉽게 예상할 수 있다. 특히나 수학 기호나 미적분학 용어만 보면 멀미가 나는 사람이 대부분이라고 가정했을 때는 더욱 그렇다.

그러니까 거북함이라는 것은 어쩌면 그냥 ‘나한테 익숙하지 않아서‘인지도 모르겠다. 내가 모르거나 익숙하지 않은 말을 들었을 때 상대방이 왠지 잘난 체 하는 것 같다는 인상을 받는 것처럼 말이다.

이렇게 별의 별 것이 상대적으로 인식된다고 생각하기 시작하면 내가 남을 무제한적으로 배려해야 하는지, 아니면 그 반대인지 혼란스러워지기 시작한다. 이상적으로야 똘레랑스를 바탕으로 서로를 자유롭게 받아들여야 마땅하겠지만, 끼리 끼리 논다는 말이 좀 더 현실적이다. 결국에는 비슷한 취향의 사람들이나 서로 받아들일 수 있는 취향을 가진 사람들끼리 모이게 되는 것 같다.

그리고 이런 끼리 끼리 성향은 나이가 들 수록 고착화되고 새 친구를 사귀기 어렵게 만들어, 노년에는 세상을 떠나가는 친구들을 그리워하는 마음만 깊어지게 되는 것 아닐까, 어렴풋이 두려움을 깔고 생각해 본다.

추석맞이 이직 1년 결산

9월 1일, 이직을 했다. 나는 덕택에 풀타임 오픈 소서 및 수석 소프트웨어 엔지니어라는 명칭을 얻었다. 전보다 훨씬 많은 급여를 받게 되었다. 거기다가 마치 ‘노동의 미래‘나 ‘세계는 평평하다‘를 그대로 옮겨 놓은 듯한 재택 근무 작업 환경도 차렸다.

무엇을 사더라도 예전보다도 ‘더 멋진’ 물건을 찾기 시작했다. 더 많은 음반이 아마존을 통해 내 콜렉션에 추가되었다. 더 좋은 의료 서비스를 받을 수 있게 되었고, 그 결과 전반적인 건강상의 향상도 얻었다. 이 정도라면 거의 모든 면에서 내 삶의 질은 진보했다, 라고 평할 수 있을까?

다소 설명하기 어렵지만, 그에 비해 내면의 질은 많은 향상을 필요로 하고 있다. 나는 전보다 더 바빠졌다. 수영왕 펠프스가 연습시간에는 수영을 하고, 수영 녹화 영상을 보는 것으로 여가를 보낸다면, 프로그래밍왕도 아닌 나는 시간대를 허물어가며 업무 시간에는 프로그래밍을 하고, 프로그래밍으로 여가를 보내고 있다.

아마도 그것은 단순히 새 직장 때문은 아닐 것이다. 그저 나의 프로그래밍에 대한 순수한, 아니 본능에 가까운 욕구가 새 직장이라는 침실을 차지하게 되면서 생긴 예측된 수순에 다름없다. 이러한 실락원에서나 볼법한 뜨거운 만남은 나를 자발적 과로로 인한 커리어 파멸로 이끌 것인가, 아니면 다음 단계로의 도약으로 이끌 것인가.

정답이 없는 것이 당연하다고 할 수 있겠지만, 그래도 어떻게 될 지 내 스스로도 모르겠다. 나는 전보다 교양서나 소설을 더 적게 읽는다. 뿐만 아니라 기술서도 더 적게 읽는다. 이 상황의 심각성은 내가 ‘알만큼 알아서‘와는 거리가 먼 상황에 놓여 있기 때문에 더욱 두드러진다. 한 때 날렸던 누군가가 새로 들어온 똑똑한 친구에게 험담이나 당하는 꼴을 상상하게 되는 것은 한 때 그 ‘똑똑한 친구‘에 해당했던 나로서는 당연한 일이 아닐 수 없다.

나는 어디까지 갈 수 있고, 또 어디까지 가야 하는가. 그것은 다르게 말한다면 ‘내가 가장 유능할 수 있는 위치는 어디까지이고, 내가 가장 무능할 수 있는 위치는 어디부터인가‘일 것이다. 그리고 그 위치는 지금으로서는 뭐랄까, 다소 무섭게 다가온다.

그렇기에 오늘도, 조금은 힘들지만 변화를 꿈꾼다.

한국 미나 사용자께 드리는 네티로의 전환 권고

미나 프로젝트에서 손을 뗀 후, 지난 약 3개월간 네티 프로젝트 에 상당한 시간을 할애하였고, 그 결과 현재 API 와 구현 모두 안정화 단계에 접어들었습니다.

적절한 문서화, 공개 가능한 성능 테스트 보고서 작성 및 웹 사이트 컨텐츠 보강을 마치는 대로 최초의 안정 버전이 릴리즈될 예정입니다. 지금까지 적어도 2 주에 한 번씩은 릴리즈를 해 왔고, 앞으로 2 주 내로 릴리즈 계획이 되어 있으므로, 미나처럼 불필요하게 프로젝트의 릴리즈가 늦어지는 일은 없을 것으로 예상합니다.

네티 안정 버전의 릴리즈와 동시에 미나 프로젝트에 대한 한국어 지원에 할애하는 시간을 많이 줄일 예정이니, 양해 부탁드리겠습니다. (상황에 따라 답장을 못 보내 드릴 수도 있습니다.) 따라서 모든 미나 관련 문의는 공식 커뮤니티를 통해 해 주시면 감사하겠습니다.

네티 프로젝트는 제가 직접 운영하는 한국어 사용자 그룹을 갖고 있습니다. 따라서 네티 프로젝트에 대한 모든 문의 사항 및 피드 백은 네티 한국어 사용자 그룹 을 통해 해 주시면 성심 성의껏 회신하도록 하겠습니다.

만약 진행중이신 프로젝트가 초기 단계에 있다면 네티로의 전환을 심각하게 고려하시기를 권합니다. 부가 기능 (예: JMX) 면에서 부족함이 있으나, 스루풋, 레이턴시, 유연성, 안정성 등 대부분의 면에서 비교할 수 없는 경험이 되리라 확신합니다.

Netty reaches 75%+ test coverage. What's next?

Netty 3.0.0.CR3 was my first serious attempt to achieve high test coverage by writing many test cases in my life, and it was very exciting for me. I was able to fix more than a dozen bugs with them.

EclEmma was very helpful to review all the source code and to achieve high test coverage. The byte code instrumented by Emma ran much faster than other open source test coverage tools, yet providing reliable numbers.

Granted that both the API and implementation of Netty became stable enough, I’m going to focus on writing JavaDoc and user guide for the next week. The first GA is expected in a couple weeks – please stay tuned!

As always, your feed back is more than appreciated.

Netty moves its nest from GoogleCode to JBoss.org with its first release candidate

Finally, I have managed to move Netty to JBoss.org and announce the first release candidate. If you were using Google Groups, you need to subscribe again.

Here’s the new URL:

and here are the related announcement messages:

For those who don’t know what Netty is yet — The Netty project is an effort to provide an asynchronous · event-driven network application framework for rapid development of maintainable high-performance · high-scalability protocol servers and clients, including its related out-of-the-box protocol extensions and tool suite. To somewhat oversimplify, it’s a framework that allows you to write a NIO client and server very easily.

Using GNOME Evolution with a huge IMAP mailbox

Update: The latest development snapshot of Opera 9.5 has full support for virtual folder with powerful filter which supports a regular expression. Let’s give it a try! Adding GPG signing and better multiple identity support would be an icing on the cake.

I’ve been struggling with various mail clients to deal with my huge IMAP mailbox which contains about 200k messages. I didn’t want to split it into more than one. It’s not my job but an e-mail client’s job; see what GMail does!

Although Opera Mail does a great job on dealing with such a big IMAP mailbox, it lacks a couple critical features which make me have a difficulty in organizing my messages. I have missed Evolution virtual folders especially.

However, all other GUI/Web-based e-mail clients than Opera Mail are very poor at dealing with a huge IMAP mailbox. It seems like IMAP support is not a high priority task for most e-mail clients in the world. The latest alpha version of RoundCube seems to be very efficient but it lacks too many features at this moment.

Before I give up again and stick to Opera Mail, I decided to give a try to various IMAP-to-Maildir synchronization tools – isync and OfflineIMAP. Long time agao, I had a bad experience with OfflineIMAP and it seems like it doesn’t work with my account yet. By contrast, isync was indeed a great tool which does its job without any problem.

One problem with isync is that its configuration file format is somewhat undocumented, so I’d like to share my settings:

# ~/.mbsyncrc
MaildirStore Local
Path ~/.maildir
Inbox ~/.maildir

IMAPStore Remote
Host <hostname>
User <username>
Pass <password>
CertificateFile ~/.mbsync.crt

Channel Local-Remote
Master :Remote:
Slave :Local:
Create Slave
Sync All

You also need to set up your crontab:http://en.wikipedia.org/wiki/Vixie_cron#Modern_versions to run mbsync:http://isync.sourceforge.net/mbsync.html periodically because Evolution doesn’t provide an option to execute a command before refreshing the local Maildir. Here’s my crontab:

*/5 * * * * pgrep -f "^(/usr/bin/)?evolution" > /dev/null && mbsync -a -q

Please note that I used pgrep to make sure mbsync runs only when Evolution is running.

Finally, I succeeded to make Evolution run pretty fast with my IMAP mailbox, although it’s a kind of workaround. However, I don’t think this is a ugly hack. Considering that Opera Mail stores my all messages in its local storage, what Evolution does for the local Maildir is very similar to what Opera Mail does. It creates a full text index for all messages and maintains the index for every message operation. What’s missing is immediate mailbox synchronization based on IMAP notification, and it shouldn’t be difficult to be integrated into Evolution codebase IMHO.

PS: I had a difficulty searching for isync because Apple has a product with the same name. The actual executable of isync is mbsync, so try this google search.

Filling the gap between blocking I/O and NIO

A non-blocking NIO Channel and a blocking InputStream have inevitable impedance mismatch. Because an InputStream is supposed to block for every read operation unless there’s some data available in the buffer, any InputStream-based decoder implementation can’t be used with a non-blocking NIO application right away.

A common workaround is to prepend a length field for each message so you can wait until you read a whole message before calling InputStream.read(). However, this turns your NIO application incompatible with a legacy blocking I/O application because the legacy application doesn’t prepend a length field at all. You might have managed to modify the legacy application to prepend a length field, but we know it’s not always the case. We need something to fill this gap between two I/O paradigms.

An ObjectInput/OutputStream-based blocking I/O network applications are the most common case because it was considered to be the easiest quick-and-dirty solution for intranet Java object exchange. It’s as simple as wrapping an InputStream of a Socket with an ObjectInputStream (i.e. in = new ObjectInputStream(socket.getInputStream());).

How can we implement a NIO network application to be interoperable with those legacy applications without any modification? It was considered to be impossible… until today!

I’ve just released a new milestone of Netty which addresses the issue I described above. It provides CompatibleObjectEncoder and CompatibleObjectDecoder, which retains interoperability with the legacy ObjectInput/OutputStream-based socket applications.

You will also find you can do the same for any kind of InputStream implementations with Netty’s ReplayingDecoder with fairly small amount of effort, which means you can shift the paradigm of your complicated blocking protocol client/server to more scalable non-blocking paradigm while retaining most legacy code.

The excitement of ReplayingDecoder doesn’t stop here. It also allows you to implement a non-blocking decoder in a blocking paradigm. In a non-blocking paradigm, you always had to check if there’s enough data in the buffer, like the following:

public boolean decode(ByteBuffer in) {
  if (in.remaining() < 4) {
    return false;
  }

  // Read the length header.
  int position = in.position();
  int length = in.getInt();
  if (in.remaining() < length) {
    in.position(position);
    return false;
  }

  // Read the body.
  byte[] data = new byte[length];
  in.get(data);
  ...
  return true;
}

With ReplayingDecoder, you don’t need to check the availability of the input buffer at all:

public void decode(ByteBuffer in) {
  // Read the length header.
  int length = in.getInt();

  // Read the body.
  byte[] data = new byte[length];
  in.get(data);
  ...
}

How could this work? ReplayingDecoder uses a sort of continuation technique. It rewinds the buffer position to the beginning when there’s not enough data in the buffer automatically and calls decode() again (i.e. replays the decode) when more data is received from a remote peer.

You might think this is pretty inefficient, but it turned out to be very efficient in most cases. Higher throughput means lower chance of replay because we will receive more than one message for a single input buffer (often dozens). Consequently, most messages will be decoded in one shot without a replay. In case of slow connection, it will be less than optimal but you won’t see much difference because it’s already slow because the connection itself is slow. Just compare the code complexity of the two paradigms. I’d definitely go for the latter.

A problem with recent RSS feeds in gleamynode.net

Brett Porter kindly pointed me out that there’s some problem with the RSS feed of gleamynode.net. I have updated FeedBurner information along with the meta tags to make sure the same problem doesn’t occur again. Also, please make sure that you are using the FeedBurner RSS feed if you are subscribed to this blog. Apologies.

Brett Porter가 친절하게도 gleamynode.net의 RSS 피드에 문제가 있다는 사실을 지적해 주었습니다. 메타 태그와 피드버너 정보를 수정해서 같은 문제가 다시 일어나지 않도록 조치했습니다. 또, 만약 이 블로그를 구독중이시라면 피드버너 RSS 주소를 사용하고 있는지 확인해 주시길 바랍니다. 불편을 끼쳐 드려 죄송합니다.