1// --------------------------------------------------------------------------
2// Name: sndesd.cpp
3// Purpose:
4// Date: 08/11/1999
5// Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999
6// CVSID: $Id: sndesd.cpp 35650 2005-09-23 12:56:45Z MR $
7// wxWindows licence
8// --------------------------------------------------------------------------
9
10#include "wx/wxprec.h"
11
12#ifndef WX_PRECOMP
13    #include "wx/defs.h"
14    #include "wx/string.h"
15#endif
16
17#ifdef __BORLANDC__
18    #pragma hdrstop
19#endif
20
21// --------------------------------------------------------------------------
22// MMedia headers
23// --------------------------------------------------------------------------
24
25#include "wx/mmedia/sndbase.h"
26#include "wx/mmedia/sndesd.h"
27#include "wx/mmedia/sndpcm.h"
28
29// --------------------------------------------------------------------------
30// System headers
31// --------------------------------------------------------------------------
32
33#ifdef HAVE_ESD_H
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <unistd.h>
37#include <esd.h>
38#ifdef __WXGTK__
39#include <gdk/gdk.h>
40#endif
41#endif
42
43// --------------------------------------------------------------------------
44
45#define MY_ESD_NAME "wxWidgets/wxSoundStreamESD"
46
47// --------------------------------------------------------------------------
48// wxSoundStreamESD: ESD sound driver
49
50// --------------------------------------------------------------------------
51// Constructors/Destructors
52// --------------------------------------------------------------------------
53
54wxSoundStreamESD::wxSoundStreamESD(const wxString& hostname)
55{
56#ifndef HAVE_ESD_H
57    m_snderror = wxSOUND_INVDEV;
58    return;
59#else
60    wxSoundFormatPcm pcm_default;
61
62    // First, we make some basic test: is there ESD on this computer ?
63    m_esd_ok = false;
64
65    if (hostname.IsNull())
66        m_fd_output = esd_play_stream(ESD_PLAY | ESD_STREAM, 22050,
67                                      hostname.mb_str(), MY_ESD_NAME);
68    else
69        m_fd_output = esd_play_stream(ESD_PLAY | ESD_STREAM, 22050,
70                                      NULL, MY_ESD_NAME);
71    if (m_fd_output == -1) {
72        // Answer: no. We return with an error.
73        m_snderror = wxSOUND_INVDEV;
74        return;
75    }
76
77    // Close this unuseful stream.
78    esd_close(m_fd_output);
79
80    m_hostname = hostname;
81
82    // Set the default audio format
83    SetSoundFormat(pcm_default);
84
85    // Initialize some variable
86    m_snderror = wxSOUND_NOERROR;
87    m_esd_stop = true;
88    m_q_filled = true;
89    m_esd_ok   = true;
90    m_fd_output= -1;
91    m_fd_input = -1;
92#endif // defined HAVE_ESD_H
93}
94
95wxSoundStreamESD::~wxSoundStreamESD()
96{
97#ifdef HAVE_ESD_H
98    if (!m_esd_stop)
99        StopProduction();
100#endif // defined HAVE_ESD_H
101}
102
103// --------------------------------------------------------------------------
104// Read several samples
105// --------------------------------------------------------------------------
106
107wxSoundStream& wxSoundStreamESD::Read(void *buffer, wxUint32 len)
108{
109#ifndef HAVE_ESD_H
110    m_snderror = wxSOUND_INVDEV;
111    return *this;
112#else
113    int ret;
114
115    if (m_esd_stop) {
116        m_snderror = wxSOUND_NOTSTARTED;
117        return *this;
118    }
119
120    ret = read(m_fd_input, buffer, len);
121    m_lastcount = (wxUint32)ret;
122
123    if (ret < 0)
124        m_snderror = wxSOUND_IOERROR;
125    else
126        m_snderror = wxSOUND_NOERROR;
127
128    return *this;
129#endif // defined HAVE_ESD_H
130}
131
132// --------------------------------------------------------------------------
133// Write several samples
134// --------------------------------------------------------------------------
135wxSoundStream& wxSoundStreamESD::Write(const void *buffer, wxUint32 len)
136{
137#ifndef HAVE_ESD_H
138    m_snderror = wxSOUND_INVDEV;
139    return *this;
140#else
141    int ret;
142
143    if (m_esd_stop) {
144        m_lastcount = 0;
145        m_snderror = wxSOUND_NOTSTARTED;
146        return *this;
147    }
148
149    ret = write(m_fd_output, buffer, len);
150    m_lastcount = (wxUint32)ret;
151
152    if (ret < 0)
153        m_snderror = wxSOUND_IOERROR;
154  else
155      m_snderror = wxSOUND_NOERROR;
156
157    m_q_filled = true;
158
159    return *this;
160#endif // defined HAVE_ESD_H
161}
162
163// --------------------------------------------------------------------------
164// SetSoundFormat(): this function specifies which format we want and which
165// format is available
166// --------------------------------------------------------------------------
167bool wxSoundStreamESD::SetSoundFormat(const wxSoundFormatBase& format)
168{
169#ifndef HAVE_ESD_H
170    m_snderror = wxSOUND_INVDEV;
171    return false;
172#else
173    wxSoundFormatPcm *pcm_format;
174
175    if (format.GetType() != wxSOUND_PCM) {
176        m_snderror = wxSOUND_INVFRMT;
177        return false;
178    }
179
180    if (!m_esd_ok) {
181        m_snderror = wxSOUND_INVDEV;
182        return false;
183    }
184
185    if (m_sndformat)
186        delete m_sndformat;
187
188    m_sndformat = format.Clone();
189    if (!m_sndformat) {
190        m_snderror = wxSOUND_MEMERROR;
191        return false;
192    }
193    pcm_format = (wxSoundFormatPcm *)m_sndformat;
194
195    // Detect the best format
196    DetectBest(pcm_format);
197
198    m_snderror = wxSOUND_NOERROR;
199    if (*pcm_format != format) {
200        m_snderror = wxSOUND_NOEXACT;
201        return false;
202    }
203    return true;
204#endif // defined HAVE_ESD_H
205}
206
207// --------------------------------------------------------------------------
208// _wxSound_OSS_CBack (internal): it is called when the driver (ESD) is
209// ready for a next buffer.
210// --------------------------------------------------------------------------
211#if defined(__WXGTK__) && defined(HAVE_ESD_H)
212static void _wxSound_OSS_CBack(gpointer data, int source,
213                               GdkInputCondition condition)
214{
215    wxSoundStreamESD *esd = (wxSoundStreamESD *)data;
216
217    switch (condition) {
218        case GDK_INPUT_READ:
219            esd->WakeUpEvt(wxSOUND_INPUT);
220            break;
221        case GDK_INPUT_WRITE:
222            esd->WakeUpEvt(wxSOUND_OUTPUT);
223            break;
224        default:
225            break;
226    }
227}
228#endif
229
230
231// --------------------------------------------------------------------------
232// WakeUpEvt() (internal): it is called by _wxSound_OSS_CBack to bypass the
233// C++ protection
234// --------------------------------------------------------------------------
235void wxSoundStreamESD::WakeUpEvt(int evt)
236{
237    m_q_filled = false;
238    OnSoundEvent(evt);
239}
240
241// --------------------------------------------------------------------------
242// StartProduction(): see wxSoundStream
243// --------------------------------------------------------------------------
244bool wxSoundStreamESD::StartProduction(int evt)
245{
246#ifndef HAVE_ESD_H
247    m_snderror = wxSOUND_INVDEV;
248    return false;
249#else
250    wxSoundFormatPcm *pcm;
251    int flag = 0;
252
253    if (!m_esd_ok) {
254        m_snderror = wxSOUND_INVDEV;
255        return false;
256    }
257
258    if (!m_esd_stop)
259        StopProduction();
260
261    pcm = (wxSoundFormatPcm *)m_sndformat;
262
263    flag |= (pcm->GetBPS() == 16) ? ESD_BITS16 : ESD_BITS8;
264    flag |= (pcm->GetChannels() == 2) ? ESD_STEREO : ESD_MONO;
265
266    if ((evt & wxSOUND_OUTPUT) != 0) {
267        flag |= ESD_PLAY | ESD_STREAM;
268        m_fd_output = esd_play_stream(flag, pcm->GetSampleRate(), NULL,
269                                      MY_ESD_NAME);
270    }
271
272    if ((evt & wxSOUND_INPUT) != 0) {
273        flag |= ESD_RECORD | ESD_STREAM;
274        m_fd_input = esd_record_stream(flag, pcm->GetSampleRate(), NULL,
275                                       MY_ESD_NAME);
276    }
277
278#ifdef __WXGTK__
279    if ((evt & wxSOUND_OUTPUT) != 0) {
280        m_tag_output = gdk_input_add(m_fd_output, GDK_INPUT_WRITE,
281                                     _wxSound_OSS_CBack, (gpointer)this);
282    }
283    if ((evt & wxSOUND_INPUT) != 0) {
284        m_tag_input = gdk_input_add(m_fd_input, GDK_INPUT_READ,
285                                    _wxSound_OSS_CBack, (gpointer)this);
286    }
287#endif
288
289    m_esd_stop = false;
290    m_q_filled = false;
291
292    return true;
293#endif // defined HAVE_ESD_H
294}
295
296// --------------------------------------------------------------------------
297// StopProduction(): see wxSoundStream
298// --------------------------------------------------------------------------
299bool wxSoundStreamESD::StopProduction()
300{
301#ifndef HAVE_ESD_H
302    m_snderror = wxSOUND_INVDEV;
303    return false;
304#else
305    if (m_esd_stop)
306        return false;
307
308    if (m_fd_input != -1) {
309        esd_close(m_fd_input);
310#ifdef __WXGTK__
311        gdk_input_remove(m_tag_input);
312#endif
313    }
314    if (m_fd_output != -1) {
315        esd_close(m_fd_output);
316#ifdef __WXGTK__
317        gdk_input_remove(m_tag_output);
318#endif
319    }
320
321    m_fd_input = -1;
322    m_fd_output= -1;
323    m_esd_stop = true;
324    m_q_filled = true;
325    return true;
326#endif // defined HAVE_ESD_H
327}
328
329//
330// Detect the closest format (The best).
331//
332void wxSoundStreamESD::DetectBest(wxSoundFormatPcm *pcm)
333{
334#ifndef HAVE_ESD_H
335    m_snderror = wxSOUND_INVDEV;
336    return;
337#else
338    wxSoundFormatPcm best_pcm;
339
340    // We change neither the number of channels nor the sample rate
341    // because ESD is clever.
342
343    best_pcm.SetSampleRate(pcm->GetSampleRate());
344    best_pcm.SetChannels(pcm->GetChannels());
345
346    // It supports 16 bits
347    if (pcm->GetBPS() >= 16)
348        best_pcm.SetBPS(16);
349    else
350        best_pcm.SetBPS(8);
351
352    best_pcm.SetOrder(wxLITTLE_ENDIAN);
353    best_pcm.Signed(true);
354
355    // Finally recopy the new format
356    *pcm = best_pcm;
357#endif // defined HAVE_ESD_H
358}
359
360