/[svn]/linuxsampler/trunk/src/diskthread.cpp
ViewVC logotype

Annotation of /linuxsampler/trunk/src/diskthread.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10 - (hide annotations) (download)
Tue Nov 11 23:30:47 2003 UTC (20 years, 4 months ago) by senoner
File size: 10272 byte(s)
* src/audiothread.cpp, src/audiothread.h: added Sustain Pedal support
  implemented by postponing note-offs and leting multiple voices play
  on the same MIDI key.
* added the RTELMemoryPool Class which is a fast RT-safe memory allocator
  and list manger
* src/linuxsampler.cpp: added a voice and stream counter debug message

1 schoenebeck 9 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003 by Benno Senoner *
6     * *
7     * This program is free software; you can redistribute it and/or modify *
8     * it under the terms of the GNU General Public License as published by *
9     * the Free Software Foundation; either version 2 of the License, or *
10     * (at your option) any later version. *
11     * *
12     * This program is distributed in the hope that it will be useful, *
13     * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15     * GNU General Public License for more details. *
16     * *
17     * You should have received a copy of the GNU General Public License *
18     * along with this program; if not, write to the Free Software *
19     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
20     * MA 02111-1307 USA *
21     ***************************************************************************/
22    
23     #include "diskthread.h"
24    
25    
26     // *********** DiskThread **************
27     // *
28    
29    
30     // #########################################################################
31     // # Foreign Thread Section
32     // # (following code intended to be interface for audio thread)
33    
34    
35     /**
36     * Returns -1 if command queue is full, 0 on success (will be called by audio
37     * thread within the voice class).
38     */
39     int DiskThread::OrderNewStream(Stream::reference_t* pStreamRef, gig::Sample* pSample, unsigned long SampleOffset) {
40     dmsg(("Disk Thread: new stream ordered\n"));
41     if (CreationQueue->write_space() < 1) return -1;
42    
43     pStreamRef->State = Stream::state_active;
44     pStreamRef->OrderID = CreateOrderID();
45     pStreamRef->hStream = CreateHandle();
46     pStreamRef->pStream = NULL; // a stream has to be activated by the disk thread first
47    
48     create_command_t cmd;
49     cmd.pStreamRef = pStreamRef;
50     cmd.pSample = pSample;
51     cmd.SampleOffset = SampleOffset;
52    
53     CreationQueue->write(&cmd, 1);
54     return 0;
55     }
56    
57     /**
58     * Returns -1 if command queue is full, 0 on success (will be called by audio
59     * thread within the voice class).
60     */
61     int DiskThread::OrderDeletionOfStream(Stream::reference_t* pStreamRef) {
62     dmsg(("Disk Thread: stream deletion ordered\n"));
63     if (DeletionQueue->write_space() < 1) return -1;
64    
65     delete_command_t cmd;
66     cmd.pStream = pStreamRef->pStream;
67     cmd.hStream = pStreamRef->hStream;
68     cmd.OrderID = pStreamRef->OrderID;
69    
70     DeletionQueue->write(&cmd, 1);
71     return 0;
72     }
73    
74     /**
75     * Returns the pointer to a disk stream if the ordered disk stream
76     * represented by the \a StreamOrderID was already activated by the disk
77     * thread, returns NULL otherwise. If the call was successful, thus if it
78     * returned a valid stream pointer, the caller has to the store that pointer
79     * by himself, because it's not possible to call this method again with the
80     * same used order ID; this method is just intended for picking up an ordered
81     * disk stream. This method will usually be called by the voice class (within
82     * the audio thread).
83     *
84     * @param StreamOrderID - ID previously returned by OrderNewStream()
85     * @returns pointer to created stream object, NULL otherwise
86     */
87     Stream* DiskThread::AskForCreatedStream(Stream::OrderID_t StreamOrderID) {
88     dmsg(("Disk Thread: been asked if stream already created, OrderID=%x ", StreamOrderID));
89     Stream* pStream = pCreatedStreams[StreamOrderID];
90     if (pStream) { dmsg(("(yes created)")) }
91     else { dmsg(("(no not yet created)")) }
92     pCreatedStreams[StreamOrderID] = NULL; // free the slot for a new order
93     return pStream;
94     }
95    
96    
97    
98     // #########################################################################
99     // # Disk Thread Only Section
100     // # (following code should only be executed by the disk thread)
101    
102    
103     DiskThread::DiskThread(uint BufferWrapElements) : Thread(false, 1, -2) {
104     CreationQueue = new RingBuffer<create_command_t>(1024);
105     DeletionQueue = new RingBuffer<delete_command_t>(1024);
106     Streams = MAX_INPUT_STREAMS;
107     RefillStreamsPerRun = 4;
108     for (int i = 0; i < MAX_INPUT_STREAMS; i++) {
109     pStreams[i] = new Stream(131072, BufferWrapElements); // 131072 sample words
110     }
111     for (int i = 1; i <= MAX_INPUT_STREAMS; i++) {
112     pCreatedStreams[i] = NULL;
113     }
114     }
115    
116     DiskThread::~DiskThread() {
117     for (int i = 0; i < MAX_INPUT_STREAMS; i++) {
118     if (pStreams[i]) delete pStreams[i];
119     }
120     if (CreationQueue) delete CreationQueue;
121     if (DeletionQueue) delete DeletionQueue;
122     }
123    
124     int DiskThread::Main() {
125     dmsg(("Disk thread running\n"));
126     while (true) {
127     IsIdle = true; // will be set to false if a stream got filled
128    
129     // if there are creation commands, create new streams
130     while (Stream::UnusedStreams > 0 && CreationQueue->read_space() > 0) {
131     create_command_t command;
132     CreationQueue->read(&command, 1);
133     CreateStream(command);
134     }
135    
136     // if there are deletion commands, delete those streams
137     while (Stream::UnusedStreams < Streams && DeletionQueue->read_space() > 0) {
138     delete_command_t command;
139     DeletionQueue->read(&command, 1);
140     DeleteStream(command);
141     }
142    
143     RefillStreams(); // refill the most empty streams
144    
145     // if nothing was done during this iteration (eg no streambuffer
146     // filled with data) then sleep for 50ms
147     if (IsIdle) usleep(30000);
148 senoner 10
149     int act_streams=0;
150     for (int i = Streams - 1; i >= 0; i--) {
151     if (pStreams[i]->GetState() == Stream::state_active) act_streams++;
152     }
153     ActiveStreamCount=act_streams;
154    
155 schoenebeck 9 }
156    
157     return EXIT_FAILURE;
158     }
159    
160     void DiskThread::CreateStream(create_command_t& Command) {
161     // search for unused stream
162     Stream* newstream = NULL;
163     for (int i = Streams - 1; i >= 0; i--) {
164     if (pStreams[i]->GetState() == Stream::state_unused) {
165     newstream = pStreams[i];
166     break;
167     }
168     }
169     if (!newstream) {
170     dmsg(("No unused stream found (OrderID:%x) - report if this happens, this is a bug!\n", Command.pStreamRef->OrderID));
171     return;
172     }
173     dmsg(("new Stream launched by disk thread (OrderID:%x,StreamHandle:%x)\n", Command.pStreamRef->OrderID, Command.pStreamRef->hStream));
174     newstream->Launch(Command.pStreamRef, Command.pSample, Command.SampleOffset);
175     pCreatedStreams[Command.pStreamRef->OrderID] = newstream;
176     }
177    
178     void DiskThread::DeleteStream(delete_command_t& Command) {
179     if (Command.pStream) Command.pStream->Kill();
180     else { // the stream wasn't created by disk thread or picked up by audio thread yet
181    
182     // stream was created but not picked up yet
183     Stream* pStream = pCreatedStreams[Command.OrderID];
184     if (pStream) {
185     pStream->Kill();
186     pCreatedStreams[Command.OrderID] = NULL; // free slot for new order
187     return;
188     }
189    
190     // else we have to compare the handle with the one's of all streams
191     // (this case should occur only very rarely)
192     for (int i = 0; i < this->Streams; i++) {
193     if (pStreams[i]->GetHandle() == Command.hStream) {
194     pStreams[i]->Kill();
195     return;
196     }
197     }
198     }
199     }
200    
201     void DiskThread::RefillStreams() {
202     // sort the streams by most empty stream
203     qsort(pStreams, Streams, sizeof(Stream*), CompareStreamWriteSpace);
204    
205     // refill the most empty streams
206     for (uint i = 0; i < RefillStreamsPerRun; i++) {
207     if (pStreams[i]->GetState() == Stream::state_active) {
208    
209     //float filledpercentage = (float) pStreams[i]->GetReadSpace() / 131072.0 * 100.0;
210     //dmsg(("\nbuffer fill: %.1f%\n", filledpercentage));
211    
212     int writespace = pStreams[i]->GetWriteSpaceToEnd();
213     if (writespace == 0) break;
214    
215     int capped_writespace = writespace;
216     // if there is too much buffer space available then cut the read/write
217     // size to MAX_REFILL_SIZE which is by default 65536 samples = 256KBytes
218     if (writespace > MAX_REFILL_SIZE) capped_writespace = MAX_REFILL_SIZE;
219    
220     // adjust the amount to read in order to ensure that the buffer wraps correctly
221     int read_amount = pStreams[i]->AdjustWriteSpaceToAvoidBoundary(writespace, capped_writespace);
222     // if we wasn't able to refill one of the stream buffers by more than
223     // MIN_REFILL_SIZE we'll send the disk thread to sleep later
224     if (pStreams[i]->ReadAhead(read_amount) > MIN_REFILL_SIZE) this->IsIdle = false;
225     }
226     }
227     }
228    
229     /// Handle Generator
230     Stream::Handle DiskThread::CreateHandle() {
231     static uint32_t counter = 0;
232     if (counter == 0xffffffff) counter = 1; // we use '0' as 'invalid handle' only, so we skip 0
233     else counter++;
234     return counter;
235     }
236    
237     /// order ID Generator
238     Stream::OrderID_t DiskThread::CreateOrderID() {
239     static uint32_t counter = 0;
240     do {
241     if (counter == MAX_INPUT_STREAMS) counter = 1; // we use '0' as 'invalid order' only, so we skip 0
242     else counter++;
243     } while (pCreatedStreams[counter]); // until empty slot found
244     return counter;
245     }
246    
247    
248    
249     // *********** C functions **************
250     // *
251    
252     /**
253     * This is the comparison function the qsort algo uses to determine if a value is
254     * bigger than another one or special in our case; if the writespace of a stream
255     * is bigger than another one.
256     */
257     int CompareStreamWriteSpace(const void* A, const void* B) {
258     Stream* a = *(Stream**) A;
259     Stream* b = *(Stream**) B;
260     return b->GetWriteSpace() - a->GetWriteSpace();
261     }

  ViewVC Help
Powered by ViewVC